# Kalman Filter

A recursive estimator combining a dynamic model with noisy measurements to track a system's state optimally under linear-Gaussian assumptions.

**Rests on:** Recursive Thinking

*Mathematical Foundations · Numerical Methods*


## Why learn this

You need the Kalman filter to track the position of something moving—like a car, a satellite, or even a stock price—when you only have imperfect, noisy measurements. Without it, your tracking would jitter wildly with every new data point. It is the standard method used in GPS navigation, autonomous driving, and robotics to smooth out sensor errors and provide a clear, reliable path. Learning this gives you the practical tool to turn messy real-world data into stable, usable estimates.


## The intuition

Imagine you are trying to locate your friend in a crowded market. Your friend sends a text saying they are near the chai stall, but their GPS might be off by a few meters. You also have a rough guess from where you last saw them standing. The Kalman filter works by combining these two pieces of information: it takes your prediction (where you think your friend is) and your measurement (the GPS text), and blends them together. It weighs each based on how confident you are in that specific source. If the GPS is known to be very accurate, you trust the text more. If you know your friend moves erratically, you might trust your prediction of their path more than the shaky GPS reading. The result is a single, smoothed estimate of where your friend is now. This process happens in a repeating cycle: you use this new best estimate to predict where they will be in the next moment, then combine that with the next noisy measurement. Because it uses its own previous output as the starting point for the next step, this cycle is called recursive.


## An analogy

Think of tracking a bird flying across a field with a companion who is very good at predicting motion. You have two inputs: your intuition about where the bird should be based on its last known speed and direction (the prediction), and occasional photos from a camera that captures the bird’s location but is sometimes blurry or slow (the measurement). Your companion doesn't just trust the photo or just trust the prediction. Instead, they act as a weighter. If the camera is sharp, they lean heavily on the photo. If the wind is gusting wildly and making the bird's path hard to predict, they lean more on the physics of the bird’s flight. They produce a single, smooth estimate of the bird's location. The analogy breaks down if the bird suddenly performs a maneuver that defies normal physics, because the filter assumes movement follows predictable patterns.


## Definition

A Kalman filter is a repeated algorithm that estimates the current condition of a moving system by constantly blending a predicted position with a noisy measurement, using confidence levels to weight each source, resulting in the most accurate possible estimate for linear systems with random noise.


## How it connects

This concept sits within Numerical Methods, which covers how to compute mathematical answers quickly and accurately using limited computational resources. It directly relies on Probability, specifically the treatment of errors as random variations around an average (known as Gaussian distribution), and it connects to Linear Algebra, which provides the tools for handling multiple variables simultaneously through vectors and matrices.


## The mathematics

The Kalman filter is a method for estimating the state of a system (like the position and speed of a car) when you have a model of how it moves and noisy measurements of where it actually is. It works by repeating two steps: prediction and update.

We use several symbols to keep track of our knowledge. Here is what they mean:

- x-hat with a subscript k (read 'x-hat sub k'): This is our best estimate of the system's state at time step k. For a car, this might be its position and velocity.
- P with a subscript k: This is the uncertainty (variance) associated with our estimate at time step k. A larger number means we are less sure.
- F (read 'F'): This is the transition matrix. It describes how the state changes from one time step to the next based on our physical model (e.g., velocity adds to position).
- Q (read 'Q'): This is the process noise covariance. It represents the uncertainty in our model itself (e.g., wind gusts affecting a car's motion).
- H (read 'H'): This is the observation matrix. It maps the state (position/velocity) to what we can measure (e.g., just position).
- R (read 'R'): This is the measurement noise covariance. It represents how noisy our sensors are.
- K (read 'K' or 'Kalman gain'): This is a weight that tells us how much to trust the new measurement versus our prediction.
- z with a subscript k: This is the actual measurement we receive at time step k.

The algorithm repeats these two phases for each new time step k:

Phase 1: Prediction (Predicting where we think we are)

First, we predict the next state using our model. We take the previous estimate and apply the transition matrix F. If there is an external input u (like a steering command), we add its effect via matrix B.

Formula for predicted state:
x-hat with subscript k-minus (read 'x-hat sub k-minus') = F times x-hat with subscript k-minus-one + B times u with subscript k.

Next, we predict the uncertainty. The uncertainty grows because our model is imperfect. We take the previous uncertainty P with subscript k-minus-one, transform it by F, and add the process noise Q.

Formula for predicted uncertainty:
P with subscript k-minus = F times P with subscript k-minus-one times F-transpose + Q.

Note: 'F-transpose' means we flip the rows and columns of matrix F. This is a standard matrix operation.

Phase 2: Update (Correcting with new data)

Now we receive a measurement z with subscript k. We compare it to what we predicted we would measure. The difference is called the innovation (or residual).

Innovation y = z with subscript k - H times x-hat with subscript k-minus.

We also need to know how uncertain this innovation is. This depends on our predicted uncertainty P with subscript k-minus and the sensor noise R.

Innovation covariance S = H times P with subscript k-minus times H-transpose + R.

Now we calculate the Kalman gain K. This tells us how much weight to give the new measurement. If the sensor is very noisy (large R), K will be small, and we trust our prediction more. If the sensor is precise (small R), K will be larger, and we trust the measurement more.

Formula for Kalman gain:
K = P with subscript k-minus times H-transpose divided by S.

(Note: In the scalar case, division is simple. In the matrix case, we multiply by the inverse of S. The inverse of a matrix M, written M-inverse, is a matrix that, when multiplied by M, gives the identity matrix.)

Finally, we update our estimate and uncertainty.

Updated state:
x-hat with subscript k = x-hat with subscript k-minus + K times y.

Updated uncertainty:
P with subscript k = (Identity matrix minus K times H) times P with subscript k-minus.

The Identity matrix is a square matrix with 1s on the diagonal and 0s elsewhere. Multiplying by it leaves a number or matrix unchanged.

This process repeats for every new time step, using the updated x-hat and P as the starting point for the next prediction.


## A worked example

Let's walk through one cycle of the Kalman filter for a car moving in a straight line. We will track its position (in meters) and velocity (in meters per second).

Initial State (Time 0):
- Estimated position: 100 meters.
- Estimated velocity: 20 meters/second.
- Uncertainty in position: 25 (variance).
- Uncertainty in velocity: 4 (variance).
- We assume the position and velocity uncertainties are not correlated initially, so the off-diagonal terms are 0. Our initial uncertainty matrix P_0 is:
  [[25, 0],
   [0, 4]]

Model Parameters:
- Time step: 1 second.
- Constant velocity model: Position updates by adding velocity * time. Velocity stays constant.
- Transition Matrix F: Since position = old_position + velocity * dt, and velocity = old_velocity, the matrix is:
  [[1, 1],
   [0, 1]]
  (The '1' in the top right means velocity contributes to position change. The '1' on the diagonal means each state component partially retains its own value.)
- Process Noise Q: Uncertainty in our model. Let's say position model has variance 1 and velocity model has variance 0.01. We assume they are uncorrelated.
  [[1, 0],
   [0, 0.01]]
- Observation Matrix H: We only measure position, not velocity. So we pick the first component of the state.
  [[1, 0]]
- Measurement Noise R: The GPS has variance 100.
  [100]

Step 1: Prediction (from Time 0 to Time 1)

1. Predicted State (x-hat_1_minus):
   We multiply the previous state vector [100, 20] by F.
   New position estimate = 1 * 100 + 1 * 20 = 120 meters.
   New velocity estimate = 0 * 100 + 1 * 20 = 20 meters/second.
   So, x-hat_1_minus = [120, 20].

2. Predicted Uncertainty (P_1_minus):
   We calculate F * P_0 * F-transpose + Q.
   First, F * P_0:
   [[1, 1],   [[25, 0],   [[25, 0]
    [0, 1]] *  [0, 4]] =  [0, 4]]
   
   Next, (F * P_0) * F-transpose:
   F-transpose is [[1, 0], [1, 1]].
   [[25, 0],   [[1, 0],   [[25, 0]
    [0, 4]] *  [1, 1]] =  [4, 4]]
   
   Finally, add Q:
   [[25, 0],   [[1, 0],   [[26, 0]
    [4, 4]] +  [0, 0.01]] = [4, 4.01]]
   
   So, P_1_minus = [[26, 0], [4, 4.01]].
   Note: The position uncertainty increased from 25 to 26 due to process noise. The cross-term is still 0 because we assumed no correlation in initial state or process noise.

Step 2: Update (at Time 1)

3. Measurement (z_1):
   Suppose the GPS reads 130 meters.
   z_1 = [130].

4. Innovation (y):
   We predict what the measurement should be: H * x-hat_1_minus.
   [[1, 0]] * [120] = [120].
   Innovation y = z_1 - predicted_measurement = 130 - 120 = 10 meters.

5. Innovation Covariance (S):
   S = H * P_1_minus * H-transpose + R.
   First, H * P_1_minus:
   [[1, 0]] * [[26, 0],   [[26, 0]
                [4, 4.01]] = [4, 4.01]]
   
   Next, (H * P_1_minus) * H-transpose:
   H-transpose is [[1], [0]].
   [[26, 0]] * [[1],   [[26]
    [4, 4.01]]  [0]] = [4]
   
   Add R (which is 100):
   S = 26 + 100 = 126.

6. Kalman Gain (K):
   K = P_1_minus * H-transpose / S.
   We already calculated P_1_minus * H-transpose as [[26], [4]].
   So, K = [[26], [4]] / 126.
   K_position = 26 / 126 ≈ 0.2063.
   K_velocity = 4 / 126 ≈ 0.0317.
   So, K = [[0.2063], [0.0317]].

7. Updated State (x-hat_1):
   x-hat_1 = x-hat_1_minus + K * y.
   Correction for position = 0.2063 * 10 = 2.063 meters.
   Correction for velocity = 0.0317 * 10 = 0.317 meters/second.
   New position estimate = 120 + 2.063 = 122.063 meters.
   New velocity estimate = 20 + 0.317 = 20.317 meters/second.
   So, x-hat_1 = [122.063, 20.317].

8. Updated Uncertainty (P_1):
   P_1 = (I - K * H) * P_1_minus.
   First, calculate K * H:
   [[0.2063],   [[1, 0]]   [[0.2063, 0]
    [0.0317]] *  [0, 1]] = [0.0317, 0]]
   
   Next, I - K * H:
   I is [[1, 0], [0, 1]].
   [[1, 0],   [[0.2063, 0],   [[0.7937, 0]
    [0, 1]] -  [0.0317, 0]] = [-0.0317, 1]]
   
   Finally, multiply by P_1_minus:
   [[0.7937, 0],   [[26, 0],     [[20.6362, 0]
    [-0.0317, 1]] * [4, 4.01]] = [-0.8242, 4.01]]
   
   So, P_1 ≈ [[20.64, -0.82], [-0.82, 4.01]].
   The position uncertainty dropped from 26 to ~20.64 because we incorporated the measurement.

Result after Time Step 1:
- Estimated Position: 122.06 meters.
- Estimated Velocity: 20.32 meters/second.
- Uncertainty in Position: ~20.64 (down from 26).
- Uncertainty in Velocity: ~4.01 (slightly up, as we didn't measure velocity directly, but the correlation with position helps slightly).


## From scratch, in Python

```python
# Kalman filter from scratch, Python
# We'll use lists for vectors and matrices (as lists of lists)

# --- Python basics ---
# A list is an ordered collection, e.g., [1, 2, 3]. We'll use a 2D list for a matrix:
# [[1, 2], [3, 4]] is a 2x2 matrix.
# A function is a reusable block of code. We'll define functions for each step.

# --- Data structures ---
# State: [position, velocity]
x = [100.0, 20.0]
# Covariance matrix: 2x2, representing uncertainty
P = [[25.0, 0.0], [0.0, 4.0]]

# Process noise covariance (constant)
Q = [[1.0, 0.0], [0.0, 0.01]]
# Measurement noise variance (scalar, for position)
R = 100.0

# State transition matrix: for constant velocity, dt=1
F = [[1.0, 1.0], [0.0, 1.0]]
# Measurement matrix: we measure position only
H = [[1.0, 0.0]]

# --- Helper functions ---

def matrix_multiply(A, B):
    """Multiply two matrices, returns a new matrix."""
    rows_A = len(A)
    cols_A = len(A[0])
    cols_B = len(B[0])
    result = [[0.0 for _ in range(cols_B)] for _ in range(rows_A)]
    for i in range(rows_A):
        for j in range(cols_B):
            total = 0.0
            for k in range(cols_A):
                total += A[i][k] * B[k][j]
            result[i][j] = total
    return result

def matrix_add(A, B):
    """Add two matrices element-wise."""
    return [[A[i][j] + B[i][j] for j in range(len(A[0]))] for i in range(len(A))]

def matrix_scalar_multiply(scalar, A):
    """Multiply every element of a matrix by a scalar."""
    return [[scalar * elem for elem in row] for row in A]

def matrix_transpose(A):
    """Transpose a matrix."""
    return [[A[j][i] for j in range(len(A))] for i in range(len(A[0]))]

def matrix_inverse_2x2(A):
    """Inverse of a 2x2 matrix using the formula."""
    a, b = A[0][0], A[0][1]
    c, d = A[1][0], A[1][1]
    det = a*d - b*c
    return [[d/det, -b/det], [-c/det, a/det]]

def vector_matrix_multiply(v, M):
    """Multiply a row vector (list) by a matrix (list of lists)."""
    result = [0.0 for _ in range(len(M[0]))]
    for i in range(len(M)):
        for j in range(len(M[0])):
            result[j] += v[i] * M[i][j]
    return result

def matrix_vector_multiply(M, v):
    """Multiply a matrix by a column vector (list)."""
    result = [0.0 for _ in range(len(M))]
    for i in range(len(M)):
        for j in range(len(v)):
            result[i] += M[i][j] * v[j]
    return result

def predict(x, P):
    """Prediction step."""
    x_pred = matrix_vector_multiply(F, x)
    P_pred = matrix_add(matrix_multiply(matrix_multiply(F, P), matrix_transpose(F)), Q)
    return x_pred, P_pred

def update(x_pred, P_pred, z):
    """Update step with measurement z."""
    # Innovation
    y = [z - (H[0][0]*x_pred[0] + H[0][1]*x_pred[1])]
    
    # Innovation covariance
    S = matrix_add(matrix_multiply(matrix_multiply(H, P_pred), matrix_transpose(H)), [[R]])
    
    # Kalman gain
    K = matrix_multiply(matrix_multiply(P_pred, matrix_transpose(H)), matrix_inverse_2x2(S))
    
    # Update state
    x_new = [x_pred[i] + sum(K[i][j] * y[j] for j in range(len(y))) for i in range(len(x_pred))]
    
    # Update covariance
    I = [[1.0, 0.0], [0.0, 1.0]]
    I_minus_KH = matrix_add(I, matrix_scalar_multiply(-1, matrix_multiply(K, H)))
    P_new = matrix_multiply(I_minus_KH, P_pred)
    
    return x_new, P_new

# --- Run one cycle ---
measurement = 130.0
x_pred, P_pred = predict(x, P)
x_new, P_new = update(x_pred, P_pred, measurement)
print("Predicted:", x_pred)
print("Updated:", x_new)
print("Covariance:", P_new)
```


## The same thing, with a framework

```python
With NumPy, the same steps become concise matrix operations. `numpy.dot` does the matrix multiplication, and `numpy.linalg.inv` computes the inverse. You would write: `x_pred = F @ x`, `P_pred = F @ P @ F.T + Q`, and so on. The library is doing exactly what the from-scratch code does, just with faster, more reliable linear algebra.
```


## What the code does

The code sets up the state vector (position, velocity) and covariance matrix. The helper functions implement basic matrix operations: multiply, add, scalar multiply, transpose, and 2x2 inverse — each of these is a small loop over the elements. Then `predict` applies the state transition to get a new state and increases the uncertainty. `update` computes the innovation (measurement minus predicted measurement), the innovation covariance, the Kalman gain, and finally adjusts the state and shrinks the covariance. The main loop runs predict and update once with the measurement 130, printing the results.


## Common misconceptions

**Kalman filter is only for linear systems** — I thought the Kalman filter only works with straight-line motion, so it's not useful for real-world AI.

The classic Kalman filter is optimal for linear dynamics, but it's a good heuristic for many nonlinear systems if the nonlinearity is mild. For strongly nonlinear systems, there are extensions like the Extended Kalman Filter (EKF) and Unscented Kalman Filter (UKF) that linearize or approximate the system, and they're widely used. In fact: The Kalman filter is a family of algorithms; the basic one is for linear systems, but variants handle nonlinearity by linearising or sampling.

**It requires all measurements to be available** — I thought you need continuous measurements for the filter to work.

The filter runs in two steps; between measurements, you just do the prediction step, and the uncertainty grows. When a measurement arrives, you update. So it handles irregular measurement times naturally. In fact: The filter is designed for online operation: it predicts between measurements and updates when they come, so it works with sporadic data.

**The filter stores all past data** — I thought the Kalman filter needs to remember every measurement to compute the best estimate.

The filter is recursive: it only needs the current estimate and covariance, plus the current measurement. This is why it has constant memory — a key feature. In fact: It's a 'recursive' filter: the entire history is summarised in the state and covariance, so it uses constant memory.

**A Kalman filter is a kind of neural network** — Since it's used in AI, I thought it might be a type of neural network.

It's a statistical estimator based on probability theory, not a learned model. It doesn't have weights trained from data; instead, it uses a mathematical model of the system and noise. In fact: It's a model-based technique from control theory and statistics, distinct from machine learning approaches.

**The Kalman gain is always 0 or 1** — I thought you either fully trust the measurement or fully trust the prediction.

The gain is a continuous number between 0 and 1, computed from the relative uncertainties. It's rarely exactly 0 or 1 except in extremes. In fact: The Kalman gain blends prediction and measurement based on their variances — it's a fraction, not a choice.


## In short

The Kalman filter is a recursive estimator that fuses a model prediction with noisy measurements, outputting a state estimate and its uncertainty. It alternates predict and update, uses constant memory, and is optimal under linear-Gaussian assumptions. You can implement it in plain Python with lists and loops — the formulas are just arithmetic — and use libraries in practice. Remember: it's about blending two sources of information weighted by their reliability.


## Where this is used

The Kalman filter is everywhere in applied AI, even where it isn't the headline. In **large language models** (LLMs), it's not used directly — LLMs process static text, not changing states. But the *principle* of balancing prediction with noisy evidence appears in how they handle ambiguous tokens; the filter itself would be overkill. In **small models on devices** (e.g., phones), Kalman filters are used for sensor fusion: combining accelerometer, gyroscope, and GPS data to estimate device orientation and position. A developer using something like `pykalman` writes a loop that feeds sensor readings into the filter and gets a smooth pose estimate. In **retrieval-augmented generation** (RAG), the Kalman filter doesn't appear — RAG is about matching query to documents, not tracking. But in **agentic systems** that plan and act, a Kalman filter can track the state of the environment (e.g., where a robot arm is) to decide the next action. A developer might use a library like `filterpy` to estimate the position of objects in a scene. In **everyday applied ML**, Kalman filters are used for time-series forecasting (e.g., stock prices, demand), for tracking objects in computer vision (e.g., in self-driving cars to track pedestrians), and for noise reduction in signals. In each case, the developer instantiates the filter with a model of the system dynamics and measurement noise, then feeds in measurements to get estimates. Done badly — with wrong noise assumptions or a mismatched model — the filter can diverge or produce jittery outputs. Done well, it gives a clean, real-time estimate with quantified uncertainty, which is why it's a standard tool in robotics and control.


---

Read this on the web: https://basavapurushottam.com/learn-ai/kalman-filter
