Re: [eigen] Eigen and rigid body simulation

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


Hi,

I have a working prototype with LieGroup<Quaternion> and LieGroup<Matrix<1,7> >, but I'm not yet satisfied. Besides, several names (for example the get function) should be renamed. The code is in the attached file.

I choose to use partial specialization, so I have a main class which methods call static methods from a structures.

For example :

template<class GG> EIGEN_STRONG_INLINE ReturnVal operator*(const LieGroup<GG>& other) const {
      return ei_lie_composition<ReturnVal>::run(this->get(), other.get());
}

  template<class G> struct ei_lie_composition{
template<class GG> static EIGEN_STRONG_INLINE typename LieGroup<G>::ReturnVal run(const G& a, const GG& b)
    {
      return LieGroup<G>::ReturnVal(a * b);
    }
  };

I use the property ReturnVal of the traits classes to manipulate LieGroup<G> and LieGroup<Map<G> > together. For example, the return type (ReturnVal) of Quaternion<Scalar> and Map<Quaternion<Scalar>, PacketAccess> are Quaternion<Scalar>. It's the type of temporary that will be created during the composition operation. That's why I have to use the following template, with classes G and GG :

  template<class G> struct ei_lie_composition{
template<class GG> static EIGEN_STRONG_INLINE typename LieGroup<G>::ReturnVal run(const G& a, const GG& b)
    {
      return LieGroup<G>::ReturnVal(a * b);
    }
  };

so G and GG could be either Quaternion or Map<Quaternion>.

So i can then add some typedef :

typedef Eigen::LieGroup<Eigen::Quaterniond> Rotd;
typedef Eigen::LieGroup<Eigen::Map<Eigen::Quaterniond, 0> > RotMapd;

and I then use only one class, Rotd and the mapped one, RotMapd

I have several (at least 4) points to clarify (that's why I'm not satisfied) :

1) I think that I have to used derived class instead of typedef to add new constructor, for example a

Rotd(const scalar, const scalar, const scalar, const scalar);

 instead using

LieGroup<Quaternion<Scalar> >((Quaternion<Scalar>(const scalar, const scalar, const scalar, const scalar));

or additionnal methods, such as getTranslation or getRotation for the SE(3) element (LieGroup<Matrix<Scalar, 1, 7> and LieGroup<Map...>)

2) I have some inlining problem with LieGroup<Matrix<Scalar, 1, 7> > when I use a simpler class, there are less call in the generated assembly

3) I have issues with some operations, for example :

      getTranslation(res) = getQuaternion(a) * getTranslation(b);
      getTranslation(res) += getTranslation(a);

works where :

getTranslation(res) = getQuaternion(a) * getTranslation(b) + getTranslation(a);

give a YOU_MIX_DIFFERENT_SIZE assert, although all types are Matrix<Scalar, 1, 3> and the multiplication return a Matrix<Scalar, 1, 3>


4) I feel that is a little bit over designed or too complicated.

--
Mathieu





namespace Eigen{

  // forward declaration
  template<class G> struct LieGroup;

  // dummy traits declaration (optionnal)
  template<class G>
  struct ei_traits<LieGroup<G> >
  {
    typedef typename ei_traits<G>::Scalar Scalar;
    typedef Matrix<typename ei_traits<G>::Scalar,0,0> Algebra;       // Lie algebra associatet to the Lie Group
    typedef Matrix<typename ei_traits<G>::Scalar,0,0> Coalgebra;     // Lie coalgebra associatet to the Lie Group
    typedef LieGroup<G> ReturnVal;                                   // Return type or unmapped value, when G = Map<G'>, return type has to be LieGroup<G'>
  };

  // Lie Group class
  template<class G>
  struct LieGroup {
    // aliases
    typedef typename ei_traits<G>::Scalar Scalar;
    typedef typename ei_traits<LieGroup<G> >::Algebra Algebra;
    typedef typename ei_traits<LieGroup<G> >::Coalgebra Coalgebra;
    typedef typename ei_traits<LieGroup<G> >::ReturnVal ReturnVal;
    typedef typename ei_traits<LieGroup<G> >::AdjMatrix AdjMatrix;

    // inverse
    ReturnVal inverse() const { 
      return ei_lie_inverse<G>::run(this->get());
    }
    // identity
    static ReturnVal Identity() { 
      return ei_lie_identity<G>::run(); 
    }
    // composition
    template<class GG> EIGEN_STRONG_INLINE ReturnVal operator*(const LieGroup<GG>& other) const {
      return ei_lie_composition<ReturnVal>::run(this->get(), other.get()); 
    }
    // assignation
    template<class GG> EIGEN_STRONG_INLINE LieGroup& operator=(const LieGroup<GG>& other){
      m_element = other.get();
      return *this;
    }

    AdjMatrix adjoint(void) const {return ei_lie_adjoint<ReturnVal>::run(this->get());}
    AdjMatrix transposedAdjoint(void) const {return ei_lie_adjoint<ReturnVal>::run(this->get()).transpose();} // a distinct function will be faster

    // log : map from the group to the algebra
    Algebra log(const Scalar precision = 1e-6) const{return ei_lie_log<ReturnVal>::run(this->get(), precision);} // ReturnVal is used to allow log working with both G and Map<G> 

    // constructors
    template<class GG> LieGroup(const GG& el) : m_element(el) {}; // /!\ [XXX] handle properly the map case
    LieGroup(const double* data) : m_element(data){};
    LieGroup(){};

    // accessor to the wrapped element
    G& get(){return m_element;}
    const G& get() const {return m_element;}

  protected:
    G m_element;
  };

  //***** generic functions on Lie Group

  template<class G> struct ei_lie_identity{
    static inline typename LieGroup<G>::ReturnVal run(){
      return LieGroup<G>::ReturnVal(G::Identity());
    }
  };

  template<class G> struct ei_lie_inverse{
    static inline typename LieGroup<G>::ReturnVal run(const G& g){
      return LieGroup<G>::ReturnVal(g.inverse());
    }
  };

  template<class G> struct ei_lie_composition{
    template<class GG> static EIGEN_STRONG_INLINE typename LieGroup<G>::ReturnVal run(const G& a, const GG& b)
    {
      return LieGroup<G>::ReturnVal(a * b);
    }
  };
  
  template<class G> struct ei_lie_log{
  };

  template<class G> struct ei_lie_adjoint{
  };

  //****************  specialization

  // specialization : quaternion : rotation in 3D
  template<typename Derived>
  struct ei_traits<LieGroup<QuaternionBase<Derived> > >{
    typedef typename ei_traits<Derived>::Scalar Scalar;
    typedef Matrix<Scalar,1,3> Algebra;
    typedef Matrix<Scalar,1,3> Coalgebra;
    typedef Quaternion<Scalar> ReturnVal;
    typedef Matrix<Scalar, 3, 3> AdjMatrix;
  };

  template<typename Scalar>
  struct ei_traits<LieGroup<Quaternion<Scalar> > > : public ei_traits<LieGroup<QuaternionBase<Quaternion<Scalar> > > >
  {
  };

  template<typename Scalar, int PacketAccess>
  struct ei_traits<LieGroup<Map<Quaternion<Scalar>, PacketAccess> > > : public ei_traits<LieGroup<QuaternionBase<Map<Quaternion<Scalar>, PacketAccess> > > >
  {
  };

  template<typename Scalar> struct ei_lie_log<Quaternion<Scalar> > 
  {
    typedef typename ei_traits<LieGroup<Quaternion<Scalar> > >::Algebra Algebra;

    template<class Derived> static Algebra run(const QuaternionBase<Derived>& q, const Scalar precision){
      const Scalar n2 = q.vec().squaredNorm();
      const Scalar n = ei_sqrt(n2);

      if (n < precision) 
        return Algebra((2 / q.w()) * q.vec());
      else 
        return Algebra(ei_atan2(2 * n * q.w(), q.w() * q.w() - n2) / n * q.vec());
    }
  };

  template<typename Scalar> struct ei_lie_adjoint<Quaternion<Scalar> >
  {
    typedef typename ei_traits<LieGroup<Quaternion<Scalar> > >::AdjMatrix AdjMatrix;

    template<class Derived> static inline AdjMatrix run(const QuaternionBase<Derived>& q){
      return q.toRotationMatrix();
    }
  };

  //specialization : Frame / Displacement : rotation and translation in 3D
  // 1) helper
  template<class Derived> inline Map<Matrix<typename ei_traits<Derived>::Scalar, 1, 3> > getTranslation(const MatrixBase<Derived>& d){
    return Map<Matrix<typename ei_traits<Derived>::Scalar, 1, 3> >(d.derived().data() + 4);
  }

  template<class Derived> inline LieGroup<Map<Quaternion<typename ei_traits<Derived>::Scalar> > > getRotation(const MatrixBase<Derived>& d){
    return LieGroup<Map<Quaternion<typename ei_traits<Derived>::Scalar> > >(d.derived().data());
  }
  
  template<class Derived> inline Map<Quaternion<typename ei_traits<Derived>::Scalar> > getQuaternion(const MatrixBase<Derived>& d){
    return Map<Quaternion<typename ei_traits<Derived>::Scalar> >(d.derived().data());
  }

  // 2) specialization
  template<typename _Scalar>
  struct ei_traits<LieGroup<Matrix<_Scalar, 1, 7> > >{
    typedef _Scalar Scalar;
    typedef Matrix<Scalar, 1, 6> Algebra;
    typedef Matrix<Scalar, 1, 6> Coalgebra;
    typedef Matrix<Scalar, 1, 7> ReturnVal;
    typedef Matrix<Scalar, 6, 6> AdjMatrix;
  };

  template<typename Scalar>
  struct ei_traits<LieGroup<Map<Matrix<Scalar, 1, 7> > > > : ei_traits<LieGroup<Matrix<Scalar, 1, 7> > >{
  };

  // identity
  template<typename Scalar> struct ei_lie_identity<Matrix<Scalar, 1, 7>>{
    static inline typename LieGroup<Matrix<Scalar, 1, 7> >::ReturnVal run(){
      return (LieGroup<Matrix<Scalar, 1, 7> >::ReturnVal() << 0,0,0,1,0,0,0);
    }
  };

  // inverse
  template<typename Scalar> struct ei_lie_inverse<Matrix<Scalar, 1, 7> >{
    template<class Derived> static inline typename LieGroup<Matrix<Scalar, 1, 7> >::ReturnVal run(const MatrixBase<Derived>& d){
      LieGroup<Matrix<Scalar, 1, 7> >::ReturnVal res;

      getRotation(res) = getRotation(d).inverse(); // rotation : rot^-1
      getTranslation(res) = -getRotation(res) * getTranslation(d); // translation : -rot^-1*trans

      return res;
    }
  };

  // composition :
  // [ R1   p1 ] * [ R2   p2 ] = [ R1*R2   R1*p2 + p1 ]  
  // [ 0    1  ]   [ 0    1  ]   [   0          1     ]
  //
  template<typename Scalar> struct ei_lie_composition<Matrix<Scalar, 1, 7> >{
    template<class Derived, class OtherDerived >static EIGEN_STRONG_INLINE typename LieGroup<Matrix<Scalar,1,7> >::ReturnVal run(const MatrixBase<Derived>& a, const MatrixBase<OtherDerived>& b)
    {
      LieGroup<Matrix<Scalar, 1, 7>>::ReturnVal res;

      getRotation(res) =  getRotation(a) * getRotation(b);       // rot1 * rot2;
      //Matrix<Scalar, 1, 3>::Map(res.derived().data()) = Map<Quaternion<Scalar> >(a.derived().data()) * Matrix<Scalar, 1, 3>::Map(b.derived().data()) + Matrix<Scalar, 1, 3>::Map(b.derived().data()); 
      getTranslation(res) = getQuaternion(a) * getTranslation(b); // + getTranslation(a);  [XXX] ? ASSERT : YOU_MIX_DIFFERENT_SIZE ?
      getTranslation(res) += getTranslation(a);                   //rot1 * trans2 + trans1

      return res;
    }
  };

  // log SE(3) -> se(3)
  //
  // log [ R   p ] = [ wtilde   B*p ]
  //     [ 0   1 ]   [   0       0  ]
  //
  // wtilde = log R
  // B = I - 0.5*wtilde + (2*sin||w||-||w||(1+cos||w||))/(2*||w||^2*sin||w||) * wtilde^2
  //
  template<typename Scalar> struct ei_lie_log<Matrix<Scalar, 1, 7> > 
  {
    typedef typename ei_traits<LieGroup<Matrix<Scalar, 1, 7> > >::Algebra Algebra;
    typedef typename Matrix<Scalar, 1, 3> Vector3;

    template<class Derived> static Algebra run(const MatrixBase<Derived>& m, const Scalar precision){
      LieGroup<Quaternion<Scalar> >::Algebra ang =  getRotation(m).log(precision);

      const Scalar n2 = ang.squaredNorm();  // ||wtidle||^2
      Algebra al; // [XXX]
      if(n2 < precision){
        al << ang, getTranslation(m);  // [XXX]
        return al; // return a matrix<Scalar, 1, 6> : first 3 : angle, last 3 : translation vector
      }
      else{
        const Scalar n = ei_sqrt(n2); 
        const Scalar sn = ei_sin(n);

        Scalar val = (2.0 * sn - n * (1.0 + ei_cos(n))) / (2.0 *n2 * sn);

        Map<Vector3> mv = getTranslation(m);
        Vector3 lin = -0.5*ang.cross(mv);
        lin += (1. - val * n2 ) * mv;
        lin += val * ang.dot(mv) * ang;

        al << ang, lin;
        return al;
      }
    }
  };

 /* template<class G> struct ei_lie_adjoint{
    TODO
  };*/
}

// Some example of typedef
typedef Eigen::LieGroup<Eigen::Quaterniond> Rotd;
typedef Eigen::LieGroup<Eigen::Map<Eigen::Quaterniond, 0> > RotMapd;

typedef Eigen::LieGroup<Eigen::Matrix<double, 1, 7> > Displd;



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/