Files
chenshijue 4240bb889a 初始提交:关节模组仿真平台
- 三接口契约:自包含 MJCF / 配置 schema / 报告计算规范
- Python 流水线:urdf_to_mjcf → generate_schema → simulate_report(validate_module 一键编排)
- 输入案例 urdf + 生成产物 output(自包含 MJCF/schema/报告/网格副本)
- 详细架构说明 docs/architecture.md
2026-08-28 11:37:47 +08:00

202 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 关节模组仿真平台 —— 架构与开发说明(详细版)
> 想快速上手,先看根目录 [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`):目前无真值,先不改。