第 11 章 动手与贡献:学习路线与带走的东西

本章代码走读:两个仓库的 AGENTS.md/CONTRIBUTING.md 中与上手直接相关的部分。

11.1 三种读者的路线图(v0.2,全部真实命令)

路线 A · 系统工程向(Rust)

第 1–2 周   Rust 基础;通读主仓库 README、docs/design/architecture.md、robotd-design.md
第 3 周     scripts/duck-sim 起鸭;robotctl 遍历命令;对照第 3 章精读 duck-ipc-proto 的 Call 枚举
第 4–5 周   精读 robotd/src/control.rs(750 行,纯计算可单测)与 duck-control/src/obs.rs;
            用 scripts/duck-sim boot 进容器,观察 systemd 单元与健康门禁
第 6 周起   挑 docs/project/ 里的已知问题或 CONTRIBUTING.md 的入门议题,提第一个 PR

路线 B · 强化学习向(物理 AI)

第 1 周   概念补课(PPO、域随机化);跑 uv run list-envs 认识任务注册表
第 2 周   冒烟测试 → 4096 环境训 Velocity(无卡用 --hf-jobs)→ play 复查
第 3–4 周 改配方重训:动一个奖励权重(先按第 8 章"算现价")或一个 DR 开关,
          对比 wandb 指标(air_time、熵、追踪得分)
第 5 周起 挑一个模板造新任务:episodic 特技→standup 模板;两态→sitstand;
          动作→roulade(读它的 cfg 文档注释——一条五次实验的教训链)
第 6 周起 走完 export → infer_policy → publish,发一条自己的策略到 HF Hub

路线 C · 产品/硬件向:Sandbox 体验 → 研读第 5 章的更新/配网/媒体设计(updater-design.md、app-path-design.md、remote-access-design.md)→ 对照自己的产品找差距。

11.2 贡献须知(来自仓库的原话)

主仓库 AGENTS.md 的规矩第 2 章已述(机制一文档一归属;行为与文档不一致时文档是 bug)。RL 仓库侧另有两条硬要求:

高价值贡献方向:新任务与奖励设计(附仿真与真机对照视频)、tofd 深度接入策略观测(社区最易做的增量实验——当前全部策略纯本体感知)、duck-sim 边界扩展、文档与 faq 改进。

11.3 带走五件事(v0.2,每件都有代码为证)

  1. 契约即类型,两处同步即故障源——Call 枚举绑定方法与参数、default-members 收拢二进制清单、ONNX Runtime 版本三方互锁(第 2、3 章);
  2. 例行故障必须不可见——丢一次总线包滑行三拍,因为观测本来就滞后一拍(第 4 章 COAST_TICKS);
  3. 权重是价格,只罚可逃避的错误——upright 先算每步现价再调倍数;head droop 对瞬时振荡免税、只罚 1 秒 EMA 的直流偏差(第 8 章);
  4. 结构化误差显式建模,随机化留给残差——齿隙用被动铰链加编码器侧反馈建模,摩擦随机化开在 BAM 正确的口子上(第 9 章);
  5. 分发链唯一、自包含、预校验——归一化器进图、发布器直调导出器、上传侧替 daemon 先拒坏文件(第 10 章)。

11.4 为什么这只鸭子不该接 ROS 2

读到这里的 ROS 2 用户多半会问同一个问题:这套 Rust 守护进程 + Unix socket 的活,用 ROS 2 不是现成的吗? 这个问题值得认真回答一次,因为答案不是"不能",而是三条算得清楚的账。

一个反常的对照

先看事实:Pollen Robotics 的科研平台 Reachy 2 原生跑 ROS 2 Humble,暴露 ros2_control、TF、robot_state_publisher——这是 Pollen 工程师 Remi Fabre 2025-04-24 在 ROS Discourse 的官方发布帖里写明的,LeRobot 的 reachy2 文档里那行 -e ROS_DOMAIN_ID 与挂载 ~/.ros/log 的 Docker 参数也是佐证。

同一家公司、同一套 Rust 底层、同一个团队的两种选择。 所以 Microduck 不用 ROS 2 不是能力问题,是定位问题:Reachy 2 是给研究者接 RViz、写论文、复用生态的;Microduck 是给消费者拆箱拿手柄就走的。判据不在技术,在"用户拿到它要干什么"。

四条算不过来的账

其一,硬件预算。 RK3566 + 1 GB RAM + 32 GB eMMC,同时要喂 ONNX Runtime、WebRTC、NPU 视觉检测。整套 Rust 工程约 30 个依赖(媒体报道口径)。ROS 2 不是跑不动,是它带进来的一整套发行版依赖、每节点数十 MB 常驻、DDS discovery 的组播与内存开销,在这块板子上每一样都要从别处挤。

其二,回路要的是确定性,不是互操作。 第 4.4 节的 COAST_TICKS = 3 是量着"一次丢包 + 一次重试 + 一次慢拍"定出来的 60 ms 容差,前提是一个进程、一个循环、不被抢占。往里塞 executor 回调、DDS 序列化、跨进程 hops,加的不是平均延迟,是尾延迟——而尾延迟正是"随机微小抽搐"的成因。

其三,契约已经做成类型了,中间件反而表达不了。 第 3.6 节讨论过 gRPC/DDS:连续调用是 notification、离散才是 request,谁应答写进 Service 枚举,连"PIN 绝不路由回 BLE"这种禁区都有测试看守。这套语义在 DDS 里要么表达不了、要么得绕着框架做。附带的好处很朴素:NDJSON over Unix socket,nc 和 jq 就能调,1 GB 的板子上没装 Foxglove 也能排障。

其四,ROS 2 不替你还产品债。 OTA 签名与回滚、PIN 配对、WebRTC、设备账号——这些第 5 章的活必须做,跟用不用 ROS 2 无关,用了还得多维护一层。

更关键:它在解决一个已经被解决的问题

桥接真正的价值在跨机——Unix socket 是机内的,出了板子就不存在。但 Microduck 的跨机通道已经有了:第 2.4 节那条守则说得明白,消费级走 WebRTC(端到端加密、控制与视频同一会话、有回传路径),media.stream 只是备用;开发者侧还有 duckctl over BLE,连 SSH/WiFi 都不用配。再开一条 DDS 通道,是替一道已解出的题写第二种解法。

至于 ROS 2 生态里最诱人的四样东西,鸭子一样也吃不下:

ROS 2 能给 Microduck 用得着吗 为什么
RViz / TF 可视化 否 TF 树就是两条腿加一个头,可视化价值接近于零
rosbag 录制 基本否 它是 RL 策略不是模仿学习——策略靠仿真域随机化练出来,不吃真机演示数据
Nav2 导航 否 媒体报道其 ToF 为 8×8(64 像素)深度,够避障,建不了图
多机协同 否 chorale 已经用无线电 + 三条 JSON-RPC 通知做完了(第 3.5 节)

什么时候才真的该接

三条判据同时成立再动手:

  1. 硬件在手——能实测尾延迟,而不是纸上推演;
  2. 能说出要复用的具体包名——是 ros2_control 还是 Nav2 还是某个驱动,不是"生态"两个字;
  3. 上层计算不在板上,且现有 WebRTC 通道扛不住——比如要 50 Hz 双向流式控制外加多传感器硬时间同步。

第 3 条是唯一可能成真的:RK3566 跑不动 VLM,想做"VLM 看一眼 → 指挥鸭子"必然跨机。但那条路 gRPC/WebSocket 也走得通,DDS 不是唯一解,更不是默认解。

真要接,也只接外围:写一个桥接节点作为 duck-ipc-proto 的又一个客户端(身份与 duckctl 同级),往外出 /joint_states、/imu、/tf、相机与 ToF,往里把 /cmd_vel 翻译成 RobotMove 通知、把技能与策略切换做成 service。并且守住三条纪律:只有 robotd 能写电机(ROS 侧发的是意图,不是指令);连续量走 notification、离散才走 request,别给 50 Hz 指令加请求-等待;61 维观测的组装权只属于 duck-control,桥里不许再算一遍(第 1 章那句"最高风险代码")。核心回路一行不改。

比桥接更值得做的一件事

如果真想在鸭子身上做点官方没做的东西,不是桥接,是数据回流管线:

真机 rollout → 落 61 维观测 + 14 维动作 + 关节电流/电压 + 同步视频 → 转成数据集推 HF Hub → 反哺仿真的执行器参数辨识。

第 9 章那批参数——库仑摩擦、齿隙 ±1°、电压-力矩曲线——目前是仿真里编出来的。真机录下来的电流与位置-指令误差是它们唯一的校准来源,而这条闭环官方尚未提供。它不需要 ROS 2(一个 duck-ipc-proto 订阅者加一个 parquet 写入器就够),价值却直接落在 sim2real 这一本书的主线上。

一句话

ROS 2 解决的是多团队、多进程、可复用组件之间的互操作;Microduck 的问题是单板、单闭环、单供应商。前者要松耦合,后者要确定性。当系统里只有一个 50 Hz 闭环时,中间件不是资产,是抖动来源。

判断力体现在"知道什么时候不用",而不在"知道怎么用"。

本节来源:Reachy 2 的 ROS 2 栈——Remi Fabre(Pollen Robotics),ROS Discourse,2025-04-24;LeRobot 官方文档《Reachy 2》Docker 参数段。Microduck 硬件与依赖规模——Gear Live《Hugging Face's $399 Microduck》2026-08、cocoloop《399 美元开源机器鸭》2026-09。书内引用:COAST_TICKS(第 4.4 节)、chorale(第 3.5 节)、WebRTC 主备关系(第 2.4 节)、61 维观测(第 1.4 节)、执行器随机化(第 9 章)。

11.5 结语

v0.1 说"把本书当地图,把仓库当领土"。v0.2 写完后可以更进一步:这本书引用的每一段代码都来自两个仓库的真实文件,但仓库在流动——策略集已到 v5,61 维布局也曾是 51/54/49/85 维的演化终点。读的时候请保持第 2 章那条警惕:注释的嗓门不等于现状的优先级,行为的归属以设计文档为准。愿你训出的第一条步态,在第一次上机时稳稳落地。

附录 A:关键链接

附录 B:本书引用的主要源码文件

文件 行数 出现章节
microduck/duck-ipc-proto/src/lib.rs ~6200 1、3、5、10
microduck/robotd/src/main.rs ~8100 4
microduck/robotd/src/control.rs ~750 4
microduck/duck-control/src/obs.rs — 1、4
microduck/kinematics/src/lib.rs — 5
microduck/scripts/duck-sim — 6
microduck_rl/src/mjlab_microduck/tasks/symmetry.py 169 8
microduck_rl/.../tasks/microduck_velocity_env_cfg.py 949 7、8
microduck_rl/.../tasks/mdp.py ~7400 8
microduck_rl/.../actuator/friction_dr_bam.py 112 9
microduck_rl/.../robot/microduck/add_backlash.py — 9
microduck_rl/src/mjlab_microduck/export.py — 10
microduck_rl/src/mjlab_microduck/publish/manifest.py — 10
两仓库 AGENTS.md、主仓库 Cargo.toml、docs/design/*.md — 全书