第 11 章

PID:解决一切

我们怎么既消除静差,又止住晃动?

第 11 章:PID:解决一切 ↗

甩不掉的静差

西奥: 还记得第 2 章吗?我们的 P 控制器让无人机停在 2 m 以下,无论 KpK_p 取多少都解决不了。现在该找出什么办法能搞定了。

要托住无人机,电机必须一直推 mg≈4.9mg \approx 4.9 N,永远如此。P 控制器只有在有误差时才会推:T=Kp e\eff{T} = K_p\,\err{e}。所以要推出 4.9 N,它需要一个 e=mg/Kp\err{e} = mg/K_p 的误差。当 KpK_p = 20 N/m 时,就是 e\err e = 4.9/20 ≈ 24.5 cm,永远如此。静差不是我们调参的失误,而是 P 托住任何东西都要付出的代价。

朱恩: 所以我们需要控制器里有一部分能在零误差下推出 4.9 N。得有点记忆才行。

I:一堆过去的误差

思路是这样的。把误差不断累加起来,也就是误差曲线下的面积,正是第 3 章里说的“累积面积”。按照这堆面积的大小成比例地推:

TI=Ki∫0te dt\eff{T_I} = K_i \int_0^t \err{e}\,dt

只要无人机还在 2 m 以下,误差就是正的,这堆面积就不断变大,推力也就不断变大。只有当误差恰好为零时,这堆面积才停止增长。到那时它就停在那儿,保持已经积累起来的推力。这就是朱恩想要的记忆。

西奥: 哦,这招真狡猾。无人机只能在 2 米处稳定下来,其他任何地方都不行,因为别的位置上那堆东西还在变化。

第 11 章:PID:解决一切 ↗

先预测一下! 米卡想快速消除静差,所以打算把 KiK_i 调得很大。会发生什么?

组件一开始是 KiK_i = 10 N/(m·s)。注意看三样东西:红色阴影面积(正在堆积的误差)、推力中橙色虚线表示的积分部分一路爬升到紫色重力线,以及静差读数。把 KiK_i 拖到 0,第 2 章的静差就回来了,然后再试试更大的值。KpK_p 固定为 20 N/m。

第 11 章:PID:解决一切 ↗

第 11 章:PID:解决一切 ↗

米卡: 给我拉满!Ki=50K_i = 50!用火力把它干掉!

米卡: ……它怎么停不下来?弹跳越来越大,现在它上下蹦个不停,幅度有半米,永远不停!

西奥: 到 50 的时候,你越过了某条边界。我试了几个值:大约 40 以下它能稳定下来,40 以上就不行(而且溜溜球大约一秒摆一次)。我们马上就会看到原因。这堆面积总是慢半拍:无人机已经越过 2 m 了,它还在往上推。

这就是米卡的错误,而且非常常见:积分修掉了静差,但它带来了滞后,而滞后会让系统来回摆(淋浴已经教过我们这一点)。我们需要一个能对抗速度的东西。(按数学来说,米卡的摆动应该永远变大下去。它们停在大约半米,只是因为每次摆动推力都碰到 0 N:螺旋桨没法往下拉,这就是第 10 章的限制。)

D:一个虚拟减震器

朱恩: 在第 7 章里,是阻尼器让弹簧平静下来的。如果让控制器假装自己是个阻尼器呢?

阻尼器对抗的是速度。所以加一项,对抗无人机运动得有多快:

TD=−Kd dhdt\eff{T_D} = -K_d\,\frac{d\out{h}}{dt}

看看无人机的方程:阻力已经给出了 −c h˙-c\,\dot h。D 项又在上面加了 −Kd h˙-K_d\,\dot h,所以无人机的表现就好像它的阻力是 c+Kdc + K_d。我们是在软件里加空气阻力。

第 11 章:PID:解决一切 ↗

第 11 章:PID:解决一切 ↗

打开米卡的大 I 增益,再加点阻尼。哪怕 Kd=0.5K_d = 0.5 N·s/m 也能让它重新稳定(边界被推到 Ki=60K_i = 60):没完没了的溜溜球变成了逐渐消失的弹跳。但不管加多少 D,它仍然至少超调四分之一。那么大的一堆面积就是记得太多了。现在关掉米卡的 KiK_i(回到 10),试试 Kd≈3K_d \approx 3–5:安静了。I 和 D 此消彼长:D 能给 I 腾出空间,但空间不是无限的。

朱恩: 4 好的话,10 肯定更好。减震器拉满!

米卡: 它爬得好慢……然后还是越过了 2 m?还来得这么晚?

西奥: 朱恩,这又是第 7 章你那架蜂蜜无人机!Kd≈5.3K_d \approx 5.3 以上,快速那一对极点就过阻尼了。但现在还有一堆面积,无人机慢吞吞的,正好给了这堆面积时间去堆过头。

临界阻尼就是 ζ = 1 的地方:Kd=2mKp−c≈5.3K_d = 2\sqrt{m K_p} - c \approx 5.3 N·s/m(在 KpK_p = 20 时)。在 KiK_i = 10 时,把 KdK_d 从 4 调到 10,超调会从 0 变成大约 8 %,调节时间从不到一秒变成大约 5 s。D 并不是越多越好;你调过头时,组件现在也会告诉你。

米卡: 所以 D 预测未来!它看到无人机要去哪里,就先到达那里。

西奥: 不完全是。它只知道此刻的斜率。这就像假设球会一直直线飞下去来猜它落在哪里。通常猜得还不错,但这不是魔法。等我们给它接上一个抖动的传感器你就知道了。

每个旋钮对极点做了什么

是时候用第 10 章的地图了。把整个回路写下来,其中 e=r−h\err{e} = \sp{r} - \out{h}:

mh¨+ch˙=Kp e+Ki ⁣∫ ⁣e dt−Kd h˙⏟T−mgm\ddot{\out h} + c\dot{\out h} = \underbrace{K_p\,\err e + K_i\!\int\!\err e\,dt - K_d\,\dot{\out h}}_{\eff{T}} - \dis{mg}

积分不好处理,所以对两边求斜率。斜率和面积互相抵消(第 3 章),所以 ∫e dt\int \err e\,dt 的斜率就是 e\err e。重力是常数,所以它的斜率是零:它直接消失了。给定值固定时,e˙=−h˙\dot{\err e} = -\dot{\out h}。逐项来看:

ddt(Kp e)=−Kp h˙ddt(Ki ⁣∫ ⁣e dt)=Ki (r−h)ddt(−Kd h˙)=−Kd h¨\begin{aligned} \tfrac{d}{dt}\big(K_p\,\err e\big) &= -K_p\,\dot{\out h} \\ \tfrac{d}{dt}\Big(K_i\!\int\!\err e\,dt\Big) &= K_i\,(\sp r - \out h) \\ \tfrac{d}{dt}\big(-K_d\,\dot{\out h}\big) &= -K_d\,\ddot{\out h} \end{aligned}

把它们重新拼起来,把所有带 h\out h 的项移到左边。h...\dddot{\out h} 上的三个点表示斜率的斜率的斜率:

mh...+(c+Kd) h¨+Kp h˙+Ki h=Ki r\begin{aligned} m\dddot{\out h} + (c + K_d)\,\ddot{\out h} &+ K_p\,\dot{\out h} + K_i\,\out h \\ &= K_i\,\sp r \end{aligned}

西奥: 重力从方程中消失了。这就是代数形式的静差杀手:最终高度根本不在乎 mgmg。不过前提是它真的会稳定下来。米卡那架 Ki=50K_i = 50 的无人机就从来没有稳定过。

再猜一次 este^{st}。I 起作用时,方程是三次的,所以有三个极点。如果 Ki=0K_i=0,就没有积分器:求斜率时多出了一个假的根 s=0s = 0,公因子 ss 约掉后,PD 无人机只有两个极点。

ms3+(c+Kd) s2+Kp s+Ki=0m s^3 + (c + K_d)\,s^2 + K_p\,s + K_i = 0

对于所有系数都为正的三次方程 a3s3+a2s2+a1s+a0a_3 s^3 + a_2 s^2 + a_1 s + a_0,有一个经典判据(劳斯–赫尔维茨判据)指出:当且仅当 a2a1>a3a0a_2 a_1 > a_3 a_0 时,三个极点才全部位于左半平面。对我们的无人机来说,这就是悬崖边缘:

(c+Kd) Kp>m Ki⟺Ki<(c+Kd) Kpm(c + K_d)\,K_p > m\,K_i \quad\Longleftrightarrow\quad K_i < \frac{(c+K_d)\,K_p}{m}

米卡: 一条挂着两个人名字的规则。我们只能相信它吗?

西奥: 才不是。我们可以自己找到那条边界。

一对会摆动的极点到达竖轴的那一刻,实部为零,所以在三次方程里试试 s=iωs=i\omega。因为 i2=−1i^2 = -1、i3=−ii^3 = -i,实部和侧向部分必须各自加起来等于零:

Ki−(c+Kd) ω2=0,ω (Kp−m ω2)=0K_i - (c+K_d)\,\omega^2 = 0, \qquad \omega\,\big(K_p - m\,\omega^2\big) = 0

第二个式子说明 ω2=Kp/m\omega^2 = K_p/m。把它代入第一个:

ω2=Kpm,Ki=(c+Kd)ω2=(c+Kd)Kpm\omega^2=\frac{K_p}{m},\qquad K_i=(c+K_d)\omega^2=\frac{(c+K_d)K_p}{m}

第 11 章:PID:解决一切 ↗

在初始值下(KpK_p = 20,没有 D),边界是 40。米卡的无人机正是在那里失控的,它的溜溜球大约一秒摆一次:6.32 rad/s 正是第 7 章里 P 无人机自己的 ωn。加上 Kd=4K_d = 4,边界就推到 200。别光信我们:改改上面的数,或者下面的滑块,看看极点、小地图和阶跃响应怎么变。

第 11 章:PID:解决一切 ↗

  • I 旋钮(KiK_i)把第三个极点带入回路,并可能把一对极点推向右侧。太多就会越过那条线。
  • D 旋钮(KdK_d)通过增加阻尼来提高 I 增益的稳定上限。极点的路径可能会转弯:增加 D 并不会让每个极点都向左移动。
  • P 旋钮(KpK_p)主要让那对会晃的极点上下分开:响应更快,但晃得也更厉害,除非 D 能跟上。

调参游乐场

现在来真的:用真实电机(0–20 N)从地面起飞到 2 m。集齐四颗星。每次你改变增益,上一次飞行都会留下淡淡的残影。

第 11 章:PID:解决一切 ↗

朱恩: 我关掉了抗积分饱和,设 KpK_p = 30、KiK_i = 60、KdK_d = 2……结果无人机冲到了 4 m,直接飞出了它的画框!

西奥: 电机全力输出的整段时间里,这堆面积一直在长。开着抗积分饱和,同样的增益最高只到 2.8 m。同样的旋钮,记忆却天差地别。

关于 D 的两个警告

警告 1:微分冲击

无人机悬停在 1 m。在 t=1t = 1 s 时,我们把给定值移到 1.5 m。如果 D 用的是误差的斜率,那么误差会在一瞬间跳变半米。它的斜率巨大,推力也巨大。

第 11 章:PID:解决一切 ↗

第 11 章:PID:解决一切 ↗

米卡: 对误差求导,它要求超过 200 N?电机最多才 20 N 啊?

朱恩: 而且电机没法礼貌地拒绝。它们一下子猛推到底,无人机就像被踹了一脚。

改为对测量高度求斜率就能解决这个问题:无人机不可能瞬移,所以它的高度不会跳变。阻尼效果一样,却不会产生冲击。几乎所有真实的 PID 都是这么做的。

警告 2:噪声

真实的高度传感器会有几厘米的抖动。几厘米的抖动,每秒上千次,算出来的斜率大得吓人。D 会把它放大后直接送进电机。解决办法是在求斜率之前先对测量值做平滑(用一个时间常数为 τf\tau_f 的滤波器),代价是反应会稍微慢一点。

第 11 章:PID:解决一切 ↗

第 11 章:PID:解决一切 ↗

米卡: 好吧,D 并不能预测未来。它预测的只是噪声在一毫秒前碰巧是什么样子。

我们解决了什么

我们搞清楚了什么

  • P 按误差成比例地用力,但必须留下一点静差才能把无人机撑住。
  • I 把过去的误差堆积起来;唯一的静止点是零误差,所以它能消灭静差,静差以大约 Kp/KiK_p/K_i 的时间常数消失。它会增加滞后,所以太多会让系统来回摆。
  • D 对抗速度:一个虚拟阻尼器,加在阻力 cc 上,所以 ζ=(c+Kd)/(2mKp)\zeta = (c+K_d)/(2\sqrt{mK_p})。D 并不是越多越好:过阻尼时,无人机慢慢爬,那堆面积就会堆过头。
  • 闭环系统为 ms3+(c+Kd)s2+Kps+Kim s^3 + (c + K_d)s^2 + K_p s + K_i;当且仅当 (c+Kd)Kp>mKi(c+K_d)K_p > mK_i 时稳定。
  • 从测量值中取 D(避免冲击),并对其进行滤波(减少噪声)。

朱恩把我们这种无人机风格的 PID 用在了淋浴上……结果它晃动的样子和我们在第 0 章里一模一样。延迟到底是怎么回事?

第 11 章:PID:解决一切 ↗