A command is a request

Open loop on real hardware: gain that is not 1, a dead band, a lag, and a robot that curves.

U1.2The robot as a systemUniversity25 min

Do this lesson in the simulator

forward(60) does not mean "go forward at 60 cm/s". It means "drive the motors at 60 percent". What the robot then does is a fact about the hardware, not about your program.

The BugBot moves by four vibration motors pressed against a silicone mat. It is a cheap, odd, thoroughly real drive, and it behaves like one.

What actually happens

from bugbot import *
connect()

forward(60, distance=60)
stop()
x, y = position()
print("asked for 60 cm forward, got x =", x, " y =", y, " heading", round(heading()))

Run this in the simulator

It prints x = -1.1 y = 60.1 heading 358: sixty centimetres forward, and the robot has also slid a centimetre to the left and turned two degrees. Over a longer run that grows. Nothing is broken. Four things are going on, and all four are in every real machine:

  • Gain. The forward motors are not identical to the sideways ones, and no motor is exactly at its nominal strength. Command 100 gives this robot its own top speed, not the catalogue's.
  • Dead band. Below about 15 percent the vibration drive does not move the robot at all. It buzzes and stays put.
  • Lag. Speed does not step, it rises. This drive takes roughly a quarter of a second to get most of the way to the speed you asked for.
  • Coupling. Driving forward leaks a little sideways and a little rotation. The robot curves.
Speed against time after forward(60) and forward(10), each against the speed a perfect drive would give at once00.250.511.504812time after the command, secondsspeed, cm/sa perfect drive: 12forward(60)rises, and settles lowera perfect drive: 2forward(10): nothing
All three at once. forward(60) takes a quarter of a second to get most of the way up, then settles below the 12 cm/s a perfect drive would give. forward(10) is inside the dead band and does not move the robot at all.

Measuring instead of assuming

flow() reads the optical flow sensor underneath: how fast the mat is actually sliding past, in centimetres per second, along the robot's own right and forward axes. That is a measurement of the drive, not of your command.

from bugbot import *
connect()

for power in (20, 40, 60, 80, 100):
    forward(power)
    wait(1.0)                       # let it settle: the lag is about a quarter of a second
    readings = []
    for i in range(10):
        readings.append(flow()[1])
        wait(0.1)
    print(power, "->", round(sum(readings) / len(readings), 1), "cm/s")
    stop()
    wait(0.5)

Run this in the simulator

20 -> 3.6 cm/s
40 -> 7.0 cm/s
60 -> 11.4 cm/s
80 -> 14.7 cm/s
100 -> 18.7 cm/s

Three things to notice in that table. It is roughly a straight line, and it heads for the origin: about 0.19 cm/s per percent. It stops being a line below the table, where the dead band is: command 10 gives nothing at all, 15 gives about 3 cm/s. And the slope is this robot's, not the model's: 18.7 cm/s at 100, not 20.

The settled speed for each command, with the dead band below 15 and the model's straight line0152040608010005101520command, percentspeed, cm/sdeadbandthe model: 20 at 100this robot, measurednothingat 10
The lesson's measurements, with 10 and 15 added. Below 15 percent the robot does not move. Above it the points lie close to a straight line, but under the model's line, because this robot's motors are a little weaker than the model's.

Why this matters

Open loop means acting on the command and hoping. It works when the machine is stiff, repeatable and unloaded, which describes almost nothing. Every technique in the rest of this course exists because the command is not the result:

  • measure what happened (flow(), imu(), the camera) rather than assume;
  • correct as you go, which is feedback (U5);
  • and keep an estimate of the state rather than a position you once commanded (U6).

Task: how fast is this robot?

Measure this robot's top forward speed in centimetres per second and print it as top speed: 18.4. Measure it, do not look it up.

from bugbot import *
connect()

forward(100)
wait(1.0)

Challenges

  1. Measure the sideways top speed and the top turn rate the same way.
  2. Find the dead band: the smallest command that moves the robot at all.
  3. Fit a straight line to the table above (speed against command) and use it to predict the command for 12 cm/s. Test the prediction.