Choosing the filter

Matching the cut-off to the signal you care about, by measurement rather than by taste.

U4.6Noise and filteringUniversity25 min

Do this lesson in the simulator

Two forces, pulling opposite ways. More filtering means less noise and more delay. The right answer is not a matter of taste; it comes from the signal you are trying to see.

Ask what you are trying to keep

Every filter is a decision about which frequencies matter.

  • The signal here is the wall getting closer as the robot drives: a change over seconds.
  • The noise is a new random value every tenth of a second.

They are far apart in frequency, which is the happy case: a filter can remove most of the noise and hardly touch the signal. When the signal and the noise are at the same frequency, no filter can separate them, and no amount of tuning will change that. Recognising that case early saves a great deal of tuning.

A procedure that works

  1. Measure the noise with the robot still (U4.1). That is sigma.
  2. Decide how much noise you can live with in the controller's input. Usually: enough that the command it produces is not dominated by jitter.
  3. That ratio gives you the effective window, and alpha ~ 2 / (window + 1).
  4. Check the resulting delay against the system's time constant. If the delay is too big, the answer is not a better filter, it is a better sensor or a slower controller.

Measuring instead of guessing

Run several filters side by side on the same data. It costs almost nothing and it removes the argument.

from bugbot import *
connect()

ALPHAS = [0.05, 0.1, 0.3, 0.6]
state = {a: distance() for a in ALPHAS}
history = {a: [] for a in ALPHAS}

for i in range(80):
    raw = distance()
    for a in ALPHAS:
        state[a] = a * raw + (1 - a) * state[a]
        history[a].append(state[a])
    wait(0.1)

for a in ALPHAS:
    vals = history[a][20:]
    mean = sum(vals) / len(vals)
    spread = (sum((v - mean) ** 2 for v in vals) / len(vals)) ** 0.5
    print("alpha", a, "-> spread", round(spread, 2), "cm, lag", round((1 - a) / a * 0.1, 2), "s")

Run this in the simulator

Two columns: noise left, delay right. Choosing a filter is choosing a row of that table, and the choice belongs to whatever is going to use the number.

The standing robot is only half the test

The table above was measured standing still, so smaller spread always looks better and alpha 0.05 wins every time. Run the same comparison while driving at the wall and the slowest filter is visibly wrong: it reports a distance the robot passed a second ago.

Always test a filter on the motion you actually care about. A filter tuned on a stationary robot is tuned for a robot that never moves.

Task: tune the filter

Standing still, run at least three alphas at once, print the spread each one leaves, and print best alpha:.

from bugbot import *
connect()

ALPHAS = [0.05, 0.1, 0.3, 0.6]

Challenges

  1. Repeat the comparison while driving at the wall and say which alpha wins now.
  2. Add a column for how far the robot travelled during each filter's delay, at 20 cm/s.
  3. Filter the flow sensor instead and pick an alpha for use in odometry. Does the answer differ, and why?