Tuning

Finding the gain where it oscillates, and working back from there to something usable.

U5.5Feedback controlUniversity30 min

Do this lesson in the simulator

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.

  1. Set Ki and Kd to zero.
  2. Raise Kp until the system oscillates steadily: not growing, not dying. That gain is the ultimate gain, Ku.
  3. Measure the period of that oscillation, Tu, in seconds.
  4. 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")

Run this in the simulator

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

  1. Apply the Ziegler-Nichols PID row and run it. Is it as lively as the table warns?
  2. Halve the resulting Kp and compare the two responses.
  3. Tune the same loop with a 0.3 s period instead of 0.1 s. How much does Ku fall?