The feedback loop

Setpoint, error, controller, plant, measurement: the block diagram and the names.

U5.1Feedback controlUniversity25 min

Do this lesson in the simulator

Every controller in this module is the same five boxes. Learning their names makes the literature readable, and most of the literature is worth reading.

setpoint --->(+/-)---> controller ---> actuator ---> plant ---> output
                ^                                                |
                |------------------ sensor ----------------------|
Name On this robot
setpoint the gap you want: 20 cm
measurement distance(), filtered
error setpoint minus measurement
controller the arithmetic that turns error into a number
actuator drive(), saturating at 100, dead below 15
plant the robot and the mat, with a time constant of about 0.25 s

Open loop is setpoint to actuator with nothing coming back. It works when the plant is perfectly known and nothing disturbs it, which is never. Closed loop is the diagram above, and it is why a robot can drive to a point on a drive as ill behaved as this one.

The first loop

from bugbot import *
connect()

TARGET = 20.0
for tick in range(200):
    error = distance() - TARGET
    plot("error", error)
    drive(max(-50, min(50, 2.0 * error)), 0, 0)
    wait(0.1)
stop()
print("ended", round(distance(), 1), "cm from the wall")

Run this in the simulator

The error starts large and positive, the command is large and forward, the robot approaches, the error shrinks, the command shrinks with it. Negative feedback.

It does not quite arrive. The error levels off at about 5 cm and the robot ends about 26 cm from the wall, not 20. Once the error is under 7.5 cm, 2.0 * error asks for less than 15 percent, and this drive does not move below 15. That leftover gap has a name, and U5.2 is about it.

The error of the first loop over twenty seconds, falling fast, then slowly, then levelling off above zero0510152007.52550time, secondserror, cm5.4 s7.6 scommandclamped at 50command =2 × errorunder 15 %:the drive does nothing
The first loop's error. Above 25 cm the command is stuck at its clamp of 50, so the error falls at a steady rate. Below that it eases off with the error. At 7.6 s the error drops under 7.5 cm, 2 × error asks for less than 15 percent, and it levels off at about 5 cm instead of reaching zero.

The sign

error = measurement - setpoint or setpoint - measurement? Both conventions exist, and the difference is the sign of every gain in the controller.

Pick one, write it in a comment, and check it against the physical case: too far away should mean drive forwards. Here distance() - TARGET is positive when the robot is too far from the wall, and positive drive() means forwards. Consistent.

Get it backwards and the robot accelerates away from the target as fast as the gain allows, which is at least unmistakable.

Where the target is and which way the robot drives, with the error worked out both ways roundwalltarget, 20 cm outerror = distance() − TARGET = 90 − 20 = +70positive, so drive() goes forwards, towards the targeterror = TARGET − distance() = 20 − 90 = -70negative, so drive() goes backwards, away from itdistance() = 90 cm
The robot at the start, 90 cm from the wall. With the error as distance minus target it is positive, and a positive gain drives forwards, which is right. Swap the two round and the same gain sends it backwards, away from the target.

What makes a good loop

Four questions, and every controller is judged by them:

  • Stability. Does it settle, or does it oscillate for ever?
  • Accuracy. Does it end up where it was asked, or near it?
  • Speed. How long to get there?
  • Effort. How hard is it working, and is it saturating?

Those four fight. More gain buys speed and accuracy and spends stability. Filtering buys smoothness and spends speed. There is no setting that wins all four, and a large part of control engineering is knowing which one you are allowed to give up.

Task: close the loop

Settle 20 cm from the wall, plotting the error as you go. The wall's face is 150 cm up the mat and the robot starts at 60.

from bugbot import *
connect()

TARGET = 20.0

Challenges

  1. Reverse the sign of the gain and describe what happens, in one sentence, before you run it.
  2. Start the robot closer than the target. Does the same loop push it back out?
  3. Put a filter on distance() and see whether the loop is smoother, slower, or both.