机器人的 PID 控制:一份学习路线图
把带噪声的测量值变成平滑的误差信号,并整定出一个稳定的 PID 控制器,让它跟踪目标而不振荡。
PID 控制器是机器人领域最常用的主力算法:它把机器人物理所在的位置与本应所在的位置之间的差距变成一个修正动作。但一个控制器的好坏,不超过它所依据的信号质量,所以这条路线先教你把带噪声的传感器读数清理干净,再教你去整定比例、积分和微分三项,让机器人平滑跟踪而不是振荡。
动手节点里顺序很关键。先整定一个裸 PID 回路——单一设定点、单一被控对象,让超调、振荡和稳态误差各自孤立地呈现出来。之后才把这同样的三个增益搬到循迹车上,在那里被控对象会反击,误差来自一个真实的传感器——而整定正是在那里不再整洁。
这条路线的终点,是前面那些节点带你去不了的地方。到此为止的每一个被控对象都是稳定的:不管它,它就待在那里,所以整定是在做“比什么都不做更好”这件事。而自平衡车并不稳定——不管它,一秒之内它就倒在地上了。就这一个改变,就把良好的整定从一种改进变成了一项必需,并且引出了这条路线最终要落在的那个结构性想法:当你无法直接命令你真正关心的那个量时,你把两个回路嵌套起来,让外层回路去移动内层回路的目标。
开始之前
你应该已经会读传感器,也会以指令速度驱动电机——电机控制路线 两样都涵盖。请带上一个机器人,或者一个改一个数字就能立刻看到效果的模拟器。
你将会做到什么
- 按一个有意安排的顺序先整定 P、再整定 I、最后整定 D,而不是一次改三个数字然后碰运气。
- 读一条响应曲线,仅凭形状就指出问题所在:超调、持续振荡,或者永远不收敛的稳态误差。
- 在执行器饱和时认出积分饱和,并用钳位来修它,而不是把增益降到症状被藏起来为止。
- 嵌套两个回路,让外层控制器去移动内层控制器的设定点。
大家卡在哪里
**把三个增益一起整定。**在三个互相影响的旋钮和一个输出面前,一次性全改, 意味着你从这个结果里什么也学不到。把 P 加到振荡再回退;之后才加 D; I 放在最后,而且只在还剩稳态偏差时才加。
**对误差求微分。**当设定点阶跃时,误差也随之阶跃,它的导数在瞬间极其巨大 ——机器人毫无物理原因地猛地一抽。改为对测量值求导。数学上几乎一样, 而那一记冲击消失了。
**把带噪声的信号喂给 D。**微分运算会放大噪声。如果你的传感器在抖动, D 就把这些抖动变成电机指令。先做滤波——这正是这条路线从 传感器噪声开始、而不是从控制器开始的原因。
**假定增益可以直接搬过去。**增益是某个特定被控对象的属性。新的电池、 更重的底盘、或者不同的地面都会改变被控对象,而昨天还完美的整定 今天就不合适了。
**前后不一致的回路速率。**I 项和 D 项都取决于过去了多少时间。如果你的回路 想跑就跑,两者都是对着一个谎言算出来的。把间隔固定下来,或者测量它, 并把真实经过的时间传进计算里。
你需要什么
| 什么 | 这条路线为什么需要它 | 说明 |
|---|---|---|
| 一个能被激励的被控对象 | 某个会对指令作出响应并稳定下来的东西——电机、加热器、机器人 | 如果一样都没有,这三个实验台都能替代 |
| 一种画图的办法 | 盯着机器人整定是猜谜,读曲线整定不是 | 串口绘图器,或者这条路线上的实验台 |
| 固定的回路间隔 | I 和 D 都是按经过时间算的。一个想跑就跑的回路算出来的两项都是谎言 | millis() 模式 |
| 一次只动一个旋钮的耐心 | 整套方法本身 | — |
这条路线的每一个节点都可以在浏览器里完成。三个实验台是跑在真实 (仿真的)被控对象上的真控制器,而整定流程可以直接搬到硬件上—— 这正是先在这里做的意义所在。
为什么按这个顺序
这个顺序是挑过的,目的是让每个阶段恰好只增加一项新的困难。
滤波排在最前,因为一个控制器的好坏不超过它的输入,而 D 正是那个 会惩罚你跳过这一步的项。微分会放大噪声:一个每 10 ms 采样一次、抖动 ±2 个计数的传感器,会产生一个每秒摆动 200 个计数的微分估计,而 D 会把 它直接变成电机指令。在未经过滤的信号上整定控制器,只会教你一个错误的 道理——D 没用。
裸回路排在机器人之前,因为它把控制器隔离了出来。一个设定点、 一个被控对象、三个增益,没有转向几何、没有传感器布置、没有轮子打滑。 当响应在这里出现超调时,原因就是增益,不可能是别的。
循迹车接着上,因为它一次加了两样东西,而且两样都是诚实的增加: 误差现在来自一个视场有限的真实传感器,而被控对象会反击——机器人自身的 运动改变误差的速度比控制器预期的更快。一组看起来已经完成的整定, 正是在这里不再算完成。
**平衡车排在最后,而它是整条路线的重点。**在它之前的每一个被控对象 都是稳定的:不管它,它就待在那里,所以整定是一种优化。而倒立摆 并不稳定——不管它一秒,它就倒在地上了。就这一个改变,就把良好的整定 从一种改进变成了一项必需,并且逼出了这条路线最终落点上的那个结构性想法: 当你无法直接命令你真正关心的那个量时,你就把两个回路嵌套起来。
检查点:如何判断一个阶段已经落地
| 阶段 | 检查内容 | 通过是什么样 |
|---|---|---|
| 滤波 | 一边扰动传感器,一边把原始信号和滤波后的信号画在同一组坐标轴上 | 你能指出滤波付出的滞后是多少毫秒,并说出它为什么值得付 |
| 只用 P | 把 P 加到出现稳定振荡,记下数值,再减半 | 临界增益有了数字,而响应此时会超调并稳定下来 |
| 加 D | 加大 D,直到超调被压住 | 超调下降而响应没有变迟钝;D 过量则会变得参差不齐 |
| 加 I | 只在还剩稳态偏差时才加 | 偏差收敛了,而且你在测试接近饱和之前就已经钳位了累加器 |
| 读曲线 | 看着三条响应,指出各自的问题 | 你能仅凭形状区分超调、持续振荡和稳态误差 |
| 饱和 | 指令一个远超执行器可达范围的设定点,然后释放 | 系统释放时不会冲过头——如果会,说明你的钳位漏了 |
| 回路速率 | 打印一分钟内你的实际间隔 | 它是固定的,或者你把实测的经过时间传进了计算 |
| 串级 | 在一个平衡被控对象上指令位置 | 内层回路比外层快好几倍,而且你能说出这个比值 |
这条路线到哪里为止
PID 是主力算法,而不是整个领域,而这条路线对它在哪里结束是诚实的。
它停在单输入单输出控制。真实机器人往往有若干互相耦合、彼此干扰的 回路,而那是一整门 PID 并不涉脚的学科。
它不涵盖前馈,而这大概是大多数机器人回路所缺失的最大一项实用改进。 PID 在构造上就是被动的——没有误差存在时它无法动作。如果你已经知道自己 正在指令的动作,就可以提前算出所需输出的大部分,只把差值留给 PID 修正。 运动曲线教程就是这个想法的前半段。
它不涵盖基于模型的控制——LQR、极点配置,或者任何从写下被控对象 方程开始的做法。当你有一个准确的模型时,它们能给出更好的结果, 而大多数小型机器人都没有。
它也不涵盖增益调度,这是对“增益搬不过去”这个事实的标准答案: 保存几套整定,然后按速度、负载或电池电压在它们之间切换。
学习路线
路线
按顺序完成各个节点——完成一个就会解锁下一个。你的进度会保存在这台设备上。
已完成 0 / 7
常见问题
常见问题解答
PID 增益应该按什么顺序整定?
先 P,再 D,最后 I,而且永远一次只调一个。增大比例增益,直到响应出现持续振荡,然后退回到大约一半的数值——这是经典的齐格勒–尼科尔斯起点,能让你很快接近目标。接下来加入微分,抑制 P 留下的超调。最后才加积分,而且只在还剩下 P 单独消除不了的稳态偏差时才加。三个一起改什么也学不到,因为面对三个互相影响的旋钮和一个输出,你没法把看到的变化归因到你拧的那个旋钮上。
为什么我的 PID 控制器会振荡?
通常是比例增益相对于被控对象的延迟来说太高了。每个回路都有滞后——传感器滤波、电机惯性、回路间隔本身——而 P 作用的是已经过时的误差。增益超过一定程度后,修正来得太晚,反而让情况变得更糟,回路就开始振荡。降低 P 并加入 D,D 作用于变化率,因此能起到预判的作用。如果振荡又快又参差不齐,而不是平滑的,那就要怀疑是噪声进入了 D:微分会放大噪声,抖动的传感器就会变成抖动的电机指令。
什么是积分饱和,怎么解决?
当执行器饱和——电机已经满功率——误差就不再缩小,但积分项还在不断累加它。等到达设定点时,积分里已经存了一个很大的值必须释放掉,于是系统一冲而过,要花很长时间才能回来。解决办法是把积分累加器钳位在一个合理的范围内,或者在输出饱和时停止累加。你不应该做的,是一味降低积分增益直到症状被藏起来,那只是用一个永远消除不了的偏差去换掉超调。
我的 PID 增益能用到另一台机器人上吗?
不能,而且往往连明天的同一台机器人都用不了。增益是被控对象的属性,而不是算法的属性,而被控对象包括电池的荷电状态、底盘的质量、地面,以及减速箱的温度。一组在新电池上完美的整定,到了电池快没电时就会变得迟钝,因为同样的占空比现在产生的扭矩更小了。这就是为什么这条路线坚持按流程整定,而不是照抄数字——增益搬不过去,流程却可以。
为什么要对测量值求导,而不是对误差求导?
因为设定点一阶跃,误差也随之阶跃,而阶跃的导数在瞬间极其巨大。机器人会猛地一抽,原因却与物理毫无关系——实际上什么都没动,你只是提出了不同的要求。改为对测量值求导,你能得到关于系统实际变化快慢的同样信息,却完全没有设定点冲击。这只是一行代码的改动,而且几乎总是正确的做法。
什么是串级 PID,什么时候需要它?
两个嵌套的回路,外层回路的输出就是内层回路的设定点。只要你无法直接命令你真正关心的那个量,就需要它。在自平衡机器人上,你想控制的是位置,但电机控制的是车轮扭矩,而移动的唯一办法就是有意地倾斜——所以内层回路把倾角保持在给定的任意角度上,外层回路则为了行进而请求一个倾斜量。内层回路必须比外层快好几倍,否则外层回路就是在追一个内层回路还没达到的目标。