Tracking a dog in a hallway
As in the discrete Bayes filter section, you track a dog that moves along a long hallway. A sensor reports the dog’s distance from the left end of the hallway in meters. A reading of 23.4 means the dog is 23.4 m from the left end. The sensor is not perfect. A reading of 23.4 could come from a dog at 23.7 or at 23.0. It is very unlikely to come from a dog at 47.6. Tests show that the errors are small and spread evenly on both sides of the true position: a dog at 23 m is as likely to read 22.9 as 23.1. A Gaussian is a natural model for this error. You also predict that the dog moves. That prediction is not perfect either. Sometimes it overshoots and sometimes it undershoots, and small misses are more likely than large ones. A Gaussian models this error too.Beliefs as Gaussians
You can express your belief about the dog’s position with a Gaussian. Say you believe the dog is at 10 m, and the variance of that belief is 1 m. You write this belief as . Its probability density function (pdf) isTracking with Gaussian probabilities
The discrete Bayes filter used a histogram of probabilities to track the dog. Each bin of the histogram is a position, and its value is the probability that the dog is there. Tracking was a cycle of predictions and updates: Here is the prior, is the likelihood of the measurement given the state, is the process model, and denotes convolution. is bold because it is a histogram of numbers, that is, a vector. This method works, but its histograms can imply that the dog is in several places at once. It is also slow for large problems. Can you replace the histogram with a Gaussian ? Yes. A Gaussian is a single pair of numbers, and it can stand in for an entire histogram of probabilities:Predictions with Gaussians
Newton’s equation of motion gives the current position from the previous position and the velocity: The notation replaces to keep the equations short. If the dog is at 10 m, its velocity is 15 m/s, and the time step is 2 seconds long, then You are uncertain about both the current position and the velocity, so a single number will not do. You need to express that uncertainty with Gaussians. Position is easy. You define as a Gaussian. If you think the dog is at 10 m with a standard deviation of 0.2 m, then . The movement is a Gaussian too. If the dog’s velocity is 15 m/s, the time step is 1 second, and the standard deviation of your uncertainty is 0.7 m/s, then . The prior is The sum of two independent Gaussians is another Gaussian, with For the numbers above, and their sum is The predicted position is the previous position plus the movement, which makes sense. The variance is harder to build intuition for. Recall that the predict step of the discrete Bayes filter always lost information. You do not know exactly how the dog moves, so your confidence should drop and the variance should grow. is the uncertainty that the imperfect motion prediction adds, so you add it to the uncertainty you already have. In code, a Gaussian is a pair of numbers: a mean and a variance. Giving the two fields names lets you writeg.mean and g.var instead of g[0] and g[1].
pos and movement are Gaussians:
Updates with Gaussians
The discrete Bayes filter stores its belief about the dog’s position in a histogram of probabilities. That distribution is discrete and can be multimodal: it can express a strong belief that the dog is in two places at once. You now want to replace the histogram with a Gaussian. The discrete Bayes filter computed the posterior with this code:gaussian_multiply() that implements this product, the update step of the filter is one line:
update(prior, likelihood) keeps in view that the update multiplies the likelihood by the prior.
You now have most of a filter. The claim that multiplying Gaussians performs the update deserves a closer look, so the next part works through a few products.
Understanding Gaussian multiplication
Consider . Before you look at the result, try to predict it. What is the new mean? Is the curve wider, narrower, or the same as ?Your first Kalman filter
You havepredict() and update(). To try them you need a simulated dog and its measurements. The DogSimulation class below moves the dog by its velocity plus Gaussian process noise at each time step. It then returns the true position plus Gaussian sensor noise.
Code walkthrough
prior is , as the printed table shows. After the prediction you believe the dog is at 1.0, and the variance has grown from 400 to 401. The prediction always increases the variance, because it loses information.
Then the update combines this prior with the first measurement. The prior variance of 401 m means you have almost no confidence in the prior. So the posterior mean lands very close to the measurement.
The posterior variance is 1.990 m, down from 401 m. The sensor variance is only 2.0 m, so you trust it far more than the prior. The prior still carries a little information, so the posterior variance is slightly smaller than 2.0.
The loop then alternates predict() and update(). By the end, the estimate is close to the true final position, and the posterior variance has converged to 1.0 m.
The filtering itself takes three lines:
predict and update functions the loop would read:
Exercise: modify variance values
Changeprocess_var and sensor_var and note the effect on the filter and on its variance. Which has the larger effect on the converged variance? For example, which of these settings gives the smaller variance?
Solution
The cell below runs the filter with both settings on the same random seed and reports the converged posterior variance.The belief over time
The next figure draws the posterior Gaussian of the 25-step run at several time steps. At the first step the belief is low and wide, so the filter moves its estimate a long way toward the measurement. As the steps go by, the Gaussian becomes taller and narrower, and the filter’s output settles into a nearly straight line. After the variance converges, a single noisy measurement moves the estimate much less than the first measurement did.Kalman gain
The filter works. Now return to the math to see why. The posterior is the likelihood times the prior (), and both are Gaussians. So the mean of the posterior is where the subscript denotes the measurement. You can rewrite this as In this form the measurement and the prior are each scaled by a weight: The weights sum to one because the denominator normalizes them. Call the first weight : where is the Kalman gain, the heart of the Kalman filter. It is a scaling term that picks a value partway between and . Work a few examples. If the measurement is nine times more accurate than the prior, then , and So : the posterior takes nine tenths of the measurement and one tenth of the prior. If the measurement and the prior are equally accurate, then and which is the average of the two means. Taking the average of two equally accurate values makes sense. You can also write the variance in terms of the Kalman gain: The chart below shows one step of the filter. The prediction moves the previous estimate forward. The residual is the difference between the measurement and the prediction. The new estimate lies on the residual, a fraction of the way from the prediction to the measurement.update() and predict():
Full description of the algorithm
The Kalman filter makes a prediction, takes a measurement, and forms a new estimate somewhere between the two, exactly as in the residual chart above. Every filter in this family implements the same algorithm with different mathematical details. The math can become hard, but the idea is easy to state. Look past the details of a specific filter’s equations and ask what they compute and why. There are many filters. Their choice of math affects the quality of the results and which problems they can represent, but not the underlying idea. Here is the generic algorithm. First, initialize the state of the filter and your belief in that state. Then repeat two steps. The predict step:- Use the system behavior to predict the state at the next time step.
- Adjust the belief to account for the uncertainty in the prediction.
- Get a measurement and a belief about its accuracy.
- Compute the residual between the estimated state and the measurement.
- Compute a scaling factor based on whether the measurement or the prediction is more accurate.
- Set the state between the prediction and the measurement, according to the scaling factor.
- Update the belief in the state based on how certain you are of the measurement.
Comparison with g-h and discrete Bayes filters
The three filters differ in how they model errors. A g-h filter blends a prediction and a measurement with fixed weights. It treats each measurement as a value with an error bar, as in this graph:Designing a filter for a thermometer
So far you have filtered a position sensor. To get a feel for designing a filter for a different problem, build one for a thermometer. The sensor outputs a voltage that corresponds to the measured temperature. The manufacturer’s specification says that the sensor has white noise with a standard deviation of 0.13 volts. This function simulates a sensor reading:volt() and pass the result to update(). But what about the prediction? There is no sensor for “movement” of the voltage, and over a short time you expect the voltage to stay constant.
Trust the math. There is no known movement, so set it to zero. But a zero-movement prediction with zero uncertainty says the temperature never changes. The filter would then grow ever more confident and eventually ignore measurements caused by a real temperature change. This is called a smug filter, and you want to avoid it. So add a little error to the prediction step, which tells the filter not to discount changes in voltage over time. The code sets process_var = 0.05**2, the expected variance of the change in voltage over each time step. For a real sensor you would set it to the change you actually expect: a very small number for a thermometer measuring room air, and a much larger one for a thermocouple in a chemical reaction chamber.
The loop also records the Kalman gain at each step.
Example: extreme amounts of noise
The dog filter above used modest noise and guessed that the dog started at 0. How does the filter cope with harsher conditions? Start by adding far more noise to the sensor while keeping the process variance at 2 m. The sensor now has a standard deviation of 300 meters. An actual position of 1.0 m might be reported as 287.9 m, or as m. What do you expect the filter to do?Example: incorrect process variance
The last result looks excellent. So why not always set the process variance very low, to guarantee a smooth, straight output? The process variance tells the filter how much the system changes over time. If you set it artificially low, the filter cannot react to changes that really happen. In the next run the dog speeds up by a small amount at every time step, while the filter uses a process variance of 0.001 m.Example: bad initial estimate
Now look at what happens with a bad initial estimate of position. To keep the picture clear, the sensor variance drops to , but the initial position is set to 1000 meters. Can the filter recover from a 1000 meter error?Example: large noise and bad initial estimate
What about the worst of both worlds, large noise and a bad initial estimate?Exercise: a parameter study
Write a function that simulates the dog from a start position, sensor noise, velocity and process noise, runs the Kalman filter on the measurements, and returns the measurements and the estimates. Then compare a few settings side by side to see how each parameter changes the filter’s behavior.Solution
The function below does that. Process noise models how much the dog wanders off course at each time step, so the simulation adds it to the dog’s position at every step. The filter always starts from , whatever the true start position. Each run uses the same random seed, so the panels differ only in the parameters.Exercise: nonlinear systems
The Kalman filter equations are linear: Do you expect this filter to work well or poorly on a nonlinear system? Implement a Kalman filter whose measurements come fromSolution
Discussion
The result is poor. The output looks nothing like a sine wave, except in the crudest way. With linear systems you could add extreme noise and still extract an accurate result, but here even modest noise gives a bad result. The filter always chooses a value partway between its prediction and the measurement. A signal like this one is always accelerating, while the process model assumes a constant velocity, so the filter is guaranteed to lag the signal. Practitioners noticed the poor performance of Kalman filters on nonlinear systems soon after they began using them, and devised extensions such as the extended and unscented Kalman filters to deal with it.Fixed-gain filters
Embedded computers often have very limited processors, and many lack floating point hardware. Even these simple equations can be a burden. That matters less as hardware improves, but do not underestimate the value of saving one dollar per processor when you buy millions of them. In the examples above, the variance of the filter converged to a fixed value. This always happens when the measurement and process variances are constant. If the variance converges, so does the Kalman gain. You can find the converged gain once, offline, and hard-code it. As long as you start the filter from a good initial guess, such as the first measurement, it performs very well. The next cell finds the steady-state gain of the 25-step dog filter by iterating the variance recursion. It then defines a fixed-gain filter that never touches a variance.FilterPy’s implementation
FilterPy implementspredict() and update(). They work for the univariate case in this section and for the multivariate case, so their interface is slightly different from the functions above. They do not take Gaussians as pairs. They take the mean and the variance as two separately named arguments.
predict() takes several arguments, but you need only these four:
x is the state of the system, P is its variance, u is the movement due to the process, and Q is the process noise. Use named arguments when you call predict(), because most of its arguments are optional. The third positional argument of predict() is not u.
These names may strike you as terrible. They come from the long history of control theory, and every paper and book uses them, so it pays to learn them.
Try it on the state and the movement . You expect a position of 11 () with a variance of 7 ().
update() also takes several arguments. For now you need these four:
x and P are the state and its variance. z is the measurement and R is the measurement variance. Run the predict step again to get the prior, and then update it with a measurement:
x, not prior. The Kalman filter equations use for both. Both the prior and the posterior are estimates of the state of the system. The prior is the estimate before the measurement is incorporated, and the posterior is the estimate after.
Summary
The one-dimensional Kalman filter is a special, restricted case of the multivariate filter. Many texts skip it, but it is a valuable stepping stone. It shows that the g-h filter, the discrete Bayes filter and the Kalman filter all use the same algorithm and the same reasoning: predict, measure, and choose a value between the prediction and the measurement. The multivariate filter uses more sophisticated math, which can hide that simplicity, but in exchange it performs markedly better than the filters in this section. Gaussians are a good representation of a unimodal belief, whether that is the position of a dog in a hallway, the position of an aircraft in the sky, or the temperature of a chemical reaction chamber. Multiplying Gaussians computes a new belief from the prior belief and a new measurement. Adding Gaussians updates the belief when the state moves. Spend time with the full description of the algorithm until you see how it relates to the g-h filter and the discrete Bayes filter. Each algorithm performs the same step, choosing a value between a prediction and a measurement, with different math.Found an error on this page? Report an issue.

