Seeing the signal
plot(): reading a control loop as a chart instead of a column of numbers.
Do this lesson in the simulatorA control loop produces one number per tick. Printed, that is a column of digits scrolling past faster than anyone can read. Plotted, it is a shape, and the shape tells you what is wrong at a glance.
plot(name, value) adds one point to a chart under the console. Up to eight named lines, one call per line per tick.
from bugbot import *
connect()
def wrapped(a):
return (a + 180) % 360 - 180
while position()[1] < 110:
error = wrapped(0 - heading())
rotation = error * 3
plot("error", error)
plot("rotation", rotation)
drive(70, 0, rotation)
wait(0.1)
stop()
What to plot
For any feedback loop, three lines answer most questions:
| Line | What it tells you |
|---|---|
| the measurement | what the robot thinks the world is doing |
| the error, target minus measurement | whether the loop is winning |
| the command you sent | whether you are asking for more than the hardware can give |
Plot the command and you can see saturation: the error stays large while the command sits flat at 100, which means no gain in the world will fix it. That single plot has saved more time than any other in control engineering.
Reading the shapes
- Settles smoothly to zero. Good.
- Settles to something other than zero. A steady state error. It needs an integral term (U5.4).
- Overshoots, comes back, overshoots less. Under-damped. Gain a little high, or some delay in the loop.
- Grows. Wrong sign, or far too much gain.
- Sawtooth at the loop rate. The loop is fighting its own sampling. Slow the gain down or speed the loop up.
- Fuzzy band around zero. Sensor noise reaching the motors. Filter it (U4).
Plotting truth against belief
The chart is also the honest way to show an estimator working, which is most of Modules U6 and U7:
from bugbot import *
connect()
for tick in range(40):
forward(60)
wait(0.1)
plot("believed y", odometry()[1])
plot("true y", position()[1])
stop()
Two lines that start together and separate: that picture is what the whole of state estimation is trying to prevent.
Task: plot the error
Drive into the green zone under proportional control, plotting two lines as you go: error and rotation.
from bugbot import *
connect()
def wrapped(a):
return (a + 180) % 360 - 180
Challenges
- Plot
heading()andimu()[0]together. What is the gap, and is it constant? - Plot the same error with the gain at 1, 3 and 9, one run each, and describe the three shapes.
- Plot the distance to the wall ahead while driving towards it. Where does the noise become a problem?