做过策略部署的人都知道,切换模型不是点个按钮的事。你得停机、加载新权重、祈祷输入输出维度对得上、然后重新校准。Microduck 不这么干。它能在行走中突然换成摔倒恢复策略,再无缝切回行走——靠的是一套 61 维的观察契约,所有策略共用同一份输入规格,热切换不需要重启。
今天拆开看这套契约的设计。
61 维里面有什么
Microduck 的策略输入是一个 61 维向量,分成两块:48 维本体感知 + 13 维指令块。
48 维本体感知覆盖了机器人对自身状态的完整描述:关节角度、关节速度、IMU 姿态和角速度。这些是所有策略都需要的基础信息——不管你在走路还是做前滚翻,你总得知道自己现在什么姿势。
13 维指令块的结构是 [twist(3), head_pose(4), body_pose(6)]。twist 是三维线速度和角速度指令,告诉机器人”往前走多快、转多急”。head_pose 是四维头部姿态目标,body_pose 是六维躯干姿态目标。
关键规则在这里:不使用某个指令槽的策略不能删掉它,必须零填充。一个只管站立的策略不需要 twist 指令,但它的观察向量里 twist 那三维必须存在且填零。这样做的代价是多了几个零,换来的是所有策略的输入维度严格一致。
为什么不能各搞各的
直觉上,不同任务定制不同观察空间更高效。走路策略不需要头部姿态指令,前滚翻策略不需要 twist 速度指令。裁掉冗余维度,网络更小、推理更快。
Microduck 不这么做,原因很实际:策略是在运行时热切换的。
robotd 的控制循环跑在 50 Hz,每个 tick 20 毫秒。当前活跃的策略可能因为各种原因被换掉——用户按了手柄上的恢复键、安全层检测到即将摔倒、或者从行走模式切到滚轮模式。切换发生时,新策略拿到的必须是同一个 61 维向量,否则你得在 20 毫秒内完成观察空间重映射,这比推理本身还复杂。
CLAUDE.md 里把这条写成了硬约束:”Obs layout is 61D (actor) and shared across the whole policy family so policies are hot-swappable in the runtime。” 这不是建议,是不变量。任何新策略如果改了观察维度,就会破坏热切换能力。
零填充不是免费午餐
零填充看起来没成本,实际上有。策略网络看到指令槽全是零,它学到的是”这些维度永远是零”。这在训练时没问题,但如果运行时突然往这个槽里塞了非零值——比如一个站立策略意外收到了 twist 指令——行为就不可预测了。
Microduck 的处理方式是在训练时就采样极小范围的噪声填进不用的指令槽,而不是严格填零。CLAUDE.md 的原话是 “keep the obs term, sample tiny ranges”。这样策略对不用的维度有鲁棒性,不会因为微小扰动就崩溃。
这个细节很容易被忽略。很多人在做多策略部署时会选择严格零填充,然后部署时遇到传感器噪声就翻车。Microduck 的经验是:即使某个维度”不用”,也要让策略见过它的随机变体。
从训练到部署的完整链路
训练时,61 维观察向量由 MuJoCo 仿真环境组装。mdp.py 里的观察函数按固定顺序拼接本体感知和指令块,每个任务家族的配置文件(比如 microduck_velocity_env_cfg.py)定义自己用哪些指令、不用哪些。
训练完成后,策略导出为 ONNX 格式。这一步有个必须注意的细节:观察归一化器必须被烘焙进 ONNX。训练时观察值会被归一化处理,如果导出时忘了这一步,部署到真机上的策略收到的就是未归一化的原始值,行为完全不可控。scripts/export.py 负责这件事,CLAUDE.md 明确警告不要手动转换 checkpoint。
部署到真机后,robotd 每个 tick 组装 61 维观察向量,喂给当前活跃的 ONNX 策略,拿到动作输出,执行。切换策略时只是换了 ONNX 文件,观察组装逻辑完全不变。
infer_policy.py:部署前的彩排
Microduck 提供了一个叫 infer_policy.py 的工具,用 CPU MuJoCo 跑部署演练。你可以同时加载多个策略,模拟运行时的热切换场景:
uv run scripts/infer_policy.py --walking walk.onnx --standing stand.onnx \
--sitstand sitstand.onnx --roulade roulade.onnx --new-cmd-obs
键盘控制速度指令,按 G 触发地面拾取,按 Y 切换坐站,按 R 触发前滚翻。这个工具的意义不仅是验证单个策略能不能跑,更是验证多个策略在共享 61 维观察空间下能不能无缝切换。
--new-cmd-obs 参数的作用是使用新的指令观察格式,确保导出的 ONNX 和运行时的观察布局匹配。--save-csv 和 --record 可以保存仿真数据用于 sim2real 对比。
契约的代价
61 维共享观察空间的代价是什么?信息效率。一个只需要 48 维的策略被迫处理 61 维输入,多出的 13 维对它来说是噪声。网络需要学会忽略这些维度,这增加了训练难度——虽然不大的难度。
换来的是架构上的简洁:一个控制循环、一套观察组装代码、无差别的策略加载机制。如果每个策略都有自己的观察格式,robotd 需要维护一个格式路由表,每次切换策略要切换组装逻辑,出错的可能性指数级上升。
Microduck 选择了工程简洁性而非理论最优性。在一个需要频繁切换策略的小型双足机器人上,这个选择是对的。
数据来源:pollen-robotics/microduck_rl 仓库 CLAUDE.md、README.md,pollen-robotics/microduck 仓库架构设计文档。
