diff --git a/CMakeLists.txt b/CMakeLists.txt index 67ef6f0b03b6d7d9c8638a38439607ddb35b09d9..b11c6f81823fd082020c9fe0fa0c37d3ab3c3884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,6 +129,9 @@ SET(HDRS_COMMON SET(HDRS_MATH include/IMU/math/IMU_tools.h ) +SET(HDRS_MATH + include/IMU/math/IMU_tools_Lie.h + ) SET(HDRS_UTILS ) SET(HDRS_CAPTURE @@ -160,6 +163,9 @@ SET(SRCS_COMMON SET(SRCS_MATH include/IMU/math/IMU_tools.h ) +SET(SRCS_MATH + include/IMU/math/IMU_tools_Lie.h + ) SET(SRCS_UTILS ) diff --git a/include/IMU/math/IMU_tools_Lie.h b/include/IMU/math/IMU_tools_Lie.h new file mode 100644 index 0000000000000000000000000000000000000000..a6abf56578104e405775db1612c148aaa0d04c6b --- /dev/null +++ b/include/IMU/math/IMU_tools_Lie.h @@ -0,0 +1,702 @@ +/* + * imu_tools_Lie.h + * + * Created on: Oct 7, 2019 + * Author: mfourmy + */ + +#ifndef IMU_TOOLS_LIE_H_ +#define IMU_TOOLS_LIE_H_ + +#include "core/common/wolf.h" +#include "core/math/rotations.h" + +/* + * Most functions in this file are explained in the document: + * + * Joan Sola, "IMU pre-integration", 2015-2017 IRI-CSIC + * + * They relate manipulations of Delta motion magnitudes used for IMU pre-integration. + * + * The Delta is defined as + * Delta = [Dp, Dq, Dv] + * with + * Dp : position delta + * Dq : quaternion delta + * Dv : velocity delta + * + * They are listed below: + * + * - identity: I = Delta at the origin, with Dp = [0,0,0]; Dq = [0,0,0,1], Dv = [0,0,0] + * - inverse: so that D (+) D.inv = I + * - compose: Dc = D1 (+) D2 + * - between: Db = D2 (-) D1, so that D2 = D1 (+) Db + * - composeOverState: x2 = x1 (+) D + * - betweenStates: D = x2 (-) x1, so that x2 = x1 (+) D + * - log: got from Delta manifold to tangent space (equivalent to log() in rotations) + * - exp_IMU: go from tangent space to delta manifold (equivalent to exp() in rotations) + * - plus: D2 = D1 (+) exp_IMU(d) + * - diff: d = log_IMU( D2 (-) D1 ) + * - body2delta: construct a delta from body magnitudes of linAcc and angVel + */ + +namespace wolf +{ +namespace imu_with_dt { +using namespace Eigen; + +template<typename D1, typename D2, typename D3, typename T> +inline void identity(MatrixBase<D1>& p, QuaternionBase<D2>& q, MatrixBase<D3>& v, T& dt) +{ + // Same as imu_tools.h with dt added to the state + p = MatrixBase<D1>::Zero(3,1); + q = QuaternionBase<D2>::Identity(); + v = MatrixBase<D3>::Zero(3,1); + dt = T(0); +} + +template<typename D1, typename D2, typename D3, typename T> +inline void identity(MatrixBase<D1>& p, MatrixBase<D2>& q, MatrixBase<D3>& v, T& dt) +{ + typedef typename D1::Scalar T1; + typedef typename D2::Scalar T2; + typedef typename D3::Scalar T3; + p << T1(0), T1(0), T1(0); + q << T2(0), T2(0), T2(0), T2(1); + v << T3(0), T3(0), T3(0); + dt = T(0); +} + +template<typename T = Scalar> +inline Matrix<T, 11, 1> identity() +{ + Matrix<T, 11, 1> ret; + ret<< T(0), T(0), T(0), + T(0), T(0), T(0), T(1), + T(0), T(0), T(0), + T(0); + return ret; +} + +template<typename D1, typename D2> +inline void adjoint(const MatrixBase<D1> d, MatrixBase<D1> adjd) +{ + // Adjoint matrix associated to the adjoint operator + Matrix<typename D1::Scalar, 3, 1> dp = d.segment<3>(0); + Matrix<typename D1::Scalar, 3, 3> dR = q2R(d.segment<4>(3)); + Matrix<typename D1::Scalar, 3, 1> dv = d.segment<3>(7); + typename D1::Scalar dt = d(10); + + adjd.setIdentity(); + adjd<3,3>(0,0) = dR; + adjd<3,3>(0,3) = skew(dp - dv * dt) * dR; + adjd<3,3>(0,6) = -dR * dt; + adjd<3,1>(0,9) = dv; + adjd<3,3>(3,3) = dR; + adjd<3,3>(6,3) = skew(dv) * dR; + adjd<3,3>(6,6) = dR; +} + +template<typename D> +inline Matrix<typename D::Scalar, 10, 10> adjoint(const MatrixBase<D> d){ + Matrix<typename D::Scalar, 10, 10> adjd; + adjoint(d, adjd); + return adjd; +} + + +// template<typename D1, typename D2> +// inline Matrix<T, 11, 1> smalladjoint(const MatrixBase<D1> d, MatrixBase<D1> sadjd) +// { +// // Adjoint matrix associated to the adjoint operator + // TODO + +// return ret; +// } + +template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename T> +inline void inverse(const MatrixBase<D1>& dp, const QuaternionBase<D2>& dq, const MatrixBase<D3>& dv, const T& dt, + MatrixBase<D4>& idp, QuaternionBase<D5>& idq, MatrixBase<D6>& idv, T& idt) +{ + // Same as imu_tools.h + MatrixSizeCheck<3, 1>::check(dp); + MatrixSizeCheck<3, 1>::check(dv); + MatrixSizeCheck<3, 1>::check(idp); + MatrixSizeCheck<3, 1>::check(idv); + + idp = - ( dq.conjugate() * (dp - dv * typename D3::Scalar(dt) ) ); + idq = dq.conjugate(); + idv = - ( dq.conjugate() * dv ); + idt = - dt; +} + +template<typename D1, typename D2> +inline void inverse(const MatrixBase<D1>& d, + MatrixBase<D2>& id) +{ + MatrixSizeCheck<11, 1>::check(d); + MatrixSizeCheck<11, 1>::check(id); + + typedef typename D1::Scalar T; + + Map<const Matrix<typename D1::Scalar, 3, 1> > dp ( & d( 0 ) ); + Map<const Quaternion<typename D1::Scalar> > dq ( & d( 3 ) ); + Map<const Matrix<typename D1::Scalar, 3, 1> > dv ( & d( 7 ) ); + const T& dt = d( 10 ) ; + Map<Matrix<typename D2::Scalar, 3, 1> > idp ( & id( 0 ) ); + Map<Quaternion<typename D2::Scalar> > idq ( & id( 3 ) ); + Map<Matrix<typename D2::Scalar, 3, 1> > idv ( & id( 7 ) ); + T& idt = id( 10 ) ; + + inverse(dp, dq, dv, dt, idp, idq, idv, idt); +} + +template<typename D> +inline Matrix<typename D::Scalar, 11, 1> inverse(const MatrixBase<D>& d) +{ + Matrix<typename D::Scalar, 11, 1> id; + inverse(d, id); + return id; +} + +template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9, typename T> +inline void compose(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const MatrixBase<D3>& dv1, const T& dt1, + const MatrixBase<D4>& dp2, const QuaternionBase<D5>& dq2, const MatrixBase<D6>& dv2, const T& dt2, + MatrixBase<D7>& sum_p, QuaternionBase<D8>& sum_q, MatrixBase<D9>& sum_v, T& sum_dt) +{ + // Same as imu_tools.h + MatrixSizeCheck<3, 1>::check(dp1); + MatrixSizeCheck<3, 1>::check(dv1); + MatrixSizeCheck<3, 1>::check(dp2); + MatrixSizeCheck<3, 1>::check(dv2); + MatrixSizeCheck<3, 1>::check(sum_p); + MatrixSizeCheck<3, 1>::check(sum_v); + + sum_p = dp1 + dv1*dt2 + dq1*dp2; + sum_v = dv1 + dq1*dv2; + sum_q = dq1*dq2; + sum_dt = dt1 + dt2; +} + +template<typename D1, typename D2, typename D3> +inline void compose(const MatrixBase<D1>& d1, + const MatrixBase<D2>& d2, + MatrixBase<D3>& sum) +{ + MatrixSizeCheck<11, 1>::check(d1); + MatrixSizeCheck<11, 1>::check(d2); + MatrixSizeCheck<11, 1>::check(sum); + + Map<const Matrix<typename D1::Scalar, 3, 1> > dp1 ( & d1( 0 ) ); + Map<const Quaternion<typename D1::Scalar> > dq1 ( & d1( 3 ) ); + Map<const Matrix<typename D1::Scalar, 3, 1> > dv1 ( & d1( 7 ) ); + const typename D1::Scalar& dt1 = d1(10); + Map<const Matrix<typename D2::Scalar, 3, 1> > dp2 ( & d2( 0 ) ); + Map<const Quaternion<typename D2::Scalar> > dq2 ( & d2( 3 ) ); + Map<const Matrix<typename D2::Scalar, 3, 1> > dv2 ( & d2( 7 ) ); + const typename D2::Scalar& dt2 = d2(10); + Map<Matrix<typename D3::Scalar, 3, 1> > sum_p ( & sum( 0 ) ); + Map<Quaternion<typename D3::Scalar> > sum_q ( & sum( 3 ) ); + Map<Matrix<typename D3::Scalar, 3, 1> > sum_v ( & sum( 7 ) ); + typename D3::Scalar& sum_dt = sum(10); + + compose(dp1, dq1, dv1, dt1, dp2, dq2, dv2, dt2, sum_p, sum_q, sum_v, sum_dt); +} + +template<typename D1, typename D2> +inline Matrix<typename D1::Scalar, 11, 1> compose(const MatrixBase<D1>& d1, + const MatrixBase<D2>& d2) +{ + Matrix<typename D1::Scalar, 11, 1> ret; + compose(d1, d2, ret); + return ret; +} + +template<typename D1, typename D2, typename D3, typename D4, typename D5> +inline void compose(const MatrixBase<D1>& d1, + const MatrixBase<D2>& d2, + MatrixBase<D3>& sum, + MatrixBase<D4>& J_sum_d1, + MatrixBase<D5>& J_sum_d2) +{ + // Same as imu_tools.h + MatrixSizeCheck<11, 1>::check(d1); + MatrixSizeCheck<11, 1>::check(d2); + MatrixSizeCheck<11, 1>::check(sum); + MatrixSizeCheck< 10, 10>::check(J_sum_d1); + MatrixSizeCheck< 10, 10>::check(J_sum_d2); + + // Jac wrt first delta -> adjoint inverse + J_sum_d1 = adjoint(inverse(d1)); + + // Jac wrt second delta -> indentity + J_sum_d2.setIdentity(); // + + // compose deltas -- done here to avoid aliasing when calling with input `d1` and result `sum` referencing the same variable + compose(d1, d2, sum); +} + +template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9, typename T> +inline void between(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const MatrixBase<D3>& dv1, const T dt1, + const MatrixBase<D4>& dp2, const QuaternionBase<D5>& dq2, const MatrixBase<D6>& dv2, const T dt2, + MatrixBase<D7>& diff_p, QuaternionBase<D8>& diff_q, MatrixBase<D9>& diff_v, T& diff_dt ) +{ + // Different from imu_tools.h + MatrixSizeCheck<3, 1>::check(dp1); + MatrixSizeCheck<3, 1>::check(dv1); + MatrixSizeCheck<3, 1>::check(dp2); + MatrixSizeCheck<3, 1>::check(dv2); + MatrixSizeCheck<3, 1>::check(diff_p); + MatrixSizeCheck<3, 1>::check(diff_v); + + diff_p = dq1.conjugate() * ( dp2 - dp1 - dv1*(dt2 - dt1)); // very different! + diff_v = dq1.conjugate() * ( dv2 - dv1 ); + diff_q = dq1.conjugate() * dq2; + diff_dt = dt2 - dt1; +} + +template<typename D1, typename D2, typename D3> +inline void between(const MatrixBase<D1>& d1, + const MatrixBase<D2>& d2, + MatrixBase<D3>& d2_minus_d1) +{ + MatrixSizeCheck<11, 1>::check(d1); + MatrixSizeCheck<11, 1>::check(d2); + MatrixSizeCheck<11, 1>::check(d2_minus_d1); + + Map<const Matrix<typename D1::Scalar, 3, 1> > dp1 ( & d1(0) ); + Map<const Quaternion<typename D1::Scalar> > dq1 ( & d1(3) ); + Map<const Matrix<typename D1::Scalar, 3, 1> > dv1 ( & d1(7) ); + const typename D1::Scalar& dt1 = d1(10); + Map<const Matrix<typename D2::Scalar, 3, 1> > dp2 ( & d2(0) ); + Map<const Quaternion<typename D2::Scalar> > dq2 ( & d2(3) ); + Map<const Matrix<typename D2::Scalar, 3, 1> > dv2 ( & d2(7) ); + const typename D2::Scalar& dt2 = d2(10); + Map<Matrix<typename D3::Scalar, 3, 1> > diff_p ( & d2_minus_d1(0) ); + Map<Quaternion<typename D3::Scalar> > diff_q ( & d2_minus_d1(3) ); + Map<Matrix<typename D3::Scalar, 3, 1> > diff_v ( & d2_minus_d1(7) ); + typename D3::Scalar& diff_dt = d2_minus_d1(10); + + between(dp1, dq1, dv1, dt1, dp2, dq2, dv2, dt2, diff_p, diff_q, diff_v, diff_dt); +} + +template<typename D1, typename D2> +inline Matrix<typename D1::Scalar, 11, 1> between(const MatrixBase<D1>& d1, + const MatrixBase<D2>& d2) +{ + Matrix<typename D1::Scalar, 11, 1> diff; + between(d1, d2, diff); + return diff; +} + +template<typename D1, typename D2, typename D3> +inline void composeOverState(const MatrixBase<D1>& x, + const MatrixBase<D2>& d, + MatrixBase<D3>& x_plus_d) +{ + // Same as imu_tools.h + MatrixSizeCheck<10, 1>::check(x); + MatrixSizeCheck<11, 1>::check(d); + MatrixSizeCheck<10, 1>::check(x_plus_d); + + Map<const Matrix<typename D1::Scalar, 3, 1> > p ( & x( 0 ) ); + Map<const Quaternion<typename D1::Scalar> > q ( & x( 3 ) ); + Map<const Matrix<typename D1::Scalar, 3, 1> > v ( & x( 7 ) ); + Map<const Matrix<typename D2::Scalar, 3, 1> > dp ( & d( 0 ) ); + Map<const Quaternion<typename D2::Scalar> > dq ( & d( 3 ) ); + Map<const Matrix<typename D2::Scalar, 3, 1> > dv ( & d( 7 ) ); + const typename D2::Scalar& dt = d(10); + Map<Matrix<typename D3::Scalar, 3, 1> > p_plus_d ( & x_plus_d( 0 ) ); + Map<Quaternion<typename D3::Scalar> > q_plus_d ( & x_plus_d( 3 ) ); + Map<Matrix<typename D3::Scalar, 3, 1> > v_plus_d ( & x_plus_d( 7 ) ); + + p_plus_d = p + v*dt + 0.5*gravity()*dt*dt + q*dp; + v_plus_d = v + gravity()*dt + q*dv; + q_plus_d = q*dq; // dq here to avoid possible aliasing between x and x_plus_d +} + +template<typename D1, typename D2> +inline Matrix<typename D1::Scalar, 10, 1> composeOverState(const MatrixBase<D1>& x, + const MatrixBase<D2>& d) +{ + Matrix<typename D1::Scalar, 10, 1> ret; + composeOverState(x, d, ret); + return ret; +} + +template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9, class T> +inline void betweenStates(const MatrixBase<D1>& p1, const QuaternionBase<D2>& q1, const MatrixBase<D3>& v1, + const MatrixBase<D4>& p2, const QuaternionBase<D5>& q2, const MatrixBase<D6>& v2, + const T dt, + MatrixBase<D7>& dp, QuaternionBase<D8>& dq, MatrixBase<D9>& dv, T& Dt) +{ + // Same as imu_tools.h + MatrixSizeCheck<3, 1>::check(p1); + MatrixSizeCheck<3, 1>::check(v1); + MatrixSizeCheck<3, 1>::check(p2); + MatrixSizeCheck<3, 1>::check(v2); + MatrixSizeCheck<3, 1>::check(dp); + MatrixSizeCheck<3, 1>::check(dv); + + dp = q1.conjugate() * ( p2 - p1 - v1*dt - (T)0.5*gravity().cast<T>()*(T)dt*(T)dt ); + dv = q1.conjugate() * ( v2 - v1 - gravity().cast<T>()*(T)dt ); + dq = q1.conjugate() * q2; + Dt = dt; // a bit stupid... to keep things consistant +} + +template<typename D1, typename D2, typename D3, class T> +inline void betweenStates(const MatrixBase<D1>& x1, + const MatrixBase<D2>& x2, + T dt, + MatrixBase<D3>& x2_minus_x1) +{ + MatrixSizeCheck<10, 1>::check(x1); + MatrixSizeCheck<10, 1>::check(x2); + MatrixSizeCheck<11, 1>::check(x2_minus_x1); + + Map<const Matrix<typename D1::Scalar, 3, 1> > p1 ( & x1(0) ); + Map<const Quaternion<typename D1::Scalar> > q1 ( & x1(3) ); + Map<const Matrix<typename D1::Scalar, 3, 1> > v1 ( & x1(7) ); + Map<const Matrix<typename D2::Scalar, 3, 1> > p2 ( & x2(0) ); + Map<const Quaternion<typename D2::Scalar> > q2 ( & x2(3) ); + Map<const Matrix<typename D2::Scalar, 3, 1> > v2 ( & x2(7) ); + Map<Matrix<typename D3::Scalar, 3, 1> > dp ( & x2_minus_x1(0) ); + Map<Quaternion<typename D3::Scalar> > dq ( & x2_minus_x1(3) ); + Map<Matrix<typename D3::Scalar, 3, 1> > dv ( & x2_minus_x1(7) ); + T& Dt = x2_minus_x1(10); + + betweenStates(p1, q1, v1, p2, q2, v2, dt, dp, dq, dv, Dt); +} + +template<typename D1, typename D2, class T> +inline Matrix<typename D1::Scalar, 11, 1> betweenStates(const MatrixBase<D1>& x1, + const MatrixBase<D2>& x2, + T dt) +{ + Matrix<typename D1::Scalar, 11, 1> ret; + betweenStates(x1, x2, dt, ret); + return ret; +} + + +template<typename D1, typename D2, typename D3> +inline void QandPmat(const MatrixBase<D1>& th, MatrixBase<D2>& Q, MatrixBase<D3>& P){ + // See equations 29-31 humanoids fourmy 19 + // th = omega * dt + MatrixSizeCheck<3, 1>::check(th); + MatrixSizeCheck<3, 3>::check(Q); + MatrixSizeCheck<3, 3>::check(P); + + typename D1::Scalar thn = th.norm(); + if (thn < 1e-8){ + Q.setIdentity(); + P.setIdentity(); + P *= 0.5; + } + else{ + Matrix<typename D1::Scalar, 3, 3> Id; + Id.setIdentity(); + Matrix<typename D1::Scalar, 3, 1> u = th / thn; + Matrix<typename D1::Scalar, 3, 3> ux = skew(u); + + Q = Id + + + ((1 - cos(thn))/thn)*ux + + ((thn - sin(thn))/(thn*thn))*(ux*ux); + + P = 0.5*Id + + ((thn - sin(thn))/(thn*thn))*ux + + ((cos(thn) + 0.5*thn*thn - 1)/(thn*thn))*(ux*ux); + } +} + +template<typename Derived> +Matrix<typename Derived::Scalar, 11, 1> exp_IMU(const MatrixBase<Derived>& d_in) + +{ + MatrixSizeCheck<10, 1>::check(d_in); + + Matrix<typename Derived::Scalar, 11, 1> ret; + + Map<const Matrix<typename Derived::Scalar, 3, 1> > dp_in ( & d_in(0) ); + Map<const Matrix<typename Derived::Scalar, 3, 1> > do_in ( & d_in(3) ); + Map<const Matrix<typename Derived::Scalar, 3, 1> > dv_in ( & d_in(6) ); + const typename Derived::Scalar& dt_in = d_in(9); + Map<Matrix<typename Derived::Scalar, 3, 1> > dp ( & ret(0) ); + Map<Quaternion<typename Derived::Scalar> > dq ( & ret(3) ); + Map<Matrix<typename Derived::Scalar, 3, 1> > dv ( & ret(7) ); + typename Derived::Scalar& dt = ret(10); + + Matrix<typename Derived::Scalar, 3, 3> Q; + Matrix<typename Derived::Scalar, 3, 3> P; + QandPmat(do_in, Q, P); + std::cout << "Q" << std::endl; + std::cout << Q << std::endl; + std::cout << "P" << std::endl; + std::cout << P << std::endl; + + dp = Q*dp_in + P*dv_in*dt_in; + dv = Q*dp_in; + dq = exp_q(do_in); + dt = dt_in; + + return ret; +} + +template<typename Derived> +Matrix<typename Derived::Scalar, 10, 1> log_IMU(const MatrixBase<Derived>& delta_in) +{ + MatrixSizeCheck<11, 1>::check(delta_in); + Matrix<typename Derived::Scalar, 10, 1> ret; + + Map<const Matrix<typename Derived::Scalar, 3, 1> > dp_in ( & delta_in(0) ); + Map<const Quaternion<typename Derived::Scalar> > dq_in ( & delta_in(3) ); + Map<const Matrix<typename Derived::Scalar, 3, 1> > dv_in ( & delta_in(7) ); + const typename Derived::Scalar& dt_in = delta_in(9); + Map<Matrix<typename Derived::Scalar, 3, 1> > dp_ret ( & ret(0) ); + Map<Matrix<typename Derived::Scalar, 3, 1> > do_ret ( & ret(3) ); + Map<Matrix<typename Derived::Scalar, 3, 1> > dv_ret ( & ret(6) ); + typename Derived::Scalar& dt_ret = ret(9); + + Matrix<typename Derived::Scalar, 3, 3> Q; + Matrix<typename Derived::Scalar, 3, 3> Qinv; + Matrix<typename Derived::Scalar, 3, 3> P; + do_ret = log_q(dq_in); + QandPmat(do_ret, Q, P); + Qinv = Q.inverse(); + + dp_ret = Qinv*(dp_in - P*Qinv*dv_in*dt_in); + dv_ret = Qinv*dv_in; + dt_ret = dt_in; + + return ret; +} + + + +// template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9> +// inline void plus(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const MatrixBase<D3>& dv1, +// const MatrixBase<D4>& dp2, const MatrixBase<D5>& do2, const MatrixBase<D6>& dv2, +// MatrixBase<D7>& plus_p, QuaternionBase<D8>& plus_q, MatrixBase<D9>& plus_v ) +// { +// plus_p = dp1 + dp2; +// plus_v = dv1 + dv2; +// plus_q = dq1 * exp_q(do2); +// } + +// template<typename D1, typename D2, typename D3> +// inline void plus(const MatrixBase<D1>& d1, +// const MatrixBase<D2>& d2, +// MatrixBase<D3>& d_pert) +// { +// Map<const Matrix<typename D1::Scalar, 3, 1> > dp1 ( & d1(0) ); +// Map<const Quaternion<typename D1::Scalar> > dq1 ( & d1(3) ); +// Map<const Matrix<typename D1::Scalar, 3, 1> > dv1 ( & d1(7) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > dp2 ( & d2(0) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > do2 ( & d2(3) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > dv2 ( & d2(6) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > dp_p ( & d_pert(0) ); +// Map<Quaternion<typename D3::Scalar> > dq_p ( & d_pert(3) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > dv_p ( & d_pert(7) ); + +// plus(dp1, dq1, dv1, dp2, do2, dv2, dp_p, dq_p, dv_p); +// } + +// template<typename D1, typename D2> +// inline Matrix<typename D1::Scalar, 10, 1> plus(const MatrixBase<D1>& d1, +// const MatrixBase<D2>& d2) +// { +// Matrix<typename D1::Scalar, 10, 1> ret; +// plus(d1, d2, ret); +// return ret; +// } + +// template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9> +// inline void diff(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const MatrixBase<D3>& dv1, +// const MatrixBase<D4>& dp2, const QuaternionBase<D5>& dq2, const MatrixBase<D6>& dv2, +// MatrixBase<D7>& diff_p, MatrixBase<D8>& diff_o, MatrixBase<D9>& diff_v ) +// { +// diff_p = dp2 - dp1; +// diff_v = dv2 - dv1; +// diff_o = log_q(dq1.conjugate() * dq2); +// } + +// template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7, typename D8, typename D9, typename D10, typename D11> +// inline void diff(const MatrixBase<D1>& dp1, const QuaternionBase<D2>& dq1, const MatrixBase<D3>& dv1, +// const MatrixBase<D4>& dp2, const QuaternionBase<D5>& dq2, const MatrixBase<D6>& dv2, +// MatrixBase<D7>& diff_p, MatrixBase<D8>& diff_o, MatrixBase<D9>& diff_v , +// MatrixBase<D10>& J_do_dq1, MatrixBase<D11>& J_do_dq2) +// { +// diff(dp1, dq1, dv1, dp2, dq2, dv2, diff_p, diff_o, diff_v); + +// J_do_dq1 = - jac_SO3_left_inv(diff_o); +// J_do_dq2 = jac_SO3_right_inv(diff_o); +// } + +// template<typename D1, typename D2, typename D3> +// inline void diff(const MatrixBase<D1>& d1, +// const MatrixBase<D2>& d2, +// MatrixBase<D3>& err) +// { +// Map<const Matrix<typename D1::Scalar, 3, 1> > dp1 ( & d1(0) ); +// Map<const Quaternion<typename D1::Scalar> > dq1 ( & d1(3) ); +// Map<const Matrix<typename D1::Scalar, 3, 1> > dv1 ( & d1(7) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > dp2 ( & d2(0) ); +// Map<const Quaternion<typename D2::Scalar> > dq2 ( & d2(3) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > dv2 ( & d2(7) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > diff_p ( & err(0) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > diff_o ( & err(3) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > diff_v ( & err(6) ); + +// diff(dp1, dq1, dv1, dp2, dq2, dv2, diff_p, diff_o, diff_v); +// } + +// template<typename D1, typename D2, typename D3, typename D4, typename D5> +// inline void diff(const MatrixBase<D1>& d1, +// const MatrixBase<D2>& d2, +// MatrixBase<D3>& dif, +// MatrixBase<D4>& J_diff_d1, +// MatrixBase<D5>& J_diff_d2) +// { +// Map<const Matrix<typename D1::Scalar, 3, 1> > dp1 ( & d1(0) ); +// Map<const Quaternion<typename D1::Scalar> > dq1 ( & d1(3) ); +// Map<const Matrix<typename D1::Scalar, 3, 1> > dv1 ( & d1(7) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > dp2 ( & d2(0) ); +// Map<const Quaternion<typename D2::Scalar> > dq2 ( & d2(3) ); +// Map<const Matrix<typename D2::Scalar, 3, 1> > dv2 ( & d2(7) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > diff_p ( & dif(0) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > diff_o ( & dif(3) ); +// Map<Matrix<typename D3::Scalar, 3, 1> > diff_v ( & dif(6) ); + +// Matrix<typename D4::Scalar, 3, 3> J_do_dq1, J_do_dq2; + +// diff(dp1, dq1, dv1, dp2, dq2, dv2, diff_p, diff_o, diff_v, J_do_dq1, J_do_dq2); + +// /* d = diff(d1, d2) is +// * dp = dp2 - dp1 +// * do = Log(dq1.conj * dq2) +// * dv = dv2 - dv1 +// * +// * With trivial Jacobians for dp and dv, and: +// * J_do_dq1 = - J_l_inv(theta) +// * J_do_dq2 = J_r_inv(theta) +// */ + +// J_diff_d1 = - Matrix<typename D4::Scalar, 9, 9>::Identity();// d(p2 - p1) / d(p1) = - Identity +// J_diff_d1.block(3,3,3,3) = J_do_dq1; // d(R1.tr*R2) / d(R1) = - J_l_inv(theta) + +// J_diff_d2.setIdentity(); // d(R1.tr*R2) / d(R2) = Identity +// J_diff_d2.block(3,3,3,3) = J_do_dq2; // d(R1.tr*R2) / d(R1) = J_r_inv(theta) +// } + +// template<typename D1, typename D2> +// inline Matrix<typename D1::Scalar, 9, 1> diff(const MatrixBase<D1>& d1, +// const MatrixBase<D2>& d2) +// { +// Matrix<typename D1::Scalar, 9, 1> ret; +// diff(d1, d2, ret); +// return ret; +// } + +// template<typename D1, typename D2, typename D3, typename D4, typename D5> +// inline void body2delta(const MatrixBase<D1>& a, +// const MatrixBase<D2>& w, +// const typename D1::Scalar& dt, +// MatrixBase<D3>& dp, +// QuaternionBase<D4>& dq, +// MatrixBase<D5>& dv) +// { +// MatrixSizeCheck<3,1>::check(a); +// MatrixSizeCheck<3,1>::check(w); +// MatrixSizeCheck<3,1>::check(dp); +// MatrixSizeCheck<3,1>::check(dv); + +// dp = 0.5 * a * dt * dt; +// dv = a * dt; +// dq = exp_q(w * dt); +// } + +// template<typename D1> +// inline Matrix<typename D1::Scalar, 10, 1> body2delta(const MatrixBase<D1>& body, +// const typename D1::Scalar& dt) +// { +// MatrixSizeCheck<6,1>::check(body); + +// typedef typename D1::Scalar T; + +// Matrix<T, 10, 1> delta; + +// Map< Matrix<T, 3, 1>> dp ( & delta(0) ); +// Map< Quaternion<T>> dq ( & delta(3) ); +// Map< Matrix<T, 3, 1>> dv ( & delta(7) ); + +// body2delta(body.block(0,0,3,1), body.block(3,0,3,1), dt, dp, dq, dv); + +// return delta; +// } + +// template<typename D1, typename D2, typename D3> +// inline void body2delta(const MatrixBase<D1>& body, +// const typename D1::Scalar& dt, +// MatrixBase<D2>& delta, +// MatrixBase<D3>& jac_body) +// { +// MatrixSizeCheck<6,1>::check(body); +// MatrixSizeCheck<9,6>::check(jac_body); + +// typedef typename D1::Scalar T; + +// delta = body2delta(body, dt); + +// Matrix<T, 3, 1> w = body.block(3,0,3,1); + +// jac_body.setZero(); +// jac_body.block(0,0,3,3) = 0.5 * dt * dt * Matrix<T, 3, 3>::Identity(); +// jac_body.block(3,3,3,3) = dt * jac_SO3_right(w * dt); +// jac_body.block(6,0,3,3) = dt * Matrix<T, 3, 3>::Identity(); +// } + +// template<typename D1, typename D2, typename D3, typename D4, typename D5, typename D6, typename D7> +// void motion2data(const MatrixBase<D1>& a, const MatrixBase<D2>& w, const QuaternionBase<D3>& q, const MatrixBase<D4>& a_b, const MatrixBase<D5>& w_b, MatrixBase<D6>& a_m, MatrixBase<D7>& w_m) +// { +// MatrixSizeCheck<3,1>::check(a); +// MatrixSizeCheck<3,1>::check(w); +// MatrixSizeCheck<3,1>::check(a_b); +// MatrixSizeCheck<3,1>::check(w_b); +// MatrixSizeCheck<3,1>::check(a_m); +// MatrixSizeCheck<3,1>::check(w_m); + +// // Note: data = (a_m , w_m) +// a_m = a + a_b - q.conjugate()*gravity(); +// w_m = w + w_b; +// } + +// /* Create IMU data from body motion +// * Input: +// * motion : [ax, ay, az, wx, wy, wz] the motion in body frame +// * q : the current orientation wrt horizontal +// * bias : the bias of the IMU +// * Output: +// * return : the data vector as created by the IMU (with motion, gravity, and bias) +// */ +// template<typename D1, typename D2, typename D3> +// Matrix<typename D1::Scalar, 6, 1> motion2data(const MatrixBase<D1>& motion, const QuaternionBase<D2>& q, const MatrixBase<D3>& bias) +// { +// Matrix<typename D1::Scalar, 6, 1> data; +// Map<Matrix<typename D1::Scalar, 3, 1>> a_m (data.data() ); +// Map<Matrix<typename D1::Scalar, 3, 1>> w_m (data.data() + 3); + +// motion2data(motion.block(0,0,3,1), +// motion.block(3,0,3,1), +// q, +// bias.block(0,0,3,1), +// bias.block(3,0,3,1), +// a_m, +// w_m ); + +// return data; +// } + +} // namespace imu +} // namespace wolf + +#endif /* IMU_TOOLS_LIE_H_ */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 266bdfb1e5e5e3faf8adf566570285b0be5eee57..d4d5539b9100f6cad5edce7dc7a88a693a81156c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -22,6 +22,9 @@ target_link_libraries(gtest_IMU ${PLUGIN_NAME} ${wolf_LIBRARY}) wolf_add_gtest(gtest_IMU_tools gtest_IMU_tools.cpp) target_link_libraries(gtest_IMU_tools ${PLUGIN_NAME} ${wolf_LIBRARY}) +wolf_add_gtest(gtest_IMU_tools_Lie gtest_IMU_tools_Lie.cpp) +target_link_libraries(gtest_IMU_tools_Lie ${PLUGIN_NAME} ${wolf_LIBRARY}) + wolf_add_gtest(gtest_processor_IMU_jacobians gtest_processor_IMU_jacobians.cpp) target_link_libraries(gtest_processor_IMU_jacobians ${PLUGIN_NAME} ${wolf_LIBRARY}) diff --git a/test/gtest_IMU_tools_Lie.cpp b/test/gtest_IMU_tools_Lie.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97fdfebb271e675fdf7dcb04b49089b9d1d0f6f1 --- /dev/null +++ b/test/gtest_IMU_tools_Lie.cpp @@ -0,0 +1,636 @@ +/* + * gtest_imu_tools_Lie.cpp + * + * Created on: Oct 7, 2019 + * Author: mfourmy + */ + +#include "IMU/math/IMU_tools_Lie.h" +#include <core/utils/utils_gtest.h> + +using namespace Eigen; +using namespace wolf; +using namespace imu_with_dt; // used in IMU_tools_Lie + +TEST(IMU_tools, identity) +{ + VectorXs id_true; + id_true.setZero(11); + id_true(6) = 1.0; + + VectorXs id = identity<Scalar>(); + ASSERT_MATRIX_APPROX(id, id_true, 1e-10); +} + +TEST(IMU_tools, identity_split) +{ + VectorXs p(3), qv(4), v(3); + Scalar dt; + Quaternions q; + + identity(p,q,v,dt); + ASSERT_MATRIX_APPROX(p, Vector3s::Zero(), 1e-10); + ASSERT_QUATERNION_APPROX(q, Quaternions::Identity(), 1e-10); + ASSERT_MATRIX_APPROX(v, Vector3s::Zero(), 1e-10); + ASSERT_NEAR(dt, 0.0, 1e-10); + + identity(p,qv,v,dt); + ASSERT_MATRIX_APPROX(p , Vector3s::Zero(), 1e-10); + ASSERT_MATRIX_APPROX(qv, (Vector4s()<<0,0,0,1).finished(), 1e-10); + ASSERT_MATRIX_APPROX(v , Vector3s::Zero(), 1e-10); + ASSERT_NEAR(dt, 0.0, 1e-10); +} + +TEST(IMU_tools, inverse) +{ + VectorXs d(11), id(11), iid(11), iiid(11), I(11); + Vector4s qv; + + qv = (Vector4s() << 3, 4, 5, 6).finished().normalized(); + d << 0, 1, 2, qv, 7, 8, 9, 0.1; + + id = inverse(d); + iid = inverse(id); + + ASSERT_MATRIX_APPROX(d, iid, 1e-10); + + compose(d, id, I); + ASSERT_MATRIX_APPROX(I, identity(), 1e-10); + compose(id, d, I); + ASSERT_MATRIX_APPROX(I, identity(), 1e-10); + + ASSERT_MATRIX_APPROX( d, iid, 1e-10); + iiid = inverse(iid); + ASSERT_MATRIX_APPROX(id, iiid, 1e-10); +} + +TEST(IMU_tools, compose_between) +{ + VectorXs dx1(11), dx2(11), dx3(11); + Matrix<Scalar, 11, 1> d1, d2, d3; + Vector4s qv; + Scalar dt = 0.1; + + qv = (Vector4s() << 3, 4, 5, 6).finished().normalized(); + dx1 << 0, 1, 2, qv, 7, 8, 9, dt; + d1 = dx1; + qv = (Vector4s() << 6, 5, 4, 3).finished().normalized(); + dx2 << 9, 8, 7, qv, 2, 1, 0, dt; + d2 = dx2; + + // combinations of templates for sum() + compose(dx1, dx2, dx3); + compose(d1, d2, d3); + ASSERT_MATRIX_APPROX(d3, dx3, 1e-10); // ?? compare results from the same fonction? + + compose(d1, dx2, dx3); + d3 = compose(dx1, d2); + ASSERT_MATRIX_APPROX(d3, dx3, 1e-10); + + // minus(d1, d3) should recover delta_2 + VectorXs diffx(11); + Matrix<Scalar,11,1> diff; + between(d1, d3, diff); + ASSERT_MATRIX_APPROX(diff, d2, 1e-10); + + // minus(d3, d1) should recover inverse(d2) + diff = between(d3, d1); + ASSERT_MATRIX_APPROX(diff, inverse(d2), 1e-10); +} + +TEST(IMU_tools, compose_between_with_state) +{ + VectorXs x(10), d(11), x2(10), x3(10), d2(11), d3(11); + Vector4s qv; + Scalar dt = 0.1; + + qv = (Vector4s() << 3, 4, 5, 6).finished().normalized(); + x << 0, 1, 2, qv, 7, 8, 9; + qv = (Vector4s() << 6, 5, 4, 3).finished().normalized(); + d << 9, 8, 7, qv, 2, 1, 0, dt; + + composeOverState(x, d, x2); + x3 = composeOverState(x, d); + ASSERT_MATRIX_APPROX(x3, x2, 1e-10); + + // betweenStates(x, x2) should recover d + betweenStates(x, x2, dt, d2); + d3 = betweenStates(x, x2, dt); + ASSERT_MATRIX_APPROX(d2, d, 1e-10); + ASSERT_MATRIX_APPROX(d3, d, 1e-10); + ASSERT_MATRIX_APPROX(betweenStates(x, x2, dt), d, 1e-10); + + // x + (x2 - x) = x2 + ASSERT_MATRIX_APPROX(composeOverState(x, betweenStates(x, x2, dt)), x2, 1e-10); + + // (x + d) - x = d + ASSERT_MATRIX_APPROX(betweenStates(x, composeOverState(x, d), dt), d, 1e-10); +} + +TEST(IMU_tools, lift_retract) +{ + VectorXs d_min(10); + Scalar dt_min = 0.1; + d_min << 0, 1, 2, .3, .4, .5, 6, 7, 8, dt_min; // use angles in the ball theta < pi + + // transform to delta + VectorXs delta = exp_IMU(d_min); + + // expected delta -> reimplement formulas TODO + // Vector3s dp = d_min.head(3); + // Quaternions dq = v2q(d_min.segment<3>(3)); + // Vector3s dv = d_min.tail(3); + // Scalar dt = d_min(9); + // VectorXs delta_true(11); delta_true << dp, dq.coeffs(), dv, dt; + // ASSERT_MATRIX_APPROX(delta, delta_true, 1e-10); + + // transform to a new tangent -- should be the original tangent + VectorXs d_from_delta = log_IMU(delta); + ASSERT_MATRIX_APPROX(d_from_delta, d_min, 1e-10); + + // transform to a new delta -- should be the previous delta + VectorXs delta_from_d = exp_IMU(d_from_delta); + ASSERT_MATRIX_APPROX(delta_from_d, delta, 1e-10); +} + +// TEST(IMU_tools, plus) +// { +// VectorXs d1(10), d2(10), d3(10); +// VectorXs err(9); +// Vector4s qv = (Vector4s() << 3, 4, 5, 6).finished().normalized(); +// d1 << 0, 1, 2, qv, 7, 8, 9; +// err << 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09; + +// d3.head(3) = d1.head(3) + err.head(3); +// d3.segment(3,4) = (Quaternions(qv.data()) * exp_q(err.segment(3,3))).coeffs(); +// d3.tail(3) = d1.tail(3) + err.tail(3); + +// plus(d1, err, d2); +// ASSERT_MATRIX_APPROX(diff(d3, d2), VectorXs::Zero(9), 1e-10); +// } + +// TEST(IMU_tools, diff) +// { +// VectorXs d1(10), d2(10); +// Vector4s qv = (Vector4s() << 3, 4, 5, 6).finished().normalized(); +// d1 << 0, 1, 2, qv, 7, 8, 9; +// d2 = d1; + +// VectorXs err(9); +// diff(d1, d2, err); +// ASSERT_MATRIX_APPROX(err, VectorXs::Zero(9), 1e-10); +// ASSERT_MATRIX_APPROX(diff(d1, d2), VectorXs::Zero(9), 1e-10); + +// VectorXs d3(10); +// d3.setRandom(); d3.segment(3,4).normalize(); +// err.head(3) = d3.head(3) - d1.head(3); +// err.segment(3,3) = log_q(Quaternions(d1.data()+3).conjugate()*Quaternions(d3.data()+3)); +// err.tail(3) = d3.tail(3) - d1.tail(3); + +// ASSERT_MATRIX_APPROX(err, diff(d1, d3), 1e-10); + +// } + +// TEST(IMU_tools, compose_jacobians) +// { +// VectorXs d1(10), d2(10), d3(10), d1_pert(10), d2_pert(10), d3_pert(10); // deltas +// VectorXs t1(9), t2(9); // tangents +// Matrix<Scalar, 9, 9> J1_a, J2_a, J1_n, J2_n; +// Vector4s qv1, qv2; +// Scalar dt = 0.1; +// Scalar dx = 1e-6; +// qv1 = (Vector4s() << 3, 4, 5, 6).finished().normalized(); +// d1 << 0, 1, 2, qv1, 7, 8, 9; +// qv2 = (Vector4s() << 1, 2, 3, 4).finished().normalized(); +// d2 << 9, 8, 7, qv2, 2, 1, 0; + +// // analytical jacobians +// compose(d1, d2, dt, d3, J1_a, J2_a); + +// // numerical jacobians +// for (unsigned int i = 0; i<9; i++) +// { +// t1 . setZero(); +// t1(i) = dx; + +// // Jac wrt first delta +// d1_pert = plus(d1, t1); // (d1 + t1) +// d3_pert = compose(d1_pert, d2, dt); // (d1 + t1) + d2 +// t2 = diff(d3, d3_pert); // { (d2 + t1) + d2 } - { d1 + d2 } +// J1_n.col(i) = t2/dx; // [ { (d2 + t1) + d2 } - { d1 + d2 } ] / dx + +// // Jac wrt second delta +// d2_pert = plus(d2, t1); // (d2 + t1) +// d3_pert = compose(d1, d2_pert, dt); // d1 + (d2 + t1) +// t2 = diff(d3, d3_pert); // { d1 + (d2 + t1) } - { d1 + d2 } +// J2_n.col(i) = t2/dx; // [ { d1 + (d2 + t1) } - { d1 + d2 } ] / dx +// } + +// // check that numerical and analytical match +// ASSERT_MATRIX_APPROX(J1_a, J1_n, 1e-4); +// ASSERT_MATRIX_APPROX(J2_a, J2_n, 1e-4); +// } + +// TEST(IMU_tools, diff_jacobians) +// { +// VectorXs d1(10), d2(10), d3(9), d1_pert(10), d2_pert(10), d3_pert(9); // deltas +// VectorXs t1(9), t2(9); // tangents +// Matrix<Scalar, 9, 9> J1_a, J2_a, J1_n, J2_n; +// Vector4s qv1, qv2; +// Scalar dx = 1e-6; +// qv1 = (Vector4s() << 3, 4, 5, 6).finished().normalized(); +// d1 << 0, 1, 2, qv1, 7, 8, 9; +// qv2 = (Vector4s() << 1, 2, 3, 4).finished().normalized(); +// d2 << 9, 8, 7, qv2, 2, 1, 0; + +// // analytical jacobians +// diff(d1, d2, d3, J1_a, J2_a); + +// // numerical jacobians +// for (unsigned int i = 0; i<9; i++) +// { +// t1 . setZero(); +// t1(i) = dx; + +// // Jac wrt first delta +// d1_pert = plus(d1, t1); // (d1 + t1) +// d3_pert = diff(d1_pert, d2); // d2 - (d1 + t1) +// t2 = d3_pert - d3; // { d2 - (d1 + t1) } - { d2 - d1 } +// J1_n.col(i) = t2/dx; // [ { d2 - (d1 + t1) } - { d2 - d1 } ] / dx + +// // Jac wrt second delta +// d2_pert = plus(d2, t1); // (d2 + t1) +// d3_pert = diff(d1, d2_pert); // (d2 + t1) - d1 +// t2 = d3_pert - d3; // { (d2 + t1) - d1 } - { d2 - d1 } +// J2_n.col(i) = t2/dx; // [ { (d2 + t1) - d1 } - { d2 - d1 } ] / dx +// } + +// // check that numerical and analytical match +// ASSERT_MATRIX_APPROX(J1_a, J1_n, 1e-4); +// ASSERT_MATRIX_APPROX(J2_a, J2_n, 1e-4); +// } + +// TEST(IMU_tools, body2delta_jacobians) +// { +// VectorXs delta(10), delta_pert(10); // deltas +// VectorXs body(6), pert(6); +// VectorXs tang(9); // tangents +// Matrix<Scalar, 9, 6> J_a, J_n; // analytic and numerical jacs +// Vector4s qv;; +// Scalar dt = 0.1; +// Scalar dx = 1e-6; +// qv = (Vector4s() << 3, 4, 5, 6).finished().normalized(); +// delta << 0, 1, 2, qv, 7, 8, 9; +// body << 1, 2, 3, 3, 2, 1; + +// // analytical jacobians +// body2delta(body, dt, delta, J_a); + +// // numerical jacobians +// for (unsigned int i = 0; i<6; i++) +// { +// pert . setZero(); +// pert(i) = dx; + +// // Jac wrt first delta +// delta_pert = body2delta(body + pert, dt); // delta(body + pert) +// tang = diff(delta, delta_pert); // delta(body + pert) -- delta(body) +// J_n.col(i) = tang/dx; // ( delta(body + pert) -- delta(body) ) / dx +// } + +// // check that numerical and analytical match +// ASSERT_MATRIX_APPROX(J_a, J_n, 1e-4); +// } + +// TEST(motion2data, zero) +// { +// Vector6s motion; +// Vector6s bias; +// Quaternions q; + +// motion .setZero(); +// bias .setZero(); +// q .setIdentity(); + +// Vector6s data = imu::motion2data(motion, q, bias); + +// Vector6s data_true; data_true << -gravity(), Vector3s::Zero(); + +// ASSERT_MATRIX_APPROX(data, data_true, 1e-12); +// } + +// TEST(motion2data, motion) +// { +// Vector6s motion, g_extended; +// Vector6s bias; +// Quaternions q; + +// g_extended << gravity() , Vector3s::Zero(); + +// motion << 1,2,3, 4,5,6; +// bias .setZero(); +// q .setIdentity(); + +// Vector6s data = imu::motion2data(motion, q, bias); + +// Vector6s data_true; data_true = motion - g_extended; + +// ASSERT_MATRIX_APPROX(data, data_true, 1e-12); +// } + +// TEST(motion2data, bias) +// { +// Vector6s motion, g_extended; +// Vector6s bias; +// Quaternions q; + +// g_extended << gravity() , Vector3s::Zero(); + +// motion .setZero(); +// bias << 1,2,3, 4,5,6; +// q .setIdentity(); + +// Vector6s data = imu::motion2data(motion, q, bias); + +// Vector6s data_true; data_true = bias - g_extended; + +// ASSERT_MATRIX_APPROX(data, data_true, 1e-12); +// } + +// TEST(motion2data, orientation) +// { +// Vector6s motion, g_extended; +// Vector6s bias; +// Quaternions q; + +// g_extended << gravity() , Vector3s::Zero(); + +// motion .setZero(); +// bias .setZero(); +// q = v2q(Vector3s(M_PI/2, 0, 0)); // turn 90 deg in X axis + +// Vector6s data = imu::motion2data(motion, q, bias); + +// Vector6s data_true; data_true << 0,-gravity()(2),0, 0,0,0; + +// ASSERT_MATRIX_APPROX(data, data_true, 1e-12); +// } + +// TEST(motion2data, AllRandom) +// { +// Vector6s motion, g_extended; +// Vector6s bias; +// Quaternions q; + +// motion .setRandom(); +// bias .setRandom(); +// q.coeffs() .setRandom().normalize(); + +// g_extended << q.conjugate()*gravity() , Vector3s::Zero(); + +// Vector6s data = imu::motion2data(motion, q, bias); + +// Vector6s data_true; data_true = motion + bias - g_extended; + +// ASSERT_MATRIX_APPROX(data, data_true, 1e-12); +// } + +// /* Integrate acc and angVel motion, obtain Delta_preintegrated +// * Input: +// * N: number of steps +// * q0: initial orientaiton +// * motion: [ax, ay, az, wx, wy, wz] as the true magnitudes in body brame +// * bias_real: the real bias of the IMU +// * bias_preint: the bias used for Delta pre-integration +// * Output: +// * return: the preintegrated delta +// */ +// VectorXs integrateDelta(int N, const Quaternions& q0, const VectorXs& motion, const VectorXs& bias_real, const VectorXs& bias_preint, Scalar dt) +// { +// VectorXs data(6); +// VectorXs body(6); +// VectorXs delta(10); +// VectorXs Delta(10), Delta_plus(10); +// Delta << 0,0,0, 0,0,0,1, 0,0,0; +// Quaternions q(q0); +// for (int n = 0; n<N; n++) +// { +// data = motion2data(motion, q, bias_real); +// q = q*exp_q(motion.tail(3)*dt); +// body = data - bias_preint; +// delta = body2delta(body, dt); +// Delta_plus = compose(Delta, delta, dt); +// Delta = Delta_plus; +// } +// return Delta; +// } + +// /* Integrate acc and angVel motion, obtain Delta_preintegrated +// * Input: +// * N: number of steps +// * q0: initial orientaiton +// * motion: [ax, ay, az, wx, wy, wz] as the true magnitudes in body brame +// * bias_real: the real bias of the IMU +// * bias_preint: the bias used for Delta pre-integration +// * Output: +// * J_D_b: the Jacobian of the preintegrated delta wrt the bias +// * return: the preintegrated delta +// */ +// VectorXs integrateDelta(int N, const Quaternions& q0, const VectorXs& motion, const VectorXs& bias_real, const VectorXs& bias_preint, Scalar dt, Matrix<Scalar, 9, 6>& J_D_b) +// { +// VectorXs data(6); +// VectorXs body(6); +// VectorXs delta(10); +// Matrix<Scalar, 9, 6> J_d_d, J_d_b; +// Matrix<Scalar, 9, 9> J_D_D, J_D_d; +// VectorXs Delta(10), Delta_plus(10); +// Quaternions q; + +// Delta << 0,0,0, 0,0,0,1, 0,0,0; +// J_D_b.setZero(); +// q = q0; +// for (int n = 0; n<N; n++) +// { +// // Simulate data +// data = motion2data(motion, q, bias_real); +// q = q*exp_q(motion.tail(3)*dt); +// // Motion::integrateOneStep() +// { // IMU::computeCurrentDelta +// body = data - bias_preint; +// body2delta(body, dt, delta, J_d_d); +// J_d_b = - J_d_d; +// } +// { // IMU::deltaPlusDelta +// compose(Delta, delta, dt, Delta_plus, J_D_D, J_D_d); +// } +// // Motion:: jac calib +// J_D_b = J_D_D*J_D_b + J_D_d*J_d_b; +// // Motion:: buffer +// Delta = Delta_plus; +// } +// return Delta; +// } + +// TEST(IMU_tools, integral_jacobian_bias) +// { +// VectorXs Delta(10), Delta_pert(10); // deltas +// VectorXs bias_real(6), pert(6), bias_pert(6), bias_preint(6); +// VectorXs body(6), data(6), motion(6); +// VectorXs tang(9); // tangents +// Matrix<Scalar, 9, 6> J_a, J_n; // analytic and numerical jacs +// Scalar dt = 0.001; +// Scalar dx = 1e-4; +// Quaternions q0(3, 4, 5, 6); q0.normalize(); +// motion << .1, .2, .3, .3, .2, .1; +// bias_real << .002, .004, .001, .003, .002, .001; +// bias_preint << .004, .005, .006, .001, .002, .003; + +// int N = 500; // # steps of integration + +// // Analytical Jacobians +// Delta = integrateDelta(N, q0, motion, bias_real, bias_preint, dt, J_a); + +// // numerical jacobians +// for (unsigned int i = 0; i<6; i++) +// { +// pert . setZero(); +// pert(i) = dx; + +// bias_pert = bias_preint + pert; +// Delta_pert = integrateDelta(N, q0, motion, bias_real, bias_pert, dt); +// tang = diff(Delta, Delta_pert); // Delta(bias + pert) -- Delta(bias) +// J_n.col(i) = tang/dx; // ( Delta(bias + pert) -- Delta(bias) ) / dx +// } + +// // check that numerical and analytical match +// ASSERT_MATRIX_APPROX(J_a, J_n, 1e-4); +// } + +// TEST(IMU_tools, delta_correction) +// { +// VectorXs Delta(10), Delta_preint(10), Delta_corr; // deltas +// VectorXs bias(6), pert(6), bias_preint(6); +// VectorXs body(6), motion(6); +// VectorXs tang(9); // tangents +// Matrix<Scalar, 9, 6> J_b; // analytic and numerical jacs +// Vector4s qv;; +// Scalar dt = 0.001; +// Quaternions q0(3, 4, 5, 6); q0.normalize(); +// motion << 1, 2, 3, 3, 2, 1; motion *= .1; + +// int N = 1000; // # steps of integration + +// // preintegration with correct bias +// bias << .004, .005, .006, .001, .002, .003; +// Delta = integrateDelta(N, q0, motion, bias, bias, dt); + +// // preintegration with wrong bias +// pert << .002, -.001, .003, -.0003, .0002, -.0001; +// bias_preint = bias + pert; +// Delta_preint = integrateDelta(N, q0, motion, bias, bias_preint, dt, J_b); + +// // correct perturbated +// Vector9s step = J_b*(bias-bias_preint); +// Delta_corr = plus(Delta_preint, step); + +// // Corrected delta should match real delta +// ASSERT_MATRIX_APPROX(Delta, Delta_corr, 1e-5); + +// // diff between real and corrected should be zero +// ASSERT_MATRIX_APPROX(diff(Delta, Delta_corr), Vector9s::Zero(), 1e-5); + +// // diff between preint and corrected deltas should be the jacobian-computed step +// ASSERT_MATRIX_APPROX(diff(Delta_preint, Delta_corr), step, 1e-5); +// } + +// TEST(IMU_tools, full_delta_residual) +// { +// VectorXs x1(10), x2(10); +// VectorXs Delta(10), Delta_preint(10), Delta_corr(10); +// VectorXs Delta_real(9), Delta_err(9), Delta_diff(10), Delta_exp(10); +// VectorXs bias(6), pert(6), bias_preint(6), bias_null(6); +// VectorXs body(6), motion(6); +// VectorXs tang(9); // tangents +// Matrix<Scalar, 9, 6> J_b; // analytic and numerical jacs +// Scalar dt = 0.001; +// Quaternions q0; q0.setIdentity(); + +// x1 << 0,0,0, 0,0,0,1, 0,0,0; +// motion << .3, .2, .1, .1, .2, .3; // acc and gyro +// // motion << .3, .2, .1, .0, .0, .0; // only acc +// // motion << .0, .0, .0, .1, .2, .3; // only gyro +// bias << 0.01, 0.02, 0.003, 0.002, 0.005, 0.01; +// bias_null << 0, 0, 0, 0, 0, 0; +// // bias_preint << 0.003, 0.002, 0.001, 0.001, 0.002, 0.003; +// bias_preint = bias_null; + +// int N = 1000; // # steps of integration + +// // preintegration with no bias +// Delta_real = integrateDelta(N, q0, motion, bias_null, bias_null, dt); + +// // final state +// x2 = composeOverState(x1, Delta_real, 1000*dt); + +// // preintegration with the correct bias +// Delta = integrateDelta(N, q0, motion, bias, bias, dt); + +// ASSERT_MATRIX_APPROX(Delta, Delta_real, 1e-4); + +// // preintegration with wrong bias +// Delta_preint = integrateDelta(N, q0, motion, bias, bias_preint, dt, J_b); + +// // compute correction step +// Vector9s step = J_b*(bias-bias_preint); + +// // correct preintegrated delta +// Delta_corr = plus(Delta_preint, step); + +// // Corrected delta should match real delta +// ASSERT_MATRIX_APPROX(Delta_real, Delta_corr, 1e-3); + +// // diff between real and corrected should be zero +// ASSERT_MATRIX_APPROX(diff(Delta_real, Delta_corr), Vector9s::Zero(), 1e-3); + +// // expected delta +// Delta_exp = betweenStates(x1, x2, N*dt); + +// ASSERT_MATRIX_APPROX(Delta_exp, Delta_corr, 1e-3); + +// // compute diff between expected and corrected +// // Matrix<Scalar, 9, 9> J_err_exp, J_err_corr; +// diff(Delta_exp, Delta_corr, Delta_err); //, J_err_exp, J_err_corr); + +// ASSERT_MATRIX_APPROX(Delta_err, Vector9s::Zero(), 1e-3); + +// // compute error between expected and preintegrated +// Delta_err = diff(Delta_preint, Delta_exp); + +// // diff between preint and corrected deltas should be the jacobian-computed step +// ASSERT_MATRIX_APPROX(diff(Delta_preint, Delta_corr), step, 1e-3); +// /* This implies: +// * - Since D_corr is expected to be similar to D_exp +// * step ~ diff(Delta_preint, Delta_exp) +// * - the residual can be computed with a regular '-' : +// * residual = diff(D_preint, D_exp) - J_bias * (bias - bias_preint) +// */ + +// // WOLF_TRACE("Delta real: ", Delta_real.transpose()); +// // WOLF_TRACE("x2: ", x2.transpose()); +// // WOLF_TRACE("Delta: ", Delta.transpose()); +// // WOLF_TRACE("Delta pre: ", Delta_preint.transpose()); +// // WOLF_TRACE("Jac bias: \n", J_b); +// // WOLF_TRACE("Delta step: ", step.transpose()); +// // WOLF_TRACE("Delta cor: ", Delta_corr.transpose()); +// // WOLF_TRACE("Delta exp: ", Delta_exp.transpose()); +// // WOLF_TRACE("Delta err: ", Delta_err.transpose()); +// // WOLF_TRACE("Delta err exp-pre: ", Delta_err.transpose()); +// } + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); +// ::testing::GTEST_FLAG(filter) = "IMU_tools.delta_correction"; + return RUN_ALL_TESTS(); +} +