DEEP 92 · 第九辑 · 电压专项 · LEARN NOTES

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

解读 92 · 电压遥测与电量计:BATTERY_FULL_V/EMPTY_V

解读对象:duck-control/src/model.rs(241 行,电池节 107–135)
需要的前置:解读 45("没有电量计"这个前提)、解读 93(换电压时这两个常量怎么改)

model.rs 的电池节开头就交代了硬件真相:"There is no fuel gauge and no ADC"(model.rs:109)——整机没有任何电量测量电路。那电量百分比从哪来?答案:舵机自己。每个舵机会回报自己的供电电压,主机把它当作"从总线上看到的电池"。理解了这个出身,后面每个数字的怪脾气都说得通。

1. 测量路径:寄存器 144 起 3 字节

bus.rs 的慢速块用一次事务读回电压和温度:SLOW_READ_ADDR: u8 = 144(bus.rs:44)——寄存器 144 是 present_input_voltage(u16,0.1 V/LSB,bus.rs:47),146 是 present_temperature(u8),三个字节,电压与热学共用一次 sync_read(bus.rs:529)。换算先记住:读数 74 就是 7.4 V。

2. 两个常量:满 8.2、空 6.6

3. battery_percent:一条线性式和它的边界

((volts - BATTERY_EMPTY_V) / (BATTERY_FULL_V - BATTERY_EMPTY_V)).clamp(0.0, 1.0) * 100.0

f64 是 Rust 的 64 位浮点数,.clamp(0.0, 1.0) 把结果夹在区间内。算例:读数 7.4 V → (7.4−6.6)/(8.2−6.6) = 0.8/1.6 = 50%(测试断言,model.rs:191);9.5 V 超满 → 钳到 100(model.rs:199);5.0 V → 0(model.rs:200)。入口守卫(model.rs:131):非有限值或 ≤0 一律返回 0——NaN 是"对空集合求均值"的产物,0.0 是"总线没人应答",两者都该显示"未知",而不是一个吓人的百分数。

注释还记了一条教训:这个映射住在 model.rs 而不是 robotd,为的是全仓只有一个映射——原型曾把它在 CLI 写一份、应用里又推一遍,于是两块屏幕对同一块电池各说各话(model.rs:122-126)。

4. 消费端:EMA 平滑与没电前的坐下

原始电压每走一步塌零点几伏、迈步间又回弹,直接显示会乱摆。robotd 用 BATTERY_EMA_ALPHA = 0.1(main.rs:118)每秒刷新一次,时间常数约 10 秒——数字可读了,但也意味着负载塌陷到不了阈值,注释说破:"a pack that gets there is spent"(main.rs:2445-2446)。跌破 BATTERY_EMPTY_V 触发的不是急断电,而是先坐下再断电的序列(main.rs:2444-2451):能坐就缓降坐下、顺路播一声"peck",然后才切扭矩断电。

你带走的收获

延伸

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