diff --git a/src/imu_tools.h b/src/imu_tools.h
index 9a5a83b736ae5b07a84f455c3a923500ad18e41c..a1831bde54104c142533510dbb64afca79f37a01 100644
--- a/src/imu_tools.h
+++ b/src/imu_tools.h
@@ -150,8 +150,8 @@ inline void compose(const MatrixBase<D1>& d1,
     MatrixSizeCheck<10, 1>::check(d1);
     MatrixSizeCheck<10, 1>::check(d2);
     MatrixSizeCheck<10, 1>::check(sum);
-    MatrixSizeCheck< 9, 1>::check(J_sum_d1);
-    MatrixSizeCheck< 9, 1>::check(J_sum_d2);
+    MatrixSizeCheck< 9, 9>::check(J_sum_d1);
+    MatrixSizeCheck< 9, 9>::check(J_sum_d2);
 
     // Maps over provided data
     Map<const Matrix<typename D1::Scalar, 3, 1> >   dp1    ( & d1( 0 ) );
@@ -364,19 +364,79 @@ Matrix<typename Derived::Scalar, 10, 1> retract(const MatrixBase<Derived>& d_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_q = dq1 * exp_q(do2);
+        plus_v = dv1 + dv2;
+}
+
+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_o = log_q(dq1.conjugate() * dq2);
+        diff_v = dv2 - dv1;
+}
+
+
 template<typename D1, typename D2, typename D3>
 inline void diff(const MatrixBase<D1>& d1,
                  const MatrixBase<D2>& d2,
                  MatrixBase<D3>& err)
 {
-    err = lift( between(d1, d2, 0.0) );
+    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>
 inline Matrix<typename D1::Scalar, 9, 1> diff(const MatrixBase<D1>& d1,
                                               const MatrixBase<D2>& d2)
 {
-    return lift( between(d1, d2, 0.0) );
+    Matrix<typename D1::Scalar, 9, 1> ret;
+    diff(d1, d2, ret);
+    return ret;
 }
 
 
diff --git a/src/test/gtest_imu_tools.cpp b/src/test/gtest_imu_tools.cpp
index 37be31e3bde637e1b55ea65e627d1605d263ae24..b44ddfebb8052a49957c2130bc2b9a6beced5cf4 100644
--- a/src/test/gtest_imu_tools.cpp
+++ b/src/test/gtest_imu_tools.cpp
@@ -12,6 +12,7 @@
 
 using namespace Eigen;
 using namespace wolf;
+using namespace imu;
 
 TEST(IMU_tools, identity)
 {
@@ -19,7 +20,7 @@ TEST(IMU_tools, identity)
     id_true.setZero(10);
     id_true(6) = 1.0;
 
-    VectorXs id = imu::identity<Scalar>();
+    VectorXs id = identity<Scalar>();
     ASSERT_MATRIX_APPROX(id, id_true, 1e-10);
 }
 
@@ -28,12 +29,12 @@ TEST(IMU_tools, identity_split)
     VectorXs p(3), qv(4), v(3);
     Quaternions q;
 
-    imu::identity(p,q,v);
+    identity(p,q,v);
     ASSERT_MATRIX_APPROX(p, Vector3s::Zero(), 1e-10);
     ASSERT_QUATERNION_APPROX(q, Quaternions::Identity(), 1e-10);
     ASSERT_MATRIX_APPROX(v, Vector3s::Zero(), 1e-10);
 
-    imu::identity(p,qv,v);
+    identity(p,qv,v);
     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);
@@ -48,16 +49,16 @@ TEST(IMU_tools, inverse)
     qv = (Vector4s() << 3, 4, 5, 6).finished().normalized();
     d << 0, 1, 2, qv, 7, 8, 9;
 
-    id   = imu::inverse(d, dt);
+    id   = inverse(d, dt);
 
-    imu::compose(id, d, dt, I);
-    ASSERT_MATRIX_APPROX(I, imu::identity(), 1e-10);
-    imu::compose(d, id, -dt, I); // Observe -dt is reversed !!
-    ASSERT_MATRIX_APPROX(I, imu::identity(), 1e-10);
+    compose(id, d, dt, I);
+    ASSERT_MATRIX_APPROX(I, identity(), 1e-10);
+    compose(d, id, -dt, I); // Observe -dt is reversed !!
+    ASSERT_MATRIX_APPROX(I, identity(), 1e-10);
 
-    imu::inverse(id, -dt, iid); // Observe -dt is reversed !!
+    inverse(id, -dt, iid); // Observe -dt is reversed !!
     ASSERT_MATRIX_APPROX( d,  iid, 1e-10);
-    iiid = imu::inverse(iid, dt);
+    iiid = inverse(iid, dt);
     ASSERT_MATRIX_APPROX(id, iiid, 1e-10);
 }
 
@@ -76,23 +77,23 @@ TEST(IMU_tools, compose_between)
     d2 = dx2;
 
     // combinations of templates for sum()
-    imu::compose(dx1, dx2, dt, dx3);
-    imu::compose(d1, d2, dt, d3);
+    compose(dx1, dx2, dt, dx3);
+    compose(d1, d2, dt, d3);
     ASSERT_MATRIX_APPROX(d3, dx3, 1e-10);
 
-    imu::compose(d1, dx2, dt, dx3);
-    d3 = imu::compose(dx1, d2, dt);
+    compose(d1, dx2, dt, dx3);
+    d3 = compose(dx1, d2, dt);
     ASSERT_MATRIX_APPROX(d3, dx3, 1e-10);
 
     // minus(d1, d3) should recover delta_2
     VectorXs diffx(10);
     Matrix<Scalar,10,1> diff;
-    imu::between(d1, d3, dt, diff);
+    between(d1, d3, dt, diff);
     ASSERT_MATRIX_APPROX(diff, d2, 1e-10);
 
     // minus(d3, d1, -dt) should recover inverse(d2, dt)
-    diff = imu::between(d3, d1, -dt);
-    ASSERT_MATRIX_APPROX(diff, imu::inverse(d2, dt), 1e-10);
+    diff = between(d3, d1, -dt);
+    ASSERT_MATRIX_APPROX(diff, inverse(d2, dt), 1e-10);
 }
 
 TEST(IMU_tools, compose_between_with_state)
@@ -106,22 +107,22 @@ TEST(IMU_tools, compose_between_with_state)
     qv = (Vector4s() << 6, 5, 4, 3).finished().normalized();
     d << 9, 8, 7, qv, 2, 1, 0;
 
-    imu::composeOverState(x, d, dt, x2);
-    x3 = imu::composeOverState(x, d, dt);
+    composeOverState(x, d, dt, x2);
+    x3 = composeOverState(x, d, dt);
     ASSERT_MATRIX_APPROX(x3, x2, 1e-10);
 
     // betweenStates(x, x2) should recover d
-    imu::betweenStates(x, x2, dt, d2);
-    d3 = imu::betweenStates(x, x2, dt);
+    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(imu::betweenStates(x, x2, dt), d, 1e-10);
+    ASSERT_MATRIX_APPROX(betweenStates(x, x2, dt), d, 1e-10);
 
     // x + (x2 - x) = x2
-    ASSERT_MATRIX_APPROX(imu::composeOverState(x, imu::betweenStates(x, x2, dt), dt), x2, 1e-10);
+    ASSERT_MATRIX_APPROX(composeOverState(x, betweenStates(x, x2, dt), dt), x2, 1e-10);
 
     // (x + d) - x = d
-    ASSERT_MATRIX_APPROX(imu::betweenStates(x, imu::composeOverState(x, d, dt), dt), d, 1e-10);
+    ASSERT_MATRIX_APPROX(betweenStates(x, composeOverState(x, d, dt), dt), d, 1e-10);
 }
 
 TEST(IMU_tools, lift_retract)
@@ -129,7 +130,7 @@ TEST(IMU_tools, lift_retract)
     VectorXs d_min(9); d_min << 0, 1, 2, .3, .4, .5, 6, 7, 8; // use angles in the ball theta < pi
 
     // transform to delta
-    VectorXs delta = imu::retract(d_min);
+    VectorXs delta = retract(d_min);
 
     // expected delta
     Vector3s dp = d_min.head(3);
@@ -139,11 +140,11 @@ TEST(IMU_tools, lift_retract)
     ASSERT_MATRIX_APPROX(delta, delta_true, 1e-10);
 
     // transform to a new tangent -- should be the original tangent
-    VectorXs d_from_delta = imu::lift(delta);
+    VectorXs d_from_delta = lift(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 = imu::retract(d_from_delta);
+    VectorXs delta_from_d = retract(d_from_delta);
     ASSERT_MATRIX_APPROX(delta_from_d, delta, 1e-10);
 }
 
@@ -155,9 +156,49 @@ TEST(IMU_tools, diff)
     d2 = d1;
 
     VectorXs err(9);
-    imu::diff(d1, d2, err);
+    diff(d1, d2, err);
     ASSERT_MATRIX_APPROX(err, VectorXs::Zero(9), 1e-10);
-    ASSERT_MATRIX_APPROX(imu::diff(d1, d2), VectorXs::Zero(9), 1e-10);
+    ASSERT_MATRIX_APPROX(diff(d1, d2), VectorXs::Zero(9), 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);
 }
 
 int main(int argc, char **argv)