Controlling speed
Speed from the remaining distance, the dead band, clamping.
Do this lesson in the simulatorSo far the controller steered. It can just as well decide how fast to go. This lesson parks the robot at an exact distance from a wall by making the speed depend on how far there is still to go.
The bang-bang park
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# as long as the thing ahead is further than 22 cm
while distance() > 22:
# drive forward at 60 (keeps going until the next command)
forward(60)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
# pause 0.5 s (the robot keeps doing what it was told)
wait(0.5)
print("stopped", distance(), "cm from the wall")
Fast all the way and stop dead: the robot coasts past 22 and ends closer. Same problem as lesson 3.1, sideways.
Proportional speed
Speed in proportion to the remaining distance: 40 cm to go, drive fast; 4 cm to go, crawl.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
target = 22
# do this 100 times (tick counts from 0)
for tick in range(100):
error = distance() - target
if abs(error) < 1:
# leave the loop
break
# gain 4
speed = error * 4
forward(speed)
# pause 0.05 s (the robot keeps doing what it was told)
wait(0.05)
# all motors off
stop()
# pause 0.5 s (the robot keeps doing what it was told)
wait(0.5)
print("stopped", distance(), "cm from the wall, last speed asked for:", round(speed))
It gets close and then stalls short of the target, asking for a speed that does nothing.
The dead band
The vibration motors do nothing below about 15. A proportional controller asks for 8, then 6, then 4, and the robot just sits there. Real actuators all have a dead band like this, and the fix is to never ask for less than the minimum:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
target = 22
# again and again, for ever
while True:
error = distance() - target
if abs(error) < 1:
# leave the loop
break
# never below 16, never above 60
speed = max(16, min(60, abs(error) * 4))
if error > 0:
forward(speed)
else:
backward(speed)
# pause 0.05 s (the robot keeps doing what it was told)
wait(0.05)
# all motors off
stop()
# pause 0.5 s (the robot keeps doing what it was told)
wait(0.5)
print("stopped", distance(), "cm from the wall")
max(16, min(60, value)) is called clamping: it keeps a number between two limits. You will write it a lot. And because the loop checks the sign, the robot backs up if it went too far.
The pattern
Every P controller in this module has the same shape. Learn it once:
error = target - measured
output = clamp(error * gain)
apply(output)
Steering: measured is the heading, apply is the rotation. Speed: measured is the distance, apply is forward or backward. Same loop, different sensor and motor.
Task: park at twenty
Stop with the front of the robot 20 to 24 cm from the wall, the green band, without touching the wall.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# as long as the thing ahead is further than 22 cm
while distance() > 22:
# drive forward at 60 (keeps going until the next command)
forward(60)
# pause 0.1 s (the robot keeps doing what it was told)
wait(0.1)
# all motors off
stop()
Challenges
- Park at 40 cm, then at 10 cm, with the same loop and a different
target. - Print the speed every tick and watch it fall as the robot approaches.
- Start the robot closer than the target (edit the loop to drive there first) and check it backs up.