DEEP 198 · 第十六辑 · 小白基础课 · LEARN NOTES

收进学习手册 · 2026-09-11 · duck.whatled.com

解读 198 · 50Hz 的由来

解读对象:duck-control/src/model.rs(241 行,常量区自第 80 行起)· 交叉验证 infer_policy.py
需要的前置:解读 107(model.rs 常量全表);解读 01(一个节拍里怎么安排 16 个对话)。

一句话答案

50Hz 不是拍脑袋,是三道算术互相咬合的结果:仿真侧 0.005s × 4 步 = 20ms;总线侧 16 台设备在 1Mbps 上的问答必须塞进这 20ms;而且修好的关键参数不是波特率,是一个出厂默认值。

第一道:训练侧的 0.005 × 4

回放脚本 infer_policy.py 启动时打印:Control frequency: 50 Hz (decimation: 4),配 model.opt.timestep = 0.005。物理引擎每 5 毫秒推一步,策略每攒 4 步(20ms)想一次——这是训练时就定下的节拍。策略在什么节拍上学的,部署就必须在什么节拍上跑,50Hz 首先是训练合同的复制。

第二道:总线侧的 8ms 账单

真机上每个节拍要干一件事:一条 sync_read 问遍总线上的 16 台设备(IMU 板 ID 200 + 15 只舵机),一条 sync_write 写回目标位置。总线带宽是 BAUD_RATE: u32 = 1_000_000(model.rs:80,1Mbps)。真正的暗雷在 EXPECTED_REGISTERS 的注释里(model.rs:89–94):

return_delay_time is the load-bearing one: the XL330 ships at 250, which is 500 µs of turnaround per device. Across 16 devices that is 8 ms per tick — 40% of a 20 ms budget — spent waiting for servos to get around to answering.

翻译:每只舵机收到指令后要默认等 500 微秒才应答(出厂寄存器值 250,单位 2µs)。16 台设备排队,光"轮到你我再开口"就要烧掉 8 毫秒——20ms 预算的 40% 什么都没干,纯等。所以启动时 check_registers 会把这个寄存器强制改写成 0:("return_delay_time", 0)。8ms 的账单一笔勾销,读写才塞得进一个节拍。同表里 ("baud_rate", 3) 也是防漂移:寄存器值 3 必须 = 1Mbps,与 BAUD_RATE 对账。

第三道:预算的纪律

20ms 里不只有问答:61 维观测要拼装、ONNX 要推理、14 个动作要写回、安全检查要过。bus.rs 为此把每个环节都按预算设计:

超时 30ms 比 20ms 大,矛盾吗?不——它是"坏情况的上限",健康时 16 台设备的问答远到不了 20ms,预算余量就是用来吸收失联那一拍的。

你带走的收获

延伸

什么鸭 WHATDUCK · 复刻学习笔记 ← 全部手册 · 首页