Skip to content

Commit d7ceca0

Browse files
committed
IMU factor with gravity
1 parent 2315c68 commit d7ceca0

File tree

7 files changed

+301
-16
lines changed

7 files changed

+301
-16
lines changed

gtsam/navigation/ImuFactor.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,102 @@ Vector ImuFactor2::evaluateError(const NavState& state_i,
255255

256256
//------------------------------------------------------------------------------
257257

258+
//------------------------------------------------------------------------------
259+
// ImuFactorWithGravity methods
260+
//------------------------------------------------------------------------------
261+
ImuFactorWithGravity::ImuFactorWithGravity(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, Key gravity,
262+
const PreintegratedImuMeasurements& pim) :
263+
Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i,
264+
pose_j, vel_j, bias, gravity), _PIM_(pim) {
265+
}
266+
267+
//------------------------------------------------------------------------------
268+
NonlinearFactor::shared_ptr ImuFactorWithGravity::clone() const {
269+
return boost::static_pointer_cast<NonlinearFactor>(
270+
NonlinearFactor::shared_ptr(new This(*this)));
271+
}
272+
273+
//------------------------------------------------------------------------------
274+
std::ostream& operator<<(std::ostream& os, const ImuFactorWithGravity& f) {
275+
f._PIM_.print("preintegrated measurements:\n");
276+
os << " noise model sigmas: " << f.noiseModel_->sigmas().transpose();
277+
return os;
278+
}
279+
280+
//------------------------------------------------------------------------------
281+
void ImuFactorWithGravity::print(const string& s, const KeyFormatter& keyFormatter) const {
282+
cout << (s.empty() ? s : s + "\n") << "ImuFactorWithGravity(" << keyFormatter(this->key<1>())
283+
<< "," << keyFormatter(this->key<2>()) << "," << keyFormatter(this->key<3>())
284+
<< "," << keyFormatter(this->key<4>()) << "," << keyFormatter(this->key<5>()) << "," << keyFormatter(this->key<6>())
285+
<< ")\n";
286+
cout << *this << endl;
287+
}
288+
289+
//------------------------------------------------------------------------------
290+
bool ImuFactorWithGravity::equals(const NonlinearFactor& other, double tol) const {
291+
const This *e = dynamic_cast<const This*>(&other);
292+
const bool base = Base::equals(*e, tol);
293+
const bool pim = _PIM_.equals(e->_PIM_, tol);
294+
return e != nullptr && base && pim;
295+
}
296+
297+
//------------------------------------------------------------------------------
298+
Vector ImuFactorWithGravity::evaluateError(const Pose3& pose_i, const Vector3& vel_i,
299+
const Pose3& pose_j, const Vector3& vel_j,
300+
const imuBias::ConstantBias& bias_i, const Vector3& gravity, boost::optional<Matrix&> H1,
301+
boost::optional<Matrix&> H2, boost::optional<Matrix&> H3,
302+
boost::optional<Matrix&> H4, boost::optional<Matrix&> H5, boost::optional<Matrix&> H6) const {
303+
std::cout << "Evaluating error" << std::endl;
304+
return _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, gravity,
305+
H1, H2, H3, H4, H5, H6);
306+
}
307+
308+
//------------------------------------------------------------------------------
309+
#ifdef GTSAM_TANGENT_PREINTEGRATION
310+
PreintegratedImuMeasurements ImuFactorWithGravity::Merge(
311+
const PreintegratedImuMeasurements& pim01,
312+
const PreintegratedImuMeasurements& pim12) {
313+
if (!pim01.matchesParamsWith(pim12))
314+
throw std::domain_error(
315+
"Cannot merge PreintegratedImuMeasurements with different params");
316+
317+
if (pim01.params()->body_P_sensor)
318+
throw std::domain_error(
319+
"Cannot merge PreintegratedImuMeasurements with sensor pose yet");
320+
321+
// the bias for the merged factor will be the bias from 01
322+
PreintegratedImuMeasurements pim02 = pim01;
323+
324+
Matrix9 H1, H2;
325+
pim02.mergeWith(pim12, &H1, &H2);
326+
327+
return pim02;
328+
}
329+
330+
//------------------------------------------------------------------------------
331+
ImuFactorWithGravity::shared_ptr ImuFactorWithGravity::Merge(const shared_ptr& f01,
332+
const shared_ptr& f12) {
333+
// IMU bias keys must be the same.
334+
if (f01->key<5>() != f12->key<5>())
335+
throw std::domain_error("ImuFactorWithGravity::Merge: IMU bias keys must be the same");
336+
337+
// expect intermediate pose, velocity keys to matchup.
338+
if (f01->key<3>() != f12->key<1>() || f01->key<4>() != f12->key<2>())
339+
throw std::domain_error(
340+
"ImuFactorWithGravity::Merge: intermediate pose, velocity keys need to match up");
341+
342+
// return new factor
343+
auto pim02 =
344+
Merge(f01->preintegratedMeasurements(), f12->preintegratedMeasurements());
345+
return boost::make_shared<ImuFactorWithGravity>(f01->key<1>(), // P0
346+
f01->key<2>(), // V0
347+
f12->key<3>(), // P2
348+
f12->key<4>(), // V2
349+
f01->key<5>(), // B
350+
f01->key<6>(), // G
351+
pim02);
352+
}
353+
#endif
354+
258355
}
259356
// namespace gtsam

gtsam/navigation/ImuFactor.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class GTSAM_EXPORT PreintegratedImuMeasurements: public PreintegrationType {
6969

7070
friend class ImuFactor;
7171
friend class ImuFactor2;
72+
friend class ImuFactorWithGravity;
7273

7374
protected:
7475

@@ -331,6 +332,106 @@ class GTSAM_EXPORT ImuFactor2 : public NoiseModelFactorN<NavState, NavState, imu
331332
};
332333
// class ImuFactor2
333334

335+
/**
336+
* ImuFactorWithGravity is a 6-ways factor involving previous state (pose and velocity of
337+
* the vehicle at previous time step), current state (pose and velocity at
338+
* current time step), the bias estimate and the gravity. Following the preintegration
339+
* scheme proposed in [2], the ImuFactorWithGravity includes many IMU measurements, which
340+
* are "summarized" using the PreintegratedIMUMeasurements class.
341+
* Note that this factor does not model "temporal consistency" of the biases
342+
* (which are usually slowly varying quantities), which is up to the caller.
343+
* See also CombinedImuFactor for a class that does this for you.
344+
*
345+
* @ingroup navigation
346+
*/
347+
class GTSAM_EXPORT ImuFactorWithGravity: public NoiseModelFactorN<Pose3, Vector3, Pose3, Vector3,
348+
imuBias::ConstantBias, Vector3> {
349+
private:
350+
351+
typedef ImuFactorWithGravity This;
352+
typedef NoiseModelFactorN<Pose3, Vector3, Pose3, Vector3,
353+
imuBias::ConstantBias, Vector3> Base;
354+
355+
PreintegratedImuMeasurements _PIM_;
356+
357+
public:
358+
359+
/** Shorthand for a smart pointer to a factor */
360+
#if !defined(_MSC_VER) && __GNUC__ == 4 && __GNUC_MINOR__ > 5
361+
typedef typename boost::shared_ptr<ImuFactorWithGravity> shared_ptr;
362+
#else
363+
typedef boost::shared_ptr<ImuFactorWithGravity> shared_ptr;
364+
#endif
365+
366+
/** Default constructor - only use for serialization */
367+
ImuFactorWithGravity() {}
368+
369+
/**
370+
* Constructor
371+
* @param pose_i Previous pose key
372+
* @param vel_i Previous velocity key
373+
* @param pose_j Current pose key
374+
* @param vel_j Current velocity key
375+
* @param bias Previous bias key
376+
* @param preintegratedMeasurements The preintegreated measurements since the
377+
* last pose.
378+
*/
379+
ImuFactorWithGravity(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, Key gravity,
380+
const PreintegratedImuMeasurements& preintegratedMeasurements);
381+
382+
~ImuFactorWithGravity() override {
383+
}
384+
385+
/// @return a deep copy of this factor
386+
gtsam::NonlinearFactor::shared_ptr clone() const override;
387+
388+
/// @name Testable
389+
/// @{
390+
GTSAM_EXPORT friend std::ostream& operator<<(std::ostream& os, const ImuFactorWithGravity&);
391+
void print(const std::string& s = "", const KeyFormatter& keyFormatter =
392+
DefaultKeyFormatter) const override;
393+
bool equals(const NonlinearFactor& expected, double tol = 1e-9) const override;
394+
/// @}
395+
396+
/** Access the preintegrated measurements. */
397+
398+
const PreintegratedImuMeasurements& preintegratedMeasurements() const {
399+
return _PIM_;
400+
}
401+
402+
/** implement functions needed to derive from Factor */
403+
404+
/// vector of errors
405+
Vector evaluateError(const Pose3& pose_i, const Vector3& vel_i,
406+
const Pose3& pose_j, const Vector3& vel_j,
407+
const imuBias::ConstantBias& bias_i, const Vector3& gravity, boost::optional<Matrix&> H1 =
408+
boost::none, boost::optional<Matrix&> H2 = boost::none,
409+
boost::optional<Matrix&> H3 = boost::none, boost::optional<Matrix&> H4 =
410+
boost::none, boost::optional<Matrix&> H5 = boost::none, boost::optional<Matrix&> H6 = boost::none) const override;
411+
412+
#ifdef GTSAM_TANGENT_PREINTEGRATION
413+
/// Merge two pre-integrated measurement classes
414+
static PreintegratedImuMeasurements Merge(
415+
const PreintegratedImuMeasurements& pim01,
416+
const PreintegratedImuMeasurements& pim12);
417+
418+
/// Merge two factors
419+
static shared_ptr Merge(const shared_ptr& f01, const shared_ptr& f12);
420+
#endif
421+
422+
private:
423+
/** Serialization function */
424+
friend class boost::serialization::access;
425+
template<class ARCHIVE>
426+
void serialize(ARCHIVE & ar, const unsigned int /*version*/) {
427+
// NoiseModelFactor5 instead of NoiseModelFactorN for backward compatibility
428+
ar & boost::serialization::make_nvp("NoiseModelFactor6",
429+
boost::serialization::base_object<Base>(*this));
430+
ar & BOOST_SERIALIZATION_NVP(_PIM_);
431+
}
432+
};
433+
// class ImuFactorWithGravity
434+
334435
template <>
335436
struct traits<PreintegratedImuMeasurements> : public Testable<PreintegratedImuMeasurements> {};
336437

@@ -340,4 +441,7 @@ struct traits<ImuFactor> : public Testable<ImuFactor> {};
340441
template <>
341442
struct traits<ImuFactor2> : public Testable<ImuFactor2> {};
342443

444+
template <>
445+
struct traits<ImuFactorWithGravity> : public Testable<ImuFactorWithGravity> {};
446+
343447
} /// namespace gtsam

gtsam/navigation/PreintegrationBase.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,18 @@ void PreintegrationBase::integrateMeasurement(const Vector3& measuredAcc,
115115

116116
//------------------------------------------------------------------------------
117117
NavState PreintegrationBase::predict(const NavState& state_i,
118-
const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1,
119-
OptionalJacobian<9, 6> H2) const {
118+
const imuBias::ConstantBias& bias_i, const Vector3& n_gravity, OptionalJacobian<9, 9> H1,
119+
OptionalJacobian<9, 6> H2, OptionalJacobian<9, 3> H3) const {
120120
Matrix96 D_biasCorrected_bias;
121121
Vector9 biasCorrected = biasCorrectedDelta(bias_i,
122122
H2 ? &D_biasCorrected_bias : nullptr);
123123

124124
// Correct for initial velocity and gravity
125125
Matrix9 D_delta_state, D_delta_biasCorrected;
126-
Vector9 xi = state_i.correctPIM(biasCorrected, deltaTij_, p().n_gravity,
126+
Matrix93 D_delta_gravity;
127+
Vector9 xi = state_i.correctPIM(biasCorrected, deltaTij_, n_gravity,
127128
p().omegaCoriolis, p().use2ndOrderCoriolis, H1 ? &D_delta_state : nullptr,
128-
H2 ? &D_delta_biasCorrected : nullptr);
129+
H2 ? &D_delta_biasCorrected : nullptr, H3 ? &D_delta_gravity : nullptr);
129130

130131
// Use retract to get back to NavState manifold
131132
Matrix9 D_predict_state, D_predict_delta;
@@ -136,41 +137,64 @@ NavState PreintegrationBase::predict(const NavState& state_i,
136137
*H1 = D_predict_state + D_predict_delta * D_delta_state;
137138
if (H2)
138139
*H2 = D_predict_delta * D_delta_biasCorrected * D_biasCorrected_bias;
140+
if (H3)
141+
*H3 = D_predict_delta * D_delta_gravity;
139142
return state_j;
140143
}
141144

145+
//------------------------------------------------------------------------------
146+
NavState PreintegrationBase::predict(const NavState& state_i,
147+
const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1,
148+
OptionalJacobian<9, 6> H2) const {
149+
return predict(state_i, bias_i, p().n_gravity, H1, H2, boost::none);
150+
}
151+
142152
//------------------------------------------------------------------------------
143153
Vector9 PreintegrationBase::computeError(const NavState& state_i,
144154
const NavState& state_j,
145155
const imuBias::ConstantBias& bias_i,
156+
const Vector3& n_gravity,
146157
OptionalJacobian<9, 9> H1,
147158
OptionalJacobian<9, 9> H2,
148-
OptionalJacobian<9, 6> H3) const {
159+
OptionalJacobian<9, 6> H3,
160+
OptionalJacobian<9, 3> H4) const {
149161
// Predict state at time j
150162
Matrix9 D_predict_state_i;
151163
Matrix96 D_predict_bias_i;
164+
Matrix93 D_predict_gravity;
152165
NavState predictedState_j = predict(
153-
state_i, bias_i, H1 ? &D_predict_state_i : 0, H3 ? &D_predict_bias_i : 0);
166+
state_i, bias_i, n_gravity, H1 ? &D_predict_state_i : 0, H3 ? &D_predict_bias_i : 0, H4 ? &D_predict_gravity : 0);
154167

155168
// Calculate error
156169
Matrix9 D_error_state_j, D_error_predict;
157170
Vector9 error =
158171
state_j.localCoordinates(predictedState_j, H2 ? &D_error_state_j : 0,
159-
H1 || H3 ? &D_error_predict : 0);
172+
H1 || H3 || H4 ? &D_error_predict : 0);
160173

161174
if (H1) *H1 << D_error_predict * D_predict_state_i;
162175
if (H2) *H2 << D_error_state_j;
163176
if (H3) *H3 << D_error_predict * D_predict_bias_i;
177+
if (H4) *H4 << D_error_predict * D_predict_gravity;
164178

165179
return error;
166180
}
167181

182+
//------------------------------------------------------------------------------
183+
Vector9 PreintegrationBase::computeError(const NavState& state_i,
184+
const NavState& state_j,
185+
const imuBias::ConstantBias& bias_i,
186+
OptionalJacobian<9, 9> H1,
187+
OptionalJacobian<9, 9> H2,
188+
OptionalJacobian<9, 6> H3) const {
189+
return computeError(state_i, state_j, bias_i, p().n_gravity, H1, H2, H3, boost::none);
190+
}
191+
168192
//------------------------------------------------------------------------------
169193
Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i,
170194
const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j,
171-
const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H1,
195+
const imuBias::ConstantBias& bias_i, const Vector3& n_gravity, OptionalJacobian<9, 6> H1,
172196
OptionalJacobian<9, 3> H2, OptionalJacobian<9, 6> H3,
173-
OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5) const {
197+
OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5, OptionalJacobian<9, 3> H6) const {
174198

175199
// Note that derivative of constructors below is not identity for velocity, but
176200
// a 9*3 matrix == Z_3x3, Z_3x3, state.R().transpose()
@@ -179,8 +203,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i,
179203

180204
// Predict state at time j
181205
Matrix9 D_error_state_i, D_error_state_j;
182-
Vector9 error = computeError(state_i, state_j, bias_i,
183-
H1 || H2 ? &D_error_state_i : 0, H3 || H4 ? &D_error_state_j : 0, H5);
206+
Vector9 error = computeError(state_i, state_j, bias_i, n_gravity,
207+
H1 || H2 ? &D_error_state_i : 0, H3 || H4 ? &D_error_state_j : 0, H5, H6);
184208

185209
// Separate out derivatives in terms of 5 arguments
186210
// Note that doing so requires special treatment of velocities, as when treated as
@@ -197,4 +221,13 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i,
197221

198222
//------------------------------------------------------------------------------
199223

224+
//------------------------------------------------------------------------------
225+
Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i,
226+
const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j,
227+
const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H1,
228+
OptionalJacobian<9, 3> H2, OptionalJacobian<9, 6> H3,
229+
OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5) const {
230+
return computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, p().n_gravity, H1, H2, H3, H4, H5, boost::none);
231+
}
232+
200233
} // namespace gtsam

gtsam/navigation/PreintegrationBase.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ class GTSAM_EXPORT PreintegrationBase {
150150
virtual Vector9 biasCorrectedDelta(const imuBias::ConstantBias& bias_i,
151151
OptionalJacobian<9, 6> H = {}) const = 0;
152152

153+
/// Predict state at time j
154+
NavState predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, const Vector3& n_gravity,
155+
OptionalJacobian<9, 9> H1 = boost::none,
156+
OptionalJacobian<9, 6> H2 = boost::none,
157+
OptionalJacobian<9, 3> H3 = boost::none) const;
158+
153159
/// Predict state at time j
154160
NavState predict(const NavState& state_i, const imuBias::ConstantBias& bias_i,
155161
OptionalJacobian<9, 9> H1 = {},
@@ -161,6 +167,12 @@ class GTSAM_EXPORT PreintegrationBase {
161167
OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2,
162168
OptionalJacobian<9, 6> H3) const;
163169

170+
/// Calculate error given navStates and gravity
171+
Vector9 computeError(const NavState& state_i, const NavState& state_j,
172+
const imuBias::ConstantBias& bias_i, const Vector3& n_gravity,
173+
OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2,
174+
OptionalJacobian<9, 6> H3, OptionalJacobian<9, 3> H4) const;
175+
164176
/**
165177
* Compute errors w.r.t. preintegrated measurements and jacobians
166178
* wrt pose_i, vel_i, bias_i, pose_j, bias_j
@@ -172,6 +184,13 @@ class GTSAM_EXPORT PreintegrationBase {
172184
OptionalJacobian<9, 6> H3 = {}, OptionalJacobian<9, 3> H4 = {},
173185
OptionalJacobian<9, 6> H5 = {}) const;
174186

187+
Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i,
188+
const Pose3& pose_j, const Vector3& vel_j,
189+
const imuBias::ConstantBias& bias_i, const Vector3& n_gravity, OptionalJacobian<9, 6> H1 =
190+
boost::none, OptionalJacobian<9, 3> H2 = boost::none,
191+
OptionalJacobian<9, 6> H3 = boost::none, OptionalJacobian<9, 3> H4 =
192+
boost::none, OptionalJacobian<9, 6> H5 = boost::none, OptionalJacobian<9, 3> H6 = boost::none) const;
193+
175194
private:
176195
#if GTSAM_ENABLE_BOOST_SERIALIZATION
177196
/** Serialization function */

0 commit comments

Comments
 (0)