Proportional control

Correct in proportion to the error. Gain, sign, feedback.

3.2ControlRobot club15 min

Do this lesson in the simulator

Bang-bang goes at one speed whether the error is 160 degrees or 3. A better controller pushes in proportion to the error: hard when far off, gently when close. It is called a proportional controller, P for short, and it runs inside almost every machine you own.

The robot that curves

This lesson's robot has a fault: whenever it drives, it turns to the right. Watch:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

# drive forward at 70 for 60 cm, then stop
forward(70, distance=60)
# where am I? (cm from where I started)
x, y = position()
print(f"ended at x={x}, facing {round(heading())}")

Run this in the simulator

Sixty centimetres forward, and it is 15 cm off the line and facing 30 degrees off. Every robot has a version of this. Yours is a bad one on purpose.

Steer by the error

drive(forward, sideways, rotation) can turn while driving. Set the rotation from the heading error, each time round the loop:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

def wrapped(h):
    return (h + 180) % 360 - 180

while position()[1] < 60:
    # positive: we need to turn right
    error = wrapped(0 - heading())
    # rotation in proportion to the error
    drive(70, 0, error * 4)
    # pause 0.1 s (the robot keeps doing what it was told)
    wait(0.1)
# all motors off
stop()
# where am I? (cm from where I started)
x, y = position()
print(f"ended at x={x}, facing {round(heading())}")

Run this in the simulator

Same robot, same distance, and now it ends facing nearly 0. The 4 is the gain: how much rotation per degree of error. Four degrees off means rotation 16; half a degree off means rotation 2, so the correction fades away as the error does. No overshoot, no bursts, no settling.

The sign

Get the sign wrong and the controller pushes the error bigger instead of smaller. Try it:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

def wrapped(h):
    return (h + 180) % 360 - 180

# do this 30 times (tick counts from 0)
for tick in range(30):
    error = wrapped(0 - heading())
    # wrong sign
    drive(70, 0, -error * 4)
    # pause 0.1 s (the robot keeps doing what it was told)
    wait(0.1)
# all motors off
stop()
print("after 3 seconds: facing", round(heading()), "at", position())

Run this in the simulator

A controller with the wrong sign runs away. When yours does something wild, check the sign first.

Why it is called feedback

The heading comes out of the robot, goes into your loop, and comes back to the motors as a correction. Output fed back to input: a feedback loop. Open loop hopes; closed loop checks.

Task: hold the heading

Drive into the green zone and finish still facing 0, within 6 degrees, on the curving robot.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

# drive forward at 70 for 70 cm, then stop
forward(70, distance=70)

Challenges

  1. Print the error every tick and watch it shrink.
  2. Hold a heading of 20 instead of 0, so the robot drives a straight diagonal.
  3. Replace wait(0.1) with wait(0.5). What happens, and why?