mrs_lib
Various reusable classes, functions and utilities for use in MRS projects
Loading...
Searching...
No Matches
ukf.hpp
Go to the documentation of this file.
1// clang: MatousFormat
2
3#ifndef UKF_HPP
4#define UKF_HPP
5
12#ifndef UKF_H
13#include <mrs_lib/ukf.h>
14#endif
15
16#include <iostream>
17
18namespace mrs_lib
19{
20 /* constructor //{ */
21
22 template <int n_states, int n_inputs, int n_measurements>
26
27 template <int n_states, int n_inputs, int n_measurements>
28 UKF<n_states, n_inputs, n_measurements>::UKF(const transition_model_t& transition_model, const observation_model_t& observation_model, const double alpha,
29 const double kappa, const double beta)
30 : m_alpha(alpha), m_kappa(kappa), m_beta(beta), m_Wm(W_t::Zero()), m_Wc(W_t::Zero()), m_transition_model(transition_model),
31 m_observation_model(observation_model)
32 {
33 assert(alpha > 0.0);
34 computeWeights();
35 }
36
37 //}
38
39 /* computeWeights() //{ */
40
41 template <int n_states, int n_inputs, int n_measurements>
43 {
44 // initialize lambda
45 /* m_lambda = double(n) * (m_alpha * m_alpha - 1.0); */
46 m_lambda = m_alpha * m_alpha * (double(n) + m_kappa) - double(n);
47
48 // initialize first terms of the weights
49 m_Wm(0) = m_lambda / (double(n) + m_lambda);
50 m_Wc(0) = m_Wm(0) + (1.0 - m_alpha * m_alpha + m_beta);
51
52 // initialize the rest of the weights
53 for (int i = 1; i < w; i++)
54 {
55 m_Wm(i) = (1.0 - m_Wm(0)) / (w - 1.0);
56 m_Wc(i) = m_Wm(i);
57 }
58 }
59
60 //}
61
62 /* setConstants() //{ */
63
64 template <int n_states, int n_inputs, int n_measurements>
65 // update the UKF constants
66 void UKF<n_states, n_inputs, n_measurements>::setConstants(const double alpha, const double kappa, const double beta)
67 {
68 m_alpha = alpha;
69 m_kappa = kappa;
70 m_beta = beta;
71
72 computeWeights();
73 }
74
75 //}
76
77 /* setTransitionModel() method //{ */
78
79 template <int n_states, int n_inputs, int n_measurements>
81 {
82 m_transition_model = transition_model;
83 }
84
85 //}
86
87 /* setObservationModel() method //{ */
88
89 template <int n_states, int n_inputs, int n_measurements>
91 {
92 m_observation_model = observation_model;
93 }
94
95 //}
96
97 /* computePaSqrt() method //{ */
98
99 template <int n_states, int n_inputs, int n_measurements>
101 {
102 // calculate the square root of the covariance matrix
103 const P_t Pa = (double(n) + m_lambda) * P;
104
105 /* Eigen::SelfAdjointEigenSolver<P_t> es(Pa); */
106 Eigen::LLT<P_t> llt(Pa);
107 if (llt.info() != Eigen::Success)
108 {
109 P_t tmp = Pa + (double(n) + m_lambda) * 1e-9 * P_t::Identity();
110 llt.compute(tmp);
111 if (llt.info() != Eigen::Success)
112 {
113 std::cout << "UKF: taking the square root of covariance during sigma point generation failed." << std::endl;
114 throw square_root_exception();
115 }
116 }
117
118 const P_t Pa_sqrt = llt.matrixL();
119 return Pa_sqrt;
120 }
121 //}
122
123 /* computeInverse() method //{ */
124 template <int n_states, int n_inputs, int n_measurements>
125 typename UKF<n_states, n_inputs, n_measurements>::Pzz_t UKF<n_states, n_inputs, n_measurements>::computeInverse(const Pzz_t& Pzz) const
126 {
127 Eigen::ColPivHouseholderQR<Pzz_t> qr(Pzz);
128 if (!qr.isInvertible())
129 {
130 // add some stuff to the tmp matrix diagonal to make it invertible
131 Pzz_t tmp = Pzz + 1e-9 * Pzz_t::Identity(Pzz.rows(), Pzz.cols());
132 qr.compute(tmp);
133 if (!qr.isInvertible())
134 {
135 // never managed to make this happen except for explicitly putting NaNs in the input
136 std::cout << "UKF: could not compute matrix inversion!!! Fix your covariances (the measurement's is probably too low...)" << std::endl;
137 throw inverse_exception();
138 }
139 std::cout << "UKF: artificially inflating matrix for inverse computation! Check your covariances (the measurement's might be too low...)" << std::endl;
140 }
141 Pzz_t ret = qr.inverse();
142 return ret;
143 }
144 //}
145
146 /* computeKalmanGain() method //{ */
147 template <int n_states, int n_inputs, int n_measurements>
148 typename UKF<n_states, n_inputs, n_measurements>::K_t UKF<n_states, n_inputs, n_measurements>::computeKalmanGain([[maybe_unused]] const x_t& x,
149 [[maybe_unused]] const z_t& inn,
150 const K_t& Pxz, const Pzz_t& Pzz) const
151 {
152 const Pzz_t Pzz_inv = computeInverse(Pzz);
153 const K_t K = Pxz * Pzz_inv;
154 return K;
155 }
156 //}
157
158 /* computeSigmas() method //{ */
159 template <int n_states, int n_inputs, int n_measurements>
160 typename UKF<n_states, n_inputs, n_measurements>::X_t UKF<n_states, n_inputs, n_measurements>::computeSigmas(const x_t& x, const P_t& P) const
161 {
162 // calculate sigma points
163 // fill in the middle of the elipsoid
164 X_t S;
165 S.col(0) = x;
166
167 const P_t P_sqrt = computePaSqrt(P);
168 const auto xrep = x.replicate(1, n);
169
170 // positive sigma points
171 S.template block<n, n>(0, 1) = xrep + P_sqrt;
172
173 // negative sigma points
174 S.template block<n, n>(0, n + 1) = xrep - P_sqrt;
175
176 /* std::cout << "x: " << std::endl << x << std::endl; */
177 /* std::cout << "S rowmean: " << std::endl << S.rowwise().mean() << std::endl; */
178
179 return S;
180 }
181 //}
182
183 /* predict() method //{ */
184
185 template <int n_states, int n_inputs, int n_measurements>
187 const Q_t& Q, double dt) const
188 {
189 const auto& x = sc.x;
190 const auto& P = sc.P;
191 statecov_t ret;
192
193 const X_t S = computeSigmas(x, P);
194
195 // propagate sigmas through the transition model
196 X_t X;
197 for (int i = 0; i < w; i++)
198 {
199 X.col(i) = m_transition_model(S.col(i), u, dt);
200 }
201
202 /* std::cout << "x: " << std::endl << x << std::endl; */
203 /* std::cout << "X rowmean: " << std::endl << X.rowwise().mean() << std::endl; */
204 /* std::cout << "m_Wm sum: " << m_Wm.sum() << std::endl; */
205
206 // recompute the state vector
207 ret.x = x_t::Zero();
208 for (int i = 0; i < w; i++)
209 {
210 // TODO: WHY DOES THIS SHIT WORK IF I SUBSTITUTE m_Wm(i) FOR 1.0/w ??
211 x_t tmp = 1.0 / w * X.col(i);
212 /* x_t tmp = m_Wm(i) * X.col(i); */
213 ret.x += tmp;
214 }
215
216 // recompute the covariance
217 ret.P = P_t::Zero();
218 for (int i = 0; i < w; i++)
219 {
220 ret.P += m_Wc(i) * (X.col(i) - ret.x) * (X.col(i) - ret.x).transpose();
221 }
222 ret.P += Q;
223
224 return ret;
225 }
226
227 //}
228
229 /* correct() method //{ */
230
231 template <int n_states, int n_inputs, int n_measurements>
233 const R_t& R) const
234 {
235 const auto& x = sc.x;
236 const auto& P = sc.P;
237 const X_t S = computeSigmas(x, P);
238
239 // propagate sigmas through the observation model
240 Z_t Z_exp(z.rows(), w);
241 for (int i = 0; i < w; i++)
242 {
243 Z_exp.col(i) = m_observation_model(S.col(i));
244 }
245
246 // compute expected measurement
247 z_t z_exp = z_t::Zero(z.rows());
248 for (int i = 0; i < w; i++)
249 {
250 z_exp += m_Wm(i) * Z_exp.col(i);
251 }
252
253 // compute the covariance of measurement
254 Pzz_t Pzz = Pzz_t::Zero(z.rows(), z.rows());
255 for (int i = 0; i < w; i++)
256 {
257 Pzz += m_Wc(i) * (Z_exp.col(i) - z_exp) * (Z_exp.col(i) - z_exp).transpose();
258 }
259 Pzz += R;
260
261 // compute cross covariance
262 K_t Pxz = K_t::Zero(n, z.rows());
263 for (int i = 0; i < w; i++)
264 {
265 Pxz += m_Wc(i) * (S.col(i) - x) * (Z_exp.col(i) - z_exp).transpose();
266 }
267
268 // compute Kalman gain
269 const z_t inn = (z - z_exp); // innovation
270 const K_t K = computeKalmanGain(sc.x, inn, Pxz, Pzz);
271
272 // check whether the inverse produced valid numbers
273 if (!K.array().isFinite().all())
274 {
275 std::cout << "UKF: inverting of Pzz in correction update produced non-finite numbers!!! Fix your covariances (the measurement's is probably too low...)"
276 << std::endl;
277 throw inverse_exception();
278 }
279
280 // correct
281 statecov_t ret;
282 ret.x = x + K * inn;
283 ret.P = P - K * Pzz * K.transpose();
284 return ret;
285 }
286
287 //}
288
289} // namespace mrs_lib
290
291#endif
Implementation of the Unscented Kalman filter .
Definition ukf.h:40
typename Eigen::Matrix< double, p, w > Z_t
Measurement sigma points matrix.
Definition ukf.h:51
typename Base_class::R_t R_t
measurement covariance p*p typedef
Definition ukf.h:67
typename Eigen::Matrix< double, n, w > X_t
State sigma points matrix.
Definition ukf.h:50
void setObservationModel(const observation_model_t &observation_model)
Changes the observation model function.
Definition ukf.hpp:90
virtual statecov_t predict(const statecov_t &sc, const u_t &u, const Q_t &Q, double dt) const override
Implements the state prediction step (time update).
Definition ukf.hpp:186
typename Base_class::x_t x_t
state vector n*1 typedef
Definition ukf.h:59
typename Base_class::statecov_t statecov_t
typedef of a helper struct for state and covariance
Definition ukf.h:73
void setConstants(const double alpha, const double kappa, const double beta)
Changes the Unscented Transform parameters.
Definition ukf.hpp:66
typename Base_class::u_t u_t
input vector m*1 typedef
Definition ukf.h:61
void setTransitionModel(const transition_model_t &transition_model)
Changes the transition model function.
Definition ukf.hpp:80
typename Base_class::z_t z_t
measurement vector p*1 typedef
Definition ukf.h:63
typename std::function< x_t(const x_t &, const u_t &, double)> transition_model_t
function of the state transition model typedef
Definition ukf.h:75
typename Base_class::Q_t Q_t
process covariance n*n typedef
Definition ukf.h:69
typename Base_class::P_t P_t
state covariance n*n typedef
Definition ukf.h:65
typename std::function< z_t(const x_t &)> observation_model_t
function of the observation model typedef
Definition ukf.h:77
typename Eigen::Matrix< double, w, 1 > W_t
weights vector (2n+1)*1 typedef
Definition ukf.h:71
typename Eigen::Matrix< double, n, p > K_t
Kalman gain matrix.
Definition ukf.h:53
UKF()
Convenience default constructor.
Definition ukf.hpp:23
virtual statecov_t correct(const statecov_t &sc, const z_t &z, const R_t &R) const override
Implements the state correction step (measurement update).
Definition ukf.hpp:232
typename Eigen::Matrix< double, p, p > Pzz_t
Pzz helper matrix.
Definition ukf.h:52
All mrs_lib functions, classes, variables and definitions are contained in this namespace.
Definition attitude_converter.h:24
is thrown when taking the inverse of a matrix fails during kalman gain calculation
Definition ukf.h:90
Defines UKF - a class implementing the Unscented Kalman Filter .