- 三接口契约:自包含 MJCF / 配置 schema / 报告计算规范 - Python 流水线:urdf_to_mjcf → generate_schema → simulate_report(validate_module 一键编排) - 输入案例 urdf + 生成产物 output(自包含 MJCF/schema/报告/网格副本) - 详细架构说明 docs/architecture.md
11 KiB
关节模组仿真平台 —— 架构与开发说明(详细版)
想快速上手,先看根目录 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 | 输入/输出关节名、减速比、限位、仿真参数 | 后端从 URDF 生成 |
| ③ 报告规范 | report_spec.md | 报告要测哪些值、怎么算 | 固定逻辑,前端照实现 |
关系:MJCF 管「怎么动」,schema 管「观测什么」,报告规范管「算出什么」。 schema 不描述内部构型(几级、几个行星轮都在 MJCF 里),只统一观测接口。
3. 本案例怎么跑起来
一条命令跑完整条流水线(推荐):
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 时):
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,程序分不清哪级是末端输出)。
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,仅转换步骤需要)。
工具做的事(详见脚本头注释):
- 照搬不改:关节层级、
<joint><origin>→子<body pos>、<axis>→<joint axis>、<limit>→<joint range>、<mimic>→<equality>polycoef、<visual><origin>→<geom pos>。 - 补质量/惯量:从每个 link 的 STL 做体积分,按脚本顶部的密度常数算出质量/重心/惯性张量, 多网格用平行轴定理合成。
- 补作动器:
input_motor(输入端)+load_motor(输出端负载)。 - 渲染网格覆盖:个别 STL 面数超 MuJoCo 上限,渲染换降采样版(质量仍按原始网格算)。
要改的常数都在脚本顶部:
| 常数 | 位置 | 说明 |
|---|---|---|
材料密度 DENSITIES / DEFAULT_DENSITY |
顶部 | kg/m³,换材料时改这里 |
TORQUE_LIMIT |
顶部 | 力矩限位(占位,应填真实电机额定值) |
JOINT_DAMPING / JOINT_ARMATURE |
顶部 | 数值稳定参数 |
⚠️ 本工具是关节模组专用。它假设「一个电机输入 + 一个模组输出 + 减速比关系」, 用「无
<mimic>的关节 = 输入、正 multiplier 跟随者 = 输出」自动识别。所有类别 URDF 的 通用转换器由团队后续开发,本工具只覆盖关节模组。
5. 把「本案例」接入网页需要什么
- 模型文件:
planetary_joint_split_motor_demo.xml+meshes/(自包含,一起上传)。 - 配置:按 schema.md 第 4 节填好这份 JSON,随模型一起给前端。
- 报告逻辑:前端按 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 干的同一件事):
- 接收 + 校验:解压 zip,检查 URDF 可解析、mesh 引用齐全、路径无穿越(
../之类)。 - 转换:跑
urdf_to_mjcf.py→ 产出自包含 MJCF + 网格。 - 生成 schema:跑
generate_schema.py(输入/输出关节显式指定,减速比从<mimic>取或手动给)→ 产出 schema JSON。 - 返回给前端:
{ mjcf + meshes, schema }。
前端拿到这两样后,与「本案例」走同一条路(见第 5 节)。用户始终只传 URDF+mesh, 不传 schema——schema 是后端算出来的。
6.1 内置模组 vs 用户上传,靠谁区分?
是的,全看前端在哪一步分叉。 分叉点只有一个:模型从哪来。分叉之后,两条路立刻并回 同一条流水线(跑仿真 → 出报告 → 出曲线),后面不再有任何区别:
┌─ 内置模组:直接用预生成的 MJCF + schema(跳过①②)
前端选择 ────────┤
└─ 用户上传:上传 URDF+mesh → 后端 ①转MJCF ②生成schema
(此分支需要额外给「输入/输出关节」)
↓
两条路都拿到 { MJCF + schema }
↓
③ 跑仿真 → 出报告 → 出曲线(完全相同)
所以前端要做的「分割」只有两件事:
- 选哪个入口:内置(有现成的 MJCF+schema)还是上传(现转)。
- 给输入/输出关节:内置模组的输入/输出已经写死在它自带的 schema 里;上传模组则要 前端在用户上传时让用户选一下(或后端自动识别 + 用户确认),因为只有用户/构型才知道 哪两个关节是输入输出端——这一步程序猜不靠谱(见第 4 节)。
除这两点外,报告计算、曲线绘制、安全余量判定零差别,前端不需要写两套报告逻辑。
7. 各角色去哪个文件看什么
| 你想知道… | 去看… |
|---|---|
| 哪个是 URDF / MJCF / 仿真脚本 | 本文件第 1 节 |
| 转换工具怎么跑、改什么常数 | ../scripts/urdf_to_mjcf.py 头部注释 + 本文件第 4 节 |
| schema 怎么从 URDF 生成 | ../scripts/generate_schema.py 头部注释 |
| 一键验证 / 用户上传流水线怎么编排 | ../scripts/validate_module.py 头部注释 |
| schema 的字段和示例 | schema.md |
| 报告测什么、怎么算 | report_spec.md |
| URDF→MJCF 的关键语义(易错点) | ../scripts/urdf_to_mjcf.py 头部注释(<joint><origin>→<body pos> 等) |
| 通用仿真/报告参考实现 | ../scripts/simulate_report.py |
8. 仍需填的占位参数
- 力矩限位(默认 ±10 N·m,对应 URDF
effort="1"占位):填真实电机额定值。有两处:- MJCF 作动器
ctrlrange:../scripts/urdf_to_mjcf.py顶部的TORQUE_LIMIT; - 报告安全余量用的限位:
../scripts/generate_schema.py的--torque-limit(写进 schema.limits.torque)。
- MJCF 作动器
- 关节阻尼(
JOINT_DAMPING = 0.01):报告中 ~98% 的「效率损失」来自这个阻尼; 真实摩擦/效率需用实际参数建模。 - 材料密度(
../scripts/urdf_to_mjcf.py顶部DENSITIES/DEFAULT_DENSITY):目前无真值,先不改。