Tuning
Finding the gain where it oscillates, and working back from there to something usable.
Do this lesson in the simulatorThree 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 a common and sensible next move.
Finding Ku on this robot
from bugbot import *
connect()
TARGET = 40.0
def swing(kp, ticks=80):
errors = []
for tick in range(ticks):
error = distance() - TARGET
errors.append(error)
plot("kp %s" % kp, error)
drive(max(-80, min(80, kp * error)), 0, 0)
wait(0.1)
stop()
wait(0.4)
late = errors[ticks // 2:]
return max(late) - min(late)
for kp in (2.0, 5.0, 9.0, 14.0):
print("kp", kp, "swing", round(swing(kp), 1), "cm")
The swing column stays small while the loop is well behaved and grows sharply once it is not. Ku is where that happens.
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 loop at several gains, print the swing at each, and print ku: and tu:.
from bugbot import *
connect()
TARGET = 40.0
DT = 0.1
Challenges
- Apply the Ziegler-Nichols PID row and run it. Is it as lively as the table warns?
- Halve the resulting Kp and compare the two responses.
- Tune the same loop with a 0.3 s period instead of 0.1 s. How much does Ku fall?