30 #include <boost/math/distributions/chi_squared.hpp>
36 void StateHelper::EKFPropagation(std::shared_ptr<State> state,
const std::vector<std::shared_ptr<Type>> &order_NEW,
37 const std::vector<std::shared_ptr<Type>> &order_OLD,
const Eigen::MatrixXd &Phi,
38 const Eigen::MatrixXd &Q) {
41 if (order_NEW.empty() || order_OLD.empty()) {
42 PRINT_ERROR(
RED "StateHelper::EKFPropagation() - Called with empty variable arrays!\n" RESET);
43 std::exit(EXIT_FAILURE);
47 int size_order_NEW = order_NEW.at(0)->size();
48 for (
size_t i = 0; i < order_NEW.size() - 1; i++) {
49 if (order_NEW.at(i)->id() + order_NEW.at(i)->size() != order_NEW.at(i + 1)->id()) {
50 PRINT_ERROR(
RED "StateHelper::EKFPropagation() - Called with non-contiguous state elements!\n" RESET);
52 RED "StateHelper::EKFPropagation() - This code only support a state transition which is in the same order as the state\n" RESET);
53 std::exit(EXIT_FAILURE);
55 size_order_NEW += order_NEW.at(i + 1)->size();
59 int size_order_OLD = order_OLD.at(0)->size();
60 for (
size_t i = 0; i < order_OLD.size() - 1; i++) {
61 size_order_OLD += order_OLD.at(i + 1)->size();
65 assert(size_order_NEW == Phi.rows());
66 assert(size_order_OLD == Phi.cols());
67 assert(size_order_NEW == Q.cols());
68 assert(size_order_NEW == Q.rows());
72 std::vector<int> Phi_id;
73 for (
const auto &var : order_OLD) {
74 Phi_id.push_back(current_it);
75 current_it += var->size();
80 Eigen::MatrixXd Cov_PhiT = Eigen::MatrixXd::Zero(state->_Cov.rows(), Phi.rows());
81 for (
size_t i = 0; i < order_OLD.size(); i++) {
82 std::shared_ptr<Type> var = order_OLD.at(i);
84 state->_Cov.block(0, var->id(), state->_Cov.rows(), var->size()) * Phi.block(0, Phi_id[i], Phi.rows(), var->size()).transpose();
88 Eigen::MatrixXd Phi_Cov_PhiT = Q.selfadjointView<Eigen::Upper>();
89 for (
size_t i = 0; i < order_OLD.size(); i++) {
90 std::shared_ptr<Type> var = order_OLD.at(i);
91 Phi_Cov_PhiT.noalias() += Phi.block(0, Phi_id[i], Phi.rows(), var->size()) * Cov_PhiT.block(var->id(), 0, var->size(), Phi.rows());
95 int start_id = order_NEW.at(0)->id();
96 int phi_size = Phi.rows();
97 int total_size = state->_Cov.rows();
98 state->_Cov.block(start_id, 0, phi_size, total_size) = Cov_PhiT.transpose();
99 state->_Cov.block(0, start_id, total_size, phi_size) = Cov_PhiT;
100 state->_Cov.block(start_id, start_id, phi_size, phi_size) = Phi_Cov_PhiT;
103 Eigen::VectorXd diags = state->_Cov.diagonal();
104 bool found_neg =
false;
105 for (
int i = 0; i < diags.rows(); i++) {
106 if (diags(i) < 0.0) {
112 std::exit(EXIT_FAILURE);
116 void StateHelper::EKFUpdate(std::shared_ptr<State> state,
const std::vector<std::shared_ptr<Type>> &H_order,
const Eigen::MatrixXd &H,
117 const Eigen::VectorXd &res,
const Eigen::MatrixXd &R) {
122 assert(res.rows() == R.rows());
123 assert(H.rows() == res.rows());
124 Eigen::MatrixXd M_a = Eigen::MatrixXd::Zero(state->_Cov.rows(), res.rows());
128 std::vector<int> H_id;
129 for (
const auto &meas_var : H_order) {
130 H_id.push_back(current_it);
131 current_it += meas_var->size();
137 for (
const auto &var : state->_variables) {
139 Eigen::MatrixXd M_i = Eigen::MatrixXd::Zero(var->size(), res.rows());
140 for (
size_t i = 0; i < H_order.size(); i++) {
141 std::shared_ptr<Type> meas_var = H_order[i];
142 M_i.noalias() += state->_Cov.block(var->id(), meas_var->id(), var->size(), meas_var->size()) *
143 H.block(0, H_id[i], H.rows(), meas_var->size()).transpose();
145 M_a.block(var->id(), 0, var->size(), res.rows()) = M_i;
151 Eigen::MatrixXd P_small = StateHelper::get_marginal_covariance(state, H_order);
154 Eigen::MatrixXd S(R.rows(), R.rows());
155 S.triangularView<Eigen::Upper>() = H * P_small * H.transpose();
156 S.triangularView<Eigen::Upper>() += R;
160 Eigen::MatrixXd Sinv = Eigen::MatrixXd::Identity(R.rows(), R.rows());
161 S.selfadjointView<Eigen::Upper>().llt().solveInPlace(Sinv);
162 Eigen::MatrixXd K = M_a * Sinv.selfadjointView<Eigen::Upper>();
166 state->_Cov.triangularView<Eigen::Upper>() -= K * M_a.transpose();
167 state->_Cov = state->_Cov.selfadjointView<Eigen::Upper>();
172 Eigen::VectorXd diags = state->_Cov.diagonal();
173 bool found_neg =
false;
174 for (
int i = 0; i < diags.rows(); i++) {
175 if (diags(i) < 0.0) {
181 std::exit(EXIT_FAILURE);
185 Eigen::VectorXd dx = K * res;
186 for (
size_t i = 0; i < state->_variables.size(); i++) {
187 state->_variables.at(i)->update(dx.block(state->_variables.at(i)->id(), 0, state->_variables.at(i)->size(), 1));
192 if (state->_options.do_calib_camera_intrinsics) {
193 for (
auto const &calib : state->_cam_intrinsics) {
194 state->_cam_intrinsics_cameras.at(calib.first)->set_value(calib.second->value());
199 void StateHelper::set_initial_covariance(std::shared_ptr<State> state,
const Eigen::MatrixXd &covariance,
200 const std::vector<std::shared_ptr<ov_type::Type>> &order) {
214 for (
size_t i = 0; i < order.size(); i++) {
216 for (
size_t k = 0; k < order.size(); k++) {
217 state->_Cov.block(order[i]->
id(), order[k]->
id(), order[i]->size(), order[k]->size()) =
218 covariance.block(i_index, k_index, order[i]->size(), order[k]->size());
219 k_index += order[k]->size();
221 i_index += order[i]->size();
223 state->_Cov = state->_Cov.selfadjointView<Eigen::Upper>();
226 Eigen::MatrixXd StateHelper::get_marginal_covariance(std::shared_ptr<State> state,
227 const std::vector<std::shared_ptr<Type>> &small_variables) {
231 for (
size_t i = 0; i < small_variables.size(); i++) {
232 cov_size += small_variables[i]->size();
236 Eigen::MatrixXd Small_cov = Eigen::MatrixXd::Zero(cov_size, cov_size);
241 for (
size_t i = 0; i < small_variables.size(); i++) {
243 for (
size_t k = 0; k < small_variables.size(); k++) {
244 Small_cov.block(i_index, k_index, small_variables[i]->size(), small_variables[k]->size()) =
245 state->_Cov.block(small_variables[i]->
id(), small_variables[k]->
id(), small_variables[i]->size(), small_variables[k]->size());
246 k_index += small_variables[k]->size();
248 i_index += small_variables[i]->size();
256 Eigen::MatrixXd StateHelper::get_full_covariance(std::shared_ptr<State> state) {
259 int cov_size = (int)state->_Cov.rows();
262 Eigen::MatrixXd full_cov = Eigen::MatrixXd::Zero(cov_size, cov_size);
265 full_cov.block(0, 0, state->_Cov.rows(), state->_Cov.rows()) = state->_Cov;
271 void StateHelper::marginalize(std::shared_ptr<State> state, std::shared_ptr<Type> marg) {
274 if (std::find(state->_variables.begin(), state->_variables.end(), marg) == state->_variables.end()) {
275 PRINT_ERROR(
RED "StateHelper::marginalize() - Called on variable that is not in the state\n" RESET);
276 PRINT_ERROR(
RED "StateHelper::marginalize() - Marginalization, does NOT work on sub-variables yet...\n" RESET);
277 std::exit(EXIT_FAILURE);
293 int marg_size = marg->size();
294 int marg_id = marg->id();
295 int x2_size = (int)state->_Cov.rows() - marg_id - marg_size;
297 Eigen::MatrixXd Cov_new(state->_Cov.rows() - marg_size, state->_Cov.rows() - marg_size);
300 Cov_new.block(0, 0, marg_id, marg_id) = state->_Cov.block(0, 0, marg_id, marg_id);
303 Cov_new.block(0, marg_id, marg_id, x2_size) = state->_Cov.block(0, marg_id + marg_size, marg_id, x2_size);
306 Cov_new.block(marg_id, 0, x2_size, marg_id) = Cov_new.block(0, marg_id, marg_id, x2_size).transpose();
309 Cov_new.block(marg_id, marg_id, x2_size, x2_size) = state->_Cov.block(marg_id + marg_size, marg_id + marg_size, x2_size, x2_size);
313 state->_Cov = Cov_new;
315 assert(state->_Cov.rows() == Cov_new.rows());
319 std::vector<std::shared_ptr<Type>> remaining_variables;
320 for (
size_t i = 0; i < state->_variables.size(); i++) {
322 if (state->_variables.at(i) != marg) {
323 if (state->_variables.at(i)->id() > marg_id) {
325 state->_variables.at(i)->set_local_id(state->_variables.at(i)->id() - marg_size);
327 remaining_variables.push_back(state->_variables.at(i));
335 marg->set_local_id(-1);
338 state->_variables = remaining_variables;
341 std::shared_ptr<Type> StateHelper::clone(std::shared_ptr<State> state, std::shared_ptr<Type> variable_to_clone) {
344 int total_size = variable_to_clone->size();
345 int old_size = (int)state->_Cov.rows();
346 int new_loc = (int)state->_Cov.rows();
349 state->_Cov.conservativeResizeLike(Eigen::MatrixXd::Zero(old_size + total_size, old_size + total_size));
352 const std::vector<std::shared_ptr<Type>> new_variables = state->_variables;
353 std::shared_ptr<Type> new_clone =
nullptr;
356 for (
size_t k = 0; k < state->_variables.size(); k++) {
360 std::shared_ptr<Type> type_check = state->_variables.at(k)->check_if_subvariable(variable_to_clone);
361 if (state->_variables.at(k) == variable_to_clone) {
362 type_check = state->_variables.at(k);
363 }
else if (type_check != variable_to_clone) {
368 int old_loc = type_check->id();
371 state->_Cov.block(new_loc, new_loc, total_size, total_size) = state->_Cov.block(old_loc, old_loc, total_size, total_size);
372 state->_Cov.block(0, new_loc, old_size, total_size) = state->_Cov.block(0, old_loc, old_size, total_size);
373 state->_Cov.block(new_loc, 0, total_size, old_size) = state->_Cov.block(old_loc, 0, total_size, old_size);
376 new_clone = type_check->clone();
377 new_clone->set_local_id(new_loc);
382 if (new_clone ==
nullptr) {
384 PRINT_ERROR(
RED "StateHelper::clone() - Ensure that the variable specified is a variable, or sub-variable..\n" RESET);
385 std::exit(EXIT_FAILURE);
389 state->_variables.push_back(new_clone);
393 bool StateHelper::initialize(std::shared_ptr<State> state, std::shared_ptr<Type> new_variable,
394 const std::vector<std::shared_ptr<Type>> &H_order, Eigen::MatrixXd &H_R, Eigen::MatrixXd &H_L,
395 Eigen::MatrixXd &R, Eigen::VectorXd &res,
double chi_2_mult) {
398 if (std::find(state->_variables.begin(), state->_variables.end(), new_variable) != state->_variables.end()) {
399 PRINT_ERROR(
"StateHelper::initialize_invertible() - Called on variable that is already in the state\n");
400 PRINT_ERROR(
"StateHelper::initialize_invertible() - Found this variable at %d in covariance\n", new_variable->id());
401 std::exit(EXIT_FAILURE);
406 assert(R.rows() == R.cols());
407 assert(R.rows() > 0);
408 for (
int r = 0; r < R.rows(); r++) {
409 for (
int c = 0; c < R.cols(); c++) {
410 if (r == c && R(0, 0) != R(r, c)) {
412 PRINT_ERROR(
RED "StateHelper::initialize() - Found a value of %.2f verses value of %.2f\n" RESET, R(r, c), R(0, 0));
413 std::exit(EXIT_FAILURE);
414 }
else if (r != c && R(r, c) != 0.0) {
416 PRINT_ERROR(
RED "StateHelper::initialize() - Found a value of %.2f at row %d and column %d\n" RESET, R(r, c), r, c);
417 std::exit(EXIT_FAILURE);
426 size_t new_var_size = new_variable->size();
427 assert((
int)new_var_size == H_L.cols());
429 Eigen::JacobiRotation<double> tempHo_GR;
430 for (
int n = 0; n < H_L.cols(); ++n) {
431 for (
int m = (
int)H_L.rows() - 1; m > n; m--) {
433 tempHo_GR.makeGivens(H_L(m - 1, n), H_L(m, n));
437 (H_L.block(m - 1, n, 2, H_L.cols() - n)).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
438 (res.block(m - 1, 0, 2, 1)).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
439 (H_R.block(m - 1, 0, 2, H_R.cols())).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
445 Eigen::MatrixXd Hxinit = H_R.block(0, 0, new_var_size, H_R.cols());
446 Eigen::MatrixXd H_finit = H_L.block(0, 0, new_var_size, new_var_size);
447 Eigen::VectorXd resinit = res.block(0, 0, new_var_size, 1);
448 Eigen::MatrixXd Rinit = R.block(0, 0, new_var_size, new_var_size);
451 Eigen::MatrixXd Hup = H_R.block(new_var_size, 0, H_R.rows() - new_var_size, H_R.cols());
452 Eigen::VectorXd resup = res.block(new_var_size, 0, res.rows() - new_var_size, 1);
453 Eigen::MatrixXd Rup = R.block(new_var_size, new_var_size, R.rows() - new_var_size, R.rows() - new_var_size);
459 Eigen::MatrixXd P_up = get_marginal_covariance(state, H_order);
460 assert(Rup.rows() == Hup.rows());
461 assert(Hup.cols() == P_up.cols());
462 Eigen::MatrixXd S = Hup * P_up * Hup.transpose() + Rup;
463 double chi2 = resup.dot(S.llt().solve(resup));
466 boost::math::chi_squared chi_squared_dist(res.rows());
467 double chi2_check = boost::math::quantile(chi_squared_dist, 0.95);
468 if (chi2 > chi_2_mult * chi2_check) {
475 StateHelper::initialize_invertible(state, new_variable, H_order, Hxinit, H_finit, Rinit, resinit);
478 if (Hup.rows() > 0) {
479 StateHelper::EKFUpdate(state, H_order, Hup, resup, Rup);
484 void StateHelper::initialize_invertible(std::shared_ptr<State> state, std::shared_ptr<Type> new_variable,
485 const std::vector<std::shared_ptr<Type>> &H_order,
const Eigen::MatrixXd &H_R,
486 const Eigen::MatrixXd &H_L,
const Eigen::MatrixXd &R,
const Eigen::VectorXd &res) {
489 if (std::find(state->_variables.begin(), state->_variables.end(), new_variable) != state->_variables.end()) {
490 PRINT_ERROR(
"StateHelper::initialize_invertible() - Called on variable that is already in the state\n");
491 PRINT_ERROR(
"StateHelper::initialize_invertible() - Found this variable at %d in covariance\n", new_variable->id());
492 std::exit(EXIT_FAILURE);
497 assert(R.rows() == R.cols());
498 assert(R.rows() > 0);
499 for (
int r = 0; r < R.rows(); r++) {
500 for (
int c = 0; c < R.cols(); c++) {
501 if (r == c && R(0, 0) != R(r, c)) {
502 PRINT_ERROR(
RED "StateHelper::initialize_invertible() - Your noise is not isotropic!\n" RESET);
503 PRINT_ERROR(
RED "StateHelper::initialize_invertible() - Found a value of %.2f verses value of %.2f\n" RESET, R(r, c), R(0, 0));
504 std::exit(EXIT_FAILURE);
505 }
else if (r != c && R(r, c) != 0.0) {
506 PRINT_ERROR(
RED "StateHelper::initialize_invertible() - Your noise is not diagonal!\n" RESET);
507 PRINT_ERROR(
RED "StateHelper::initialize_invertible() - Found a value of %.2f at row %d and column %d\n" RESET, R(r, c), r, c);
508 std::exit(EXIT_FAILURE);
516 assert(res.rows() == R.rows());
517 assert(H_L.rows() == res.rows());
518 assert(H_L.rows() == H_R.rows());
519 Eigen::MatrixXd M_a = Eigen::MatrixXd::Zero(state->_Cov.rows(), res.rows());
523 std::vector<int> H_id;
524 for (
const auto &meas_var : H_order) {
525 H_id.push_back(current_it);
526 current_it += meas_var->size();
532 for (
const auto &var : state->_variables) {
534 Eigen::MatrixXd M_i = Eigen::MatrixXd::Zero(var->size(), res.rows());
535 for (
size_t i = 0; i < H_order.size(); i++) {
536 std::shared_ptr<Type> meas_var = H_order.at(i);
537 M_i += state->_Cov.block(var->id(), meas_var->id(), var->size(), meas_var->size()) *
538 H_R.block(0, H_id[i], H_R.rows(), meas_var->size()).transpose();
540 M_a.block(var->id(), 0, var->size(), res.rows()) = M_i;
546 Eigen::MatrixXd P_small = StateHelper::get_marginal_covariance(state, H_order);
549 Eigen::MatrixXd M(H_R.rows(), H_R.rows());
550 M.triangularView<Eigen::Upper>() = H_R * P_small * H_R.transpose();
551 M.triangularView<Eigen::Upper>() += R;
554 assert(H_L.rows() == H_L.cols());
555 assert(H_L.rows() == new_variable->size());
556 Eigen::MatrixXd H_Linv = H_L.inverse();
557 Eigen::MatrixXd P_LL = H_Linv * M.selfadjointView<Eigen::Upper>() * H_Linv.transpose();
560 size_t oldSize = state->_Cov.rows();
561 state->_Cov.conservativeResizeLike(Eigen::MatrixXd::Zero(oldSize + new_variable->size(), oldSize + new_variable->size()));
562 state->_Cov.block(0, oldSize, oldSize, new_variable->size()).noalias() = -M_a * H_Linv.transpose();
563 state->_Cov.block(oldSize, 0, new_variable->size(), oldSize) = state->_Cov.block(0, oldSize, oldSize, new_variable->size()).transpose();
564 state->_Cov.block(oldSize, oldSize, new_variable->size(), new_variable->size()) = P_LL;
568 new_variable->update(H_Linv * res);
571 new_variable->set_local_id(oldSize);
572 state->_variables.push_back(new_variable);
579 void StateHelper::augment_clone(std::shared_ptr<State> state, Eigen::Matrix<double, 3, 1> last_w) {
582 if (state->_clones_IMU.find(state->_timestamp) != state->_clones_IMU.end()) {
583 PRINT_ERROR(
RED "TRIED TO INSERT A CLONE AT THE SAME TIME AS AN EXISTING CLONE, EXITING!#!@#!@#\n" RESET);
584 std::exit(EXIT_FAILURE);
589 std::shared_ptr<Type> posetemp = StateHelper::clone(state, state->_imu->pose());
592 std::shared_ptr<PoseJPL> pose = std::dynamic_pointer_cast<PoseJPL>(posetemp);
593 if (pose ==
nullptr) {
594 PRINT_ERROR(
RED "INVALID OBJECT RETURNED FROM STATEHELPER CLONE, EXITING!#!@#!@#\n" RESET);
595 std::exit(EXIT_FAILURE);
599 state->_clones_IMU[state->_timestamp] = pose;
604 if (state->_options.do_calib_camera_timeoffset) {
606 Eigen::Matrix<double, 6, 1> dnc_dt = Eigen::MatrixXd::Zero(6, 1);
607 dnc_dt.block(0, 0, 3, 1) = last_w;
608 dnc_dt.block(3, 0, 3, 1) = state->_imu->vel();
611 state->_Cov.block(0, pose->id(), state->_Cov.rows(), 6) +=
612 state->_Cov.block(0, state->_calib_dt_CAMtoIMU->id(), state->_Cov.rows(), 1) * dnc_dt.transpose();
613 state->_Cov.block(pose->id(), 0, 6, state->_Cov.rows()) +=
614 dnc_dt * state->_Cov.block(state->_calib_dt_CAMtoIMU->id(), 0, 1, state->_Cov.rows());
618 void StateHelper::marginalize_old_clone(std::shared_ptr<State> state) {
619 if ((
int)state->_clones_IMU.size() > state->_options.max_clone_size) {
620 double marginal_time = state->margtimestep();
622 std::lock_guard<std::mutex> lock(state->_mutex_state);
623 assert(marginal_time != INFINITY);
624 StateHelper::marginalize(state, state->_clones_IMU.at(marginal_time));
627 state->_clones_IMU.erase(marginal_time);
631 void StateHelper::marginalize_slam(std::shared_ptr<State> state) {
634 int ct_marginalized = 0;
635 auto it0 = state->_features_SLAM.begin();
636 while (it0 != state->_features_SLAM.end()) {
637 if ((*it0).second->should_marg && (
int)(*it0).first > 4 * state->_options.max_aruco_features) {
638 StateHelper::marginalize(state, (*it0).second);
639 it0 = state->_features_SLAM.erase(it0);