From dd4a37ae82350ed2bc8dc4ac29956dbdabc3f74e Mon Sep 17 00:00:00 2001
From: Mansard <nmansard@laas.fr>
Date: Tue, 26 Aug 2014 12:01:09 +0200
Subject: [PATCH] First working vesrion of CRBA (checked VS RBDL for revolute
 joints).

---
 CMakeLists.txt                           |   7 ++
 doc/latex/se3.tex                        |  11 +-
 src/algorithm/crba.hpp                   | 146 +++++++++++++++++++++++
 src/algorithm/rnea.hpp                   |   6 +-
 src/exception.hpp                        |  28 +++++
 src/multibody/constraint.hpp             |   8 +-
 src/multibody/joint/joint-base.hpp       |  36 +++++-
 src/multibody/joint/joint-free-flyer.hpp |  17 ++-
 src/multibody/joint/joint-generic.hpp    | 132 ++++++++++++++++++++
 src/multibody/joint/joint-revolute.hpp   |  21 +++-
 src/multibody/model.hpp                  |  28 ++++-
 src/multibody/parser/urdf.hpp            |  10 +-
 src/spatial/inertia.hpp                  |  23 +++-
 unittest/crba.cpp                        |  94 +++++++++++++++
 unittest/tspatial.cpp                    |  11 ++
 unittest/variant.cpp                     |   8 +-
 16 files changed, 558 insertions(+), 28 deletions(-)
 create mode 100644 src/algorithm/crba.hpp
 create mode 100644 src/exception.hpp
 create mode 100644 src/multibody/joint/joint-generic.hpp
 create mode 100644 unittest/crba.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c8fca6b1..3146c6f50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -31,6 +31,7 @@ ADD_REQUIRED_DEPENDENCY("urdfdom >= v0.3.0")
 # --- INCLUDE ----------------------------------------
 # ----------------------------------------------------
 SET(${PROJECT_NAME}_HEADERS
+  exception.hpp
   spatial/se3.hpp
   spatial/motion.hpp
   spatial/force.hpp
@@ -43,11 +44,13 @@ SET(${PROJECT_NAME}_HEADERS
   multibody/joint/joint-revolute.hpp
   multibody/joint/joint-free-flyer.hpp
   multibody/joint/joint-variant.hpp
+  multibody/joint/joint-generic.hpp
   multibody/model.hpp
   multibody/visitor.hpp
   multibody/parser/urdf.hpp
   tools/timer.hpp
   algorithm/rnea.hpp
+  algorithm/crba.hpp
   algorithm/kinematics.hpp
 )
 
@@ -84,6 +87,10 @@ PKG_CONFIG_USE_DEPENDENCY(tspatial eigenpy)
 ADD_EXECUTABLE(rnea EXCLUDE_FROM_ALL unittest/rnea.cpp)
 PKG_CONFIG_USE_DEPENDENCY(rnea eigenpy)
 
+ADD_EXECUTABLE(crba EXCLUDE_FROM_ALL unittest/crba.cpp)
+PKG_CONFIG_USE_DEPENDENCY(crba eigenpy)
+PKG_CONFIG_USE_DEPENDENCY(crba urdfdom)
+
 ADD_EXECUTABLE(dg EXCLUDE_FROM_ALL unittest/dg.cpp)
 PKG_CONFIG_USE_DEPENDENCY(dg eigenpy)
 
diff --git a/doc/latex/se3.tex b/doc/latex/se3.tex
index 470a9aa2d..382e9850e 100644
--- a/doc/latex/se3.tex
+++ b/doc/latex/se3.tex
@@ -103,7 +103,8 @@ For Featherstone, $E = \bRa =\aRb^T$ and $r = \apb$. Then:
 $$\bXa^* = \BIN \bRa & 0 \\ -\bRa \apb_\times & \bRa \BOUT = \BIN E & 0 \\ - E r_\times & E \BOUT $$
 $$\aXb^* = \BIN \bRa^T & 0 \\  \apb_\times \bRa^T & \bRa^T \BOUT = \BIN E^T & 0 \\ r_\times E^T & E^T \BOUT $$
 
-\section{Inertia application}
+\section{Inertia}
+\subsection{Inertia application}
 
 $$\aY: \avs \rightarrow \afs = \aY \avs$$
 
@@ -131,6 +132,14 @@ Nota: the square of the cross product is:
 $$\BIN x\\y\\z\BOUT_ \times^2 = \BIN 0&-z&y \\ z&0&-x \\ -y&x&0 \BOUT^2 = \BIN -y^2-z^2&xy&xz \\ xy&-x^2-z^2&yz \\ xz&yz&-x^2-y^2 \BOUT$$
 There is no computational interest in using it.
 
+\subsection{Inertia addition}
+
+$$ Y_p = \BIN m_p &  m_p  p_\times^T \\ m_p p_\times &  I_p + m_p  p_\times p_\times^T \BOUT$$
+$$ Y_q = \BIN m_q &  m_q  q_\times^T \\ m_q q_\times &  I_q + m_q  q_\times q_\times^T \BOUT$$
+
+
+
+
 \section{Cross products}
 
 Motion-motion product:
diff --git a/src/algorithm/crba.hpp b/src/algorithm/crba.hpp
new file mode 100644
index 000000000..cc49ad2cd
--- /dev/null
+++ b/src/algorithm/crba.hpp
@@ -0,0 +1,146 @@
+#ifndef __se3_crba_hpp__
+#define __se3_crba_hpp__
+
+#include "pinocchio/multibody/visitor.hpp"
+#include "pinocchio/multibody/model.hpp"
+#include "pinocchio/multibody/joint/joint-generic.hpp"
+  
+namespace se3
+{
+  inline const Eigen::VectorXd&
+  crba(const Model & model, Data& data,
+       const Eigen::VectorXd & q,
+       const Eigen::VectorXd & v,
+       const Eigen::VectorXd & a);
+
+} // namespace se3 
+
+/* --- Details -------------------------------------------------------------------- */
+namespace se3 
+{
+  struct CrbaForwardStep : public fusion::JointVisitor<CrbaForwardStep>
+  {
+    typedef boost::fusion::vector< const se3::Model&,
+			    se3::Data&,
+			    const int&,
+			    const Eigen::VectorXd &
+			    > ArgsType;
+
+    JOINT_VISITOR_INIT(CrbaForwardStep);
+
+    template<typename JointModel>
+    static void algo(const se3::JointModelBase<JointModel> & jmodel,
+		     se3::JointDataBase<typename JointModel::JointData> & jdata,
+		     const se3::Model& model,
+		     se3::Data& data,
+		     const int &i,
+		     const Eigen::VectorXd & q)
+    {
+      using namespace Eigen;
+      using namespace se3;
+      
+      jmodel.calc(jdata.derived(),q);
+      
+      const Model::Index & parent = model.parents[i];
+      data.liMi[i] = model.jointPlacements[i]*jdata.M();
+      
+      if(parent>0) data.oMi[i] = data.oMi[parent]*data.liMi[i];
+      else         data.oMi[i] = data.liMi[i];
+      
+      data.Ycrb[i] = model.inertias[i];
+    }
+
+  };
+
+  struct CrbaBackwardStep : public fusion::JointVisitor<CrbaBackwardStep>
+  {
+    typedef boost::fusion::vector<const Model&,
+				  Data&,
+				  const int &>  ArgsType;
+    
+    JOINT_VISITOR_INIT(CrbaBackwardStep);
+
+    template<typename JointModel>
+    static void algo(const JointModelBase<JointModel> & jmodel,
+		     JointDataBase<typename JointModel::JointData> & jdata,
+		     const Model& model,
+		     Data& data,
+		     int i)
+    {
+      /*
+       * F = Yi*Si
+       * for j<i 
+       *    F = ljXj.act(F)
+       *    M.block(j,i,nvj,nvi) = Sj'*F
+       */
+      std::cout << "*** joint " << i << std::endl;
+
+      Model::Index parent = model.parents[i];
+      if( parent>0 )
+	data.Ycrb[parent] += data.liMi[i].act(data.Ycrb[i]);
+
+      //std::cout << "liMi = " << (SE3::Matrix4)data.liMi[i] << std::endl;
+
+      Eigen::Matrix<double,6,JointModel::nv> F;
+      ConstraintXd S = jdata.S();
+      F = data.Ycrb[i].toMatrix() * S.matrix() ;
+
+      std::cout << "*** joint " << i << std::endl;
+      std::cout << "iYi = " << (Inertia::Matrix6)data.Ycrb[i] << std::endl;
+      std::cout << "iSi = " << S.matrix() << std::endl;
+      std::cout << "iFi = " << F << std::endl;
+
+      data.M.block<JointModel::nv,JointModel::nv>(jmodel.idx_v(),jmodel.idx_v())
+	= S.matrix().transpose() * F;
+
+      SE3::Matrix6 ljXj = data.liMi[i];
+      while(parent>0)
+	{
+	  JointDataGeneric jdataparent( data.joints[parent] );
+	  JointModelGeneric jmodelparent( model.joints[parent] );
+
+	  F = ljXj.inverse().transpose()*F; // equivalent to ljF = ljMj.act(jF)
+	  const ConstraintXd::DenseBase & S = jdataparent.S.matrix();
+	  std::cout << "jFi = " << F <<std::endl;
+	  std::cout << "jS = " << S <<std::endl;
+
+	  data.M.block(jmodelparent.idx_v(),jmodel.idx_v(),S.cols(),JointModel::nv)
+	    = S.transpose() * F;
+
+	  std::cout << "\t\t on parent #i,j = " << i<<","<<parent << "   ... compute block "
+		    << jmodelparent.idx_v() << ":" 
+		    << jmodelparent.idx_v() + S.cols() << " x "
+		    << jmodel.idx_v() << ":"
+		    << jmodel.idx_v() + JointModel::nv << std::endl;
+	  std::cout << "jFi = " << F << std::endl;
+	  std::cout << "jSj = " << S << std::endl;
+
+	  ljXj = data.liMi[parent];
+	  parent = model.parents[parent];
+	}
+
+    }
+  };
+
+  inline const Eigen::MatrixXd&
+  crba(const Model & model, Data& data,
+       const Eigen::VectorXd & q)
+  {
+    for( int i=1;i<model.nbody;++i )
+      {
+	CrbaForwardStep::run(model.joints[i],data.joints[i],
+			     CrbaForwardStep::ArgsType(model,data,i,q));
+      }
+    
+    for( int i=model.nbody-1;i>0;--i )
+      {
+	CrbaBackwardStep::run(model.joints[i],data.joints[i],
+			      CrbaBackwardStep::ArgsType(model,data,i));
+      }
+
+    return data.M;
+  }
+} // namespace se3
+
+#endif // ifndef __se3_crba_hpp__
+
diff --git a/src/algorithm/rnea.hpp b/src/algorithm/rnea.hpp
index bee46880b..5e10b6670 100644
--- a/src/algorithm/rnea.hpp
+++ b/src/algorithm/rnea.hpp
@@ -1,5 +1,5 @@
-#ifndef __se3_rne_hpp__
-#define __se3_rne_hpp__
+#ifndef __se3_rnea_hpp__
+#define __se3_rnea_hpp__
 
 #include "pinocchio/multibody/visitor.hpp"
 #include "pinocchio/multibody/model.hpp"
@@ -109,5 +109,5 @@ namespace se3
   }
 } // namespace se3
 
-#endif // ifndef __se3_rne_hpp__
+#endif // ifndef __se3_rnea_hpp__
 
diff --git a/src/exception.hpp b/src/exception.hpp
new file mode 100644
index 000000000..b7f28d299
--- /dev/null
+++ b/src/exception.hpp
@@ -0,0 +1,28 @@
+#ifndef __se3_exception_hpp__
+#define __se3_exception_hpp__
+
+#include <exception>
+#include <string>
+
+namespace se3
+{
+  class Exception : public std::exception
+  {
+  public:
+    Exception() : message() {}
+    Exception(std::string msg) : message(msg) {}
+    const char *what() const throw()
+    {
+      return this->getMessage().c_str();
+    }
+    ~Exception() throw() {}
+    virtual const std::string & getMessage() const { return message; }
+    std::string copyMessage() const { return getMessage(); }
+
+  protected:
+    std::string message;
+   };
+
+} // namespace 
+
+#endif // ifndef __se3_exception_hpp__
diff --git a/src/multibody/constraint.hpp b/src/multibody/constraint.hpp
index f82bd9ede..18eddb33c 100644
--- a/src/multibody/constraint.hpp
+++ b/src/multibody/constraint.hpp
@@ -11,7 +11,7 @@
 
 namespace se3
 {
-  template<int _Dim, typename _Scalar, int _Options>
+  template<int _Dim, typename _Scalar, int _Options=0>
   class ConstraintTpl
   { 
   public:
@@ -51,8 +51,10 @@ namespace se3
     DenseBase S;
   };
 
-};
-
+  typedef ConstraintTpl<1,double,0> Constraint1d;
+  typedef ConstraintTpl<6,double,0> Constraint6d;
+  typedef ConstraintTpl<Eigen::Dynamic,double,0> ConstraintXd;
 
+} // namespace se3
 
 #endif // ifndef __se3_constraint_hpp__
diff --git a/src/multibody/joint/joint-base.hpp b/src/multibody/joint/joint-base.hpp
index 94dcc3727..3b8c4e177 100644
--- a/src/multibody/joint/joint-base.hpp
+++ b/src/multibody/joint/joint-base.hpp
@@ -12,9 +12,10 @@ namespace se3
 {
   template<class C> struct traits {};
 
-  /*
+  /* RNEA operations
+   *
    * *** FORWARD ***
-   * J::calc()
+   * J::calc(q,vq)
    * SE3    = SE3 * J::SE3
    * Motion = J::Motion
    * Motion = J::Constraint*J::JointMotion + J::Bias + Motion^J::Motion
@@ -24,6 +25,21 @@ namespace se3
    * J::JointForce = J::Constraint::Transpose*J::Force
    */
 
+  /* CRBA operations
+   *
+   * *** FORWARD ***
+   * J::calc(q)
+   * Inertia = Inertia
+   *
+   * *** BACKWARD *** 
+   * Inertia += SE3::act(Inertia)
+   * F = Inertia*J::Constraint
+   * JointInertia.block = J::Constraint::Transpose*F
+   * *** *** INNER ***
+   *     F = SE3::act(f)
+   *     JointInertia::block = J::Constraint::Transpose*F
+   */
+
 #define SE3_JOINT_TYPEDEF \
   typedef typename traits<Joint>::JointData JointData; \
   typedef typename traits<Joint>::JointModel JointModel; \
@@ -36,6 +52,11 @@ namespace se3
     nv = traits<Joint>::nv \
   }
 
+#define SE3_JOINT_USE_INDEXES \
+    typedef JointModelBase<JointModel> Base; \
+    using Base::idx_q; \
+    using Base::idx_v
+
   template<typename _JointData>
   struct JointDataBase
   {
@@ -45,10 +66,10 @@ namespace se3
     JointData& derived() { return *static_cast<JointData*>(this); }
     const JointData& derived() const { return *static_cast<const JointData*>(this); }
 
-    const Constraint_t     & S()   { return static_cast<JointData*>(this)->S;   }
-    const Transformation_t & M()   { return static_cast<JointData*>(this)->M;   }
-    const Motion_t       & v()   { return static_cast<JointData*>(this)->v;   }
-    const Bias_t           & c()   { return static_cast<JointData*>(this)->c;   }
+    const Constraint_t     & S() const  { return static_cast<const JointData*>(this)->S;   }
+    const Transformation_t & M() const  { return static_cast<const JointData*>(this)->M;   }
+    const Motion_t         & v() const  { return static_cast<const JointData*>(this)->v;   }
+    const Bias_t           & c() const  { return static_cast<const JointData*>(this)->c;   }
   };
 
   template<typename _JointModel>
@@ -61,6 +82,9 @@ namespace se3
     const JointModel& derived() const { return *static_cast<const JointModel*>(this); }
 
     JointData createData() const { return static_cast<const JointModel*>(this)->createData(); }
+    void calc( JointData& data, 
+	       const Eigen::VectorXd & qs ) const
+    { return static_cast<const JointModel*>(this)->calc(data,qs); }
     void calc( JointData& data, 
 	       const Eigen::VectorXd & qs, 
 	       const Eigen::VectorXd & vs ) const
diff --git a/src/multibody/joint/joint-free-flyer.hpp b/src/multibody/joint/joint-free-flyer.hpp
index 21e4beef3..bae28b67f 100644
--- a/src/multibody/joint/joint-free-flyer.hpp
+++ b/src/multibody/joint/joint-free-flyer.hpp
@@ -2,6 +2,7 @@
 #define __se3_joint_free_flyer_hpp__
 
 #include "pinocchio/multibody/joint/joint-base.hpp"
+#include "pinocchio/multibody/constraint.hpp"
 
 namespace se3
 {
@@ -11,13 +12,17 @@ namespace se3
 
   struct JointFreeFlyer 
   {
-    struct BiasZero {};
+    struct BiasZero 
+    {
+      operator Motion () const { return Motion::Zero(); }
+    };
     friend const Motion & operator+ ( const Motion& v, const BiasZero&) { return v; }
     friend const Motion & operator+ ( const BiasZero&,const Motion& v) { return v; }
 
     struct ConstraintIdentity
     {
       const ConstraintIdentity& transpose() const { return *this; }
+      operator ConstraintXd () const { return ConstraintXd(Eigen::MatrixXd::Identity(6,6)); }
     };
     template<typename D>
     friend Motion operator* (const ConstraintIdentity&, const Eigen::MatrixBase<D>& v)
@@ -30,9 +35,6 @@ namespace se3
     {  return phi.toVector();  }
   };
 
-  struct JointModelFreeFlyer;
-  struct JointDataFreeFlyer;
-
   template<>
   struct traits<JointFreeFlyer>
   {
@@ -75,6 +77,13 @@ namespace se3
     SE3_JOINT_TYPEDEF;
 
     JointData createData() const { return JointData(); }
+    void calc( JointData& data, 
+	       const Eigen::VectorXd & qs) const
+    {
+      Eigen::VectorXd::ConstFixedSegmentReturnType<nq>::Type q = qs.segment<nq>(idx_q());
+      JointData::Quaternion quat(Eigen::Matrix<double,4,1>(q.tail(4))); // TODO
+      data.M = SE3(quat.matrix(),q.head<3>());
+    }
     void calc( JointData& data, 
 	       const Eigen::VectorXd & qs, 
 	       const Eigen::VectorXd & vs ) const
diff --git a/src/multibody/joint/joint-generic.hpp b/src/multibody/joint/joint-generic.hpp
new file mode 100644
index 000000000..533b87c56
--- /dev/null
+++ b/src/multibody/joint/joint-generic.hpp
@@ -0,0 +1,132 @@
+#ifndef __se3_joint_generic_hpp__
+#define __se3_joint_generic_hpp__
+
+#include "pinocchio/exception.hpp"
+#include "pinocchio/multibody/joint/joint-base.hpp"
+#include "pinocchio/multibody/joint/joint-variant.hpp"
+
+namespace se3
+{
+
+  struct JointGeneric;
+  struct JointModelGeneric;
+  struct JointDataGeneric;
+
+  template<>
+  struct traits<JointGeneric>
+  {
+    typedef JointDataGeneric JointData;
+    typedef JointModelGeneric JointModel;
+    typedef ConstraintXd Constraint_t;
+    typedef SE3 Transformation_t;
+    typedef Motion Motion_t;
+    typedef Motion Bias_t;
+    enum {
+      nq = -1,
+      nv = -1
+    };
+  };
+  template<> struct traits<JointDataGeneric> { typedef JointGeneric Joint; };
+  template<> struct traits<JointModelGeneric> { typedef JointGeneric Joint; };
+
+  struct JointDataGeneric : public JointDataBase<JointDataGeneric> 
+  {
+    typedef JointGeneric Joint;
+    SE3_JOINT_TYPEDEF;
+
+    Constraint_t S;
+    Transformation_t M;
+    Motion_t v;
+    Bias_t c;
+
+    JointDataGeneric()  {}
+    JointDataGeneric( const JointDataVariant & jvar );
+  };
+
+  struct JointModelGeneric : public JointModelBase<JointModelGeneric> 
+  {
+    typedef JointGeneric Joint;
+    SE3_JOINT_TYPEDEF;
+    SE3_JOINT_USE_INDEXES;
+
+    JointData createData() const { return JointData(); }
+
+    virtual void jcalc(JointData& /*data*/, 
+		       const Eigen::VectorXd & /*qs*/) const 
+    {
+      throw se3::Exception("Virtual function calc not implemented in the generic class. Derive it.");
+    }
+    virtual void jcalc(JointData& /*data*/, 
+		       const Eigen::VectorXd & /*qs*/, 
+		       const Eigen::VectorXd & /*vs*/ ) const 
+    {
+      throw se3::Exception("Virtual function calc not implemented in the generic class. Derive it.");
+    }
+
+    JointModelGeneric() {}
+    JointModelGeneric( const JointModelVariant & jvar );
+    JointModelGeneric & operator= (const JointModelGeneric& clone)
+    { 
+      setIndexes(clone.idx_q(),clone.idx_v()); return *this;
+    }
+  };
+
+  /* --- Convert Data ------------------------------------------------------ */
+  namespace internal
+  {
+    struct JointDataVariantToGeneric : public boost::static_visitor< JointDataGeneric >
+    {
+      template <typename D>
+      JointDataGeneric operator() ( const JointDataBase<D> & jdata ) const
+      {
+	JointDataGeneric jgen;
+	jgen.S = jdata.S();
+	jgen.M = jdata.M();
+	jgen.v = jdata.v();
+	jgen.c = jdata.c();
+	return jgen;
+      }
+
+      static JointDataGeneric run ( const JointDataVariant & jdata )
+      {
+	return boost::apply_visitor( JointDataVariantToGeneric(),jdata );
+      }
+    };
+  }// namespace internal
+
+  JointDataGeneric::JointDataGeneric( const JointDataVariant & jvar )
+  {
+    const JointDataGeneric & clone = internal::JointDataVariantToGeneric::run(jvar);
+    (*this) = clone;
+  }
+  
+  /* --- Convert Model ----------------------------------------------------- */
+  namespace internal
+  {
+    struct JointModelVariantToGeneric : public boost::static_visitor< JointModelGeneric >
+    {
+      template <typename D>
+      JointModelGeneric operator() ( const JointModelBase<D> & jmodel ) const
+      {
+	JointModelGeneric jgen;
+	jgen.setIndexes(jmodel.idx_q(),jmodel.idx_v());
+	return jgen;
+      }
+
+      static JointModelGeneric run ( const JointModelVariant & jmodel )
+      {
+	return boost::apply_visitor( JointModelVariantToGeneric(),jmodel );
+      }
+    };
+  }// namespace internal
+
+  JointModelGeneric::JointModelGeneric( const JointModelVariant & jvar )
+  {
+    const JointModelGeneric & clone = internal::JointModelVariantToGeneric::run(jvar);
+    (*this) = clone;
+  }
+
+} // namespace se3
+
+
+#endif // ifndef __se3_joint_generic_hpp__
diff --git a/src/multibody/joint/joint-revolute.hpp b/src/multibody/joint/joint-revolute.hpp
index 71db05102..a9f9d25eb 100644
--- a/src/multibody/joint/joint-revolute.hpp
+++ b/src/multibody/joint/joint-revolute.hpp
@@ -2,6 +2,7 @@
 #define __se3_joint_revolute_hpp__
 
 #include "pinocchio/multibody/joint/joint-base.hpp"
+#include "pinocchio/multibody/constraint.hpp"
 
 namespace se3
 {
@@ -16,6 +17,7 @@ namespace se3
     {
       double w; 
       CartesianVector3(const double & w) : w(w) {}
+      CartesianVector3() : w(1) {}
       operator Eigen::Vector3d (); // { return Eigen::Vector3d(w,0,0); }
     };
     template<>CartesianVector3<0>::operator Eigen::Vector3d () { return Eigen::Vector3d(w,0,0); }
@@ -31,7 +33,10 @@ namespace se3
 
   template<int axis> 
   struct JointRevolute {
-    struct BiasZero {};
+    struct BiasZero 
+    {
+      operator Motion () const { return Motion::Zero(); }
+    };
     friend const Motion & operator+ ( const Motion& v, const BiasZero&) { return v; }
     friend const Motion & operator+ ( const BiasZero&,const Motion& v) { return v; }
 
@@ -62,6 +67,13 @@ namespace se3
      Force::Vector3::ConstFixedSegmentReturnType<1>::Type
      operator*( const Force& f ) const
      { return f.angular().segment<1>(axis); }
+
+      operator ConstraintXd () const
+      {
+	Eigen::Matrix<double,6,1> S;
+	S << Eigen::Vector3d::Zero(), (Eigen::Vector3d)revolute::CartesianVector3<axis>();
+	return ConstraintXd(S);
+      }
     }; // struct ConstraintRevolute
 
     static Eigen::Matrix3d cartesianRotation(const double & angle); 
@@ -191,6 +203,13 @@ namespace se3
     using JointModelBase<JointModelRevolute>::setIndexes;
     
     JointData createData() const { return JointData(); }
+    void calc( JointData& data, 
+	       const Eigen::VectorXd & qs ) const
+    {
+      const double & q = qs[idx_q()];
+      data.M.rotation(JointRevolute<axis>::cartesianRotation(q));
+    }
+
     void calc( JointData& data, 
 	       const Eigen::VectorXd & qs, 
 	       const Eigen::VectorXd & vs ) const
diff --git a/src/multibody/model.hpp b/src/multibody/model.hpp
index cf53f9363..eda2c96f1 100644
--- a/src/multibody/model.hpp
+++ b/src/multibody/model.hpp
@@ -71,9 +71,15 @@ namespace se3
     std::vector<SE3> liMi;                // Body relative placement (wrt parent)
     Eigen::VectorXd tau;                  // Joint forces
 
-    Data( const Model& ref );
+    std::vector<Inertia> Ycrb;            // Inertia of the sub-tree composit rigid body
+    Eigen::MatrixXd M;                    // Joint Inertia
+    Eigen::MatrixXd Fcrb;                 // Spatial forces set, used in CRBA
+
+    std::vector<Model::Index> lastChild;  // Index of the last child (for CRBA)
 
+    Data( const Model& ref );
 
+    void computeLastChild(const Model& model);
   };
 
   const Eigen::Vector3d Model::gravity981 (0,0,-9.81);
@@ -144,11 +150,31 @@ namespace se3
     ,oMi(ref.nbody)
     ,liMi(ref.nbody)
     ,tau(ref.nv)
+    ,Ycrb(ref.nbody)
+    ,M(ref.nv,ref.nv)
+    ,Fcrb(6,ref.nv)
+    ,lastChild(ref.nbody)
   {
     for(int i=0;i<model.nbody;++i) 
       joints.push_back(CreateJointData::run(model.joints[i]));
+    M.fill(NAN);
+    computeLastChild(ref);
   }
 
+  void Data::computeLastChild(const Model& model)
+  {
+    typedef Model::Index Index;
+    //lastChild.fill(-1);  TODO use fill algorithm
+    for( int i=0;i<model.nbody;++i ) lastChild[i] = -1;
+
+
+    for( int i=model.nbody-1;i>=0;--i )
+      {
+	if(lastChild[i] == -1) lastChild[i] = i;
+	const Index & parent = model.parents[i];
+	lastChild[parent] = std::max(lastChild[i],lastChild[parent]);
+      }
+  }
 
 } // namespace se3
 
diff --git a/src/multibody/parser/urdf.hpp b/src/multibody/parser/urdf.hpp
index 17ab1fc95..b43daae96 100644
--- a/src/multibody/parser/urdf.hpp
+++ b/src/multibody/parser/urdf.hpp
@@ -66,8 +66,11 @@ namespace se3
     //   std::cout << "#" << link->parent_joint->name << std::endl;
     // else std::cout << "###ROOT" << std::endl;
  
-    assert(link->inertial && "The parser cannot accept trivial mass");
-    const Inertia & Y = convertFromUrdf(*link->inertial);
+    // assert(link->inertial && "The parser cannot accept trivial mass");
+    const Inertia & Y = (link->inertial) ?
+      convertFromUrdf(*link->inertial)
+      : Inertia::Identity();
+
     //std::cout << "Inertia: " << Y << std::endl;
 
     if(joint!=NULL)
@@ -118,7 +121,8 @@ namespace se3
     else /* (joint==NULL) */
       { /* The link is the root of the body. */
 	//std::cout << "Parent = 0 (universe)" << std::endl;
-	model.addBody( 0, JointModelFreeFlyer(), SE3::Identity(), Y, "root" );
+	//model.addBody( 0, JointModelFreeFlyer(), SE3::Identity(), Y, "root" ); // TODO replace RX by FF
+	model.addBody( 0, JointModelRX(), SE3::Identity(), Y, "root" ); // TODO replace RX by FF
       }
 
     BOOST_FOREACH(urdf::LinkConstPtr child,link->child_links)
diff --git a/src/spatial/inertia.hpp b/src/spatial/inertia.hpp
index 87291b894..263a67c62 100644
--- a/src/spatial/inertia.hpp
+++ b/src/spatial/inertia.hpp
@@ -67,9 +67,16 @@ namespace se3
     }
     static Inertia Random()
     {
+      /* We have to shoot "I" definite positive and not only symmetric. */
+      Matrix3 M3 = Matrix3::Random(); // TODO clean that mess
+      Matrix3 D = Vector3::Random().cwiseAbs().asDiagonal();
+      Matrix3 M3D2 = M3+2*D;
+      Matrix3 posdiag 
+	= M3D2.template selfadjointView<Eigen::Upper>();
       return InertiaTpl(Eigen::internal::random<Scalar>(),
 			Vector3::Random(),
-			Matrix3::Random().template selfadjointView<Eigen::Upper>());
+			//Matrix3::Random().template selfadjointView<Eigen::Upper>());
+			posdiag);
     }
 
     // Getters
@@ -91,7 +98,19 @@ namespace se3
     // Arithmetic operators
     Inertia operator+(const InertiaTpl &other) const
     {
-      return InertiaTpl(m+other.m, c+other.c, I+other.I);
+      const double & mm = m+other.m;
+      const Matrix3 & X = skew((c-other.c).eval());
+      Matrix3 mmmXX = m*other.m/mm*X*X; // TODO clean this mess
+      return InertiaTpl(mm, (m*c+other.m*other.c)/mm, dense_I+other.dense_I-mmmXX); // TODO: += in Eigen::Symmetric?
+    }
+    Inertia& operator+=(const InertiaTpl &other)
+    {
+      const Matrix3 & X = skew((c-other.c).eval());
+      Matrix3 mmXX = m*other.m*X*X; // TODO: clean this mess
+      c *=m; c+=other.m*other.c;
+      m+=other.m; c/=m;  
+      dense_I+=other.dense_I-mmXX/m; // TODO: += in Eigen::Symmetric?
+      return *this;
     }
 
     Force operator*(const Motion &v) const 
diff --git a/unittest/crba.cpp b/unittest/crba.cpp
new file mode 100644
index 000000000..410e269ae
--- /dev/null
+++ b/unittest/crba.cpp
@@ -0,0 +1,94 @@
+#include "pinocchio/spatial/fwd.hpp"
+#include "pinocchio/spatial/se3.hpp"
+#include "pinocchio/multibody/joint.hpp"
+#include "pinocchio/multibody/visitor.hpp"
+#include "pinocchio/multibody/model.hpp"
+#include "pinocchio/algorithm/crba.hpp"
+#include "pinocchio/algorithm/rnea.hpp"
+#include "pinocchio/multibody/parser/urdf.hpp"
+
+#include <iostream>
+
+#include "pinocchio/tools/timer.hpp"
+
+
+//#define __SSE3__
+#include <fenv.h>
+#ifdef __SSE3__
+#include <pmmintrin.h>
+#endif
+
+int main(int argc, const char ** argv)
+{
+#ifdef __SSE3__
+  _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+#endif
+
+
+  using namespace Eigen;
+  using namespace se3;
+
+  SE3::Matrix3 I3 = SE3::Matrix3::Identity();
+
+  se3::Model model;
+
+  // std::string filename = "/home/nmansard/src/metapod/data/simple_arm.urdf";
+  // if(argc>1) filename = argv[1];
+  // model = se3::buildModel(filename);
+
+
+  // SIMPLE no FF
+  // model.addBody(model.getBodyId("universe"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"rleg1");
+  // model.addBody(model.getBodyId("rleg1"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"rleg2");
+  // model.addBody(model.getBodyId("rleg2"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"rleg3");
+
+  // model.addBody(model.getBodyId("universe"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"lleg1");
+  // model.addBody(model.getBodyId("lleg1"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"lleg2");
+  // model.addBody(model.getBodyId("lleg2"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"lleg3");
+
+
+  //SIMPLE with FF
+  model.addBody(model.getBodyId("universe"),JointModelFreeFlyer(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"root");
+
+  model.addBody(model.getBodyId("root"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"rleg1");
+  model.addBody(model.getBodyId("rleg1"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"rleg2");
+  model.addBody(model.getBodyId("rleg2"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"rleg3");
+
+  model.addBody(model.getBodyId("root"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"lleg1");
+  model.addBody(model.getBodyId("lleg1"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"lleg2");
+  model.addBody(model.getBodyId("lleg2"),JointModelRX(),SE3(I3,SE3::Vector3::Random()),Inertia::Random(),"lleg3");
+
+
+  se3::Data data(model);
+
+  VectorXd q = VectorXd::Zero(model.nq);
+ 
+  StackTicToc timer(StackTicToc::US); timer.tic();
+  SMOOTH(1)
+    {
+      crba(model,data,q);
+    }
+  timer.toc(std::cout,1000);
+
+  std::cout << "Mcrb = [ " << data.M << "  ];" << std::endl;
+
+#ifdef __se3_rnea_hpp__    
+  /* Joint inertia from iterative crba. */
+  {
+    Eigen::MatrixXd M(model.nv,model.nv);
+    Eigen::VectorXd v = Eigen::VectorXd::Zero(model.nv);
+    Eigen::VectorXd a = Eigen::VectorXd::Zero(model.nv);
+    const Eigen::VectorXd bias = rnea(model,data,q,v,a);
+
+    for(int i=0;i<model.nv;++i)
+      { 
+	
+	M.col(i) = rnea(model,data,q,v,Eigen::VectorXd::Unit(model.nv,i)) - bias;
+      }
+    std::cout << "Mrne = [  " << M << " ]; " << std::endl;
+  }	
+#endif // ifdef __se3_rnea_hpp__    
+
+
+  return 0;
+}
diff --git a/unittest/tspatial.cpp b/unittest/tspatial.cpp
index d771a42d8..554b7e3c8 100644
--- a/unittest/tspatial.cpp
+++ b/unittest/tspatial.cpp
@@ -221,6 +221,17 @@ bool testInertia()
   Force vxIv = aI.vxiv(v);
   assert( vxf.toVector().isApprox(vxIv.toVector()) );
 
+  // Test operator+
+  I1 = Inertia::Random();
+  Inertia I2 = Inertia::Random();
+  assert( (I1.toMatrix()+I2.toMatrix()).isApprox((I1+I2).toMatrix()) );
+  
+  Inertia I12 = I1;
+  I12 += I2;
+  assert( (I1.toMatrix()+I2.toMatrix()).isApprox(I12.toMatrix()) );
+
+
+
   return true;
 }
 
diff --git a/unittest/variant.cpp b/unittest/variant.cpp
index 42e9aef1b..ee65a3146 100644
--- a/unittest/variant.cpp
+++ b/unittest/variant.cpp
@@ -2,6 +2,7 @@
 #include "pinocchio/spatial/se3.hpp"
 #include "pinocchio/multibody/joint.hpp"
 #include "pinocchio/multibody/model.hpp"
+#include "pinocchio/multibody/joint/joint-generic.hpp"
 
 #include <iostream>
 
@@ -16,12 +17,11 @@ int main()
   using namespace se3;
 
 
-  JointModelVariant jmodel = JointModelRX(0,0);
+  JointModelVariant jmodel = JointModelRX();
   const JointDataVariant & jdata = CreateJointData::run(jmodel);
 
-
-  
-
+  JointDataGeneric jdatagen(jdata);
+  JointModelGeneric jmodelgen(jmodel);
 
   se3::Model model;
 }
-- 
GitLab