初始提交:关节模组仿真平台
- 三接口契约:自包含 MJCF / 配置 schema / 报告计算规范 - Python 流水线:urdf_to_mjcf → generate_schema → simulate_report(validate_module 一键编排) - 输入案例 urdf + 生成产物 output(自包含 MJCF/schema/报告/网格副本) - 详细架构说明 docs/architecture.md
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
# 关节模组仿真平台 —— 架构与开发说明(详细版)
|
||||
|
||||
> 想快速上手,先看根目录 [README.md](../README.md)(一页看懂);本文件是**详细版**,讲脚本语义、网页接入、用户上传流程与占位参数。
|
||||
|
||||
本项目是「浏览器在线 MuJoCo 仿真平台」的**关节模组**部分:把关节模组(行星轮系)在本地用
|
||||
MuJoCo 跑通,并留好三个接口给网页团队,最终让「平台内置的关节模组」和「用户上传的关节模组」
|
||||
都能在浏览器里跑仿真、出报告。
|
||||
|
||||
> 团队三模块分工:关节模组(本目录)/ 机械臂 / 四足机器狗。团队用 MuJoCo WASM
|
||||
> (`@mujoco/mujoco`)做网页展示,引擎与本地 Python 是同一个 C++ 核心,**MJCF(.xml)是统一模型格式**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 文件清单(哪个文件是什么)
|
||||
|
||||
| 文件 | 是什么 | 给谁用 |
|
||||
|---|---|---|
|
||||
| `../urdf/planetary_joint_split_motor_demo.urdf` | **原始 URDF**(本案例的输入,只有运动学+视觉,缺质量/惯量/电机) | 转换工具输入 |
|
||||
| `../urdf/meshes/*.stl` | 原始网格(STL,单位 mm) | 转换工具读体积分 |
|
||||
| `../output/planetary_joint_split_motor_demo.xml` | **MJCF 模型**(`urdf_to_mjcf.py` 生成,自包含,可直接仿真) | 网页端加载 / 仿真 |
|
||||
| `../output/meshes/*.stl` | 自包含网格副本(MJCF 的 `meshdir` 指向这里) | 随 MJCF 一起交付 |
|
||||
| `../output/*.json / report.txt / timeseries.csv / report_curves.png` | 生成的 schema / 报告 / 观测 / 曲线 | 交付与对照 |
|
||||
| `../scripts/urdf_to_mjcf.py` | **URDF→MJCF 转换工具**(关节模组专用) | 后端转换 |
|
||||
| `../scripts/generate_schema.py` | **URDF→schema 生成器**(读输入/输出关节、减速比、限位) | 后端生成 |
|
||||
| `../scripts/simulate_report.py` | **通用仿真 + 报告脚本**(读 schema 跑仿真、采数据、出报告) | 本地验证 / 报告参考实现 |
|
||||
| `../scripts/validate_module.py` | **一键验证入口**(编排上面三步:URDF→MJCF→schema→报告) | 本地验证 / 后端流水线 |
|
||||
| `schema.md` | **接口 2:配置 schema**(观测接口:输入/输出/减速比/限位) | 网页团队 |
|
||||
| `report_spec.md` | **接口 3:报告计算规范**(指标→公式) | 网页团队 |
|
||||
|
||||
**一句话速记:** URDF 是「输入」,MJCF 是「引擎吃的模型」,schema/report_spec 是「网页团队要看的接口文档」。
|
||||
流水线是 `urdf_to_mjcf.py → generate_schema.py → simulate_report.py` 三步,`validate_module.py` 把三步打包成一条命令。
|
||||
|
||||
---
|
||||
|
||||
## 2. 三个接口(给网页团队)
|
||||
|
||||
| 接口 | 文件 | 是什么 | 谁负责产出 |
|
||||
|---|---|---|---|
|
||||
| ① 自包含 MJCF | `*.xml` + `meshes/` | 浏览器加载的模型,含网格/质量/惯量/齿轮约束/电机 | 转换工具 |
|
||||
| ② 配置 schema | [schema.md](schema.md) | 输入/输出关节名、减速比、限位、仿真参数 | 后端从 URDF 生成 |
|
||||
| ③ 报告规范 | [report_spec.md](report_spec.md) | 报告要测哪些值、怎么算 | 固定逻辑,前端照实现 |
|
||||
|
||||
关系:**MJCF 管「怎么动」,schema 管「观测什么」,报告规范管「算出什么」。**
|
||||
schema 不描述内部构型(几级、几个行星轮都在 MJCF 里),只统一观测接口。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本案例怎么跑起来
|
||||
|
||||
**一条命令跑完整条流水线(推荐):**
|
||||
|
||||
```bash
|
||||
cd scripts
|
||||
python3 validate_module.py \
|
||||
--urdf ../urdf/planetary_joint_split_motor_demo.urdf \
|
||||
--input sun_input_joint --output carrier_output_joint \
|
||||
--work-dir ../output --plot
|
||||
```
|
||||
|
||||
产出(都在 `../output/`):`planetary_joint_split_motor_demo.xml`(MJCF)+ `planetary_joint_split_motor_demo.json`
|
||||
(schema)+ `timeseries.csv`(逐时间步)+ `report_curves.png`(曲线)。
|
||||
|
||||
**只想跑最后一步仿真/报告(已有 MJCF + schema 时):**
|
||||
|
||||
```bash
|
||||
cd scripts
|
||||
python3 simulate_report.py --schema ../output/planetary_joint_split_motor_demo.json --plot # 读 schema 跑
|
||||
python3 simulate_report.py --schema ../output/planetary_joint_split_motor_demo.json --headless # 无显示器,只计算
|
||||
```
|
||||
|
||||
依赖:`mujoco`、`numpy`(绘图需 `matplotlib`;转换需 `trimesh`)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 三个脚本各自怎么用(分步跑)
|
||||
|
||||
流水线三步各自都能单独跑,`validate_module.py` 只是把它们按顺序调用。三步都**显式指定
|
||||
输入/输出关节**(`--input` / `--output`),因为自动识别在真实 URDF 上不可靠(多级级联有
|
||||
多个正 multiplier 的 mimic、fixed 关节也没有 mimic,程序分不清哪级是末端输出)。
|
||||
|
||||
```bash
|
||||
cd scripts
|
||||
|
||||
# ① URDF → MJCF(自包含:<compiler meshdir="meshes"/> + 拷贝网格)
|
||||
python3 urdf_to_mjcf.py \
|
||||
--urdf ../urdf/planetary_joint_split_motor_demo.urdf \
|
||||
--input sun_input_joint --output carrier_output_joint \
|
||||
--out ../output/planetary_joint_split_motor_demo.xml --meshdir ../output/meshes
|
||||
|
||||
# ② URDF → schema(读输入/输出、减速比、限位、默认仿真参数)
|
||||
python3 generate_schema.py \
|
||||
--urdf ../urdf/planetary_joint_split_motor_demo.urdf \
|
||||
--input sun_input_joint --output carrier_output_joint \
|
||||
--model planetary_joint_split_motor_demo.xml \
|
||||
--out ../output/planetary_joint_split_motor_demo.json
|
||||
|
||||
# ③ schema → 仿真 + 报告
|
||||
python3 simulate_report.py --schema ../output/planetary_joint_split_motor_demo.json --plot
|
||||
```
|
||||
|
||||
依赖:`numpy`、`trimesh`(`pip install trimesh`,仅转换步骤需要)。
|
||||
|
||||
工具做的事(详见脚本头注释):
|
||||
|
||||
1. **照搬不改**:关节层级、`<joint><origin>`→子 `<body pos>`、`<axis>`→`<joint axis>`、
|
||||
`<limit>`→`<joint range>`、`<mimic>`→`<equality>` polycoef、`<visual><origin>`→`<geom pos>`。
|
||||
2. **补质量/惯量**:从每个 link 的 STL 做体积分,按脚本顶部的**密度常数**算出质量/重心/惯性张量,
|
||||
多网格用平行轴定理合成。
|
||||
3. **补作动器**:`input_motor`(输入端)+ `load_motor`(输出端负载)。
|
||||
4. **渲染网格覆盖**:个别 STL 面数超 MuJoCo 上限,渲染换降采样版(质量仍按原始网格算)。
|
||||
|
||||
**要改的常数都在脚本顶部**:
|
||||
|
||||
| 常数 | 位置 | 说明 |
|
||||
|---|---|---|
|
||||
| 材料密度 `DENSITIES` / `DEFAULT_DENSITY` | 顶部 | kg/m³,换材料时改这里 |
|
||||
| `TORQUE_LIMIT` | 顶部 | 力矩限位(占位,应填真实电机额定值) |
|
||||
| `JOINT_DAMPING` / `JOINT_ARMATURE` | 顶部 | 数值稳定参数 |
|
||||
|
||||
> ⚠️ **本工具是关节模组专用**。它假设「一个电机输入 + 一个模组输出 + 减速比关系」,
|
||||
> 用「无 `<mimic>` 的关节 = 输入、正 multiplier 跟随者 = 输出」自动识别。**所有类别 URDF 的
|
||||
> 通用转换器由团队后续开发**,本工具只覆盖关节模组。
|
||||
|
||||
---
|
||||
|
||||
## 5. 把「本案例」接入网页需要什么
|
||||
|
||||
1. **模型文件**:`planetary_joint_split_motor_demo.xml` + `meshes/`(自包含,一起上传)。
|
||||
2. **配置**:按 [schema.md](schema.md) 第 4 节填好这份 JSON,随模型一起给前端。
|
||||
3. **报告逻辑**:前端按 [report_spec.md](report_spec.md) 实现(或后端把本案例的
|
||||
`simulate_report.py` 逻辑翻译到 JS)。
|
||||
|
||||
前端只需:加载 MJCF → 读 schema 拿 `input_joint`/`output_joint`/`gear_ratio`/`limits` →
|
||||
跑仿真 → 按报告规范输出。**不需要**理解行星轮系的内部构型。
|
||||
|
||||
---
|
||||
|
||||
## 6. 把「用户上传 URDF」接入网页需要什么
|
||||
|
||||
用户上传的是 **zip 包(URDF + mesh 文件)**,不是单个 .urdf(只有 urdf 没有 STL 加载不出模型)。
|
||||
|
||||
后端流水线(就是 `validate_module.py` 干的同一件事):
|
||||
|
||||
1. **接收 + 校验**:解压 zip,检查 URDF 可解析、mesh 引用齐全、路径无穿越(`../` 之类)。
|
||||
2. **转换**:跑 `urdf_to_mjcf.py` → 产出自包含 MJCF + 网格。
|
||||
3. **生成 schema**:跑 `generate_schema.py`(输入/输出关节**显式指定**,减速比从 `<mimic>` 取或手动给)→ 产出 schema JSON。
|
||||
4. **返回给前端**:`{ mjcf + meshes, schema }`。
|
||||
|
||||
前端拿到这两样后,与「本案例」走同一条路(见第 5 节)。**用户始终只传 URDF+mesh,
|
||||
不传 schema**——schema 是后端算出来的。
|
||||
|
||||
### 6.1 内置模组 vs 用户上传,靠谁区分?
|
||||
|
||||
**是的,全看前端在哪一步分叉。** 分叉点只有一个:**模型从哪来**。分叉之后,两条路立刻并回
|
||||
同一条流水线(跑仿真 → 出报告 → 出曲线),后面不再有任何区别:
|
||||
|
||||
```text
|
||||
┌─ 内置模组:直接用预生成的 MJCF + schema(跳过①②)
|
||||
前端选择 ────────┤
|
||||
└─ 用户上传:上传 URDF+mesh → 后端 ①转MJCF ②生成schema
|
||||
(此分支需要额外给「输入/输出关节」)
|
||||
↓
|
||||
两条路都拿到 { MJCF + schema }
|
||||
↓
|
||||
③ 跑仿真 → 出报告 → 出曲线(完全相同)
|
||||
```
|
||||
|
||||
所以前端要做的「分割」只有两件事:
|
||||
|
||||
1. **选哪个入口**:内置(有现成的 MJCF+schema)还是上传(现转)。
|
||||
2. **给输入/输出关节**:内置模组的输入/输出已经写死在它自带的 schema 里;上传模组则要
|
||||
前端在用户上传时让用户选一下(或后端自动识别 + 用户确认),因为只有用户/构型才知道
|
||||
哪两个关节是输入输出端——这一步程序猜不靠谱(见第 4 节)。
|
||||
|
||||
除这两点外,报告计算、曲线绘制、安全余量判定**零差别**,前端不需要写两套报告逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 7. 各角色去哪个文件看什么
|
||||
|
||||
| 你想知道… | 去看… |
|
||||
|---|---|
|
||||
| 哪个是 URDF / MJCF / 仿真脚本 | 本文件第 1 节 |
|
||||
| 转换工具怎么跑、改什么常数 | `../scripts/urdf_to_mjcf.py` 头部注释 + 本文件第 4 节 |
|
||||
| schema 怎么从 URDF 生成 | [../scripts/generate_schema.py](../scripts/generate_schema.py) 头部注释 |
|
||||
| 一键验证 / 用户上传流水线怎么编排 | [../scripts/validate_module.py](../scripts/validate_module.py) 头部注释 |
|
||||
| schema 的字段和示例 | [schema.md](schema.md) |
|
||||
| 报告测什么、怎么算 | [report_spec.md](report_spec.md) |
|
||||
| URDF→MJCF 的关键语义(易错点) | `../scripts/urdf_to_mjcf.py` 头部注释(`<joint><origin>`→`<body pos>` 等) |
|
||||
| 通用仿真/报告参考实现 | [../scripts/simulate_report.py](../scripts/simulate_report.py) |
|
||||
|
||||
---
|
||||
|
||||
## 8. 仍需填的占位参数
|
||||
|
||||
1. **力矩限位**(默认 ±10 N·m,对应 URDF `effort="1"` 占位):填真实电机额定值。有两处:
|
||||
- MJCF 作动器 `ctrlrange`:`../scripts/urdf_to_mjcf.py` 顶部的 `TORQUE_LIMIT`;
|
||||
- 报告安全余量用的限位:`../scripts/generate_schema.py` 的 `--torque-limit`(写进 schema.limits.torque)。
|
||||
2. **关节阻尼**(`JOINT_DAMPING = 0.01`):报告中 ~98% 的「效率损失」来自这个阻尼;
|
||||
真实摩擦/效率需用实际参数建模。
|
||||
3. **材料密度**(`../scripts/urdf_to_mjcf.py` 顶部 `DENSITIES` / `DEFAULT_DENSITY`):目前无真值,先不改。
|
||||
@@ -0,0 +1,161 @@
|
||||
# 接口 3 —— 报告计算规范(指标接口)
|
||||
|
||||
> 用途:定义「关节模组仿真报告」里**固定要测、要输出的值**,以及每个值**怎么从 MuJoCo 数据算出来**。
|
||||
> 前端(WASM)照着这份规范实现报告模块即可,**不需要后端再给脚本**。
|
||||
|
||||
**为什么报告逻辑是固定的?** 运动关系随上传的构型不同而不同(几级、几个行星轮都不一样),但
|
||||
「要测什么」是确定的:输入/输出的位置、速度、加速度、力矩,以及由它们导出的功率、效率、减速比、
|
||||
安全余量。唯一随构型变的是**哪个关节是输入、哪个是输出**——这由 [schema.md](schema.md) 提供。
|
||||
|
||||
---
|
||||
|
||||
## 1. 输入
|
||||
|
||||
报告模块需要的输入分三类:
|
||||
|
||||
| 输入 | 来源 | 说明 |
|
||||
|---|---|---|
|
||||
| 模型 | MJCF(`schema.model`) | 含网格、质量/惯量、齿轮约束、作动器 |
|
||||
| 观测配置 | schema | `input_joint` / `output_joint` / `gear_ratio` / `limits` |
|
||||
| 场景参数 | schema 的 `simulation` | 步长、时长、PD 增益、参考轨迹、负载 |
|
||||
|
||||
约定记号(下文统一用):
|
||||
|
||||
| 记号 | 含义 |
|
||||
|---|---|
|
||||
| `q_in`, `qd_in`, `qacc_in` | 输入关节位置 / 速度 / 加速度 |
|
||||
| `q_out`, `qd_out`, `qacc_out` | 输出关节位置 / 速度 / 加速度 |
|
||||
| `τ_in`, `τ_out` | 输入力矩 / 输出力矩 |
|
||||
| `N` | 减速比(`schema.gear_ratio`) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 仿真流程(固定)
|
||||
|
||||
```
|
||||
加载 MJCF → mj_resetData
|
||||
for k in 0..(duration/timestep)-1:
|
||||
t = k · timestep
|
||||
q_ref = amplitude · sin(2π · frequency · t)
|
||||
qd_ref = amplitude · 2π·frequency · cos(2π · frequency · t)
|
||||
|
||||
τ_in = kp·(q_ref − q_in) + kd·(qd_ref − qd_in) # PD 位置控制
|
||||
ctrl[input_motor] = τ_in # 输入电机力矩
|
||||
ctrl[load_motor] = load_torque # 输出端恒值负载
|
||||
mj_step() # 齿轮约束在步内解算
|
||||
记录本步数据
|
||||
```
|
||||
|
||||
> 齿轮耦合是**软约束**(`<equality>` + `solref`),`mj_step` 内自动解算;输出力矩
|
||||
> `τ_out` 从约束反力读出(见下)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 指标 → 数据源 → 公式(核心表)
|
||||
|
||||
MuJoCo 数据源以 `d.*` 表示 `MjData` 的字段;`input_dof` / `output_dof` 是输入/输出关节
|
||||
的自由度下标(`m.jnt_dofadr[...]`)。
|
||||
|
||||
### 3.1 运动学(跟踪精度)
|
||||
|
||||
| 指标 | 符号 | 数据源(逐时间步) | 单位 |
|
||||
|---|---|---|---|
|
||||
| 输入位置 | `q_in` | `d.qpos[input_dof]` | rad |
|
||||
| 输入速度 | `qd_in` | `d.qvel[input_dof]` | rad/s |
|
||||
| 输入加速度 | `qacc_in` | `d.qacc[input_dof]` | rad/s² |
|
||||
| 输出位置 | `q_out` | `d.qpos[output_dof]` | rad |
|
||||
| 输出速度 | `qd_out` | `d.qvel[output_dof]` | rad/s |
|
||||
| 输出加速度 | `qacc_out` | `d.qacc[output_dof]` | rad/s² |
|
||||
|
||||
**汇总(对整段轨迹统计):**
|
||||
|
||||
| 指标 | 公式 | 说明 |
|
||||
|---|---|---|
|
||||
| 输入位置峰值 | `max |q_in|` | 参考幅值 `amplitude` |
|
||||
| 位置跟踪误差 RMS | `√ mean((q_in − q_ref)²)` | 越小越准 |
|
||||
| 速度跟踪误差 RMS | `√ mean((qd_in − qd_ref)²)` | 越小越准 |
|
||||
| 输出位置峰值 | `max |q_out|` | 期望 ≈ `amplitude / N` |
|
||||
| **传动比实测** | 带截距最小二乘斜率(`q_out` 对 `q_in` 拟合,取稳态后半段) | 期望 ≈ `1/N` |
|
||||
|
||||
> 不要用逐点 `q_out/q_in` 再取均值:正弦参考下 `q_in` 每周期过零,软约束相位滞后会让
|
||||
> 过零处比值爆表甚至变号,把均值带偏(实测 0.112368 vs 真实 0.111111)。带截距的
|
||||
> 最小二乘斜率对相位滞后和负载静偏置(`q_out` 恒滞后一个常数角)都不敏感,能精确还原 `1/N`。
|
||||
|
||||
### 3.2 动力学(力矩 / 功率)
|
||||
|
||||
| 指标 | 符号 | 数据源 / 公式 | 单位 |
|
||||
|---|---|---|---|
|
||||
| 输入力矩 | `τ_in` | `d.actuator_force[input_motor]` | N·m |
|
||||
| 输出力矩 | `τ_out` | `d.qfrc_constraint[output_dof]` | N·m |
|
||||
| 输出功率 | `P_out` | `τ_out · qd_out` | W |
|
||||
| 理想输出力矩 | `τ_ideal` | `N · τ_in` | N·m |
|
||||
| 力矩损失 | `Δτ` | `N·τ_in − τ_out` | N·m |
|
||||
| **效率** | `η` | `τ_out / (N · τ_in) × 100%`(稳态均值) | % |
|
||||
|
||||
> 稳态均值 = 取时间序列后半段(让瞬态衰减完)的 `mean(|·|)`。
|
||||
|
||||
### 3.3 安全性(安全余量)
|
||||
|
||||
| 指标 | 公式 | 单位 |
|
||||
|---|---|---|
|
||||
| 位置余量 | `(‖limit_pos‖ − max|q_in|) / ‖limit_pos‖ × 100%` | % |
|
||||
| 力矩余量 | `(‖limit_trq‖ − max|τ_in|) / ‖limit_trq‖ × 100%` | % |
|
||||
| 过载判定 | 力矩余量 < 20% → 警告 | — |
|
||||
|
||||
其中 `limit_pos = schema.limits.position[1]`,`limit_trq = schema.limits.torque[1]`。
|
||||
|
||||
### 3.4 可选:行星轮观测(仅行星构型)
|
||||
|
||||
若模组有行星轮(本案例有 3 个),可额外记录某一行星轮的位置/速度 `q_p, qd_p`(`d.qpos[planet_dof]`)。
|
||||
这是**可选项**——报告必填的只有输入/输出端;行星轮数量/命名随构型变,不属于固定指标。
|
||||
|
||||
---
|
||||
|
||||
## 4. 输出格式
|
||||
|
||||
### 4.1 逐时间步数据(CSV)
|
||||
|
||||
列名与关节名**解耦**(用固定的 `input_*` / `output_*`,不写死太阳轮/行星架),
|
||||
前端按 `schema.input_joint` / `schema.output_joint` 填充即可:
|
||||
|
||||
| 列 | 内容 |
|
||||
|---|---|
|
||||
| `time` | 时间 [s] |
|
||||
| `input_q, input_qd, input_qacc` | 输入位置/速度/加速度(`schema.input_joint`) |
|
||||
| `q_ref, qd_ref` | 参考轨迹位置/速度 |
|
||||
| `output_q, output_qd, output_qacc` | 输出位置/速度/加速度(`schema.output_joint`) |
|
||||
| `planet_q, planet_qd` | (可选)行星轮位置/速度,仅行星构型且有记录时才有 |
|
||||
| `tau_in, tau_out, power_out` | 输入/输出力矩、输出功率 |
|
||||
|
||||
> 本地 `simulate_report.py` 按固定顺序写出:`time, input_q, input_qd, input_qacc, q_ref,
|
||||
> qd_ref, output_q, output_qd, output_qacc, [planet_q, planet_qd,] tau_in, tau_out, power_out`。
|
||||
|
||||
### 4.2 汇总报告(终端 / JSON)
|
||||
|
||||
按第 3 节的三类汇总指标输出:运动学(峰值、跟踪误差、传动比实测)、动力学(力矩、
|
||||
功率、效率)、安全性(两类余量 + 过载判定)。
|
||||
|
||||
报告首行需注明**仿真模式**(`schema.simulation.mode`,`normal` / `overload`),并显示当前
|
||||
使用的负载力矩与阻尼。当 `mode = overload` 时,额外产出一份 **过载报告**(`overload_report.txt`):
|
||||
|
||||
| 指标 | 公式 | 单位 |
|
||||
|---|---|---|
|
||||
| 额定力矩限位 | `limit_trq = schema.limits.torque[1]` | N·m |
|
||||
| 施加负载力矩 | `schema.simulation.load_torque` | N·m |
|
||||
| 输入力矩峰值 | `max |τ_in|` | N·m |
|
||||
| 力矩余量 | `(limit_trq − max|τ_in|) / limit_trq × 100%` | % |
|
||||
| 位置跟踪误差 | `√ mean((q_in − q_ref)²)` | rad |
|
||||
| 过载判定 | 余量 ≤ 0 → 已过载;0 < 余量 < 20% → 接近过载;否则未过载 | — |
|
||||
|
||||
---
|
||||
|
||||
## 5. 参考实现
|
||||
|
||||
本地参考实现见 [../scripts/simulate_report.py](../scripts/simulate_report.py),它就是这份规范的可执行版本。
|
||||
前端实现时应**以本规范为准**,脚本只作对照。
|
||||
|
||||
**两个易错点(实现时务必注意):**
|
||||
|
||||
1. **输出力矩读约束反力,不读作动器力。** 齿轮耦合是 `<equality>` 约束,`τ_out` 取
|
||||
`d.qfrc_constraint[output_dof]`;若读 `load_motor` 的 `actuator_force` 只会得到常数负载。
|
||||
2. **稳态统计取后半段。** 参考轨迹从静止起跳会有初始瞬态尖峰,前半段不参与均值/效率计算。
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
# 接口 2 —— 关节模组配置 Schema
|
||||
|
||||
> 用途:给「浏览器端 WASM 仿真」一个**统一观测接口**。无论用户上传的是什么构型的关节模组
|
||||
> (一级/多级、三个/四个行星轮、太阳轮+行星架、谐波、摆线…),后端把它归一化成这份 JSON,
|
||||
> 前端只读这份 JSON 就能:找到输入端/输出端、知道减速比、拿到限位、按默认参数跑仿真。
|
||||
|
||||
---
|
||||
|
||||
## 1. Schema 是什么 / 不是什么
|
||||
|
||||
| | 说明 |
|
||||
|---|---|
|
||||
| ✅ 是什么 | 关节模组的**对外观测契约**:输入关节、输出关节、减速比、限位、默认仿真参数 |
|
||||
| ❌ 不是什么 | **不描述内部构型**(几级传动、几个行星轮、齿数、几何)。这些全部已经编码在 MJCF 里,前端不需要知道 |
|
||||
| ❌ 不是用户上传的 | 这份 JSON 是**后端从 URDF 生成**的,不是用户填写的。用户上传的始终是「URDF + mesh」的 zip |
|
||||
|
||||
**为什么必须要有 schema?**
|
||||
1. 报告里要测的输入/输出力矩,是「哪个关节」取决于构型。schema 告诉前端哪两个关节是输入/输出端。
|
||||
2. 减速比的标称值(`gear_ratio`)可能来自 URDF 的 `<mimic>`,也可能是标定/手动填的,需要显式声明来源。
|
||||
3. 前端要做统一的限位检查、统一的安全余量,就需要统一格式的限位。
|
||||
|
||||
> 一句话:**MJCF 管「怎么动」,schema 管「观测什么」,报告规范管「算出什么」。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 字段定义(格式契约,定义一次)
|
||||
|
||||
| 字段 | 类型 | 单位 | 必填 | 含义 | 取值来源 |
|
||||
|---|---|---|---|---|---|
|
||||
| `module_id` | string | — | ✅ | 模组唯一标识 | 后端生成(如模型名) |
|
||||
| `name` | string | — | | 显示名 | 后端生成 |
|
||||
| `model` | string | — | ✅ | 要加载的 MJCF 文件名(自包含,网格与它同目录) | 转换工具输出 |
|
||||
| `input_joint` | string | — | ✅ | **输入关节名**(电机端) | URDF 里唯一没有 `<mimic>` 的关节 |
|
||||
| `output_joint` | string | — | ✅ | **输出关节名**(模组输出端) | URDF 里以正 multiplier 跟随输入的关节 |
|
||||
| `gear_ratio` | number | — | ✅ | 标称减速比 = 输入 / 输出 = `1/multiplier` | 默认取 `<mimic multiplier>` 倒数;可标定覆盖 |
|
||||
| `gear_ratio_source` | enum | — | ✅ | `mimic` / `calibrated` / `manual` | 减速比来源 |
|
||||
| `limits.position` | [number, number] | rad | ✅ | 输入关节位置限位 `[lower, upper]` | URDF `<limit lower/upper>` |
|
||||
| `limits.torque` | [number, number] | N·m | ✅ | 输入力矩限位 `[-T, +T]` | MJCF `<actuator ctrlrange>`(占位,待填真实值) |
|
||||
| `limits.velocity` | [number, number] | rad/s | | 速度限位 | URDF `<limit velocity>`(若有) |
|
||||
| `simulation.timestep` | number | s | ✅ | 仿真步长 | 默认 `0.001` |
|
||||
| `simulation.duration` | number | s | ✅ | 仿真时长 | 默认 `4.0` |
|
||||
| `simulation.kp` / `kd` | number | — | ✅ | 输入 PD 位置控制增益 | 默认 `20.0` / `0.3` |
|
||||
| `simulation.amplitude` | number | rad | ✅ | 参考轨迹幅值 | 默认 `2π`(1 圈) |
|
||||
| `simulation.frequency` | number | Hz | ✅ | 参考轨迹频率 | 默认 `0.25` |
|
||||
| `simulation.load_torque` | number | N·m | ✅ | 输出端恒值负载(负 = 阻力) | 默认 `-3.0` |
|
||||
| `simulation.damping` | number | N·m·s/rad | ✅ | 关节粘性阻尼 | 默认 `0.01` |
|
||||
| `simulation.mode` | enum | — | ✅ | 仿真模式:`normal` / `overload` | 默认 `normal` |
|
||||
|
||||
> 所有带默认值的字段,前端都可以覆盖(用户调参)。`input_joint` / `output_joint` /
|
||||
> `limits` / `gear_ratio` 是后端算好的「事实」,前端只读。
|
||||
|
||||
---
|
||||
|
||||
## 3. 形式化 JSON Schema(供前端校验用,draft 2020-12)
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "joint-module.schema.json",
|
||||
"title": "关节模组配置",
|
||||
"type": "object",
|
||||
"required": ["module_id", "model", "input_joint", "output_joint",
|
||||
"gear_ratio", "gear_ratio_source", "limits", "simulation"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"module_id": { "type": "string" },
|
||||
"name": { "type": "string" },
|
||||
"model": { "type": "string" },
|
||||
"input_joint": { "type": "string" },
|
||||
"output_joint": { "type": "string" },
|
||||
"gear_ratio": { "type": "number", "exclusiveMinimum": 0 },
|
||||
"gear_ratio_source": { "type": "string", "enum": ["mimic", "calibrated", "manual"] },
|
||||
"limits": {
|
||||
"type": "object",
|
||||
"required": ["position", "torque"],
|
||||
"properties": {
|
||||
"position": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
|
||||
"torque": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
|
||||
"velocity": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 }
|
||||
}
|
||||
},
|
||||
"simulation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timestep": { "type": "number", "default": 0.001 },
|
||||
"duration": { "type": "number", "default": 4.0 },
|
||||
"kp": { "type": "number", "default": 20.0 },
|
||||
"kd": { "type": "number", "default": 0.3 },
|
||||
"amplitude": { "type": "number", "default": 6.283185307179586 },
|
||||
"frequency": { "type": "number", "default": 0.25 },
|
||||
"load_torque": { "type": "number", "default": -3.0 },
|
||||
"damping": { "type": "number", "default": 0.01 },
|
||||
"mode": { "type": "string", "enum": ["normal", "overload"], "default": "normal" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 本案例的实例
|
||||
|
||||
```json
|
||||
{
|
||||
"module_id": "planetary_joint_split_motor_demo",
|
||||
"name": "行星轮系分体电机关节模组",
|
||||
"model": "planetary_joint_split_motor_demo.xml",
|
||||
|
||||
"input_joint": "sun_input_joint",
|
||||
"output_joint": "carrier_output_joint",
|
||||
|
||||
"gear_ratio": 6.0,
|
||||
"gear_ratio_source": "mimic",
|
||||
|
||||
"limits": {
|
||||
"position": [-37.6991118431, 37.6991118431],
|
||||
"torque": [-10.0, 10.0],
|
||||
"velocity": [-6.28318530718, 6.28318530718]
|
||||
},
|
||||
|
||||
"simulation": {
|
||||
"timestep": 0.001,
|
||||
"duration": 4.0,
|
||||
"kp": 20.0,
|
||||
"kd": 0.3,
|
||||
"amplitude": 6.283185307179586,
|
||||
"frequency": 0.25,
|
||||
"load_torque": -3.0,
|
||||
"damping": 0.01,
|
||||
"mode": "normal"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
对照说明:
|
||||
|
||||
| 实例值 | 怎么来的 |
|
||||
|---|---|
|
||||
| `input_joint = sun_input_joint` | URDF 里 5 个关节中,唯一没有 `<mimic>` 的是 `sun_input_joint`(独立驱动源) |
|
||||
| `output_joint = carrier_output_joint` | `<mimic joint="sun_input_joint" multiplier="0.16667">`,正 multiplier → 减速输出端 |
|
||||
| `gear_ratio = 6.0` | `1 / 0.166666666666667 = 6`(1 级行星轮系,齿圈固定,太阳轮:行星架 = 6:1) |
|
||||
| `limits.position = ±37.6991` | URDF `<limit lower="-37.6991118431" upper="37.6991118431">`(±6 圈 = ±12π) |
|
||||
| `limits.torque = ±10` | MJCF `<actuator ctrlrange="-10 10">`(**占位**,待填真实电机额定值) |
|
||||
| `limits.velocity = ±6.2832` | URDF `<limit velocity="6.28318530718">`(1 圈/秒) |
|
||||
| `simulation.*` | 报告规范的默认场景参数,见 [report_spec.md](report_spec.md) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 减速比来源的优先级(多来源怎么取)
|
||||
|
||||
后端生成 schema 时,`gear_ratio` 按下面顺序确定,并在 `gear_ratio_source` 里记录用的是哪个:
|
||||
|
||||
1. **`mimic`** —— URDF `<mimic multiplier>` 的倒数(最优先,本案例)。
|
||||
2. **`calibrated`** —— 跑一次仿真,`mean(q_out / q_in)` 标定得到(无 `<mimic>` 或传动力不可靠时)。
|
||||
3. **`manual`** —— 前端手动填(都没有时兜底)。
|
||||
|
||||
> 减速比只进 schema 与报告,**不进 MJCF**——MJCF 里的传动关系由 `<equality>` 精确表达,
|
||||
> schema 里的 `gear_ratio` 只是「标称参考值」,用于报告的效率/理想力矩计算。
|
||||
Reference in New Issue
Block a user