// This file is part of Sophus. // // Copyright 2011-2013 Hauke Strasdat // Copyrifht 2012-2013 Steven Lovegrove // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. #ifndef SOPHUS_SO3_HPP #define SOPHUS_SO3_HPP #include "sophus.hpp" //////////////////////////////////////////////////////////////////////////// // Forward Declarations / typedefs //////////////////////////////////////////////////////////////////////////// namespace Sophus { template class SO3Group; typedef EIGEN_DEPRECATED SO3Group SO3; typedef SO3Group SO3d; /**< double precision SO3 */ typedef SO3Group SO3f; /**< single precision SO3 */ } //////////////////////////////////////////////////////////////////////////// // Eigen Traits (For querying derived types in CRTP hierarchy) //////////////////////////////////////////////////////////////////////////// namespace Eigen { namespace internal { template struct traits > { typedef _Scalar Scalar; typedef Quaternion QuaternionType; }; template struct traits, _Options> > : traits > { typedef _Scalar Scalar; typedef Map,_Options> QuaternionType; }; template struct traits, _Options> > : traits > { typedef _Scalar Scalar; typedef Map,_Options> QuaternionType; }; } } namespace Sophus { using namespace Eigen; /** * \brief SO3 base type - implements SO3 class but is storage agnostic * * [add more detailed description/tutorial] */ template class SO3GroupBase { public: /** \brief scalar type */ typedef typename internal::traits::Scalar Scalar; /** \brief quaternion reference type */ typedef typename internal::traits::QuaternionType & QuaternionReference; /** \brief quaternion const reference type */ typedef const typename internal::traits::QuaternionType & ConstQuaternionReference; /** \brief degree of freedom of group * (three for rotation) */ static const int DoF = 3; /** \brief number of internal parameters used * (unit quaternion for rotation) */ static const int num_parameters = 4; /** \brief group transformations are NxN matrices */ static const int N = 3; /** \brief group transfomation type */ typedef Matrix Transformation; /** \brief point type */ typedef Matrix Point; /** \brief tangent vector type */ typedef Matrix Tangent; /** \brief adjoint transformation type */ typedef Matrix Adjoint; /** * \brief Adjoint transformation * * This function return the adjoint transformation \f$ Ad \f$ of the * group instance \f$ A \f$ such that for all \f$ x \f$ * it holds that \f$ \widehat{Ad_A\cdot x} = A\widehat{x}A^{-1} \f$ * with \f$\ \widehat{\cdot} \f$ being the hat()-Vector4Scalaror. * * For SO3, it simply returns the rotation matrix corresponding to \f$ A \f$. */ inline const Adjoint Adj() const { return matrix(); } /** * \returns copy of instance casted to NewScalarType */ template inline SO3Group cast() const { return SO3Group(unit_quaternion() .template cast() ); } /** * \returns pointer to internal data * * This provides unsafe read/write access to internal data. SO3 is represented * by an Eigen::Quaternion (four parameters). When using direct write access, * the user needs to take care of that the quaternion stays normalized. * * Note: The first three Scalars represent the imaginary parts, while the * forth Scalar represent the real part. * * \see normalize() */ inline Scalar* data() { return unit_quaternion_nonconst().coeffs().data(); } /** * \returns const pointer to internal data * * Const version of data(). */ inline const Scalar* data() const { return unit_quaternion().coeffs().data(); } /** * \brief Fast group multiplication * * This method is a fast version of operator*=(), since it does not perform * normalization. It is up to the user to call normalize() once in a while. * * \see operator*=() */ inline void fastMultiply(const SO3Group& other) { unit_quaternion_nonconst() *= other.unit_quaternion(); } /** * \returns group inverse of instance */ inline const SO3Group inverse() const { return SO3Group(unit_quaternion().conjugate()); } /** * \brief Logarithmic map * * \returns tangent space representation (=rotation vector) of instance * * \see log(). */ inline const Tangent log() const { return SO3Group::log(*this); } /** * \brief Normalize quaternion * * It re-normalizes unit_quaternion to unit length. This method only needs to * be called in conjunction with fastMultiply() or data() write access. */ inline void normalize() { Scalar length = unit_quaternion_nonconst().norm(); if (length < SophusConstants::epsilon()) { throw SophusException("Quaternion is (near) zero!"); } unit_quaternion_nonconst().coeffs() /= length; } /** * \returns 3x3 matrix representation of instance * * For SO3, the matrix representation is an orthogonal matrix R with det(R)=1, * thus the so-called rotation matrix. */ inline const Transformation matrix() const { return unit_quaternion().toRotationMatrix(); } /** * \brief Assignment operator */ template inline SO3GroupBase& operator=(const SO3GroupBase & other) { unit_quaternion_nonconst() = other.unit_quaternion(); return *this; } /** * \brief Group multiplication * \see operator*=() */ inline const SO3Group operator*(const SO3Group& other) const { SO3Group result(*this); result *= other; return result; } /** * \brief Group action on \f$ \mathbf{R}^3 \f$ * * \param p point \f$p \in \mathbf{R}^3 \f$ * \returns point \f$p' \in \mathbf{R}^3 \f$, rotated version of \f$p\f$ * * This function rotates a point \f$ p \f$ in \f$ \mathbf{R}^3 \f$ by the * SO3 transformation \f$R\f$ (=rotation matrix): \f$ p' = R\cdot p \f$. * * * Since SO3 is intenally represented by a unit quaternion \f$q\f$, it is * implemented as \f$ p' = q\cdot p \cdot q^{*} \f$ * with \f$ q^{*} \f$ being the quaternion conjugate of \f$ q \f$. * * Geometrically, \f$ p \f$ is rotated by angle \f$|\omega|\f$ around the * axis \f$\frac{\omega}{|\omega|}\f$ with \f$ \omega = \log(R)^\vee \f$. * * \see log() */ inline const Point operator*(const Point & p) const { return unit_quaternion()._transformVector(p); } /** * \brief In-place group multiplication * * \see fastMultiply() * \see operator*() */ inline void operator*=(const SO3Group& other) { fastMultiply(other); normalize(); } /** * \brief Setter of internal unit quaternion representation * * \param quaternion * \pre the quaternion must not be zero * * The quaternion is normalized to unit length. */ inline void setQuaternion(const Quaternion& quaternion) { unit_quaternion_nonconst() = quaternion; normalize(); } /** * \brief Accessor of unit quaternion * * No direct write access is given to ensure the quaternion stays normalized. */ EIGEN_STRONG_INLINE ConstQuaternionReference unit_quaternion() const { return static_cast(this)->unit_quaternion(); } //////////////////////////////////////////////////////////////////////////// // public static functions //////////////////////////////////////////////////////////////////////////// /** * \param b 3-vector representation of Lie algebra element * \returns derivative of Lie bracket * * This function returns \f$ \frac{\partial}{\partial a} [a, b]_{so3} \f$ * with \f$ [a, b]_{so3} \f$ being the lieBracket() of the Lie algebra so3. * * \see lieBracket() */ inline static const Adjoint d_lieBracketab_by_d_a(const Tangent & b) { return -hat(b); } /** * \brief Group exponential * * \param omega tangent space element (=rotation vector \f$ \omega \f$) * \returns corresponding element of the group SO3 * * To be more specific, this function computes \f$ \exp(\widehat{\omega}) \f$ * with \f$ \exp(\cdot) \f$ being the matrix exponential * and \f$ \widehat{\cdot} \f$ the hat()-operator of SO3. * * \see expAndTheta() * \see hat() * \see log() */ inline static const SO3Group exp(const Tangent & omega) { Scalar theta; return expAndTheta(omega, &theta); } /** * \brief Group exponential and theta * * \param omega tangent space element (=rotation vector \f$ \omega \f$) * \param[out] theta angle of rotation \f$ \theta = |\omega| \f$ * \returns corresponding element of the group SO3 * * \see exp() for details */ inline static const SO3Group expAndTheta(const Tangent & omega, Scalar * theta) { const Scalar theta_sq = omega.squaredNorm(); *theta = std::sqrt(theta_sq); const Scalar half_theta = static_cast(0.5)*(*theta); Scalar imag_factor; Scalar real_factor;; if((*theta)::epsilon()) { const Scalar theta_po4 = theta_sq*theta_sq; imag_factor = static_cast(0.5) - static_cast(1.0/48.0)*theta_sq + static_cast(1.0/3840.0)*theta_po4; real_factor = static_cast(1) - static_cast(0.5)*theta_sq + static_cast(1.0/384.0)*theta_po4; } else { const Scalar sin_half_theta = std::sin(half_theta); imag_factor = sin_half_theta/(*theta); real_factor = std::cos(half_theta); } return SO3Group(Quaternion(real_factor, imag_factor*omega.x(), imag_factor*omega.y(), imag_factor*omega.z())); } /** * \brief Generators * * \pre \f$ i \in \{0,1,2\} \f$ * \returns \f$ i \f$th generator \f$ G_i \f$ of SO3 * * The infinitesimal generators of SO3 * are \f$ * G_0 = \left( \begin{array}{ccc} * 0& 0& 0& \\ * 0& 0& -1& \\ * 0& 1& 0& * \end{array} \right), * G_1 = \left( \begin{array}{ccc} * 0& 0& 1& \\ * 0& 0& 0& \\ * -1& 0& 0& * \end{array} \right), * G_2 = \left( \begin{array}{ccc} * 0& -1& 0& \\ * 1& 0& 0& \\ * 0& 0& 0& * \end{array} \right). * \f$ * \see hat() */ inline static const Transformation generator(int i) { if (i<0 || i>2) { throw SophusException("i is not in range [0,2]."); } Tangent e; e.setZero(); e[i] = static_cast(1); return hat(e); } /** * \brief hat-operator * * \param omega 3-vector representation of Lie algebra element * \returns 3x3-matrix representatin of Lie algebra element * * Formally, the hat-operator of SO3 is defined * as \f$ \widehat{\cdot}: \mathbf{R}^3 \rightarrow \mathbf{R}^{3\times 3}, * \quad \widehat{\omega} = \sum_{i=0}^2 G_i \omega_i \f$ * with \f$ G_i \f$ being the ith infinitesial generator(). * * \see generator() * \see vee() */ inline static const Transformation hat(const Tangent & omega) { Transformation Omega; Omega << static_cast(0), -omega(2), omega(1) , omega(2), static_cast(0), -omega(0) , -omega(1), omega(0), static_cast(0); return Omega; } /** * \brief Lie bracket * * \param omega1 3-vector representation of Lie algebra element * \param omega2 3-vector representation of Lie algebra element * \returns 3-vector representation of Lie algebra element * * It computes the bracket of SO3. To be more specific, it * computes \f$ [\omega_1, \omega_2]_{so3} * := [\widehat{\omega_1}, \widehat{\omega_2}]^\vee \f$ * with \f$ [A,B] = AB-BA \f$ being the matrix * commutator, \f$ \widehat{\cdot} \f$ the * hat()-operator and \f$ (\cdot)^\vee \f$ the vee()-operator of SO3. * * For the Lie algebra so3, the Lie bracket is simply the * cross product: \f$ [\omega_1, \omega_2]_{so3} * = \omega_1 \times \omega_2 \f$. * * \see hat() * \see vee() */ inline static const Tangent lieBracket(const Tangent & omega1, const Tangent & omega2) { return omega1.cross(omega2); } /** * \brief Logarithmic map * * \param other element of the group SO3 * \returns corresponding tangent space element * (=rotation vector \f$ \omega \f$) * * Computes the logarithmic, the inverse of the group exponential. * To be specific, this function computes \f$ \log({\cdot})^\vee \f$ * with \f$ \vee(\cdot) \f$ being the matrix logarithm * and \f$ \vee{\cdot} \f$ the vee()-operator of SO3. * * \see exp() * \see logAndTheta() * \see vee() */ inline static const Tangent log(const SO3Group & other) { Scalar theta; return logAndTheta(other, &theta); } /** * \brief Logarithmic map and theta * * \param other element of the group SO3 * \param[out] theta angle of rotation \f$ \theta = |\omega| \f$ * \returns corresponding tangent space element * (=rotation vector \f$ \omega \f$) * * \see log() for details */ inline static const Tangent logAndTheta(const SO3Group & other, Scalar * theta) { const Scalar squared_n = other.unit_quaternion().vec().squaredNorm(); const Scalar n = std::sqrt(squared_n); const Scalar w = other.unit_quaternion().w(); Scalar two_atan_nbyw_by_n; // Atan-based log thanks to // // C. Hertzberg et al.: // "Integrating Generic Sensor Fusion Algorithms with Sound State // Representation through Encapsulation of Manifolds" // Information Fusion, 2011 if (n < SophusConstants::epsilon()) { // If quaternion is normalized and n=0, then w should be 1; // w=0 should never happen here! if (std::abs(w) < SophusConstants::epsilon()) { throw SophusException("Quaternion is not normalized!"); } const Scalar squared_w = w*w; two_atan_nbyw_by_n = static_cast(2) / w - static_cast(2)*(squared_n)/(w*squared_w); } else { if (std::abs(w)::epsilon()) { if (w > static_cast(0)) { two_atan_nbyw_by_n = M_PI/n; } else { two_atan_nbyw_by_n = -M_PI/n; } }else{ two_atan_nbyw_by_n = static_cast(2) * atan(n/w) / n; } } *theta = two_atan_nbyw_by_n*n; return two_atan_nbyw_by_n * other.unit_quaternion().vec(); } /** * \brief vee-operator * * \param Omega 3x3-matrix representation of Lie algebra element * \pr Omega must be a skew-symmetric matrix * \returns 3-vector representatin of Lie algebra element * * This is the inverse of the hat()-operator. * * \see hat() */ inline static const Tangent vee(const Transformation & Omega) { return static_cast(0.5) * Tangent(Omega(2,1) - Omega(1,2), Omega(0,2) - Omega(2,0), Omega(1,0) - Omega(0,1)); } private: // Mutator of unit_quaternion is private so users are hampered // from setting non-unit quaternions. EIGEN_STRONG_INLINE QuaternionReference unit_quaternion_nonconst() { return static_cast(this)->unit_quaternion_nonconst(); } }; /** * \brief SO3 default type - Constructors and default storage for SO3 Type */ template class SO3Group : public SO3GroupBase > { typedef SO3GroupBase > Base; public: /** \brief scalar type */ typedef typename internal::traits > ::Scalar Scalar; /** \brief quaternion type */ typedef typename internal::traits > ::QuaternionType & QuaternionReference; typedef const typename internal::traits > ::QuaternionType & ConstQuaternionReference; /** \brief degree of freedom of group */ static const int DoF = Base::DoF; /** \brief number of internal parameters used */ static const int num_parameters = Base::num_parameters; /** \brief group transformations are NxN matrices */ static const int N = Base::N; /** \brief group transfomation type */ typedef typename Base::Transformation Transformation; /** \brief point type */ typedef typename Base::Point Point; /** \brief tangent vector type */ typedef typename Base::Tangent Tangent; /** \brief adjoint transformation type */ typedef typename Base::Adjoint Adjoint; // base is friend so unit_quaternion_nonconst can be accessed from base friend class SO3GroupBase >; EIGEN_MAKE_ALIGNED_OPERATOR_NEW /** * \brief Default constructor * * Initialize Quaternion to identity rotation. */ inline SO3Group() : unit_quaternion_(static_cast(1), static_cast(0), static_cast(0), static_cast(0)) { } /** * \brief Copy constructor */ template inline SO3Group(const SO3GroupBase & other) : unit_quaternion_(other.unit_quaternion()) { } /** * \brief Constructor from rotation matrix * * \pre rotation matrix need to be orthogonal with determinant of 1 */ inline SO3Group(const Transformation & R) : unit_quaternion_(R) { } /** * \brief Constructor from quaternion * * \pre quaternion must not be zero */ inline explicit SO3Group(const Quaternion & quat) : unit_quaternion_(quat) { Base::normalize(); } /** * \brief Constructor from Euler angles * * \param alpha1 rotation around x-axis * \param alpha2 rotation around y-axis * \param alpha3 rotation around z-axis * * Since rotations in 3D do not commute, the order of the individual rotations * matter. Here, the following convention is used. We calculate a SO3 member * corresponding to the rotation matrix \f$ R \f$ such * that \f$ R=\exp\left(\begin{array}{c}\alpha_1\\ 0\\ 0\end{array}\right) * \cdot \exp\left(\begin{array}{c}0\\ \alpha_2\\ 0\end{array}\right) * \cdot \exp\left(\begin{array}{c}0\\ 0\\ \alpha_3\end{array}\right)\f$. */ inline SO3Group(Scalar alpha1, Scalar alpha2, Scalar alpha3) { const static Scalar zero = static_cast(0); unit_quaternion_ = ( SO3Group::exp(Tangent(alpha1, zero, zero)) *SO3Group::exp(Tangent( zero, alpha2, zero)) *SO3Group::exp(Tangent( zero, zero, alpha3)) ) .unit_quaternion_; } /** * \brief Accessor of unit quaternion * * No direct write access is given to ensure the quaternion stays normalized. */ EIGEN_STRONG_INLINE ConstQuaternionReference unit_quaternion() const { return unit_quaternion_; } protected: // Mutator of unit_quaternion is protected so users are hampered // from setting non-unit quaternions. EIGEN_STRONG_INLINE QuaternionReference unit_quaternion_nonconst() { return unit_quaternion_; } Quaternion unit_quaternion_; }; } // end namespace namespace Eigen { /** * \brief Specialisation of Eigen::Map for SO3GroupBase * * Allows us to wrap SO3 Objects around POD array * (e.g. external c style quaternion) */ template class Map, _Options> : public Sophus::SO3GroupBase, _Options> > { typedef Sophus::SO3GroupBase, _Options> > Base; public: /** \brief scalar type */ typedef typename internal::traits::Scalar Scalar; /** \brief quaternion reference type */ typedef typename internal::traits::QuaternionType & QuaternionReference; /** \brief quaternion const reference type */ typedef const typename internal::traits::QuaternionType & ConstQuaternionReference; /** \brief degree of freedom of group */ static const int DoF = Base::DoF; /** \brief number of internal parameters used */ static const int num_parameters = Base::num_parameters; /** \brief group transformations are NxN matrices */ static const int N = Base::N; /** \brief group transfomation type */ typedef typename Base::Transformation Transformation; /** \brief point type */ typedef typename Base::Point Point; /** \brief tangent vector type */ typedef typename Base::Tangent Tangent; /** \brief adjoint transformation type */ typedef typename Base::Adjoint Adjoint; // base is friend so unit_quaternion_nonconst can be accessed from base friend class Sophus::SO3GroupBase, _Options> >; EIGEN_INHERIT_ASSIGNMENT_EQUAL_OPERATOR(Map) using Base::operator*=; using Base::operator*; EIGEN_STRONG_INLINE Map(Scalar* coeffs) : unit_quaternion_(coeffs) { } /** * \brief Accessor of unit quaternion * * No direct write access is given to ensure the quaternion stays normalized. */ EIGEN_STRONG_INLINE ConstQuaternionReference unit_quaternion() const { return unit_quaternion_; } protected: // Mutator of unit_quaternion is protected so users are hampered // from setting non-unit quaternions. EIGEN_STRONG_INLINE QuaternionReference unit_quaternion_nonconst() { return unit_quaternion_; } Map,_Options> unit_quaternion_; }; /** * \brief Specialisation of Eigen::Map for const SO3GroupBase * * Allows us to wrap SO3 Objects around POD array * (e.g. external c style quaternion) */ template class Map, _Options> : public Sophus::SO3GroupBase< Map, _Options> > { typedef Sophus::SO3GroupBase, _Options> > Base; public: /** \brief scalar type */ typedef typename internal::traits::Scalar Scalar; /** \brief quaternion const reference type */ typedef const typename internal::traits::QuaternionType & ConstQuaternionReference; /** \brief degree of freedom of group */ static const int DoF = Base::DoF; /** \brief number of internal parameters used */ static const int num_parameters = Base::num_parameters; /** \brief group transformations are NxN matrices */ static const int N = Base::N; /** \brief group transfomation type */ typedef typename Base::Transformation Transformation; /** \brief point type */ typedef typename Base::Point Point; /** \brief tangent vector type */ typedef typename Base::Tangent Tangent; /** \brief adjoint transformation type */ typedef typename Base::Adjoint Adjoint; EIGEN_INHERIT_ASSIGNMENT_EQUAL_OPERATOR(Map) using Base::operator*=; using Base::operator*; EIGEN_STRONG_INLINE Map(const Scalar* coeffs) : unit_quaternion_(coeffs) { } /** * \brief Accessor of unit quaternion * * No direct write access is given to ensure the quaternion stays normalized. */ EIGEN_STRONG_INLINE const ConstQuaternionReference unit_quaternion() const { return unit_quaternion_; } protected: const Map,_Options> unit_quaternion_; }; } #endif