抖动这种故障有个特点:它看起来很小,但它会让你怀疑整个系统的精度。
我的云台在收到固定的 JointState 指令时,两个轴都在微小地颤,
幅度大概 0.3° 到 0.5°,肉眼勉强可见,但装在云台上的激光雷达把这件事放大了——
点云在原地"呼吸",测距值来回跳 ±12 mm。
舵机抖动的成因,按我这些年遇到的频率排序,基本就三类:电源、信号、机械。 按这个顺序查,省时间,因为越靠前越好验证。
01电源:最好排除,先做
舵机在堵转和启动瞬间会拉很大的电流。一颗常见的数字舵机,静止时几十毫安, 转动瞬间可以拉到 800 mA 以上。如果它和逻辑电路共用一路电源,这个尖峰会把 3V3 拉下去—— 这件事我在上一篇掉线排查里已经吃过一次亏,所以这次第一件事就是查它。
静止保持 42 mA
收到指令瞬间 760 mA(峰值,约 15 ms)
回落 55 mA
5 V 跌落 5.02 V → 4.71 V // 0.31 V,可接受
3V3 跌落 3.298 V → 3.294 V // 几乎没动
0.31 V 的跌落不至于让舵机复位,逻辑侧更是纹丝不动。电源排除。 不过为了后面不再反复,我还是顺手补了 220 µF 的电容,把跌落压到 0.11 V——这不解决抖动,但让我心里踏实。
02信号:示波器不会说谎
第二类怀疑是 PWM 本身不稳。舵机靠脉宽定位,典型的 50 Hz、0.5–2.5 ms 映射 0–180°。 如果脉宽在抖,舵机就会跟着抖,这是最直接的因果。
我把示波器挂在信号线上,让系统持续发送同一个角度指令,测了 200 个周期:
平均 1.5002 ms 标准差 0.0031 ms // ±3.1 µs 极值 1.492 / 1.508 ms 换算成角度:±3.1 µs ÷ 11.1 µs/° ≈ ±0.28°
找到嫌疑了。±3.1 µs 的脉宽抖动,换算过来正好是 ±0.28°,和观察到的抖动幅度吻合。
但这只是"吻合",不是"证明"。我做了个对照实验:把信号源从 MCU 的硬件 PWM 换成一台信号发生器, 输出纯净的 1.500 ms 方波——舵机照抖。
这一下把信号这条线也砍了一半:脉宽抖动确实存在,但它不是抖动的原因, 最多是放大器。干净的 1.500 ms 照样抖,说明舵机自己不想安静。
03机械:最意外的一种
前两类都排得差不多的时候,我做了个一直没做的动作:用手按住云台支架。
不抖了。
松开,继续抖。再按住,又不抖。这个实验非常土,但它两秒钟就告诉了我真相: 抖的不是舵机,是整个结构在共振。舵机内部的 PID 在不停地纠正一个微小的位置误差, 纠正产生的力传递给支架,支架的形变又反馈成新的位置误差,形成闭环。 系统在一个它自己制造的正反馈里循环。
找到根因之后,修法就很直接了,一共三步,按效果排序:
- 加刚度。 支架从 2 mm 亚克力换成 3 mm 铝板,并在悬臂处补一颗 M3 螺丝。 这一条解决了 80% 的问题,肉眼看不出抖了。
- 加死区。 在固件里判断:目标角度与上一次指令相差小于 0.5° 时, 不发送。舵机不必为一个它到不了的位置反复纠正。
- 降指令频率。 原本 50 Hz 的指令刷新改成 20 Hz。舵机的控制周期本身也就 20 Hz 上下, 发得更快只是让它在两次有效动作之间多抖几下。
static float last_deg[2] = {0}; void write_servo(int id, float deg) { // 小于 0.5° 的变化不值得发一次指令: // 舵机到不了,只会让它在目标附近来回纠正 if (fabsf(deg - last_deg[id]) < 0.5f) return; last_deg[id] = deg; servo_set_angle(id, deg); }
04修完之后的一点想法
这次最花时间的不是解决问题,是承认问题不在我熟悉的地方。 我是写固件的,遇到抖动本能地去看 PWM 波形、去看定时器精度,那是我擅长也舒服的区域。 而真正的答案在我手能摸到的地方——一块太软的塑料板。
所以我给自己的排查顺序加了一条:在打开示波器之前,先用手摸一遍结构。 听起来不专业,但它最快。
- 2026.08.20 补充
- 有同行指出数字舵机的 PID 参数在部分型号上可通过串口调整,降低 P 增益也能缓解自激。 我手上这两颗不支持,没有实测,仅作记录。
- 2026.08.06 修订
- 更正:初稿写"脉宽抖动 ±3.1 µs 是主因",对照实验后应降级为次要因素,正文已改。
留言
正在载入留言…