Speed and precision
Coasting, overshoot, and why fast and accurate pull in different directions.
Do this lesson in the simulatorPrint it: worksheet (PDF) · answers (PDF) · as a web page
Guide: PWM and motor control explained
Fast robots overshoot. Slow robots take forever. Every real robot, from a warehouse trolley to a Mars rover, lives with this trade. In this lesson you measure it, see how the robot deals with it, and learn the standard trick for getting both.
Coasting
Drive by time and stop, and see how far the robot slides after stop():
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
for speed in [30, 60, 100]:
forward(speed)
# pause 2 s (the robot keeps doing what it was told)
wait(2)
x, y_at_stop = position()
# all motors off
stop()
# pause 1 s (the robot keeps doing what it was told)
wait(1)
x, y_at_rest = position()
print(f"speed {speed}: slid {round(y_at_rest - y_at_stop, 1)} cm after stop()")
backward(60, distance=y_at_rest)
At full speed the robot slides about 4 cm after the motors switch off (4.1); at 30 it slides about 1 cm (1.2), and at 60 in between (2.5). Faster means more coasting, in proportion: twice the speed, about twice the slide.
Why: momentum
When the motors stop, the robot is still moving, and the mat's friction takes a moment to stop it. The distance it slides depends on how fast it was going. Timing a drive can never be precise, because the timing does not know about the slide.
The robot allows for it
distance= is different. The robot measures how far it has gone, knows how fast it is moving, and switches the motors off early, by the amount it expects to slide:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
for speed in [30, 60, 100]:
forward(speed, distance=20)
# where am I? (cm from where I started)
x, y = position()
print(f"speed {speed}: asked 20, got {y}")
backward(60, distance=y)
All three land within a millimetre of 20 (19.9, 20.0 and 20.0). The prediction of the slide is good, but it is still a prediction, and the faster you go the more slide there is to predict: at 100 it is 4 cm to allow for, at 30 barely 1. On its own, forward(100, distance=20) ends at 20.2.
The trick: fast, then slow
When the last centimetre matters, do most of the distance fast and the last part slowly:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# fast for most of the way
forward(100, distance=15)
# slow for the last bit
forward(25, distance=5)
# where am I? (cm from where I started)
x, y = position()
print(f"asked 20, got {y}")
It takes 3.3 s, against 2.0 s flat out and 5.0 s at 25 all the way, and it lands on 20.0 where flat out gave 20.2: the finish is at the slow speed's accuracy. The longer the drive, the more time the fast part saves. Real robots do exactly this: a fast approach and a slow final approach.
Turning has the same problem
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# turn 90 degrees clockwise at speed 100
turn_right(100, angle=90)
print("fast turn:", round(heading(), 1))
# turn 90 degrees anticlockwise at speed 100
turn_left(100, angle=90)
# turn 90 degrees clockwise at speed 20
turn_right(20, angle=90)
print("slow turn:", round(heading(), 1))
Turning takes a speed as its first argument. A fast turn overshoots by about a degree; a slow one by a fraction of that. Over a long route those degrees add up.
Measure it yourself
Real engineering starts with measuring. This cell drives the same distance at several speeds and collects the errors in a list:
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
errors = []
for speed in [20, 40, 60, 80, 100]:
forward(speed, distance=20)
# where am I? (cm from where I started)
x, y = position()
errors.append(round(y - 20, 1))
backward(50, distance=y)
print("errors by speed:", errors)
Task: gentle stop
Stop with the robot's centre within 2 cm of the small square 50 cm ahead, having driven at least 40 cm. The starter times the drive; timing is never that accurate.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
# drive forward at 100 (keeps going until the next command)
forward(100)
# pause 2.5 s (the robot keeps doing what it was told)
wait(2.5)
# all motors off
stop()
Challenges
- Get the gentle stop within 1 cm.
- Write
precise_forward(cm)that does the fast-then-slow trick for any distance. - Do the same for turns:
precise_turn(deg).