Model formulation
We consider the following general state space model made of a state equation and an observation equation:
where:
- is the state vector at time step ,
- is the observation/measurement vector.
We also have the following hyperparameters assumed to be known:
- is the state transition matrix,
- is the observation matrix,
- is the process noise covariance,
- is the observation noise covariance.
The aim is to estimate the state given the noisy observations .
Kalman filtering
We start with an initial state and we want to determine the conditional distribution of the next state. Using Baye's rule, one has The likelihood can easily be determined thanks to the chosen observation equation. The marginal distribution can be obtained by marginalizing . This marginal distribution can be seen as our prior knowledge before seeing . It can easily be shown that with This first estimate is what we will call the prediction step. Using this result, we can determine : with which gives us our estimate the next state given the observation . This is what we call the update step.
General equations
For an arbitrary time step , the prediction step yields: and the update step is given by where the Kalman gain is defined as
Implementing the Kalman filter boils down to implement these few equations!
Crate
At the end of the chapter, we obtain a small standalone crate with the following layout:
├── Cargo.toml
└── src
├── algorithm.rs
└── lib.rs
The module algorithm.rs
implements the whole Kalman filter method defined as a struct with a few implementations.
For this crate, we only need a few imports from nalgebra
, rand
, and rand_distr
. In the algorithm module, we have:
#![allow(unused)] fn main() { use nalgebra::{Cholesky, DMatrix, DVector}; use rand::thread_rng; use rand_distr::{Distribution, StandardNormal}; }
And the Cargo.toml
configuration file is given by
[package]
name = "kalman_filter"
version = "0.1.0"
edition = "2024"
[dependencies]
rustineers = { path = "../../" }
nalgebra = "0.33"
rand = "0.8"
rand_distr = "0.4"
thiserror = "1.0"