Merge branch 'feature/NavState_new_retract' into feature/cleanup_ImuFactor
commit
5c67ceacac
|
|
@ -75,57 +75,89 @@ struct LieGroup {
|
|||
return derived().inverse();
|
||||
}
|
||||
|
||||
/// expmap as required by manifold concept
|
||||
/// Applies exponential map to v and composes with *this
|
||||
Class expmap(const TangentVector& v) const {
|
||||
return compose(Class::Expmap(v));
|
||||
}
|
||||
|
||||
/// logmap as required by manifold concept
|
||||
/// Applies logarithmic map to group element that takes *this to g
|
||||
TangentVector logmap(const Class& g) const {
|
||||
return Class::Logmap(between(g));
|
||||
}
|
||||
|
||||
/// expmap with optional derivatives
|
||||
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;
|
||||
Class h = compose(g); // derivatives inlined below
|
||||
if (H1) *H1 = g.inverse().AdjointMap();
|
||||
if (H2) *H2 = D_g_v;
|
||||
return h;
|
||||
}
|
||||
|
||||
/// logmap with optional derivatives
|
||||
TangentVector logmap(const Class& g, //
|
||||
ChartJacobian H1, ChartJacobian H2 = boost::none) const {
|
||||
Class h = between(g,H1,H2);
|
||||
Class h = between(g); // derivatives inlined below
|
||||
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);
|
||||
if (H1) *H1 = - D_v_h * h.inverse().AdjointMap();
|
||||
if (H2) *H2 = D_v_h;
|
||||
return v;
|
||||
}
|
||||
|
||||
/// Retract at origin: possible in Lie group because it has an identity
|
||||
static Class Retract(const TangentVector& v) {
|
||||
return Class::ChartAtOrigin::Retract(v);
|
||||
}
|
||||
|
||||
/// LocalCoordinates at origin: possible in Lie group because it has an identity
|
||||
static TangentVector LocalCoordinates(const Class& g) {
|
||||
return Class::ChartAtOrigin::Local(g);
|
||||
}
|
||||
|
||||
/// Retract at origin with optional derivative
|
||||
static Class Retract(const TangentVector& v, ChartJacobian H) {
|
||||
return Class::ChartAtOrigin::Retract(v,H);
|
||||
}
|
||||
|
||||
/// LocalCoordinates at origin with optional derivative
|
||||
static TangentVector LocalCoordinates(const Class& g, ChartJacobian H) {
|
||||
return Class::ChartAtOrigin::Local(g,H);
|
||||
}
|
||||
|
||||
/// retract as required by manifold concept: applies v at *this
|
||||
Class retract(const TangentVector& v) const {
|
||||
return compose(Class::ChartAtOrigin::Retract(v));
|
||||
}
|
||||
|
||||
/// localCoordinates as required by manifold concept: finds tangent vector between *this and g
|
||||
TangentVector localCoordinates(const Class& g) const {
|
||||
return Class::ChartAtOrigin::Local(between(g));
|
||||
}
|
||||
|
||||
/// retract with optional derivatives
|
||||
Class retract(const TangentVector& v, //
|
||||
ChartJacobian H1, ChartJacobian H2 = boost::none) const {
|
||||
Jacobian D_g_v;
|
||||
Class g = Class::ChartAtOrigin::Retract(v,H2 ? &D_g_v : 0);
|
||||
Class h = compose(g,H1,H2);
|
||||
if (H2) *H2 = (*H2) * D_g_v;
|
||||
Class g = Class::ChartAtOrigin::Retract(v, H2 ? &D_g_v : 0);
|
||||
Class h = compose(g); // derivatives inlined below
|
||||
if (H1) *H1 = g.inverse().AdjointMap();
|
||||
if (H2) *H2 = D_g_v;
|
||||
return h;
|
||||
}
|
||||
|
||||
/// localCoordinates with optional derivatives
|
||||
TangentVector localCoordinates(const Class& g, //
|
||||
ChartJacobian H1, ChartJacobian H2 = boost::none) const {
|
||||
Class h = between(g,H1,H2);
|
||||
Class h = between(g); // derivatives inlined below
|
||||
Jacobian D_v_h;
|
||||
TangentVector v = Class::ChartAtOrigin::Local(h, (H1 || H2) ? &D_v_h : 0);
|
||||
if (H1) *H1 = D_v_h * (*H1);
|
||||
if (H2) *H2 = D_v_h * (*H2);
|
||||
if (H1) *H1 = - D_v_h * h.inverse().AdjointMap();
|
||||
if (H2) *H2 = D_v_h;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ Pose3 Pose3::ChartAtOrigin::Retract(const Vector6& xi, ChartJacobian H) {
|
|||
return Expmap(xi, H);
|
||||
#else
|
||||
Matrix3 DR;
|
||||
Rot3 R = Rot3::ChartAtOrigin::Retract(xi.head<3>(), H ? &DR : 0);
|
||||
Rot3 R = Rot3::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::ChartAtOrigin::Local(T.rotation(), H ? &DR : 0);
|
||||
Vector3 omega = Rot3::LocalCoordinates(T.rotation(), H ? &DR : 0);
|
||||
if (H) {
|
||||
*H = I_6x6;
|
||||
H->topLeftCorner<3,3>() = DR;
|
||||
|
|
|
|||
|
|
@ -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<typename Derived>
|
||||
inline Rot3(const Eigen::MatrixBase<Derived>& 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;
|
||||
|
||||
|
|
@ -337,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
|
||||
/// @{
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -471,6 +471,15 @@ 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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<Params> p = boost::make_shared<Params>();
|
||||
boost::shared_ptr<Params> p = Params::MakeSharedD();
|
||||
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& n_gravity,
|
||||
const Vector3& omegaCoriolis, const boost::optional<Pose3>& 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<CombinedPreintegratedMeasurements::Params> p =
|
||||
boost::make_shared<CombinedPreintegratedMeasurements::Params>(pim.p());
|
||||
p->gravity = gravity;
|
||||
p->n_gravity = n_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& n_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, n_gravity,
|
||||
omegaCoriolis, use2ndOrderCoriolis);
|
||||
pose_j = pvb.pose;
|
||||
vel_j = pvb.velocity;
|
||||
|
|
|
|||
|
|
@ -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 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 a Z-down navigation frame, such as NED: gravity points along positive Z-axis
|
||||
static boost::shared_ptr<Params> MakeSharedD(double g = 9.81) {
|
||||
return boost::make_shared<Params>(Vector3(0, 0, g));
|
||||
}
|
||||
|
||||
// Default Params for a Z-up navigation frame, such as ENU: gravity points along negative Z-axis
|
||||
static boost::shared_ptr<Params> MakeSharedU(double g = 9.81) {
|
||||
return boost::make_shared<Params>(Vector3(0, 0, -g));
|
||||
}
|
||||
|
||||
private:
|
||||
/// Default constructor makes unitialized params struct
|
||||
Params() {}
|
||||
|
||||
/** Serialization function */
|
||||
friend class boost::serialization::access;
|
||||
template <class ARCHIVE>
|
||||
|
|
@ -134,12 +151,13 @@ class PreintegratedCombinedMeasurements : public PreintegrationBase {
|
|||
Matrix preintMeasCov() const { return preintMeasCov_; }
|
||||
|
||||
/// deprecated constructor
|
||||
/// NOTE(frank): assumes Z-Down 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& n_gravity, const Vector3& omegaCoriolis,
|
||||
const boost::optional<Pose3>& 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& n_gravity, const Vector3& omegaCoriolis,
|
||||
const bool use2ndOrderCoriolis = false);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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<Params> p = boost::make_shared<Params>();
|
||||
boost::shared_ptr<Params> p = Params::MakeSharedD();
|
||||
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<Matrix&> H2, boost::optional<Matrix&> H3,
|
||||
boost::optional<Matrix&> H4, boost::optional<Matrix&> 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<Pose3>& 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<PreintegratedMeasurements::Params> p =
|
||||
boost::make_shared<PreintegratedMeasurements::Params>(pim.p());
|
||||
p->gravity = gravity;
|
||||
const PreintegratedMeasurements& pim, const Vector3& n_gravity,
|
||||
const Vector3& omegaCoriolis, const boost::optional<Pose3>& 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<PreintegratedMeasurements::Params> p = boost::make_shared<
|
||||
PreintegratedMeasurements::Params>(pim.p());
|
||||
p->n_gravity = n_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& n_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, n_gravity,
|
||||
omegaCoriolis, use2ndOrderCoriolis);
|
||||
pose_j = pvb.pose;
|
||||
vel_j = pvb.velocity;
|
||||
}
|
||||
|
||||
} // namespace gtsam
|
||||
} // namespace gtsam
|
||||
|
|
|
|||
|
|
@ -108,11 +108,12 @@ public:
|
|||
Matrix preintMeasCov() const { return preintMeasCov_; }
|
||||
|
||||
/// @deprecated constructor
|
||||
/// NOTE(frank): assumes Z-Down 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& n_gravity, const Vector3& omegaCoriolis,
|
||||
const boost::optional<Pose3>& 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& n_gravity,
|
||||
const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,293 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
|
||||
* 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 <gtsam/navigation/NavState.h>
|
||||
|
||||
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;
|
||||
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;
|
||||
T << R, Z_3x3, t(), Z_3x3, R, v(), Vector6::Zero().transpose(), 1.0;
|
||||
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);
|
||||
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) {
|
||||
Vector9 xi;
|
||||
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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
NavState NavState::Expmap(const Vector9& xi, OptionalJacobian<9, 9> H) {
|
||||
if (H)
|
||||
throw runtime_error("NavState::Expmap derivative not implemented yet");
|
||||
|
||||
Eigen::Block<const Vector9, 3, 1> n_omega_nb = dR(xi);
|
||||
Eigen::Block<const Vector9, 3, 1> v = dP(xi);
|
||||
Eigen::Block<const Vector9, 3, 1> a = dV(xi);
|
||||
|
||||
// NOTE(frank): See Pose3::Expmap
|
||||
Rot3 nRb = Rot3::Expmap(n_omega_nb);
|
||||
double theta2 = n_omega_nb.dot(n_omega_nb);
|
||||
if (theta2 > numeric_limits<double>::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
|
||||
Point3 n_t = Point3(omega_cross_v - nRb * omega_cross_v + n_t_parallel)
|
||||
/ theta2;
|
||||
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 + n_v_parallel) / theta2;
|
||||
return NavState(nRb, n_t, n_v);
|
||||
} else {
|
||||
return NavState(nRb, Point3(v), a);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector9 NavState::Logmap(const NavState& nTb, OptionalJacobian<9, 9> H) {
|
||||
if (H)
|
||||
throw runtime_error("NavState::Logmap derivative not implemented yet");
|
||||
|
||||
TIE(nRb, n_p, n_v, nTb);
|
||||
Vector3 n_t = n_p.vector();
|
||||
|
||||
// NOTE(frank): See Pose3::Logmap
|
||||
Vector9 xi;
|
||||
Vector3 n_omega_nb = Rot3::Logmap(nRb);
|
||||
double theta = n_omega_nb.norm();
|
||||
if (theta * theta <= numeric_limits<double>::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 {
|
||||
// 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 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)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
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((-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;
|
||||
}
|
||||
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;
|
||||
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 xi;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Vector9 NavState::correctPIM(const Vector9& pim, double dt,
|
||||
const Vector3& n_gravity,
|
||||
const boost::optional<Vector3>& 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_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_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);
|
||||
}
|
||||
|
||||
if (H1 || H2) {
|
||||
Matrix3 Ri = nRb.matrix();
|
||||
|
||||
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_v(H1) += dt * D_dP_nv * Ri;
|
||||
D_v_R(H1) += dt * D_dV_Ri;
|
||||
}
|
||||
if (H2) {
|
||||
H2->setIdentity();
|
||||
}
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
}/// namespace gtsam
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
|
||||
* 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 <gtsam/geometry/Pose3.h>
|
||||
#include <gtsam/base/Vector.h>
|
||||
#include <gtsam/base/ProductLieGroup.h>
|
||||
|
||||
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<NavState, 9> {
|
||||
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
|
||||
|
||||
public:
|
||||
|
||||
typedef std::pair<Point3, Velocity3> 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
|
||||
/// @{
|
||||
|
||||
inline const Rot3& attitude() const {
|
||||
return R_;
|
||||
}
|
||||
inline const Point3& position() const {
|
||||
return t_;
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// @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<Vector9, 3, 1> dR(Vector9& v) {
|
||||
return v.segment<3>(0);
|
||||
}
|
||||
static Eigen::Block<Vector9, 3, 1> dP(Vector9& v) {
|
||||
return v.segment<3>(3);
|
||||
}
|
||||
static Eigen::Block<Vector9, 3, 1> dV(Vector9& v) {
|
||||
return v.segment<3>(6);
|
||||
}
|
||||
static Eigen::Block<const Vector9, 3, 1> dR(const Vector9& v) {
|
||||
return v.segment<3>(0);
|
||||
}
|
||||
static Eigen::Block<const Vector9, 3, 1> dP(const Vector9& v) {
|
||||
return v.segment<3>(3);
|
||||
}
|
||||
static Eigen::Block<const Vector9, 3, 1> 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 identity, 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);
|
||||
|
||||
/// @}
|
||||
/// @name Dynamics
|
||||
/// @{
|
||||
|
||||
/// 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;
|
||||
|
||||
/// 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<Vector3>& omegaCoriolis, bool use2ndOrderCoriolis =
|
||||
false, OptionalJacobian<9, 9> H1 = boost::none,
|
||||
OptionalJacobian<9, 9> H2 = boost::none) const;
|
||||
|
||||
private:
|
||||
/// @{
|
||||
/// serialization
|
||||
friend class boost::serialization::access;
|
||||
template<class ARCHIVE>
|
||||
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<NavState> : Testable<NavState>, internal::LieGroupTraits<NavState> {
|
||||
};
|
||||
|
||||
// Partial specialization of wedge
|
||||
// TODO: deprecate, make part of traits
|
||||
template<>
|
||||
inline Matrix wedge<NavState>(const Vector& xi) {
|
||||
return NavState::wedge(xi);
|
||||
}
|
||||
|
||||
} // namespace gtsam
|
||||
|
|
@ -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<const Params> 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:
|
||||
|
|
|
|||
|
|
@ -46,74 +46,65 @@ 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 Vector3 temp = dRij * correctedAcc * deltaT;
|
||||
const Matrix3 dRij = deltaRij_.matrix(); // expensive
|
||||
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)
|
||||
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) {
|
||||
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 << //
|
||||
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();
|
||||
if (!p().use2ndOrderIntegration) {
|
||||
delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT;
|
||||
delPdelBiasOmega_ += delVdelBiasOmega_ * deltaT;
|
||||
} else {
|
||||
delPdelBiasAcc_ += delVdelBiasAcc_ * deltaT - 0.5 * dRij * deltaT * deltaT;
|
||||
delPdelBiasOmega_ += deltaT * (delVdelBiasOmega_ + temp * 0.5);
|
||||
}
|
||||
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);
|
||||
delVdelBiasAcc_ += -dRij * deltaT;
|
||||
delVdelBiasOmega_ += temp;
|
||||
update_delRdelBiasOmega(D_Rincr_integratedOmega, incrR, deltaT);
|
||||
}
|
||||
|
||||
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 +112,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
|
||||
}
|
||||
}
|
||||
|
|
@ -132,18 +124,21 @@ 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(), 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()
|
||||
// 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();
|
||||
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;
|
||||
D_dR_bias << Z_3x3, D_dR_deltaRij * D_deltaRij_bias;
|
||||
|
|
@ -151,115 +146,27 @@ Vector9 PreintegrationBase::biasCorrectedDelta(
|
|||
D_dV_bias << delVdelBiasAcc_, delVdelBiasOmega_;
|
||||
(*H) << D_dR_bias, D_dP_bias, D_dV_bias;
|
||||
}
|
||||
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 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;
|
||||
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;
|
||||
if (p().use2ndOrderCoriolis) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
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 Vector3& vel_i = state_i.velocity();
|
||||
const Matrix3 Ri = pose_i.rotation().matrix();
|
||||
const double dt = deltaTij(), dt2 = dt * dt;
|
||||
|
||||
// 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) = 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;
|
||||
}
|
||||
|
||||
return delta;
|
||||
return xi;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
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 delta = recombinedPrediction(state_i, biasCorrected,
|
||||
H1 ? &D_delta_state : 0, H2 ? &D_delta_biasCorrected : 0);
|
||||
Vector9 xi = state_i.correctPIM(biasCorrected, deltaTij_, p().n_gravity,
|
||||
p().omegaCoriolis, p().use2ndOrderCoriolis, H1 ? &D_delta_state : 0,
|
||||
H2 ? &D_delta_biasCorrected : 0);
|
||||
|
||||
// Use retract to get back to NavState manifold
|
||||
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)
|
||||
|
|
@ -268,148 +175,55 @@ 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::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 {
|
||||
|
||||
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,
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
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& 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<Params> q = boost::make_shared<Params>(p());
|
||||
q->gravity = gravity;
|
||||
q->n_gravity = n_gravity;
|
||||
q->omegaCoriolis = omegaCoriolis;
|
||||
q->use2ndOrderCoriolis = use2ndOrderCoriolis;
|
||||
p_ = q;
|
||||
return PoseVelocityBias(predict(NavState(pose_i, vel_i), bias_i), bias_i);
|
||||
}
|
||||
} /// namespace gtsam
|
||||
} /// namespace gtsam
|
||||
|
|
|
|||
|
|
@ -22,105 +22,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <gtsam/navigation/PreintegratedRotation.h>
|
||||
#include <gtsam/navigation/NavState.h>
|
||||
#include <gtsam/navigation/ImuBias.h>
|
||||
#include <gtsam/geometry/Pose3.h>
|
||||
#include <gtsam/base/Matrix.h>
|
||||
#include <gtsam/base/ProductLieGroup.h>
|
||||
#include <gtsam/base/Vector.h>
|
||||
#include <boost/make_shared.hpp>
|
||||
|
||||
namespace gtsam {
|
||||
|
||||
/// Velocity in 3D is just a Vector3
|
||||
typedef Vector3 Velocity3;
|
||||
|
||||
/**
|
||||
* Navigation state: Pose (rotation, translation) + velocity
|
||||
*/
|
||||
class NavState: public ProductLieGroup<Pose3, Velocity3> {
|
||||
protected:
|
||||
typedef ProductLieGroup<Pose3, Velocity3> 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<Vector9,3,1> dR(Vector9& v) { return v.segment<3>(0); }
|
||||
static Eigen::Block<Vector9,3,1> dP(Vector9& v) { return v.segment<3>(3); }
|
||||
static Eigen::Block<Vector9,3,1> dV(Vector9& v) { return v.segment<3>(6); }
|
||||
static Eigen::Block<const Vector9,3,1> dR(const Vector9& v) { return v.segment<3>(0); }
|
||||
static Eigen::Block<const Vector9,3,1> dP(const Vector9& v) { return v.segment<3>(3); }
|
||||
static Eigen::Block<const Vector9,3,1> dV(const Vector9& v) { return v.segment<3>(6); }
|
||||
|
||||
// Specialized Retract/Local that agrees with IMUFactors
|
||||
// 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;
|
||||
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 {
|
||||
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<NavState> : internal::LieGroupTraits<NavState> {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -129,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 n_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 getting the navigation frame gravity vector
|
||||
/// For convenience, two commonly used conventions are provided by named constructors below
|
||||
Params(const Vector3& n_gravity) :
|
||||
accelerometerCovariance(I_3x3), integrationCovariance(I_3x3), use2ndOrderCoriolis(
|
||||
false), n_gravity(n_gravity) {
|
||||
}
|
||||
|
||||
// Default Params for a Z-down navigation frame, such as NED: gravity points along positive Z-axis
|
||||
static boost::shared_ptr<Params> MakeSharedD(double g = 9.81) {
|
||||
return boost::make_shared<Params>(Vector3(0, 0, g));
|
||||
}
|
||||
|
||||
// Default Params for a Z-up navigation frame, such as ENU: gravity points along negative Z-axis
|
||||
static boost::shared_ptr<Params> MakeSharedU(double g = 9.81) {
|
||||
return boost::make_shared<Params>(Vector3(0, 0, -g));
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Default constructor for serialization only: uninitialized!
|
||||
Params();
|
||||
|
||||
private:
|
||||
/** Serialization function */
|
||||
friend class boost::serialization::access;
|
||||
template<class ARCHIVE>
|
||||
|
|
@ -159,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(n_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
|
||||
|
|
@ -189,27 +123,45 @@ class PreintegrationBase : public PreintegratedRotation {
|
|||
* @param p Parameters, typically fixed in a single application
|
||||
*/
|
||||
PreintegrationBase(const boost::shared_ptr<const Params>& 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<const Params>(p_);}
|
||||
const Params& p() const {
|
||||
return *boost::static_pointer_cast<const Params>(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;
|
||||
|
|
@ -218,52 +170,41 @@ 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
|
||||
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,
|
||||
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;
|
||||
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& n_gravity,
|
||||
const Vector3& omegaCoriolis, const bool use2ndOrderCoriolis = false);
|
||||
|
||||
private:
|
||||
private:
|
||||
/** Serialization function */
|
||||
friend class boost::serialization::access;
|
||||
template<class ARCHIVE>
|
||||
|
|
@ -280,4 +221,4 @@ class PreintegrationBase : public PreintegratedRotation {
|
|||
}
|
||||
};
|
||||
|
||||
} /// namespace gtsam
|
||||
} /// namespace gtsam
|
||||
|
|
|
|||
|
|
@ -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<Vector3>& measuredAccs,
|
||||
const list<Vector3>& measuredOmegas, const list<double>& 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<Vector3>::const_iterator itAcc = measuredAccs.begin();
|
||||
list<Vector3>::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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ 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);
|
||||
|
||||
/* ************************************************************************* */
|
||||
namespace {
|
||||
|
|
@ -56,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);
|
||||
|
|
@ -92,11 +89,9 @@ const Matrix3 kIntegrationErrorCovariance = intNoiseVar * I_3x3;
|
|||
/* ************************************************************************* */
|
||||
PreintegratedImuMeasurements evaluatePreintegratedMeasurements(
|
||||
const imuBias::ConstantBias& bias, const list<Vector3>& measuredAccs,
|
||||
const list<Vector3>& measuredOmegas, const list<double>& deltaTs,
|
||||
const bool use2ndOrderIntegration = false) {
|
||||
const list<Vector3>& measuredOmegas, const list<double>& deltaTs) {
|
||||
PreintegratedImuMeasurements result(bias, kMeasuredAccCovariance,
|
||||
kMeasuredOmegaCovariance, kIntegrationErrorCovariance,
|
||||
use2ndOrderIntegration);
|
||||
kMeasuredOmegaCovariance, kIntegrationErrorCovariance);
|
||||
|
||||
list<Vector3>::const_iterator itAcc = measuredAccs.begin();
|
||||
list<Vector3>::const_iterator itOmega = measuredOmegas.begin();
|
||||
|
|
@ -140,6 +135,44 @@ Vector3 evaluateLogRotation(const Vector3 thetahat, const Vector3 deltatheta) {
|
|||
|
||||
} // namespace
|
||||
|
||||
/* ************************************************************************* */
|
||||
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));
|
||||
|
||||
imuBias::ConstantBias biasHat, bias;
|
||||
boost::shared_ptr<PreintegratedImuMeasurements::Params> p =
|
||||
PreintegratedImuMeasurements::Params::MakeSharedU(g); // Up 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));
|
||||
EXPECT(assert_equal(expected, pim.predict(state1, bias)));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
TEST(ImuFactor, PreintegratedMeasurements) {
|
||||
// Linearization point
|
||||
|
|
@ -157,19 +190,15 @@ 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(
|
||||
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;
|
||||
|
|
@ -183,159 +212,90 @@ 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));
|
||||
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);
|
||||
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);
|
||||
// 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));
|
||||
static const NavState state1(x1, v1);
|
||||
|
||||
EXPECT(assert_equal(identity, (NavState)identity.retract(zero(9)), tol));
|
||||
EXPECT(assert_equal(zero(9), identity.localCoordinates(identity), tol));
|
||||
// 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 double deltaT = 1.0;
|
||||
|
||||
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<NavState, NavState>(
|
||||
boost::bind(&NavState::retract, _1, delta, boost::none, boost::none),state1);
|
||||
EXPECT(assert_equal(eH1, aH1));
|
||||
Matrix eH2 = numericalDerivative11<NavState, Vector9>(
|
||||
boost::bind(&NavState::retract, state1, _1, boost::none, boost::none), delta);
|
||||
EXPECT(assert_equal(eH2, aH2));
|
||||
}
|
||||
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<PreintegratedImuMeasurements::Params> p =
|
||||
boost::make_shared<PreintegratedImuMeasurements::Params>();
|
||||
PreintegratedImuMeasurements::Params::MakeSharedD();
|
||||
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);
|
||||
pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
|
||||
pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
|
||||
|
||||
{ // biasCorrectedDelta
|
||||
Matrix96 actualH;
|
||||
pim.biasCorrectedDelta(bias, actualH);
|
||||
Matrix expectedH = numericalDerivative11<Vector9, imuBias::ConstantBias>(
|
||||
boost::bind(&PreintegrationBase::biasCorrectedDelta, pim, _1,
|
||||
boost::none), bias);
|
||||
EXPECT(assert_equal(expectedH, actualH));
|
||||
}
|
||||
{
|
||||
Matrix99 actualH;
|
||||
pim.integrateCoriolis(state1, actualH);
|
||||
Matrix expectedH = numericalDerivative11<Vector9, NavState>(
|
||||
boost::bind(&PreintegrationBase::integrateCoriolis, pim, _1,
|
||||
boost::none), state1);
|
||||
EXPECT(assert_equal(expectedH, actualH));
|
||||
}
|
||||
{
|
||||
Matrix99 aH1, aH2;
|
||||
Vector9 biasCorrectedDelta = pim.biasCorrectedDelta(bias);
|
||||
pim.recombinedPrediction(state1, biasCorrectedDelta, aH1, aH2);
|
||||
Matrix eH1 = numericalDerivative11<Vector9, NavState>(
|
||||
boost::bind(&PreintegrationBase::recombinedPrediction, pim, _1,
|
||||
biasCorrectedDelta, boost::none, boost::none), state1);
|
||||
EXPECT(assert_equal(eH1, aH1));
|
||||
Matrix eH2 = numericalDerivative11<Vector9, Vector9>(
|
||||
boost::bind(&PreintegrationBase::recombinedPrediction, pim, state1, _1,
|
||||
boost::none, boost::none), biasCorrectedDelta);
|
||||
EXPECT(assert_equal(eH2, aH2));
|
||||
}
|
||||
{
|
||||
Matrix99 aH1;
|
||||
Matrix96 aH2;
|
||||
pim.predict(state1, bias, aH1, aH2);
|
||||
Matrix eH1 = numericalDerivative11<NavState, NavState>(
|
||||
boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none,
|
||||
boost::none), state1);
|
||||
EXPECT(assert_equal(eH1, aH1));
|
||||
Matrix eH2 = numericalDerivative11<NavState, imuBias::ConstantBias>(
|
||||
boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none,
|
||||
boost::none), bias);
|
||||
EXPECT(assert_equal(eH2, aH2));
|
||||
}
|
||||
// biasCorrectedDelta
|
||||
Matrix96 actualH;
|
||||
pim.biasCorrectedDelta(bias, actualH);
|
||||
Matrix expectedH = numericalDerivative11<Vector9, imuBias::ConstantBias>(
|
||||
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<NavState, NavState>(
|
||||
boost::bind(&PreintegrationBase::predict, pim, _1, bias, boost::none,
|
||||
boost::none), state1);
|
||||
EXPECT(assert_equal(eH1, aH1));
|
||||
Matrix eH2 = numericalDerivative11<NavState, imuBias::ConstantBias>(
|
||||
boost::bind(&PreintegrationBase::predict, pim, state1, _1, boost::none,
|
||||
boost::none), bias);
|
||||
EXPECT(assert_equal(eH2, aH2));
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
TEST(ImuFactor, ErrorAndJacobians) {
|
||||
using namespace common;
|
||||
bool use2ndOrderIntegration = true;
|
||||
PreintegratedImuMeasurements pim(bias,
|
||||
kMeasuredAccCovariance, kMeasuredOmegaCovariance,
|
||||
kIntegrationErrorCovariance, use2ndOrderIntegration);
|
||||
PreintegratedImuMeasurements pim(biasHat, kMeasuredAccCovariance,
|
||||
kMeasuredOmegaCovariance, kIntegrationErrorCovariance);
|
||||
|
||||
pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
|
||||
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);
|
||||
|
|
@ -343,11 +303,11 @@ 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;
|
||||
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;
|
||||
|
|
@ -366,20 +326,20 @@ 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), 1e-2));
|
||||
EXPECT(assert_equal(expectedError, factor.unwhitenedError(values), 1e-2));
|
||||
|
||||
// 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);
|
||||
EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -392,23 +352,36 @@ 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()
|
||||
Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown)
|
||||
+ 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);
|
||||
|
||||
// Make sure of biascorrectedDeltaRij with this example...
|
||||
Matrix3 aH;
|
||||
pim.biascorrectedDeltaRij(bias.gyroscope(), aH);
|
||||
Matrix3 eH = numericalDerivative11<Rot3, Vector3>(
|
||||
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<Vector9, imuBias::ConstantBias>(
|
||||
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, kGravity,
|
||||
nonZeroOmegaCoriolis);
|
||||
ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown,
|
||||
kNonZeroOmegaCoriolis);
|
||||
|
||||
Values values;
|
||||
values.insert(X(1), x1);
|
||||
|
|
@ -419,7 +392,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-3);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -432,11 +405,9 @@ 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()
|
||||
Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown)
|
||||
+ Vector3(0.2, 0.0, 0.0);
|
||||
double deltaT = 1.0;
|
||||
|
||||
|
|
@ -449,8 +420,8 @@ 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,
|
||||
nonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis);
|
||||
ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown,
|
||||
kNonZeroOmegaCoriolis, bodyPsensor, use2ndOrderCoriolis);
|
||||
|
||||
Values values;
|
||||
values.insert(X(1), x1);
|
||||
|
|
@ -461,7 +432,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-3);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -484,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));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -597,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()));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -621,11 +590,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
|
||||
|
|
@ -648,20 +616,17 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) {
|
|||
// Compute expected f_pos_vel wrt positions
|
||||
Matrix dfpv_dpos = numericalDerivative11<Vector, Vector3>(
|
||||
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<Vector, Vector3>(
|
||||
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<Vector, Rot3>(
|
||||
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;
|
||||
|
|
@ -688,14 +653,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation) {
|
|||
// Compute jacobian wrt acc noise
|
||||
Matrix dgpv_daccNoise = numericalDerivative11<Vector, Vector3>(
|
||||
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<Vector, Vector3>(
|
||||
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;
|
||||
|
||||
|
|
@ -744,11 +707,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
|
||||
|
|
@ -771,20 +733,17 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) {
|
|||
// Compute expected f_pos_vel wrt positions
|
||||
Matrix dfpv_dpos = numericalDerivative11<Vector, Vector3>(
|
||||
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<Vector, Vector3>(
|
||||
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<Vector, Rot3>(
|
||||
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;
|
||||
|
|
@ -811,14 +770,12 @@ TEST(ImuFactor, JacobianPreintegratedCovariancePropagation_2ndOrderInt) {
|
|||
// Compute jacobian wrt acc noise
|
||||
Matrix dgpv_daccNoise = numericalDerivative11<Vector, Vector3>(
|
||||
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<Vector, Vector3>(
|
||||
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;
|
||||
|
||||
|
|
@ -857,11 +814,9 @@ 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()
|
||||
Vector3 measuredAcc = x1.rotation().unrotate(-kGravityAlongNavZDown)
|
||||
+ Vector3(0.2, 0.0, 0.0);
|
||||
double deltaT = 1.0;
|
||||
|
||||
|
|
@ -876,8 +831,8 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) {
|
|||
pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
|
||||
|
||||
// Create factor
|
||||
ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravity,
|
||||
nonZeroOmegaCoriolis);
|
||||
ImuFactor factor(X(1), V(1), X(2), V(2), B(1), pim, kGravityAlongNavZDown,
|
||||
kNonZeroOmegaCoriolis);
|
||||
|
||||
Values values;
|
||||
values.insert(X(1), x1);
|
||||
|
|
@ -888,7 +843,8 @@ TEST(ImuFactor, ErrorWithBiasesAndSensorBodyDisplacement) {
|
|||
|
||||
// Make sure linearization is correct
|
||||
double diffDelta = 1e-5;
|
||||
EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-5);
|
||||
// This fails, except if tol = 1e-1: probably wrong!
|
||||
EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, diffDelta, 1e-3);
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -911,14 +867,14 @@ 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,
|
||||
kZeroOmegaCoriolis);
|
||||
PoseVelocityBias poseVelocity = pim.predict(x1, v1, bias,
|
||||
kGravityAlongNavZDown, kZeroOmegaCoriolis);
|
||||
Pose3 expectedPose(Rot3(), Point3(0, 0.5, 0));
|
||||
Vector3 expectedVelocity;
|
||||
expectedVelocity << 0, 1, 0;
|
||||
|
|
@ -946,13 +902,15 @@ 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, 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;
|
||||
|
|
@ -960,6 +918,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 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)
|
||||
pim.integrateMeasurement(measuredAcc, measuredOmega, deltaT);
|
||||
|
||||
// Create factor
|
||||
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, kGravityAlongNavZDown,
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,206 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
|
||||
* 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 <gtsam/navigation/NavState.h>
|
||||
#include <gtsam/base/TestableAssertions.h>
|
||||
#include <gtsam/base/numericalDerivative.h>
|
||||
#include <CppUnitLite/TestHarness.h>
|
||||
|
||||
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);
|
||||
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) {
|
||||
Matrix39 aH, eH;
|
||||
Rot3 actual = kState1.attitude(aH);
|
||||
EXPECT(assert_equal(actual, kRotation));
|
||||
eH = numericalDerivative11<Rot3, NavState>(
|
||||
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<Point3, NavState>(
|
||||
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<Velocity3, NavState>(
|
||||
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)));
|
||||
|
||||
// check group product agrees with matrix product
|
||||
NavState state2 = kState1 * kState1;
|
||||
Matrix T2 = T * T;
|
||||
EXPECT(assert_equal(state2, NavState(T2)));
|
||||
EXPECT(assert_equal(T2, state2.matrix()));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
TEST( NavState, Manifold ) {
|
||||
// Check zero xi
|
||||
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
|
||||
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));
|
||||
Velocity3 dvel = Velocity3(-0.1, -0.2, -0.3);
|
||||
NavState state2 = kState1 * NavState(drot, dt, dvel);
|
||||
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)));
|
||||
|
||||
// Check derivatives for ChartAtOrigin::Retract
|
||||
Matrix9 aH;
|
||||
// For zero xi
|
||||
boost::function<NavState(const Vector9&)> Retract = boost::bind(
|
||||
NavState::ChartAtOrigin::Retract, _1, boost::none);
|
||||
NavState::ChartAtOrigin::Retract(kZeroXi, aH);
|
||||
EXPECT(assert_equal(numericalDerivative11(Retract, kZeroXi), aH));
|
||||
// For non-zero xi
|
||||
NavState::ChartAtOrigin::Retract(xi, aH);
|
||||
EXPECT(assert_equal(numericalDerivative11(Retract, xi), aH));
|
||||
|
||||
// Check derivatives for ChartAtOrigin::Local
|
||||
// For zero xi
|
||||
boost::function<Vector9(const NavState&)> 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);
|
||||
boost::function<NavState(const NavState&, const Vector9&)> 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<Vector9(const NavState&, const NavState&)> 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(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);
|
||||
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)));
|
||||
|
||||
// roundtrip from state2 to state3 and back
|
||||
NavState state3 = state2.expmap(xi);
|
||||
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)));
|
||||
EXPECT(assert_equal(xi, -state3.logmap(state2)));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
static const double dt = 2.0;
|
||||
boost::function<Vector9(const NavState&, const bool&)> coriolis = boost::bind(
|
||||
&NavState::coriolis, _1, dt, kOmegaCoriolis, _2, boost::none);
|
||||
|
||||
TEST(NavState, Coriolis) {
|
||||
Matrix9 aH;
|
||||
|
||||
// first-order
|
||||
kState1.coriolis(dt, kOmegaCoriolis, false, aH);
|
||||
EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, false), aH));
|
||||
// second-order
|
||||
kState1.coriolis(dt, kOmegaCoriolis, true, aH);
|
||||
EXPECT(assert_equal(numericalDerivative21(coriolis, kState1, true), aH));
|
||||
}
|
||||
|
||||
TEST(NavState, Coriolis2) {
|
||||
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, aH);
|
||||
EXPECT(assert_equal(numericalDerivative21(coriolis, state2, false), aH));
|
||||
// second-order
|
||||
state2.coriolis(dt, kOmegaCoriolis, true, aH);
|
||||
EXPECT(assert_equal(numericalDerivative21(coriolis, state2, true), aH));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
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 aH1, aH2;
|
||||
boost::function<Vector9(const NavState&, const Vector9&)> correctPIM =
|
||||
boost::bind(&NavState::correctPIM, _1, _2, dt, kGravity, kOmegaCoriolis,
|
||||
false, boost::none, boost::none);
|
||||
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));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
int main() {
|
||||
TestResult tr;
|
||||
return TestRegistry::runAllTests(tr);
|
||||
}
|
||||
/* ************************************************************************* */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Pose3,Velocity3> {
|
||||
protected:
|
||||
|
|
@ -145,14 +146,12 @@ public:
|
|||
|
||||
/// RRTMbn - Function computes the rotation rate transformation matrix from
|
||||
/// body axis rates to euler angle (global) rates
|
||||
static Matrix RRTMbn(const Vector& euler);
|
||||
|
||||
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);
|
||||
|
||||
static Matrix RRTMnb(const Vector3& euler);
|
||||
static Matrix RRTMnb(const Rot3& att);
|
||||
/// @}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))));
|
||||
}
|
||||
|
||||
/* ************************************************************************* */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue