学习主题:MuJoCo 接触、外力交互与视频记录。本文根据已有构建日志、实验录像、JSON 报告和执行后的 Notebook 整理,2026 年 10 月 16 日完成核对。没有重新训练策略。
这次 Task 2,我主要理解了两件事:小鸭子如何在浏览器中完成“观察—决策—运动”的闭环,以及外界扰动怎样改变身体姿态和地面接触。已有实验记录包含五组鼠标拖拽,以及一段使用行走策略的速度扰动回放。
1. 浏览器里发生了什么
mjswan 将 MicroDuck 的场景和策略构建成静态网页应用。浏览器加载场景包和 ONNX 模型后,由 MuJoCo WASM 计算动力学,ONNX Runtime Web 执行策略推理,Three.js 显示运动过程。
读取仿真状态 → 组成 61 维观测 → 策略输出 14 维动作 → 执行器作用于关节 → MuJoCo 计算运动和接触 → 更新画面鼠标拖拽也会进入这个物理过程:先选中机器人刚体上的点,再根据目标点与实际位置的偏差生成外力和力矩。mjswan 调用mjv_applyPerturbForce,将扰动写入mjData.xfrc_applied。相关机制可对照 MuJoCo 官方扰动说明。
因此,拖动鼠标后看到的倾斜、离地和翻倒,是策略动作、外力、重力及接触共同作用的结果。看到它重新站在初始位置时,还要区分自动重置、手动 Reset 与策略主动恢复。
2. 已完成的构建与验证
| 检查项 | 已有结果与本次核对 |
|---|---|
| 软件版本 | mjswan 0.11.6;Playground 提交7a29396f07c4ce8c5a6851bc0abd2bafa5511c1c |
| 构建 | 日志记录msp build microduck成功,静态产物约 73 MB |
| 场景与策略 | 4 个场景、9 个策略入口,包括行走、站姿、起坐、踢球和轮滑 |
| 模型文件 | 远程重新检查 26 个 ONNX,全部通过 checker;其中 9 个策略模型均为obs[1,61] → actions[1,14] |
| 网页服务 | 日志记录入口、manifest 和抽样资源返回 200,并设置 COOP/COEP 响应头 |
| 浏览器交互 | 已有录像显示模型、面板与拖拽;录制日志记录PAGE_ERRORS=0 |
26 个 ONNX 包括策略模型、观测计算图及终止判断等辅助图,不能写成“训练了 26 个策略”。checker 通过说明模型格式有效,也不能代替实际运动效果验证。
3. 五组拖拽:方向和作用位置怎样影响身体
实验使用Duck 场景、Walk 策略,速度指令保持(0,0,0)。后续各组开始前点击 Reset。前三组保持相同初始屏幕选点,改变拖拽路径;后两组把选点移到更低或更高的位置。
完整录像:五组拖拽实验,约 6 分 15 秒。下表时间是录像播放时间,不是仿真时间。
| 组别 | 操作 | 录像中的现象 | 关键画面 |
|---|---|---|---|
| 01 | 躯干附近向右上拖拽 | 红色箭头向右上伸出,身体倾斜,脚部离地,随后翻倒 | 约 01:21 |
| 02 | 同一初始选点向左拖拽 | 出现向左的外力箭头,身体朝另一方向明显翻转 | 约 02:05 |
| 03 | 同一初始选点斜向摆动 | 身体随拖拽旋转、倾斜,出现失去正常站立姿态的过程 | 约 03:26 |
| 04 | 腿脚附近水平拖拽 | 箭头位于较低处,腿脚位置与支撑姿态改变,身体倾斜 | 约 04:41 |
| 05 | 头部附近上提 | 头颈姿态改变,所选画面中身体仍较接近站立姿态 | 约 05:56 |
我的理解是,外力既能改变身体的平移,也能通过力矩改变转动。力矩与作用点相对于参考点的位置有关,可写为τ = r × F。脚与地面之间的支撑和摩擦也会改变,机器人能否维持平衡取决于整个动态过程。
这组实验适合做定性观察:拖拽路径的方向和长度有变化,后两组还同时改变了作用位置,没有记录实际力值,也没有做多次统计。因此,不能从中推出“推头一定比推脚稳定”或某个方向的抗扰性能最好。接触现象属于画面观察,没有测量接触力或摩擦系数。
核对材料时还有一个收获:部分旧的mid_drag截图看起来仍是站姿,但同组录像中确实出现了箭头和翻倒。单张截图没拍到扰动,不代表整组实验没有发生扰动。本文配图重新从本地 MP4 提取,时间与来源校验值保存在帧索引中。
4. Notebook 回放:先看代码,再解释数字
执行后的 Notebook还保存了另一条回放链路:使用已有行走模型,在 Python 仿真中运行 250 个控制步,策略通过 Torch 在 GPU 上执行。
| 项目 | 已保存的结果 |
|---|---|
| 模型输入 / 输出 | 61 维观测 / 14 维动作 |
| 前进指令 | 脚本配置为 0.28 m/s |
| 回放长度 | 250 步,对应名义仿真时间 5.0 s |
| 扰动时机 | 循环索引step == 75和step == 175 |
| 终止或截断后的重置 | 1 次,完成第 103 个控制步时记录 |
| 最长连续未重置片段 | 147 步,即 2.94 s |
| 平均策略推理耗时 | 0.6406 ms,约 0.64 ms |
结果见原始报告,视频见扰动回放。这里的推理耗时是脚本记录的 GPU 策略推理耗时,不能当作浏览器推理耗时或整个控制周期耗时。
尤其需要区分两种扰动:
| 比较项 | 浏览器拖拽 | Notebook 回放 |
|---|---|---|
| 实际改变什么 | 被选中刚体受到的外力和力矩 | 基座的速度状态 |
| 代码接口 | mjData.xfrc_applied | write_root_com_velocity_to_sim |
| 操作方式 | 随鼠标持续变化 | 在指定循环索引写入一次速度 |
Notebook 脚本先创建一个六维零向量,再把第一维设为+0.55或−0.45m/s,随后写入仿真。准确说,这是把基座速度设置为指定值,其余五个分量设为零,并非在当前速度上额外加上这个数,也没有施加一个以牛顿计量的力。
报告记录的是“终止或截断后重置”,没有单独给出触发原因。结合画面可观察到明显失稳,但仅靠这个字段不能判定具体终止条件。重置后继续运行也不能解释成策略自己完成了起身。
5. 复现时记住的几个点
构建入口是官方 mjswan Playground。已有记录使用uv sync --group previews准备环境,再执行uv run msp build microduck --output-dir dist/microduck。
历史记录出现过 Node 路径和 npm 网络问题:需要让 mjswan 自带.nodeenv/bin中的node可被找到;证书报错时检查代理配置和证书链;包源连接慢时再按自己的网络环境选择镜像。不要为了下载速度随意改变锁定的依赖版本。
构建完成后,用教程提供的静态服务器打开页面。服务要正确返回 JavaScript、WASM 的 MIME 类型;本次服务器同时设置了Cross-Origin-Opener-Policy: same-origin和Cross-Origin-Embedder-Policy: require-corp。加载异常时,应结合浏览器报错、资源请求和隔离状态排查。
拖拽时先确认选中了机器人刚体,再看外力箭头和连续画面。截图应同时保存施力前、施力中和松手后的状态;如果要比较抗扰能力,还需要固定初始状态、实际力值、持续时间和作用位置,并重复实验。
6. 学习收获
Task 2 让我把“策略决定动作”和“物理引擎计算后果”联系起来了。网络输出关节动作,但机器人最终怎样运动,还取决于质量、惯量、外力、接触和摩擦。浏览器交互让这些关系可以直接观察,录像与原始数据则帮助我避免只凭观感下结论。
现有材料完成了网页构建、五组拖拽观察、视频保存和 GPU 扰动回放。Notebook 的 9 个代码单元都有执行计数,未保存异常输出;其中 RDK/BPU 环节因模型缺失或设备不可达而跳过,不能算板端验收通过。本次也没有重新训练策略或验证实机控制效果。
接下来如果继续实验,我会优先把鼠标拖拽改成可重复的扰动,记录实际力、姿态和接触信息,再比较不同作用位置对平衡的影响。
实验细节:拖拽对比记录 · 两种扰动与回放结果 · 构建验证记录。