做运动控制时,真正消耗时间的往往不是把公式写进代码,而是判断一个现象究竟来自模型、采样、执行器,还是测量噪声。本文从最常见的速度环出发,记录一套可重复的排查顺序。
先明确控制对象
开始调参前,至少要回答下面三个问题:
- 控制器的输入和输出分别是什么?
- 速度反馈经过了哪些滤波和单位换算?
- 执行器是否存在饱和、死区或明显延迟?
如果这些问题没有写清楚,后续看到的“振荡”可能只是时间戳错位,“跟踪误差”也可能只是量纲不一致。
先让数据链路可信,再讨论控制器是否优秀。
一份最小检查清单
- 确认采样周期稳定,并记录实际抖动;
- 对齐指令、反馈和控制输出的时间戳;
- 分别记录原始信号与滤波后信号;
- 给限幅、死区补偿和保护逻辑留下独立日志。
从简单模型获得量级感
速度环可以先用一阶模型近似:惯量决定响应的基本速度,阻尼和负载影响稳态表现。模型不必一开始就很精确,但必须能帮助我们判断参数量级。
| 观测现象 | 可能原因 | 优先检查 |
|---|---|---|
| 响应很慢 | 比例增益偏小、输出限幅 | 控制输出是否长期饱和 |
| 高频抖动 | 增益偏大、测速噪声 | 原始速度与滤波延迟 |
| 稳态有误差 | 摩擦、负载扰动 | 积分项与死区补偿 |
| 周期性振荡 | 延迟、结构共振 | 采样周期与机械频率 |
控制器代码应该表达意图
下面的代码只保留了最核心的结构。实际工程中还需要处理积分限幅、模式切换和异常输入。
struct PIController {
double kp{0.0};
double ki{0.0};
double integral{0.0};
double update(double target, double measured, double dt) {
const double error = target - measured;
integral += error * dt;
return kp * error + ki * integral;
}
};
行内代码也适合描述变量,例如 dt 必须来自可靠的时钟,而不应假设每次循环都严格等于配置周期。
为什么要单独记录饱和状态
当控制输出被限制时,积分项仍可能继续累积。解除限制后,这部分累积会带来明显过冲,这就是常说的积分饱和。把 is_saturated 写入日志,可以让问题定位直接很多。
图像和实验记录

实验图应该能独立回答一个问题。与其在一张图中堆叠十条曲线,不如分别展示参考值与反馈、控制输出、误差和关键状态。
接下来做什么
完成基本闭环后,可以继续研究前馈、扰动观测器和更系统的辨识方法。阅读资料时我通常从 Astro 文档 这类结构清晰的技术文档学习其表达方式:先给可操作的主线,再补充边界和细节。
这套顺序并不能替代经验,但它能让每一次调试留下可复用的证据,而不只是“这组参数好像更稳”。