Tuning
Finding the gain where it oscillates, and working back from there to something usable.
Do this lesson in the simulatorPrint it: worksheet (PDF) · answers (PDF) · as a web page
Guide: PID control explained
Three gains, and turning knobs until it looks right is not a method. Here is one that is.
The Ziegler-Nichols method
Published in 1942, still in use, and it takes four minutes.
- Set
KiandKdto zero. - Raise
Kpuntil the system oscillates steadily: not growing, not dying. That gain is the ultimate gain,Ku. - Measure the period of that oscillation,
Tu, in seconds. - Read off the gains:
| Controller | Kp | Ki | Kd |
|---|---|---|---|
| P only | 0.5 Ku | ||
| PI | 0.45 Ku | 0.54 Ku / Tu | |
| PID | 0.6 Ku | 1.2 Ku / Tu | 0.075 Ku Tu |
Ziegler-Nichols is aggressive: it aims for a quarter amplitude decay, which means each swing is a quarter of the one before, which most people find too lively. It is a starting point, not a finishing one. Halving the resulting Kp is the usual first move, and on a long move the Ki it gives is often far too big as well, because the table assumes small upsets, not a big step that gives the integral time to wind up (challenge 1).
Finding Ku on this robot
This is done on the heading, a quarter turn on the spot with the loop looking four times a second, as in U5.2. The distance loop is no use for it: this robot is too slow for its own lag, and even at Kp = 32 it only shuffles a couple of centimetres on the sensor noise instead of oscillating.
from bugbot import *
connect()
DT = 0.25
def swing(kp, ticks=32):
target = heading() + 90 # a quarter turn from where it is now
errors = []
for tick in range(ticks):
error = (target - heading() + 180) % 360 - 180
errors.append(error)
plot("kp %s" % kp, error)
drive(0, 0, max(-100, min(100, kp * error)))
wait(DT)
stop()
wait(0.5)
late = errors[ticks // 2:] # the second half, after the first rush
return max(late) - min(late)
for kp in (2.0, 4.0, 6.0, 8.0, 10.0):
print("kp", kp, "swing", round(swing(kp), 1), "degrees")
The swing column stays small while the loop is well behaved and grows sharply once it is not. Ku is where that happens. Here it is 0 at 2, about 3 at 4, 9 at 6, then about 21 degrees at 8 and 25 at 10. On the chart the swing at 8 is dying away only slowly, and at 10 it holds steady at about 12 degrees either side, so Ku is about 8 to 10. The error flips sign every second or third look, and the time from one peak to the next, Tu, is about 1 s.
Tuning by hand, with the chart
In practice most tuning is done by looking at the shape of the response. The mapping from shape to fix is worth memorising:
| What the chart shows | What to change |
|---|---|
| slow, never arrives | raise Kp |
| arrives, settles short, stays short | add or raise Ki |
| overshoots and rings | lower Kp, or add Kd |
| noisy, twitchy command | lower Kd, or filter the measurement |
| slow wallowing oscillation | lower Ki |
| fast oscillation at the loop rate | lower Kp, or run the loop faster |
Do it in the right order
P first, then D, then I. Get a proportional gain that is fast without ringing. Add D to take the ring out. Add I last, and only as much as you need to remove the offset. Adding I early makes everything else harder to judge, because the offset you are trying to see keeps disappearing.
The gains are not universal
They depend on the plant, the loop rate, the filter, and the battery. A controller tuned on a full battery is a different controller at 60 percent. Serious systems either re-tune in flight, which is adaptive control, or are tuned conservatively enough to work across the range. A number that only works on a good day is not a tuning.
Task: find the ultimate gain
Run the heading loop at several gains, print the swing at each, and print ku: and tu:.
from bugbot import *
connect()
DT = 0.25
Challenges
- Apply the Ziegler-Nichols PID row and run it on the quarter turn. Is it as lively as the table warns, and which term is to blame?
- Halve the resulting Kp and compare the two responses. Then try cutting Ki instead.
- Find Ku again with the loop looking ten times a second, DT = 0.1. How much does Ku rise, and why?