Tracking a dog
Suppose your workplace is dog friendly, and dogs sometimes wander out of offices and down the halls. A sonar sensor on a dog’s collar emits a signal and listens for the echo. From the echo it can tell whether the dog is in front of an open doorway or a wall. It also senses when the dog walks and reports the direction. It sends an update once a second. You want to track a dog named Simon. The sensor reports “door”, “hall”, “hall”, and so on. How can you turn that stream into a position? To keep the problem small, assume the hallway has only 10 positions, numbered 0 to 9, where 1 is to the right of 0. Assume also that the hallway is circular: moving right from position 9 takes you to position 0. When you begin listening, you have no reason to believe Simon is at any particular position. Each of the 10 positions is equally likely, so each has probability 1/10. You store this belief in an array.Extracting information from sensor readings
Set the code aside for a moment. Suppose the sensor reports this sequence:- door
- move right
- door
Noisy sensors
Perfect sensors are rare. The sensor might miss a door if Simon sits in front of it to scratch himself, or misread if he is not facing down the hall. So when you read “door” you cannot give each door probability 1/3. You have to give each door a little less, and each wall a little more than zero, something likez. The letters and are the usual names for a measurement in the literature.
normalize.
“3 times as likely to be right as wrong” is an awkward way to state accuracy. It is more natural to give the probability that the sensor is correct and derive the scale factor from it:
The for loop can also go. Comparing the hallway with a value gives a boolean mask:
True. So the loop becomes
z are multiplied by scale. Here is the improved version:
belief[hall == z] *= scale, you computed how likely each position is given the measurement. A likelihood is not a probability distribution, because it need not sum to 1.
Together these give
When you talk about a filter’s output, the state after prediction is the prior or prediction. The state after the update is the posterior or estimated state. Most of the literature uses these terms, so they are worth learning well.
scaled_update() performs exactly this computation. Recast into that form, it reads:
Incorporating movement
With perfect sensors, a few readings and one move gave an exact answer. Can you get an exact answer with noisy sensors? No. Even if every reading matches a complicated hallway map perfectly, there is a tiny chance that every reading was wrong. In practice most readings are right, and you can get close to 100% sure, but never all the way. Start with the simple case. The movement sensor is perfect, and it reports that the dog moved one space to the right. How should the belief change? Shift every value one space to the right. If you were 50% sure Simon was at position 3, then after he moves right you are 50% sure he is at position 4. The hallway is circular, so the shift wraps around with modular arithmetic.Terminology
The system is what you are trying to model or filter. Here the system is the dog. The state is the system’s current configuration or value. In this section the state is the dog’s position. You rarely know the true state, so a filter produces an estimated state. In practice people often call that “the state”, so watch the context. One cycle of prediction and update is called the state or system evolution, short for time evolution [7]. Another name is system propagation. It describes how the state changes over time. For filters, time usually moves in discrete steps, such as 1 second. For the dog tracker, the state evolution is the dog’s position after one step of time. You model the system’s behavior with the process model. Here the process model says the dog moves one or more positions each time step. That is not a very accurate model of dogs. The error in the model is the system error or process error. The prediction is your new prior. Time has moved forward, and you predicted without the benefit of a measurement. Here is an example. The dog is at 17 m. The time step is 2 seconds, and the dog moves at 15 m/s. Where will he be in two seconds? A bar over a variable marks it as a prior (a prediction). You can write the process model as Here is the current position or state. If the dog is at 17 m then . The function is the state propagation function for . It describes how much changes over one time step. In the example it computes , so you would define it asAdding uncertainty to the prediction
perfect_predict() assumes perfect movement measurements, but every sensor has noise. What if the sensor said the dog moved one space, but he really moved two, or none?
Model it. Assume the movement measurement is right 80% of the time, overshoots by one position 10% of the time, and undershoots by one position 10% of the time. If the sensor reports a move of 4 spaces to the right, the dog moved 4 spaces with probability 0.8, 3 spaces with probability 0.1, and 5 spaces with probability 0.1.
Each entry of the new belief now combines three cases. Suppose the reported move is 2 and you are certain the dog started at position 3. Then he is at 5 with probability 0.8, and at 4 or 6 with probability 0.1 each.
Generalizing with convolution
So far the movement error is at most one position. It could be two, three, or more. You want one method that handles any error model. That method is convolution. Convolution modifies one function with another. Here it modifies a probability distribution with the error function of the movement sensor.predict_move() is already a convolution, under another name. Formally, convolution is
where means “convolve with ”. It does not mean multiply.
The integral is for continuous functions. For discrete ones, replace the integral with a sum and the parentheses with brackets:
predict_move() computes exactly this sum of products.
The idea is to slide an array called the kernel across the belief, multiply neighboring cells by the kernel values, and sum. The earlier example used 0.8 for moving the right amount, 0.1 for undershooting, and 0.1 for overshooting. As a kernel that is [0.1, 0.8, 0.1]. The code below loops over each element of the belief, multiplies its neighborhood by the kernel, and sums. The belief is named pdf to stress that it is a probability distribution.
offset and then does a circular (“wrap”) convolution with the kernel:
predict function used from here on.
Integrating measurements and movement updates
Losing information in every prediction might suggest the filter soon knows nothing. But each prediction is followed by an update that folds in a measurement, and the update improves your knowledge. The update’s output feeds the next prediction, which degrades certainty. That goes into the next update, which raises certainty again. Think about it intuitively. You track a dog that is sitting still. Each prediction says he does not move, and your filter quickly converges on his position. Then the kitchen microwave turns on and he races off. You do not know this, so your next prediction still says he is in the same spot. The measurements disagree. As you fold them in, your belief smears along the hallway toward the kitchen. Each cycle, your belief that he is sitting still shrinks, and your belief that he is heading for the kitchen grows. That is the intuition. Now check the math. You already have the predict and update steps. Feed each one’s output into the other and you have a dog tracker. The measurements below are what you would see if the dog started at position 0 and moved right one position per step. As in a real application, you start with no knowledge, so every position gets equal probability.The discrete Bayes algorithm
The algorithm is the predict and update cycle drawn at the start of this section. The filter equations are: is the usual symbol for the likelihood function. The notation means normalize: you scale the product of the likelihood and the prior so that is a probability distribution that sums to 1. In words:- Initialize your belief in the state.
- Predict. Use the system behavior to predict the state at the next time step, and widen the belief to account for the uncertainty in that prediction.
- Update. Get a measurement and a belief about its accuracy. Compute how likely each state is given the measurement, and multiply the prior by this likelihood.
- Repeat steps 2 and 3.
z_prob = 1.0 means. The next part looks at imperfect ones.
Note also how sharp the estimate becomes in front of a door, and how it softens in the middle of the hallway. There are only a few doors, so sensing a door says a lot about the position. A long stretch of wall says much less.
The effect of bad sensor data
So far every measurement was correct. A filter should also reject bad measurements. Does this one? To keep the figures readable, change the hallway to mostly alternating doors and walls, and run 6 correct measurements:Drawbacks and limitations
The examples are simple, but the filter is complete and robust. If you need a multimodal, discrete filter, this one works in real systems. It is still not used often, because it has several limitations. The first is scaling. The dog problem tracks one variable, the position. Most interesting problems track several variables in a large space. You would at least want the dog’s coordinates, and probably his velocity as well. In more dimensions the array becomes a grid that stores a probability at each discrete location. Everyupdate() and predict() touches every cell, so a four-variable problem costs per time step. Realistic filters can track 10 or more variables.
The second is that the filter is discrete, while the world is continuous. A 100 m hallway needs 10,000 positions for 1 cm resolution, so every predict and update works on 10,000 probabilities. It gets exponentially worse with more dimensions. A 100 m by 100 m courtyard needs 100,000,000 cells for 1 cm resolution.
The third is that the filter is multimodal. In the last example you ended up with strong beliefs that the dog was at one of two positions. That is not always a problem. Particle filters are multimodal too, and are often chosen for that reason. But imagine a car navigation system telling you it is 40% sure you are on D Street and 30% sure you are on Willow Avenue.
The fourth is that it needs a measurement of the change in state. You need a motion sensor to know how far the dog moved. There are ways around this, but they are beyond this section.
Even so, for a small problem that fits, this filter is a good choice. It is easy to implement, debug, and understand.
Tracking and control
So far you have passively tracked an object that moves on its own. Now consider a closely related problem. You are automating a warehouse and want robots to collect the items in each order. A simple design puts the robots on a track. You send a robot a destination and it drives there. But tracks and motors are imperfect. Wheels slip, and a robot rarely stops exactly where you commanded. There are several robots, and you need to know where each one is so they do not collide. So you add sensors. Perhaps you mount magnets along the track every few feet and use a Hall sensor to count the magnets the robot passes. If it counts 10 magnets, it should be at the 10th magnet. It might miss a magnet or count one twice, so you allow for some error. Counting magnets is very similar to sensing doors, so the hallway filter applies directly. There is one more source of information. You know the commands you send to the robot. Say that once a second you command it to move left 1 unit, move right 1 unit, or stay put. If you command “move left 1 unit”, you expect it to be 1 unit to the left a second later. (This ignores acceleration.) Wheels and motors are imperfect, so it might move 0.9 units, or 1.2. The dog filter assumed the dog keeps moving the way he was moving. That is a weak assumption for a dog. Robots are far more predictable. Instead of guessing the motion, you pass the commanded movement topredict(), together with a kernel that describes how likely each actual movement is.
Simulating the train
You need a simulated, imperfect train. When commanded to move, it sometimes makes a small mistake, and its sensor sometimes returns a wrong value.Bayes’ theorem and the total probability theorem
You built the math in this section by reasoning about the information available at each moment. Along the way you found Bayes’ theorem and the total probability theorem. Bayes’ theorem tells you how to compute the probability of an event given earlier information. Theupdate() function computes
That is Bayes’ theorem. Every Bayesian filter is an expression of it. A more formal treatment adds notation, but the idea stays this simple:
where means normalize.
You reached this by reasoning about a dog in a hallway, yet the same equation applies to a wide range of filtering problems, including the one-dimensional Kalman filter.
The predict() step computes the total probability of several possible events. In statistics this is the total probability theorem [6].
For now, the key point is that Bayes’ theorem is a rule for combining new information with existing information.
Summary
The code is short, yet it is a working Bayesian filter. You started with no information and extracted position from noisy sensors. The sensors in this section are quite noisy (most real sensors are more than 80% accurate), and the filter still converged quickly on the most likely position. The predict step always degrades your knowledge. Adding a measurement, even a noisy one, improves it, and the cycle converges. The one-dimensional Kalman filter uses different math but the same logic. It also forms estimates from measurements and a process model using Bayesian reasoning. If this section is clear to you, the Kalman filter will be too. The core idea carries over directly: multiply probabilities when you measure, and shift probabilities when you predict, and the estimate converges.References
- D. Fox, W. Burgard, and S. Thrun. “Monte Carlo localization: Efficient position estimation for mobile robots.” Journal of Artificial Intelligence Research, 1999. http://www.cs.cmu.edu/afs/cs/project/jair/pub/volume11/fox99a-html/jair-localize.html
- Dieter Fox et al. “Bayesian Filters for Location Estimation.” IEEE Pervasive Computing, September 2003. http://swarmlab.unimaas.nl/wp-content/uploads/2012/07/fox2003bayesian.pdf
- Sebastian Thrun. “Artificial Intelligence for Robotics.” https://www.udacity.com/course/cs373
- Khan Academy. “Introduction to the Convolution.” https://www.khanacademy.org/math/differential-equations/laplace-transform/convolution-integral/v/introduction-to-the-convolution
- Wikipedia. “Convolution.” http://en.wikipedia.org/wiki/Convolution
- Wikipedia. “Law of total probability.” http://en.wikipedia.org/wiki/Law_of_total_probability
- Wikipedia. “Time evolution.” https://en.wikipedia.org/wiki/Time_evolution
Found an error on this page? Report an issue.

