UpdaterHelper.cpp
Go to the documentation of this file.
1 /*
2  * OpenVINS: An Open Platform for Visual-Inertial Research
3  * Copyright (C) 2018-2023 Patrick Geneva
4  * Copyright (C) 2018-2023 Guoquan Huang
5  * Copyright (C) 2018-2023 OpenVINS Contributors
6  * Copyright (C) 2018-2019 Kevin Eckenhoff
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include "UpdaterHelper.h"
23 
24 #include "state/State.h"
25 
26 #include "utils/quat_ops.h"
27 
28 using namespace ov_core;
29 using namespace ov_type;
30 using namespace ov_msckf;
31 
32 void UpdaterHelper::get_feature_jacobian_representation(std::shared_ptr<State> state, UpdaterHelperFeature &feature, Eigen::MatrixXd &H_f,
33  std::vector<Eigen::MatrixXd> &H_x, std::vector<std::shared_ptr<Type>> &x_order) {
34 
35  // Global XYZ representation
36  if (feature.feat_representation == LandmarkRepresentation::Representation::GLOBAL_3D) {
37  H_f.resize(3, 3);
38  H_f.setIdentity();
39  return;
40  }
41 
42  // Global inverse depth representation
43  if (feature.feat_representation == LandmarkRepresentation::Representation::GLOBAL_FULL_INVERSE_DEPTH) {
44 
45  // Get the feature linearization point
46  Eigen::Matrix<double, 3, 1> p_FinG = (state->_options.do_fej) ? feature.p_FinG_fej : feature.p_FinG;
47 
48  // Get inverse depth representation (should match what is in Landmark.cpp)
49  double g_rho = 1 / p_FinG.norm();
50  double g_phi = std::acos(g_rho * p_FinG(2));
51  // double g_theta = std::asin(g_rho*p_FinG(1)/std::sin(g_phi));
52  double g_theta = std::atan2(p_FinG(1), p_FinG(0));
53  Eigen::Matrix<double, 3, 1> p_invFinG;
54  p_invFinG(0) = g_theta;
55  p_invFinG(1) = g_phi;
56  p_invFinG(2) = g_rho;
57 
58  // Get inverse depth bearings
59  double sin_th = std::sin(p_invFinG(0, 0));
60  double cos_th = std::cos(p_invFinG(0, 0));
61  double sin_phi = std::sin(p_invFinG(1, 0));
62  double cos_phi = std::cos(p_invFinG(1, 0));
63  double rho = p_invFinG(2, 0);
64 
65  // Construct the Jacobian
66  H_f.resize(3, 3);
67  H_f << -(1.0 / rho) * sin_th * sin_phi, (1.0 / rho) * cos_th * cos_phi, -(1.0 / (rho * rho)) * cos_th * sin_phi,
68  (1.0 / rho) * cos_th * sin_phi, (1.0 / rho) * sin_th * cos_phi, -(1.0 / (rho * rho)) * sin_th * sin_phi, 0.0,
69  -(1.0 / rho) * sin_phi, -(1.0 / (rho * rho)) * cos_phi;
70  return;
71  }
72 
73  //======================================================================
74  //======================================================================
75  //======================================================================
76 
77  // Assert that we have an anchor pose for this feature
78  assert(feature.anchor_cam_id != -1);
79 
80  // Anchor pose orientation and position, and camera calibration for our anchor camera
81  Eigen::Matrix3d R_ItoC = state->_calib_IMUtoCAM.at(feature.anchor_cam_id)->Rot();
82  Eigen::Vector3d p_IinC = state->_calib_IMUtoCAM.at(feature.anchor_cam_id)->pos();
83  Eigen::Matrix3d R_GtoI = state->_clones_IMU.at(feature.anchor_clone_timestamp)->Rot();
84  Eigen::Vector3d p_IinG = state->_clones_IMU.at(feature.anchor_clone_timestamp)->pos();
85  Eigen::Vector3d p_FinA = feature.p_FinA;
86 
87  // If I am doing FEJ, I should FEJ the anchor states (should we fej calibration???)
88  // Also get the FEJ position of the feature if we are
89  if (state->_options.do_fej) {
90  // "Best" feature in the global frame
91  Eigen::Vector3d p_FinG_best = R_GtoI.transpose() * R_ItoC.transpose() * (feature.p_FinA - p_IinC) + p_IinG;
92  // Transform the best into our anchor frame using FEJ
93  R_GtoI = state->_clones_IMU.at(feature.anchor_clone_timestamp)->Rot_fej();
94  p_IinG = state->_clones_IMU.at(feature.anchor_clone_timestamp)->pos_fej();
95  p_FinA = (R_GtoI.transpose() * R_ItoC.transpose()).transpose() * (p_FinG_best - p_IinG) + p_IinC;
96  }
97  Eigen::Matrix3d R_CtoG = R_GtoI.transpose() * R_ItoC.transpose();
98 
99  // Jacobian for our anchor pose
100  Eigen::Matrix<double, 3, 6> H_anc;
101  H_anc.block(0, 0, 3, 3).noalias() = -R_GtoI.transpose() * skew_x(R_ItoC.transpose() * (p_FinA - p_IinC));
102  H_anc.block(0, 3, 3, 3).setIdentity();
103 
104  // Add anchor Jacobians to our return vector
105  x_order.push_back(state->_clones_IMU.at(feature.anchor_clone_timestamp));
106  H_x.push_back(H_anc);
107 
108  // Get calibration Jacobians (for anchor clone)
109  if (state->_options.do_calib_camera_pose) {
110  Eigen::Matrix<double, 3, 6> H_calib;
111  H_calib.block(0, 0, 3, 3).noalias() = -R_CtoG * skew_x(p_FinA - p_IinC);
112  H_calib.block(0, 3, 3, 3) = -R_CtoG;
113  x_order.push_back(state->_calib_IMUtoCAM.at(feature.anchor_cam_id));
114  H_x.push_back(H_calib);
115  }
116 
117  // If we are doing anchored XYZ feature
118  if (feature.feat_representation == LandmarkRepresentation::Representation::ANCHORED_3D) {
119  H_f = R_CtoG;
120  return;
121  }
122 
123  // If we are doing full inverse depth
124  if (feature.feat_representation == LandmarkRepresentation::Representation::ANCHORED_FULL_INVERSE_DEPTH) {
125 
126  // Get inverse depth representation (should match what is in Landmark.cpp)
127  double a_rho = 1 / p_FinA.norm();
128  double a_phi = std::acos(a_rho * p_FinA(2));
129  double a_theta = std::atan2(p_FinA(1), p_FinA(0));
130  Eigen::Matrix<double, 3, 1> p_invFinA;
131  p_invFinA(0) = a_theta;
132  p_invFinA(1) = a_phi;
133  p_invFinA(2) = a_rho;
134 
135  // Using anchored inverse depth
136  double sin_th = std::sin(p_invFinA(0, 0));
137  double cos_th = std::cos(p_invFinA(0, 0));
138  double sin_phi = std::sin(p_invFinA(1, 0));
139  double cos_phi = std::cos(p_invFinA(1, 0));
140  double rho = p_invFinA(2, 0);
141  // assert(p_invFinA(2,0)>=0.0);
142 
143  // Jacobian of anchored 3D position wrt inverse depth parameters
144  Eigen::Matrix<double, 3, 3> d_pfinA_dpinv;
145  d_pfinA_dpinv << -(1.0 / rho) * sin_th * sin_phi, (1.0 / rho) * cos_th * cos_phi, -(1.0 / (rho * rho)) * cos_th * sin_phi,
146  (1.0 / rho) * cos_th * sin_phi, (1.0 / rho) * sin_th * cos_phi, -(1.0 / (rho * rho)) * sin_th * sin_phi, 0.0,
147  -(1.0 / rho) * sin_phi, -(1.0 / (rho * rho)) * cos_phi;
148  H_f = R_CtoG * d_pfinA_dpinv;
149  return;
150  }
151 
152  // If we are doing the MSCKF version of inverse depth
153  if (feature.feat_representation == LandmarkRepresentation::Representation::ANCHORED_MSCKF_INVERSE_DEPTH) {
154 
155  // Get inverse depth representation (should match what is in Landmark.cpp)
156  Eigen::Matrix<double, 3, 1> p_invFinA_MSCKF;
157  p_invFinA_MSCKF(0) = p_FinA(0) / p_FinA(2);
158  p_invFinA_MSCKF(1) = p_FinA(1) / p_FinA(2);
159  p_invFinA_MSCKF(2) = 1 / p_FinA(2);
160 
161  // Using the MSCKF version of inverse depth
162  double alpha = p_invFinA_MSCKF(0, 0);
163  double beta = p_invFinA_MSCKF(1, 0);
164  double rho = p_invFinA_MSCKF(2, 0);
165 
166  // Jacobian of anchored 3D position wrt inverse depth parameters
167  Eigen::Matrix<double, 3, 3> d_pfinA_dpinv;
168  d_pfinA_dpinv << (1.0 / rho), 0.0, -(1.0 / (rho * rho)) * alpha, 0.0, (1.0 / rho), -(1.0 / (rho * rho)) * beta, 0.0, 0.0,
169  -(1.0 / (rho * rho));
170  H_f = R_CtoG * d_pfinA_dpinv;
171  return;
172  }
173 
175  if (feature.feat_representation == LandmarkRepresentation::Representation::ANCHORED_INVERSE_DEPTH_SINGLE) {
176 
177  // Get inverse depth representation (should match what is in Landmark.cpp)
178  double rho = 1.0 / p_FinA(2);
179  Eigen::Vector3d bearing = rho * p_FinA;
180 
181  // Jacobian of anchored 3D position wrt inverse depth parameters
182  Eigen::Vector3d d_pfinA_drho;
183  d_pfinA_drho << -(1.0 / (rho * rho)) * bearing;
184  H_f = R_CtoG * d_pfinA_drho;
185  return;
186  }
187 
188  // Failure, invalid representation that is not programmed
189  assert(false);
190 }
191 
192 void UpdaterHelper::get_feature_jacobian_full(std::shared_ptr<State> state, UpdaterHelperFeature &feature, Eigen::MatrixXd &H_f,
193  Eigen::MatrixXd &H_x, Eigen::VectorXd &res, std::vector<std::shared_ptr<Type>> &x_order) {
194 
195  // Total number of measurements for this feature
196  int total_meas = 0;
197  for (auto const &pair : feature.timestamps) {
198  total_meas += (int)pair.second.size();
199  }
200 
201  // Compute the size of the states involved with this feature
202  int total_hx = 0;
203  std::unordered_map<std::shared_ptr<Type>, size_t> map_hx;
204  for (auto const &pair : feature.timestamps) {
205 
206  // Our extrinsics and intrinsics
207  std::shared_ptr<PoseJPL> calibration = state->_calib_IMUtoCAM.at(pair.first);
208  std::shared_ptr<Vec> distortion = state->_cam_intrinsics.at(pair.first);
209 
210  // If doing calibration extrinsics
211  if (state->_options.do_calib_camera_pose) {
212  map_hx.insert({calibration, total_hx});
213  x_order.push_back(calibration);
214  total_hx += calibration->size();
215  }
216 
217  // If doing calibration intrinsics
218  if (state->_options.do_calib_camera_intrinsics) {
219  map_hx.insert({distortion, total_hx});
220  x_order.push_back(distortion);
221  total_hx += distortion->size();
222  }
223 
224  // Loop through all measurements for this specific camera
225  for (size_t m = 0; m < feature.timestamps[pair.first].size(); m++) {
226 
227  // Add this clone if it is not added already
228  std::shared_ptr<PoseJPL> clone_Ci = state->_clones_IMU.at(feature.timestamps[pair.first].at(m));
229  if (map_hx.find(clone_Ci) == map_hx.end()) {
230  map_hx.insert({clone_Ci, total_hx});
231  x_order.push_back(clone_Ci);
232  total_hx += clone_Ci->size();
233  }
234  }
235  }
236 
237  // If we are using an anchored representation, make sure that the anchor is also added
238  if (LandmarkRepresentation::is_relative_representation(feature.feat_representation)) {
239 
240  // Assert we have a clone
241  assert(feature.anchor_cam_id != -1);
242 
243  // Add this anchor if it is not added already
244  std::shared_ptr<PoseJPL> clone_Ai = state->_clones_IMU.at(feature.anchor_clone_timestamp);
245  if (map_hx.find(clone_Ai) == map_hx.end()) {
246  map_hx.insert({clone_Ai, total_hx});
247  x_order.push_back(clone_Ai);
248  total_hx += clone_Ai->size();
249  }
250 
251  // Also add its calibration if we are doing calibration
252  if (state->_options.do_calib_camera_pose) {
253  // Add this anchor if it is not added already
254  std::shared_ptr<PoseJPL> clone_calib = state->_calib_IMUtoCAM.at(feature.anchor_cam_id);
255  if (map_hx.find(clone_calib) == map_hx.end()) {
256  map_hx.insert({clone_calib, total_hx});
257  x_order.push_back(clone_calib);
258  total_hx += clone_calib->size();
259  }
260  }
261  }
262 
263  //=========================================================================
264  //=========================================================================
265 
266  // Calculate the position of this feature in the global frame
267  // If anchored, then we need to calculate the position of the feature in the global
268  Eigen::Vector3d p_FinG = feature.p_FinG;
269  if (LandmarkRepresentation::is_relative_representation(feature.feat_representation)) {
270  // Assert that we have an anchor pose for this feature
271  assert(feature.anchor_cam_id != -1);
272  // Get calibration for our anchor camera
273  Eigen::Matrix3d R_ItoC = state->_calib_IMUtoCAM.at(feature.anchor_cam_id)->Rot();
274  Eigen::Vector3d p_IinC = state->_calib_IMUtoCAM.at(feature.anchor_cam_id)->pos();
275  // Anchor pose orientation and position
276  Eigen::Matrix3d R_GtoI = state->_clones_IMU.at(feature.anchor_clone_timestamp)->Rot();
277  Eigen::Vector3d p_IinG = state->_clones_IMU.at(feature.anchor_clone_timestamp)->pos();
278  // Feature in the global frame
279  p_FinG = R_GtoI.transpose() * R_ItoC.transpose() * (feature.p_FinA - p_IinC) + p_IinG;
280  }
281 
282  // Calculate the position of this feature in the global frame FEJ
283  // If anchored, then we can use the "best" p_FinG since the value of p_FinA does not matter
284  Eigen::Vector3d p_FinG_fej = feature.p_FinG_fej;
285  if (LandmarkRepresentation::is_relative_representation(feature.feat_representation)) {
286  p_FinG_fej = p_FinG;
287  }
288 
289  //=========================================================================
290  //=========================================================================
291 
292  // Allocate our residual and Jacobians
293  int c = 0;
294  int jacobsize = (feature.feat_representation != LandmarkRepresentation::Representation::ANCHORED_INVERSE_DEPTH_SINGLE) ? 3 : 1;
295  res = Eigen::VectorXd::Zero(2 * total_meas);
296  H_f = Eigen::MatrixXd::Zero(2 * total_meas, jacobsize);
297  H_x = Eigen::MatrixXd::Zero(2 * total_meas, total_hx);
298 
299  // Derivative of p_FinG in respect to feature representation.
300  // This only needs to be computed once and thus we pull it out of the loop
301  Eigen::MatrixXd dpfg_dlambda;
302  std::vector<Eigen::MatrixXd> dpfg_dx;
303  std::vector<std::shared_ptr<Type>> dpfg_dx_order;
304  UpdaterHelper::get_feature_jacobian_representation(state, feature, dpfg_dlambda, dpfg_dx, dpfg_dx_order);
305 
306  // Assert that all the ones in our order are already in our local jacobian mapping
307 #ifndef NDEBUG
308  for (auto &type : dpfg_dx_order) {
309  assert(map_hx.find(type) != map_hx.end());
310  }
311 #endif
312 
313  // Loop through each camera for this feature
314  for (auto const &pair : feature.timestamps) {
315 
316  // Our calibration between the IMU and CAMi frames
317  std::shared_ptr<Vec> distortion = state->_cam_intrinsics.at(pair.first);
318  std::shared_ptr<PoseJPL> calibration = state->_calib_IMUtoCAM.at(pair.first);
319  Eigen::Matrix3d R_ItoC = calibration->Rot();
320  Eigen::Vector3d p_IinC = calibration->pos();
321 
322  // Loop through all measurements for this specific camera
323  for (size_t m = 0; m < feature.timestamps[pair.first].size(); m++) {
324 
325  //=========================================================================
326  //=========================================================================
327 
328  // Get current IMU clone state
329  std::shared_ptr<PoseJPL> clone_Ii = state->_clones_IMU.at(feature.timestamps[pair.first].at(m));
330  Eigen::Matrix3d R_GtoIi = clone_Ii->Rot();
331  Eigen::Vector3d p_IiinG = clone_Ii->pos();
332 
333  // Get current feature in the IMU
334  Eigen::Vector3d p_FinIi = R_GtoIi * (p_FinG - p_IiinG);
335 
336  // Project the current feature into the current frame of reference
337  Eigen::Vector3d p_FinCi = R_ItoC * p_FinIi + p_IinC;
338  Eigen::Vector2d uv_norm;
339  uv_norm << p_FinCi(0) / p_FinCi(2), p_FinCi(1) / p_FinCi(2);
340 
341  // Distort the normalized coordinates (radtan or fisheye)
342  Eigen::Vector2d uv_dist;
343  uv_dist = state->_cam_intrinsics_cameras.at(pair.first)->distort_d(uv_norm);
344 
345  // Our residual
346  Eigen::Vector2d uv_m;
347  uv_m << (double)feature.uvs[pair.first].at(m)(0), (double)feature.uvs[pair.first].at(m)(1);
348  res.block(2 * c, 0, 2, 1) = uv_m - uv_dist;
349 
350  //=========================================================================
351  //=========================================================================
352 
353  // If we are doing first estimate Jacobians, then overwrite with the first estimates
354  if (state->_options.do_fej) {
355  R_GtoIi = clone_Ii->Rot_fej();
356  p_IiinG = clone_Ii->pos_fej();
357  // R_ItoC = calibration->Rot_fej();
358  // p_IinC = calibration->pos_fej();
359  p_FinIi = R_GtoIi * (p_FinG_fej - p_IiinG);
360  p_FinCi = R_ItoC * p_FinIi + p_IinC;
361  // uv_norm << p_FinCi(0)/p_FinCi(2),p_FinCi(1)/p_FinCi(2);
362  // cam_d = state->get_intrinsics_CAM(pair.first)->fej();
363  }
364 
365  // Compute Jacobians in respect to normalized image coordinates and possibly the camera intrinsics
366  Eigen::MatrixXd dz_dzn, dz_dzeta;
367  state->_cam_intrinsics_cameras.at(pair.first)->compute_distort_jacobian(uv_norm, dz_dzn, dz_dzeta);
368 
369  // Normalized coordinates in respect to projection function
370  Eigen::MatrixXd dzn_dpfc = Eigen::MatrixXd::Zero(2, 3);
371  dzn_dpfc << 1 / p_FinCi(2), 0, -p_FinCi(0) / (p_FinCi(2) * p_FinCi(2)), 0, 1 / p_FinCi(2), -p_FinCi(1) / (p_FinCi(2) * p_FinCi(2));
372 
373  // Derivative of p_FinCi in respect to p_FinIi
374  Eigen::MatrixXd dpfc_dpfg = R_ItoC * R_GtoIi;
375 
376  // Derivative of p_FinCi in respect to camera clone state
377  Eigen::MatrixXd dpfc_dclone = Eigen::MatrixXd::Zero(3, 6);
378  dpfc_dclone.block(0, 0, 3, 3).noalias() = R_ItoC * skew_x(p_FinIi);
379  dpfc_dclone.block(0, 3, 3, 3) = -dpfc_dpfg;
380 
381  //=========================================================================
382  //=========================================================================
383 
384  // Precompute some matrices
385  Eigen::MatrixXd dz_dpfc = dz_dzn * dzn_dpfc;
386  Eigen::MatrixXd dz_dpfg = dz_dpfc * dpfc_dpfg;
387 
388  // CHAINRULE: get the total feature Jacobian
389  H_f.block(2 * c, 0, 2, H_f.cols()).noalias() = dz_dpfg * dpfg_dlambda;
390 
391  // CHAINRULE: get state clone Jacobian
392  H_x.block(2 * c, map_hx[clone_Ii], 2, clone_Ii->size()).noalias() = dz_dpfc * dpfc_dclone;
393 
394  // CHAINRULE: loop through all extra states and add their
395  // NOTE: we add the Jacobian here as we might be in the anchoring pose for this measurement
396  for (size_t i = 0; i < dpfg_dx_order.size(); i++) {
397  H_x.block(2 * c, map_hx[dpfg_dx_order.at(i)], 2, dpfg_dx_order.at(i)->size()).noalias() += dz_dpfg * dpfg_dx.at(i);
398  }
399 
400  //=========================================================================
401  //=========================================================================
402 
403  // Derivative of p_FinCi in respect to camera calibration (R_ItoC, p_IinC)
404  if (state->_options.do_calib_camera_pose) {
405 
406  // Calculate the Jacobian
407  Eigen::MatrixXd dpfc_dcalib = Eigen::MatrixXd::Zero(3, 6);
408  dpfc_dcalib.block(0, 0, 3, 3) = skew_x(p_FinCi - p_IinC);
409  dpfc_dcalib.block(0, 3, 3, 3) = Eigen::Matrix<double, 3, 3>::Identity();
410 
411  // Chainrule it and add it to the big jacobian
412  H_x.block(2 * c, map_hx[calibration], 2, calibration->size()).noalias() += dz_dpfc * dpfc_dcalib;
413  }
414 
415  // Derivative of measurement in respect to distortion parameters
416  if (state->_options.do_calib_camera_intrinsics) {
417  H_x.block(2 * c, map_hx[distortion], 2, distortion->size()) = dz_dzeta;
418  }
419 
420  // Move the Jacobian and residual index forward
421  c++;
422  }
423  }
424 }
425 
426 void UpdaterHelper::nullspace_project_inplace(Eigen::MatrixXd &H_f, Eigen::MatrixXd &H_x, Eigen::VectorXd &res) {
427 
428  // Apply the left nullspace of H_f to all variables
429  // Based on "Matrix Computations 4th Edition by Golub and Van Loan"
430  // See page 252, Algorithm 5.2.4 for how these two loops work
431  // They use "matlab" index notation, thus we need to subtract 1 from all index
432  Eigen::JacobiRotation<double> tempHo_GR;
433  for (int n = 0; n < H_f.cols(); ++n) {
434  for (int m = (int)H_f.rows() - 1; m > n; m--) {
435  // Givens matrix G
436  tempHo_GR.makeGivens(H_f(m - 1, n), H_f(m, n));
437  // Multiply G to the corresponding lines (m-1,m) in each matrix
438  // Note: we only apply G to the nonzero cols [n:Ho.cols()-n-1], while
439  // it is equivalent to applying G to the entire cols [0:Ho.cols()-1].
440  (H_f.block(m - 1, n, 2, H_f.cols() - n)).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
441  (H_x.block(m - 1, 0, 2, H_x.cols())).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
442  (res.block(m - 1, 0, 2, 1)).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
443  }
444  }
445 
446  // The H_f jacobian max rank is 3 if it is a 3d position, thus size of the left nullspace is Hf.rows()-3
447  // NOTE: need to eigen3 eval here since this experiences aliasing!
448  // H_f = H_f.block(H_f.cols(),0,H_f.rows()-H_f.cols(),H_f.cols()).eval();
449  H_x = H_x.block(H_f.cols(), 0, H_x.rows() - H_f.cols(), H_x.cols()).eval();
450  res = res.block(H_f.cols(), 0, res.rows() - H_f.cols(), res.cols()).eval();
451 
452  // Sanity check
453  assert(H_x.rows() == res.rows());
454 }
455 
456 void UpdaterHelper::measurement_compress_inplace(Eigen::MatrixXd &H_x, Eigen::VectorXd &res) {
457 
458  // Return if H_x is a fat matrix (there is no need to compress in this case)
459  if (H_x.rows() <= H_x.cols())
460  return;
461 
462  // Do measurement compression through givens rotations
463  // Based on "Matrix Computations 4th Edition by Golub and Van Loan"
464  // See page 252, Algorithm 5.2.4 for how these two loops work
465  // They use "matlab" index notation, thus we need to subtract 1 from all index
466  Eigen::JacobiRotation<double> tempHo_GR;
467  for (int n = 0; n < H_x.cols(); n++) {
468  for (int m = (int)H_x.rows() - 1; m > n; m--) {
469  // Givens matrix G
470  tempHo_GR.makeGivens(H_x(m - 1, n), H_x(m, n));
471  // Multiply G to the corresponding lines (m-1,m) in each matrix
472  // Note: we only apply G to the nonzero cols [n:Ho.cols()-n-1], while
473  // it is equivalent to applying G to the entire cols [0:Ho.cols()-1].
474  (H_x.block(m - 1, n, 2, H_x.cols() - n)).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
475  (res.block(m - 1, 0, 2, 1)).applyOnTheLeft(0, 1, tempHo_GR.adjoint());
476  }
477  }
478 
479  // If H is a fat matrix, then use the rows
480  // Else it should be same size as our state
481  int r = std::min(H_x.rows(), H_x.cols());
482 
483  // Construct the smaller jacobian and residual after measurement compression
484  assert(r <= H_x.rows());
485  H_x.conservativeResize(r, H_x.cols());
486  res.conservativeResize(r, res.cols());
487 }
ov_msckf::UpdaterHelper::UpdaterHelperFeature::anchor_cam_id
int anchor_cam_id
What camera ID our pose is anchored in!! By default the first measurement is the anchor.
Definition: UpdaterHelper.h:72
ov_msckf::UpdaterHelper::UpdaterHelperFeature::p_FinA
Eigen::Vector3d p_FinA
Triangulated position of this feature, in the anchor frame.
Definition: UpdaterHelper.h:78
ov_core::skew_x
Eigen::Matrix< double, 3, 3 > skew_x(const Eigen::Matrix< double, 3, 1 > &w)
quat_ops.h
UpdaterHelper.h
ov_msckf::UpdaterHelper::UpdaterHelperFeature::uvs
std::unordered_map< size_t, std::vector< Eigen::VectorXf > > uvs
UV coordinates that this feature has been seen from (mapped by camera ID)
Definition: UpdaterHelper.h:60
ov_msckf
Extended Kalman Filter estimator.
Definition: VioManager.h:46
ov_msckf::UpdaterHelper::UpdaterHelperFeature
Feature object that our UpdaterHelper leverages, has all measurements and means.
Definition: UpdaterHelper.h:54
ov_msckf::UpdaterHelper::UpdaterHelperFeature::feat_representation
ov_type::LandmarkRepresentation::Representation feat_representation
What representation our feature is in.
Definition: UpdaterHelper.h:69
ov_msckf::UpdaterHelper::UpdaterHelperFeature::p_FinG
Eigen::Vector3d p_FinG
Triangulated position of this feature, in the global frame.
Definition: UpdaterHelper.h:84
ov_type
ov_msckf::UpdaterHelper::UpdaterHelperFeature::anchor_clone_timestamp
double anchor_clone_timestamp
Timestamp of anchor clone.
Definition: UpdaterHelper.h:75
State.h
ov_msckf::UpdaterHelper::UpdaterHelperFeature::p_FinG_fej
Eigen::Vector3d p_FinG_fej
Triangulated position of this feature, in the global frame first estimate.
Definition: UpdaterHelper.h:87
ov_core
ov_msckf::UpdaterHelper::UpdaterHelperFeature::timestamps
std::unordered_map< size_t, std::vector< double > > timestamps
Timestamps of each UV measurement (mapped by camera ID)
Definition: UpdaterHelper.h:66


ov_msckf
Author(s): Patrick Geneva , Kevin Eckenhoff , Guoquan Huang
autogenerated on Mon Dec 16 2024 03:06:54