Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Kalman Filter and RTS Smoother

This notebook demonstrates the gaussx Kalman filter and Rauch-Tung-Striebel (RTS) smoother recipes on a simple linear dynamical system.

What you’ll learn:

  1. Setting up a linear-Gaussian state-space model
  2. Running gaussx.kalman_filter for online state estimation
  3. Running gaussx.rts_smoother for offline smoothing
  4. Comparing filtered vs smoothed estimates
  5. The filter is fully differentiable via JAX

Background

A linear-Gaussian state-space model (SSM) is defined by:

xt=A xt−1+qt,qt∼N(0,Q)x_t = A \, x_{t-1} + q_t, \quad q_t \sim \mathcal{N}(0, Q)
yt=H xt+rt,rt∼N(0,R)y_t = H \, x_t + r_t, \quad r_t \sim \mathcal{N}(0, R)

The Kalman filter computes the filtering distribution p(xt∣y1:t)p(x_t \mid y_{1:t}) in a single forward pass. The RTS smoother then refines these estimates using future observations, giving the smoothing distribution p(xt∣y1:T)p(x_t \mid y_{1:T}).

The smoother always has lower (or equal) posterior variance than the filter, because it conditions on strictly more data.

Kalman filter equations

The filter alternates between a predict step and an update step:

Predict:

x^t∣t−1=Ax^t−1∣t−1,Pt∣t−1=APt−1∣t−1A⊤+Q\hat{x}_{t|t-1} = A \hat{x}_{t-1|t-1}, \qquad P_{t|t-1} = A P_{t-1|t-1} A^\top + Q

Update:

St=HPt∣t−1H⊤+R(innovation covariance)S_t = H P_{t|t-1} H^\top + R \qquad \text{(innovation covariance)}
Kt=Pt∣t−1H⊤St−1(Kalman gain)K_t = P_{t|t-1} H^\top S_t^{-1} \qquad \text{(Kalman gain)}
x^t∣t=x^t∣t−1+Kt(yt−Hx^t∣t−1)\hat{x}_{t|t} = \hat{x}_{t|t-1} + K_t(y_t - H \hat{x}_{t|t-1})
Pt∣t=(I−KtH)Pt∣t−1P_{t|t} = (I - K_t H) P_{t|t-1}

Note that each step involves a linear system solve (for KtK_t), which is where gaussx primitives are used.

Define the model

We use a 2D state xt=[position,velocity]⊤x_t = [\text{position}, \text{velocity}]^\top with a damped oscillator transition (discretized spring-mass-damper). Only position is observed with noise.

  • AA: discretized spring-mass-damper transition
  • H=[10]H = \begin{bmatrix} 1 & 0 \end{bmatrix} (observe position only)
  • QQ: process noise driving the oscillator
  • R=[0.5]R = [0.5]: observation noise variance
A =
 [[ 1.     0.1  ]
 [-0.1    0.985]]
H = [[1. 0.]]
Q =
 [[0.0001 0.0015]
 [0.0015 0.03  ]]
R = [[0.5]]

Generate data

We simulate the true trajectory from the model itself, so the Kalman filter’s assumptions are satisfied. This shows the filter performing as designed — tracking a randomly evolving state from noisy observations.

true_states shape: (200, 2)
observations shape: (200, 1)

Run Kalman filter

gaussx.kalman_filter takes the model matrices and observations, returning a FilterState with filtered means, covariances, and the total log-likelihood.

Filtered means shape: (200, 2)
Filtered covs shape: (200, 2, 2)
Log-likelihood: -223.3188576581507

Plot filtered results

The filtered estimate tracks the true position closely despite the noisy observations. The shaded band shows the ±2σ\pm 2\sigma credible interval.

<Figure size 1200x600 with 2 Axes>

Run RTS smoother

The RTS smoother takes the filter output and refines the estimates using a backward pass. This produces tighter credible intervals, especially in the middle of the time series.

The smoother computes a backward recursion from t=T−1t = T-1 down to t=1t = 1:

Gt=Pt∣tA⊤Pt+1∣t−1G_t = P_{t|t} A^\top P_{t+1|t}^{-1}
x^t∣T=x^t∣t+Gt(x^t+1∣T−Ax^t∣t)\hat{x}_{t|T} = \hat{x}_{t|t} + G_t(\hat{x}_{t+1|T} - A \hat{x}_{t|t})
Pt∣T=Pt∣t+Gt(Pt+1∣T−Pt+1∣t)Gt⊤P_{t|T} = P_{t|t} + G_t(P_{t+1|T} - P_{t+1|t}) G_t^\top

The smoother refines filter estimates by incorporating future observations. This optimal smoother was derived by Rauch, Tung, & Striebel (1965).

Smoothed means shape: (200, 2)
Smoothed covs shape: (200, 2, 2)

Plot filtered vs smoothed

The smoother uncertainty is everywhere less than or equal to the filter uncertainty, because it conditions on all observations y1:Ty_{1:T}.

<Figure size 1200x700 with 2 Axes>

Differentiability

The Kalman filter is implemented via jax.lax.scan, so it is fully differentiable. We can compute gradients of the log-likelihood with respect to model parameters — useful for learning SSM parameters via gradient-based optimization.

log(R) = -0.6931
Negative log-likelihood = 223.3189
Gradient d(-LL)/d(log R) = 12.2965

Summary

  • gaussx.kalman_filter implements the standard Kalman filter forward pass, returning filtered means, covariances, and the total log-likelihood.
  • gaussx.rts_smoother refines filtered estimates using a backward pass, producing tighter credible intervals.
  • Both are implemented with jax.lax.scan and are fully compatible with JAX transforms: jit, vmap, and grad all work out of the box.
  • This makes gaussx suitable for learning SSM parameters via gradient-based optimization of the log-likelihood.

References

  • Kalman, R. E. (1960). A new approach to linear filtering and prediction problems. J. Basic Engineering, 82(1), 35--45.
  • Rauch, H. E., Tung, F., & Striebel, C. T. (1965). Maximum likelihood estimates of linear dynamic systems. AIAA Journal, 3(8), 1445--1450.
  • Sarkka, S. (2013). Bayesian Filtering and Smoothing. Cambridge University Press.
  • Anderson, B. D. O. & Moore, J. B. (1979). Optimal Filtering. Prentice-Hall.