Speed and precision

Coasting, overshoot, and why fast and accurate pull in different directions.

1.5DrivingRobot club15 min

Do this lesson in the simulator

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)

Run this in the simulator

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)

Run this in the simulator

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}")

Run this in the simulator

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))

Run this in the simulator

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)

Run this in the simulator

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

  1. Get the gentle stop within 1 cm.
  2. Write precise_forward(cm) that does the fast-then-slow trick for any distance.
  3. Do the same for turns: precise_turn(deg).