4240bb889a
- 三接口契约:自包含 MJCF / 配置 schema / 报告计算规范 - Python 流水线:urdf_to_mjcf → generate_schema → simulate_report(validate_module 一键编排) - 输入案例 urdf + 生成产物 output(自包含 MJCF/schema/报告/网格副本) - 详细架构说明 docs/architecture.md
202 lines
11 KiB
Markdown
202 lines
11 KiB
Markdown
# 关节模组仿真平台 —— 架构与开发说明(详细版)
|
||
|
||
> 想快速上手,先看根目录 [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`):目前无真值,先不改。
|