Q and R

The two numbers you actually choose, what they mean, and how to measure them.

U6.4State estimationUniversity30 min

Do this lesson in the simulator

Two numbers, and they are the whole of tuning a Kalman filter.

R: how noisy the measurement is

R is the variance of the measurement noise, in squared units of whatever you are measuring. It is measurable, and you have already measured it: U4.1 was exactly this.

sigma = 5 cm  ->  R = 25

Measure it, do not guess it. Stand the robot still, take a hundred readings, take the variance. If the sensor's noise changes with range or with surface, R can change per reading, and a filter that adjusts R when the measurement is known to be worse is doing something genuinely useful.

Q: how wrong the model can be

Q is the process noise: how much the state can change between ticks in ways your prediction does not know about. Slip, a bump, a gust, an unmodelled acceleration.

It is not directly measurable, and it is the number people fiddle with. Some ways to think about it:

  • If the model were perfect, Q would be zero, and the filter would eventually stop listening to measurements entirely. That is why Q = 0 is always wrong on a real machine.
  • Roughly, Q is the square of how far the state can drift in one tick without you knowing. A robot that can slip 0.5 cm in a tick: Q about 0.25.
  • Only the ratio Q/R matters for the gain. Doubling both changes nothing.
from bugbot import *
connect()

readings = []
for i in range(60):
    readings.append(distance())
    wait(0.1)
mean = sum(readings) / len(readings)
R = sum((v - mean) ** 2 for v in readings) / len(readings)
print("measured R:", round(R, 1), "(sigma", round(R ** 0.5, 2), "cm)")

for Q in (0.01, 1.0, 100.0):
    est, p = readings[0], 100.0
    for v in readings:
        p += Q
        k = p / (p + R)
        est += k * (v - est)
        p = (1 - k) * p
    print("Q", Q, "-> steady gain", round(p / (p + R), 3))

Run this in the simulator

The two failure modes

Q too small, or R too large. The filter trusts its model and ignores measurements. It is smooth, confident and wrong, and it stays wrong: the estimate wanders off and the filter reports a small variance the whole time. This is called filter divergence and it is the dangerous failure, because the filter's own statement of its uncertainty is the thing that is broken.

Q too large, or R too small. The filter trusts every measurement. The output is as noisy as the raw sensor and you have gained nothing but complexity.

Between them: the estimate should look smoother than the measurement and should still track the truth when the truth moves.

Checking a filter honestly

The test is the innovation, again. The filter predicts that measured - est should have variance p + R. So:

normalised = (measured - est) ** 2 / (p + R)

should average about 1. Much less than 1 means the filter is claiming more uncertainty than it has, and could be tuned tighter. Much more than 1 means it is claiming less uncertainty than it has, which is the dangerous direction. This ratio has a name, the normalised innovation squared, and printing its running average is the single most useful diagnostic a filter can carry.

Task: tune Q and R

Standing still, measure R and print it as measured r:. Then run the filter at two or three values of Q and print what each does.

from bugbot import *
connect()

readings = []

Challenges

  1. Compute the normalised innovation squared for your filter and check it is near 1.
  2. Set Q = 0 and drive the robot. Watch the estimate stop listening.
  3. Make R depend on range: double it beyond 100 cm. Does the estimate improve?