初始提交:关节模组仿真平台

- 三接口契约:自包含 MJCF / 配置 schema / 报告计算规范
- Python 流水线:urdf_to_mjcf → generate_schema → simulate_report(validate_module 一键编排)
- 输入案例 urdf + 生成产物 output(自包含 MJCF/schema/报告/网格副本)
- 详细架构说明 docs/architecture.md
This commit is contained in:
2026-08-28 11:37:47 +08:00
commit 4240bb889a
34 changed files with 6294 additions and 0 deletions
+161
View File
@@ -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. **稳态统计取后半段。** 参考轨迹从静止起跳会有初始瞬态尖峰,前半段不参与均值/效率计算。