From 77f69146f65e0ff179e538f0877f1cb62abbe24f Mon Sep 17 00:00:00 2001 From: krunalchande Date: Mon, 2 Mar 2015 15:14:05 -0500 Subject: [PATCH 001/142] Unit test that fails! Bias estimated is opposite in direction for test bodyPSensorWithBias! Not equal: expected: .biasAcc [0 0 0] expected: .biasGyro [ 0 0.01 0] actual: .biasAcc [-0.00012 3.6e-16 0.00015] actual: .biasGyro [-3.7e-18 -0.01 -3.6e-18] --- gtsam/navigation/tests/testImuFactor.cpp | 135 +++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 9c82a7dfa..ecf019a04 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -922,6 +922,141 @@ TEST(ImuFactor, PredictRotation) { EXPECT(assert_equal(Vector(expectedVelocity), Vector(poseVelocity.velocity))); } +/* ************************************************************************* */ +TEST(ImuFactor, bodyPSensorNoBias) { + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0.1, 0)); // Biases (acc, rot) + + // Measurements + Vector3 n_gravity; n_gravity << 0, 0, -9.81; // z-up nav frame + Vector3 omegaCoriolis; omegaCoriolis << 0, 0, 0; + // Sensor frame is z-down + // Gyroscope measurement is the angular velocity of sensor w.r.t nav frame in sensor frame + Vector3 s_omegaMeas_ns; s_omegaMeas_ns << 0, 0.1, M_PI/10; + // Acc measurement is acceleration of sensor in the sensor frame, when stationary, table exerts an equal and opposite force + // w.r.t gravity + Vector3 s_accMeas; s_accMeas << 0,0,-9.81; + double dt = 0.001; + Pose3 b_P_s(Rot3::ypr(0,0,M_PI), Point3(0,0,0)); // Rotate sensor (z-down) to body (same as navigation) i.e. z-up + + Matrix I6x6(6,6); + I6x6 = Matrix::Identity(6,6); + + ImuFactor::PreintegratedMeasurements pre_int_data(bias, Matrix3::Zero(), + Matrix3::Zero(), Matrix3::Zero(), true); + + for (int i = 0; i<1000; ++i) pre_int_data.integrateMeasurement(s_accMeas, s_omegaMeas_ns, dt, b_P_s); + + // Create factor + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, n_gravity, omegaCoriolis); + + // Predict + Pose3 x1; + Vector3 v1(0, 0.0, 0.0); + PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, n_gravity, omegaCoriolis); + Pose3 expectedPose(Rot3().ypr(-M_PI/10, 0, 0), Point3(0, 0, 0)); + Vector3 expectedVelocity; expectedVelocity<<0,0,0; + EXPECT(assert_equal(expectedPose, poseVelocity.pose)); + EXPECT(assert_equal(Vector(expectedVelocity), Vector(poseVelocity.velocity))); +} + +/* ************************************************************************* */ +#include +#include +#include +#include +#include + +TEST(ImuFactor, bodyPSensorWithBias) { + int numFactors = 80; + imuBias::ConstantBias zeroBias(Vector3(0, 0, 0), Vector3(0.0, 0, 0)); // Biases (acc, rot) + Vector6 noiseBetweenBiasSigma; noiseBetweenBiasSigma << Vector3(2.0e-5, 2.0e-5, 2.0e-5), Vector3(3.0e-6, 3.0e-6, 3.0e-6); + SharedDiagonal biasNoiseModel = noiseModel::Diagonal::Sigmas(noiseBetweenBiasSigma); + + // Measurements + Vector3 n_gravity; n_gravity << 0, 0, -9.81; + Vector3 omegaCoriolis; omegaCoriolis << 0, 0, 0; + + // Sensor frame is z-down + // Gyroscope measurement is the angular velocity of sensor w.r.t nav frame in sensor frame + Vector3 measuredOmega; measuredOmega << 0, 0.01, 0.0; + // Acc measurement is acceleration of sensor in the sensor frame, when stationary, table exerts an equal and opposite force + // w.r.t gravity + Vector3 measuredAcc; measuredAcc << 0,0,-9.81; + + Pose3 bPs(Rot3::ypr(0,0,M_PI), Point3()); + + Matrix3 accCov = 1e-4 * I_3x3; + Matrix3 gyroCov = 1e-6 * I_3x3; + Matrix3 integrationCov = 1e-8 * I_3x3; + double deltaT = 0.005; + + // Specify noise values on priors + Vector6 priorNoisePoseSigmas((Vector(6) << 0.001, 0.001, 0.001, 0.01, 0.01, 0.01).finished()); + Vector3 priorNoiseVelSigmas((Vector(3) << 0.1, 0.1, 0.1).finished()); + Vector6 priorNoiseBiasSigmas((Vector(6) << 0.1, 0.1, 0.1, 0.5e-1, 0.5e-1, 0.5e-1).finished()); + SharedDiagonal priorNoisePose = noiseModel::Diagonal::Sigmas(priorNoisePoseSigmas); + SharedDiagonal priorNoiseVel = noiseModel::Diagonal::Sigmas(priorNoiseVelSigmas); + SharedDiagonal priorNoiseBias = noiseModel::Diagonal::Sigmas(priorNoiseBiasSigmas); + Vector3 zeroVel(0, 0.0, 0.0); + + + + Values values; + NonlinearFactorGraph graph; + + PriorFactor priorPose(X(0), Pose3(), priorNoisePose); + graph.add(priorPose); + values.insert(X(0), Pose3()); + + PriorFactor priorVel(V(0), zeroVel, priorNoiseVel); + graph.add(priorVel); + values.insert(V(0), zeroVel); + + PriorFactor priorBias(B(0), zeroBias, priorNoiseBias); + graph.add(priorBias); + values.insert(B(0), zeroBias); + + for (int i = 1; i < numFactors; i++) { + ImuFactor::PreintegratedMeasurements pre_int_data = ImuFactor::PreintegratedMeasurements(imuBias::ConstantBias(Vector3(0, 0.0, 0.0), + Vector3(0.0, 0.0, 0.0)), accCov, gyroCov, integrationCov, true); + for (int j = 0; j<200; ++j) pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT, bPs); + + // Create factor + ImuFactor factor(X(i-1), V(i-1), X(i), V(i), B(i-1), pre_int_data, n_gravity, omegaCoriolis); + graph.add(factor); + graph.add(BetweenFactor(B(i-1), B(i), zeroBias, biasNoiseModel)); + + values.insert(X(i), Pose3()); + values.insert(V(i), zeroVel); + values.insert(B(i), zeroBias); + } + Values results = LevenbergMarquardtOptimizer(graph, values).optimize(); + cout.precision(2); + Marginals marginals(graph, results); + imuBias::ConstantBias biasActual = results.at(B(numFactors-1)); + + imuBias::ConstantBias biasExpected(Vector3(0,0,0), Vector3(0, 0.01, 0.0)); + EXPECT(assert_equal(biasExpected, biasActual, 1e-3)); + +// results.print("Results: \n"); +// +// for (int i = 0; i < numFactors; i++) { +// imuBias::ConstantBias currentBias = results.at(B(i)); +// cout << "currentBiasEstimate: " << currentBias.vector().transpose() << endl; +// } +// for (int i = 0; i < numFactors; i++) { +// Matrix biasCov = marginals.marginalCovariance(B(i)); +// cout << "b" << i << " cov: " << biasCov.diagonal().transpose() << endl; +// } +// +// for (int i = 0; i < numFactors; i++) { +// Pose3 currentPose = results.at(X(i)); +// cout << "currentYPREstimate: " << currentPose.rotation().ypr().transpose()*180/M_PI << endl; +// } +// for (int i = 0; i < numFactors; i++) +// cout << "x" << i << " covariance: " << marginals.marginalCovariance(X(i)).diagonal().transpose() << endl; +} + /* ************************************************************************* */ int main() { TestResult tr; return TestRegistry::runAllTests(tr);} /* ************************************************************************* */ From 04cf6686b4c1205ce322d22e2d2bda8d3cead666 Mon Sep 17 00:00:00 2001 From: krunalchande Date: Mon, 2 Mar 2015 16:55:58 -0500 Subject: [PATCH 002/142] Better noise values, little cleanup --- gtsam/navigation/tests/testImuFactor.cpp | 43 ++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index ecf019a04..b47612088 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -968,7 +968,6 @@ TEST(ImuFactor, bodyPSensorNoBias) { TEST(ImuFactor, bodyPSensorWithBias) { int numFactors = 80; - imuBias::ConstantBias zeroBias(Vector3(0, 0, 0), Vector3(0.0, 0, 0)); // Biases (acc, rot) Vector6 noiseBetweenBiasSigma; noiseBetweenBiasSigma << Vector3(2.0e-5, 2.0e-5, 2.0e-5), Vector3(3.0e-6, 3.0e-6, 3.0e-6); SharedDiagonal biasNoiseModel = noiseModel::Diagonal::Sigmas(noiseBetweenBiasSigma); @@ -985,9 +984,9 @@ TEST(ImuFactor, bodyPSensorWithBias) { Pose3 bPs(Rot3::ypr(0,0,M_PI), Point3()); - Matrix3 accCov = 1e-4 * I_3x3; - Matrix3 gyroCov = 1e-6 * I_3x3; - Matrix3 integrationCov = 1e-8 * I_3x3; + Matrix3 accCov = 1e-7 * I_3x3; + Matrix3 gyroCov = 1e-8 * I_3x3; + Matrix3 integrationCov = 1e-9 * I_3x3; double deltaT = 0.005; // Specify noise values on priors @@ -1012,9 +1011,10 @@ TEST(ImuFactor, bodyPSensorWithBias) { graph.add(priorVel); values.insert(V(0), zeroVel); - PriorFactor priorBias(B(0), zeroBias, priorNoiseBias); - graph.add(priorBias); - values.insert(B(0), zeroBias); + imuBias::ConstantBias priorBias(Vector3(0, 0, 0), Vector3(0.0, 0.01, 0)); // Biases (acc, rot) + PriorFactor priorBiasFactor(B(0), priorBias, priorNoiseBias); + graph.add(priorBiasFactor); + values.insert(B(0), priorBias); for (int i = 1; i < numFactors; i++) { ImuFactor::PreintegratedMeasurements pre_int_data = ImuFactor::PreintegratedMeasurements(imuBias::ConstantBias(Vector3(0, 0.0, 0.0), @@ -1024,37 +1024,38 @@ TEST(ImuFactor, bodyPSensorWithBias) { // Create factor ImuFactor factor(X(i-1), V(i-1), X(i), V(i), B(i-1), pre_int_data, n_gravity, omegaCoriolis); graph.add(factor); - graph.add(BetweenFactor(B(i-1), B(i), zeroBias, biasNoiseModel)); + imuBias::ConstantBias betweenBias(Vector3(0, 0, 0), Vector3(0.0, 0, 0)); + graph.add(BetweenFactor(B(i-1), B(i), betweenBias, biasNoiseModel)); values.insert(X(i), Pose3()); values.insert(V(i), zeroVel); - values.insert(B(i), zeroBias); + values.insert(B(i), priorBias); } Values results = LevenbergMarquardtOptimizer(graph, values).optimize(); cout.precision(2); Marginals marginals(graph, results); imuBias::ConstantBias biasActual = results.at(B(numFactors-1)); - imuBias::ConstantBias biasExpected(Vector3(0,0,0), Vector3(0, 0.01, 0.0)); - EXPECT(assert_equal(biasExpected, biasActual, 1e-3)); + results.print("Results: \n"); -// results.print("Results: \n"); -// -// for (int i = 0; i < numFactors; i++) { -// imuBias::ConstantBias currentBias = results.at(B(i)); -// cout << "currentBiasEstimate: " << currentBias.vector().transpose() << endl; -// } + for (int i = 0; i < numFactors; i++) { + imuBias::ConstantBias currentBias = results.at(B(i)); + cout << "currentBiasEstimate: " << currentBias.vector().transpose() << endl; + } // for (int i = 0; i < numFactors; i++) { // Matrix biasCov = marginals.marginalCovariance(B(i)); // cout << "b" << i << " cov: " << biasCov.diagonal().transpose() << endl; // } // -// for (int i = 0; i < numFactors; i++) { -// Pose3 currentPose = results.at(X(i)); -// cout << "currentYPREstimate: " << currentPose.rotation().ypr().transpose()*180/M_PI << endl; -// } + for (int i = 0; i < numFactors; i++) { + Pose3 currentPose = results.at(X(i)); + cout << "currentYPREstimate (in Deg): " << currentPose.rotation().ypr().transpose()*180/M_PI << endl; + } // for (int i = 0; i < numFactors; i++) // cout << "x" << i << " covariance: " << marginals.marginalCovariance(X(i)).diagonal().transpose() << endl; + + imuBias::ConstantBias biasExpected(Vector3(0,0,0), Vector3(0, 0.01, 0.0)); + EXPECT(assert_equal(biasExpected, biasActual, 1e-3)); } /* ************************************************************************* */ From 0fafffb02e9ac536244f1db2e1aa1b334a7e937d Mon Sep 17 00:00:00 2001 From: krunalchande Date: Mon, 2 Mar 2015 17:04:19 -0500 Subject: [PATCH 003/142] Made functional correctMeasurementsByBiasAndSensorPose --- gtsam/navigation/CombinedImuFactor.cpp | 2 +- gtsam/navigation/ImuFactor.cpp | 2 +- gtsam/navigation/PreintegrationBase.h | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 1c22bab9a..edb222261 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -78,7 +78,7 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). Vector3 correctedAcc, correctedOmega; - correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, correctedAcc, correctedOmega, body_P_sensor); + boost::tie(correctedAcc, correctedOmega) = correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, body_P_sensor); const Vector3 integratedOmega = correctedOmega * deltaT; // rotation vector describing rotation increment computed from the current rotation rate measurement Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 81120b7c6..4adf324c7 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -67,7 +67,7 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement( OptionalJacobian<9, 9> F_test, OptionalJacobian<9, 9> G_test) { Vector3 correctedAcc, correctedOmega; - correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, correctedAcc, correctedOmega, body_P_sensor); + boost::tie(correctedAcc, correctedOmega) = correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, body_P_sensor); const Vector3 integratedOmega = correctedOmega * deltaT; // rotation vector describing rotation increment computed from the current rotation rate measurement Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 29b9b6e66..08c16846a 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -180,9 +180,10 @@ public: update_delRdelBiasOmega(D_Rincr_integratedOmega,incrR,deltaT); } - void correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, - const Vector3& measuredOmega, Vector3& correctedAcc, - Vector3& correctedOmega, boost::optional body_P_sensor) { + boost::tuple + correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, + const Vector3& measuredOmega, boost::optional body_P_sensor) { + Vector3 correctedAcc, correctedOmega; correctedAcc = biasHat_.correctAccelerometer(measuredAcc); correctedOmega = biasHat_.correctGyroscope(measuredOmega); @@ -195,6 +196,7 @@ public: correctedAcc = body_R_sensor * correctedAcc - body_omega_body__cross * body_omega_body__cross * body_P_sensor->translation().vector(); // linear acceleration vector in the body frame } + return boost::make_tuple(correctedAcc, correctedOmega); } /// Predict state at time j From a261587d4b3f1104dc8985394ad498d3f5d1ec35 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 23 May 2015 17:28:39 -0700 Subject: [PATCH 004/142] Minor cleanup, moved reset method closer to constructor --- gtsam/navigation/ImuFactor.cpp | 35 +++++++------- gtsam/navigation/PreintegratedRotation.h | 60 +++++++++++++----------- gtsam/navigation/PreintegrationBase.cpp | 34 +++++++------- gtsam/navigation/PreintegrationBase.h | 10 ++-- 4 files changed, 73 insertions(+), 66 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 01de5a8f3..17c68139c 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -69,25 +69,27 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement( correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, correctedAcc, correctedOmega, body_P_sensor); - const Vector3 integratedOmega = correctedOmega * deltaT; // rotation vector describing rotation increment computed from the current rotation rate measurement - Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr - const Rot3 Rincr = Rot3::Expmap(integratedOmega, D_Rincr_integratedOmega); // rotation increment computed from the current rotation rate measurement + // rotation increment computed from the current rotation rate measurement + const Vector3 integratedOmega = correctedOmega * deltaT; + Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr + // rotation increment computed from the current rotation rate measurement + const Rot3 Rincr = Rot3::Expmap(integratedOmega, D_Rincr_integratedOmega); // Update Jacobians updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT); // Update preintegrated measurements (also get Jacobian) - const Matrix3 R_i = deltaRij(); // store this, which is useful to compute G_test - Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) + const Matrix3 R_i = deltaRij(); // store this, which is useful to compute G_test + Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F); // first order covariance propagation: - // as in [2] we consider a first order propagation that can be seen as a prediction phase in an EKF framework - /* ----------------------------------------------------------------------------------------------------------------------- */ - // preintMeasCov = F * preintMeasCov * F.transpose() + G * (1/deltaT) * measurementCovariance * G.transpose(); + // as in [2] we consider a first order propagation that can be seen as a prediction phase in EKF + /* --------------------------------------------------------------------------------------------*/ + // preintMeasCov = F * preintMeasCov * F.transpose() + G * (1/deltaT) * measurementCovariance * G' // NOTE 1: (1/deltaT) allows to pass from continuous time noise to discrete time noise // measurementCovariance_discrete = measurementCovariance_contTime * (1/deltaT) - // NOTE 2: the computation of G * (1/deltaT) * measurementCovariance * G.transpose() is done block-wise, + // NOTE 2: computation of G * (1/deltaT) * measurementCovariance * G.transpose() done block-wise, // as G and measurementCovariance are block-diagonal matrices preintMeasCov_ = F * preintMeasCov_ * F.transpose(); preintMeasCov_.block<3, 3>(0, 0) += integrationCovariance() * deltaT; @@ -101,23 +103,24 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement( F_pos_noiseacc = 0.5 * R_i * deltaT * deltaT; preintMeasCov_.block<3, 3>(0, 0) += (1 / deltaT) * F_pos_noiseacc * accelerometerCovariance() * F_pos_noiseacc.transpose(); - Matrix3 temp = F_pos_noiseacc * accelerometerCovariance() * R_i.transpose(); // already includes 1/deltaT + Matrix3 temp = F_pos_noiseacc * accelerometerCovariance() * R_i.transpose(); // has 1/deltaT preintMeasCov_.block<3, 3>(0, 3) += temp; preintMeasCov_.block<3, 3>(3, 0) += temp.transpose(); } // F_test and G_test are given as output for testing purposes and are not needed by the factor - if (F_test) { // This in only for testing + if (F_test) { (*F_test) << F; } - if (G_test) { // This in only for testing & documentation, while the actual computation is done block-wise + if (G_test) { + // This in only for testing & documentation, while the actual computation is done block-wise if (!use2ndOrderIntegration()) F_pos_noiseacc = Z_3x3; // intNoise accNoise omegaNoise - (*G_test) << I_3x3 * deltaT, F_pos_noiseacc, Z_3x3, // pos - Z_3x3, R_i * deltaT, Z_3x3, // vel - Z_3x3, Z_3x3, D_Rincr_integratedOmega * deltaT; // angle + (*G_test) << I_3x3 * deltaT, F_pos_noiseacc, Z_3x3, // pos + Z_3x3, R_i * deltaT, Z_3x3, // vel + Z_3x3, Z_3x3, D_Rincr_integratedOmega * deltaT; // angle } } @@ -176,4 +179,4 @@ Vector ImuFactor::evaluateError(const Pose3& pose_i, const Vector3& vel_i, gravity_, omegaCoriolis_, use2ndOrderCoriolis_, H1, H2, H3, H4, H5); } -} /// namespace gtsam +} // namespace gtsam diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index 2379f58af..ba10fd090 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -31,31 +31,40 @@ namespace gtsam { * It includes the definitions of the preintegrated rotation. */ class PreintegratedRotation { - - Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) - double deltaTij_; ///< Time interval from i to j + Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) + double deltaTij_; ///< Time interval from i to j /// Jacobian of preintegrated rotation w.r.t. angular rate bias Matrix3 delRdelBiasOmega_; - Matrix3 gyroscopeCovariance_; ///< continuous-time "Covariance" of gyroscope measurements -public: + ///< continuous-time "Covariance" of gyroscope measurements + const Matrix3 gyroscopeCovariance_; - /** - * Default constructor, initializes the variables in the base class - */ - PreintegratedRotation(const Matrix3& measuredOmegaCovariance) : + public: + /// Default constructor, initializes the variables in the base class + explicit PreintegratedRotation(const Matrix3& measuredOmegaCovariance) : deltaRij_(Rot3()), deltaTij_(0.0), delRdelBiasOmega_(Z_3x3), gyroscopeCovariance_( measuredOmegaCovariance) { } - /// methods to access class variables + /// Re-initialize PreintegratedMeasurements + void resetIntegration() { + deltaRij_ = Rot3(); + deltaTij_ = 0.0; + delRdelBiasOmega_ = Z_3x3; + } + + /// @name Access instance variables + /// @{ + + // Return 3*3 matrix of rotation from time i to time j. Expensive in quaternion case Matrix3 deltaRij() const { return deltaRij_.matrix(); - } // expensive + } + // Return log(rotation) from time i to time j. Expensive in both Rot3M and quaternion case. Vector3 thetaRij(OptionalJacobian<3, 3> H = boost::none) const { return Rot3::Logmap(deltaRij_, H); - } // super-expensive + } const double& deltaTij() const { return deltaTij_; } @@ -65,15 +74,17 @@ public: const Matrix3& gyroscopeCovariance() const { return gyroscopeCovariance_; } + /// @} + + /// @name Testable + /// @{ - /// Needed for testable void print(const std::string& s) const { std::cout << s << std::endl; std::cout << " deltaTij [" << deltaTij_ << "]" << std::endl; std::cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << std::endl; } - /// Needed for testable bool equals(const PreintegratedRotation& expected, double tol) const { return deltaRij_.equals(expected.deltaRij_, tol) && fabs(deltaTij_ - expected.deltaTij_) < tol @@ -83,12 +94,7 @@ public: expected.gyroscopeCovariance_, tol); } - /// Re-initialize PreintegratedMeasurements - void resetIntegration() { - deltaRij_ = Rot3(); - deltaTij_ = 0.0; - delRdelBiasOmega_ = Z_3x3; - } + /// @} /// Update preintegrated measurements void updateIntegratedRotationAndDeltaT(const Rot3& incrR, const double deltaT, @@ -104,8 +110,7 @@ public: void update_delRdelBiasOmega(const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, double deltaT) { const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - - D_Rincr_integratedOmega * deltaT; + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - D_Rincr_integratedOmega * deltaT; } /// Return a bias corrected version of the integrated rotation - expensive @@ -125,12 +130,11 @@ public: // This was done via an expmap, now we go *back* to so<3> const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected, Jrinv_theta_bc); - const Matrix3 Jr_JbiasOmegaIncr = // - Rot3::ExpmapDerivative(delRdelBiasOmega_ * biasOmegaIncr); + const Matrix3 Jr_JbiasOmegaIncr = Rot3::ExpmapDerivative(delRdelBiasOmega_ * biasOmegaIncr); (*H) = Jrinv_theta_bc * Jr_JbiasOmegaIncr * delRdelBiasOmega_; return biascorrectedOmega; } - //else + // else return Rot3::Logmap(deltaRij_biascorrected); } @@ -140,15 +144,15 @@ public: return rot_i.transpose() * omegaCoriolis * deltaTij(); } -private: + private: /** Serialization function */ friend class boost::serialization::access; template - void serialize(ARCHIVE & ar, const unsigned int /*version*/) { + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { // NOLINT ar & BOOST_SERIALIZATION_NVP(deltaRij_); ar & BOOST_SERIALIZATION_NVP(deltaTij_); ar & BOOST_SERIALIZATION_NVP(delRdelBiasOmega_); } }; -} /// namespace gtsam +} // namespace gtsam diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 496569599..be604e784 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -41,6 +41,17 @@ PreintegrationBase::PreintegrationBase(const imuBias::ConstantBias& bias, integrationCovariance_(integrationErrorCovariance) { } +/// Re-initialize PreintegratedMeasurements +void PreintegrationBase::resetIntegration() { + PreintegratedRotation::resetIntegration(); + deltaPij_ = Vector3::Zero(); + deltaVij_ = Vector3::Zero(); + delPdelBiasAcc_ = Z_3x3; + delPdelBiasOmega_ = Z_3x3; + delVdelBiasAcc_ = Z_3x3; + delVdelBiasOmega_ = Z_3x3; +} + /// Needed for testable void PreintegrationBase::print(const std::string& s) const { PreintegratedRotation::print(s); @@ -62,17 +73,6 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) con && equal_with_abs_tol(integrationCovariance_, other.integrationCovariance_, tol); } -/// Re-initialize PreintegratedMeasurements -void PreintegrationBase::resetIntegration() { - PreintegratedRotation::resetIntegration(); - deltaPij_ = Vector3::Zero(); - deltaVij_ = Vector3::Zero(); - delPdelBiasAcc_ = Z_3x3; - delPdelBiasOmega_ = Z_3x3; - delVdelBiasAcc_ = Z_3x3; - delVdelBiasOmega_ = Z_3x3; -} - /// Update preintegrated measurements void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correctedAcc, const Rot3& incrR, const double deltaT, @@ -161,7 +161,7 @@ PoseVelocityBias PreintegrationBase::predict( const Vector3 pos_i = pose_i.translation().vector(); // Predict state at time j - /* ---------------------------------------------------------------------------------------------------- */ + /* ---------------------------------------------------------------------------------------------*/ const Vector3 deltaPij_biascorrected = deltaPij() + delPdelBiasAcc() * biasAccIncr + delPdelBiasOmega() * biasOmegaIncr; if (deltaPij_biascorrected_out) // if desired, store this @@ -177,13 +177,13 @@ PoseVelocityBias PreintegrationBase::predict( if (deltaVij_biascorrected_out) // if desired, store this *deltaVij_biascorrected_out = deltaVij_biascorrected; - Vector3 vel_j = Vector3( - vel_i + Rot_i_matrix * deltaVij_biascorrected - 2 * omegaCoriolis.cross(vel_i) * dt // Coriolis term - + gravity * dt); + Vector3 vel_j = Vector3(vel_i + Rot_i_matrix * deltaVij_biascorrected - + 2 * omegaCoriolis.cross(vel_i) * dt // Coriolis term + + gravity * dt); if (use2ndOrderCoriolis) { - pos_j += -0.5 * omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt2; // 2nd order coriolis term for position - vel_j += -omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt; // 2nd order term for velocity + pos_j += -0.5 * omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt2; // for position + vel_j += -omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt; // for velocity } const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasOmegaIncr); diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index f6b24e752..85ffae8a2 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -52,8 +52,8 @@ struct PoseVelocityBias { */ class PreintegrationBase : public PreintegratedRotation { - imuBias::ConstantBias biasHat_; ///< Acceleration and angular rate bias values used during preintegration - bool use2ndOrderIntegration_; ///< Controls the order of integration + const imuBias::ConstantBias biasHat_; ///< Acceleration and gyro bias used for preintegration + const bool use2ndOrderIntegration_; ///< Controls the order of integration Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i) Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame) @@ -79,6 +79,9 @@ class PreintegrationBase : public PreintegratedRotation { const Matrix3& measuredOmegaCovariance, const Matrix3&integrationErrorCovariance, const bool use2ndOrderIntegration); + /// Re-initialize PreintegratedMeasurements + void resetIntegration(); + /// methods to access class variables bool use2ndOrderIntegration() const { return use2ndOrderIntegration_; @@ -121,9 +124,6 @@ class PreintegrationBase : public PreintegratedRotation { /// check equality bool equals(const PreintegrationBase& other, double tol) const; - /// Re-initialize PreintegratedMeasurements - void resetIntegration(); - /// Update preintegrated measurements void updatePreintegratedMeasurements(const Vector3& correctedAcc, const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F); From ba0d16adf0217645a121e9f811a90896ca6ad2f7 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 23 May 2015 17:30:19 -0700 Subject: [PATCH 005/142] Moved things to appropriate header --- gtsam/nonlinear/expressionTesting.h | 31 -------------------- gtsam/nonlinear/factorTesting.h | 44 +++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/gtsam/nonlinear/expressionTesting.h b/gtsam/nonlinear/expressionTesting.h index 47f61b8b1..abaa9288a 100644 --- a/gtsam/nonlinear/expressionTesting.h +++ b/gtsam/nonlinear/expressionTesting.h @@ -26,39 +26,8 @@ #include #include -#include -#include -#include - namespace gtsam { -namespace internal { -// CPPUnitLite-style test for linearization of a factor -void testFactorJacobians(TestResult& result_, const std::string& name_, - const NoiseModelFactor& factor, const gtsam::Values& values, double delta, - double tolerance) { - - // Create expected value by numerical differentiation - JacobianFactor expected = linearizeNumerically(factor, values, delta); - - // Create actual value by linearize - boost::shared_ptr actual = // - boost::dynamic_pointer_cast(factor.linearize(values)); - - // Check cast result and then equality - CHECK(actual); - EXPECT(assert_equal(expected, *actual, tolerance)); -} -} - -/// \brief Check the Jacobians produced by a factor against finite differences. -/// \param factor The factor to test. -/// \param values Values filled in for testing the Jacobians. -/// \param numerical_derivative_step The step to use when computing the numerical derivative Jacobians -/// \param tolerance The numerical tolerance to use when comparing Jacobians. -#define EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, numerical_derivative_step, tolerance) \ - { gtsam::internal::testFactorJacobians(result_, name_, factor, values, numerical_derivative_step, tolerance); } - namespace internal { // CPPUnitLite-style test for linearization of an ExpressionFactor template diff --git a/gtsam/nonlinear/factorTesting.h b/gtsam/nonlinear/factorTesting.h index 442b29382..14aeeec4c 100644 --- a/gtsam/nonlinear/factorTesting.h +++ b/gtsam/nonlinear/factorTesting.h @@ -22,6 +22,10 @@ #include #include +#include +#include +#include + namespace gtsam { /** @@ -30,9 +34,8 @@ namespace gtsam { * involved to evaluate the factor. If all the machinery of gtsam is working * correctly, we should get the correct numerical derivatives out the other side. */ -JacobianFactor linearizeNumerically(const NoiseModelFactor& factor, - const Values& values, double delta = 1e-5) { - +JacobianFactor linearizeNumerically(const NoiseModelFactor& factor, const Values& values, + double delta = 1e-5) { // We will fill a vector of key/Jacobians pairs (a map would sort) std::vector > jacobians; @@ -58,11 +61,42 @@ JacobianFactor linearizeNumerically(const NoiseModelFactor& factor, Eigen::VectorXd right = factor.whitenedError(eval_values); J.col(col) = (left - right) * (1.0 / (2.0 * delta)); } - jacobians.push_back(std::make_pair(key,J)); + jacobians.push_back(std::make_pair(key, J)); } // Next step...return JacobianFactor return JacobianFactor(jacobians, -e); } -} // namespace gtsam +namespace internal { + +// CPPUnitLite-style test for linearization of a factor +void testFactorJacobians(TestResult& result_, const std::string& name_, + const NoiseModelFactor& factor, const gtsam::Values& values, double delta, + double tolerance) { + // Create expected value by numerical differentiation + JacobianFactor expected = linearizeNumerically(factor, values, delta); + + // Create actual value by linearize + boost::shared_ptr actual = // + boost::dynamic_pointer_cast(factor.linearize(values)); + + // Check cast result and then equality + CHECK(actual); + EXPECT(assert_equal(expected, *actual, tolerance)); +} + +} // namespace internal +} // namespace gtsam + +/// \brief Check the Jacobians produced by a factor against finite differences. +/// \param factor The factor to test. +/// \param values Values filled in for testing the Jacobians. +/// \param numerical_derivative_step The step to use when computing the numerical derivative +/// Jacobians +/// \param tolerance The numerical tolerance to use when comparing Jacobians. +#define EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, numerical_derivative_step, tolerance) \ + { \ + gtsam::internal::testFactorJacobians(result_, name_, factor, values, \ + numerical_derivative_step, tolerance); \ + } From 69c0d68891c30902355e5196f7e0a9a48d48f16e Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 23 May 2015 17:30:36 -0700 Subject: [PATCH 006/142] Removed a lot of copy paste, use factorTesting macro --- gtsam/navigation/tests/testImuFactor.cpp | 747 ++++++++--------------- 1 file changed, 256 insertions(+), 491 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index c27921fc9..9a93948d9 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -36,31 +36,26 @@ using symbol_shorthand::X; using symbol_shorthand::V; using symbol_shorthand::B; +static const Vector3 kGravity(0, 0, 9.81); +static const Vector3 kZeroOmegaCoriolis(0, 0, 0); + /* ************************************************************************* */ namespace { // Auxiliary functions to test evaluate error in ImuFactor /* ************************************************************************* */ -Vector callEvaluateError(const ImuFactor& factor, const Pose3& pose_i, - const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, - const imuBias::ConstantBias& bias) { - return factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias); -} - -Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, - const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, - const imuBias::ConstantBias& bias) { - return Rot3::Expmap( - factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).tail(3)); +Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, const Vector3& vel_i, + const Pose3& pose_j, const Vector3& vel_j, + const imuBias::ConstantBias& bias) { + return Rot3::Expmap(factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).tail(3)); } // Auxiliary functions to test Jacobians F and G used for // covariance propagation during preintegration /* ************************************************************************* */ -Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, - const Vector3& deltaVij_old, const Rot3& deltaRij_old, - const Vector3& correctedAcc, const Vector3& correctedOmega, - const double deltaT, const bool use2ndOrderIntegration_) { - +Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, const Vector3& deltaVij_old, + const Rot3& deltaRij_old, const Vector3& correctedAcc, + const Vector3& correctedOmega, const double deltaT, + const bool use2ndOrderIntegration_) { Matrix3 dRij = deltaRij_old.matrix(); Vector3 temp = dRij * correctedAcc * deltaT; Vector3 deltaPij_new; @@ -76,8 +71,8 @@ Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, return result; } -Rot3 updatePreintegratedRot(const Rot3& deltaRij_old, - const Vector3& correctedOmega, const double deltaT) { +Rot3 updatePreintegratedRot(const Rot3& deltaRij_old, const Vector3& correctedOmega, + const double deltaT) { Rot3 deltaRij_new = deltaRij_old * Rot3::Expmap(correctedOmega * deltaT); return deltaRij_new; } @@ -98,10 +93,8 @@ ImuFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( const imuBias::ConstantBias& bias, const list& measuredAccs, const list& measuredOmegas, const list& deltaTs, const bool use2ndOrderIntegration = false) { - ImuFactor::PreintegratedMeasurements result(bias, - kMeasuredAccCovariance, - kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, + ImuFactor::PreintegratedMeasurements result(bias, kMeasuredAccCovariance, + kMeasuredOmegaCovariance, kIntegrationErrorCovariance, use2ndOrderIntegration); list::const_iterator itAcc = measuredAccs.begin(); @@ -113,30 +106,29 @@ ImuFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( return result; } -Vector3 evaluatePreintegratedMeasurementsPosition( - const imuBias::ConstantBias& bias, const list& measuredAccs, - const list& measuredOmegas, const list& deltaTs) { - return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs).deltaPij(); +Vector3 evaluatePreintegratedMeasurementsPosition(const imuBias::ConstantBias& bias, + const list& measuredAccs, + const list& measuredOmegas, + const list& deltaTs) { + return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs).deltaPij(); } -Vector3 evaluatePreintegratedMeasurementsVelocity( - const imuBias::ConstantBias& bias, const list& measuredAccs, - const list& measuredOmegas, const list& deltaTs) { - return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs).deltaVij(); +Vector3 evaluatePreintegratedMeasurementsVelocity(const imuBias::ConstantBias& bias, + const list& measuredAccs, + const list& measuredOmegas, + const list& deltaTs) { + return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs).deltaVij(); } -Rot3 evaluatePreintegratedMeasurementsRotation( - const imuBias::ConstantBias& bias, const list& measuredAccs, - const list& measuredOmegas, const list& deltaTs) { +Rot3 evaluatePreintegratedMeasurementsRotation(const imuBias::ConstantBias& bias, + const list& measuredAccs, + const list& measuredOmegas, + const list& deltaTs) { return Rot3( - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs).deltaRij()); + evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs).deltaRij()); } -Rot3 evaluateRotation(const Vector3 measuredOmega, const Vector3 biasOmega, - const double deltaT) { +Rot3 evaluateRotation(const Vector3 measuredOmega, const Vector3 biasOmega, const double deltaT) { return Rot3::Expmap((measuredOmega - biasOmega) * deltaT); } @@ -144,12 +136,12 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { return Rot3::Logmap(Rot3::Expmap(thetahat).compose(Rot3::Expmap(deltatheta))); } -} +} // namespace /* ************************************************************************* */ -TEST( ImuFactor, PreintegratedMeasurements ) { +TEST(ImuFactor, PreintegratedMeasurements) { // Linearization point - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); ///< Current estimate of acceleration and angular rate biases + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Measurements Vector3 measuredAcc(0.1, 0.0, 0.0); @@ -165,25 +157,20 @@ TEST( ImuFactor, PreintegratedMeasurements ) { bool use2ndOrderIntegration = true; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements actual1(bias, - kMeasuredAccCovariance, + ImuFactor::PreintegratedMeasurements actual1(bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, - use2ndOrderIntegration); + kIntegrationErrorCovariance, use2ndOrderIntegration); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT( - assert_equal(Vector(expectedDeltaP1), Vector(actual1.deltaPij()), 1e-6)); - EXPECT( - assert_equal(Vector(expectedDeltaV1), Vector(actual1.deltaVij()), 1e-6)); + EXPECT(assert_equal(Vector(expectedDeltaP1), Vector(actual1.deltaPij()), 1e-6)); + EXPECT(assert_equal(Vector(expectedDeltaV1), Vector(actual1.deltaVij()), 1e-6)); EXPECT(assert_equal(expectedDeltaR1, Rot3(actual1.deltaRij()), 1e-6)); DOUBLES_EQUAL(expectedDeltaT1, actual1.deltaTij(), 1e-6); // Integrate again Vector3 expectedDeltaP2; expectedDeltaP2 << 0.025 + expectedDeltaP1(0) + 0.5 * 0.1 * 0.5 * 0.5, 0, 0; - Vector3 expectedDeltaV2 = Vector3(0.05, 0.0, 0.0) - + expectedDeltaR1.matrix() * measuredAcc * 0.5; + Vector3 expectedDeltaV2 = Vector3(0.05, 0.0, 0.0) + expectedDeltaR1.matrix() * measuredAcc * 0.5; Rot3 expectedDeltaR2 = Rot3::RzRyRx(2.0 * 0.5 * M_PI / 100.0, 0.0, 0.0); double expectedDeltaT2(1); @@ -191,145 +178,100 @@ TEST( ImuFactor, PreintegratedMeasurements ) { ImuFactor::PreintegratedMeasurements actual2 = actual1; actual2.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT( - assert_equal(Vector(expectedDeltaP2), Vector(actual2.deltaPij()), 1e-6)); - EXPECT( - assert_equal(Vector(expectedDeltaV2), Vector(actual2.deltaVij()), 1e-6)); + EXPECT(assert_equal(Vector(expectedDeltaP2), Vector(actual2.deltaPij()), 1e-6)); + EXPECT(assert_equal(Vector(expectedDeltaV2), Vector(actual2.deltaVij()), 1e-6)); EXPECT(assert_equal(expectedDeltaR2, Rot3(actual2.deltaRij()), 1e-6)); DOUBLES_EQUAL(expectedDeltaT2, actual2.deltaTij(), 1e-6); } -/* ************************************************************************* */ -TEST( ImuFactor, ErrorAndJacobians ) { - // Linearization point - imuBias::ConstantBias bias; // Bias - Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), - Point3(5.0, 1.0, -50.0)); - Vector3 v1(Vector3(0.5, 0.0, 0.0)); - Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), - Point3(5.5, 1.0, -50.0)); - Vector3 v2(Vector3(0.5, 0.0, 0.0)); +// Common linearization point and measurements for tests +namespace common { +imuBias::ConstantBias bias; // Bias +Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), Point3(5.0, 1.0, -50.0)); +Vector3 v1(Vector3(0.5, 0.0, 0.0)); +Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), Point3(5.5, 1.0, -50.0)); +Vector3 v2(Vector3(0.5, 0.0, 0.0)); - // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0, 0; - Vector3 measuredOmega; - measuredOmega << M_PI / 100, 0, 0; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(gravity)).vector(); - double deltaT = 1.0; +// Measurements +Vector3 measuredOmega(M_PI / 100, 0, 0); +Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector(); +double deltaT = 1.0; +} // namespace common + +/* ************************************************************************* */ +TEST(ImuFactor, ErrorAndJacobians) { + using namespace common; bool use2ndOrderIntegration = true; - ImuFactor::PreintegratedMeasurements pre_int_data(bias, - kMeasuredAccCovariance, - kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, - use2ndOrderIntegration); + ImuFactor::PreintegratedMeasurements pre_int_data( + bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, + use2ndOrderIntegration); pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis); - - Vector errorActual = factor.evaluateError(x1, v1, x2, v2, bias); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, kZeroOmegaCoriolis); // Expected error Vector errorExpected(9); errorExpected << 0, 0, 0, 0, 0, 0, 0, 0, 0; - EXPECT(assert_equal(errorExpected, errorActual, 1e-6)); - - // Actual Jacobians - Matrix H1a, H2a, H3a, H4a, H5a; - (void) factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); - - // Expected Jacobians - /////////////////// H1 /////////////////////////// - Matrix H1e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, _1, v1, x2, v2, bias), x1); - // Jacobians are around zero, so the rotation part is the same as: - Matrix H1Rot3 = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); - EXPECT(assert_equal(H1Rot3, H1e.bottomRows(3))); - EXPECT(assert_equal(H1e, H1a)); - - /////////////////// H2 /////////////////////////// - Matrix H2e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, _1, x2, v2, bias), v1); - EXPECT(assert_equal(H2e, H2a)); - - /////////////////// H3 /////////////////////////// - Matrix H3e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, _1, v2, bias), x2); - // Jacobians are around zero, so the rotation part is the same as: - Matrix H3Rot3 = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); - EXPECT(assert_equal(H3Rot3, H3e.bottomRows(3))); - EXPECT(assert_equal(H3e, H3a)); - - /////////////////// H4 /////////////////////////// - Matrix H4e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, _1, bias), v2); - EXPECT(assert_equal(H4e, H4a)); - - /////////////////// H5 /////////////////////////// - Matrix H5e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, v2, _1), bias); - EXPECT(assert_equal(H5e, H5a)); - - //////////////////////////////////////////////////////////////////////////// - // Evaluate error with wrong values - Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1); - errorActual = factor.evaluateError(x1, v1, x2, v2_wrong, bias); - errorExpected << 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901, 0, 0, 0; - EXPECT(assert_equal(errorExpected, errorActual, 1e-6)); + EXPECT(assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2, bias), 1e-6)); Values values; values.insert(X(1), x1); values.insert(V(1), v1); values.insert(X(2), x2); - values.insert(V(2), v2_wrong); + values.insert(V(2), v2); values.insert(B(1), bias); + EXPECT(assert_equal(errorExpected, factor.unwhitenedError(values), 1e-6)); + + // Make sure linearization is correct + double diffDelta = 1e-5; + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); + + // Actual Jacobians + Matrix H1a, H2a, H3a, H4a, H5a; + (void)factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); + + // Make sure rotation part is correct when error is interpreted as axis-angle + // Jacobians are around zero, so the rotation part is the same as: + Matrix H1Rot3 = numericalDerivative11( + boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); + EXPECT(assert_equal(H1Rot3, H1a.bottomRows(3))); + + Matrix H3Rot3 = numericalDerivative11( + boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); + EXPECT(assert_equal(H3Rot3, H3a.bottomRows(3))); + + // Evaluate error with wrong values + Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1); + values.update(V(2), v2_wrong); errorExpected << 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901, 0, 0, 0; - EXPECT(assert_equal(factor.unwhitenedError(values), errorActual, 1e-6)); + EXPECT(assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-6)); + EXPECT(assert_equal(errorExpected, factor.unwhitenedError(values), 1e-6)); // Make sure the whitening is done correctly Matrix cov = pre_int_data.preintMeasCov(); Matrix R = RtR(cov.inverse()); - Vector whitened = R * errorActual; + Vector whitened = R * errorExpected; EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values), 1e-6)); - /////////////////////////////////////////////////////////////////////////////// // Make sure linearization is correct - // Create expected value by numerical differentiation - JacobianFactor expected = linearizeNumerically(factor, values, 1e-8); - - // Create actual value by linearize - GaussianFactor::shared_ptr linearized = factor.linearize(values); - JacobianFactor* actual = dynamic_cast(linearized.get()); - - // Check cast result and then equality - EXPECT(assert_equal(expected, *actual, 1e-4)); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); } /* ************************************************************************* */ -TEST( ImuFactor, ErrorAndJacobianWithBiases ) { - imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) - Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 10.0), - Point3(5.0, 1.0, -50.0)); - Vector3 v1(Vector3(0.5, 0.0, 0.0)); - Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), - Point3(5.5, 1.0, -50.0)); - Vector3 v2(Vector3(0.5, 0.0, 0.0)); +TEST(ImuFactor, ErrorAndJacobianWithBiases) { + using common::x1; + using common::v1; + using common::v2; + imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) + Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), Point3(5.5, 1.0, -50.0)); // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0.1, 0.1; + Vector3 nonZeroOmegaCoriolis; + nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(gravity)).vector() - + Vector3(0.2, 0.0, 0.0); + Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; ImuFactor::PreintegratedMeasurements pre_int_data( @@ -338,68 +280,34 @@ TEST( ImuFactor, ErrorAndJacobianWithBiases ) { pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, nonZeroOmegaCoriolis); - SETDEBUG("ImuFactor evaluateError", false); - Vector errorActual = factor.evaluateError(x1, v1, x2, v2, bias); - SETDEBUG("ImuFactor evaluateError", false); + Values values; + values.insert(X(1), x1); + values.insert(V(1), v1); + values.insert(X(2), x2); + values.insert(V(2), v2); + values.insert(B(1), bias); - // Expected error (should not be zero in this test, as we want to evaluate Jacobians - // at a nontrivial linearization point) - // Vector errorExpected(9); errorExpected << 0, 0, 0, 0, 0, 0, 0, 0, 0; - // EXPECT(assert_equal(errorExpected, errorActual, 1e-6)); - - // Expected Jacobians - Matrix H1e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, _1, v1, x2, v2, bias), x1); - Matrix H2e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, _1, x2, v2, bias), v1); - Matrix H3e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, _1, v2, bias), x2); - Matrix H4e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, _1, bias), v2); - Matrix H5e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, v2, _1), bias); - - // Check rotation Jacobians - Matrix RH1e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); - Matrix RH3e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); - Matrix RH5e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, x2, v2, _1), bias); - - // Actual Jacobians - Matrix H1a, H2a, H3a, H4a, H5a; - (void) factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); - - EXPECT(assert_equal(H1e, H1a)); - EXPECT(assert_equal(H2e, H2a)); - EXPECT(assert_equal(H3e, H3a)); - EXPECT(assert_equal(H4e, H4a)); - EXPECT(assert_equal(H5e, H5a)); + // Make sure linearization is correct + double diffDelta = 1e-5; + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); } /* ************************************************************************* */ -TEST( ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis ) { - imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) - Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 10.0), - Point3(5.0, 1.0, -50.0)); - Vector3 v1(Vector3(0.5, 0.0, 0.0)); - Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), - Point3(5.5, 1.0, -50.0)); - Vector3 v2(Vector3(0.5, 0.0, 0.0)); +TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { + using common::x1; + using common::v1; + using common::v2; + imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) + Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), Point3(5.5, 1.0, -50.0)); // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0.1, 0.1; + Vector3 nonZeroOmegaCoriolis; + nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(gravity)).vector() - + Vector3(0.2, 0.0, 0.0); + Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; ImuFactor::PreintegratedMeasurements pre_int_data( @@ -410,83 +318,50 @@ TEST( ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis ) { // Create factor Pose3 bodyPsensor = Pose3(); bool use2ndOrderCoriolis = true; - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis, bodyPsensor, use2ndOrderCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, nonZeroOmegaCoriolis, + bodyPsensor, use2ndOrderCoriolis); - SETDEBUG("ImuFactor evaluateError", false); - Vector errorActual = factor.evaluateError(x1, v1, x2, v2, bias); - SETDEBUG("ImuFactor evaluateError", false); + Values values; + values.insert(X(1), x1); + values.insert(V(1), v1); + values.insert(X(2), x2); + values.insert(V(2), v2); + values.insert(B(1), bias); - // Expected error (should not be zero in this test, as we want to evaluate Jacobians - // at a nontrivial linearization point) - // Vector errorExpected(9); errorExpected << 0, 0, 0, 0, 0, 0, 0, 0, 0; - // EXPECT(assert_equal(errorExpected, errorActual, 1e-6)); - - // Expected Jacobians - Matrix H1e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, _1, v1, x2, v2, bias), x1); - Matrix H2e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, _1, x2, v2, bias), v1); - Matrix H3e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, _1, v2, bias), x2); - Matrix H4e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, _1, bias), v2); - Matrix H5e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, v2, _1), bias); - - // Check rotation Jacobians - Matrix RH1e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); - Matrix RH3e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); - Matrix RH5e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, x2, v2, _1), bias); - - // Actual Jacobians - Matrix H1a, H2a, H3a, H4a, H5a; - (void) factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); - - EXPECT(assert_equal(H1e, H1a)); - EXPECT(assert_equal(H2e, H2a)); - EXPECT(assert_equal(H3e, H3a)); - EXPECT(assert_equal(H4e, H4a)); - EXPECT(assert_equal(H5e, H5a)); + // Make sure linearization is correct + double diffDelta = 1e-5; + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); } /* ************************************************************************* */ -TEST( ImuFactor, PartialDerivative_wrt_Bias ) { +TEST(ImuFactor, PartialDerivative_wrt_Bias) { // Linearization point - Vector3 biasOmega; - biasOmega << 0, 0, 0; ///< Current estimate of rotation rate bias + Vector3 biasOmega(0, 0, 0); // Current estimate of rotation rate bias // Measurements - Vector3 measuredOmega; - measuredOmega << 0.1, 0, 0; + Vector3 measuredOmega(0.1, 0, 0); double deltaT = 0.5; // Compute numerical derivatives Matrix expectedDelRdelBiasOmega = numericalDerivative11( - boost::bind(&evaluateRotation, measuredOmega, _1, deltaT), - Vector3(biasOmega)); + boost::bind(&evaluateRotation, measuredOmega, _1, deltaT), Vector3(biasOmega)); - const Matrix3 Jr = Rot3::ExpmapDerivative( - (measuredOmega - biasOmega) * deltaT); + const Matrix3 Jr = Rot3::ExpmapDerivative((measuredOmega - biasOmega) * deltaT); - Matrix3 actualdelRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign + Matrix3 actualdelRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign // Compare Jacobians - EXPECT(assert_equal(expectedDelRdelBiasOmega, actualdelRdelBiasOmega, 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations + // 1e-3 needs to be added only when using quaternions for rotations + EXPECT(assert_equal(expectedDelRdelBiasOmega, actualdelRdelBiasOmega, 1e-3)); } /* ************************************************************************* */ -TEST( ImuFactor, PartialDerivativeLogmap ) { +TEST(ImuFactor, PartialDerivativeLogmap) { // Linearization point - Vector3 thetahat; - thetahat << 0.1, 0.1, 0; ///< Current estimate of rotation rate bias + Vector3 thetahat(0.1, 0.1, 0); // Current estimate of rotation rate bias // Measurements - Vector3 deltatheta; - deltatheta << 0, 0, 0; + Vector3 deltatheta(0, 0, 0); // Compute numerical derivatives Matrix expectedDelFdeltheta = numericalDerivative11( @@ -499,14 +374,12 @@ TEST( ImuFactor, PartialDerivativeLogmap ) { } /* ************************************************************************* */ -TEST( ImuFactor, fistOrderExponential ) { +TEST(ImuFactor, fistOrderExponential) { // Linearization point - Vector3 biasOmega; - biasOmega << 0, 0, 0; ///< Current estimate of rotation rate bias + Vector3 biasOmega(0, 0, 0); // Current estimate of rotation rate bias // Measurements - Vector3 measuredOmega; - measuredOmega << 0.1, 0, 0; + Vector3 measuredOmega(0.1, 0, 0); double deltaT = 1.0; // change w.r.t. linearization point @@ -514,28 +387,25 @@ TEST( ImuFactor, fistOrderExponential ) { Vector3 deltabiasOmega; deltabiasOmega << alpha, alpha, alpha; - const Matrix3 Jr = Rot3::ExpmapDerivative( - (measuredOmega - biasOmega) * deltaT); + const Matrix3 Jr = Rot3::ExpmapDerivative((measuredOmega - biasOmega) * deltaT); - Matrix3 delRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign + Matrix3 delRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign - const Matrix expectedRot = Rot3::Expmap( - (measuredOmega - biasOmega - deltabiasOmega) * deltaT).matrix(); + const Matrix expectedRot = + Rot3::Expmap((measuredOmega - biasOmega - deltabiasOmega) * deltaT).matrix(); - const Matrix3 hatRot = - Rot3::Expmap((measuredOmega - biasOmega) * deltaT).matrix(); - const Matrix3 actualRot = hatRot - * Rot3::Expmap(delRdelBiasOmega * deltabiasOmega).matrix(); - //hatRot * (Matrix3::Identity() + skewSymmetric(delRdelBiasOmega * deltabiasOmega)); + const Matrix3 hatRot = Rot3::Expmap((measuredOmega - biasOmega) * deltaT).matrix(); + const Matrix3 actualRot = hatRot * Rot3::Expmap(delRdelBiasOmega * deltabiasOmega).matrix(); + // hatRot * (Matrix3::Identity() + skewSymmetric(delRdelBiasOmega * deltabiasOmega)); // This is a first order expansion so the equality is only an approximation EXPECT(assert_equal(expectedRot, actualRot)); } /* ************************************************************************* */ -TEST( ImuFactor, FirstOrderPreIntegratedMeasurements ) { +TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { // Linearization point - imuBias::ConstantBias bias; ///< Current estimate of acceleration and rotation rate biases + imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.1, 0.1)), Point3(1, 0, 1)); @@ -550,56 +420,51 @@ TEST( ImuFactor, FirstOrderPreIntegratedMeasurements ) { deltaTs.push_back(0.01); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back( - Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); + measuredOmegas.push_back(Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } // Actual preintegrated values ImuFactor::PreintegratedMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs); + evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs); // Compute numerical derivatives - Matrix expectedDelPdelBias = numericalDerivative11( - boost::bind(&evaluatePreintegratedMeasurementsPosition, _1, measuredAccs, - measuredOmegas, deltaTs), bias); + Matrix expectedDelPdelBias = numericalDerivative11( + boost::bind(&evaluatePreintegratedMeasurementsPosition, _1, measuredAccs, measuredOmegas, + deltaTs), + bias); Matrix expectedDelPdelBiasAcc = expectedDelPdelBias.leftCols(3); Matrix expectedDelPdelBiasOmega = expectedDelPdelBias.rightCols(3); - Matrix expectedDelVdelBias = numericalDerivative11( - boost::bind(&evaluatePreintegratedMeasurementsVelocity, _1, measuredAccs, - measuredOmegas, deltaTs), bias); + Matrix expectedDelVdelBias = numericalDerivative11( + boost::bind(&evaluatePreintegratedMeasurementsVelocity, _1, measuredAccs, measuredOmegas, + deltaTs), + bias); Matrix expectedDelVdelBiasAcc = expectedDelVdelBias.leftCols(3); Matrix expectedDelVdelBiasOmega = expectedDelVdelBias.rightCols(3); - Matrix expectedDelRdelBias = - numericalDerivative11( - boost::bind(&evaluatePreintegratedMeasurementsRotation, _1, - measuredAccs, measuredOmegas, deltaTs), bias); + Matrix expectedDelRdelBias = numericalDerivative11( + boost::bind(&evaluatePreintegratedMeasurementsRotation, _1, measuredAccs, measuredOmegas, + deltaTs), + bias); Matrix expectedDelRdelBiasAcc = expectedDelRdelBias.leftCols(3); Matrix expectedDelRdelBiasOmega = expectedDelRdelBias.rightCols(3); // Compare Jacobians EXPECT(assert_equal(expectedDelPdelBiasAcc, preintegrated.delPdelBiasAcc())); - EXPECT( - assert_equal(expectedDelPdelBiasOmega, preintegrated.delPdelBiasOmega())); + EXPECT(assert_equal(expectedDelPdelBiasOmega, preintegrated.delPdelBiasOmega())); EXPECT(assert_equal(expectedDelVdelBiasAcc, preintegrated.delVdelBiasAcc())); - EXPECT( - assert_equal(expectedDelVdelBiasOmega, preintegrated.delVdelBiasOmega())); + EXPECT(assert_equal(expectedDelVdelBiasOmega, preintegrated.delVdelBiasOmega())); EXPECT(assert_equal(expectedDelRdelBiasAcc, Matrix::Zero(3, 3))); - EXPECT( - assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega(), - 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations + EXPECT(assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega(), + 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations } /* ************************************************************************* */ -TEST( ImuFactor, JacobianPreintegratedCovariancePropagation ) { +TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Linearization point - imuBias::ConstantBias bias; ///< Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); + imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases + Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); // Measurements list measuredAccs, measuredOmegas; @@ -612,50 +477,48 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation ) { deltaTs.push_back(0.01); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back( - Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); + measuredOmegas.push_back(Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } bool use2ndOrderIntegration = false; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs, use2ndOrderIntegration); + ImuFactor::PreintegratedMeasurements preintegrated = evaluatePreintegratedMeasurements( + bias, measuredAccs, measuredOmegas, deltaTs, use2ndOrderIntegration); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); const double newDeltaT = 0.01; - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement + const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement + const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement + const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement Matrix oldPreintCovariance = preintegrated.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, body_P_sensor, Factual, Gactual); + preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, body_P_sensor, + Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F ////////////////////////////////////////////////////////////////////////////////////////////// // Compute expected f_pos_vel wrt positions Matrix dfpv_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, newMeasuredAcc, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaPij_old); // Compute expected f_pos_vel wrt velocities Matrix dfpv_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, newMeasuredAcc, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaVij_old); // Compute expected f_pos_vel wrt angles Matrix dfpv_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, newMeasuredAcc, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaRij_old); Matrix FexpectedTop6(6, 9); @@ -663,8 +526,7 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation ) { // Compute expected f_rot wrt angles Matrix dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), - deltaRij_old); + boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), deltaRij_old); Matrix FexpectedBottom3(3, 9); FexpectedBottom3 << Z_3x3, Z_3x3, dfr_dangle; @@ -678,26 +540,25 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation ) { ////////////////////////////////////////////////////////////////////////////////////////////// // Compute jacobian wrt integration noise Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3 * newDeltaT, Z_3x3; + dgpv_dintNoise << I_3x3* newDeltaT, Z_3x3; // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), newMeasuredAcc); + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, _1, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + newMeasuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, + newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), newMeasuredOmega); Matrix GexpectedTop6(6, 9); GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), - newMeasuredOmega); + boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), newMeasuredOmega); Matrix GexpectedBottom3(3, 9); GexpectedBottom3 << Z_3x3, Z_3x3, dgr_dangle; @@ -708,22 +569,22 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation ) { // Check covariance propagation Matrix9 measurementCovariance; - measurementCovariance << intNoiseVar * I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar - * I_3x3, Z_3x3, Z_3x3, Z_3x3, omegaNoiseVar * I_3x3; + measurementCovariance << intNoiseVar* I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar* I_3x3, Z_3x3, + Z_3x3, Z_3x3, omegaNoiseVar* I_3x3; - Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance - * Factual.transpose() - + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); + Matrix newPreintCovarianceExpected = + Factual * oldPreintCovariance * Factual.transpose() + + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); } /* ************************************************************************* */ -TEST( ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt ) { +TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Linearization point - imuBias::ConstantBias bias; ///< Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); + imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases + Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); // Measurements list measuredAccs, measuredOmegas; @@ -736,50 +597,48 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt ) { deltaTs.push_back(0.01); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back( - Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); + measuredOmegas.push_back(Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } bool use2ndOrderIntegration = true; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs, use2ndOrderIntegration); + ImuFactor::PreintegratedMeasurements preintegrated = evaluatePreintegratedMeasurements( + bias, measuredAccs, measuredOmegas, deltaTs, use2ndOrderIntegration); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); const double newDeltaT = 0.01; - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement + const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement + const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement + const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement Matrix oldPreintCovariance = preintegrated.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, body_P_sensor, Factual, Gactual); + preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, body_P_sensor, + Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F ////////////////////////////////////////////////////////////////////////////////////////////// // Compute expected f_pos_vel wrt positions Matrix dfpv_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, newMeasuredAcc, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaPij_old); // Compute expected f_pos_vel wrt velocities Matrix dfpv_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, newMeasuredAcc, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaVij_old); // Compute expected f_pos_vel wrt angles Matrix dfpv_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, newMeasuredAcc, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaRij_old); Matrix FexpectedTop6(6, 9); @@ -787,8 +646,7 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt ) { // Compute expected f_rot wrt angles Matrix dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), - deltaRij_old); + boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), deltaRij_old); Matrix FexpectedBottom3(3, 9); FexpectedBottom3 << Z_3x3, Z_3x3, dfr_dangle; @@ -802,26 +660,25 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt ) { ////////////////////////////////////////////////////////////////////////////////////////////// // Compute jacobian wrt integration noise Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3 * newDeltaT, Z_3x3; + dgpv_dintNoise << I_3x3* newDeltaT, Z_3x3; // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), newMeasuredAcc); + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, _1, + newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + newMeasuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, + newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), newMeasuredOmega); Matrix GexpectedTop6(6, 9); GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), - newMeasuredOmega); + boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), newMeasuredOmega); Matrix GexpectedBottom3(3, 9); GexpectedBottom3 << Z_3x3, Z_3x3, dgr_dangle; @@ -832,95 +689,34 @@ TEST( ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt ) { // Check covariance propagation Matrix9 measurementCovariance; - measurementCovariance << intNoiseVar * I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar - * I_3x3, Z_3x3, Z_3x3, Z_3x3, omegaNoiseVar * I_3x3; + measurementCovariance << intNoiseVar* I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar* I_3x3, Z_3x3, + Z_3x3, Z_3x3, omegaNoiseVar* I_3x3; - Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance - * Factual.transpose() - + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); + Matrix newPreintCovarianceExpected = + Factual * oldPreintCovariance * Factual.transpose() + + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); } -//#include -///* ************************************************************************* */ -//TEST( ImuFactor, LinearizeTiming) -//{ -// // Linearization point -// Pose3 x1(Rot3::RzRyRx(M_PI/12.0, M_PI/6.0, M_PI/4.0), Point3(5.0, 1.0, -50.0)); -// Vector3 v1(Vector3(0.5, 0.0, 0.0)); -// Pose3 x2(Rot3::RzRyRx(M_PI/12.0 + M_PI/100.0, M_PI/6.0, M_PI/4.0), Point3(5.5, 1.0, -50.0)); -// Vector3 v2(Vector3(0.5, 0.0, 0.0)); -// imuBias::ConstantBias bias(Vector3(0.001, 0.002, 0.008), Vector3(0.002, 0.004, 0.012)); -// -// // Pre-integrator -// imuBias::ConstantBias biasHat(Vector3(0, 0, 0.10), Vector3(0, 0, 0.10)); -// Vector3 gravity; gravity << 0, 0, 9.81; -// Vector3 omegaCoriolis; omegaCoriolis << 0.0001, 0, 0.01; -// ImuFactor::PreintegratedMeasurements pre_int_data(biasHat, Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity()); -// -// // Pre-integrate Measurements -// Vector3 measuredAcc(0.1, 0.0, 0.0); -// Vector3 measuredOmega(M_PI/100.0, 0.0, 0.0); -// double deltaT = 0.5; -// for(size_t i = 0; i < 50; ++i) { -// pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); -// } -// -// // Create factor -// noiseModel::Base::shared_ptr model = noiseModel::Gaussian::Covariance(pre_int_data.MeasurementCovariance()); -// ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, omegaCoriolis); -// -// Values values; -// values.insert(X(1), x1); -// values.insert(X(2), x2); -// values.insert(V(1), v1); -// values.insert(V(2), v2); -// values.insert(B(1), bias); -// -// Ordering ordering; -// ordering.push_back(X(1)); -// ordering.push_back(V(1)); -// ordering.push_back(X(2)); -// ordering.push_back(V(2)); -// ordering.push_back(B(1)); -// -// GaussianFactorGraph graph; -// gttic_(LinearizeTiming); -// for(size_t i = 0; i < 100000; ++i) { -// GaussianFactor::shared_ptr g = factor.linearize(values, ordering); -// graph.push_back(g); -// } -// gttoc_(LinearizeTiming); -// tictoc_finishedIteration_(); -// std::cout << "Linear Error: " << graph.error(values.zeroVectors(ordering)) << std::endl; -// tictoc_print_(); -//} - /* ************************************************************************* */ -TEST( ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement ) { - - imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) +TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { + imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) Pose3 x1(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0)), Point3(5.0, 1.0, -50.0)); Vector3 v1(Vector3(0.5, 0.0, 0.0)); - Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0 + M_PI / 10.0)), - Point3(5.5, 1.0, -50.0)); + Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0 + M_PI / 10.0)), Point3(5.5, 1.0, -50.0)); Vector3 v2(Vector3(0.5, 0.0, 0.0)); // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0.1, 0.1; + Vector3 nonZeroOmegaCoriolis; + nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(gravity)).vector() - + Vector3(0.2, 0.0, 0.0); + Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), - Point3(1, 0, 0)); + const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), Point3(1, 0, 0)); ImuFactor::PreintegratedMeasurements pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, @@ -929,51 +725,27 @@ TEST( ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement ) { pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, nonZeroOmegaCoriolis); - // Expected Jacobians - Matrix H1e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, _1, v1, x2, v2, bias), x1); - Matrix H2e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, _1, x2, v2, bias), v1); - Matrix H3e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, _1, v2, bias), x2); - Matrix H4e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, _1, bias), v2); - Matrix H5e = numericalDerivative11( - boost::bind(&callEvaluateError, factor, x1, v1, x2, v2, _1), bias); + Values values; + values.insert(X(1), x1); + values.insert(V(1), v1); + values.insert(X(2), x2); + values.insert(V(2), v2); + values.insert(B(1), bias); - // Check rotation Jacobians - Matrix RH1e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); - Matrix RH3e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); - Matrix RH5e = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, x2, v2, _1), bias); - - // Actual Jacobians - Matrix H1a, H2a, H3a, H4a, H5a; - (void) factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); - - EXPECT(assert_equal(H1e, H1a)); - EXPECT(assert_equal(H2e, H2a)); - EXPECT(assert_equal(H3e, H3a)); - EXPECT(assert_equal(H4e, H4a)); - EXPECT(assert_equal(H5e, H5a)); + // Make sure linearization is correct + double diffDelta = 1e-5; + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); } /* ************************************************************************* */ TEST(ImuFactor, PredictPositionAndVelocity) { - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0, 0; Vector3 measuredOmega; - measuredOmega << 0, 0, 0; //M_PI/10.0+0.3; + measuredOmega << 0, 0, 0; // M_PI/10.0+0.3; Vector3 measuredAcc; measuredAcc << 0, 1, -9.81; double deltaT = 0.001; @@ -989,14 +761,12 @@ TEST(ImuFactor, PredictPositionAndVelocity) { pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, kZeroOmegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, gravity, - omegaCoriolis); + PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, kGravity, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 1, 0; @@ -1006,15 +776,11 @@ TEST(ImuFactor, PredictPositionAndVelocity) { /* ************************************************************************* */ TEST(ImuFactor, PredictRotation) { - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0, 0; Vector3 measuredOmega; - measuredOmega << 0, 0, M_PI / 10; //M_PI/10.0+0.3; + measuredOmega << 0, 0, M_PI / 10; // M_PI/10.0+0.3; Vector3 measuredAcc; measuredAcc << 0, 0, -9.81; double deltaT = 0.001; @@ -1030,15 +796,14 @@ TEST(ImuFactor, PredictRotation) { pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, kZeroOmegaCoriolis); // Predict Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, factor.preintegratedMeasurements(), - gravity, omegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, factor.preintegratedMeasurements(), kGravity, + kZeroOmegaCoriolis); Pose3 expectedPose(Rot3().ypr(M_PI / 10, 0, 0), Point3(0, 0, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 0, 0; From b01eb4e960a51d6e64be6faa8641b3d1dc3f4b15 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 23 May 2015 17:49:52 -0700 Subject: [PATCH 007/142] Added a test for PoseVelocityBias --- .../navigation/tests/testPoseVelocityBias.cpp | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 gtsam/navigation/tests/testPoseVelocityBias.cpp diff --git a/gtsam/navigation/tests/testPoseVelocityBias.cpp b/gtsam/navigation/tests/testPoseVelocityBias.cpp new file mode 100644 index 000000000..ce419fdd0 --- /dev/null +++ b/gtsam/navigation/tests/testPoseVelocityBias.cpp @@ -0,0 +1,64 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file testPoseVelocityBias.cpp + * @brief Unit test for PoseVelocityBias + * @author Frank Dellaert + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace gtsam; + +// Should be seen as between(pvb1,pvb2), i.e., written as pvb2 \omin pvb1 +Vector9 error(const PoseVelocityBias& pvb1, const PoseVelocityBias& pvb2) { + Matrix3 R1 = pvb1.pose.rotation().matrix(); + // Ri.transpose() translate the error from the global frame into pose1's frame + const Vector3 fp = R1.transpose() * (pvb2.pose.translation() - pvb1.pose.translation()).vector(); + const Vector3 fv = R1.transpose() * (pvb2.velocity - pvb1.velocity); + const Rot3 R1BetweenR2 = pvb1.pose.rotation().between(pvb2.pose.rotation()); + const Vector3 fR = Rot3::Logmap(R1BetweenR2); + Vector9 r; + r << fp, fv, fR; + return r; +} + +/* ************************************************************************************************/ +TEST(PoseVelocityBias, error) { + Point3 i1(0, 1, 0), j1(-1, 0, 0), k1(0, 0, 1); + Pose3 x1(Rot3(i1, j1, k1), Point3(5.0, 1.0, 0.0)); + Vector3 v1(Vector3(0.5, 0.0, 0.0)); + imuBias::ConstantBias bias1(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); + + Pose3 x2(Rot3(i1, j1, k1).expmap(Vector3(0.1, 0.2, 0.3)), Point3(5.5, 1.0, 6.0)); + Vector3 v2(Vector3(0.5, 4.0, 3.0)); + imuBias::ConstantBias bias2(Vector3(0.1, 0.2, -0.3), Vector3(0.2, 0.3, 0.1)); + + PoseVelocityBias pvb1(x1, v1, bias1), pvb2(x2, v2, bias2); + + Vector9 expected, actual = error(pvb1, pvb2); + expected << 0.0, -0.5, 6.0, 4.0, 0.0, 3.0, 0.1, 0.2, 0.3; + EXPECT(assert_equal(expected, actual, 1e-9)); +} + +/* ************************************************************************************************/ +int main() { + TestResult tr; + return TestRegistry::runAllTests(tr); +} +/* ************************************************************************************************/ From 0c3bb85547cc3c04ecdabcf9ad1229b715c5679f Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 23 May 2015 19:36:42 -0700 Subject: [PATCH 008/142] Added test of localCoordinates --- gtsam_unstable/dynamics/tests/testPoseRTV.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp index 5b052eb02..10ec8464a 100644 --- a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp +++ b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp @@ -94,6 +94,21 @@ TEST( testPoseRTV, Lie ) { EXPECT(assert_equal(delta, -state2.localCoordinates(state1), 1e-1)); } +/* ************************************************************************* */ +TEST(testPoseRTV, localCoordinates) { + Point3 i1(0, 1, 0), j1(-1, 0, 0), k1(0, 0, 1); + Pose3 x1(Rot3(i1, j1, k1), Point3(5.0, 1.0, 0.0)); + Vector3 v1(Vector3(0.5, 0.0, 0.0)); + + Pose3 x2(Rot3(i1, j1, k1).expmap(Vector3(0.1, 0.2, 0.3)), Point3(5.5, 1.0, 6.0)); + Vector3 v2(Vector3(0.5, 4.0, 3.0)); + + PoseRTV rtv1(x1,v1), rtv2(x2,v2); + Vector9 expected, actual = rtv1.localCoordinates(rtv2); + expected << 0.1, 0.2, 0.3, 0.0, -0.5, 6.0, 4.0, 0.0, 3.0; + EXPECT(assert_equal(expected, actual, 1e-9)); +} + /* ************************************************************************* */ TEST( testPoseRTV, dynamics_identities ) { // general dynamics should produce the same measurements as the imuPrediction function From e7c2e8183166b7740e4a5fb61460183958254e44 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 15 Jul 2015 23:24:24 -0700 Subject: [PATCH 009/142] Re-formatted to BORG-style --- gtsam/navigation/tests/testImuFactor.cpp | 387 +++++++++++++---------- 1 file changed, 218 insertions(+), 169 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 9a93948d9..6b66be342 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -43,19 +43,20 @@ static const Vector3 kZeroOmegaCoriolis(0, 0, 0); namespace { // Auxiliary functions to test evaluate error in ImuFactor /* ************************************************************************* */ -Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, const Vector3& vel_i, - const Pose3& pose_j, const Vector3& vel_j, - const imuBias::ConstantBias& bias) { - return Rot3::Expmap(factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).tail(3)); +Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, + const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, + const imuBias::ConstantBias& bias) { + return Rot3::Expmap( + factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).tail(3)); } // Auxiliary functions to test Jacobians F and G used for // covariance propagation during preintegration /* ************************************************************************* */ -Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, const Vector3& deltaVij_old, - const Rot3& deltaRij_old, const Vector3& correctedAcc, - const Vector3& correctedOmega, const double deltaT, - const bool use2ndOrderIntegration_) { +Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, + const Vector3& deltaVij_old, const Rot3& deltaRij_old, + const Vector3& correctedAcc, const Vector3& correctedOmega, + const double deltaT, const bool use2ndOrderIntegration_) { Matrix3 dRij = deltaRij_old.matrix(); Vector3 temp = dRij * correctedAcc * deltaT; Vector3 deltaPij_new; @@ -71,8 +72,8 @@ Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, const Vector3& delt return result; } -Rot3 updatePreintegratedRot(const Rot3& deltaRij_old, const Vector3& correctedOmega, - const double deltaT) { +Rot3 updatePreintegratedRot(const Rot3& deltaRij_old, + const Vector3& correctedOmega, const double deltaT) { Rot3 deltaRij_new = deltaRij_old * Rot3::Expmap(correctedOmega * deltaT); return deltaRij_new; } @@ -94,8 +95,8 @@ ImuFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( const list& measuredOmegas, const list& deltaTs, const bool use2ndOrderIntegration = false) { ImuFactor::PreintegratedMeasurements result(bias, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, - use2ndOrderIntegration); + kMeasuredOmegaCovariance, kIntegrationErrorCovariance, + use2ndOrderIntegration); list::const_iterator itAcc = measuredAccs.begin(); list::const_iterator itOmega = measuredOmegas.begin(); @@ -106,29 +107,30 @@ ImuFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( return result; } -Vector3 evaluatePreintegratedMeasurementsPosition(const imuBias::ConstantBias& bias, - const list& measuredAccs, - const list& measuredOmegas, - const list& deltaTs) { - return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs).deltaPij(); +Vector3 evaluatePreintegratedMeasurementsPosition( + const imuBias::ConstantBias& bias, const list& measuredAccs, + const list& measuredOmegas, const list& deltaTs) { + return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + deltaTs).deltaPij(); } -Vector3 evaluatePreintegratedMeasurementsVelocity(const imuBias::ConstantBias& bias, - const list& measuredAccs, - const list& measuredOmegas, - const list& deltaTs) { - return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs).deltaVij(); +Vector3 evaluatePreintegratedMeasurementsVelocity( + const imuBias::ConstantBias& bias, const list& measuredAccs, + const list& measuredOmegas, const list& deltaTs) { + return evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + deltaTs).deltaVij(); } -Rot3 evaluatePreintegratedMeasurementsRotation(const imuBias::ConstantBias& bias, - const list& measuredAccs, - const list& measuredOmegas, - const list& deltaTs) { +Rot3 evaluatePreintegratedMeasurementsRotation( + const imuBias::ConstantBias& bias, const list& measuredAccs, + const list& measuredOmegas, const list& deltaTs) { return Rot3( - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs).deltaRij()); + evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + deltaTs).deltaRij()); } -Rot3 evaluateRotation(const Vector3 measuredOmega, const Vector3 biasOmega, const double deltaT) { +Rot3 evaluateRotation(const Vector3 measuredOmega, const Vector3 biasOmega, + const double deltaT) { return Rot3::Expmap((measuredOmega - biasOmega) * deltaT); } @@ -136,7 +138,7 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { return Rot3::Logmap(Rot3::Expmap(thetahat).compose(Rot3::Expmap(deltatheta))); } -} // namespace +} // namespace /* ************************************************************************* */ TEST(ImuFactor, PreintegratedMeasurements) { @@ -158,19 +160,22 @@ TEST(ImuFactor, PreintegratedMeasurements) { bool use2ndOrderIntegration = true; // Actual preintegrated values ImuFactor::PreintegratedMeasurements actual1(bias, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, use2ndOrderIntegration); + kMeasuredOmegaCovariance, kIntegrationErrorCovariance, + use2ndOrderIntegration); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT(assert_equal(Vector(expectedDeltaP1), Vector(actual1.deltaPij()), 1e-6)); - EXPECT(assert_equal(Vector(expectedDeltaV1), Vector(actual1.deltaVij()), 1e-6)); + EXPECT( + assert_equal(Vector(expectedDeltaP1), Vector(actual1.deltaPij()), 1e-6)); + EXPECT( + assert_equal(Vector(expectedDeltaV1), Vector(actual1.deltaVij()), 1e-6)); EXPECT(assert_equal(expectedDeltaR1, Rot3(actual1.deltaRij()), 1e-6)); DOUBLES_EQUAL(expectedDeltaT1, actual1.deltaTij(), 1e-6); // Integrate again Vector3 expectedDeltaP2; expectedDeltaP2 << 0.025 + expectedDeltaP1(0) + 0.5 * 0.1 * 0.5 * 0.5, 0, 0; - Vector3 expectedDeltaV2 = Vector3(0.05, 0.0, 0.0) + expectedDeltaR1.matrix() * measuredAcc * 0.5; + Vector3 expectedDeltaV2 = Vector3(0.05, 0.0, 0.0) + + expectedDeltaR1.matrix() * measuredAcc * 0.5; Rot3 expectedDeltaR2 = Rot3::RzRyRx(2.0 * 0.5 * M_PI / 100.0, 0.0, 0.0); double expectedDeltaT2(1); @@ -178,42 +183,49 @@ TEST(ImuFactor, PreintegratedMeasurements) { ImuFactor::PreintegratedMeasurements actual2 = actual1; actual2.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT(assert_equal(Vector(expectedDeltaP2), Vector(actual2.deltaPij()), 1e-6)); - EXPECT(assert_equal(Vector(expectedDeltaV2), Vector(actual2.deltaVij()), 1e-6)); + EXPECT( + assert_equal(Vector(expectedDeltaP2), Vector(actual2.deltaPij()), 1e-6)); + EXPECT( + assert_equal(Vector(expectedDeltaV2), Vector(actual2.deltaVij()), 1e-6)); EXPECT(assert_equal(expectedDeltaR2, Rot3(actual2.deltaRij()), 1e-6)); DOUBLES_EQUAL(expectedDeltaT2, actual2.deltaTij(), 1e-6); } // Common linearization point and measurements for tests namespace common { -imuBias::ConstantBias bias; // Bias -Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), Point3(5.0, 1.0, -50.0)); +imuBias::ConstantBias bias; // Bias +Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), + Point3(5.0, 1.0, -50.0)); Vector3 v1(Vector3(0.5, 0.0, 0.0)); -Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), Point3(5.5, 1.0, -50.0)); +Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), + Point3(5.5, 1.0, -50.0)); Vector3 v2(Vector3(0.5, 0.0, 0.0)); // Measurements Vector3 measuredOmega(M_PI / 100, 0, 0); Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector(); double deltaT = 1.0; -} // namespace common +} // namespace common /* ************************************************************************* */ TEST(ImuFactor, ErrorAndJacobians) { using namespace common; bool use2ndOrderIntegration = true; - ImuFactor::PreintegratedMeasurements pre_int_data( - bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, - use2ndOrderIntegration); + ImuFactor::PreintegratedMeasurements pre_int_data(bias, + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance, use2ndOrderIntegration); pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, kZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + kZeroOmegaCoriolis); // Expected error Vector errorExpected(9); errorExpected << 0, 0, 0, 0, 0, 0, 0, 0, 0; - EXPECT(assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2, bias), 1e-6)); + EXPECT( + assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2, bias), + 1e-6)); Values values; values.insert(X(1), x1); @@ -229,7 +241,7 @@ TEST(ImuFactor, ErrorAndJacobians) { // Actual Jacobians Matrix H1a, H2a, H3a, H4a, H5a; - (void)factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); + (void) factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); // Make sure rotation part is correct when error is interpreted as axis-angle // Jacobians are around zero, so the rotation part is the same as: @@ -245,7 +257,9 @@ TEST(ImuFactor, ErrorAndJacobians) { Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1); values.update(V(2), v2_wrong); errorExpected << 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901, 0, 0, 0; - EXPECT(assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-6)); + EXPECT( + assert_equal(errorExpected, + factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-6)); EXPECT(assert_equal(errorExpected, factor.unwhitenedError(values), 1e-6)); // Make sure the whitening is done correctly @@ -263,24 +277,28 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { using common::x1; using common::v1; using common::v2; - imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) - Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), Point3(5.5, 1.0, -50.0)); + imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) + Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), + Point3(5.5, 1.0, -50.0)); // Measurements Vector3 nonZeroOmegaCoriolis; nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3(0.2, 0.0, 0.0); + Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; ImuFactor::PreintegratedMeasurements pre_int_data( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance); + imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance); pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, nonZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + nonZeroOmegaCoriolis); Values values; values.insert(X(1), x1); @@ -299,27 +317,30 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { using common::x1; using common::v1; using common::v2; - imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) - Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), Point3(5.5, 1.0, -50.0)); + imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0.1, 0, 0.3)); // Biases (acc, rot) + Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 10.0 + M_PI / 10.0)), + Point3(5.5, 1.0, -50.0)); // Measurements Vector3 nonZeroOmegaCoriolis; nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3(0.2, 0.0, 0.0); + Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; ImuFactor::PreintegratedMeasurements pre_int_data( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance); + imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance); pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor Pose3 bodyPsensor = Pose3(); bool use2ndOrderCoriolis = true; - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, nonZeroOmegaCoriolis, - bodyPsensor, use2ndOrderCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + nonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis); Values values; values.insert(X(1), x1); @@ -336,7 +357,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { /* ************************************************************************* */ TEST(ImuFactor, PartialDerivative_wrt_Bias) { // Linearization point - Vector3 biasOmega(0, 0, 0); // Current estimate of rotation rate bias + Vector3 biasOmega(0, 0, 0); // Current estimate of rotation rate bias // Measurements Vector3 measuredOmega(0.1, 0, 0); @@ -344,11 +365,13 @@ TEST(ImuFactor, PartialDerivative_wrt_Bias) { // Compute numerical derivatives Matrix expectedDelRdelBiasOmega = numericalDerivative11( - boost::bind(&evaluateRotation, measuredOmega, _1, deltaT), Vector3(biasOmega)); + boost::bind(&evaluateRotation, measuredOmega, _1, deltaT), + Vector3(biasOmega)); - const Matrix3 Jr = Rot3::ExpmapDerivative((measuredOmega - biasOmega) * deltaT); + const Matrix3 Jr = Rot3::ExpmapDerivative( + (measuredOmega - biasOmega) * deltaT); - Matrix3 actualdelRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign + Matrix3 actualdelRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign // Compare Jacobians // 1e-3 needs to be added only when using quaternions for rotations @@ -358,7 +381,7 @@ TEST(ImuFactor, PartialDerivative_wrt_Bias) { /* ************************************************************************* */ TEST(ImuFactor, PartialDerivativeLogmap) { // Linearization point - Vector3 thetahat(0.1, 0.1, 0); // Current estimate of rotation rate bias + Vector3 thetahat(0.1, 0.1, 0); // Current estimate of rotation rate bias // Measurements Vector3 deltatheta(0, 0, 0); @@ -376,7 +399,7 @@ TEST(ImuFactor, PartialDerivativeLogmap) { /* ************************************************************************* */ TEST(ImuFactor, fistOrderExponential) { // Linearization point - Vector3 biasOmega(0, 0, 0); // Current estimate of rotation rate bias + Vector3 biasOmega(0, 0, 0); // Current estimate of rotation rate bias // Measurements Vector3 measuredOmega(0.1, 0, 0); @@ -387,15 +410,18 @@ TEST(ImuFactor, fistOrderExponential) { Vector3 deltabiasOmega; deltabiasOmega << alpha, alpha, alpha; - const Matrix3 Jr = Rot3::ExpmapDerivative((measuredOmega - biasOmega) * deltaT); + const Matrix3 Jr = Rot3::ExpmapDerivative( + (measuredOmega - biasOmega) * deltaT); - Matrix3 delRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign + Matrix3 delRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign - const Matrix expectedRot = - Rot3::Expmap((measuredOmega - biasOmega - deltabiasOmega) * deltaT).matrix(); + const Matrix expectedRot = Rot3::Expmap( + (measuredOmega - biasOmega - deltabiasOmega) * deltaT).matrix(); - const Matrix3 hatRot = Rot3::Expmap((measuredOmega - biasOmega) * deltaT).matrix(); - const Matrix3 actualRot = hatRot * Rot3::Expmap(delRdelBiasOmega * deltabiasOmega).matrix(); + const Matrix3 hatRot = + Rot3::Expmap((measuredOmega - biasOmega) * deltaT).matrix(); + const Matrix3 actualRot = hatRot + * Rot3::Expmap(delRdelBiasOmega * deltabiasOmega).matrix(); // hatRot * (Matrix3::Identity() + skewSymmetric(delRdelBiasOmega * deltabiasOmega)); // This is a first order expansion so the equality is only an approximation @@ -405,7 +431,7 @@ TEST(ImuFactor, fistOrderExponential) { /* ************************************************************************* */ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { // Linearization point - imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases + imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.1, 0.1)), Point3(1, 0, 1)); @@ -420,51 +446,56 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { deltaTs.push_back(0.01); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); + measuredOmegas.push_back( + Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } // Actual preintegrated values ImuFactor::PreintegratedMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs); + evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + deltaTs); // Compute numerical derivatives - Matrix expectedDelPdelBias = numericalDerivative11( - boost::bind(&evaluatePreintegratedMeasurementsPosition, _1, measuredAccs, measuredOmegas, - deltaTs), - bias); + Matrix expectedDelPdelBias = numericalDerivative11( + boost::bind(&evaluatePreintegratedMeasurementsPosition, _1, measuredAccs, + measuredOmegas, deltaTs), bias); Matrix expectedDelPdelBiasAcc = expectedDelPdelBias.leftCols(3); Matrix expectedDelPdelBiasOmega = expectedDelPdelBias.rightCols(3); - Matrix expectedDelVdelBias = numericalDerivative11( - boost::bind(&evaluatePreintegratedMeasurementsVelocity, _1, measuredAccs, measuredOmegas, - deltaTs), - bias); + Matrix expectedDelVdelBias = numericalDerivative11( + boost::bind(&evaluatePreintegratedMeasurementsVelocity, _1, measuredAccs, + measuredOmegas, deltaTs), bias); Matrix expectedDelVdelBiasAcc = expectedDelVdelBias.leftCols(3); Matrix expectedDelVdelBiasOmega = expectedDelVdelBias.rightCols(3); - Matrix expectedDelRdelBias = numericalDerivative11( - boost::bind(&evaluatePreintegratedMeasurementsRotation, _1, measuredAccs, measuredOmegas, - deltaTs), - bias); + Matrix expectedDelRdelBias = + numericalDerivative11( + boost::bind(&evaluatePreintegratedMeasurementsRotation, _1, + measuredAccs, measuredOmegas, deltaTs), bias); Matrix expectedDelRdelBiasAcc = expectedDelRdelBias.leftCols(3); Matrix expectedDelRdelBiasOmega = expectedDelRdelBias.rightCols(3); // Compare Jacobians EXPECT(assert_equal(expectedDelPdelBiasAcc, preintegrated.delPdelBiasAcc())); - EXPECT(assert_equal(expectedDelPdelBiasOmega, preintegrated.delPdelBiasOmega())); + EXPECT( + assert_equal(expectedDelPdelBiasOmega, preintegrated.delPdelBiasOmega())); EXPECT(assert_equal(expectedDelVdelBiasAcc, preintegrated.delVdelBiasAcc())); - EXPECT(assert_equal(expectedDelVdelBiasOmega, preintegrated.delVdelBiasOmega())); + EXPECT( + assert_equal(expectedDelVdelBiasOmega, preintegrated.delVdelBiasOmega())); EXPECT(assert_equal(expectedDelRdelBiasAcc, Matrix::Zero(3, 3))); - EXPECT(assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega(), - 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations + EXPECT( + assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega(), + 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations } /* ************************************************************************* */ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Linearization point - imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); + imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases + Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); // Measurements list measuredAccs, measuredOmegas; @@ -477,48 +508,50 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { deltaTs.push_back(0.01); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); + measuredOmegas.push_back( + Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } bool use2ndOrderIntegration = false; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = evaluatePreintegratedMeasurements( - bias, measuredAccs, measuredOmegas, deltaTs, use2ndOrderIntegration); + ImuFactor::PreintegratedMeasurements preintegrated = + evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + deltaTs, use2ndOrderIntegration); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); const double newDeltaT = 0.01; - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement + const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement + const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement + const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement Matrix oldPreintCovariance = preintegrated.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, body_P_sensor, - Factual, Gactual); + preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, + newDeltaT, body_P_sensor, Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F ////////////////////////////////////////////////////////////////////////////////////////////// // Compute expected f_pos_vel wrt positions Matrix dfpv_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, newMeasuredAcc, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, + newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaPij_old); // Compute expected f_pos_vel wrt velocities Matrix dfpv_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, newMeasuredAcc, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, + newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaVij_old); // Compute expected f_pos_vel wrt angles Matrix dfpv_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, newMeasuredAcc, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, + newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaRij_old); Matrix FexpectedTop6(6, 9); @@ -526,7 +559,8 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Compute expected f_rot wrt angles Matrix dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), deltaRij_old); + boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), + deltaRij_old); Matrix FexpectedBottom3(3, 9); FexpectedBottom3 << Z_3x3, Z_3x3, dfr_dangle; @@ -540,25 +574,26 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { ////////////////////////////////////////////////////////////////////////////////////////////// // Compute jacobian wrt integration noise Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3* newDeltaT, Z_3x3; + dgpv_dintNoise << I_3x3 * newDeltaT, Z_3x3; // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, _1, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - newMeasuredAcc); + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, + deltaRij_old, _1, newMeasuredOmega, newDeltaT, + use2ndOrderIntegration), newMeasuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, - newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, + deltaRij_old, newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), newMeasuredOmega); Matrix GexpectedTop6(6, 9); GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), newMeasuredOmega); + boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), + newMeasuredOmega); Matrix GexpectedBottom3(3, 9); GexpectedBottom3 << Z_3x3, Z_3x3, dgr_dangle; @@ -569,12 +604,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Check covariance propagation Matrix9 measurementCovariance; - measurementCovariance << intNoiseVar* I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar* I_3x3, Z_3x3, - Z_3x3, Z_3x3, omegaNoiseVar* I_3x3; + measurementCovariance << intNoiseVar * I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar + * I_3x3, Z_3x3, Z_3x3, Z_3x3, omegaNoiseVar * I_3x3; - Matrix newPreintCovarianceExpected = - Factual * oldPreintCovariance * Factual.transpose() + - (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); + Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance + * Factual.transpose() + + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); @@ -583,8 +618,8 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { /* ************************************************************************* */ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Linearization point - imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); + imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases + Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); // Measurements list measuredAccs, measuredOmegas; @@ -597,48 +632,50 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { deltaTs.push_back(0.01); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); + measuredOmegas.push_back( + Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } bool use2ndOrderIntegration = true; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = evaluatePreintegratedMeasurements( - bias, measuredAccs, measuredOmegas, deltaTs, use2ndOrderIntegration); + ImuFactor::PreintegratedMeasurements preintegrated = + evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + deltaTs, use2ndOrderIntegration); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); const double newDeltaT = 0.01; - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement + const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement + const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement + const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement Matrix oldPreintCovariance = preintegrated.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, body_P_sensor, - Factual, Gactual); + preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, + newDeltaT, body_P_sensor, Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F ////////////////////////////////////////////////////////////////////////////////////////////// // Compute expected f_pos_vel wrt positions Matrix dfpv_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, newMeasuredAcc, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, + newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaPij_old); // Compute expected f_pos_vel wrt velocities Matrix dfpv_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, newMeasuredAcc, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, + newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaVij_old); // Compute expected f_pos_vel wrt angles Matrix dfpv_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, newMeasuredAcc, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, + newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), deltaRij_old); Matrix FexpectedTop6(6, 9); @@ -646,7 +683,8 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Compute expected f_rot wrt angles Matrix dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), deltaRij_old); + boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), + deltaRij_old); Matrix FexpectedBottom3(3, 9); FexpectedBottom3 << Z_3x3, Z_3x3, dfr_dangle; @@ -660,25 +698,26 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { ////////////////////////////////////////////////////////////////////////////////////////////// // Compute jacobian wrt integration noise Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3* newDeltaT, Z_3x3; + dgpv_dintNoise << I_3x3 * newDeltaT, Z_3x3; // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, _1, - newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - newMeasuredAcc); + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, + deltaRij_old, _1, newMeasuredOmega, newDeltaT, + use2ndOrderIntegration), newMeasuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, - newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), + boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, + deltaRij_old, newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), newMeasuredOmega); Matrix GexpectedTop6(6, 9); GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), newMeasuredOmega); + boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), + newMeasuredOmega); Matrix GexpectedBottom3(3, 9); GexpectedBottom3 << Z_3x3, Z_3x3, dgr_dangle; @@ -689,12 +728,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Check covariance propagation Matrix9 measurementCovariance; - measurementCovariance << intNoiseVar* I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar* I_3x3, Z_3x3, - Z_3x3, Z_3x3, omegaNoiseVar* I_3x3; + measurementCovariance << intNoiseVar * I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar + * I_3x3, Z_3x3, Z_3x3, Z_3x3, omegaNoiseVar * I_3x3; - Matrix newPreintCovarianceExpected = - Factual * oldPreintCovariance * Factual.transpose() + - (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); + Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance + * Factual.transpose() + + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); @@ -702,10 +741,11 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { /* ************************************************************************* */ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { - imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) + imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) Pose3 x1(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0)), Point3(5.0, 1.0, -50.0)); Vector3 v1(Vector3(0.5, 0.0, 0.0)); - Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0 + M_PI / 10.0)), Point3(5.5, 1.0, -50.0)); + Pose3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0 + M_PI / 10.0)), + Point3(5.5, 1.0, -50.0)); Vector3 v2(Vector3(0.5, 0.0, 0.0)); // Measurements @@ -713,19 +753,23 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3(0.2, 0.0, 0.0); + Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), Point3(1, 0, 0)); + const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), + Point3(1, 0, 0)); ImuFactor::PreintegratedMeasurements pre_int_data( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance); + imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance); pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, nonZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + nonZeroOmegaCoriolis); Values values; values.insert(X(1), x1); @@ -741,11 +785,11 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { /* ************************************************************************* */ TEST(ImuFactor, PredictPositionAndVelocity) { - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) // Measurements Vector3 measuredOmega; - measuredOmega << 0, 0, 0; // M_PI/10.0+0.3; + measuredOmega << 0, 0, 0; // M_PI/10.0+0.3; Vector3 measuredAcc; measuredAcc << 0, 1, -9.81; double deltaT = 0.001; @@ -754,19 +798,22 @@ TEST(ImuFactor, PredictPositionAndVelocity) { I6x6 = Matrix::Identity(6, 6); ImuFactor::PreintegratedMeasurements pre_int_data( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); + imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance, true); for (int i = 0; i < 1000; ++i) pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, kZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + kZeroOmegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, kGravity, kZeroOmegaCoriolis); + PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, kGravity, + kZeroOmegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 1, 0; @@ -776,11 +823,11 @@ TEST(ImuFactor, PredictPositionAndVelocity) { /* ************************************************************************* */ TEST(ImuFactor, PredictRotation) { - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) // Measurements Vector3 measuredOmega; - measuredOmega << 0, 0, M_PI / 10; // M_PI/10.0+0.3; + measuredOmega << 0, 0, M_PI / 10; // M_PI/10.0+0.3; Vector3 measuredAcc; measuredAcc << 0, 0, -9.81; double deltaT = 0.001; @@ -789,21 +836,23 @@ TEST(ImuFactor, PredictRotation) { I6x6 = Matrix::Identity(6, 6); ImuFactor::PreintegratedMeasurements pre_int_data( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); + imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance, true); for (int i = 0; i < 1000; ++i) pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, kZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + kZeroOmegaCoriolis); // Predict Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, factor.preintegratedMeasurements(), kGravity, - kZeroOmegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, factor.preintegratedMeasurements(), + kGravity, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3().ypr(M_PI / 10, 0, 0), Point3(0, 0, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 0, 0; From b8f514174357faa032643a77dd602e9085c24643 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 16 Jul 2015 22:38:17 -0700 Subject: [PATCH 010/142] HasRange --- gtsam_unstable/dynamics/PoseRTV.h | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gtsam_unstable/dynamics/PoseRTV.h b/gtsam_unstable/dynamics/PoseRTV.h index a6bc6006a..bef397cb6 100644 --- a/gtsam_unstable/dynamics/PoseRTV.h +++ b/gtsam_unstable/dynamics/PoseRTV.h @@ -173,14 +173,7 @@ struct traits : public internal::LieGroup {}; // Define Range functor specializations that are used in RangeFactor template struct Range; -template <> -struct Range { - typedef double result_type; - double operator()(const PoseRTV& pose1, const PoseRTV& pose2, - OptionalJacobian<1, 9> H1 = boost::none, - OptionalJacobian<1, 9> H2 = boost::none) { - return pose1.range(pose2, H1, H2); - } -}; +template<> +struct Range : HasRange {}; } // \namespace gtsam From 459226800d84bc10bd60a9e7896428dfa442c55e Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 16 Jul 2015 22:38:30 -0700 Subject: [PATCH 011/142] Replaced failing test with two new, working tests --- gtsam_unstable/dynamics/tests/testPoseRTV.cpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp index 10ec8464a..2cc613d65 100644 --- a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp +++ b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp @@ -84,7 +84,8 @@ TEST( testPoseRTV, Lie ) { EXPECT(assert_equal(state1, (PoseRTV)state1.retract(zero(9)), tol)); EXPECT(assert_equal(zero(9), state1.localCoordinates(state1), tol)); - Vector delta = (Vector(9) << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3).finished(); + Vector delta(9); + delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3; Rot3 rot2 = rot.retract(repeat(3, 0.1)); Point3 pt2 = pt + rot * Point3(0.2, 0.3, 0.4); Velocity3 vel2 = vel + rot * Velocity3(-0.1,-0.2,-0.3); @@ -92,21 +93,14 @@ TEST( testPoseRTV, Lie ) { EXPECT(assert_equal(state2, (PoseRTV)state1.retract(delta), 1e-1)); EXPECT(assert_equal(delta, state1.localCoordinates(state2), 1e-1)); EXPECT(assert_equal(delta, -state2.localCoordinates(state1), 1e-1)); -} -/* ************************************************************************* */ -TEST(testPoseRTV, localCoordinates) { - Point3 i1(0, 1, 0), j1(-1, 0, 0), k1(0, 0, 1); - Pose3 x1(Rot3(i1, j1, k1), Point3(5.0, 1.0, 0.0)); - Vector3 v1(Vector3(0.5, 0.0, 0.0)); + // roundtrip from state2 to state3 and back + PoseRTV state3 = state2.retract(delta); + EXPECT(assert_equal(delta, state2.localCoordinates(state3), 1e-1)); - Pose3 x2(Rot3(i1, j1, k1).expmap(Vector3(0.1, 0.2, 0.3)), Point3(5.5, 1.0, 6.0)); - Vector3 v2(Vector3(0.5, 4.0, 3.0)); - - PoseRTV rtv1(x1,v1), rtv2(x2,v2); - Vector9 expected, actual = rtv1.localCoordinates(rtv2); - expected << 0.1, 0.2, 0.3, 0.0, -0.5, 6.0, 4.0, 0.0, 3.0; - EXPECT(assert_equal(expected, actual, 1e-9)); + // roundtrip from state3 to state4 and back, with expmap + PoseRTV state4 = state3.expmap(delta); + EXPECT(assert_equal(delta, state3.logmap(state4), 1e-1)); } /* ************************************************************************* */ From 03be9280658d5c8a49c06c5413b820b4e152ee14 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 16 Jul 2015 23:45:57 -0700 Subject: [PATCH 012/142] static Retract and Local are superfluous (do not belong to any concept) --- gtsam/base/Lie.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/gtsam/base/Lie.h b/gtsam/base/Lie.h index 05c7bc20f..36370b4f5 100644 --- a/gtsam/base/Lie.h +++ b/gtsam/base/Lie.h @@ -83,22 +83,6 @@ struct LieGroup { return Class::Logmap(between(g)); } - static Class Retract(const TangentVector& v) { - return Class::ChartAtOrigin::Retract(v); - } - - static TangentVector LocalCoordinates(const Class& g) { - return Class::ChartAtOrigin::Local(g); - } - - static Class Retract(const TangentVector& v, ChartJacobian H) { - return Class::ChartAtOrigin::Retract(v,H); - } - - static TangentVector LocalCoordinates(const Class& g, ChartJacobian H) { - return Class::ChartAtOrigin::Local(g,H); - } - Class retract(const TangentVector& v) const { return compose(Class::ChartAtOrigin::Retract(v)); } From f5c8b07f6697b8d8722e8add2e32d7c5e82189e3 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 16 Jul 2015 23:46:39 -0700 Subject: [PATCH 013/142] ChartOrigin is only meant to generate a Lie group class --- gtsam/geometry/Pose3.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtsam/geometry/Pose3.cpp b/gtsam/geometry/Pose3.cpp index ac08f0797..4fbc2a2cb 100644 --- a/gtsam/geometry/Pose3.cpp +++ b/gtsam/geometry/Pose3.cpp @@ -157,7 +157,7 @@ Pose3 Pose3::ChartAtOrigin::Retract(const Vector6& xi, ChartJacobian H) { return Expmap(xi, H); #else Matrix3 DR; - Rot3 R = Rot3::Retract(xi.head<3>(), H ? &DR : 0); + Rot3 R = Rot3::ChartAtOrigin::Retract(xi.head<3>(), H ? &DR : 0); if (H) { *H = I_6x6; H->topLeftCorner<3,3>() = DR; @@ -172,7 +172,7 @@ Vector6 Pose3::ChartAtOrigin::Local(const Pose3& T, ChartJacobian H) { return Logmap(T, H); #else Matrix3 DR; - Vector3 omega = Rot3::LocalCoordinates(T.rotation(), H ? &DR : 0); + Vector3 omega = Rot3::ChartAtOrigin::Local(T.rotation(), H ? &DR : 0); if (H) { *H = I_6x6; H->topLeftCorner<3,3>() = DR; From 233fe13e60456b5a29df248cc042d78c09989e14 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 16 Jul 2015 23:47:57 -0700 Subject: [PATCH 014/142] No more static Local/Retract --- gtsam/geometry/tests/testPose3.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gtsam/geometry/tests/testPose3.cpp b/gtsam/geometry/tests/testPose3.cpp index f6f8b7b40..50c143bb9 100644 --- a/gtsam/geometry/tests/testPose3.cpp +++ b/gtsam/geometry/tests/testPose3.cpp @@ -471,15 +471,6 @@ TEST( Pose3, transformPose_to) EXPECT(assert_equal(expected, actual, 0.001)); } -/* ************************************************************************* */ -TEST(Pose3, Retract_LocalCoordinates) -{ - Vector6 d; - d << 1,2,3,4,5,6; d/=10; - R = Rot3::Retract(d.head<3>()); - Pose3 t = Pose3::Retract(d); - EXPECT(assert_equal(d, Pose3::LocalCoordinates(t))); -} /* ************************************************************************* */ TEST(Pose3, retract_localCoordinates) { From d1271fd9d5caad802f9b2d1ec9363c82cf2f0561 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 16 Jul 2015 23:48:56 -0700 Subject: [PATCH 015/142] Fixed product retract/localCoordinates and corresponding tests --- gtsam/base/ProductLieGroup.h | 60 +++++-------------- gtsam_unstable/dynamics/tests/testPoseRTV.cpp | 22 +++---- tests/testLie.cpp | 4 +- 3 files changed, 30 insertions(+), 56 deletions(-) diff --git a/gtsam/base/ProductLieGroup.h b/gtsam/base/ProductLieGroup.h index 90aeb54d1..ceb7fa48c 100644 --- a/gtsam/base/ProductLieGroup.h +++ b/gtsam/base/ProductLieGroup.h @@ -74,17 +74,21 @@ public: typedef Eigen::Matrix TangentVector; typedef OptionalJacobian ChartJacobian; - static ProductLieGroup Retract(const TangentVector& v) { - return ProductLieGroup::ChartAtOrigin::Retract(v); + ProductLieGroup retract(const TangentVector& v, // + ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { + if (H1||H2) throw std::runtime_error("ProductLieGroup::retract derivatives not implemented yet"); + G g = traits::Retract(this->first, v.template head()); + H h = traits::Retract(this->second, v.template tail()); + return ProductLieGroup(g,h); } - static TangentVector LocalCoordinates(const ProductLieGroup& g) { - return ProductLieGroup::ChartAtOrigin::Local(g); - } - ProductLieGroup retract(const TangentVector& v) const { - return compose(ProductLieGroup::ChartAtOrigin::Retract(v)); - } - TangentVector localCoordinates(const ProductLieGroup& g) const { - return ProductLieGroup::ChartAtOrigin::Local(between(g)); + TangentVector localCoordinates(const ProductLieGroup& g, // + ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { + if (H1||H2) throw std::runtime_error("ProductLieGroup::localCoordinates derivatives not implemented yet"); + typename traits::TangentVector v1 = traits::Local(this->first, g.first); + typename traits::TangentVector v2 = traits::Local(this->second, g.second); + TangentVector v; + v << v1, v2; + return v; } /// @} @@ -147,51 +151,19 @@ public: v << v1, v2; return v; } - struct ChartAtOrigin { - static TangentVector Local(const ProductLieGroup& m, ChartJacobian Hm = boost::none) { - return Logmap(m, Hm); - } - static ProductLieGroup Retract(const TangentVector& v, ChartJacobian Hv = boost::none) { - return Expmap(v, Hv); - } - }; ProductLieGroup expmap(const TangentVector& v) const { return compose(ProductLieGroup::Expmap(v)); } TangentVector logmap(const ProductLieGroup& g) const { return ProductLieGroup::Logmap(between(g)); } - static ProductLieGroup Retract(const TangentVector& v, ChartJacobian H1) { - return ProductLieGroup::ChartAtOrigin::Retract(v,H1); - } - static TangentVector LocalCoordinates(const ProductLieGroup& g, ChartJacobian H1) { - return ProductLieGroup::ChartAtOrigin::Local(g,H1); - } - ProductLieGroup retract(const TangentVector& v, // - ChartJacobian H1, ChartJacobian H2 = boost::none) const { - Jacobian D_g_v; - ProductLieGroup g = ProductLieGroup::ChartAtOrigin::Retract(v,H2 ? &D_g_v : 0); - ProductLieGroup h = compose(g,H1,H2); - if (H2) *H2 = (*H2) * D_g_v; - return h; - } - TangentVector localCoordinates(const ProductLieGroup& g, // - ChartJacobian H1, ChartJacobian H2 = boost::none) const { - ProductLieGroup h = between(g,H1,H2); - Jacobian D_v_h; - TangentVector v = ProductLieGroup::ChartAtOrigin::Local(h, (H1 || H2) ? &D_v_h : 0); - if (H1) *H1 = D_v_h * (*H1); - if (H2) *H2 = D_v_h * (*H2); - return v; - } /// @} }; // Define any direct product group to be a model of the multiplicative Group concept template -struct traits > : internal::LieGroupTraits< - ProductLieGroup > { -}; +struct traits > : internal::LieGroupTraits > {}; + } // namespace gtsam diff --git a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp index 2cc613d65..a8721a60d 100644 --- a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp +++ b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp @@ -86,21 +86,23 @@ TEST( testPoseRTV, Lie ) { Vector delta(9); delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3; - Rot3 rot2 = rot.retract(repeat(3, 0.1)); - Point3 pt2 = pt + rot * Point3(0.2, 0.3, 0.4); - Velocity3 vel2 = vel + rot * Velocity3(-0.1,-0.2,-0.3); - PoseRTV state2(pt2, rot2, vel2); - EXPECT(assert_equal(state2, (PoseRTV)state1.retract(delta), 1e-1)); - EXPECT(assert_equal(delta, state1.localCoordinates(state2), 1e-1)); - EXPECT(assert_equal(delta, -state2.localCoordinates(state1), 1e-1)); + Pose3 pose2 = Pose3(rot, pt).retract(delta.head<6>()); + Velocity3 vel2 = vel + Velocity3(-0.1, -0.2, -0.3); + PoseRTV state2(pose2.translation(), pose2.rotation(), vel2); + EXPECT(assert_equal(state2, (PoseRTV)state1.retract(delta), tol)); + EXPECT(assert_equal(delta, state1.localCoordinates(state2), tol)); // roundtrip from state2 to state3 and back PoseRTV state3 = state2.retract(delta); - EXPECT(assert_equal(delta, state2.localCoordinates(state3), 1e-1)); + EXPECT(assert_equal(delta, state2.localCoordinates(state3), tol)); - // roundtrip from state3 to state4 and back, with expmap + // roundtrip from state3 to state4 and back, with expmap. PoseRTV state4 = state3.expmap(delta); - EXPECT(assert_equal(delta, state3.logmap(state4), 1e-1)); + EXPECT(assert_equal(delta, state3.logmap(state4), tol)); + + // For the expmap/logmap (not necessarily retract/local) -delta goes other way + EXPECT(assert_equal(state3, (PoseRTV)state4.expmap(-delta), tol)); + EXPECT(assert_equal(delta, -state4.logmap(state3), tol)); } /* ************************************************************************* */ diff --git a/tests/testLie.cpp b/tests/testLie.cpp index 7be629053..c153adf5f 100644 --- a/tests/testLie.cpp +++ b/tests/testLie.cpp @@ -54,9 +54,9 @@ TEST(Lie, ProductLieGroup) { Vector5 d; d << 1, 2, 0.1, 0.2, 0.3; Product expected(Point2(1, 2), Pose2::Expmap(Vector3(0.1, 0.2, 0.3))); - Product pair2 = pair1.retract(d); + Product pair2 = pair1.expmap(d); EXPECT(assert_equal(expected, pair2, 1e-9)); - EXPECT(assert_equal(d, pair1.localCoordinates(pair2), 1e-9)); + EXPECT(assert_equal(d, pair1.logmap(pair2), 1e-9)); } /* ************************************************************************* */ From 8ff4c983372836ab26f509180ef13a575bf07e2f Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 00:52:47 -0700 Subject: [PATCH 016/142] Rationalize predict --- gtsam/navigation/CombinedImuFactor.h | 12 ++-- gtsam/navigation/ImuFactor.h | 12 ++-- gtsam/navigation/PreintegrationBase.cpp | 94 ++++++++++++------------- gtsam/navigation/PreintegrationBase.h | 25 +++++-- 4 files changed, 72 insertions(+), 71 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index a65a4d3f7..7fdafd8ce 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -226,17 +226,13 @@ public: boost::optional H4 = boost::none, boost::optional H5 = boost::none, boost::optional H6 = boost::none) const; - /** @deprecated The following function has been deprecated, use PreintegrationBase::predict with the same signature instead */ + // @deprecated The following function has been deprecated, use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, const CombinedPreintegratedMeasurements& PIM, const Vector3& gravity, - const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false, - boost::optional deltaPij_biascorrected_out = boost::none, - boost::optional deltaVij_biascorrected_out = boost::none) { - PoseVelocityBias PVB( - PIM.predict(pose_i, vel_i, bias_i, gravity, omegaCoriolis, - use2ndOrderCoriolis, deltaPij_biascorrected_out, - deltaVij_biascorrected_out)); + const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false) { + PoseVelocityBias PVB = PIM.predict(pose_i, vel_i, bias_i, gravity, + omegaCoriolis, use2ndOrderCoriolis); pose_j = PVB.pose; vel_j = PVB.velocity; } diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 50e6c835f..57f4d0e89 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -206,17 +206,13 @@ public: boost::optional H3 = boost::none, boost::optional H4 = boost::none, boost::optional H5 = boost::none) const; - /** @deprecated The following function has been deprecated, use PreintegrationBase::predict with the same signature instead */ + /// @deprecated The following function has been deprecated, use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, const PreintegratedMeasurements& PIM, const Vector3& gravity, - const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false, - boost::optional deltaPij_biascorrected_out = boost::none, - boost::optional deltaVij_biascorrected_out = boost::none) { - PoseVelocityBias PVB( - PIM.predict(pose_i, vel_i, bias_i, gravity, omegaCoriolis, - use2ndOrderCoriolis, deltaPij_biascorrected_out, - deltaVij_biascorrected_out)); + const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false) { + PoseVelocityBias PVB = PIM.predict(pose_i, vel_i, bias_i, gravity, + omegaCoriolis, use2ndOrderCoriolis); pose_j = PVB.pose; vel_j = PVB.velocity; } diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index be604e784..c02aa01c6 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -146,57 +146,50 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( /// Predict state at time j //------------------------------------------------------------------------------ -PoseVelocityBias PreintegrationBase::predict( - const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis, - boost::optional deltaPij_biascorrected_out, - boost::optional deltaVij_biascorrected_out) const { - - const imuBias::ConstantBias biasIncr = bias_i - biasHat(); - const Vector3& biasAccIncr = biasIncr.accelerometer(); - const Vector3& biasOmegaIncr = biasIncr.gyroscope(); - - const Rot3& Rot_i = pose_i.rotation(); - const Matrix3 Rot_i_matrix = Rot_i.matrix(); - const Vector3 pos_i = pose_i.translation().vector(); - - // Predict state at time j - /* ---------------------------------------------------------------------------------------------*/ - const Vector3 deltaPij_biascorrected = deltaPij() + delPdelBiasAcc() * biasAccIncr - + delPdelBiasOmega() * biasOmegaIncr; - if (deltaPij_biascorrected_out) // if desired, store this - *deltaPij_biascorrected_out = deltaPij_biascorrected; +PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, + const Vector3& vel_i, const imuBias::ConstantBias& bias_i, + const Vector3& gravity, const Vector3& omegaCoriolis, + const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, + const Vector3& deltaVij_biascorrected, + const bool use2ndOrderCoriolis) const { const double dt = deltaTij(), dt2 = dt * dt; - Vector3 pos_j = pos_i + Rot_i_matrix * deltaPij_biascorrected + vel_i * dt - - omegaCoriolis.cross(vel_i) * dt2 // Coriolis term - we got rid of the 2 wrt ins paper - + 0.5 * gravity * dt2; - const Vector3 deltaVij_biascorrected = deltaVij() + delVdelBiasAcc() * biasAccIncr - + delVdelBiasOmega() * biasOmegaIncr; - if (deltaVij_biascorrected_out) // if desired, store this - *deltaVij_biascorrected_out = deltaVij_biascorrected; + // Rotation + const Matrix3 Ri = pose_i.rotation().matrix(); + const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected); + const Vector3 dR = biascorrectedOmega + - Ri.transpose() * omegaCoriolis * dt; // Coriolis term - Vector3 vel_j = Vector3(vel_i + Rot_i_matrix * deltaVij_biascorrected - - 2 * omegaCoriolis.cross(vel_i) * dt // Coriolis term - + gravity * dt); + // Translation + Vector3 dP = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * gravity * dt2 + - omegaCoriolis.cross(vel_i) * dt2; // Coriolis term - we got rid of the 2 wrt INS paper + + // Velocity + Vector3 dV = Ri * deltaVij_biascorrected + gravity * dt + - 2 * omegaCoriolis.cross(vel_i) * dt; // Coriolis term if (use2ndOrderCoriolis) { - pos_j += -0.5 * omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt2; // for position - vel_j += -omegaCoriolis.cross(omegaCoriolis.cross(pos_i)) * dt; // for velocity + Vector3 v = omegaCoriolis.cross(omegaCoriolis.cross(pose_i.translation().vector())); + dP -= 0.5 * v * dt2; + dV -= v * dt; } - const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasOmegaIncr); - // deltaRij_biascorrected = deltaRij * expmap(delRdelBiasOmega * biasOmegaIncr) + // TODO(frank): pose update below is separate expmap for R,t. Is that kosher? + const Pose3 pose_j = Pose3(pose_i.rotation().expmap(dR), pose_i.translation() + Point3(dP)); + return PoseVelocityBias(pose_j, vel_i + dV, bias_i); // bias is predicted as a constant +} - const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected); - const Vector3 correctedOmega = biascorrectedOmega - - Rot_i_matrix.transpose() * omegaCoriolis * dt; // Coriolis term - const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega); - const Rot3 Rot_j = Rot_i.compose(correctedDeltaRij); - - const Pose3 pose_j = Pose3(Rot_j, Point3(pos_j)); - return PoseVelocityBias(pose_j, vel_j, bias_i); // bias is predicted as a constant +/// Predict state at time j +//------------------------------------------------------------------------------ +PoseVelocityBias PreintegrationBase::predict( + const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, + const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) const { + const imuBias::ConstantBias biasIncr = bias_i - biasHat_; + return predict(pose_i, vel_i, bias_i, gravity, omegaCoriolis, + biascorrectedDeltaRij(biasIncr.gyroscope()), + biascorrectedDeltaPij(biasIncr), biascorrectedDeltaVij(biasIncr), + use2ndOrderCoriolis); } /// Compute errors w.r.t. preintegrated measurements and Jacobians wrpt pose_i, vel_i, bias_i, pose_j, bias_j @@ -213,9 +206,6 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5) const { - // We need the mismatch w.r.t. the biases used for preintegration - const Vector3 biasOmegaIncr = bias_i.gyroscope() - biasHat().gyroscope(); - // we give some shorter name to rotations and translations const Rot3& Ri = pose_i.rotation(); const Matrix3 Ri_transpose_matrix = Ri.transpose(); @@ -225,10 +215,13 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Evaluate residual error, according to [3] /* ---------------------------------------------------------------------------------------------------- */ - Vector3 deltaPij_biascorrected, deltaVij_biascorrected; - PoseVelocityBias predictedState_j = predict(pose_i, vel_i, bias_i, gravity, omegaCoriolis, - use2ndOrderCoriolis, deltaPij_biascorrected, - deltaVij_biascorrected); + const imuBias::ConstantBias biasIncr = bias_i - biasHat_; + const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasIncr.gyroscope()); + const Vector3 deltaPij_biascorrected = biascorrectedDeltaPij(biasIncr); + const Vector3 deltaVij_biascorrected = biascorrectedDeltaVij(biasIncr); + PoseVelocityBias predictedState_j = predict(pose_i, vel_i, bias_i, gravity, + omegaCoriolis, deltaRij_biascorrected, deltaPij_biascorrected, + deltaVij_biascorrected, use2ndOrderCoriolis); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance const Vector3 fp = Ri_transpose_matrix * (pos_j - predictedState_j.pose.translation().vector()); @@ -242,8 +235,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Get Get so<3> version of bias corrected rotation // If H5 is asked for, we will need the Jacobian, which we store in H5 // H5 will then be corrected below to take into account the Coriolis effect + // TODO(frank): biascorrectedThetaRij calculates deltaRij_biascorrected, which we already have Matrix3 D_cThetaRij_biasOmegaIncr; - const Vector3 biascorrectedOmega = biascorrectedThetaRij(biasOmegaIncr, + const Vector3 biascorrectedOmega = biascorrectedThetaRij(biasIncr.gyroscope(), H5 ? &D_cThetaRij_biasOmegaIncr : 0); // Coriolis term, note inconsistent with AHRS, where coriolisHat is *after* integration diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 85ffae8a2..722091b40 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -138,12 +138,27 @@ class PreintegrationBase : public PreintegratedRotation { Vector3& correctedOmega, boost::optional body_P_sensor); + Vector3 biascorrectedDeltaPij(const imuBias::ConstantBias& biasIncr) const { + return deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() + + delPdelBiasOmega_ * biasIncr.gyroscope(); + } + + Vector3 biascorrectedDeltaVij(const imuBias::ConstantBias& biasIncr) const { + return deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() + + delVdelBiasOmega_ * biasIncr.gyroscope(); + } + + /// Predict state at time j, with bias-corrected quantities given + PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, + const imuBias::ConstantBias& bias_i, const Vector3& gravity, const Vector3& omegaCoriolis, + const Rot3& deltaRij_biascorrected, + const Vector3& deltaPij_biascorrected, const Vector3& deltaVij_biascorrected, + const bool use2ndOrderCoriolis = false) const; + /// Predict state at time j - PoseVelocityBias predict( - const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false, - boost::optional deltaPij_biascorrected_out = boost::none, - boost::optional deltaVij_biascorrected_out = boost::none) const; + PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, + const imuBias::ConstantBias& bias_i, const Vector3& gravity, + const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false) const; /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, From d45d2e17edcb8e2808a00dfb9124ceca5f13b76b Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 01:07:15 -0700 Subject: [PATCH 017/142] inline transpose --- gtsam/navigation/PreintegrationBase.cpp | 39 +++++++++++++------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index c02aa01c6..2c48331d9 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -170,9 +170,9 @@ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, - 2 * omegaCoriolis.cross(vel_i) * dt; // Coriolis term if (use2ndOrderCoriolis) { - Vector3 v = omegaCoriolis.cross(omegaCoriolis.cross(pose_i.translation().vector())); - dP -= 0.5 * v * dt2; - dV -= v * dt; + Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(pose_i.translation().vector())); + dP -= 0.5 * temp * dt2; + dV -= temp * dt; } // TODO(frank): pose update below is separate expmap for R,t. Is that kosher? @@ -207,10 +207,10 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const OptionalJacobian<9, 6> H5) const { // we give some shorter name to rotations and translations - const Rot3& Ri = pose_i.rotation(); - const Matrix3 Ri_transpose_matrix = Ri.transpose(); + const Rot3& rot_i = pose_i.rotation(); + const Matrix Ri = rot_i.matrix(); - const Rot3& Rj = pose_j.rotation(); + const Rot3& rot_j = pose_j.rotation(); const Vector3 pos_j = pose_j.translation().vector(); // Evaluate residual error, according to [3] @@ -224,10 +224,10 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const deltaVij_biascorrected, use2ndOrderCoriolis); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fp = Ri_transpose_matrix * (pos_j - predictedState_j.pose.translation().vector()); + const Vector3 fp = Ri.transpose() * (pos_j - predictedState_j.pose.translation().vector()); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fv = Ri_transpose_matrix * (vel_j - predictedState_j.velocity); + const Vector3 fv = Ri.transpose() * (vel_j - predictedState_j.velocity); // fR will be computed later. Note: it is the same as: fR = (predictedState_j.pose.translation()).between(Rot_j) @@ -241,20 +241,17 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const H5 ? &D_cThetaRij_biasOmegaIncr : 0); // Coriolis term, note inconsistent with AHRS, where coriolisHat is *after* integration - const Vector3 coriolis = integrateCoriolis(Ri, omegaCoriolis); + const Vector3 coriolis = integrateCoriolis(rot_i, omegaCoriolis); const Vector3 correctedOmega = biascorrectedOmega - coriolis; // Calculate Jacobians, matrices below needed only for some Jacobians... Vector3 fR; Matrix3 D_cDeltaRij_cOmega, D_coriolis, D_fR_fRrot, Ritranspose_omegaCoriolisHat; - if (H1 || H2) - Ritranspose_omegaCoriolisHat = Ri_transpose_matrix * skewSymmetric(omegaCoriolis); - // This is done to save computation: we ask for the jacobians only when they are needed const double dt = deltaTij(), dt2 = dt*dt; Rot3 fRrot; - const Rot3 RiBetweenRj = Rot3(Ri_transpose_matrix) * Rj; + const Rot3 RiBetweenRj = rot_i.between(rot_j); if (H1 || H2 || H3 || H4 || H5) { const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, D_cDeltaRij_cOmega); // Residual rotation error @@ -267,10 +264,14 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const fRrot = correctedDeltaRij.between(RiBetweenRj); fR = Rot3::Logmap(fRrot); } + + if (H1 || H2) + Ritranspose_omegaCoriolisHat = Ri.transpose() * skewSymmetric(omegaCoriolis); + if (H1) { Matrix3 dfPdPi = -I_3x3, dfVdPi = Z_3x3; if (use2ndOrderCoriolis) { - // this is the same as: Ri.transpose() * omegaCoriolisHat * omegaCoriolisHat * Ri.matrix() + // this is the same as: Ri.transpose() * omegaCoriolisHat * omegaCoriolisHat * Ri const Matrix3 temp = Ritranspose_omegaCoriolisHat * (-Ritranspose_omegaCoriolisHat.transpose()); dfPdPi += 0.5 * temp * dt2; @@ -286,23 +287,23 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // dfV/dPi dfVdPi, // dfR/dRi - D_fR_fRrot * (-Rj.between(Ri).matrix() - fRrot.inverse().matrix() * D_coriolis), + D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), // dfR/dPi Z_3x3; } if (H2) { (*H2) << // dfP/dVi - -Ri_transpose_matrix * dt + Ritranspose_omegaCoriolisHat * dt2, // Coriolis term - we got rid of the 2 wrt ins paper + -Ri.transpose() * dt + Ritranspose_omegaCoriolisHat * dt2, // Coriolis term - we got rid of the 2 wrt ins paper // dfV/dVi - -Ri_transpose_matrix + 2 * Ritranspose_omegaCoriolisHat * dt, // Coriolis term + -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * dt, // Coriolis term // dfR/dVi Z_3x3; } if (H3) { (*H3) << // dfP/dPosej - Z_3x3, Ri_transpose_matrix * Rj.matrix(), + Z_3x3, Ri.transpose() * rot_j.matrix(), // dfV/dPosej Matrix::Zero(3, 6), // dfR/dPosej @@ -313,7 +314,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // dfP/dVj Z_3x3, // dfV/dVj - Ri_transpose_matrix, + Ri.transpose(), // dfR/dVj Z_3x3; } From 66a9c348404a74647c5457bb6883f3c8fefc4c8c Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 01:14:44 -0700 Subject: [PATCH 018/142] Clean up jacobians a bit --- gtsam/navigation/PreintegrationBase.cpp | 59 +++++++++---------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 2c48331d9..7302d0330 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -246,10 +246,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Calculate Jacobians, matrices below needed only for some Jacobians... Vector3 fR; - Matrix3 D_cDeltaRij_cOmega, D_coriolis, D_fR_fRrot, Ritranspose_omegaCoriolisHat; + Matrix3 D_cDeltaRij_cOmega, D_coriolis, D_fR_fRrot; // This is done to save computation: we ask for the jacobians only when they are needed - const double dt = deltaTij(), dt2 = dt*dt; Rot3 fRrot; const Rot3 RiBetweenRj = rot_i.between(rot_j); if (H1 || H2 || H3 || H4 || H5) { @@ -265,6 +264,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const fR = Rot3::Logmap(fRrot); } + const double dt = deltaTij(), dt2 = dt*dt; + Matrix3 Ritranspose_omegaCoriolisHat; if (H1 || H2) Ritranspose_omegaCoriolisHat = Ri.transpose() * skewSymmetric(omegaCoriolis); @@ -278,56 +279,38 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const dfVdPi += temp * dt; } (*H1) << - // dfP/dRi - skewSymmetric(fp + deltaPij_biascorrected), - // dfP/dPi - dfPdPi, - // dfV/dRi - skewSymmetric(fv + deltaVij_biascorrected), - // dfV/dPi - dfVdPi, - // dfR/dRi - D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), - // dfR/dPi - Z_3x3; + skewSymmetric(fp + deltaPij_biascorrected), // dfP/dRi + dfPdPi, // dfP/dPi + skewSymmetric(fv + deltaVij_biascorrected), // dfV/dRi + dfVdPi, // dfV/dPi + D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi + Z_3x3; // dfR/dPi } if (H2) { (*H2) << - // dfP/dVi - -Ri.transpose() * dt + Ritranspose_omegaCoriolisHat * dt2, // Coriolis term - we got rid of the 2 wrt ins paper - // dfV/dVi - -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * dt, // Coriolis term - // dfR/dVi - Z_3x3; + -Ri.transpose() * dt + Ritranspose_omegaCoriolisHat * dt2, // dfP/dVi + -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * dt, // dfV/dVi + Z_3x3; // dfR/dVi } if (H3) { (*H3) << - // dfP/dPosej - Z_3x3, Ri.transpose() * rot_j.matrix(), - // dfV/dPosej - Matrix::Zero(3, 6), - // dfR/dPosej - D_fR_fRrot, Z_3x3; + Z_3x3, Ri.transpose() * rot_j.matrix(), // dfP/dPosej + Matrix::Zero(3, 6), // dfV/dPosej + D_fR_fRrot, Z_3x3; // dfR/dPosej } if (H4) { (*H4) << - // dfP/dVj - Z_3x3, - // dfV/dVj - Ri.transpose(), - // dfR/dVj - Z_3x3; + Z_3x3, // dfP/dVj + Ri.transpose(), // dfV/dVj + Z_3x3; // dfR/dVj } if (H5) { // H5 by this point already contains 3*3 biascorrectedThetaRij derivative const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_cThetaRij_biasOmegaIncr; (*H5) << - // dfP/dBias - -delPdelBiasAcc(), -delPdelBiasOmega(), - // dfV/dBias - -delVdelBiasAcc(), -delVdelBiasOmega(), - // dfR/dBias - Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega); + -delPdelBiasAcc(), -delPdelBiasOmega(), // dfP/dBias + -delVdelBiasAcc(), -delVdelBiasOmega(), // dfV/dBias + Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega); // dfR/dBias } Vector9 r; r << fp, fv, fR; From 2d425ad7be613c8378113cf0be5eef1807dc2a4e Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 01:27:07 -0700 Subject: [PATCH 019/142] More substantial jacobian refactor --- gtsam/navigation/PreintegrationBase.cpp | 27 ++++++++----------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 7302d0330..92d4b9520 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -240,29 +240,17 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Vector3 biascorrectedOmega = biascorrectedThetaRij(biasIncr.gyroscope(), H5 ? &D_cThetaRij_biasOmegaIncr : 0); - // Coriolis term, note inconsistent with AHRS, where coriolisHat is *after* integration + // Coriolis term, NOTE inconsistent with AHRS, where coriolisHat is *after* integration const Vector3 coriolis = integrateCoriolis(rot_i, omegaCoriolis); const Vector3 correctedOmega = biascorrectedOmega - coriolis; - // Calculate Jacobians, matrices below needed only for some Jacobians... - Vector3 fR; - Matrix3 D_cDeltaRij_cOmega, D_coriolis, D_fR_fRrot; - - // This is done to save computation: we ask for the jacobians only when they are needed - Rot3 fRrot; + // Residual rotation error + Matrix3 D_cDeltaRij_cOmega; + const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, H1 || H5 ? &D_cDeltaRij_cOmega : 0); const Rot3 RiBetweenRj = rot_i.between(rot_j); - if (H1 || H2 || H3 || H4 || H5) { - const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, D_cDeltaRij_cOmega); - // Residual rotation error - fRrot = correctedDeltaRij.between(RiBetweenRj); - fR = Rot3::Logmap(fRrot, D_fR_fRrot); - D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis); - } else { - const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega); - // Residual rotation error - fRrot = correctedDeltaRij.between(RiBetweenRj); - fR = Rot3::Logmap(fRrot); - } + const Rot3 fRrot = correctedDeltaRij.between(RiBetweenRj); + Matrix3 D_fR_fRrot; + const Vector3 fR = Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); const double dt = deltaTij(), dt2 = dt*dt; Matrix3 Ritranspose_omegaCoriolisHat; @@ -270,6 +258,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Ritranspose_omegaCoriolisHat = Ri.transpose() * skewSymmetric(omegaCoriolis); if (H1) { + const Matrix3 D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis); Matrix3 dfPdPi = -I_3x3, dfVdPi = Z_3x3; if (use2ndOrderCoriolis) { // this is the same as: Ri.transpose() * omegaCoriolisHat * omegaCoriolisHat * Ri From 52baa97ecac095be881e4cb47cc616567d42b0df Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 12:00:03 -0700 Subject: [PATCH 020/142] Some fixed-size matrix optimizations --- gtsam/navigation/CombinedImuFactor.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 3547719ac..1db2f1861 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -108,7 +108,7 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( Matrix3 H_angles_biasomega = -D_Rincr_integratedOmega * deltaT; // overall Jacobian wrt preintegrated measurements (df/dx) - Matrix F(15, 15); + Eigen::Matrix F; // for documentation: // F << I_3x3, I_3x3 * deltaT, Z_3x3, Z_3x3, Z_3x3, // Z_3x3, I_3x3, H_vel_angles, H_vel_biasacc, Z_3x3, @@ -123,9 +123,10 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( // first order uncertainty propagation // Optimized matrix multiplication (1/deltaT) * G * measurementCovariance * G.transpose() - Matrix G_measCov_Gt = Matrix::Zero(15, 15); + Eigen::Matrix G_measCov_Gt; + G_measCov_Gt.setZero(15, 15); -// BLOCK DIAGONAL TERMS + // BLOCK DIAGONAL TERMS G_measCov_Gt.block<3, 3>(0, 0) = deltaT * integrationCovariance(); G_measCov_Gt.block<3, 3>(3, 3) = (1 / deltaT) * (H_vel_biasacc) * (accelerometerCovariance() + biasAccOmegaInit_.block<3, 3>(0, 0)) @@ -135,6 +136,7 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( * (H_angles_biasomega.transpose()); G_measCov_Gt.block<3, 3>(9, 9) = (1 / deltaT) * biasAccCovariance_; G_measCov_Gt.block<3, 3>(12, 12) = (1 / deltaT) * biasOmegaCovariance_; + // OFF BLOCK DIAGONAL TERMS Matrix3 block23 = H_vel_biasacc * biasAccOmegaInit_.block<3, 3>(3, 0) * H_angles_biasomega.transpose(); @@ -234,25 +236,25 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, H1->resize(15, 6); H1->block<9, 6>(0, 0) = D_r_pose_i; // adding: [dBiasAcc/dPi ; dBiasOmega/dPi] - H1->block<6, 6>(9, 0) = Z_6x6; + H1->block<6, 6>(9, 0).setZero(); } if (H2) { H2->resize(15, 3); H2->block<9, 3>(0, 0) = D_r_vel_i; // adding: [dBiasAcc/dVi ; dBiasOmega/dVi] - H2->block<6, 3>(9, 0) = Matrix::Zero(6, 3); + H2->block<6, 3>(9, 0).setZero(); } if (H3) { H3->resize(15, 6); H3->block<9, 6>(0, 0) = D_r_pose_j; // adding: [dBiasAcc/dPj ; dBiasOmega/dPj] - H3->block<6, 6>(9, 0) = Z_6x6; + H3->block<6, 6>(9, 0).setZero(); } if (H4) { H4->resize(15, 3); H4->block<9, 3>(0, 0) = D_r_vel_j; // adding: [dBiasAcc/dVi ; dBiasOmega/dVi] - H4->block<6, 3>(9, 0) = Matrix::Zero(6, 3); + H4->block<6, 3>(9, 0).setZero(); } if (H5) { H5->resize(15, 6); @@ -262,7 +264,7 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, } if (H6) { H6->resize(15, 6); - H6->block<9, 6>(0, 0) = Matrix::Zero(9, 6); + H6->block<9, 6>(0, 0).setZero(); // adding: [dBiasAcc/dBias_j ; dBiasOmega/dBias_j] H6->block<6, 6>(9, 0) = Hbias_j; } From fd0ad8ae78a6574259c5b52847c490fbf70b6edb Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 15:32:58 -0700 Subject: [PATCH 021/142] Removed some useles computation --- gtsam/navigation/AHRSFactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam/navigation/AHRSFactor.cpp b/gtsam/navigation/AHRSFactor.cpp index 2f03d72a4..917b80c9a 100644 --- a/gtsam/navigation/AHRSFactor.cpp +++ b/gtsam/navigation/AHRSFactor.cpp @@ -199,7 +199,7 @@ Vector AHRSFactor::evaluateError(const Rot3& Ri, const Rot3& Rj, if (H2) { // dfR/dPosej H2->resize(3, 3); - (*H2) << D_fR_fRrot * Matrix3::Identity(); + (*H2) << D_fR_fRrot; } if (H3) { From 61c58c6fa6e1203a210ec4dad442076ab46a3f2b Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 15:33:20 -0700 Subject: [PATCH 022/142] Fixed naming convention --- .../tests/testCombinedImuFactor.cpp | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/gtsam/navigation/tests/testCombinedImuFactor.cpp b/gtsam/navigation/tests/testCombinedImuFactor.cpp index 9fb0f939b..c5e9886d9 100644 --- a/gtsam/navigation/tests/testCombinedImuFactor.cpp +++ b/gtsam/navigation/tests/testCombinedImuFactor.cpp @@ -189,37 +189,37 @@ TEST( CombinedImuFactor, ErrorWithBiases ) { pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - CombinedImuFactor::CombinedPreintegratedMeasurements Combined_pre_int_data( + CombinedImuFactor::CombinedPreintegratedMeasurements combined_pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), 2 * Matrix3::Identity(), I6x6); - Combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, + combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, + ImuFactor imuFactor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, omegaCoriolis); noiseModel::Gaussian::shared_ptr Combinedmodel = - noiseModel::Gaussian::Covariance(Combined_pre_int_data.preintMeasCov()); - CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - Combined_pre_int_data, gravity, omegaCoriolis); + noiseModel::Gaussian::Covariance(combined_pre_int_data.preintMeasCov()); + CombinedImuFactor combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), + combined_pre_int_data, gravity, omegaCoriolis); - Vector errorExpected = factor.evaluateError(x1, v1, x2, v2, bias); + Vector errorExpected = imuFactor.evaluateError(x1, v1, x2, v2, bias); - Vector errorActual = Combinedfactor.evaluateError(x1, v1, x2, v2, bias, + Vector errorActual = combinedfactor.evaluateError(x1, v1, x2, v2, bias, bias2); EXPECT(assert_equal(errorExpected, errorActual.head(9), tol)); // Expected Jacobians Matrix H1e, H2e, H3e, H4e, H5e; - (void) factor.evaluateError(x1, v1, x2, v2, bias, H1e, H2e, H3e, H4e, H5e); + (void) imuFactor.evaluateError(x1, v1, x2, v2, bias, H1e, H2e, H3e, H4e, H5e); // Actual Jacobians Matrix H1a, H2a, H3a, H4a, H5a, H6a; - (void) Combinedfactor.evaluateError(x1, v1, x2, v2, bias, bias2, H1a, H2a, + (void) combinedfactor.evaluateError(x1, v1, x2, v2, bias, bias2, H1a, H2a, H3a, H4a, H5a, H6a); EXPECT(assert_equal(H1e, H1a.topRows(9))); @@ -310,24 +310,24 @@ TEST(CombinedImuFactor, PredictPositionAndVelocity) { Matrix I6x6(6, 6); I6x6 = Matrix::Identity(6, 6); - CombinedImuFactor::CombinedPreintegratedMeasurements Combined_pre_int_data( + CombinedImuFactor::CombinedPreintegratedMeasurements combined_pre_int_data( bias, Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), 2 * Matrix3::Identity(), I6x6, true); for (int i = 0; i < 1000; ++i) - Combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, + combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - noiseModel::Gaussian::shared_ptr Combinedmodel = - noiseModel::Gaussian::Covariance(Combined_pre_int_data.preintMeasCov()); + noiseModel::Gaussian::shared_ptr combinedmodel = + noiseModel::Gaussian::Covariance(combined_pre_int_data.preintMeasCov()); CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - Combined_pre_int_data, gravity, omegaCoriolis); + combined_pre_int_data, gravity, omegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocityBias = Combined_pre_int_data.predict(x1, v1, + PoseVelocityBias poseVelocityBias = combined_pre_int_data.predict(x1, v1, bias, gravity, omegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; @@ -341,7 +341,7 @@ TEST(CombinedImuFactor, PredictPositionAndVelocity) { TEST(CombinedImuFactor, PredictRotation) { imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) Matrix I6x6(6, 6); - CombinedImuFactor::CombinedPreintegratedMeasurements Combined_pre_int_data( + CombinedImuFactor::CombinedPreintegratedMeasurements combined_pre_int_data( bias, Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), 2 * Matrix3::Identity(), I6x6, true); Vector3 measuredAcc; @@ -355,10 +355,10 @@ TEST(CombinedImuFactor, PredictRotation) { double deltaT = 0.001; double tol = 1e-4; for (int i = 0; i < 1000; ++i) - Combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, + combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - Combined_pre_int_data, gravity, omegaCoriolis); + combined_pre_int_data, gravity, omegaCoriolis); // Predict Pose3 x(Rot3().ypr(0, 0, 0), Point3(0, 0, 0)), x2; From 55269d642cd881d9d1d0a339e217fbfd266e2ed3 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 15:34:58 -0700 Subject: [PATCH 023/142] Fixed order of components in error to match RTV order --- gtsam/navigation/PreintegrationBase.cpp | 24 ++++++++++++------------ gtsam/navigation/tests/testImuFactor.cpp | 8 ++++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 92d4b9520..cc4fcee16 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -268,41 +268,41 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const dfVdPi += temp * dt; } (*H1) << + D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi + Z_3x3, // dfR/dPi skewSymmetric(fp + deltaPij_biascorrected), // dfP/dRi dfPdPi, // dfP/dPi skewSymmetric(fv + deltaVij_biascorrected), // dfV/dRi - dfVdPi, // dfV/dPi - D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi - Z_3x3; // dfR/dPi + dfVdPi; // dfV/dPi } if (H2) { (*H2) << + Z_3x3, // dfR/dVi -Ri.transpose() * dt + Ritranspose_omegaCoriolisHat * dt2, // dfP/dVi - -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * dt, // dfV/dVi - Z_3x3; // dfR/dVi + -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * dt; // dfV/dVi } if (H3) { (*H3) << + D_fR_fRrot, Z_3x3, // dfR/dPosej Z_3x3, Ri.transpose() * rot_j.matrix(), // dfP/dPosej - Matrix::Zero(3, 6), // dfV/dPosej - D_fR_fRrot, Z_3x3; // dfR/dPosej + Matrix::Zero(3, 6); // dfV/dPosej } if (H4) { (*H4) << + Z_3x3, // dfR/dVj Z_3x3, // dfP/dVj - Ri.transpose(), // dfV/dVj - Z_3x3; // dfR/dVj + Ri.transpose(); // dfV/dVj } if (H5) { // H5 by this point already contains 3*3 biascorrectedThetaRij derivative const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_cThetaRij_biasOmegaIncr; (*H5) << + Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega), // dfR/dBias -delPdelBiasAcc(), -delPdelBiasOmega(), // dfP/dBias - -delVdelBiasAcc(), -delVdelBiasOmega(), // dfV/dBias - Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega); // dfR/dBias + -delVdelBiasAcc(), -delVdelBiasOmega(); // dfV/dBias } Vector9 r; - r << fp, fv, fR; + r << fR, fp, fv; return r; } diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 6b66be342..c6aa48b30 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -47,7 +47,7 @@ Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, const imuBias::ConstantBias& bias) { return Rot3::Expmap( - factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).tail(3)); + factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).head(3)); } // Auxiliary functions to test Jacobians F and G used for @@ -247,16 +247,16 @@ TEST(ImuFactor, ErrorAndJacobians) { // Jacobians are around zero, so the rotation part is the same as: Matrix H1Rot3 = numericalDerivative11( boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); - EXPECT(assert_equal(H1Rot3, H1a.bottomRows(3))); + EXPECT(assert_equal(H1Rot3, H1a.topRows(3))); Matrix H3Rot3 = numericalDerivative11( boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); - EXPECT(assert_equal(H3Rot3, H3a.bottomRows(3))); + EXPECT(assert_equal(H3Rot3, H3a.topRows(3))); // Evaluate error with wrong values Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1); values.update(V(2), v2_wrong); - errorExpected << 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901, 0, 0, 0; + errorExpected << 0, 0, 0, 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901; EXPECT( assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-6)); From d5bf2493fe0be9aef43c4a2cb43ca55d97983d7e Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 16:57:16 -0700 Subject: [PATCH 024/142] Fixed remaining method call --- gtsam/base/ProductLieGroup.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam/base/ProductLieGroup.h b/gtsam/base/ProductLieGroup.h index ceb7fa48c..463b5f5d9 100644 --- a/gtsam/base/ProductLieGroup.h +++ b/gtsam/base/ProductLieGroup.h @@ -55,7 +55,7 @@ public: traits::Compose(this->second,other.second)); } ProductLieGroup inverse() const { - return ProductLieGroup(this->first.inverse(), this->second.inverse()); + return ProductLieGroup(traits::Inverse(this->first), traits::Inverse(this->second)); } ProductLieGroup compose(const ProductLieGroup& g) const { return (*this) * g; From 233cabb3fbd33a83419217156c111ac8b7717f26 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 17 Jul 2015 17:36:29 -0700 Subject: [PATCH 025/142] Made Velocity a Vector3 --- gtsam_unstable/dynamics/PoseRTV.cpp | 13 +++++++------ gtsam_unstable/dynamics/PoseRTV.h | 4 ++-- gtsam_unstable/dynamics/VelocityConstraint.h | 11 ++++++----- gtsam_unstable/dynamics/tests/testIMUSystem.cpp | 12 ++++++------ gtsam_unstable/dynamics/tests/testPoseRTV.cpp | 4 ++-- .../dynamics/tests/testVelocityConstraint.cpp | 4 ++-- gtsam_unstable/gtsam_unstable.h | 8 ++++---- 7 files changed, 29 insertions(+), 27 deletions(-) diff --git a/gtsam_unstable/dynamics/PoseRTV.cpp b/gtsam_unstable/dynamics/PoseRTV.cpp index 942e1ab55..92f3b9b0b 100644 --- a/gtsam_unstable/dynamics/PoseRTV.cpp +++ b/gtsam_unstable/dynamics/PoseRTV.cpp @@ -38,13 +38,14 @@ Vector PoseRTV::vector() const { Vector rtv(9); rtv.head(3) = rotation().xyz(); rtv.segment(3,3) = translation().vector(); - rtv.tail(3) = velocity().vector(); + rtv.tail(3) = velocity(); return rtv; } /* ************************************************************************* */ bool PoseRTV::equals(const PoseRTV& other, double tol) const { - return pose().equals(other.pose(), tol) && velocity().equals(other.velocity(), tol); + return pose().equals(other.pose(), tol) + && equal_with_abs_tol(velocity(), other.velocity(), tol); } /* ************************************************************************* */ @@ -52,7 +53,7 @@ void PoseRTV::print(const string& s) const { cout << s << ":" << endl; gtsam::print((Vector)R().xyz(), " R:rpy"); t().print(" T"); - velocity().print(" V"); + gtsam::print((Vector)velocity(), " V"); } /* ************************************************************************* */ @@ -137,7 +138,7 @@ Vector6 PoseRTV::imuPrediction(const PoseRTV& x2, double dt) const { Vector6 imu; // acceleration - Vector3 accel = (v2-v1).vector() / dt; + Vector3 accel = (v2-v1) / dt; imu.head<3>() = r2.transpose() * (accel - kGravity); // rotation rates @@ -160,7 +161,7 @@ Vector6 PoseRTV::imuPrediction(const PoseRTV& x2, double dt) const { Point3 PoseRTV::translationIntegration(const Rot3& r2, const Velocity3& v2, double dt) const { // predict point for constraint // NOTE: uses simple Euler approach for prediction - Point3 pred_t2 = t() + v2 * dt; + Point3 pred_t2 = t().retract(v2 * dt); return pred_t2; } @@ -187,7 +188,7 @@ PoseRTV PoseRTV::transformed_from(const Pose3& trans, ChartJacobian Dglobal, // Note that we rotate the velocity Matrix3 D_newvel_R, D_newvel_v; - Velocity3 newvel = trans.rotation().rotate(velocity(), D_newvel_R, D_newvel_v); + Velocity3 newvel = trans.rotation().rotate(Point3(velocity()), D_newvel_R, D_newvel_v).vector(); if (Dglobal) { Dglobal->setZero(); diff --git a/gtsam_unstable/dynamics/PoseRTV.h b/gtsam_unstable/dynamics/PoseRTV.h index bef397cb6..e89d570b7 100644 --- a/gtsam_unstable/dynamics/PoseRTV.h +++ b/gtsam_unstable/dynamics/PoseRTV.h @@ -13,7 +13,7 @@ namespace gtsam { /// Syntactic sugar to clarify components -typedef Point3 Velocity3; +typedef Vector3 Velocity3; /** * Robot state for use with IMU measurements @@ -66,7 +66,7 @@ public: // and avoidance of Point3 Vector vector() const; Vector translationVec() const { return pose().translation().vector(); } - Vector velocityVec() const { return velocity().vector(); } + const Velocity3& velocityVec() const { return velocity(); } // testable bool equals(const PoseRTV& other, double tol=1e-6) const; diff --git a/gtsam_unstable/dynamics/VelocityConstraint.h b/gtsam_unstable/dynamics/VelocityConstraint.h index c9db449f9..c41ea220c 100644 --- a/gtsam_unstable/dynamics/VelocityConstraint.h +++ b/gtsam_unstable/dynamics/VelocityConstraint.h @@ -106,12 +106,13 @@ private: static gtsam::Vector evaluateError_(const PoseRTV& x1, const PoseRTV& x2, double dt, const dynamics::IntegrationMode& mode) { - const Velocity3& v1 = x1.v(), v2 = x2.v(), p1 = x1.t(), p2 = x2.t(); - Velocity3 hx; + const Velocity3& v1 = x1.v(), v2 = x2.v(); + const Point3& p1 = x1.t(), p2 = x2.t(); + Point3 hx; switch(mode) { - case dynamics::TRAPEZOIDAL: hx = p1 + (v1 + v2) * dt *0.5; break; - case dynamics::EULER_START: hx = p1 + v1 * dt; break; - case dynamics::EULER_END : hx = p1 + v2 * dt; break; + case dynamics::TRAPEZOIDAL: hx = p1.retract((v1 + v2) * dt *0.5); break; + case dynamics::EULER_START: hx = p1.retract(v1 * dt); break; + case dynamics::EULER_END : hx = p1.retract(v2 * dt); break; default: assert(false); break; } return (p2 - hx).vector(); diff --git a/gtsam_unstable/dynamics/tests/testIMUSystem.cpp b/gtsam_unstable/dynamics/tests/testIMUSystem.cpp index 51d027b3c..3fff06de1 100644 --- a/gtsam_unstable/dynamics/tests/testIMUSystem.cpp +++ b/gtsam_unstable/dynamics/tests/testIMUSystem.cpp @@ -56,9 +56,9 @@ TEST( testIMUSystem, optimize_chain ) { // create a simple chain of poses to generate IMU measurements const double dt = 1.0; PoseRTV pose1, - pose2(Point3(1.0, 1.0, 0.0), Rot3::ypr(0.1, 0.0, 0.0), Point3(2.0, 2.0, 0.0)), - pose3(Point3(2.0, 2.0, 0.0), Rot3::ypr(0.2, 0.0, 0.0), Point3(0.0, 0.0, 0.0)), - pose4(Point3(3.0, 3.0, 0.0), Rot3::ypr(0.3, 0.0, 0.0), Point3(2.0, 2.0, 0.0)); + pose2(Point3(1.0, 1.0, 0.0), Rot3::ypr(0.1, 0.0, 0.0), Velocity3(2.0, 2.0, 0.0)), + pose3(Point3(2.0, 2.0, 0.0), Rot3::ypr(0.2, 0.0, 0.0), Velocity3(0.0, 0.0, 0.0)), + pose4(Point3(3.0, 3.0, 0.0), Rot3::ypr(0.3, 0.0, 0.0), Velocity3(2.0, 2.0, 0.0)); // create measurements SharedDiagonal model = noiseModel::Unit::Create(6); @@ -102,9 +102,9 @@ TEST( testIMUSystem, optimize_chain_fullfactor ) { // create a simple chain of poses to generate IMU measurements const double dt = 1.0; PoseRTV pose1, - pose2(Point3(1.0, 0.0, 0.0), Rot3::ypr(0.0, 0.0, 0.0), Point3(1.0, 0.0, 0.0)), - pose3(Point3(2.0, 0.0, 0.0), Rot3::ypr(0.0, 0.0, 0.0), Point3(1.0, 0.0, 0.0)), - pose4(Point3(3.0, 0.0, 0.0), Rot3::ypr(0.0, 0.0, 0.0), Point3(1.0, 0.0, 0.0)); + pose2(Point3(1.0, 0.0, 0.0), Rot3::ypr(0.0, 0.0, 0.0), Velocity3(1.0, 0.0, 0.0)), + pose3(Point3(2.0, 0.0, 0.0), Rot3::ypr(0.0, 0.0, 0.0), Velocity3(1.0, 0.0, 0.0)), + pose4(Point3(3.0, 0.0, 0.0), Rot3::ypr(0.0, 0.0, 0.0), Velocity3(1.0, 0.0, 0.0)); // create measurements SharedDiagonal model = noiseModel::Isotropic::Sigma(9, 1.0); diff --git a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp index a8721a60d..ff3508f45 100644 --- a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp +++ b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp @@ -198,7 +198,7 @@ TEST( testPoseRTV, transformed_from_1 ) { Matrix actDTrans, actDGlobal; PoseRTV actual = start.transformed_from(transform, actDGlobal, actDTrans); - PoseRTV expected(transform.compose(start.pose()), transform.rotation().rotate(V)); + PoseRTV expected(transform.compose(start.pose()), transform.rotation().matrix() * V); EXPECT(assert_equal(expected, actual, tol)); Matrix numDGlobal = numericalDerivative21(transformed_from_proxy, start, transform, 1e-5); // At 1e-8, fails @@ -217,7 +217,7 @@ TEST( testPoseRTV, transformed_from_2 ) { Matrix actDTrans, actDGlobal; PoseRTV actual = start.transformed_from(transform, actDGlobal, actDTrans); - PoseRTV expected(transform.compose(start.pose()), transform.rotation().rotate(V)); + PoseRTV expected(transform.compose(start.pose()), transform.rotation().matrix() * V); EXPECT(assert_equal(expected, actual, tol)); Matrix numDGlobal = numericalDerivative21(transformed_from_proxy, start, transform, 1e-5); // At 1e-8, fails diff --git a/gtsam_unstable/dynamics/tests/testVelocityConstraint.cpp b/gtsam_unstable/dynamics/tests/testVelocityConstraint.cpp index 56d38714a..f253be371 100644 --- a/gtsam_unstable/dynamics/tests/testVelocityConstraint.cpp +++ b/gtsam_unstable/dynamics/tests/testVelocityConstraint.cpp @@ -15,9 +15,9 @@ const Key x1 = 1, x2 = 2; const double dt = 1.0; PoseRTV origin, - pose1(Point3(0.5, 0.0, 0.0), Rot3::identity(), Point3(1.0, 0.0, 0.0)), + pose1(Point3(0.5, 0.0, 0.0), Rot3::identity(), Velocity3(1.0, 0.0, 0.0)), pose1a(Point3(0.5, 0.0, 0.0)), - pose2(Point3(1.5, 0.0, 0.0), Rot3::identity(), Point3(1.0, 0.0, 0.0)); + pose2(Point3(1.5, 0.0, 0.0), Rot3::identity(), Velocity3(1.0, 0.0, 0.0)); /* ************************************************************************* */ TEST( testVelocityConstraint, trapezoidal ) { diff --git a/gtsam_unstable/gtsam_unstable.h b/gtsam_unstable/gtsam_unstable.h index bbfcaa418..99b33182f 100644 --- a/gtsam_unstable/gtsam_unstable.h +++ b/gtsam_unstable/gtsam_unstable.h @@ -53,9 +53,9 @@ class Dummy { class PoseRTV { PoseRTV(); PoseRTV(Vector rtv); - PoseRTV(const gtsam::Point3& pt, const gtsam::Rot3& rot, const gtsam::Point3& vel); - PoseRTV(const gtsam::Rot3& rot, const gtsam::Point3& pt, const gtsam::Point3& vel); - PoseRTV(const gtsam::Pose3& pose, const gtsam::Point3& vel); + PoseRTV(const gtsam::Point3& pt, const gtsam::Rot3& rot, const Vector& vel); + PoseRTV(const gtsam::Rot3& rot, const gtsam::Point3& pt, const Vector& vel); + PoseRTV(const gtsam::Pose3& pose, const Vector& vel); PoseRTV(const gtsam::Pose3& pose); PoseRTV(double roll, double pitch, double yaw, double x, double y, double z, double vx, double vy, double vz); @@ -66,7 +66,7 @@ class PoseRTV { // access gtsam::Point3 translation() const; gtsam::Rot3 rotation() const; - gtsam::Point3 velocity() const; + Vector velocity() const; gtsam::Pose3 pose() const; // Vector interfaces From 2c765c735abb76038c5b6b383bf5df0e254276a7 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Fri, 17 Jul 2015 22:09:49 -0700 Subject: [PATCH 026/142] Velocity3 default constructor does not zero --- gtsam_unstable/dynamics/PoseRTV.h | 4 ++-- gtsam_unstable/dynamics/tests/testPoseRTV.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gtsam_unstable/dynamics/PoseRTV.h b/gtsam_unstable/dynamics/PoseRTV.h index e89d570b7..b1603efe2 100644 --- a/gtsam_unstable/dynamics/PoseRTV.h +++ b/gtsam_unstable/dynamics/PoseRTV.h @@ -34,11 +34,11 @@ public: PoseRTV(const Rot3& rot, const Point3& t, const Velocity3& vel) : Base(Pose3(rot, t), vel) {} explicit PoseRTV(const Point3& t) - : Base(Pose3(Rot3(), t),Velocity3()) {} + : Base(Pose3(Rot3(), t),Vector3::Zero()) {} PoseRTV(const Pose3& pose, const Velocity3& vel) : Base(pose, vel) {} explicit PoseRTV(const Pose3& pose) - : Base(pose,Velocity3()) {} + : Base(pose,Vector3::Zero()) {} // Construct from Base PoseRTV(const Base& base) diff --git a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp index ff3508f45..36f097a37 100644 --- a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp +++ b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp @@ -32,7 +32,7 @@ TEST( testPoseRTV, constructors ) { PoseRTV state2; EXPECT(assert_equal(Point3(), state2.t(), tol)); EXPECT(assert_equal(Rot3(), state2.R(), tol)); - EXPECT(assert_equal(Velocity3(), state2.v(), tol)); + EXPECT(assert_equal(zero(3), state2.v(), tol)); EXPECT(assert_equal(Pose3(), state2.pose(), tol)); PoseRTV state3(Pose3(rot, pt), vel); @@ -44,7 +44,7 @@ TEST( testPoseRTV, constructors ) { PoseRTV state4(Pose3(rot, pt)); EXPECT(assert_equal(pt, state4.t(), tol)); EXPECT(assert_equal(rot, state4.R(), tol)); - EXPECT(assert_equal(Velocity3(), state4.v(), tol)); + EXPECT(assert_equal(zero(3), state4.v(), tol)); EXPECT(assert_equal(Pose3(rot, pt), state4.pose(), tol)); Vector vec_init = (Vector(9) << 0.1, 0.2, 0.3, 1.0, 2.0, 3.0, 0.4, 0.5, 0.6).finished(); From 45e99f05b670e0e3e00073f1f85ad4f3698ddc8c Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 18 Jul 2015 18:28:39 -0700 Subject: [PATCH 027/142] Fixed test --- gtsam/base/Testable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtsam/base/Testable.h b/gtsam/base/Testable.h index 4790530ab..92ccb9156 100644 --- a/gtsam/base/Testable.h +++ b/gtsam/base/Testable.h @@ -123,8 +123,8 @@ namespace gtsam { double tol_; equals_star(double tol = 1e-9) : tol_(tol) {} bool operator()(const boost::shared_ptr& expected, const boost::shared_ptr& actual) { - if (!actual || !expected) return false; - return (traits::Equals(*actual,*expected, tol_)); + if (!actual || !expected) return true; + return actual && expected && traits::Equals(*actual,*expected, tol_); } }; From 0df1e345a3b27cd29f94ced33f6f4c6d80c64811 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Sat, 18 Jul 2015 18:30:42 -0700 Subject: [PATCH 028/142] Complete refactor with a shared parameter to fixed parameters. Tests still use old-style and all pass, because of hacky backwards compatible functions. --- gtsam/navigation/AHRSFactor.cpp | 117 ++++---- gtsam/navigation/AHRSFactor.h | 175 ++++++------ gtsam/navigation/CombinedImuFactor.cpp | 153 ++++++----- gtsam/navigation/CombinedImuFactor.h | 257 +++++++++--------- gtsam/navigation/ImuFactor.cpp | 134 +++++---- gtsam/navigation/ImuFactor.h | 195 +++++++------ gtsam/navigation/ImuFactorBase.h | 94 ------- gtsam/navigation/PreintegratedRotation.h | 85 +++--- gtsam/navigation/PreintegrationBase.cpp | 176 +++++------- gtsam/navigation/PreintegrationBase.h | 171 ++++++------ gtsam/navigation/tests/testAHRSFactor.cpp | 2 +- .../tests/testCombinedImuFactor.cpp | 15 +- gtsam/navigation/tests/testImuFactor.cpp | 32 +-- 13 files changed, 762 insertions(+), 844 deletions(-) delete mode 100644 gtsam/navigation/ImuFactorBase.h diff --git a/gtsam/navigation/AHRSFactor.cpp b/gtsam/navigation/AHRSFactor.cpp index 917b80c9a..92ec0dd9b 100644 --- a/gtsam/navigation/AHRSFactor.cpp +++ b/gtsam/navigation/AHRSFactor.cpp @@ -27,52 +27,36 @@ namespace gtsam { //------------------------------------------------------------------------------ // Inner class PreintegratedMeasurements //------------------------------------------------------------------------------ -AHRSFactor::PreintegratedMeasurements::PreintegratedMeasurements( - const Vector3& bias, const Matrix3& measuredOmegaCovariance) : - PreintegratedRotation(measuredOmegaCovariance), biasHat_(bias) -{ - preintMeasCov_.setZero(); -} - -//------------------------------------------------------------------------------ -AHRSFactor::PreintegratedMeasurements::PreintegratedMeasurements() : - PreintegratedRotation(I_3x3), biasHat_(Vector3()) -{ - preintMeasCov_.setZero(); -} - -//------------------------------------------------------------------------------ -void AHRSFactor::PreintegratedMeasurements::print(const string& s) const { +void PreintegratedAhrsMeasurements::print(const string& s) const { PreintegratedRotation::print(s); cout << "biasHat [" << biasHat_.transpose() << "]" << endl; cout << " PreintMeasCov [ " << preintMeasCov_ << " ]" << endl; } //------------------------------------------------------------------------------ -bool AHRSFactor::PreintegratedMeasurements::equals( - const PreintegratedMeasurements& other, double tol) const { - return PreintegratedRotation::equals(other, tol) - && equal_with_abs_tol(biasHat_, other.biasHat_, tol); +bool PreintegratedAhrsMeasurements::equals( + const PreintegratedAhrsMeasurements& other, double tol) const { + return PreintegratedRotation::equals(other, tol) && + equal_with_abs_tol(biasHat_, other.biasHat_, tol); } //------------------------------------------------------------------------------ -void AHRSFactor::PreintegratedMeasurements::resetIntegration() { +void PreintegratedAhrsMeasurements::resetIntegration() { PreintegratedRotation::resetIntegration(); preintMeasCov_.setZero(); } //------------------------------------------------------------------------------ -void AHRSFactor::PreintegratedMeasurements::integrateMeasurement( - const Vector3& measuredOmega, double deltaT, - boost::optional body_P_sensor) { +void PreintegratedAhrsMeasurements::integrateMeasurement( + const Vector3& measuredOmega, double deltaT) { // First we compensate the measurements for the bias Vector3 correctedOmega = measuredOmega - biasHat_; // Then compensate for sensor-body displacement: we express the quantities // (originally in the IMU frame) into the body frame - if (body_P_sensor) { - Matrix3 body_R_sensor = body_P_sensor->rotation().matrix(); + if (p().body_P_sensor) { + Matrix3 body_R_sensor = p().body_P_sensor->rotation().matrix(); // rotation rate vector in the body frame correctedOmega = body_R_sensor * correctedOmega; } @@ -92,18 +76,17 @@ void AHRSFactor::PreintegratedMeasurements::integrateMeasurement( // first order uncertainty propagation // the deltaT allows to pass from continuous time noise to discrete time noise - preintMeasCov_ = Fr * preintMeasCov_ * Fr.transpose() - + gyroscopeCovariance() * deltaT; + preintMeasCov_ = Fr * preintMeasCov_ * Fr.transpose() + p().gyroscopeCovariance * deltaT; } //------------------------------------------------------------------------------ -Vector3 AHRSFactor::PreintegratedMeasurements::predict(const Vector3& bias, - boost::optional H) const { +Vector3 PreintegratedAhrsMeasurements::predict(const Vector3& bias, + OptionalJacobian<3,3> H) const { const Vector3 biasOmegaIncr = bias - biasHat_; return biascorrectedThetaRij(biasOmegaIncr, H); } //------------------------------------------------------------------------------ -Vector AHRSFactor::PreintegratedMeasurements::DeltaAngles( +Vector PreintegratedAhrsMeasurements::DeltaAngles( const Vector& msr_gyro_t, const double msr_dt, const Vector3& delta_angles) { @@ -121,22 +104,16 @@ Vector AHRSFactor::PreintegratedMeasurements::DeltaAngles( //------------------------------------------------------------------------------ // AHRSFactor methods //------------------------------------------------------------------------------ -AHRSFactor::AHRSFactor() : - _PIM_(Vector3(), Z_3x3) { -} +AHRSFactor::AHRSFactor( + Key rot_i, Key rot_j, Key bias, + const PreintegratedAhrsMeasurements& preintegratedMeasurements) + : Base(noiseModel::Gaussian::Covariance( + preintegratedMeasurements.preintMeasCov_), + rot_i, rot_j, bias), + _PIM_(preintegratedMeasurements) {} -AHRSFactor::AHRSFactor(Key rot_i, Key rot_j, Key bias, - const PreintegratedMeasurements& preintegratedMeasurements, - const Vector3& omegaCoriolis, boost::optional body_P_sensor) : - Base( - noiseModel::Gaussian::Covariance( - preintegratedMeasurements.preintMeasCov_), rot_i, rot_j, bias), _PIM_( - preintegratedMeasurements), omegaCoriolis_(omegaCoriolis), body_P_sensor_( - body_P_sensor) { -} - -//------------------------------------------------------------------------------ gtsam::NonlinearFactor::shared_ptr AHRSFactor::clone() const { +//------------------------------------------------------------------------------ return boost::static_pointer_cast( gtsam::NonlinearFactor::shared_ptr(new This(*this))); } @@ -147,20 +124,13 @@ void AHRSFactor::print(const string& s, cout << s << "AHRSFactor(" << keyFormatter(this->key1()) << "," << keyFormatter(this->key2()) << "," << keyFormatter(this->key3()) << ","; _PIM_.print(" preintegrated measurements:"); - cout << " omegaCoriolis: [ " << omegaCoriolis_.transpose() << " ]" << endl; noiseModel_->print(" noise model: "); - if (body_P_sensor_) - body_P_sensor_->print(" sensor pose in body frame: "); } //------------------------------------------------------------------------------ bool AHRSFactor::equals(const NonlinearFactor& other, double tol) const { const This *e = dynamic_cast(&other); - return e != NULL && Base::equals(*e, tol) && _PIM_.equals(e->_PIM_, tol) - && equal_with_abs_tol(omegaCoriolis_, e->omegaCoriolis_, tol) - && ((!body_P_sensor_ && !e->body_P_sensor_) - || (body_P_sensor_ && e->body_P_sensor_ - && body_P_sensor_->equals(*e->body_P_sensor_))); + return e != NULL && Base::equals(*e, tol) && _PIM_.equals(e->_PIM_, tol); } //------------------------------------------------------------------------------ @@ -172,7 +142,7 @@ Vector AHRSFactor::evaluateError(const Rot3& Ri, const Rot3& Rj, const Vector3 biascorrectedOmega = _PIM_.predict(bias, H3); // Coriolis term - const Vector3 coriolis = _PIM_.integrateCoriolis(Ri, omegaCoriolis_); + const Vector3 coriolis = _PIM_.integrateCoriolis(Ri); const Matrix3 coriolisHat = skewSymmetric(coriolis); const Vector3 correctedOmega = biascorrectedOmega - coriolis; @@ -215,15 +185,13 @@ Vector AHRSFactor::evaluateError(const Rot3& Ri, const Rot3& Rj, } //------------------------------------------------------------------------------ -Rot3 AHRSFactor::predict(const Rot3& rot_i, const Vector3& bias, - const PreintegratedMeasurements preintegratedMeasurements, - const Vector3& omegaCoriolis, boost::optional body_P_sensor) { - +Rot3 AHRSFactor::Predict( + const Rot3& rot_i, const Vector3& bias, + const PreintegratedAhrsMeasurements preintegratedMeasurements) { const Vector3 biascorrectedOmega = preintegratedMeasurements.predict(bias); // Coriolis term - const Vector3 coriolis = // - preintegratedMeasurements.integrateCoriolis(rot_i, omegaCoriolis); + const Vector3 coriolis = preintegratedMeasurements.integrateCoriolis(rot_i); const Vector3 correctedOmega = biascorrectedOmega - coriolis; const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega); @@ -231,4 +199,31 @@ Rot3 AHRSFactor::predict(const Rot3& rot_i, const Vector3& bias, return rot_i.compose(correctedDeltaRij); } -} //namespace gtsam +//------------------------------------------------------------------------------ +AHRSFactor::AHRSFactor(Key rot_i, Key rot_j, Key bias, + const PreintegratedMeasurements& pim, + const Vector3& omegaCoriolis, + const boost::optional& body_P_sensor) + : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), rot_i, rot_j, bias), + _PIM_(pim) { + boost::shared_ptr p = + boost::make_shared(pim.p()); + p->body_P_sensor = body_P_sensor; + _PIM_.p_ = p; +} + +//------------------------------------------------------------------------------ +Rot3 AHRSFactor::predict(const Rot3& rot_i, const Vector3& bias, + const PreintegratedMeasurements pim, + const Vector3& omegaCoriolis, + const boost::optional& body_P_sensor) { + boost::shared_ptr p = + boost::make_shared(pim.p()); + p->omegaCoriolis = omegaCoriolis; + p->body_P_sensor = body_P_sensor; + PreintegratedMeasurements newPim = pim; + newPim.p_ = p; + return Predict(rot_i, bias, newPim); +} + +} // namespace gtsam diff --git a/gtsam/navigation/AHRSFactor.h b/gtsam/navigation/AHRSFactor.h index fbf7d51a1..f9f846790 100644 --- a/gtsam/navigation/AHRSFactor.h +++ b/gtsam/navigation/AHRSFactor.h @@ -26,91 +26,92 @@ namespace gtsam { -class GTSAM_EXPORT AHRSFactor: public NoiseModelFactor3 { -public: +/** + * PreintegratedAHRSMeasurements accumulates (integrates) the Gyroscope + * measurements (rotation rates) and the corresponding covariance matrix. + * Can be built incrementally so as to avoid costly integration at time of factor construction. + */ +class GTSAM_EXPORT PreintegratedAhrsMeasurements : public PreintegratedRotation { + + protected: + + Vector3 biasHat_; ///< Angular rate bias values used during preintegration. + Matrix3 preintMeasCov_; ///< Covariance matrix of the preintegrated measurements (first-order propagation from *measurementCovariance*) + + /// Default constructor, only for serialization + PreintegratedAhrsMeasurements() {} + + friend class AHRSFactor; + + public: /** - * CombinedPreintegratedMeasurements accumulates (integrates) the Gyroscope - * measurements (rotation rates) and the corresponding covariance matrix. - * The measurements are then used to build the Preintegrated AHRS factor. - * Can be built incrementally so as to avoid costly integration at time of - * factor construction. + * Default constructor, initialize with no measurements + * @param bias Current estimate of acceleration and rotation rate biases */ - class GTSAM_EXPORT PreintegratedMeasurements : public PreintegratedRotation { + PreintegratedAhrsMeasurements(const boost::shared_ptr& p, const Vector3& biasHat) + : PreintegratedRotation(p), biasHat_(biasHat) { + resetIntegration(); + } - friend class AHRSFactor; + const Params& p() const { return *boost::static_pointer_cast(p_);} + const Vector3& biasHat() const { return biasHat_; } + const Matrix3& preintMeasCov() const { return preintMeasCov_; } - protected: - Vector3 biasHat_; ///< Acceleration and angular rate bias values used during preintegration. Note that we won't be using the accelerometer - Matrix3 preintMeasCov_; ///< Covariance matrix of the preintegrated measurements (first-order propagation from *measurementCovariance*) + /// print + void print(const std::string& s = "Preintegrated Measurements: ") const; - public: + /// equals + bool equals(const PreintegratedAhrsMeasurements&, double tol = 1e-9) const; - /// Default constructor - PreintegratedMeasurements(); + /// Reset inetgrated quantities to zero + void resetIntegration(); - /** - * Default constructor, initialize with no measurements - * @param bias Current estimate of acceleration and rotation rate biases - * @param measuredOmegaCovariance Covariance matrix of measured angular rate - */ - PreintegratedMeasurements(const Vector3& bias, - const Matrix3& measuredOmegaCovariance); + /** + * Add a single Gyroscope measurement to the preintegration. + * @param measureOmedga Measured angular velocity (in body frame) + * @param deltaT Time step + */ + void integrateMeasurement(const Vector3& measuredOmega, double deltaT); - Vector3 biasHat() const { - return biasHat_; - } - const Matrix3& preintMeasCov() const { - return preintMeasCov_; - } + /// Predict bias-corrected incremental rotation + /// TODO: The matrix Hbias is the derivative of predict? Unit-test? + Vector3 predict(const Vector3& bias, OptionalJacobian<3,3> H = boost::none) const; - /// print - void print(const std::string& s = "Preintegrated Measurements: ") const; + // This function is only used for test purposes + // (compare numerical derivatives wrt analytic ones) + static Vector DeltaAngles(const Vector& msr_gyro_t, const double msr_dt, + const Vector3& delta_angles); - /// equals - bool equals(const PreintegratedMeasurements&, double tol = 1e-9) const; - - /// TODO: Document - void resetIntegration(); - - /** - * Add a single Gyroscope measurement to the preintegration. - * @param measureOmedga Measured angular velocity (in body frame) - * @param deltaT Time step - * @param body_P_sensor Optional sensor frame - */ - void integrateMeasurement(const Vector3& measuredOmega, double deltaT, - boost::optional body_P_sensor = boost::none); - - /// Predict bias-corrected incremental rotation - /// TODO: The matrix Hbias is the derivative of predict? Unit-test? - Vector3 predict(const Vector3& bias, boost::optional H = - boost::none) const; - - // This function is only used for test purposes - // (compare numerical derivatives wrt analytic ones) - static Vector DeltaAngles(const Vector& msr_gyro_t, const double msr_dt, - const Vector3& delta_angles); - - private: - - /** Serialization function */ - friend class boost::serialization::access; - template - void serialize(ARCHIVE & ar, const unsigned int /*version*/) { - ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation); - ar & BOOST_SERIALIZATION_NVP(biasHat_); - } - }; + /// @deprecated constructor + PreintegratedAhrsMeasurements(const Vector3& biasHat, + const Matrix3& measuredOmegaCovariance) + : PreintegratedRotation(boost::make_shared()), + biasHat_(biasHat) { + resetIntegration(); + } private: + + /** Serialization function */ + friend class boost::serialization::access; + template + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { + ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation); + ar & BOOST_SERIALIZATION_NVP(p_); + ar & BOOST_SERIALIZATION_NVP(biasHat_); + } +}; + +class GTSAM_EXPORT AHRSFactor: public NoiseModelFactor3 { + typedef AHRSFactor This; typedef NoiseModelFactor3 Base; - PreintegratedMeasurements _PIM_; - Vector3 gravity_; - Vector3 omegaCoriolis_; ///< Controls whether higher order terms are included when calculating the Coriolis Effect - boost::optional body_P_sensor_; ///< The pose of the sensor in the body frame + PreintegratedAhrsMeasurements _PIM_; + + /** Default constructor - only use for serialization */ + AHRSFactor() {} public: @@ -121,22 +122,15 @@ public: typedef boost::shared_ptr shared_ptr; #endif - /** Default constructor - only use for serialization */ - AHRSFactor(); - /** * Constructor * @param rot_i previous rot key * @param rot_j current rot key * @param bias previous bias key * @param preintegratedMeasurements preintegrated measurements - * @param omegaCoriolis rotation rate of the inertial frame - * @param body_P_sensor Optional pose of the sensor frame in the body frame */ AHRSFactor(Key rot_i, Key rot_j, Key bias, - const PreintegratedMeasurements& preintegratedMeasurements, - const Vector3& omegaCoriolis, - boost::optional body_P_sensor = boost::none); + const PreintegratedAhrsMeasurements& preintegratedMeasurements); virtual ~AHRSFactor() { } @@ -152,14 +146,10 @@ public: virtual bool equals(const NonlinearFactor&, double tol = 1e-9) const; /// Access the preintegrated measurements. - const PreintegratedMeasurements& preintegratedMeasurements() const { + const PreintegratedAhrsMeasurements& preintegratedMeasurements() const { return _PIM_; } - const Vector3& omegaCoriolis() const { - return omegaCoriolis_; - } - /** implement functions needed to derive from Factor */ /// vector of errors @@ -169,10 +159,25 @@ public: boost::none) const; /// predicted states from IMU + /// TODO(frank): relationship with PIM predict ?? + static Rot3 Predict( + const Rot3& rot_i, const Vector3& bias, + const PreintegratedAhrsMeasurements preintegratedMeasurements); + + /// @deprecated name + typedef PreintegratedAhrsMeasurements PreintegratedMeasurements; + + /// @deprecated constructor + AHRSFactor(Key rot_i, Key rot_j, Key bias, + const PreintegratedMeasurements& preintegratedMeasurements, + const Vector3& omegaCoriolis, + const boost::optional& body_P_sensor = boost::none); + + /// @deprecated static function static Rot3 predict(const Rot3& rot_i, const Vector3& bias, const PreintegratedMeasurements preintegratedMeasurements, const Vector3& omegaCoriolis, - boost::optional body_P_sensor = boost::none); + const boost::optional& body_P_sensor = boost::none); private: @@ -184,13 +189,9 @@ private: & boost::serialization::make_nvp("NoiseModelFactor3", boost::serialization::base_object(*this)); ar & BOOST_SERIALIZATION_NVP(_PIM_); - ar & BOOST_SERIALIZATION_NVP(omegaCoriolis_); - ar & BOOST_SERIALIZATION_NVP(body_P_sensor_); } }; // AHRSFactor -typedef AHRSFactor::PreintegratedMeasurements AHRSFactorPreintegratedMeasurements; - } //namespace gtsam diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 1db2f1861..c1ec7f361 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -29,53 +29,30 @@ namespace gtsam { using namespace std; //------------------------------------------------------------------------------ -// Inner class CombinedPreintegratedMeasurements +// Inner class PreintegratedCombinedMeasurements //------------------------------------------------------------------------------ -CombinedImuFactor::CombinedPreintegratedMeasurements::CombinedPreintegratedMeasurements( - const imuBias::ConstantBias& bias, const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, const Matrix3& biasAccCovariance, - const Matrix3& biasOmegaCovariance, const Matrix& biasAccOmegaInit, - const bool use2ndOrderIntegration) : - PreintegrationBase(bias, measuredAccCovariance, measuredOmegaCovariance, - integrationErrorCovariance, use2ndOrderIntegration), biasAccCovariance_( - biasAccCovariance), biasOmegaCovariance_(biasOmegaCovariance), biasAccOmegaInit_( - biasAccOmegaInit) { - preintMeasCov_.setZero(); -} - -//------------------------------------------------------------------------------ -void CombinedImuFactor::CombinedPreintegratedMeasurements::print( +void PreintegratedCombinedMeasurements::print( const string& s) const { PreintegrationBase::print(s); - cout << " biasAccCovariance [ " << biasAccCovariance_ << " ]" << endl; - cout << " biasOmegaCovariance [ " << biasOmegaCovariance_ << " ]" << endl; - cout << " biasAccOmegaInit [ " << biasAccOmegaInit_ << " ]" << endl; cout << " preintMeasCov [ " << preintMeasCov_ << " ]" << endl; } //------------------------------------------------------------------------------ -bool CombinedImuFactor::CombinedPreintegratedMeasurements::equals( - const CombinedPreintegratedMeasurements& expected, double tol) const { - return equal_with_abs_tol(biasAccCovariance_, expected.biasAccCovariance_, - tol) - && equal_with_abs_tol(biasOmegaCovariance_, expected.biasOmegaCovariance_, - tol) - && equal_with_abs_tol(biasAccOmegaInit_, expected.biasAccOmegaInit_, tol) - && equal_with_abs_tol(preintMeasCov_, expected.preintMeasCov_, tol) - && PreintegrationBase::equals(expected, tol); +bool PreintegratedCombinedMeasurements::equals( + const PreintegratedCombinedMeasurements& other, double tol) const { + return PreintegrationBase::equals(other, tol) && + equal_with_abs_tol(preintMeasCov_, other.preintMeasCov_, tol); } //------------------------------------------------------------------------------ -void CombinedImuFactor::CombinedPreintegratedMeasurements::resetIntegration() { +void PreintegratedCombinedMeasurements::resetIntegration() { PreintegrationBase::resetIntegration(); preintMeasCov_.setZero(); } //------------------------------------------------------------------------------ -void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( +void PreintegratedCombinedMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - boost::optional body_P_sensor, boost::optional F_test, boost::optional G_test) { // NOTE: order is important here because each update uses old values, e.g., velocity and position updates are based on previous rotation estimate. @@ -83,7 +60,7 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( Vector3 correctedAcc, correctedOmega; correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, - correctedAcc, correctedOmega, body_P_sensor); + &correctedAcc, &correctedOmega); const Vector3 integratedOmega = correctedOmega * deltaT; // rotation vector describing rotation increment computed from the current rotation rate measurement Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr @@ -91,20 +68,19 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( // Update Jacobians /* ----------------------------------------------------------------------------------------------------------------------- */ - updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, - deltaT); + updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT); // Update preintegrated measurements covariance: as in [2] we consider a first order propagation that // can be seen as a prediction phase in an EKF framework. In this implementation, contrarily to [2] we // consider the uncertainty of the bias selection and we keep correlation between biases and preintegrated measurements /* ----------------------------------------------------------------------------------------------------------------------- */ - const Matrix3 R_i = deltaRij(); // store this + const Matrix3 dRij = deltaRij().matrix(); // expensive when quaternion // Update preintegrated measurements. TODO Frank moved from end of this function !!! Matrix9 F_9x9; updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F_9x9); // Single Jacobians to propagate covariance - Matrix3 H_vel_biasacc = -R_i * deltaT; + Matrix3 H_vel_biasacc = -dRij * deltaT; Matrix3 H_angles_biasomega = -D_Rincr_integratedOmega * deltaT; // overall Jacobian wrt preintegrated measurements (df/dx) @@ -127,18 +103,18 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( G_measCov_Gt.setZero(15, 15); // BLOCK DIAGONAL TERMS - G_measCov_Gt.block<3, 3>(0, 0) = deltaT * integrationCovariance(); + G_measCov_Gt.block<3, 3>(0, 0) = deltaT * p().integrationCovariance; G_measCov_Gt.block<3, 3>(3, 3) = (1 / deltaT) * (H_vel_biasacc) - * (accelerometerCovariance() + biasAccOmegaInit_.block<3, 3>(0, 0)) + * (p().accelerometerCovariance + p().biasAccOmegaInit.block<3, 3>(0, 0)) * (H_vel_biasacc.transpose()); G_measCov_Gt.block<3, 3>(6, 6) = (1 / deltaT) * (H_angles_biasomega) - * (gyroscopeCovariance() + biasAccOmegaInit_.block<3, 3>(3, 3)) + * (p().gyroscopeCovariance + p().biasAccOmegaInit.block<3, 3>(3, 3)) * (H_angles_biasomega.transpose()); - G_measCov_Gt.block<3, 3>(9, 9) = (1 / deltaT) * biasAccCovariance_; - G_measCov_Gt.block<3, 3>(12, 12) = (1 / deltaT) * biasOmegaCovariance_; + G_measCov_Gt.block<3, 3>(9, 9) = (1 / deltaT) * p().biasAccCovariance; + G_measCov_Gt.block<3, 3>(12, 12) = (1 / deltaT) * p().biasOmegaCovariance; // OFF BLOCK DIAGONAL TERMS - Matrix3 block23 = H_vel_biasacc * biasAccOmegaInit_.block<3, 3>(3, 0) + Matrix3 block23 = H_vel_biasacc * p().biasAccOmegaInit.block<3, 3>(3, 0) * H_angles_biasomega.transpose(); G_measCov_Gt.block<3, 3>(3, 6) = block23; G_measCov_Gt.block<3, 3>(6, 3) = block23.transpose(); @@ -162,26 +138,35 @@ void CombinedImuFactor::CombinedPreintegratedMeasurements::integrateMeasurement( } } +//------------------------------------------------------------------------------ +PreintegratedCombinedMeasurements::PreintegratedCombinedMeasurements( + const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, + const Matrix3& measuredOmegaCovariance, + const Matrix3& integrationErrorCovariance, const Matrix3& biasAccCovariance, + const Matrix3& biasOmegaCovariance, const Matrix6& biasAccOmegaInit, + const bool use2ndOrderIntegration) { + biasHat_ = biasHat; + boost::shared_ptr p = boost::make_shared(); + p->gyroscopeCovariance = measuredOmegaCovariance; + p->accelerometerCovariance = measuredAccCovariance; + p->integrationCovariance = integrationErrorCovariance; + p->biasAccCovariance = biasAccCovariance; + p->biasOmegaCovariance = biasOmegaCovariance; + p->biasAccOmegaInit = biasAccOmegaInit; + p->use2ndOrderIntegration = use2ndOrderIntegration; + p_ = p; + resetIntegration(); + preintMeasCov_.setZero(); +} //------------------------------------------------------------------------------ // CombinedImuFactor methods //------------------------------------------------------------------------------ -CombinedImuFactor::CombinedImuFactor() : - ImuFactorBase(), _PIM_(imuBias::ConstantBias(), Z_3x3, Z_3x3, Z_3x3, Z_3x3, - Z_3x3, Z_6x6) { -} - -//------------------------------------------------------------------------------ -CombinedImuFactor::CombinedImuFactor(Key pose_i, Key vel_i, Key pose_j, - Key vel_j, Key bias_i, Key bias_j, - const CombinedPreintegratedMeasurements& preintegratedMeasurements, - const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor, const bool use2ndOrderCoriolis) : - Base( - noiseModel::Gaussian::Covariance( - preintegratedMeasurements.preintMeasCov_), pose_i, vel_i, pose_j, - vel_j, bias_i, bias_j), ImuFactorBase(gravity, omegaCoriolis, - body_P_sensor, use2ndOrderCoriolis), _PIM_(preintegratedMeasurements) { -} +CombinedImuFactor::CombinedImuFactor( + Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, + const PreintegratedCombinedMeasurements& pim) + : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, + pose_j, vel_j, bias_i, bias_j), + _PIM_(pim) {} //------------------------------------------------------------------------------ gtsam::NonlinearFactor::shared_ptr CombinedImuFactor::clone() const { @@ -196,17 +181,14 @@ void CombinedImuFactor::print(const string& s, << keyFormatter(this->key2()) << "," << keyFormatter(this->key3()) << "," << keyFormatter(this->key4()) << "," << keyFormatter(this->key5()) << "," << keyFormatter(this->key6()) << ")\n"; - ImuFactorBase::print(""); _PIM_.print(" preintegrated measurements:"); this->noiseModel_->print(" noise model: "); } //------------------------------------------------------------------------------ -bool CombinedImuFactor::equals(const NonlinearFactor& expected, - double tol) const { - const This *e = dynamic_cast(&expected); - return e != NULL && Base::equals(*e, tol) && _PIM_.equals(e->_PIM_, tol) - && ImuFactorBase::equals(*e, tol); +bool CombinedImuFactor::equals(const NonlinearFactor& other, double tol) const { + const This* e = dynamic_cast(&other); + return e != NULL && Base::equals(*e, tol) && _PIM_.equals(e->_PIM_, tol); } //------------------------------------------------------------------------------ @@ -226,8 +208,7 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, Matrix93 D_r_vel_i, D_r_vel_j; // error wrt preintegrated measurements - Vector9 r_pvR = _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, - bias_i, gravity_, omegaCoriolis_, use2ndOrderCoriolis_, + Vector9 r_pvR = _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, H1 ? &D_r_pose_i : 0, H2 ? &D_r_vel_i : 0, H3 ? &D_r_pose_j : 0, H4 ? &D_r_vel_j : 0, H5 ? &D_r_bias_i : 0); @@ -275,4 +256,42 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, return r; } +//------------------------------------------------------------------------------ +CombinedImuFactor::CombinedImuFactor( + Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, + const CombinedPreintegratedMeasurements& pim, const Vector3& gravity, + const Vector3& omegaCoriolis, const boost::optional& body_P_sensor, + const bool use2ndOrderCoriolis) + : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, + pose_j, vel_j, bias_i, bias_j), + _PIM_(pim) { + boost::shared_ptr p = + boost::make_shared(pim.p()); + p->gravity = gravity; + p->omegaCoriolis = omegaCoriolis; + p->body_P_sensor = body_P_sensor; + p->use2ndOrderCoriolis = use2ndOrderCoriolis; + _PIM_.p_ = p; +} +//------------------------------------------------------------------------------ +void CombinedImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, + Pose3& pose_j, Vector3& vel_j, + const imuBias::ConstantBias& bias_i, + const CombinedPreintegratedMeasurements& pim, + const Vector3& gravity, + const Vector3& omegaCoriolis, + const bool use2ndOrderCoriolis) { + // NOTE(frank): hack below only for backward compatibility + boost::shared_ptr p = + boost::make_shared(pim.p()); + p->gravity = gravity; + p->omegaCoriolis = omegaCoriolis; + p->use2ndOrderCoriolis = use2ndOrderCoriolis; + CombinedPreintegratedMeasurements newPim = pim; + newPim.p_ = p; + PoseVelocityBias pvb = newPim.predict(pose_i, vel_i, bias_i); + pose_j = pvb.pose; + vel_j = pvb.velocity; +} + } /// namespace gtsam diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 7fdafd8ce..a70d1d24f 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -24,13 +24,113 @@ /* GTSAM includes */ #include #include -#include -#include +#include namespace gtsam { /** - * + * PreintegratedCombinedMeasurements integrates the IMU measurements + * (rotation rates and accelerations) and the corresponding covariance matrix. + * The measurements are then used to build the CombinedImuFactor. Integration + * is done incrementally (ideally, one integrates the measurement as soon as + * it is received from the IMU) so as to avoid costly integration at time of + * factor construction. + */ +class PreintegratedCombinedMeasurements : public PreintegrationBase { + + /// Parameters for pre-integration: + /// Usage: Create just a single Params and pass a shared pointer to the constructor + struct Params : PreintegrationBase::Params { + Matrix3 biasAccCovariance; ///< continuous-time "Covariance" describing accelerometer bias random walk + Matrix3 biasOmegaCovariance; ///< continuous-time "Covariance" describing gyroscope bias random walk + Matrix6 biasAccOmegaInit; ///< covariance of bias used for pre-integration + + Params():biasAccCovariance(I_3x3), biasOmegaCovariance(I_3x3), biasAccOmegaInit(I_6x6) {} + + private: + /** Serialization function */ + friend class boost::serialization::access; + template + void serialize(ARCHIVE& ar, const unsigned int /*version*/) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation::Params); + ar& BOOST_SERIALIZATION_NVP(biasAccCovariance); + ar& BOOST_SERIALIZATION_NVP(biasOmegaCovariance); + ar& BOOST_SERIALIZATION_NVP(biasAccOmegaInit); + } + }; + + protected: + /* Covariance matrix of the preintegrated measurements + * COVARIANCE OF: [PreintPOSITION PreintVELOCITY PreintROTATION BiasAcc BiasOmega] + * (first-order propagation from *measurementCovariance*). + * PreintegratedCombinedMeasurements also include the biases and keep the correlation + * between the preintegrated measurements and the biases + */ + Eigen::Matrix preintMeasCov_; + + PreintegratedCombinedMeasurements() {} + + friend class CombinedImuFactor; + + public: + /** + * Default constructor, initializes the class with no measurements + * @param bias Current estimate of acceleration and rotation rate biases + */ + PreintegratedCombinedMeasurements(const boost::shared_ptr& p, + const imuBias::ConstantBias& biasHat) + : PreintegrationBase(p, biasHat) { + preintMeasCov_.setZero(); + } + + const Params& p() const { return *boost::static_pointer_cast(p_);} + + /// print + void print(const std::string& s = "Preintegrated Measurements:") const; + + /// equals + bool equals(const PreintegratedCombinedMeasurements& expected, + double tol = 1e-9) const; + + /// Re-initialize PreintegratedCombinedMeasurements + void resetIntegration(); + + /** + * Add a single IMU measurement to the preintegration. + * @param measuredAcc Measured acceleration (in body frame, as given by the + * sensor) + * @param measuredOmega Measured angular velocity (as given by the sensor) + * @param deltaT Time interval between two consecutive IMU measurements + * @param body_P_sensor Optional sensor frame (pose of the IMU in the body + * frame) + */ + void integrateMeasurement( + const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, + boost::optional F_test = boost::none, + boost::optional G_test = boost::none); + + /// methods to access class variables + Matrix preintMeasCov() const { return preintMeasCov_; } + + /// deprecated constructor + PreintegratedCombinedMeasurements(const imuBias::ConstantBias& biasHat, + const Matrix3& measuredAccCovariance, + const Matrix3& measuredOmegaCovariance, + const Matrix3& integrationErrorCovariance, + const Matrix3& biasAccCovariance, const Matrix3& biasOmegaCovariance, + const Matrix6& biasAccOmegaInit, const bool use2ndOrderIntegration = false); + + private: + /// Serialization function + friend class boost::serialization::access; + template + void serialize(ARCHIVE& ar, const unsigned int /*version*/) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegrationBase); + ar& BOOST_SERIALIZATION_NVP(preintMeasCov_); + } +}; + +/** * @addtogroup SLAM * * If you are using the factor, please cite: @@ -46,14 +146,12 @@ namespace gtsam { * TRO, 28(1):61-76, 2012. * [3] L. Carlone, S. Williams, R. Roberts, "Preintegrated IMU factor: * Computation of the Jacobian Matrices", Tech. Report, 2013. - */ - -/** + * * CombinedImuFactor is a 6-ways factor involving previous state (pose and * velocity of the vehicle, as well as bias at previous time step), and current * state (pose, velocity, bias at current time step). Following the pre- * integration scheme proposed in [2], the CombinedImuFactor includes many IMU - * measurements, which are "summarized" using the CombinedPreintegratedMeasurements + * measurements, which are "summarized" using the PreintegratedCombinedMeasurements * class. There are 3 main differences wrpt the ImuFactor class: * 1) The factor is 6-ways, meaning that it also involves both biases (previous * and current time step).Therefore, the factor internally imposes the biases @@ -61,105 +159,24 @@ namespace gtsam { * "biasOmegaCovariance" described the random walk that models bias evolution. * 2) The preintegration covariance takes into account the noise in the bias * estimate used for integration. - * 3) The covariance matrix of the CombinedPreintegratedMeasurements preserves + * 3) The covariance matrix of the PreintegratedCombinedMeasurements preserves * the correlation between the bias uncertainty and the preintegrated * measurements uncertainty. */ class CombinedImuFactor: public NoiseModelFactor6, public ImuFactorBase { + Vector3, imuBias::ConstantBias, imuBias::ConstantBias> { public: - /** - * CombinedPreintegratedMeasurements integrates the IMU measurements - * (rotation rates and accelerations) and the corresponding covariance matrix. - * The measurements are then used to build the CombinedImuFactor. Integration - * is done incrementally (ideally, one integrates the measurement as soon as - * it is received from the IMU) so as to avoid costly integration at time of - * factor construction. - */ - class CombinedPreintegratedMeasurements: public PreintegrationBase { - - friend class CombinedImuFactor; - - protected: - - Matrix3 biasAccCovariance_; ///< continuous-time "Covariance" describing accelerometer bias random walk - Matrix3 biasOmegaCovariance_; ///< continuous-time "Covariance" describing gyroscope bias random walk - Matrix6 biasAccOmegaInit_; ///< covariance of bias used for pre-integration - - Eigen::Matrix preintMeasCov_; ///< Covariance matrix of the preintegrated measurements - ///< COVARIANCE OF: [PreintPOSITION PreintVELOCITY PreintROTATION BiasAcc BiasOmega] - ///< (first-order propagation from *measurementCovariance*). CombinedPreintegratedMeasurements also include the biases and keep the correlation - ///< between the preintegrated measurements and the biases - - public: - - /** - * Default constructor, initializes the class with no measurements - * @param bias Current estimate of acceleration and rotation rate biases - * @param measuredAccCovariance Covariance matrix of measuredAcc - * @param measuredOmegaCovariance Covariance matrix of measured Angular Rate - * @param integrationErrorCovariance Covariance matrix of integration errors (velocity -> position) - * @param biasAccCovariance Covariance matrix of biasAcc (random walk describing BIAS evolution) - * @param biasOmegaCovariance Covariance matrix of biasOmega (random walk describing BIAS evolution) - * @param biasAccOmegaInit Covariance of biasAcc & biasOmega when preintegrating measurements - * @param use2ndOrderIntegration Controls the order of integration - * (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) - */ - CombinedPreintegratedMeasurements(const imuBias::ConstantBias& bias, - const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, - const Matrix3& biasAccCovariance, const Matrix3& biasOmegaCovariance, - const Matrix& biasAccOmegaInit, const bool use2ndOrderIntegration = - false); - - /// print - void print(const std::string& s = "Preintegrated Measurements:") const; - - /// equals - bool equals(const CombinedPreintegratedMeasurements& expected, double tol = - 1e-9) const; - - /// Re-initialize CombinedPreintegratedMeasurements - void resetIntegration(); - - /** - * Add a single IMU measurement to the preintegration. - * @param measuredAcc Measured acceleration (in body frame, as given by the sensor) - * @param measuredOmega Measured angular velocity (as given by the sensor) - * @param deltaT Time interval between two consecutive IMU measurements - * @param body_P_sensor Optional sensor frame (pose of the IMU in the body frame) - */ - void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double deltaT, - boost::optional body_P_sensor = boost::none, - boost::optional F_test = boost::none, - boost::optional G_test = boost::none); - - /// methods to access class variables - Matrix preintMeasCov() const { - return preintMeasCov_; - } - - private: - - /// Serialization function - friend class boost::serialization::access; - template - void serialize(ARCHIVE & ar, const unsigned int /*version*/) { - ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegrationBase); - ar & BOOST_SERIALIZATION_NVP(preintMeasCov_); - } - }; - private: typedef CombinedImuFactor This; typedef NoiseModelFactor6 Base; - CombinedPreintegratedMeasurements _PIM_; + PreintegratedCombinedMeasurements _PIM_; + + /** Default constructor - only use for serialization */ + CombinedImuFactor() {} public: @@ -170,9 +187,6 @@ public: typedef boost::shared_ptr shared_ptr; #endif - /** Default constructor - only use for serialization */ - CombinedImuFactor(); - /** * Constructor * @param pose_i Previous pose key @@ -181,21 +195,13 @@ public: * @param vel_j Current velocity key * @param bias_i Previous bias key * @param bias_j Current bias key - * @param CombinedPreintegratedMeasurements CombinedPreintegratedMeasurements IMU measurements - * @param gravity Gravity vector expressed in the global frame - * @param omegaCoriolis Rotation rate of the global frame w.r.t. an inertial frame - * @param body_P_sensor Optional pose of the sensor frame in the body frame - * @param use2ndOrderCoriolis When true, the second-order term is used in the calculation of the Coriolis Effect + * @param PreintegratedCombinedMeasurements Combined IMU measurements */ - CombinedImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, - Key bias_j, - const CombinedPreintegratedMeasurements& preintegratedMeasurements, - const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor = boost::none, - const bool use2ndOrderCoriolis = false); + CombinedImuFactor( + Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, + const PreintegratedCombinedMeasurements& preintegratedMeasurements); - virtual ~CombinedImuFactor() { - } + virtual ~CombinedImuFactor() {} /// @return a deep copy of this factor virtual gtsam::NonlinearFactor::shared_ptr clone() const; @@ -211,7 +217,7 @@ public: /** Access the preintegrated measurements. */ - const CombinedPreintegratedMeasurements& preintegratedMeasurements() const { + const PreintegratedCombinedMeasurements& preintegratedMeasurements() const { return _PIM_; } @@ -226,16 +232,22 @@ public: boost::optional H4 = boost::none, boost::optional H5 = boost::none, boost::optional H6 = boost::none) const; - // @deprecated The following function has been deprecated, use PreintegrationBase::predict + /// @deprecated typename + typedef gtsam::PreintegratedCombinedMeasurements CombinedPreintegratedMeasurements; + + /// @deprecated constructor + CombinedImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, + Key bias_j, const CombinedPreintegratedMeasurements& pim, + const Vector3& gravity, const Vector3& omegaCoriolis, + const boost::optional& body_P_sensor = boost::none, + const bool use2ndOrderCoriolis = false); + + // @deprecated use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, - Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const CombinedPreintegratedMeasurements& PIM, const Vector3& gravity, - const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false) { - PoseVelocityBias PVB = PIM.predict(pose_i, vel_i, bias_i, gravity, - omegaCoriolis, use2ndOrderCoriolis); - pose_j = PVB.pose; - vel_j = PVB.velocity; - } + Vector3& vel_j, const imuBias::ConstantBias& bias_i, + const CombinedPreintegratedMeasurements& pim, + const Vector3& gravity, const Vector3& omegaCoriolis, + const bool use2ndOrderCoriolis = false); private: @@ -246,13 +258,8 @@ private: ar & boost::serialization::make_nvp("NoiseModelFactor6", boost::serialization::base_object(*this)); ar & BOOST_SERIALIZATION_NVP(_PIM_); - ar & BOOST_SERIALIZATION_NVP(gravity_); - ar & BOOST_SERIALIZATION_NVP(omegaCoriolis_); - ar & BOOST_SERIALIZATION_NVP(body_P_sensor_); } }; // class CombinedImuFactor -typedef CombinedImuFactor::CombinedPreintegratedMeasurements CombinedImuFactorPreintegratedMeasurements; - } /// namespace gtsam diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 17c68139c..024bdd65e 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -31,43 +31,32 @@ using namespace std; //------------------------------------------------------------------------------ // Inner class PreintegratedMeasurements //------------------------------------------------------------------------------ -ImuFactor::PreintegratedMeasurements::PreintegratedMeasurements( - const imuBias::ConstantBias& bias, const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, - const bool use2ndOrderIntegration) : - PreintegrationBase(bias, measuredAccCovariance, measuredOmegaCovariance, - integrationErrorCovariance, use2ndOrderIntegration) { - preintMeasCov_.setZero(); -} - -//------------------------------------------------------------------------------ -void ImuFactor::PreintegratedMeasurements::print(const string& s) const { +void PreintegratedImuMeasurements::print(const string& s) const { PreintegrationBase::print(s); } //------------------------------------------------------------------------------ -bool ImuFactor::PreintegratedMeasurements::equals( - const PreintegratedMeasurements& expected, double tol) const { - return equal_with_abs_tol(preintMeasCov_, expected.preintMeasCov_, tol) - && PreintegrationBase::equals(expected, tol); +bool PreintegratedImuMeasurements::equals( + const PreintegratedImuMeasurements& other, double tol) const { + return PreintegrationBase::equals(other, tol) && + equal_with_abs_tol(preintMeasCov_, other.preintMeasCov_, tol); } //------------------------------------------------------------------------------ -void ImuFactor::PreintegratedMeasurements::resetIntegration() { +void PreintegratedImuMeasurements::resetIntegration() { PreintegrationBase::resetIntegration(); preintMeasCov_.setZero(); } //------------------------------------------------------------------------------ -void ImuFactor::PreintegratedMeasurements::integrateMeasurement( +void PreintegratedImuMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - boost::optional body_P_sensor, OptionalJacobian<9, 9> F_test, + OptionalJacobian<9, 9> F_test, OptionalJacobian<9, 9> G_test) { Vector3 correctedAcc, correctedOmega; correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, - correctedAcc, correctedOmega, body_P_sensor); + &correctedAcc, &correctedOmega); // rotation increment computed from the current rotation rate measurement const Vector3 integratedOmega = correctedOmega * deltaT; @@ -79,7 +68,7 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement( updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT); // Update preintegrated measurements (also get Jacobian) - const Matrix3 R_i = deltaRij(); // store this, which is useful to compute G_test + const Matrix3 dRij = deltaRij().matrix(); // store this, which is useful to compute G_test Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F); @@ -92,18 +81,18 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement( // NOTE 2: computation of G * (1/deltaT) * measurementCovariance * G.transpose() done block-wise, // as G and measurementCovariance are block-diagonal matrices preintMeasCov_ = F * preintMeasCov_ * F.transpose(); - preintMeasCov_.block<3, 3>(0, 0) += integrationCovariance() * deltaT; - preintMeasCov_.block<3, 3>(3, 3) += R_i * accelerometerCovariance() - * R_i.transpose() * deltaT; + preintMeasCov_.block<3, 3>(0, 0) += p().integrationCovariance * deltaT; + preintMeasCov_.block<3, 3>(3, 3) += dRij * p().accelerometerCovariance + * dRij.transpose() * deltaT; preintMeasCov_.block<3, 3>(6, 6) += D_Rincr_integratedOmega - * gyroscopeCovariance() * D_Rincr_integratedOmega.transpose() * deltaT; + * p().gyroscopeCovariance * D_Rincr_integratedOmega.transpose() * deltaT; Matrix3 F_pos_noiseacc; - if (use2ndOrderIntegration()) { - F_pos_noiseacc = 0.5 * R_i * deltaT * deltaT; + if (p().use2ndOrderIntegration) { + F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; preintMeasCov_.block<3, 3>(0, 0) += (1 / deltaT) * F_pos_noiseacc - * accelerometerCovariance() * F_pos_noiseacc.transpose(); - Matrix3 temp = F_pos_noiseacc * accelerometerCovariance() * R_i.transpose(); // has 1/deltaT + * p().accelerometerCovariance * F_pos_noiseacc.transpose(); + Matrix3 temp = F_pos_noiseacc * p().accelerometerCovariance * dRij.transpose(); // has 1/deltaT preintMeasCov_.block<3, 3>(0, 3) += temp; preintMeasCov_.block<3, 3>(3, 0) += temp.transpose(); } @@ -114,34 +103,40 @@ void ImuFactor::PreintegratedMeasurements::integrateMeasurement( } if (G_test) { // This in only for testing & documentation, while the actual computation is done block-wise - if (!use2ndOrderIntegration()) + if (!p().use2ndOrderIntegration) F_pos_noiseacc = Z_3x3; // intNoise accNoise omegaNoise (*G_test) << I_3x3 * deltaT, F_pos_noiseacc, Z_3x3, // pos - Z_3x3, R_i * deltaT, Z_3x3, // vel + Z_3x3, dRij * deltaT, Z_3x3, // vel Z_3x3, Z_3x3, D_Rincr_integratedOmega * deltaT; // angle } } +//------------------------------------------------------------------------------ +PreintegratedImuMeasurements::PreintegratedImuMeasurements( + const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, + const Matrix3& measuredOmegaCovariance, + const Matrix3& integrationErrorCovariance, + const bool use2ndOrderIntegration) + { + biasHat_ = biasHat; + boost::shared_ptr p = boost::make_shared(); + p->gyroscopeCovariance = measuredOmegaCovariance; + p->accelerometerCovariance = measuredAccCovariance; + p->integrationCovariance = integrationErrorCovariance; + p->use2ndOrderIntegration = use2ndOrderIntegration; + p_ = p; + resetIntegration(); +} //------------------------------------------------------------------------------ // ImuFactor methods -//------------------------------------------------------------------------------ -ImuFactor::ImuFactor() : - ImuFactorBase(), _PIM_(imuBias::ConstantBias(), Z_3x3, Z_3x3, Z_3x3) { -} - //------------------------------------------------------------------------------ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, - const PreintegratedMeasurements& preintegratedMeasurements, - const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor, const bool use2ndOrderCoriolis) : - Base( - noiseModel::Gaussian::Covariance( - preintegratedMeasurements.preintMeasCov_), pose_i, vel_i, pose_j, - vel_j, bias), ImuFactorBase(gravity, omegaCoriolis, body_P_sensor, - use2ndOrderCoriolis), _PIM_(preintegratedMeasurements) { -} + const PreintegratedImuMeasurements& pim) + : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, + pose_j, vel_j, bias), + _PIM_(pim) {} //------------------------------------------------------------------------------ gtsam::NonlinearFactor::shared_ptr ImuFactor::clone() const { @@ -155,17 +150,17 @@ void ImuFactor::print(const string& s, const KeyFormatter& keyFormatter) const { << keyFormatter(this->key2()) << "," << keyFormatter(this->key3()) << "," << keyFormatter(this->key4()) << "," << keyFormatter(this->key5()) << ")\n"; - ImuFactorBase::print(""); + Base::print(""); _PIM_.print(" preintegrated measurements:"); // Print standard deviations on covariance only cout << " noise model sigmas: " << this->noiseModel_->sigmas().transpose() << endl; } //------------------------------------------------------------------------------ -bool ImuFactor::equals(const NonlinearFactor& expected, double tol) const { - const This *e = dynamic_cast(&expected); +bool ImuFactor::equals(const NonlinearFactor& other, double tol) const { + const This *e = dynamic_cast(&other); return e != NULL && Base::equals(*e, tol) && _PIM_.equals(e->_PIM_, tol) - && ImuFactorBase::equals(*e, tol); + && Base::equals(*e, tol); } //------------------------------------------------------------------------------ @@ -174,9 +169,46 @@ Vector ImuFactor::evaluateError(const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, boost::optional H1, boost::optional H2, boost::optional H3, boost::optional H4, boost::optional H5) const { - return _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, - gravity_, omegaCoriolis_, use2ndOrderCoriolis_, H1, H2, H3, H4, H5); + H1, H2, H3, H4, H5); +} + +//------------------------------------------------------------------------------ +ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, + const PreintegratedMeasurements& pim, + const Vector3& gravity, const Vector3& omegaCoriolis, + const boost::optional& body_P_sensor, + const bool use2ndOrderCoriolis) + : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, + pose_j, vel_j, bias), + _PIM_(pim) { + boost::shared_ptr p = + boost::make_shared(pim.p()); + p->gravity = gravity; + p->omegaCoriolis = omegaCoriolis; + p->body_P_sensor = body_P_sensor; + p->use2ndOrderCoriolis = use2ndOrderCoriolis; + _PIM_.p_ = p; +} + +//------------------------------------------------------------------------------ +void ImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, + Pose3& pose_j, Vector3& vel_j, + const imuBias::ConstantBias& bias_i, + const PreintegratedMeasurements& pim, + const Vector3& gravity, const Vector3& omegaCoriolis, + const bool use2ndOrderCoriolis) { + // NOTE(frank): hack below only for backward compatibility + boost::shared_ptr p = + boost::make_shared(pim.p()); + p->gravity = gravity; + p->omegaCoriolis = omegaCoriolis; + p->use2ndOrderCoriolis = use2ndOrderCoriolis; + PreintegratedMeasurements newPim = pim; + newPim.p_ = p; + PoseVelocityBias pvb = newPim.predict(pose_i, vel_i, bias_i); + pose_j = pvb.pose; + vel_j = pvb.velocity; } } // namespace gtsam diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 57f4d0e89..eb94675fa 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -24,11 +24,86 @@ /* GTSAM includes */ #include #include -#include #include namespace gtsam { +/** + * PreintegratedIMUMeasurements accumulates (integrates) the IMU measurements + * (rotation rates and accelerations) and the corresponding covariance matrix. + * The measurements are then used to build the Preintegrated IMU factor. + * Integration is done incrementally (ideally, one integrates the measurement + * as soon as it is received from the IMU) so as to avoid costly integration + * at time of factor construction. + */ +class PreintegratedImuMeasurements: public PreintegrationBase { + + friend class ImuFactor; + +protected: + + Eigen::Matrix preintMeasCov_; ///< COVARIANCE OF: [PreintPOSITION PreintVELOCITY PreintROTATION] + ///< (first-order propagation from *measurementCovariance*). + + /// Default constructor for serialization + PreintegratedImuMeasurements() {} + +public: + + /** + * Constructor, initializes the class with no measurements + * @param bias Current estimate of acceleration and rotation rate biases + * @param p Parameters, typically fixed in a single application + */ + PreintegratedImuMeasurements(const boost::shared_ptr& p, + const imuBias::ConstantBias& biasHat) : + PreintegrationBase(p,biasHat) { + preintMeasCov_.setZero(); + } + + /// print + void print(const std::string& s = "Preintegrated Measurements:") const; + + /// equals + bool equals(const PreintegratedImuMeasurements& expected, + double tol = 1e-9) const; + + /// Re-initialize PreintegratedIMUMeasurements + void resetIntegration(); + + /** + * Add a single IMU measurement to the preintegration. + * @param measuredAcc Measured acceleration (in body frame, as given by the sensor) + * @param measuredOmega Measured angular velocity (as given by the sensor) + * @param deltaT Time interval between this and the last IMU measurement + * @param body_P_sensor Optional sensor frame (pose of the IMU in the body frame) + * @param Fout, Gout Jacobians used internally (only needed for testing) + */ + void integrateMeasurement(const Vector3& measuredAcc, + const Vector3& measuredOmega, double deltaT, + OptionalJacobian<9, 9> Fout = boost::none, OptionalJacobian<9, 9> Gout = boost::none); + + /// Return pre-integrated measurement covariance + Matrix preintMeasCov() const { return preintMeasCov_; } + + /// @deprecated constructor + PreintegratedImuMeasurements(const imuBias::ConstantBias& biasHat, + const Matrix3& measuredAccCovariance, + const Matrix3& measuredOmegaCovariance, + const Matrix3& integrationErrorCovariance, + const bool use2ndOrderIntegration = false); + +private: + + /// Serialization function + friend class boost::serialization::access; + template + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { + ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegrationBase); + ar & BOOST_SERIALIZATION_NVP(preintMeasCov_); + } +}; + /** * * @addtogroup SLAM @@ -53,96 +128,22 @@ namespace gtsam { * the vehicle at previous time step), current state (pose and velocity at * current time step), and the bias estimate. Following the preintegration * scheme proposed in [2], the ImuFactor includes many IMU measurements, which - * are "summarized" using the PreintegratedMeasurements class. + * are "summarized" using the PreintegratedIMUMeasurements class. * Note that this factor does not model "temporal consistency" of the biases * (which are usually slowly varying quantities), which is up to the caller. * See also CombinedImuFactor for a class that does this for you. */ class ImuFactor: public NoiseModelFactor5, public ImuFactorBase { + imuBias::ConstantBias> { public: - /** - * PreintegratedMeasurements accumulates (integrates) the IMU measurements - * (rotation rates and accelerations) and the corresponding covariance matrix. - * The measurements are then used to build the Preintegrated IMU factor. - * Integration is done incrementally (ideally, one integrates the measurement - * as soon as it is received from the IMU) so as to avoid costly integration - * at time of factor construction. - */ - class PreintegratedMeasurements: public PreintegrationBase { - - friend class ImuFactor; - - protected: - - Eigen::Matrix preintMeasCov_; ///< COVARIANCE OF: [PreintPOSITION PreintVELOCITY PreintROTATION] - ///< (first-order propagation from *measurementCovariance*). - - public: - - /** - * Default constructor, initializes the class with no measurements - * @param bias Current estimate of acceleration and rotation rate biases - * @param measuredAccCovariance Covariance matrix of measuredAcc - * @param measuredOmegaCovariance Covariance matrix of measured Angular Rate - * @param integrationErrorCovariance Covariance matrix of integration errors (velocity -> position) - * @param use2ndOrderIntegration Controls the order of integration - * (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) - */ - PreintegratedMeasurements(const imuBias::ConstantBias& bias, - const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, - const bool use2ndOrderIntegration = false); - - /// print - void print(const std::string& s = "Preintegrated Measurements:") const; - - /// equals - bool equals(const PreintegratedMeasurements& expected, - double tol = 1e-9) const; - - /// Re-initialize PreintegratedMeasurements - void resetIntegration(); - - /** - * Add a single IMU measurement to the preintegration. - * @param measuredAcc Measured acceleration (in body frame, as given by the sensor) - * @param measuredOmega Measured angular velocity (as given by the sensor) - * @param deltaT Time interval between this and the last IMU measurement - * @param body_P_sensor Optional sensor frame (pose of the IMU in the body frame) - * @param Fout, Gout Jacobians used internally (only needed for testing) - */ - void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double deltaT, - boost::optional body_P_sensor = boost::none, - OptionalJacobian<9, 9> Fout = boost::none, OptionalJacobian<9, 9> Gout = - boost::none); - - /// methods to access class variables - Matrix preintMeasCov() const { - return preintMeasCov_; - } - - private: - - /// Serialization function - friend class boost::serialization::access; - template - void serialize(ARCHIVE & ar, const unsigned int /*version*/) { - ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegrationBase); - ar & BOOST_SERIALIZATION_NVP(preintMeasCov_); - } - }; - private: typedef ImuFactor This; typedef NoiseModelFactor5 Base; - PreintegratedMeasurements _PIM_; + PreintegratedImuMeasurements _PIM_; public: @@ -163,17 +164,9 @@ public: * @param pose_j Current pose key * @param vel_j Current velocity key * @param bias Previous bias key - * @param preintegratedMeasurements Preintegrated IMU measurements - * @param gravity Gravity vector expressed in the global frame - * @param omegaCoriolis Rotation rate of the global frame w.r.t. an inertial frame - * @param body_P_sensor Optional pose of the sensor frame in the body frame - * @param use2ndOrderCoriolis When true, the second-order term is used in the calculation of the Coriolis Effect */ ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, - const PreintegratedMeasurements& preintegratedMeasurements, - const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor = boost::none, - const bool use2ndOrderCoriolis = false); + const PreintegratedImuMeasurements& preintegratedMeasurements); virtual ~ImuFactor() { } @@ -192,7 +185,7 @@ public: /** Access the preintegrated measurements. */ - const PreintegratedMeasurements& preintegratedMeasurements() const { + const PreintegratedImuMeasurements& preintegratedMeasurements() const { return _PIM_; } @@ -206,16 +199,21 @@ public: boost::optional H3 = boost::none, boost::optional H4 = boost::none, boost::optional H5 = boost::none) const; - /// @deprecated The following function has been deprecated, use PreintegrationBase::predict + /// @deprecated typename + typedef PreintegratedImuMeasurements PreintegratedMeasurements; + + /// @deprecated constructor + ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, + const PreintegratedMeasurements& preintegratedMeasurements, + const Vector3& gravity, const Vector3& omegaCoriolis, + const boost::optional& body_P_sensor = boost::none, + const bool use2ndOrderCoriolis = false); + + /// @deprecated use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const PreintegratedMeasurements& PIM, const Vector3& gravity, - const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false) { - PoseVelocityBias PVB = PIM.predict(pose_i, vel_i, bias_i, gravity, - omegaCoriolis, use2ndOrderCoriolis); - pose_j = PVB.pose; - vel_j = PVB.velocity; - } + const PreintegratedMeasurements& pim, const Vector3& gravity, + const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: @@ -226,13 +224,8 @@ private: ar & boost::serialization::make_nvp("NoiseModelFactor5", boost::serialization::base_object(*this)); ar & BOOST_SERIALIZATION_NVP(_PIM_); - ar & BOOST_SERIALIZATION_NVP(gravity_); - ar & BOOST_SERIALIZATION_NVP(omegaCoriolis_); - ar & BOOST_SERIALIZATION_NVP(body_P_sensor_); } }; // class ImuFactor -typedef ImuFactor::PreintegratedMeasurements ImuFactorPreintegratedMeasurements; - } /// namespace gtsam diff --git a/gtsam/navigation/ImuFactorBase.h b/gtsam/navigation/ImuFactorBase.h deleted file mode 100644 index 1e4e51220..000000000 --- a/gtsam/navigation/ImuFactorBase.h +++ /dev/null @@ -1,94 +0,0 @@ -/* ---------------------------------------------------------------------------- - - * GTSAM Copyright 2010, Georgia Tech Research Corporation, - * Atlanta, Georgia 30332-0415 - * All Rights Reserved - * Authors: Frank Dellaert, et al. (see THANKS for the full author list) - - * See LICENSE for the license information - - * -------------------------------------------------------------------------- */ - -/** - * @file PreintegrationBase.h - * @author Luca Carlone - * @author Stephen Williams - * @author Richard Roberts - * @author Vadim Indelman - * @author David Jensen - * @author Frank Dellaert - **/ - -#pragma once - -/* GTSAM includes */ -#include -#include - -namespace gtsam { - -class ImuFactorBase { - -protected: - - Vector3 gravity_; - Vector3 omegaCoriolis_; - boost::optional body_P_sensor_; ///< The pose of the sensor in the body frame - bool use2ndOrderCoriolis_; ///< Controls whether higher order terms are included when calculating the Coriolis Effect - -public: - - /** Default constructor - only use for serialization */ - ImuFactorBase() : - gravity_(Vector3(0.0, 0.0, 9.81)), omegaCoriolis_(Vector3(0.0, 0.0, 0.0)), body_P_sensor_( - boost::none), use2ndOrderCoriolis_(false) { - } - - /** - * Default constructor, stores basic quantities required by the Imu factors - * @param gravity Gravity vector expressed in the global frame - * @param omegaCoriolis Rotation rate of the global frame w.r.t. an inertial frame - * @param body_P_sensor Optional pose of the sensor frame in the body frame - * @param use2ndOrderCoriolis When true, the second-order term is used in the calculation of the Coriolis Effect - */ - ImuFactorBase(const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor = boost::none, - const bool use2ndOrderCoriolis = false) : - gravity_(gravity), omegaCoriolis_(omegaCoriolis), body_P_sensor_( - body_P_sensor), use2ndOrderCoriolis_(use2ndOrderCoriolis) { - } - - /// Methods to access class variables - const Vector3& gravity() const { - return gravity_; - } - const Vector3& omegaCoriolis() const { - return omegaCoriolis_; - } - - /// Needed for testable - //------------------------------------------------------------------------------ - void print(const std::string& /*s*/) const { - std::cout << " gravity: [ " << gravity_.transpose() << " ]" << std::endl; - std::cout << " omegaCoriolis: [ " << omegaCoriolis_.transpose() << " ]" - << std::endl; - std::cout << " use2ndOrderCoriolis: [ " << use2ndOrderCoriolis_ << " ]" - << std::endl; - if (this->body_P_sensor_) - this->body_P_sensor_->print(" sensor pose in body frame: "); - } - - /// Needed for testable - //------------------------------------------------------------------------------ - bool equals(const ImuFactorBase& expected, double tol) const { - return equal_with_abs_tol(gravity_, expected.gravity_, tol) - && equal_with_abs_tol(omegaCoriolis_, expected.omegaCoriolis_, tol) - && (use2ndOrderCoriolis_ == expected.use2ndOrderCoriolis_) - && ((!body_P_sensor_ && !expected.body_P_sensor_) - || (body_P_sensor_ && expected.body_P_sensor_ - && body_P_sensor_->equals(*expected.body_P_sensor_))); - } - -}; - -} /// namespace gtsam diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index ba10fd090..a1276b91c 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -21,7 +21,8 @@ #pragma once -#include +#include +#include namespace gtsam { @@ -31,49 +32,64 @@ namespace gtsam { * It includes the definitions of the preintegrated rotation. */ class PreintegratedRotation { - Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) + public: + + /// Parameters for pre-integration: + /// Usage: Create just a single Params and pass a shared pointer to the constructor + struct Params { + Matrix3 gyroscopeCovariance; ///< continuous-time "Covariance" of gyroscope measurements + boost::optional omegaCoriolis; ///< Coriolis constant + boost::optional body_P_sensor; ///< The pose of the sensor in the body frame + + Params():gyroscopeCovariance(I_3x3) {} + + private: + /** Serialization function */ + friend class boost::serialization::access; + template + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { + ar & BOOST_SERIALIZATION_NVP(gyroscopeCovariance); + ar & BOOST_SERIALIZATION_NVP(omegaCoriolis); + ar & BOOST_SERIALIZATION_NVP(body_P_sensor); + } + }; + + private: double deltaTij_; ///< Time interval from i to j + Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) + Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias - /// Jacobian of preintegrated rotation w.r.t. angular rate bias - Matrix3 delRdelBiasOmega_; + protected: + /// Parameters + boost::shared_ptr p_; - ///< continuous-time "Covariance" of gyroscope measurements - const Matrix3 gyroscopeCovariance_; + /// Default constructor for serialization + PreintegratedRotation() {} public: - /// Default constructor, initializes the variables in the base class - explicit PreintegratedRotation(const Matrix3& measuredOmegaCovariance) : - deltaRij_(Rot3()), deltaTij_(0.0), delRdelBiasOmega_(Z_3x3), gyroscopeCovariance_( - measuredOmegaCovariance) { + /// Default constructor, resets integration to zero + explicit PreintegratedRotation(const boost::shared_ptr& p) : p_(p) { + resetIntegration(); } /// Re-initialize PreintegratedMeasurements void resetIntegration() { - deltaRij_ = Rot3(); deltaTij_ = 0.0; + deltaRij_ = Rot3(); delRdelBiasOmega_ = Z_3x3; } /// @name Access instance variables /// @{ - - // Return 3*3 matrix of rotation from time i to time j. Expensive in quaternion case - Matrix3 deltaRij() const { - return deltaRij_.matrix(); - } - // Return log(rotation) from time i to time j. Expensive in both Rot3M and quaternion case. - Vector3 thetaRij(OptionalJacobian<3, 3> H = boost::none) const { - return Rot3::Logmap(deltaRij_, H); - } const double& deltaTij() const { return deltaTij_; } + const Rot3& deltaRij() const { + return deltaRij_; + } const Matrix3& delRdelBiasOmega() const { return delRdelBiasOmega_; } - const Matrix3& gyroscopeCovariance() const { - return gyroscopeCovariance_; - } /// @} /// @name Testable @@ -85,13 +101,10 @@ class PreintegratedRotation { std::cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << std::endl; } - bool equals(const PreintegratedRotation& expected, double tol) const { - return deltaRij_.equals(expected.deltaRij_, tol) - && fabs(deltaTij_ - expected.deltaTij_) < tol - && equal_with_abs_tol(delRdelBiasOmega_, expected.delRdelBiasOmega_, - tol) - && equal_with_abs_tol(gyroscopeCovariance_, - expected.gyroscopeCovariance_, tol); + bool equals(const PreintegratedRotation& other, double tol) const { + return deltaRij_.equals(other.deltaRij_, tol) && + fabs(deltaTij_ - other.deltaTij_) < tol && + equal_with_abs_tol(delRdelBiasOmega_, other.delRdelBiasOmega_, tol); } /// @} @@ -128,8 +141,7 @@ class PreintegratedRotation { if (H) { Matrix3 Jrinv_theta_bc; // This was done via an expmap, now we go *back* to so<3> - const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected, - Jrinv_theta_bc); + const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected, Jrinv_theta_bc); const Matrix3 Jr_JbiasOmegaIncr = Rot3::ExpmapDerivative(delRdelBiasOmega_ * biasOmegaIncr); (*H) = Jrinv_theta_bc * Jr_JbiasOmegaIncr * delRdelBiasOmega_; return biascorrectedOmega; @@ -139,9 +151,9 @@ class PreintegratedRotation { } /// Integrate coriolis correction in body frame rot_i - Vector3 integrateCoriolis(const Rot3& rot_i, - const Vector3& omegaCoriolis) const { - return rot_i.transpose() * omegaCoriolis * deltaTij(); + Vector3 integrateCoriolis(const Rot3& rot_i) const { + if (!p_->omegaCoriolis) return Vector3::Zero(); + return rot_i.transpose() * (*p_->omegaCoriolis) * deltaTij(); } private: @@ -149,8 +161,9 @@ class PreintegratedRotation { friend class boost::serialization::access; template void serialize(ARCHIVE & ar, const unsigned int /*version*/) { // NOLINT - ar & BOOST_SERIALIZATION_NVP(deltaRij_); + ar & BOOST_SERIALIZATION_NVP(p_); ar & BOOST_SERIALIZATION_NVP(deltaTij_); + ar & BOOST_SERIALIZATION_NVP(deltaRij_); ar & BOOST_SERIALIZATION_NVP(delRdelBiasOmega_); } }; diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index cc4fcee16..bef45e044 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -20,27 +20,12 @@ **/ #include "PreintegrationBase.h" +#include + +using namespace std; namespace gtsam { -PreintegrationBase::PreintegrationBase(const imuBias::ConstantBias& bias, - const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3&integrationErrorCovariance, - const bool use2ndOrderIntegration) - : PreintegratedRotation(measuredOmegaCovariance), - biasHat_(bias), - use2ndOrderIntegration_(use2ndOrderIntegration), - deltaPij_(Vector3::Zero()), - deltaVij_(Vector3::Zero()), - delPdelBiasAcc_(Z_3x3), - delPdelBiasOmega_(Z_3x3), - delVdelBiasAcc_(Z_3x3), - delVdelBiasOmega_(Z_3x3), - accelerometerCovariance_(measuredAccCovariance), - integrationCovariance_(integrationErrorCovariance) { -} - /// Re-initialize PreintegratedMeasurements void PreintegrationBase::resetIntegration() { PreintegratedRotation::resetIntegration(); @@ -53,24 +38,23 @@ void PreintegrationBase::resetIntegration() { } /// Needed for testable -void PreintegrationBase::print(const std::string& s) const { +void PreintegrationBase::print(const string& s) const { PreintegratedRotation::print(s); - std::cout << " deltaPij [ " << deltaPij_.transpose() << " ]" << std::endl; - std::cout << " deltaVij [ " << deltaVij_.transpose() << " ]" << std::endl; + cout << " deltaPij [ " << deltaPij_.transpose() << " ]" << endl; + cout << " deltaVij [ " << deltaVij_.transpose() << " ]" << endl; biasHat_.print(" biasHat"); } /// Needed for testable bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) const { - return PreintegratedRotation::equals(other, tol) && biasHat_.equals(other.biasHat_, tol) - && equal_with_abs_tol(deltaPij_, other.deltaPij_, tol) - && equal_with_abs_tol(deltaVij_, other.deltaVij_, tol) - && equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol) - && equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol) - && equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol) - && equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol) - && equal_with_abs_tol(accelerometerCovariance_, other.accelerometerCovariance_, tol) - && equal_with_abs_tol(integrationCovariance_, other.integrationCovariance_, tol); + return PreintegratedRotation::equals(other, tol) && + biasHat_.equals(other.biasHat_, tol) && + equal_with_abs_tol(deltaPij_, other.deltaPij_, tol) && + equal_with_abs_tol(deltaVij_, other.deltaVij_, tol) && + equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol) && + equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol) && + equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol) && + equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol); } /// Update preintegrated measurements @@ -78,9 +62,10 @@ void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correcte const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F) { - const Matrix3 dRij = deltaRij(); // expensive + const Matrix3 dRij = deltaRij().matrix(); // expensive const Vector3 temp = dRij * correctedAcc * deltaT; - if (!use2ndOrderIntegration_) { + + if (!p().use2ndOrderIntegration) { deltaPij_ += deltaVij_ * deltaT; } else { deltaPij_ += deltaVij_ * deltaT + 0.5 * temp * deltaT; @@ -89,13 +74,13 @@ void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correcte Matrix3 R_i, F_angles_angles; if (F) - R_i = deltaRij(); // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij + R_i = dRij; // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0); if (F) { const Matrix3 F_vel_angles = -R_i * skewSymmetric(correctedAcc) * deltaT; Matrix3 F_pos_angles; - if (use2ndOrderIntegration_) + if (p().use2ndOrderIntegration) F_pos_angles = 0.5 * F_vel_angles * deltaT; else F_pos_angles = Z_3x3; @@ -112,9 +97,9 @@ void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correcte void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAcc, const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, double deltaT) { - const Matrix3 dRij = deltaRij(); // expensive + const Matrix3 dRij = deltaRij().matrix(); // expensive const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT * delRdelBiasOmega(); - if (!use2ndOrderIntegration_) { + if (!p().use2ndOrderIntegration) { delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT; delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT; } else { @@ -127,19 +112,19 @@ void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAc } void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( - const Vector3& measuredAcc, const Vector3& measuredOmega, Vector3& correctedAcc, - Vector3& correctedOmega, boost::optional body_P_sensor) { - correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - correctedOmega = biasHat_.correctGyroscope(measuredOmega); + const Vector3& measuredAcc, const Vector3& measuredOmega, Vector3* correctedAcc, + Vector3* correctedOmega) { + *correctedAcc = biasHat_.correctAccelerometer(measuredAcc); + *correctedOmega = biasHat_.correctGyroscope(measuredOmega); // Then compensate for sensor-body displacement: we express the quantities // (originally in the IMU frame) into the body frame - if (body_P_sensor) { - Matrix3 body_R_sensor = body_P_sensor->rotation().matrix(); - correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame - Matrix3 body_omega_body__cross = skewSymmetric(correctedOmega); - correctedAcc = body_R_sensor * correctedAcc - - body_omega_body__cross * body_omega_body__cross * body_P_sensor->translation().vector(); + if (p().body_P_sensor) { + Matrix3 body_R_sensor = p().body_P_sensor->rotation().matrix(); + *correctedOmega = body_R_sensor * (*correctedOmega); // rotation rate vector in the body frame + Matrix3 body_omega_body__cross = skewSymmetric(*correctedOmega); + *correctedAcc = body_R_sensor * (*correctedAcc) + - body_omega_body__cross * body_omega_body__cross * p().body_P_sensor->translation().vector(); // linear acceleration vector in the body frame } } @@ -148,31 +133,27 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( //------------------------------------------------------------------------------ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, const Vector3& omegaCoriolis, const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, - const Vector3& deltaVij_biascorrected, - const bool use2ndOrderCoriolis) const { + const Vector3& deltaVij_biascorrected) const { const double dt = deltaTij(), dt2 = dt * dt; - - // Rotation const Matrix3 Ri = pose_i.rotation().matrix(); - const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected); - const Vector3 dR = biascorrectedOmega - - Ri.transpose() * omegaCoriolis * dt; // Coriolis term - // Translation - Vector3 dP = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * gravity * dt2 - - omegaCoriolis.cross(vel_i) * dt2; // Coriolis term - we got rid of the 2 wrt INS paper + // Rotation, translation, and velocity: + Vector3 dR = Rot3::Logmap(deltaRij_biascorrected); + Vector3 dP = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * p().gravity * dt2; + Vector3 dV = Ri * deltaVij_biascorrected + p().gravity * dt; - // Velocity - Vector3 dV = Ri * deltaVij_biascorrected + gravity * dt - - 2 * omegaCoriolis.cross(vel_i) * dt; // Coriolis term - - if (use2ndOrderCoriolis) { - Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(pose_i.translation().vector())); - dP -= 0.5 * temp * dt2; - dV -= temp * dt; + if (p().omegaCoriolis) { + const Vector3& omegaCoriolis = *p().omegaCoriolis; + dR -= Ri.transpose() * omegaCoriolis * dt; // Coriolis term + dP -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper + dV -= 2 * omegaCoriolis.cross(vel_i) * dt; + if (p().use2ndOrderCoriolis) { + Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(pose_i.translation().vector())); + dP -= 0.5 * temp * dt2; + dV -= temp * dt; + } } // TODO(frank): pose update below is separate expmap for R,t. Is that kosher? @@ -183,13 +164,12 @@ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, /// Predict state at time j //------------------------------------------------------------------------------ PoseVelocityBias PreintegrationBase::predict( - const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) const { + const Pose3& pose_i, const Vector3& vel_i, + const imuBias::ConstantBias& bias_i) const { const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - return predict(pose_i, vel_i, bias_i, gravity, omegaCoriolis, - biascorrectedDeltaRij(biasIncr.gyroscope()), - biascorrectedDeltaPij(biasIncr), biascorrectedDeltaVij(biasIncr), - use2ndOrderCoriolis); + return predict( + pose_i, vel_i, bias_i, biascorrectedDeltaRij(biasIncr.gyroscope()), + biascorrectedDeltaPij(biasIncr), biascorrectedDeltaVij(biasIncr)); } /// Compute errors w.r.t. preintegrated measurements and Jacobians wrpt pose_i, vel_i, bias_i, pose_j, bias_j @@ -197,9 +177,6 @@ PoseVelocityBias PreintegrationBase::predict( Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, - const Vector3& omegaCoriolis, - const bool use2ndOrderCoriolis, OptionalJacobian<9, 6> H1, OptionalJacobian<9, 3> H2, OptionalJacobian<9, 6> H3, @@ -219,9 +196,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasIncr.gyroscope()); const Vector3 deltaPij_biascorrected = biascorrectedDeltaPij(biasIncr); const Vector3 deltaVij_biascorrected = biascorrectedDeltaVij(biasIncr); - PoseVelocityBias predictedState_j = predict(pose_i, vel_i, bias_i, gravity, - omegaCoriolis, deltaRij_biascorrected, deltaPij_biascorrected, - deltaVij_biascorrected, use2ndOrderCoriolis); + PoseVelocityBias predictedState_j = + predict(pose_i, vel_i, bias_i, deltaRij_biascorrected, + deltaPij_biascorrected, deltaVij_biascorrected); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance const Vector3 fp = Ri.transpose() * (pos_j - predictedState_j.pose.translation().vector()); @@ -241,7 +218,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const H5 ? &D_cThetaRij_biasOmegaIncr : 0); // Coriolis term, NOTE inconsistent with AHRS, where coriolisHat is *after* integration - const Vector3 coriolis = integrateCoriolis(rot_i, omegaCoriolis); + // TODO(frank): move derivatives to predict and do coriolis branching there + const Vector3 coriolis = integrateCoriolis(rot_i); const Vector3 correctedOmega = biascorrectedOmega - coriolis; // Residual rotation error @@ -253,17 +231,16 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Vector3 fR = Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); const double dt = deltaTij(), dt2 = dt*dt; - Matrix3 Ritranspose_omegaCoriolisHat; - if (H1 || H2) - Ritranspose_omegaCoriolisHat = Ri.transpose() * skewSymmetric(omegaCoriolis); + Matrix3 RitOmegaCoriolisHat = Z_3x3; + if ((H1 || H2) && p().omegaCoriolis) + RitOmegaCoriolisHat = Ri.transpose() * skewSymmetric(*p().omegaCoriolis); if (H1) { const Matrix3 D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis); Matrix3 dfPdPi = -I_3x3, dfVdPi = Z_3x3; - if (use2ndOrderCoriolis) { - // this is the same as: Ri.transpose() * omegaCoriolisHat * omegaCoriolisHat * Ri - const Matrix3 temp = Ritranspose_omegaCoriolisHat - * (-Ritranspose_omegaCoriolisHat.transpose()); + if (p().use2ndOrderCoriolis) { + // this is the same as: Ri.transpose() * p().omegaCoriolisHat * p().omegaCoriolisHat * Ri + const Matrix3 temp = RitOmegaCoriolisHat * (-RitOmegaCoriolisHat.transpose()); dfPdPi += 0.5 * temp * dt2; dfVdPi += temp * dt; } @@ -278,8 +255,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const if (H2) { (*H2) << Z_3x3, // dfR/dVi - -Ri.transpose() * dt + Ritranspose_omegaCoriolisHat * dt2, // dfP/dVi - -Ri.transpose() + 2 * Ritranspose_omegaCoriolisHat * dt; // dfV/dVi + -Ri.transpose() * dt + RitOmegaCoriolisHat * dt2, // dfP/dVi + -Ri.transpose() + 2 * RitOmegaCoriolisHat * dt; // dfV/dVi } if (H3) { (*H3) << @@ -306,19 +283,16 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const return r; } -ImuBase::ImuBase() - : gravity_(Vector3(0.0, 0.0, 9.81)), - omegaCoriolis_(Vector3(0.0, 0.0, 0.0)), - body_P_sensor_(boost::none), - use2ndOrderCoriolis_(false) { +//------------------------------------------------------------------------------ +PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& vel_i, + const imuBias::ConstantBias& bias_i, const Vector3& gravity, const Vector3& omegaCoriolis, + const bool use2ndOrderCoriolis) { + // NOTE(frank): parameters are supposed to be constant, below is only provided for compatibility + boost::shared_ptr q = boost::make_shared(p()); + q->gravity = gravity; + q->omegaCoriolis = omegaCoriolis; + q->use2ndOrderCoriolis = use2ndOrderCoriolis; + p_ = q; + return predict(pose_i, vel_i, bias_i); } - -ImuBase::ImuBase(const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor, const bool use2ndOrderCoriolis) - : gravity_(gravity), - omegaCoriolis_(omegaCoriolis), - body_P_sensor_(body_P_sensor), - use2ndOrderCoriolis_(use2ndOrderCoriolis) { -} - } /// namespace gtsam diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 722091b40..c810eb538 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -37,11 +37,9 @@ struct PoseVelocityBias { Vector3 velocity; imuBias::ConstantBias bias; - PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, const imuBias::ConstantBias _bias) - : pose(_pose), - velocity(_velocity), - bias(_bias) { - } + PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, + const imuBias::ConstantBias _bias) + : pose(_pose), velocity(_velocity), bias(_bias) {} }; /** @@ -52,71 +50,85 @@ struct PoseVelocityBias { */ class PreintegrationBase : public PreintegratedRotation { - const imuBias::ConstantBias biasHat_; ///< Acceleration and gyro bias used for preintegration - const bool use2ndOrderIntegration_; ///< Controls the order of integration + public: + + /// Parameters for pre-integration: + /// Usage: Create just a single Params and pass a shared pointer to the constructor + struct Params : PreintegratedRotation::Params { + Matrix3 accelerometerCovariance; ///< continuous-time "Covariance" of accelerometer + Matrix3 integrationCovariance; ///< continuous-time "Covariance" describing integration uncertainty + /// (to compensate errors in Euler integration) + bool use2ndOrderIntegration; ///< Controls the order of integration + /// (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) + bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration + Vector3 gravity; ///< Gravity constant + + Params() + : accelerometerCovariance(I_3x3), + integrationCovariance(I_3x3), + use2ndOrderIntegration(false), + use2ndOrderCoriolis(false), + gravity(0, 0, 9.8) {} + + private: + /** Serialization function */ + friend class boost::serialization::access; + template + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { + ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation::Params); + ar & BOOST_SERIALIZATION_NVP(accelerometerCovariance); + ar & BOOST_SERIALIZATION_NVP(integrationCovariance); + ar & BOOST_SERIALIZATION_NVP(use2ndOrderIntegration); + ar & BOOST_SERIALIZATION_NVP(use2ndOrderCoriolis); + ar & BOOST_SERIALIZATION_NVP(gravity); + } + }; + + protected: + + /// Acceleration and gyro bias used for preintegration + imuBias::ConstantBias biasHat_; Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i) Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame) - Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias + Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias Matrix3 delPdelBiasOmega_; ///< Jacobian of preintegrated position w.r.t. angular rate bias - Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias + Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias Matrix3 delVdelBiasOmega_; ///< Jacobian of preintegrated velocity w.r.t. angular rate bias - const Matrix3 accelerometerCovariance_; ///< continuous-time "Covariance" of accelerometer measurements - const Matrix3 integrationCovariance_; ///< continuous-time "Covariance" describing integration uncertainty - /// (to compensate errors in Euler integration) + /// Default constructor for serialization + PreintegrationBase() {} public: /** - * Default constructor, initializes the variables in the base class + * Constructor, initializes the variables in the base class * @param bias Current estimate of acceleration and rotation rate biases - * @param use2ndOrderIntegration Controls the order of integration - * (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) + * @param p Parameters, typically fixed in a single application */ - PreintegrationBase(const imuBias::ConstantBias& bias, const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3&integrationErrorCovariance, const bool use2ndOrderIntegration); + PreintegrationBase(const boost::shared_ptr& p, + const imuBias::ConstantBias& biasHat) + : PreintegratedRotation(p), biasHat_(biasHat) { + resetIntegration(); + } /// Re-initialize PreintegratedMeasurements void resetIntegration(); - /// methods to access class variables - bool use2ndOrderIntegration() const { - return use2ndOrderIntegration_; - } - const Vector3& deltaPij() const { - return deltaPij_; - } - const Vector3& deltaVij() const { - return deltaVij_; - } - const imuBias::ConstantBias& biasHat() const { - return biasHat_; - } - Vector6 biasHatVector() const { - return biasHat_.vector(); - } // expensive - const Matrix3& delPdelBiasAcc() const { - return delPdelBiasAcc_; - } - const Matrix3& delPdelBiasOmega() const { - return delPdelBiasOmega_; - } - const Matrix3& delVdelBiasAcc() const { - return delVdelBiasAcc_; - } - const Matrix3& delVdelBiasOmega() const { - return delVdelBiasOmega_; - } + const Params& p() const { return *boost::static_pointer_cast(p_);} - const Matrix3& accelerometerCovariance() const { - return accelerometerCovariance_; - } - const Matrix3& integrationCovariance() const { - return integrationCovariance_; - } + /// getters + const imuBias::ConstantBias& biasHat() const { return biasHat_; } + const Vector3& deltaPij() const { return deltaPij_; } + const Vector3& deltaVij() const { return deltaVij_; } + const Matrix3& delPdelBiasAcc() const { return delPdelBiasAcc_; } + const Matrix3& delPdelBiasOmega() const { return delPdelBiasOmega_; } + const Matrix3& delVdelBiasAcc() const { return delVdelBiasAcc_; } + const Matrix3& delVdelBiasOmega() const { return delVdelBiasOmega_; } + + // Exposed for MATLAB + Vector6 biasHatVector() const { return biasHat_.vector(); } /// print void print(const std::string& s) const; @@ -134,9 +146,9 @@ class PreintegrationBase : public PreintegratedRotation { double deltaT); void correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, - const Vector3& measuredOmega, Vector3& correctedAcc, - Vector3& correctedOmega, - boost::optional body_P_sensor); + const Vector3& measuredOmega, + Vector3* correctedAcc, + Vector3* correctedOmega); Vector3 biascorrectedDeltaPij(const imuBias::ConstantBias& biasIncr) const { return deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() @@ -150,33 +162,36 @@ class PreintegrationBase : public PreintegratedRotation { /// Predict state at time j, with bias-corrected quantities given PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i, const Vector3& gravity, const Vector3& omegaCoriolis, - const Rot3& deltaRij_biascorrected, - const Vector3& deltaPij_biascorrected, const Vector3& deltaVij_biascorrected, - const bool use2ndOrderCoriolis = false) const; + const imuBias::ConstantBias& bias_i, + const Rot3& deltaRij_biascorrected, + const Vector3& deltaPij_biascorrected, + const Vector3& deltaVij_biascorrected) const; /// Predict state at time j PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i, const Vector3& gravity, - const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false) const; + const imuBias::ConstantBias& bias_i) const; /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, const Vector3& omegaCoriolis, - const bool use2ndOrderCoriolis, OptionalJacobian<9, 6> H1 = - boost::none, + OptionalJacobian<9, 6> H1 = boost::none, OptionalJacobian<9, 3> H2 = boost::none, OptionalJacobian<9, 6> H3 = boost::none, OptionalJacobian<9, 3> H4 = boost::none, OptionalJacobian<9, 6> H5 = boost::none) const; + /// @deprecated predict + PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, + const imuBias::ConstantBias& bias_i, const Vector3& gravity, const Vector3& omegaCoriolis, + const bool use2ndOrderCoriolis = false); + private: /** Serialization function */ friend class boost::serialization::access; template void serialize(ARCHIVE & ar, const unsigned int /*version*/) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation); + ar & BOOST_SERIALIZATION_NVP(p_); ar & BOOST_SERIALIZATION_NVP(biasHat_); ar & BOOST_SERIALIZATION_NVP(deltaPij_); ar & BOOST_SERIALIZATION_NVP(deltaVij_); @@ -187,32 +202,4 @@ class PreintegrationBase : public PreintegratedRotation { } }; -class ImuBase { - - protected: - - Vector3 gravity_; - Vector3 omegaCoriolis_; - boost::optional body_P_sensor_; ///< The pose of the sensor in the body frame - bool use2ndOrderCoriolis_; ///< Controls whether higher order terms are included when calculating the Coriolis Effect - - public: - - /// Default constructor, with decent gravity and zero coriolis - ImuBase(); - - /// Fully fledge constructor - ImuBase(const Vector3& gravity, const Vector3& omegaCoriolis, - boost::optional body_P_sensor = boost::none, - const bool use2ndOrderCoriolis = false); - - const Vector3& gravity() const { - return gravity_; - } - const Vector3& omegaCoriolis() const { - return omegaCoriolis_; - } - -}; - } /// namespace gtsam diff --git a/gtsam/navigation/tests/testAHRSFactor.cpp b/gtsam/navigation/tests/testAHRSFactor.cpp index 928c0f74f..73e30ac32 100644 --- a/gtsam/navigation/tests/testAHRSFactor.cpp +++ b/gtsam/navigation/tests/testAHRSFactor.cpp @@ -50,7 +50,7 @@ Rot3 evaluateRotationError(const AHRSFactor& factor, const Rot3 rot_i, AHRSFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( const Vector3& bias, const list& measuredOmegas, const list& deltaTs, - const Vector3& initialRotationRate = Vector3()) { + const Vector3& initialRotationRate = Vector3::Zero()) { AHRSFactor::PreintegratedMeasurements result(bias, Matrix3::Identity()); list::const_iterator itOmega = measuredOmegas.begin(); diff --git a/gtsam/navigation/tests/testCombinedImuFactor.cpp b/gtsam/navigation/tests/testCombinedImuFactor.cpp index c5e9886d9..a6703272c 100644 --- a/gtsam/navigation/tests/testCombinedImuFactor.cpp +++ b/gtsam/navigation/tests/testCombinedImuFactor.cpp @@ -146,15 +146,9 @@ TEST( CombinedImuFactor, PreintegratedMeasurements ) { actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT( - assert_equal(Vector(expected1.deltaPij()), Vector(actual1.deltaPij()), - tol)); - EXPECT( - assert_equal(Vector(expected1.deltaVij()), Vector(actual1.deltaVij()), - tol)); - EXPECT( - assert_equal(Matrix(expected1.deltaRij()), Matrix(actual1.deltaRij()), - tol)); + EXPECT(assert_equal(Vector(expected1.deltaPij()), actual1.deltaPij(), tol)); + EXPECT(assert_equal(Vector(expected1.deltaVij()), actual1.deltaVij(), tol)); + EXPECT(assert_equal(expected1.deltaRij(), actual1.deltaRij(), tol)); DOUBLES_EQUAL(expected1.deltaTij(), actual1.deltaTij(), tol); } @@ -373,7 +367,6 @@ TEST(CombinedImuFactor, PredictRotation) { TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { // Linearization point imuBias::ConstantBias bias_old = imuBias::ConstantBias(); ///< Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // Measurements list measuredAccs, measuredOmegas; @@ -408,7 +401,7 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { Matrix Factual, Gactual; preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, body_P_sensor, Factual, Gactual); + newDeltaT, Factual, Gactual); bool use2ndOrderIntegration = false; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index c6aa48b30..bffc62d90 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -90,11 +90,11 @@ const Matrix3 kIntegrationErrorCovariance = intNoiseVar * Matrix3::Identity(); // Auxiliary functions to test preintegrated Jacobians // delPdelBiasAcc_ delPdelBiasOmega_ delVdelBiasAcc_ delVdelBiasOmega_ delRdelBiasOmega_ /* ************************************************************************* */ -ImuFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( +PreintegratedImuMeasurements evaluatePreintegratedMeasurements( const imuBias::ConstantBias& bias, const list& measuredAccs, const list& measuredOmegas, const list& deltaTs, const bool use2ndOrderIntegration = false) { - ImuFactor::PreintegratedMeasurements result(bias, kMeasuredAccCovariance, + PreintegratedImuMeasurements result(bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, use2ndOrderIntegration); @@ -159,7 +159,7 @@ TEST(ImuFactor, PreintegratedMeasurements) { bool use2ndOrderIntegration = true; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements actual1(bias, kMeasuredAccCovariance, + PreintegratedImuMeasurements actual1(bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, use2ndOrderIntegration); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); @@ -180,7 +180,7 @@ TEST(ImuFactor, PreintegratedMeasurements) { double expectedDeltaT2(1); // Actual preintegrated values - ImuFactor::PreintegratedMeasurements actual2 = actual1; + PreintegratedImuMeasurements actual2 = actual1; actual2.integrateMeasurement(measuredAcc, measuredOmega, deltaT); EXPECT( @@ -211,7 +211,7 @@ double deltaT = 1.0; TEST(ImuFactor, ErrorAndJacobians) { using namespace common; bool use2ndOrderIntegration = true; - ImuFactor::PreintegratedMeasurements pre_int_data(bias, + PreintegratedImuMeasurements pre_int_data(bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, use2ndOrderIntegration); pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); @@ -290,7 +290,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - ImuFactor::PreintegratedMeasurements pre_int_data( + PreintegratedImuMeasurements pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); @@ -330,7 +330,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - ImuFactor::PreintegratedMeasurements pre_int_data( + PreintegratedImuMeasurements pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); @@ -452,7 +452,7 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { } // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = + PreintegratedImuMeasurements preintegrated = evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs); @@ -495,7 +495,6 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Linearization point imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); // Measurements list measuredAccs, measuredOmegas; @@ -514,7 +513,7 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { } bool use2ndOrderIntegration = false; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = + PreintegratedImuMeasurements preintegrated = evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs, use2ndOrderIntegration); @@ -531,7 +530,7 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { Matrix Factual, Gactual; preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, body_P_sensor, Factual, Gactual); + newDeltaT, Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F @@ -619,7 +618,6 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Linearization point imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor = Pose3(); // (Rot3::Expmap(Vector3(0,0.1,0.1)), Point3(1, 0, 1)); // Measurements list measuredAccs, measuredOmegas; @@ -638,7 +636,7 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { } bool use2ndOrderIntegration = true; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements preintegrated = + PreintegratedImuMeasurements preintegrated = evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs, use2ndOrderIntegration); @@ -655,7 +653,7 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { Matrix Factual, Gactual; preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, body_P_sensor, Factual, Gactual); + newDeltaT, Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F @@ -760,7 +758,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), Point3(1, 0, 0)); - ImuFactor::PreintegratedMeasurements pre_int_data( + PreintegratedImuMeasurements pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); @@ -797,7 +795,7 @@ TEST(ImuFactor, PredictPositionAndVelocity) { Matrix I6x6(6, 6); I6x6 = Matrix::Identity(6, 6); - ImuFactor::PreintegratedMeasurements pre_int_data( + PreintegratedImuMeasurements pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); @@ -835,7 +833,7 @@ TEST(ImuFactor, PredictRotation) { Matrix I6x6(6, 6); I6x6 = Matrix::Identity(6, 6); - ImuFactor::PreintegratedMeasurements pre_int_data( + PreintegratedImuMeasurements pre_int_data( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); From a5d49a261e699069c355df3453665560b035f69e Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 18 Jul 2015 23:11:27 -0700 Subject: [PATCH 029/142] Fixed MATLAB wrapper (old-style interface still) --- gtsam.h | 57 +++++++++++++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/gtsam.h b/gtsam.h index 1ddae3f48..b382e9272 100644 --- a/gtsam.h +++ b/gtsam.h @@ -2481,18 +2481,18 @@ class ConstantBias { class PoseVelocityBias{ PoseVelocityBias(const gtsam::Pose3& pose, Vector velocity, const gtsam::imuBias::ConstantBias& bias); }; -class ImuFactorPreintegratedMeasurements { +class PreintegratedImuMeasurements { // Standard Constructor - ImuFactorPreintegratedMeasurements(const gtsam::imuBias::ConstantBias& bias, Matrix measuredAccCovariance,Matrix measuredOmegaCovariance, Matrix integrationErrorCovariance, bool use2ndOrderIntegration); - ImuFactorPreintegratedMeasurements(const gtsam::imuBias::ConstantBias& bias, Matrix measuredAccCovariance,Matrix measuredOmegaCovariance, Matrix integrationErrorCovariance); - // ImuFactorPreintegratedMeasurements(const gtsam::ImuFactorPreintegratedMeasurements& rhs); + PreintegratedImuMeasurements(const gtsam::imuBias::ConstantBias& bias, Matrix measuredAccCovariance,Matrix measuredOmegaCovariance, Matrix integrationErrorCovariance, bool use2ndOrderIntegration); + PreintegratedImuMeasurements(const gtsam::imuBias::ConstantBias& bias, Matrix measuredAccCovariance,Matrix measuredOmegaCovariance, Matrix integrationErrorCovariance); + // PreintegratedImuMeasurements(const gtsam::PreintegratedImuMeasurements& rhs); // Testable void print(string s) const; - bool equals(const gtsam::ImuFactorPreintegratedMeasurements& expected, double tol); + bool equals(const gtsam::PreintegratedImuMeasurements& expected, double tol); double deltaTij() const; - Matrix deltaRij() const; + gtsam::Rot3 deltaRij() const; Vector deltaPij() const; Vector deltaVij() const; Vector biasHatVector() const; @@ -2505,25 +2505,24 @@ class ImuFactorPreintegratedMeasurements { // Standard Interface void integrateMeasurement(Vector measuredAcc, Vector measuredOmega, double deltaT); - void integrateMeasurement(Vector measuredAcc, Vector measuredOmega, double deltaT, const gtsam::Pose3& body_P_sensor); gtsam::PoseVelocityBias predict(const gtsam::Pose3& pose_i, Vector vel_i, const gtsam::imuBias::ConstantBias& bias, Vector gravity, Vector omegaCoriolis) const; }; virtual class ImuFactor : gtsam::NonlinearFactor { ImuFactor(size_t pose_i, size_t vel_i, size_t pose_j, size_t vel_j, size_t bias, - const gtsam::ImuFactorPreintegratedMeasurements& preintegratedMeasurements, Vector gravity, Vector omegaCoriolis); + const gtsam::PreintegratedImuMeasurements& preintegratedMeasurements, Vector gravity, Vector omegaCoriolis); ImuFactor(size_t pose_i, size_t vel_i, size_t pose_j, size_t vel_j, size_t bias, - const gtsam::ImuFactorPreintegratedMeasurements& preintegratedMeasurements, Vector gravity, Vector omegaCoriolis, + const gtsam::PreintegratedImuMeasurements& preintegratedMeasurements, Vector gravity, Vector omegaCoriolis, const gtsam::Pose3& body_P_sensor); // Standard Interface - gtsam::ImuFactorPreintegratedMeasurements preintegratedMeasurements() const; + gtsam::PreintegratedImuMeasurements preintegratedMeasurements() const; }; #include -class CombinedImuFactorPreintegratedMeasurements { +class PreintegratedCombinedMeasurements { // Standard Constructor - CombinedImuFactorPreintegratedMeasurements( + PreintegratedCombinedMeasurements( const gtsam::imuBias::ConstantBias& bias, Matrix measuredAccCovariance, Matrix measuredOmegaCovariance, @@ -2531,7 +2530,7 @@ class CombinedImuFactorPreintegratedMeasurements { Matrix biasAccCovariance, Matrix biasOmegaCovariance, Matrix biasAccOmegaInit); - CombinedImuFactorPreintegratedMeasurements( + PreintegratedCombinedMeasurements( const gtsam::imuBias::ConstantBias& bias, Matrix measuredAccCovariance, Matrix measuredOmegaCovariance, @@ -2540,14 +2539,14 @@ class CombinedImuFactorPreintegratedMeasurements { Matrix biasOmegaCovariance, Matrix biasAccOmegaInit, bool use2ndOrderIntegration); - // CombinedImuFactorPreintegratedMeasurements(const gtsam::CombinedImuFactorPreintegratedMeasurements& rhs); + // PreintegratedCombinedMeasurements(const gtsam::PreintegratedCombinedMeasurements& rhs); // Testable void print(string s) const; - bool equals(const gtsam::CombinedImuFactorPreintegratedMeasurements& expected, double tol); + bool equals(const gtsam::PreintegratedCombinedMeasurements& expected, double tol); double deltaTij() const; - Matrix deltaRij() const; + gtsam::Rot3 deltaRij() const; Vector deltaPij() const; Vector deltaVij() const; Vector biasHatVector() const; @@ -2560,53 +2559,51 @@ class CombinedImuFactorPreintegratedMeasurements { // Standard Interface void integrateMeasurement(Vector measuredAcc, Vector measuredOmega, double deltaT); - void integrateMeasurement(Vector measuredAcc, Vector measuredOmega, double deltaT, const gtsam::Pose3& body_P_sensor); gtsam::PoseVelocityBias predict(const gtsam::Pose3& pose_i, Vector vel_i, const gtsam::imuBias::ConstantBias& bias, Vector gravity, Vector omegaCoriolis) const; }; virtual class CombinedImuFactor : gtsam::NonlinearFactor { CombinedImuFactor(size_t pose_i, size_t vel_i, size_t pose_j, size_t vel_j, size_t bias_i, size_t bias_j, - const gtsam::CombinedImuFactorPreintegratedMeasurements& CombinedPreintegratedMeasurements, Vector gravity, Vector omegaCoriolis); + const gtsam::PreintegratedCombinedMeasurements& CombinedPreintegratedMeasurements, Vector gravity, Vector omegaCoriolis); // Standard Interface - gtsam::CombinedImuFactorPreintegratedMeasurements preintegratedMeasurements() const; + gtsam::PreintegratedCombinedMeasurements preintegratedMeasurements() const; }; #include -class AHRSFactorPreintegratedMeasurements { +class PreintegratedAhrsMeasurements { // Standard Constructor - AHRSFactorPreintegratedMeasurements(Vector bias, Matrix measuredOmegaCovariance); - AHRSFactorPreintegratedMeasurements(Vector bias, Matrix measuredOmegaCovariance); - AHRSFactorPreintegratedMeasurements(const gtsam::AHRSFactorPreintegratedMeasurements& rhs); + PreintegratedAhrsMeasurements(Vector bias, Matrix measuredOmegaCovariance); + PreintegratedAhrsMeasurements(Vector bias, Matrix measuredOmegaCovariance); + PreintegratedAhrsMeasurements(const gtsam::PreintegratedAhrsMeasurements& rhs); // Testable void print(string s) const; - bool equals(const gtsam::AHRSFactorPreintegratedMeasurements& expected, double tol); + bool equals(const gtsam::PreintegratedAhrsMeasurements& expected, double tol); // get Data - Matrix deltaRij() const; + gtsam::Rot3 deltaRij() const; double deltaTij() const; Vector biasHat() const; // Standard Interface void integrateMeasurement(Vector measuredOmega, double deltaT); - void integrateMeasurement(Vector measuredOmega, double deltaT, const gtsam::Pose3& body_P_sensor); void resetIntegration() ; }; virtual class AHRSFactor : gtsam::NonlinearFactor { AHRSFactor(size_t rot_i, size_t rot_j,size_t bias, - const gtsam::AHRSFactorPreintegratedMeasurements& preintegratedMeasurements, Vector omegaCoriolis); + const gtsam::PreintegratedAhrsMeasurements& preintegratedMeasurements, Vector omegaCoriolis); AHRSFactor(size_t rot_i, size_t rot_j, size_t bias, - const gtsam::AHRSFactorPreintegratedMeasurements& preintegratedMeasurements, Vector omegaCoriolis, + const gtsam::PreintegratedAhrsMeasurements& preintegratedMeasurements, Vector omegaCoriolis, const gtsam::Pose3& body_P_sensor); // Standard Interface - gtsam::AHRSFactorPreintegratedMeasurements preintegratedMeasurements() const; + gtsam::PreintegratedAhrsMeasurements preintegratedMeasurements() const; Vector evaluateError(const gtsam::Rot3& rot_i, const gtsam::Rot3& rot_j, Vector bias) const; gtsam::Rot3 predict(const gtsam::Rot3& rot_i, Vector bias, - const gtsam::AHRSFactorPreintegratedMeasurements& preintegratedMeasurements, + const gtsam::PreintegratedAhrsMeasurements& preintegratedMeasurements, Vector omegaCoriolis) const; }; From 272a56302260c530476db7f4c16b957a361b2f31 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 00:39:47 -0700 Subject: [PATCH 030/142] expmap/logmap derivatives --- gtsam/base/Lie.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/gtsam/base/Lie.h b/gtsam/base/Lie.h index 36370b4f5..036b961e8 100644 --- a/gtsam/base/Lie.h +++ b/gtsam/base/Lie.h @@ -83,6 +83,25 @@ struct LieGroup { return Class::Logmap(between(g)); } + Class expmap(const TangentVector& v, // + ChartJacobian H1, ChartJacobian H2 = boost::none) const { + Jacobian D_g_v; + Class g = Class::Expmap(v,H2 ? &D_g_v : 0); + Class h = compose(g,H1,H2); + if (H2) *H2 = (*H2) * D_g_v; + return h; + } + + TangentVector logmap(const Class& g, // + ChartJacobian H1, ChartJacobian H2 = boost::none) const { + Class h = between(g,H1,H2); + Jacobian D_v_h; + TangentVector v = Class::Logmap(h, (H1 || H2) ? &D_v_h : 0); + if (H1) *H1 = D_v_h * (*H1); + if (H2) *H2 = D_v_h * (*H2); + return v; + } + Class retract(const TangentVector& v) const { return compose(Class::ChartAtOrigin::Retract(v)); } From c72a8344ec1dbeccb78f06c61078753dbfbfd376 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 00:40:07 -0700 Subject: [PATCH 031/142] Added derivatives where they should be... --- gtsam/navigation/PreintegratedRotation.h | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index a1276b91c..724d6661f 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -126,9 +126,13 @@ class PreintegratedRotation { delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - D_Rincr_integratedOmega * deltaT; } - /// Return a bias corrected version of the integrated rotation - expensive - Rot3 biascorrectedDeltaRij(const Vector3& biasOmegaIncr) const { - return deltaRij_ * Rot3::Expmap(delRdelBiasOmega_ * biasOmegaIncr); + /// Return a bias corrected version of the integrated rotation, with optional Jacobian + Rot3 biascorrectedDeltaRij(const Vector3& biasOmegaIncr, + OptionalJacobian<3, 3> H = boost::none) const { + const Vector3 biasInducedOmega = delRdelBiasOmega_ * biasOmegaIncr; + const Rot3 deltaRij_biascorrected = deltaRij_.expmap(biasInducedOmega, boost::none, H); + if (H) (*H) *= delRdelBiasOmega_; + return deltaRij_biascorrected; } /// Get so<3> version of bias corrected rotation, with optional Jacobian @@ -136,18 +140,16 @@ class PreintegratedRotation { Vector3 biascorrectedThetaRij(const Vector3& biasOmegaIncr, OptionalJacobian<3, 3> H = boost::none) const { // First, we correct deltaRij using the biasOmegaIncr, rotated - const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasOmegaIncr); + Matrix3 D_biascorrected_biasOmegaIncr; + const Rot3 biascorrected = biascorrectedDeltaRij(biasOmegaIncr, + H ? &D_biascorrected_biasOmegaIncr : 0); - if (H) { - Matrix3 Jrinv_theta_bc; - // This was done via an expmap, now we go *back* to so<3> - const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected, Jrinv_theta_bc); - const Matrix3 Jr_JbiasOmegaIncr = Rot3::ExpmapDerivative(delRdelBiasOmega_ * biasOmegaIncr); - (*H) = Jrinv_theta_bc * Jr_JbiasOmegaIncr * delRdelBiasOmega_; - return biascorrectedOmega; - } - // else - return Rot3::Logmap(deltaRij_biascorrected); + // This was done via an expmap, now we go *back* to so<3> + Matrix3 D_omega_biascorrected; + const Vector3 omega = Rot3::Logmap(biascorrected, H ? &D_omega_biascorrected : 0); + + if (H) (*H) = D_omega_biascorrected * D_biascorrected_biasOmegaIncr; + return omega; } /// Integrate coriolis correction in body frame rot_i From 814b8c22bfa04604d9e91f9ba486a40acddae77b Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 00:40:42 -0700 Subject: [PATCH 032/142] Moved a derivative down --- gtsam/navigation/AHRSFactor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gtsam/navigation/AHRSFactor.cpp b/gtsam/navigation/AHRSFactor.cpp index 92ec0dd9b..3e5654427 100644 --- a/gtsam/navigation/AHRSFactor.cpp +++ b/gtsam/navigation/AHRSFactor.cpp @@ -143,7 +143,6 @@ Vector AHRSFactor::evaluateError(const Rot3& Ri, const Rot3& Rj, // Coriolis term const Vector3 coriolis = _PIM_.integrateCoriolis(Ri); - const Matrix3 coriolisHat = skewSymmetric(coriolis); const Vector3 correctedOmega = biascorrectedOmega - coriolis; // Prediction @@ -161,7 +160,7 @@ Vector AHRSFactor::evaluateError(const Rot3& Ri, const Rot3& Rj, if (H1) { // dfR/dRi H1->resize(3, 3); - Matrix3 D_coriolis = -D_cDeltaRij_cOmega * coriolisHat; + Matrix3 D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis); (*H1) << D_fR_fRrot * (-actualRij.transpose() - fRrot.transpose() * D_coriolis); } From 3c59168406177e6675627c5006a1b061bbf7115e Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 00:54:25 -0700 Subject: [PATCH 033/142] Inlined Logmap and derivatives, removed from PreintegratedRotation --- gtsam/navigation/AHRSFactor.cpp | 6 +++++- gtsam/navigation/PreintegratedRotation.h | 17 ----------------- gtsam/navigation/PreintegrationBase.cpp | 12 ++++++------ 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/gtsam/navigation/AHRSFactor.cpp b/gtsam/navigation/AHRSFactor.cpp index 3e5654427..814b020b1 100644 --- a/gtsam/navigation/AHRSFactor.cpp +++ b/gtsam/navigation/AHRSFactor.cpp @@ -83,7 +83,11 @@ void PreintegratedAhrsMeasurements::integrateMeasurement( Vector3 PreintegratedAhrsMeasurements::predict(const Vector3& bias, OptionalJacobian<3,3> H) const { const Vector3 biasOmegaIncr = bias - biasHat_; - return biascorrectedThetaRij(biasOmegaIncr, H); + const Rot3 biascorrected = biascorrectedDeltaRij(biasOmegaIncr, H); + Matrix3 D_omega_biascorrected; + const Vector3 omega = Rot3::Logmap(biascorrected, H ? &D_omega_biascorrected : 0); + if (H) (*H) = D_omega_biascorrected * (*H); + return omega; } //------------------------------------------------------------------------------ Vector PreintegratedAhrsMeasurements::DeltaAngles( diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index 724d6661f..0475e52e2 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -135,23 +135,6 @@ class PreintegratedRotation { return deltaRij_biascorrected; } - /// Get so<3> version of bias corrected rotation, with optional Jacobian - // Implements: log( deltaRij_ * expmap(delRdelBiasOmega_ * biasOmegaIncr) ) - Vector3 biascorrectedThetaRij(const Vector3& biasOmegaIncr, - OptionalJacobian<3, 3> H = boost::none) const { - // First, we correct deltaRij using the biasOmegaIncr, rotated - Matrix3 D_biascorrected_biasOmegaIncr; - const Rot3 biascorrected = biascorrectedDeltaRij(biasOmegaIncr, - H ? &D_biascorrected_biasOmegaIncr : 0); - - // This was done via an expmap, now we go *back* to so<3> - Matrix3 D_omega_biascorrected; - const Vector3 omega = Rot3::Logmap(biascorrected, H ? &D_omega_biascorrected : 0); - - if (H) (*H) = D_omega_biascorrected * D_biascorrected_biasOmegaIncr; - return omega; - } - /// Integrate coriolis correction in body frame rot_i Vector3 integrateCoriolis(const Rot3& rot_i) const { if (!p_->omegaCoriolis) return Vector3::Zero(); diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index bef45e044..0f2d96bac 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -193,7 +193,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Evaluate residual error, according to [3] /* ---------------------------------------------------------------------------------------------------- */ const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij(biasIncr.gyroscope()); + Matrix3 D_biascorrected_biasIncr; + const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij( + biasIncr.gyroscope(), H5 ? &D_biascorrected_biasIncr : 0); const Vector3 deltaPij_biascorrected = biascorrectedDeltaPij(biasIncr); const Vector3 deltaVij_biascorrected = biascorrectedDeltaVij(biasIncr); PoseVelocityBias predictedState_j = @@ -212,10 +214,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Get Get so<3> version of bias corrected rotation // If H5 is asked for, we will need the Jacobian, which we store in H5 // H5 will then be corrected below to take into account the Coriolis effect - // TODO(frank): biascorrectedThetaRij calculates deltaRij_biascorrected, which we already have - Matrix3 D_cThetaRij_biasOmegaIncr; - const Vector3 biascorrectedOmega = biascorrectedThetaRij(biasIncr.gyroscope(), - H5 ? &D_cThetaRij_biasOmegaIncr : 0); + Matrix3 D_omega_biascorrected; + const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected, H5 ? &D_omega_biascorrected : 0); // Coriolis term, NOTE inconsistent with AHRS, where coriolisHat is *after* integration // TODO(frank): move derivatives to predict and do coriolis branching there @@ -272,7 +272,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const } if (H5) { // H5 by this point already contains 3*3 biascorrectedThetaRij derivative - const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_cThetaRij_biasOmegaIncr; + const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_omega_biascorrected * D_biascorrected_biasIncr; (*H5) << Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega), // dfR/dBias -delPdelBiasAcc(), -delPdelBiasOmega(), // dfP/dBias From 042d874f082ba024f13cf35a5677d360279b812d Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 01:52:08 -0700 Subject: [PATCH 034/142] Introduce and use NavState for predict --- gtsam/navigation/PreintegrationBase.cpp | 39 +++++++++++----------- gtsam/navigation/PreintegrationBase.h | 43 ++++++++++++++++++------- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 0f2d96bac..fcf932bf9 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -129,13 +129,15 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( } } -/// Predict state at time j //------------------------------------------------------------------------------ -PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, - const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, +NavState PreintegrationBase::predict(const NavState& state_i, + const Rot3& deltaRij_biascorrected, + const Vector3& deltaPij_biascorrected, const Vector3& deltaVij_biascorrected) const { + const Pose3& pose_i = state_i.pose(); + const Vector3& vel_i = state_i.velocity(); + const double dt = deltaTij(), dt2 = dt * dt; const Matrix3 Ri = pose_i.rotation().matrix(); @@ -158,21 +160,17 @@ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, // TODO(frank): pose update below is separate expmap for R,t. Is that kosher? const Pose3 pose_j = Pose3(pose_i.rotation().expmap(dR), pose_i.translation() + Point3(dP)); - return PoseVelocityBias(pose_j, vel_i + dV, bias_i); // bias is predicted as a constant + return NavState(pose_j, vel_i + dV); } -/// Predict state at time j //------------------------------------------------------------------------------ -PoseVelocityBias PreintegrationBase::predict( - const Pose3& pose_i, const Vector3& vel_i, +NavState PreintegrationBase::predict(const NavState& state_i, const imuBias::ConstantBias& bias_i) const { const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - return predict( - pose_i, vel_i, bias_i, biascorrectedDeltaRij(biasIncr.gyroscope()), + return predict(state_i, biascorrectedDeltaRij(biasIncr.gyroscope()), biascorrectedDeltaPij(biasIncr), biascorrectedDeltaVij(biasIncr)); } -/// Compute errors w.r.t. preintegrated measurements and Jacobians wrpt pose_i, vel_i, bias_i, pose_j, bias_j //------------------------------------------------------------------------------ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, @@ -190,25 +188,28 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Rot3& rot_j = pose_j.rotation(); const Vector3 pos_j = pose_j.translation().vector(); - // Evaluate residual error, according to [3] - /* ---------------------------------------------------------------------------------------------------- */ + /// Compute bias-corrected quantities const imuBias::ConstantBias biasIncr = bias_i - biasHat_; Matrix3 D_biascorrected_biasIncr; const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij( biasIncr.gyroscope(), H5 ? &D_biascorrected_biasIncr : 0); const Vector3 deltaPij_biascorrected = biascorrectedDeltaPij(biasIncr); const Vector3 deltaVij_biascorrected = biascorrectedDeltaVij(biasIncr); - PoseVelocityBias predictedState_j = - predict(pose_i, vel_i, bias_i, deltaRij_biascorrected, + + /// Predict state at time j + NavState predictedState_j = + predict(NavState(pose_i, vel_i), deltaRij_biascorrected, deltaPij_biascorrected, deltaVij_biascorrected); + // Evaluate residual error, according to [3] // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fp = Ri.transpose() * (pos_j - predictedState_j.pose.translation().vector()); + const Vector3 fp = Ri.transpose() * (pos_j - predictedState_j.pose().translation().vector()); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fv = Ri.transpose() * (vel_j - predictedState_j.velocity); + const Vector3 fv = Ri.transpose() * (vel_j - predictedState_j.velocity()); - // fR will be computed later. Note: it is the same as: fR = (predictedState_j.pose.translation()).between(Rot_j) + // fR will be computed later. + // Note: it is the same as: fR = predictedState_j.pose.rotation().between(Rot_j) /* ---------------------------------------------------------------------------------------------------- */ // Get Get so<3> version of bias corrected rotation @@ -293,6 +294,6 @@ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& q->omegaCoriolis = omegaCoriolis; q->use2ndOrderCoriolis = use2ndOrderCoriolis; p_ = q; - return predict(pose_i, vel_i, bias_i); + return PoseVelocityBias(predict(NavState(pose_i, vel_i), bias_i), bias_i); } } /// namespace gtsam diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index c810eb538..5ef076420 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -25,21 +25,44 @@ #include #include #include +#include #include namespace gtsam { +/// Velocity in 3D is just a Vector3 +typedef Vector3 Velocity3; + /** - * Struct to hold all state variables of returned by Predict function + * Navigation state: Pose (rotation, translation) + velocity */ +class NavState: private ProductLieGroup { +protected: + typedef ProductLieGroup Base; + typedef OptionalJacobian<9, 9> ChartJacobian; + +public: + // constructors + NavState() {} + NavState(const Pose3& pose, const Velocity3& vel) : Base(pose, vel) {} + + // access + const Pose3& pose() const { return first; } + const Point3& translation() const { return pose().translation(); } + const Rot3& rotation() const { return pose().rotation(); } + const Velocity3& velocity() const { return second; } +}; + +/// @deprecated struct PoseVelocityBias { Pose3 pose; Vector3 velocity; imuBias::ConstantBias bias; - - PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, - const imuBias::ConstantBias _bias) + PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, const imuBias::ConstantBias _bias) : pose(_pose), velocity(_velocity), bias(_bias) {} + PoseVelocityBias(const NavState& navState, const imuBias::ConstantBias _bias) + : pose(navState.pose()), velocity(navState.velocity()), bias(_bias) {} + NavState navState() const { return NavState(pose,velocity);} }; /** @@ -161,15 +184,13 @@ class PreintegrationBase : public PreintegratedRotation { } /// Predict state at time j, with bias-corrected quantities given - PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i, - const Rot3& deltaRij_biascorrected, - const Vector3& deltaPij_biascorrected, - const Vector3& deltaVij_biascorrected) const; + NavState predict(const NavState& navState, const Rot3& deltaRij_biascorrected, + const Vector3& deltaPij_biascorrected, + const Vector3& deltaVij_biascorrected) const; /// Predict state at time j - PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i) const; + NavState predict(const NavState& state_i, + const imuBias::ConstantBias& bias_i) const; /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, From 7b02a01a443a522bd36861668ebbc55bf232a228 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 01:52:23 -0700 Subject: [PATCH 035/142] Simplified deprecated methods --- gtsam/navigation/CombinedImuFactor.cpp | 14 ++++---------- gtsam/navigation/CombinedImuFactor.h | 2 +- gtsam/navigation/ImuFactor.cpp | 14 ++++---------- gtsam/navigation/ImuFactor.h | 2 +- 4 files changed, 10 insertions(+), 22 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index c1ec7f361..620305d77 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -277,19 +277,13 @@ CombinedImuFactor::CombinedImuFactor( void CombinedImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const CombinedPreintegratedMeasurements& pim, + CombinedPreintegratedMeasurements& pim, const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { - // NOTE(frank): hack below only for backward compatibility - boost::shared_ptr p = - boost::make_shared(pim.p()); - p->gravity = gravity; - p->omegaCoriolis = omegaCoriolis; - p->use2ndOrderCoriolis = use2ndOrderCoriolis; - CombinedPreintegratedMeasurements newPim = pim; - newPim.p_ = p; - PoseVelocityBias pvb = newPim.predict(pose_i, vel_i, bias_i); + // use deprecated predict + PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, gravity, + omegaCoriolis, use2ndOrderCoriolis); pose_j = pvb.pose; vel_j = pvb.velocity; } diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index a70d1d24f..5641b4c3e 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -245,7 +245,7 @@ public: // @deprecated use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const CombinedPreintegratedMeasurements& pim, + CombinedPreintegratedMeasurements& pim, const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 024bdd65e..8f7e28385 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -195,18 +195,12 @@ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, void ImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const PreintegratedMeasurements& pim, + PreintegratedMeasurements& pim, const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { - // NOTE(frank): hack below only for backward compatibility - boost::shared_ptr p = - boost::make_shared(pim.p()); - p->gravity = gravity; - p->omegaCoriolis = omegaCoriolis; - p->use2ndOrderCoriolis = use2ndOrderCoriolis; - PreintegratedMeasurements newPim = pim; - newPim.p_ = p; - PoseVelocityBias pvb = newPim.predict(pose_i, vel_i, bias_i); + // use deprecated predict + PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, gravity, + omegaCoriolis, use2ndOrderCoriolis); pose_j = pvb.pose; vel_j = pvb.velocity; } diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index eb94675fa..9cd1fcada 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -212,7 +212,7 @@ public: /// @deprecated use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - const PreintegratedMeasurements& pim, const Vector3& gravity, + PreintegratedMeasurements& pim, const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: From 93826414459c87713d3062909b08237857bb2df8 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 01:52:53 -0700 Subject: [PATCH 036/142] Use constants, slight renaming --- .../tests/testCombinedImuFactor.cpp | 75 +++++++------------ gtsam/navigation/tests/testImuFactor.cpp | 58 +++++++------- 2 files changed, 54 insertions(+), 79 deletions(-) diff --git a/gtsam/navigation/tests/testCombinedImuFactor.cpp b/gtsam/navigation/tests/testCombinedImuFactor.cpp index a6703272c..87d3f4385 100644 --- a/gtsam/navigation/tests/testCombinedImuFactor.cpp +++ b/gtsam/navigation/tests/testCombinedImuFactor.cpp @@ -88,8 +88,8 @@ CombinedImuFactor::CombinedPreintegratedMeasurements evaluatePreintegratedMeasur const imuBias::ConstantBias& bias, const list& measuredAccs, const list& measuredOmegas, const list& deltaTs) { CombinedImuFactor::CombinedPreintegratedMeasurements result(bias, - Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), - Matrix3::Identity(), Matrix3::Identity(), Matrix::Identity(6, 6), false); + I_3x3, I_3x3, I_3x3, + I_3x3, I_3x3, I_6x6, false); list::const_iterator itAcc = measuredAccs.begin(); list::const_iterator itOmega = measuredOmegas.begin(); @@ -136,13 +136,13 @@ TEST( CombinedImuFactor, PreintegratedMeasurements ) { double tol = 1e-6; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements expected1(bias, Matrix3::Zero(), - Matrix3::Zero(), Matrix3::Zero()); + ImuFactor::PreintegratedMeasurements expected1(bias, Z_3x3, + Z_3x3, Z_3x3); expected1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); CombinedImuFactor::CombinedPreintegratedMeasurements actual1(bias, - Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero(), - Matrix3::Zero(), Matrix::Zero(6, 6)); + Z_3x3, Z_3x3, Z_3x3, Z_3x3, + Z_3x3, Z_6x6); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); @@ -174,37 +174,28 @@ TEST( CombinedImuFactor, ErrorWithBiases ) { double deltaT = 1.0; double tol = 1e-6; - Matrix I6x6(6, 6); - I6x6 = Matrix::Identity(6, 6); - - ImuFactor::PreintegratedMeasurements pre_int_data( + ImuFactor::PreintegratedMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), - Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity()); + I_3x3, I_3x3, I_3x3); - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - CombinedImuFactor::CombinedPreintegratedMeasurements combined_pre_int_data( + CombinedImuFactor::CombinedPreintegratedMeasurements combined_pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), - Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), - Matrix3::Identity(), 2 * Matrix3::Identity(), I6x6); + I_3x3, I_3x3, I_3x3, I_3x3, 2 * I_3x3, I_6x6); - combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, - deltaT); + combined_pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor imuFactor(X(1), V(1), X(2), V(2), B(1), pre_int_data, gravity, - omegaCoriolis); + ImuFactor imuFactor(X(1), V(1), X(2), V(2), B(1), pim, gravity, omegaCoriolis); noiseModel::Gaussian::shared_ptr Combinedmodel = - noiseModel::Gaussian::Covariance(combined_pre_int_data.preintMeasCov()); + noiseModel::Gaussian::Covariance(combined_pim.preintMeasCov()); CombinedImuFactor combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - combined_pre_int_data, gravity, omegaCoriolis); + combined_pim, gravity, omegaCoriolis); Vector errorExpected = imuFactor.evaluateError(x1, v1, x2, v2, bias); - - Vector errorActual = combinedfactor.evaluateError(x1, v1, x2, v2, bias, - bias2); - + Vector errorActual = combinedfactor.evaluateError(x1, v1, x2, v2, bias, bias2); EXPECT(assert_equal(errorExpected, errorActual.head(9), tol)); // Expected Jacobians @@ -301,27 +292,23 @@ TEST(CombinedImuFactor, PredictPositionAndVelocity) { measuredAcc << 0, 1.1, -9.81; double deltaT = 0.001; - Matrix I6x6(6, 6); - I6x6 = Matrix::Identity(6, 6); - - CombinedImuFactor::CombinedPreintegratedMeasurements combined_pre_int_data( - bias, Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), - Matrix3::Identity(), 2 * Matrix3::Identity(), I6x6, true); + CombinedImuFactor::CombinedPreintegratedMeasurements pim(bias, I_3x3, I_3x3, + I_3x3, I_3x3, 2 * I_3x3, I_6x6, true); for (int i = 0; i < 1000; ++i) - combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor noiseModel::Gaussian::shared_ptr combinedmodel = - noiseModel::Gaussian::Covariance(combined_pre_int_data.preintMeasCov()); + noiseModel::Gaussian::Covariance(pim.preintMeasCov()); CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - combined_pre_int_data, gravity, omegaCoriolis); + pim, gravity, omegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocityBias = combined_pre_int_data.predict(x1, v1, + PoseVelocityBias poseVelocityBias = pim.predict(x1, v1, bias, gravity, omegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; @@ -334,10 +321,8 @@ TEST(CombinedImuFactor, PredictPositionAndVelocity) { /* ************************************************************************* */ TEST(CombinedImuFactor, PredictRotation) { imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) - Matrix I6x6(6, 6); - CombinedImuFactor::CombinedPreintegratedMeasurements combined_pre_int_data( - bias, Matrix3::Identity(), Matrix3::Identity(), Matrix3::Identity(), - Matrix3::Identity(), 2 * Matrix3::Identity(), I6x6, true); + CombinedImuFactor::CombinedPreintegratedMeasurements pim(bias, I_3x3, I_3x3, + I_3x3, I_3x3, 2 * I_3x3, I_6x6, true); Vector3 measuredAcc; measuredAcc << 0, 0, -9.81; Vector3 gravity; @@ -349,16 +334,14 @@ TEST(CombinedImuFactor, PredictRotation) { double deltaT = 0.001; double tol = 1e-4; for (int i = 0; i < 1000; ++i) - combined_pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, - deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - combined_pre_int_data, gravity, omegaCoriolis); + pim, gravity, omegaCoriolis); // Predict Pose3 x(Rot3().ypr(0, 0, 0), Point3(0, 0, 0)), x2; Vector3 v(0, 0, 0), v2; - CombinedImuFactor::Predict(x, v, x2, v2, bias, - Combinedfactor.preintegratedMeasurements(), gravity, omegaCoriolis); + CombinedImuFactor::Predict(x, v, x2, v2, bias, pim, gravity, omegaCoriolis); Pose3 expectedPose(Rot3().ypr(M_PI / 10, 0, 0), Point3(0, 0, 0)); EXPECT(assert_equal(expectedPose, x2, tol)); } @@ -489,7 +472,7 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { // Compute expected G wrt bias random walk noise (15,6) Matrix df_rwBias(15, 6); // random walk on the bias does not appear in the first 9 entries df_rwBias.setZero(); - df_rwBias.block<6, 6>(9, 0) = eye(6); + df_rwBias.block<6, 6>(9, 0) = I_6x6; // Compute expected G wrt gyro noise (15,6) Matrix df_dinitBias = numericalDerivative11( @@ -502,7 +485,7 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), bias_old); - df_dinitBias.block<6, 6>(9, 0) = Matrix::Zero(6, 6); // only has to influence first 9 rows + df_dinitBias.block<6, 6>(9, 0) = Z_6x6; // only has to influence first 9 rows Matrix Gexpected(15, 21); Gexpected << df_dintNoise, df_daccNoise, df_domegaNoise, df_rwBias, df_dinitBias; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index bffc62d90..df0739c27 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -83,9 +83,9 @@ Rot3 updatePreintegratedRot(const Rot3& deltaRij_old, double accNoiseVar = 0.01; double omegaNoiseVar = 0.03; double intNoiseVar = 0.0001; -const Matrix3 kMeasuredAccCovariance = accNoiseVar * Matrix3::Identity(); -const Matrix3 kMeasuredOmegaCovariance = omegaNoiseVar * Matrix3::Identity(); -const Matrix3 kIntegrationErrorCovariance = intNoiseVar * Matrix3::Identity(); +const Matrix3 kMeasuredAccCovariance = accNoiseVar * I_3x3; +const Matrix3 kMeasuredOmegaCovariance = omegaNoiseVar * I_3x3; +const Matrix3 kIntegrationErrorCovariance = intNoiseVar * I_3x3; // Auxiliary functions to test preintegrated Jacobians // delPdelBiasAcc_ delPdelBiasOmega_ delVdelBiasAcc_ delVdelBiasOmega_ delRdelBiasOmega_ @@ -211,13 +211,13 @@ double deltaT = 1.0; TEST(ImuFactor, ErrorAndJacobians) { using namespace common; bool use2ndOrderIntegration = true; - PreintegratedImuMeasurements pre_int_data(bias, + PreintegratedImuMeasurements pim(bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, use2ndOrderIntegration); - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kZeroOmegaCoriolis); // Expected error @@ -263,7 +263,7 @@ TEST(ImuFactor, ErrorAndJacobians) { EXPECT(assert_equal(errorExpected, factor.unwhitenedError(values), 1e-6)); // Make sure the whitening is done correctly - Matrix cov = pre_int_data.preintMeasCov(); + Matrix cov = pim.preintMeasCov(); Matrix R = RtR(cov.inverse()); Vector whitened = R * errorExpected; EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values), 1e-6)); @@ -290,14 +290,14 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - PreintegratedImuMeasurements pre_int_data( + PreintegratedImuMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, nonZeroOmegaCoriolis); Values values; @@ -330,16 +330,16 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - PreintegratedImuMeasurements pre_int_data( + PreintegratedImuMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor Pose3 bodyPsensor = Pose3(); bool use2ndOrderCoriolis = true; - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, nonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis); Values values; @@ -422,7 +422,7 @@ TEST(ImuFactor, fistOrderExponential) { Rot3::Expmap((measuredOmega - biasOmega) * deltaT).matrix(); const Matrix3 actualRot = hatRot * Rot3::Expmap(delRdelBiasOmega * deltabiasOmega).matrix(); - // hatRot * (Matrix3::Identity() + skewSymmetric(delRdelBiasOmega * deltabiasOmega)); + // hatRot * (I_3x3 + skewSymmetric(delRdelBiasOmega * deltabiasOmega)); // This is a first order expansion so the equality is only an approximation EXPECT(assert_equal(expectedRot, actualRot)); @@ -758,15 +758,15 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), Point3(1, 0, 0)); - PreintegratedImuMeasurements pre_int_data( + PreintegratedImuMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, nonZeroOmegaCoriolis); Values values; @@ -792,25 +792,22 @@ TEST(ImuFactor, PredictPositionAndVelocity) { measuredAcc << 0, 1, -9.81; double deltaT = 0.001; - Matrix I6x6(6, 6); - I6x6 = Matrix::Identity(6, 6); - - PreintegratedImuMeasurements pre_int_data( + PreintegratedImuMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); for (int i = 0; i < 1000; ++i) - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kZeroOmegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, kGravity, + PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias, kGravity, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; @@ -830,27 +827,22 @@ TEST(ImuFactor, PredictRotation) { measuredAcc << 0, 0, -9.81; double deltaT = 0.001; - Matrix I6x6(6, 6); - I6x6 = Matrix::Identity(6, 6); - - PreintegratedImuMeasurements pre_int_data( + PreintegratedImuMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); for (int i = 0; i < 1000; ++i) - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, - kZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kZeroOmegaCoriolis); // Predict Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, factor.preintegratedMeasurements(), - kGravity, kZeroOmegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravity, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3().ypr(M_PI / 10, 0, 0), Point3(0, 0, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 0, 0; From f32a7cbd008063f9f1edf856a26caa6651fa2004 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 02:54:28 -0700 Subject: [PATCH 037/142] Parsed out tangent space operations --- gtsam/navigation/PreintegrationBase.cpp | 76 +++++++++++++++++-------- gtsam/navigation/PreintegrationBase.h | 8 +++ 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index fcf932bf9..e39200cea 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -129,38 +129,68 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( } } +static Eigen::Block dR(Vector9& v) { return v.segment<3>(0); } +static Eigen::Block dP(Vector9& v) { return v.segment<3>(3); } +static Eigen::Block dV(Vector9& v) { return v.segment<3>(6); } + //------------------------------------------------------------------------------ -NavState PreintegrationBase::predict(const NavState& state_i, +Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { + Vector9 result = Vector9::Zero(); + if (p().omegaCoriolis) { + const Pose3& pose_i = state_i.pose(); + const Vector3& vel_i = state_i.velocity(); + const Matrix3 Ri = pose_i.rotation().matrix(); + const double dt = deltaTij(), dt2 = dt * dt; + + const Vector3& omegaCoriolis = *p().omegaCoriolis; + dR(result) -= Ri.transpose() * omegaCoriolis * dt; + dP(result) -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper + dV(result) -= 2 * omegaCoriolis.cross(vel_i) * dt; + if (p().use2ndOrderCoriolis) { + Vector3 temp = omegaCoriolis.cross( + omegaCoriolis.cross(pose_i.translation().vector())); + dP(result) -= 0.5 * temp * dt2; + dV(result) -= temp * dt; + } + } + return result; +} + +//------------------------------------------------------------------------------ +Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, const Vector3& deltaVij_biascorrected) const { const Pose3& pose_i = state_i.pose(); const Vector3& vel_i = state_i.velocity(); - - const double dt = deltaTij(), dt2 = dt * dt; const Matrix3 Ri = pose_i.rotation().matrix(); + const double dt = deltaTij(), dt2 = dt * dt; // Rotation, translation, and velocity: - Vector3 dR = Rot3::Logmap(deltaRij_biascorrected); - Vector3 dP = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * p().gravity * dt2; - Vector3 dV = Ri * deltaVij_biascorrected + p().gravity * dt; + Vector9 delta; + dR(delta) = Rot3::Logmap(deltaRij_biascorrected); + dP(delta) = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * p().gravity * dt2; + dV(delta) = Ri * deltaVij_biascorrected + p().gravity * dt; - if (p().omegaCoriolis) { - const Vector3& omegaCoriolis = *p().omegaCoriolis; - dR -= Ri.transpose() * omegaCoriolis * dt; // Coriolis term - dP -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper - dV -= 2 * omegaCoriolis.cross(vel_i) * dt; - if (p().use2ndOrderCoriolis) { - Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(pose_i.translation().vector())); - dP -= 0.5 * temp * dt2; - dV -= temp * dt; - } - } + if (p().omegaCoriolis) delta += integrateCoriolis(state_i); + return delta; +} + +//------------------------------------------------------------------------------ +NavState PreintegrationBase::predict(const NavState& state_i, + const Rot3& deltaRij_biascorrected, + const Vector3& deltaPij_biascorrected, + const Vector3& deltaVij_biascorrected) const { + + Vector9 delta = recombinedPrediction(state_i, deltaRij_biascorrected, + deltaPij_biascorrected, deltaVij_biascorrected); // TODO(frank): pose update below is separate expmap for R,t. Is that kosher? - const Pose3 pose_j = Pose3(pose_i.rotation().expmap(dR), pose_i.translation() + Point3(dP)); - return NavState(pose_j, vel_i + dV); + const Pose3& pose_i = state_i.pose(); + const Vector3& vel_i = state_i.velocity(); + const Pose3 pose_j = Pose3(pose_i.rotation().expmap(dR(delta)), pose_i.translation() + Point3(dP(delta))); + return NavState(pose_j, vel_i + dV(delta)); } //------------------------------------------------------------------------------ @@ -197,9 +227,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Vector3 deltaVij_biascorrected = biascorrectedDeltaVij(biasIncr); /// Predict state at time j - NavState predictedState_j = - predict(NavState(pose_i, vel_i), deltaRij_biascorrected, - deltaPij_biascorrected, deltaVij_biascorrected); + NavState state_i(pose_i, vel_i); + NavState predictedState_j = predict(state_i, deltaRij_biascorrected, + deltaPij_biascorrected, deltaVij_biascorrected); // Evaluate residual error, according to [3] // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance @@ -220,7 +250,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Coriolis term, NOTE inconsistent with AHRS, where coriolisHat is *after* integration // TODO(frank): move derivatives to predict and do coriolis branching there - const Vector3 coriolis = integrateCoriolis(rot_i); + const Vector3 coriolis = PreintegratedRotation::integrateCoriolis(rot_i); const Vector3 correctedOmega = biascorrectedOmega - coriolis; // Residual rotation error diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 5ef076420..01df67c69 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -183,6 +183,14 @@ class PreintegrationBase : public PreintegratedRotation { + delVdelBiasOmega_ * biasIncr.gyroscope(); } + /// Integrate coriolis correction in body frame state_i + Vector9 integrateCoriolis(const NavState& state_i) const; + + /// Recombine the preintegration, gravity, and coriolis in a single NavState tangent vector + Vector9 recombinedPrediction(const NavState& state_i, + const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, + const Vector3& deltaVij_biascorrected) const; + /// Predict state at time j, with bias-corrected quantities given NavState predict(const NavState& navState, const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, From d4d99c390d38538ffc1581c262b50391bbdddf5e Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 03:31:11 -0700 Subject: [PATCH 038/142] A custom retract does the trick --- gtsam/navigation/PreintegrationBase.cpp | 27 +++++++------------ gtsam/navigation/PreintegrationBase.h | 36 ++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index e39200cea..281e1e176 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -129,10 +129,6 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( } } -static Eigen::Block dR(Vector9& v) { return v.segment<3>(0); } -static Eigen::Block dP(Vector9& v) { return v.segment<3>(3); } -static Eigen::Block dV(Vector9& v) { return v.segment<3>(6); } - //------------------------------------------------------------------------------ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { Vector9 result = Vector9::Zero(); @@ -143,14 +139,14 @@ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { const double dt = deltaTij(), dt2 = dt * dt; const Vector3& omegaCoriolis = *p().omegaCoriolis; - dR(result) -= Ri.transpose() * omegaCoriolis * dt; - dP(result) -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper - dV(result) -= 2 * omegaCoriolis.cross(vel_i) * dt; + NavState::dR(result) -= Ri.transpose() * omegaCoriolis * dt; + NavState::dP(result) -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper + NavState::dV(result) -= 2 * omegaCoriolis.cross(vel_i) * dt; if (p().use2ndOrderCoriolis) { Vector3 temp = omegaCoriolis.cross( omegaCoriolis.cross(pose_i.translation().vector())); - dP(result) -= 0.5 * temp * dt2; - dV(result) -= temp * dt; + NavState::dP(result) -= 0.5 * temp * dt2; + NavState::dV(result) -= temp * dt; } } return result; @@ -169,9 +165,9 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, // Rotation, translation, and velocity: Vector9 delta; - dR(delta) = Rot3::Logmap(deltaRij_biascorrected); - dP(delta) = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * p().gravity * dt2; - dV(delta) = Ri * deltaVij_biascorrected + p().gravity * dt; + NavState::dR(delta) = Rot3::Logmap(deltaRij_biascorrected); + NavState::dP(delta) = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * p().gravity * dt2; + NavState::dV(delta) = Ri * deltaVij_biascorrected + p().gravity * dt; if (p().omegaCoriolis) delta += integrateCoriolis(state_i); return delta; @@ -186,11 +182,7 @@ NavState PreintegrationBase::predict(const NavState& state_i, Vector9 delta = recombinedPrediction(state_i, deltaRij_biascorrected, deltaPij_biascorrected, deltaVij_biascorrected); - // TODO(frank): pose update below is separate expmap for R,t. Is that kosher? - const Pose3& pose_i = state_i.pose(); - const Vector3& vel_i = state_i.velocity(); - const Pose3 pose_j = Pose3(pose_i.rotation().expmap(dR(delta)), pose_i.translation() + Point3(dP(delta))); - return NavState(pose_j, vel_i + dV(delta)); + return state_i.retract(delta); } //------------------------------------------------------------------------------ @@ -309,6 +301,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const -delPdelBiasAcc(), -delPdelBiasOmega(), // dfP/dBias -delVdelBiasAcc(), -delVdelBiasOmega(); // dfV/dBias } + // TODO(frank): Vector9 r = state_i.localCoordinates(predictedState_j); does not work ??? Vector9 r; r << fR, fp, fv; return r; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 01df67c69..1226eaa6c 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -36,23 +36,57 @@ typedef Vector3 Velocity3; /** * Navigation state: Pose (rotation, translation) + velocity */ -class NavState: private ProductLieGroup { +class NavState: public ProductLieGroup { protected: typedef ProductLieGroup Base; typedef OptionalJacobian<9, 9> ChartJacobian; + using Base::first; + using Base::second; public: // constructors NavState() {} NavState(const Pose3& pose, const Velocity3& vel) : Base(pose, vel) {} + NavState(const Rot3& rot, const Point3& t, const Velocity3& vel): Base(Pose3(rot, t), vel) {} + NavState(const Base& product) : Base(product) {} // access const Pose3& pose() const { return first; } const Point3& translation() const { return pose().translation(); } const Rot3& rotation() const { return pose().rotation(); } const Velocity3& velocity() const { return second; } + + /// @name Manifold + /// @{ + + // NavState tangent space sugar. + static Eigen::Block dR(Vector9& v) { return v.segment<3>(0); } + static Eigen::Block dP(Vector9& v) { return v.segment<3>(3); } + static Eigen::Block dV(Vector9& v) { return v.segment<3>(6); } + + // Specialize Retract/Local that agrees with IMUFactors + // TODO(frank): This is a very specific retract. Talk to Luca about implications. + NavState retract(Vector9& v, // + ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { + if (H1||H2) throw std::runtime_error("NavState::retract derivatives not implemented yet"); + return NavState(rotation().expmap(dR(v)), translation() + Point3(dP(v)), velocity() + dV(v)); + } + Vector9 localCoordinates(const NavState& g, // + ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { + if (H1||H2) throw std::runtime_error("NavState::localCoordinates derivatives not implemented yet"); + Vector9 v; + dR(v) = rotation().logmap(g.rotation()); + dP(v) = (g.translation() - translation()).vector(); + dV(v) = g.velocity() - velocity(); + return v; + } + /// @} }; +// Specialize NavState traits to use a Retract/Local that agrees with IMUFactors +template<> +struct traits : internal::LieGroupTraits {}; + /// @deprecated struct PoseVelocityBias { Pose3 pose; From 13cca7be005b26c3c74dcafb92ff47d33a1189e6 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 21:23:16 -0700 Subject: [PATCH 039/142] MakeParams --- gtsam/navigation/ImuFactor.cpp | 18 +++++++++++++++--- gtsam/navigation/ImuFactor.h | 9 ++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 8f7e28385..b34cf1f19 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -31,6 +31,20 @@ using namespace std; //------------------------------------------------------------------------------ // Inner class PreintegratedMeasurements //------------------------------------------------------------------------------ +boost::shared_ptr PreintegratedImuMeasurements::MakeParams( + const Matrix3& measuredAccCovariance, + const Matrix3& measuredOmegaCovariance, + const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration, + bool use2ndOrderCoriolis) { + boost::shared_ptr p = boost::make_shared(); + p->accelerometerCovariance = measuredAccCovariance; + p->gyroscopeCovariance = measuredOmegaCovariance; + p->integrationCovariance = integrationErrorCovariance; + p->use2ndOrderIntegration = use2ndOrderIntegration; + p->use2ndOrderCoriolis = use2ndOrderCoriolis; + return p; +} +//------------------------------------------------------------------------------ void PreintegratedImuMeasurements::print(const string& s) const { PreintegrationBase::print(s); } @@ -116,9 +130,7 @@ void PreintegratedImuMeasurements::integrateMeasurement( PreintegratedImuMeasurements::PreintegratedImuMeasurements( const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, - const bool use2ndOrderIntegration) - { + const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration) { biasHat_ = biasHat; boost::shared_ptr p = boost::make_shared(); p->gyroscopeCovariance = measuredOmegaCovariance; diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 9cd1fcada..1ef7e5679 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -50,6 +50,13 @@ protected: public: + /// Construct parameters + static boost::shared_ptr MakeParams( + const Matrix3& measuredAccCovariance, + const Matrix3& measuredOmegaCovariance, + const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration = + false, bool use2ndOrderCoriolis = false); + /** * Constructor, initializes the class with no measurements * @param bias Current estimate of acceleration and rotation rate biases @@ -91,7 +98,7 @@ public: const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, const Matrix3& integrationErrorCovariance, - const bool use2ndOrderIntegration = false); + bool use2ndOrderIntegration = false); private: From 1229ca6feea00ec026f7501da8c32bf19b064d55 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 21:24:17 -0700 Subject: [PATCH 040/142] OptionalJacobians --- gtsam/navigation/ImuBias.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/gtsam/navigation/ImuBias.h b/gtsam/navigation/ImuBias.h index 571fb7249..047f24e8f 100644 --- a/gtsam/navigation/ImuBias.h +++ b/gtsam/navigation/ImuBias.h @@ -78,20 +78,18 @@ public: /** Correct an accelerometer measurement using this bias model, and optionally compute Jacobians */ Vector3 correctAccelerometer(const Vector3& measurement, - boost::optional H = boost::none) const { + OptionalJacobian<3, 6> H = boost::none) const { if (H) { - H->resize(3, 6); - (*H) << -Matrix3::Identity(), Matrix3::Zero(); + (*H) << -I_3x3, Z_3x3; } return measurement - biasAcc_; } /** Correct a gyroscope measurement using this bias model, and optionally compute Jacobians */ Vector3 correctGyroscope(const Vector3& measurement, - boost::optional H = boost::none) const { + OptionalJacobian<3, 6> H = boost::none) const { if (H) { - H->resize(3, 6); - (*H) << Matrix3::Zero(), -Matrix3::Identity(); + (*H) << Z_3x3, -I_3x3; } return measurement - biasGyro_; } From 76abf553b0fb786eec5fce303b1a3ae02c12a772 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 21:24:38 -0700 Subject: [PATCH 041/142] biasCorrectedDelta and test of its derivatives --- gtsam/navigation/PreintegrationBase.cpp | 88 +++++++++++++----------- gtsam/navigation/PreintegrationBase.h | 26 +++---- gtsam/navigation/tests/testImuFactor.cpp | 19 +++++ 3 files changed, 73 insertions(+), 60 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 281e1e176..6c3cf1607 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -129,6 +129,31 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( } } +//------------------------------------------------------------------------------ +Vector9 PreintegrationBase::biasCorrectedDelta( + const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H) const { + const imuBias::ConstantBias biasIncr = bias_i - biasHat_; + Matrix3 D_deltaRij_bias; + Rot3 deltaRij = PreintegratedRotation::biascorrectedDeltaRij( + biasIncr.gyroscope(), H ? &D_deltaRij_bias : 0); + + Vector9 delta; + Matrix3 D_dR_deltaRij; + NavState::dR(delta) = Rot3::Logmap(deltaRij, H ? &D_dR_deltaRij : 0); + NavState::dP(delta) = deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() + + delPdelBiasOmega_ * biasIncr.gyroscope(); + NavState::dV(delta) = deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() + + delVdelBiasOmega_ * biasIncr.gyroscope(); + if (H) { + Matrix36 D_dR_bias, D_dP_bias, D_dV_bias; + D_dR_bias << Z_3x3, D_dR_deltaRij * D_deltaRij_bias; + D_dP_bias << delPdelBiasAcc_, delPdelBiasOmega_; + D_dV_bias << delVdelBiasAcc_, delVdelBiasOmega_; + (*H) << D_dR_bias, D_dP_bias, D_dV_bias; + } + return delta; +} + //------------------------------------------------------------------------------ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { Vector9 result = Vector9::Zero(); @@ -154,9 +179,8 @@ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { //------------------------------------------------------------------------------ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, - const Rot3& deltaRij_biascorrected, - const Vector3& deltaPij_biascorrected, - const Vector3& deltaVij_biascorrected) const { + Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1, + OptionalJacobian<9, 9> H2) const { const Pose3& pose_i = state_i.pose(); const Vector3& vel_i = state_i.velocity(); @@ -165,9 +189,9 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, // Rotation, translation, and velocity: Vector9 delta; - NavState::dR(delta) = Rot3::Logmap(deltaRij_biascorrected); - NavState::dP(delta) = Ri * deltaPij_biascorrected + vel_i * dt + 0.5 * p().gravity * dt2; - NavState::dV(delta) = Ri * deltaVij_biascorrected + p().gravity * dt; + NavState::dR(delta) = NavState::dR(biasCorrectedDelta); + NavState::dP(delta) = Ri * NavState::dP(biasCorrectedDelta) + vel_i * dt + 0.5 * p().gravity * dt2; + NavState::dV(delta) = Ri * NavState::dV(biasCorrectedDelta) + p().gravity * dt; if (p().omegaCoriolis) delta += integrateCoriolis(state_i); return delta; @@ -175,24 +199,15 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, //------------------------------------------------------------------------------ NavState PreintegrationBase::predict(const NavState& state_i, - const Rot3& deltaRij_biascorrected, - const Vector3& deltaPij_biascorrected, - const Vector3& deltaVij_biascorrected) const { - - Vector9 delta = recombinedPrediction(state_i, deltaRij_biascorrected, - deltaPij_biascorrected, deltaVij_biascorrected); - + const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1, + OptionalJacobian<9, 6> H2) const { + Vector9 biasCorrected = biasCorrectedDelta(bias_i, H2); + Matrix9 D_delta_biasCorrected; + Vector9 delta = recombinedPrediction(state_i, biasCorrected, H1, H2 ? &D_delta_biasCorrected : 0); + if (H2) *H2 = D_delta_biasCorrected * (*H2); return state_i.retract(delta); } -//------------------------------------------------------------------------------ -NavState PreintegrationBase::predict(const NavState& state_i, - const imuBias::ConstantBias& bias_i) const { - const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - return predict(state_i, biascorrectedDeltaRij(biasIncr.gyroscope()), - biascorrectedDeltaPij(biasIncr), biascorrectedDeltaVij(biasIncr)); -} - //------------------------------------------------------------------------------ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, @@ -210,18 +225,15 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Rot3& rot_j = pose_j.rotation(); const Vector3 pos_j = pose_j.translation().vector(); - /// Compute bias-corrected quantities - const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - Matrix3 D_biascorrected_biasIncr; - const Rot3 deltaRij_biascorrected = biascorrectedDeltaRij( - biasIncr.gyroscope(), H5 ? &D_biascorrected_biasIncr : 0); - const Vector3 deltaPij_biascorrected = biascorrectedDeltaPij(biasIncr); - const Vector3 deltaVij_biascorrected = biascorrectedDeltaVij(biasIncr); + // Compute bias-corrected quantities + // TODO(frank): now redundant with biasCorrected below + Matrix96 D_biasCorrected_bias; + Vector9 biasCorrected = biasCorrectedDelta(bias_i, D_biasCorrected_bias); /// Predict state at time j NavState state_i(pose_i, vel_i); - NavState predictedState_j = predict(state_i, deltaRij_biascorrected, - deltaPij_biascorrected, deltaVij_biascorrected); + Vector9 delta = recombinedPrediction(state_i, biasCorrected); + NavState predictedState_j = state_i.retract(delta); // Evaluate residual error, according to [3] // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance @@ -233,17 +245,10 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // fR will be computed later. // Note: it is the same as: fR = predictedState_j.pose.rotation().between(Rot_j) - /* ---------------------------------------------------------------------------------------------------- */ - // Get Get so<3> version of bias corrected rotation - // If H5 is asked for, we will need the Jacobian, which we store in H5 - // H5 will then be corrected below to take into account the Coriolis effect - Matrix3 D_omega_biascorrected; - const Vector3 biascorrectedOmega = Rot3::Logmap(deltaRij_biascorrected, H5 ? &D_omega_biascorrected : 0); - // Coriolis term, NOTE inconsistent with AHRS, where coriolisHat is *after* integration // TODO(frank): move derivatives to predict and do coriolis branching there const Vector3 coriolis = PreintegratedRotation::integrateCoriolis(rot_i); - const Vector3 correctedOmega = biascorrectedOmega - coriolis; + const Vector3 correctedOmega = NavState::dR(biasCorrected) - coriolis; // Residual rotation error Matrix3 D_cDeltaRij_cOmega; @@ -270,9 +275,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const (*H1) << D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi Z_3x3, // dfR/dPi - skewSymmetric(fp + deltaPij_biascorrected), // dfP/dRi + skewSymmetric(fp + NavState::dP(biasCorrected)), // dfP/dRi dfPdPi, // dfP/dPi - skewSymmetric(fv + deltaVij_biascorrected), // dfV/dRi + skewSymmetric(fv + NavState::dV(biasCorrected)), // dfV/dRi dfVdPi; // dfV/dPi } if (H2) { @@ -294,8 +299,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Ri.transpose(); // dfV/dVj } if (H5) { - // H5 by this point already contains 3*3 biascorrectedThetaRij derivative - const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_omega_biascorrected * D_biascorrected_biasIncr; + const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_biasCorrected_bias.block<3,3>(0,3); (*H5) << Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega), // dfR/dBias -delPdelBiasAcc(), -delPdelBiasOmega(), // dfP/dBias diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 1226eaa6c..b2fa130be 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -207,32 +207,22 @@ class PreintegrationBase : public PreintegratedRotation { Vector3* correctedAcc, Vector3* correctedOmega); - Vector3 biascorrectedDeltaPij(const imuBias::ConstantBias& biasIncr) const { - return deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() - + delPdelBiasOmega_ * biasIncr.gyroscope(); - } - - Vector3 biascorrectedDeltaVij(const imuBias::ConstantBias& biasIncr) const { - return deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() - + delVdelBiasOmega_ * biasIncr.gyroscope(); - } + /// Given the estimate of the bias, return a NavState tangent vector + /// summarizing the preintegrated IMU measurements so far + Vector9 biasCorrectedDelta(const imuBias::ConstantBias& bias_i, + OptionalJacobian<9, 6> H = boost::none) const; /// Integrate coriolis correction in body frame state_i Vector9 integrateCoriolis(const NavState& state_i) const; /// Recombine the preintegration, gravity, and coriolis in a single NavState tangent vector Vector9 recombinedPrediction(const NavState& state_i, - const Rot3& deltaRij_biascorrected, const Vector3& deltaPij_biascorrected, - const Vector3& deltaVij_biascorrected) const; - - /// Predict state at time j, with bias-corrected quantities given - NavState predict(const NavState& navState, const Rot3& deltaRij_biascorrected, - const Vector3& deltaPij_biascorrected, - const Vector3& deltaVij_biascorrected) const; + Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1 = boost::none, + OptionalJacobian<9, 9> H2 = boost::none) const; /// Predict state at time j - NavState predict(const NavState& state_i, - const imuBias::ConstantBias& bias_i) const; + NavState predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, + OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 6> H2 = boost::none) const; /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index df0739c27..e1df186cb 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -207,6 +207,25 @@ Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector(); double deltaT = 1.0; } // namespace common +/* ************************************************************************* */ +TEST(ImuFactor, BiasCorrectedDelta) { + using namespace common; + boost::shared_ptr p = + PreintegratedImuMeasurements::MakeParams(kMeasuredAccCovariance, + kMeasuredOmegaCovariance, kIntegrationErrorCovariance); + PreintegratedImuMeasurements pim(p, bias); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + + Vector9 expected; // TODO(frank): taken from output. Should really verify. + expected << 0.0628318531, 0.0, 0.0, 4.905, -2.19885135, -8.20622494, 9.81, -4.13885394, -16.4774682; + Matrix96 actualH; + EXPECT(assert_equal(expected, pim.biasCorrectedDelta(bias,actualH), 1e-5)); + Matrix expectedH = numericalDerivative11( + boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, boost::none), bias); + EXPECT(assert_equal(expectedH, actualH)); +} + /* ************************************************************************* */ TEST(ImuFactor, ErrorAndJacobians) { using namespace common; From ee747604e794ba5c08856d86f238421061d80e2a Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 23:39:19 -0700 Subject: [PATCH 042/142] No more MakeParams --- gtsam/navigation/ImuFactor.cpp | 14 -------------- gtsam/navigation/ImuFactor.h | 7 ------- 2 files changed, 21 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index b34cf1f19..7d6b77d07 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -31,20 +31,6 @@ using namespace std; //------------------------------------------------------------------------------ // Inner class PreintegratedMeasurements //------------------------------------------------------------------------------ -boost::shared_ptr PreintegratedImuMeasurements::MakeParams( - const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration, - bool use2ndOrderCoriolis) { - boost::shared_ptr p = boost::make_shared(); - p->accelerometerCovariance = measuredAccCovariance; - p->gyroscopeCovariance = measuredOmegaCovariance; - p->integrationCovariance = integrationErrorCovariance; - p->use2ndOrderIntegration = use2ndOrderIntegration; - p->use2ndOrderCoriolis = use2ndOrderCoriolis; - return p; -} -//------------------------------------------------------------------------------ void PreintegratedImuMeasurements::print(const string& s) const { PreintegrationBase::print(s); } diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 1ef7e5679..c85b56872 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -50,13 +50,6 @@ protected: public: - /// Construct parameters - static boost::shared_ptr MakeParams( - const Matrix3& measuredAccCovariance, - const Matrix3& measuredOmegaCovariance, - const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration = - false, bool use2ndOrderCoriolis = false); - /** * Constructor, initializes the class with no measurements * @param bias Current estimate of acceleration and rotation rate biases From a56c6e728bccf63aad983f351686398cd93f7711 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 23:39:44 -0700 Subject: [PATCH 043/142] Small refactor --- gtsam/geometry/Rot3M.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gtsam/geometry/Rot3M.cpp b/gtsam/geometry/Rot3M.cpp index 18628aec3..324c05ae4 100644 --- a/gtsam/geometry/Rot3M.cpp +++ b/gtsam/geometry/Rot3M.cpp @@ -131,10 +131,8 @@ Matrix3 Rot3::transpose() const { /* ************************************************************************* */ Point3 Rot3::rotate(const Point3& p, OptionalJacobian<3,3> H1, OptionalJacobian<3,3> H2) const { - if (H1 || H2) { - if (H1) *H1 = rot_ * skewSymmetric(-p.x(), -p.y(), -p.z()); - if (H2) *H2 = rot_; - } + if (H1) *H1 = rot_ * skewSymmetric(-p.x(), -p.y(), -p.z()); + if (H2) *H2 = rot_; return Point3(rot_ * p.vector()); } From 5f6d3a600f59723057fa111093708dbd828d68cc Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 23:40:20 -0700 Subject: [PATCH 044/142] First order coriolis derivatives --- gtsam/navigation/PreintegrationBase.cpp | 68 ++++++++++++++++++++---- gtsam/navigation/PreintegrationBase.h | 10 ++-- gtsam/navigation/tests/testImuFactor.cpp | 58 ++++++++++++++++---- 3 files changed, 112 insertions(+), 24 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 6c3cf1607..e731249bd 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -155,8 +155,29 @@ Vector9 PreintegrationBase::biasCorrectedDelta( } //------------------------------------------------------------------------------ -Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { +static Vector3 rotate(const Matrix3& R, const Vector3& p, + OptionalJacobian<3, 3> H1 = boost::none, OptionalJacobian<3, 3> H2 = boost::none) { + if (H1) *H1 = R * skewSymmetric(-p.x(), -p.y(), -p.z()); + if (H2) *H2 = R; + return R * p; +} + +//------------------------------------------------------------------------------ +static Vector3 unrotate(const Matrix3& R, const Vector3& p, + OptionalJacobian<3, 3> H1 = boost::none, OptionalJacobian<3, 3> H2 = boost::none) { + const Matrix3 Rt = R.transpose(); + Vector3 q = Rt * p; + const double wx = q.x(), wy = q.y(), wz = q.z(); + if (H1) *H1 << 0.0, -wz, +wy, +wz, 0.0, -wx, -wy, +wx, 0.0; + if (H2) *H2 = Rt; + return q; +} + +//------------------------------------------------------------------------------ +Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, + OptionalJacobian<9, 9> H) const { Vector9 result = Vector9::Zero(); + if (H) H->setZero(); if (p().omegaCoriolis) { const Pose3& pose_i = state_i.pose(); const Vector3& vel_i = state_i.velocity(); @@ -164,22 +185,29 @@ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i) const { const double dt = deltaTij(), dt2 = dt * dt; const Vector3& omegaCoriolis = *p().omegaCoriolis; - NavState::dR(result) -= Ri.transpose() * omegaCoriolis * dt; + Matrix3 D_dP_Ri; + NavState::dR(result) -= unrotate(Ri, omegaCoriolis * dt, H ? &D_dP_Ri : 0); NavState::dP(result) -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper - NavState::dV(result) -= 2 * omegaCoriolis.cross(vel_i) * dt; + NavState::dV(result) -= 2.0 * omegaCoriolis.cross(vel_i) * dt; if (p().use2ndOrderCoriolis) { Vector3 temp = omegaCoriolis.cross( omegaCoriolis.cross(pose_i.translation().vector())); NavState::dP(result) -= 0.5 * temp * dt2; NavState::dV(result) -= temp * dt; } + if (H) { + const Matrix3 omegaCoriolisHat = skewSymmetric(omegaCoriolis); + H->block<3,3>(0,0) = -D_dP_Ri; + H->block<3,3>(3,6) = - omegaCoriolisHat * dt2; + H->block<3,3>(6,6) = - 2.0 * omegaCoriolisHat * dt; + } } return result; } //------------------------------------------------------------------------------ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, - Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1, + const Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { const Pose3& pose_i = state_i.pose(); @@ -189,11 +217,29 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, // Rotation, translation, and velocity: Vector9 delta; + Matrix3 D_dP_Ri, D_dP_bc, D_dV_Ri, D_dV_bc; NavState::dR(delta) = NavState::dR(biasCorrectedDelta); - NavState::dP(delta) = Ri * NavState::dP(biasCorrectedDelta) + vel_i * dt + 0.5 * p().gravity * dt2; - NavState::dV(delta) = Ri * NavState::dV(biasCorrectedDelta) + p().gravity * dt; + NavState::dP(delta) = rotate(Ri, NavState::dP(biasCorrectedDelta), D_dP_Ri, D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; + NavState::dV(delta) = rotate(Ri, NavState::dV(biasCorrectedDelta), D_dV_Ri, D_dV_bc) + p().gravity * dt; + + Matrix9 Hcoriolis; + if (p().omegaCoriolis) { + delta += integrateCoriolis(state_i, H1 ? &Hcoriolis : 0); + } + if (H1) { + H1->setZero(); + H1->block<3,3>(3,0) = D_dP_Ri; + H1->block<3,3>(3,6) = I_3x3 * dt; + H1->block<3,3>(6,0) = D_dV_Ri; + if (p().omegaCoriolis) *H1 += Hcoriolis; + } + if (H2) { + H2->setZero(); + H2->block<3,3>(0,0) = I_3x3; + H2->block<3,3>(3,3) = Ri; + H2->block<3,3>(6,6) = Ri; + } - if (p().omegaCoriolis) delta += integrateCoriolis(state_i); return delta; } @@ -299,11 +345,11 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Ri.transpose(); // dfV/dVj } if (H5) { - const Matrix3 JbiasOmega = D_cDeltaRij_cOmega * D_biasCorrected_bias.block<3,3>(0,3); + const Matrix36 JbiasOmega = D_cDeltaRij_cOmega * D_biasCorrected_bias.middleRows<3>(0); (*H5) << - Z_3x3, D_fR_fRrot * (-fRrot.inverse().matrix() * JbiasOmega), // dfR/dBias - -delPdelBiasAcc(), -delPdelBiasOmega(), // dfP/dBias - -delVdelBiasAcc(), -delVdelBiasOmega(); // dfV/dBias + -D_fR_fRrot * fRrot.inverse().matrix() * JbiasOmega, // dfR/dBias + -D_biasCorrected_bias.middleRows<3>(3), // dfP/dBias + -D_biasCorrected_bias.middleRows<3>(6); // dfV/dBias } // TODO(frank): Vector9 r = state_i.localCoordinates(predictedState_j); does not work ??? Vector9 r; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index b2fa130be..2b61b95bb 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -63,10 +63,13 @@ public: static Eigen::Block dR(Vector9& v) { return v.segment<3>(0); } static Eigen::Block dP(Vector9& v) { return v.segment<3>(3); } static Eigen::Block dV(Vector9& v) { return v.segment<3>(6); } + static Eigen::Block dR(const Vector9& v) { return v.segment<3>(0); } + static Eigen::Block dP(const Vector9& v) { return v.segment<3>(3); } + static Eigen::Block dV(const Vector9& v) { return v.segment<3>(6); } // Specialize Retract/Local that agrees with IMUFactors // TODO(frank): This is a very specific retract. Talk to Luca about implications. - NavState retract(Vector9& v, // + NavState retract(const Vector9& v, // ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { if (H1||H2) throw std::runtime_error("NavState::retract derivatives not implemented yet"); return NavState(rotation().expmap(dR(v)), translation() + Point3(dP(v)), velocity() + dV(v)); @@ -213,11 +216,12 @@ class PreintegrationBase : public PreintegratedRotation { OptionalJacobian<9, 6> H = boost::none) const; /// Integrate coriolis correction in body frame state_i - Vector9 integrateCoriolis(const NavState& state_i) const; + Vector9 integrateCoriolis(const NavState& state_i, + OptionalJacobian<9, 9> H = boost::none) const; /// Recombine the preintegration, gravity, and coriolis in a single NavState tangent vector Vector9 recombinedPrediction(const NavState& state_i, - Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1 = boost::none, + const Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 9> H2 = boost::none) const; /// Predict state at time j diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index e1df186cb..cd64c2212 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -200,6 +200,7 @@ Vector3 v1(Vector3(0.5, 0.0, 0.0)); Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), Point3(5.5, 1.0, -50.0)); Vector3 v2(Vector3(0.5, 0.0, 0.0)); +NavState state1(x1, v1); // Measurements Vector3 measuredOmega(M_PI / 100, 0, 0); @@ -208,22 +209,59 @@ double deltaT = 1.0; } // namespace common /* ************************************************************************* */ -TEST(ImuFactor, BiasCorrectedDelta) { +TEST(ImuFactor, PreintegrationBaseMethods) { using namespace common; boost::shared_ptr p = - PreintegratedImuMeasurements::MakeParams(kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance); + boost::make_shared(); + p->gyroscopeCovariance = kMeasuredOmegaCovariance; + p->omegaCoriolis = Vector3(0.02, 0.03, 0.04); + p->accelerometerCovariance = kMeasuredAccCovariance; + p->integrationCovariance = kIntegrationErrorCovariance; + p->use2ndOrderIntegration = false; + p->use2ndOrderCoriolis = false; + PreintegratedImuMeasurements pim(p, bias); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - Vector9 expected; // TODO(frank): taken from output. Should really verify. - expected << 0.0628318531, 0.0, 0.0, 4.905, -2.19885135, -8.20622494, 9.81, -4.13885394, -16.4774682; - Matrix96 actualH; - EXPECT(assert_equal(expected, pim.biasCorrectedDelta(bias,actualH), 1e-5)); - Matrix expectedH = numericalDerivative11( - boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, boost::none), bias); - EXPECT(assert_equal(expectedH, actualH)); + { // biasCorrectedDelta + Vector9 expected; // TODO(frank): taken from output. Should really verify. + expected << 0.0628318531, 0.0, 0.0, 4.905, -2.19885135, -8.20622494, 9.81, -4.13885394, -16.4774682; + Matrix96 actualH; + EXPECT(assert_equal(expected, pim.biasCorrectedDelta(bias, actualH), 1e-5)); + Matrix expectedH = numericalDerivative11( + boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, + boost::none), bias); + EXPECT(assert_equal(expectedH, actualH)); + } + { + Vector9 expected; // TODO(frank): taken from output. Should really verify. + expected << -0.0212372436, -0.0407423986, -0.0974116854, 0.0, -0.08, 0.06, 0.0, -0.08, 0.06; + Matrix99 actualH; + EXPECT(assert_equal(expected, pim.integrateCoriolis(state1, actualH), 1e-5)); + Matrix expectedH = numericalDerivative11( + boost::bind(&PreintegrationBase::integrateCoriolis, pim, _1, + boost::none), state1); + EXPECT(assert_equal(expectedH, actualH)); + } + { + Vector9 expected; // TODO(frank): taken from output. Should really verify. + expected << 0.0415946095, -0.0407423986, -0.0974116854, 1, -0.08, 9.85, -0.187214027, 0.110178303, 0.0436304821; + Matrix99 actualH1, actualH2; + Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias); + EXPECT( + assert_equal(expected, + pim.recombinedPrediction(state1, biasCorrectedDelta, actualH1, + actualH2), 1e-5)); + Matrix expectedH1 = numericalDerivative11( + boost::bind(&PreintegrationBase::recombinedPrediction, pim, _1, + biasCorrectedDelta, boost::none, boost::none), state1); + EXPECT(assert_equal(expectedH1, actualH1)); + Matrix expectedH2 = numericalDerivative11( + boost::bind(&PreintegrationBase::recombinedPrediction, pim, state1, _1, + boost::none, boost::none), biasCorrectedDelta); + EXPECT(assert_equal(expectedH2, actualH2)); + } } /* ************************************************************************* */ From 2df20b4f375afab5cb51ee5637b0bcfb2e4cd6c6 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 19 Jul 2015 23:57:00 -0700 Subject: [PATCH 045/142] Second order coriolis done --- gtsam/navigation/PreintegrationBase.cpp | 15 ++++++++++----- gtsam/navigation/tests/testImuFactor.cpp | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index e731249bd..2c9b8b6ae 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -182,6 +182,7 @@ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, const Pose3& pose_i = state_i.pose(); const Vector3& vel_i = state_i.velocity(); const Matrix3 Ri = pose_i.rotation().matrix(); + const Vector3& t_i = state_i.translation().vector(); const double dt = deltaTij(), dt2 = dt * dt; const Vector3& omegaCoriolis = *p().omegaCoriolis; @@ -190,16 +191,20 @@ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, NavState::dP(result) -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper NavState::dV(result) -= 2.0 * omegaCoriolis.cross(vel_i) * dt; if (p().use2ndOrderCoriolis) { - Vector3 temp = omegaCoriolis.cross( - omegaCoriolis.cross(pose_i.translation().vector())); + Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(t_i)); NavState::dP(result) -= 0.5 * temp * dt2; NavState::dV(result) -= temp * dt; } if (H) { const Matrix3 omegaCoriolisHat = skewSymmetric(omegaCoriolis); - H->block<3,3>(0,0) = -D_dP_Ri; - H->block<3,3>(3,6) = - omegaCoriolisHat * dt2; - H->block<3,3>(6,6) = - 2.0 * omegaCoriolisHat * dt; + H->block<3,3>(0,0) -= D_dP_Ri; + H->block<3,3>(3,6) -= omegaCoriolisHat * dt2; + H->block<3,3>(6,6) -= 2.0 * omegaCoriolisHat * dt; + if (p().use2ndOrderCoriolis) { + const Matrix3 omegaCoriolisHat2 = omegaCoriolisHat * omegaCoriolisHat; + H->block<3,3>(3,3) -= 0.5 * omegaCoriolisHat2 * dt2; + H->block<3,3>(6,3) -= omegaCoriolisHat2 * dt; + } } } return result; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index cd64c2212..70b20ef00 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -218,7 +218,7 @@ TEST(ImuFactor, PreintegrationBaseMethods) { p->accelerometerCovariance = kMeasuredAccCovariance; p->integrationCovariance = kIntegrationErrorCovariance; p->use2ndOrderIntegration = false; - p->use2ndOrderCoriolis = false; + p->use2ndOrderCoriolis = true; PreintegratedImuMeasurements pim(p, bias); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); @@ -236,7 +236,7 @@ TEST(ImuFactor, PreintegrationBaseMethods) { } { Vector9 expected; // TODO(frank): taken from output. Should really verify. - expected << -0.0212372436, -0.0407423986, -0.0974116854, 0.0, -0.08, 0.06, 0.0, -0.08, 0.06; + expected << -0.0212372436, -0.0407423986, -0.0974116854, 0.1038, 0.038, -0.0804, 0.1038, 0.038, -0.0804; Matrix99 actualH; EXPECT(assert_equal(expected, pim.integrateCoriolis(state1, actualH), 1e-5)); Matrix expectedH = numericalDerivative11( @@ -246,7 +246,7 @@ TEST(ImuFactor, PreintegrationBaseMethods) { } { Vector9 expected; // TODO(frank): taken from output. Should really verify. - expected << 0.0415946095, -0.0407423986, -0.0974116854, 1, -0.08, 9.85, -0.187214027, 0.110178303, 0.0436304821; + expected << 0.0415946095, -0.0407423986, -0.0974116854, 1.1038, 0.038, 9.7096, -0.0834140265, 0.228178303, -0.0967695179; Matrix99 actualH1, actualH2; Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias); EXPECT( From 3a941a0ef40699c32360042f6cf755e1d3e03d79 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 20 Jul 2015 00:55:34 -0700 Subject: [PATCH 046/142] NavState retract derivatives --- gtsam/navigation/PreintegrationBase.h | 31 +++++++-- gtsam/navigation/tests/testImuFactor.cpp | 87 +++++++++++++++++++----- 2 files changed, 96 insertions(+), 22 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 2b61b95bb..65fafe79a 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -67,12 +67,23 @@ public: static Eigen::Block dP(const Vector9& v) { return v.segment<3>(3); } static Eigen::Block dV(const Vector9& v) { return v.segment<3>(6); } - // Specialize Retract/Local that agrees with IMUFactors + // Specialized Retract/Local that agrees with IMUFactors // TODO(frank): This is a very specific retract. Talk to Luca about implications. - NavState retract(const Vector9& v, // + NavState retract(const Vector9& xi, // ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { - if (H1||H2) throw std::runtime_error("NavState::retract derivatives not implemented yet"); - return NavState(rotation().expmap(dR(v)), translation() + Point3(dP(v)), velocity() + dV(v)); + Matrix3 H1R, H2R; + const Rot3 R = rotation().expmap(dR(xi), H1 ? &H1R : 0, H2 ? &H2R : 0); + const Point3 p = translation() + Point3(dP(xi)); + const Vector v = velocity() + dV(xi); + if (H1) { + H1->setIdentity(); + H1->topLeftCorner<3,3>() = H1R; + } + if (H2) { + H2->setIdentity(); + H2->topLeftCorner<3,3>() = H2R; + } + return NavState(R, p, v); } Vector9 localCoordinates(const NavState& g, // ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { @@ -88,7 +99,17 @@ public: // Specialize NavState traits to use a Retract/Local that agrees with IMUFactors template<> -struct traits : internal::LieGroupTraits {}; +struct traits : internal::LieGroupTraits { + static void Print(const NavState& m, const std::string& s = "") { + m.rotation().print(s+".R"); + m.translation().print(s+".P"); + print((Vector)m.velocity(),s+".V"); + } + static bool Equals(const NavState& m1, const NavState& m2, double tol = 1e-8) { + return m1.pose().equals(m2.pose(), tol) + && equal_with_abs_tol(m1.velocity(), m2.velocity(), tol); + } +}; /// @deprecated struct PoseVelocityBias { diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 70b20ef00..ad41b81a5 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -208,6 +208,55 @@ Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector(); double deltaT = 1.0; } // namespace common +/* ************************************************************************* */ +TEST( NavState, Lie ) { + // origin and zero deltas + NavState identity; + const double tol=1e-5; + Rot3 rot = Rot3::RzRyRx(0.1, 0.2, 0.3); + Point3 pt(1.0, 2.0, 3.0); + Velocity3 vel(0.4, 0.5, 0.6); + + EXPECT(assert_equal(identity, (NavState)identity.retract(zero(9)), tol)); + EXPECT(assert_equal(zero(9), identity.localCoordinates(identity), tol)); + + NavState state1(rot, pt, vel); + EXPECT(assert_equal(state1, (NavState)state1.retract(zero(9)), tol)); + EXPECT(assert_equal(zero(9), state1.localCoordinates(state1), tol)); + + // Special retract + Vector delta(9); + delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3; + Rot3 rot2 = rot.expmap(delta.head<3>()); + Point3 t2 = pt + Point3(delta.segment<3>(3)); + Velocity3 vel2 = vel + Velocity3(-0.1, -0.2, -0.3); + NavState state2(rot2, t2, vel2); + EXPECT(assert_equal(state2, (NavState)state1.retract(delta), tol)); + EXPECT(assert_equal(delta, state1.localCoordinates(state2), tol)); + + // roundtrip from state2 to state3 and back + NavState state3 = state2.retract(delta); + EXPECT(assert_equal(delta, state2.localCoordinates(state3), tol)); + + // roundtrip from state3 to state4 and back, with expmap. + NavState state4 = state3.expmap(delta); + EXPECT(assert_equal(delta, state3.logmap(state4), tol)); + + // For the expmap/logmap (not necessarily retract/local) -delta goes other way + EXPECT(assert_equal(state3, (NavState)state4.expmap(-delta), tol)); + EXPECT(assert_equal(delta, -state4.logmap(state3), tol)); + + // retract derivatives + Matrix9 aH1, aH2; + state1.retract(delta, aH1, aH2); + Matrix eH1 = numericalDerivative11( + boost::bind(&NavState::retract, _1, delta, boost::none, boost::none),state1); + EXPECT(assert_equal(eH1, aH1)); + Matrix eH2 = numericalDerivative11( + boost::bind(&NavState::retract, state1, _1, boost::none, boost::none), delta); + EXPECT(assert_equal(eH2, aH2)); +} + /* ************************************************************************* */ TEST(ImuFactor, PreintegrationBaseMethods) { using namespace common; @@ -225,42 +274,46 @@ TEST(ImuFactor, PreintegrationBaseMethods) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); { // biasCorrectedDelta - Vector9 expected; // TODO(frank): taken from output. Should really verify. - expected << 0.0628318531, 0.0, 0.0, 4.905, -2.19885135, -8.20622494, 9.81, -4.13885394, -16.4774682; Matrix96 actualH; - EXPECT(assert_equal(expected, pim.biasCorrectedDelta(bias, actualH), 1e-5)); + pim.biasCorrectedDelta(bias, actualH); Matrix expectedH = numericalDerivative11( boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, boost::none), bias); EXPECT(assert_equal(expectedH, actualH)); } { - Vector9 expected; // TODO(frank): taken from output. Should really verify. - expected << -0.0212372436, -0.0407423986, -0.0974116854, 0.1038, 0.038, -0.0804, 0.1038, 0.038, -0.0804; Matrix99 actualH; - EXPECT(assert_equal(expected, pim.integrateCoriolis(state1, actualH), 1e-5)); + pim.integrateCoriolis(state1, actualH); Matrix expectedH = numericalDerivative11( boost::bind(&PreintegrationBase::integrateCoriolis, pim, _1, boost::none), state1); EXPECT(assert_equal(expectedH, actualH)); } { - Vector9 expected; // TODO(frank): taken from output. Should really verify. - expected << 0.0415946095, -0.0407423986, -0.0974116854, 1.1038, 0.038, 9.7096, -0.0834140265, 0.228178303, -0.0967695179; - Matrix99 actualH1, actualH2; + Matrix99 aH1, aH2; Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias); - EXPECT( - assert_equal(expected, - pim.recombinedPrediction(state1, biasCorrectedDelta, actualH1, - actualH2), 1e-5)); - Matrix expectedH1 = numericalDerivative11( + pim.recombinedPrediction(state1, biasCorrectedDelta, aH1, aH2); + Matrix eH1 = numericalDerivative11( boost::bind(&PreintegrationBase::recombinedPrediction, pim, _1, biasCorrectedDelta, boost::none, boost::none), state1); - EXPECT(assert_equal(expectedH1, actualH1)); - Matrix expectedH2 = numericalDerivative11( + EXPECT(assert_equal(eH1, aH1)); + Matrix eH2 = numericalDerivative11( boost::bind(&PreintegrationBase::recombinedPrediction, pim, state1, _1, boost::none, boost::none), biasCorrectedDelta); - EXPECT(assert_equal(expectedH2, actualH2)); + EXPECT(assert_equal(eH2, aH2)); + } + { + Matrix99 aH1; + Matrix96 aH2; + pim.predict(state1, bias, aH1, aH2); + Matrix eH1 = numericalDerivative11( + boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none, + boost::none), state1); + EXPECT(assert_equal(eH1, aH1)); + Matrix eH2 = numericalDerivative11( + boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, + boost::none), bias); + EXPECT(assert_equal(eH2, aH2)); } } From 1ce6ab893d7aea655a21ae3add45dfbb2d997973 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 20 Jul 2015 00:58:41 -0700 Subject: [PATCH 047/142] predict derivative works ! --- gtsam/navigation/PreintegrationBase.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 2c9b8b6ae..e60b14b05 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -252,11 +252,19 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, NavState PreintegrationBase::predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 6> H2) const { - Vector9 biasCorrected = biasCorrectedDelta(bias_i, H2); - Matrix9 D_delta_biasCorrected; - Vector9 delta = recombinedPrediction(state_i, biasCorrected, H1, H2 ? &D_delta_biasCorrected : 0); - if (H2) *H2 = D_delta_biasCorrected * (*H2); - return state_i.retract(delta); + Matrix96 D_biasCorrected_bias; + Vector9 biasCorrected = biasCorrectedDelta(bias_i, + H2 ? &D_biasCorrected_bias : 0); + Matrix9 D_delta_state, D_delta_biasCorrected; + Vector9 delta = recombinedPrediction(state_i, biasCorrected, + H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0); + Matrix9 D_predict_state, D_predict_delta; + NavState state_j = state_i.retract(delta, D_predict_state, D_predict_delta); + if (H1) + *H1 = D_predict_state + D_predict_delta * D_delta_state; + if (H2) + *H2 = D_predict_delta * D_delta_biasCorrected * D_biasCorrected_bias; + return state_j; } //------------------------------------------------------------------------------ From 6496bd49ed26cb503e9bb20158939968aace6a40 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 20 Jul 2015 09:09:28 -0700 Subject: [PATCH 048/142] Separate and much simplified computeError works --- gtsam/navigation/PreintegrationBase.cpp | 46 ++++++++++++++++++++----- gtsam/navigation/PreintegrationBase.h | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index e60b14b05..28d2c7842 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -267,6 +267,36 @@ NavState PreintegrationBase::predict(const NavState& state_i, return state_j; } +//------------------------------------------------------------------------------ +// TODO(frank): this is *almost* state_j.localCoordinates(predict), +// except for the damn Ri.transpose. Ri is also the only way this depends on state_i. +// That is not an accident! Put R in computed covariances instead ? +static Vector9 computeError(const NavState& state_i, const NavState& state_j, + const NavState& predictedState_j) { + + const Rot3& rot_i = state_i.rotation(); + const Matrix Ri = rot_i.matrix(); + + // Residual rotation error + // TODO: this also seems to be flipped from localCoordinates + const Rot3 fRrot = predictedState_j.rotation().between(state_j.rotation()); + const Vector3 fR = Rot3::Logmap(fRrot); + + // Evaluate residual error, according to [3] + // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance + const Vector3 fp = Ri.transpose() + * (state_j.translation() - predictedState_j.translation()).vector(); + + // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance + const Vector3 fv = Ri.transpose() + * (state_j.velocity() - predictedState_j.velocity()); + + Vector9 r; + r << fR, fp, fv; + return r; + // return state_j.localCoordinates(predictedState_j); +} + //------------------------------------------------------------------------------ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, @@ -280,9 +310,11 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // we give some shorter name to rotations and translations const Rot3& rot_i = pose_i.rotation(); const Matrix Ri = rot_i.matrix(); + NavState state_i(pose_i, vel_i); const Rot3& rot_j = pose_j.rotation(); const Vector3 pos_j = pose_j.translation().vector(); + NavState state_j(pose_j, vel_j); // Compute bias-corrected quantities // TODO(frank): now redundant with biasCorrected below @@ -290,9 +322,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector9 biasCorrected = biasCorrectedDelta(bias_i, D_biasCorrected_bias); /// Predict state at time j - NavState state_i(pose_i, vel_i); - Vector9 delta = recombinedPrediction(state_i, biasCorrected); - NavState predictedState_j = state_i.retract(delta); + Matrix99 D_predict_state; + Matrix96 D_predict_bias; + NavState predictedState_j = predict(state_i, bias_i, D_predict_state, D_predict_bias); // Evaluate residual error, according to [3] // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance @@ -315,7 +347,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const const Rot3 RiBetweenRj = rot_i.between(rot_j); const Rot3 fRrot = correctedDeltaRij.between(RiBetweenRj); Matrix3 D_fR_fRrot; - const Vector3 fR = Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); + Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); const double dt = deltaTij(), dt2 = dt*dt; Matrix3 RitOmegaCoriolisHat = Z_3x3; @@ -364,10 +396,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const -D_biasCorrected_bias.middleRows<3>(3), // dfP/dBias -D_biasCorrected_bias.middleRows<3>(6); // dfV/dBias } - // TODO(frank): Vector9 r = state_i.localCoordinates(predictedState_j); does not work ??? - Vector9 r; - r << fR, fp, fv; - return r; + // TODO(frank): Do everything via derivatives of function below + return computeError(state_i, state_j, predictedState_j); } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 65fafe79a..e9a0b908e 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -68,7 +68,7 @@ public: static Eigen::Block dV(const Vector9& v) { return v.segment<3>(6); } // Specialized Retract/Local that agrees with IMUFactors - // TODO(frank): This is a very specific retract. Talk to Luca about implications. + // NOTE(frank): This also agrees with Pose3.retract NavState retract(const Vector9& xi, // ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { Matrix3 H1R, H2R; From 814c170caaa8de0473ffb689e965a537aa1043c9 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 20 Jul 2015 20:57:47 -0700 Subject: [PATCH 049/142] Added reference, made documentation consistent --- gtsam/navigation/CombinedImuFactor.h | 39 +++++++++++++++----------- gtsam/navigation/ImuFactor.h | 42 +++++++++++++++------------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 5641b4c3e..6bd2f7867 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -28,6 +28,25 @@ namespace gtsam { +/* + * If you are using the factor, please cite: + * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating + * conditionally independent sets in factor graphs: a unifying perspective based + * on smart factors, Int. Conf. on Robotics and Automation (ICRA), 2014. + * + * REFERENCES: + * [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", + * Volume 2, 2008. + * [2] T. Lupton and S.Sukkarieh, "Visual-Inertial-Aided Navigation for + * High-Dynamic Motion in Built Environments Without Initial Conditions", + * TRO, 28(1):61-76, 2012. + * [3] L. Carlone, S. Williams, R. Roberts, "Preintegrated IMU factor: + * Computation of the Jacobian Matrices", Tech. Report, 2013. + * [4] C. Forster, L. Carlone, F. Dellaert, D. Scaramuzza, IMU Preintegration on + * Manifold for Efficient Visual-Inertial Maximum-a-Posteriori Estimation, + * Robotics: Science and Systems (RSS), 2015. + */ + /** * PreintegratedCombinedMeasurements integrates the IMU measurements * (rotation rates and accelerations) and the corresponding covariance matrix. @@ -35,6 +54,8 @@ namespace gtsam { * is done incrementally (ideally, one integrates the measurement as soon as * it is received from the IMU) so as to avoid costly integration at time of * factor construction. + * + * @addtogroup SLAM */ class PreintegratedCombinedMeasurements : public PreintegrationBase { @@ -131,22 +152,6 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { }; /** - * @addtogroup SLAM - * - * If you are using the factor, please cite: - * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating - * conditionally independent sets in factor graphs: a unifying perspective based - * on smart factors, Int. Conf. on Robotics and Automation (ICRA), 2014. - * - ** REFERENCES: - * [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", - * Volume 2, 2008. - * [2] T. Lupton and S.Sukkarieh, "Visual-Inertial-Aided Navigation for - * High-Dynamic Motion in Built Environments Without Initial Conditions", - * TRO, 28(1):61-76, 2012. - * [3] L. Carlone, S. Williams, R. Roberts, "Preintegrated IMU factor: - * Computation of the Jacobian Matrices", Tech. Report, 2013. - * * CombinedImuFactor is a 6-ways factor involving previous state (pose and * velocity of the vehicle, as well as bias at previous time step), and current * state (pose, velocity, bias at current time step). Following the pre- @@ -162,6 +167,8 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { * 3) The covariance matrix of the PreintegratedCombinedMeasurements preserves * the correlation between the bias uncertainty and the preintegrated * measurements uncertainty. + * + * @addtogroup SLAM */ class CombinedImuFactor: public NoiseModelFactor6 { diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index c85b56872..0b86472f5 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -28,6 +28,25 @@ namespace gtsam { +/* + * If you are using the factor, please cite: + * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating + * conditionally independent sets in factor graphs: a unifying perspective based + * on smart factors, Int. Conf. on Robotics and Automation (ICRA), 2014. + * + * REFERENCES: + * [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", + * Volume 2, 2008. + * [2] T. Lupton and S.Sukkarieh, "Visual-Inertial-Aided Navigation for + * High-Dynamic Motion in Built Environments Without Initial Conditions", + * TRO, 28(1):61-76, 2012. + * [3] L. Carlone, S. Williams, R. Roberts, "Preintegrated IMU factor: + * Computation of the Jacobian Matrices", Tech. Report, 2013. + * [4] C. Forster, L. Carlone, F. Dellaert, D. Scaramuzza, IMU Preintegration on + * Manifold for Efficient Visual-Inertial Maximum-a-Posteriori Estimation, + * Robotics: Science and Systems (RSS), 2015. + */ + /** * PreintegratedIMUMeasurements accumulates (integrates) the IMU measurements * (rotation rates and accelerations) and the corresponding covariance matrix. @@ -35,6 +54,8 @@ namespace gtsam { * Integration is done incrementally (ideally, one integrates the measurement * as soon as it is received from the IMU) so as to avoid costly integration * at time of factor construction. + * + * @addtogroup SLAM */ class PreintegratedImuMeasurements: public PreintegrationBase { @@ -104,25 +125,6 @@ private: } }; -/** - * - * @addtogroup SLAM - * - * If you are using the factor, please cite: - * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating conditionally - * independent sets in factor graphs: a unifying perspective based on smart factors, - * Int. Conf. on Robotics and Automation (ICRA), 2014. - * - ** REFERENCES: - * [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", - * Volume 2, 2008. - * [2] T. Lupton and S.Sukkarieh, "Visual-Inertial-Aided Navigation for - * High-Dynamic Motion in Built Environments Without Initial Conditions", - * TRO, 28(1):61-76, 2012. - * [3] L. Carlone, S. Williams, R. Roberts, "Preintegrated IMU factor: - * Computation of the Jacobian Matrices", Tech. Report, 2013. - */ - /** * ImuFactor is a 5-ways factor involving previous state (pose and velocity of * the vehicle at previous time step), current state (pose and velocity at @@ -132,6 +134,8 @@ private: * Note that this factor does not model "temporal consistency" of the biases * (which are usually slowly varying quantities), which is up to the caller. * See also CombinedImuFactor for a class that does this for you. + * + * @addtogroup SLAM */ class ImuFactor: public NoiseModelFactor5 { From 8a8503ee33ec202b0127cf82f05cb841bc747d50 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 20 Jul 2015 21:40:58 -0700 Subject: [PATCH 050/142] New retract. Still some bug in derivative --- gtsam/navigation/PreintegrationBase.h | 36 +++++++++++++----------- gtsam/navigation/tests/testImuFactor.cpp | 8 +++--- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index e9a0b908e..b601015bf 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -67,31 +67,33 @@ public: static Eigen::Block dP(const Vector9& v) { return v.segment<3>(3); } static Eigen::Block dV(const Vector9& v) { return v.segment<3>(6); } - // Specialized Retract/Local that agrees with IMUFactors - // NOTE(frank): This also agrees with Pose3.retract + // Retract/Local that creates a de-novo Nav-state from xi, on all components + // separately, but then composes with this, i.e., xi is in rotated frame! + // NOTE(frank): This agrees with Pose3.retract, in non-EXPMAP mode, treating V like P NavState retract(const Vector9& xi, // ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { - Matrix3 H1R, H2R; - const Rot3 R = rotation().expmap(dR(xi), H1 ? &H1R : 0, H2 ? &H2R : 0); - const Point3 p = translation() + Point3(dP(xi)); - const Vector v = velocity() + dV(xi); - if (H1) { - H1->setIdentity(); - H1->topLeftCorner<3,3>() = H1R; - } + Matrix3 D_R_xi; + const Rot3 R = Rot3::Expmap(dR(xi), H2 ? &D_R_xi : 0); + const Point3 p = Point3(dP(xi)); + const Vector v = dV(xi); + const NavState delta(R, p, v); + // retract only depends on this via compose, and second derivative in delta is I_9x9 + const NavState result = this->compose(delta, H1); if (H2) { - H2->setIdentity(); - H2->topLeftCorner<3,3>() = H2R; + *H2 << D_R_xi, Z_3x3, Z_3x3, // + Z_3x3, I_3x3, Z_3x3, // + Z_3x3, Z_3x3, I_3x3; } - return NavState(R, p, v); - } + return result; +} Vector9 localCoordinates(const NavState& g, // ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { if (H1||H2) throw std::runtime_error("NavState::localCoordinates derivatives not implemented yet"); + const NavState delta = this->between(g); Vector9 v; - dR(v) = rotation().logmap(g.rotation()); - dP(v) = (g.translation() - translation()).vector(); - dV(v) = g.velocity() - velocity(); + dR(v) = Rot3::Logmap(delta.rotation()); + dP(v) = delta.translation().vector(); + dV(v) = delta.velocity(); return v; } /// @} diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index ad41b81a5..944a38156 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -227,10 +227,10 @@ TEST( NavState, Lie ) { // Special retract Vector delta(9); delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3; - Rot3 rot2 = rot.expmap(delta.head<3>()); - Point3 t2 = pt + Point3(delta.segment<3>(3)); - Velocity3 vel2 = vel + Velocity3(-0.1, -0.2, -0.3); - NavState state2(rot2, t2, vel2); + Rot3 drot = Rot3::Expmap(delta.head<3>()); + Point3 dt = Point3(delta.segment<3>(3)); + Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3); + NavState state2 = state1 * NavState(drot, dt, dvel); EXPECT(assert_equal(state2, (NavState)state1.retract(delta), tol)); EXPECT(assert_equal(delta, state1.localCoordinates(state2), tol)); From 205d20d4dc1061ae83a9179bcb7d9e0ff8649de5 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 11:22:49 -0700 Subject: [PATCH 051/142] Rot3 action on Vector3, and templated constructor --- gtsam/geometry/Rot3.h | 23 +++++++++++++++++++---- gtsam/geometry/Rot3M.cpp | 12 ------------ gtsam/geometry/Rot3Q.cpp | 8 -------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/gtsam/geometry/Rot3.h b/gtsam/geometry/Rot3.h index 608f41954..1dec6eafe 100644 --- a/gtsam/geometry/Rot3.h +++ b/gtsam/geometry/Rot3.h @@ -86,10 +86,14 @@ namespace gtsam { double R31, double R32, double R33); /** constructor from a rotation matrix */ - Rot3(const Matrix3& R); - - /** constructor from a rotation matrix */ - Rot3(const Matrix& R); + template + inline Rot3(const Eigen::MatrixBase& R) { + #ifdef GTSAM_USE_QUATERNIONS + quaternion_=R + #else + rot_ = R; + #endif + } /** Constructor from a quaternion. This can also be called using a plain * Vector, due to implicit conversion from Vector to Quaternion @@ -330,6 +334,17 @@ namespace gtsam { Point3 rotate(const Point3& p, OptionalJacobian<3,3> H1 = boost::none, OptionalJacobian<3,3> H2 = boost::none) const; + /// operator* for Vector3 + inline Vector3 operator*(const Vector3& v) const { + return rotate(Point3(v)).vector(); + } + + /// rotate for Vector3 + Vector3 rotate(const Vector3& v, OptionalJacobian<3, 3> H1 = boost::none, + OptionalJacobian<3, 3> H2 = boost::none) const { + return rotate(Point3(v), H1, H2).vector(); + } + /// rotate point from rotated coordinate frame to world = R*p Point3 operator*(const Point3& p) const; diff --git a/gtsam/geometry/Rot3M.cpp b/gtsam/geometry/Rot3M.cpp index 324c05ae4..a7b654070 100644 --- a/gtsam/geometry/Rot3M.cpp +++ b/gtsam/geometry/Rot3M.cpp @@ -50,18 +50,6 @@ Rot3::Rot3(double R11, double R12, double R13, R31, R32, R33; } -/* ************************************************************************* */ -Rot3::Rot3(const Matrix3& R) { - rot_ = R; -} - -/* ************************************************************************* */ -Rot3::Rot3(const Matrix& R) { - if (R.rows()!=3 || R.cols()!=3) - throw invalid_argument("Rot3 constructor expects 3*3 matrix"); - rot_ = R; -} - /* ************************************************************************* */ Rot3::Rot3(const Quaternion& q) : rot_(q.toRotationMatrix()) { } diff --git a/gtsam/geometry/Rot3Q.cpp b/gtsam/geometry/Rot3Q.cpp index c97e76718..b255b082d 100644 --- a/gtsam/geometry/Rot3Q.cpp +++ b/gtsam/geometry/Rot3Q.cpp @@ -48,14 +48,6 @@ namespace gtsam { R21, R22, R23, R31, R32, R33).finished()) {} - /* ************************************************************************* */ - Rot3::Rot3(const Matrix3& R) : - quaternion_(R) {} - - /* ************************************************************************* */ - Rot3::Rot3(const Matrix& R) : - quaternion_(Matrix3(R)) {} - /* ************************************************************************* */ Rot3::Rot3(const Quaternion& q) : quaternion_(q) { From 4c8c669d71bdb080c067ae87f948b5ab7ef13c08 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 11:23:24 -0700 Subject: [PATCH 052/142] Moved NavState to its own header and totally revamped as a semi-direct product --- gtsam/navigation/NavState.cpp | 120 +++++++++++++ gtsam/navigation/NavState.h | 225 ++++++++++++++++++++++++ gtsam/navigation/PreintegrationBase.cpp | 10 +- gtsam/navigation/PreintegrationBase.h | 88 +-------- gtsam/navigation/tests/testNavState.cpp | 124 +++++++++++++ 5 files changed, 475 insertions(+), 92 deletions(-) create mode 100644 gtsam/navigation/NavState.cpp create mode 100644 gtsam/navigation/NavState.h create mode 100644 gtsam/navigation/tests/testNavState.cpp diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp new file mode 100644 index 000000000..6a8977fd5 --- /dev/null +++ b/gtsam/navigation/NavState.cpp @@ -0,0 +1,120 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file NavState.h + * @brief Navigation state composing of attitude, position, and velocity + * @author Frank Dellaert + * @date July 2015 + **/ + +#include + +namespace gtsam { + +#define TIE(R,t,v,x) const Rot3& R = (x).R_;const Point3& t = (x).t_;const Velocity3& v = (x).v_; + +Matrix7 NavState::matrix() const { + Matrix3 R = this->R(); + Matrix7 T; + T << R, Z_3x3, t(), Z_3x3, R, v(), Vector6::Zero().transpose(), 1.0; + return T; +} + +void NavState::print(const std::string& s) const { + attitude().print(s + ".R"); + position().print(s + ".p"); + gtsam::print((Vector) v_, s + ".v"); +} + +bool NavState::equals(const NavState& other, double tol) const { + return R_.equals(other.R_, tol) && t_.equals(other.t_, tol) + && equal_with_abs_tol(v_, other.v_, tol); +} + +NavState NavState::inverse() const { + TIE(nRb, n_t, n_v, *this); + const Rot3 bRn = nRb.inverse(); + return NavState(bRn, -(bRn * n_t), -(bRn * n_v)); +} + +NavState NavState::operator*(const NavState& bTc) const { + TIE(nRb, n_t, n_v, *this); + TIE(bRc, b_t, b_v, bTc); + return NavState(nRb * bRc, nRb * b_t + n_t, nRb * b_v + n_v); +} + +NavState::PositionAndVelocity // +NavState::operator*(const PositionAndVelocity& b_tv) const { + TIE(nRb, n_t, n_v, *this); + const Point3& b_t = b_tv.first; + const Velocity3& b_v = b_tv.second; + return PositionAndVelocity(nRb * b_t + n_t, nRb * b_v + n_v); +} + +Point3 NavState::operator*(const Point3& b_t) const { + return Point3(R_ * b_t + t_); +} + +Velocity3 NavState::operator*(const Velocity3& b_v) const { + return Velocity3(R_ * b_v + v_); +} + +NavState NavState::ChartAtOrigin::Retract(const Vector9& xi, + OptionalJacobian<9, 9> H) { + Matrix3 D_R_xi; + const Rot3 R = Rot3::Expmap(dR(xi), H ? &D_R_xi : 0); + const Point3 p = Point3(dP(xi)); + const Vector v = dV(xi); + const NavState result(R, p, v); + if (H) { + *H << D_R_xi, Z_3x3, Z_3x3, // + Z_3x3, R.transpose(), Z_3x3, // + Z_3x3, Z_3x3, R.transpose(); + } + return result; +} + +Vector9 NavState::ChartAtOrigin::Local(const NavState& x, + OptionalJacobian<9, 9> H) { + if (H) + throw std::runtime_error( + "NavState::ChartOrigin::Local derivative not implemented yet"); + Vector9 xi; + xi << Rot3::Logmap(x.R_), x.t(), x.v(); + return xi; +} + +NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { + if (H) + throw std::runtime_error("NavState::Expmap derivative not implemented yet"); + // use brute force matrix exponential + return expm(xi); +} + +Vector9 NavState::Logmap(const NavState& p, OptionalJacobian<9, 9> H) { + if (H) + throw std::runtime_error("NavState::Logmap derivative not implemented yet"); + return Vector9::Zero(); +} + +Matrix9 NavState::AdjointMap() const { + throw std::runtime_error("NavState::AdjointMap not implemented yet"); +} + +Matrix7 NavState::wedge(const Vector9& xi) { + const Matrix3 Omega = skewSymmetric(dR(xi)); + Matrix7 T; + T << Omega, Z_3x3, dP(xi), Z_3x3, Omega, dV(xi), Vector6::Zero().transpose(), 1.0; + return T; +} + +} /// namespace gtsam diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h new file mode 100644 index 000000000..e7aab4b7c --- /dev/null +++ b/gtsam/navigation/NavState.h @@ -0,0 +1,225 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file NavState.h + * @brief Navigation state composing of attitude, position, and velocity + * @author Frank Dellaert + * @date July 2015 + **/ + +#pragma once + +#include +#include +#include + +namespace gtsam { + +/// Velocity is currently typedef'd to Vector3 +typedef Vector3 Velocity3; + +/** + * Navigation state: Pose (rotation, translation) + velocity + * Implements semi-direct Lie group product of SO(3) and R^6, where R^6 is position/velocity + */ +class NavState: public LieGroup { +private: + Rot3 R_; ///< Rotation nRb, rotates points/velocities in body to points/velocities in nav + Point3 t_; ///< position n_t, in nav frame + Velocity3 v_; ///< velocity n_v in nav frame + +public: + + typedef std::pair PositionAndVelocity; + + /// @name Constructors + /// @{ + + /// Default constructor + NavState() : + v_(Vector3::Zero()) { + } + /// Construct from attitude, position, velocity + NavState(const Rot3& R, const Point3& t, const Velocity3& v) : + R_(R), t_(t), v_(v) { + } + /// Construct from pose and velocity + NavState(const Pose3& pose, const Velocity3& v) : + R_(pose.rotation()), t_(pose.translation()), v_(v) { + } + /// Construct from Matrix group representation (no checking) + NavState(const Matrix7& T) : + R_(T.block<3, 3>(0, 0)), t_(T.block<3, 1>(0, 6)), v_(T.block<3, 1>(3, 6)) { + } + /// Construct from SO(3) and R^6 + NavState(const Matrix3& R, const Vector9 tv) : + R_(R), t_(tv.head<3>()), v_(tv.tail<3>()) { + } + + /// @} + /// @name Component Access + /// @{ + + const Rot3& attitude() const { + return R_; + } + const Point3& position() const { + return t_; + } + const Velocity3& velocity() const { + return v_; + } + const Pose3 pose() const { + return Pose3(attitude(), position()); + } + + /// @} + /// @name Derived quantities + /// @{ + + /// Return rotation matrix. Induces computation in quaternion mode + Matrix3 R() const { + return R_.matrix(); + } + /// Return position as Vector3 + Vector3 t() const { + return t_.vector(); + } + /// Return velocity as Vector3. Computation-free. + const Vector3& v() const { + return v_; + } + /// Return quaternion. Induces computation in matrix mode + Quaternion quaternion() const { + return R_.toQuaternion(); + } + /// Return matrix group representation, in MATLAB notation: + /// nTb = [nRb 0 n_t; 0 nRb n_v; 0 0 1] + /// With this embedding in GL(3), matrix product agrees with compose + Matrix7 matrix() const; + + /// @} + /// @name Testable + /// @{ + + /// print + void print(const std::string& s = "") const; + + /// equals + bool equals(const NavState& other, double tol = 1e-8) const; + + /// @} + /// @name Group + /// @{ + + /// identity for group operation + static NavState identity() { + return NavState(); + } + + /// inverse transformation with derivatives + NavState inverse() const; + + /// Group compose is the semi-direct product as specified below: + /// nTc = nTb * bTc = (nRb * bRc, nRb * b_t + n_t, nRb * b_v + n_v) + NavState operator*(const NavState& bTc) const; + + /// Native group action is on position/velocity pairs *in the body frame* as follows: + /// nTb * (b_t,b_v) = (nRb * b_t + n_t, nRb * b_v + n_v) + PositionAndVelocity operator*(const PositionAndVelocity& b_tv) const; + + /// Act on position alone, n_t = nRb * b_t + n_t + Point3 operator*(const Point3& b_t) const; + + /// Act on velocity alone, n_v = nRb * b_v + n_v + Velocity3 operator*(const Velocity3& b_v) const; + + /// @} + /// @name Manifold + /// @{ + + // Tangent space sugar. + // TODO(frank): move to private navstate namespace in cpp + static Eigen::Block dR(Vector9& v) { + return v.segment<3>(0); + } + static Eigen::Block dP(Vector9& v) { + return v.segment<3>(3); + } + static Eigen::Block dV(Vector9& v) { + return v.segment<3>(6); + } + static Eigen::Block dR(const Vector9& v) { + return v.segment<3>(0); + } + static Eigen::Block dP(const Vector9& v) { + return v.segment<3>(3); + } + static Eigen::Block dV(const Vector9& v) { + return v.segment<3>(6); + } + + // Chart at origin, constructs components separately (as opposed to Expmap) + struct ChartAtOrigin { + static NavState Retract(const Vector9& xi, // + OptionalJacobian<9, 9> H = boost::none); + static Vector9 Local(const NavState& x, // + OptionalJacobian<9, 9> H = boost::none); + }; + + /// @} + /// @name Lie Group + /// @{ + + /// Exponential map at identity - create a NavState from canonical coordinates + static NavState Expmap(const Vector9& xi, // + OptionalJacobian<9, 9> H = boost::none); + + /// Log map at identity - return the canonical coordinates for this NavState + static Vector9 Logmap(const NavState& p, // + OptionalJacobian<9, 9> H = boost::none); + + /// Calculate Adjoint map, a 9x9 matrix that takes a tangent vector in the body frame, and transforms + /// it to a tangent vector at idenity, such that Exmap(AdjointMap()*xi) = (*this) * Exmpap(xi); + Matrix9 AdjointMap() const; + + /// wedge creates Lie algebra element from tangent vector + static Matrix7 wedge(const Vector9& xi); + + /// @} + +private: + /// @{ + /// serialization + friend class boost::serialization::access; + template + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { + ar & BOOST_SERIALIZATION_NVP(R_); + ar & BOOST_SERIALIZATION_NVP(t_); + ar & BOOST_SERIALIZATION_NVP(v_); + } + /// @} +}; + +// Specialize NavState traits to use a Retract/Local that agrees with IMUFactors +template<> +struct traits : Testable, internal::LieGroupTraits { +}; + +// Partial specialization of wedge +// TODO: deprecate, make part of traits +template<> +inline Matrix wedge(const Vector& xi) { + return NavState::wedge(xi); +} + +} // namespace gtsam diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 28d2c7842..d2775bfb2 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -182,7 +182,7 @@ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, const Pose3& pose_i = state_i.pose(); const Vector3& vel_i = state_i.velocity(); const Matrix3 Ri = pose_i.rotation().matrix(); - const Vector3& t_i = state_i.translation().vector(); + const Vector3& t_i = state_i.position().vector(); const double dt = deltaTij(), dt2 = dt * dt; const Vector3& omegaCoriolis = *p().omegaCoriolis; @@ -220,7 +220,7 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, const Matrix3 Ri = pose_i.rotation().matrix(); const double dt = deltaTij(), dt2 = dt * dt; - // Rotation, translation, and velocity: + // Rotation, position, and velocity: Vector9 delta; Matrix3 D_dP_Ri, D_dP_bc, D_dV_Ri, D_dV_bc; NavState::dR(delta) = NavState::dR(biasCorrectedDelta); @@ -274,18 +274,18 @@ NavState PreintegrationBase::predict(const NavState& state_i, static Vector9 computeError(const NavState& state_i, const NavState& state_j, const NavState& predictedState_j) { - const Rot3& rot_i = state_i.rotation(); + const Rot3& rot_i = state_i.attitude(); const Matrix Ri = rot_i.matrix(); // Residual rotation error // TODO: this also seems to be flipped from localCoordinates - const Rot3 fRrot = predictedState_j.rotation().between(state_j.rotation()); + const Rot3 fRrot = predictedState_j.attitude().between(state_j.attitude()); const Vector3 fR = Rot3::Logmap(fRrot); // Evaluate residual error, according to [3] // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance const Vector3 fp = Ri.transpose() - * (state_j.translation() - predictedState_j.translation()).vector(); + * (state_j.position() - predictedState_j.position()).vector(); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance const Vector3 fv = Ri.transpose() diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index b601015bf..359db8c83 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -22,97 +22,11 @@ #pragma once #include +#include #include -#include -#include -#include -#include namespace gtsam { -/// Velocity in 3D is just a Vector3 -typedef Vector3 Velocity3; - -/** - * Navigation state: Pose (rotation, translation) + velocity - */ -class NavState: public ProductLieGroup { -protected: - typedef ProductLieGroup Base; - typedef OptionalJacobian<9, 9> ChartJacobian; - using Base::first; - using Base::second; - -public: - // constructors - NavState() {} - NavState(const Pose3& pose, const Velocity3& vel) : Base(pose, vel) {} - NavState(const Rot3& rot, const Point3& t, const Velocity3& vel): Base(Pose3(rot, t), vel) {} - NavState(const Base& product) : Base(product) {} - - // access - const Pose3& pose() const { return first; } - const Point3& translation() const { return pose().translation(); } - const Rot3& rotation() const { return pose().rotation(); } - const Velocity3& velocity() const { return second; } - - /// @name Manifold - /// @{ - - // NavState tangent space sugar. - static Eigen::Block dR(Vector9& v) { return v.segment<3>(0); } - static Eigen::Block dP(Vector9& v) { return v.segment<3>(3); } - static Eigen::Block dV(Vector9& v) { return v.segment<3>(6); } - static Eigen::Block dR(const Vector9& v) { return v.segment<3>(0); } - static Eigen::Block dP(const Vector9& v) { return v.segment<3>(3); } - static Eigen::Block dV(const Vector9& v) { return v.segment<3>(6); } - - // Retract/Local that creates a de-novo Nav-state from xi, on all components - // separately, but then composes with this, i.e., xi is in rotated frame! - // NOTE(frank): This agrees with Pose3.retract, in non-EXPMAP mode, treating V like P - NavState retract(const Vector9& xi, // - ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { - Matrix3 D_R_xi; - const Rot3 R = Rot3::Expmap(dR(xi), H2 ? &D_R_xi : 0); - const Point3 p = Point3(dP(xi)); - const Vector v = dV(xi); - const NavState delta(R, p, v); - // retract only depends on this via compose, and second derivative in delta is I_9x9 - const NavState result = this->compose(delta, H1); - if (H2) { - *H2 << D_R_xi, Z_3x3, Z_3x3, // - Z_3x3, I_3x3, Z_3x3, // - Z_3x3, Z_3x3, I_3x3; - } - return result; -} - Vector9 localCoordinates(const NavState& g, // - ChartJacobian H1 = boost::none, ChartJacobian H2 = boost::none) const { - if (H1||H2) throw std::runtime_error("NavState::localCoordinates derivatives not implemented yet"); - const NavState delta = this->between(g); - Vector9 v; - dR(v) = Rot3::Logmap(delta.rotation()); - dP(v) = delta.translation().vector(); - dV(v) = delta.velocity(); - return v; - } - /// @} -}; - -// Specialize NavState traits to use a Retract/Local that agrees with IMUFactors -template<> -struct traits : internal::LieGroupTraits { - static void Print(const NavState& m, const std::string& s = "") { - m.rotation().print(s+".R"); - m.translation().print(s+".P"); - print((Vector)m.velocity(),s+".V"); - } - static bool Equals(const NavState& m1, const NavState& m2, double tol = 1e-8) { - return m1.pose().equals(m2.pose(), tol) - && equal_with_abs_tol(m1.velocity(), m2.velocity(), tol); - } -}; - /// @deprecated struct PoseVelocityBias { Pose3 pose; diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp new file mode 100644 index 000000000..675fe95c0 --- /dev/null +++ b/gtsam/navigation/tests/testNavState.cpp @@ -0,0 +1,124 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file testNavState.cpp + * @brief Unit tests for NavState + * @author Frank Dellaert + * @date July 2015 + */ + +#include +#include +#include +#include + +using namespace std; +using namespace gtsam; + +static const Rot3 kRotation = Rot3::RzRyRx(0.1, 0.2, 0.3); +static const Point3 kPosition(1.0, 2.0, 3.0); +static const Velocity3 kVelocity(0.4, 0.5, 0.6); +static const NavState kIdentity; +static const NavState kState1(kRotation, kPosition, kVelocity); + +const double tol = 1e-5; + +/* ************************************************************************* */ +TEST( NavState, MatrixGroup ) { + // check roundtrip conversion to 7*7 matrix representation + Matrix7 T = kState1.matrix(); + EXPECT(assert_equal(kState1, NavState(T), tol)); + + // check group product agrees with matrix product + NavState state2 = kState1 * kState1; + Matrix T2 = T * T; + EXPECT(assert_equal(state2, NavState(T2), tol)); + EXPECT(assert_equal(T2, state2.matrix(), tol)); +} + +/* ************************************************************************* */ +TEST( NavState, Manifold ) { + // Check zero xi + EXPECT(assert_equal(kIdentity, kIdentity.retract(zero(9)), tol)); + EXPECT(assert_equal(zero(9), kIdentity.localCoordinates(kIdentity), tol)); + EXPECT(assert_equal(kState1, kState1.retract(zero(9)), tol)); + EXPECT(assert_equal(zero(9), kState1.localCoordinates(kState1), tol)); + + // Check definition of retract as operating on components separately + Vector xi(9); + xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; + Rot3 drot = Rot3::Expmap(xi.head<3>()); + Point3 dt = Point3(xi.segment < 3 > (3)); + Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3); + NavState state2 = kState1 * NavState(drot, dt, dvel); + EXPECT(assert_equal(state2, kState1.retract(xi), tol)); + EXPECT(assert_equal(xi, kState1.localCoordinates(state2), tol)); + + // roundtrip from state2 to state3 and back + NavState state3 = state2.retract(xi); + EXPECT(assert_equal(xi, state2.localCoordinates(state3), tol)); + + // Check derivatives for ChartAtOrigin::Retract + Matrix9 aH, eH; + // For zero xi + boost::function f = boost::bind( + NavState::ChartAtOrigin::Retract, _1, boost::none); + NavState::ChartAtOrigin::Retract(zero(9), aH); + eH = numericalDerivative11(f, zero(9)); + EXPECT(assert_equal((Matrix )eH, aH)); + // For non-zero xi + NavState::ChartAtOrigin::Retract(xi, aH); + eH = numericalDerivative11(f, xi); + EXPECT(assert_equal((Matrix )eH, aH)); + + // Check retract derivatives +// Matrix9 aH1, aH2; +// kState1.retract(xi, aH1, aH2); +// Matrix eH1 = numericalDerivative11( +// boost::bind(&NavState::retract, _1, xi, boost::none, boost::none), +// kState1); +// EXPECT(assert_equal(eH1, aH1)); +// Matrix eH2 = numericalDerivative11( +// boost::bind(&NavState::retract, kState1, _1, boost::none, boost::none), +// xi); +// EXPECT(assert_equal(eH2, aH2)); +} + +/* ************************************************************************* */ +TEST( NavState, Lie ) { + // Check zero xi + EXPECT(assert_equal(kIdentity, kIdentity.expmap(zero(9)), tol)); + EXPECT(assert_equal(zero(9), kIdentity.logmap(kIdentity), tol)); + EXPECT(assert_equal(kState1, kState1.expmap(zero(9)), tol)); + EXPECT(assert_equal(zero(9), kState1.logmap(kState1), tol)); + + // Expmap/Logmap roundtrip + Vector xi(9); + xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; + NavState state2 = NavState::Expmap(xi); + EXPECT(assert_equal(xi, NavState::Logmap(state2), tol)); + + // roundtrip from state2 to state3 and back + NavState state3 = state2.expmap(xi); + EXPECT(assert_equal(xi, state2.logmap(state3), tol)); + + // For the expmap/logmap (not necessarily expmap/local) -xi goes other way + EXPECT(assert_equal(state2, state3.expmap(-xi), tol)); + EXPECT(assert_equal(xi, -state3.logmap(state2), tol)); +} + +/* ************************************************************************* */ +int main() { + TestResult tr; + return TestRegistry::runAllTests(tr); +} +/* ************************************************************************* */ From 1a47a334de20429ec6f21cb19e0aafac960cc6ac Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 11:23:42 -0700 Subject: [PATCH 053/142] Deal with changes in Rot3 --- gtsam_unstable/dynamics/PoseRTV.cpp | 13 ++++++------- gtsam_unstable/dynamics/PoseRTV.h | 7 +++++-- gtsam_unstable/slam/Mechanization_bRn2.cpp | 2 +- gtsam_unstable/slam/Mechanization_bRn2.h | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/gtsam_unstable/dynamics/PoseRTV.cpp b/gtsam_unstable/dynamics/PoseRTV.cpp index 92f3b9b0b..51737285a 100644 --- a/gtsam_unstable/dynamics/PoseRTV.cpp +++ b/gtsam_unstable/dynamics/PoseRTV.cpp @@ -205,10 +205,10 @@ PoseRTV PoseRTV::transformed_from(const Pose3& trans, ChartJacobian Dglobal, } /* ************************************************************************* */ -Matrix PoseRTV::RRTMbn(const Vector& euler) { +Matrix PoseRTV::RRTMbn(const Vector3& euler) { assert(euler.size() == 3); - const double s1 = sin(euler(1-1)), c1 = cos(euler(1-1)); - const double t2 = tan(euler(2-1)), c2 = cos(euler(2-1)); + const double s1 = sin(euler.x()), c1 = cos(euler.x()); + const double t2 = tan(euler.y()), c2 = cos(euler.y()); Matrix Ebn(3,3); Ebn << 1.0, s1 * t2, c1 * t2, 0.0, c1, -s1, @@ -222,11 +222,10 @@ Matrix PoseRTV::RRTMbn(const Rot3& att) { } /* ************************************************************************* */ -Matrix PoseRTV::RRTMnb(const Vector& euler) { - assert(euler.size() == 3); +Matrix PoseRTV::RRTMnb(const Vector3& euler) { Matrix Enb(3,3); - const double s1 = sin(euler(1-1)), c1 = cos(euler(1-1)); - const double s2 = sin(euler(2-1)), c2 = cos(euler(2-1)); + const double s1 = sin(euler.x()), c1 = cos(euler.x()); + const double s2 = sin(euler.y()), c2 = cos(euler.y()); Enb << 1.0, 0.0, -s2, 0.0, c1, s1*c2, 0.0, -s1, c1*c2; diff --git a/gtsam_unstable/dynamics/PoseRTV.h b/gtsam_unstable/dynamics/PoseRTV.h index b1603efe2..ed5fa9be0 100644 --- a/gtsam_unstable/dynamics/PoseRTV.h +++ b/gtsam_unstable/dynamics/PoseRTV.h @@ -18,6 +18,7 @@ typedef Vector3 Velocity3; /** * Robot state for use with IMU measurements * - contains translation, translational velocity and rotation + * TODO(frank): Alex should deprecate/move to project */ class GTSAM_UNSTABLE_EXPORT PoseRTV : public ProductLieGroup { protected: @@ -145,13 +146,15 @@ public: /// RRTMbn - Function computes the rotation rate transformation matrix from /// body axis rates to euler angle (global) rates - static Matrix RRTMbn(const Vector& euler); + /// TODO(frank): seems to ignore euler.z() + static Matrix RRTMbn(const Vector3& euler); static Matrix RRTMbn(const Rot3& att); /// RRTMnb - Function computes the rotation rate transformation matrix from /// euler angle rates to body axis rates - static Matrix RRTMnb(const Vector& euler); + /// TODO(frank): seems to ignore euler.z() + static Matrix RRTMnb(const Vector3& euler); static Matrix RRTMnb(const Rot3& att); /// @} diff --git a/gtsam_unstable/slam/Mechanization_bRn2.cpp b/gtsam_unstable/slam/Mechanization_bRn2.cpp index d2dd02dd3..f6f1c4a5c 100644 --- a/gtsam_unstable/slam/Mechanization_bRn2.cpp +++ b/gtsam_unstable/slam/Mechanization_bRn2.cpp @@ -101,7 +101,7 @@ Mechanization_bRn2 Mechanization_bRn2::integrate(const Vector3& u, // convert to navigation frame Rot3 nRb = bRn_.inverse(); - Vector3 n_omega_bn = (nRb*b_omega_bn).vector(); + Vector3 n_omega_bn = nRb * b_omega_bn; // integrate bRn using exponential map, assuming constant over dt Rot3 delta_nRn = Rot3::Rodrigues(n_omega_bn*dt); diff --git a/gtsam_unstable/slam/Mechanization_bRn2.h b/gtsam_unstable/slam/Mechanization_bRn2.h index fa33ce5b9..4c85614d4 100644 --- a/gtsam_unstable/slam/Mechanization_bRn2.h +++ b/gtsam_unstable/slam/Mechanization_bRn2.h @@ -42,7 +42,7 @@ public: /// gravity in the body frame Vector3 b_g(double g_e) const { Vector3 n_g(0, 0, g_e); - return (bRn_ * n_g).vector(); + return bRn_ * n_g; } const Rot3& bRn() const {return bRn_; } From c7bc497014b6b4323bb92f503bb8db2396289bbd Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 11:36:28 -0700 Subject: [PATCH 054/142] Working Expmap --- gtsam/navigation/NavState.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 6a8977fd5..c1b901b22 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -96,8 +96,26 @@ Vector9 NavState::ChartAtOrigin::Local(const NavState& x, NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { if (H) throw std::runtime_error("NavState::Expmap derivative not implemented yet"); - // use brute force matrix exponential - return expm(xi); + + Eigen::Block n_omega_nb = dR(xi); + Eigen::Block v = dP(xi); + Eigen::Block a = dV(xi); + + Rot3 nRb = Rot3::Expmap(n_omega_nb); + double theta2 = n_omega_nb.dot(n_omega_nb); + if (theta2 > std::numeric_limits::epsilon()) { + double omega_v = n_omega_nb.dot(v); // translation parallel to axis + Vector3 omega_cross_v = n_omega_nb.cross(v); // points towards axis + Point3 n_t = Point3(omega_cross_v - nRb * omega_cross_v + omega_v * n_omega_nb) + / theta2; + double omega_a = n_omega_nb.dot(a); // translation parallel to axis + Vector3 omega_cross_a = n_omega_nb.cross(a); // points towards axis + Vector3 n_v = (omega_cross_a - nRb * omega_cross_a + omega_a * n_omega_nb) + / theta2; + return NavState(nRb, n_t, n_v); + } else { + return NavState(nRb, Point3(v), a); + } } Vector9 NavState::Logmap(const NavState& p, OptionalJacobian<9, 9> H) { From 2dbad989d1aab1f8b279b490eb0398b9b0af2a1e Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 16:09:56 -0400 Subject: [PATCH 055/142] Working Logmap ! --- gtsam/navigation/NavState.cpp | 36 ++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index c1b901b22..bd2656b50 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -101,27 +101,49 @@ NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { Eigen::Block v = dP(xi); Eigen::Block a = dV(xi); + // NOTE(frank): mostly copy/paste from Pose3 Rot3 nRb = Rot3::Expmap(n_omega_nb); double theta2 = n_omega_nb.dot(n_omega_nb); if (theta2 > std::numeric_limits::epsilon()) { - double omega_v = n_omega_nb.dot(v); // translation parallel to axis + // Expmap implements a "screw" motion in the direction of n_omega_nb + Vector3 n_t_parallel = n_omega_nb * n_omega_nb.dot(v); // component along rotation axis Vector3 omega_cross_v = n_omega_nb.cross(v); // points towards axis - Point3 n_t = Point3(omega_cross_v - nRb * omega_cross_v + omega_v * n_omega_nb) + Point3 n_t = Point3(omega_cross_v - nRb * omega_cross_v + n_t_parallel) / theta2; - double omega_a = n_omega_nb.dot(a); // translation parallel to axis + Vector3 n_v_parallel = n_omega_nb * n_omega_nb.dot(a); // component along rotation axis Vector3 omega_cross_a = n_omega_nb.cross(a); // points towards axis - Vector3 n_v = (omega_cross_a - nRb * omega_cross_a + omega_a * n_omega_nb) - / theta2; + Vector3 n_v = (omega_cross_a - nRb * omega_cross_a + n_v_parallel) / theta2; return NavState(nRb, n_t, n_v); } else { return NavState(nRb, Point3(v), a); } } -Vector9 NavState::Logmap(const NavState& p, OptionalJacobian<9, 9> H) { +Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { if (H) throw std::runtime_error("NavState::Logmap derivative not implemented yet"); - return Vector9::Zero(); + + TIE(nRb, n_p, n_v, nTb); + Vector3 n_t = n_p.vector(); + + // NOTE(frank): mostly copy/paste from Pose3 + Vector9 xi; + Vector3 n_omega_nb = Rot3::Logmap(nRb); + double theta = n_omega_nb.norm(); + if (theta * theta <= std::numeric_limits::epsilon()) { + xi << n_omega_nb, n_t, n_v; + } else { + Matrix3 W = skewSymmetric(n_omega_nb / theta); + // Formula from Agrawal06iros, equation (14) + // simplified with Mathematica, and multiplying in n_t to avoid matrix math + double factor = (1 - theta / (2. * tan(0.5 * theta))); + Vector3 n_x = W * n_t; + Vector3 v = n_t - (0.5 * theta) * n_x + factor * (W * n_x); + Vector3 n_y = W * n_v; + Vector3 a = n_v - (0.5 * theta) * n_y + factor * (W * n_y); + xi << n_omega_nb, v, a; + } + return xi; } Matrix9 NavState::AdjointMap() const { From 2dd7987478d41185519821829427661ef7072dd8 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 16:25:58 -0400 Subject: [PATCH 056/142] Working AdjointMap and hence also derived derivatives of retract/localCoordinates --- gtsam/navigation/NavState.cpp | 13 ++++++++++--- gtsam/navigation/NavState.h | 2 +- gtsam/navigation/tests/testNavState.cpp | 20 ++++++++++---------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index bd2656b50..e7c2e0b55 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -101,7 +101,7 @@ NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { Eigen::Block v = dP(xi); Eigen::Block a = dV(xi); - // NOTE(frank): mostly copy/paste from Pose3 + // NOTE(frank): See Pose3::Expmap Rot3 nRb = Rot3::Expmap(n_omega_nb); double theta2 = n_omega_nb.dot(n_omega_nb); if (theta2 > std::numeric_limits::epsilon()) { @@ -126,7 +126,7 @@ Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { TIE(nRb, n_p, n_v, nTb); Vector3 n_t = n_p.vector(); - // NOTE(frank): mostly copy/paste from Pose3 + // NOTE(frank): See Pose3::Logmap Vector9 xi; Vector3 n_omega_nb = Rot3::Logmap(nRb); double theta = n_omega_nb.norm(); @@ -147,7 +147,14 @@ Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { } Matrix9 NavState::AdjointMap() const { - throw std::runtime_error("NavState::AdjointMap not implemented yet"); + // NOTE(frank): See Pose3::AdjointMap + const Matrix3 nRb = R(); + Matrix3 pAr = skewSymmetric(t()) * nRb; + Matrix3 vAr = skewSymmetric(v()) * nRb; + Matrix9 adj; + // nR/bR nR/bP nR/bV nP/bR nP/bP nP/bV nV/bR nV/bP nV/bV + adj << nRb, Z_3x3, Z_3x3, pAr, nRb, Z_3x3, vAr, Z_3x3, nRb; + return adj; } Matrix7 NavState::wedge(const Vector9& xi) { diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index e7aab4b7c..0edac0f65 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -189,7 +189,7 @@ public: OptionalJacobian<9, 9> H = boost::none); /// Calculate Adjoint map, a 9x9 matrix that takes a tangent vector in the body frame, and transforms - /// it to a tangent vector at idenity, such that Exmap(AdjointMap()*xi) = (*this) * Exmpap(xi); + /// it to a tangent vector at identity, such that Exmap(AdjointMap()*xi) = (*this) * Exmpap(xi); Matrix9 AdjointMap() const; /// wedge creates Lie algebra element from tangent vector diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index 675fe95c0..8cf14fec4 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -81,16 +81,16 @@ TEST( NavState, Manifold ) { EXPECT(assert_equal((Matrix )eH, aH)); // Check retract derivatives -// Matrix9 aH1, aH2; -// kState1.retract(xi, aH1, aH2); -// Matrix eH1 = numericalDerivative11( -// boost::bind(&NavState::retract, _1, xi, boost::none, boost::none), -// kState1); -// EXPECT(assert_equal(eH1, aH1)); -// Matrix eH2 = numericalDerivative11( -// boost::bind(&NavState::retract, kState1, _1, boost::none, boost::none), -// xi); -// EXPECT(assert_equal(eH2, aH2)); + Matrix9 aH1, aH2; + kState1.retract(xi, aH1, aH2); + Matrix eH1 = numericalDerivative11( + boost::bind(&NavState::retract, _1, xi, boost::none, boost::none), + kState1); + EXPECT(assert_equal(eH1, aH1)); + Matrix eH2 = numericalDerivative11( + boost::bind(&NavState::retract, kState1, _1, boost::none, boost::none), + xi); + EXPECT(assert_equal(eH2, aH2)); } /* ************************************************************************* */ From 4dbe5e68d213faa1891c646f14d549c23a933919 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 18:28:02 -0400 Subject: [PATCH 057/142] Component access derivatives --- gtsam/navigation/NavState.cpp | 15 ++++++ gtsam/navigation/NavState.h | 10 ++-- gtsam/navigation/tests/testNavState.cpp | 65 +++++++++++++++++-------- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index e7c2e0b55..ec35e543a 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -22,6 +22,21 @@ namespace gtsam { #define TIE(R,t,v,x) const Rot3& R = (x).R_;const Point3& t = (x).t_;const Velocity3& v = (x).v_; +const Rot3& NavState::attitude(OptionalJacobian<3, 9> H) const { + if (H) *H << I_3x3, Z_3x3, Z_3x3; + return R_; +} + +const Point3& NavState::position(OptionalJacobian<3, 9> H) const { + if (H) *H << Z_3x3, R(), Z_3x3; + return t_; +} + +const Vector3& NavState::velocity(OptionalJacobian<3, 9> H) const { + if (H) *H << Z_3x3, Z_3x3, R(); + return v_; +} + Matrix7 NavState::matrix() const { Matrix3 R = this->R(); Matrix7 T; diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 0edac0f65..3011f6ac6 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -69,15 +69,19 @@ public: /// @name Component Access /// @{ - const Rot3& attitude() const { + inline const Rot3& attitude() const { return R_; } - const Point3& position() const { + inline const Point3& position() const { return t_; } - const Velocity3& velocity() const { + inline const Velocity3& velocity() const { return v_; } + const Rot3& attitude(OptionalJacobian<3, 9> H) const; + const Point3& position(OptionalJacobian<3, 9> H) const; + const Velocity3& velocity(OptionalJacobian<3, 9> H) const; + const Pose3 pose() const { return Pose3(attitude(), position()); } diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index 8cf14fec4..de8ba3a8d 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -30,28 +30,53 @@ static const Velocity3 kVelocity(0.4, 0.5, 0.6); static const NavState kIdentity; static const NavState kState1(kRotation, kPosition, kVelocity); -const double tol = 1e-5; - +/* ************************************************************************* */ +TEST( NavState, Attitude) { + Matrix39 aH, eH; + Rot3 actual = kState1.attitude(aH); + EXPECT(assert_equal(actual, kRotation)); + eH = numericalDerivative11( + boost::bind(&NavState::attitude, _1, boost::none), kState1); + EXPECT(assert_equal((Matrix )eH, aH)); +} +/* ************************************************************************* */ +TEST( NavState, Position) { + Matrix39 aH, eH; + Point3 actual = kState1.position(aH); + EXPECT(assert_equal(actual, kPosition)); + eH = numericalDerivative11( + boost::bind(&NavState::position, _1, boost::none), kState1); + EXPECT(assert_equal((Matrix )eH, aH)); +} +/* ************************************************************************* */ +TEST( NavState, Velocity) { + Matrix39 aH, eH; + Velocity3 actual = kState1.velocity(aH); + EXPECT(assert_equal(actual, kVelocity)); + eH = numericalDerivative11( + boost::bind(&NavState::velocity, _1, boost::none), kState1); + EXPECT(assert_equal((Matrix )eH, aH)); +} /* ************************************************************************* */ TEST( NavState, MatrixGroup ) { // check roundtrip conversion to 7*7 matrix representation Matrix7 T = kState1.matrix(); - EXPECT(assert_equal(kState1, NavState(T), tol)); + EXPECT(assert_equal(kState1, NavState(T))); // check group product agrees with matrix product NavState state2 = kState1 * kState1; Matrix T2 = T * T; - EXPECT(assert_equal(state2, NavState(T2), tol)); - EXPECT(assert_equal(T2, state2.matrix(), tol)); + EXPECT(assert_equal(state2, NavState(T2))); + EXPECT(assert_equal(T2, state2.matrix())); } /* ************************************************************************* */ TEST( NavState, Manifold ) { // Check zero xi - EXPECT(assert_equal(kIdentity, kIdentity.retract(zero(9)), tol)); - EXPECT(assert_equal(zero(9), kIdentity.localCoordinates(kIdentity), tol)); - EXPECT(assert_equal(kState1, kState1.retract(zero(9)), tol)); - EXPECT(assert_equal(zero(9), kState1.localCoordinates(kState1), tol)); + EXPECT(assert_equal(kIdentity, kIdentity.retract(zero(9)))); + EXPECT(assert_equal(zero(9), kIdentity.localCoordinates(kIdentity))); + EXPECT(assert_equal(kState1, kState1.retract(zero(9)))); + EXPECT(assert_equal(zero(9), kState1.localCoordinates(kState1))); // Check definition of retract as operating on components separately Vector xi(9); @@ -60,12 +85,12 @@ TEST( NavState, Manifold ) { Point3 dt = Point3(xi.segment < 3 > (3)); Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3); NavState state2 = kState1 * NavState(drot, dt, dvel); - EXPECT(assert_equal(state2, kState1.retract(xi), tol)); - EXPECT(assert_equal(xi, kState1.localCoordinates(state2), tol)); + EXPECT(assert_equal(state2, kState1.retract(xi))); + EXPECT(assert_equal(xi, kState1.localCoordinates(state2))); // roundtrip from state2 to state3 and back NavState state3 = state2.retract(xi); - EXPECT(assert_equal(xi, state2.localCoordinates(state3), tol)); + EXPECT(assert_equal(xi, state2.localCoordinates(state3))); // Check derivatives for ChartAtOrigin::Retract Matrix9 aH, eH; @@ -96,24 +121,24 @@ TEST( NavState, Manifold ) { /* ************************************************************************* */ TEST( NavState, Lie ) { // Check zero xi - EXPECT(assert_equal(kIdentity, kIdentity.expmap(zero(9)), tol)); - EXPECT(assert_equal(zero(9), kIdentity.logmap(kIdentity), tol)); - EXPECT(assert_equal(kState1, kState1.expmap(zero(9)), tol)); - EXPECT(assert_equal(zero(9), kState1.logmap(kState1), tol)); + EXPECT(assert_equal(kIdentity, kIdentity.expmap(zero(9)))); + EXPECT(assert_equal(zero(9), kIdentity.logmap(kIdentity))); + EXPECT(assert_equal(kState1, kState1.expmap(zero(9)))); + EXPECT(assert_equal(zero(9), kState1.logmap(kState1))); // Expmap/Logmap roundtrip Vector xi(9); xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; NavState state2 = NavState::Expmap(xi); - EXPECT(assert_equal(xi, NavState::Logmap(state2), tol)); + EXPECT(assert_equal(xi, NavState::Logmap(state2))); // roundtrip from state2 to state3 and back NavState state3 = state2.expmap(xi); - EXPECT(assert_equal(xi, state2.logmap(state3), tol)); + EXPECT(assert_equal(xi, state2.logmap(state3))); // For the expmap/logmap (not necessarily expmap/local) -xi goes other way - EXPECT(assert_equal(state2, state3.expmap(-xi), tol)); - EXPECT(assert_equal(xi, -state3.logmap(state2), tol)); + EXPECT(assert_equal(state2, state3.expmap(-xi))); + EXPECT(assert_equal(xi, -state3.logmap(state2))); } /* ************************************************************************* */ From 4c177c0ce2397737e8ab3c41f95ecc2881eab1b4 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 18:28:31 -0400 Subject: [PATCH 058/142] unrotate now defined for vector --- gtsam/geometry/Rot3.h | 6 ++++ gtsam/navigation/PreintegrationBase.cpp | 43 +++++++------------------ 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/gtsam/geometry/Rot3.h b/gtsam/geometry/Rot3.h index 1dec6eafe..3e95bf04d 100644 --- a/gtsam/geometry/Rot3.h +++ b/gtsam/geometry/Rot3.h @@ -352,6 +352,12 @@ namespace gtsam { Point3 unrotate(const Point3& p, OptionalJacobian<3,3> H1 = boost::none, OptionalJacobian<3,3> H2=boost::none) const; + /// unrotate for Vector3 + Vector3 unrotate(const Vector3& v, OptionalJacobian<3, 3> H1 = boost::none, + OptionalJacobian<3, 3> H2 = boost::none) const { + return unrotate(Point3(v), H1, H2).vector(); + } + /// @} /// @name Group Action on Unit3 /// @{ diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index d2775bfb2..30c1ad0e3 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -154,52 +154,33 @@ Vector9 PreintegrationBase::biasCorrectedDelta( return delta; } -//------------------------------------------------------------------------------ -static Vector3 rotate(const Matrix3& R, const Vector3& p, - OptionalJacobian<3, 3> H1 = boost::none, OptionalJacobian<3, 3> H2 = boost::none) { - if (H1) *H1 = R * skewSymmetric(-p.x(), -p.y(), -p.z()); - if (H2) *H2 = R; - return R * p; -} - -//------------------------------------------------------------------------------ -static Vector3 unrotate(const Matrix3& R, const Vector3& p, - OptionalJacobian<3, 3> H1 = boost::none, OptionalJacobian<3, 3> H2 = boost::none) { - const Matrix3 Rt = R.transpose(); - Vector3 q = Rt * p; - const double wx = q.x(), wy = q.y(), wz = q.z(); - if (H1) *H1 << 0.0, -wz, +wy, +wz, 0.0, -wx, -wy, +wx, 0.0; - if (H2) *H2 = Rt; - return q; -} - //------------------------------------------------------------------------------ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, OptionalJacobian<9, 9> H) const { Vector9 result = Vector9::Zero(); if (H) H->setZero(); if (p().omegaCoriolis) { - const Pose3& pose_i = state_i.pose(); + const Rot3& rot_i = state_i.attitude(); + const Point3& t_i = state_i.position(); const Vector3& vel_i = state_i.velocity(); - const Matrix3 Ri = pose_i.rotation().matrix(); - const Vector3& t_i = state_i.position().vector(); const double dt = deltaTij(), dt2 = dt * dt; const Vector3& omegaCoriolis = *p().omegaCoriolis; Matrix3 D_dP_Ri; - NavState::dR(result) -= unrotate(Ri, omegaCoriolis * dt, H ? &D_dP_Ri : 0); - NavState::dP(result) -= omegaCoriolis.cross(vel_i) * dt2; // NOTE(luca): we got rid of the 2 wrt INS paper - NavState::dV(result) -= 2.0 * omegaCoriolis.cross(vel_i) * dt; + NavState::dR(result) -= rot_i.unrotate(omegaCoriolis * dt, H ? &D_dP_Ri : 0); + NavState::dP(result) -= (dt2 * omegaCoriolis.cross(vel_i)); // NOTE(luca): we got rid of the 2 wrt INS paper + NavState::dV(result) -= ((2.0 * dt) * omegaCoriolis.cross(vel_i)); if (p().use2ndOrderCoriolis) { - Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(t_i)); + Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(t_i.vector())); NavState::dP(result) -= 0.5 * temp * dt2; NavState::dV(result) -= temp * dt; } if (H) { + // Matrix3 Ri = rot_i.matrix(); const Matrix3 omegaCoriolisHat = skewSymmetric(omegaCoriolis); H->block<3,3>(0,0) -= D_dP_Ri; H->block<3,3>(3,6) -= omegaCoriolisHat * dt2; - H->block<3,3>(6,6) -= 2.0 * omegaCoriolisHat * dt; + H->block<3,3>(6,6) -= (2.0 * dt) * omegaCoriolisHat; if (p().use2ndOrderCoriolis) { const Matrix3 omegaCoriolisHat2 = omegaCoriolisHat * omegaCoriolisHat; H->block<3,3>(3,3) -= 0.5 * omegaCoriolisHat2 * dt2; @@ -215,17 +196,16 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, const Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { - const Pose3& pose_i = state_i.pose(); + const Rot3& rot_i = state_i.attitude(); const Vector3& vel_i = state_i.velocity(); - const Matrix3 Ri = pose_i.rotation().matrix(); const double dt = deltaTij(), dt2 = dt * dt; // Rotation, position, and velocity: Vector9 delta; Matrix3 D_dP_Ri, D_dP_bc, D_dV_Ri, D_dV_bc; NavState::dR(delta) = NavState::dR(biasCorrectedDelta); - NavState::dP(delta) = rotate(Ri, NavState::dP(biasCorrectedDelta), D_dP_Ri, D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; - NavState::dV(delta) = rotate(Ri, NavState::dV(biasCorrectedDelta), D_dV_Ri, D_dV_bc) + p().gravity * dt; + NavState::dP(delta) = rot_i.rotate(NavState::dP(biasCorrectedDelta), D_dP_Ri, D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; + NavState::dV(delta) = rot_i.rotate(NavState::dV(biasCorrectedDelta), D_dV_Ri, D_dV_bc) + p().gravity * dt; Matrix9 Hcoriolis; if (p().omegaCoriolis) { @@ -240,6 +220,7 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, } if (H2) { H2->setZero(); + Matrix3 Ri = rot_i.matrix(); H2->block<3,3>(0,0) = I_3x3; H2->block<3,3>(3,3) = Ri; H2->block<3,3>(6,6) = Ri; From a9b4cdbe471aeff2c5ebc856337b5964795e221a Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 21 Jul 2015 20:35:33 -0400 Subject: [PATCH 059/142] coriolis now lives in NavState --- gtsam/navigation/NavState.cpp | 64 ++++++-- gtsam/navigation/NavState.h | 8 + gtsam/navigation/PreintegrationBase.cpp | 193 +++++++++++------------ gtsam/navigation/tests/testImuFactor.cpp | 6 +- gtsam/navigation/tests/testNavState.cpp | 45 ++++++ 5 files changed, 199 insertions(+), 117 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index ec35e543a..37bd7adcc 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -18,22 +18,27 @@ #include +using namespace std; + namespace gtsam { #define TIE(R,t,v,x) const Rot3& R = (x).R_;const Point3& t = (x).t_;const Velocity3& v = (x).v_; const Rot3& NavState::attitude(OptionalJacobian<3, 9> H) const { - if (H) *H << I_3x3, Z_3x3, Z_3x3; + if (H) + *H << I_3x3, Z_3x3, Z_3x3; return R_; } const Point3& NavState::position(OptionalJacobian<3, 9> H) const { - if (H) *H << Z_3x3, R(), Z_3x3; + if (H) + *H << Z_3x3, R(), Z_3x3; return t_; } const Vector3& NavState::velocity(OptionalJacobian<3, 9> H) const { - if (H) *H << Z_3x3, Z_3x3, R(); + if (H) + *H << Z_3x3, Z_3x3, R(); return v_; } @@ -44,7 +49,7 @@ Matrix7 NavState::matrix() const { return T; } -void NavState::print(const std::string& s) const { +void NavState::print(const string& s) const { attitude().print(s + ".R"); position().print(s + ".p"); gtsam::print((Vector) v_, s + ".v"); @@ -101,7 +106,7 @@ NavState NavState::ChartAtOrigin::Retract(const Vector9& xi, Vector9 NavState::ChartAtOrigin::Local(const NavState& x, OptionalJacobian<9, 9> H) { if (H) - throw std::runtime_error( + throw runtime_error( "NavState::ChartOrigin::Local derivative not implemented yet"); Vector9 xi; xi << Rot3::Logmap(x.R_), x.t(), x.v(); @@ -110,7 +115,7 @@ Vector9 NavState::ChartAtOrigin::Local(const NavState& x, NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { if (H) - throw std::runtime_error("NavState::Expmap derivative not implemented yet"); + throw runtime_error("NavState::Expmap derivative not implemented yet"); Eigen::Block n_omega_nb = dR(xi); Eigen::Block v = dP(xi); @@ -119,7 +124,7 @@ NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { // NOTE(frank): See Pose3::Expmap Rot3 nRb = Rot3::Expmap(n_omega_nb); double theta2 = n_omega_nb.dot(n_omega_nb); - if (theta2 > std::numeric_limits::epsilon()) { + if (theta2 > numeric_limits::epsilon()) { // Expmap implements a "screw" motion in the direction of n_omega_nb Vector3 n_t_parallel = n_omega_nb * n_omega_nb.dot(v); // component along rotation axis Vector3 omega_cross_v = n_omega_nb.cross(v); // points towards axis @@ -136,7 +141,7 @@ NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { if (H) - throw std::runtime_error("NavState::Logmap derivative not implemented yet"); + throw runtime_error("NavState::Logmap derivative not implemented yet"); TIE(nRb, n_p, n_v, nTb); Vector3 n_t = n_p.vector(); @@ -145,7 +150,7 @@ Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { Vector9 xi; Vector3 n_omega_nb = Rot3::Logmap(nRb); double theta = n_omega_nb.norm(); - if (theta * theta <= std::numeric_limits::epsilon()) { + if (theta * theta <= numeric_limits::epsilon()) { xi << n_omega_nb, n_t, n_v; } else { Matrix3 W = skewSymmetric(n_omega_nb / theta); @@ -179,4 +184,45 @@ Matrix7 NavState::wedge(const Vector9& xi) { return T; } +// sugar for derivative blocks +#define D_R_R(H) H->block<3,3>(0,0) +#define D_t_t(H) H->block<3,3>(3,3) +#define D_t_v(H) H->block<3,3>(3,6) +#define D_v_t(H) H->block<3,3>(6,3) +#define D_v_v(H) H->block<3,3>(6,6) + +Vector9 NavState::coriolis(const Vector3& omega, double dt, bool secondOrder, + OptionalJacobian<9, 9> H) const { + Vector9 result; + const Rot3& nRb = attitude(); + const Point3& n_t = position(); // derivative is R() ! + const Vector3& n_v = velocity(); // ditto + const double dt2 = dt * dt; + + const Vector3 omega_cross_vel = omega.cross(n_v); + Matrix3 D_dP_R; + dR(result) << -nRb.unrotate(omega * dt, H ? &D_dP_R : 0); + dP(result) << ((-dt2) * omega_cross_vel); // NOTE(luca): we got rid of the 2 wrt INS paper + dV(result) << ((-2.0 * dt) * omega_cross_vel); + if (secondOrder) { + const Vector3 omega_cross2_t = omega.cross(omega.cross(n_t.vector())); + dP(result) -= (0.5 * dt2) * omega_cross2_t; + dV(result) -= dt * omega_cross2_t; + } + if (H) { + const Matrix3 Omega = skewSymmetric(omega); + const Matrix3 D_cross_state = Omega * R(); + H->setZero(); + D_R_R(H) << -D_dP_R; + D_t_v(H) << (-dt2) * D_cross_state; + D_v_v(H) << (-2.0 * dt) * D_cross_state; + if (secondOrder) { + const Matrix3 D_cross2_state = Omega * D_cross_state; + D_t_t(H) -= (0.5 * dt2) * D_cross2_state; + D_v_t(H) -= dt * D_cross2_state; + } + } + return result; +} + } /// namespace gtsam diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 3011f6ac6..12b56fd87 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -200,6 +200,14 @@ public: static Matrix7 wedge(const Vector9& xi); /// @} + /// @name Dynamics + /// @{ + + // Compute tangent space contribution due to coriolis forces + Vector9 coriolis(const Vector3& omega, double dt, bool secondOrder, + OptionalJacobian<9, 9> H) const; + + /// @} private: /// @{ diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 30c1ad0e3..c575e5bf8 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -46,23 +46,24 @@ void PreintegrationBase::print(const string& s) const { } /// Needed for testable -bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) const { - return PreintegratedRotation::equals(other, tol) && - biasHat_.equals(other.biasHat_, tol) && - equal_with_abs_tol(deltaPij_, other.deltaPij_, tol) && - equal_with_abs_tol(deltaVij_, other.deltaVij_, tol) && - equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol) && - equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol) && - equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol) && - equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol); +bool PreintegrationBase::equals(const PreintegrationBase& other, + double tol) const { + return PreintegratedRotation::equals(other, tol) + && biasHat_.equals(other.biasHat_, tol) + && equal_with_abs_tol(deltaPij_, other.deltaPij_, tol) + && equal_with_abs_tol(deltaVij_, other.deltaVij_, tol) + && equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol) + && equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol) + && equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol) + && equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol); } /// Update preintegrated measurements -void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correctedAcc, - const Rot3& incrR, const double deltaT, - OptionalJacobian<9, 9> F) { +void PreintegrationBase::updatePreintegratedMeasurements( + const Vector3& correctedAcc, const Rot3& incrR, const double deltaT, + OptionalJacobian<9, 9> F) { - const Matrix3 dRij = deltaRij().matrix(); // expensive + const Matrix3 dRij = deltaRij().matrix(); // expensive const Vector3 temp = dRij * correctedAcc * deltaT; if (!p().use2ndOrderIntegration) { @@ -74,7 +75,7 @@ void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correcte Matrix3 R_i, F_angles_angles; if (F) - R_i = dRij; // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij + R_i = dRij; // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0); if (F) { @@ -86,19 +87,20 @@ void PreintegrationBase::updatePreintegratedMeasurements(const Vector3& correcte F_pos_angles = Z_3x3; // pos vel angle - *F << // - I_3x3, I_3x3 * deltaT, F_pos_angles, // pos - Z_3x3, I_3x3, F_vel_angles, // vel - Z_3x3, Z_3x3, F_angles_angles; // angle + *F << // + I_3x3, I_3x3 * deltaT, F_pos_angles, // pos + Z_3x3, I_3x3, F_vel_angles, // vel + Z_3x3, Z_3x3, F_angles_angles; // angle } } /// Update Jacobians to be used during preintegration -void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAcc, - const Matrix3& D_Rincr_integratedOmega, - const Rot3& incrR, double deltaT) { - const Matrix3 dRij = deltaRij().matrix(); // expensive - const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT * delRdelBiasOmega(); +void PreintegrationBase::updatePreintegratedJacobians( + const Vector3& correctedAcc, const Matrix3& D_Rincr_integratedOmega, + const Rot3& incrR, double deltaT) { + const Matrix3 dRij = deltaRij().matrix(); // expensive + const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT + * delRdelBiasOmega(); if (!p().use2ndOrderIntegration) { delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT; delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT; @@ -112,8 +114,8 @@ void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAc } void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( - const Vector3& measuredAcc, const Vector3& measuredOmega, Vector3* correctedAcc, - Vector3* correctedOmega) { + const Vector3& measuredAcc, const Vector3& measuredOmega, + Vector3* correctedAcc, Vector3* correctedOmega) { *correctedAcc = biasHat_.correctAccelerometer(measuredAcc); *correctedOmega = biasHat_.correctGyroscope(measuredOmega); @@ -121,10 +123,11 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( // (originally in the IMU frame) into the body frame if (p().body_P_sensor) { Matrix3 body_R_sensor = p().body_P_sensor->rotation().matrix(); - *correctedOmega = body_R_sensor * (*correctedOmega); // rotation rate vector in the body frame + *correctedOmega = body_R_sensor * (*correctedOmega); // rotation rate vector in the body frame Matrix3 body_omega_body__cross = skewSymmetric(*correctedOmega); *correctedAcc = body_R_sensor * (*correctedAcc) - - body_omega_body__cross * body_omega_body__cross * p().body_P_sensor->translation().vector(); + - body_omega_body__cross * body_omega_body__cross + * p().body_P_sensor->translation().vector(); // linear acceleration vector in the body frame } } @@ -157,38 +160,14 @@ Vector9 PreintegrationBase::biasCorrectedDelta( //------------------------------------------------------------------------------ Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, OptionalJacobian<9, 9> H) const { - Vector9 result = Vector9::Zero(); - if (H) H->setZero(); if (p().omegaCoriolis) { - const Rot3& rot_i = state_i.attitude(); - const Point3& t_i = state_i.position(); - const Vector3& vel_i = state_i.velocity(); - const double dt = deltaTij(), dt2 = dt * dt; - - const Vector3& omegaCoriolis = *p().omegaCoriolis; - Matrix3 D_dP_Ri; - NavState::dR(result) -= rot_i.unrotate(omegaCoriolis * dt, H ? &D_dP_Ri : 0); - NavState::dP(result) -= (dt2 * omegaCoriolis.cross(vel_i)); // NOTE(luca): we got rid of the 2 wrt INS paper - NavState::dV(result) -= ((2.0 * dt) * omegaCoriolis.cross(vel_i)); - if (p().use2ndOrderCoriolis) { - Vector3 temp = omegaCoriolis.cross(omegaCoriolis.cross(t_i.vector())); - NavState::dP(result) -= 0.5 * temp * dt2; - NavState::dV(result) -= temp * dt; - } - if (H) { - // Matrix3 Ri = rot_i.matrix(); - const Matrix3 omegaCoriolisHat = skewSymmetric(omegaCoriolis); - H->block<3,3>(0,0) -= D_dP_Ri; - H->block<3,3>(3,6) -= omegaCoriolisHat * dt2; - H->block<3,3>(6,6) -= (2.0 * dt) * omegaCoriolisHat; - if (p().use2ndOrderCoriolis) { - const Matrix3 omegaCoriolisHat2 = omegaCoriolisHat * omegaCoriolisHat; - H->block<3,3>(3,3) -= 0.5 * omegaCoriolisHat2 * dt2; - H->block<3,3>(6,3) -= omegaCoriolisHat2 * dt; - } - } + return state_i.coriolis(*(p().omegaCoriolis), deltaTij(), + p().use2ndOrderCoriolis, H); + } else { + if (H) + H->setZero(); + return Vector9::Zero(); } - return result; } //------------------------------------------------------------------------------ @@ -204,8 +183,10 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, Vector9 delta; Matrix3 D_dP_Ri, D_dP_bc, D_dV_Ri, D_dV_bc; NavState::dR(delta) = NavState::dR(biasCorrectedDelta); - NavState::dP(delta) = rot_i.rotate(NavState::dP(biasCorrectedDelta), D_dP_Ri, D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; - NavState::dV(delta) = rot_i.rotate(NavState::dV(biasCorrectedDelta), D_dV_Ri, D_dV_bc) + p().gravity * dt; + NavState::dP(delta) = rot_i.rotate(NavState::dP(biasCorrectedDelta), D_dP_Ri, + D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; + NavState::dV(delta) = rot_i.rotate(NavState::dV(biasCorrectedDelta), D_dV_Ri, + D_dV_bc) + p().gravity * dt; Matrix9 Hcoriolis; if (p().omegaCoriolis) { @@ -213,17 +194,18 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, } if (H1) { H1->setZero(); - H1->block<3,3>(3,0) = D_dP_Ri; - H1->block<3,3>(3,6) = I_3x3 * dt; - H1->block<3,3>(6,0) = D_dV_Ri; - if (p().omegaCoriolis) *H1 += Hcoriolis; + H1->block<3, 3>(3, 0) = D_dP_Ri; + H1->block<3, 3>(3, 6) = I_3x3 * dt; + H1->block<3, 3>(6, 0) = D_dV_Ri; + if (p().omegaCoriolis) + *H1 += Hcoriolis; } if (H2) { H2->setZero(); Matrix3 Ri = rot_i.matrix(); - H2->block<3,3>(0,0) = I_3x3; - H2->block<3,3>(3,3) = Ri; - H2->block<3,3>(6,6) = Ri; + H2->block<3, 3>(0, 0) = I_3x3; + H2->block<3, 3>(3, 3) = Ri; + H2->block<3, 3>(6, 6) = Ri; } return delta; @@ -279,14 +261,11 @@ static Vector9 computeError(const NavState& state_i, const NavState& state_j, } //------------------------------------------------------------------------------ -Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, - const Pose3& pose_j, const Vector3& vel_j, - const imuBias::ConstantBias& bias_i, - OptionalJacobian<9, 6> H1, - OptionalJacobian<9, 3> H2, - OptionalJacobian<9, 6> H3, - OptionalJacobian<9, 3> H4, - OptionalJacobian<9, 6> H5) const { +Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, + const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, + const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H1, + OptionalJacobian<9, 3> H2, OptionalJacobian<9, 6> H3, + OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5) const { // we give some shorter name to rotations and translations const Rot3& rot_i = pose_i.rotation(); @@ -305,11 +284,13 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const /// Predict state at time j Matrix99 D_predict_state; Matrix96 D_predict_bias; - NavState predictedState_j = predict(state_i, bias_i, D_predict_state, D_predict_bias); + NavState predictedState_j = predict(state_i, bias_i, D_predict_state, + D_predict_bias); // Evaluate residual error, according to [3] // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fp = Ri.transpose() * (pos_j - predictedState_j.pose().translation().vector()); + const Vector3 fp = Ri.transpose() + * (pos_j - predictedState_j.pose().translation().vector()); // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance const Vector3 fv = Ri.transpose() * (vel_j - predictedState_j.velocity()); @@ -324,66 +305,68 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const // Residual rotation error Matrix3 D_cDeltaRij_cOmega; - const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, H1 || H5 ? &D_cDeltaRij_cOmega : 0); + const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, + H1 || H5 ? &D_cDeltaRij_cOmega : 0); const Rot3 RiBetweenRj = rot_i.between(rot_j); const Rot3 fRrot = correctedDeltaRij.between(RiBetweenRj); Matrix3 D_fR_fRrot; Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); - const double dt = deltaTij(), dt2 = dt*dt; + const double dt = deltaTij(), dt2 = dt * dt; Matrix3 RitOmegaCoriolisHat = Z_3x3; if ((H1 || H2) && p().omegaCoriolis) - RitOmegaCoriolisHat = Ri.transpose() * skewSymmetric(*p().omegaCoriolis); + RitOmegaCoriolisHat = Ri.transpose() * skewSymmetric(*p().omegaCoriolis); if (H1) { const Matrix3 D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis); Matrix3 dfPdPi = -I_3x3, dfVdPi = Z_3x3; if (p().use2ndOrderCoriolis) { // this is the same as: Ri.transpose() * p().omegaCoriolisHat * p().omegaCoriolisHat * Ri - const Matrix3 temp = RitOmegaCoriolisHat * (-RitOmegaCoriolisHat.transpose()); + const Matrix3 temp = RitOmegaCoriolisHat + * (-RitOmegaCoriolisHat.transpose()); dfPdPi += 0.5 * temp * dt2; dfVdPi += temp * dt; } - (*H1) << - D_fR_fRrot * (-rot_j.between(rot_i).matrix() - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi - Z_3x3, // dfR/dPi - skewSymmetric(fp + NavState::dP(biasCorrected)), // dfP/dRi - dfPdPi, // dfP/dPi - skewSymmetric(fv + NavState::dV(biasCorrected)), // dfV/dRi - dfVdPi; // dfV/dPi + (*H1) + << D_fR_fRrot + * (-rot_j.between(rot_i).matrix() + - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi + Z_3x3, // dfR/dPi + skewSymmetric(fp + NavState::dP(biasCorrected)), // dfP/dRi + dfPdPi, // dfP/dPi + skewSymmetric(fv + NavState::dV(biasCorrected)), // dfV/dRi + dfVdPi; // dfV/dPi } if (H2) { - (*H2) << - Z_3x3, // dfR/dVi - -Ri.transpose() * dt + RitOmegaCoriolisHat * dt2, // dfP/dVi - -Ri.transpose() + 2 * RitOmegaCoriolisHat * dt; // dfV/dVi + (*H2) << Z_3x3, // dfR/dVi + -Ri.transpose() * dt + RitOmegaCoriolisHat * dt2, // dfP/dVi + -Ri.transpose() + 2 * RitOmegaCoriolisHat * dt; // dfV/dVi } if (H3) { - (*H3) << - D_fR_fRrot, Z_3x3, // dfR/dPosej + (*H3) << D_fR_fRrot, Z_3x3, // dfR/dPosej Z_3x3, Ri.transpose() * rot_j.matrix(), // dfP/dPosej - Matrix::Zero(3, 6); // dfV/dPosej + Matrix::Zero(3, 6); // dfV/dPosej } if (H4) { - (*H4) << - Z_3x3, // dfR/dVj - Z_3x3, // dfP/dVj + (*H4) << Z_3x3, // dfR/dVj + Z_3x3, // dfP/dVj Ri.transpose(); // dfV/dVj } if (H5) { - const Matrix36 JbiasOmega = D_cDeltaRij_cOmega * D_biasCorrected_bias.middleRows<3>(0); - (*H5) << - -D_fR_fRrot * fRrot.inverse().matrix() * JbiasOmega, // dfR/dBias - -D_biasCorrected_bias.middleRows<3>(3), // dfP/dBias - -D_biasCorrected_bias.middleRows<3>(6); // dfV/dBias + const Matrix36 JbiasOmega = D_cDeltaRij_cOmega + * D_biasCorrected_bias.middleRows<3>(0); + (*H5) << -D_fR_fRrot * fRrot.inverse().matrix() * JbiasOmega, // dfR/dBias + -D_biasCorrected_bias.middleRows<3>(3), // dfP/dBias + -D_biasCorrected_bias.middleRows<3>(6); // dfV/dBias } // TODO(frank): Do everything via derivatives of function below return computeError(state_i, state_j, predictedState_j); } //------------------------------------------------------------------------------ -PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i, const Vector3& gravity, const Vector3& omegaCoriolis, +PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, + const Vector3& vel_i, const imuBias::ConstantBias& bias_i, + const Vector3& gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // NOTE(frank): parameters are supposed to be constant, below is only provided for compatibility boost::shared_ptr q = boost::make_shared(p()); @@ -393,4 +376,4 @@ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& p_ = q; return PoseVelocityBias(predict(NavState(pose_i, vel_i), bias_i), bias_i); } -} /// namespace gtsam +} /// namespace gtsam diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 944a38156..fa97f5664 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -282,7 +282,7 @@ TEST(ImuFactor, PreintegrationBaseMethods) { EXPECT(assert_equal(expectedH, actualH)); } { - Matrix99 actualH; + Matrix9 actualH; pim.integrateCoriolis(state1, actualH); Matrix expectedH = numericalDerivative11( boost::bind(&PreintegrationBase::integrateCoriolis, pim, _1, @@ -290,7 +290,7 @@ TEST(ImuFactor, PreintegrationBaseMethods) { EXPECT(assert_equal(expectedH, actualH)); } { - Matrix99 aH1, aH2; + Matrix9 aH1, aH2; Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias); pim.recombinedPrediction(state1, biasCorrectedDelta, aH1, aH2); Matrix eH1 = numericalDerivative11( @@ -303,7 +303,7 @@ TEST(ImuFactor, PreintegrationBaseMethods) { EXPECT(assert_equal(eH2, aH2)); } { - Matrix99 aH1; + Matrix9 aH1; Matrix96 aH2; pim.predict(state1, bias, aH1, aH2); Matrix eH1 = numericalDerivative11( diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index de8ba3a8d..c5b46831e 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -141,6 +141,51 @@ TEST( NavState, Lie ) { EXPECT(assert_equal(xi, -state3.logmap(state2))); } +/* ************************************************************************* */ +TEST(NavState, Coriolis) { + Matrix9 actualH; + Vector3 omegaCoriolis(0.02, 0.03, 0.04); + double dt = 0.5; + // first-order + bool secondOrder = false; + kState1.coriolis(omegaCoriolis, dt, secondOrder, actualH); + Matrix expectedH = numericalDerivative11( + boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, + boost::none), kState1); + EXPECT(assert_equal(expectedH, actualH)); + // second-order + secondOrder = true; + kState1.coriolis(omegaCoriolis, dt, secondOrder, actualH); + expectedH = numericalDerivative11( + boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, + boost::none), kState1); + EXPECT(assert_equal(expectedH, actualH)); +} + +/* ************************************************************************* */ +TEST(NavState, Coriolis2) { + NavState state2(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), + Point3(5.0, 1.0, -50.0), Vector3(0.5, 0.0, 0.0)); + + Matrix9 actualH; + Vector3 omegaCoriolis(0.02, 0.03, 0.04); + double dt = 2.0; + // first-order + bool secondOrder = false; + state2.coriolis(omegaCoriolis, dt, secondOrder, actualH); + Matrix expectedH = numericalDerivative11( + boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, + boost::none), state2); + EXPECT(assert_equal(expectedH, actualH)); + // second-order + secondOrder = true; + state2.coriolis(omegaCoriolis, dt, secondOrder, actualH); + expectedH = numericalDerivative11( + boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, + boost::none), state2); + EXPECT(assert_equal(expectedH, actualH)); +} + /* ************************************************************************* */ int main() { TestResult tr; From 0ced22841390b44cc3ce093ea0fcc217f222cfcb Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 00:36:32 -0400 Subject: [PATCH 060/142] Regression test for predict with arbitrary measurements --- gtsam/navigation/tests/testImuFactor.cpp | 53 ++++++++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 6b66be342..1f1815fba 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -38,6 +38,7 @@ using symbol_shorthand::B; static const Vector3 kGravity(0, 0, 9.81); static const Vector3 kZeroOmegaCoriolis(0, 0, 0); +static const Vector3 kNonZeroOmegaCoriolis(0, 0.1, 0.1); /* ************************************************************************* */ namespace { @@ -282,8 +283,6 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { Point3(5.5, 1.0, -50.0)); // Measurements - Vector3 nonZeroOmegaCoriolis; - nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() @@ -298,7 +297,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, - nonZeroOmegaCoriolis); + kNonZeroOmegaCoriolis); Values values; values.insert(X(1), x1); @@ -322,8 +321,6 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { Point3(5.5, 1.0, -50.0)); // Measurements - Vector3 nonZeroOmegaCoriolis; - nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() @@ -340,7 +337,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { Pose3 bodyPsensor = Pose3(); bool use2ndOrderCoriolis = true; ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, - nonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis); + kNonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis); Values values; values.insert(X(1), x1); @@ -749,8 +746,6 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { Vector3 v2(Vector3(0.5, 0.0, 0.0)); // Measurements - Vector3 nonZeroOmegaCoriolis; - nonZeroOmegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() @@ -769,7 +764,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, - nonZeroOmegaCoriolis); + kNonZeroOmegaCoriolis); Values values; values.insert(X(1), x1); @@ -860,6 +855,46 @@ TEST(ImuFactor, PredictRotation) { EXPECT(assert_equal(Vector(expectedVelocity), Vector(v2))); } +/* ************************************************************************* */ +TEST(ImuFactor, PredictArbitrary) { + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) + + // Measurements + Vector3 measuredOmega(M_PI / 10, M_PI / 10, M_PI / 10); + Vector3 measuredAcc(0.1, 0.2, -9.81); + double deltaT = 0.001; + + ImuFactor::PreintegratedMeasurements pre_int_data( + imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), + kMeasuredAccCovariance, kMeasuredOmegaCovariance, + kIntegrationErrorCovariance, true); + + for (int i = 0; i < 1000; ++i) + pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + + // Create factor + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + kZeroOmegaCoriolis); + + // Predict + Pose3 x1, x2; + Vector3 v1 = Vector3(0, 0.0, 0.0); + Vector3 v2; + ImuFactor::Predict(x1, v1, x2, v2, bias, factor.preintegratedMeasurements(), + kGravity, kZeroOmegaCoriolis); + + // Regression test for Imu Refactor + Rot3 expectedR( // + +0.903715275, -0.250741668, 0.347026393, // + +0.347026393, 0.903715275, -0.250741668, // + -0.250741668, 0.347026393, 0.903715275); + Point3 expectedT(-0.505517319, 0.569413747, 0.0861035711); + Vector3 expectedV(-1.59121524, 1.55353139, 0.3376838540); + Pose3 expectedPose(expectedR, expectedT); + EXPECT(assert_equal(expectedPose, x2, 1e-7)); + EXPECT(assert_equal(Vector(expectedV), v2, 1e-7)); +} + /* ************************************************************************* */ int main() { TestResult tr; From 2a38ed9603e7527bc892d95e1956d15dd37b6517 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 01:01:19 -0400 Subject: [PATCH 061/142] Additive version for coriolis forces --- gtsam/navigation/NavState.cpp | 52 ++++++++++++++++++------ gtsam/navigation/NavState.h | 9 +++- gtsam/navigation/PreintegrationBase.cpp | 40 +++++++----------- gtsam/navigation/PreintegrationBase.h | 4 -- gtsam/navigation/tests/testImuFactor.cpp | 8 ---- 5 files changed, 61 insertions(+), 52 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 37bd7adcc..d41b28bf0 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -24,24 +24,28 @@ namespace gtsam { #define TIE(R,t,v,x) const Rot3& R = (x).R_;const Point3& t = (x).t_;const Velocity3& v = (x).v_; +//------------------------------------------------------------------------------ const Rot3& NavState::attitude(OptionalJacobian<3, 9> H) const { if (H) *H << I_3x3, Z_3x3, Z_3x3; return R_; } +//------------------------------------------------------------------------------ const Point3& NavState::position(OptionalJacobian<3, 9> H) const { if (H) *H << Z_3x3, R(), Z_3x3; return t_; } +//------------------------------------------------------------------------------ const Vector3& NavState::velocity(OptionalJacobian<3, 9> H) const { if (H) *H << Z_3x3, Z_3x3, R(); return v_; } +//------------------------------------------------------------------------------ Matrix7 NavState::matrix() const { Matrix3 R = this->R(); Matrix7 T; @@ -49,29 +53,34 @@ Matrix7 NavState::matrix() const { return T; } +//------------------------------------------------------------------------------ void NavState::print(const string& s) const { attitude().print(s + ".R"); position().print(s + ".p"); gtsam::print((Vector) v_, s + ".v"); } +//------------------------------------------------------------------------------ bool NavState::equals(const NavState& other, double tol) const { return R_.equals(other.R_, tol) && t_.equals(other.t_, tol) && equal_with_abs_tol(v_, other.v_, tol); } +//------------------------------------------------------------------------------ NavState NavState::inverse() const { TIE(nRb, n_t, n_v, *this); const Rot3 bRn = nRb.inverse(); return NavState(bRn, -(bRn * n_t), -(bRn * n_v)); } +//------------------------------------------------------------------------------ NavState NavState::operator*(const NavState& bTc) const { TIE(nRb, n_t, n_v, *this); TIE(bRc, b_t, b_v, bTc); return NavState(nRb * bRc, nRb * b_t + n_t, nRb * b_v + n_v); } +//------------------------------------------------------------------------------ NavState::PositionAndVelocity // NavState::operator*(const PositionAndVelocity& b_tv) const { TIE(nRb, n_t, n_v, *this); @@ -80,14 +89,17 @@ NavState::operator*(const PositionAndVelocity& b_tv) const { return PositionAndVelocity(nRb * b_t + n_t, nRb * b_v + n_v); } +//------------------------------------------------------------------------------ Point3 NavState::operator*(const Point3& b_t) const { return Point3(R_ * b_t + t_); } +//------------------------------------------------------------------------------ Velocity3 NavState::operator*(const Velocity3& b_v) const { return Velocity3(R_ * b_v + v_); } +//------------------------------------------------------------------------------ NavState NavState::ChartAtOrigin::Retract(const Vector9& xi, OptionalJacobian<9, 9> H) { Matrix3 D_R_xi; @@ -103,6 +115,7 @@ NavState NavState::ChartAtOrigin::Retract(const Vector9& xi, return result; } +//------------------------------------------------------------------------------ Vector9 NavState::ChartAtOrigin::Local(const NavState& x, OptionalJacobian<9, 9> H) { if (H) @@ -113,6 +126,7 @@ Vector9 NavState::ChartAtOrigin::Local(const NavState& x, return xi; } +//------------------------------------------------------------------------------ NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { if (H) throw runtime_error("NavState::Expmap derivative not implemented yet"); @@ -139,6 +153,7 @@ NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) { } } +//------------------------------------------------------------------------------ Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { if (H) throw runtime_error("NavState::Logmap derivative not implemented yet"); @@ -166,6 +181,7 @@ Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) { return xi; } +//------------------------------------------------------------------------------ Matrix9 NavState::AdjointMap() const { // NOTE(frank): See Pose3::AdjointMap const Matrix3 nRb = R(); @@ -177,6 +193,7 @@ Matrix9 NavState::AdjointMap() const { return adj; } +//------------------------------------------------------------------------------ Matrix7 NavState::wedge(const Vector9& xi) { const Matrix3 Omega = skewSymmetric(dR(xi)); Matrix7 T; @@ -184,6 +201,7 @@ Matrix7 NavState::wedge(const Vector9& xi) { return T; } +//------------------------------------------------------------------------------ // sugar for derivative blocks #define D_R_R(H) H->block<3,3>(0,0) #define D_t_t(H) H->block<3,3>(3,3) @@ -191,9 +209,9 @@ Matrix7 NavState::wedge(const Vector9& xi) { #define D_v_t(H) H->block<3,3>(6,3) #define D_v_v(H) H->block<3,3>(6,6) -Vector9 NavState::coriolis(const Vector3& omega, double dt, bool secondOrder, - OptionalJacobian<9, 9> H) const { - Vector9 result; +//------------------------------------------------------------------------------ +void NavState::addCoriolis(Vector9* xi, const Vector3& omega, double dt, + bool secondOrder, OptionalJacobian<9, 9> H) const { const Rot3& nRb = attitude(); const Point3& n_t = position(); // derivative is R() ! const Vector3& n_v = velocity(); // ditto @@ -201,28 +219,38 @@ Vector9 NavState::coriolis(const Vector3& omega, double dt, bool secondOrder, const Vector3 omega_cross_vel = omega.cross(n_v); Matrix3 D_dP_R; - dR(result) << -nRb.unrotate(omega * dt, H ? &D_dP_R : 0); - dP(result) << ((-dt2) * omega_cross_vel); // NOTE(luca): we got rid of the 2 wrt INS paper - dV(result) << ((-2.0 * dt) * omega_cross_vel); + dR(*xi) -= nRb.unrotate(omega * dt, H ? &D_dP_R : 0); + dP(*xi) -= (dt2 * omega_cross_vel); // NOTE(luca): we got rid of the 2 wrt INS paper + dV(*xi) -= ((2.0 * dt) * omega_cross_vel); if (secondOrder) { const Vector3 omega_cross2_t = omega.cross(omega.cross(n_t.vector())); - dP(result) -= (0.5 * dt2) * omega_cross2_t; - dV(result) -= dt * omega_cross2_t; + dP(*xi) -= (0.5 * dt2) * omega_cross2_t; + dV(*xi) -= dt * omega_cross2_t; } if (H) { const Matrix3 Omega = skewSymmetric(omega); const Matrix3 D_cross_state = Omega * R(); H->setZero(); - D_R_R(H) << -D_dP_R; - D_t_v(H) << (-dt2) * D_cross_state; - D_v_v(H) << (-2.0 * dt) * D_cross_state; + D_R_R(H) -= D_dP_R; + D_t_v(H) -= dt2 * D_cross_state; + D_v_v(H) -= (2.0 * dt) * D_cross_state; if (secondOrder) { const Matrix3 D_cross2_state = Omega * D_cross_state; D_t_t(H) -= (0.5 * dt2) * D_cross2_state; D_v_t(H) -= dt * D_cross2_state; } } - return result; +} + +//------------------------------------------------------------------------------ +Vector9 NavState::coriolis(const Vector3& omega, double dt, bool secondOrder, + OptionalJacobian<9, 9> H) const { + Vector9 xi; + xi.setZero(); + if (H) + H->setZero(); + addCoriolis(&xi, omega, dt, secondOrder, H); + return xi; } } /// namespace gtsam diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 12b56fd87..290cc0bd5 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -204,8 +204,13 @@ public: /// @{ // Compute tangent space contribution due to coriolis forces - Vector9 coriolis(const Vector3& omega, double dt, bool secondOrder, - OptionalJacobian<9, 9> H) const; + Vector9 coriolis(const Vector3& omega, double dt, bool secondOrder = false, + OptionalJacobian<9, 9> H = boost::none) const; + + // Add tangent space contribution due to coriolis forces + // Additively modifies xi and H in place (if given) + void addCoriolis(Vector9* xi, const Vector3& omega, double dt, + bool secondOrder = false, OptionalJacobian<9, 9> H = boost::none) const; /// @} diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index c575e5bf8..1e3f3520e 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -140,12 +140,12 @@ Vector9 PreintegrationBase::biasCorrectedDelta( Rot3 deltaRij = PreintegratedRotation::biascorrectedDeltaRij( biasIncr.gyroscope(), H ? &D_deltaRij_bias : 0); - Vector9 delta; + Vector9 xi; Matrix3 D_dR_deltaRij; - NavState::dR(delta) = Rot3::Logmap(deltaRij, H ? &D_dR_deltaRij : 0); - NavState::dP(delta) = deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() + NavState::dR(xi) = Rot3::Logmap(deltaRij, H ? &D_dR_deltaRij : 0); + NavState::dP(xi) = deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() + delPdelBiasOmega_ * biasIncr.gyroscope(); - NavState::dV(delta) = deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() + NavState::dV(xi) = deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() + delVdelBiasOmega_ * biasIncr.gyroscope(); if (H) { Matrix36 D_dR_bias, D_dP_bias, D_dV_bias; @@ -154,20 +154,7 @@ Vector9 PreintegrationBase::biasCorrectedDelta( D_dV_bias << delVdelBiasAcc_, delVdelBiasOmega_; (*H) << D_dR_bias, D_dP_bias, D_dV_bias; } - return delta; -} - -//------------------------------------------------------------------------------ -Vector9 PreintegrationBase::integrateCoriolis(const NavState& state_i, - OptionalJacobian<9, 9> H) const { - if (p().omegaCoriolis) { - return state_i.coriolis(*(p().omegaCoriolis), deltaTij(), - p().use2ndOrderCoriolis, H); - } else { - if (H) - H->setZero(); - return Vector9::Zero(); - } + return xi; } //------------------------------------------------------------------------------ @@ -180,17 +167,18 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, const double dt = deltaTij(), dt2 = dt * dt; // Rotation, position, and velocity: - Vector9 delta; + Vector9 xi; Matrix3 D_dP_Ri, D_dP_bc, D_dV_Ri, D_dV_bc; - NavState::dR(delta) = NavState::dR(biasCorrectedDelta); - NavState::dP(delta) = rot_i.rotate(NavState::dP(biasCorrectedDelta), D_dP_Ri, + NavState::dR(xi) = NavState::dR(biasCorrectedDelta); + NavState::dP(xi) = rot_i.rotate(NavState::dP(biasCorrectedDelta), D_dP_Ri, D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; - NavState::dV(delta) = rot_i.rotate(NavState::dV(biasCorrectedDelta), D_dV_Ri, + NavState::dV(xi) = rot_i.rotate(NavState::dV(biasCorrectedDelta), D_dV_Ri, D_dV_bc) + p().gravity * dt; Matrix9 Hcoriolis; if (p().omegaCoriolis) { - delta += integrateCoriolis(state_i, H1 ? &Hcoriolis : 0); + state_i.addCoriolis(&xi, *(p().omegaCoriolis), deltaTij(), + p().use2ndOrderCoriolis, H1 ? &Hcoriolis : 0); } if (H1) { H1->setZero(); @@ -208,7 +196,7 @@ Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, H2->block<3, 3>(6, 6) = Ri; } - return delta; + return xi; } //------------------------------------------------------------------------------ @@ -219,10 +207,10 @@ NavState PreintegrationBase::predict(const NavState& state_i, Vector9 biasCorrected = biasCorrectedDelta(bias_i, H2 ? &D_biasCorrected_bias : 0); Matrix9 D_delta_state, D_delta_biasCorrected; - Vector9 delta = recombinedPrediction(state_i, biasCorrected, + Vector9 xi = recombinedPrediction(state_i, biasCorrected, H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0); Matrix9 D_predict_state, D_predict_delta; - NavState state_j = state_i.retract(delta, D_predict_state, D_predict_delta); + NavState state_j = state_i.retract(xi, D_predict_state, D_predict_delta); if (H1) *H1 = D_predict_state + D_predict_delta * D_delta_state; if (H2) diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 359db8c83..8e7841f7c 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -152,10 +152,6 @@ class PreintegrationBase : public PreintegratedRotation { Vector9 biasCorrectedDelta(const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H = boost::none) const; - /// Integrate coriolis correction in body frame state_i - Vector9 integrateCoriolis(const NavState& state_i, - OptionalJacobian<9, 9> H = boost::none) const; - /// Recombine the preintegration, gravity, and coriolis in a single NavState tangent vector Vector9 recombinedPrediction(const NavState& state_i, const Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1 = boost::none, diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 070b6bd76..8ed2fb135 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -282,14 +282,6 @@ TEST(ImuFactor, PreintegrationBaseMethods) { boost::none), bias); EXPECT(assert_equal(expectedH, actualH)); } - { - Matrix9 actualH; - pim.integrateCoriolis(state1, actualH); - Matrix expectedH = numericalDerivative11( - boost::bind(&PreintegrationBase::integrateCoriolis, pim, _1, - boost::none), state1); - EXPECT(assert_equal(expectedH, actualH)); - } { Matrix9 aH1, aH2; Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias); From 8ae4e2afd9d3752d70668fb86e5bd620fd97678e Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 01:56:13 -0400 Subject: [PATCH 062/142] A fully functioning predict from preintegrated tangent vector --- gtsam/navigation/NavState.cpp | 121 ++++++++++++++++++------ gtsam/navigation/NavState.h | 20 ++-- gtsam/navigation/tests/testNavState.cpp | 74 +++++++++------ 3 files changed, 149 insertions(+), 66 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index d41b28bf0..aa33fe858 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -203,54 +203,115 @@ Matrix7 NavState::wedge(const Vector9& xi) { //------------------------------------------------------------------------------ // sugar for derivative blocks -#define D_R_R(H) H->block<3,3>(0,0) -#define D_t_t(H) H->block<3,3>(3,3) -#define D_t_v(H) H->block<3,3>(3,6) -#define D_v_t(H) H->block<3,3>(6,3) -#define D_v_v(H) H->block<3,3>(6,6) +#define D_R_R(H) (H)->block<3,3>(0,0) +#define D_R_t(H) (H)->block<3,3>(0,3) +#define D_R_v(H) (H)->block<3,3>(0,6) +#define D_t_R(H) (H)->block<3,3>(3,0) +#define D_t_t(H) (H)->block<3,3>(3,3) +#define D_t_v(H) (H)->block<3,3>(3,6) +#define D_v_R(H) (H)->block<3,3>(6,0) +#define D_v_t(H) (H)->block<3,3>(6,3) +#define D_v_v(H) (H)->block<3,3>(6,6) //------------------------------------------------------------------------------ -void NavState::addCoriolis(Vector9* xi, const Vector3& omega, double dt, - bool secondOrder, OptionalJacobian<9, 9> H) const { - const Rot3& nRb = attitude(); - const Point3& n_t = position(); // derivative is R() ! - const Vector3& n_v = velocity(); // ditto +Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder, + OptionalJacobian<9, 9> H) const { + TIE(nRb, n_t, n_v, *this); const double dt2 = dt * dt; - const Vector3 omega_cross_vel = omega.cross(n_v); + + Vector9 xi; Matrix3 D_dP_R; - dR(*xi) -= nRb.unrotate(omega * dt, H ? &D_dP_R : 0); - dP(*xi) -= (dt2 * omega_cross_vel); // NOTE(luca): we got rid of the 2 wrt INS paper - dV(*xi) -= ((2.0 * dt) * omega_cross_vel); + dR(xi) << nRb.unrotate((-dt) * omega, H ? &D_dP_R : 0); + dP(xi) << ((-dt2) * omega_cross_vel); // NOTE(luca): we got rid of the 2 wrt INS paper + dV(xi) << ((-2.0 * dt) * omega_cross_vel); if (secondOrder) { const Vector3 omega_cross2_t = omega.cross(omega.cross(n_t.vector())); - dP(*xi) -= (0.5 * dt2) * omega_cross2_t; - dV(*xi) -= dt * omega_cross2_t; + dP(xi) -= (0.5 * dt2) * omega_cross2_t; + dV(xi) -= dt * omega_cross2_t; } if (H) { + H->setZero(); const Matrix3 Omega = skewSymmetric(omega); const Matrix3 D_cross_state = Omega * R(); H->setZero(); - D_R_R(H) -= D_dP_R; - D_t_v(H) -= dt2 * D_cross_state; - D_v_v(H) -= (2.0 * dt) * D_cross_state; + D_R_R(H) << D_dP_R; + D_t_v(H) << (-dt2) * D_cross_state; + D_v_v(H) << (-2.0 * dt) * D_cross_state; if (secondOrder) { const Matrix3 D_cross2_state = Omega * D_cross_state; D_t_t(H) -= (0.5 * dt2) * D_cross2_state; D_v_t(H) -= dt * D_cross2_state; } } -} - -//------------------------------------------------------------------------------ -Vector9 NavState::coriolis(const Vector3& omega, double dt, bool secondOrder, - OptionalJacobian<9, 9> H) const { - Vector9 xi; - xi.setZero(); - if (H) - H->setZero(); - addCoriolis(&xi, omega, dt, secondOrder, H); return xi; } -} /// namespace gtsam +//------------------------------------------------------------------------------ +Vector9 NavState::predictXi(const Vector9& pim, double dt, + const Vector3& gravity, boost::optional omegaCoriolis, + bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, + OptionalJacobian<9, 9> H2) const { + const Rot3& nRb = R_; + const Velocity3& n_v = v_; // derivative is Ri ! + const double dt2 = dt * dt; + + Vector9 delta; + Matrix3 D_dP_Ri, D_dP_dP, D_dV_Ri, D_dV_dV; + dR(delta) = dR(pim); + // TODO(frank): + // - why do we integrate n_v here ? + // - the dP and dV should be in body ! How come semi-retract works out ?? + // - should we rename t_ to p_? if not, we should rename dP do dT + dP(delta) = nRb.rotate(dP(pim), H1 ? &D_dP_Ri : 0, H2 ? &D_dP_dP : 0) + + n_v * dt + 0.5 * gravity * dt2; + dV(delta) = nRb.rotate(dV(pim), H1 ? &D_dV_Ri : 0, H2 ? &D_dV_dV : 0) + + gravity * dt; + + if (omegaCoriolis) { + delta += coriolis(dt, *omegaCoriolis, use2ndOrderCoriolis, H1); + } + + if (H1 || H2) { + Matrix3 Ri = nRb.matrix(); + + if (H1) { + if (!omegaCoriolis) + H1->setZero(); // if coriolis H1 is already initialized + D_t_R(H1) += D_dP_Ri; + D_t_v(H1) += I_3x3 * dt * Ri; + D_v_R(H1) += D_dV_Ri; + } + if (H2) { + H2->setZero(); + D_R_R(H2) << I_3x3; + D_t_t(H2) << D_dP_dP; + D_v_v(H2) << D_dV_dV; + } + } + + return delta; +} +//------------------------------------------------------------------------------ +NavState NavState::predict(const Vector9& pim, double dt, + const Vector3& gravity, boost::optional omegaCoriolis, + bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, + OptionalJacobian<9, 9> H2) const { + + Matrix9 D_delta_state, D_delta_pim; + Vector9 delta = predictXi(pim, dt, gravity, omegaCoriolis, + use2ndOrderCoriolis, H1 ? &D_delta_state : 0, H2 ? &D_delta_pim : 0); + + Matrix9 D_predicted_state, D_predicted_delta; + NavState predicted = retract(delta, H1 ? &D_predicted_state : 0, + H1 || H2 ? &D_predicted_delta : 0); + // TODO(frank): the derivatives of retract are very sparse: inline below: + if (H1) + *H1 = D_predicted_state + D_predicted_delta * D_delta_state; + if (H2) + *H2 = D_predicted_delta * D_delta_pim; + return predicted; +} +//------------------------------------------------------------------------------ + +}/// namespace gtsam diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 290cc0bd5..f9b046fe3 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -203,15 +203,23 @@ public: /// @name Dynamics /// @{ - // Compute tangent space contribution due to coriolis forces - Vector9 coriolis(const Vector3& omega, double dt, bool secondOrder = false, + /// Compute tangent space contribution due to Coriolis forces + Vector9 coriolis(double dt, const Vector3& omega, bool secondOrder = false, OptionalJacobian<9, 9> H = boost::none) const; - // Add tangent space contribution due to coriolis forces - // Additively modifies xi and H in place (if given) - void addCoriolis(Vector9* xi, const Vector3& omega, double dt, - bool secondOrder = false, OptionalJacobian<9, 9> H = boost::none) const; + /// Combine preintegrated measurements, in the form of a tangent space vector, + /// with gravity, velocity, and Coriolis forces into a tangent space update. + Vector9 predictXi(const Vector9& pim, double dt, const Vector3& gravity, + boost::optional omegaCoriolis, bool use2ndOrderCoriolis = + false, OptionalJacobian<9, 9> H1 = boost::none, + OptionalJacobian<9, 9> H2 = boost::none) const; + /// Combine preintegrated measurements, in the form of a tangent space vector, + /// with gravity, velocity, and Coriolis forces into a new state. + NavState predict(const Vector9& pim, double dt, const Vector3& gravity, + boost::optional omegaCoriolis, bool use2ndOrderCoriolis = + false, OptionalJacobian<9, 9> H1 = boost::none, + OptionalJacobian<9, 9> H2 = boost::none) const; /// @} private: diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index c5b46831e..683a97d50 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -29,6 +29,8 @@ static const Point3 kPosition(1.0, 2.0, 3.0); static const Velocity3 kVelocity(0.4, 0.5, 0.6); static const NavState kIdentity; static const NavState kState1(kRotation, kPosition, kVelocity); +static const Vector3 kOmegaCoriolis(0.02, 0.03, 0.04); +static const Vector3 kGravity(0, 0, 9.81); /* ************************************************************************* */ TEST( NavState, Attitude) { @@ -142,48 +144,60 @@ TEST( NavState, Lie ) { } /* ************************************************************************* */ +static const double dt = 2.0; +boost::function coriolis = boost::bind( + &NavState::coriolis, _1, dt, kOmegaCoriolis, _2, boost::none); + TEST(NavState, Coriolis) { Matrix9 actualH; - Vector3 omegaCoriolis(0.02, 0.03, 0.04); - double dt = 0.5; + // first-order - bool secondOrder = false; - kState1.coriolis(omegaCoriolis, dt, secondOrder, actualH); - Matrix expectedH = numericalDerivative11( - boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, - boost::none), kState1); - EXPECT(assert_equal(expectedH, actualH)); + kState1.coriolis(dt, kOmegaCoriolis, false, actualH); + EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, false), actualH)); // second-order - secondOrder = true; - kState1.coriolis(omegaCoriolis, dt, secondOrder, actualH); - expectedH = numericalDerivative11( - boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, - boost::none), kState1); - EXPECT(assert_equal(expectedH, actualH)); + kState1.coriolis(dt, kOmegaCoriolis, true, actualH); + EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, true), actualH)); } -/* ************************************************************************* */ TEST(NavState, Coriolis2) { + Matrix9 actualH; NavState state2(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), Point3(5.0, 1.0, -50.0), Vector3(0.5, 0.0, 0.0)); - Matrix9 actualH; - Vector3 omegaCoriolis(0.02, 0.03, 0.04); - double dt = 2.0; // first-order - bool secondOrder = false; - state2.coriolis(omegaCoriolis, dt, secondOrder, actualH); - Matrix expectedH = numericalDerivative11( - boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, - boost::none), state2); - EXPECT(assert_equal(expectedH, actualH)); + state2.coriolis(dt, kOmegaCoriolis, false, actualH); + EXPECT(assert_equal(numericalDerivative21(coriolis, state2, false), actualH)); // second-order - secondOrder = true; - state2.coriolis(omegaCoriolis, dt, secondOrder, actualH); - expectedH = numericalDerivative11( - boost::bind(&NavState::coriolis, _1, omegaCoriolis, dt, secondOrder, - boost::none), state2); - EXPECT(assert_equal(expectedH, actualH)); + state2.coriolis(dt, kOmegaCoriolis, true, actualH); + EXPECT(assert_equal(numericalDerivative21(coriolis, state2, true), actualH)); +} + +/* ************************************************************************* */ +TEST(NavState, PredictXi) { + Vector9 xi; + xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; + double dt = 0.5; + Matrix9 actualH1, actualH2; + boost::function predictXi = + boost::bind(&NavState::predictXi, _1, _2, dt, kGravity, kOmegaCoriolis, + false, boost::none, boost::none); + kState1.predictXi(xi, dt, kGravity, kOmegaCoriolis, false, actualH1, actualH2); + EXPECT(assert_equal(numericalDerivative21(predictXi, kState1, xi), actualH1)); + EXPECT(assert_equal(numericalDerivative22(predictXi, kState1, xi), actualH2)); +} + +/* ************************************************************************* */ +TEST(NavState, Predict) { + Vector9 xi; + xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; + double dt = 0.5; + Matrix9 actualH1, actualH2; + boost::function predict = + boost::bind(&NavState::predict, _1, _2, dt, kGravity, kOmegaCoriolis, + false, boost::none, boost::none); + kState1.predict(xi, dt, kGravity, kOmegaCoriolis, false, actualH1, actualH2); + EXPECT(assert_equal(numericalDerivative21(predict, kState1, xi), actualH1)); + EXPECT(assert_equal(numericalDerivative22(predict, kState1, xi), actualH2)); } /* ************************************************************************* */ From a098289861f4931b4a767bc8b25f176b9358f28b Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 22:22:05 +0200 Subject: [PATCH 063/142] Moved fields to protected --- gtsam/navigation/PreintegratedRotation.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index 0475e52e2..a93c54127 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -54,12 +54,11 @@ class PreintegratedRotation { } }; - private: + protected: double deltaTij_; ///< Time interval from i to j Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias - protected: /// Parameters boost::shared_ptr p_; @@ -138,7 +137,7 @@ class PreintegratedRotation { /// Integrate coriolis correction in body frame rot_i Vector3 integrateCoriolis(const Rot3& rot_i) const { if (!p_->omegaCoriolis) return Vector3::Zero(); - return rot_i.transpose() * (*p_->omegaCoriolis) * deltaTij(); + return rot_i.transpose() * (*p_->omegaCoriolis) * deltaTij_; } private: From 9b3c051ca1168d057f4b9b3df3f16b7903006b1b Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 22:22:31 +0200 Subject: [PATCH 064/142] Fied argument types --- gtsam/navigation/NavState.cpp | 4 ++-- gtsam/navigation/NavState.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index aa33fe858..b63f25b4d 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -249,7 +249,7 @@ Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder, //------------------------------------------------------------------------------ Vector9 NavState::predictXi(const Vector9& pim, double dt, - const Vector3& gravity, boost::optional omegaCoriolis, + const Vector3& gravity, const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { const Rot3& nRb = R_; @@ -294,7 +294,7 @@ Vector9 NavState::predictXi(const Vector9& pim, double dt, } //------------------------------------------------------------------------------ NavState NavState::predict(const Vector9& pim, double dt, - const Vector3& gravity, boost::optional omegaCoriolis, + const Vector3& gravity, const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index f9b046fe3..ed0345ada 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -210,14 +210,14 @@ public: /// Combine preintegrated measurements, in the form of a tangent space vector, /// with gravity, velocity, and Coriolis forces into a tangent space update. Vector9 predictXi(const Vector9& pim, double dt, const Vector3& gravity, - boost::optional omegaCoriolis, bool use2ndOrderCoriolis = + const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis = false, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 9> H2 = boost::none) const; /// Combine preintegrated measurements, in the form of a tangent space vector, /// with gravity, velocity, and Coriolis forces into a new state. NavState predict(const Vector9& pim, double dt, const Vector3& gravity, - boost::optional omegaCoriolis, bool use2ndOrderCoriolis = + const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis = false, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 9> H2 = boost::none) const; /// @} From 7ccfb95339421203edc1e605e96b41bfe9ac3b06 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 22:23:14 +0200 Subject: [PATCH 065/142] Favor fields not methods --- gtsam/navigation/CombinedImuFactor.cpp | 2 +- gtsam/navigation/ImuFactor.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 620305d77..2d58534aa 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -74,7 +74,7 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( // can be seen as a prediction phase in an EKF framework. In this implementation, contrarily to [2] we // consider the uncertainty of the bias selection and we keep correlation between biases and preintegrated measurements /* ----------------------------------------------------------------------------------------------------------------------- */ - const Matrix3 dRij = deltaRij().matrix(); // expensive when quaternion + const Matrix3 dRij = deltaRij_.matrix(); // expensive when quaternion // Update preintegrated measurements. TODO Frank moved from end of this function !!! Matrix9 F_9x9; updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F_9x9); diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 7d6b77d07..058ea1538 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -68,7 +68,7 @@ void PreintegratedImuMeasurements::integrateMeasurement( updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT); // Update preintegrated measurements (also get Jacobian) - const Matrix3 dRij = deltaRij().matrix(); // store this, which is useful to compute G_test + const Matrix3 dRij = deltaRij_.matrix(); // store this, which is useful to compute G_test Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F); From a99911b997d6563c5fd96c89e91b31d162071c65 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 22:24:19 +0200 Subject: [PATCH 066/142] Now uses NavState::predict ! --- gtsam/navigation/PreintegrationBase.cpp | 55 +++--------------------- gtsam/navigation/PreintegrationBase.h | 5 --- gtsam/navigation/tests/testImuFactor.cpp | 46 ++++++++------------ 3 files changed, 25 insertions(+), 81 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 1e3f3520e..e2f1466a6 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -63,7 +63,7 @@ void PreintegrationBase::updatePreintegratedMeasurements( const Vector3& correctedAcc, const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F) { - const Matrix3 dRij = deltaRij().matrix(); // expensive + const Matrix3 dRij = deltaRij_.matrix(); // expensive const Vector3 temp = dRij * correctedAcc * deltaT; if (!p().use2ndOrderIntegration) { @@ -98,9 +98,9 @@ void PreintegrationBase::updatePreintegratedMeasurements( void PreintegrationBase::updatePreintegratedJacobians( const Vector3& correctedAcc, const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, double deltaT) { - const Matrix3 dRij = deltaRij().matrix(); // expensive + const Matrix3 dRij = deltaRij_.matrix(); // expensive const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT - * delRdelBiasOmega(); + * delRdelBiasOmega_; if (!p().use2ndOrderIntegration) { delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT; delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT; @@ -157,48 +157,6 @@ Vector9 PreintegrationBase::biasCorrectedDelta( return xi; } -//------------------------------------------------------------------------------ -Vector9 PreintegrationBase::recombinedPrediction(const NavState& state_i, - const Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1, - OptionalJacobian<9, 9> H2) const { - - const Rot3& rot_i = state_i.attitude(); - const Vector3& vel_i = state_i.velocity(); - const double dt = deltaTij(), dt2 = dt * dt; - - // Rotation, position, and velocity: - Vector9 xi; - Matrix3 D_dP_Ri, D_dP_bc, D_dV_Ri, D_dV_bc; - NavState::dR(xi) = NavState::dR(biasCorrectedDelta); - NavState::dP(xi) = rot_i.rotate(NavState::dP(biasCorrectedDelta), D_dP_Ri, - D_dP_bc) + vel_i * dt + 0.5 * p().gravity * dt2; - NavState::dV(xi) = rot_i.rotate(NavState::dV(biasCorrectedDelta), D_dV_Ri, - D_dV_bc) + p().gravity * dt; - - Matrix9 Hcoriolis; - if (p().omegaCoriolis) { - state_i.addCoriolis(&xi, *(p().omegaCoriolis), deltaTij(), - p().use2ndOrderCoriolis, H1 ? &Hcoriolis : 0); - } - if (H1) { - H1->setZero(); - H1->block<3, 3>(3, 0) = D_dP_Ri; - H1->block<3, 3>(3, 6) = I_3x3 * dt; - H1->block<3, 3>(6, 0) = D_dV_Ri; - if (p().omegaCoriolis) - *H1 += Hcoriolis; - } - if (H2) { - H2->setZero(); - Matrix3 Ri = rot_i.matrix(); - H2->block<3, 3>(0, 0) = I_3x3; - H2->block<3, 3>(3, 3) = Ri; - H2->block<3, 3>(6, 6) = Ri; - } - - return xi; -} - //------------------------------------------------------------------------------ NavState PreintegrationBase::predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1, @@ -207,8 +165,9 @@ NavState PreintegrationBase::predict(const NavState& state_i, Vector9 biasCorrected = biasCorrectedDelta(bias_i, H2 ? &D_biasCorrected_bias : 0); Matrix9 D_delta_state, D_delta_biasCorrected; - Vector9 xi = recombinedPrediction(state_i, biasCorrected, - H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0); + Vector9 xi = state_i.predictXi(biasCorrected, deltaTij_, p().gravity, + p().omegaCoriolis, p().use2ndOrderCoriolis, H1 ? &D_delta_state : 0, + H2 ? &D_delta_biasCorrected : 0); Matrix9 D_predict_state, D_predict_delta; NavState state_j = state_i.retract(xi, D_predict_state, D_predict_delta); if (H1) @@ -300,7 +259,7 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, Matrix3 D_fR_fRrot; Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); - const double dt = deltaTij(), dt2 = dt * dt; + const double dt = deltaTij_, dt2 = dt * dt; Matrix3 RitOmegaCoriolisHat = Z_3x3; if ((H1 || H2) && p().omegaCoriolis) RitOmegaCoriolisHat = Ri.transpose() * skewSymmetric(*p().omegaCoriolis); diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 8e7841f7c..2ee6b79e6 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -152,11 +152,6 @@ class PreintegrationBase : public PreintegratedRotation { Vector9 biasCorrectedDelta(const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H = boost::none) const; - /// Recombine the preintegration, gravity, and coriolis in a single NavState tangent vector - Vector9 recombinedPrediction(const NavState& state_i, - const Vector9& biasCorrectedDelta, OptionalJacobian<9, 9> H1 = boost::none, - OptionalJacobian<9, 9> H2 = boost::none) const; - /// Predict state at time j NavState predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 6> H2 = boost::none) const; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 8ed2fb135..7bc037da9 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -213,26 +213,26 @@ double deltaT = 1.0; TEST( NavState, Lie ) { // origin and zero deltas NavState identity; - const double tol=1e-5; + const double tol = 1e-5; Rot3 rot = Rot3::RzRyRx(0.1, 0.2, 0.3); Point3 pt(1.0, 2.0, 3.0); Velocity3 vel(0.4, 0.5, 0.6); - EXPECT(assert_equal(identity, (NavState)identity.retract(zero(9)), tol)); + EXPECT(assert_equal(identity, (NavState )identity.retract(zero(9)), tol)); EXPECT(assert_equal(zero(9), identity.localCoordinates(identity), tol)); NavState state1(rot, pt, vel); - EXPECT(assert_equal(state1, (NavState)state1.retract(zero(9)), tol)); + EXPECT(assert_equal(state1, (NavState )state1.retract(zero(9)), tol)); EXPECT(assert_equal(zero(9), state1.localCoordinates(state1), tol)); // Special retract Vector delta(9); - delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3; + delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; Rot3 drot = Rot3::Expmap(delta.head<3>()); - Point3 dt = Point3(delta.segment<3>(3)); + Point3 dt = Point3(delta.segment < 3 > (3)); Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3); NavState state2 = state1 * NavState(drot, dt, dvel); - EXPECT(assert_equal(state2, (NavState)state1.retract(delta), tol)); + EXPECT(assert_equal(state2, (NavState )state1.retract(delta), tol)); EXPECT(assert_equal(delta, state1.localCoordinates(state2), tol)); // roundtrip from state2 to state3 and back @@ -244,17 +244,19 @@ TEST( NavState, Lie ) { EXPECT(assert_equal(delta, state3.logmap(state4), tol)); // For the expmap/logmap (not necessarily retract/local) -delta goes other way - EXPECT(assert_equal(state3, (NavState)state4.expmap(-delta), tol)); + EXPECT(assert_equal(state3, (NavState )state4.expmap(-delta), tol)); EXPECT(assert_equal(delta, -state4.logmap(state3), tol)); // retract derivatives Matrix9 aH1, aH2; state1.retract(delta, aH1, aH2); Matrix eH1 = numericalDerivative11( - boost::bind(&NavState::retract, _1, delta, boost::none, boost::none),state1); + boost::bind(&NavState::retract, _1, delta, boost::none, boost::none), + state1); EXPECT(assert_equal(eH1, aH1)); Matrix eH2 = numericalDerivative11( - boost::bind(&NavState::retract, state1, _1, boost::none, boost::none), delta); + boost::bind(&NavState::retract, state1, _1, boost::none, boost::none), + delta); EXPECT(assert_equal(eH2, aH2)); } @@ -282,19 +284,6 @@ TEST(ImuFactor, PreintegrationBaseMethods) { boost::none), bias); EXPECT(assert_equal(expectedH, actualH)); } - { - Matrix9 aH1, aH2; - Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias); - pim.recombinedPrediction(state1, biasCorrectedDelta, aH1, aH2); - Matrix eH1 = numericalDerivative11( - boost::bind(&PreintegrationBase::recombinedPrediction, pim, _1, - biasCorrectedDelta, boost::none, boost::none), state1); - EXPECT(assert_equal(eH1, aH1)); - Matrix eH2 = numericalDerivative11( - boost::bind(&PreintegrationBase::recombinedPrediction, pim, state1, _1, - boost::none, boost::none), biasCorrectedDelta); - EXPECT(assert_equal(eH2, aH2)); - } { Matrix9 aH1; Matrix96 aH2; @@ -302,11 +291,11 @@ TEST(ImuFactor, PreintegrationBaseMethods) { Matrix eH1 = numericalDerivative11( boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none, boost::none), state1); - EXPECT(assert_equal(eH1, aH1)); + EXPECT(assert_equal(eH1, aH1, 1e-8)); Matrix eH2 = numericalDerivative11( boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, boost::none), bias); - EXPECT(assert_equal(eH2, aH2)); + EXPECT(assert_equal(eH2, aH2, 1e-8)); } } @@ -314,9 +303,9 @@ TEST(ImuFactor, PreintegrationBaseMethods) { TEST(ImuFactor, ErrorAndJacobians) { using namespace common; bool use2ndOrderIntegration = true; - PreintegratedImuMeasurements pim(bias, - kMeasuredAccCovariance, kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, use2ndOrderIntegration); + PreintegratedImuMeasurements pim(bias, kMeasuredAccCovariance, + kMeasuredOmegaCovariance, kIntegrationErrorCovariance, + use2ndOrderIntegration); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor @@ -933,7 +922,8 @@ TEST(ImuFactor, PredictRotation) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kZeroOmegaCoriolis); + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + kZeroOmegaCoriolis); // Predict Pose3 x1, x2; From 85085e882db34a9e63dc6e7531df98d174efbe2f Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 22 Jul 2015 22:24:53 +0200 Subject: [PATCH 067/142] Removed Lie tests (now in testNavState) --- gtsam/navigation/tests/testImuFactor.cpp | 51 ------------------------ 1 file changed, 51 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 7bc037da9..c9ba2c5d7 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -209,57 +209,6 @@ Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector(); double deltaT = 1.0; } // namespace common -/* ************************************************************************* */ -TEST( NavState, Lie ) { - // origin and zero deltas - NavState identity; - const double tol = 1e-5; - Rot3 rot = Rot3::RzRyRx(0.1, 0.2, 0.3); - Point3 pt(1.0, 2.0, 3.0); - Velocity3 vel(0.4, 0.5, 0.6); - - EXPECT(assert_equal(identity, (NavState )identity.retract(zero(9)), tol)); - EXPECT(assert_equal(zero(9), identity.localCoordinates(identity), tol)); - - NavState state1(rot, pt, vel); - EXPECT(assert_equal(state1, (NavState )state1.retract(zero(9)), tol)); - EXPECT(assert_equal(zero(9), state1.localCoordinates(state1), tol)); - - // Special retract - Vector delta(9); - delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; - Rot3 drot = Rot3::Expmap(delta.head<3>()); - Point3 dt = Point3(delta.segment < 3 > (3)); - Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3); - NavState state2 = state1 * NavState(drot, dt, dvel); - EXPECT(assert_equal(state2, (NavState )state1.retract(delta), tol)); - EXPECT(assert_equal(delta, state1.localCoordinates(state2), tol)); - - // roundtrip from state2 to state3 and back - NavState state3 = state2.retract(delta); - EXPECT(assert_equal(delta, state2.localCoordinates(state3), tol)); - - // roundtrip from state3 to state4 and back, with expmap. - NavState state4 = state3.expmap(delta); - EXPECT(assert_equal(delta, state3.logmap(state4), tol)); - - // For the expmap/logmap (not necessarily retract/local) -delta goes other way - EXPECT(assert_equal(state3, (NavState )state4.expmap(-delta), tol)); - EXPECT(assert_equal(delta, -state4.logmap(state3), tol)); - - // retract derivatives - Matrix9 aH1, aH2; - state1.retract(delta, aH1, aH2); - Matrix eH1 = numericalDerivative11( - boost::bind(&NavState::retract, _1, delta, boost::none, boost::none), - state1); - EXPECT(assert_equal(eH1, aH1)); - Matrix eH2 = numericalDerivative11( - boost::bind(&NavState::retract, state1, _1, boost::none, boost::none), - delta); - EXPECT(assert_equal(eH2, aH2)); -} - /* ************************************************************************* */ TEST(ImuFactor, PreintegrationBaseMethods) { using namespace common; From de763144884a4ac12f8cf4f4f22976bc278d0610 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 23 Jul 2015 14:21:29 +0200 Subject: [PATCH 068/142] Straight line example, including finding defect in using first-order approximation --- gtsam/navigation/tests/testImuFactor.cpp | 87 +++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index c9ba2c5d7..33409a7f8 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -141,6 +141,84 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { } // namespace +/* ************************************************************************* */ +namespace straight { +// Set up IMU measurements +static const double g = 10; // make this easy to check +static const double a = 0.2, dt = 3.0; +const double exact = dt * dt / 2; +Vector3 measuredAcc(a, 0, -g), measuredOmega(0, 0, 0); + +// Set up body pointing towards y axis, and start at 10,20,0 with zero velocity +// TODO(frank): clean up Rot3 mess +static const Rot3 nRb(Point3(0, 1, 0), Point3(1, 0, 0), Point3(0, 0, -1)); +static const Point3 initial_position(10, 20, 0); +static const NavState state1(nRb, initial_position, Velocity3(0, 0, 0)); +} + +TEST(ImuFactor, StraightLineSecondOrder) { + using namespace straight; + + imuBias::ConstantBias biasHat, bias; + boost::shared_ptr p = + PreintegratedImuMeasurements::Params::MakeSharedFRD(); + p->use2ndOrderIntegration = true; + p->b_gravity = Vector3(0, 0, g); // FRD convention + + PreintegratedImuMeasurements pim(p, biasHat); + for (size_t i = 0; i < 10; i++) + pim.integrateMeasurement(measuredAcc, measuredOmega, dt / 10); + + // Check integrated quantities in body frame: gravity measured by IMU is integrated! + EXPECT(assert_equal(Rot3(), pim.deltaRij())); + EXPECT(assert_equal(Vector3(a * exact, 0, -g * exact), pim.deltaPij())); + EXPECT(assert_equal(Vector3(a * dt, 0, -g * dt), pim.deltaVij())); + + // Check bias-corrected delta: also in body frame + Vector9 expectedBC; + expectedBC << 0, 0, 0, a * exact, 0, -g * exact, a * dt, 0, -g * dt; + EXPECT(assert_equal(expectedBC, pim.biasCorrectedDelta(bias))); + + // Check prediction, note we move along x in body, along y in nav + NavState expected(nRb, Point3(10, 20 + a * exact, 0), + Velocity3(0, a * dt, 0)); + GTSAM_PRINT(pim); + EXPECT(assert_equal(expected, pim.predict(state1, bias))); +} + +TEST(ImuFactor, StraightLineFirstOrder) { + using namespace straight; + + imuBias::ConstantBias biasHat, bias; + boost::shared_ptr p = + PreintegratedImuMeasurements::Params::MakeSharedFRD(); + p->use2ndOrderIntegration = false; + p->b_gravity = Vector3(0, 0, g); // FRD convention + + // We do a rough approximation: + PreintegratedImuMeasurements pim(p, biasHat); + for (size_t i = 0; i < 10; i++) + pim.integrateMeasurement(measuredAcc, measuredOmega, dt / 10); + + // Check integrated quantities in body frame: gravity measured by IMU is integrated! + const double approx = exact * 0.9; // approximation for dt split into 10 intervals + EXPECT(assert_equal(Rot3(), pim.deltaRij())); + EXPECT(assert_equal(Vector3(a * approx, 0, -g * approx), pim.deltaPij())); + EXPECT(assert_equal(Vector3(a * dt, 0, -g * dt), pim.deltaVij())); // still correct + + // Check bias-corrected delta: also in body frame + Vector9 expectedBC; + expectedBC << 0, 0, 0, a * approx, 0, -g * approx, a * dt, 0, -g * dt; + EXPECT(assert_equal(expectedBC, pim.biasCorrectedDelta(bias))); + + // Check prediction, note we move along x in body, along y in nav + // In this instance the position is just an approximation, + // but gravity should be subtracted out exactly + NavState expected(nRb, Point3(10, 20 + a * approx, 0), Velocity3(0, a * dt, 0)); + GTSAM_PRINT(pim); + EXPECT(assert_equal(expected, pim.predict(state1, bias))); +} + /* ************************************************************************* */ TEST(ImuFactor, PreintegratedMeasurements) { // Linearization point @@ -192,16 +270,19 @@ TEST(ImuFactor, PreintegratedMeasurements) { DOUBLES_EQUAL(expectedDeltaT2, actual2.deltaTij(), 1e-6); } +/* ************************************************************************* */ // Common linearization point and measurements for tests namespace common { imuBias::ConstantBias bias; // Bias Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), Point3(5.0, 1.0, -50.0)); Vector3 v1(Vector3(0.5, 0.0, 0.0)); + +NavState state1(x1, v1); Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), Point3(5.5, 1.0, -50.0)); Vector3 v2(Vector3(0.5, 0.0, 0.0)); -NavState state1(x1, v1); +NavState state2(x2, v2); // Measurements Vector3 measuredOmega(M_PI / 100, 0, 0); @@ -213,7 +294,7 @@ double deltaT = 1.0; TEST(ImuFactor, PreintegrationBaseMethods) { using namespace common; boost::shared_ptr p = - boost::make_shared(); + PreintegratedImuMeasurements::Params::MakeSharedFRD(); p->gyroscopeCovariance = kMeasuredOmegaCovariance; p->omegaCoriolis = Vector3(0.02, 0.03, 0.04); p->accelerometerCovariance = kMeasuredAccCovariance; @@ -255,7 +336,9 @@ TEST(ImuFactor, ErrorAndJacobians) { PreintegratedImuMeasurements pim(bias, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, use2ndOrderIntegration); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + EXPECT(assert_equal(state2, pim.predict(state1, bias), 1e-6)); // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, From 0bb73bae9e3d628a0ba6cabdd401990be32d2cda Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 23 Jul 2015 16:59:26 +0200 Subject: [PATCH 069/142] Comments --- gtsam_unstable/dynamics/PoseRTV.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gtsam_unstable/dynamics/PoseRTV.h b/gtsam_unstable/dynamics/PoseRTV.h index ed5fa9be0..b59ea4614 100644 --- a/gtsam_unstable/dynamics/PoseRTV.h +++ b/gtsam_unstable/dynamics/PoseRTV.h @@ -146,16 +146,12 @@ public: /// RRTMbn - Function computes the rotation rate transformation matrix from /// body axis rates to euler angle (global) rates - /// TODO(frank): seems to ignore euler.z() static Matrix RRTMbn(const Vector3& euler); - static Matrix RRTMbn(const Rot3& att); /// RRTMnb - Function computes the rotation rate transformation matrix from /// euler angle rates to body axis rates - /// TODO(frank): seems to ignore euler.z() static Matrix RRTMnb(const Vector3& euler); - static Matrix RRTMnb(const Rot3& att); /// @} From c6b68e6795388019fa06f595446338926f3dbe83 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 23 Jul 2015 17:00:07 +0200 Subject: [PATCH 070/142] No more second-order integration flag --- gtsam/navigation/CombinedImuFactor.cpp | 13 +- gtsam/navigation/CombinedImuFactor.h | 26 +++- gtsam/navigation/ImuFactor.cpp | 88 ++++++------- gtsam/navigation/ImuFactor.h | 7 +- gtsam/navigation/NavState.cpp | 39 +++--- gtsam/navigation/NavState.h | 15 ++- gtsam/navigation/PreintegrationBase.cpp | 49 ++++---- gtsam/navigation/PreintegrationBase.h | 154 ++++++++++++++--------- gtsam/navigation/tests/testImuFactor.cpp | 121 +++++------------- gtsam/navigation/tests/testNavState.cpp | 10 +- 10 files changed, 250 insertions(+), 272 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 2d58534aa..f3647fcc0 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -145,15 +145,16 @@ PreintegratedCombinedMeasurements::PreintegratedCombinedMeasurements( const Matrix3& integrationErrorCovariance, const Matrix3& biasAccCovariance, const Matrix3& biasOmegaCovariance, const Matrix6& biasAccOmegaInit, const bool use2ndOrderIntegration) { + if (!use2ndOrderIntegration) + throw("PreintegratedImuMeasurements no longer supports first-order integration: it incorrectly compensated for gravity"); biasHat_ = biasHat; - boost::shared_ptr p = boost::make_shared(); + boost::shared_ptr p = Params::MakeSharedFRD(); p->gyroscopeCovariance = measuredOmegaCovariance; p->accelerometerCovariance = measuredAccCovariance; p->integrationCovariance = integrationErrorCovariance; p->biasAccCovariance = biasAccCovariance; p->biasOmegaCovariance = biasOmegaCovariance; p->biasAccOmegaInit = biasAccOmegaInit; - p->use2ndOrderIntegration = use2ndOrderIntegration; p_ = p; resetIntegration(); preintMeasCov_.setZero(); @@ -259,7 +260,7 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, //------------------------------------------------------------------------------ CombinedImuFactor::CombinedImuFactor( Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, - const CombinedPreintegratedMeasurements& pim, const Vector3& gravity, + const CombinedPreintegratedMeasurements& pim, const Vector3& b_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor, const bool use2ndOrderCoriolis) : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, @@ -267,7 +268,7 @@ CombinedImuFactor::CombinedImuFactor( _PIM_(pim) { boost::shared_ptr p = boost::make_shared(pim.p()); - p->gravity = gravity; + p->b_gravity = b_gravity; p->omegaCoriolis = omegaCoriolis; p->body_P_sensor = body_P_sensor; p->use2ndOrderCoriolis = use2ndOrderCoriolis; @@ -278,11 +279,11 @@ void CombinedImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, CombinedPreintegratedMeasurements& pim, - const Vector3& gravity, + const Vector3& b_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // use deprecated predict - PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, gravity, + PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, b_gravity, omegaCoriolis, use2ndOrderCoriolis); pose_j = pvb.pose; vel_j = pvb.velocity; diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 6bd2f7867..142e8706f 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -66,9 +66,26 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { Matrix3 biasOmegaCovariance; ///< continuous-time "Covariance" describing gyroscope bias random walk Matrix6 biasAccOmegaInit; ///< covariance of bias used for pre-integration - Params():biasAccCovariance(I_3x3), biasOmegaCovariance(I_3x3), biasAccOmegaInit(I_6x6) {} + /// See two named constructors below for good values of b_gravity in body frame + Params(const Vector3& b_gravity) : + PreintegrationBase::Params(b_gravity), biasAccCovariance(I_3x3), biasOmegaCovariance( + I_3x3), biasAccOmegaInit(I_6x6) { + } + + // Default Params for Front-Right-Down convention: b_gravity points along positive Z-axis + static boost::shared_ptr MakeSharedFRD(double g = 9.81) { + return boost::make_shared(Vector3(0, 0, g)); + } + + // Default Params for Front-Left-Up convention: b_gravity points along negative Z-axis + static boost::shared_ptr MakeSharedFLU(double g = 9.81) { + return boost::make_shared(Vector3(0, 0, -g)); + } private: + /// Default constructor makes unitialized params struct + Params() {} + /** Serialization function */ friend class boost::serialization::access; template @@ -134,12 +151,13 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { Matrix preintMeasCov() const { return preintMeasCov_; } /// deprecated constructor + /// NOTE(frank): assumes FRD convention, only second order integration supported PreintegratedCombinedMeasurements(const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, const Matrix3& integrationErrorCovariance, const Matrix3& biasAccCovariance, const Matrix3& biasOmegaCovariance, - const Matrix6& biasAccOmegaInit, const bool use2ndOrderIntegration = false); + const Matrix6& biasAccOmegaInit, const bool use2ndOrderIntegration = true); private: /// Serialization function @@ -245,7 +263,7 @@ public: /// @deprecated constructor CombinedImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, const CombinedPreintegratedMeasurements& pim, - const Vector3& gravity, const Vector3& omegaCoriolis, + const Vector3& b_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor = boost::none, const bool use2ndOrderCoriolis = false); @@ -253,7 +271,7 @@ public: static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, CombinedPreintegratedMeasurements& pim, - const Vector3& gravity, const Vector3& omegaCoriolis, + const Vector3& b_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 058ea1538..1b3736bec 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -38,8 +38,8 @@ void PreintegratedImuMeasurements::print(const string& s) const { //------------------------------------------------------------------------------ bool PreintegratedImuMeasurements::equals( const PreintegratedImuMeasurements& other, double tol) const { - return PreintegrationBase::equals(other, tol) && - equal_with_abs_tol(preintMeasCov_, other.preintMeasCov_, tol); + return PreintegrationBase::equals(other, tol) + && equal_with_abs_tol(preintMeasCov_, other.preintMeasCov_, tol); } //------------------------------------------------------------------------------ @@ -51,8 +51,7 @@ void PreintegratedImuMeasurements::resetIntegration() { //------------------------------------------------------------------------------ void PreintegratedImuMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - OptionalJacobian<9, 9> F_test, - OptionalJacobian<9, 9> G_test) { + OptionalJacobian<9, 9> F_test, OptionalJacobian<9, 9> G_test) { Vector3 correctedAcc, correctedOmega; correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, @@ -60,16 +59,17 @@ void PreintegratedImuMeasurements::integrateMeasurement( // rotation increment computed from the current rotation rate measurement const Vector3 integratedOmega = correctedOmega * deltaT; - Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr + Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr // rotation increment computed from the current rotation rate measurement const Rot3 Rincr = Rot3::Expmap(integratedOmega, D_Rincr_integratedOmega); // Update Jacobians - updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT); + updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, + deltaT); // Update preintegrated measurements (also get Jacobian) - const Matrix3 dRij = deltaRij_.matrix(); // store this, which is useful to compute G_test - Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) + const Matrix3 dRij = deltaRij_.matrix(); // store this, which is useful to compute G_test + Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F); // first order covariance propagation: @@ -88,14 +88,13 @@ void PreintegratedImuMeasurements::integrateMeasurement( * p().gyroscopeCovariance * D_Rincr_integratedOmega.transpose() * deltaT; Matrix3 F_pos_noiseacc; - if (p().use2ndOrderIntegration) { - F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; - preintMeasCov_.block<3, 3>(0, 0) += (1 / deltaT) * F_pos_noiseacc - * p().accelerometerCovariance * F_pos_noiseacc.transpose(); - Matrix3 temp = F_pos_noiseacc * p().accelerometerCovariance * dRij.transpose(); // has 1/deltaT - preintMeasCov_.block<3, 3>(0, 3) += temp; - preintMeasCov_.block<3, 3>(3, 0) += temp.transpose(); - } + F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; + preintMeasCov_.block<3, 3>(0, 0) += (1 / deltaT) * F_pos_noiseacc + * p().accelerometerCovariance * F_pos_noiseacc.transpose(); + Matrix3 temp = F_pos_noiseacc * p().accelerometerCovariance + * dRij.transpose(); // has 1/deltaT + preintMeasCov_.block<3, 3>(0, 3) += temp; + preintMeasCov_.block<3, 3>(3, 0) += temp.transpose(); // F_test and G_test are given as output for testing purposes and are not needed by the factor if (F_test) { @@ -103,13 +102,10 @@ void PreintegratedImuMeasurements::integrateMeasurement( } if (G_test) { // This in only for testing & documentation, while the actual computation is done block-wise - if (!p().use2ndOrderIntegration) - F_pos_noiseacc = Z_3x3; - // intNoise accNoise omegaNoise - (*G_test) << I_3x3 * deltaT, F_pos_noiseacc, Z_3x3, // pos - Z_3x3, dRij * deltaT, Z_3x3, // vel - Z_3x3, Z_3x3, D_Rincr_integratedOmega * deltaT; // angle + (*G_test) << I_3x3 * deltaT, F_pos_noiseacc, Z_3x3, // pos + Z_3x3, dRij * deltaT, Z_3x3, // vel + Z_3x3, Z_3x3, D_Rincr_integratedOmega * deltaT; // angle } } //------------------------------------------------------------------------------ @@ -117,12 +113,13 @@ PreintegratedImuMeasurements::PreintegratedImuMeasurements( const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration) { + if (!use2ndOrderIntegration) + throw("PreintegratedImuMeasurements no longer supports first-order integration: it incorrectly compensated for gravity"); biasHat_ = biasHat; - boost::shared_ptr p = boost::make_shared(); + boost::shared_ptr p = Params::MakeSharedFRD(); p->gyroscopeCovariance = measuredOmegaCovariance; p->accelerometerCovariance = measuredAccCovariance; p->integrationCovariance = integrationErrorCovariance; - p->use2ndOrderIntegration = use2ndOrderIntegration; p_ = p; resetIntegration(); } @@ -131,10 +128,10 @@ PreintegratedImuMeasurements::PreintegratedImuMeasurements( // ImuFactor methods //------------------------------------------------------------------------------ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, - const PreintegratedImuMeasurements& pim) - : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, - pose_j, vel_j, bias), - _PIM_(pim) {} + const PreintegratedImuMeasurements& pim) : + Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, + pose_j, vel_j, bias), _PIM_(pim) { +} //------------------------------------------------------------------------------ gtsam::NonlinearFactor::shared_ptr ImuFactor::clone() const { @@ -151,7 +148,8 @@ void ImuFactor::print(const string& s, const KeyFormatter& keyFormatter) const { Base::print(""); _PIM_.print(" preintegrated measurements:"); // Print standard deviations on covariance only - cout << " noise model sigmas: " << this->noiseModel_->sigmas().transpose() << endl; + cout << " noise model sigmas: " << this->noiseModel_->sigmas().transpose() + << endl; } //------------------------------------------------------------------------------ @@ -168,21 +166,19 @@ Vector ImuFactor::evaluateError(const Pose3& pose_i, const Vector3& vel_i, boost::optional H2, boost::optional H3, boost::optional H4, boost::optional H5) const { return _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, - H1, H2, H3, H4, H5); + H1, H2, H3, H4, H5); } //------------------------------------------------------------------------------ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, - const PreintegratedMeasurements& pim, - const Vector3& gravity, const Vector3& omegaCoriolis, - const boost::optional& body_P_sensor, - const bool use2ndOrderCoriolis) - : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, - pose_j, vel_j, bias), - _PIM_(pim) { - boost::shared_ptr p = - boost::make_shared(pim.p()); - p->gravity = gravity; + const PreintegratedMeasurements& pim, const Vector3& b_gravity, + const Vector3& omegaCoriolis, const boost::optional& body_P_sensor, + const bool use2ndOrderCoriolis) : + Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, + pose_j, vel_j, bias), _PIM_(pim) { + boost::shared_ptr p = boost::make_shared< + PreintegratedMeasurements::Params>(pim.p()); + p->b_gravity = b_gravity; p->omegaCoriolis = omegaCoriolis; p->body_P_sensor = body_P_sensor; p->use2ndOrderCoriolis = use2ndOrderCoriolis; @@ -191,16 +187,14 @@ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, //------------------------------------------------------------------------------ void ImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, - Pose3& pose_j, Vector3& vel_j, - const imuBias::ConstantBias& bias_i, - PreintegratedMeasurements& pim, - const Vector3& gravity, const Vector3& omegaCoriolis, - const bool use2ndOrderCoriolis) { + Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, + PreintegratedMeasurements& pim, const Vector3& b_gravity, + const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // use deprecated predict - PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, gravity, + PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, b_gravity, omegaCoriolis, use2ndOrderCoriolis); pose_j = pvb.pose; vel_j = pvb.velocity; } -} // namespace gtsam +} // namespace gtsam diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 0b86472f5..c739120d3 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -108,11 +108,12 @@ public: Matrix preintMeasCov() const { return preintMeasCov_; } /// @deprecated constructor + /// NOTE(frank): assumes FRD convention, only second order integration supported PreintegratedImuMeasurements(const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, const Matrix3& integrationErrorCovariance, - bool use2ndOrderIntegration = false); + bool use2ndOrderIntegration = true); private: @@ -209,14 +210,14 @@ public: /// @deprecated constructor ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, const PreintegratedMeasurements& preintegratedMeasurements, - const Vector3& gravity, const Vector3& omegaCoriolis, + const Vector3& b_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor = boost::none, const bool use2ndOrderCoriolis = false); /// @deprecated use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - PreintegratedMeasurements& pim, const Vector3& gravity, + PreintegratedMeasurements& pim, const Vector3& b_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index b63f25b4d..8ce8a200b 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -248,25 +248,19 @@ Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder, } //------------------------------------------------------------------------------ -Vector9 NavState::predictXi(const Vector9& pim, double dt, - const Vector3& gravity, const boost::optional& omegaCoriolis, - bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, - OptionalJacobian<9, 9> H2) const { +Vector9 NavState::integrateTangent(const Vector9& pim, double dt, + const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, + OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { const Rot3& nRb = R_; const Velocity3& n_v = v_; // derivative is Ri ! const double dt2 = dt * dt; Vector9 delta; - Matrix3 D_dP_Ri, D_dP_dP, D_dV_Ri, D_dV_dV; + Matrix3 D_dP_Ri, D_dP_nv; dR(delta) = dR(pim); - // TODO(frank): - // - why do we integrate n_v here ? - // - the dP and dV should be in body ! How come semi-retract works out ?? - // - should we rename t_ to p_? if not, we should rename dP do dT - dP(delta) = nRb.rotate(dP(pim), H1 ? &D_dP_Ri : 0, H2 ? &D_dP_dP : 0) - + n_v * dt + 0.5 * gravity * dt2; - dV(delta) = nRb.rotate(dV(pim), H1 ? &D_dV_Ri : 0, H2 ? &D_dV_dV : 0) - + gravity * dt; + dP(delta) = dP(pim) + + dt * nRb.unrotate(n_v, H1 ? &D_dP_Ri : 0, H2 ? &D_dP_nv : 0); + dV(delta) = dV(pim); if (omegaCoriolis) { delta += coriolis(dt, *omegaCoriolis, use2ndOrderCoriolis, H1); @@ -278,15 +272,11 @@ Vector9 NavState::predictXi(const Vector9& pim, double dt, if (H1) { if (!omegaCoriolis) H1->setZero(); // if coriolis H1 is already initialized - D_t_R(H1) += D_dP_Ri; - D_t_v(H1) += I_3x3 * dt * Ri; - D_v_R(H1) += D_dV_Ri; + D_t_R(H1) += dt * D_dP_Ri; + D_t_v(H1) += dt * D_dP_nv * Ri; } if (H2) { - H2->setZero(); - D_R_R(H2) << I_3x3; - D_t_t(H2) << D_dP_dP; - D_v_v(H2) << D_dV_dV; + H2->setIdentity(); } } @@ -294,13 +284,12 @@ Vector9 NavState::predictXi(const Vector9& pim, double dt, } //------------------------------------------------------------------------------ NavState NavState::predict(const Vector9& pim, double dt, - const Vector3& gravity, const boost::optional& omegaCoriolis, - bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, - OptionalJacobian<9, 9> H2) const { + const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, + OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { Matrix9 D_delta_state, D_delta_pim; - Vector9 delta = predictXi(pim, dt, gravity, omegaCoriolis, - use2ndOrderCoriolis, H1 ? &D_delta_state : 0, H2 ? &D_delta_pim : 0); + Vector9 delta = integrateTangent(pim, dt, omegaCoriolis, use2ndOrderCoriolis, + H1 ? &D_delta_state : 0, H2 ? &D_delta_pim : 0); Matrix9 D_predicted_state, D_predicted_delta; NavState predicted = retract(delta, H1 ? &D_predicted_state : 0, diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index ed0345ada..7326b8df7 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -33,6 +33,9 @@ typedef Vector3 Velocity3; */ class NavState: public LieGroup { private: + + // TODO(frank): + // - should we rename t_ to p_? if not, we should rename dP do dT Rot3 R_; ///< Rotation nRb, rotates points/velocities in body to points/velocities in nav Point3 t_; ///< position n_t, in nav frame Velocity3 v_; ///< velocity n_v in nav frame @@ -207,16 +210,16 @@ public: Vector9 coriolis(double dt, const Vector3& omega, bool secondOrder = false, OptionalJacobian<9, 9> H = boost::none) const; - /// Combine preintegrated measurements, in the form of a tangent space vector, - /// with gravity, velocity, and Coriolis forces into a tangent space update. - Vector9 predictXi(const Vector9& pim, double dt, const Vector3& gravity, + /// Integrate a tangent vector forwards on tangent space, taking into account + /// Coriolis forces if omegaCoriolis is given. + Vector9 integrateTangent(const Vector9& pim, double dt, const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis = false, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 9> H2 = boost::none) const; - /// Combine preintegrated measurements, in the form of a tangent space vector, - /// with gravity, velocity, and Coriolis forces into a new state. - NavState predict(const Vector9& pim, double dt, const Vector3& gravity, + /// Integrate a tangent vector forwards on tangent space, taking into account + /// Coriolis forces if omegaCoriolis is given. Calls retract after to yield a NavState + NavState predict(const Vector9& pim, double dt, const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis = false, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 9> H2 = boost::none) const; diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index e2f1466a6..2c4694e41 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -64,14 +64,11 @@ void PreintegrationBase::updatePreintegratedMeasurements( OptionalJacobian<9, 9> F) { const Matrix3 dRij = deltaRij_.matrix(); // expensive - const Vector3 temp = dRij * correctedAcc * deltaT; + const Vector3 j_acc = dRij * correctedAcc; // acceleration in current frame - if (!p().use2ndOrderIntegration) { - deltaPij_ += deltaVij_ * deltaT; - } else { - deltaPij_ += deltaVij_ * deltaT + 0.5 * temp * deltaT; - } - deltaVij_ += temp; + double dt22 = 0.5 * deltaT * deltaT; + deltaPij_ += deltaVij_ * deltaT + dt22 * j_acc; + deltaVij_ += deltaT * j_acc; Matrix3 R_i, F_angles_angles; if (F) @@ -81,10 +78,7 @@ void PreintegrationBase::updatePreintegratedMeasurements( if (F) { const Matrix3 F_vel_angles = -R_i * skewSymmetric(correctedAcc) * deltaT; Matrix3 F_pos_angles; - if (p().use2ndOrderIntegration) - F_pos_angles = 0.5 * F_vel_angles * deltaT; - else - F_pos_angles = Z_3x3; + F_pos_angles = 0.5 * F_vel_angles * deltaT; // pos vel angle *F << // @@ -101,13 +95,8 @@ void PreintegrationBase::updatePreintegratedJacobians( const Matrix3 dRij = deltaRij_.matrix(); // expensive const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT * delRdelBiasOmega_; - if (!p().use2ndOrderIntegration) { - delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT; - delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT; - } else { - delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - 0.5 * dRij * deltaT * deltaT; - delPdelBiasOmega_ += deltaT * (delVdelBiasOmega_ + temp * 0.5); - } + delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - 0.5 * dRij * deltaT * deltaT; + delPdelBiasOmega_ += deltaT * (delVdelBiasOmega_ + temp * 0.5); delVdelBiasAcc_ += -dRij * deltaT; delVdelBiasOmega_ += temp; update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT); @@ -135,10 +124,9 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( //------------------------------------------------------------------------------ Vector9 PreintegrationBase::biasCorrectedDelta( const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H) const { + // Correct deltaRij, derivative is delRdelBiasOmega_ const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - Matrix3 D_deltaRij_bias; - Rot3 deltaRij = PreintegratedRotation::biascorrectedDeltaRij( - biasIncr.gyroscope(), H ? &D_deltaRij_bias : 0); + Rot3 deltaRij = biascorrectedDeltaRij(biasIncr.gyroscope()); Vector9 xi; Matrix3 D_dR_deltaRij; @@ -147,9 +135,10 @@ Vector9 PreintegrationBase::biasCorrectedDelta( + delPdelBiasOmega_ * biasIncr.gyroscope(); NavState::dV(xi) = deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() + delVdelBiasOmega_ * biasIncr.gyroscope(); + if (H) { Matrix36 D_dR_bias, D_dP_bias, D_dV_bias; - D_dR_bias << Z_3x3, D_dR_deltaRij * D_deltaRij_bias; + D_dR_bias << Z_3x3, D_dR_deltaRij * delRdelBiasOmega_; D_dP_bias << delPdelBiasAcc_, delPdelBiasOmega_; D_dV_bias << delVdelBiasAcc_, delVdelBiasOmega_; (*H) << D_dR_bias, D_dP_bias, D_dV_bias; @@ -161,13 +150,23 @@ Vector9 PreintegrationBase::biasCorrectedDelta( NavState PreintegrationBase::predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 6> H2) const { + // correct for bias Matrix96 D_biasCorrected_bias; Vector9 biasCorrected = biasCorrectedDelta(bias_i, H2 ? &D_biasCorrected_bias : 0); + + // integrate on tangent space Matrix9 D_delta_state, D_delta_biasCorrected; - Vector9 xi = state_i.predictXi(biasCorrected, deltaTij_, p().gravity, + Vector9 xi = state_i.integrateTangent(biasCorrected, deltaTij_, p().omegaCoriolis, p().use2ndOrderCoriolis, H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0); + + // Correct for gravity + double dt = deltaTij_, dt2 = dt * dt; + NavState::dP(xi) += 0.5 * p().b_gravity * dt2; + NavState::dV(xi) += p().b_gravity * dt; + + // Use retract to get back to NavState manifold Matrix9 D_predict_state, D_predict_delta; NavState state_j = state_i.retract(xi, D_predict_state, D_predict_delta); if (H1) @@ -313,11 +312,11 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, //------------------------------------------------------------------------------ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Vector3& gravity, const Vector3& omegaCoriolis, + const Vector3& b_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // NOTE(frank): parameters are supposed to be constant, below is only provided for compatibility boost::shared_ptr q = boost::make_shared(p()); - q->gravity = gravity; + q->b_gravity = b_gravity; q->omegaCoriolis = omegaCoriolis; q->use2ndOrderCoriolis = use2ndOrderCoriolis; p_ = q; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 2ee6b79e6..3079cd5c8 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -24,6 +24,7 @@ #include #include #include +#include namespace gtsam { @@ -32,11 +33,16 @@ struct PoseVelocityBias { Pose3 pose; Vector3 velocity; imuBias::ConstantBias bias; - PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, const imuBias::ConstantBias _bias) - : pose(_pose), velocity(_velocity), bias(_bias) {} - PoseVelocityBias(const NavState& navState, const imuBias::ConstantBias _bias) - : pose(navState.pose()), velocity(navState.velocity()), bias(_bias) {} - NavState navState() const { return NavState(pose,velocity);} + PoseVelocityBias(const Pose3& _pose, const Vector3& _velocity, + const imuBias::ConstantBias _bias) : + pose(_pose), velocity(_velocity), bias(_bias) { + } + PoseVelocityBias(const NavState& navState, const imuBias::ConstantBias _bias) : + pose(navState.pose()), velocity(navState.velocity()), bias(_bias) { + } + NavState navState() const { + return NavState(pose, velocity); + } }; /** @@ -45,29 +51,41 @@ struct PoseVelocityBias { * It includes the definitions of the preintegrated variables and the methods * to access, print, and compare them. */ -class PreintegrationBase : public PreintegratedRotation { +class PreintegrationBase: public PreintegratedRotation { - public: +public: /// Parameters for pre-integration: /// Usage: Create just a single Params and pass a shared pointer to the constructor - struct Params : PreintegratedRotation::Params { - Matrix3 accelerometerCovariance; ///< continuous-time "Covariance" of accelerometer + struct Params: PreintegratedRotation::Params { + Matrix3 accelerometerCovariance; ///< continuous-time "Covariance" of accelerometer Matrix3 integrationCovariance; ///< continuous-time "Covariance" describing integration uncertainty /// (to compensate errors in Euler integration) - bool use2ndOrderIntegration; ///< Controls the order of integration /// (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) - bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration - Vector3 gravity; ///< Gravity constant + bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration + Vector3 b_gravity; ///< Gravity constant in body frame - Params() - : accelerometerCovariance(I_3x3), - integrationCovariance(I_3x3), - use2ndOrderIntegration(false), - use2ndOrderCoriolis(false), - gravity(0, 0, 9.8) {} + /// The Params constructor insists on the user passing in gravity in the *body* frame. + /// For convenience, two commonly used conventions are provided by named constructors below + Params(const Vector3& b_gravity) : + accelerometerCovariance(I_3x3), integrationCovariance(I_3x3), use2ndOrderCoriolis( + false), b_gravity(b_gravity) { + } + + // Default Params for Front-Right-Down convention: gravity points along positive Z-axis + static boost::shared_ptr MakeSharedFRD(double g = 9.81) { + return boost::make_shared(Vector3(0, 0, g)); + } + + // Default Params for Front-Left-Up convention: gravity points along negative Z-axis + static boost::shared_ptr MakeSharedFLU(double g = 9.81) { + return boost::make_shared(Vector3(0, 0, -g)); + } + + protected: + /// Default constructor for serialization only: uninitialized! + Params(); - private: /** Serialization function */ friend class boost::serialization::access; template @@ -75,29 +93,29 @@ class PreintegrationBase : public PreintegratedRotation { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation::Params); ar & BOOST_SERIALIZATION_NVP(accelerometerCovariance); ar & BOOST_SERIALIZATION_NVP(integrationCovariance); - ar & BOOST_SERIALIZATION_NVP(use2ndOrderIntegration); ar & BOOST_SERIALIZATION_NVP(use2ndOrderCoriolis); - ar & BOOST_SERIALIZATION_NVP(gravity); + ar & BOOST_SERIALIZATION_NVP(b_gravity); } }; - protected: +protected: /// Acceleration and gyro bias used for preintegration imuBias::ConstantBias biasHat_; - Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i) - Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame) + Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i) + Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame) - Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias - Matrix3 delPdelBiasOmega_; ///< Jacobian of preintegrated position w.r.t. angular rate bias - Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias - Matrix3 delVdelBiasOmega_; ///< Jacobian of preintegrated velocity w.r.t. angular rate bias + Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias + Matrix3 delPdelBiasOmega_; ///< Jacobian of preintegrated position w.r.t. angular rate bias + Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias + Matrix3 delVdelBiasOmega_; ///< Jacobian of preintegrated velocity w.r.t. angular rate bias /// Default constructor for serialization - PreintegrationBase() {} + PreintegrationBase() { + } - public: +public: /** * Constructor, initializes the variables in the base class @@ -105,27 +123,45 @@ class PreintegrationBase : public PreintegratedRotation { * @param p Parameters, typically fixed in a single application */ PreintegrationBase(const boost::shared_ptr& p, - const imuBias::ConstantBias& biasHat) - : PreintegratedRotation(p), biasHat_(biasHat) { + const imuBias::ConstantBias& biasHat) : + PreintegratedRotation(p), biasHat_(biasHat) { resetIntegration(); } /// Re-initialize PreintegratedMeasurements void resetIntegration(); - const Params& p() const { return *boost::static_pointer_cast(p_);} + const Params& p() const { + return *boost::static_pointer_cast(p_); + } /// getters - const imuBias::ConstantBias& biasHat() const { return biasHat_; } - const Vector3& deltaPij() const { return deltaPij_; } - const Vector3& deltaVij() const { return deltaVij_; } - const Matrix3& delPdelBiasAcc() const { return delPdelBiasAcc_; } - const Matrix3& delPdelBiasOmega() const { return delPdelBiasOmega_; } - const Matrix3& delVdelBiasAcc() const { return delVdelBiasAcc_; } - const Matrix3& delVdelBiasOmega() const { return delVdelBiasOmega_; } + const imuBias::ConstantBias& biasHat() const { + return biasHat_; + } + const Vector3& deltaPij() const { + return deltaPij_; + } + const Vector3& deltaVij() const { + return deltaVij_; + } + const Matrix3& delPdelBiasAcc() const { + return delPdelBiasAcc_; + } + const Matrix3& delPdelBiasOmega() const { + return delPdelBiasOmega_; + } + const Matrix3& delVdelBiasAcc() const { + return delVdelBiasAcc_; + } + const Matrix3& delVdelBiasOmega() const { + return delVdelBiasOmega_; + } // Exposed for MATLAB - Vector6 biasHatVector() const { return biasHat_.vector(); } + Vector6 biasHatVector() const { + return biasHat_.vector(); + } /// print void print(const std::string& s) const; @@ -134,18 +170,16 @@ class PreintegrationBase : public PreintegratedRotation { bool equals(const PreintegrationBase& other, double tol) const; /// Update preintegrated measurements - void updatePreintegratedMeasurements(const Vector3& correctedAcc, const Rot3& incrR, - const double deltaT, OptionalJacobian<9, 9> F); + void updatePreintegratedMeasurements(const Vector3& correctedAcc, + const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F); /// Update Jacobians to be used during preintegration void updatePreintegratedJacobians(const Vector3& correctedAcc, - const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, - double deltaT); + const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, double deltaT); void correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, - const Vector3& measuredOmega, - Vector3* correctedAcc, - Vector3* correctedOmega); + const Vector3& measuredOmega, Vector3* correctedAcc, + Vector3* correctedOmega); /// Given the estimate of the bias, return a NavState tangent vector /// summarizing the preintegrated IMU measurements so far @@ -154,23 +188,23 @@ class PreintegrationBase : public PreintegratedRotation { /// Predict state at time j NavState predict(const NavState& state_i, const imuBias::ConstantBias& bias_i, - OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 6> H2 = boost::none) const; + OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 6> H2 = + boost::none) const; /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j - Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, - const Vector3& vel_j, const imuBias::ConstantBias& bias_i, - OptionalJacobian<9, 6> H1 = boost::none, - OptionalJacobian<9, 3> H2 = boost::none, - OptionalJacobian<9, 6> H3 = boost::none, - OptionalJacobian<9, 3> H4 = boost::none, - OptionalJacobian<9, 6> H5 = boost::none) const; + Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, + const Pose3& pose_j, const Vector3& vel_j, + const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H1 = + boost::none, OptionalJacobian<9, 3> H2 = boost::none, + OptionalJacobian<9, 6> H3 = boost::none, OptionalJacobian<9, 3> H4 = + boost::none, OptionalJacobian<9, 6> H5 = boost::none) const; /// @deprecated predict PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i, const Vector3& gravity, const Vector3& omegaCoriolis, - const bool use2ndOrderCoriolis = false); + const imuBias::ConstantBias& bias_i, const Vector3& b_gravity, + const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); - private: +private: /** Serialization function */ friend class boost::serialization::access; template @@ -187,4 +221,4 @@ class PreintegrationBase : public PreintegratedRotation { } }; -} /// namespace gtsam +} /// namespace gtsam diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 33409a7f8..a2746f1ea 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -57,15 +57,11 @@ Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, const Vector3& deltaVij_old, const Rot3& deltaRij_old, const Vector3& correctedAcc, const Vector3& correctedOmega, - const double deltaT, const bool use2ndOrderIntegration_) { + const double deltaT) { Matrix3 dRij = deltaRij_old.matrix(); Vector3 temp = dRij * correctedAcc * deltaT; Vector3 deltaPij_new; - if (!use2ndOrderIntegration_) { - deltaPij_new = deltaPij_old + deltaVij_old * deltaT; - } else { - deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; - } + deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; Vector3 deltaVij_new = deltaVij_old + temp; Vector result(6); @@ -93,11 +89,9 @@ const Matrix3 kIntegrationErrorCovariance = intNoiseVar * I_3x3; /* ************************************************************************* */ PreintegratedImuMeasurements evaluatePreintegratedMeasurements( const imuBias::ConstantBias& bias, const list& measuredAccs, - const list& measuredOmegas, const list& deltaTs, - const bool use2ndOrderIntegration = false) { + const list& measuredOmegas, const list& deltaTs) { PreintegratedImuMeasurements result(bias, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, - use2ndOrderIntegration); + kMeasuredOmegaCovariance, kIntegrationErrorCovariance); list::const_iterator itAcc = measuredAccs.begin(); list::const_iterator itOmega = measuredOmegas.begin(); @@ -142,27 +136,22 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { } // namespace /* ************************************************************************* */ -namespace straight { -// Set up IMU measurements -static const double g = 10; // make this easy to check -static const double a = 0.2, dt = 3.0; -const double exact = dt * dt / 2; -Vector3 measuredAcc(a, 0, -g), measuredOmega(0, 0, 0); +TEST(ImuFactor, StraightLine) { + // Set up IMU measurements + static const double g = 10; // make this easy to check + static const double a = 0.2, dt = 3.0; + const double exact = dt * dt / 2; + Vector3 measuredAcc(a, 0, -g), measuredOmega(0, 0, 0); -// Set up body pointing towards y axis, and start at 10,20,0 with zero velocity -// TODO(frank): clean up Rot3 mess -static const Rot3 nRb(Point3(0, 1, 0), Point3(1, 0, 0), Point3(0, 0, -1)); -static const Point3 initial_position(10, 20, 0); -static const NavState state1(nRb, initial_position, Velocity3(0, 0, 0)); -} - -TEST(ImuFactor, StraightLineSecondOrder) { - using namespace straight; + // Set up body pointing towards y axis, and start at 10,20,0 with zero velocity + // TODO(frank): clean up Rot3 mess + static const Rot3 nRb(Point3(0, 1, 0), Point3(1, 0, 0), Point3(0, 0, -1)); + static const Point3 initial_position(10, 20, 0); + static const NavState state1(nRb, initial_position, Velocity3(0, 0, 0)); imuBias::ConstantBias biasHat, bias; boost::shared_ptr p = PreintegratedImuMeasurements::Params::MakeSharedFRD(); - p->use2ndOrderIntegration = true; p->b_gravity = Vector3(0, 0, g); // FRD convention PreintegratedImuMeasurements pim(p, biasHat); @@ -186,39 +175,6 @@ TEST(ImuFactor, StraightLineSecondOrder) { EXPECT(assert_equal(expected, pim.predict(state1, bias))); } -TEST(ImuFactor, StraightLineFirstOrder) { - using namespace straight; - - imuBias::ConstantBias biasHat, bias; - boost::shared_ptr p = - PreintegratedImuMeasurements::Params::MakeSharedFRD(); - p->use2ndOrderIntegration = false; - p->b_gravity = Vector3(0, 0, g); // FRD convention - - // We do a rough approximation: - PreintegratedImuMeasurements pim(p, biasHat); - for (size_t i = 0; i < 10; i++) - pim.integrateMeasurement(measuredAcc, measuredOmega, dt / 10); - - // Check integrated quantities in body frame: gravity measured by IMU is integrated! - const double approx = exact * 0.9; // approximation for dt split into 10 intervals - EXPECT(assert_equal(Rot3(), pim.deltaRij())); - EXPECT(assert_equal(Vector3(a * approx, 0, -g * approx), pim.deltaPij())); - EXPECT(assert_equal(Vector3(a * dt, 0, -g * dt), pim.deltaVij())); // still correct - - // Check bias-corrected delta: also in body frame - Vector9 expectedBC; - expectedBC << 0, 0, 0, a * approx, 0, -g * approx, a * dt, 0, -g * dt; - EXPECT(assert_equal(expectedBC, pim.biasCorrectedDelta(bias))); - - // Check prediction, note we move along x in body, along y in nav - // In this instance the position is just an approximation, - // but gravity should be subtracted out exactly - NavState expected(nRb, Point3(10, 20 + a * approx, 0), Velocity3(0, a * dt, 0)); - GTSAM_PRINT(pim); - EXPECT(assert_equal(expected, pim.predict(state1, bias))); -} - /* ************************************************************************* */ TEST(ImuFactor, PreintegratedMeasurements) { // Linearization point @@ -236,11 +192,9 @@ TEST(ImuFactor, PreintegratedMeasurements) { Rot3 expectedDeltaR1 = Rot3::RzRyRx(0.5 * M_PI / 100.0, 0.0, 0.0); double expectedDeltaT1(0.5); - bool use2ndOrderIntegration = true; // Actual preintegrated values PreintegratedImuMeasurements actual1(bias, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, - use2ndOrderIntegration); + kMeasuredOmegaCovariance, kIntegrationErrorCovariance); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); EXPECT( @@ -294,12 +248,11 @@ double deltaT = 1.0; TEST(ImuFactor, PreintegrationBaseMethods) { using namespace common; boost::shared_ptr p = - PreintegratedImuMeasurements::Params::MakeSharedFRD(); + PreintegratedImuMeasurements::Params::MakeSharedFRD(); p->gyroscopeCovariance = kMeasuredOmegaCovariance; p->omegaCoriolis = Vector3(0.02, 0.03, 0.04); p->accelerometerCovariance = kMeasuredAccCovariance; p->integrationCovariance = kIntegrationErrorCovariance; - p->use2ndOrderIntegration = false; p->use2ndOrderCoriolis = true; PreintegratedImuMeasurements pim(p, bias); @@ -332,10 +285,8 @@ TEST(ImuFactor, PreintegrationBaseMethods) { /* ************************************************************************* */ TEST(ImuFactor, ErrorAndJacobians) { using namespace common; - bool use2ndOrderIntegration = true; PreintegratedImuMeasurements pim(bias, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, - use2ndOrderIntegration); + kMeasuredOmegaCovariance, kIntegrationErrorCovariance); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); EXPECT(assert_equal(state2, pim.predict(state1, bias), 1e-6)); @@ -631,11 +582,10 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } - bool use2ndOrderIntegration = false; // Actual preintegrated values PreintegratedImuMeasurements preintegrated = evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs, use2ndOrderIntegration); + deltaTs); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians @@ -658,20 +608,17 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Compute expected f_pos_vel wrt positions Matrix dfpv_dpos = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - deltaPij_old); + newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaPij_old); // Compute expected f_pos_vel wrt velocities Matrix dfpv_dvel = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - deltaVij_old); + newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaVij_old); // Compute expected f_pos_vel wrt angles Matrix dfpv_dangle = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - deltaRij_old); + newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaRij_old); Matrix FexpectedTop6(6, 9); FexpectedTop6 << dfpv_dpos, dfpv_dvel, dfpv_dangle; @@ -698,14 +645,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), newMeasuredAcc); + deltaRij_old, _1, newMeasuredOmega, newDeltaT), newMeasuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), - newMeasuredOmega); + deltaRij_old, newMeasuredAcc, _1, newDeltaT), newMeasuredOmega); Matrix GexpectedTop6(6, 9); GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; @@ -754,11 +699,10 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } - bool use2ndOrderIntegration = true; // Actual preintegrated values PreintegratedImuMeasurements preintegrated = evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs, use2ndOrderIntegration); + deltaTs); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians @@ -781,20 +725,17 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Compute expected f_pos_vel wrt positions Matrix dfpv_dpos = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - deltaPij_old); + newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaPij_old); // Compute expected f_pos_vel wrt velocities Matrix dfpv_dvel = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - deltaVij_old); + newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaVij_old); // Compute expected f_pos_vel wrt angles Matrix dfpv_dangle = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, - newMeasuredAcc, newMeasuredOmega, newDeltaT, use2ndOrderIntegration), - deltaRij_old); + newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaRij_old); Matrix FexpectedTop6(6, 9); FexpectedTop6 << dfpv_dpos, dfpv_dvel, dfpv_dangle; @@ -821,14 +762,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), newMeasuredAcc); + deltaRij_old, _1, newMeasuredOmega, newDeltaT), newMeasuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, newMeasuredAcc, _1, newDeltaT, use2ndOrderIntegration), - newMeasuredOmega); + deltaRij_old, newMeasuredAcc, _1, newDeltaT), newMeasuredOmega); Matrix GexpectedTop6(6, 9); GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index 683a97d50..e5f25ffa5 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -178,12 +178,12 @@ TEST(NavState, PredictXi) { xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; double dt = 0.5; Matrix9 actualH1, actualH2; - boost::function predictXi = - boost::bind(&NavState::predictXi, _1, _2, dt, kGravity, kOmegaCoriolis, + boost::function integrateTangent = + boost::bind(&NavState::integrateTangent, _1, _2, dt, kGravity, kOmegaCoriolis, false, boost::none, boost::none); - kState1.predictXi(xi, dt, kGravity, kOmegaCoriolis, false, actualH1, actualH2); - EXPECT(assert_equal(numericalDerivative21(predictXi, kState1, xi), actualH1)); - EXPECT(assert_equal(numericalDerivative22(predictXi, kState1, xi), actualH2)); + kState1.integrateTangent(xi, dt, kGravity, kOmegaCoriolis, false, actualH1, actualH2); + EXPECT(assert_equal(numericalDerivative21(integrateTangent, kState1, xi), actualH1)); + EXPECT(assert_equal(numericalDerivative22(integrateTangent, kState1, xi), actualH2)); } /* ************************************************************************* */ From 323ed5220b7d0a365a341006849a7f24d9c90f91 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 13:22:32 +0200 Subject: [PATCH 071/142] Gravity should be specified in NAV coordinates! Default Nav frame is assumed to be *Z down* for the old-style constructors. --- gtsam/navigation/CombinedImuFactor.cpp | 10 +-- gtsam/navigation/CombinedImuFactor.h | 20 ++--- gtsam/navigation/ImuFactor.cpp | 10 +-- gtsam/navigation/ImuFactor.h | 6 +- gtsam/navigation/NavState.cpp | 32 ++----- gtsam/navigation/NavState.h | 14 +-- gtsam/navigation/PreintegrationBase.cpp | 11 +-- gtsam/navigation/PreintegrationBase.h | 20 ++--- gtsam/navigation/tests/testImuFactor.cpp | 106 +++++++++++------------ gtsam/navigation/tests/testNavState.cpp | 48 ++++------ 10 files changed, 114 insertions(+), 163 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index f3647fcc0..9c2dedea1 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -148,7 +148,7 @@ PreintegratedCombinedMeasurements::PreintegratedCombinedMeasurements( if (!use2ndOrderIntegration) throw("PreintegratedImuMeasurements no longer supports first-order integration: it incorrectly compensated for gravity"); biasHat_ = biasHat; - boost::shared_ptr p = Params::MakeSharedFRD(); + boost::shared_ptr p = Params::MakeSharedD(); p->gyroscopeCovariance = measuredOmegaCovariance; p->accelerometerCovariance = measuredAccCovariance; p->integrationCovariance = integrationErrorCovariance; @@ -260,7 +260,7 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, //------------------------------------------------------------------------------ CombinedImuFactor::CombinedImuFactor( Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, - const CombinedPreintegratedMeasurements& pim, const Vector3& b_gravity, + const CombinedPreintegratedMeasurements& pim, const Vector3& n_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor, const bool use2ndOrderCoriolis) : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, @@ -268,7 +268,7 @@ CombinedImuFactor::CombinedImuFactor( _PIM_(pim) { boost::shared_ptr p = boost::make_shared(pim.p()); - p->b_gravity = b_gravity; + p->n_gravity = n_gravity; p->omegaCoriolis = omegaCoriolis; p->body_P_sensor = body_P_sensor; p->use2ndOrderCoriolis = use2ndOrderCoriolis; @@ -279,11 +279,11 @@ void CombinedImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, CombinedPreintegratedMeasurements& pim, - const Vector3& b_gravity, + const Vector3& n_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // use deprecated predict - PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, b_gravity, + PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, n_gravity, omegaCoriolis, use2ndOrderCoriolis); pose_j = pvb.pose; vel_j = pvb.velocity; diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 142e8706f..192cc3d99 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -66,19 +66,19 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { Matrix3 biasOmegaCovariance; ///< continuous-time "Covariance" describing gyroscope bias random walk Matrix6 biasAccOmegaInit; ///< covariance of bias used for pre-integration - /// See two named constructors below for good values of b_gravity in body frame - Params(const Vector3& b_gravity) : - PreintegrationBase::Params(b_gravity), biasAccCovariance(I_3x3), biasOmegaCovariance( + /// See two named constructors below for good values of n_gravity in body frame + Params(const Vector3& n_gravity) : + PreintegrationBase::Params(n_gravity), biasAccCovariance(I_3x3), biasOmegaCovariance( I_3x3), biasAccOmegaInit(I_6x6) { } - // Default Params for Front-Right-Down convention: b_gravity points along positive Z-axis - static boost::shared_ptr MakeSharedFRD(double g = 9.81) { + // Default Params for a Z-down navigation frame, such as NED: gravity points along positive Z-axis + static boost::shared_ptr MakeSharedD(double g = 9.81) { return boost::make_shared(Vector3(0, 0, g)); } - // Default Params for Front-Left-Up convention: b_gravity points along negative Z-axis - static boost::shared_ptr MakeSharedFLU(double g = 9.81) { + // Default Params for a Z-up navigation frame, such as ENU: gravity points along negative Z-axis + static boost::shared_ptr MakeSharedU(double g = 9.81) { return boost::make_shared(Vector3(0, 0, -g)); } @@ -151,7 +151,7 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { Matrix preintMeasCov() const { return preintMeasCov_; } /// deprecated constructor - /// NOTE(frank): assumes FRD convention, only second order integration supported + /// NOTE(frank): assumes Z-Down convention, only second order integration supported PreintegratedCombinedMeasurements(const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, @@ -263,7 +263,7 @@ public: /// @deprecated constructor CombinedImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias_i, Key bias_j, const CombinedPreintegratedMeasurements& pim, - const Vector3& b_gravity, const Vector3& omegaCoriolis, + const Vector3& n_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor = boost::none, const bool use2ndOrderCoriolis = false); @@ -271,7 +271,7 @@ public: static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, CombinedPreintegratedMeasurements& pim, - const Vector3& b_gravity, const Vector3& omegaCoriolis, + const Vector3& n_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 1b3736bec..1589f1a1b 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -116,7 +116,7 @@ PreintegratedImuMeasurements::PreintegratedImuMeasurements( if (!use2ndOrderIntegration) throw("PreintegratedImuMeasurements no longer supports first-order integration: it incorrectly compensated for gravity"); biasHat_ = biasHat; - boost::shared_ptr p = Params::MakeSharedFRD(); + boost::shared_ptr p = Params::MakeSharedD(); p->gyroscopeCovariance = measuredOmegaCovariance; p->accelerometerCovariance = measuredAccCovariance; p->integrationCovariance = integrationErrorCovariance; @@ -171,14 +171,14 @@ Vector ImuFactor::evaluateError(const Pose3& pose_i, const Vector3& vel_i, //------------------------------------------------------------------------------ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, - const PreintegratedMeasurements& pim, const Vector3& b_gravity, + const PreintegratedMeasurements& pim, const Vector3& n_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor, const bool use2ndOrderCoriolis) : Base(noiseModel::Gaussian::Covariance(pim.preintMeasCov_), pose_i, vel_i, pose_j, vel_j, bias), _PIM_(pim) { boost::shared_ptr p = boost::make_shared< PreintegratedMeasurements::Params>(pim.p()); - p->b_gravity = b_gravity; + p->n_gravity = n_gravity; p->omegaCoriolis = omegaCoriolis; p->body_P_sensor = body_P_sensor; p->use2ndOrderCoriolis = use2ndOrderCoriolis; @@ -188,10 +188,10 @@ ImuFactor::ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, //------------------------------------------------------------------------------ void ImuFactor::Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - PreintegratedMeasurements& pim, const Vector3& b_gravity, + PreintegratedMeasurements& pim, const Vector3& n_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // use deprecated predict - PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, b_gravity, + PoseVelocityBias pvb = pim.predict(pose_i, vel_i, bias_i, n_gravity, omegaCoriolis, use2ndOrderCoriolis); pose_j = pvb.pose; vel_j = pvb.velocity; diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index c739120d3..f66d28828 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -108,7 +108,7 @@ public: Matrix preintMeasCov() const { return preintMeasCov_; } /// @deprecated constructor - /// NOTE(frank): assumes FRD convention, only second order integration supported + /// NOTE(frank): assumes Z-Down convention, only second order integration supported PreintegratedImuMeasurements(const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, const Matrix3& measuredOmegaCovariance, @@ -210,14 +210,14 @@ public: /// @deprecated constructor ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, const PreintegratedMeasurements& preintegratedMeasurements, - const Vector3& b_gravity, const Vector3& omegaCoriolis, + const Vector3& n_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor = boost::none, const bool use2ndOrderCoriolis = false); /// @deprecated use PreintegrationBase::predict static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, - PreintegratedMeasurements& pim, const Vector3& b_gravity, + PreintegratedMeasurements& pim, const Vector3& n_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 8ce8a200b..989a300fe 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -248,7 +248,8 @@ Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder, } //------------------------------------------------------------------------------ -Vector9 NavState::integrateTangent(const Vector9& pim, double dt, +Vector9 NavState::correctPIM(const Vector9& pim, double dt, + const Vector3& n_gravity, const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { const Rot3& nRb = R_; @@ -256,11 +257,12 @@ Vector9 NavState::integrateTangent(const Vector9& pim, double dt, const double dt2 = dt * dt; Vector9 delta; - Matrix3 D_dP_Ri, D_dP_nv; + Matrix3 D_dP_Ri1, D_dP_Ri2, D_dP_nv, D_dV_Ri; dR(delta) = dR(pim); dP(delta) = dP(pim) - + dt * nRb.unrotate(n_v, H1 ? &D_dP_Ri : 0, H2 ? &D_dP_nv : 0); - dV(delta) = dV(pim); + + dt * nRb.unrotate(n_v, H1 ? &D_dP_Ri1 : 0, H2 ? &D_dP_nv : 0) + + (0.5 * dt2) * nRb.unrotate(n_gravity, H1 ? &D_dP_Ri2 : 0); + dV(delta) = dV(pim) + dt * nRb.unrotate(n_gravity, H1 ? &D_dV_Ri : 0); if (omegaCoriolis) { delta += coriolis(dt, *omegaCoriolis, use2ndOrderCoriolis, H1); @@ -272,8 +274,9 @@ Vector9 NavState::integrateTangent(const Vector9& pim, double dt, if (H1) { if (!omegaCoriolis) H1->setZero(); // if coriolis H1 is already initialized - D_t_R(H1) += dt * D_dP_Ri; + D_t_R(H1) += dt * D_dP_Ri1 + (0.5 * dt2) * D_dP_Ri2; D_t_v(H1) += dt * D_dP_nv * Ri; + D_v_R(H1) += dt * D_dV_Ri; } if (H2) { H2->setIdentity(); @@ -283,24 +286,5 @@ Vector9 NavState::integrateTangent(const Vector9& pim, double dt, return delta; } //------------------------------------------------------------------------------ -NavState NavState::predict(const Vector9& pim, double dt, - const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, - OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { - - Matrix9 D_delta_state, D_delta_pim; - Vector9 delta = integrateTangent(pim, dt, omegaCoriolis, use2ndOrderCoriolis, - H1 ? &D_delta_state : 0, H2 ? &D_delta_pim : 0); - - Matrix9 D_predicted_state, D_predicted_delta; - NavState predicted = retract(delta, H1 ? &D_predicted_state : 0, - H1 || H2 ? &D_predicted_delta : 0); - // TODO(frank): the derivatives of retract are very sparse: inline below: - if (H1) - *H1 = D_predicted_state + D_predicted_delta * D_delta_state; - if (H2) - *H2 = D_predicted_delta * D_delta_pim; - return predicted; -} -//------------------------------------------------------------------------------ }/// namespace gtsam diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 7326b8df7..8eb8c54d7 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -210,21 +210,13 @@ public: Vector9 coriolis(double dt, const Vector3& omega, bool secondOrder = false, OptionalJacobian<9, 9> H = boost::none) const; - /// Integrate a tangent vector forwards on tangent space, taking into account - /// Coriolis forces if omegaCoriolis is given. - Vector9 integrateTangent(const Vector9& pim, double dt, + /// Correct preintegrated tangent vector with our velocity and rotated gravity, + /// taking into account Coriolis forces if omegaCoriolis is given. + Vector9 correctPIM(const Vector9& pim, double dt, const Vector3& n_gravity, const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis = false, OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 9> H2 = boost::none) const; - /// Integrate a tangent vector forwards on tangent space, taking into account - /// Coriolis forces if omegaCoriolis is given. Calls retract after to yield a NavState - NavState predict(const Vector9& pim, double dt, - const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis = - false, OptionalJacobian<9, 9> H1 = boost::none, - OptionalJacobian<9, 9> H2 = boost::none) const; - /// @} - private: /// @{ /// serialization diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 2c4694e41..f45b0f49c 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -157,15 +157,10 @@ NavState PreintegrationBase::predict(const NavState& state_i, // integrate on tangent space Matrix9 D_delta_state, D_delta_biasCorrected; - Vector9 xi = state_i.integrateTangent(biasCorrected, deltaTij_, + Vector9 xi = state_i.correctPIM(biasCorrected, deltaTij_, p().n_gravity, p().omegaCoriolis, p().use2ndOrderCoriolis, H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0); - // Correct for gravity - double dt = deltaTij_, dt2 = dt * dt; - NavState::dP(xi) += 0.5 * p().b_gravity * dt2; - NavState::dV(xi) += p().b_gravity * dt; - // Use retract to get back to NavState manifold Matrix9 D_predict_state, D_predict_delta; NavState state_j = state_i.retract(xi, D_predict_state, D_predict_delta); @@ -312,11 +307,11 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, //------------------------------------------------------------------------------ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, const Vector3& vel_i, const imuBias::ConstantBias& bias_i, - const Vector3& b_gravity, const Vector3& omegaCoriolis, + const Vector3& n_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis) { // NOTE(frank): parameters are supposed to be constant, below is only provided for compatibility boost::shared_ptr q = boost::make_shared(p()); - q->b_gravity = b_gravity; + q->n_gravity = n_gravity; q->omegaCoriolis = omegaCoriolis; q->use2ndOrderCoriolis = use2ndOrderCoriolis; p_ = q; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 3079cd5c8..8182693ed 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -63,22 +63,22 @@ public: /// (to compensate errors in Euler integration) /// (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration - Vector3 b_gravity; ///< Gravity constant in body frame + Vector3 n_gravity; ///< Gravity constant in body frame - /// The Params constructor insists on the user passing in gravity in the *body* frame. + /// The Params constructor insists on getting the navigation frame gravity vector /// For convenience, two commonly used conventions are provided by named constructors below - Params(const Vector3& b_gravity) : + Params(const Vector3& n_gravity) : accelerometerCovariance(I_3x3), integrationCovariance(I_3x3), use2ndOrderCoriolis( - false), b_gravity(b_gravity) { + false), n_gravity(n_gravity) { } - // Default Params for Front-Right-Down convention: gravity points along positive Z-axis - static boost::shared_ptr MakeSharedFRD(double g = 9.81) { + // Default Params for a Z-down navigation frame, such as NED: gravity points along positive Z-axis + static boost::shared_ptr MakeSharedD(double g = 9.81) { return boost::make_shared(Vector3(0, 0, g)); } - // Default Params for Front-Left-Up convention: gravity points along negative Z-axis - static boost::shared_ptr MakeSharedFLU(double g = 9.81) { + // Default Params for a Z-up navigation frame, such as ENU: gravity points along negative Z-axis + static boost::shared_ptr MakeSharedU(double g = 9.81) { return boost::make_shared(Vector3(0, 0, -g)); } @@ -94,7 +94,7 @@ public: ar & BOOST_SERIALIZATION_NVP(accelerometerCovariance); ar & BOOST_SERIALIZATION_NVP(integrationCovariance); ar & BOOST_SERIALIZATION_NVP(use2ndOrderCoriolis); - ar & BOOST_SERIALIZATION_NVP(b_gravity); + ar & BOOST_SERIALIZATION_NVP(n_gravity); } }; @@ -201,7 +201,7 @@ public: /// @deprecated predict PoseVelocityBias predict(const Pose3& pose_i, const Vector3& vel_i, - const imuBias::ConstantBias& bias_i, const Vector3& b_gravity, + const imuBias::ConstantBias& bias_i, const Vector3& n_gravity, const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false); private: diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index a2746f1ea..f433c2a9e 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -36,7 +36,7 @@ using symbol_shorthand::X; using symbol_shorthand::V; using symbol_shorthand::B; -static const Vector3 kGravity(0, 0, 9.81); +static const Vector3 kGravityAlongNavZDown(0, 0, 9.81); static const Vector3 kZeroOmegaCoriolis(0, 0, 0); static const Vector3 kNonZeroOmegaCoriolis(0, 0.1, 0.1); @@ -151,8 +151,7 @@ TEST(ImuFactor, StraightLine) { imuBias::ConstantBias biasHat, bias; boost::shared_ptr p = - PreintegratedImuMeasurements::Params::MakeSharedFRD(); - p->b_gravity = Vector3(0, 0, g); // FRD convention + PreintegratedImuMeasurements::Params::MakeSharedU(g); // Up convention! PreintegratedImuMeasurements pim(p, biasHat); for (size_t i = 0; i < 10; i++) @@ -171,7 +170,6 @@ TEST(ImuFactor, StraightLine) { // Check prediction, note we move along x in body, along y in nav NavState expected(nRb, Point3(10, 20 + a * exact, 0), Velocity3(0, a * dt, 0)); - GTSAM_PRINT(pim); EXPECT(assert_equal(expected, pim.predict(state1, bias))); } @@ -197,12 +195,10 @@ TEST(ImuFactor, PreintegratedMeasurements) { kMeasuredOmegaCovariance, kIntegrationErrorCovariance); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT( - assert_equal(Vector(expectedDeltaP1), Vector(actual1.deltaPij()), 1e-6)); - EXPECT( - assert_equal(Vector(expectedDeltaV1), Vector(actual1.deltaVij()), 1e-6)); - EXPECT(assert_equal(expectedDeltaR1, Rot3(actual1.deltaRij()), 1e-6)); - DOUBLES_EQUAL(expectedDeltaT1, actual1.deltaTij(), 1e-6); + EXPECT(assert_equal(Vector(expectedDeltaP1), Vector(actual1.deltaPij()))); + EXPECT(assert_equal(Vector(expectedDeltaV1), Vector(actual1.deltaVij()))); + EXPECT(assert_equal(expectedDeltaR1, Rot3(actual1.deltaRij()))); + DOUBLES_EQUAL(expectedDeltaT1, actual1.deltaTij(), 1e-9); // Integrate again Vector3 expectedDeltaP2; @@ -216,39 +212,38 @@ TEST(ImuFactor, PreintegratedMeasurements) { PreintegratedImuMeasurements actual2 = actual1; actual2.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT( - assert_equal(Vector(expectedDeltaP2), Vector(actual2.deltaPij()), 1e-6)); - EXPECT( - assert_equal(Vector(expectedDeltaV2), Vector(actual2.deltaVij()), 1e-6)); - EXPECT(assert_equal(expectedDeltaR2, Rot3(actual2.deltaRij()), 1e-6)); - DOUBLES_EQUAL(expectedDeltaT2, actual2.deltaTij(), 1e-6); + EXPECT(assert_equal(Vector(expectedDeltaP2), Vector(actual2.deltaPij()))); + EXPECT(assert_equal(Vector(expectedDeltaV2), Vector(actual2.deltaVij()))); + EXPECT(assert_equal(expectedDeltaR2, Rot3(actual2.deltaRij()))); + DOUBLES_EQUAL(expectedDeltaT2, actual2.deltaTij(), 1e-9); } /* ************************************************************************* */ // Common linearization point and measurements for tests namespace common { -imuBias::ConstantBias bias; // Bias -Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), - Point3(5.0, 1.0, -50.0)); -Vector3 v1(Vector3(0.5, 0.0, 0.0)); - -NavState state1(x1, v1); -Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0), - Point3(5.5, 1.0, -50.0)); -Vector3 v2(Vector3(0.5, 0.0, 0.0)); -NavState state2(x2, v2); +static const imuBias::ConstantBias biasHat, bias; // Bias +static const Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), + Point3(5.0, 1.0, 0)); +static const Vector3 v1(Vector3(0.5, 0.0, 0.0)); +static const NavState state1(x1, v1); // Measurements -Vector3 measuredOmega(M_PI / 100, 0, 0); -Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector(); -double deltaT = 1.0; +static const double w = M_PI / 100; +static const Vector3 measuredOmega(w, 0, 0); +static const Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown); +static const double deltaT = 1.0; + +static const Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + w, M_PI / 6.0, M_PI / 4.0), + Point3(5.5, 1.0, 0)); +static const Vector3 v2(Vector3(0.5, 0.0, 0.0)); +static const NavState state2(x2, v2); } // namespace common /* ************************************************************************* */ TEST(ImuFactor, PreintegrationBaseMethods) { using namespace common; boost::shared_ptr p = - PreintegratedImuMeasurements::Params::MakeSharedFRD(); + PreintegratedImuMeasurements::Params::MakeSharedD(); p->gyroscopeCovariance = kMeasuredOmegaCovariance; p->omegaCoriolis = Vector3(0.02, 0.03, 0.04); p->accelerometerCovariance = kMeasuredAccCovariance; @@ -285,22 +280,21 @@ TEST(ImuFactor, PreintegrationBaseMethods) { /* ************************************************************************* */ TEST(ImuFactor, ErrorAndJacobians) { using namespace common; - PreintegratedImuMeasurements pim(bias, kMeasuredAccCovariance, + PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT(assert_equal(state2, pim.predict(state1, bias), 1e-6)); + EXPECT(assert_equal(state2, pim.predict(state1, bias))); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); // Expected error - Vector errorExpected(9); - errorExpected << 0, 0, 0, 0, 0, 0, 0, 0, 0; + Vector expectedError(9); + expectedError << 0, 0, 0, 0, 0, 0, 0, 0, 0; EXPECT( - assert_equal(errorExpected, factor.evaluateError(x1, v1, x2, v2, bias), - 1e-6)); + assert_equal(expectedError, factor.evaluateError(x1, v1, x2, v2, bias))); Values values; values.insert(X(1), x1); @@ -308,7 +302,7 @@ TEST(ImuFactor, ErrorAndJacobians) { values.insert(X(2), x2); values.insert(V(2), v2); values.insert(B(1), bias); - EXPECT(assert_equal(errorExpected, factor.unwhitenedError(values), 1e-6)); + EXPECT(assert_equal(expectedError, factor.unwhitenedError(values))); // Make sure linearization is correct double diffDelta = 1e-5; @@ -331,17 +325,17 @@ TEST(ImuFactor, ErrorAndJacobians) { // Evaluate error with wrong values Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1); values.update(V(2), v2_wrong); - errorExpected << 0, 0, 0, 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901; + expectedError << 0, 0, 0, 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901; EXPECT( - assert_equal(errorExpected, - factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-6)); - EXPECT(assert_equal(errorExpected, factor.unwhitenedError(values), 1e-6)); + assert_equal(expectedError, + factor.evaluateError(x1, v1, x2, v2_wrong, bias))); + EXPECT(assert_equal(expectedError, factor.unwhitenedError(values))); // Make sure the whitening is done correctly Matrix cov = pim.preintMeasCov(); Matrix R = RtR(cov.inverse()); - Vector whitened = R * errorExpected; - EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values), 1e-6)); + Vector whitened = R * expectedError; + EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values),1e-5)); // Make sure linearization is correct EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); @@ -359,7 +353,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { // Measurements Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown) + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; @@ -370,7 +364,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kNonZeroOmegaCoriolis); Values values; @@ -397,7 +391,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { // Measurements Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown) + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; @@ -410,7 +404,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { // Create factor Pose3 bodyPsensor = Pose3(); bool use2ndOrderCoriolis = true; - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kNonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis); Values values; @@ -808,7 +802,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { // Measurements Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; - Vector3 measuredAcc = x1.rotation().unrotate(-Point3(kGravity)).vector() + Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown) + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; @@ -823,7 +817,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kNonZeroOmegaCoriolis); Values values; @@ -858,13 +852,13 @@ TEST(ImuFactor, PredictPositionAndVelocity) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias, kGravity, + PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias, kGravityAlongNavZDown, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; @@ -893,14 +887,14 @@ TEST(ImuFactor, PredictRotation) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); // Predict Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravity, kZeroOmegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3().ypr(M_PI / 10, 0, 0), Point3(0, 0, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 0, 0; @@ -926,14 +920,14 @@ TEST(ImuFactor, PredictArbitrary) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); // Predict Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravity, kZeroOmegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); // Regression test for Imu Refactor Rot3 expectedR( // diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index e5f25ffa5..ca6c2ffc1 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -149,55 +149,41 @@ boost::function coriolis = boost::bind( &NavState::coriolis, _1, dt, kOmegaCoriolis, _2, boost::none); TEST(NavState, Coriolis) { - Matrix9 actualH; + Matrix9 aH; // first-order - kState1.coriolis(dt, kOmegaCoriolis, false, actualH); - EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, false), actualH)); + kState1.coriolis(dt, kOmegaCoriolis, false, aH); + EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, false), aH)); // second-order - kState1.coriolis(dt, kOmegaCoriolis, true, actualH); - EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, true), actualH)); + kState1.coriolis(dt, kOmegaCoriolis, true, aH); + EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, true), aH)); } TEST(NavState, Coriolis2) { - Matrix9 actualH; + Matrix9 aH; NavState state2(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), Point3(5.0, 1.0, -50.0), Vector3(0.5, 0.0, 0.0)); // first-order - state2.coriolis(dt, kOmegaCoriolis, false, actualH); - EXPECT(assert_equal(numericalDerivative21(coriolis, state2, false), actualH)); + state2.coriolis(dt, kOmegaCoriolis, false, aH); + EXPECT(assert_equal(numericalDerivative21(coriolis, state2, false), aH)); // second-order - state2.coriolis(dt, kOmegaCoriolis, true, actualH); - EXPECT(assert_equal(numericalDerivative21(coriolis, state2, true), actualH)); + state2.coriolis(dt, kOmegaCoriolis, true, aH); + EXPECT(assert_equal(numericalDerivative21(coriolis, state2, true), aH)); } /* ************************************************************************* */ -TEST(NavState, PredictXi) { +TEST(NavState, correctPIM) { Vector9 xi; xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; double dt = 0.5; - Matrix9 actualH1, actualH2; - boost::function integrateTangent = - boost::bind(&NavState::integrateTangent, _1, _2, dt, kGravity, kOmegaCoriolis, + Matrix9 aH1, aH2; + boost::function correctPIM = + boost::bind(&NavState::correctPIM, _1, _2, dt, kGravity, kOmegaCoriolis, false, boost::none, boost::none); - kState1.integrateTangent(xi, dt, kGravity, kOmegaCoriolis, false, actualH1, actualH2); - EXPECT(assert_equal(numericalDerivative21(integrateTangent, kState1, xi), actualH1)); - EXPECT(assert_equal(numericalDerivative22(integrateTangent, kState1, xi), actualH2)); -} - -/* ************************************************************************* */ -TEST(NavState, Predict) { - Vector9 xi; - xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; - double dt = 0.5; - Matrix9 actualH1, actualH2; - boost::function predict = - boost::bind(&NavState::predict, _1, _2, dt, kGravity, kOmegaCoriolis, - false, boost::none, boost::none); - kState1.predict(xi, dt, kGravity, kOmegaCoriolis, false, actualH1, actualH2); - EXPECT(assert_equal(numericalDerivative21(predict, kState1, xi), actualH1)); - EXPECT(assert_equal(numericalDerivative22(predict, kState1, xi), actualH2)); + kState1.correctPIM(xi, dt, kGravity, kOmegaCoriolis, false, aH1, aH2); + EXPECT(assert_equal(numericalDerivative21(correctPIM, kState1, xi), aH1)); + EXPECT(assert_equal(numericalDerivative22(correctPIM, kState1, xi), aH2)); } /* ************************************************************************* */ From 77d8e7d0bd024e1bdc16c1a5740dd44141417a59 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 13:39:50 +0200 Subject: [PATCH 072/142] All tests pass now ! --- .../tests/testCombinedImuFactor.cpp | 74 +++++++++---------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/gtsam/navigation/tests/testCombinedImuFactor.cpp b/gtsam/navigation/tests/testCombinedImuFactor.cpp index 87d3f4385..53c0d7e23 100644 --- a/gtsam/navigation/tests/testCombinedImuFactor.cpp +++ b/gtsam/navigation/tests/testCombinedImuFactor.cpp @@ -78,7 +78,7 @@ Rot3 updatePreintegratedMeasurementsRot(const Vector3 deltaPij_old, deltaVij_old, deltaRij_old, bias_old, correctedAcc, correctedOmega, deltaT, use2ndOrderIntegration); - return Rot3::Expmap(result.segment<3>(6)); + return Rot3::Expmap(result.segment < 3 > (6)); } // Auxiliary functions to test preintegrated Jacobians @@ -87,9 +87,8 @@ Rot3 updatePreintegratedMeasurementsRot(const Vector3 deltaPij_old, CombinedImuFactor::CombinedPreintegratedMeasurements evaluatePreintegratedMeasurements( const imuBias::ConstantBias& bias, const list& measuredAccs, const list& measuredOmegas, const list& deltaTs) { - CombinedImuFactor::CombinedPreintegratedMeasurements result(bias, - I_3x3, I_3x3, I_3x3, - I_3x3, I_3x3, I_6x6, false); + CombinedImuFactor::CombinedPreintegratedMeasurements result(bias, I_3x3, + I_3x3, I_3x3, I_3x3, I_3x3, I_6x6); list::const_iterator itAcc = measuredAccs.begin(); list::const_iterator itOmega = measuredOmegas.begin(); @@ -136,13 +135,11 @@ TEST( CombinedImuFactor, PreintegratedMeasurements ) { double tol = 1e-6; // Actual preintegrated values - ImuFactor::PreintegratedMeasurements expected1(bias, Z_3x3, - Z_3x3, Z_3x3); + ImuFactor::PreintegratedMeasurements expected1(bias, Z_3x3, Z_3x3, Z_3x3); expected1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - CombinedImuFactor::CombinedPreintegratedMeasurements actual1(bias, - Z_3x3, Z_3x3, Z_3x3, Z_3x3, - Z_3x3, Z_6x6); + CombinedImuFactor::CombinedPreintegratedMeasurements actual1(bias, Z_3x3, + Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_6x6); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); @@ -187,7 +184,8 @@ TEST( CombinedImuFactor, ErrorWithBiases ) { combined_pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - ImuFactor imuFactor(X(1), V(1), X(2), V(2), B(1), pim, gravity, omegaCoriolis); + ImuFactor imuFactor(X(1), V(1), X(2), V(2), B(1), pim, gravity, + omegaCoriolis); noiseModel::Gaussian::shared_ptr Combinedmodel = noiseModel::Gaussian::Covariance(combined_pim.preintMeasCov()); @@ -195,7 +193,8 @@ TEST( CombinedImuFactor, ErrorWithBiases ) { combined_pim, gravity, omegaCoriolis); Vector errorExpected = imuFactor.evaluateError(x1, v1, x2, v2, bias); - Vector errorActual = combinedfactor.evaluateError(x1, v1, x2, v2, bias, bias2); + Vector errorActual = combinedfactor.evaluateError(x1, v1, x2, v2, bias, + bias2); EXPECT(assert_equal(errorExpected, errorActual.head(9), tol)); // Expected Jacobians @@ -238,7 +237,7 @@ TEST( CombinedImuFactor, FirstOrderPreIntegratedMeasurements ) { } // Actual preintegrated values - CombinedImuFactor::CombinedPreintegratedMeasurements preintegrated = + CombinedImuFactor::CombinedPreintegratedMeasurements pim = evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, deltaTs); @@ -265,16 +264,12 @@ TEST( CombinedImuFactor, FirstOrderPreIntegratedMeasurements ) { Matrix expectedDelRdelBiasOmega = expectedDelRdelBias.rightCols(3); // Compare Jacobians - EXPECT(assert_equal(expectedDelPdelBiasAcc, preintegrated.delPdelBiasAcc())); - EXPECT( - assert_equal(expectedDelPdelBiasOmega, preintegrated.delPdelBiasOmega())); - EXPECT(assert_equal(expectedDelVdelBiasAcc, preintegrated.delVdelBiasAcc())); - EXPECT( - assert_equal(expectedDelVdelBiasOmega, preintegrated.delVdelBiasOmega())); + EXPECT(assert_equal(expectedDelPdelBiasAcc, pim.delPdelBiasAcc())); + EXPECT(assert_equal(expectedDelPdelBiasOmega, pim.delPdelBiasOmega())); + EXPECT(assert_equal(expectedDelVdelBiasAcc, pim.delVdelBiasAcc())); + EXPECT(assert_equal(expectedDelVdelBiasOmega, pim.delVdelBiasOmega())); EXPECT(assert_equal(expectedDelRdelBiasAcc, Matrix::Zero(3, 3))); - EXPECT( - assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega(), - 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations + EXPECT(assert_equal(expectedDelRdelBiasOmega, pim.delRdelBiasOmega(), 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations } /* ************************************************************************* */ @@ -293,23 +288,22 @@ TEST(CombinedImuFactor, PredictPositionAndVelocity) { double deltaT = 0.001; CombinedImuFactor::CombinedPreintegratedMeasurements pim(bias, I_3x3, I_3x3, - I_3x3, I_3x3, 2 * I_3x3, I_6x6, true); + I_3x3, I_3x3, 2 * I_3x3, I_6x6); for (int i = 0; i < 1000; ++i) - pim.integrateMeasurement(measuredAcc, measuredOmega, - deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor noiseModel::Gaussian::shared_ptr combinedmodel = noiseModel::Gaussian::Covariance(pim.preintMeasCov()); - CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - pim, gravity, omegaCoriolis); + CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), pim, + gravity, omegaCoriolis); // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocityBias = pim.predict(x1, v1, - bias, gravity, omegaCoriolis); + PoseVelocityBias poseVelocityBias = pim.predict(x1, v1, bias, gravity, + omegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 1, 0; @@ -322,7 +316,7 @@ TEST(CombinedImuFactor, PredictPositionAndVelocity) { TEST(CombinedImuFactor, PredictRotation) { imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) CombinedImuFactor::CombinedPreintegratedMeasurements pim(bias, I_3x3, I_3x3, - I_3x3, I_3x3, 2 * I_3x3, I_6x6, true); + I_3x3, I_3x3, 2 * I_3x3, I_6x6); Vector3 measuredAcc; measuredAcc << 0, 0, -9.81; Vector3 gravity; @@ -335,8 +329,8 @@ TEST(CombinedImuFactor, PredictRotation) { double tol = 1e-4; for (int i = 0; i < 1000; ++i) pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), - pim, gravity, omegaCoriolis); + CombinedImuFactor Combinedfactor(X(1), V(1), X(2), V(2), B(1), B(2), pim, + gravity, omegaCoriolis); // Predict Pose3 x(Rot3().ypr(0, 0, 0), Point3(0, 0, 0)), x2; @@ -366,8 +360,8 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); deltaTs.push_back(0.01); } - // Actual preintegrated values - CombinedImuFactor::CombinedPreintegratedMeasurements preintegrated = + // Actual pim values + CombinedImuFactor::CombinedPreintegratedMeasurements pim = evaluatePreintegratedMeasurements(bias_old, measuredAccs, measuredOmegas, deltaTs); @@ -376,14 +370,14 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); const double newDeltaT = 0.01; - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement + const Rot3 deltaRij_old = pim.deltaRij(); // before adding new measurement + const Vector3 deltaVij_old = pim.deltaVij(); // before adding new measurement + const Vector3 deltaPij_old = pim.deltaPij(); // before adding new measurement - Matrix oldPreintCovariance = preintegrated.preintMeasCov(); + Matrix oldPreintCovariance = pim.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, + pim.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, Factual, Gactual); bool use2ndOrderIntegration = false; @@ -438,7 +432,7 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { Matrix Fexpected(15, 15); Fexpected << df_dpos, df_dvel, df_dangle, df_dbias; - EXPECT(assert_equal(Fexpected, Factual)); + EXPECT(assert_equal(Fexpected, Factual,1e-5)); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR G @@ -496,7 +490,7 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance * Factual.transpose() + (1 / newDeltaT) * Gactual * Gactual.transpose(); - Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); + Matrix newPreintCovarianceActual = pim.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); } From 9bcdf972f87d220d9ea4752f345c98b7331931c6 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 14:17:46 +0200 Subject: [PATCH 073/142] Asserting that computeError is just localCoordinates --- gtsam/navigation/PreintegrationBase.cpp | 6 +-- gtsam/navigation/PreintegrationBase.h | 3 ++ gtsam/navigation/tests/testImuFactor.cpp | 69 +++++++++++++----------- 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index f45b0f49c..e4159e9d7 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -175,8 +175,8 @@ NavState PreintegrationBase::predict(const NavState& state_i, // TODO(frank): this is *almost* state_j.localCoordinates(predict), // except for the damn Ri.transpose. Ri is also the only way this depends on state_i. // That is not an accident! Put R in computed covariances instead ? -static Vector9 computeError(const NavState& state_i, const NavState& state_j, - const NavState& predictedState_j) { +Vector9 PreintegrationBase::computeError(const NavState& state_i, + const NavState& state_j, const NavState& predictedState_j) { const Rot3& rot_i = state_i.attitude(); const Matrix Ri = rot_i.matrix(); @@ -198,7 +198,7 @@ static Vector9 computeError(const NavState& state_i, const NavState& state_j, Vector9 r; r << fR, fp, fv; return r; - // return state_j.localCoordinates(predictedState_j); +// return state_j.localCoordinates(predictedState_j); } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 8182693ed..50754636a 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -191,6 +191,9 @@ public: OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 6> H2 = boost::none) const; + static Vector9 computeError(const NavState& state_i, const NavState& state_j, + const NavState& predictedState_j); + /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index f433c2a9e..6419cc079 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -230,7 +230,8 @@ static const NavState state1(x1, v1); // Measurements static const double w = M_PI / 100; static const Vector3 measuredOmega(w, 0, 0); -static const Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown); +static const Vector3 measuredAcc = x1.rotation().unrotate( + -kGravityAlongNavZDown); static const double deltaT = 1.0; static const Pose3 x2(Rot3::RzRyRx(M_PI / 12.0 + w, M_PI / 6.0, M_PI / 4.0), @@ -254,27 +255,32 @@ TEST(ImuFactor, PreintegrationBaseMethods) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - { // biasCorrectedDelta - Matrix96 actualH; - pim.biasCorrectedDelta(bias, actualH); - Matrix expectedH = numericalDerivative11( - boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, - boost::none), bias); - EXPECT(assert_equal(expectedH, actualH)); - } - { - Matrix9 aH1; - Matrix96 aH2; - pim.predict(state1, bias, aH1, aH2); - Matrix eH1 = numericalDerivative11( - boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none, - boost::none), state1); - EXPECT(assert_equal(eH1, aH1, 1e-8)); - Matrix eH2 = numericalDerivative11( - boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, - boost::none), bias); - EXPECT(assert_equal(eH2, aH2, 1e-8)); - } + // biasCorrectedDelta + Matrix96 actualH; + pim.biasCorrectedDelta(bias, actualH); + Matrix expectedH = numericalDerivative11( + boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, + boost::none), bias); + EXPECT(assert_equal(expectedH, actualH)); + + Matrix9 aH1; + Matrix96 aH2; + NavState predictedState = pim.predict(state1, bias, aH1, aH2); + Matrix eH1 = numericalDerivative11( + boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none, + boost::none), state1); + EXPECT(assert_equal(eH1, aH1, 1e-8)); + Matrix eH2 = numericalDerivative11( + boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, + boost::none), bias); + EXPECT(assert_equal(eH2, aH2, 1e-8)); + + EXPECT( + assert_equal(Vector(-state1.localCoordinates(predictedState)), + PreintegratedImuMeasurements::computeError(state1, state1, + predictedState), 1e-8)); + return; + } /* ************************************************************************* */ @@ -335,7 +341,7 @@ TEST(ImuFactor, ErrorAndJacobians) { Matrix cov = pim.preintMeasCov(); Matrix R = RtR(cov.inverse()); Vector whitened = R * expectedError; - EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values),1e-5)); + EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values), 1e-5)); // Make sure linearization is correct EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); @@ -357,10 +363,9 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { + Vector3(0.2, 0.0, 0.0); double deltaT = 1.0; - PreintegratedImuMeasurements pim( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)), - kMeasuredAccCovariance, kMeasuredOmegaCovariance, - kIntegrationErrorCovariance); + imuBias::ConstantBias biasHat(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.1)); + PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, + kMeasuredOmegaCovariance, kIntegrationErrorCovariance); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor @@ -858,8 +863,8 @@ TEST(ImuFactor, PredictPositionAndVelocity) { // Predict Pose3 x1; Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias, kGravityAlongNavZDown, - kZeroOmegaCoriolis); + PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias, + kGravityAlongNavZDown, kZeroOmegaCoriolis); Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 1, 0; @@ -894,7 +899,8 @@ TEST(ImuFactor, PredictRotation) { Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, + kZeroOmegaCoriolis); Pose3 expectedPose(Rot3().ypr(M_PI / 10, 0, 0), Point3(0, 0, 0)); Vector3 expectedVelocity; expectedVelocity << 0, 0, 0; @@ -927,7 +933,8 @@ TEST(ImuFactor, PredictArbitrary) { Pose3 x1, x2; Vector3 v1 = Vector3(0, 0.0, 0.0); Vector3 v2; - ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); + ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, + kZeroOmegaCoriolis); // Regression test for Imu Refactor Rot3 expectedR( // From 7a78d54fc3a68759efd0560762eba0630d8588dc Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 14:43:06 +0200 Subject: [PATCH 074/142] derivatives for Local and localCoordinates --- gtsam/navigation/NavState.cpp | 11 +++-- gtsam/navigation/tests/testNavState.cpp | 62 +++++++++++++++---------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 989a300fe..a52b74704 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -118,11 +118,14 @@ NavState NavState::ChartAtOrigin::Retract(const Vector9& xi, //------------------------------------------------------------------------------ Vector9 NavState::ChartAtOrigin::Local(const NavState& x, OptionalJacobian<9, 9> H) { - if (H) - throw runtime_error( - "NavState::ChartOrigin::Local derivative not implemented yet"); Vector9 xi; - xi << Rot3::Logmap(x.R_), x.t(), x.v(); + Matrix3 D_xi_R; + xi << Rot3::Logmap(x.R_, H ? &D_xi_R : 0), x.t(), x.v(); + if (H) { + *H << D_xi_R, Z_3x3, Z_3x3, // + Z_3x3, x.R(), Z_3x3, // + Z_3x3, Z_3x3, x.R(); + } return xi; } diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index ca6c2ffc1..c0797bd9c 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -31,6 +31,7 @@ static const NavState kIdentity; static const NavState kState1(kRotation, kPosition, kVelocity); static const Vector3 kOmegaCoriolis(0.02, 0.03, 0.04); static const Vector3 kGravity(0, 0, 9.81); +static const Vector9 kZeroXi = Vector9::Zero(); /* ************************************************************************* */ TEST( NavState, Attitude) { @@ -75,16 +76,16 @@ TEST( NavState, MatrixGroup ) { /* ************************************************************************* */ TEST( NavState, Manifold ) { // Check zero xi - EXPECT(assert_equal(kIdentity, kIdentity.retract(zero(9)))); - EXPECT(assert_equal(zero(9), kIdentity.localCoordinates(kIdentity))); - EXPECT(assert_equal(kState1, kState1.retract(zero(9)))); - EXPECT(assert_equal(zero(9), kState1.localCoordinates(kState1))); + EXPECT(assert_equal(kIdentity, kIdentity.retract(kZeroXi))); + EXPECT(assert_equal(kZeroXi, kIdentity.localCoordinates(kIdentity))); + EXPECT(assert_equal(kState1, kState1.retract(kZeroXi))); + EXPECT(assert_equal(kZeroXi, kState1.localCoordinates(kState1))); // Check definition of retract as operating on components separately - Vector xi(9); + Vector9 xi; xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; Rot3 drot = Rot3::Expmap(xi.head<3>()); - Point3 dt = Point3(xi.segment < 3 > (3)); + Point3 dt = Point3(xi.segment<3>(3)); Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3); NavState state2 = kState1 * NavState(drot, dt, dvel); EXPECT(assert_equal(state2, kState1.retract(xi))); @@ -95,38 +96,49 @@ TEST( NavState, Manifold ) { EXPECT(assert_equal(xi, state2.localCoordinates(state3))); // Check derivatives for ChartAtOrigin::Retract - Matrix9 aH, eH; + Matrix9 aH; // For zero xi - boost::function f = boost::bind( + boost::function Retract = boost::bind( NavState::ChartAtOrigin::Retract, _1, boost::none); - NavState::ChartAtOrigin::Retract(zero(9), aH); - eH = numericalDerivative11(f, zero(9)); - EXPECT(assert_equal((Matrix )eH, aH)); + NavState::ChartAtOrigin::Retract(kZeroXi, aH); + EXPECT(assert_equal(numericalDerivative11(Retract, kZeroXi), aH)); // For non-zero xi NavState::ChartAtOrigin::Retract(xi, aH); - eH = numericalDerivative11(f, xi); - EXPECT(assert_equal((Matrix )eH, aH)); + EXPECT(assert_equal(numericalDerivative11(Retract, xi), aH)); + + // Check derivatives for ChartAtOrigin::Local + // For zero xi + boost::function Local = boost::bind( + NavState::ChartAtOrigin::Local, _1, boost::none); + NavState::ChartAtOrigin::Local(kIdentity, aH); + EXPECT(assert_equal(numericalDerivative11(Local, kIdentity), aH)); + // For non-zero xi + NavState::ChartAtOrigin::Local(kState1, aH); + EXPECT(assert_equal(numericalDerivative11(Local, kState1), aH)); // Check retract derivatives Matrix9 aH1, aH2; kState1.retract(xi, aH1, aH2); - Matrix eH1 = numericalDerivative11( - boost::bind(&NavState::retract, _1, xi, boost::none, boost::none), - kState1); - EXPECT(assert_equal(eH1, aH1)); - Matrix eH2 = numericalDerivative11( - boost::bind(&NavState::retract, kState1, _1, boost::none, boost::none), - xi); - EXPECT(assert_equal(eH2, aH2)); + boost::function retract = + boost::bind(&NavState::retract, _1, _2, boost::none, boost::none); + EXPECT(assert_equal(numericalDerivative21(retract, kState1, xi), aH1)); + EXPECT(assert_equal(numericalDerivative22(retract, kState1, xi), aH2)); + + // Check localCoordinates derivatives + kState1.localCoordinates(state2, aH1, aH2); + boost::function localCoordinates = + boost::bind(&NavState::localCoordinates, _1, _2, boost::none, boost::none); + EXPECT(assert_equal(numericalDerivative21(localCoordinates, kState1, state2), aH1)); + EXPECT(assert_equal(numericalDerivative22(localCoordinates, kState1, state2), aH2)); } /* ************************************************************************* */ TEST( NavState, Lie ) { // Check zero xi - EXPECT(assert_equal(kIdentity, kIdentity.expmap(zero(9)))); - EXPECT(assert_equal(zero(9), kIdentity.logmap(kIdentity))); - EXPECT(assert_equal(kState1, kState1.expmap(zero(9)))); - EXPECT(assert_equal(zero(9), kState1.logmap(kState1))); + EXPECT(assert_equal(kIdentity, kIdentity.expmap(kZeroXi))); + EXPECT(assert_equal(kZeroXi, kIdentity.logmap(kIdentity))); + EXPECT(assert_equal(kState1, kState1.expmap(kZeroXi))); + EXPECT(assert_equal(kZeroXi, kState1.logmap(kState1))); // Expmap/Logmap roundtrip Vector xi(9); From 110a046fb6498adc493366d342efb6d9d62ebe11 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 16:05:15 +0200 Subject: [PATCH 075/142] Fixed compile issue and tightened tolerances --- gtsam_unstable/dynamics/tests/testPoseRTV.cpp | 141 +++++++++--------- 1 file changed, 70 insertions(+), 71 deletions(-) diff --git a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp index 36f097a37..0386d8bcd 100644 --- a/gtsam_unstable/dynamics/tests/testPoseRTV.cpp +++ b/gtsam_unstable/dynamics/tests/testPoseRTV.cpp @@ -15,44 +15,43 @@ using namespace gtsam; GTSAM_CONCEPT_TESTABLE_INST(PoseRTV) GTSAM_CONCEPT_LIE_INST(PoseRTV) -const double tol=1e-5; - -Rot3 rot = Rot3::RzRyRx(0.1, 0.2, 0.3); -Point3 pt(1.0, 2.0, 3.0); -Velocity3 vel(0.4, 0.5, 0.6); +static const Rot3 rot = Rot3::RzRyRx(0.1, 0.2, 0.3); +static const Point3 pt(1.0, 2.0, 3.0); +static const Velocity3 vel(0.4, 0.5, 0.6); +static const Vector3 kZero3 = Vector3::Zero(); /* ************************************************************************* */ TEST( testPoseRTV, constructors ) { PoseRTV state1(pt, rot, vel); - EXPECT(assert_equal(pt, state1.t(), tol)); - EXPECT(assert_equal(rot, state1.R(), tol)); - EXPECT(assert_equal(vel, state1.v(), tol)); - EXPECT(assert_equal(Pose3(rot, pt), state1.pose(), tol)); + EXPECT(assert_equal(pt, state1.t())); + EXPECT(assert_equal(rot, state1.R())); + EXPECT(assert_equal(vel, state1.v())); + EXPECT(assert_equal(Pose3(rot, pt), state1.pose())); PoseRTV state2; - EXPECT(assert_equal(Point3(), state2.t(), tol)); - EXPECT(assert_equal(Rot3(), state2.R(), tol)); - EXPECT(assert_equal(zero(3), state2.v(), tol)); - EXPECT(assert_equal(Pose3(), state2.pose(), tol)); + EXPECT(assert_equal(Point3(), state2.t())); + EXPECT(assert_equal(Rot3(), state2.R())); + EXPECT(assert_equal(kZero3, state2.v())); + EXPECT(assert_equal(Pose3(), state2.pose())); PoseRTV state3(Pose3(rot, pt), vel); - EXPECT(assert_equal(pt, state3.t(), tol)); - EXPECT(assert_equal(rot, state3.R(), tol)); - EXPECT(assert_equal(vel, state3.v(), tol)); - EXPECT(assert_equal(Pose3(rot, pt), state3.pose(), tol)); + EXPECT(assert_equal(pt, state3.t())); + EXPECT(assert_equal(rot, state3.R())); + EXPECT(assert_equal(vel, state3.v())); + EXPECT(assert_equal(Pose3(rot, pt), state3.pose())); PoseRTV state4(Pose3(rot, pt)); - EXPECT(assert_equal(pt, state4.t(), tol)); - EXPECT(assert_equal(rot, state4.R(), tol)); - EXPECT(assert_equal(zero(3), state4.v(), tol)); - EXPECT(assert_equal(Pose3(rot, pt), state4.pose(), tol)); + EXPECT(assert_equal(pt, state4.t())); + EXPECT(assert_equal(rot, state4.R())); + EXPECT(assert_equal(kZero3, state4.v())); + EXPECT(assert_equal(Pose3(rot, pt), state4.pose())); Vector vec_init = (Vector(9) << 0.1, 0.2, 0.3, 1.0, 2.0, 3.0, 0.4, 0.5, 0.6).finished(); PoseRTV state5(vec_init); - EXPECT(assert_equal(pt, state5.t(), tol)); - EXPECT(assert_equal(rot, state5.R(), tol)); - EXPECT(assert_equal(vel, state5.v(), tol)); - EXPECT(assert_equal(vec_init, state5.vector(), tol)); + EXPECT(assert_equal(pt, state5.t())); + EXPECT(assert_equal(rot, state5.R())); + EXPECT(assert_equal(vel, state5.v())); + EXPECT(assert_equal(vec_init, state5.vector())); } /* ************************************************************************* */ @@ -65,44 +64,44 @@ TEST( testPoseRTV, dim ) { /* ************************************************************************* */ TEST( testPoseRTV, equals ) { PoseRTV state1, state2(pt, rot, vel), state3(state2), state4(Pose3(rot, pt)); - EXPECT(assert_equal(state1, state1, tol)); - EXPECT(assert_equal(state2, state3, tol)); - EXPECT(assert_equal(state3, state2, tol)); - EXPECT(assert_inequal(state1, state2, tol)); - EXPECT(assert_inequal(state2, state1, tol)); - EXPECT(assert_inequal(state2, state4, tol)); + EXPECT(assert_equal(state1, state1)); + EXPECT(assert_equal(state2, state3)); + EXPECT(assert_equal(state3, state2)); + EXPECT(assert_inequal(state1, state2)); + EXPECT(assert_inequal(state2, state1)); + EXPECT(assert_inequal(state2, state4)); } /* ************************************************************************* */ TEST( testPoseRTV, Lie ) { // origin and zero deltas PoseRTV identity; - EXPECT(assert_equal(identity, (PoseRTV)identity.retract(zero(9)), tol)); - EXPECT(assert_equal(zero(9), identity.localCoordinates(identity), tol)); + EXPECT(assert_equal(identity, (PoseRTV)identity.retract(zero(9)))); + EXPECT(assert_equal(zero(9), identity.localCoordinates(identity))); PoseRTV state1(pt, rot, vel); - EXPECT(assert_equal(state1, (PoseRTV)state1.retract(zero(9)), tol)); - EXPECT(assert_equal(zero(9), state1.localCoordinates(state1), tol)); + EXPECT(assert_equal(state1, (PoseRTV)state1.retract(zero(9)))); + EXPECT(assert_equal(zero(9), state1.localCoordinates(state1))); Vector delta(9); delta << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4,-0.1,-0.2,-0.3; Pose3 pose2 = Pose3(rot, pt).retract(delta.head<6>()); Velocity3 vel2 = vel + Velocity3(-0.1, -0.2, -0.3); PoseRTV state2(pose2.translation(), pose2.rotation(), vel2); - EXPECT(assert_equal(state2, (PoseRTV)state1.retract(delta), tol)); - EXPECT(assert_equal(delta, state1.localCoordinates(state2), tol)); + EXPECT(assert_equal(state2, (PoseRTV)state1.retract(delta))); + EXPECT(assert_equal(delta, state1.localCoordinates(state2))); // roundtrip from state2 to state3 and back PoseRTV state3 = state2.retract(delta); - EXPECT(assert_equal(delta, state2.localCoordinates(state3), tol)); + EXPECT(assert_equal(delta, state2.localCoordinates(state3))); // roundtrip from state3 to state4 and back, with expmap. PoseRTV state4 = state3.expmap(delta); - EXPECT(assert_equal(delta, state3.logmap(state4), tol)); + EXPECT(assert_equal(delta, state3.logmap(state4))); // For the expmap/logmap (not necessarily retract/local) -delta goes other way - EXPECT(assert_equal(state3, (PoseRTV)state4.expmap(-delta), tol)); - EXPECT(assert_equal(delta, -state4.logmap(state3), tol)); + EXPECT(assert_equal(state3, (PoseRTV)state4.expmap(-delta))); + EXPECT(assert_equal(delta, -state4.logmap(state3))); } /* ************************************************************************* */ @@ -119,15 +118,15 @@ TEST( testPoseRTV, dynamics_identities ) { x3 = x2.generalDynamics(accel, gyro, dt); x4 = x3.generalDynamics(accel, gyro, dt); -// EXPECT(assert_equal(imu01, x0.imuPrediction(x1, dt).first, tol)); -// EXPECT(assert_equal(imu12, x1.imuPrediction(x2, dt).first, tol)); -// EXPECT(assert_equal(imu23, x2.imuPrediction(x3, dt).first, tol)); -// EXPECT(assert_equal(imu34, x3.imuPrediction(x4, dt).first, tol)); +// EXPECT(assert_equal(imu01, x0.imuPrediction(x1, dt).first)); +// EXPECT(assert_equal(imu12, x1.imuPrediction(x2, dt).first)); +// EXPECT(assert_equal(imu23, x2.imuPrediction(x3, dt).first)); +// EXPECT(assert_equal(imu34, x3.imuPrediction(x4, dt).first)); // -// EXPECT(assert_equal(x1.translation(), x0.imuPrediction(x1, dt).second, tol)); -// EXPECT(assert_equal(x2.translation(), x1.imuPrediction(x2, dt).second, tol)); -// EXPECT(assert_equal(x3.translation(), x2.imuPrediction(x3, dt).second, tol)); -// EXPECT(assert_equal(x4.translation(), x3.imuPrediction(x4, dt).second, tol)); +// EXPECT(assert_equal(x1.translation(), x0.imuPrediction(x1, dt).second)); +// EXPECT(assert_equal(x2.translation(), x1.imuPrediction(x2, dt).second)); +// EXPECT(assert_equal(x3.translation(), x2.imuPrediction(x3, dt).second)); +// EXPECT(assert_equal(x4.translation(), x3.imuPrediction(x4, dt).second)); } @@ -140,8 +139,8 @@ TEST( testPoseRTV, compose ) { state1.compose(state2, actH1, actH2); Matrix numericH1 = numericalDerivative21(compose_proxy, state1, state2); Matrix numericH2 = numericalDerivative22(compose_proxy, state1, state2); - EXPECT(assert_equal(numericH1, actH1, tol)); - EXPECT(assert_equal(numericH2, actH2, tol)); + EXPECT(assert_equal(numericH1, actH1)); + EXPECT(assert_equal(numericH2, actH2)); } /* ************************************************************************* */ @@ -153,8 +152,8 @@ TEST( testPoseRTV, between ) { state1.between(state2, actH1, actH2); Matrix numericH1 = numericalDerivative21(between_proxy, state1, state2); Matrix numericH2 = numericalDerivative22(between_proxy, state1, state2); - EXPECT(assert_equal(numericH1, actH1, tol)); - EXPECT(assert_equal(numericH2, actH2, tol)); + EXPECT(assert_equal(numericH1, actH1)); + EXPECT(assert_equal(numericH2, actH2)); } /* ************************************************************************* */ @@ -165,7 +164,7 @@ TEST( testPoseRTV, inverse ) { Matrix actH1; state1.inverse(actH1); Matrix numericH1 = numericalDerivative11(inverse_proxy, state1); - EXPECT(assert_equal(numericH1, actH1, tol)); + EXPECT(assert_equal(numericH1, actH1)); } /* ************************************************************************* */ @@ -173,16 +172,16 @@ double range_proxy(const PoseRTV& A, const PoseRTV& B) { return A.range(B); } TEST( testPoseRTV, range ) { Point3 tA(1.0, 2.0, 3.0), tB(3.0, 2.0, 3.0); PoseRTV rtvA(tA), rtvB(tB); - EXPECT_DOUBLES_EQUAL(0.0, rtvA.range(rtvA), tol); - EXPECT_DOUBLES_EQUAL(2.0, rtvA.range(rtvB), tol); - EXPECT_DOUBLES_EQUAL(2.0, rtvB.range(rtvA), tol); + EXPECT_DOUBLES_EQUAL(0.0, rtvA.range(rtvA), 1e-9); + EXPECT_DOUBLES_EQUAL(2.0, rtvA.range(rtvB), 1e-9); + EXPECT_DOUBLES_EQUAL(2.0, rtvB.range(rtvA), 1e-9); Matrix actH1, actH2; rtvA.range(rtvB, actH1, actH2); Matrix numericH1 = numericalDerivative21(range_proxy, rtvA, rtvB); Matrix numericH2 = numericalDerivative22(range_proxy, rtvA, rtvB); - EXPECT(assert_equal(numericH1, actH1, tol)); - EXPECT(assert_equal(numericH2, actH2, tol)); + EXPECT(assert_equal(numericH1, actH1)); + EXPECT(assert_equal(numericH2, actH2)); } /* ************************************************************************* */ @@ -199,12 +198,12 @@ TEST( testPoseRTV, transformed_from_1 ) { Matrix actDTrans, actDGlobal; PoseRTV actual = start.transformed_from(transform, actDGlobal, actDTrans); PoseRTV expected(transform.compose(start.pose()), transform.rotation().matrix() * V); - EXPECT(assert_equal(expected, actual, tol)); + EXPECT(assert_equal(expected, actual)); Matrix numDGlobal = numericalDerivative21(transformed_from_proxy, start, transform, 1e-5); // At 1e-8, fails Matrix numDTrans = numericalDerivative22(transformed_from_proxy, start, transform, 1e-8); // Sensitive to step size - EXPECT(assert_equal(numDGlobal, actDGlobal, tol)); - EXPECT(assert_equal(numDTrans, actDTrans, tol)); // FIXME: still needs analytic derivative + EXPECT(assert_equal(numDGlobal, actDGlobal)); + EXPECT(assert_equal(numDTrans, actDTrans, 1e-5)); // FIXME: still needs analytic derivative } /* ************************************************************************* */ @@ -218,26 +217,26 @@ TEST( testPoseRTV, transformed_from_2 ) { Matrix actDTrans, actDGlobal; PoseRTV actual = start.transformed_from(transform, actDGlobal, actDTrans); PoseRTV expected(transform.compose(start.pose()), transform.rotation().matrix() * V); - EXPECT(assert_equal(expected, actual, tol)); + EXPECT(assert_equal(expected, actual)); Matrix numDGlobal = numericalDerivative21(transformed_from_proxy, start, transform, 1e-5); // At 1e-8, fails Matrix numDTrans = numericalDerivative22(transformed_from_proxy, start, transform, 1e-8); // Sensitive to step size - EXPECT(assert_equal(numDGlobal, actDGlobal, tol)); - EXPECT(assert_equal(numDTrans, actDTrans, tol)); // FIXME: still needs analytic derivative + EXPECT(assert_equal(numDGlobal, actDGlobal)); + EXPECT(assert_equal(numDTrans, actDTrans, 1e-5)); // FIXME: still needs analytic derivative } /* ************************************************************************* */ TEST(testPoseRTV, RRTMbn) { - EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMbn(zero(3)), tol)); - EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMbn(Rot3()), tol)); - EXPECT(assert_equal(PoseRTV::RRTMbn(Vector3(0.3, 0.2, 0.1)), PoseRTV::RRTMbn(Rot3::ypr(0.1, 0.2, 0.3)), tol)); + EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMbn(kZero3))); + EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMbn(Rot3()))); + EXPECT(assert_equal(PoseRTV::RRTMbn(Vector3(0.3, 0.2, 0.1)), PoseRTV::RRTMbn(Rot3::ypr(0.1, 0.2, 0.3)))); } /* ************************************************************************* */ TEST(testPoseRTV, RRTMnb) { - EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMnb(zero(3)), tol)); - EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMnb(Rot3()), tol)); - EXPECT(assert_equal(PoseRTV::RRTMnb(Vector3(0.3, 0.2, 0.1)), PoseRTV::RRTMnb(Rot3::ypr(0.1, 0.2, 0.3)), tol)); + EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMnb(kZero3))); + EXPECT(assert_equal(Matrix::Identity(3,3), PoseRTV::RRTMnb(Rot3()))); + EXPECT(assert_equal(PoseRTV::RRTMnb(Vector3(0.3, 0.2, 0.1)), PoseRTV::RRTMnb(Rot3::ypr(0.1, 0.2, 0.3)))); } /* ************************************************************************* */ From 7ebcb4c18f3d33a5ed4681eebea85ba0f95843c2 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 16:45:55 +0200 Subject: [PATCH 076/142] Replaced large complicated original function with just a call to localCoordinates. --- gtsam/navigation/PreintegrationBase.cpp | 140 ++++------------------- gtsam/navigation/PreintegrationBase.h | 3 - gtsam/navigation/tests/testImuFactor.cpp | 19 ++- 3 files changed, 30 insertions(+), 132 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index e4159e9d7..5f8da0392 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -171,36 +171,6 @@ NavState PreintegrationBase::predict(const NavState& state_i, return state_j; } -//------------------------------------------------------------------------------ -// TODO(frank): this is *almost* state_j.localCoordinates(predict), -// except for the damn Ri.transpose. Ri is also the only way this depends on state_i. -// That is not an accident! Put R in computed covariances instead ? -Vector9 PreintegrationBase::computeError(const NavState& state_i, - const NavState& state_j, const NavState& predictedState_j) { - - const Rot3& rot_i = state_i.attitude(); - const Matrix Ri = rot_i.matrix(); - - // Residual rotation error - // TODO: this also seems to be flipped from localCoordinates - const Rot3 fRrot = predictedState_j.attitude().between(state_j.attitude()); - const Vector3 fR = Rot3::Logmap(fRrot); - - // Evaluate residual error, according to [3] - // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fp = Ri.transpose() - * (state_j.position() - predictedState_j.position()).vector(); - - // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fv = Ri.transpose() - * (state_j.velocity() - predictedState_j.velocity()); - - Vector9 r; - r << fR, fp, fv; - return r; -// return state_j.localCoordinates(predictedState_j); -} - //------------------------------------------------------------------------------ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, @@ -208,100 +178,36 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, OptionalJacobian<9, 3> H2, OptionalJacobian<9, 6> H3, OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5) const { - // we give some shorter name to rotations and translations - const Rot3& rot_i = pose_i.rotation(); - const Matrix Ri = rot_i.matrix(); NavState state_i(pose_i, vel_i); - - const Rot3& rot_j = pose_j.rotation(); - const Vector3 pos_j = pose_j.translation().vector(); NavState state_j(pose_j, vel_j); - // Compute bias-corrected quantities - // TODO(frank): now redundant with biasCorrected below - Matrix96 D_biasCorrected_bias; - Vector9 biasCorrected = biasCorrectedDelta(bias_i, D_biasCorrected_bias); - /// Predict state at time j - Matrix99 D_predict_state; - Matrix96 D_predict_bias; - NavState predictedState_j = predict(state_i, bias_i, D_predict_state, - D_predict_bias); + Matrix99 D_predict_state_i; + Matrix96 D_predict_bias_i; + NavState predictedState_j = predict(state_i, bias_i, + H1 || H2 ? &D_predict_state_i : 0, H5 ? &D_predict_bias_i : 0); - // Evaluate residual error, according to [3] - // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fp = Ri.transpose() - * (pos_j - predictedState_j.pose().translation().vector()); + Matrix9 D_error_state_j, D_error_predict; + Vector9 error = state_j.localCoordinates(predictedState_j, + H3 || H4 ? &D_error_state_j : 0, H1 || H2 || H5 ? &D_error_predict : 0); - // Ri.transpose() is important here to preserve a model with *additive* Gaussian noise of correct covariance - const Vector3 fv = Ri.transpose() * (vel_j - predictedState_j.velocity()); + // Separate out derivatives in terms of 5 arguments + // Note that doing so requires special treatment of velocities, as when treated as + // separate variables the retract applied will not be the semi-direct product in NavState + // Instead, the velocities in nav are updated using a straight addition + // This is difference is accounted for by the R().transpose calls below + if (H1) + *H1 << D_error_predict * D_predict_state_i.leftCols<6>(); + if (H2) + *H2 << D_error_predict * D_predict_state_i.rightCols<3>() * state_i.R().transpose(); + if (H3) + *H3 << D_error_state_j.leftCols<6>(); + if (H4) + *H4 << D_error_state_j.rightCols<3>() * state_j.R().transpose(); + if (H5) + *H5 << D_error_predict * D_predict_bias_i; - // fR will be computed later. - // Note: it is the same as: fR = predictedState_j.pose.rotation().between(Rot_j) - - // Coriolis term, NOTE inconsistent with AHRS, where coriolisHat is *after* integration - // TODO(frank): move derivatives to predict and do coriolis branching there - const Vector3 coriolis = PreintegratedRotation::integrateCoriolis(rot_i); - const Vector3 correctedOmega = NavState::dR(biasCorrected) - coriolis; - - // Residual rotation error - Matrix3 D_cDeltaRij_cOmega; - const Rot3 correctedDeltaRij = Rot3::Expmap(correctedOmega, - H1 || H5 ? &D_cDeltaRij_cOmega : 0); - const Rot3 RiBetweenRj = rot_i.between(rot_j); - const Rot3 fRrot = correctedDeltaRij.between(RiBetweenRj); - Matrix3 D_fR_fRrot; - Rot3::Logmap(fRrot, H1 || H3 || H5 ? &D_fR_fRrot : 0); - - const double dt = deltaTij_, dt2 = dt * dt; - Matrix3 RitOmegaCoriolisHat = Z_3x3; - if ((H1 || H2) && p().omegaCoriolis) - RitOmegaCoriolisHat = Ri.transpose() * skewSymmetric(*p().omegaCoriolis); - - if (H1) { - const Matrix3 D_coriolis = -D_cDeltaRij_cOmega * skewSymmetric(coriolis); - Matrix3 dfPdPi = -I_3x3, dfVdPi = Z_3x3; - if (p().use2ndOrderCoriolis) { - // this is the same as: Ri.transpose() * p().omegaCoriolisHat * p().omegaCoriolisHat * Ri - const Matrix3 temp = RitOmegaCoriolisHat - * (-RitOmegaCoriolisHat.transpose()); - dfPdPi += 0.5 * temp * dt2; - dfVdPi += temp * dt; - } - (*H1) - << D_fR_fRrot - * (-rot_j.between(rot_i).matrix() - - fRrot.inverse().matrix() * D_coriolis), // dfR/dRi - Z_3x3, // dfR/dPi - skewSymmetric(fp + NavState::dP(biasCorrected)), // dfP/dRi - dfPdPi, // dfP/dPi - skewSymmetric(fv + NavState::dV(biasCorrected)), // dfV/dRi - dfVdPi; // dfV/dPi - } - if (H2) { - (*H2) << Z_3x3, // dfR/dVi - -Ri.transpose() * dt + RitOmegaCoriolisHat * dt2, // dfP/dVi - -Ri.transpose() + 2 * RitOmegaCoriolisHat * dt; // dfV/dVi - } - if (H3) { - (*H3) << D_fR_fRrot, Z_3x3, // dfR/dPosej - Z_3x3, Ri.transpose() * rot_j.matrix(), // dfP/dPosej - Matrix::Zero(3, 6); // dfV/dPosej - } - if (H4) { - (*H4) << Z_3x3, // dfR/dVj - Z_3x3, // dfP/dVj - Ri.transpose(); // dfV/dVj - } - if (H5) { - const Matrix36 JbiasOmega = D_cDeltaRij_cOmega - * D_biasCorrected_bias.middleRows<3>(0); - (*H5) << -D_fR_fRrot * fRrot.inverse().matrix() * JbiasOmega, // dfR/dBias - -D_biasCorrected_bias.middleRows<3>(3), // dfP/dBias - -D_biasCorrected_bias.middleRows<3>(6); // dfV/dBias - } - // TODO(frank): Do everything via derivatives of function below - return computeError(state_i, state_j, predictedState_j); + return error; } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 50754636a..8182693ed 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -191,9 +191,6 @@ public: OptionalJacobian<9, 9> H1 = boost::none, OptionalJacobian<9, 6> H2 = boost::none) const; - static Vector9 computeError(const NavState& state_i, const NavState& state_j, - const NavState& predictedState_j); - /// Compute errors w.r.t. preintegrated measurements and jacobians wrt pose_i, vel_i, bias_i, pose_j, bias_j Vector9 computeErrorAndJacobians(const Pose3& pose_i, const Vector3& vel_i, const Pose3& pose_j, const Vector3& vel_j, diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 6419cc079..c2445a348 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -274,11 +274,6 @@ TEST(ImuFactor, PreintegrationBaseMethods) { boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, boost::none), bias); EXPECT(assert_equal(eH2, aH2, 1e-8)); - - EXPECT( - assert_equal(Vector(-state1.localCoordinates(predictedState)), - PreintegratedImuMeasurements::computeError(state1, state1, - predictedState), 1e-8)); return; } @@ -312,7 +307,7 @@ TEST(ImuFactor, ErrorAndJacobians) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); // Actual Jacobians Matrix H1a, H2a, H3a, H4a, H5a; @@ -331,10 +326,10 @@ TEST(ImuFactor, ErrorAndJacobians) { // Evaluate error with wrong values Vector3 v2_wrong = v2 + Vector3(0.1, 0.1, 0.1); values.update(V(2), v2_wrong); - expectedError << 0, 0, 0, 0, 0, 0, 0.0724744871, 0.040715657, 0.151952901; + expectedError << 0, 0, 0, 0, 0, 0, -0.0724744871, -0.040715657, -0.151952901; EXPECT( assert_equal(expectedError, - factor.evaluateError(x1, v1, x2, v2_wrong, bias))); + factor.evaluateError(x1, v1, x2, v2_wrong, bias),1e-2)); EXPECT(assert_equal(expectedError, factor.unwhitenedError(values))); // Make sure the whitening is done correctly @@ -344,7 +339,7 @@ TEST(ImuFactor, ErrorAndJacobians) { EXPECT(assert_equal(0.5 * whitened.squaredNorm(), factor.error(values), 1e-5)); // Make sure linearization is correct - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } /* ************************************************************************* */ @@ -381,7 +376,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-1); } /* ************************************************************************* */ @@ -421,7 +416,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-2); } /* ************************************************************************* */ @@ -834,7 +829,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-1); } /* ************************************************************************* */ From 670781231c961ef451ceeb27fa7c6f8df459a968 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 24 Jul 2015 17:40:35 +0200 Subject: [PATCH 077/142] Fixed derivative of biasCorrectedDelta --- gtsam/navigation/PreintegrationBase.cpp | 7 +++-- gtsam/navigation/tests/testImuFactor.cpp | 37 +++++++++++++++++------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 5f8da0392..d8293cf78 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -126,10 +126,13 @@ Vector9 PreintegrationBase::biasCorrectedDelta( const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H) const { // Correct deltaRij, derivative is delRdelBiasOmega_ const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - Rot3 deltaRij = biascorrectedDeltaRij(biasIncr.gyroscope()); + Matrix3 D_deltaRij_bias; + Rot3 deltaRij = biascorrectedDeltaRij(biasIncr.gyroscope(), H ? &D_deltaRij_bias : 0); Vector9 xi; Matrix3 D_dR_deltaRij; + // TODO(frank): could line below be simplified? It is equivalent to + // LogMap(deltaRij_.compose(Expmap(delRdelBiasOmega_ * biasIncr.gyroscope()))) NavState::dR(xi) = Rot3::Logmap(deltaRij, H ? &D_dR_deltaRij : 0); NavState::dP(xi) = deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() + delPdelBiasOmega_ * biasIncr.gyroscope(); @@ -138,7 +141,7 @@ Vector9 PreintegrationBase::biasCorrectedDelta( if (H) { Matrix36 D_dR_bias, D_dP_bias, D_dV_bias; - D_dR_bias << Z_3x3, D_dR_deltaRij * delRdelBiasOmega_; + D_dR_bias << Z_3x3, D_dR_deltaRij * D_deltaRij_bias; D_dP_bias << delPdelBiasAcc_, delPdelBiasOmega_; D_dV_bias << delVdelBiasAcc_, delVdelBiasOmega_; (*H) << D_dR_bias, D_dP_bias, D_dV_bias; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index c2445a348..eb6b85bae 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -269,11 +269,11 @@ TEST(ImuFactor, PreintegrationBaseMethods) { Matrix eH1 = numericalDerivative11( boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none, boost::none), state1); - EXPECT(assert_equal(eH1, aH1, 1e-8)); + EXPECT(assert_equal(eH1, aH1)); Matrix eH2 = numericalDerivative11( boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, boost::none), bias); - EXPECT(assert_equal(eH2, aH2, 1e-8)); + EXPECT(assert_equal(eH2, aH2)); return; } @@ -329,8 +329,8 @@ TEST(ImuFactor, ErrorAndJacobians) { expectedError << 0, 0, 0, 0, 0, 0, -0.0724744871, -0.040715657, -0.151952901; EXPECT( assert_equal(expectedError, - factor.evaluateError(x1, v1, x2, v2_wrong, bias),1e-2)); - EXPECT(assert_equal(expectedError, factor.unwhitenedError(values))); + factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-2)); + EXPECT(assert_equal(expectedError, factor.unwhitenedError(values), 1e-2)); // Make sure the whitening is done correctly Matrix cov = pim.preintMeasCov(); @@ -363,6 +363,22 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { kMeasuredOmegaCovariance, kIntegrationErrorCovariance); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + // Make sure of biascorrectedDeltaRij with this example... + Matrix3 aH; + pim.biascorrectedDeltaRij(bias.gyroscope(), aH); + Matrix3 eH = numericalDerivative11( + boost::bind(&PreintegrationBase::biascorrectedDeltaRij, pim, _1, + boost::none), bias.gyroscope()); + EXPECT(assert_equal(eH, aH)); + + // ... and of biasCorrectedDelta + Matrix96 actualH; + pim.biasCorrectedDelta(bias, actualH); + Matrix expectedH = numericalDerivative11( + boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, + boost::none), bias); + EXPECT(assert_equal(expectedH, actualH)); + // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kNonZeroOmegaCoriolis); @@ -376,7 +392,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-1); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } /* ************************************************************************* */ @@ -416,7 +432,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-2); + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } /* ************************************************************************* */ @@ -439,8 +455,7 @@ TEST(ImuFactor, PartialDerivative_wrt_Bias) { Matrix3 actualdelRdelBiasOmega = -Jr * deltaT; // the delta bias appears with the minus sign // Compare Jacobians - // 1e-3 needs to be added only when using quaternions for rotations - EXPECT(assert_equal(expectedDelRdelBiasOmega, actualdelRdelBiasOmega, 1e-3)); + EXPECT(assert_equal(expectedDelRdelBiasOmega, actualdelRdelBiasOmega, 1e-9)); } /* ************************************************************************* */ @@ -552,8 +567,7 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { assert_equal(expectedDelVdelBiasOmega, preintegrated.delVdelBiasOmega())); EXPECT(assert_equal(expectedDelRdelBiasAcc, Matrix::Zero(3, 3))); EXPECT( - assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega(), - 1e-3)); // 1e-3 needs to be added only when using quaternions for rotations + assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega())); } /* ************************************************************************* */ @@ -829,7 +843,8 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { // Make sure linearization is correct double diffDelta = 1e-5; - EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-1); + // This fails, except if tol = 1e-1: probably wrong! + EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } /* ************************************************************************* */ From 0310eb4e7b63cbb131f6d5fc624d2e706218fd79 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 25 Jul 2015 10:02:38 +0200 Subject: [PATCH 078/142] Fixed compilation errors --- gtsam/navigation/PreintegrationBase.cpp | 13 ++++++++----- gtsam/navigation/PreintegrationBase.h | 20 +++----------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 496569599..108063c22 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -126,9 +126,10 @@ void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAc update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT); } -void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( - const Vector3& measuredAcc, const Vector3& measuredOmega, Vector3& correctedAcc, - Vector3& correctedOmega, boost::optional body_P_sensor) { +std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( + const Vector3& measuredAcc, const Vector3& measuredOmega, + boost::optional body_P_sensor) const { + Vector3 correctedAcc, correctedOmega; correctedAcc = biasHat_.correctAccelerometer(measuredAcc); correctedOmega = biasHat_.correctGyroscope(measuredOmega); @@ -136,12 +137,14 @@ void PreintegrationBase::correctMeasurementsByBiasAndSensorPose( // (originally in the IMU frame) into the body frame if (body_P_sensor) { Matrix3 body_R_sensor = body_P_sensor->rotation().matrix(); - correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame + correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame Matrix3 body_omega_body__cross = skewSymmetric(correctedOmega); correctedAcc = body_R_sensor * correctedAcc - - body_omega_body__cross * body_omega_body__cross * body_P_sensor->translation().vector(); + - body_omega_body__cross * body_omega_body__cross + * body_P_sensor->translation().vector(); // linear acceleration vector in the body frame } + return std::make_pair(correctedAcc, correctedOmega); } /// Predict state at time j diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 1b2d6f783..3528231bf 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -133,24 +133,10 @@ class PreintegrationBase : public PreintegratedRotation { const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, double deltaT); - boost::tuple + std::pair correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, - const Vector3& measuredOmega, boost::optional body_P_sensor) { - Vector3 correctedAcc, correctedOmega; - correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - correctedOmega = biasHat_.correctGyroscope(measuredOmega); - - // Then compensate for sensor-body displacement: we express the quantities - // (originally in the IMU frame) into the body frame - if(body_P_sensor){ - Matrix3 body_R_sensor = body_P_sensor->rotation().matrix(); - correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame - Matrix3 body_omega_body__cross = skewSymmetric(correctedOmega); - correctedAcc = body_R_sensor * correctedAcc - body_omega_body__cross * body_omega_body__cross * body_P_sensor->translation().vector(); - // linear acceleration vector in the body frame - } - return boost::make_tuple(correctedAcc, correctedOmega); - } + const Vector3& measuredOmega, + boost::optional body_P_sensor) const; /// Predict state at time j PoseVelocityBias predict( From 18ff25b67f184786660774d8422e79c6e26f1e9c Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 25 Jul 2015 17:34:16 +0200 Subject: [PATCH 079/142] Cleaned up test while reading it --- gtsam/navigation/tests/testImuFactor.cpp | 145 +++++++++++------------ 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 964693547..db54fc1fd 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -1051,36 +1051,38 @@ TEST(ImuFactor, bodyPSensorNoBias) { imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0.1, 0)); // Biases (acc, rot) // Measurements - Vector3 n_gravity; n_gravity << 0, 0, -9.81; // z-up nav frame - Vector3 omegaCoriolis; omegaCoriolis << 0, 0, 0; + Vector3 n_gravity(0, 0, -9.81); // z-up nav frame + Vector3 omegaCoriolis(0, 0, 0); // Sensor frame is z-down // Gyroscope measurement is the angular velocity of sensor w.r.t nav frame in sensor frame - Vector3 s_omegaMeas_ns; s_omegaMeas_ns << 0, 0.1, M_PI/10; - // Acc measurement is acceleration of sensor in the sensor frame, when stationary, table exerts an equal and opposite force - // w.r.t gravity - Vector3 s_accMeas; s_accMeas << 0,0,-9.81; + Vector3 s_omegaMeas_ns(0, 0.1, M_PI / 10); + // Acc measurement is acceleration of sensor in the sensor frame, when stationary, + // table exerts an equal and opposite force w.r.t gravity + Vector3 s_accMeas(0, 0, -9.81); double dt = 0.001; - Pose3 b_P_s(Rot3::ypr(0,0,M_PI), Point3(0,0,0)); // Rotate sensor (z-down) to body (same as navigation) i.e. z-up - Matrix I6x6(6,6); - I6x6 = Matrix::Identity(6,6); + // Rotate sensor (z-down) to body (same as navigation) i.e. z-up + Pose3 body_P_sensor(Rot3::ypr(0, 0, M_PI), Point3(0, 0, 0)); - ImuFactor::PreintegratedMeasurements pre_int_data(bias, Matrix3::Zero(), - Matrix3::Zero(), Matrix3::Zero(), true); + ImuFactor::PreintegratedMeasurements pim(bias, Z_3x3, Z_3x3, Z_3x3, true); - for (int i = 0; i<1000; ++i) pre_int_data.integrateMeasurement(s_accMeas, s_omegaMeas_ns, dt, b_P_s); + for (int i = 0; i < 1000; ++i) + pim.integrateMeasurement(s_accMeas, s_omegaMeas_ns, dt, body_P_sensor); - // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, n_gravity, omegaCoriolis); + // Create factor + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, n_gravity, omegaCoriolis); - // Predict - Pose3 x1; - Vector3 v1(0, 0.0, 0.0); - PoseVelocityBias poseVelocity = pre_int_data.predict(x1, v1, bias, n_gravity, omegaCoriolis); - Pose3 expectedPose(Rot3().ypr(-M_PI/10, 0, 0), Point3(0, 0, 0)); - Vector3 expectedVelocity; expectedVelocity<<0,0,0; - EXPECT(assert_equal(expectedPose, poseVelocity.pose)); - EXPECT(assert_equal(Vector(expectedVelocity), Vector(poseVelocity.velocity))); + // Predict + Pose3 x1; + Vector3 v1(0, 0, 0); + PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias, n_gravity, + omegaCoriolis); + + Pose3 expectedPose(Rot3().ypr(-M_PI / 10, 0, 0), Point3(0, 0, 0)); + EXPECT(assert_equal(expectedPose, poseVelocity.pose)); + + Vector3 expectedVelocity(0, 0, 0); + EXPECT(assert_equal(Vector(expectedVelocity), Vector(poseVelocity.velocity))); } /* ************************************************************************* */ @@ -1091,22 +1093,27 @@ TEST(ImuFactor, bodyPSensorNoBias) { #include TEST(ImuFactor, bodyPSensorWithBias) { + using noiseModel::Diagonal; + typedef imuBias::ConstantBias Bias; + int numFactors = 80; - Vector6 noiseBetweenBiasSigma; noiseBetweenBiasSigma << Vector3(2.0e-5, 2.0e-5, 2.0e-5), Vector3(3.0e-6, 3.0e-6, 3.0e-6); - SharedDiagonal biasNoiseModel = noiseModel::Diagonal::Sigmas(noiseBetweenBiasSigma); + Vector6 noiseBetweenBiasSigma; + noiseBetweenBiasSigma << Vector3(2.0e-5, 2.0e-5, 2.0e-5), Vector3(3.0e-6, + 3.0e-6, 3.0e-6); + SharedDiagonal biasNoiseModel = Diagonal::Sigmas(noiseBetweenBiasSigma); // Measurements - Vector3 n_gravity; n_gravity << 0, 0, -9.81; - Vector3 omegaCoriolis; omegaCoriolis << 0, 0, 0; + Vector3 n_gravity(0, 0, -9.81); + Vector3 omegaCoriolis(0, 0, 0); // Sensor frame is z-down // Gyroscope measurement is the angular velocity of sensor w.r.t nav frame in sensor frame - Vector3 measuredOmega; measuredOmega << 0, 0.01, 0.0; - // Acc measurement is acceleration of sensor in the sensor frame, when stationary, table exerts an equal and opposite force - // w.r.t gravity - Vector3 measuredAcc; measuredAcc << 0,0,-9.81; + Vector3 measuredOmega(0, 0.01, 0); + // Acc measurement is acceleration of sensor in the sensor frame, when stationary, + // table exerts an equal and opposite force w.r.t gravity + Vector3 measuredAcc(0, 0, -9.81); - Pose3 bPs(Rot3::ypr(0,0,M_PI), Point3()); + Pose3 body_P_sensor(Rot3::ypr(0, 0, M_PI), Point3()); Matrix3 accCov = 1e-7 * I_3x3; Matrix3 gyroCov = 1e-8 * I_3x3; @@ -1114,18 +1121,19 @@ TEST(ImuFactor, bodyPSensorWithBias) { double deltaT = 0.005; // Specify noise values on priors - Vector6 priorNoisePoseSigmas((Vector(6) << 0.001, 0.001, 0.001, 0.01, 0.01, 0.01).finished()); - Vector3 priorNoiseVelSigmas((Vector(3) << 0.1, 0.1, 0.1).finished()); - Vector6 priorNoiseBiasSigmas((Vector(6) << 0.1, 0.1, 0.1, 0.5e-1, 0.5e-1, 0.5e-1).finished()); - SharedDiagonal priorNoisePose = noiseModel::Diagonal::Sigmas(priorNoisePoseSigmas); - SharedDiagonal priorNoiseVel = noiseModel::Diagonal::Sigmas(priorNoiseVelSigmas); - SharedDiagonal priorNoiseBias = noiseModel::Diagonal::Sigmas(priorNoiseBiasSigmas); - Vector3 zeroVel(0, 0.0, 0.0); + Vector6 priorNoisePoseSigmas( + (Vector(6) << 0.001, 0.001, 0.001, 0.01, 0.01, 0.01).finished()); + Vector3 priorNoiseVelSigmas((Vector(3) << 0.1, 0.1, 0.1).finished()); + Vector6 priorNoiseBiasSigmas( + (Vector(6) << 0.1, 0.1, 0.1, 0.5e-1, 0.5e-1, 0.5e-1).finished()); + SharedDiagonal priorNoisePose = Diagonal::Sigmas(priorNoisePoseSigmas); + SharedDiagonal priorNoiseVel = Diagonal::Sigmas(priorNoiseVelSigmas); + SharedDiagonal priorNoiseBias = Diagonal::Sigmas(priorNoiseBiasSigmas); + Vector3 zeroVel(0, 0, 0); - - - Values values; + // Create a factor graph with priors on initial pose, vlocity and bias NonlinearFactorGraph graph; + Values values; PriorFactor priorPose(X(0), Pose3(), priorNoisePose); graph.add(priorPose); @@ -1135,50 +1143,41 @@ TEST(ImuFactor, bodyPSensorWithBias) { graph.add(priorVel); values.insert(V(0), zeroVel); - imuBias::ConstantBias priorBias(Vector3(0, 0, 0), Vector3(0.0, 0.01, 0)); // Biases (acc, rot) - PriorFactor priorBiasFactor(B(0), priorBias, priorNoiseBias); + // The key to this test is that we specify the bias, in the sensor frame, as known a priori + // We also create factors below that encode our assumption that this bias is constant over time + // In theory, after optimization, we should recover that same bias estimate + Bias priorBias(Vector3(0, 0, 0), Vector3(0, 0.01, 0)); // Biases (acc, rot) + PriorFactor priorBiasFactor(B(0), priorBias, priorNoiseBias); graph.add(priorBiasFactor); values.insert(B(0), priorBias); + // Now add IMU factors and bias noise models + Bias zeroBias(Vector3(0, 0, 0), Vector3(0, 0, 0)); for (int i = 1; i < numFactors; i++) { - ImuFactor::PreintegratedMeasurements pre_int_data = ImuFactor::PreintegratedMeasurements(imuBias::ConstantBias(Vector3(0, 0.0, 0.0), - Vector3(0.0, 0.0, 0.0)), accCov, gyroCov, integrationCov, true); - for (int j = 0; j<200; ++j) pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT, bPs); + ImuFactor::PreintegratedMeasurements pim = + ImuFactor::PreintegratedMeasurements(zeroBias, accCov, gyroCov, + integrationCov, true); + for (int j = 0; j < 200; ++j) + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT, + body_P_sensor); - // Create factor - ImuFactor factor(X(i-1), V(i-1), X(i), V(i), B(i-1), pre_int_data, n_gravity, omegaCoriolis); - graph.add(factor); - imuBias::ConstantBias betweenBias(Vector3(0, 0, 0), Vector3(0.0, 0, 0)); - graph.add(BetweenFactor(B(i-1), B(i), betweenBias, biasNoiseModel)); + // Create factors + graph.add( + ImuFactor(X(i - 1), V(i - 1), X(i), V(i), B(i - 1), pim, n_gravity, + omegaCoriolis)); + graph.add(BetweenFactor(B(i - 1), B(i), zeroBias, biasNoiseModel)); values.insert(X(i), Pose3()); values.insert(V(i), zeroVel); values.insert(B(i), priorBias); } + + // Finally, optimize, and get bias at last time step Values results = LevenbergMarquardtOptimizer(graph, values).optimize(); - cout.precision(2); - Marginals marginals(graph, results); - imuBias::ConstantBias biasActual = results.at(B(numFactors-1)); + Bias biasActual = results.at(B(numFactors - 1)); - results.print("Results: \n"); - - for (int i = 0; i < numFactors; i++) { - imuBias::ConstantBias currentBias = results.at(B(i)); - cout << "currentBiasEstimate: " << currentBias.vector().transpose() << endl; - } -// for (int i = 0; i < numFactors; i++) { -// Matrix biasCov = marginals.marginalCovariance(B(i)); -// cout << "b" << i << " cov: " << biasCov.diagonal().transpose() << endl; -// } -// - for (int i = 0; i < numFactors; i++) { - Pose3 currentPose = results.at(X(i)); - cout << "currentYPREstimate (in Deg): " << currentPose.rotation().ypr().transpose()*180/M_PI << endl; - } -// for (int i = 0; i < numFactors; i++) -// cout << "x" << i << " covariance: " << marginals.marginalCovariance(X(i)).diagonal().transpose() << endl; - - imuBias::ConstantBias biasExpected(Vector3(0,0,0), Vector3(0, 0.01, 0.0)); + // And compare it with expected value (our prior) + Bias biasExpected(Vector3(0, 0, 0), Vector3(0, 0.01, 0)); EXPECT(assert_equal(biasExpected, biasActual, 1e-3)); } From 654cb4d31a945d147e1123f0a9a7b072f0efc948 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 26 Jul 2015 07:55:43 +0200 Subject: [PATCH 080/142] Re-factored conversion from sensor to body --- gtsam/navigation/PreintegrationBase.cpp | 31 ++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 108063c22..3c29f48c3 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -129,22 +129,27 @@ void PreintegrationBase::updatePreintegratedJacobians(const Vector3& correctedAc std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( const Vector3& measuredAcc, const Vector3& measuredOmega, boost::optional body_P_sensor) const { - Vector3 correctedAcc, correctedOmega; - correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - correctedOmega = biasHat_.correctGyroscope(measuredOmega); + // Correct for bias in the sensor frame + Vector3 s_correctedAcc, s_correctedOmega; + s_correctedAcc = biasHat_.correctAccelerometer(measuredAcc); + s_correctedOmega = biasHat_.correctGyroscope(measuredOmega); - // Then compensate for sensor-body displacement: we express the quantities + // Compensate for sensor-body displacement if needed: we express the quantities // (originally in the IMU frame) into the body frame + // Equations below assume the "body" frame is the CG if (body_P_sensor) { - Matrix3 body_R_sensor = body_P_sensor->rotation().matrix(); - correctedOmega = body_R_sensor * correctedOmega; // rotation rate vector in the body frame - Matrix3 body_omega_body__cross = skewSymmetric(correctedOmega); - correctedAcc = body_R_sensor * correctedAcc - - body_omega_body__cross * body_omega_body__cross - * body_P_sensor->translation().vector(); - // linear acceleration vector in the body frame - } - return std::make_pair(correctedAcc, correctedOmega); + Matrix3 bRs = body_P_sensor->rotation().matrix(); + Vector3 b_correctedOmega = bRs * s_correctedOmega; // rotation rate vector in the body frame + Matrix3 body_omega_body__cross = skewSymmetric(b_correctedOmega); + Vector3 b_arm = body_P_sensor->translation().vector(); + Vector3 b_velocity_bs = b_correctedOmega.cross(b_arm); // magnitude: omega * arm + // Subtract out the the centripetal acceleration from the measured one + // to get linear acceleration vector in the body frame: + Vector3 b_correctedAcc = bRs * s_correctedAcc + - b_correctedOmega.cross(b_velocity_bs); + return std::make_pair(b_correctedAcc, b_correctedOmega); + } else + return std::make_pair(correctedAcc, s_correctedOmega); } /// Predict state at time j From a02a167da4b1e01dff88200070225e06093cba34 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 26 Jul 2015 20:51:51 +0200 Subject: [PATCH 081/142] Made new bias tests by Krunal compile. reinstated backwards compatible method. --- gtsam/navigation/AHRSFactor.h | 5 +++-- gtsam/navigation/CombinedImuFactor.cpp | 3 ++- gtsam/navigation/CombinedImuFactor.h | 4 ++-- gtsam/navigation/ImuFactor.cpp | 11 ++++++++++- gtsam/navigation/ImuFactor.h | 11 ++++++++--- gtsam/navigation/PreintegratedRotation.h | 4 ++-- gtsam/navigation/PreintegrationBase.cpp | 12 +++++------- gtsam/navigation/PreintegrationBase.h | 4 ++-- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/gtsam/navigation/AHRSFactor.h b/gtsam/navigation/AHRSFactor.h index f9f846790..6c79ea137 100644 --- a/gtsam/navigation/AHRSFactor.h +++ b/gtsam/navigation/AHRSFactor.h @@ -49,8 +49,9 @@ class GTSAM_EXPORT PreintegratedAhrsMeasurements : public PreintegratedRotation * Default constructor, initialize with no measurements * @param bias Current estimate of acceleration and rotation rate biases */ - PreintegratedAhrsMeasurements(const boost::shared_ptr& p, const Vector3& biasHat) - : PreintegratedRotation(p), biasHat_(biasHat) { + PreintegratedAhrsMeasurements(const boost::shared_ptr& p, + const Vector3& biasHat) : + PreintegratedRotation(p), biasHat_(biasHat) { resetIntegration(); } diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index c54137c90..c6bc8282a 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -59,7 +59,8 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). Vector3 correctedAcc, correctedOmega; - boost::tie(correctedAcc, correctedOmega) = correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, body_P_sensor); + boost::tie(correctedAcc, correctedOmega) = + correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega); const Vector3 integratedOmega = correctedOmega * deltaT; // rotation vector describing rotation increment computed from the current rotation rate measurement Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 192cc3d99..1289f22c6 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -115,13 +115,13 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { * Default constructor, initializes the class with no measurements * @param bias Current estimate of acceleration and rotation rate biases */ - PreintegratedCombinedMeasurements(const boost::shared_ptr& p, + PreintegratedCombinedMeasurements(const boost::shared_ptr& p, const imuBias::ConstantBias& biasHat) : PreintegrationBase(p, biasHat) { preintMeasCov_.setZero(); } - const Params& p() const { return *boost::static_pointer_cast(p_);} + Params& p() const { return *boost::static_pointer_cast(p_);} /// print void print(const std::string& s = "Preintegrated Measurements:") const; diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index eca91f06e..187261685 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -54,7 +54,8 @@ void PreintegratedImuMeasurements::integrateMeasurement( OptionalJacobian<9, 9> F_test, OptionalJacobian<9, 9> G_test) { Vector3 correctedAcc, correctedOmega; - boost::tie(correctedAcc, correctedOmega) = correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, body_P_sensor); + boost::tie(correctedAcc, correctedOmega) = + correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega); // rotation increment computed from the current rotation rate measurement const Vector3 integratedOmega = correctedOmega * deltaT; @@ -123,6 +124,14 @@ PreintegratedImuMeasurements::PreintegratedImuMeasurements( resetIntegration(); } +//------------------------------------------------------------------------------ +void PreintegratedImuMeasurements::integrateMeasurement( + const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, + const Pose3& body_P_sensor) { + p_->body_P_sensor = body_P_sensor; + integrateMeasurement(measuredAcc, measuredOmega, deltaT); +} + //------------------------------------------------------------------------------ // ImuFactor methods //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index f66d28828..980f7d3f3 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -76,9 +76,9 @@ public: * @param bias Current estimate of acceleration and rotation rate biases * @param p Parameters, typically fixed in a single application */ - PreintegratedImuMeasurements(const boost::shared_ptr& p, - const imuBias::ConstantBias& biasHat) : - PreintegrationBase(p,biasHat) { + PreintegratedImuMeasurements(const boost::shared_ptr& p, + const imuBias::ConstantBias& biasHat) : + PreintegrationBase(p, biasHat) { preintMeasCov_.setZero(); } @@ -115,6 +115,11 @@ public: const Matrix3& integrationErrorCovariance, bool use2ndOrderIntegration = true); + /// @deprecated version of integrateMeasurement with body_P_sensor + /// Use parameters instead + void integrateMeasurement(const Vector3& measuredAcc, + const Vector3& measuredOmega, double deltaT, const Pose3& body_P_sensor); + private: /// Serialization function diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index a93c54127..df521341f 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -60,14 +60,14 @@ class PreintegratedRotation { Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias /// Parameters - boost::shared_ptr p_; + boost::shared_ptr p_; /// Default constructor for serialization PreintegratedRotation() {} public: /// Default constructor, resets integration to zero - explicit PreintegratedRotation(const boost::shared_ptr& p) : p_(p) { + explicit PreintegratedRotation(const boost::shared_ptr& p) : p_(p) { resetIntegration(); } diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 57c9c8e7c..da6b03019 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -103,8 +103,7 @@ void PreintegrationBase::updatePreintegratedJacobians( } std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( - const Vector3& measuredAcc, const Vector3& measuredOmega, - boost::optional body_P_sensor) const { + const Vector3& measuredAcc, const Vector3& measuredOmega) const { // Correct for bias in the sensor frame Vector3 s_correctedAcc, s_correctedOmega; s_correctedAcc = biasHat_.correctAccelerometer(measuredAcc); @@ -113,11 +112,10 @@ std::pair PreintegrationBase::correctMeasurementsByBiasAndSens // Compensate for sensor-body displacement if needed: we express the quantities // (originally in the IMU frame) into the body frame // Equations below assume the "body" frame is the CG - if (body_P_sensor) { - Matrix3 bRs = body_P_sensor->rotation().matrix(); + if (p().body_P_sensor) { + Matrix3 bRs = p().body_P_sensor->rotation().matrix(); + Vector3 b_arm = p().body_P_sensor->translation().vector(); Vector3 b_correctedOmega = bRs * s_correctedOmega; // rotation rate vector in the body frame - Matrix3 body_omega_body__cross = skewSymmetric(b_correctedOmega); - Vector3 b_arm = body_P_sensor->translation().vector(); Vector3 b_velocity_bs = b_correctedOmega.cross(b_arm); // magnitude: omega * arm // Subtract out the the centripetal acceleration from the measured one // to get linear acceleration vector in the body frame: @@ -125,7 +123,7 @@ std::pair PreintegrationBase::correctMeasurementsByBiasAndSens - b_correctedOmega.cross(b_velocity_bs); return std::make_pair(b_correctedAcc, b_correctedOmega); } else - return std::make_pair(correctedAcc, s_correctedOmega); + return std::make_pair(s_correctedAcc, s_correctedOmega); } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 4f53041f0..e41114b07 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -122,7 +122,7 @@ public: * @param bias Current estimate of acceleration and rotation rate biases * @param p Parameters, typically fixed in a single application */ - PreintegrationBase(const boost::shared_ptr& p, + PreintegrationBase(const boost::shared_ptr& p, const imuBias::ConstantBias& biasHat) : PreintegratedRotation(p), biasHat_(biasHat) { resetIntegration(); @@ -132,7 +132,7 @@ public: void resetIntegration(); const Params& p() const { - return *boost::static_pointer_cast(p_); + return *boost::static_pointer_cast(p_); } /// getters From 1ac790da1cfb34ec58a3cfa8dac66c74f204d50e Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 27 Jul 2015 16:00:06 +0200 Subject: [PATCH 082/142] Some minor refactoring/renaming --- gtsam/navigation/PreintegrationBase.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index da6b03019..367a62360 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -64,25 +64,22 @@ void PreintegrationBase::updatePreintegratedMeasurements( OptionalJacobian<9, 9> F) { const Matrix3 dRij = deltaRij_.matrix(); // expensive - const Vector3 j_acc = dRij * correctedAcc; // acceleration in current frame + const Vector3 i_acc = dRij * correctedAcc; // acceleration in i frame double dt22 = 0.5 * deltaT * deltaT; - deltaPij_ += deltaVij_ * deltaT + dt22 * j_acc; - deltaVij_ += deltaT * j_acc; + deltaPij_ += deltaVij_ * deltaT + dt22 * i_acc; + deltaVij_ += deltaT * i_acc; Matrix3 R_i, F_angles_angles; - if (F) - R_i = dRij; // has to be executed before updateIntegratedRotationAndDeltaT as that updates deltaRij updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0); if (F) { - const Matrix3 F_vel_angles = -R_i * skewSymmetric(correctedAcc) * deltaT; + const Matrix3 F_vel_angles = -dRij * skewSymmetric(correctedAcc) * deltaT; Matrix3 F_pos_angles; - F_pos_angles = 0.5 * F_vel_angles * deltaT; // pos vel angle *F << // - I_3x3, I_3x3 * deltaT, F_pos_angles, // pos + I_3x3, I_3x3 * deltaT, 0.5 * F_vel_angles * deltaT, // pos Z_3x3, I_3x3, F_vel_angles, // vel Z_3x3, Z_3x3, F_angles_angles; // angle } From 11d0ad0d78c9235fb8758ed9469d99571aded864 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 27 Jul 2015 16:12:41 +0200 Subject: [PATCH 083/142] Covariance regression values --- gtsam/navigation/tests/testImuFactor.cpp | 37 ++++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 1f1815fba..0ab2767f3 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -755,15 +755,27 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), Point3(1, 0, 0)); - ImuFactor::PreintegratedMeasurements pre_int_data( + ImuFactor::PreintegratedMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + Matrix expected(9,9); + expected << + 0.0001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290687976, 0.0, 0.0,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290687976, 0.0,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.03; + EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-7)); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kNonZeroOmegaCoriolis); Values values; @@ -864,16 +876,29 @@ TEST(ImuFactor, PredictArbitrary) { Vector3 measuredAcc(0.1, 0.2, -9.81); double deltaT = 0.001; - ImuFactor::PreintegratedMeasurements pre_int_data( + ImuFactor::PreintegratedMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); for (int i = 0; i < 1000; ++i) - pre_int_data.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + + Matrix expected(9,9); + expected << // + 0.142448905, 0.00345595825, -0.0225794125, 0.34774305, 0.0119449979, -0.075667905, -0.0144839494, -0.0454268484, 0.00489577218,// + 0.00345595825, 0.143318431, 0.0200549262, 0.0112877167, 0.351503176, 0.0629164907, 0.044978128, -0.0149428917, 0.00839301168, // + -0.0225794125, 0.0200549262, 0.0104041905, -0.0580647212, 0.051116506, 0.0285371399, 0.0100471195, 0.00609093775, 0.000448720395, // + 0.34774305, 0.0112877167, -0.0580647212, 0.911721561, 0.0412249672, -0.205920425, -0.0409843415, -0.13554868, 0.00879031682, // + 0.0119449979, 0.351503176, 0.051116506, 0.0412249672, 0.928013807, 0.169935105, 0.134423822, -0.0471183681, 0.0162199769, // + -0.075667905, 0.0629164907, 0.0285371399, -0.205920425, 0.169935105, 0.09407764, 0.0383280513, 0.0247643646, 0.00112485862,// + -0.0144839494, 0.044978128, 0.0100471195, -0.0409843415, 0.134423822, 0.0383280513, 0.0299999995, 0.0, 0.0, // + -0.0454268484, -0.0149428917, 0.00609093775, -0.13554868, -0.0471183681, 0.0247643646, 0.0, 0.0299999995, 0.0, // + 0.00489577218, 0.00839301168, 0.000448720395, 0.00879031682, 0.0162199769, 0.00112485862, 0.0, 0.0, 0.0299999995; + EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-7)); // Create factor - ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pre_int_data, kGravity, + ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kZeroOmegaCoriolis); // Predict From 20073919875f875fbf96231d12c8ad0fe7f5b6a7 Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 27 Jul 2015 18:35:56 +0200 Subject: [PATCH 084/142] Better test, with 2nd order integration --- gtsam/navigation/tests/testImuFactor.cpp | 41 +++++++++++++++++------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 0ab2767f3..374ac9c35 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -758,26 +758,43 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { ImuFactor::PreintegratedMeasurements pim( imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), kMeasuredAccCovariance, kMeasuredOmegaCovariance, - kIntegrationErrorCovariance); + kIntegrationErrorCovariance, true); - pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT, body_P_sensor); Matrix expected(9,9); expected << - 0.0001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290687976, 0.0, 0.0,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290687976, 0.0,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.03; - EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-7)); + 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, 0.0,// + 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290780477, 0.0, 9.23468723e-05,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290688208, 4.62505461e-06,// + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.23468723e-05, 4.62505461e-06, 0.0299907267; + EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-6)); // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity, kNonZeroOmegaCoriolis); + // Predict + Pose3 actual_x2; + Vector3 actual_v2; + ImuFactor::Predict(x1, v1, actual_x2, actual_v2, bias, factor.preintegratedMeasurements(), + kGravity, kZeroOmegaCoriolis); + + // Regression test with + Rot3 expectedR( // + 0.456795409, -0.888128414, 0.0506544554, // + 0.889548908, 0.45563417, -0.0331699173, // + 0.00637924528, 0.0602114814, 0.998165258); + Point3 expectedT(5.30373101, 0.768972495, -49.9942188); + Vector3 expected_v2(0.107462014, -0.46205501, 0.0115624037); + Pose3 expected_x2(expectedR, expectedT); + EXPECT(assert_equal(expected_x2, actual_x2, 1e-7)); + EXPECT(assert_equal(Vector(expected_v2), actual_v2, 1e-7)); + Values values; values.insert(X(1), x1); values.insert(V(1), v1); From 96c139ac87688f2db045793f7f87defc1047baad Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 15:42:45 +0200 Subject: [PATCH 085/142] Diff of individual Jacobians --- gtsam/nonlinear/factorTesting.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/gtsam/nonlinear/factorTesting.h b/gtsam/nonlinear/factorTesting.h index 52f103630..2a9d70cb4 100644 --- a/gtsam/nonlinear/factorTesting.h +++ b/gtsam/nonlinear/factorTesting.h @@ -85,9 +85,23 @@ bool testFactorJacobians(TestResult& result_, const std::string& name_, // Create actual value by linearize boost::shared_ptr actual = // boost::dynamic_pointer_cast(factor.linearize(values)); + if (!actual) return false; // Check cast result and then equality - return actual && assert_equal(expected, *actual, tolerance); + bool equal = assert_equal(expected, *actual, tolerance); + + // if not equal, test individual jacobians: + if (!equal) { + for (size_t i = 0; i < actual->size(); i++) { + bool i_good = assert_equal((Matrix) (expected.getA(expected.begin() + i)), + (Matrix) (actual->getA(actual->begin() + i)), tolerance); + if (!i_good) { + std::cout << "Mismatch in Jacobian " << i+1 << " (base 1), as shown above" << std::endl; + } + } + } + + return equal; } } From 5b9bf9affa1150f44dd7e1da00c1d02cf7d1ddf1 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 15:43:42 +0200 Subject: [PATCH 086/142] Fix the noise covariances to conform to new error order... --- gtsam/navigation/ImuFactor.cpp | 34 +++++++++++------- gtsam/navigation/ImuFactor.h | 2 +- gtsam/navigation/PreintegrationBase.cpp | 27 +++++++------- gtsam/navigation/tests/testImuFactor.cpp | 46 +++++++++++------------- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 187261685..6a720972c 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -48,6 +48,16 @@ void PreintegratedImuMeasurements::resetIntegration() { preintMeasCov_.setZero(); } +// sugar for derivative blocks +#define D_R_R(H) (H)->block<3,3>(0,0) +#define D_R_t(H) (H)->block<3,3>(0,3) +#define D_R_v(H) (H)->block<3,3>(0,6) +#define D_t_R(H) (H)->block<3,3>(3,0) +#define D_t_t(H) (H)->block<3,3>(3,3) +#define D_t_v(H) (H)->block<3,3>(3,6) +#define D_v_R(H) (H)->block<3,3>(6,0) +#define D_v_t(H) (H)->block<3,3>(6,3) +#define D_v_v(H) (H)->block<3,3>(6,6) //------------------------------------------------------------------------------ void PreintegratedImuMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, @@ -81,20 +91,19 @@ void PreintegratedImuMeasurements::integrateMeasurement( // NOTE 2: computation of G * (1/deltaT) * measurementCovariance * G.transpose() done block-wise, // as G and measurementCovariance are block-diagonal matrices preintMeasCov_ = F * preintMeasCov_ * F.transpose(); - preintMeasCov_.block<3, 3>(0, 0) += p().integrationCovariance * deltaT; - preintMeasCov_.block<3, 3>(3, 3) += dRij * p().accelerometerCovariance - * dRij.transpose() * deltaT; - preintMeasCov_.block<3, 3>(6, 6) += D_Rincr_integratedOmega - * p().gyroscopeCovariance * D_Rincr_integratedOmega.transpose() * deltaT; + D_t_t(&preintMeasCov_) += p().integrationCovariance * deltaT; + D_v_v(&preintMeasCov_) += dRij * p().accelerometerCovariance * dRij.transpose() * deltaT; + D_R_R(&preintMeasCov_) += D_Rincr_integratedOmega * p().gyroscopeCovariance + * D_Rincr_integratedOmega.transpose() * deltaT; Matrix3 F_pos_noiseacc; F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; - preintMeasCov_.block<3, 3>(0, 0) += (1 / deltaT) * F_pos_noiseacc + D_t_t(&preintMeasCov_) += (1 / deltaT) * F_pos_noiseacc * p().accelerometerCovariance * F_pos_noiseacc.transpose(); Matrix3 temp = F_pos_noiseacc * p().accelerometerCovariance * dRij.transpose(); // has 1/deltaT - preintMeasCov_.block<3, 3>(0, 3) += temp; - preintMeasCov_.block<3, 3>(3, 0) += temp.transpose(); + D_t_v(&preintMeasCov_) += temp; + D_v_t(&preintMeasCov_) += temp.transpose(); // F_test and G_test are given as output for testing purposes and are not needed by the factor if (F_test) { @@ -102,10 +111,11 @@ void PreintegratedImuMeasurements::integrateMeasurement( } if (G_test) { // This in only for testing & documentation, while the actual computation is done block-wise - // intNoise accNoise omegaNoise - (*G_test) << I_3x3 * deltaT, F_pos_noiseacc, Z_3x3, // pos - Z_3x3, dRij * deltaT, Z_3x3, // vel - Z_3x3, Z_3x3, D_Rincr_integratedOmega * deltaT; // angle + // omegaNoise intNoise accNoise + (*G_test) << + D_Rincr_integratedOmega * deltaT, Z_3x3, Z_3x3, // angle + Z_3x3, I_3x3 * deltaT, F_pos_noiseacc, // pos + Z_3x3, Z_3x3, dRij * deltaT; // vel } } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 980f7d3f3..32ee4185e 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -63,7 +63,7 @@ class PreintegratedImuMeasurements: public PreintegrationBase { protected: - Eigen::Matrix preintMeasCov_; ///< COVARIANCE OF: [PreintPOSITION PreintVELOCITY PreintROTATION] + Matrix9 preintMeasCov_; ///< COVARIANCE OF: [PreintPOSITION PreintVELOCITY PreintROTATION] ///< (first-order propagation from *measurementCovariance*). /// Default constructor for serialization diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 367a62360..92f50fa3a 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -63,25 +63,22 @@ void PreintegrationBase::updatePreintegratedMeasurements( const Vector3& correctedAcc, const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F) { - const Matrix3 dRij = deltaRij_.matrix(); // expensive - const Vector3 i_acc = dRij * correctedAcc; // acceleration in i frame + // Calculate acceleration in *current* i frame, i.e., before rotation update below + Matrix3 D_acc_R; + const Vector3 i_acc = deltaRij_.rotate(correctedAcc, F? &D_acc_R : 0); - double dt22 = 0.5 * deltaT * deltaT; - deltaPij_ += deltaVij_ * deltaT + dt22 * i_acc; - deltaVij_ += deltaT * i_acc; - - Matrix3 R_i, F_angles_angles; + Matrix3 F_angles_angles; updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0); - if (F) { - const Matrix3 F_vel_angles = -dRij * skewSymmetric(correctedAcc) * deltaT; - Matrix3 F_pos_angles; + double dt22 = 0.5 * deltaT * deltaT; + deltaPij_ += dt22 * i_acc + deltaT * deltaVij_; + deltaVij_ += deltaT * i_acc; - // pos vel angle - *F << // - I_3x3, I_3x3 * deltaT, 0.5 * F_vel_angles * deltaT, // pos - Z_3x3, I_3x3, F_vel_angles, // vel - Z_3x3, Z_3x3, F_angles_angles; // angle + if (F) { + *F << // angle pos vel + F_angles_angles, Z_3x3, Z_3x3, // angle + dt22 * D_acc_R, I_3x3, I_3x3 * deltaT, // pos + deltaT * D_acc_R, Z_3x3, I_3x3; // vel } } diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index c95c1d667..9ab2d8a51 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -628,18 +628,15 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaRij_old); - Matrix FexpectedTop6(6, 9); - FexpectedTop6 << dfpv_dpos, dfpv_dvel, dfpv_dangle; - // Compute expected f_rot wrt angles Matrix dfr_dangle = numericalDerivative11( boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), deltaRij_old); - Matrix FexpectedBottom3(3, 9); - FexpectedBottom3 << Z_3x3, Z_3x3, dfr_dangle; Matrix Fexpected(9, 9); - Fexpected << FexpectedTop6, FexpectedBottom3; + Fexpected << // + dfr_dangle, Z_3x3, Z_3x3, // + dfpv_dangle, dfpv_dpos, dfpv_dvel; EXPECT(assert_equal(Fexpected, Factual)); @@ -659,25 +656,25 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { Matrix dgpv_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, newMeasuredAcc, _1, newDeltaT), newMeasuredOmega); - Matrix GexpectedTop6(6, 9); - GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), newMeasuredOmega); - Matrix GexpectedBottom3(3, 9); - GexpectedBottom3 << Z_3x3, Z_3x3, dgr_dangle; Matrix Gexpected(9, 9); - Gexpected << GexpectedTop6, GexpectedBottom3; + Gexpected << // + dgr_dangle, Z_3x3, Z_3x3, // + dgpv_domegaNoise, dgpv_dintNoise, dgpv_daccNoise; EXPECT(assert_equal(Gexpected, Gactual)); // Check covariance propagation Matrix9 measurementCovariance; - measurementCovariance << intNoiseVar * I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar - * I_3x3, Z_3x3, Z_3x3, Z_3x3, omegaNoiseVar * I_3x3; + measurementCovariance << // + omegaNoiseVar * I_3x3, Z_3x3, Z_3x3, // + Z_3x3, intNoiseVar * I_3x3, Z_3x3, // + Z_3x3, Z_3x3, accNoiseVar * I_3x3; Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance * Factual.transpose() @@ -745,18 +742,15 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaRij_old); - Matrix FexpectedTop6(6, 9); - FexpectedTop6 << dfpv_dpos, dfpv_dvel, dfpv_dangle; - // Compute expected f_rot wrt angles Matrix dfr_dangle = numericalDerivative11( boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), deltaRij_old); - Matrix FexpectedBottom3(3, 9); - FexpectedBottom3 << Z_3x3, Z_3x3, dfr_dangle; Matrix Fexpected(9, 9); - Fexpected << FexpectedTop6, FexpectedBottom3; + Fexpected << // + dfr_dangle, Z_3x3, Z_3x3, // + dfpv_dangle, dfpv_dpos, dfpv_dvel; EXPECT(assert_equal(Fexpected, Factual)); @@ -776,25 +770,25 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { Matrix dgpv_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, newMeasuredAcc, _1, newDeltaT), newMeasuredOmega); - Matrix GexpectedTop6(6, 9); - GexpectedTop6 << dgpv_dintNoise, dgpv_daccNoise, dgpv_domegaNoise; // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), newMeasuredOmega); - Matrix GexpectedBottom3(3, 9); - GexpectedBottom3 << Z_3x3, Z_3x3, dgr_dangle; Matrix Gexpected(9, 9); - Gexpected << GexpectedTop6, GexpectedBottom3; + Gexpected << // + dgr_dangle, Z_3x3, Z_3x3, // + dgpv_domegaNoise, dgpv_dintNoise, dgpv_daccNoise; EXPECT(assert_equal(Gexpected, Gactual)); // Check covariance propagation Matrix9 measurementCovariance; - measurementCovariance << intNoiseVar * I_3x3, Z_3x3, Z_3x3, Z_3x3, accNoiseVar - * I_3x3, Z_3x3, Z_3x3, Z_3x3, omegaNoiseVar * I_3x3; + measurementCovariance << // + omegaNoiseVar * I_3x3, Z_3x3, Z_3x3, // + Z_3x3, intNoiseVar * I_3x3, Z_3x3, // + Z_3x3, Z_3x3, accNoiseVar * I_3x3; Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance * Factual.transpose() From 5ca67d0a3a4a29458de7a49b939248e64f93582f Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 11:20:35 -0700 Subject: [PATCH 087/142] FromPoseVelocity --- gtsam/navigation/NavState.cpp | 16 +++++++++++++--- gtsam/navigation/NavState.h | 3 +++ gtsam/navigation/tests/testNavState.cpp | 22 +++++++++++++++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index a52b74704..c45b86450 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -24,6 +24,16 @@ namespace gtsam { #define TIE(R,t,v,x) const Rot3& R = (x).R_;const Point3& t = (x).t_;const Velocity3& v = (x).v_; +//------------------------------------------------------------------------------ +NavState NavState::FromPoseVelocity(const Pose3& pose, const Vector3& vel, + OptionalJacobian<9, 6> H1, OptionalJacobian<9, 3> H2) { + if (H1) + *H1 << I_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3; + if (H2) + *H2 << Z_3x3, Z_3x3, pose.rotation().transpose(); + return NavState(pose, vel); +} + //------------------------------------------------------------------------------ const Rot3& NavState::attitude(OptionalJacobian<3, 9> H) const { if (H) @@ -252,9 +262,9 @@ Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder, //------------------------------------------------------------------------------ Vector9 NavState::correctPIM(const Vector9& pim, double dt, - const Vector3& n_gravity, - const boost::optional& omegaCoriolis, bool use2ndOrderCoriolis, - OptionalJacobian<9, 9> H1, OptionalJacobian<9, 9> H2) const { + const Vector3& n_gravity, const boost::optional& omegaCoriolis, + bool use2ndOrderCoriolis, OptionalJacobian<9, 9> H1, + OptionalJacobian<9, 9> H2) const { const Rot3& nRb = R_; const Velocity3& n_v = v_; // derivative is Ri ! const double dt2 = dt * dt; diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 8eb8c54d7..996ae763b 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -67,6 +67,9 @@ public: NavState(const Matrix3& R, const Vector9 tv) : R_(R), t_(tv.head<3>()), v_(tv.tail<3>()) { } + /// Named constructor with derivatives + static NavState FromPoseVelocity(const Pose3& pose, const Vector3& vel, + OptionalJacobian<9, 6> H1, OptionalJacobian<9, 3> H2); /// @} /// @name Component Access diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index c0797bd9c..1d71d4e7c 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -26,6 +26,7 @@ using namespace gtsam; static const Rot3 kRotation = Rot3::RzRyRx(0.1, 0.2, 0.3); static const Point3 kPosition(1.0, 2.0, 3.0); +static const Pose3 kPose(kRotation, kPosition); static const Velocity3 kVelocity(0.4, 0.5, 0.6); static const NavState kIdentity; static const NavState kState1(kRotation, kPosition, kVelocity); @@ -33,6 +34,16 @@ static const Vector3 kOmegaCoriolis(0.02, 0.03, 0.04); static const Vector3 kGravity(0, 0, 9.81); static const Vector9 kZeroXi = Vector9::Zero(); +/* ************************************************************************* */ +TEST(NavState, Constructor) { + boost::function construct = + boost::bind(&NavState::FromPoseVelocity, _1, _2, boost::none, + boost::none); + Matrix aH1, aH2; + EXPECT(assert_equal(kState1, NavState::FromPoseVelocity(kPose, kVelocity, aH1, aH2))); + EXPECT(assert_equal(numericalDerivative21(construct, kPose, kVelocity), aH1)); + EXPECT(assert_equal(numericalDerivative22(construct, kPose, kVelocity), aH2)); +} /* ************************************************************************* */ TEST( NavState, Attitude) { Matrix39 aH, eH; @@ -127,9 +138,14 @@ TEST( NavState, Manifold ) { // Check localCoordinates derivatives kState1.localCoordinates(state2, aH1, aH2); boost::function localCoordinates = - boost::bind(&NavState::localCoordinates, _1, _2, boost::none, boost::none); - EXPECT(assert_equal(numericalDerivative21(localCoordinates, kState1, state2), aH1)); - EXPECT(assert_equal(numericalDerivative22(localCoordinates, kState1, state2), aH2)); + boost::bind(&NavState::localCoordinates, _1, _2, boost::none, + boost::none); + EXPECT( + assert_equal(numericalDerivative21(localCoordinates, kState1, state2), + aH1)); + EXPECT( + assert_equal(numericalDerivative22(localCoordinates, kState1, state2), + aH2)); } /* ************************************************************************* */ From 999a19a57d26a78cbfb470007d0a0c896ba77be8 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 11:52:46 -0700 Subject: [PATCH 088/142] More tests on localCoordinates --- gtsam/navigation/tests/testNavState.cpp | 26 +++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index 1d71d4e7c..6732643e1 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -40,7 +40,9 @@ TEST(NavState, Constructor) { boost::bind(&NavState::FromPoseVelocity, _1, _2, boost::none, boost::none); Matrix aH1, aH2; - EXPECT(assert_equal(kState1, NavState::FromPoseVelocity(kPose, kVelocity, aH1, aH2))); + EXPECT( + assert_equal(kState1, + NavState::FromPoseVelocity(kPose, kVelocity, aH1, aH2))); EXPECT(assert_equal(numericalDerivative21(construct, kPose, kVelocity), aH1)); EXPECT(assert_equal(numericalDerivative22(construct, kPose, kVelocity), aH2)); } @@ -136,16 +138,20 @@ TEST( NavState, Manifold ) { EXPECT(assert_equal(numericalDerivative22(retract, kState1, xi), aH2)); // Check localCoordinates derivatives + boost::function local = + boost::bind(&NavState::localCoordinates, _1, _2, boost::none, boost::none); + // from state1 to state2 kState1.localCoordinates(state2, aH1, aH2); - boost::function localCoordinates = - boost::bind(&NavState::localCoordinates, _1, _2, boost::none, - boost::none); - EXPECT( - assert_equal(numericalDerivative21(localCoordinates, kState1, state2), - aH1)); - EXPECT( - assert_equal(numericalDerivative22(localCoordinates, kState1, state2), - aH2)); + EXPECT(assert_equal(numericalDerivative21(local, kState1, state2), aH1)); + EXPECT(assert_equal(numericalDerivative22(local, kState1, state2), aH2)); + // from identity to state2 + kIdentity.localCoordinates(state2, aH1, aH2); + EXPECT(assert_equal(numericalDerivative21(local, kIdentity, state2), aH1)); + EXPECT(assert_equal(numericalDerivative22(local, kIdentity, state2), aH2)); + // from state2 to identity + state2.localCoordinates(kIdentity, aH1, aH2); + EXPECT(assert_equal(numericalDerivative21(local, state2, kIdentity), aH1)); + EXPECT(assert_equal(numericalDerivative22(local, state2, kIdentity), aH2)); } /* ************************************************************************* */ From 9d1111a76702d327c1ee0ebec61da516b6169768 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 11:53:24 -0700 Subject: [PATCH 089/142] Derivative accuracy --- gtsam/navigation/PreintegrationBase.cpp | 2 ++ gtsam/navigation/tests/testImuFactor.cpp | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 367a62360..3b1cea06f 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -183,6 +183,8 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, OptionalJacobian<9, 3> H2, OptionalJacobian<9, 6> H3, OptionalJacobian<9, 3> H4, OptionalJacobian<9, 6> H5) const { + // Note that derivative of constructors below is not identity for velocity, but + // a 9*3 matrix == Z_3x3, Z_3x3, state.R().transpose() NavState state_i(pose_i, vel_i); NavState state_j(pose_j, vel_j); diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index c95c1d667..79497f63c 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -872,8 +872,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { values.insert(B(1), bias); // Make sure linearization is correct - double diffDelta = 1e-5; - // This fails, except if tol = 1e-1: probably wrong! + double diffDelta = 1e-7; EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } From 75cc3020eb4574a6121ebab18c01ac569bbb8bb1 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 12:01:22 -0700 Subject: [PATCH 090/142] Fixed all but sensor_pose error --- gtsam/navigation/tests/testImuFactor.cpp | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 8c87306a0..7e540b793 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -306,7 +306,7 @@ TEST(ImuFactor, ErrorAndJacobians) { EXPECT(assert_equal(expectedError, factor.unwhitenedError(values))); // Make sure linearization is correct - double diffDelta = 1e-5; + double diffDelta = 1e-7; EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); // Actual Jacobians @@ -391,7 +391,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { values.insert(B(1), bias); // Make sure linearization is correct - double diffDelta = 1e-5; + double diffDelta = 1e-7; EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } @@ -431,7 +431,7 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { values.insert(B(1), bias); // Make sure linearization is correct - double diffDelta = 1e-5; + double diffDelta = 1e-7; EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } @@ -826,15 +826,15 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { Matrix expected(9,9); expected << - 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, 0.0,// - 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290780477, 0.0, 9.23468723e-05,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0290688208, 4.62505461e-06,// - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 9.23468723e-05, 4.62505461e-06, 0.0299907267; + 0.0290780477, 4.63277848e-07, 9.23468723e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // + 4.63277848e-07, 0.0290688208, 4.62505461e-06, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // + 9.23468723e-05, 4.62505461e-06, 0.0299907267, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // + 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, // + 0.0, 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, // + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, // + 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, // + 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, // + 0.0, 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01; EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-6)); // Create factor @@ -960,15 +960,15 @@ TEST(ImuFactor, PredictArbitrary) { Matrix expected(9,9); expected << // - 0.142448905, 0.00345595825, -0.0225794125, 0.34774305, 0.0119449979, -0.075667905, -0.0144839494, -0.0454268484, 0.00489577218,// - 0.00345595825, 0.143318431, 0.0200549262, 0.0112877167, 0.351503176, 0.0629164907, 0.044978128, -0.0149428917, 0.00839301168, // - -0.0225794125, 0.0200549262, 0.0104041905, -0.0580647212, 0.051116506, 0.0285371399, 0.0100471195, 0.00609093775, 0.000448720395, // - 0.34774305, 0.0112877167, -0.0580647212, 0.911721561, 0.0412249672, -0.205920425, -0.0409843415, -0.13554868, 0.00879031682, // - 0.0119449979, 0.351503176, 0.051116506, 0.0412249672, 0.928013807, 0.169935105, 0.134423822, -0.0471183681, 0.0162199769, // - -0.075667905, 0.0629164907, 0.0285371399, -0.205920425, 0.169935105, 0.09407764, 0.0383280513, 0.0247643646, 0.00112485862,// - -0.0144839494, 0.044978128, 0.0100471195, -0.0409843415, 0.134423822, 0.0383280513, 0.0299999995, 0.0, 0.0, // - -0.0454268484, -0.0149428917, 0.00609093775, -0.13554868, -0.0471183681, 0.0247643646, 0.0, 0.0299999995, 0.0, // - 0.00489577218, 0.00839301168, 0.000448720395, 0.00879031682, 0.0162199769, 0.00112485862, 0.0, 0.0, 0.0299999995; + 0.0299999995, 2.46739898e-10, 2.46739896e-10, -0.0144839494, 0.044978128, 0.0100471195, -0.0409843415, 0.134423822, 0.0383280513, // + 2.46739898e-10, 0.0299999995, 2.46739902e-10, -0.0454268484, -0.0149428917, 0.00609093775, -0.13554868, -0.0471183681, 0.0247643646, // + 2.46739896e-10, 2.46739902e-10, 0.0299999995, 0.00489577218, 0.00839301168, 0.000448720395, 0.00879031682, 0.0162199769, 0.00112485862, // + -0.0144839494, -0.0454268484, 0.00489577218, 0.142448905, 0.00345595825, -0.0225794125, 0.34774305, 0.0119449979, -0.075667905, // + 0.044978128, -0.0149428917, 0.00839301168, 0.00345595825, 0.143318431, 0.0200549262, 0.0112877167, 0.351503176, 0.0629164907, // + 0.0100471195, 0.00609093775, 0.000448720395, -0.0225794125, 0.0200549262, 0.0104041905, -0.0580647212, 0.051116506, 0.0285371399, // + -0.0409843415, -0.13554868, 0.00879031682, 0.34774305, 0.0112877167, -0.0580647212, 0.911721561, 0.0412249672, -0.205920425, // + 0.134423822, -0.0471183681, 0.0162199769, 0.0119449979, 0.351503176, 0.051116506, 0.0412249672, 0.928013807, 0.169935105, // + 0.0383280513, 0.0247643646, 0.00112485862, -0.075667905, 0.0629164907, 0.0285371399, -0.205920425, 0.169935105, 0.09407764; EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-7)); // Create factor From 0b4919e099d5221066ac9886e9ebd7293864f8ab Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 15:49:03 -0700 Subject: [PATCH 091/142] Refactoring of integrateMeasurement to reduce copy/paste --- gtsam/navigation/AHRSFactor.cpp | 10 +++-- gtsam/navigation/CombinedImuFactor.cpp | 25 +++-------- gtsam/navigation/ImuFactor.cpp | 25 +++-------- gtsam/navigation/PreintegratedRotation.h | 39 +++++++++++------ gtsam/navigation/PreintegrationBase.cpp | 54 +++++++++++++----------- gtsam/navigation/PreintegrationBase.h | 9 ++-- 6 files changed, 79 insertions(+), 83 deletions(-) diff --git a/gtsam/navigation/AHRSFactor.cpp b/gtsam/navigation/AHRSFactor.cpp index 814b020b1..85a95c35a 100644 --- a/gtsam/navigation/AHRSFactor.cpp +++ b/gtsam/navigation/AHRSFactor.cpp @@ -64,15 +64,17 @@ void PreintegratedAhrsMeasurements::integrateMeasurement( // rotation vector describing rotation increment computed from the // current rotation rate measurement const Vector3 theta_incr = correctedOmega * deltaT; - Matrix3 D_Rincr_integratedOmega; - const Rot3 incrR = Rot3::Expmap(theta_incr, D_Rincr_integratedOmega); // expensive !! + Matrix3 D_incrR_integratedOmega; + const Rot3 incrR = Rot3::Expmap(theta_incr, D_incrR_integratedOmega); // expensive !! // Update Jacobian - update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT); + const Matrix3 incrRt = incrR.transpose(); + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - D_incrR_integratedOmega * deltaT; // Update rotation and deltaTij. Matrix3 Fr; // Jacobian of the update - updateIntegratedRotationAndDeltaT(incrR, deltaT, Fr); + deltaRij_ = deltaRij_.compose(incrR, Fr); + deltaTij_ += deltaT; // first order uncertainty propagation // the deltaT allows to pass from continuous time noise to discrete time noise diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index c6bc8282a..ea68f724e 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -55,33 +55,22 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, boost::optional F_test, boost::optional G_test) { - // NOTE: order is important here because each update uses old values, e.g., velocity and position updates are based on previous rotation estimate. - // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). + const Matrix3 dRij = deltaRij_.matrix(); // expensive when quaternion - Vector3 correctedAcc, correctedOmega; - boost::tie(correctedAcc, correctedOmega) = - correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega); - - const Vector3 integratedOmega = correctedOmega * deltaT; // rotation vector describing rotation increment computed from the current rotation rate measurement - Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr - const Rot3 Rincr = Rot3::Expmap(integratedOmega, D_Rincr_integratedOmega); // rotation increment computed from the current rotation rate measurement - - // Update Jacobians - /* ----------------------------------------------------------------------------------------------------------------------- */ - updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, deltaT); + // Update preintegrated measurements. + Matrix3 D_incrR_integratedOmega; // Right jacobian computed at theta_incr + Matrix9 F_9x9; // overall Jacobian wrt preintegrated measurements (df/dx) + updatePreintegratedMeasurements(measuredAcc, measuredOmega, deltaT, + &D_incrR_integratedOmega, &F_9x9); // Update preintegrated measurements covariance: as in [2] we consider a first order propagation that // can be seen as a prediction phase in an EKF framework. In this implementation, contrarily to [2] we // consider the uncertainty of the bias selection and we keep correlation between biases and preintegrated measurements /* ----------------------------------------------------------------------------------------------------------------------- */ - const Matrix3 dRij = deltaRij_.matrix(); // expensive when quaternion - // Update preintegrated measurements. TODO Frank moved from end of this function !!! - Matrix9 F_9x9; - updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F_9x9); // Single Jacobians to propagate covariance Matrix3 H_vel_biasacc = -dRij * deltaT; - Matrix3 H_angles_biasomega = -D_Rincr_integratedOmega * deltaT; + Matrix3 H_angles_biasomega = -D_incrR_integratedOmega * deltaT; // overall Jacobian wrt preintegrated measurements (df/dx) Eigen::Matrix F; diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 6a720972c..6745a86fc 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -63,24 +63,13 @@ void PreintegratedImuMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, OptionalJacobian<9, 9> F_test, OptionalJacobian<9, 9> G_test) { - Vector3 correctedAcc, correctedOmega; - boost::tie(correctedAcc, correctedOmega) = - correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega); - - // rotation increment computed from the current rotation rate measurement - const Vector3 integratedOmega = correctedOmega * deltaT; - Matrix3 D_Rincr_integratedOmega; // Right jacobian computed at theta_incr - // rotation increment computed from the current rotation rate measurement - const Rot3 Rincr = Rot3::Expmap(integratedOmega, D_Rincr_integratedOmega); - - // Update Jacobians - updatePreintegratedJacobians(correctedAcc, D_Rincr_integratedOmega, Rincr, - deltaT); + const Matrix3 dRij = deltaRij_.matrix(); // store this, which is useful to compute G_test // Update preintegrated measurements (also get Jacobian) - const Matrix3 dRij = deltaRij_.matrix(); // store this, which is useful to compute G_test + Matrix3 D_incrR_integratedOmega; // Right jacobian computed at theta_incr Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) - updatePreintegratedMeasurements(correctedAcc, Rincr, deltaT, F); + updatePreintegratedMeasurements(measuredAcc, measuredOmega, deltaT, + &D_incrR_integratedOmega, &F); // first order covariance propagation: // as in [2] we consider a first order propagation that can be seen as a prediction phase in EKF @@ -93,8 +82,8 @@ void PreintegratedImuMeasurements::integrateMeasurement( preintMeasCov_ = F * preintMeasCov_ * F.transpose(); D_t_t(&preintMeasCov_) += p().integrationCovariance * deltaT; D_v_v(&preintMeasCov_) += dRij * p().accelerometerCovariance * dRij.transpose() * deltaT; - D_R_R(&preintMeasCov_) += D_Rincr_integratedOmega * p().gyroscopeCovariance - * D_Rincr_integratedOmega.transpose() * deltaT; + D_R_R(&preintMeasCov_) += D_incrR_integratedOmega * p().gyroscopeCovariance + * D_incrR_integratedOmega.transpose() * deltaT; Matrix3 F_pos_noiseacc; F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; @@ -113,7 +102,7 @@ void PreintegratedImuMeasurements::integrateMeasurement( // This in only for testing & documentation, while the actual computation is done block-wise // omegaNoise intNoise accNoise (*G_test) << - D_Rincr_integratedOmega * deltaT, Z_3x3, Z_3x3, // angle + D_incrR_integratedOmega * deltaT, Z_3x3, Z_3x3, // angle Z_3x3, I_3x3 * deltaT, F_pos_noiseacc, // pos Z_3x3, Z_3x3, dRij * deltaT; // vel } diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index df521341f..df0953945 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -108,21 +108,34 @@ class PreintegratedRotation { /// @} - /// Update preintegrated measurements - void updateIntegratedRotationAndDeltaT(const Rot3& incrR, const double deltaT, - OptionalJacobian<3, 3> H = boost::none) { - deltaRij_ = deltaRij_.compose(incrR, H, boost::none); - deltaTij_ += deltaT; - } + void integrateMeasurement(const Vector3& measuredOmega, + const Vector3& biasHat, double deltaT, Matrix3* D_incrR_integratedOmega, + Matrix3* Fr) { - /** - * Update Jacobians to be used during preintegration - * TODO: explain arguments - */ - void update_delRdelBiasOmega(const Matrix3& D_Rincr_integratedOmega, - const Rot3& incrR, double deltaT) { + // First we compensate the measurements for the bias + Vector3 correctedOmega = measuredOmega - biasHat; + + // Then compensate for sensor-body displacement: we express the quantities + // (originally in the IMU frame) into the body frame + if (p_->body_P_sensor) { + Matrix3 body_R_sensor = p_->body_P_sensor->rotation().matrix(); + // rotation rate vector in the body frame + correctedOmega = body_R_sensor * correctedOmega; + } + + // rotation vector describing rotation increment computed from the + // current rotation rate measurement + const Vector3 theta_incr = correctedOmega * deltaT; + const Rot3 incrR = Rot3::Expmap(theta_incr, D_incrR_integratedOmega); // expensive !! + + // Update deltaTij and rotation + deltaTij_ += deltaT; + deltaRij_ = deltaRij_.compose(incrR, Fr); + + // Update Jacobian const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - D_Rincr_integratedOmega * deltaT; + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ + - *D_incrR_integratedOmega * deltaT; } /// Return a bias corrected version of the integrated rotation, with optional Jacobian diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index f6664e5dd..13baa973d 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -60,40 +60,46 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, /// Update preintegrated measurements void PreintegrationBase::updatePreintegratedMeasurements( - const Vector3& correctedAcc, const Rot3& incrR, const double deltaT, - OptionalJacobian<9, 9> F) { + const Vector3& measuredAcc, const Vector3& measuredOmega, + const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F) { + + Matrix3 D_Rij_incrR; + + // NOTE: order is important here because each update uses old values, e.g., velocity and position updates are based on previous rotation estimate. + // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). + + Vector3 correctedAcc, correctedOmega; + boost::tie(correctedAcc, correctedOmega) = + correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega); + + // rotation vector describing rotation increment computed from the current rotation rate measurement + const Vector3 integratedOmega = correctedOmega * deltaT; + const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // rotation increment computed from the current rotation rate measurement // Calculate acceleration in *current* i frame, i.e., before rotation update below Matrix3 D_acc_R; - const Vector3 i_acc = deltaRij_.rotate(correctedAcc, F? &D_acc_R : 0); - - Matrix3 F_angles_angles; - updateIntegratedRotationAndDeltaT(incrR, deltaT, F ? &F_angles_angles : 0); + const Matrix3 dRij = deltaRij_.matrix(); // expensive + const Vector3 i_acc = deltaRij_.rotate(correctedAcc, F ? &D_acc_R : 0); double dt22 = 0.5 * deltaT * deltaT; + deltaTij_ += deltaT; + deltaRij_ = deltaRij_.compose(incrR, F ? &D_Rij_incrR : 0); deltaPij_ += dt22 * i_acc + deltaT * deltaVij_; deltaVij_ += deltaT * i_acc; - if (F) { - *F << // angle pos vel - F_angles_angles, Z_3x3, Z_3x3, // angle - dt22 * D_acc_R, I_3x3, I_3x3 * deltaT, // pos - deltaT * D_acc_R, Z_3x3, I_3x3; // vel - } -} + *F << // angle pos vel + D_Rij_incrR, Z_3x3, Z_3x3, // angle + dt22 * D_acc_R, I_3x3, I_3x3 * deltaT, // pos + deltaT * D_acc_R, Z_3x3, I_3x3; // vel -/// Update Jacobians to be used during preintegration -void PreintegrationBase::updatePreintegratedJacobians( - const Vector3& correctedAcc, const Matrix3& D_Rincr_integratedOmega, - const Rot3& incrR, double deltaT) { - const Matrix3 dRij = deltaRij_.matrix(); // expensive - const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * deltaT - * delRdelBiasOmega_; - delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - 0.5 * dRij * deltaT * deltaT; - delPdelBiasOmega_ += deltaT * (delVdelBiasOmega_ + temp * 0.5); + const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * delRdelBiasOmega_; + delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - dt22 * dRij; + delPdelBiasOmega_ += deltaT * delVdelBiasOmega_ + dt22 * temp; delVdelBiasAcc_ += -dRij * deltaT; - delVdelBiasOmega_ += temp; - update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT); + delVdelBiasOmega_ += temp * deltaT; + const Matrix3 incrRt = incrR.transpose(); + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ + - *D_incrR_integratedOmega * deltaT; } std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index e41114b07..bcb38a8f9 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -170,12 +170,9 @@ public: bool equals(const PreintegrationBase& other, double tol) const; /// Update preintegrated measurements - void updatePreintegratedMeasurements(const Vector3& correctedAcc, - const Rot3& incrR, const double deltaT, OptionalJacobian<9, 9> F); - - /// Update Jacobians to be used during preintegration - void updatePreintegratedJacobians(const Vector3& correctedAcc, - const Matrix3& D_Rincr_integratedOmega, const Rot3& incrR, double deltaT); + void updatePreintegratedMeasurements(const Vector3& measuredAcc, + const Vector3& measuredOmega, const double deltaT, + Matrix3* D_incrR_integratedOmega, Matrix9* F); std::pair correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, From aebe8161dddbb3dc81bb8c95d823017e2e9359da Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 16:14:42 -0700 Subject: [PATCH 092/142] Strengthened AHRS tests --- gtsam/navigation/AHRSFactor.h | 1 + gtsam/navigation/tests/testAHRSFactor.cpp | 84 +++++++++++------------ 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/gtsam/navigation/AHRSFactor.h b/gtsam/navigation/AHRSFactor.h index 6c79ea137..c2a88bd44 100644 --- a/gtsam/navigation/AHRSFactor.h +++ b/gtsam/navigation/AHRSFactor.h @@ -89,6 +89,7 @@ class GTSAM_EXPORT PreintegratedAhrsMeasurements : public PreintegratedRotation const Matrix3& measuredOmegaCovariance) : PreintegratedRotation(boost::make_shared()), biasHat_(biasHat) { + p_->gyroscopeCovariance = measuredOmegaCovariance; resetIntegration(); } diff --git a/gtsam/navigation/tests/testAHRSFactor.cpp b/gtsam/navigation/tests/testAHRSFactor.cpp index 73e30ac32..9e8e78efe 100644 --- a/gtsam/navigation/tests/testAHRSFactor.cpp +++ b/gtsam/navigation/tests/testAHRSFactor.cpp @@ -35,6 +35,12 @@ using symbol_shorthand::X; using symbol_shorthand::V; using symbol_shorthand::B; +Vector3 kZeroOmegaCoriolis(0,0,0); + +// Define covariance matrices +double accNoiseVar = 0.01; +const Matrix3 kMeasuredAccCovariance = accNoiseVar * I_3x3; + //****************************************************************************** namespace { Vector callEvaluateError(const AHRSFactor& factor, const Rot3 rot_i, @@ -51,7 +57,7 @@ AHRSFactor::PreintegratedMeasurements evaluatePreintegratedMeasurements( const Vector3& bias, const list& measuredOmegas, const list& deltaTs, const Vector3& initialRotationRate = Vector3::Zero()) { - AHRSFactor::PreintegratedMeasurements result(bias, Matrix3::Identity()); + AHRSFactor::PreintegratedMeasurements result(bias, I_3x3); list::const_iterator itOmega = measuredOmegas.begin(); list::const_iterator itDeltaT = deltaTs.begin(); @@ -95,7 +101,7 @@ TEST( AHRSFactor, PreintegratedMeasurements ) { double expectedDeltaT1(0.5); // Actual preintegrated values - AHRSFactor::PreintegratedMeasurements actual1(bias, Matrix3::Zero()); + AHRSFactor::PreintegratedMeasurements actual1(bias, Z_3x3); actual1.integrateMeasurement(measuredOmega, deltaT); EXPECT(assert_equal(expectedDeltaR1, Rot3(actual1.deltaRij()), 1e-6)); @@ -121,18 +127,14 @@ TEST(AHRSFactor, Error) { Rot3 x2(Rot3::RzRyRx(M_PI / 12.0 + M_PI / 100.0, M_PI / 6.0, M_PI / 4.0)); // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0, 0; Vector3 measuredOmega; measuredOmega << M_PI / 100, 0, 0; double deltaT = 1.0; - AHRSFactor::PreintegratedMeasurements pre_int_data(bias, Matrix3::Zero()); - pre_int_data.integrateMeasurement(measuredOmega, deltaT); + AHRSFactor::PreintegratedMeasurements pim(bias, Z_3x3); + pim.integrateMeasurement(measuredOmega, deltaT); // Create factor - AHRSFactor factor(X(1), X(2), B(1), pre_int_data, omegaCoriolis, boost::none); + AHRSFactor factor(X(1), X(2), B(1), pim, kZeroOmegaCoriolis, boost::none); Vector3 errorActual = factor.evaluateError(x1, x2, bias); @@ -182,18 +184,16 @@ TEST(AHRSFactor, ErrorWithBiases) { Rot3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0 + M_PI / 10.0))); // Measurements - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0.0, 0.0; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0 + 0.3; double deltaT = 1.0; - AHRSFactor::PreintegratedMeasurements pre_int_data(Vector3(0,0,0), - Matrix3::Zero()); - pre_int_data.integrateMeasurement(measuredOmega, deltaT); + AHRSFactor::PreintegratedMeasurements pim(Vector3(0,0,0), + Z_3x3); + pim.integrateMeasurement(measuredOmega, deltaT); // Create factor - AHRSFactor factor(X(1), X(2), B(1), pre_int_data, omegaCoriolis); + AHRSFactor factor(X(1), X(2), B(1), pim, kZeroOmegaCoriolis); Vector errorActual = factor.evaluateError(x1, x2, bias); @@ -269,7 +269,7 @@ TEST( AHRSFactor, PartialDerivativeLogmap ) { const Vector3 x = thetahat; // parametrization of so(3) const Matrix3 X = skewSymmetric(x); // element of Lie algebra so(3): X = x^ double normx = norm_2(x); - const Matrix3 actualDelFdeltheta = Matrix3::Identity() + 0.5 * X + const Matrix3 actualDelFdeltheta = I_3x3 + 0.5 * X + (1 / (normx * normx) - (1 + cos(normx)) / (2 * normx * sin(normx))) * X * X; @@ -359,8 +359,6 @@ TEST( AHRSFactor, ErrorWithBiasesAndSensorBodyDisplacement ) { Rot3 x2(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0 + M_PI / 10.0))); // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; Vector3 omegaCoriolis; omegaCoriolis << 0, 0.1, 0.1; Vector3 measuredOmega; @@ -370,13 +368,15 @@ TEST( AHRSFactor, ErrorWithBiasesAndSensorBodyDisplacement ) { const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), Point3(1, 0, 0)); - AHRSFactor::PreintegratedMeasurements pre_int_data(Vector3::Zero(), - Matrix3::Zero()); + AHRSFactor::PreintegratedMeasurements pim(Vector3::Zero(), kMeasuredAccCovariance); - pre_int_data.integrateMeasurement(measuredOmega, deltaT); + pim.integrateMeasurement(measuredOmega, deltaT); + + // Check preintegrated covariance + EXPECT(assert_equal(kMeasuredAccCovariance, pim.preintMeasCov())); // Create factor - AHRSFactor factor(X(1), X(2), B(1), pre_int_data, omegaCoriolis); + AHRSFactor factor(X(1), X(2), B(1), pim, omegaCoriolis); // Expected Jacobians Matrix H1e = numericalDerivative11( @@ -407,33 +407,34 @@ TEST (AHRSFactor, predictTest) { Vector3 bias(0,0,0); // Measurements - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0.0, 0.0; Vector3 measuredOmega; measuredOmega << 0, 0, M_PI / 10.0; - double deltaT = 0.001; - AHRSFactor::PreintegratedMeasurements pre_int_data(bias, Matrix3::Zero()); + double deltaT = 0.2; + AHRSFactor::PreintegratedMeasurements pim(bias, kMeasuredAccCovariance); for (int i = 0; i < 1000; ++i) { - pre_int_data.integrateMeasurement(measuredOmega, deltaT); + pim.integrateMeasurement(measuredOmega, deltaT); } - AHRSFactor factor(X(1), X(2), B(1), pre_int_data, omegaCoriolis); + // Check preintegrated covariance + Matrix expectedMeasCov(3,3); + expectedMeasCov = 200*kMeasuredAccCovariance; + EXPECT(assert_equal(expectedMeasCov, pim.preintMeasCov())); + + AHRSFactor factor(X(1), X(2), B(1), pim, kZeroOmegaCoriolis); // Predict Rot3 x; - Rot3 expectedRot = Rot3().ypr(M_PI / 10, 0, 0); - Rot3 actualRot = factor.predict(x, bias, pre_int_data, omegaCoriolis); + Rot3 expectedRot = Rot3().ypr(20*M_PI, 0, 0); + Rot3 actualRot = factor.predict(x, bias, pim, kZeroOmegaCoriolis); EXPECT(assert_equal(expectedRot, actualRot, 1e-6)); // AHRSFactor::PreintegratedMeasurements::predict Matrix expectedH = numericalDerivative11( boost::bind(&AHRSFactor::PreintegratedMeasurements::predict, - &pre_int_data, _1, boost::none), bias); + &pim, _1, boost::none), bias); // Actual Jacobians Matrix H; - (void) pre_int_data.predict(bias,H); + (void) pim.predict(bias,H); EXPECT(assert_equal(expectedH, H)); } //****************************************************************************** @@ -448,12 +449,7 @@ TEST (AHRSFactor, graphTest) { // PreIntegrator Vector3 biasHat(0, 0, 0); - Vector3 gravity; - gravity << 0, 0, 9.81; - Vector3 omegaCoriolis; - omegaCoriolis << 0, 0, 0; - AHRSFactor::PreintegratedMeasurements pre_int_data(biasHat, - Matrix3::Identity()); + AHRSFactor::PreintegratedMeasurements pim(biasHat, kMeasuredAccCovariance); // Pre-integrate measurements Vector3 measuredOmega(0, M_PI / 20, 0); @@ -461,15 +457,15 @@ TEST (AHRSFactor, graphTest) { // Create Factor noiseModel::Base::shared_ptr model = // - noiseModel::Gaussian::Covariance(pre_int_data.preintMeasCov()); + noiseModel::Gaussian::Covariance(pim.preintMeasCov()); NonlinearFactorGraph graph; Values values; for (size_t i = 0; i < 5; ++i) { - pre_int_data.integrateMeasurement(measuredOmega, deltaT); + pim.integrateMeasurement(measuredOmega, deltaT); } - // pre_int_data.print("Pre integrated measurementes"); - AHRSFactor factor(X(1), X(2), B(1), pre_int_data, omegaCoriolis); + // pim.print("Pre integrated measurementes"); + AHRSFactor factor(X(1), X(2), B(1), pim, kZeroOmegaCoriolis); values.insert(X(1), x1); values.insert(X(2), x2); values.insert(B(1), bias); From d0467c53dd9b83fee744929abec46224a9832fdb Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 16:15:48 -0700 Subject: [PATCH 093/142] Use PreintegratedRotation::integrateMeasurement --- gtsam/navigation/AHRSFactor.cpp | 28 ++---------- gtsam/navigation/ImuFactor.cpp | 1 + gtsam/navigation/PreintegrationBase.cpp | 61 ++++++++++--------------- gtsam/navigation/PreintegrationBase.h | 6 +-- 4 files changed, 28 insertions(+), 68 deletions(-) diff --git a/gtsam/navigation/AHRSFactor.cpp b/gtsam/navigation/AHRSFactor.cpp index 85a95c35a..0d7e05515 100644 --- a/gtsam/navigation/AHRSFactor.cpp +++ b/gtsam/navigation/AHRSFactor.cpp @@ -50,31 +50,9 @@ void PreintegratedAhrsMeasurements::resetIntegration() { void PreintegratedAhrsMeasurements::integrateMeasurement( const Vector3& measuredOmega, double deltaT) { - // First we compensate the measurements for the bias - Vector3 correctedOmega = measuredOmega - biasHat_; - - // Then compensate for sensor-body displacement: we express the quantities - // (originally in the IMU frame) into the body frame - if (p().body_P_sensor) { - Matrix3 body_R_sensor = p().body_P_sensor->rotation().matrix(); - // rotation rate vector in the body frame - correctedOmega = body_R_sensor * correctedOmega; - } - - // rotation vector describing rotation increment computed from the - // current rotation rate measurement - const Vector3 theta_incr = correctedOmega * deltaT; - Matrix3 D_incrR_integratedOmega; - const Rot3 incrR = Rot3::Expmap(theta_incr, D_incrR_integratedOmega); // expensive !! - - // Update Jacobian - const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - D_incrR_integratedOmega * deltaT; - - // Update rotation and deltaTij. - Matrix3 Fr; // Jacobian of the update - deltaRij_ = deltaRij_.compose(incrR, Fr); - deltaTij_ += deltaT; + Matrix3 D_incrR_integratedOmega, Fr; + PreintegratedRotation::integrateMeasurement(measuredOmega, + biasHat_, deltaT, &D_incrR_integratedOmega, &Fr); // first order uncertainty propagation // the deltaT allows to pass from continuous time noise to discrete time noise diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 6745a86fc..bdad84e6b 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -48,6 +48,7 @@ void PreintegratedImuMeasurements::resetIntegration() { preintMeasCov_.setZero(); } +//------------------------------------------------------------------------------ // sugar for derivative blocks #define D_R_R(H) (H)->block<3,3>(0,0) #define D_R_t(H) (H)->block<3,3>(0,3) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 13baa973d..7a4e40f16 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -63,27 +63,40 @@ void PreintegrationBase::updatePreintegratedMeasurements( const Vector3& measuredAcc, const Vector3& measuredOmega, const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F) { - Matrix3 D_Rij_incrR; - // NOTE: order is important here because each update uses old values, e.g., velocity and position updates are based on previous rotation estimate. // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). - Vector3 correctedAcc, correctedOmega; - boost::tie(correctedAcc, correctedOmega) = - correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega); + // Correct for bias in the sensor frame + Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - // rotation vector describing rotation increment computed from the current rotation rate measurement - const Vector3 integratedOmega = correctedOmega * deltaT; - const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // rotation increment computed from the current rotation rate measurement + // Compensate for sensor-body displacement if needed: we express the quantities + // (originally in the IMU frame) into the body frame + // Equations below assume the "body" frame is the CG + if (p().body_P_sensor) { + // Correct omega: slight duplication as this is also done in integrateMeasurement below + Matrix3 bRs = p().body_P_sensor->rotation().matrix(); + Vector3 s_correctedOmega = biasHat_.correctGyroscope(measuredOmega); + Vector3 b_correctedOmega = bRs * s_correctedOmega; // rotation rate vector in the body frame + + // Correct acceleration + Vector3 b_arm = p().body_P_sensor->translation().vector(); + Vector3 b_velocity_bs = b_correctedOmega.cross(b_arm); // magnitude: omega * arm + // Subtract out the the centripetal acceleration from the measured one + // to get linear acceleration vector in the body frame: + correctedAcc = bRs * correctedAcc - b_correctedOmega.cross(b_velocity_bs); + } // Calculate acceleration in *current* i frame, i.e., before rotation update below Matrix3 D_acc_R; const Matrix3 dRij = deltaRij_.matrix(); // expensive const Vector3 i_acc = deltaRij_.rotate(correctedAcc, F ? &D_acc_R : 0); + const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * delRdelBiasOmega_; + + Matrix3 D_Rij_incrR; + PreintegratedRotation::integrateMeasurement(measuredOmega, + biasHat_.gyroscope(), deltaT, D_incrR_integratedOmega, &D_Rij_incrR); double dt22 = 0.5 * deltaT * deltaT; - deltaTij_ += deltaT; - deltaRij_ = deltaRij_.compose(incrR, F ? &D_Rij_incrR : 0); deltaPij_ += dt22 * i_acc + deltaT * deltaVij_; deltaVij_ += deltaT * i_acc; @@ -92,38 +105,10 @@ void PreintegrationBase::updatePreintegratedMeasurements( dt22 * D_acc_R, I_3x3, I_3x3 * deltaT, // pos deltaT * D_acc_R, Z_3x3, I_3x3; // vel - const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * delRdelBiasOmega_; delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - dt22 * dRij; delPdelBiasOmega_ += deltaT * delVdelBiasOmega_ + dt22 * temp; delVdelBiasAcc_ += -dRij * deltaT; delVdelBiasOmega_ += temp * deltaT; - const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - - *D_incrR_integratedOmega * deltaT; -} - -std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( - const Vector3& measuredAcc, const Vector3& measuredOmega) const { - // Correct for bias in the sensor frame - Vector3 s_correctedAcc, s_correctedOmega; - s_correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - s_correctedOmega = biasHat_.correctGyroscope(measuredOmega); - - // Compensate for sensor-body displacement if needed: we express the quantities - // (originally in the IMU frame) into the body frame - // Equations below assume the "body" frame is the CG - if (p().body_P_sensor) { - Matrix3 bRs = p().body_P_sensor->rotation().matrix(); - Vector3 b_arm = p().body_P_sensor->translation().vector(); - Vector3 b_correctedOmega = bRs * s_correctedOmega; // rotation rate vector in the body frame - Vector3 b_velocity_bs = b_correctedOmega.cross(b_arm); // magnitude: omega * arm - // Subtract out the the centripetal acceleration from the measured one - // to get linear acceleration vector in the body frame: - Vector3 b_correctedAcc = bRs * s_correctedAcc - - b_correctedOmega.cross(b_velocity_bs); - return std::make_pair(b_correctedAcc, b_correctedOmega); - } else - return std::make_pair(s_correctedAcc, s_correctedOmega); } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index bcb38a8f9..f3d8a3ff2 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -174,11 +174,7 @@ public: const Vector3& measuredOmega, const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F); - std::pair - correctMeasurementsByBiasAndSensorPose(const Vector3& measuredAcc, - const Vector3& measuredOmega) const; - - /// Given the estimate of the bias, return a NavState tangent vector + /// Given the estimate of the bias, return a NavState tangent vector /// summarizing the preintegrated IMU measurements so far Vector9 biasCorrectedDelta(const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H = boost::none) const; From e5ace26d5f44f07d13c29d96b08930a286755933 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 21:04:11 -0700 Subject: [PATCH 094/142] Split method in const and non-const part --- gtsam/navigation/PreintegratedRotation.h | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index df0953945..9bb288b11 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -108,9 +108,11 @@ class PreintegratedRotation { /// @} - void integrateMeasurement(const Vector3& measuredOmega, - const Vector3& biasHat, double deltaT, Matrix3* D_incrR_integratedOmega, - Matrix3* Fr) { + /// Take the gyro measurement, correct it using the (constant) bias estimate + /// and possibly the sensor pose, and then integrate it forward in time to yield + /// an incremental rotation. + Rot3 incrementalRotation(const Vector3& measuredOmega, const Vector3& biasHat, + double deltaT, OptionalJacobian<3, 3> D_incrR_integratedOmega) const { // First we compensate the measurements for the bias Vector3 correctedOmega = measuredOmega - biasHat; @@ -125,12 +127,22 @@ class PreintegratedRotation { // rotation vector describing rotation increment computed from the // current rotation rate measurement - const Vector3 theta_incr = correctedOmega * deltaT; - const Rot3 incrR = Rot3::Expmap(theta_incr, D_incrR_integratedOmega); // expensive !! + const Vector3 integratedOmega = correctedOmega * deltaT; + return Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! + } + + /// Calculate an incremental rotation given the gyro measurement and a time interval, + /// and update both deltaTij_ and deltaRij_. + void integrateMeasurement(const Vector3& measuredOmega, + const Vector3& biasHat, double deltaT, Matrix3* D_incrR_integratedOmega, + Matrix3* F) { + + const Rot3 incrR = incrementalRotation(measuredOmega, biasHat, deltaT, + D_incrR_integratedOmega); // Update deltaTij and rotation deltaTij_ += deltaT; - deltaRij_ = deltaRij_.compose(incrR, Fr); + deltaRij_ = deltaRij_.compose(incrR, F); // Update Jacobian const Matrix3 incrRt = incrR.transpose(); From 7149b8ee425c223e9022862943fc4bb47003a91d Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 21:21:03 -0700 Subject: [PATCH 095/142] Avoid duplicate calculation of D_acc_R --- gtsam/navigation/PreintegrationBase.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 7a4e40f16..bf702c4a0 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -89,8 +89,8 @@ void PreintegrationBase::updatePreintegratedMeasurements( // Calculate acceleration in *current* i frame, i.e., before rotation update below Matrix3 D_acc_R; const Matrix3 dRij = deltaRij_.matrix(); // expensive - const Vector3 i_acc = deltaRij_.rotate(correctedAcc, F ? &D_acc_R : 0); - const Matrix3 temp = -dRij * skewSymmetric(correctedAcc) * delRdelBiasOmega_; + const Vector3 i_acc = deltaRij_.rotate(correctedAcc, D_acc_R); + const Matrix3 D_acc_biasOmega = D_acc_R * delRdelBiasOmega_; Matrix3 D_Rij_incrR; PreintegratedRotation::integrateMeasurement(measuredOmega, @@ -106,9 +106,9 @@ void PreintegrationBase::updatePreintegratedMeasurements( deltaT * D_acc_R, Z_3x3, I_3x3; // vel delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - dt22 * dRij; - delPdelBiasOmega_ += deltaT * delVdelBiasOmega_ + dt22 * temp; + delPdelBiasOmega_ += deltaT * delVdelBiasOmega_ + dt22 * D_acc_biasOmega; delVdelBiasAcc_ += -dRij * deltaT; - delVdelBiasOmega_ += temp * deltaT; + delVdelBiasOmega_ += D_acc_biasOmega * deltaT; } //------------------------------------------------------------------------------ From 9c35c931f65f92547ff3f0e1b2252261af67f5ed Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 29 Jul 2015 21:53:18 -0700 Subject: [PATCH 096/142] Modernized test --- gtsam/navigation/tests/testImuFactor.cpp | 70 ++++++++++++------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 7e540b793..dcd2a0bfc 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -54,7 +54,7 @@ Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, // Auxiliary functions to test Jacobians F and G used for // covariance propagation during preintegration /* ************************************************************************* */ -Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, +Vector6 updatePreintegratedPosVel(const Vector3 deltaPij_old, const Vector3& deltaVij_old, const Rot3& deltaRij_old, const Vector3& correctedAcc, const Vector3& correctedOmega, const double deltaT) { @@ -64,7 +64,7 @@ Vector updatePreintegratedPosVel(const Vector3 deltaPij_old, deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; Vector3 deltaVij_new = deltaVij_old + temp; - Vector result(6); + Vector6 result; result << deltaPij_new, deltaVij_new; return result; } @@ -576,19 +576,22 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases // Measurements + const Vector3 measuredAcc(0.1, 0.0, 0.0); + const Vector3 measuredOmega(M_PI / 100.0, 0.0, 0.0); + const double deltaT = 0.01; list measuredAccs, measuredOmegas; list deltaTs; - measuredAccs.push_back(Vector3(0.1, 0.0, 0.0)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, 0.0, 0.0)); - deltaTs.push_back(0.01); - measuredAccs.push_back(Vector3(0.1, 0.0, 0.0)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, 0.0, 0.0)); - deltaTs.push_back(0.01); + measuredAccs.push_back(measuredAcc); + measuredOmegas.push_back(measuredOmega); + deltaTs.push_back(deltaT); + measuredAccs.push_back(measuredAcc); + measuredOmegas.push_back(measuredOmega); + deltaTs.push_back(deltaT); for (int i = 1; i < 100; i++) { measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); measuredOmegas.push_back( Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); - deltaTs.push_back(0.01); + deltaTs.push_back(deltaT); } // Actual preintegrated values PreintegratedImuMeasurements preintegrated = @@ -597,9 +600,6 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians - const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); - const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); - const double newDeltaT = 0.01; const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement @@ -607,36 +607,36 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { Matrix oldPreintCovariance = preintegrated.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, Factual, Gactual); + preintegrated.integrateMeasurement(measuredAcc, measuredOmega, deltaT, + Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F ////////////////////////////////////////////////////////////////////////////////////////////// // Compute expected f_pos_vel wrt positions - Matrix dfpv_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaPij_old); + boost::function f = + boost::bind(&updatePreintegratedPosVel, _1, _2, _3, measuredAcc, + measuredOmega, deltaT); + Matrix63 dfpv_dpos = numericalDerivative31(f, deltaPij_old, deltaVij_old, + deltaRij_old); // Compute expected f_pos_vel wrt velocities - Matrix dfpv_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaVij_old); + Matrix63 dfpv_dvel = numericalDerivative32(f, deltaPij_old, deltaVij_old, + deltaRij_old); // Compute expected f_pos_vel wrt angles - Matrix dfpv_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, - newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaRij_old); + Matrix63 dfpv_dangle = numericalDerivative33(f, deltaPij_old, deltaVij_old, + deltaRij_old); // Compute expected f_rot wrt angles - Matrix dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), + Matrix3 dfr_dangle = numericalDerivative11( + boost::bind(&updatePreintegratedRot, _1, measuredOmega, deltaT), deltaRij_old); Matrix Fexpected(9, 9); Fexpected << // dfr_dangle, Z_3x3, Z_3x3, // - dfpv_dangle, dfpv_dpos, dfpv_dvel; + dfpv_dangle, dfpv_dpos, dfpv_dvel; EXPECT(assert_equal(Fexpected, Factual)); @@ -645,27 +645,27 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { ////////////////////////////////////////////////////////////////////////////////////////////// // Compute jacobian wrt integration noise Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3 * newDeltaT, Z_3x3; + dgpv_dintNoise << I_3x3 * deltaT, Z_3x3; // Compute jacobian wrt acc noise Matrix dgpv_daccNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, newMeasuredOmega, newDeltaT), newMeasuredAcc); + deltaRij_old, _1, measuredOmega, deltaT), measuredAcc); // Compute expected F wrt gyro noise Matrix dgpv_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, newMeasuredAcc, _1, newDeltaT), newMeasuredOmega); + deltaRij_old, measuredAcc, _1, deltaT), measuredOmega); // Compute expected f_rot wrt gyro noise Matrix dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), - newMeasuredOmega); + boost::bind(&updatePreintegratedRot, deltaRij_old, _1, deltaT), + measuredOmega); Matrix Gexpected(9, 9); Gexpected << // dgr_dangle, Z_3x3, Z_3x3, // - dgpv_domegaNoise, dgpv_dintNoise, dgpv_daccNoise; + dgpv_domegaNoise, dgpv_dintNoise, dgpv_daccNoise; EXPECT(assert_equal(Gexpected, Gactual)); @@ -673,12 +673,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { Matrix9 measurementCovariance; measurementCovariance << // omegaNoiseVar * I_3x3, Z_3x3, Z_3x3, // - Z_3x3, intNoiseVar * I_3x3, Z_3x3, // - Z_3x3, Z_3x3, accNoiseVar * I_3x3; + Z_3x3, intNoiseVar * I_3x3, Z_3x3, // + Z_3x3, Z_3x3, accNoiseVar * I_3x3; Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance * Factual.transpose() - + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); + + (1 / deltaT) * Gactual * measurementCovariance * Gactual.transpose(); Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); From 13f8935c52302785743b0f76d5898d632e28d0f0 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 30 Jul 2015 08:05:39 -0700 Subject: [PATCH 097/142] update prototype --- gtsam/navigation/NavState.cpp | 44 +++++++++++++++++++++++++ gtsam/navigation/NavState.h | 6 ++++ gtsam/navigation/tests/testNavState.cpp | 26 +++++++++++++-- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index c45b86450..9f21001b8 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -226,6 +226,50 @@ Matrix7 NavState::wedge(const Vector9& xi) { #define D_v_t(H) (H)->block<3,3>(6,3) #define D_v_v(H) (H)->block<3,3>(6,6) +//------------------------------------------------------------------------------ +NavState NavState::update(const Vector3& omega, const Vector3& acceleration, + const double deltaT, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, + OptionalJacobian<9, 3> G2) const { + + TIE(nRb, n_t, n_v, *this); + + // Calculate acceleration in *current* i frame, i.e., before rotation update below + Matrix3 D_acc_R; + const Matrix3 dRij = R(); // expensive in quaternion case + const Vector3 i_acc = nRb.rotate(acceleration, D_acc_R); + + // rotation vector describing rotation increment computed from the + // current rotation rate measurement + const Vector3 integratedOmega = omega * deltaT; + Matrix3 D_incrR_integratedOmega; + Rot3 incrR = Rot3::Expmap(integratedOmega, G1 ? &D_incrR_integratedOmega : 0); // expensive !! + + Matrix3 D_Rij_incrR; + double dt22 = 0.5 * deltaT * deltaT; + /// TODO(frank): use retract(deltaT*[omega, bRn*n_v * 0.5*deltaT*acceleration, acceleration]) + /// But before we do, figure out retract derivatives that are nicer than Lie-generated ones + NavState result(nRb.compose(incrR, D_Rij_incrR), + n_t + Point3(dt22 * i_acc + deltaT * n_v), n_v + deltaT * i_acc); + + // derivative wrt state + if (F) { + F->setIdentity(); + D_R_R(F) << D_Rij_incrR; + D_t_R(F) << dt22 * D_acc_R; + D_t_v(F) << I_3x3 * deltaT; + D_v_R(F) << deltaT * D_acc_R; + } + // derivative wrt omega + if (G1) { + *G1 << D_incrR_integratedOmega * deltaT, Z_3x3, Z_3x3; + } + // derivative wrt acceleration + if (G2) { + *G2 << Z_3x3, dt22 * dRij, dRij * deltaT; + } + return result; +} + //------------------------------------------------------------------------------ Vector9 NavState::coriolis(double dt, const Vector3& omega, bool secondOrder, OptionalJacobian<9, 9> H) const { diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 996ae763b..faf6a9c9f 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -209,6 +209,12 @@ public: /// @name Dynamics /// @{ + /// Integrate forward in time given angular velocity and acceleration + /// Uses second order integration for position, returns derivatives except deltaT. + NavState update(const Vector3& omega, const Vector3& acceleration, + const double deltaT, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, + OptionalJacobian<9, 3> G2) const; + /// Compute tangent space contribution due to Coriolis forces Vector9 coriolis(double dt, const Vector3& omega, bool secondOrder = false, OptionalJacobian<9, 9> H = boost::none) const; diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index 6732643e1..986b9aa13 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -139,7 +139,8 @@ TEST( NavState, Manifold ) { // Check localCoordinates derivatives boost::function local = - boost::bind(&NavState::localCoordinates, _1, _2, boost::none, boost::none); + boost::bind(&NavState::localCoordinates, _1, _2, boost::none, + boost::none); // from state1 to state2 kState1.localCoordinates(state2, aH1, aH2); EXPECT(assert_equal(numericalDerivative21(local, kState1, state2), aH1)); @@ -177,6 +178,27 @@ TEST( NavState, Lie ) { EXPECT(assert_equal(xi, -state3.logmap(state2))); } +/* ************************************************************************* */ +TEST(NavState, Update) { + const Vector3 omega(M_PI / 100.0, 0.0, 0.0); + const Vector3 acc(0.1, 0.0, 0.0); + const double deltaT = 10; + Matrix9 aF; + Matrix93 aG1, aG2; + boost::function update = + boost::bind(&NavState::update, _1, _2, _3, deltaT, boost::none, + boost::none, boost::none); + Vector3 b_acc = kRotation * acc; + NavState expected(kRotation.expmap(deltaT * omega), + kPosition + Point3((kVelocity + b_acc * deltaT / 2) * deltaT), + kVelocity + b_acc * deltaT); + NavState actual = kState1.update(omega, acc, deltaT, aF, aG1, aG2); + EXPECT(assert_equal(expected, actual)); + EXPECT(assert_equal(numericalDerivative31(update, kState1, omega, acc), aF)); + EXPECT(assert_equal(numericalDerivative32(update, kState1, omega, acc), aG1)); + EXPECT(assert_equal(numericalDerivative33(update, kState1, omega, acc), aG2)); +} + /* ************************************************************************* */ static const double dt = 2.0; boost::function coriolis = boost::bind( @@ -207,7 +229,7 @@ TEST(NavState, Coriolis2) { } /* ************************************************************************* */ -TEST(NavState, correctPIM) { +TEST(NavState, CorrectPIM) { Vector9 xi; xi << 0.1, 0.1, 0.1, 0.2, 0.3, 0.4, -0.1, -0.2, -0.3; double dt = 0.5; From cefc441fba1ba8d6e0ea75d03e85b89f6bdc491e Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 30 Jul 2015 09:39:25 -0700 Subject: [PATCH 098/142] bodyVelocity and working update derivatives --- gtsam/navigation/NavState.cpp | 71 +++++++++++++------------ gtsam/navigation/NavState.h | 15 +++--- gtsam/navigation/tests/testNavState.cpp | 32 +++++++---- 3 files changed, 70 insertions(+), 48 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 9f21001b8..59f381659 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -55,6 +55,17 @@ const Vector3& NavState::velocity(OptionalJacobian<3, 9> H) const { return v_; } +//------------------------------------------------------------------------------ +Vector3 NavState::bodyVelocity(OptionalJacobian<3, 9> H) const { + const Rot3& nRb = R_; + const Vector3& n_v = v_; + Matrix3 D_bv_nRb; + Vector3 b_v = nRb.unrotate(n_v, H ? &D_bv_nRb : 0); + if (H) + *H << D_bv_nRb, Z_3x3, I_3x3; + return b_v; +} + //------------------------------------------------------------------------------ Matrix7 NavState::matrix() const { Matrix3 R = this->R(); @@ -228,44 +239,38 @@ Matrix7 NavState::wedge(const Vector9& xi) { //------------------------------------------------------------------------------ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, - const double deltaT, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, + const double dt, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { - TIE(nRb, n_t, n_v, *this); + Vector9 xi; + Matrix39 D_xiP_state; + double dt22 = 0.5 * dt * dt; + dR(xi) << dt * omega; + dP(xi) << dt * bodyVelocity(D_xiP_state) + dt22 * acceleration; + dV(xi) << dt * acceleration; - // Calculate acceleration in *current* i frame, i.e., before rotation update below - Matrix3 D_acc_R; - const Matrix3 dRij = R(); // expensive in quaternion case - const Vector3 i_acc = nRb.rotate(acceleration, D_acc_R); + Matrix9 D_result_xi; + NavState result = retract(xi, F, D_result_xi); - // rotation vector describing rotation increment computed from the - // current rotation rate measurement - const Vector3 integratedOmega = omega * deltaT; - Matrix3 D_incrR_integratedOmega; - Rot3 incrR = Rot3::Expmap(integratedOmega, G1 ? &D_incrR_integratedOmega : 0); // expensive !! - - Matrix3 D_Rij_incrR; - double dt22 = 0.5 * deltaT * deltaT; - /// TODO(frank): use retract(deltaT*[omega, bRn*n_v * 0.5*deltaT*acceleration, acceleration]) - /// But before we do, figure out retract derivatives that are nicer than Lie-generated ones - NavState result(nRb.compose(incrR, D_Rij_incrR), - n_t + Point3(dt22 * i_acc + deltaT * n_v), n_v + deltaT * i_acc); - - // derivative wrt state + // Derivative wrt state is computed by retract directly + // However, as xi also depends on n_v, we need to add that contribution if (F) { - F->setIdentity(); - D_R_R(F) << D_Rij_incrR; - D_t_R(F) << dt22 * D_acc_R; - D_t_v(F) << I_3x3 * deltaT; - D_v_R(F) << deltaT * D_acc_R; + Matrix9 D_xi_state; + D_xi_state.setZero(); + D_xi_state.middleRows<3>(3) = dt * D_xiP_state; + *F += D_result_xi * D_xi_state; } // derivative wrt omega if (G1) { - *G1 << D_incrR_integratedOmega * deltaT, Z_3x3, Z_3x3; + Matrix93 D_xi_omega; + D_xi_omega << dt * I_3x3, Z_3x3, Z_3x3; + *G1 = D_result_xi * D_xi_omega; } // derivative wrt acceleration if (G2) { - *G2 << Z_3x3, dt22 * dRij, dRij * deltaT; + Matrix93 D_xi_acceleration; + D_xi_acceleration << Z_3x3, dt22 * I_3x3, dt * I_3x3; + *G2 = D_result_xi * D_xi_acceleration; } return result; } @@ -313,16 +318,16 @@ Vector9 NavState::correctPIM(const Vector9& pim, double dt, const Velocity3& n_v = v_; // derivative is Ri ! const double dt2 = dt * dt; - Vector9 delta; + Vector9 xi; Matrix3 D_dP_Ri1, D_dP_Ri2, D_dP_nv, D_dV_Ri; - dR(delta) = dR(pim); - dP(delta) = dP(pim) + dR(xi) = dR(pim); + dP(xi) = dP(pim) + dt * nRb.unrotate(n_v, H1 ? &D_dP_Ri1 : 0, H2 ? &D_dP_nv : 0) + (0.5 * dt2) * nRb.unrotate(n_gravity, H1 ? &D_dP_Ri2 : 0); - dV(delta) = dV(pim) + dt * nRb.unrotate(n_gravity, H1 ? &D_dV_Ri : 0); + dV(xi) = dV(pim) + dt * nRb.unrotate(n_gravity, H1 ? &D_dV_Ri : 0); if (omegaCoriolis) { - delta += coriolis(dt, *omegaCoriolis, use2ndOrderCoriolis, H1); + xi += coriolis(dt, *omegaCoriolis, use2ndOrderCoriolis, H1); } if (H1 || H2) { @@ -340,7 +345,7 @@ Vector9 NavState::correctPIM(const Vector9& pim, double dt, } } - return delta; + return xi; } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index faf6a9c9f..f1e850037 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -100,6 +100,10 @@ public: Matrix3 R() const { return R_.matrix(); } + /// Return quaternion. Induces computation in matrix mode + Quaternion quaternion() const { + return R_.toQuaternion(); + } /// Return position as Vector3 Vector3 t() const { return t_.vector(); @@ -108,10 +112,9 @@ public: const Vector3& v() const { return v_; } - /// Return quaternion. Induces computation in matrix mode - Quaternion quaternion() const { - return R_.toQuaternion(); - } + // Return velocity in body frame + Velocity3 bodyVelocity(OptionalJacobian<3, 9> H) const; + /// Return matrix group representation, in MATLAB notation: /// nTb = [nRb 0 n_t; 0 nRb n_v; 0 0 1] /// With this embedding in GL(3), matrix product agrees with compose @@ -210,9 +213,9 @@ public: /// @{ /// Integrate forward in time given angular velocity and acceleration - /// Uses second order integration for position, returns derivatives except deltaT. + /// Uses second order integration for position, returns derivatives except dt. NavState update(const Vector3& omega, const Vector3& acceleration, - const double deltaT, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, + const double dt, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const; /// Compute tangent space contribution due to Coriolis forces diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index 986b9aa13..a885936aa 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -24,12 +24,12 @@ using namespace std; using namespace gtsam; -static const Rot3 kRotation = Rot3::RzRyRx(0.1, 0.2, 0.3); +static const Rot3 kAttitude = Rot3::RzRyRx(0.1, 0.2, 0.3); static const Point3 kPosition(1.0, 2.0, 3.0); -static const Pose3 kPose(kRotation, kPosition); +static const Pose3 kPose(kAttitude, kPosition); static const Velocity3 kVelocity(0.4, 0.5, 0.6); static const NavState kIdentity; -static const NavState kState1(kRotation, kPosition, kVelocity); +static const NavState kState1(kAttitude, kPosition, kVelocity); static const Vector3 kOmegaCoriolis(0.02, 0.03, 0.04); static const Vector3 kGravity(0, 0, 9.81); static const Vector9 kZeroXi = Vector9::Zero(); @@ -46,15 +46,17 @@ TEST(NavState, Constructor) { EXPECT(assert_equal(numericalDerivative21(construct, kPose, kVelocity), aH1)); EXPECT(assert_equal(numericalDerivative22(construct, kPose, kVelocity), aH2)); } + /* ************************************************************************* */ TEST( NavState, Attitude) { Matrix39 aH, eH; Rot3 actual = kState1.attitude(aH); - EXPECT(assert_equal(actual, kRotation)); + EXPECT(assert_equal(actual, kAttitude)); eH = numericalDerivative11( boost::bind(&NavState::attitude, _1, boost::none), kState1); EXPECT(assert_equal((Matrix )eH, aH)); } + /* ************************************************************************* */ TEST( NavState, Position) { Matrix39 aH, eH; @@ -64,6 +66,7 @@ TEST( NavState, Position) { boost::bind(&NavState::position, _1, boost::none), kState1); EXPECT(assert_equal((Matrix )eH, aH)); } + /* ************************************************************************* */ TEST( NavState, Velocity) { Matrix39 aH, eH; @@ -73,6 +76,17 @@ TEST( NavState, Velocity) { boost::bind(&NavState::velocity, _1, boost::none), kState1); EXPECT(assert_equal((Matrix )eH, aH)); } + +/* ************************************************************************* */ +TEST( NavState, BodyVelocity) { + Matrix39 aH, eH; + Velocity3 actual = kState1.bodyVelocity(aH); + EXPECT(assert_equal(actual, kAttitude.unrotate(kVelocity))); + eH = numericalDerivative11( + boost::bind(&NavState::bodyVelocity, _1, boost::none), kState1); + EXPECT(assert_equal((Matrix )eH, aH)); +} + /* ************************************************************************* */ TEST( NavState, MatrixGroup ) { // check roundtrip conversion to 7*7 matrix representation @@ -188,15 +202,15 @@ TEST(NavState, Update) { boost::function update = boost::bind(&NavState::update, _1, _2, _3, deltaT, boost::none, boost::none, boost::none); - Vector3 b_acc = kRotation * acc; - NavState expected(kRotation.expmap(deltaT * omega), + Vector3 b_acc = kAttitude * acc; + NavState expected(kAttitude.expmap(deltaT * omega), kPosition + Point3((kVelocity + b_acc * deltaT / 2) * deltaT), kVelocity + b_acc * deltaT); NavState actual = kState1.update(omega, acc, deltaT, aF, aG1, aG2); EXPECT(assert_equal(expected, actual)); - EXPECT(assert_equal(numericalDerivative31(update, kState1, omega, acc), aF)); - EXPECT(assert_equal(numericalDerivative32(update, kState1, omega, acc), aG1)); - EXPECT(assert_equal(numericalDerivative33(update, kState1, omega, acc), aG2)); + EXPECT(assert_equal(numericalDerivative31(update, kState1, omega, acc, 1e-7), aF, 1e-7)); + EXPECT(assert_equal(numericalDerivative32(update, kState1, omega, acc, 1e-7), aG1, 1e-7)); + EXPECT(assert_equal(numericalDerivative33(update, kState1, omega, acc, 1e-7), aG2, 1e-7)); } /* ************************************************************************* */ From 2ad50ab86ea8a681caefc73850e7cc4136db475c Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 30 Jul 2015 09:48:00 -0700 Subject: [PATCH 099/142] More efficient derivatives --- gtsam/navigation/NavState.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 59f381659..a955cc7dd 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -253,24 +253,17 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, NavState result = retract(xi, F, D_result_xi); // Derivative wrt state is computed by retract directly - // However, as xi also depends on n_v, we need to add that contribution + // However, as dP(xi) also depends on state, we need to add that contribution if (F) { - Matrix9 D_xi_state; - D_xi_state.setZero(); - D_xi_state.middleRows<3>(3) = dt * D_xiP_state; - *F += D_result_xi * D_xi_state; + F->middleRows<3>(3) += dt * D_t_t(F) * D_xiP_state; } // derivative wrt omega if (G1) { - Matrix93 D_xi_omega; - D_xi_omega << dt * I_3x3, Z_3x3, Z_3x3; - *G1 = D_result_xi * D_xi_omega; + *G1 = dt * D_result_xi.leftCols<3>(); } // derivative wrt acceleration if (G2) { - Matrix93 D_xi_acceleration; - D_xi_acceleration << Z_3x3, dt22 * I_3x3, dt * I_3x3; - *G2 = D_result_xi * D_xi_acceleration; + *G2 = dt22 * D_result_xi.middleCols<3>(3) + dt * D_result_xi.rightCols<3>(); } return result; } From 91eeede05a242222125e64623bf634e638810f66 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 30 Jul 2015 21:55:40 -0700 Subject: [PATCH 100/142] Better documentation, and conditional evaluation of derivatives --- gtsam/navigation/NavState.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index a955cc7dd..23a72a6bb 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -246,11 +246,11 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, Matrix39 D_xiP_state; double dt22 = 0.5 * dt * dt; dR(xi) << dt * omega; - dP(xi) << dt * bodyVelocity(D_xiP_state) + dt22 * acceleration; + dP(xi) << dt * bodyVelocity(F ? &D_xiP_state : 0) + dt22 * acceleration; dV(xi) << dt * acceleration; Matrix9 D_result_xi; - NavState result = retract(xi, F, D_result_xi); + NavState result = retract(xi, F, G1 || G2 ? &D_result_xi : 0); // Derivative wrt state is computed by retract directly // However, as dP(xi) also depends on state, we need to add that contribution @@ -259,11 +259,19 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, } // derivative wrt omega if (G1) { - *G1 = dt * D_result_xi.leftCols<3>(); + // D_result_dRxi = D_result_xi.leftCols<3>() + // D_dRxi_omega = dt * I_3x3 + // *G1 = D_result_omega = D_result_dRxi * D_dRxi_omega + *G1 = D_result_xi.leftCols<3>() * dt; } // derivative wrt acceleration if (G2) { - *G2 = dt22 * D_result_xi.middleCols<3>(3) + dt * D_result_xi.rightCols<3>(); + // D_result_dPxi = D_result_xi.middleCols<3>(3) + // D_dPxi_acc = dt22 * I_3x3 + // D_result_dVxi = D_result_xi.rightCols<3>() + // D_dVxi_acc = dt * I_3x3 + // *G2 = D_result_acc = D_result_dPxi * D_dPxi_acc + D_result_dVxi * D_dVxi_acc + *G2 = D_result_xi.middleCols<3>(3) * dt22 + D_result_xi.rightCols<3>() * dt; } return result; } From 3cdf8973d459b4f826c391dc6eecff891d6b40d0 Mon Sep 17 00:00:00 2001 From: dellaert Date: Thu, 30 Jul 2015 22:50:06 -0700 Subject: [PATCH 101/142] Monte Carlo analysis --- gtsam/navigation/tests/testImuFactor.cpp | 89 ++++++++++++++++++------ 1 file changed, 67 insertions(+), 22 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index dcd2a0bfc..ca01de3ec 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -16,15 +16,16 @@ */ #include -#include -#include -#include #include #include +#include +#include +#include +#include #include #include -#include +#include #include #include @@ -648,17 +649,17 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { dgpv_dintNoise << I_3x3 * deltaT, Z_3x3; // Compute jacobian wrt acc noise - Matrix dgpv_daccNoise = numericalDerivative11( + Matrix63 dgpv_daccNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, _1, measuredOmega, deltaT), measuredAcc); // Compute expected F wrt gyro noise - Matrix dgpv_domegaNoise = numericalDerivative11( + Matrix63 dgpv_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, deltaRij_old, measuredAcc, _1, deltaT), measuredOmega); // Compute expected f_rot wrt gyro noise - Matrix dgr_dangle = numericalDerivative11( + Matrix3 dgr_dangle = numericalDerivative11( boost::bind(&updatePreintegratedRot, deltaRij_old, _1, deltaT), measuredOmega); @@ -798,6 +799,40 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); } +/* ************************************************************************* */ +Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, + const NavState& state, const imuBias::ConstantBias& bias, double dt, + const Pose3& body_P_sensor, const Vector3& measuredAcc, + const Vector3& measuredOmega, size_t N = 1000) { + // Get mean prediction from "ground truth" measurements + PreintegratedImuMeasurements pim1 = pim; + pim1.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); + NavState mean = pim1.predict(state, bias); + + // Do a Monte Carlo analysis to determine empirical density on the predicted state + Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar))); + Sampler sampleOmegaNoise(Vector3::Constant(sqrt(omegaNoiseVar))); + Matrix samples(9, N); + Vector9 sum = Vector9::Zero(); + for (size_t i = 0; i < N; i++) { + PreintegratedImuMeasurements pim2 = pim; + Vector3 perturbedAcc = measuredAcc + sampleAccelerationNoise.sample(); + Vector3 perturbedOmega = measuredOmega + sampleOmegaNoise.sample(); + pim2.integrateMeasurement(perturbedAcc, perturbedOmega, dt, body_P_sensor); + NavState prediction = pim2.predict(state, bias); + samples.col(i) = mean.localCoordinates(prediction); + sum += samples.col(i); + } + Vector9 sampleMean = sum / N; + Matrix9 Q; + Q.setZero(); + for (size_t i = 0; i < N; i++) { + Vector9 xi = samples.col(i) - sampleMean; + Q += xi * xi.transpose() / (N - 1); + } + return Q; +} + /* ************************************************************************* */ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) @@ -812,17 +847,18 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { measuredOmega << 0, 0, M_PI / 10.0 + 0.3; Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown) + Vector3(0.2, 0.0, 0.0); - double deltaT = 1.0; + double dt = 1.0; - const Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.10, 0.10)), - Point3(1, 0, 0)); + Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.1, 0.1)), Point3(1, 0, 0)); + imuBias::ConstantBias biasHat(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); - PreintegratedImuMeasurements pim( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), - kMeasuredAccCovariance, kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, true); + // Get mean prediction from "ground truth" measurements + PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, + kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); + Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, dt, body_P_sensor, + measuredAcc, measuredOmega, 1000); + cout << Q << endl; - pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT, body_P_sensor); Matrix expected(9,9); expected << @@ -835,6 +871,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, // 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, // 0.0, 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01; + pim.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-6)); // Create factor @@ -943,20 +980,28 @@ TEST(ImuFactor, PredictRotation) { /* ************************************************************************* */ TEST(ImuFactor, PredictArbitrary) { - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); // Biases (acc, rot) + imuBias::ConstantBias biasHat(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); // Measurements Vector3 measuredOmega(M_PI / 10, M_PI / 10, M_PI / 10); Vector3 measuredAcc(0.1, 0.2, -9.81); - double deltaT = 0.001; + double dt = 0.001; ImuFactor::PreintegratedMeasurements pim( - imuBias::ConstantBias(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)), + biasHat, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); + Pose3 x1; + Vector3 v1 = Vector3(0, 0, 0); + Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, dt, Pose3(), + measuredAcc, measuredOmega, 1000); + cout << Q << endl; - for (int i = 0; i < 1000; ++i) - pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); + pim.integrateMeasurement(measuredAcc, measuredOmega, dt); + cout << pim.preintMeasCov() << endl; + + for (int i = 0; i < 999; ++i) + pim.integrateMeasurement(measuredAcc, measuredOmega, dt); Matrix expected(9,9); expected << // @@ -976,9 +1021,9 @@ TEST(ImuFactor, PredictArbitrary) { kZeroOmegaCoriolis); // Predict - Pose3 x1, x2; - Vector3 v1 = Vector3(0, 0.0, 0.0); + Pose3 x2; Vector3 v2; + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); From c40ffa0174ade565b2044d8d86a4666bc888ecf2 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 06:37:39 -0700 Subject: [PATCH 102/142] Some more documentation --- gtsam/navigation/NavState.cpp | 26 +++++++++++++++----------- gtsam/navigation/NavState.h | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 23a72a6bb..3b2093d1a 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -244,13 +244,17 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, Vector9 xi; Matrix39 D_xiP_state; + Vector3 b_v = bodyVelocity(F ? &D_xiP_state : 0); double dt22 = 0.5 * dt * dt; + + // Integrate on tangent space. TODO(frank): coriolis? dR(xi) << dt * omega; - dP(xi) << dt * bodyVelocity(F ? &D_xiP_state : 0) + dt22 * acceleration; + dP(xi) << dt * b_v + dt22 * acceleration; dV(xi) << dt * acceleration; - Matrix9 D_result_xi; - NavState result = retract(xi, F, G1 || G2 ? &D_result_xi : 0); + // Bring back to manifold + Matrix9 D_newState_xi; + NavState newState = retract(xi, F, G1 || G2 ? &D_newState_xi : 0); // Derivative wrt state is computed by retract directly // However, as dP(xi) also depends on state, we need to add that contribution @@ -259,21 +263,21 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, } // derivative wrt omega if (G1) { - // D_result_dRxi = D_result_xi.leftCols<3>() + // D_newState_dRxi = D_newState_xi.leftCols<3>() // D_dRxi_omega = dt * I_3x3 - // *G1 = D_result_omega = D_result_dRxi * D_dRxi_omega - *G1 = D_result_xi.leftCols<3>() * dt; + // *G1 = D_newState_omega = D_newState_dRxi * D_dRxi_omega + *G1 = D_newState_xi.leftCols<3>() * dt; } // derivative wrt acceleration if (G2) { - // D_result_dPxi = D_result_xi.middleCols<3>(3) + // D_newState_dPxi = D_newState_xi.middleCols<3>(3) // D_dPxi_acc = dt22 * I_3x3 - // D_result_dVxi = D_result_xi.rightCols<3>() + // D_newState_dVxi = D_newState_xi.rightCols<3>() // D_dVxi_acc = dt * I_3x3 - // *G2 = D_result_acc = D_result_dPxi * D_dPxi_acc + D_result_dVxi * D_dVxi_acc - *G2 = D_result_xi.middleCols<3>(3) * dt22 + D_result_xi.rightCols<3>() * dt; + // *G2 = D_newState_acc = D_newState_dPxi * D_dPxi_acc + D_newState_dVxi * D_dVxi_acc + *G2 = D_newState_xi.middleCols<3>(3) * dt22 + D_newState_xi.rightCols<3>() * dt; } - return result; + return newState; } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index f1e850037..e33b5fc9f 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -113,7 +113,7 @@ public: return v_; } // Return velocity in body frame - Velocity3 bodyVelocity(OptionalJacobian<3, 9> H) const; + Velocity3 bodyVelocity(OptionalJacobian<3, 9> H = boost::none) const; /// Return matrix group representation, in MATLAB notation: /// nTb = [nRb 0 n_t; 0 nRb n_v; 0 0 1] From dd468ab4956d305ed9637e07a9014a95bcde2b40 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 11:31:01 -0700 Subject: [PATCH 103/142] Inlined rotation part --- gtsam/navigation/PreintegrationBase.cpp | 27 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index bf702c4a0..3a5d6d559 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -58,7 +58,6 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, && equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol); } -/// Update preintegrated measurements void PreintegrationBase::updatePreintegratedMeasurements( const Vector3& measuredAcc, const Vector3& measuredOmega, const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F) { @@ -68,6 +67,7 @@ void PreintegrationBase::updatePreintegratedMeasurements( // Correct for bias in the sensor frame Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); + Vector3 correctedOmega = biasHat_.correctGyroscope(measuredOmega); // Compensate for sensor-body displacement if needed: we express the quantities // (originally in the IMU frame) into the body frame @@ -75,15 +75,14 @@ void PreintegrationBase::updatePreintegratedMeasurements( if (p().body_P_sensor) { // Correct omega: slight duplication as this is also done in integrateMeasurement below Matrix3 bRs = p().body_P_sensor->rotation().matrix(); - Vector3 s_correctedOmega = biasHat_.correctGyroscope(measuredOmega); - Vector3 b_correctedOmega = bRs * s_correctedOmega; // rotation rate vector in the body frame + correctedOmega = bRs * correctedOmega; // rotation rate vector in the body frame // Correct acceleration Vector3 b_arm = p().body_P_sensor->translation().vector(); - Vector3 b_velocity_bs = b_correctedOmega.cross(b_arm); // magnitude: omega * arm + Vector3 b_velocity_bs = correctedOmega.cross(b_arm); // magnitude: omega * arm // Subtract out the the centripetal acceleration from the measured one // to get linear acceleration vector in the body frame: - correctedAcc = bRs * correctedAcc - b_correctedOmega.cross(b_velocity_bs); + correctedAcc = bRs * correctedAcc - correctedOmega.cross(b_velocity_bs); } // Calculate acceleration in *current* i frame, i.e., before rotation update below @@ -92,9 +91,23 @@ void PreintegrationBase::updatePreintegratedMeasurements( const Vector3 i_acc = deltaRij_.rotate(correctedAcc, D_acc_R); const Matrix3 D_acc_biasOmega = D_acc_R * delRdelBiasOmega_; +// NavState iTj(deltaRij_, deltaPij_, deltaVij_); +// iTj = iTj.update(); + + // rotation vector describing rotation increment computed from the + // current rotation rate measurement + const Vector3 integratedOmega = correctedOmega * deltaT; + const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! + + // Update deltaTij and rotation + deltaTij_ += deltaT; Matrix3 D_Rij_incrR; - PreintegratedRotation::integrateMeasurement(measuredOmega, - biasHat_.gyroscope(), deltaT, D_incrR_integratedOmega, &D_Rij_incrR); + deltaRij_ = deltaRij_.compose(incrR, D_Rij_incrR); + + // Update Jacobian + const Matrix3 incrRt = incrR.transpose(); + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ + - *D_incrR_integratedOmega * deltaT; double dt22 = 0.5 * deltaT * deltaT; deltaPij_ += dt22 * i_acc + deltaT * deltaVij_; From 628a4cc4cc5be871398136a5f8c3f3f732d5d1b9 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 11:43:53 -0700 Subject: [PATCH 104/142] Removed inheritance from PreintegratedRotation --- gtsam/navigation/ImuFactor.cpp | 4 +-- gtsam/navigation/PreintegrationBase.cpp | 16 ++++++++--- gtsam/navigation/PreintegrationBase.h | 24 ++++++++++++++--- gtsam/navigation/tests/testImuFactor.cpp | 34 ++++++++++-------------- 4 files changed, 49 insertions(+), 29 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index bdad84e6b..5a46c5da3 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -81,10 +81,10 @@ void PreintegratedImuMeasurements::integrateMeasurement( // NOTE 2: computation of G * (1/deltaT) * measurementCovariance * G.transpose() done block-wise, // as G and measurementCovariance are block-diagonal matrices preintMeasCov_ = F * preintMeasCov_ * F.transpose(); - D_t_t(&preintMeasCov_) += p().integrationCovariance * deltaT; - D_v_v(&preintMeasCov_) += dRij * p().accelerometerCovariance * dRij.transpose() * deltaT; D_R_R(&preintMeasCov_) += D_incrR_integratedOmega * p().gyroscopeCovariance * D_incrR_integratedOmega.transpose() * deltaT; + D_t_t(&preintMeasCov_) += p().integrationCovariance * deltaT; + D_v_v(&preintMeasCov_) += dRij * p().accelerometerCovariance * dRij.transpose() * deltaT; Matrix3 F_pos_noiseacc; F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 3a5d6d559..6353eb16c 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -28,7 +28,9 @@ namespace gtsam { /// Re-initialize PreintegratedMeasurements void PreintegrationBase::resetIntegration() { - PreintegratedRotation::resetIntegration(); + deltaTij_ = 0.0; + deltaRij_ = Rot3(); + delRdelBiasOmega_ = Z_3x3; deltaPij_ = Vector3::Zero(); deltaVij_ = Vector3::Zero(); delPdelBiasAcc_ = Z_3x3; @@ -39,7 +41,9 @@ void PreintegrationBase::resetIntegration() { /// Needed for testable void PreintegrationBase::print(const string& s) const { - PreintegratedRotation::print(s); + cout << s << endl; + cout << " deltaTij [" << deltaTij_ << "]" << endl; + cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << endl; cout << " deltaPij [ " << deltaPij_.transpose() << " ]" << endl; cout << " deltaVij [ " << deltaVij_.transpose() << " ]" << endl; biasHat_.print(" biasHat"); @@ -48,7 +52,9 @@ void PreintegrationBase::print(const string& s) const { /// Needed for testable bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) const { - return PreintegratedRotation::equals(other, tol) + return deltaRij_.equals(other.deltaRij_, tol) + && fabs(deltaTij_ - other.deltaTij_) < tol + && equal_with_abs_tol(delRdelBiasOmega_, other.delRdelBiasOmega_, tol) && biasHat_.equals(other.biasHat_, tol) && equal_with_abs_tol(deltaPij_, other.deltaPij_, tol) && equal_with_abs_tol(deltaVij_, other.deltaVij_, tol) @@ -130,7 +136,9 @@ Vector9 PreintegrationBase::biasCorrectedDelta( // Correct deltaRij, derivative is delRdelBiasOmega_ const imuBias::ConstantBias biasIncr = bias_i - biasHat_; Matrix3 D_deltaRij_bias; - Rot3 deltaRij = biascorrectedDeltaRij(biasIncr.gyroscope(), H ? &D_deltaRij_bias : 0); + const Vector3 biasInducedOmega = delRdelBiasOmega_ * biasIncr.gyroscope(); + const Rot3 deltaRij = deltaRij_.expmap(biasInducedOmega, boost::none, H ? &D_deltaRij_bias : 0); + if (H) D_deltaRij_bias *= delRdelBiasOmega_; Vector9 xi; Matrix3 D_dR_deltaRij; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index f3d8a3ff2..bf01db00a 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -51,7 +51,7 @@ struct PoseVelocityBias { * It includes the definitions of the preintegrated variables and the methods * to access, print, and compare them. */ -class PreintegrationBase: public PreintegratedRotation { +class PreintegrationBase { public: @@ -100,6 +100,13 @@ public: protected: + double deltaTij_; ///< Time interval from i to j + Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) + Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias + + /// Parameters + boost::shared_ptr p_; + /// Acceleration and gyro bias used for preintegration imuBias::ConstantBias biasHat_; @@ -124,7 +131,7 @@ public: */ PreintegrationBase(const boost::shared_ptr& p, const imuBias::ConstantBias& biasHat) : - PreintegratedRotation(p), biasHat_(biasHat) { + p_(p), biasHat_(biasHat) { resetIntegration(); } @@ -136,6 +143,15 @@ public: } /// getters + const double& deltaTij() const { + return deltaTij_; + } + const Rot3& deltaRij() const { + return deltaRij_; + } + const Matrix3& delRdelBiasOmega() const { + return delRdelBiasOmega_; + } const imuBias::ConstantBias& biasHat() const { return biasHat_; } @@ -202,8 +218,10 @@ private: friend class boost::serialization::access; template void serialize(ARCHIVE & ar, const unsigned int /*version*/) { - ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(PreintegratedRotation); ar & BOOST_SERIALIZATION_NVP(p_); + ar & BOOST_SERIALIZATION_NVP(deltaTij_); + ar & BOOST_SERIALIZATION_NVP(deltaRij_); + ar & BOOST_SERIALIZATION_NVP(delRdelBiasOmega_); ar & BOOST_SERIALIZATION_NVP(biasHat_); ar & BOOST_SERIALIZATION_NVP(deltaPij_); ar & BOOST_SERIALIZATION_NVP(deltaVij_); diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index ca01de3ec..176379da4 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -364,15 +364,7 @@ TEST(ImuFactor, ErrorAndJacobianWithBiases) { kMeasuredOmegaCovariance, kIntegrationErrorCovariance); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - // Make sure of biascorrectedDeltaRij with this example... - Matrix3 aH; - pim.biascorrectedDeltaRij(bias.gyroscope(), aH); - Matrix3 eH = numericalDerivative11( - boost::bind(&PreintegrationBase::biascorrectedDeltaRij, pim, _1, - boost::none), bias.gyroscope()); - EXPECT(assert_equal(eH, aH)); - - // ... and of biasCorrectedDelta + // Make sure of biasCorrectedDelta Matrix96 actualH; pim.biasCorrectedDelta(bias, actualH); Matrix expectedH = numericalDerivative11( @@ -803,11 +795,14 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, const NavState& state, const imuBias::ConstantBias& bias, double dt, const Pose3& body_P_sensor, const Vector3& measuredAcc, - const Vector3& measuredOmega, size_t N = 1000) { + const Vector3& measuredOmega, size_t N = 100) { // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim1 = pim; - pim1.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); + for (size_t k=0;k<10;k++) + pim1.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); NavState mean = pim1.predict(state, bias); + cout << "pim1.preintMeasCov():" << endl; + cout << pim1.preintMeasCov() << endl; // Do a Monte Carlo analysis to determine empirical density on the predicted state Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar))); @@ -818,18 +813,23 @@ Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, PreintegratedImuMeasurements pim2 = pim; Vector3 perturbedAcc = measuredAcc + sampleAccelerationNoise.sample(); Vector3 perturbedOmega = measuredOmega + sampleOmegaNoise.sample(); - pim2.integrateMeasurement(perturbedAcc, perturbedOmega, dt, body_P_sensor); + for (size_t k=0;k<10;k++) + pim2.integrateMeasurement(perturbedAcc, perturbedOmega, dt, body_P_sensor); NavState prediction = pim2.predict(state, bias); samples.col(i) = mean.localCoordinates(prediction); sum += samples.col(i); } Vector9 sampleMean = sum / N; + cout << ":" << endl; + cout << sampleMean << endl; Matrix9 Q; Q.setZero(); for (size_t i = 0; i < N; i++) { Vector9 xi = samples.col(i) - sampleMean; Q += xi * xi.transpose() / (N - 1); } + cout << "Q:" << endl; + cout << Q << endl; return Q; } @@ -857,8 +857,6 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, dt, body_P_sensor, measuredAcc, measuredOmega, 1000); - cout << Q << endl; - Matrix expected(9,9); expected << @@ -993,14 +991,10 @@ TEST(ImuFactor, PredictArbitrary) { kIntegrationErrorCovariance, true); Pose3 x1; Vector3 v1 = Vector3(0, 0, 0); - Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, dt, Pose3(), + Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, 0.1, Pose3(), measuredAcc, measuredOmega, 1000); - cout << Q << endl; - pim.integrateMeasurement(measuredAcc, measuredOmega, dt); - cout << pim.preintMeasCov() << endl; - - for (int i = 0; i < 999; ++i) + for (int i = 0; i < 1000; ++i) pim.integrateMeasurement(measuredAcc, measuredOmega, dt); Matrix expected(9,9); From e3d36da18817c9bcd9d0292d482b1a6175d76757 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 13:33:23 -0700 Subject: [PATCH 105/142] Switched argument order --- gtsam/navigation/NavState.cpp | 27 +++++++++++++------------ gtsam/navigation/NavState.h | 4 ++-- gtsam/navigation/tests/testNavState.cpp | 8 ++++---- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 3b2093d1a..2e7917688 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -238,7 +238,7 @@ Matrix7 NavState::wedge(const Vector9& xi) { #define D_v_v(H) (H)->block<3,3>(6,6) //------------------------------------------------------------------------------ -NavState NavState::update(const Vector3& omega, const Vector3& acceleration, +NavState NavState::update(const Vector3& b_acceleration, const Vector3& b_omega, const double dt, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { @@ -248,9 +248,9 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, double dt22 = 0.5 * dt * dt; // Integrate on tangent space. TODO(frank): coriolis? - dR(xi) << dt * omega; - dP(xi) << dt * b_v + dt22 * acceleration; - dV(xi) << dt * acceleration; + dR(xi) << dt * b_omega; + dP(xi) << dt * b_v + dt22 * b_acceleration; + dV(xi) << dt * b_acceleration; // Bring back to manifold Matrix9 D_newState_xi; @@ -261,21 +261,22 @@ NavState NavState::update(const Vector3& omega, const Vector3& acceleration, if (F) { F->middleRows<3>(3) += dt * D_t_t(F) * D_xiP_state; } - // derivative wrt omega - if (G1) { - // D_newState_dRxi = D_newState_xi.leftCols<3>() - // D_dRxi_omega = dt * I_3x3 - // *G1 = D_newState_omega = D_newState_dRxi * D_dRxi_omega - *G1 = D_newState_xi.leftCols<3>() * dt; - } // derivative wrt acceleration - if (G2) { + if (G1) { // D_newState_dPxi = D_newState_xi.middleCols<3>(3) // D_dPxi_acc = dt22 * I_3x3 // D_newState_dVxi = D_newState_xi.rightCols<3>() // D_dVxi_acc = dt * I_3x3 // *G2 = D_newState_acc = D_newState_dPxi * D_dPxi_acc + D_newState_dVxi * D_dVxi_acc - *G2 = D_newState_xi.middleCols<3>(3) * dt22 + D_newState_xi.rightCols<3>() * dt; + *G1 = D_newState_xi.middleCols<3>(3) * dt22 + + D_newState_xi.rightCols<3>() * dt; + } + // derivative wrt omega + if (G2) { + // D_newState_dRxi = D_newState_xi.leftCols<3>() + // D_dRxi_omega = dt * I_3x3 + // *G1 = D_newState_omega = D_newState_dRxi * D_dRxi_omega + *G2 = D_newState_xi.leftCols<3>() * dt; } return newState; } diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index e33b5fc9f..9561aa77b 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -212,9 +212,9 @@ public: /// @name Dynamics /// @{ - /// Integrate forward in time given angular velocity and acceleration + /// Integrate forward in time given angular velocity and acceleration in body frame /// Uses second order integration for position, returns derivatives except dt. - NavState update(const Vector3& omega, const Vector3& acceleration, + NavState update(const Vector3& b_acceleration, const Vector3& b_omega, const double dt, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const; diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index a885936aa..b05a8a3e5 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -206,11 +206,11 @@ TEST(NavState, Update) { NavState expected(kAttitude.expmap(deltaT * omega), kPosition + Point3((kVelocity + b_acc * deltaT / 2) * deltaT), kVelocity + b_acc * deltaT); - NavState actual = kState1.update(omega, acc, deltaT, aF, aG1, aG2); + NavState actual = kState1.update(acc, omega, deltaT, aF, aG1, aG2); EXPECT(assert_equal(expected, actual)); - EXPECT(assert_equal(numericalDerivative31(update, kState1, omega, acc, 1e-7), aF, 1e-7)); - EXPECT(assert_equal(numericalDerivative32(update, kState1, omega, acc, 1e-7), aG1, 1e-7)); - EXPECT(assert_equal(numericalDerivative33(update, kState1, omega, acc, 1e-7), aG2, 1e-7)); + EXPECT(assert_equal(numericalDerivative31(update, kState1, acc, omega, 1e-7), aF, 1e-7)); + EXPECT(assert_equal(numericalDerivative32(update, kState1, acc, omega, 1e-7), aG1, 1e-7)); + EXPECT(assert_equal(numericalDerivative33(update, kState1, acc, omega, 1e-7), aG2, 1e-7)); } /* ************************************************************************* */ From 325ede23fe8bc98bcffe41b916c6886a08fae1f2 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 15:08:12 -0700 Subject: [PATCH 106/142] BIG: switch to NavState delta pose --- gtsam/navigation/ImuFactor.cpp | 45 +++------ gtsam/navigation/ImuFactor.h | 10 +- gtsam/navigation/NavState.cpp | 6 +- gtsam/navigation/PreintegrationBase.cpp | 90 ++++++++---------- gtsam/navigation/PreintegrationBase.h | 43 ++++----- gtsam/navigation/tests/testImuFactor.cpp | 115 +++++++++++------------ 6 files changed, 135 insertions(+), 174 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 5a46c5da3..2f5861de5 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -61,16 +61,17 @@ void PreintegratedImuMeasurements::resetIntegration() { #define D_v_v(H) (H)->block<3,3>(6,6) //------------------------------------------------------------------------------ void PreintegratedImuMeasurements::integrateMeasurement( - const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - OptionalJacobian<9, 9> F_test, OptionalJacobian<9, 9> G_test) { + const Vector3& measuredAcc, const Vector3& measuredOmega, double dt, + OptionalJacobian<9, 9> outF, OptionalJacobian<9, 9> G) { - const Matrix3 dRij = deltaRij_.matrix(); // store this, which is useful to compute G_test + static const Matrix93 Gi = (Matrix93() << Z_3x3, I_3x3, Z_3x3).finished(); // Update preintegrated measurements (also get Jacobian) Matrix3 D_incrR_integratedOmega; // Right jacobian computed at theta_incr Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) - updatePreintegratedMeasurements(measuredAcc, measuredOmega, deltaT, - &D_incrR_integratedOmega, &F); + Matrix93 G1, G2; + updatePreintegratedMeasurements(measuredAcc, measuredOmega, dt, + &D_incrR_integratedOmega, &F, &G1, &G2); // first order covariance propagation: // as in [2] we consider a first order propagation that can be seen as a prediction phase in EKF @@ -78,35 +79,13 @@ void PreintegratedImuMeasurements::integrateMeasurement( // preintMeasCov = F * preintMeasCov * F.transpose() + G * (1/deltaT) * measurementCovariance * G' // NOTE 1: (1/deltaT) allows to pass from continuous time noise to discrete time noise // measurementCovariance_discrete = measurementCovariance_contTime * (1/deltaT) - // NOTE 2: computation of G * (1/deltaT) * measurementCovariance * G.transpose() done block-wise, - // as G and measurementCovariance are block-diagonal matrices - preintMeasCov_ = F * preintMeasCov_ * F.transpose(); - D_R_R(&preintMeasCov_) += D_incrR_integratedOmega * p().gyroscopeCovariance - * D_incrR_integratedOmega.transpose() * deltaT; - D_t_t(&preintMeasCov_) += p().integrationCovariance * deltaT; - D_v_v(&preintMeasCov_) += dRij * p().accelerometerCovariance * dRij.transpose() * deltaT; + preintMeasCov_ = F * preintMeasCov_ * F.transpose() + + G1 * (p().accelerometerCovariance / dt) * G1.transpose() + + Gi * (p().integrationCovariance * dt) * Gi.transpose() // NOTE(frank): (Gi*dt)*(C/dt)*(Gi'*dt) + + G2 * (p().gyroscopeCovariance / dt) * G2.transpose(); - Matrix3 F_pos_noiseacc; - F_pos_noiseacc = 0.5 * dRij * deltaT * deltaT; - D_t_t(&preintMeasCov_) += (1 / deltaT) * F_pos_noiseacc - * p().accelerometerCovariance * F_pos_noiseacc.transpose(); - Matrix3 temp = F_pos_noiseacc * p().accelerometerCovariance - * dRij.transpose(); // has 1/deltaT - D_t_v(&preintMeasCov_) += temp; - D_v_t(&preintMeasCov_) += temp.transpose(); - - // F_test and G_test are given as output for testing purposes and are not needed by the factor - if (F_test) { - (*F_test) << F; - } - if (G_test) { - // This in only for testing & documentation, while the actual computation is done block-wise - // omegaNoise intNoise accNoise - (*G_test) << - D_incrR_integratedOmega * deltaT, Z_3x3, Z_3x3, // angle - Z_3x3, I_3x3 * deltaT, F_pos_noiseacc, // pos - Z_3x3, Z_3x3, dRij * deltaT; // vel - } + if (outF) *outF = F; + if (G) *G << G2, Gi*dt, G1; // NOTE(frank): order here is R,P,V } //------------------------------------------------------------------------------ PreintegratedImuMeasurements::PreintegratedImuMeasurements( diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 32ee4185e..1d32623bd 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -96,13 +96,13 @@ public: * Add a single IMU measurement to the preintegration. * @param measuredAcc Measured acceleration (in body frame, as given by the sensor) * @param measuredOmega Measured angular velocity (as given by the sensor) - * @param deltaT Time interval between this and the last IMU measurement - * @param body_P_sensor Optional sensor frame (pose of the IMU in the body frame) - * @param Fout, Gout Jacobians used internally (only needed for testing) + * @param dt Time interval between this and the last IMU measurement + * @param F, F Jacobians used internally (only needed for testing) */ void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double deltaT, - OptionalJacobian<9, 9> Fout = boost::none, OptionalJacobian<9, 9> Gout = boost::none); + const Vector3& measuredOmega, double dt, // + OptionalJacobian<9, 9> F = boost::none, // + OptionalJacobian<9, 9> G = boost::none); /// Return pre-integrated measurement covariance Matrix preintMeasCov() const { return preintMeasCov_; } diff --git a/gtsam/navigation/NavState.cpp b/gtsam/navigation/NavState.cpp index 2e7917688..6266c328f 100644 --- a/gtsam/navigation/NavState.cpp +++ b/gtsam/navigation/NavState.cpp @@ -322,14 +322,14 @@ Vector9 NavState::correctPIM(const Vector9& pim, double dt, OptionalJacobian<9, 9> H2) const { const Rot3& nRb = R_; const Velocity3& n_v = v_; // derivative is Ri ! - const double dt2 = dt * dt; + const double dt22 = 0.5 * dt * dt; Vector9 xi; Matrix3 D_dP_Ri1, D_dP_Ri2, D_dP_nv, D_dV_Ri; dR(xi) = dR(pim); dP(xi) = dP(pim) + dt * nRb.unrotate(n_v, H1 ? &D_dP_Ri1 : 0, H2 ? &D_dP_nv : 0) - + (0.5 * dt2) * nRb.unrotate(n_gravity, H1 ? &D_dP_Ri2 : 0); + + dt22 * nRb.unrotate(n_gravity, H1 ? &D_dP_Ri2 : 0); dV(xi) = dV(pim) + dt * nRb.unrotate(n_gravity, H1 ? &D_dV_Ri : 0); if (omegaCoriolis) { @@ -342,7 +342,7 @@ Vector9 NavState::correctPIM(const Vector9& pim, double dt, if (H1) { if (!omegaCoriolis) H1->setZero(); // if coriolis H1 is already initialized - D_t_R(H1) += dt * D_dP_Ri1 + (0.5 * dt2) * D_dP_Ri2; + D_t_R(H1) += dt * D_dP_Ri1 + dt22 * D_dP_Ri2; D_t_v(H1) += dt * D_dP_nv * Ri; D_v_R(H1) += dt * D_dV_Ri; } diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 6353eb16c..2373c474e 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -29,10 +29,8 @@ namespace gtsam { /// Re-initialize PreintegratedMeasurements void PreintegrationBase::resetIntegration() { deltaTij_ = 0.0; - deltaRij_ = Rot3(); + deltaXij_ = NavState(); delRdelBiasOmega_ = Z_3x3; - deltaPij_ = Vector3::Zero(); - deltaVij_ = Vector3::Zero(); delPdelBiasAcc_ = Z_3x3; delPdelBiasOmega_ = Z_3x3; delVdelBiasAcc_ = Z_3x3; @@ -43,21 +41,19 @@ void PreintegrationBase::resetIntegration() { void PreintegrationBase::print(const string& s) const { cout << s << endl; cout << " deltaTij [" << deltaTij_ << "]" << endl; - cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << endl; - cout << " deltaPij [ " << deltaPij_.transpose() << " ]" << endl; - cout << " deltaVij [ " << deltaVij_.transpose() << " ]" << endl; + cout << " deltaRij.ypr = (" << deltaRij().ypr().transpose() << ")" << endl; + cout << " deltaPij [ " << deltaPij().transpose() << " ]" << endl; + cout << " deltaVij [ " << deltaVij().transpose() << " ]" << endl; biasHat_.print(" biasHat"); } /// Needed for testable bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) const { - return deltaRij_.equals(other.deltaRij_, tol) - && fabs(deltaTij_ - other.deltaTij_) < tol - && equal_with_abs_tol(delRdelBiasOmega_, other.delRdelBiasOmega_, tol) + return fabs(deltaTij_ - other.deltaTij_) < tol + && deltaXij_.equals(other.deltaXij_, tol) && biasHat_.equals(other.biasHat_, tol) - && equal_with_abs_tol(deltaPij_, other.deltaPij_, tol) - && equal_with_abs_tol(deltaVij_, other.deltaVij_, tol) + && equal_with_abs_tol(delRdelBiasOmega_, other.delRdelBiasOmega_, tol) && equal_with_abs_tol(delPdelBiasAcc_, other.delPdelBiasAcc_, tol) && equal_with_abs_tol(delPdelBiasOmega_, other.delPdelBiasOmega_, tol) && equal_with_abs_tol(delVdelBiasAcc_, other.delVdelBiasAcc_, tol) @@ -65,8 +61,8 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, } void PreintegrationBase::updatePreintegratedMeasurements( - const Vector3& measuredAcc, const Vector3& measuredOmega, - const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F) { + const Vector3& measuredAcc, const Vector3& measuredOmega, const double dt, + Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { // NOTE: order is important here because each update uses old values, e.g., velocity and position updates are based on previous rotation estimate. // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). @@ -91,43 +87,31 @@ void PreintegrationBase::updatePreintegratedMeasurements( correctedAcc = bRs * correctedAcc - correctedOmega.cross(b_velocity_bs); } - // Calculate acceleration in *current* i frame, i.e., before rotation update below + // Save current rotation for updating Jacobians + const Rot3 oldRij = deltaXij_.attitude(); + + // Do update in one fell swoop + deltaTij_ += dt; + deltaXij_ = deltaXij_.update(correctedAcc, correctedOmega, dt, F, G1, G2); + + // Update Jacobians + // TODO(frank): we are repeating some computation here: accessible in F ? Matrix3 D_acc_R; - const Matrix3 dRij = deltaRij_.matrix(); // expensive - const Vector3 i_acc = deltaRij_.rotate(correctedAcc, D_acc_R); + oldRij.rotate(correctedAcc, D_acc_R); const Matrix3 D_acc_biasOmega = D_acc_R * delRdelBiasOmega_; -// NavState iTj(deltaRij_, deltaPij_, deltaVij_); -// iTj = iTj.update(); - - // rotation vector describing rotation increment computed from the - // current rotation rate measurement - const Vector3 integratedOmega = correctedOmega * deltaT; - const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! - - // Update deltaTij and rotation - deltaTij_ += deltaT; - Matrix3 D_Rij_incrR; - deltaRij_ = deltaRij_.compose(incrR, D_Rij_incrR); - - // Update Jacobian + const Vector3 integratedOmega = correctedOmega * dt; + const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! const Matrix3 incrRt = incrR.transpose(); delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - - *D_incrR_integratedOmega * deltaT; + - *D_incrR_integratedOmega * dt; - double dt22 = 0.5 * deltaT * deltaT; - deltaPij_ += dt22 * i_acc + deltaT * deltaVij_; - deltaVij_ += deltaT * i_acc; - - *F << // angle pos vel - D_Rij_incrR, Z_3x3, Z_3x3, // angle - dt22 * D_acc_R, I_3x3, I_3x3 * deltaT, // pos - deltaT * D_acc_R, Z_3x3, I_3x3; // vel - - delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - dt22 * dRij; - delPdelBiasOmega_ += deltaT * delVdelBiasOmega_ + dt22 * D_acc_biasOmega; - delVdelBiasAcc_ += -dRij * deltaT; - delVdelBiasOmega_ += D_acc_biasOmega * deltaT; + double dt22 = 0.5 * dt * dt; + const Matrix3 dRij = oldRij.matrix(); // expensive + delPdelBiasAcc_ += delVdelBiasAcc_ * dt - dt22 * dRij; + delPdelBiasOmega_ += dt * delVdelBiasOmega_ + dt22 * D_acc_biasOmega; + delVdelBiasAcc_ += -dRij * dt; + delVdelBiasOmega_ += D_acc_biasOmega * dt; } //------------------------------------------------------------------------------ @@ -135,24 +119,26 @@ Vector9 PreintegrationBase::biasCorrectedDelta( const imuBias::ConstantBias& bias_i, OptionalJacobian<9, 6> H) const { // Correct deltaRij, derivative is delRdelBiasOmega_ const imuBias::ConstantBias biasIncr = bias_i - biasHat_; - Matrix3 D_deltaRij_bias; + Matrix3 D_correctedRij_bias; const Vector3 biasInducedOmega = delRdelBiasOmega_ * biasIncr.gyroscope(); - const Rot3 deltaRij = deltaRij_.expmap(biasInducedOmega, boost::none, H ? &D_deltaRij_bias : 0); - if (H) D_deltaRij_bias *= delRdelBiasOmega_; + const Rot3 correctedRij = deltaRij().expmap(biasInducedOmega, boost::none, + H ? &D_correctedRij_bias : 0); + if (H) + D_correctedRij_bias *= delRdelBiasOmega_; Vector9 xi; - Matrix3 D_dR_deltaRij; + Matrix3 D_dR_correctedRij; // TODO(frank): could line below be simplified? It is equivalent to // LogMap(deltaRij_.compose(Expmap(delRdelBiasOmega_ * biasIncr.gyroscope()))) - NavState::dR(xi) = Rot3::Logmap(deltaRij, H ? &D_dR_deltaRij : 0); - NavState::dP(xi) = deltaPij_ + delPdelBiasAcc_ * biasIncr.accelerometer() + NavState::dR(xi) = Rot3::Logmap(correctedRij, H ? &D_dR_correctedRij : 0); + NavState::dP(xi) = deltaPij() + delPdelBiasAcc_ * biasIncr.accelerometer() + delPdelBiasOmega_ * biasIncr.gyroscope(); - NavState::dV(xi) = deltaVij_ + delVdelBiasAcc_ * biasIncr.accelerometer() + NavState::dV(xi) = deltaVij() + delVdelBiasAcc_ * biasIncr.accelerometer() + delVdelBiasOmega_ * biasIncr.gyroscope(); if (H) { Matrix36 D_dR_bias, D_dP_bias, D_dV_bias; - D_dR_bias << Z_3x3, D_dR_deltaRij * D_deltaRij_bias; + D_dR_bias << Z_3x3, D_dR_correctedRij * D_correctedRij_bias; D_dP_bias << delPdelBiasAcc_, delPdelBiasOmega_; D_dV_bias << delVdelBiasAcc_, delVdelBiasOmega_; (*H) << D_dR_bias, D_dP_bias, D_dV_bias; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index bf01db00a..6b4626654 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -100,9 +100,14 @@ public: protected: - double deltaTij_; ///< Time interval from i to j - Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) - Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias + double deltaTij_; ///< Time interval from i to j + + /** + * Preintegrated navigation state, from frame i to frame j + * Note: relative position does not take into account velocity at time i, see deltap+, in [2] + * Note: velocity is now also in frame i, as opposed to deltaVij in [2] + */ + NavState deltaXij_; /// Parameters boost::shared_ptr p_; @@ -110,12 +115,10 @@ protected: /// Acceleration and gyro bias used for preintegration imuBias::ConstantBias biasHat_; - Vector3 deltaPij_; ///< Preintegrated relative position (does not take into account velocity at time i, see deltap+, in [2]) (in frame i) - Vector3 deltaVij_; ///< Preintegrated relative velocity (in global frame) - - Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias + Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias + Matrix3 delPdelBiasAcc_; ///< Jacobian of preintegrated position w.r.t. acceleration bias Matrix3 delPdelBiasOmega_; ///< Jacobian of preintegrated position w.r.t. angular rate bias - Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias + Matrix3 delVdelBiasAcc_; ///< Jacobian of preintegrated velocity w.r.t. acceleration bias Matrix3 delVdelBiasOmega_; ///< Jacobian of preintegrated velocity w.r.t. angular rate bias /// Default constructor for serialization @@ -147,19 +150,19 @@ public: return deltaTij_; } const Rot3& deltaRij() const { - return deltaRij_; + return deltaXij_.attitude(); } - const Matrix3& delRdelBiasOmega() const { - return delRdelBiasOmega_; + Vector3 deltaPij() const { + return deltaXij_.position().vector(); + } + Vector3 deltaVij() const { + return deltaXij_.velocity(); } const imuBias::ConstantBias& biasHat() const { return biasHat_; } - const Vector3& deltaPij() const { - return deltaPij_; - } - const Vector3& deltaVij() const { - return deltaVij_; + const Matrix3& delRdelBiasOmega() const { + return delRdelBiasOmega_; } const Matrix3& delPdelBiasAcc() const { return delPdelBiasAcc_; @@ -188,7 +191,7 @@ public: /// Update preintegrated measurements void updatePreintegratedMeasurements(const Vector3& measuredAcc, const Vector3& measuredOmega, const double deltaT, - Matrix3* D_incrR_integratedOmega, Matrix9* F); + Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2); /// Given the estimate of the bias, return a NavState tangent vector /// summarizing the preintegrated IMU measurements so far @@ -220,11 +223,9 @@ private: void serialize(ARCHIVE & ar, const unsigned int /*version*/) { ar & BOOST_SERIALIZATION_NVP(p_); ar & BOOST_SERIALIZATION_NVP(deltaTij_); - ar & BOOST_SERIALIZATION_NVP(deltaRij_); - ar & BOOST_SERIALIZATION_NVP(delRdelBiasOmega_); + ar & BOOST_SERIALIZATION_NVP(deltaXij_); ar & BOOST_SERIALIZATION_NVP(biasHat_); - ar & BOOST_SERIALIZATION_NVP(deltaPij_); - ar & BOOST_SERIALIZATION_NVP(deltaVij_); + ar & BOOST_SERIALIZATION_NVP(delRdelBiasOmega_); ar & BOOST_SERIALIZATION_NVP(delPdelBiasAcc_); ar & BOOST_SERIALIZATION_NVP(delPdelBiasOmega_); ar & BOOST_SERIALIZATION_NVP(delVdelBiasAcc_); diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 176379da4..fdf9fd04c 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -40,6 +40,7 @@ using symbol_shorthand::B; static const Vector3 kGravityAlongNavZDown(0, 0, 9.81); static const Vector3 kZeroOmegaCoriolis(0, 0, 0); static const Vector3 kNonZeroOmegaCoriolis(0, 0.1, 0.1); +static const imuBias::ConstantBias kZeroBiasHat, kZeroBias; /* ************************************************************************* */ namespace { @@ -139,46 +140,51 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { /* ************************************************************************* */ TEST(ImuFactor, StraightLine) { // Set up IMU measurements - static const double g = 10; // make this easy to check - static const double a = 0.2, dt = 3.0; - const double exact = dt * dt / 2; - Vector3 measuredAcc(a, 0, -g), measuredOmega(0, 0, 0); + static const double g = 10; // make gravity 10 to make this easy to check + static const double v = 5.0, a = 0.2, dt = 3.0; + const double dt22 = dt * dt / 2; - // Set up body pointing towards y axis, and start at 10,20,0 with zero velocity - // TODO(frank): clean up Rot3 mess + // Set up body pointing towards y axis, and start at 10,20,0 with velocity going in X + // The body itself has Z axis pointing down static const Rot3 nRb(Point3(0, 1, 0), Point3(1, 0, 0), Point3(0, 0, -1)); static const Point3 initial_position(10, 20, 0); - static const NavState state1(nRb, initial_position, Velocity3(0, 0, 0)); + static const Vector3 initial_velocity(v,0,0); + static const NavState state1(nRb, initial_position, initial_velocity); - imuBias::ConstantBias biasHat, bias; + // set up acceleration in X direction, no angular velocity. + // Since body Z-axis is pointing down, accelerometer measures table exerting force in negative Z + Vector3 measuredAcc(a, 0, -g), measuredOmega(0, 0, 0); + + // Create parameters assuming nav frame has Z up boost::shared_ptr p = - PreintegratedImuMeasurements::Params::MakeSharedU(g); // Up convention! + PreintegratedImuMeasurements::Params::MakeSharedU(g); - PreintegratedImuMeasurements pim(p, biasHat); + // Now, preintegrate for 3 seconds, in 10 steps + PreintegratedImuMeasurements pim(p, kZeroBiasHat); for (size_t i = 0; i < 10; i++) pim.integrateMeasurement(measuredAcc, measuredOmega, dt / 10); // Check integrated quantities in body frame: gravity measured by IMU is integrated! + Vector3 b_deltaP(a * dt22, 0, -g * dt22); + Vector3 b_deltaV(a * dt, 0, -g * dt); EXPECT(assert_equal(Rot3(), pim.deltaRij())); - EXPECT(assert_equal(Vector3(a * exact, 0, -g * exact), pim.deltaPij())); - EXPECT(assert_equal(Vector3(a * dt, 0, -g * dt), pim.deltaVij())); + EXPECT(assert_equal(b_deltaP, pim.deltaPij())); + EXPECT(assert_equal(b_deltaV, pim.deltaVij())); // Check bias-corrected delta: also in body frame Vector9 expectedBC; - expectedBC << 0, 0, 0, a * exact, 0, -g * exact, a * dt, 0, -g * dt; - EXPECT(assert_equal(expectedBC, pim.biasCorrectedDelta(bias))); + expectedBC << Vector3(0, 0, 0), b_deltaP, b_deltaV; + EXPECT(assert_equal(expectedBC, pim.biasCorrectedDelta(kZeroBias))); - // Check prediction, note we move along x in body, along y in nav - NavState expected(nRb, Point3(10, 20 + a * exact, 0), - Velocity3(0, a * dt, 0)); - EXPECT(assert_equal(expected, pim.predict(state1, bias))); + // Check prediction in nav, note we move along x in body, along y in nav + Point3 expected_position(10 + v*dt, 20 + a * dt22, 0); + Velocity3 expected_velocity(v, a * dt, 0); + NavState expected(nRb, expected_position, expected_velocity); + EXPECT(assert_equal(expected, pim.predict(state1, kZeroBias))); } /* ************************************************************************* */ TEST(ImuFactor, PreintegratedMeasurements) { - // Linearization point - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); - // Measurements Vector3 measuredAcc(0.1, 0.0, 0.0); Vector3 measuredOmega(M_PI / 100.0, 0.0, 0.0); @@ -192,7 +198,7 @@ TEST(ImuFactor, PreintegratedMeasurements) { double expectedDeltaT1(0.5); // Actual preintegrated values - PreintegratedImuMeasurements actual1(bias, kMeasuredAccCovariance, + PreintegratedImuMeasurements actual1(kZeroBiasHat, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); actual1.integrateMeasurement(measuredAcc, measuredOmega, deltaT); @@ -222,7 +228,6 @@ TEST(ImuFactor, PreintegratedMeasurements) { /* ************************************************************************* */ // Common linearization point and measurements for tests namespace common { -static const imuBias::ConstantBias biasHat, bias; // Bias static const Pose3 x1(Rot3::RzRyRx(M_PI / 12.0, M_PI / 6.0, M_PI / 4.0), Point3(5.0, 1.0, 0)); static const Vector3 v1(Vector3(0.5, 0.0, 0.0)); @@ -252,28 +257,28 @@ TEST(ImuFactor, PreintegrationBaseMethods) { p->integrationCovariance = kIntegrationErrorCovariance; p->use2ndOrderCoriolis = true; - PreintegratedImuMeasurements pim(p, bias); + PreintegratedImuMeasurements pim(p, kZeroBiasHat); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // biasCorrectedDelta Matrix96 actualH; - pim.biasCorrectedDelta(bias, actualH); + pim.biasCorrectedDelta(kZeroBias, actualH); Matrix expectedH = numericalDerivative11( boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1, - boost::none), bias); + boost::none), kZeroBias); EXPECT(assert_equal(expectedH, actualH)); Matrix9 aH1; Matrix96 aH2; - NavState predictedState = pim.predict(state1, bias, aH1, aH2); + NavState predictedState = pim.predict(state1, kZeroBias, aH1, aH2); Matrix eH1 = numericalDerivative11( - boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none, + boost::bind(&PreintegrationBase::predict, pim, _1, kZeroBias, boost::none, boost::none), state1); EXPECT(assert_equal(eH1, aH1)); Matrix eH2 = numericalDerivative11( boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none, - boost::none), bias); + boost::none), kZeroBias); EXPECT(assert_equal(eH2, aH2)); return; @@ -282,11 +287,11 @@ TEST(ImuFactor, PreintegrationBaseMethods) { /* ************************************************************************* */ TEST(ImuFactor, ErrorAndJacobians) { using namespace common; - PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, + PreintegratedImuMeasurements pim(kZeroBiasHat, kMeasuredAccCovariance, kMeasuredOmegaCovariance, kIntegrationErrorCovariance); pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); - EXPECT(assert_equal(state2, pim.predict(state1, bias))); + EXPECT(assert_equal(state2, pim.predict(state1, kZeroBias))); // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, @@ -296,14 +301,14 @@ TEST(ImuFactor, ErrorAndJacobians) { Vector expectedError(9); expectedError << 0, 0, 0, 0, 0, 0, 0, 0, 0; EXPECT( - assert_equal(expectedError, factor.evaluateError(x1, v1, x2, v2, bias))); + assert_equal(expectedError, factor.evaluateError(x1, v1, x2, v2, kZeroBias))); Values values; values.insert(X(1), x1); values.insert(V(1), v1); values.insert(X(2), x2); values.insert(V(2), v2); - values.insert(B(1), bias); + values.insert(B(1), kZeroBias); EXPECT(assert_equal(expectedError, factor.unwhitenedError(values))); // Make sure linearization is correct @@ -312,16 +317,16 @@ TEST(ImuFactor, ErrorAndJacobians) { // Actual Jacobians Matrix H1a, H2a, H3a, H4a, H5a; - (void) factor.evaluateError(x1, v1, x2, v2, bias, H1a, H2a, H3a, H4a, H5a); + (void) factor.evaluateError(x1, v1, x2, v2, kZeroBias, H1a, H2a, H3a, H4a, H5a); // Make sure rotation part is correct when error is interpreted as axis-angle // Jacobians are around zero, so the rotation part is the same as: Matrix H1Rot3 = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, bias), x1); + boost::bind(&evaluateRotationError, factor, _1, v1, x2, v2, kZeroBias), x1); EXPECT(assert_equal(H1Rot3, H1a.topRows(3))); Matrix H3Rot3 = numericalDerivative11( - boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, bias), x2); + boost::bind(&evaluateRotationError, factor, x1, v1, _1, v2, kZeroBias), x2); EXPECT(assert_equal(H3Rot3, H3a.topRows(3))); // Evaluate error with wrong values @@ -330,7 +335,7 @@ TEST(ImuFactor, ErrorAndJacobians) { expectedError << 0, 0, 0, 0, 0, 0, -0.0724744871, -0.040715657, -0.151952901; EXPECT( assert_equal(expectedError, - factor.evaluateError(x1, v1, x2, v2_wrong, bias), 1e-2)); + factor.evaluateError(x1, v1, x2, v2_wrong, kZeroBias), 1e-2)); EXPECT(assert_equal(expectedError, factor.unwhitenedError(values), 1e-2)); // Make sure the whitening is done correctly @@ -503,9 +508,6 @@ TEST(ImuFactor, fistOrderExponential) { /* ************************************************************************* */ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { - // Linearization point - imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.1, 0.1)), Point3(1, 0, 1)); // Measurements @@ -526,28 +528,28 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { // Actual preintegrated values PreintegratedImuMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + evaluatePreintegratedMeasurements(kZeroBias, measuredAccs, measuredOmegas, deltaTs); // Compute numerical derivatives Matrix expectedDelPdelBias = numericalDerivative11( boost::bind(&evaluatePreintegratedMeasurementsPosition, _1, measuredAccs, - measuredOmegas, deltaTs), bias); + measuredOmegas, deltaTs), kZeroBias); Matrix expectedDelPdelBiasAcc = expectedDelPdelBias.leftCols(3); Matrix expectedDelPdelBiasOmega = expectedDelPdelBias.rightCols(3); Matrix expectedDelVdelBias = numericalDerivative11( boost::bind(&evaluatePreintegratedMeasurementsVelocity, _1, measuredAccs, - measuredOmegas, deltaTs), bias); + measuredOmegas, deltaTs), kZeroBias); Matrix expectedDelVdelBiasAcc = expectedDelVdelBias.leftCols(3); Matrix expectedDelVdelBiasOmega = expectedDelVdelBias.rightCols(3); Matrix expectedDelRdelBias = numericalDerivative11( boost::bind(&evaluatePreintegratedMeasurementsRotation, _1, - measuredAccs, measuredOmegas, deltaTs), bias); + measuredAccs, measuredOmegas, deltaTs), kZeroBias); Matrix expectedDelRdelBiasAcc = expectedDelRdelBias.leftCols(3); Matrix expectedDelRdelBiasOmega = expectedDelRdelBias.rightCols(3); @@ -565,9 +567,6 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { /* ************************************************************************* */ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { - // Linearization point - imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - // Measurements const Vector3 measuredAcc(0.1, 0.0, 0.0); const Vector3 measuredOmega(M_PI / 100.0, 0.0, 0.0); @@ -587,20 +586,19 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { deltaTs.push_back(deltaT); } // Actual preintegrated values - PreintegratedImuMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, - deltaTs); + PreintegratedImuMeasurements pim = evaluatePreintegratedMeasurements( + kZeroBias, measuredAccs, measuredOmegas, deltaTs); // so far we only created a nontrivial linearization point for the preintegrated measurements // Now we add a new measurement and ask for Jacobians - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement + const Rot3 deltaRij_old = pim.deltaRij(); // before adding new measurement + const Vector3 deltaVij_old = pim.deltaVij(); // before adding new measurement + const Vector3 deltaPij_old = pim.deltaPij(); // before adding new measurement - Matrix oldPreintCovariance = preintegrated.preintMeasCov(); + Matrix oldPreintCovariance = pim.preintMeasCov(); Matrix Factual, Gactual; - preintegrated.integrateMeasurement(measuredAcc, measuredOmega, deltaT, + pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT, Factual, Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// @@ -673,15 +671,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { * Factual.transpose() + (1 / deltaT) * Gactual * measurementCovariance * Gactual.transpose(); - Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); + Matrix newPreintCovarianceActual = pim.preintMeasCov(); EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); } /* ************************************************************************* */ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { - // Linearization point - imuBias::ConstantBias bias; // Current estimate of acceleration and rotation rate biases - // Measurements list measuredAccs, measuredOmegas; list deltaTs; @@ -699,7 +694,7 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { } // Actual preintegrated values PreintegratedImuMeasurements preintegrated = - evaluatePreintegratedMeasurements(bias, measuredAccs, measuredOmegas, + evaluatePreintegratedMeasurements(kZeroBias, measuredAccs, measuredOmegas, deltaTs); // so far we only created a nontrivial linearization point for the preintegrated measurements From 8aca431913ca9a8ec2068c565740d9d31891582c Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 15:31:45 -0700 Subject: [PATCH 107/142] const update method --- gtsam/navigation/ImuFactor.cpp | 5 ++--- gtsam/navigation/PreintegrationBase.cpp | 30 ++++++++++++++++--------- gtsam/navigation/PreintegrationBase.h | 4 ++++ 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 2f5861de5..279c95d3c 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -67,11 +67,10 @@ void PreintegratedImuMeasurements::integrateMeasurement( static const Matrix93 Gi = (Matrix93() << Z_3x3, I_3x3, Z_3x3).finished(); // Update preintegrated measurements (also get Jacobian) - Matrix3 D_incrR_integratedOmega; // Right jacobian computed at theta_incr Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) Matrix93 G1, G2; - updatePreintegratedMeasurements(measuredAcc, measuredOmega, dt, - &D_incrR_integratedOmega, &F, &G1, &G2); + Matrix3 D_incrR_integratedOmega; + updatePreintegratedMeasurements(measuredAcc, measuredOmega, dt, &D_incrR_integratedOmega, &F, &G1, &G2); // first order covariance propagation: // as in [2] we consider a first order propagation that can be seen as a prediction phase in EKF diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 2373c474e..0d51e59f9 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -60,12 +60,10 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, && equal_with_abs_tol(delVdelBiasOmega_, other.delVdelBiasOmega_, tol); } -void PreintegrationBase::updatePreintegratedMeasurements( - const Vector3& measuredAcc, const Vector3& measuredOmega, const double dt, - Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { - - // NOTE: order is important here because each update uses old values, e.g., velocity and position updates are based on previous rotation estimate. - // (i.e., we have to update jacobians and covariances before updating preintegrated measurements). +//------------------------------------------------------------------------------ +NavState PreintegrationBase::update(const Vector3& measuredAcc, + const Vector3& measuredOmega, const double dt, Matrix9* F, Matrix93* G1, + Matrix93* G2) const { // Correct for bias in the sensor frame Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); @@ -87,15 +85,28 @@ void PreintegrationBase::updatePreintegratedMeasurements( correctedAcc = bRs * correctedAcc - correctedOmega.cross(b_velocity_bs); } + // Do update in one fell swoop + return deltaXij_.update(correctedAcc, correctedOmega, dt, F, G1, G2); +} + +//------------------------------------------------------------------------------ +void PreintegrationBase::updatePreintegratedMeasurements( + const Vector3& measuredAcc, const Vector3& measuredOmega, const double dt, + Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { + // Save current rotation for updating Jacobians const Rot3 oldRij = deltaXij_.attitude(); - // Do update in one fell swoop + // Do update deltaTij_ += dt; - deltaXij_ = deltaXij_.update(correctedAcc, correctedOmega, dt, F, G1, G2); + deltaXij_ = update(measuredAcc, measuredOmega, dt, F, G1, G2); // functional // Update Jacobians // TODO(frank): we are repeating some computation here: accessible in F ? + // Correct for bias in the sensor frame + Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); + Vector3 correctedOmega = biasHat_.correctGyroscope(measuredOmega); + Matrix3 D_acc_R; oldRij.rotate(correctedAcc, D_acc_R); const Matrix3 D_acc_biasOmega = D_acc_R * delRdelBiasOmega_; @@ -103,8 +114,7 @@ void PreintegrationBase::updatePreintegratedMeasurements( const Vector3 integratedOmega = correctedOmega * dt; const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - - *D_incrR_integratedOmega * dt; + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - *D_incrR_integratedOmega * dt; double dt22 = 0.5 * dt * dt; const Matrix3 dRij = oldRij.matrix(); // expensive diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 6b4626654..95de5e935 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -188,6 +188,10 @@ public: /// check equality bool equals(const PreintegrationBase& other, double tol) const; + /// Calculate the updated preintegrated measurement, does not modify + NavState update(const Vector3& measuredAcc, const Vector3& measuredOmega, + const double dt, Matrix9* F, Matrix93* G1, Matrix93* G2) const; + /// Update preintegrated measurements void updatePreintegratedMeasurements(const Vector3& measuredAcc, const Vector3& measuredOmega, const double deltaT, From 7901077a7ad0c2ff98cac8b0f30a4af94e5e5896 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 15:32:16 -0700 Subject: [PATCH 108/142] refactoring F and G --- gtsam/navigation/CombinedImuFactor.cpp | 63 +++++++------- gtsam/navigation/CombinedImuFactor.h | 8 +- .../tests/testCombinedImuFactor.cpp | 82 +++++++++---------- 3 files changed, 76 insertions(+), 77 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index ea68f724e..0b23d299c 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -50,18 +50,33 @@ void PreintegratedCombinedMeasurements::resetIntegration() { preintMeasCov_.setZero(); } +//------------------------------------------------------------------------------ +// sugar for derivative blocks +#define D_R_R(H) (H)->block<3,3>(0,0) +#define D_R_t(H) (H)->block<3,3>(0,3) +#define D_R_v(H) (H)->block<3,3>(0,6) +#define D_t_R(H) (H)->block<3,3>(3,0) +#define D_t_t(H) (H)->block<3,3>(3,3) +#define D_t_v(H) (H)->block<3,3>(3,6) +#define D_v_R(H) (H)->block<3,3>(6,0) +#define D_v_t(H) (H)->block<3,3>(6,3) +#define D_v_v(H) (H)->block<3,3>(6,6) +#define D_a_a(H) (H)->block<3,3>(9,9) +#define D_g_g(H) (H)->block<3,3>(12,12) + //------------------------------------------------------------------------------ void PreintegratedCombinedMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - boost::optional F_test, boost::optional G_test) { + OptionalJacobian<15, 15> F_test, OptionalJacobian<15, 21> G_test) { - const Matrix3 dRij = deltaRij_.matrix(); // expensive when quaternion + const Matrix3 dRij = deltaXij_.R(); // expensive when quaternion // Update preintegrated measurements. Matrix3 D_incrR_integratedOmega; // Right jacobian computed at theta_incr Matrix9 F_9x9; // overall Jacobian wrt preintegrated measurements (df/dx) + Matrix93 G1,G2; updatePreintegratedMeasurements(measuredAcc, measuredOmega, deltaT, - &D_incrR_integratedOmega, &F_9x9); + &D_incrR_integratedOmega, &F_9x9, &G1, &G2); // Update preintegrated measurements covariance: as in [2] we consider a first order propagation that // can be seen as a prediction phase in an EKF framework. In this implementation, contrarily to [2] we @@ -74,17 +89,11 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( // overall Jacobian wrt preintegrated measurements (df/dx) Eigen::Matrix F; - // for documentation: - // F << I_3x3, I_3x3 * deltaT, Z_3x3, Z_3x3, Z_3x3, - // Z_3x3, I_3x3, H_vel_angles, H_vel_biasacc, Z_3x3, - // Z_3x3, Z_3x3, H_angles_angles, Z_3x3, H_angles_biasomega, - // Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, - // Z_3x3, Z_3x3, Z_3x3, Z_3x3, I_3x3; F.setZero(); F.block<9, 9>(0, 0) = F_9x9; + F.block<3, 3>(0, 12) = H_angles_biasomega; + F.block<3, 3>(6, 9) = H_vel_biasacc; F.block<6, 6>(9, 9) = I_6x6; - F.block<3, 3>(3, 9) = H_vel_biasacc; - F.block<3, 3>(6, 12) = H_angles_biasomega; // first order uncertainty propagation // Optimized matrix multiplication (1/deltaT) * G * measurementCovariance * G.transpose() @@ -92,38 +101,36 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( G_measCov_Gt.setZero(15, 15); // BLOCK DIAGONAL TERMS - G_measCov_Gt.block<3, 3>(0, 0) = deltaT * p().integrationCovariance; - G_measCov_Gt.block<3, 3>(3, 3) = (1 / deltaT) * (H_vel_biasacc) + D_t_t(&G_measCov_Gt) = deltaT * p().integrationCovariance; + D_v_v(&G_measCov_Gt) = (1 / deltaT) * (H_vel_biasacc) * (p().accelerometerCovariance + p().biasAccOmegaInit.block<3, 3>(0, 0)) * (H_vel_biasacc.transpose()); - G_measCov_Gt.block<3, 3>(6, 6) = (1 / deltaT) * (H_angles_biasomega) + D_R_R(&G_measCov_Gt) = (1 / deltaT) * (H_angles_biasomega) * (p().gyroscopeCovariance + p().biasAccOmegaInit.block<3, 3>(3, 3)) * (H_angles_biasomega.transpose()); - G_measCov_Gt.block<3, 3>(9, 9) = (1 / deltaT) * p().biasAccCovariance; - G_measCov_Gt.block<3, 3>(12, 12) = (1 / deltaT) * p().biasOmegaCovariance; + D_a_a(&G_measCov_Gt) = (1 / deltaT) * p().biasAccCovariance; + D_g_g(&G_measCov_Gt) = (1 / deltaT) * p().biasOmegaCovariance; // OFF BLOCK DIAGONAL TERMS - Matrix3 block23 = H_vel_biasacc * p().biasAccOmegaInit.block<3, 3>(3, 0) + Matrix3 temp = H_vel_biasacc * p().biasAccOmegaInit.block<3, 3>(3, 0) * H_angles_biasomega.transpose(); - G_measCov_Gt.block<3, 3>(3, 6) = block23; - G_measCov_Gt.block<3, 3>(6, 3) = block23.transpose(); + D_v_R(&G_measCov_Gt) = temp; + D_R_v(&G_measCov_Gt) = temp.transpose(); preintMeasCov_ = F * preintMeasCov_ * F.transpose() + G_measCov_Gt; // F_test and G_test are used for testing purposes and are not needed by the factor if (F_test) { - F_test->resize(15, 15); (*F_test) << F; } if (G_test) { - G_test->resize(15, 21); // This is for testing & documentation ///< measurementCovariance_ : cov[integrationError measuredAcc measuredOmega biasAccRandomWalk biasOmegaRandomWalk biasAccInit biasOmegaInit] in R^(21 x 21) (*G_test) << // - I_3x3 * deltaT, Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_3x3, // - Z_3x3, -H_vel_biasacc, Z_3x3, Z_3x3, Z_3x3, H_vel_biasacc, Z_3x3, // - Z_3x3, Z_3x3, -H_angles_biasomega, Z_3x3, Z_3x3, Z_3x3, H_angles_biasomega, // - Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3, Z_3x3, // - Z_3x3, Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3; + -H_angles_biasomega, Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_3x3, H_angles_biasomega, // + Z_3x3, I_3x3 * deltaT, Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_3x3, // + Z_3x3, Z_3x3, -H_vel_biasacc, Z_3x3, Z_3x3, H_vel_biasacc, Z_3x3, // + Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3, Z_3x3, // + Z_3x3, Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3; } } @@ -198,7 +205,7 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, Matrix93 D_r_vel_i, D_r_vel_j; // error wrt preintegrated measurements - Vector9 r_pvR = _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, + Vector9 r_Rpv = _PIM_.computeErrorAndJacobians(pose_i, vel_i, pose_j, vel_j, bias_i, H1 ? &D_r_pose_i : 0, H2 ? &D_r_vel_i : 0, H3 ? &D_r_pose_j : 0, H4 ? &D_r_vel_j : 0, H5 ? &D_r_bias_i : 0); @@ -242,7 +249,7 @@ Vector CombinedImuFactor::evaluateError(const Pose3& pose_i, // overall error Vector r(15); - r << r_pvR, fbias; // vector of size 15 + r << r_Rpv, fbias; // vector of size 15 return r; } diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 1289f22c6..5ac6d8a7e 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -142,10 +142,10 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { * @param body_P_sensor Optional sensor frame (pose of the IMU in the body * frame) */ - void integrateMeasurement( - const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - boost::optional F_test = boost::none, - boost::optional G_test = boost::none); + void integrateMeasurement(const Vector3& measuredAcc, + const Vector3& measuredOmega, double deltaT, + OptionalJacobian<15, 15> F_test = boost::none, + OptionalJacobian<15, 21> G_test = boost::none); /// methods to access class variables Matrix preintMeasCov() const { return preintMeasCov_; } diff --git a/gtsam/navigation/tests/testCombinedImuFactor.cpp b/gtsam/navigation/tests/testCombinedImuFactor.cpp index 53c0d7e23..8da1cc3e7 100644 --- a/gtsam/navigation/tests/testCombinedImuFactor.cpp +++ b/gtsam/navigation/tests/testCombinedImuFactor.cpp @@ -47,36 +47,30 @@ namespace { Vector updatePreintegratedMeasurementsTest(const Vector3 deltaPij_old, const Vector3& deltaVij_old, const Rot3& deltaRij_old, const imuBias::ConstantBias& bias_old, const Vector3& correctedAcc, - const Vector3& correctedOmega, const double deltaT, - const bool use2ndOrderIntegration) { + const Vector3& correctedOmega, const double deltaT) { Matrix3 dRij = deltaRij_old.matrix(); Vector3 temp = dRij * (correctedAcc - bias_old.accelerometer()) * deltaT; Vector3 deltaPij_new; - if (!use2ndOrderIntegration) { - deltaPij_new = deltaPij_old + deltaVij_old * deltaT; - } else { - deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; - } + deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; Vector3 deltaVij_new = deltaVij_old + temp; Rot3 deltaRij_new = deltaRij_old * Rot3::Expmap((correctedOmega - bias_old.gyroscope()) * deltaT); Vector3 logDeltaRij_new = Rot3::Logmap(deltaRij_new); // not important any more imuBias::ConstantBias bias_new(bias_old); Vector result(15); - result << deltaPij_new, deltaVij_new, logDeltaRij_new, bias_new.vector(); + result << logDeltaRij_new, deltaPij_new, deltaVij_new, bias_new.vector(); return result; } Rot3 updatePreintegratedMeasurementsRot(const Vector3 deltaPij_old, const Vector3& deltaVij_old, const Rot3& deltaRij_old, const imuBias::ConstantBias& bias_old, const Vector3& correctedAcc, - const Vector3& correctedOmega, const double deltaT, - const bool use2ndOrderIntegration) { + const Vector3& correctedOmega, const double deltaT) { Vector result = updatePreintegratedMeasurementsTest(deltaPij_old, deltaVij_old, deltaRij_old, bias_old, correctedAcc, correctedOmega, - deltaT, use2ndOrderIntegration); + deltaT); return Rot3::Expmap(result.segment < 3 > (6)); } @@ -377,10 +371,8 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { Matrix oldPreintCovariance = pim.preintMeasCov(); Matrix Factual, Gactual; - pim.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, Factual, Gactual); - - bool use2ndOrderIntegration = false; + pim.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, Factual, + Gactual); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR F @@ -388,51 +380,51 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { // Compute expected F wrt positions (15,3) Matrix df_dpos = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, _1, deltaVij_old, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), deltaPij_old); + deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT + ), deltaPij_old); // rotation part has to be done properly, on manifold - df_dpos.block<3, 3>(6, 0) = numericalDerivative11( + df_dpos.block<3, 3>(0, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, _1, deltaVij_old, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), deltaPij_old); + deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT + ), deltaPij_old); + EXPECT(assert_equal(df_dpos, Factual.block<15, 3>(0, 3), 1e-5)); // Compute expected F wrt velocities (15,3) Matrix df_dvel = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, _1, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), deltaVij_old); + deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT + ), deltaVij_old); // rotation part has to be done properly, on manifold - df_dvel.block<3, 3>(6, 0) = numericalDerivative11( + df_dvel.block<3, 3>(0, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, _1, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), deltaVij_old); + deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT + ), deltaVij_old); + EXPECT(assert_equal(df_dvel, Factual.block<15, 3>(0, 6), 1e-5)); // Compute expected F wrt angles (15,3) Matrix df_dangle = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, deltaVij_old, _1, bias_old, newMeasuredAcc, newMeasuredOmega, - newDeltaT, use2ndOrderIntegration), deltaRij_old); + newDeltaT), deltaRij_old); // rotation part has to be done properly, on manifold - df_dangle.block<3, 3>(6, 0) = numericalDerivative11( + df_dangle.block<3, 3>(0, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, deltaVij_old, _1, bias_old, newMeasuredAcc, newMeasuredOmega, - newDeltaT, use2ndOrderIntegration), deltaRij_old); + newDeltaT), deltaRij_old); + EXPECT(assert_equal(df_dangle, Factual.block<15, 3>(0, 0), 1e-5)); // Compute expected F wrt biases (15,6) Matrix df_dbias = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT, use2ndOrderIntegration), bias_old); + newDeltaT), bias_old); // rotation part has to be done properly, on manifold - df_dbias.block<3, 6>(6, 0) = + df_dbias.block<3, 6>(0, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT, use2ndOrderIntegration), bias_old); - - Matrix Fexpected(15, 15); - Fexpected << df_dpos, df_dvel, df_dangle, df_dbias; - EXPECT(assert_equal(Fexpected, Factual,1e-5)); + newDeltaT), bias_old); + EXPECT(assert_equal(df_dbias, Factual.block<15, 6>(0, 9), 1e-5)); ////////////////////////////////////////////////////////////////////////////////////////////// // COMPUTE NUMERICAL DERIVATIVES FOR G @@ -444,24 +436,24 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { // Compute expected G wrt acc noise (15,3) Matrix df_daccNoise = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, _1, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), newMeasuredAcc); + deltaVij_old, deltaRij_old, bias_old, _1, newMeasuredOmega, newDeltaT + ), newMeasuredAcc); // rotation part has to be done properly, on manifold df_daccNoise.block<3, 3>(6, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, _1, newMeasuredOmega, newDeltaT, - use2ndOrderIntegration), newMeasuredAcc); + deltaVij_old, deltaRij_old, bias_old, _1, newMeasuredOmega, newDeltaT + ), newMeasuredAcc); // Compute expected G wrt gyro noise (15,3) Matrix df_domegaNoise = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, newMeasuredAcc, _1, newDeltaT, - use2ndOrderIntegration), newMeasuredOmega); + deltaVij_old, deltaRij_old, bias_old, newMeasuredAcc, _1, newDeltaT + ), newMeasuredOmega); // rotation part has to be done properly, on manifold df_domegaNoise.block<3, 3>(6, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, newMeasuredAcc, _1, newDeltaT, - use2ndOrderIntegration), newMeasuredOmega); + deltaVij_old, deltaRij_old, bias_old, newMeasuredAcc, _1, newDeltaT + ), newMeasuredOmega); // Compute expected G wrt bias random walk noise (15,6) Matrix df_rwBias(15, 6); // random walk on the bias does not appear in the first 9 entries @@ -472,13 +464,13 @@ TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { Matrix df_dinitBias = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT, use2ndOrderIntegration), bias_old); + newDeltaT), bias_old); // rotation part has to be done properly, on manifold df_dinitBias.block<3, 6>(6, 0) = numericalDerivative11( boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT, use2ndOrderIntegration), bias_old); + newDeltaT), bias_old); df_dinitBias.block<6, 6>(9, 0) = Z_6x6; // only has to influence first 9 rows Matrix Gexpected(15, 21); From b26bfb27acfb15dafab216ade09654d48ae949df Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 15:42:22 -0700 Subject: [PATCH 109/142] Removed F/G tests: derivatives no longer matched and are checked at a lower level anyways. --- gtsam/navigation/CombinedImuFactor.cpp | 18 +- gtsam/navigation/CombinedImuFactor.h | 4 +- gtsam/navigation/ImuFactor.cpp | 21 +- gtsam/navigation/ImuFactor.h | 5 +- .../tests/testCombinedImuFactor.cpp | 186 ------------- gtsam/navigation/tests/testImuFactor.cpp | 245 ------------------ 6 files changed, 7 insertions(+), 472 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 0b23d299c..4ee8f17de 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -66,8 +66,7 @@ void PreintegratedCombinedMeasurements::resetIntegration() { //------------------------------------------------------------------------------ void PreintegratedCombinedMeasurements::integrateMeasurement( - const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - OptionalJacobian<15, 15> F_test, OptionalJacobian<15, 21> G_test) { + const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT) { const Matrix3 dRij = deltaXij_.R(); // expensive when quaternion @@ -117,21 +116,6 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( D_v_R(&G_measCov_Gt) = temp; D_R_v(&G_measCov_Gt) = temp.transpose(); preintMeasCov_ = F * preintMeasCov_ * F.transpose() + G_measCov_Gt; - - // F_test and G_test are used for testing purposes and are not needed by the factor - if (F_test) { - (*F_test) << F; - } - if (G_test) { - // This is for testing & documentation - ///< measurementCovariance_ : cov[integrationError measuredAcc measuredOmega biasAccRandomWalk biasOmegaRandomWalk biasAccInit biasOmegaInit] in R^(21 x 21) - (*G_test) << // - -H_angles_biasomega, Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_3x3, H_angles_biasomega, // - Z_3x3, I_3x3 * deltaT, Z_3x3, Z_3x3, Z_3x3, Z_3x3, Z_3x3, // - Z_3x3, Z_3x3, -H_vel_biasacc, Z_3x3, Z_3x3, H_vel_biasacc, Z_3x3, // - Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3, Z_3x3, // - Z_3x3, Z_3x3, Z_3x3, Z_3x3, I_3x3, Z_3x3, Z_3x3; - } } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/CombinedImuFactor.h b/gtsam/navigation/CombinedImuFactor.h index 5ac6d8a7e..6ed382966 100644 --- a/gtsam/navigation/CombinedImuFactor.h +++ b/gtsam/navigation/CombinedImuFactor.h @@ -143,9 +143,7 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase { * frame) */ void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double deltaT, - OptionalJacobian<15, 15> F_test = boost::none, - OptionalJacobian<15, 21> G_test = boost::none); + const Vector3& measuredOmega, double deltaT); /// methods to access class variables Matrix preintMeasCov() const { return preintMeasCov_; } diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 279c95d3c..90a369bb1 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -48,21 +48,9 @@ void PreintegratedImuMeasurements::resetIntegration() { preintMeasCov_.setZero(); } -//------------------------------------------------------------------------------ -// sugar for derivative blocks -#define D_R_R(H) (H)->block<3,3>(0,0) -#define D_R_t(H) (H)->block<3,3>(0,3) -#define D_R_v(H) (H)->block<3,3>(0,6) -#define D_t_R(H) (H)->block<3,3>(3,0) -#define D_t_t(H) (H)->block<3,3>(3,3) -#define D_t_v(H) (H)->block<3,3>(3,6) -#define D_v_R(H) (H)->block<3,3>(6,0) -#define D_v_t(H) (H)->block<3,3>(6,3) -#define D_v_v(H) (H)->block<3,3>(6,6) //------------------------------------------------------------------------------ void PreintegratedImuMeasurements::integrateMeasurement( - const Vector3& measuredAcc, const Vector3& measuredOmega, double dt, - OptionalJacobian<9, 9> outF, OptionalJacobian<9, 9> G) { + const Vector3& measuredAcc, const Vector3& measuredOmega, double dt) { static const Matrix93 Gi = (Matrix93() << Z_3x3, I_3x3, Z_3x3).finished(); @@ -70,7 +58,8 @@ void PreintegratedImuMeasurements::integrateMeasurement( Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) Matrix93 G1, G2; Matrix3 D_incrR_integratedOmega; - updatePreintegratedMeasurements(measuredAcc, measuredOmega, dt, &D_incrR_integratedOmega, &F, &G1, &G2); + updatePreintegratedMeasurements(measuredAcc, measuredOmega, dt, + &D_incrR_integratedOmega, &F, &G1, &G2); // first order covariance propagation: // as in [2] we consider a first order propagation that can be seen as a prediction phase in EKF @@ -82,10 +71,8 @@ void PreintegratedImuMeasurements::integrateMeasurement( + G1 * (p().accelerometerCovariance / dt) * G1.transpose() + Gi * (p().integrationCovariance * dt) * Gi.transpose() // NOTE(frank): (Gi*dt)*(C/dt)*(Gi'*dt) + G2 * (p().gyroscopeCovariance / dt) * G2.transpose(); - - if (outF) *outF = F; - if (G) *G << G2, Gi*dt, G1; // NOTE(frank): order here is R,P,V } + //------------------------------------------------------------------------------ PreintegratedImuMeasurements::PreintegratedImuMeasurements( const imuBias::ConstantBias& biasHat, const Matrix3& measuredAccCovariance, diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 1d32623bd..36251a246 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -97,12 +97,9 @@ public: * @param measuredAcc Measured acceleration (in body frame, as given by the sensor) * @param measuredOmega Measured angular velocity (as given by the sensor) * @param dt Time interval between this and the last IMU measurement - * @param F, F Jacobians used internally (only needed for testing) */ void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double dt, // - OptionalJacobian<9, 9> F = boost::none, // - OptionalJacobian<9, 9> G = boost::none); + const Vector3& measuredOmega, double dt); /// Return pre-integrated measurement covariance Matrix preintMeasCov() const { return preintMeasCov_; } diff --git a/gtsam/navigation/tests/testCombinedImuFactor.cpp b/gtsam/navigation/tests/testCombinedImuFactor.cpp index 8da1cc3e7..d473207da 100644 --- a/gtsam/navigation/tests/testCombinedImuFactor.cpp +++ b/gtsam/navigation/tests/testCombinedImuFactor.cpp @@ -40,40 +40,6 @@ using symbol_shorthand::V; using symbol_shorthand::B; namespace { -/* ************************************************************************* */ -// Auxiliary functions to test Jacobians F and G used for -// covariance propagation during preintegration -/* ************************************************************************* */ -Vector updatePreintegratedMeasurementsTest(const Vector3 deltaPij_old, - const Vector3& deltaVij_old, const Rot3& deltaRij_old, - const imuBias::ConstantBias& bias_old, const Vector3& correctedAcc, - const Vector3& correctedOmega, const double deltaT) { - - Matrix3 dRij = deltaRij_old.matrix(); - Vector3 temp = dRij * (correctedAcc - bias_old.accelerometer()) * deltaT; - Vector3 deltaPij_new; - deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; - Vector3 deltaVij_new = deltaVij_old + temp; - Rot3 deltaRij_new = deltaRij_old - * Rot3::Expmap((correctedOmega - bias_old.gyroscope()) * deltaT); - Vector3 logDeltaRij_new = Rot3::Logmap(deltaRij_new); // not important any more - imuBias::ConstantBias bias_new(bias_old); - Vector result(15); - result << logDeltaRij_new, deltaPij_new, deltaVij_new, bias_new.vector(); - return result; -} - -Rot3 updatePreintegratedMeasurementsRot(const Vector3 deltaPij_old, - const Vector3& deltaVij_old, const Rot3& deltaRij_old, - const imuBias::ConstantBias& bias_old, const Vector3& correctedAcc, - const Vector3& correctedOmega, const double deltaT) { - - Vector result = updatePreintegratedMeasurementsTest(deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, correctedAcc, correctedOmega, - deltaT); - - return Rot3::Expmap(result.segment < 3 > (6)); -} // Auxiliary functions to test preintegrated Jacobians // delPdelBiasAcc_ delPdelBiasOmega_ delVdelBiasAcc_ delVdelBiasOmega_ delRdelBiasOmega_ @@ -334,158 +300,6 @@ TEST(CombinedImuFactor, PredictRotation) { EXPECT(assert_equal(expectedPose, x2, tol)); } -/* ************************************************************************* */ -TEST( CombinedImuFactor, JacobianPreintegratedCovariancePropagation ) { - // Linearization point - imuBias::ConstantBias bias_old = imuBias::ConstantBias(); ///< Current estimate of acceleration and rotation rate biases - - // Measurements - list measuredAccs, measuredOmegas; - list deltaTs; - measuredAccs.push_back(Vector3(0.1, 0.0, 0.0)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, 0.0, 0.0)); - deltaTs.push_back(0.01); - measuredAccs.push_back(Vector3(0.1, 0.0, 0.0)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, 0.0, 0.0)); - deltaTs.push_back(0.01); - for (int i = 1; i < 100; i++) { - measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back( - Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); - deltaTs.push_back(0.01); - } - // Actual pim values - CombinedImuFactor::CombinedPreintegratedMeasurements pim = - evaluatePreintegratedMeasurements(bias_old, measuredAccs, measuredOmegas, - deltaTs); - - // so far we only created a nontrivial linearization point for the preintegrated measurements - // Now we add a new measurement and ask for Jacobians - const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); - const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); - const double newDeltaT = 0.01; - const Rot3 deltaRij_old = pim.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = pim.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = pim.deltaPij(); // before adding new measurement - - Matrix oldPreintCovariance = pim.preintMeasCov(); - - Matrix Factual, Gactual; - pim.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, newDeltaT, Factual, - Gactual); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE NUMERICAL DERIVATIVES FOR F - ////////////////////////////////////////////////////////////////////////////////////////////// - // Compute expected F wrt positions (15,3) - Matrix df_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, _1, deltaVij_old, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT - ), deltaPij_old); - // rotation part has to be done properly, on manifold - df_dpos.block<3, 3>(0, 0) = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, _1, deltaVij_old, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT - ), deltaPij_old); - EXPECT(assert_equal(df_dpos, Factual.block<15, 3>(0, 3), 1e-5)); - - // Compute expected F wrt velocities (15,3) - Matrix df_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, _1, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT - ), deltaVij_old); - // rotation part has to be done properly, on manifold - df_dvel.block<3, 3>(0, 0) = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, _1, - deltaRij_old, bias_old, newMeasuredAcc, newMeasuredOmega, newDeltaT - ), deltaVij_old); - EXPECT(assert_equal(df_dvel, Factual.block<15, 3>(0, 6), 1e-5)); - - // Compute expected F wrt angles (15,3) - Matrix df_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, _1, bias_old, newMeasuredAcc, newMeasuredOmega, - newDeltaT), deltaRij_old); - // rotation part has to be done properly, on manifold - df_dangle.block<3, 3>(0, 0) = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, _1, bias_old, newMeasuredAcc, newMeasuredOmega, - newDeltaT), deltaRij_old); - EXPECT(assert_equal(df_dangle, Factual.block<15, 3>(0, 0), 1e-5)); - - // Compute expected F wrt biases (15,6) - Matrix df_dbias = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT), bias_old); - // rotation part has to be done properly, on manifold - df_dbias.block<3, 6>(0, 0) = - numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT), bias_old); - EXPECT(assert_equal(df_dbias, Factual.block<15, 6>(0, 9), 1e-5)); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE NUMERICAL DERIVATIVES FOR G - ////////////////////////////////////////////////////////////////////////////////////////////// - // Compute expected G wrt integration noise - Matrix df_dintNoise(15, 3); - df_dintNoise << I_3x3 * newDeltaT, Z_3x3, Z_3x3, Z_3x3, Z_3x3; - - // Compute expected G wrt acc noise (15,3) - Matrix df_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, _1, newMeasuredOmega, newDeltaT - ), newMeasuredAcc); - // rotation part has to be done properly, on manifold - df_daccNoise.block<3, 3>(6, 0) = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, _1, newMeasuredOmega, newDeltaT - ), newMeasuredAcc); - - // Compute expected G wrt gyro noise (15,3) - Matrix df_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, newMeasuredAcc, _1, newDeltaT - ), newMeasuredOmega); - // rotation part has to be done properly, on manifold - df_domegaNoise.block<3, 3>(6, 0) = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, deltaRij_old, bias_old, newMeasuredAcc, _1, newDeltaT - ), newMeasuredOmega); - - // Compute expected G wrt bias random walk noise (15,6) - Matrix df_rwBias(15, 6); // random walk on the bias does not appear in the first 9 entries - df_rwBias.setZero(); - df_rwBias.block<6, 6>(9, 0) = I_6x6; - - // Compute expected G wrt gyro noise (15,6) - Matrix df_dinitBias = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsTest, deltaPij_old, - deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT), bias_old); - // rotation part has to be done properly, on manifold - df_dinitBias.block<3, 6>(6, 0) = numericalDerivative11( - boost::bind(&updatePreintegratedMeasurementsRot, deltaPij_old, - deltaVij_old, deltaRij_old, _1, newMeasuredAcc, newMeasuredOmega, - newDeltaT), bias_old); - df_dinitBias.block<6, 6>(9, 0) = Z_6x6; // only has to influence first 9 rows - - Matrix Gexpected(15, 21); - Gexpected << df_dintNoise, df_daccNoise, df_domegaNoise, df_rwBias, df_dinitBias; - - EXPECT(assert_equal(Gexpected, Gactual)); - - // Check covariance propagation - Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance - * Factual.transpose() + (1 / newDeltaT) * Gactual * Gactual.transpose(); - - Matrix newPreintCovarianceActual = pim.preintMeasCov(); - EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); -} - /* ************************************************************************* */ int main() { TestResult tr; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index fdf9fd04c..341f2d29c 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -53,30 +53,6 @@ Rot3 evaluateRotationError(const ImuFactor& factor, const Pose3& pose_i, factor.evaluateError(pose_i, vel_i, pose_j, vel_j, bias).head(3)); } -// Auxiliary functions to test Jacobians F and G used for -// covariance propagation during preintegration -/* ************************************************************************* */ -Vector6 updatePreintegratedPosVel(const Vector3 deltaPij_old, - const Vector3& deltaVij_old, const Rot3& deltaRij_old, - const Vector3& correctedAcc, const Vector3& correctedOmega, - const double deltaT) { - Matrix3 dRij = deltaRij_old.matrix(); - Vector3 temp = dRij * correctedAcc * deltaT; - Vector3 deltaPij_new; - deltaPij_new = deltaPij_old + deltaVij_old * deltaT + 0.5 * temp * deltaT; - Vector3 deltaVij_new = deltaVij_old + temp; - - Vector6 result; - result << deltaPij_new, deltaVij_new; - return result; -} - -Rot3 updatePreintegratedRot(const Rot3& deltaRij_old, - const Vector3& correctedOmega, const double deltaT) { - Rot3 deltaRij_new = deltaRij_old * Rot3::Expmap(correctedOmega * deltaT); - return deltaRij_new; -} - // Define covariance matrices /* ************************************************************************* */ double accNoiseVar = 0.01; @@ -565,227 +541,6 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega())); } -/* ************************************************************************* */ -TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) { - // Measurements - const Vector3 measuredAcc(0.1, 0.0, 0.0); - const Vector3 measuredOmega(M_PI / 100.0, 0.0, 0.0); - const double deltaT = 0.01; - list measuredAccs, measuredOmegas; - list deltaTs; - measuredAccs.push_back(measuredAcc); - measuredOmegas.push_back(measuredOmega); - deltaTs.push_back(deltaT); - measuredAccs.push_back(measuredAcc); - measuredOmegas.push_back(measuredOmega); - deltaTs.push_back(deltaT); - for (int i = 1; i < 100; i++) { - measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back( - Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); - deltaTs.push_back(deltaT); - } - // Actual preintegrated values - PreintegratedImuMeasurements pim = evaluatePreintegratedMeasurements( - kZeroBias, measuredAccs, measuredOmegas, deltaTs); - - // so far we only created a nontrivial linearization point for the preintegrated measurements - // Now we add a new measurement and ask for Jacobians - const Rot3 deltaRij_old = pim.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = pim.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = pim.deltaPij(); // before adding new measurement - - Matrix oldPreintCovariance = pim.preintMeasCov(); - - Matrix Factual, Gactual; - pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT, - Factual, Gactual); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE NUMERICAL DERIVATIVES FOR F - ////////////////////////////////////////////////////////////////////////////////////////////// - // Compute expected f_pos_vel wrt positions - boost::function f = - boost::bind(&updatePreintegratedPosVel, _1, _2, _3, measuredAcc, - measuredOmega, deltaT); - Matrix63 dfpv_dpos = numericalDerivative31(f, deltaPij_old, deltaVij_old, - deltaRij_old); - - // Compute expected f_pos_vel wrt velocities - Matrix63 dfpv_dvel = numericalDerivative32(f, deltaPij_old, deltaVij_old, - deltaRij_old); - - // Compute expected f_pos_vel wrt angles - Matrix63 dfpv_dangle = numericalDerivative33(f, deltaPij_old, deltaVij_old, - deltaRij_old); - - // Compute expected f_rot wrt angles - Matrix3 dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, measuredOmega, deltaT), - deltaRij_old); - - Matrix Fexpected(9, 9); - Fexpected << // - dfr_dangle, Z_3x3, Z_3x3, // - dfpv_dangle, dfpv_dpos, dfpv_dvel; - - EXPECT(assert_equal(Fexpected, Factual)); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE NUMERICAL DERIVATIVES FOR G - ////////////////////////////////////////////////////////////////////////////////////////////// - // Compute jacobian wrt integration noise - Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3 * deltaT, Z_3x3; - - // Compute jacobian wrt acc noise - Matrix63 dgpv_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, measuredOmega, deltaT), measuredAcc); - - // Compute expected F wrt gyro noise - Matrix63 dgpv_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, measuredAcc, _1, deltaT), measuredOmega); - - // Compute expected f_rot wrt gyro noise - Matrix3 dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, deltaT), - measuredOmega); - - Matrix Gexpected(9, 9); - Gexpected << // - dgr_dangle, Z_3x3, Z_3x3, // - dgpv_domegaNoise, dgpv_dintNoise, dgpv_daccNoise; - - EXPECT(assert_equal(Gexpected, Gactual)); - - // Check covariance propagation - Matrix9 measurementCovariance; - measurementCovariance << // - omegaNoiseVar * I_3x3, Z_3x3, Z_3x3, // - Z_3x3, intNoiseVar * I_3x3, Z_3x3, // - Z_3x3, Z_3x3, accNoiseVar * I_3x3; - - Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance - * Factual.transpose() - + (1 / deltaT) * Gactual * measurementCovariance * Gactual.transpose(); - - Matrix newPreintCovarianceActual = pim.preintMeasCov(); - EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); -} - -/* ************************************************************************* */ -TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) { - // Measurements - list measuredAccs, measuredOmegas; - list deltaTs; - measuredAccs.push_back(Vector3(0.1, 0.0, 0.0)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, 0.0, 0.0)); - deltaTs.push_back(0.01); - measuredAccs.push_back(Vector3(0.1, 0.0, 0.0)); - measuredOmegas.push_back(Vector3(M_PI / 100.0, 0.0, 0.0)); - deltaTs.push_back(0.01); - for (int i = 1; i < 100; i++) { - measuredAccs.push_back(Vector3(0.05, 0.09, 0.01)); - measuredOmegas.push_back( - Vector3(M_PI / 100.0, M_PI / 300.0, 2 * M_PI / 100.0)); - deltaTs.push_back(0.01); - } - // Actual preintegrated values - PreintegratedImuMeasurements preintegrated = - evaluatePreintegratedMeasurements(kZeroBias, measuredAccs, measuredOmegas, - deltaTs); - - // so far we only created a nontrivial linearization point for the preintegrated measurements - // Now we add a new measurement and ask for Jacobians - const Vector3 newMeasuredAcc = Vector3(0.1, 0.0, 0.0); - const Vector3 newMeasuredOmega = Vector3(M_PI / 100.0, 0.0, 0.0); - const double newDeltaT = 0.01; - const Rot3 deltaRij_old = preintegrated.deltaRij(); // before adding new measurement - const Vector3 deltaVij_old = preintegrated.deltaVij(); // before adding new measurement - const Vector3 deltaPij_old = preintegrated.deltaPij(); // before adding new measurement - - Matrix oldPreintCovariance = preintegrated.preintMeasCov(); - - Matrix Factual, Gactual; - preintegrated.integrateMeasurement(newMeasuredAcc, newMeasuredOmega, - newDeltaT, Factual, Gactual); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE NUMERICAL DERIVATIVES FOR F - ////////////////////////////////////////////////////////////////////////////////////////////// - // Compute expected f_pos_vel wrt positions - Matrix dfpv_dpos = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, _1, deltaVij_old, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaPij_old); - - // Compute expected f_pos_vel wrt velocities - Matrix dfpv_dvel = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, _1, deltaRij_old, - newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaVij_old); - - // Compute expected f_pos_vel wrt angles - Matrix dfpv_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, _1, - newMeasuredAcc, newMeasuredOmega, newDeltaT), deltaRij_old); - - // Compute expected f_rot wrt angles - Matrix dfr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, _1, newMeasuredOmega, newDeltaT), - deltaRij_old); - - Matrix Fexpected(9, 9); - Fexpected << // - dfr_dangle, Z_3x3, Z_3x3, // - dfpv_dangle, dfpv_dpos, dfpv_dvel; - - EXPECT(assert_equal(Fexpected, Factual)); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // COMPUTE NUMERICAL DERIVATIVES FOR G - ////////////////////////////////////////////////////////////////////////////////////////////// - // Compute jacobian wrt integration noise - Matrix dgpv_dintNoise(6, 3); - dgpv_dintNoise << I_3x3 * newDeltaT, Z_3x3; - - // Compute jacobian wrt acc noise - Matrix dgpv_daccNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, _1, newMeasuredOmega, newDeltaT), newMeasuredAcc); - - // Compute expected F wrt gyro noise - Matrix dgpv_domegaNoise = numericalDerivative11( - boost::bind(&updatePreintegratedPosVel, deltaPij_old, deltaVij_old, - deltaRij_old, newMeasuredAcc, _1, newDeltaT), newMeasuredOmega); - - // Compute expected f_rot wrt gyro noise - Matrix dgr_dangle = numericalDerivative11( - boost::bind(&updatePreintegratedRot, deltaRij_old, _1, newDeltaT), - newMeasuredOmega); - - Matrix Gexpected(9, 9); - Gexpected << // - dgr_dangle, Z_3x3, Z_3x3, // - dgpv_domegaNoise, dgpv_dintNoise, dgpv_daccNoise; - - EXPECT(assert_equal(Gexpected, Gactual)); - - // Check covariance propagation - Matrix9 measurementCovariance; - measurementCovariance << // - omegaNoiseVar * I_3x3, Z_3x3, Z_3x3, // - Z_3x3, intNoiseVar * I_3x3, Z_3x3, // - Z_3x3, Z_3x3, accNoiseVar * I_3x3; - - Matrix newPreintCovarianceExpected = Factual * oldPreintCovariance - * Factual.transpose() - + (1 / newDeltaT) * Gactual * measurementCovariance * Gactual.transpose(); - - Matrix newPreintCovarianceActual = preintegrated.preintMeasCov(); - EXPECT(assert_equal(newPreintCovarianceExpected, newPreintCovarianceActual)); -} - /* ************************************************************************* */ Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, const NavState& state, const imuBias::ConstantBias& bias, double dt, From 7224162e60f40d396fb20b6eac62c42d574d0c1e Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 31 Jul 2015 21:04:16 -0700 Subject: [PATCH 110/142] More Monte Carlo... --- gtsam/navigation/tests/testImuFactor.cpp | 122 ++++++++++++----------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 341f2d29c..03cff1f37 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -12,7 +12,7 @@ /** * @file testImuFactor.cpp * @brief Unit test for ImuFactor - * @author Luca Carlone, Stephen Williams, Richard Roberts + * @author Luca Carlone, Stephen Williams, Richard Roberts, Frank Dellaert */ #include @@ -113,6 +113,52 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { } // namespace +/* ************************************************************************* */ +Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, + const NavState& state, const imuBias::ConstantBias& bias, double dt, + const Pose3& body_P_sensor, const Vector3& measuredAcc, + const Vector3& measuredOmega, size_t N = 10000) { + // Get mean prediction from "ground truth" measurements + PreintegratedImuMeasurements pim1 = pim; + for (size_t k=0;k<10;k++) + pim1.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); + NavState mean = pim1.predict(state, bias); + cout << "pim1.preintMeasCov():" << endl; + cout << pim1.preintMeasCov() << endl; + + // Do a Monte Carlo analysis to determine empirical density on the predicted state + Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar/dt))); + Sampler sampleOmegaNoise(Vector3::Constant(sqrt(omegaNoiseVar/dt))); + Matrix samples(9, N); + Vector9 sum = Vector9::Zero(); + for (size_t i = 0; i < N; i++) { + PreintegratedImuMeasurements pim2 = pim; + for (size_t k=0;k<10;k++) { + Vector3 perturbedAcc = measuredAcc + sampleAccelerationNoise.sample(); + Vector3 perturbedOmega = measuredOmega + sampleOmegaNoise.sample(); + pim2.integrateMeasurement(perturbedAcc, perturbedOmega, dt, body_P_sensor); + } + NavState prediction = pim2.predict(state, bias); + samples.col(i) = mean.localCoordinates(prediction); + sum += samples.col(i); + } + Vector9 sampleMean = sum / N; + cout << ":" << endl; + cout << sampleMean << endl; + Matrix9 Q; + Q.setZero(); + for (size_t i = 0; i < N; i++) { + Vector9 xi = samples.col(i); +#ifdef SUBTRACT_SAMPLE_MEAN + xi -= sampleMean; +#endif + Q += xi * xi.transpose() / (N - 1); + } + cout << "Q:" << endl; + cout << Q << endl; + return Q; +} + /* ************************************************************************* */ TEST(ImuFactor, StraightLine) { // Set up IMU measurements @@ -124,7 +170,7 @@ TEST(ImuFactor, StraightLine) { // The body itself has Z axis pointing down static const Rot3 nRb(Point3(0, 1, 0), Point3(1, 0, 0), Point3(0, 0, -1)); static const Point3 initial_position(10, 20, 0); - static const Vector3 initial_velocity(v,0,0); + static const Vector3 initial_velocity(v, 0, 0); static const NavState state1(nRb, initial_position, initial_velocity); // set up acceleration in X direction, no angular velocity. @@ -134,12 +180,20 @@ TEST(ImuFactor, StraightLine) { // Create parameters assuming nav frame has Z up boost::shared_ptr p = PreintegratedImuMeasurements::Params::MakeSharedU(g); + p->accelerometerCovariance = kMeasuredAccCovariance; + p->gyroscopeCovariance = kMeasuredOmegaCovariance; // Now, preintegrate for 3 seconds, in 10 steps PreintegratedImuMeasurements pim(p, kZeroBiasHat); for (size_t i = 0; i < 10; i++) pim.integrateMeasurement(measuredAcc, measuredOmega, dt / 10); + // Get mean prediction from "ground truth" measurements + PreintegratedImuMeasurements pimMC(kZeroBiasHat, p->accelerometerCovariance, + p->gyroscopeCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise + Matrix9 Q = MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, + measuredOmega); + // Check integrated quantities in body frame: gravity measured by IMU is integrated! Vector3 b_deltaP(a * dt22, 0, -g * dt22); Vector3 b_deltaV(a * dt, 0, -g * dt); @@ -153,7 +207,7 @@ TEST(ImuFactor, StraightLine) { EXPECT(assert_equal(expectedBC, pim.biasCorrectedDelta(kZeroBias))); // Check prediction in nav, note we move along x in body, along y in nav - Point3 expected_position(10 + v*dt, 20 + a * dt22, 0); + Point3 expected_position(10 + v * dt, 20 + a * dt22, 0); Velocity3 expected_velocity(v, a * dt, 0); NavState expected(nRb, expected_position, expected_velocity); EXPECT(assert_equal(expected, pim.predict(state1, kZeroBias))); @@ -541,48 +595,6 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega())); } -/* ************************************************************************* */ -Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, - const NavState& state, const imuBias::ConstantBias& bias, double dt, - const Pose3& body_P_sensor, const Vector3& measuredAcc, - const Vector3& measuredOmega, size_t N = 100) { - // Get mean prediction from "ground truth" measurements - PreintegratedImuMeasurements pim1 = pim; - for (size_t k=0;k<10;k++) - pim1.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); - NavState mean = pim1.predict(state, bias); - cout << "pim1.preintMeasCov():" << endl; - cout << pim1.preintMeasCov() << endl; - - // Do a Monte Carlo analysis to determine empirical density on the predicted state - Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar))); - Sampler sampleOmegaNoise(Vector3::Constant(sqrt(omegaNoiseVar))); - Matrix samples(9, N); - Vector9 sum = Vector9::Zero(); - for (size_t i = 0; i < N; i++) { - PreintegratedImuMeasurements pim2 = pim; - Vector3 perturbedAcc = measuredAcc + sampleAccelerationNoise.sample(); - Vector3 perturbedOmega = measuredOmega + sampleOmegaNoise.sample(); - for (size_t k=0;k<10;k++) - pim2.integrateMeasurement(perturbedAcc, perturbedOmega, dt, body_P_sensor); - NavState prediction = pim2.predict(state, bias); - samples.col(i) = mean.localCoordinates(prediction); - sum += samples.col(i); - } - Vector9 sampleMean = sum / N; - cout << ":" << endl; - cout << sampleMean << endl; - Matrix9 Q; - Q.setZero(); - for (size_t i = 0; i < N; i++) { - Vector9 xi = samples.col(i) - sampleMean; - Q += xi * xi.transpose() / (N - 1); - } - cout << "Q:" << endl; - cout << Q << endl; - return Q; -} - /* ************************************************************************* */ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) @@ -597,16 +609,16 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { measuredOmega << 0, 0, M_PI / 10.0 + 0.3; Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown) + Vector3(0.2, 0.0, 0.0); - double dt = 1.0; + double dt = 0.1; Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.1, 0.1)), Point3(1, 0, 0)); imuBias::ConstantBias biasHat(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, kIntegrationErrorCovariance, true); - Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, dt, body_P_sensor, - measuredAcc, measuredOmega, 1000); + kMeasuredOmegaCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise + Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), bias, dt, body_P_sensor, + measuredAcc, measuredOmega); Matrix expected(9,9); expected << @@ -735,14 +747,13 @@ TEST(ImuFactor, PredictArbitrary) { Vector3 measuredAcc(0.1, 0.2, -9.81); double dt = 0.001; - ImuFactor::PreintegratedMeasurements pim( - biasHat, - kMeasuredAccCovariance, kMeasuredOmegaCovariance, - kIntegrationErrorCovariance, true); + ImuFactor::PreintegratedMeasurements pim(biasHat, kMeasuredAccCovariance, + kMeasuredOmegaCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise Pose3 x1; Vector3 v1 = Vector3(0, 0, 0); - Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), biasHat, 0.1, Pose3(), - measuredAcc, measuredOmega, 1000); + imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); + Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), bias, 0.1, Pose3(), + measuredAcc, measuredOmega); for (int i = 0; i < 1000; ++i) pim.integrateMeasurement(measuredAcc, measuredOmega, dt); @@ -767,7 +778,6 @@ TEST(ImuFactor, PredictArbitrary) { // Predict Pose3 x2; Vector3 v2; - imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); ImuFactor::Predict(x1, v1, x2, v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); From f8df938b30c384f406ee46f865e2a645e8cc3f94 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 1 Aug 2015 17:17:14 -0700 Subject: [PATCH 111/142] New naming, old derivative code --- gtsam/navigation/CombinedImuFactor.cpp | 2 +- gtsam/navigation/ImuFactor.cpp | 12 +++++++++++- gtsam/navigation/PreintegrationBase.cpp | 12 ++++++------ gtsam/navigation/PreintegrationBase.h | 17 +++++++++++------ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/gtsam/navigation/CombinedImuFactor.cpp b/gtsam/navigation/CombinedImuFactor.cpp index 4ee8f17de..a697a5e4c 100644 --- a/gtsam/navigation/CombinedImuFactor.cpp +++ b/gtsam/navigation/CombinedImuFactor.cpp @@ -74,7 +74,7 @@ void PreintegratedCombinedMeasurements::integrateMeasurement( Matrix3 D_incrR_integratedOmega; // Right jacobian computed at theta_incr Matrix9 F_9x9; // overall Jacobian wrt preintegrated measurements (df/dx) Matrix93 G1,G2; - updatePreintegratedMeasurements(measuredAcc, measuredOmega, deltaT, + PreintegrationBase::update(measuredAcc, measuredOmega, deltaT, &D_incrR_integratedOmega, &F_9x9, &G1, &G2); // Update preintegrated measurements covariance: as in [2] we consider a first order propagation that diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 90a369bb1..9a0d3d94a 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -58,7 +58,7 @@ void PreintegratedImuMeasurements::integrateMeasurement( Matrix9 F; // overall Jacobian wrt preintegrated measurements (df/dx) Matrix93 G1, G2; Matrix3 D_incrR_integratedOmega; - updatePreintegratedMeasurements(measuredAcc, measuredOmega, dt, + PreintegrationBase::update(measuredAcc, measuredOmega, dt, &D_incrR_integratedOmega, &F, &G1, &G2); // first order covariance propagation: @@ -67,10 +67,20 @@ void PreintegratedImuMeasurements::integrateMeasurement( // preintMeasCov = F * preintMeasCov * F.transpose() + G * (1/deltaT) * measurementCovariance * G' // NOTE 1: (1/deltaT) allows to pass from continuous time noise to discrete time noise // measurementCovariance_discrete = measurementCovariance_contTime * (1/deltaT) +#ifdef OLD_JACOBIAN_CALCULATION + Matrix9 G; + G << G1, Gi, G2; + Matrix9 Cov; + Cov << p().accelerometerCovariance / dt, Z_3x3, Z_3x3, + Z_3x3, p().integrationCovariance * dt, Z_3x3, + Z_3x3, Z_3x3, p().gyroscopeCovariance / dt; + preintMeasCov_ = F * preintMeasCov_ * F.transpose() + G * Cov * G.transpose(); +#else preintMeasCov_ = F * preintMeasCov_ * F.transpose() + G1 * (p().accelerometerCovariance / dt) * G1.transpose() + Gi * (p().integrationCovariance * dt) * Gi.transpose() // NOTE(frank): (Gi*dt)*(C/dt)*(Gi'*dt) + G2 * (p().gyroscopeCovariance / dt) * G2.transpose(); +#endif } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 0d51e59f9..0e40c3183 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -61,9 +61,9 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, } //------------------------------------------------------------------------------ -NavState PreintegrationBase::update(const Vector3& measuredAcc, - const Vector3& measuredOmega, const double dt, Matrix9* F, Matrix93* G1, - Matrix93* G2) const { +NavState PreintegrationBase::updatedDeltaXij(const Vector3& measuredAcc, + const Vector3& measuredOmega, const double dt, OptionalJacobian<9, 9> F, + OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { // Correct for bias in the sensor frame Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); @@ -90,7 +90,7 @@ NavState PreintegrationBase::update(const Vector3& measuredAcc, } //------------------------------------------------------------------------------ -void PreintegrationBase::updatePreintegratedMeasurements( +void PreintegrationBase::update( const Vector3& measuredAcc, const Vector3& measuredOmega, const double dt, Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { @@ -99,7 +99,7 @@ void PreintegrationBase::updatePreintegratedMeasurements( // Do update deltaTij_ += dt; - deltaXij_ = update(measuredAcc, measuredOmega, dt, F, G1, G2); // functional + deltaXij_ = updatedDeltaXij(measuredAcc, measuredOmega, dt, F, G1, G2); // functional // Update Jacobians // TODO(frank): we are repeating some computation here: accessible in F ? @@ -139,7 +139,7 @@ Vector9 PreintegrationBase::biasCorrectedDelta( Vector9 xi; Matrix3 D_dR_correctedRij; // TODO(frank): could line below be simplified? It is equivalent to - // LogMap(deltaRij_.compose(Expmap(delRdelBiasOmega_ * biasIncr.gyroscope()))) + // LogMap(deltaRij_.compose(Expmap(biasInducedOmega))) NavState::dR(xi) = Rot3::Logmap(correctedRij, H ? &D_dR_correctedRij : 0); NavState::dP(xi) = deltaPij() + delPdelBiasAcc_ * biasIncr.accelerometer() + delPdelBiasOmega_ * biasIncr.gyroscope(); diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 95de5e935..af83bb0e0 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -146,6 +146,9 @@ public: } /// getters + const NavState& deltaXij() const { + return deltaXij_; + } const double& deltaTij() const { return deltaTij_; } @@ -189,13 +192,15 @@ public: bool equals(const PreintegrationBase& other, double tol) const; /// Calculate the updated preintegrated measurement, does not modify - NavState update(const Vector3& measuredAcc, const Vector3& measuredOmega, - const double dt, Matrix9* F, Matrix93* G1, Matrix93* G2) const; + NavState updatedDeltaXij(const Vector3& measuredAcc, + const Vector3& measuredOmega, const double dt, OptionalJacobian<9, 9> F = + boost::none, OptionalJacobian<9, 3> G1 = boost::none, + OptionalJacobian<9, 3> G2 = boost::none) const; - /// Update preintegrated measurements - void updatePreintegratedMeasurements(const Vector3& measuredAcc, - const Vector3& measuredOmega, const double deltaT, - Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2); + /// Update preintegrated measurements and get derivatives + void update(const Vector3& measuredAcc, const Vector3& measuredOmega, + const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F, + Matrix93* G1, Matrix93* G2); /// Given the estimate of the bias, return a NavState tangent vector /// summarizing the preintegrated IMU measurements so far From b8f05e1e3569c143e808f109e754427b18e2a6bd Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 1 Aug 2015 17:21:15 -0700 Subject: [PATCH 112/142] More tests --- gtsam/navigation/tests/testNavState.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/gtsam/navigation/tests/testNavState.cpp b/gtsam/navigation/tests/testNavState.cpp index b05a8a3e5..a62ca06a8 100644 --- a/gtsam/navigation/tests/testNavState.cpp +++ b/gtsam/navigation/tests/testNavState.cpp @@ -194,23 +194,31 @@ TEST( NavState, Lie ) { /* ************************************************************************* */ TEST(NavState, Update) { - const Vector3 omega(M_PI / 100.0, 0.0, 0.0); - const Vector3 acc(0.1, 0.0, 0.0); - const double deltaT = 10; + Vector3 omega(M_PI / 100.0, 0.0, 0.0); + Vector3 acc(0.1, 0.0, 0.0); + double dt = 10; Matrix9 aF; Matrix93 aG1, aG2; boost::function update = - boost::bind(&NavState::update, _1, _2, _3, deltaT, boost::none, + boost::bind(&NavState::update, _1, _2, _3, dt, boost::none, boost::none, boost::none); Vector3 b_acc = kAttitude * acc; - NavState expected(kAttitude.expmap(deltaT * omega), - kPosition + Point3((kVelocity + b_acc * deltaT / 2) * deltaT), - kVelocity + b_acc * deltaT); - NavState actual = kState1.update(acc, omega, deltaT, aF, aG1, aG2); + NavState expected(kAttitude.expmap(dt * omega), + kPosition + Point3((kVelocity + b_acc * dt / 2) * dt), + kVelocity + b_acc * dt); + NavState actual = kState1.update(acc, omega, dt, aF, aG1, aG2); EXPECT(assert_equal(expected, actual)); EXPECT(assert_equal(numericalDerivative31(update, kState1, acc, omega, 1e-7), aF, 1e-7)); EXPECT(assert_equal(numericalDerivative32(update, kState1, acc, omega, 1e-7), aG1, 1e-7)); EXPECT(assert_equal(numericalDerivative33(update, kState1, acc, omega, 1e-7), aG2, 1e-7)); + + // Try different values + omega = Vector3(0.1, 0.2, 0.3); + acc = Vector3(0.4, 0.5, 0.6); + kState1.update(acc, omega, dt, aF, aG1, aG2); + EXPECT(assert_equal(numericalDerivative31(update, kState1, acc, omega, 1e-7), aF, 1e-7)); + EXPECT(assert_equal(numericalDerivative32(update, kState1, acc, omega, 1e-7), aG1, 1e-7)); + EXPECT(assert_equal(numericalDerivative33(update, kState1, acc, omega, 1e-7), aG2, 1e-7)); } /* ************************************************************************* */ From 18d09666301dffea0493510a24b96f472058c8ea Mon Sep 17 00:00:00 2001 From: dellaert Date: Sat, 1 Aug 2015 17:21:34 -0700 Subject: [PATCH 113/142] more Monte Carlo --- gtsam/navigation/tests/testImuFactor.cpp | 53 +++++++++++++++++++----- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 03cff1f37..51aa7c7c5 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -117,14 +117,26 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, const NavState& state, const imuBias::ConstantBias& bias, double dt, const Pose3& body_P_sensor, const Vector3& measuredAcc, - const Vector3& measuredOmega, size_t N = 10000) { + const Vector3& measuredOmega, size_t N = 10, size_t M = 1) { // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim1 = pim; - for (size_t k=0;k<10;k++) + for (size_t k=0;kaccelerometerCovariance = kMeasuredAccCovariance; p->gyroscopeCovariance = kMeasuredOmegaCovariance; + // Check G1 and G2 derivatives of pim.update + // Now, preintegrate for 3 seconds, in 10 steps PreintegratedImuMeasurements pim(p, kZeroBiasHat); for (size_t i = 0; i < 10; i++) pim.integrateMeasurement(measuredAcc, measuredOmega, dt / 10); - // Get mean prediction from "ground truth" measurements + Matrix93 aG1,aG2; + boost::function f = + boost::bind(&PreintegrationBase::updatedDeltaXij, pim, _1, _2, dt/10, + boost::none, boost::none, boost::none); + pim.updatedDeltaXij(measuredAcc, measuredOmega, dt / 10, boost::none, aG1, aG2); + EXPECT(assert_equal(numericalDerivative21(f, measuredAcc, measuredOmega, 1e-7), aG1, 1e-7)); + EXPECT(assert_equal(numericalDerivative22(f, measuredAcc, measuredOmega, 1e-7), aG2, 1e-7)); + cout << "aG1" << endl; + cout << aG1 << endl; + cout << "aG2:" << endl; + cout << aG2 << endl; + + // Do Monte-Carlo analysis PreintegratedImuMeasurements pimMC(kZeroBiasHat, p->accelerometerCovariance, p->gyroscopeCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise Matrix9 Q = MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, From 3ae998d31dc9474b433030a6bb5fb870368be2dd Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 4 Aug 2015 07:32:10 -0700 Subject: [PATCH 114/142] Renamed to make frame clear --- gtsam/navigation/PreintegrationBase.cpp | 28 ++++++++++++------------- gtsam/navigation/PreintegrationBase.h | 12 ++++++----- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 0e40c3183..f31621c32 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -61,13 +61,13 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, } //------------------------------------------------------------------------------ -NavState PreintegrationBase::updatedDeltaXij(const Vector3& measuredAcc, - const Vector3& measuredOmega, const double dt, OptionalJacobian<9, 9> F, +NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, + const Vector3& j_measuredOmega, const double dt, OptionalJacobian<9, 9> F, OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { // Correct for bias in the sensor frame - Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - Vector3 correctedOmega = biasHat_.correctGyroscope(measuredOmega); + Vector3 j_correctedAcc = biasHat_.correctAccelerometer(j_measuredAcc); + Vector3 j_correctedOmega = biasHat_.correctGyroscope(j_measuredOmega); // Compensate for sensor-body displacement if needed: we express the quantities // (originally in the IMU frame) into the body frame @@ -75,23 +75,23 @@ NavState PreintegrationBase::updatedDeltaXij(const Vector3& measuredAcc, if (p().body_P_sensor) { // Correct omega: slight duplication as this is also done in integrateMeasurement below Matrix3 bRs = p().body_P_sensor->rotation().matrix(); - correctedOmega = bRs * correctedOmega; // rotation rate vector in the body frame + j_correctedOmega = bRs * j_correctedOmega; // rotation rate vector in the body frame // Correct acceleration Vector3 b_arm = p().body_P_sensor->translation().vector(); - Vector3 b_velocity_bs = correctedOmega.cross(b_arm); // magnitude: omega * arm + Vector3 b_velocity_bs = j_correctedOmega.cross(b_arm); // magnitude: omega * arm // Subtract out the the centripetal acceleration from the measured one // to get linear acceleration vector in the body frame: - correctedAcc = bRs * correctedAcc - correctedOmega.cross(b_velocity_bs); + j_correctedAcc = bRs * j_correctedAcc - j_correctedOmega.cross(b_velocity_bs); } // Do update in one fell swoop - return deltaXij_.update(correctedAcc, correctedOmega, dt, F, G1, G2); + return deltaXij_.update(j_correctedAcc, j_correctedOmega, dt, F, G1, G2); } //------------------------------------------------------------------------------ void PreintegrationBase::update( - const Vector3& measuredAcc, const Vector3& measuredOmega, const double dt, + const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, const double dt, Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { // Save current rotation for updating Jacobians @@ -99,19 +99,19 @@ void PreintegrationBase::update( // Do update deltaTij_ += dt; - deltaXij_ = updatedDeltaXij(measuredAcc, measuredOmega, dt, F, G1, G2); // functional + deltaXij_ = updatedDeltaXij(j_measuredAcc, j_measuredOmega, dt, F, G1, G2); // functional // Update Jacobians // TODO(frank): we are repeating some computation here: accessible in F ? // Correct for bias in the sensor frame - Vector3 correctedAcc = biasHat_.correctAccelerometer(measuredAcc); - Vector3 correctedOmega = biasHat_.correctGyroscope(measuredOmega); + Vector3 j_correctedAcc = biasHat_.correctAccelerometer(j_measuredAcc); + Vector3 j_correctedOmega = biasHat_.correctGyroscope(j_measuredOmega); Matrix3 D_acc_R; - oldRij.rotate(correctedAcc, D_acc_R); + oldRij.rotate(j_correctedAcc, D_acc_R); const Matrix3 D_acc_biasOmega = D_acc_R * delRdelBiasOmega_; - const Vector3 integratedOmega = correctedOmega * dt; + const Vector3 integratedOmega = j_correctedOmega * dt; const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! const Matrix3 incrRt = incrR.transpose(); delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - *D_incrR_integratedOmega * dt; diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index af83bb0e0..301459b02 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -192,13 +192,15 @@ public: bool equals(const PreintegrationBase& other, double tol) const; /// Calculate the updated preintegrated measurement, does not modify - NavState updatedDeltaXij(const Vector3& measuredAcc, - const Vector3& measuredOmega, const double dt, OptionalJacobian<9, 9> F = - boost::none, OptionalJacobian<9, 3> G1 = boost::none, - OptionalJacobian<9, 3> G2 = boost::none) const; + /// It takes measured quantities in the j frame + NavState updatedDeltaXij(const Vector3& j_measuredAcc, + const Vector3& j_measuredOmega, const double dt, + OptionalJacobian<9, 9> F = boost::none, OptionalJacobian<9, 3> G1 = + boost::none, OptionalJacobian<9, 3> G2 = boost::none) const; /// Update preintegrated measurements and get derivatives - void update(const Vector3& measuredAcc, const Vector3& measuredOmega, + /// It takes measured quantities in the j frame + void update(const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2); From 9af69254b2858eeead8353a43988324d9b466024 Mon Sep 17 00:00:00 2001 From: dellaert Date: Tue, 4 Aug 2015 08:31:47 -0700 Subject: [PATCH 115/142] Refactored arm correction but there is still a difference in regression values. Did I introduce a bug? --- gtsam/navigation/ImuFactor.cpp | 3 +- gtsam/navigation/PreintegrationBase.cpp | 44 +++++++++++++++++-------- gtsam/navigation/PreintegrationBase.h | 4 +++ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 9a0d3d94a..e49168c43 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -103,7 +103,8 @@ PreintegratedImuMeasurements::PreintegratedImuMeasurements( void PreintegratedImuMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, const Pose3& body_P_sensor) { - p_->body_P_sensor = body_P_sensor; + // modify parameters to accommodate deprecated method:-( + p_->body_P_sensor.reset(body_P_sensor); integrateMeasurement(measuredAcc, measuredOmega, deltaT); } diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index f31621c32..5533a1f9b 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -61,9 +61,8 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, } //------------------------------------------------------------------------------ -NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, - const Vector3& j_measuredOmega, const double dt, OptionalJacobian<9, 9> F, - OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { +std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( + const Vector3& j_measuredAcc, const Vector3& j_measuredOmega) const { // Correct for bias in the sensor frame Vector3 j_correctedAcc = biasHat_.correctAccelerometer(j_measuredAcc); @@ -73,18 +72,35 @@ NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, // (originally in the IMU frame) into the body frame // Equations below assume the "body" frame is the CG if (p().body_P_sensor) { - // Correct omega: slight duplication as this is also done in integrateMeasurement below - Matrix3 bRs = p().body_P_sensor->rotation().matrix(); - j_correctedOmega = bRs * j_correctedOmega; // rotation rate vector in the body frame + // Correct omega to rotation rate vector in the body frame + const Matrix3 bRs = p().body_P_sensor->rotation().matrix(); + j_correctedOmega = bRs * j_correctedOmega; // Correct acceleration - Vector3 b_arm = p().body_P_sensor->translation().vector(); - Vector3 b_velocity_bs = j_correctedOmega.cross(b_arm); // magnitude: omega * arm - // Subtract out the the centripetal acceleration from the measured one - // to get linear acceleration vector in the body frame: - j_correctedAcc = bRs * j_correctedAcc - j_correctedOmega.cross(b_velocity_bs); + j_correctedAcc = bRs * j_correctedAcc; + const Vector3 b_arm = p().body_P_sensor->translation().vector(); + if (!b_arm.isZero()) { + // Subtract out the the centripetal acceleration from the measured one + // to get linear acceleration vector in the body frame: + const Matrix3 body_Omega_body = skewSymmetric(j_correctedOmega); + const Vector3 b_velocity_bs = body_Omega_body * b_arm; // magnitude: omega * arm + j_correctedAcc -= body_Omega_body * b_velocity_bs; + } } + // Do update in one fell swoop + return make_pair(j_correctedAcc, j_correctedOmega); +} + +//------------------------------------------------------------------------------ +NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, + const Vector3& j_measuredOmega, const double dt, OptionalJacobian<9, 9> F, + OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { + + Vector3 j_correctedAcc, j_correctedOmega; + boost::tie(j_correctedAcc, j_correctedOmega) = + correctMeasurementsByBiasAndSensorPose(j_measuredAcc, j_measuredOmega); + // Do update in one fell swoop return deltaXij_.update(j_correctedAcc, j_correctedOmega, dt, F, G1, G2); } @@ -103,9 +119,9 @@ void PreintegrationBase::update( // Update Jacobians // TODO(frank): we are repeating some computation here: accessible in F ? - // Correct for bias in the sensor frame - Vector3 j_correctedAcc = biasHat_.correctAccelerometer(j_measuredAcc); - Vector3 j_correctedOmega = biasHat_.correctGyroscope(j_measuredOmega); + Vector3 j_correctedAcc, j_correctedOmega; + boost::tie(j_correctedAcc, j_correctedOmega) = + correctMeasurementsByBiasAndSensorPose(j_measuredAcc, j_measuredOmega); Matrix3 D_acc_R; oldRij.rotate(j_correctedAcc, D_acc_R); diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 301459b02..f479b0b1e 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -191,6 +191,10 @@ public: /// check equality bool equals(const PreintegrationBase& other, double tol) const; + /// Subtract estimate and correct for sensor pose + std::pair correctMeasurementsByBiasAndSensorPose( + const Vector3& j_measuredAcc, const Vector3& j_measuredOmega) const; + /// Calculate the updated preintegrated measurement, does not modify /// It takes measured quantities in the j frame NavState updatedDeltaXij(const Vector3& j_measuredAcc, From 887c0d8f59a7770b91bda23723496ec1e1ee7baf Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 9 Aug 2015 11:13:15 -0700 Subject: [PATCH 116/142] Added .cpp file and a print for parameters --- gtsam/navigation/PreintegratedRotation.cpp | 111 +++++++++++++++++++++ gtsam/navigation/PreintegratedRotation.h | 99 +++++------------- 2 files changed, 138 insertions(+), 72 deletions(-) create mode 100644 gtsam/navigation/PreintegratedRotation.cpp diff --git a/gtsam/navigation/PreintegratedRotation.cpp b/gtsam/navigation/PreintegratedRotation.cpp new file mode 100644 index 000000000..708d1a3f6 --- /dev/null +++ b/gtsam/navigation/PreintegratedRotation.cpp @@ -0,0 +1,111 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file PreintegratedRotation.cpp + * @author Luca Carlone + * @author Stephen Williams + * @author Richard Roberts + * @author Vadim Indelman + * @author David Jensen + * @author Frank Dellaert + **/ + +#include "PreintegratedRotation.h" + +using namespace std; + +namespace gtsam { + +void PreintegratedRotation::Params::print(const string& s) const { + cout << s << endl; + cout << "gyroscopeCovariance:\n[\n" << gyroscopeCovariance << "\n]" << endl; + if (omegaCoriolis) + cout << "omegaCoriolis = (" << omegaCoriolis->transpose() << ")" + << endl; + if (body_P_sensor) + body_P_sensor->print("body_P_sensor"); +} + +void PreintegratedRotation::resetIntegration() { + deltaTij_ = 0.0; + deltaRij_ = Rot3(); + delRdelBiasOmega_ = Z_3x3; +} + +void PreintegratedRotation::print(const string& s) const { + cout << s << endl; + cout << " deltaTij [" << deltaTij_ << "]" << endl; + cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << endl; +} + +bool PreintegratedRotation::equals(const PreintegratedRotation& other, + double tol) const { + return deltaRij_.equals(other.deltaRij_, tol) + && fabs(deltaTij_ - other.deltaTij_) < tol + && equal_with_abs_tol(delRdelBiasOmega_, other.delRdelBiasOmega_, tol); +} + +Rot3 PreintegratedRotation::incrementalRotation(const Vector3& measuredOmega, + const Vector3& biasHat, double deltaT, + OptionalJacobian<3, 3> D_incrR_integratedOmega) const { + + // First we compensate the measurements for the bias + Vector3 correctedOmega = measuredOmega - biasHat; + + // Then compensate for sensor-body displacement: we express the quantities + // (originally in the IMU frame) into the body frame + if (p_->body_P_sensor) { + Matrix3 body_R_sensor = p_->body_P_sensor->rotation().matrix(); + // rotation rate vector in the body frame + correctedOmega = body_R_sensor * correctedOmega; + } + + // rotation vector describing rotation increment computed from the + // current rotation rate measurement + const Vector3 integratedOmega = correctedOmega * deltaT; + return Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! +} + +void PreintegratedRotation::integrateMeasurement(const Vector3& measuredOmega, + const Vector3& biasHat, double deltaT, Matrix3* D_incrR_integratedOmega, + Matrix3* F) { + + const Rot3 incrR = incrementalRotation(measuredOmega, biasHat, deltaT, + D_incrR_integratedOmega); + + // Update deltaTij and rotation + deltaTij_ += deltaT; + deltaRij_ = deltaRij_.compose(incrR, F); + + // Update Jacobian + const Matrix3 incrRt = incrR.transpose(); + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ + - *D_incrR_integratedOmega * deltaT; +} + +Rot3 PreintegratedRotation::biascorrectedDeltaRij(const Vector3& biasOmegaIncr, + OptionalJacobian<3, 3> H) const { + const Vector3 biasInducedOmega = delRdelBiasOmega_ * biasOmegaIncr; + const Rot3 deltaRij_biascorrected = deltaRij_.expmap(biasInducedOmega, + boost::none, H); + if (H) + (*H) *= delRdelBiasOmega_; + return deltaRij_biascorrected; +} + +Vector3 PreintegratedRotation::integrateCoriolis(const Rot3& rot_i) const { + if (!p_->omegaCoriolis) + return Vector3::Zero(); + return rot_i.transpose() * (*p_->omegaCoriolis) * deltaTij_; +} + +} // namespace gtsam diff --git a/gtsam/navigation/PreintegratedRotation.h b/gtsam/navigation/PreintegratedRotation.h index 9bb288b11..002afea76 100644 --- a/gtsam/navigation/PreintegratedRotation.h +++ b/gtsam/navigation/PreintegratedRotation.h @@ -32,18 +32,22 @@ namespace gtsam { * It includes the definitions of the preintegrated rotation. */ class PreintegratedRotation { - public: +public: /// Parameters for pre-integration: /// Usage: Create just a single Params and pass a shared pointer to the constructor struct Params { Matrix3 gyroscopeCovariance; ///< continuous-time "Covariance" of gyroscope measurements boost::optional omegaCoriolis; ///< Coriolis constant - boost::optional body_P_sensor; ///< The pose of the sensor in the body frame + boost::optional body_P_sensor; ///< The pose of the sensor in the body frame - Params():gyroscopeCovariance(I_3x3) {} + Params() : + gyroscopeCovariance(I_3x3) { + } - private: + virtual void print(const std::string& s) const; + + private: /** Serialization function */ friend class boost::serialization::access; template @@ -54,29 +58,27 @@ class PreintegratedRotation { } }; - protected: - double deltaTij_; ///< Time interval from i to j - Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) - Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias +protected: + double deltaTij_; ///< Time interval from i to j + Rot3 deltaRij_; ///< Preintegrated relative orientation (in frame i) + Matrix3 delRdelBiasOmega_; ///< Jacobian of preintegrated rotation w.r.t. angular rate bias /// Parameters boost::shared_ptr p_; /// Default constructor for serialization - PreintegratedRotation() {} + PreintegratedRotation() { + } - public: +public: /// Default constructor, resets integration to zero - explicit PreintegratedRotation(const boost::shared_ptr& p) : p_(p) { + explicit PreintegratedRotation(const boost::shared_ptr& p) : + p_(p) { resetIntegration(); } /// Re-initialize PreintegratedMeasurements - void resetIntegration() { - deltaTij_ = 0.0; - deltaRij_ = Rot3(); - delRdelBiasOmega_ = Z_3x3; - } + void resetIntegration(); /// @name Access instance variables /// @{ @@ -94,17 +96,8 @@ class PreintegratedRotation { /// @name Testable /// @{ - void print(const std::string& s) const { - std::cout << s << std::endl; - std::cout << " deltaTij [" << deltaTij_ << "]" << std::endl; - std::cout << " deltaRij.ypr = (" << deltaRij_.ypr().transpose() << ")" << std::endl; - } - - bool equals(const PreintegratedRotation& other, double tol) const { - return deltaRij_.equals(other.deltaRij_, tol) && - fabs(deltaTij_ - other.deltaTij_) < tol && - equal_with_abs_tol(delRdelBiasOmega_, other.delRdelBiasOmega_, tol); - } + void print(const std::string& s) const; + bool equals(const PreintegratedRotation& other, double tol) const; /// @} @@ -112,64 +105,26 @@ class PreintegratedRotation { /// and possibly the sensor pose, and then integrate it forward in time to yield /// an incremental rotation. Rot3 incrementalRotation(const Vector3& measuredOmega, const Vector3& biasHat, - double deltaT, OptionalJacobian<3, 3> D_incrR_integratedOmega) const { - - // First we compensate the measurements for the bias - Vector3 correctedOmega = measuredOmega - biasHat; - - // Then compensate for sensor-body displacement: we express the quantities - // (originally in the IMU frame) into the body frame - if (p_->body_P_sensor) { - Matrix3 body_R_sensor = p_->body_P_sensor->rotation().matrix(); - // rotation rate vector in the body frame - correctedOmega = body_R_sensor * correctedOmega; - } - - // rotation vector describing rotation increment computed from the - // current rotation rate measurement - const Vector3 integratedOmega = correctedOmega * deltaT; - return Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! - } + double deltaT, OptionalJacobian<3, 3> D_incrR_integratedOmega) const; /// Calculate an incremental rotation given the gyro measurement and a time interval, /// and update both deltaTij_ and deltaRij_. void integrateMeasurement(const Vector3& measuredOmega, const Vector3& biasHat, double deltaT, Matrix3* D_incrR_integratedOmega, - Matrix3* F) { - - const Rot3 incrR = incrementalRotation(measuredOmega, biasHat, deltaT, - D_incrR_integratedOmega); - - // Update deltaTij and rotation - deltaTij_ += deltaT; - deltaRij_ = deltaRij_.compose(incrR, F); - - // Update Jacobian - const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - - *D_incrR_integratedOmega * deltaT; - } + Matrix3* F); /// Return a bias corrected version of the integrated rotation, with optional Jacobian Rot3 biascorrectedDeltaRij(const Vector3& biasOmegaIncr, - OptionalJacobian<3, 3> H = boost::none) const { - const Vector3 biasInducedOmega = delRdelBiasOmega_ * biasOmegaIncr; - const Rot3 deltaRij_biascorrected = deltaRij_.expmap(biasInducedOmega, boost::none, H); - if (H) (*H) *= delRdelBiasOmega_; - return deltaRij_biascorrected; - } + OptionalJacobian<3, 3> H = boost::none) const; /// Integrate coriolis correction in body frame rot_i - Vector3 integrateCoriolis(const Rot3& rot_i) const { - if (!p_->omegaCoriolis) return Vector3::Zero(); - return rot_i.transpose() * (*p_->omegaCoriolis) * deltaTij_; - } + Vector3 integrateCoriolis(const Rot3& rot_i) const; - private: +private: /** Serialization function */ friend class boost::serialization::access; template - void serialize(ARCHIVE & ar, const unsigned int /*version*/) { // NOLINT + void serialize(ARCHIVE & ar, const unsigned int /*version*/) { // NOLINT ar & BOOST_SERIALIZATION_NVP(p_); ar & BOOST_SERIALIZATION_NVP(deltaTij_); ar & BOOST_SERIALIZATION_NVP(deltaRij_); @@ -177,4 +132,4 @@ class PreintegratedRotation { } }; -} // namespace gtsam +} // namespace gtsam From a3032fe367c33c646719ad45fb70ade9bdfac685 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 9 Aug 2015 11:23:34 -0700 Subject: [PATCH 117/142] Params::print, and comments --- gtsam/navigation/PreintegrationBase.cpp | 38 +++++++++++++++++++------ gtsam/navigation/PreintegrationBase.h | 6 ++-- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index 5533a1f9b..dd385897e 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -26,7 +26,21 @@ using namespace std; namespace gtsam { -/// Re-initialize PreintegratedMeasurements +//------------------------------------------------------------------------------ +void PreintegrationBase::Params::print(const string& s) const { + PreintegratedRotation::Params::print(s); + cout << "accelerometerCovariance:\n[\n" << accelerometerCovariance << "\n]" + << endl; + cout << "integrationCovariance:\n[\n" << accelerometerCovariance << "\n]" + << endl; + if (omegaCoriolis && use2ndOrderCoriolis) + cout << "Using 2nd-order Coriolis" << endl; + if (body_P_sensor) + body_P_sensor->print(" "); + cout << "n_gravity = (" << n_gravity.transpose() << ")" << endl; +} + +//------------------------------------------------------------------------------ void PreintegrationBase::resetIntegration() { deltaTij_ = 0.0; deltaXij_ = NavState(); @@ -37,7 +51,7 @@ void PreintegrationBase::resetIntegration() { delVdelBiasOmega_ = Z_3x3; } -/// Needed for testable +//------------------------------------------------------------------------------ void PreintegrationBase::print(const string& s) const { cout << s << endl; cout << " deltaTij [" << deltaTij_ << "]" << endl; @@ -47,7 +61,7 @@ void PreintegrationBase::print(const string& s) const { biasHat_.print(" biasHat"); } -/// Needed for testable +//------------------------------------------------------------------------------ bool PreintegrationBase::equals(const PreintegrationBase& other, double tol) const { return fabs(deltaTij_ - other.deltaTij_) < tol @@ -61,7 +75,7 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, } //------------------------------------------------------------------------------ -std::pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( +pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( const Vector3& j_measuredAcc, const Vector3& j_measuredOmega) const { // Correct for bias in the sensor frame @@ -106,8 +120,8 @@ NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, } //------------------------------------------------------------------------------ -void PreintegrationBase::update( - const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, const double dt, +void PreintegrationBase::update(const Vector3& j_measuredAcc, + const Vector3& j_measuredOmega, const double dt, Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { // Save current rotation for updating Jacobians @@ -130,7 +144,8 @@ void PreintegrationBase::update( const Vector3 integratedOmega = j_correctedOmega * dt; const Rot3 incrR = Rot3::Expmap(integratedOmega, D_incrR_integratedOmega); // expensive !! const Matrix3 incrRt = incrR.transpose(); - delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ - *D_incrR_integratedOmega * dt; + delRdelBiasOmega_ = incrRt * delRdelBiasOmega_ + - *D_incrR_integratedOmega * dt; double dt22 = 0.5 * dt * dt; const Matrix3 dRij = oldRij.matrix(); // expensive @@ -227,7 +242,9 @@ Vector9 PreintegrationBase::computeErrorAndJacobians(const Pose3& pose_i, if (H1) *H1 << D_error_predict * D_predict_state_i.leftCols<6>(); if (H2) - *H2 << D_error_predict * D_predict_state_i.rightCols<3>() * state_i.R().transpose(); + *H2 + << D_error_predict * D_predict_state_i.rightCols<3>() + * state_i.R().transpose(); if (H3) *H3 << D_error_state_j.leftCols<6>(); if (H4) @@ -251,4 +268,7 @@ PoseVelocityBias PreintegrationBase::predict(const Pose3& pose_i, p_ = q; return PoseVelocityBias(predict(NavState(pose_i, vel_i), bias_i), bias_i); } -} /// namespace gtsam + +//------------------------------------------------------------------------------ + +}/// namespace gtsam diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index f479b0b1e..6eca295b2 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -60,10 +60,8 @@ public: struct Params: PreintegratedRotation::Params { Matrix3 accelerometerCovariance; ///< continuous-time "Covariance" of accelerometer Matrix3 integrationCovariance; ///< continuous-time "Covariance" describing integration uncertainty - /// (to compensate errors in Euler integration) - /// (if false: p(t+1) = p(t) + v(t) deltaT ; if true: p(t+1) = p(t) + v(t) deltaT + 0.5 * acc(t) deltaT^2) bool use2ndOrderCoriolis; ///< Whether to use second order Coriolis integration - Vector3 n_gravity; ///< Gravity constant in body frame + Vector3 n_gravity; ///< Gravity vector in nav frame /// The Params constructor insists on getting the navigation frame gravity vector /// For convenience, two commonly used conventions are provided by named constructors below @@ -82,6 +80,8 @@ public: return boost::make_shared(Vector3(0, 0, -g)); } + void print(const std::string& s) const; + protected: /// Default constructor for serialization only: uninitialized! Params(); From 7fc1befdca012f92a2b89c3d27f9ac2dc35bb306 Mon Sep 17 00:00:00 2001 From: dellaert Date: Sun, 9 Aug 2015 11:43:26 -0700 Subject: [PATCH 118/142] Two different random seeds for better Monte-Carlo --- gtsam/navigation/tests/testImuFactor.cpp | 64 ++++++++---------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 51aa7c7c5..74265b97e 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -114,66 +114,47 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { } // namespace /* ************************************************************************* */ -Matrix9 MonteCarlo(const PreintegratedImuMeasurements& pim, +bool MonteCarlo(const PreintegratedImuMeasurements& pim, const NavState& state, const imuBias::ConstantBias& bias, double dt, const Pose3& body_P_sensor, const Vector3& measuredAcc, - const Vector3& measuredOmega, size_t N = 10, size_t M = 1) { + const Vector3& measuredOmega, size_t N = 10, + size_t M = 1) { // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim1 = pim; - for (size_t k=0;kaccelerometerCovariance, p->gyroscopeCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise - Matrix9 Q = MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, - measuredOmega); + EXPECT(MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, measuredOmega)); // Check integrated quantities in body frame: gravity measured by IMU is integrated! Vector3 b_deltaP(a * dt22, 0, -g * dt22); @@ -648,8 +624,8 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, kMeasuredOmegaCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise - Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), bias, dt, body_P_sensor, - measuredAcc, measuredOmega); + EXPECT(MonteCarlo(pim, NavState(x1, v1), bias, dt, body_P_sensor, + measuredAcc, measuredOmega)); Matrix expected(9,9); expected << @@ -783,8 +759,8 @@ TEST(ImuFactor, PredictArbitrary) { Pose3 x1; Vector3 v1 = Vector3(0, 0, 0); imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); - Matrix9 Q = MonteCarlo(pim, NavState(x1, v1), bias, 0.1, Pose3(), - measuredAcc, measuredOmega); + EXPECT(MonteCarlo(pim, NavState(x1, v1), bias, 0.1, Pose3(), + measuredAcc, measuredOmega)); for (int i = 0; i < 1000; ++i) pim.integrateMeasurement(measuredAcc, measuredOmega, dt); From 7bb819437fe869ad797a335886808649d7d7d1bd Mon Sep 17 00:00:00 2001 From: Luca Date: Sun, 9 Aug 2015 13:07:26 -0400 Subject: [PATCH 119/142] added comments --- gtsam/navigation/ImuFactor.h | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 36251a246..644cad4ed 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -30,9 +30,13 @@ namespace gtsam { /* * If you are using the factor, please cite: - * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, Eliminating + * L. Carlone, Z. Kira, C. Beall, V. Indelman, F. Dellaert, "Eliminating * conditionally independent sets in factor graphs: a unifying perspective based - * on smart factors, Int. Conf. on Robotics and Automation (ICRA), 2014. + * on smart factors", Int. Conf. on Robotics and Automation (ICRA), 2014. + * + * C. Forster, L. Carlone, F. Dellaert, D. Scaramuzza, "IMU Preintegration on + * Manifold for Efficient Visual-Inertial Maximum-a-Posteriori Estimation", + * Robotics: Science and Systems (RSS), 2015. * * REFERENCES: * [1] G.S. Chirikjian, "Stochastic Models, Information Theory, and Lie Groups", @@ -42,8 +46,8 @@ namespace gtsam { * TRO, 28(1):61-76, 2012. * [3] L. Carlone, S. Williams, R. Roberts, "Preintegrated IMU factor: * Computation of the Jacobian Matrices", Tech. Report, 2013. - * [4] C. Forster, L. Carlone, F. Dellaert, D. Scaramuzza, IMU Preintegration on - * Manifold for Efficient Visual-Inertial Maximum-a-Posteriori Estimation, + * [4] C. Forster, L. Carlone, F. Dellaert, D. Scaramuzza, "IMU Preintegration on + * Manifold for Efficient Visual-Inertial Maximum-a-Posteriori Estimation", * Robotics: Science and Systems (RSS), 2015. */ @@ -115,7 +119,7 @@ public: /// @deprecated version of integrateMeasurement with body_P_sensor /// Use parameters instead void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double deltaT, const Pose3& body_P_sensor); + const Vector3& measuredOmega, double dt, const Pose3& body_P_sensor); private: @@ -209,14 +213,15 @@ public: /// @deprecated typename typedef PreintegratedImuMeasurements PreintegratedMeasurements; - /// @deprecated constructor + /// @deprecated constructor, in the new one gravity, coriolis settings are in Params ImuFactor(Key pose_i, Key vel_i, Key pose_j, Key vel_j, Key bias, const PreintegratedMeasurements& preintegratedMeasurements, const Vector3& n_gravity, const Vector3& omegaCoriolis, const boost::optional& body_P_sensor = boost::none, const bool use2ndOrderCoriolis = false); - /// @deprecated use PreintegrationBase::predict + /// @deprecated use PreintegrationBase::predict, + /// in the new one gravity, coriolis settings are in Params static void Predict(const Pose3& pose_i, const Vector3& vel_i, Pose3& pose_j, Vector3& vel_j, const imuBias::ConstantBias& bias_i, PreintegratedMeasurements& pim, const Vector3& n_gravity, From 90ea83aa38d4a65f4cd66dcb1c04e1094c6b60a6 Mon Sep 17 00:00:00 2001 From: Luca Date: Mon, 10 Aug 2015 19:30:25 -0400 Subject: [PATCH 120/142] printing covariance --- gtsam/navigation/ImuFactor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index e49168c43..8bbfc95b2 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -33,6 +33,7 @@ using namespace std; //------------------------------------------------------------------------------ void PreintegratedImuMeasurements::print(const string& s) const { PreintegrationBase::print(s); + cout << " preintMeasCov \n[" << preintMeasCov_ << "]" << endl; } //------------------------------------------------------------------------------ From 2d251c64115f755a7a56e2b0dd6eead93ec90277 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Mon, 10 Aug 2015 21:03:21 -0400 Subject: [PATCH 121/142] make one MC test passed by using non-zero random seeds and increasing the number of samples --- gtsam/navigation/tests/testImuFactor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 74265b97e..68bbda66e 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -127,7 +127,7 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, Matrix9 actual = pim1.preintMeasCov(); // Do a Monte Carlo analysis to determine empirical density on the predicted state - Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar / dt)), 0); + Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar / dt)), 234567); Sampler sampleOmegaNoise(Vector3::Constant(sqrt(omegaNoiseVar / dt)), 10); Matrix samples(9, N); Vector9 sum = Vector9::Zero(); @@ -154,7 +154,7 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, } // Compare Monte-Carlo value with actual (computed) value - return assert_equal(Matrix(1000000*Q), 1000000*actual, 1); + return assert_equal(Matrix(10000*Q), 10000*actual, 1); } /* ************************************************************************* */ @@ -199,7 +199,7 @@ TEST(ImuFactor, StraightLine) { // Do Monte-Carlo analysis PreintegratedImuMeasurements pimMC(kZeroBiasHat, p->accelerometerCovariance, p->gyroscopeCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise - EXPECT(MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, measuredOmega)); + EXPECT(MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, measuredOmega, 100000)); // Check integrated quantities in body frame: gravity measured by IMU is integrated! Vector3 b_deltaP(a * dt22, 0, -g * dt22); From cf5f859679bb501e0a18422e82d6109f7d8199ee Mon Sep 17 00:00:00 2001 From: dellaert Date: Mon, 24 Aug 2015 15:15:57 -0700 Subject: [PATCH 122/142] Boost optional for sensor pose --- gtsam/navigation/ImuFactor.cpp | 4 ++-- gtsam/navigation/ImuFactor.h | 3 ++- gtsam/navigation/tests/testImuFactor.cpp | 19 ++++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 8bbfc95b2..2080e87dd 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -103,9 +103,9 @@ PreintegratedImuMeasurements::PreintegratedImuMeasurements( //------------------------------------------------------------------------------ void PreintegratedImuMeasurements::integrateMeasurement( const Vector3& measuredAcc, const Vector3& measuredOmega, double deltaT, - const Pose3& body_P_sensor) { + boost::optional body_P_sensor) { // modify parameters to accommodate deprecated method:-( - p_->body_P_sensor.reset(body_P_sensor); + p_->body_P_sensor = body_P_sensor; integrateMeasurement(measuredAcc, measuredOmega, deltaT); } diff --git a/gtsam/navigation/ImuFactor.h b/gtsam/navigation/ImuFactor.h index 644cad4ed..855c14365 100644 --- a/gtsam/navigation/ImuFactor.h +++ b/gtsam/navigation/ImuFactor.h @@ -119,7 +119,8 @@ public: /// @deprecated version of integrateMeasurement with body_P_sensor /// Use parameters instead void integrateMeasurement(const Vector3& measuredAcc, - const Vector3& measuredOmega, double dt, const Pose3& body_P_sensor); + const Vector3& measuredOmega, double dt, + boost::optional body_P_sensor); private: diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 68bbda66e..97f76bad8 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -116,8 +116,8 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { /* ************************************************************************* */ bool MonteCarlo(const PreintegratedImuMeasurements& pim, const NavState& state, const imuBias::ConstantBias& bias, double dt, - const Pose3& body_P_sensor, const Vector3& measuredAcc, - const Vector3& measuredOmega, size_t N = 10, + boost::optional body_P_sensor, const Vector3& measuredAcc, + const Vector3& measuredOmega, size_t N = 50000, size_t M = 1) { // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim1 = pim; @@ -144,12 +144,12 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, samples.col(i) = xi; sum += xi; } - // Vector9 sampleMean = sum / N; + Vector9 sampleMean = sum / N; Matrix9 Q; Q.setZero(); for (size_t i = 0; i < N; i++) { Vector9 xi = samples.col(i); - // xi -= sampleMean; + xi -= sampleMean; Q += xi * xi.transpose() / (N - 1); } @@ -199,7 +199,9 @@ TEST(ImuFactor, StraightLine) { // Do Monte-Carlo analysis PreintegratedImuMeasurements pimMC(kZeroBiasHat, p->accelerometerCovariance, p->gyroscopeCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise - EXPECT(MonteCarlo(pimMC, state1, kZeroBias, dt/10, Pose3(), measuredAcc, measuredOmega, 100000)); + EXPECT( + MonteCarlo(pimMC, state1, kZeroBias, dt / 10, boost::none, measuredAcc, + measuredOmega)); // Check integrated quantities in body frame: gravity measured by IMU is integrated! Vector3 b_deltaP(a * dt22, 0, -g * dt22); @@ -453,10 +455,9 @@ TEST(ImuFactor, ErrorAndJacobianWith2ndOrderCoriolis) { pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT); // Create factor - Pose3 bodyPsensor = Pose3(); bool use2ndOrderCoriolis = true; ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, - kNonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis); + kNonZeroOmegaCoriolis, boost::none, use2ndOrderCoriolis); Values values; values.insert(X(1), x1); @@ -670,7 +671,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { values.insert(B(1), bias); // Make sure linearization is correct - double diffDelta = 1e-7; + double diffDelta = 1e-5; EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } @@ -759,7 +760,7 @@ TEST(ImuFactor, PredictArbitrary) { Pose3 x1; Vector3 v1 = Vector3(0, 0, 0); imuBias::ConstantBias bias(Vector3(0, 0, 0), Vector3(0, 0, 0)); - EXPECT(MonteCarlo(pim, NavState(x1, v1), bias, 0.1, Pose3(), + EXPECT(MonteCarlo(pim, NavState(x1, v1), bias, 0.1, boost::none, measuredAcc, measuredOmega)); for (int i = 0; i < 1000; ++i) From a4e58d06e758b418f4769700aa33a04b09682d26 Mon Sep 17 00:00:00 2001 From: dellaert Date: Wed, 26 Aug 2015 20:19:26 -0700 Subject: [PATCH 123/142] Tighter bounds --- gtsam/geometry/tests/testPose3.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtsam/geometry/tests/testPose3.cpp b/gtsam/geometry/tests/testPose3.cpp index f6f8b7b40..9007ce1bd 100644 --- a/gtsam/geometry/tests/testPose3.cpp +++ b/gtsam/geometry/tests/testPose3.cpp @@ -182,8 +182,8 @@ TEST(Pose3, expmaps_galore_full) xi = (Vector(6) << 0.2, 0.3, -0.8, 100.0, 120.0, -60.0).finished(); actual = Pose3::Expmap(xi); EXPECT(assert_equal(expm(xi,10), actual,1e-5)); - EXPECT(assert_equal(Agrawal06iros(xi), actual,1e-6)); - EXPECT(assert_equal(xi, Pose3::Logmap(actual),1e-6)); + EXPECT(assert_equal(Agrawal06iros(xi), actual,1e-9)); + EXPECT(assert_equal(xi, Pose3::Logmap(actual),1e-9)); } /* ************************************************************************* */ From 9f91aedd6abd30215e05653de18418f9e7261ee1 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Wed, 2 Sep 2015 16:52:54 -0400 Subject: [PATCH 124/142] test centrifugal derivative --- gtsam/navigation/tests/testImuFactor.cpp | 29 ++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 97f76bad8..50d6a8533 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -157,6 +157,35 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, return assert_equal(Matrix(10000*Q), 10000*actual, 1); } +/* ************************************************************************* */ +Vector3 centrifugal(const Vector3& omega_s, const Pose3& bTs, boost::optional H1) { + Rot3 bRs = bTs.rotation(); + Vector3 j_correctedOmega = bRs * omega_s; + + const Vector3 b_arm = bTs.translation().vector(); + + // Subtract out the the centripetal acceleration from the measured one + // to get linear acceleration vector in the body frame: + const Matrix3 body_Omega_body = skewSymmetric(j_correctedOmega); + const Vector3 b_velocity_bs = body_Omega_body * b_arm; // magnitude: omega * arm + if (H1) { + double wdp = j_correctedOmega.dot(b_arm); + *H1 = - (diag(Vector3::Constant(wdp)) + j_correctedOmega * b_arm.transpose())*bRs.matrix() + 2*b_arm*omega_s.transpose(); + } + return -body_Omega_body * b_velocity_bs; +} + +/* ************************************************************************* */ +TEST(ImuFactor, centrifugalDeriv) { + static const Pose3 bTs(Rot3::ypr(0.1,0.2,0.3), Point3(1.,4.,7.)); + Vector3 omega_s(0.2,0.4,0.6); + Matrix3 H1; + Vector3 cb = centrifugal(omega_s, bTs, H1); + (void) cb; + Matrix Hnum = numericalDerivative11(boost::bind(centrifugal, _1, bTs, boost::none), omega_s, 1e-6); + EXPECT(assert_equal(Hnum, H1, 1e-5)); +} + /* ************************************************************************* */ TEST(ImuFactor, StraightLine) { // Set up IMU measurements From 8e2915c4f3b958a2d7b57f9da97ea2ad93de6fc6 Mon Sep 17 00:00:00 2001 From: Frank Dellaert Date: Fri, 4 Sep 2015 18:14:13 +0000 Subject: [PATCH 125/142] README.md edited online with Bitbucket --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 679af5a2f..ccdc7f07e 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Prerequisites: - [Boost](http://www.boost.org/users/download/) >= 1.43 (Ubuntu: `sudo apt-get install libboost-all-dev`) - [CMake](http://www.cmake.org/cmake/resources/software.html) >= 2.6 (Ubuntu: `sudo apt-get install cmake`) +- A modern compiler, i.e., at least gcc 4.7.3 on Linux. Optional prerequisites - used automatically if findable by CMake: @@ -46,6 +47,6 @@ See the [`INSTALL`](INSTALL) file for more detailed installation instructions. GTSAM is open source under the BSD license, see the [`LICENSE`](LICENSE) and [`LICENSE.BSD`](LICENSE.BSD) files. -Please see the [`examples/`](examples) directory and the [`USAGE`](USAGE.md) file for examples on how to use GTSAM. - +Please see the [`examples/`](examples) directory and the [`USAGE`](USAGE.md) file for examples on how to use GTSAM. + GTSAM was developed in the lab of [Frank Dellaert](http://www.cc.gatech.edu/~dellaert) at the [Georgia Institute of Technology](http://www.gatech.edu), with the help of many contributors over the years, see [THANKS](THANKS). \ No newline at end of file From e01fc2da03307a03b86590d0f8114385efb63fc2 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 10 Sep 2015 22:59:44 -0400 Subject: [PATCH 126/142] use mt19937_64 generator --- gtsam/linear/Sampler.cpp | 5 ++--- gtsam/linear/Sampler.h | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/gtsam/linear/Sampler.cpp b/gtsam/linear/Sampler.cpp index 48972a953..5d58d165a 100644 --- a/gtsam/linear/Sampler.cpp +++ b/gtsam/linear/Sampler.cpp @@ -14,8 +14,7 @@ * @author Alex Cunningham */ -#include -#include +#include #include namespace gtsam { @@ -51,7 +50,7 @@ Vector Sampler::sampleDiagonal(const Vector& sigmas) { } else { typedef boost::normal_distribution Normal; Normal dist(0.0, sigma); - boost::variate_generator norm(generator_, dist); + boost::variate_generator norm(generator_, dist); result(i) = norm(); } } diff --git a/gtsam/linear/Sampler.h b/gtsam/linear/Sampler.h index f2632308b..453658147 100644 --- a/gtsam/linear/Sampler.h +++ b/gtsam/linear/Sampler.h @@ -37,7 +37,7 @@ protected: noiseModel::Diagonal::shared_ptr model_; /** generator */ - boost::minstd_rand generator_; + boost::mt19937_64 generator_; public: typedef boost::shared_ptr shared_ptr; From c9fae14a98280f529ad726ef7c8128239ebc26eb Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 10 Sep 2015 23:03:59 -0400 Subject: [PATCH 127/142] correct Jacobians for body_P_sensor case, including derivative for centripetal acc --- gtsam/navigation/ImuFactor.cpp | 2 +- gtsam/navigation/PreintegrationBase.cpp | 24 ++++++++++++++++++++---- gtsam/navigation/PreintegrationBase.h | 7 ++++++- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/gtsam/navigation/ImuFactor.cpp b/gtsam/navigation/ImuFactor.cpp index 2080e87dd..70527d91d 100644 --- a/gtsam/navigation/ImuFactor.cpp +++ b/gtsam/navigation/ImuFactor.cpp @@ -78,8 +78,8 @@ void PreintegratedImuMeasurements::integrateMeasurement( preintMeasCov_ = F * preintMeasCov_ * F.transpose() + G * Cov * G.transpose(); #else preintMeasCov_ = F * preintMeasCov_ * F.transpose() - + G1 * (p().accelerometerCovariance / dt) * G1.transpose() + Gi * (p().integrationCovariance * dt) * Gi.transpose() // NOTE(frank): (Gi*dt)*(C/dt)*(Gi'*dt) + + G1 * (p().accelerometerCovariance / dt) * G1.transpose() + G2 * (p().gyroscopeCovariance / dt) * G2.transpose(); #endif } diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index dd385897e..d6e906d8b 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -76,7 +76,8 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, //------------------------------------------------------------------------------ pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( - const Vector3& j_measuredAcc, const Vector3& j_measuredOmega) const { + const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, + OptionalJacobian<3, 3> D_correctedAcc_measuredOmega) const { // Correct for bias in the sensor frame Vector3 j_correctedAcc = biasHat_.correctAccelerometer(j_measuredAcc); @@ -99,6 +100,12 @@ pair PreintegrationBase::correctMeasurementsByBiasAndSensorPos const Matrix3 body_Omega_body = skewSymmetric(j_correctedOmega); const Vector3 b_velocity_bs = body_Omega_body * b_arm; // magnitude: omega * arm j_correctedAcc -= body_Omega_body * b_velocity_bs; + if (D_correctedAcc_measuredOmega) { + double wdp = j_correctedOmega.dot(b_arm); + *D_correctedAcc_measuredOmega = -(diag(Vector3::Constant(wdp)) + + j_correctedOmega * b_arm.transpose()) * bRs.matrix() + + 2 * b_arm * j_measuredOmega.transpose(); + } } } @@ -112,11 +119,20 @@ NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { Vector3 j_correctedAcc, j_correctedOmega; + Matrix3 D_correctedAcc_measuredOmega; boost::tie(j_correctedAcc, j_correctedOmega) = - correctMeasurementsByBiasAndSensorPose(j_measuredAcc, j_measuredOmega); - + correctMeasurementsByBiasAndSensorPose(j_measuredAcc, j_measuredOmega, D_correctedAcc_measuredOmega); // Do update in one fell swoop - return deltaXij_.update(j_correctedAcc, j_correctedOmega, dt, F, G1, G2); + NavState updated = deltaXij_.update(j_correctedAcc, j_correctedOmega, dt, F, G1, G2); + if (G1 && G2 && p().body_P_sensor) { + const Matrix3 bRs = p().body_P_sensor->rotation().matrix(); + *G2 = *G2*bRs; + if (!p().body_P_sensor->translation().vector().isZero()) { + *G2 += *G1 * D_correctedAcc_measuredOmega; + } + *G1 = *G1*bRs; + } + return updated; } //------------------------------------------------------------------------------ diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 6eca295b2..6118cc515 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -145,6 +145,10 @@ public: return *boost::static_pointer_cast(p_); } + void set_body_P_sensor(const Pose3& body_P_sensor) { + p_->body_P_sensor = body_P_sensor; + } + /// getters const NavState& deltaXij() const { return deltaXij_; @@ -193,7 +197,8 @@ public: /// Subtract estimate and correct for sensor pose std::pair correctMeasurementsByBiasAndSensorPose( - const Vector3& j_measuredAcc, const Vector3& j_measuredOmega) const; + const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, + OptionalJacobian<3, 3> D_correctedAcc_measuredOmega = boost::none) const; /// Calculate the updated preintegrated measurement, does not modify /// It takes measured quantities in the j frame From f59c442fb3363692760d278f62dd687621b3264a Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 10 Sep 2015 23:07:45 -0400 Subject: [PATCH 128/142] non-isotropic diagonal noises to check the effect of body_R_sensor. Predefine seeds for random samplers --- gtsam/navigation/tests/testImuFactor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 50d6a8533..517ee6798 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -61,6 +61,9 @@ double intNoiseVar = 0.0001; const Matrix3 kMeasuredAccCovariance = accNoiseVar * I_3x3; const Matrix3 kMeasuredOmegaCovariance = omegaNoiseVar * I_3x3; const Matrix3 kIntegrationErrorCovariance = intNoiseVar * I_3x3; +const Vector3 accNoiseVar2(0.01, 0.02, 0.03); +const Vector3 omegaNoiseVar2(0.03, 0.01, 0.02); +int32_t accSamplerSeed = 29284, omegaSamplerSeed = 10; // Auxiliary functions to test preintegrated Jacobians // delPdelBiasAcc_ delPdelBiasOmega_ delVdelBiasAcc_ delVdelBiasOmega_ delRdelBiasOmega_ From 704411de4e91a950dcf8fe10ed896c69181a0c21 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 10 Sep 2015 23:13:35 -0400 Subject: [PATCH 129/142] MonteCarlo test passed for body_P_sensor case. Unittests for Jacobians of updatedDeltaXij. Code to verify statistics and nonlinearity of generated samples. --- gtsam/navigation/tests/testImuFactor.cpp | 122 +++++++++++++++-------- 1 file changed, 81 insertions(+), 41 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 517ee6798..eceda34a0 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include using namespace std; using namespace gtsam; @@ -120,7 +121,8 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) { bool MonteCarlo(const PreintegratedImuMeasurements& pim, const NavState& state, const imuBias::ConstantBias& bias, double dt, boost::optional body_P_sensor, const Vector3& measuredAcc, - const Vector3& measuredOmega, size_t N = 50000, + const Vector3& measuredOmega, const Vector3& accNoiseVar, + const Vector3& omegaNoiseVar, size_t N = 10000, size_t M = 1) { // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim1 = pim; @@ -130,8 +132,8 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, Matrix9 actual = pim1.preintMeasCov(); // Do a Monte Carlo analysis to determine empirical density on the predicted state - Sampler sampleAccelerationNoise(Vector3::Constant(sqrt(accNoiseVar / dt)), 234567); - Sampler sampleOmegaNoise(Vector3::Constant(sqrt(omegaNoiseVar / dt)), 10); + Sampler sampleAccelerationNoise((accNoiseVar/dt).cwiseSqrt(), accSamplerSeed); + Sampler sampleOmegaNoise((omegaNoiseVar/dt).cwiseSqrt(), omegaSamplerSeed); Matrix samples(9, N); Vector9 sum = Vector9::Zero(); for (size_t i = 0; i < N; i++) { @@ -147,7 +149,8 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, samples.col(i) = xi; sum += xi; } - Vector9 sampleMean = sum / N; + + Vector9 sampleMean = sum / N; Matrix9 Q; Q.setZero(); for (size_t i = 0; i < N; i++) { @@ -156,38 +159,11 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, Q += xi * xi.transpose() / (N - 1); } - // Compare Monte-Carlo value with actual (computed) value - return assert_equal(Matrix(10000*Q), 10000*actual, 1); + // Compare MonteCarlo value with actual (computed) value + return assert_equal(Matrix(Q), actual, 1e-4); } -/* ************************************************************************* */ -Vector3 centrifugal(const Vector3& omega_s, const Pose3& bTs, boost::optional H1) { - Rot3 bRs = bTs.rotation(); - Vector3 j_correctedOmega = bRs * omega_s; - - const Vector3 b_arm = bTs.translation().vector(); - - // Subtract out the the centripetal acceleration from the measured one - // to get linear acceleration vector in the body frame: - const Matrix3 body_Omega_body = skewSymmetric(j_correctedOmega); - const Vector3 b_velocity_bs = body_Omega_body * b_arm; // magnitude: omega * arm - if (H1) { - double wdp = j_correctedOmega.dot(b_arm); - *H1 = - (diag(Vector3::Constant(wdp)) + j_correctedOmega * b_arm.transpose())*bRs.matrix() + 2*b_arm*omega_s.transpose(); - } - return -body_Omega_body * b_velocity_bs; -} - -/* ************************************************************************* */ -TEST(ImuFactor, centrifugalDeriv) { - static const Pose3 bTs(Rot3::ypr(0.1,0.2,0.3), Point3(1.,4.,7.)); - Vector3 omega_s(0.2,0.4,0.6); - Matrix3 H1; - Vector3 cb = centrifugal(omega_s, bTs, H1); - (void) cb; - Matrix Hnum = numericalDerivative11(boost::bind(centrifugal, _1, bTs, boost::none), omega_s, 1e-6); - EXPECT(assert_equal(Hnum, H1, 1e-5)); -} +#if 1 /* ************************************************************************* */ TEST(ImuFactor, StraightLine) { @@ -233,7 +209,7 @@ TEST(ImuFactor, StraightLine) { p->gyroscopeCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise EXPECT( MonteCarlo(pimMC, state1, kZeroBias, dt / 10, boost::none, measuredAcc, - measuredOmega)); + measuredOmega, Vector3::Constant(accNoiseVar), Vector3::Constant(omegaNoiseVar), 100000)); // Check integrated quantities in body frame: gravity measured by IMU is integrated! Vector3 b_deltaP(a * dt22, 0, -g * dt22); @@ -634,8 +610,13 @@ TEST(ImuFactor, FirstOrderPreIntegratedMeasurements) { EXPECT( assert_equal(expectedDelRdelBiasOmega, preintegrated.delRdelBiasOmega())); } +#endif /* ************************************************************************* */ +Vector3 correctedAcc(const PreintegratedImuMeasurements& pim, const Vector3& measuredAcc, const Vector3& measuredOmega) { + return pim.correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega).first; +} + TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { imuBias::ConstantBias bias(Vector3(0.2, 0, 0), Vector3(0, 0, 0.3)); // Biases (acc, rot) Pose3 x1(Rot3::Expmap(Vector3(0, 0, M_PI / 4.0)), Point3(5.0, 1.0, -50.0)); @@ -651,14 +632,73 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { + Vector3(0.2, 0.0, 0.0); double dt = 0.1; - Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, 0.1, 0.1)), Point3(1, 0, 0)); - imuBias::ConstantBias biasHat(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); + Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, M_PI/2, 0)), Point3(0.1, 0, 0)); + imuBias::ConstantBias biasHat(Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); // Get mean prediction from "ground truth" measurements - PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance, - kMeasuredOmegaCovariance, Z_3x3, true); // MonteCarlo does not sample integration noise + PreintegratedImuMeasurements pim(biasHat, accNoiseVar2.asDiagonal(), + omegaNoiseVar2.asDiagonal(), Z_3x3, true); // MonteCarlo does not sample integration noise + pim.set_body_P_sensor(body_P_sensor); + + // Check updatedDeltaXij derivatives + Matrix3 D_correctedAcc_measuredOmega; + pim.correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, D_correctedAcc_measuredOmega); + Matrix3 expectedD = numericalDerivative11(boost::bind(correctedAcc, pim, measuredAcc, _1), measuredOmega, 1e-6); + EXPECT(assert_equal(expectedD, D_correctedAcc_measuredOmega, 1e-5)); + + Matrix93 G1, G2; + NavState preint = pim.updatedDeltaXij(measuredAcc, measuredOmega, dt, boost::none, G1, G2); +// Matrix9 preintCov = G1*((accNoiseVar2/dt).asDiagonal())*G1.transpose() + G2*((omegaNoiseVar2/dt).asDiagonal())*G2.transpose(); + + Matrix93 expectedG1 = numericalDerivative21( + boost::bind(&PreintegratedImuMeasurements::updatedDeltaXij, pim, _1, _2, + dt, boost::none, boost::none, boost::none), measuredAcc, measuredOmega, + 1e-6); + EXPECT(assert_equal(expectedG1, G1, 1e-5)); + + Matrix93 expectedG2 = numericalDerivative22( + boost::bind(&PreintegratedImuMeasurements::updatedDeltaXij, pim, _1, _2, + dt, boost::none, boost::none, boost::none), measuredAcc, measuredOmega, + 1e-6); + EXPECT(assert_equal(expectedG2, G2, 1e-5)); + +#if 0 + /* + * This code is to verify the quality of the generated samples + * by checking if the covariance of the generated noises matches + * with the input covariance, and visualizing the nonlinearity of + * the sample set using the following matlab script: + * + noises = dlmread('noises.txt'); + cov(noises) + + samples = dlmread('noises.txt'); + figure(1); + for i=1:9 + subplot(3,3,i) + hist(samples(:,i), 500) + end + */ + size_t N = 10000; + Matrix samples(9,N); + Sampler sampleAccelerationNoise((accNoiseVar2/dt).cwiseSqrt(), 29284); + Sampler sampleOmegaNoise((omegaNoiseVar2/dt).cwiseSqrt(), 10); + ofstream samplesOut("preintSamples.txt"); + ofstream noiseOut("noises.txt"); + for (size_t i = 0; i Date: Tue, 15 Sep 2015 11:14:45 -0400 Subject: [PATCH 130/142] ImuFactor Jacobian test passed. Need to integrate at least two IMU measurements to get information on the position --- gtsam/navigation/tests/testImuFactor.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index eceda34a0..91da5598b 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -641,7 +641,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { pim.set_body_P_sensor(body_P_sensor); // Check updatedDeltaXij derivatives - Matrix3 D_correctedAcc_measuredOmega; + Matrix3 D_correctedAcc_measuredOmega = Matrix3::Zero(); pim.correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, D_correctedAcc_measuredOmega); Matrix3 expectedD = numericalDerivative11(boost::bind(correctedAcc, pim, measuredAcc, _1), measuredOmega, 1e-6); EXPECT(assert_equal(expectedD, D_correctedAcc_measuredOmega, 1e-5)); @@ -714,6 +714,10 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { pim.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-6)); + // integrate one more time (at least twice) to get position information + // otherwise factor cov noise from preint_cov is not positive definite + pim.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); + // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, kNonZeroOmegaCoriolis); @@ -723,7 +727,6 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { Vector3 actual_v2; ImuFactor::Predict(x1, v1, actual_x2, actual_v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); - // Regression test with Rot3 expectedR( // 0.456795409, -0.888128414, 0.0506544554, // @@ -742,8 +745,10 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { values.insert(V(2), v2); values.insert(B(1), bias); +// factor.get_noiseModel()->print("noise: "); // Make sure the noise is valid + // Make sure linearization is correct - double diffDelta = 1e-5; + double diffDelta = 1e-8; EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3); } From 75abc90a90f19ee1f9ae5223b252f71bee02f56d Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Wed, 16 Sep 2015 08:24:17 -0400 Subject: [PATCH 131/142] Informative derivative names. Only compute if need to. --- gtsam/navigation/PreintegrationBase.cpp | 48 ++++++++++++++++++------- gtsam/navigation/PreintegrationBase.h | 15 +++++--- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/gtsam/navigation/PreintegrationBase.cpp b/gtsam/navigation/PreintegrationBase.cpp index d6e906d8b..bb01971e6 100644 --- a/gtsam/navigation/PreintegrationBase.cpp +++ b/gtsam/navigation/PreintegrationBase.cpp @@ -77,7 +77,9 @@ bool PreintegrationBase::equals(const PreintegrationBase& other, //------------------------------------------------------------------------------ pair PreintegrationBase::correctMeasurementsByBiasAndSensorPose( const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, - OptionalJacobian<3, 3> D_correctedAcc_measuredOmega) const { + OptionalJacobian<3, 3> D_correctedAcc_measuredAcc, + OptionalJacobian<3, 3> D_correctedAcc_measuredOmega, + OptionalJacobian<3, 3> D_correctedOmega_measuredOmega) const { // Correct for bias in the sensor frame Vector3 j_correctedAcc = biasHat_.correctAccelerometer(j_measuredAcc); @@ -93,6 +95,13 @@ pair PreintegrationBase::correctMeasurementsByBiasAndSensorPos // Correct acceleration j_correctedAcc = bRs * j_correctedAcc; + + // Jacobians + if (D_correctedAcc_measuredAcc) *D_correctedAcc_measuredAcc = bRs; + if (D_correctedAcc_measuredOmega) *D_correctedAcc_measuredOmega = Matrix3::Zero(); + if (D_correctedOmega_measuredOmega) *D_correctedOmega_measuredOmega = bRs; + + // Centrifugal acceleration const Vector3 b_arm = p().body_P_sensor->translation().vector(); if (!b_arm.isZero()) { // Subtract out the the centripetal acceleration from the measured one @@ -100,6 +109,7 @@ pair PreintegrationBase::correctMeasurementsByBiasAndSensorPos const Matrix3 body_Omega_body = skewSymmetric(j_correctedOmega); const Vector3 b_velocity_bs = body_Omega_body * b_arm; // magnitude: omega * arm j_correctedAcc -= body_Omega_body * b_velocity_bs; + // Update derivative: centrifugal causes the correlation between acc and omega!!! if (D_correctedAcc_measuredOmega) { double wdp = j_correctedOmega.dot(b_arm); *D_correctedAcc_measuredOmega = -(diag(Vector3::Constant(wdp)) @@ -115,22 +125,32 @@ pair PreintegrationBase::correctMeasurementsByBiasAndSensorPos //------------------------------------------------------------------------------ NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, - const Vector3& j_measuredOmega, const double dt, OptionalJacobian<9, 9> F, - OptionalJacobian<9, 3> G1, OptionalJacobian<9, 3> G2) const { + const Vector3& j_measuredOmega, const double dt, + OptionalJacobian<9, 9> D_updated_current, + OptionalJacobian<9, 3> D_updated_measuredAcc, + OptionalJacobian<9, 3> D_updated_measuredOmega) const { Vector3 j_correctedAcc, j_correctedOmega; - Matrix3 D_correctedAcc_measuredOmega; + Matrix3 D_correctedAcc_measuredAcc, // + D_correctedAcc_measuredOmega, // + D_correctedOmega_measuredOmega; + bool needDerivs = D_updated_measuredAcc && D_updated_measuredOmega && p().body_P_sensor; boost::tie(j_correctedAcc, j_correctedOmega) = - correctMeasurementsByBiasAndSensorPose(j_measuredAcc, j_measuredOmega, D_correctedAcc_measuredOmega); + correctMeasurementsByBiasAndSensorPose(j_measuredAcc, j_measuredOmega, + (needDerivs ? &D_correctedAcc_measuredAcc : 0), + (needDerivs ? &D_correctedAcc_measuredOmega : 0), + (needDerivs ? &D_correctedOmega_measuredOmega : 0)); // Do update in one fell swoop - NavState updated = deltaXij_.update(j_correctedAcc, j_correctedOmega, dt, F, G1, G2); - if (G1 && G2 && p().body_P_sensor) { - const Matrix3 bRs = p().body_P_sensor->rotation().matrix(); - *G2 = *G2*bRs; + Matrix93 D_updated_correctedAcc, D_updated_correctedOmega; + NavState updated = deltaXij_.update(j_correctedAcc, j_correctedOmega, dt, D_updated_current, + (needDerivs ? D_updated_correctedAcc : D_updated_measuredAcc), + (needDerivs ? D_updated_correctedOmega : D_updated_measuredOmega)); + if (needDerivs) { + *D_updated_measuredAcc = D_updated_correctedAcc * D_correctedAcc_measuredAcc; + *D_updated_measuredOmega = D_updated_correctedOmega * D_correctedOmega_measuredOmega; if (!p().body_P_sensor->translation().vector().isZero()) { - *G2 += *G1 * D_correctedAcc_measuredOmega; + *D_updated_measuredOmega += D_updated_correctedAcc * D_correctedAcc_measuredOmega; } - *G1 = *G1*bRs; } return updated; } @@ -138,14 +158,16 @@ NavState PreintegrationBase::updatedDeltaXij(const Vector3& j_measuredAcc, //------------------------------------------------------------------------------ void PreintegrationBase::update(const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, const double dt, - Matrix3* D_incrR_integratedOmega, Matrix9* F, Matrix93* G1, Matrix93* G2) { + Matrix3* D_incrR_integratedOmega, Matrix9* D_updated_current, + Matrix93* D_updated_measuredAcc, Matrix93* D_updated_measuredOmega) { // Save current rotation for updating Jacobians const Rot3 oldRij = deltaXij_.attitude(); // Do update deltaTij_ += dt; - deltaXij_ = updatedDeltaXij(j_measuredAcc, j_measuredOmega, dt, F, G1, G2); // functional + deltaXij_ = updatedDeltaXij(j_measuredAcc, j_measuredOmega, dt, + D_updated_current, D_updated_measuredAcc, D_updated_measuredOmega); // functional // Update Jacobians // TODO(frank): we are repeating some computation here: accessible in F ? diff --git a/gtsam/navigation/PreintegrationBase.h b/gtsam/navigation/PreintegrationBase.h index 6118cc515..07225dd9a 100644 --- a/gtsam/navigation/PreintegrationBase.h +++ b/gtsam/navigation/PreintegrationBase.h @@ -196,22 +196,27 @@ public: bool equals(const PreintegrationBase& other, double tol) const; /// Subtract estimate and correct for sensor pose + /// Compute the derivatives due to non-identity body_P_sensor (rotation and centrifugal acc) + /// Ignore D_correctedOmega_measuredAcc as it is trivially zero std::pair correctMeasurementsByBiasAndSensorPose( const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, - OptionalJacobian<3, 3> D_correctedAcc_measuredOmega = boost::none) const; + OptionalJacobian<3, 3> D_correctedAcc_measuredAcc = boost::none, + OptionalJacobian<3, 3> D_correctedAcc_measuredOmega = boost::none, + OptionalJacobian<3, 3> D_correctedOmega_measuredOmega = boost::none) const; /// Calculate the updated preintegrated measurement, does not modify /// It takes measured quantities in the j frame NavState updatedDeltaXij(const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, const double dt, - OptionalJacobian<9, 9> F = boost::none, OptionalJacobian<9, 3> G1 = - boost::none, OptionalJacobian<9, 3> G2 = boost::none) const; + OptionalJacobian<9, 9> D_updated_current = boost::none, + OptionalJacobian<9, 3> D_updated_measuredAcc = boost::none, + OptionalJacobian<9, 3> D_updated_measuredOmega = boost::none) const; /// Update preintegrated measurements and get derivatives /// It takes measured quantities in the j frame void update(const Vector3& j_measuredAcc, const Vector3& j_measuredOmega, - const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* F, - Matrix93* G1, Matrix93* G2); + const double deltaT, Matrix3* D_incrR_integratedOmega, Matrix9* D_updated_current, + Matrix93* D_udpated_measuredAcc, Matrix93* D_updated_measuredOmega); /// Given the estimate of the bias, return a NavState tangent vector /// summarizing the preintegrated IMU measurements so far From f3b97ed2c287b936d26db3e31309fae35e6f3485 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 11:27:37 -0400 Subject: [PATCH 132/142] fix header --- gtsam/linear/Sampler.cpp | 2 -- gtsam/linear/Sampler.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/gtsam/linear/Sampler.cpp b/gtsam/linear/Sampler.cpp index 5d58d165a..bfbc222ba 100644 --- a/gtsam/linear/Sampler.cpp +++ b/gtsam/linear/Sampler.cpp @@ -14,8 +14,6 @@ * @author Alex Cunningham */ -#include - #include namespace gtsam { diff --git a/gtsam/linear/Sampler.h b/gtsam/linear/Sampler.h index 453658147..6c84bfda2 100644 --- a/gtsam/linear/Sampler.h +++ b/gtsam/linear/Sampler.h @@ -20,7 +20,7 @@ #include -#include +#include namespace gtsam { From c7e52fe861073661081a8e9d5eb57c263b708d30 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 11:28:59 -0400 Subject: [PATCH 133/142] a bit more complicated test case --- gtsam/navigation/tests/testImuFactor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 91da5598b..74c2d8409 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -632,8 +632,8 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { + Vector3(0.2, 0.0, 0.0); double dt = 0.1; - Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, M_PI/2, 0)), Point3(0.1, 0, 0)); - imuBias::ConstantBias biasHat(Vector3(0.0, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); + Pose3 body_P_sensor(Rot3::Expmap(Vector3(0, M_PI/2, 0)), Point3(0.1, 0.05, 0.01)); + imuBias::ConstantBias biasHat(Vector3(0.2, 0.0, 0.0), Vector3(0.0, 0.0, 0.0)); // Get mean prediction from "ground truth" measurements PreintegratedImuMeasurements pim(biasHat, accNoiseVar2.asDiagonal(), From cf821f51247598124f93e1850c12c37fd9f6e201 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 11:30:39 -0400 Subject: [PATCH 134/142] update api --- gtsam/navigation/tests/testImuFactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 74c2d8409..24c283bd2 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -642,7 +642,7 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { // Check updatedDeltaXij derivatives Matrix3 D_correctedAcc_measuredOmega = Matrix3::Zero(); - pim.correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, D_correctedAcc_measuredOmega); + pim.correctMeasurementsByBiasAndSensorPose(measuredAcc, measuredOmega, boost::none, D_correctedAcc_measuredOmega, boost::none); Matrix3 expectedD = numericalDerivative11(boost::bind(correctedAcc, pim, measuredAcc, _1), measuredOmega, 1e-6); EXPECT(assert_equal(expectedD, D_correctedAcc_measuredOmega, 1e-5)); From a9954b3bd5b336734eab055c641b81f4ade77a13 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 11:32:39 -0400 Subject: [PATCH 135/142] test pass when using priorBias in Preint instead of zeroBias --- gtsam/navigation/tests/testImuFactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 24c283bd2..66b3ba8d2 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -987,7 +987,7 @@ TEST(ImuFactor, bodyPSensorWithBias) { Bias zeroBias(Vector3(0, 0, 0), Vector3(0, 0, 0)); for (int i = 1; i < numFactors; i++) { ImuFactor::PreintegratedMeasurements pim = - ImuFactor::PreintegratedMeasurements(zeroBias, accCov, gyroCov, + ImuFactor::PreintegratedMeasurements(priorBias, accCov, gyroCov, integrationCov, true); for (int j = 0; j < 200; ++j) pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT, From 6fb453e7252ff4df1a8ebbd85923f18cb2bab999 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 11:34:49 -0400 Subject: [PATCH 136/142] disable experimental tests with specified expected values --- gtsam/navigation/tests/testImuFactor.cpp | 66 ++++++++++++------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 66b3ba8d2..01b331e42 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -700,19 +700,19 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { EXPECT(MonteCarlo(pim, NavState(x1, v1), bias, dt, body_P_sensor, measuredAcc, measuredOmega, accNoiseVar2, omegaNoiseVar2, 10000)); - Matrix expected(9,9); - expected << - 0.0290780477, 4.63277848e-07, 9.23468723e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // - 4.63277848e-07, 0.0290688208, 4.62505461e-06, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // - 9.23468723e-05, 4.62505461e-06, 0.0299907267, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // - 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, // - 0.0, 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, // - 0.0, 0.0, 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, // - 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, // - 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, // - 0.0, 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01; +// Matrix expected(9,9); +// expected << +// 0.0290780477, 4.63277848e-07, 9.23468723e-05, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // +// 4.63277848e-07, 0.0290688208, 4.62505461e-06, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // +// 9.23468723e-05, 4.62505461e-06, 0.0299907267, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // +// 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, 0.0, // +// 0.0, 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, 0.0, // +// 0.0, 0.0, 0.0, 0.0, 0.0, 0.0026, 0.0, 0.0, 0.005, // +// 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, 0.0, // +// 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01, 0.0, // +// 0.0, 0.0, 0.0, 0.0, 0.0, 0.005, 0.0, 0.0, 0.01; pim.integrateMeasurement(measuredAcc, measuredOmega, dt, body_P_sensor); - EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-6)); +// EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-6)); // integrate one more time (at least twice) to get position information // otherwise factor cov noise from preint_cov is not positive definite @@ -728,15 +728,15 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) { ImuFactor::Predict(x1, v1, actual_x2, actual_v2, bias, pim, kGravityAlongNavZDown, kZeroOmegaCoriolis); // Regression test with - Rot3 expectedR( // - 0.456795409, -0.888128414, 0.0506544554, // - 0.889548908, 0.45563417, -0.0331699173, // - 0.00637924528, 0.0602114814, 0.998165258); - Point3 expectedT(5.30373101, 0.768972495, -49.9942188); - Vector3 expected_v2(0.107462014, -0.46205501, 0.0115624037); - Pose3 expected_x2(expectedR, expectedT); - EXPECT(assert_equal(expected_x2, actual_x2, 1e-7)); - EXPECT(assert_equal(Vector(expected_v2), actual_v2, 1e-7)); +// Rot3 expectedR( // +// 0.456795409, -0.888128414, 0.0506544554, // +// 0.889548908, 0.45563417, -0.0331699173, // +// 0.00637924528, 0.0602114814, 0.998165258); +// Point3 expectedT(5.30373101, 0.768972495, -49.9942188); +// Vector3 expected_v2(0.107462014, -0.46205501, 0.0115624037); +// Pose3 expected_x2(expectedR, expectedT); +// EXPECT(assert_equal(expected_x2, actual_x2, 1e-7)); +// EXPECT(assert_equal(Vector(expected_v2), actual_v2, 1e-7)); Values values; values.insert(X(1), x1); @@ -843,18 +843,18 @@ TEST(ImuFactor, PredictArbitrary) { for (int i = 0; i < 1000; ++i) pim.integrateMeasurement(measuredAcc, measuredOmega, dt); - Matrix expected(9,9); - expected << // - 0.0299999995, 2.46739898e-10, 2.46739896e-10, -0.0144839494, 0.044978128, 0.0100471195, -0.0409843415, 0.134423822, 0.0383280513, // - 2.46739898e-10, 0.0299999995, 2.46739902e-10, -0.0454268484, -0.0149428917, 0.00609093775, -0.13554868, -0.0471183681, 0.0247643646, // - 2.46739896e-10, 2.46739902e-10, 0.0299999995, 0.00489577218, 0.00839301168, 0.000448720395, 0.00879031682, 0.0162199769, 0.00112485862, // - -0.0144839494, -0.0454268484, 0.00489577218, 0.142448905, 0.00345595825, -0.0225794125, 0.34774305, 0.0119449979, -0.075667905, // - 0.044978128, -0.0149428917, 0.00839301168, 0.00345595825, 0.143318431, 0.0200549262, 0.0112877167, 0.351503176, 0.0629164907, // - 0.0100471195, 0.00609093775, 0.000448720395, -0.0225794125, 0.0200549262, 0.0104041905, -0.0580647212, 0.051116506, 0.0285371399, // - -0.0409843415, -0.13554868, 0.00879031682, 0.34774305, 0.0112877167, -0.0580647212, 0.911721561, 0.0412249672, -0.205920425, // - 0.134423822, -0.0471183681, 0.0162199769, 0.0119449979, 0.351503176, 0.051116506, 0.0412249672, 0.928013807, 0.169935105, // - 0.0383280513, 0.0247643646, 0.00112485862, -0.075667905, 0.0629164907, 0.0285371399, -0.205920425, 0.169935105, 0.09407764; - EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-7)); +// Matrix expected(9,9); +// expected << // +// 0.0299999995, 2.46739898e-10, 2.46739896e-10, -0.0144839494, 0.044978128, 0.0100471195, -0.0409843415, 0.134423822, 0.0383280513, // +// 2.46739898e-10, 0.0299999995, 2.46739902e-10, -0.0454268484, -0.0149428917, 0.00609093775, -0.13554868, -0.0471183681, 0.0247643646, // +// 2.46739896e-10, 2.46739902e-10, 0.0299999995, 0.00489577218, 0.00839301168, 0.000448720395, 0.00879031682, 0.0162199769, 0.00112485862, // +// -0.0144839494, -0.0454268484, 0.00489577218, 0.142448905, 0.00345595825, -0.0225794125, 0.34774305, 0.0119449979, -0.075667905, // +// 0.044978128, -0.0149428917, 0.00839301168, 0.00345595825, 0.143318431, 0.0200549262, 0.0112877167, 0.351503176, 0.0629164907, // +// 0.0100471195, 0.00609093775, 0.000448720395, -0.0225794125, 0.0200549262, 0.0104041905, -0.0580647212, 0.051116506, 0.0285371399, // +// -0.0409843415, -0.13554868, 0.00879031682, 0.34774305, 0.0112877167, -0.0580647212, 0.911721561, 0.0412249672, -0.205920425, // +// 0.134423822, -0.0471183681, 0.0162199769, 0.0119449979, 0.351503176, 0.051116506, 0.0412249672, 0.928013807, 0.169935105, // +// 0.0383280513, 0.0247643646, 0.00112485862, -0.075667905, 0.0629164907, 0.0285371399, -0.205920425, 0.169935105, 0.09407764; +// EXPECT(assert_equal(expected, pim.preintMeasCov(), 1e-7)); // Create factor ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown, From bc99c58226fb71bee35a1f922937e0028e5b8274 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 17:44:00 -0400 Subject: [PATCH 137/142] fix Rot3-from-Matrix construction for Quaternion case --- gtsam/geometry/Pose3.cpp | 2 +- gtsam/geometry/Rot3.h | 12 ++++-------- gtsam/geometry/Rot3M.cpp | 12 ++++++++++++ gtsam/geometry/Rot3Q.cpp | 8 ++++++++ gtsam/geometry/tests/testCalibratedCamera.cpp | 2 +- gtsam/geometry/tests/testSimpleCamera.cpp | 2 +- gtsam/navigation/NavState.h | 2 +- gtsam/navigation/tests/testAHRSFactor.cpp | 2 +- 8 files changed, 29 insertions(+), 13 deletions(-) diff --git a/gtsam/geometry/Pose3.cpp b/gtsam/geometry/Pose3.cpp index 3f46df40d..f373e5ad4 100644 --- a/gtsam/geometry/Pose3.cpp +++ b/gtsam/geometry/Pose3.cpp @@ -387,7 +387,7 @@ boost::optional align(const vector& pairs) { Matrix3 UVtranspose = U * V.transpose(); Matrix3 detWeighting = I_3x3; detWeighting(2, 2) = UVtranspose.determinant(); - Rot3 R(Matrix(V * detWeighting * U.transpose())); + Rot3 R(Matrix3(V * detWeighting * U.transpose())); Point3 t = Point3(cq) - R * Point3(cp); return Pose3(R, t); } diff --git a/gtsam/geometry/Rot3.h b/gtsam/geometry/Rot3.h index 3e95bf04d..cc0dc309e 100644 --- a/gtsam/geometry/Rot3.h +++ b/gtsam/geometry/Rot3.h @@ -86,14 +86,10 @@ namespace gtsam { double R31, double R32, double R33); /** constructor from a rotation matrix */ - template - inline Rot3(const Eigen::MatrixBase& R) { - #ifdef GTSAM_USE_QUATERNIONS - quaternion_=R - #else - rot_ = R; - #endif - } + Rot3(const Matrix3& R); + + /** constructor from a rotation matrix */ + Rot3(const Matrix& R); /** Constructor from a quaternion. This can also be called using a plain * Vector, due to implicit conversion from Vector to Quaternion diff --git a/gtsam/geometry/Rot3M.cpp b/gtsam/geometry/Rot3M.cpp index a7b654070..324c05ae4 100644 --- a/gtsam/geometry/Rot3M.cpp +++ b/gtsam/geometry/Rot3M.cpp @@ -50,6 +50,18 @@ Rot3::Rot3(double R11, double R12, double R13, R31, R32, R33; } +/* ************************************************************************* */ +Rot3::Rot3(const Matrix3& R) { + rot_ = R; +} + +/* ************************************************************************* */ +Rot3::Rot3(const Matrix& R) { + if (R.rows()!=3 || R.cols()!=3) + throw invalid_argument("Rot3 constructor expects 3*3 matrix"); + rot_ = R; +} + /* ************************************************************************* */ Rot3::Rot3(const Quaternion& q) : rot_(q.toRotationMatrix()) { } diff --git a/gtsam/geometry/Rot3Q.cpp b/gtsam/geometry/Rot3Q.cpp index b255b082d..c97e76718 100644 --- a/gtsam/geometry/Rot3Q.cpp +++ b/gtsam/geometry/Rot3Q.cpp @@ -48,6 +48,14 @@ namespace gtsam { R21, R22, R23, R31, R32, R33).finished()) {} + /* ************************************************************************* */ + Rot3::Rot3(const Matrix3& R) : + quaternion_(R) {} + + /* ************************************************************************* */ + Rot3::Rot3(const Matrix& R) : + quaternion_(Matrix3(R)) {} + /* ************************************************************************* */ Rot3::Rot3(const Quaternion& q) : quaternion_(q) { diff --git a/gtsam/geometry/tests/testCalibratedCamera.cpp b/gtsam/geometry/tests/testCalibratedCamera.cpp index 199ae30ce..e7c66c48d 100644 --- a/gtsam/geometry/tests/testCalibratedCamera.cpp +++ b/gtsam/geometry/tests/testCalibratedCamera.cpp @@ -30,7 +30,7 @@ using namespace gtsam; GTSAM_CONCEPT_MANIFOLD_INST(CalibratedCamera) // Camera situated at 0.5 meters high, looking down -static const Pose3 pose1((Matrix)(Matrix(3,3) << +static const Pose3 pose1((Matrix(3,3) << 1., 0., 0., 0.,-1., 0., 0., 0.,-1. diff --git a/gtsam/geometry/tests/testSimpleCamera.cpp b/gtsam/geometry/tests/testSimpleCamera.cpp index 71979481c..3755573ac 100644 --- a/gtsam/geometry/tests/testSimpleCamera.cpp +++ b/gtsam/geometry/tests/testSimpleCamera.cpp @@ -28,7 +28,7 @@ using namespace gtsam; static const Cal3_S2 K(625, 625, 0, 0, 0); -static const Pose3 pose1((Matrix)(Matrix(3,3) << +static const Pose3 pose1((Matrix3)(Matrix(3,3) << 1., 0., 0., 0.,-1., 0., 0., 0.,-1. diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index 9561aa77b..c6c11627c 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -61,7 +61,7 @@ public: } /// Construct from Matrix group representation (no checking) NavState(const Matrix7& T) : - R_(T.block<3, 3>(0, 0)), t_(T.block<3, 1>(0, 6)), v_(T.block<3, 1>(3, 6)) { + R_((Matrix3)T.block<3, 3>(0, 0)), t_(T.block<3, 1>(0, 6)), v_(T.block<3, 1>(3, 6)) { } /// Construct from SO(3) and R^6 NavState(const Matrix3& R, const Vector9 tv) : diff --git a/gtsam/navigation/tests/testAHRSFactor.cpp b/gtsam/navigation/tests/testAHRSFactor.cpp index 9e8e78efe..2121eda35 100644 --- a/gtsam/navigation/tests/testAHRSFactor.cpp +++ b/gtsam/navigation/tests/testAHRSFactor.cpp @@ -435,7 +435,7 @@ TEST (AHRSFactor, predictTest) { // Actual Jacobians Matrix H; (void) pim.predict(bias,H); - EXPECT(assert_equal(expectedH, H)); + EXPECT(assert_equal(expectedH, H, 1e-8)); } //****************************************************************************** #include From d5aea61c47cf1189e223b7c0c457a49411e1cd87 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 22:09:06 -0400 Subject: [PATCH 138/142] fix an Eigen bug to bypass a problem of compiling Eigen's outer product x*x.transpose() when MKL is enabled. IsRowMajor is an enum value defined in DenseBase. However, in the template specialization of GeneralProduct, which is derived from ProductBase<--MatrixBase<--DenseBase, another struct is defined with the same name IsRowMajor, leading to a compilation error. --- gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h b/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h index 9e805a80f..ee63f6a89 100644 --- a/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h +++ b/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h @@ -257,7 +257,7 @@ template class GeneralProduct : public ProductBase, Lhs, Rhs> { - template struct IsRowMajor : internal::conditional<(int(T::Flags)&RowMajorBit), internal::true_type, internal::false_type>::type {}; + template struct IsRowMajorType : internal::conditional<(int(T::Flags)&RowMajorBit), internal::true_type, internal::false_type>::type {}; public: EIGEN_PRODUCT_PUBLIC_INTERFACE(GeneralProduct) @@ -281,22 +281,22 @@ class GeneralProduct template inline void evalTo(Dest& dest) const { - internal::outer_product_selector_run(*this, dest, set(), IsRowMajor()); + internal::outer_product_selector_run(*this, dest, set(), IsRowMajorType()); } template inline void addTo(Dest& dest) const { - internal::outer_product_selector_run(*this, dest, add(), IsRowMajor()); + internal::outer_product_selector_run(*this, dest, add(), IsRowMajorType()); } template inline void subTo(Dest& dest) const { - internal::outer_product_selector_run(*this, dest, sub(), IsRowMajor()); + internal::outer_product_selector_run(*this, dest, sub(), IsRowMajorType()); } template void scaleAndAddTo(Dest& dest, const Scalar& alpha) const { - internal::outer_product_selector_run(*this, dest, adds(alpha), IsRowMajor()); + internal::outer_product_selector_run(*this, dest, adds(alpha), IsRowMajorType()); } }; From a4dc5897160edc375d0d13e47e78204838cb2ac7 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Thu, 17 Sep 2015 22:10:19 -0400 Subject: [PATCH 139/142] fix indent --- gtsam/navigation/tests/testImuFactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index 01b331e42..daa8d0b26 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -155,7 +155,7 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, Q.setZero(); for (size_t i = 0; i < N; i++) { Vector9 xi = samples.col(i); - xi -= sampleMean; + xi -= sampleMean; Q += xi * xi.transpose() / (N - 1); } From 435e042aa05a14a9ed606315c43ec4ba208a696f Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Fri, 18 Sep 2015 23:35:51 -0400 Subject: [PATCH 140/142] revert the Eigen's bug as we can't touch Eigen. Fix our code to play nice with the bug by avoiding cross product. --- gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h | 10 +++++----- gtsam/navigation/tests/testImuFactor.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h b/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h index ee63f6a89..9e805a80f 100644 --- a/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h +++ b/gtsam/3rdparty/Eigen/Eigen/src/Core/GeneralProduct.h @@ -257,7 +257,7 @@ template class GeneralProduct : public ProductBase, Lhs, Rhs> { - template struct IsRowMajorType : internal::conditional<(int(T::Flags)&RowMajorBit), internal::true_type, internal::false_type>::type {}; + template struct IsRowMajor : internal::conditional<(int(T::Flags)&RowMajorBit), internal::true_type, internal::false_type>::type {}; public: EIGEN_PRODUCT_PUBLIC_INTERFACE(GeneralProduct) @@ -281,22 +281,22 @@ class GeneralProduct template inline void evalTo(Dest& dest) const { - internal::outer_product_selector_run(*this, dest, set(), IsRowMajorType()); + internal::outer_product_selector_run(*this, dest, set(), IsRowMajor()); } template inline void addTo(Dest& dest) const { - internal::outer_product_selector_run(*this, dest, add(), IsRowMajorType()); + internal::outer_product_selector_run(*this, dest, add(), IsRowMajor()); } template inline void subTo(Dest& dest) const { - internal::outer_product_selector_run(*this, dest, sub(), IsRowMajorType()); + internal::outer_product_selector_run(*this, dest, sub(), IsRowMajor()); } template void scaleAndAddTo(Dest& dest, const Scalar& alpha) const { - internal::outer_product_selector_run(*this, dest, adds(alpha), IsRowMajorType()); + internal::outer_product_selector_run(*this, dest, adds(alpha), IsRowMajor()); } }; diff --git a/gtsam/navigation/tests/testImuFactor.cpp b/gtsam/navigation/tests/testImuFactor.cpp index daa8d0b26..084213e20 100644 --- a/gtsam/navigation/tests/testImuFactor.cpp +++ b/gtsam/navigation/tests/testImuFactor.cpp @@ -156,7 +156,7 @@ bool MonteCarlo(const PreintegratedImuMeasurements& pim, for (size_t i = 0; i < N; i++) { Vector9 xi = samples.col(i); xi -= sampleMean; - Q += xi * xi.transpose() / (N - 1); + Q += xi * (xi.transpose() / (N - 1)); } // Compare MonteCarlo value with actual (computed) value From d566946600696355fbbab3a96e5fcc8adb8c24fe Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Mon, 21 Sep 2015 10:26:43 -0400 Subject: [PATCH 141/142] fix Rot3 constructor from a matrix. Revert to the generic template version, but provide a overload version for Matrix3 to avoid casting. --- gtsam/geometry/Rot3.h | 30 ++++++++++++++++--- gtsam/geometry/Rot3M.cpp | 12 -------- gtsam/geometry/Rot3Q.cpp | 8 ----- gtsam/geometry/tests/testCalibratedCamera.cpp | 2 +- gtsam/geometry/tests/testSimpleCamera.cpp | 2 +- gtsam/navigation/NavState.h | 2 +- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/gtsam/geometry/Rot3.h b/gtsam/geometry/Rot3.h index cc0dc309e..e548f8eea 100644 --- a/gtsam/geometry/Rot3.h +++ b/gtsam/geometry/Rot3.h @@ -85,11 +85,33 @@ namespace gtsam { double R21, double R22, double R23, double R31, double R32, double R33); - /** constructor from a rotation matrix */ - Rot3(const Matrix3& R); + /** + * Constructor from a rotation matrix + * Version for generic matrices. Need casting to Matrix3 + * in quaternion mode, since Eigen's quaternion doesn't + * allow assignment/construction from a generic matrix. + * See: http://stackoverflow.com/questions/27094132/cannot-understand-if-this-is-circular-dependency-or-clang#tab-top + */ + template + inline Rot3(const Eigen::MatrixBase& R) { + #ifdef GTSAM_USE_QUATERNIONS + quaternion_=Matrix3(R); + #else + rot_ = R; + #endif + } - /** constructor from a rotation matrix */ - Rot3(const Matrix& R); + /** + * Constructor from a rotation matrix + * Overload version for Matrix3 to avoid casting in quaternion mode. + */ + inline Rot3(const Matrix3& R) { + #ifdef GTSAM_USE_QUATERNIONS + quaternion_=R; + #else + rot_ = R; + #endif + } /** Constructor from a quaternion. This can also be called using a plain * Vector, due to implicit conversion from Vector to Quaternion diff --git a/gtsam/geometry/Rot3M.cpp b/gtsam/geometry/Rot3M.cpp index 324c05ae4..a7b654070 100644 --- a/gtsam/geometry/Rot3M.cpp +++ b/gtsam/geometry/Rot3M.cpp @@ -50,18 +50,6 @@ Rot3::Rot3(double R11, double R12, double R13, R31, R32, R33; } -/* ************************************************************************* */ -Rot3::Rot3(const Matrix3& R) { - rot_ = R; -} - -/* ************************************************************************* */ -Rot3::Rot3(const Matrix& R) { - if (R.rows()!=3 || R.cols()!=3) - throw invalid_argument("Rot3 constructor expects 3*3 matrix"); - rot_ = R; -} - /* ************************************************************************* */ Rot3::Rot3(const Quaternion& q) : rot_(q.toRotationMatrix()) { } diff --git a/gtsam/geometry/Rot3Q.cpp b/gtsam/geometry/Rot3Q.cpp index c97e76718..b255b082d 100644 --- a/gtsam/geometry/Rot3Q.cpp +++ b/gtsam/geometry/Rot3Q.cpp @@ -48,14 +48,6 @@ namespace gtsam { R21, R22, R23, R31, R32, R33).finished()) {} - /* ************************************************************************* */ - Rot3::Rot3(const Matrix3& R) : - quaternion_(R) {} - - /* ************************************************************************* */ - Rot3::Rot3(const Matrix& R) : - quaternion_(Matrix3(R)) {} - /* ************************************************************************* */ Rot3::Rot3(const Quaternion& q) : quaternion_(q) { diff --git a/gtsam/geometry/tests/testCalibratedCamera.cpp b/gtsam/geometry/tests/testCalibratedCamera.cpp index e7c66c48d..3b0906b44 100644 --- a/gtsam/geometry/tests/testCalibratedCamera.cpp +++ b/gtsam/geometry/tests/testCalibratedCamera.cpp @@ -30,7 +30,7 @@ using namespace gtsam; GTSAM_CONCEPT_MANIFOLD_INST(CalibratedCamera) // Camera situated at 0.5 meters high, looking down -static const Pose3 pose1((Matrix(3,3) << +static const Pose3 pose1((Matrix3() << 1., 0., 0., 0.,-1., 0., 0., 0.,-1. diff --git a/gtsam/geometry/tests/testSimpleCamera.cpp b/gtsam/geometry/tests/testSimpleCamera.cpp index 3755573ac..515542298 100644 --- a/gtsam/geometry/tests/testSimpleCamera.cpp +++ b/gtsam/geometry/tests/testSimpleCamera.cpp @@ -28,7 +28,7 @@ using namespace gtsam; static const Cal3_S2 K(625, 625, 0, 0, 0); -static const Pose3 pose1((Matrix3)(Matrix(3,3) << +static const Pose3 pose1((Matrix3() << 1., 0., 0., 0.,-1., 0., 0., 0.,-1. diff --git a/gtsam/navigation/NavState.h b/gtsam/navigation/NavState.h index c6c11627c..9561aa77b 100644 --- a/gtsam/navigation/NavState.h +++ b/gtsam/navigation/NavState.h @@ -61,7 +61,7 @@ public: } /// Construct from Matrix group representation (no checking) NavState(const Matrix7& T) : - R_((Matrix3)T.block<3, 3>(0, 0)), t_(T.block<3, 1>(0, 6)), v_(T.block<3, 1>(3, 6)) { + R_(T.block<3, 3>(0, 0)), t_(T.block<3, 1>(0, 6)), v_(T.block<3, 1>(3, 6)) { } /// Construct from SO(3) and R^6 NavState(const Matrix3& R, const Vector9 tv) : From e2ce27f712248158ada9b534805242166cccf365 Mon Sep 17 00:00:00 2001 From: Duy-Nguyen Ta Date: Fri, 18 Sep 2015 22:38:08 -0400 Subject: [PATCH 142/142] move static member definition to cpp file --- gtsam_unstable/geometry/Event.cpp | 29 +++++++++++++++++++++++++++++ gtsam_unstable/geometry/Event.h | 3 --- 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 gtsam_unstable/geometry/Event.cpp diff --git a/gtsam_unstable/geometry/Event.cpp b/gtsam_unstable/geometry/Event.cpp new file mode 100644 index 000000000..68d4d60ce --- /dev/null +++ b/gtsam_unstable/geometry/Event.cpp @@ -0,0 +1,29 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file Event + * @brief Space-time event + * @author Frank Dellaert + * @author Jay Chakravarty + * @date December 2014 + */ + +#include + +namespace gtsam { + +const double Event::Speed = 330; +const Matrix14 Event::JacobianZ = (Matrix14() << 0,0,0,1).finished(); + +} + + diff --git a/gtsam_unstable/geometry/Event.h b/gtsam_unstable/geometry/Event.h index 9d35331bb..3c622924a 100644 --- a/gtsam_unstable/geometry/Event.h +++ b/gtsam_unstable/geometry/Event.h @@ -99,9 +99,6 @@ public: } }; -const double Event::Speed = 330; -const Matrix14 Event::JacobianZ = (Matrix14() << 0,0,0,1).finished(); - // Define GTSAM traits template<> struct GTSAM_EXPORT traits : internal::Manifold {};