Controlling speed

Speed from the remaining distance, the dead band, clamping.

3.4ControlRobot club15 min

Do this lesson in the simulator

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

Run this in the simulator

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

Run this in the simulator

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

Run this in the simulator

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

  1. Park at 40 cm, then at 10 cm, with the same loop and a different target.
  2. Print the speed every tick and watch it fall as the robot approaches.
  3. Start the robot closer than the target (edit the loop to drive there first) and check it backs up.