servo_load_probe.py:判一颗舵机是不是真的坏了起因是我判错过一次。左臂 shoulder_lift 过热之后一动就让总线丢包,我从三个插值点得出"劣化,该换",连找主办方要备件的话都写好了。操作者只说了一句 「而且你应该做一个梯度测试才行呀」——扫完整条曲线,结论整个反了。这个脚本就是那次教训的固化。
单看一颗舵机永远说不清:温度高可能是它在使劲,通信失败可能是总线、供电、线缆,甚至是别人在占串口。
所以判据是对照:拿同一条总线、同一路供电、同一束线缆上的健康关节做参照,比的是同样负载下谁先崩。当晚热态的数据是这样——
ID 2 负载 184-218 -> 通信失败 15 / 23 / 33
ID 3 负载 215-223 -> 失败 0
ID 4 负载 199-224 -> 失败 0
健康的扛得住 220+,它过约 150 就让总线崩。同总线、同供电、同线缆——只能是那一颗。这一步是对的,被推翻的是下一步。
上面那三行是三个幅度点。我据此写下"劣化",其实是在两点之间画直线:默认失败率随负载单调上升、而且已经跨过了它的能力边界。
这两个假设都没有被验证过。过热会让舵机暂时降额——表面凉了不等于绕组凉了——所以"现在扛不住 150"完全可能是"此刻扛不住",而不是"永久扛不住"。分不开这两者,靠的不是更小心地解读三个点,而是把整条曲线扫出来,凉透之后再扫一遍。
--sweep:凉透之后的完整梯度| 幅度 | 角度 | ID2 失败/负载 | ID3 失败/负载 | ID4 失败/负载 |
|---|---|---|---|---|
| 5 | 0.4° | 0 / 32 | 0 / 36 | 0 / 37 |
| 10 | 0.9° | 0 / 78 | 0 / 77 | 0 / 81 |
| 15 | 1.3° | 0 / 149 | 0 / 120 | 0 / 116 |
| 20 | 1.8° | 0 / 195 | 0 / 148 | 0 / 143 |
| 25 | 2.2° | 0 / 239 | 0 / 171 | 0 / 164 |
| 30 | 2.6° | 0 / 255 | 0 / 207 | 0 / 188 |
| 40 | 3.5° | 0 / 291 | 0 / 223 | 0 / 220 |
结论:过热降额,已完全恢复。舵机是好的。
注意它翻的方向——不是"没那么糟",是反过来了:那颗被我判死刑的舵机,凉透后的带载能力高于两个健康参照。三个点给出的结论和七个点给出的结论是相反的,不是程度不同。
# 默认:左臂 ID2 为怀疑对象,ID3/ID4 为对照
python servo_load_probe.py
# 完整梯度扫描(推翻误判用的就是这条)
python servo_load_probe.py --sweep
# 换一条总线 / 换怀疑对象
python servo_load_probe.py --bus /dev/ttyACM0 --suspect 2 --refs 3 4
| 参数 | 默认 | 作用 |
|---|---|---|
--bus | /dev/ttyACM1 | 串口。左臂 ACM1,右臂 ACM0 |
--suspect | 2 | 怀疑的舵机 ID(2 = shoulder_lift) |
--refs | 3 4 | 对照组。必须同总线,否则比的是两条总线不是两颗舵机 |
--delta | 30 | 单步幅度,counts。30 ≈ 2.6° |
--sweep | 关 | 扫整条梯度,而不是只测一个幅度 |
--temp-stop | 45.0 | 超过这个温度立刻卸力矩中止 |
--temp-stop 超温中止。读不到温度时它会拒绝运动而不是继续跑:当晚踩过一次相反的坑,一个写错的电机名让"45 °C 卸力矩"这道保护从头到尾没生效,日志里"起始温度 None / 峰值 0"才暴露出来。写了但没生效的保护,比没写更危险,因为你会依赖它上面那张表就是基线。以后同一颗关节再出问题,凉透之后跑 --sweep 直接对比:
有基线和没基线的差别,就是这一页和那次误判的差别。
源码 03-software/scripts/servo_load_probe.py(230 行)。数据为 2026-09-08 凌晨实测。
相关:过热事件全过程 · 给主办方的通报 · 当晚修掉的三个遥操作 bug