Proportional control
Correct in proportion to the error. Gain, sign, feedback.
Do this lesson in the simulatorBang-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())}")
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())}")
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())
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
- Print the error every tick and watch it shrink.
- Hold a heading of 20 instead of 0, so the robot drives a straight diagonal.
- Replace
wait(0.1)withwait(0.5). What happens, and why?