UnscentedKalmanFilterTest.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 18.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 */
28
29#include <cstdlib> /* srand, rand */
30#include <ctime> /* time */
31
35
36#include <sciplot/sciplot.hpp>
37
38using T = float;
42
43constexpr long num_timesteps = 3000;
44constexpr T max_time = 1;
45constexpr T dt = max_time / num_timesteps;
46constexpr T c = (1 / max_time) * 2 * M_PI;
47
48constexpr T acc_noise_std = 0.01;
49constexpr T om_noise_std = 0.01;
50constexpr T obs_noise_std = 0.01;
51constexpr T initial_offset_angle = 0.0 * 10 * M_PI / 180;
52const Vector initial_offet_pos = 0.5 * Vector(1, 0.5, 0.7);
53
54void
56{
57 for (int i = 0; i < num_timesteps; i++)
58 {
59 SystemModelT::StateT state1, state2;
60 state1.pose = manif::SE3<T>::Random();
61 // state1.velocity.setRandom();
62 state2.pose = manif::SE3<T>::Random();
63 // state2.velocity.setRandom();
64 // sigma = state2 - state1
66 // state3 = state1 + sigma --> state3 = state2
67 SystemModelT::StateT state3 = SystemModelT::retraction(state1, sigma);
68 // ARMARX_CHECK((state2.velocity - state3.velocity).norm() < 10 * std::numeric_limits<T>::epsilon());
69 ARMARX_CHECK((state2.pose - state3.pose).coeffs().norm() <
70 10000 * std::numeric_limits<T>::epsilon())
71 << (state2.pose - state3.pose).coeffs().norm();
72
74 // TODO: fix bad accuracy
75 ARMARX_CHECK((sigma2 - sigma).norm() < 10000 * std::numeric_limits<T>::epsilon())
76 << (sigma2 - sigma).norm();
77 }
78}
79
80void
82{
83 for (int i = 1; i < num_timesteps; i++)
84 {
85 const T t = dt * i;
86 const T angle = t * c;
87 const Eigen::Matrix<T, 3, 1> pos(std::sin(angle), std::cos(angle), 0);
88 manif::SO3<T> rot = manif::SO3<T>(0, 0, angle);
89 ARMARX_CHECK(std::abs(rot.log().coeffs()(2) - angle) < 1e-6);
90 manif::SE3<T> pose(pos, rot);
91
92 manif::SE3Tangent<T> tangent = pose.log();
93 ARMARX_CHECK((tangent.coeffs().segment(0, 3) - pos).norm() < 1e-6);
94 ARMARX_CHECK(std::abs(tangent.coeffs()(5) - angle) < 1e-6);
95 }
96}
97
98void
99simulate_trajectory(std::vector<SystemModelT::StateT>& states,
100 std::vector<SystemModelT::ControlT>& omegas)
101{
103 // state.velocity = Vector(c, 0, 0);
104 state.pose = manif::SE3<T>(0, 1, 0, M_PI, M_PI, 0);
105 states.push_back(state);
106 for (int i = 1; i < num_timesteps; i++)
107 {
108 const T t = dt * i;
109 const T t_prev = dt * (i - 1);
110 const T angle = t * c;
111 // yaw goes from -pi to pi
112 const T yaw = 1 * M_PI * std::sin(angle);
113 const T roll = 0.1 * M_PI * std::sin(angle) + M_PI;
114 const T pitch = 0.1 * M_PI * std::sin(angle) + M_PI;
115 const Vector pos(std::sin(angle), std::cos(angle), 0);
116 manif::SE3Tangent<T> tangent(states.at(i - 1).pose.log());
117 ARMARX_CHECK((states.at(i - 1).pose.translation() - pos).norm() < 0.1);
118 // ARMARX_CHECK((tangent.coeffs().segment(0, 3) - pos).norm() < 0.1);
119 const Vector vel(std::cos(angle) * c, -std::sin(angle) * c, 0);
120 const Vector acc(-std::sin(angle) * (c * c), -std::cos(angle) * (c * c), 0);
121 const Vector acc_prev(-std::sin(t_prev * c) * (c * c), -std::cos(t_prev * c) * (c * c), 0);
122 // const Eigen::Vector3f vel((pos - state.position) / dt);
123 // Eigen::Vector3f acc = (vel - state.velocity) / dt;
124 manif::SO3<T> rot = manif::SO3<T>(roll, pitch, yaw);
125 // Eigen::Matrix<T, 3, 1> rot_tan = rot.log().coeffs();
126 // ARMARX_CHECK(std::abs(rot_tan(2) - yaw) < 1e-6);
128 // control.linear_accel = state.pose.rotation().inverse() * acc_prev;
129 Vector angular_velocity(0.1 * M_PI * std::cos(angle) * c,
130 0.1 * M_PI * std::cos(angle) * c,
131 M_PI * std::cos(angle) * c);
132 control.velocity.coeffs() << state.pose.asSO3().inverse().act(vel), angular_velocity;
133
135 state, control, SystemModelT::ControlNoiseT::Zero(), dt);
136 manif::SE3Tangent<T> pro_tangent = propagated.pose.log();
137 T pos_diff1 = (propagated.pose.translation() - pos).norm();
138 // ARMARX_CHECK(pos_diff1 < pos_diff2);
139 ARMARX_CHECK(pos_diff1 < 2e-5)
140 << "Position is not the same: " << pos << " vs " << propagated.pose.translation();
141 // ARMARX_CHECK((propagated.velocity - vel).norm() < 1e-4) << "Velocity is not the same: " << vel << " vs " << propagated.velocity;
142 // ARMARX_CHECK(propagated.pose.asSO3().lminus(rot).coeffs().norm() < 1e-2) << "Rotation is not the same: " << rot.rotation() << " vs " << propagated.pose.rotation();
143 // state.velocity = vel;
144 state.pose = manif::SE3<T>(pos, rot);
145 states.push_back(propagated);
146
147 tangent = state.pose.log();
148 ARMARX_CHECK((state.pose.translation() - pos).norm() < 1e-6);
149 // ARMARX_CHECK(std::abs(tangent.coeffs()(5) - yaw) < 1e-2);
150
151 // add noise
152 control.velocity.coeffs().segment(0, 3) += acc_noise_std * Vector::Random();
153 control.velocity.coeffs().segment(3, 3) += om_noise_std * Vector::Random();
154 omegas.push_back(control);
155 }
156}
157
158void
159simulate_observation(const std::vector<SystemModelT::StateT>& states,
160 std::vector<SystemModelT::ObsT>& observations)
161{
162 for (const auto& state : states)
163 {
165 SystemModelT::ObsT true_obs;
166 true_obs = state.pose.log().coeffs();
167 // true_obs.segment(0, 3) = state.pose.translation();
168 // true_obs.segment(3, 3) = state.pose.asSO3().log().coeffs();
169 ARMARX_CHECK((obs - true_obs).norm() < std::numeric_limits<T>::epsilon());
170 observations.push_back(obs + obs_noise_std * SystemModelT::ObsT::Random());
171 }
172}
173
174int
176{
177 srand(time(NULL));
178
179 test_retract();
180 // test_se3();
181
182 std::vector<SystemModelT::StateT> states;
183 std::vector<SystemModelT::ControlT> omegas;
184 std::vector<SystemModelT::ObsT> observations;
185 simulate_trajectory(states, omegas);
186 simulate_observation(states, observations);
187
188 ARMARX_INFO << "Num States: " << states.size() << " Num Controls: " << omegas.size()
189 << " Num Obs: " << observations.size();
192 Q.block<3, 3>(0, 0) *= acc_noise_std * acc_noise_std;
193 Q.block<3, 3>(3, 3) *= om_noise_std * om_noise_std;
194
199 P0.block<3, 3>(3, 3) *= initial_offset_angle * initial_offset_angle;
200 P0.block<3, 3>(0, 0) *= initial_offet_pos.norm() * initial_offet_pos.norm();
201 // P0.block<3, 3>(6, 6) *= 0.0;
204
206 manif::SO3<T> rot(0, 0, initial_offset_angle);
207 state0.pose = states.at(0).pose.lplus(manif::SE3<T>(initial_offet_pos, rot).log());
208 // state0.velocity = states.at(0).velocity;
209 UnscentedKalmanFilter<SystemModelT> ukf(Q, R, alpha, state0, P0);
210
211 std::vector<SystemModelT::StateT> ukf_states;
212 std::vector<UnscentedKalmanFilter<SystemModelT>::StateCovT> ukf_Ps;
213 ukf_states.push_back(state0);
214 ukf_Ps.push_back(P0);
215
216 std::vector<T> x_true, y_true, z_true, x_obs, y_obs, z_obs, x_ukf, y_ukf, z_ukf;
217 std::vector<T> vx_true, vy_true, vz_true, vx_ukf, vy_ukf, vz_ukf;
218 std::vector<T> a_true, b_true, c_true, a_obs, b_obs, c_obs, a_ukf, b_ukf, c_ukf;
219
220 for (int i = 1; i < num_timesteps; i++)
221 {
222 // propagate
223 TIMING_START(LOOP);
224 TIMING_START(PROPAGATION);
225 ukf.propagation(omegas.at(i - 1), dt);
226 TIMING_END(PROPAGATION);
227 if ((i - 1) % 100 == 0)
228 {
229 TIMING_START(UPDATE);
230 ukf.update(observations.at(i));
231 TIMING_END(UPDATE);
232 TIMING_START(REST);
233 const SystemModelT::StateT& current_state = ukf.getCurrentState();
234 // ARMARX_INFO << "Velocity Diff: " << (states.at(i).velocity - current_state.velocity).norm();
235 ARMARX_INFO << "Pose Diff: "
236 << (states.at(i).pose - current_state.pose).coeffs().norm();
237 // ARMARX_INFO << "Vel: " << current_state.velocity - states.at(i).velocity;
238 ARMARX_INFO << "Max Cov " << ukf.getCurrentStateCovariance().cwiseAbs().maxCoeff();
240 << "Diag: "
242 // ARMARX_CHECK((states.at(i).position - current_state.position).norm() < 1);
243
244 x_true.push_back(states.at(i).pose.log().coeffs()(0));
245 y_true.push_back(states.at(i).pose.log().coeffs()(1));
246 z_true.push_back(states.at(i).pose.log().coeffs()(2));
247
248 x_obs.push_back(observations.at(i)(0));
249 y_obs.push_back(observations.at(i)(1));
250 z_obs.push_back(observations.at(i)(2));
251
252 x_ukf.push_back(ukf.getCurrentState().pose.log().coeffs()(0));
253 y_ukf.push_back(ukf.getCurrentState().pose.log().coeffs()(1));
254 z_ukf.push_back(ukf.getCurrentState().pose.log().coeffs()(2));
255
256 // vx_true.push_back(states.at(i).velocity(0, 0));
257 // vy_true.push_back(states.at(i).velocity(1, 0));
258 // vz_true.push_back(states.at(i).velocity(2, 0));
259 //
260 // vx_ukf.push_back(ukf.getCurrentState().velocity(0, 0));
261 // vy_ukf.push_back(ukf.getCurrentState().velocity(1, 0));
262 // vz_ukf.push_back(ukf.getCurrentState().velocity(2, 0));
263
264 a_true.push_back(states.at(i).pose.log().coeffs()(3));
265 b_true.push_back(states.at(i).pose.log().coeffs()(4));
266 c_true.push_back(states.at(i).pose.log().coeffs()(5));
267
268 a_obs.push_back(observations.at(i)(3, 0));
269 b_obs.push_back(observations.at(i)(4, 0));
270 c_obs.push_back(observations.at(i)(5, 0));
271
272 a_ukf.push_back(ukf.getCurrentState().pose.log().coeffs()(3));
273 b_ukf.push_back(ukf.getCurrentState().pose.log().coeffs()(4));
274 c_ukf.push_back(ukf.getCurrentState().pose.log().coeffs()(5));
275 TIMING_END(REST);
276 }
277
278 ukf_states.push_back(ukf.getCurrentState());
279 ukf_Ps.push_back(ukf.getCurrentStateCovariance());
280
281 TIMING_END(LOOP);
282 }
283
284 sciplot::Plot3D position_plot;
285 sciplot::Plot pos_plot;
286
287 position_plot.drawCurve(x_true, y_true, z_true).label("True").lineWidth(1);
288 position_plot.drawCurve(x_obs, y_obs, z_obs).label("Obs").lineWidth(1);
289 position_plot.drawCurve(x_ukf, y_ukf, z_ukf).label("UKF").lineWidth(1);
290
291 pos_plot.drawCurve(x_true, y_true).label("True");
292 pos_plot.drawCurve(x_obs, y_obs).label("Obs");
293 pos_plot.drawCurve(x_ukf, y_ukf).label("UKF");
294
295 // sciplot::Plot3D vel_plot;
296 //
297 // vel_plot.drawCurve(vx_true, vy_true, vz_true).label("True").lineWidth(1);
298 // vel_plot.drawCurve(vx_ukf, vy_ukf, vz_ukf).label("UKF").lineWidth(1);
299
300
301 sciplot::Plot3D orientation_plot;
302
303 orientation_plot.drawCurve(a_true, b_true, c_true).label("True").lineWidth(1);
304 orientation_plot.drawCurve(a_obs, b_obs, c_obs).label("Obs").lineWidth(1);
305 orientation_plot.drawCurve(a_ukf, b_ukf, c_ukf).label("UKF").lineWidth(1);
306
307 pos_plot.show();
308 position_plot.show();
309 // vel_plot.show();
310 orientation_plot.show();
311}
#define float
Definition 16_Level.h:22
#define M_PI
Definition MathTools.h:17
void test_retract()
constexpr T c
void simulate_trajectory(std::vector< SystemModelT::StateT > &states, std::vector< SystemModelT::ControlT > &omegas)
constexpr T dt
void simulate_observation(const std::vector< SystemModelT::StateT > &states, std::vector< SystemModelT::ObsT > &observations)
constexpr T max_time
constexpr T obs_noise_std
Eigen::Matrix< T, 3, 1 > Vector
const Vector initial_offet_pos
constexpr T om_noise_std
constexpr T initial_offset_angle
constexpr T acc_noise_std
constexpr long num_timesteps
SystemModelSE3< T > SystemModelT
Eigen::Matrix< T, 3, 3 > Matrix
const StateT & getCurrentState() const
void propagation(const ControlT &omega, FloatT dt)
Eigen::Matrix< FloatT, dim::control, dim::control > PropCovT
const StateCovT & getCurrentStateCovariance() const
Eigen::Matrix< FloatT, dim::obs, dim::obs > ObsCovT
Eigen::Matrix< FloatT, 3, 1 > AlphaT
Eigen::Matrix< FloatT, dim::state, dim::state > StateCovT
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define TIMING_START(name)
Helper macro to do timing tests.
Definition TimeUtil.h:289
#define TIMING_END(name)
Prints duration.
Definition TimeUtil.h:306
This file is part of ArmarX.
double norm(const Point &a)
Definition point.hpp:102
double angle(const Point &a, const Point &b, const Point &c)
Definition point.hpp:109
manif::SE3< floatT > pose
Definition SystemModel.h:41
ControlSE3< FloatT > ControlT
Definition SystemModel.h:89
StateSE3< FloatT > StateT
Definition SystemModel.h:88
static ObsT observationFunction(const StateT &state)
static StateT retraction(const StateT &state, const SigmaPointsT &sigmaPoints)
Eigen::Matrix< FloatT, dim::state, 1 > SigmaPointsT
Definition SystemModel.h:93
static StateT propagationFunction(const StateT &state, const ControlT &control, const ControlNoiseT &noise, FloatT dt)
Eigen::Matrix< FloatT, dim::obs, 1 > ObsT
Definition SystemModel.h:90
static SigmaPointsT inverseRetraction(const StateT &state1, const StateT &state2)