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.
Do this lesson in the simulatorforward(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()))
Sixty centimetres forward, and the robot ends up sideways of where it started and facing a few degrees off. 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.
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)
Three things to notice in that table. It is roughly a straight line. It does not pass through the origin, because of the dead band. And the slope is this robot's, not 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
- Measure the sideways top speed and the top turn rate the same way.
- Find the dead band: the smallest command that moves the robot at all.
- 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.