Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
UnscentedKF.hpp
Go to the documentation of this file.
1// Unscented Kalman Filter - UKF predict/update using sigma points
2// Part of Vulcan - Aerospace Engineering Utilities
3#pragma once
4
6
7namespace vulcan::estimation {
8
9// =============================================================================
10// UKF Parameters
11// =============================================================================
12
18struct UKFParams {
19 double alpha = 1e-3;
20 double beta = 2.0;
21 double kappa = 0.0;
22
24 double lambda(int n) const { return alpha * alpha * (n + kappa) - n; }
25
27 double Wm0(int n) const { return lambda(n) / (n + lambda(n)); }
28
30 double Wc0(int n) const { return Wm0(n) + (1 - alpha * alpha + beta); }
31
33 double Wi(int n) const { return 1.0 / (2 * (n + lambda(n))); }
34};
35
36// =============================================================================
37// Sigma Point Generation
38// =============================================================================
39
54template <int N>
55Eigen::Matrix<double, N, 2 * N + 1>
56sigma_points(const Eigen::Matrix<double, N, 1> &x,
57 const Eigen::Matrix<double, N, N> &P, const UKFParams &params) {
58 constexpr int num_sigma = 2 * N + 1;
59 Eigen::Matrix<double, N, num_sigma> sigma;
60
61 // Scaling factor
62 double scale = std::sqrt(N + params.lambda(N));
63
64 // Cholesky decomposition of P: P = L*Lᵀ
65 Eigen::LLT<Eigen::Matrix<double, N, N>> llt(P);
66 Eigen::Matrix<double, N, N> L = llt.matrixL();
67 Eigen::Matrix<double, N, N> sqrtP = scale * L;
68
69 // Zeroth sigma point: mean
70 sigma.col(0) = x;
71
72 // Sigma points from positive and negative perturbations
73 for (int i = 0; i < N; ++i) {
74 sigma.col(1 + i) = x + sqrtP.col(i);
75 sigma.col(1 + N + i) = x - sqrtP.col(i);
76 }
77
78 return sigma;
79}
80
81// =============================================================================
82// UKF Predict
83// =============================================================================
84
105template <int N, int U, typename DynamicsFunc>
107 const Eigen::Matrix<double, U, 1> &u,
108 DynamicsFunc f, const ProcessNoise<N> &noise,
109 const UKFParams &params = {}) {
110 constexpr int num_sigma = 2 * N + 1;
111
112 // Generate sigma points
113 auto sigma = sigma_points<N>(state.x, state.P, params);
114
115 // Propagate sigma points through dynamics
116 Eigen::Matrix<double, N, num_sigma> sigma_prop;
117 for (int i = 0; i < num_sigma; ++i) {
118 Eigen::Matrix<double, N, 1> xi = sigma.col(i);
119 sigma_prop.col(i) = f(xi, u);
120 }
121
122 // Compute weights
123 double Wm0 = params.Wm0(N);
124 double Wc0 = params.Wc0(N);
125 double Wi = params.Wi(N);
126
127 // Weighted mean
128 FilterState<double, N> predicted;
129 predicted.x = Wm0 * sigma_prop.col(0);
130 for (int i = 1; i < num_sigma; ++i) {
131 predicted.x += Wi * sigma_prop.col(i);
132 }
133
134 // Weighted covariance
135 Eigen::Matrix<double, N, 1> diff0 = sigma_prop.col(0) - predicted.x;
136 predicted.P = Wc0 * diff0 * diff0.transpose();
137 for (int i = 1; i < num_sigma; ++i) {
138 Eigen::Matrix<double, N, 1> diff = sigma_prop.col(i) - predicted.x;
139 predicted.P += Wi * diff * diff.transpose();
140 }
141 predicted.P += noise.Q;
142
143 return predicted;
144}
145
149template <int N, typename DynamicsFunc>
151 DynamicsFunc f, const ProcessNoise<N> &noise,
152 const UKFParams &params = {}) {
153 // Wrap dynamics to accept dummy control
154 auto f_wrapped = [&f](const Eigen::Matrix<double, N, 1> &x,
155 const Eigen::Matrix<double, 1, 1> & /*u*/) {
156 return f(x);
157 };
158 Eigen::Matrix<double, 1, 1> dummy_u = Eigen::Matrix<double, 1, 1>::Zero();
159 return ukf_predict<N, 1>(state, dummy_u, f_wrapped, noise, params);
160}
161
162// =============================================================================
163// UKF Update
164// =============================================================================
165
185template <int N, int M, typename MeasurementFunc>
188 const Eigen::Matrix<double, M, 1> &z, MeasurementFunc h,
189 const MeasurementNoise<M> &noise, const UKFParams &params = {}) {
190 constexpr int num_sigma = 2 * N + 1;
191
192 // Generate sigma points from predicted state
193 auto sigma = sigma_points<N>(state.x, state.P, params);
194
195 // Propagate sigma points through measurement model
196 Eigen::Matrix<double, M, num_sigma> gamma;
197 for (int i = 0; i < num_sigma; ++i) {
198 Eigen::Matrix<double, N, 1> xi = sigma.col(i);
199 gamma.col(i) = h(xi);
200 }
201
202 // Compute weights
203 double Wm0 = params.Wm0(N);
204 double Wc0 = params.Wc0(N);
205 double Wi = params.Wi(N);
206
207 // Predicted measurement mean
208 Eigen::Matrix<double, M, 1> z_pred = Wm0 * gamma.col(0);
209 for (int i = 1; i < num_sigma; ++i) {
210 z_pred += Wi * gamma.col(i);
211 }
212
213 // Innovation covariance S = Pzz + R
214 Eigen::Matrix<double, M, 1> dz0 = gamma.col(0) - z_pred;
215 Eigen::Matrix<double, M, M> Pzz = Wc0 * dz0 * dz0.transpose();
216 for (int i = 1; i < num_sigma; ++i) {
217 Eigen::Matrix<double, M, 1> dz = gamma.col(i) - z_pred;
218 Pzz += Wi * dz * dz.transpose();
219 }
220 Eigen::Matrix<double, M, M> S = Pzz + noise.R;
221
222 // Cross-covariance Pxz
223 Eigen::Matrix<double, N, 1> dx0 = sigma.col(0) - state.x;
224 Eigen::Matrix<double, N, M> Pxz = Wc0 * dx0 * dz0.transpose();
225 for (int i = 1; i < num_sigma; ++i) {
226 Eigen::Matrix<double, N, 1> dx = sigma.col(i) - state.x;
227 Eigen::Matrix<double, M, 1> dz = gamma.col(i) - z_pred;
228 Pxz += Wi * dx * dz.transpose();
229 }
230
231 // Kalman gain
232 Eigen::Matrix<double, N, M> K = Pxz * S.inverse();
233
234 // Update state
236 updated.x = state.x + K * (z - z_pred);
237 updated.P = state.P - K * S * K.transpose();
238
239 return updated;
240}
241
242// =============================================================================
243// Combined UKF Step
244// =============================================================================
245
249template <int N, int U, int M, typename DynamicsFunc, typename MeasurementFunc>
252 const Eigen::Matrix<double, U, 1> &u,
253 const Eigen::Matrix<double, M, 1> &z, DynamicsFunc f,
254 MeasurementFunc h, const ProcessNoise<N> &Q,
255 const MeasurementNoise<M> &R, const UKFParams &params = {}) {
256 // Predict
257 auto predicted = ukf_predict<N, U>(state, u, f, Q, params);
258
259 // Update
260 return ukf_update<N, M>(predicted, z, h, R, params);
261}
262
263} // namespace vulcan::estimation
constexpr double gamma
Ratio of specific heats for air.
Definition Constants.hpp:97
constexpr double sigma
Stefan-Boltzmann constant [W/(m^2·K^4)] (CODATA 2018).
Definition Constants.hpp:114
Definition EstimationTypes.hpp:8
FilterState< double, N > ukf_predict(const FilterState< double, N > &state, const Eigen::Matrix< double, U, 1 > &u, DynamicsFunc f, const ProcessNoise< N > &noise, const UKFParams &params={})
Unscented Kalman filter predict step.
Definition UnscentedKF.hpp:106
FilterState< double, N > ukf_update(const FilterState< double, N > &state, const Eigen::Matrix< double, M, 1 > &z, MeasurementFunc h, const MeasurementNoise< M > &noise, const UKFParams &params={})
Unscented Kalman filter update step.
Definition UnscentedKF.hpp:187
FilterState< double, N > ukf_step(const FilterState< double, N > &state, const Eigen::Matrix< double, U, 1 > &u, const Eigen::Matrix< double, M, 1 > &z, DynamicsFunc f, MeasurementFunc h, const ProcessNoise< N > &Q, const MeasurementNoise< M > &R, const UKFParams &params={})
Combined UKF step (predict + update).
Definition UnscentedKF.hpp:251
Eigen::Matrix< double, N, 2 *N+1 > sigma_points(const Eigen::Matrix< double, N, 1 > &x, const Eigen::Matrix< double, N, N > &P, const UKFParams &params)
Generate sigma points for UKF.
Definition UnscentedKF.hpp:56
AtmosphericState< Scalar > state(const Scalar &altitude, double scale_height=DEFAULT_SCALE_HEIGHT)
Exponential atmosphere - Complete atmospheric state.
Definition ExponentialAtmosphere.hpp:160
Filter state container holding estimate and covariance.
Definition EstimationTypes.hpp:20
Eigen::Matrix< Scalar, N, 1 > x
State estimate.
Definition EstimationTypes.hpp:21
Measurement noise specification.
Definition EstimationTypes.hpp:79
Eigen::Matrix< double, M, M > R
Measurement noise covariance.
Definition EstimationTypes.hpp:80
Process noise specification.
Definition EstimationTypes.hpp:46
Eigen::Matrix< double, N, N > Q
Process noise covariance.
Definition EstimationTypes.hpp:47
UKF tuning parameters.
Definition UnscentedKF.hpp:18
double Wc0(int n) const
Compute weight for covariance of zeroth sigma point.
Definition UnscentedKF.hpp:30
double Wi(int n) const
Compute weight for other sigma points (same for mean and covariance).
Definition UnscentedKF.hpp:33
double lambda(int n) const
Compute lambda scaling factor.
Definition UnscentedKF.hpp:24
double beta
Prior knowledge about distribution (2 = Gaussian).
Definition UnscentedKF.hpp:20
double alpha
Spread of sigma points around mean (1e-4 to 1).
Definition UnscentedKF.hpp:19
double Wm0(int n) const
Compute weight for mean of zeroth sigma point.
Definition UnscentedKF.hpp:27
double kappa
Secondary scaling parameter.
Definition UnscentedKF.hpp:21