Chapter 11

PID: Fixing Everything

How do we kill the droop and stop the wiggle?

Chapter 11: PID: Fixing Everything ↗

The droop that won't die

Theo: Remember Chapter 2? Our P controller left the drone hanging below 2 m, and no KpK_p could fix it. Time to find out what can.

To hold the drone up, the motors must push mg≈4.9mg \approx 4.9 N, forever. A P controller only pushes when there's an error: T=Kp e\eff{T} = K_p\,\err{e}. So to push 4.9 N it needs an error of e=mg/Kp\err{e} = mg/K_p. With KpK_p = 20 N/m that's e\err e = 4.9/20 ≈ 24.5 cm, forever. The droop isn't a bug in our tuning. It's the price P charges for holding anything up.

June: So we need a part of the controller that can push 4.9 N with zero error. Something with a memory.

I: a pile of past error

Here's the idea. Keep a running total of the error, the area under the error curve, exactly the "accumulating area" from Chapter 3. Push in proportion to that pile:

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

While the drone is below 2 m, the error is positive and the pile keeps growing, so the push keeps growing. The pile only stops growing when the error is exactly zero. At that point it simply stays, holding whatever thrust it has built up. That's the memory June wanted.

Theo: Oh, that's sneaky. The drone can't settle anywhere except 2 m, because anywhere else the pile would still be changing.

Chapter 11: PID: Fixing Everything ↗

Predict first! Mika wants the droop gone fast, so plans to crank KiK_i way up. What will happen?

The widget starts at KiK_i = 10 N/(m·s). Watch three things: the red shaded area (the error being piled up), the dashed orange integral part of the thrust climbing up to the purple gravity line, and the droop read-out. Drag KiK_i to 0 to get Chapter 2's droop back, then try bigger values. KpK_p is fixed at 20 N/m.

Chapter 11: PID: Fixing Everything ↗

Chapter 11: PID: Fixing Everything ↗

Mika: Crank it! Ki=50K_i = 50! Kill it with fire!

Mika: …why won't it stop? The bounces grew and grew, and now it's yo-yoing up and down by half a metre, forever!

Theo: At 50 you went past some kind of edge. I tried a few values: below about 40 it settles, above it doesn't (and the yo-yo takes about one second per swing). We'll see why in a moment. The pile is always a little behind: it keeps pushing up after the drone has passed 2 m.

That's Mika's mistake, and it's a very common one: the integral fixes the droop, but it adds lag, and lag makes things swing (the shower taught us that). We need something that pushes back against speed. (By the maths her swings should grow for ever. They stop at about half a metre only because the thrust hits 0 N on every swing: propellers can't pull down, Chapter 10's limit.)

D: a virtual shock absorber

June: In Chapter 7 the damper was what calmed the spring down. What if the controller pretends to be a damper?

A damper pushes against velocity. So add a term that pushes against how fast the drone is moving:

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

Look at the drone's equation: drag already gives −c h˙-c\,\dot h. The D term adds −Kd h˙-K_d\,\dot h on top, so the drone behaves as if its drag were c+Kdc + K_d. We're adding air resistance in software.

Chapter 11: PID: Fixing Everything ↗

Chapter 11: PID: Fixing Everything ↗

Flip on Mika's big I gain and then add damping. Even Kd=0.5K_d = 0.5 N·s/m tips it back to stable (it moves the edge out to Ki=60K_i = 60): the endless yo-yo becomes a bounce that dies away. But it still overshoots by at least a quarter, however much D you add. That huge pile simply remembers too much. Now switch Mika's KiK_i off (back to 10) and try Kd≈3K_d \approx 3–5: calm. I and D trade off: D buys you room for I, but not unlimited room.

June: If 4 is good, 10 must be better. Maximum shock absorber!

Mika: It creeps up so slowly… and then it goes past 2 m anyway? Late?

Theo: June, that's your honey drone from Chapter 7 again! Past Kd≈5.3K_d \approx 5.3 the fast pair is overdamped. But now there's a pile too, and a slow drone gives the pile time to overfill.

Critical damping is where ζ = 1: Kd=2mKp−c≈5.3K_d = 2\sqrt{m K_p} - c \approx 5.3 N·s/m at KpK_p = 20. With KiK_i = 10, going from KdK_d = 4 to 10 takes the overshoot from 0 to about 8 %, and the settling time from under a second to about 5 s. More D is not always better; the widget now says so when you overdo it.

Mika: So D predicts the future! It sees where the drone is going and gets there first.

Theo: Not really. It only knows the slope right now. It's like guessing where a ball will land by assuming it keeps going straight. Usually a decent guess, but it's not magic. Wait until we feed it a jumpy sensor.

What each knob does to the poles

Time to use Chapter 10's map. Write the whole loop down, with 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}

The integral is awkward, so take the slope of both sides. Slope and area undo each other (Chapter 3), so the slope of ∫e dt\int \err e\,dt is just e\err e. Gravity is constant, so its slope is zero: it simply falls out. With a fixed setpoint, e˙=−h˙\dot{\err e} = -\dot{\out h}. Term by term:

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}

Put them back together and move everything with h\out h to the left. The three dots on h...\dddot{\out h} mean the slope of the slope of the slope:

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}

Theo: Gravity disappeared from the equation. That's the droop-killer in algebra form: the final height doesn't care about mgmg at all. As long as it settles at all, that is. Mika's Ki=50K_i = 50 drone never did.

Guess este^{st} again. With I active, the equation is cubic, so there are three poles. If Ki=0K_i=0, there is no integrator: taking the slope added a fake root at s=0s = 0, the common factor ss cancels, and the PD drone has two poles.

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

For a cubic with all-positive coefficients a3s3+a2s2+a1s+a0a_3 s^3 + a_2 s^2 + a_1 s + a_0, there's a classic test (the Routh–Hurwitz criterion) that says all three poles are in the left half exactly when a2a1>a3a0a_2 a_1 > a_3 a_0. For our drone that's the cliff edge:

(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}

Mika: A rule with two people's names on it. Do we just have to believe it?

Theo: Nope. We can find the edge ourselves.

At the instant a wiggly pair reaches the vertical axis, its real part is zero, so try s=iωs=i\omega in the cubic. Since i2=−1i^2 = -1 and i3=−ii^3 = -i, the real parts and the sideways parts must each add up to zero:

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

The second one says ω2=Kp/m\omega^2 = K_p/m. Put that into the first:

ω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}

Chapter 11: PID: Fixing Everything ↗

At the starting values (KpK_p = 20, no D) the edge is 40. That's exactly where Mika's drone went wild, and its yo-yo took about a second per swing: 6.32 rad/s is the P drone's own ωn from Chapter 7. Adding Kd=4K_d = 4 moves the edge out to 200. Don't trust us: change the numbers above, or the sliders below, and watch the poles, the mini-map and the step response.

Chapter 11: PID: Fixing Everything ↗

  • The I knob (KiK_i) brings a third pole into the loop and can push a pair towards the right. Too much and it crosses the line.
  • The D knob (KdK_d) raises the I-gain stability limit by adding damping. Pole paths can turn: more D does not move every pole left.
  • The P knob (KpK_p) mostly spreads the wiggly pair up and down: faster, but more wiggly, unless D keeps up.

The tuning playground

Now the real thing: take off from the ground to 2 m with real motors (0–20 N). Earn all four stars. Every time you change a gain, the last flight stays as a faint ghost.

Chapter 11: PID: Fixing Everything ↗

June: I switched anti-windup off, set KpK_p = 30, KiK_i = 60, KdK_d = 2… and the drone shot up to 4 m, right out of its picture!

Theo: The pile kept growing the whole time the motors were flat out. With anti-windup on, the same gains peak at 2.8 m. Same knobs, very different memory.

Two warnings about D

Warning 1: the derivative kick

The drone hovers at 1 m. At t=1t = 1 s we move the setpoint to 1.5 m. If D uses the slope of the error, then the error jumps by half a metre in an instant. Its slope is enormous, and so is the push.

Chapter 11: PID: Fixing Everything ↗

Chapter 11: PID: Fixing Everything ↗

Mika: On the error it asks for over 200 N? From motors that top out at 20?

June: And they can't say no politely. They slam flat out for a moment, and the drone gets a kick in the pants.

Taking the slope of the measured height instead fixes it: the drone can't teleport, so its height has no jumps. Same damping, no kick. Almost every real PID does this.

Warning 2: noise

Real height sensors jitter by a couple of centimetres. A couple of centimetres of jitter, a thousand times a second, has a huge slope. D amplifies it straight into the motors. The fix is to smooth the measurement before taking its slope (a filter with time constant τf\tau_f), at the cost of reacting a bit later.

Chapter 11: PID: Fixing Everything ↗

Chapter 11: PID: Fixing Everything ↗

Mika: Okay, D does not predict the future. It predicts whatever the noise felt like a millisecond ago.

What we fixed

What we figured out

  • P pushes in proportion to the error, but must leave a droop to hold the drone up.
  • I piles up past error; the only resting point is zero error, so it kills the droop, which dies with a time constant of about Kp/KiK_p/K_i. It adds lag, so too much makes things swing.
  • D pushes against speed: a virtual damper that adds to the drag cc, so ζ=(c+Kd)/(2mKp)\zeta = (c+K_d)/(2\sqrt{mK_p}). More D is not always better: overdamped, the drone creeps and the pile overfills.
  • The closed loop is ms3+(c+Kd)s2+Kps+Kim s^3 + (c + K_d)s^2 + K_p s + K_i; it's stable exactly when (c+Kd)Kp>mKi(c+K_d)K_p > mK_i.
  • Take D from the measurement (no kick) and filter it (less noise).

June tried our drone-style PID on the shower… and it wiggled exactly like we did in Chapter 0. What is it about delay?

Chapter 11: PID: Fixing Everything ↗