Seeing the signal

plot(): reading a control loop as a chart instead of a column of numbers.

U1.5The robot as a systemUniversity20 min

Do this lesson in the simulator

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

Run this in the simulator

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

Run this in the simulator

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

  1. Plot heading() and imu()[0] together. What is the gap, and is it constant?
  2. Plot the same error with the gain at 1, 3 and 9, one run each, and describe the three shapes.
  3. Plot the distance to the wall ahead while driving towards it. Where does the noise become a problem?