UnscentedKalmanFilter.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package ROBDEKON
17 * @author Christoph Pohl ( christoph dot pohl at kit dot edu )
18 * @date 16.03.21
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 *
22 * Code adapted to C++ from: https://github.com/CAOR-MINES-ParisTech/ukfm
23 * See also:
24 * - https://arxiv.org/pdf/2002.00878.pdf
25 * - https://ieeexplore.ieee.org/document/8206066/
26 */
27
29
30#include <complex>
31#include <utility>
32
34
35template <typename SystemModelT>
37 ObsCovT R,
38 const AlphaT& alpha,
39 StateT state0,
40 StateCovT P0) :
41 Q_(Q),
42 chol_Q_(Q.llt().matrixL().transpose()),
43 R_(std::move(R)),
44 alpha_(alpha),
45 state_(std::move(state0)),
46 P_(std::move(P0)),
47 weights_(alpha)
48{
49}
50
51template <typename SystemModelT>
52void
54{
55 StateCovT P = P_ + eps * StateCovT::Identity();
56 ARMARX_CHECK(P.allFinite());
57 ControlNoiseT w = ControlNoiseT::Zero();
58 // update mean
59 StateT state_after = SystemModelT::propagationFunction(state_, omega, w, dt);
60
61 // set sigma points (Xi in paper when on Manifold, sigma in Tangent Space)
62 Eigen::LLT<StateCovT> llt = P.llt();
63 if (llt.info() != Eigen::ComputationInfo::Success)
64 {
65 P = calculateClosestPosSemidefMatrix(P);
66 P += eps * StateCovT::Identity();
67 llt = P.llt();
68 ARMARX_CHECK(llt.info() == Eigen::ComputationInfo::Success);
69 }
70 StateCovT xi_j_before =
71 weights_.state.sqrt_d_lambda * llt.matrixL().transpose().toDenseMatrix();
74
75 auto propagate_and_retract = [&omega, &dt, &state_after](long row,
76 long offset,
77 const StateT& state,
78 const ControlNoiseT& noise,
79 auto& xi_j) -> void
80 {
81 const StateT sig_j_after = SystemModelT::propagationFunction(state, omega, noise, dt);
82 xi_j.row(offset + row) = SystemModelT::inverseRetraction(state_after, sig_j_after);
83 };
84
85 auto calculate_covariance = [](FloatT wj, FloatT w0, auto& xi_j) -> StateCovT
86 {
87 SigmaPointsT xi_0 = wj * xi_j.colwise().sum();
88 xi_j.rowwise() -= xi_0.transpose();
89 StateCovT cov_after = wj * xi_j.transpose() * xi_j + w0 * xi_0 * xi_0.transpose();
90 return cov_after;
91 };
92
93 for (long j = 0; j < dim::state; j++)
94 {
95 const SigmaPointsT sigma = xi_j_before.row(j);
96 const StateT sig_j_plus = SystemModelT::retraction(state_, sigma);
97 const StateT sig_j_minus = SystemModelT::retraction(state_, -sigma);
98 propagate_and_retract(j, 0, sig_j_plus, w, xi_j_after);
99 propagate_and_retract(j, dim::state, sig_j_minus, w, xi_j_after);
100 }
101
102 StateCovT P_after = calculate_covariance(weights_.state.wj, weights_.state.w0, xi_j_after);
103 ARMARX_CHECK(P_after.allFinite());
104 for (long j = 0; j < dim::control; j++)
105 {
106 const ControlNoiseT w_plus = weights_.control.sqrt_d_lambda * chol_Q_.row(j);
107 const ControlNoiseT w_minus = -weights_.control.sqrt_d_lambda * chol_Q_.row(j);
108 propagate_and_retract(j, 0, state_, w_plus, w_j_after);
109 propagate_and_retract(j, dim::control, state_, w_minus, w_j_after);
110 }
111
112 StateCovT Q = calculate_covariance(weights_.control.wj, weights_.control.w0, w_j_after);
113 ARMARX_CHECK(Q.allFinite());
114 StateCovT new_P = P_after + Q;
115 P_ = std::move((new_P + new_P.transpose()) / 2.0f);
116 state_ = std::move(state_after);
117}
118
119template <typename SystemModelT>
120void
122{
123 StateCovT P = P_ + eps * StateCovT::Identity();
124 ARMARX_CHECK(P.allFinite());
125 StateCovT xi_j = weights_.state.sqrt_d_lambda * P.llt().matrixL().transpose().toDenseMatrix();
126 ARMARX_CHECK(P.llt().info() == Eigen::ComputationInfo::Success);
127 ARMARX_CHECK(xi_j.allFinite());
131
132 for (long j = 0; j < dim::state; j++)
133 {
134 const SigmaPointsT sigma = xi_j.row(j);
135 const StateT sig_j_plus = SystemModelT::retraction(state_, sigma);
136 const StateT sig_j_minus = SystemModelT::retraction(state_, -sigma);
137 y_j.row(j) = SystemModelT::observationFunction(sig_j_plus);
138 y_j.row(j + dim::state) = SystemModelT::observationFunction(sig_j_minus);
139 }
140 ARMARX_CHECK(y_j.allFinite());
141 const ObsT y_mean =
142 weights_.update.wm * y_hat + weights_.update.wj * y_j.colwise().sum().transpose();
143 y_j.rowwise() -= y_mean.transpose();
144 y_hat -= y_mean;
145
146 const ObsCovT P_yy = weights_.update.w0 * y_hat * y_hat.transpose() +
147 weights_.update.wj * y_j.transpose() * y_j + R_;
148 ARMARX_CHECK(P_yy.allFinite());
150 weights_.update.wj *
151 (Eigen::Matrix<FloatT, dim::state, 2 * dim::state>() << xi_j, -xi_j).finished() *
152 y_j /* + weights_.update.w0 * xi_0 * y_hat.transpose()*/;
153 ARMARX_CHECK(P_xiy.allFinite());
155 P_yy.colPivHouseholderQr().solve(P_xiy.transpose()).transpose();
156 FloatT relative_error = (P_yy * K.transpose() - P_xiy.transpose()).norm() / P_xiy.norm();
157 ARMARX_CHECK(relative_error < 1e-4);
158 ARMARX_CHECK(K.allFinite());
159 SigmaPointsT xi_plus = K * (y - y_mean);
160 ARMARX_CHECK(xi_plus.allFinite());
161 state_ = SystemModelT::retraction(state_, xi_plus);
162 StateCovT new_P = P - K * (P_yy)*K.transpose();
163
164 // make result symmetric
165 P_ = calculateClosestPosSemidefMatrix(new_P);
166}
167
168template <typename SystemModelT>
171{
172 return state_;
173}
174
175template <typename SystemModelT>
181
182template <typename SystemModelT>
184UnscentedKalmanFilter<SystemModelT>::calculateClosestPosSemidefMatrix(
186{
187 const StateCovT new_P = ((cov + cov.transpose()) / 2.0f);
188 Eigen::EigenSolver<StateCovT> solver(new_P);
189 Eigen::Matrix<FloatT, dim::state, dim::state> D = solver.eigenvalues().real().asDiagonal();
190 const Eigen::Matrix<FloatT, dim::state, dim::state> V = solver.eigenvectors().real();
191 D = D.cwiseMax(0);
192
193 return (V * D * V.inverse());
194}
195
196template <typename SystemModelT>
197UnscentedKalmanFilter<SystemModelT>::Weights::Weights(AlphaT alpha) :
198 state(dim::state, alpha(0)), control(dim::control, alpha(1)), update(dim::state, alpha(2))
199{
200}
201
202template <typename SystemModelT>
204{
205 ARMARX_CHECK_GREATER(alpha, 0);
206 FloatT m = (alpha * alpha - 1) * l;
207 sqrt_d_lambda = std::sqrt(l + m);
208 wj = 1 / (2 * (l + m));
209 wm = m / (m + l);
210 w0 = m / (m + l) + 3 - alpha * alpha;
211}
212
214
216
218
220
221///////////////////
222
223
224template <typename SystemModelT>
226 ObsCovT R,
227 const AlphaT& alpha,
228 StateT state0,
229 StateCovT P0) :
230 R_(std::move(R)), alpha_(alpha), state_(std::move(state0)), P_(std::move(P0)), weights_(alpha)
231{
232}
233
234template <typename SystemModelT>
235void
237{
238 StateCovT P = P_ + eps * StateCovT::Identity();
239 ARMARX_CHECK(P.allFinite());
240 // ControlNoiseT w = ControlNoiseT::Zero();
241 // update mean
242 StateT state_after = SystemModelT::propagationFunction(state_, dt);
243
244 // set sigma points (Xi in paper when on Manifold, sigma in Tangent Space)
245 Eigen::LLT<StateCovT> llt = P.llt();
246 if (llt.info() != Eigen::ComputationInfo::Success)
247 {
248 P = calculateClosestPosSemidefMatrix(P);
249 P += eps * StateCovT::Identity();
250 llt = P.llt();
251 ARMARX_CHECK(llt.info() == Eigen::ComputationInfo::Success);
252 }
253 StateCovT xi_j_before =
254 weights_.state.sqrt_d_lambda * llt.matrixL().transpose().toDenseMatrix();
257
258 auto propagate_and_retract =
259 [&dt, &state_after](long row, long offset, const StateT& state, auto& xi_j) -> void
260 {
261 const StateT sig_j_after = SystemModelT::propagationFunction(state, dt);
262 xi_j.row(offset + row) = SystemModelT::inverseRetraction(state_after, sig_j_after);
263 };
264
265 auto calculate_covariance = [](FloatT wj, FloatT w0, auto& xi_j) -> StateCovT
266 {
267 SigmaPointsT xi_0 = wj * xi_j.colwise().sum();
268 xi_j.rowwise() -= xi_0.transpose();
269 StateCovT cov_after = wj * xi_j.transpose() * xi_j + w0 * xi_0 * xi_0.transpose();
270 return cov_after;
271 };
272
273 for (long j = 0; j < dim::state; j++)
274 {
275 const SigmaPointsT sigma = xi_j_before.row(j);
276 const StateT sig_j_plus = SystemModelT::retraction(state_, sigma);
277 const StateT sig_j_minus = SystemModelT::retraction(state_, -sigma);
278 propagate_and_retract(j, 0, sig_j_plus, xi_j_after);
279 propagate_and_retract(j, dim::state, sig_j_minus, xi_j_after);
280 }
281
282 StateCovT P_after = calculate_covariance(weights_.state.wj, weights_.state.w0, xi_j_after);
283 ARMARX_CHECK(P_after.allFinite());
284 // for (long j = 0; j < dim::control; j++)
285 // {
286 // const ControlNoiseT w_plus = weights_.control.sqrt_d_lambda * chol_Q_.row(j);
287 // const ControlNoiseT w_minus = -weights_.control.sqrt_d_lambda * chol_Q_.row(j);
288 // propagate_and_retract(j, 0, state_, w_plus, w_j_after);
289 // propagate_and_retract(j, dim::control, state_, w_minus, w_j_after);
290 // }
291
292 // StateCovT Q = calculate_covariance(weights_.control.wj, weights_.control.w0, w_j_after);
293 // ARMARX_CHECK(Q.allFinite());
294 StateCovT new_P = P_after; // + Q;
295 P_ = std::move((new_P + new_P.transpose()) / 2.0f);
296 // P_ = new_P;
297 state_ = std::move(state_after);
298}
299
300template <typename SystemModelT>
301void
303{
304 StateCovT P = P_ + eps * StateCovT::Identity();
305 ARMARX_CHECK(P.allFinite());
306 StateCovT xi_j = weights_.state.sqrt_d_lambda * P.llt().matrixL().transpose().toDenseMatrix();
307 ARMARX_CHECK(P.llt().info() == Eigen::ComputationInfo::Success);
308 ARMARX_CHECK(xi_j.allFinite());
312
313 for (long j = 0; j < dim::state; j++)
314 {
315 const SigmaPointsT sigma = xi_j.row(j);
316 const StateT sig_j_plus = SystemModelT::retraction(state_, sigma);
317 const StateT sig_j_minus = SystemModelT::retraction(state_, -sigma);
318 y_j.row(j) = SystemModelT::observationFunction(sig_j_plus);
319 y_j.row(j + dim::state) = SystemModelT::observationFunction(sig_j_minus);
320 }
321 ARMARX_CHECK(y_j.allFinite());
322 const ObsT y_mean =
323 weights_.update.wm * y_hat + weights_.update.wj * y_j.colwise().sum().transpose();
324 y_j.rowwise() -= y_mean.transpose();
325 y_hat -= y_mean;
326
327 const ObsCovT P_yy = weights_.update.w0 * y_hat * y_hat.transpose() +
328 weights_.update.wj * y_j.transpose() * y_j + R_;
329 ARMARX_CHECK(P_yy.allFinite());
331 weights_.update.wj *
332 (Eigen::Matrix<FloatT, dim::state, 2 * dim::state>() << xi_j, -xi_j).finished() *
333 y_j /* + weights_.update.w0 * xi_0 * y_hat.transpose()*/;
334 ARMARX_CHECK(P_xiy.allFinite());
336 P_yy.colPivHouseholderQr().solve(P_xiy.transpose()).transpose();
337 FloatT relative_error = (P_yy * K.transpose() - P_xiy.transpose()).norm() / P_xiy.norm();
338 ARMARX_CHECK(relative_error < 1e-4);
339 ARMARX_CHECK(K.allFinite());
340
341 const auto residual = y - y_mean;
342
343 SigmaPointsT xi_plus = K * residual;
344 ARMARX_CHECK(xi_plus.allFinite());
345 state_ = SystemModelT::retraction(state_, xi_plus);
346 StateCovT new_P = P - K * (P_yy)*K.transpose();
347
348 // make result symmetric
349 P_ = calculateClosestPosSemidefMatrix(new_P);
350}
351
352template <typename SystemModelT>
358
359template <typename SystemModelT>
365
366template <typename SystemModelT>
368UnscentedKalmanFilterWithoutControl<SystemModelT>::calculateClosestPosSemidefMatrix(
370{
371 const StateCovT new_P = ((cov + cov.transpose()) / 2.0f);
372 Eigen::EigenSolver<StateCovT> solver(new_P);
373 Eigen::Matrix<FloatT, dim::state, dim::state> D = solver.eigenvalues().real().asDiagonal();
374 const Eigen::Matrix<FloatT, dim::state, dim::state> V = solver.eigenvectors().real();
375 D = D.cwiseMax(0);
376
377 return (V * D * V.inverse());
378}
379
380template <typename SystemModelT>
381UnscentedKalmanFilterWithoutControl<SystemModelT>::Weights::Weights(AlphaT alpha) :
382 state(dim::state, alpha(0)), update(dim::state, alpha(2))
383{
384}
385
386template <typename SystemModelT>
388{
389 ARMARX_CHECK_GREATER(alpha, 0);
390 FloatT m = (alpha * alpha - 1) * l;
391 sqrt_d_lambda = std::sqrt(l + m);
392 wj = 1 / (2 * (l + m));
393 wm = m / (m + l);
394 w0 = m / (m + l) + 3 - alpha * alpha;
395}
constexpr T dt
typename SystemModelT::StateT StateT
const StateCovT & getCurrentStateCovariance() const
Eigen::Matrix< FloatT, dim::obs, dim::obs > ObsCovT
Eigen::Matrix< FloatT, 3, 1 > AlphaT
typename SystemModelT::FloatT FloatT
typename SystemModelT::SigmaPointsT SigmaPointsT
Eigen::Matrix< FloatT, dim::state, dim::state > StateCovT
Eigen::Matrix< FloatT, dim::obs, 1 > ObsT
typename SystemModelT::StateT StateT
const StateT & getCurrentState() const
void propagation(const ControlT &omega, FloatT dt)
UnscentedKalmanFilter()=delete
UnscentedKalmanFilter(const PropCovT &Q, ObsCovT R, const AlphaT &alpha, StateT state0, StateCovT P0)
Eigen::Matrix< FloatT, dim::control, dim::control > PropCovT
const StateCovT & getCurrentStateCovariance() const
Eigen::Matrix< FloatT, dim::obs, dim::obs > ObsCovT
typename SystemModelT::ControlNoiseT ControlNoiseT
Eigen::Matrix< FloatT, 3, 1 > AlphaT
typename SystemModelT::FloatT FloatT
typename SystemModelT::SigmaPointsT SigmaPointsT
Eigen::Matrix< FloatT, dim::state, dim::state > StateCovT
static constexpr float eps
Eigen::Matrix< FloatT, dim::obs, 1 > ObsT
typename SystemModelT::ControlT ControlT
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
Definition mongodb.cpp:68
This file is part of ArmarX.
static ObsT observationFunction(const StateT &state)
static StateT retraction(const StateT &state, const SigmaPointsT &sigmaPoints)
static StateT propagationFunction(const StateT &state, const ControlT &control, const ControlNoiseT &noise, FloatT dt)
static SigmaPointsT inverseRetraction(const StateT &state1, const StateT &state2)