|
62 | 62 | K_t = P_t^{t-1} A_t^T(A_t P_t^{t-1}A_t^T + S)^{-1}\,. |
63 | 63 | $$ |
64 | 64 |
|
65 | | -Implementing the Kalman filter boils down to implement these few equations! |
| 65 | +Implementing the Kalman filter boils down to implement these few equations! |
| 66 | + |
| 67 | +## C++ implementation |
| 68 | + |
| 69 | +The following code provides a generic templated class `KFLinear` supporting both fixed-size and dynamic-size state and measurement vectors, using the [Eigen](https://eigen.tuxfamily.org/) linear algebra library. |
| 70 | + |
| 71 | +<details> |
| 72 | +<summary>Click here to view the full implementation: <b>include/kf_linear.hpp</b>. We break into down in the sequel of this section. </summary> |
| 73 | + |
| 74 | +```cpp |
| 75 | +{{#include ../../crates/kf_linear/include/kf_linear.hpp}} |
| 76 | +``` |
| 77 | +</details> |
| 78 | +
|
| 79 | +
|
| 80 | +Here's the header file without the inlined implementations. |
| 81 | +
|
| 82 | +```cpp |
| 83 | +#pragma once |
| 84 | +
|
| 85 | +#include <Eigen/Dense> |
| 86 | +#include <iostream> |
| 87 | +#include <optional> |
| 88 | +#include <stdexcept> |
| 89 | +#include <vector> |
| 90 | +
|
| 91 | +/** |
| 92 | + * @brief Generic linear Kalman filter (templated, no control term). |
| 93 | + * |
| 94 | + * State-space model: |
| 95 | + * x_k = A x_{k-1} + w_{k-1}, w ~ N(0, Q) |
| 96 | + * z_k = H x_k + v_k, v ~ N(0, R) |
| 97 | + * |
| 98 | + * Template parameters: |
| 99 | + * Nx = state dimension (int or Eigen::Dynamic) |
| 100 | + * Ny = measurement dimension(int or Eigen::Dynamic) |
| 101 | + */ |
| 102 | +template <int Nx, int Ny> class KFLinear { |
| 103 | + public: |
| 104 | + using StateVec = Eigen::Matrix<double, Nx, 1>; |
| 105 | + using StateMat = Eigen::Matrix<double, Nx, Nx>; |
| 106 | + using MeasVec = Eigen::Matrix<double, Ny, 1>; |
| 107 | + using MeasMat = Eigen::Matrix<double, Ny, Ny>; |
| 108 | + using ObsMat = Eigen::Matrix<double, Ny, Nx>; |
| 109 | +
|
| 110 | + KFLinear(const StateVec &initial_state, const StateMat &initial_covariance, |
| 111 | + const StateMat &transition_matrix, const ObsMat &observation_matrix, |
| 112 | + const StateMat &process_covariance, const MeasMat &measurement_covariance); |
| 113 | +
|
| 114 | + void predict(); |
| 115 | + void update(const MeasVec &z); |
| 116 | + void step(const std::optional<MeasVec> &measurement); |
| 117 | + std::vector<StateVec> filter(const std::vector<std::optional<MeasVec>> &measurements); |
| 118 | +
|
| 119 | + [[nodiscard]] const StateVec &state() const { return x_; } |
| 120 | + [[nodiscard]] const StateMat &covariance() const { return P_; } |
| 121 | +
|
| 122 | + void set_transition(const StateMat &A) { A_ = A; } |
| 123 | + void set_observation(const ObsMat &H) { H_ = H; } |
| 124 | + void set_process_noise(const StateMat &Q) { Q_ = Q; } |
| 125 | + void set_measurement_noise(const MeasMat &R){ R_ = R; } |
| 126 | +
|
| 127 | + private: |
| 128 | + StateMat A_, Q_, P_; |
| 129 | + ObsMat H_; |
| 130 | + MeasMat R_; |
| 131 | + StateVec x_; |
| 132 | +}; |
| 133 | +``` |
| 134 | + |
| 135 | +A few comments: |
| 136 | + |
| 137 | +- **Predict step**: The method `predict()` propagates the state and covariance using the transition matrix A and process noise covariance Q. |
| 138 | + |
| 139 | +- **Update step**: The method `update(z)` corrects the prediction using a new measurement z. It computes the Kalman gain K efficiently by solving a linear system with LDLT factorization instead of forming the matrix inverse explicitly. The covariance update uses the Joseph form to ensure numerical stability and positive semi-definiteness. |
| 140 | + |
| 141 | +- **Step and filter**: The `step()` method combines prediction with an optional update (useful when some measurements are missing). The `filter()` method processes an entire sequence of measurements, returning the sequence of estimated states. |
| 142 | + |
| 143 | +- **Flexibility**: |
| 144 | + - Works with both fixed-size and dynamic-size Eigen matrices. |
| 145 | + - Provides setters to update system matrices online (e.g. if the model changes over time). |
| 146 | + - Uses `std::optional` to naturally handle missing observations. |
0 commit comments