#ifndef SO3_HEADER #define SO3_HEADER #include #include #include #include #include "common.h" template class SO3 { public: const static int constexpr K = 3; // manifold dimension const static int constexpr N = 4; // embedding dimension using Vector3 = Eigen::Matrix; using Vector4 = Eigen::Matrix; using Matrix3 = Eigen::Matrix; using Tangent = Eigen::Matrix; using Data = Eigen::Matrix; using Point = Eigen::Matrix; using Point4 = Eigen::Matrix; using Transformation = Eigen::Matrix; using Adjoint = Eigen::Matrix; using Quaternion = Eigen::Quaternion; EIGEN_DEVICE_FUNC SO3(Quaternion const& q) : unit_quaternion(q) { unit_quaternion.normalize(); }; EIGEN_DEVICE_FUNC SO3(const Scalar *data) : unit_quaternion(data) { unit_quaternion.normalize(); }; EIGEN_DEVICE_FUNC SO3() { unit_quaternion = Quaternion::Identity(); } EIGEN_DEVICE_FUNC SO3 inv() { return SO3(unit_quaternion.conjugate()); } EIGEN_DEVICE_FUNC Data data() const { return unit_quaternion.coeffs(); } EIGEN_DEVICE_FUNC SO3 operator*(SO3 const& other) { return SO3(unit_quaternion * other.unit_quaternion); } EIGEN_DEVICE_FUNC Point operator*(Point const& p) const { const Quaternion& q = unit_quaternion; Point uv = q.vec().cross(p); uv += uv; return p + q.w()*uv + q.vec().cross(uv); } EIGEN_DEVICE_FUNC Point4 act4(Point4 const& p) const { Point4 p1; p1 << this->operator*(p.template segment<3>(0)), p(3); return p1; } EIGEN_DEVICE_FUNC Adjoint Adj() const { return unit_quaternion.toRotationMatrix(); } EIGEN_DEVICE_FUNC Transformation Matrix() const { return unit_quaternion.toRotationMatrix(); } EIGEN_DEVICE_FUNC Eigen::Matrix Matrix4x4() const { Eigen::Matrix T = Eigen::Matrix::Identity(); T.template block<3,3>(0,0) = Matrix(); return T; } EIGEN_DEVICE_FUNC Eigen::Matrix orthogonal_projector() const { // jacobian action on a point Eigen::Matrix J = Eigen::Matrix::Zero(); J.template block<3,3>(0,0) = 0.5 * ( unit_quaternion.w() * Matrix3::Identity() + SO3::hat(-unit_quaternion.vec()) ); J.template block<1,3>(3,0) = 0.5 * (-unit_quaternion.vec()); return J; } EIGEN_DEVICE_FUNC Tangent Adj(Tangent const& a) const { return Adj() * a; } EIGEN_DEVICE_FUNC Tangent AdjT(Tangent const& a) const { return Adj().transpose() * a; } EIGEN_DEVICE_FUNC static Transformation hat(Tangent const& phi) { Transformation Phi; Phi << 0.0, -phi(2), phi(1), phi(2), 0.0, -phi(0), -phi(1), phi(0), 0.0; return Phi; } EIGEN_DEVICE_FUNC static Adjoint adj(Tangent const& phi) { return SO3::hat(phi); } EIGEN_DEVICE_FUNC Tangent Log() const { using std::abs; using std::atan; using std::sqrt; Scalar squared_n = unit_quaternion.vec().squaredNorm(); Scalar w = 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 (squared_n < EPS * EPS) { // If quaternion is normalized and n=0, then w should be 1; // w=0 should never happen here! Scalar squared_w = w * w; two_atan_nbyw_by_n = Scalar(2) / w - Scalar(2.0/3.0) * (squared_n) / (w * squared_w); } else { Scalar n = sqrt(squared_n); if (abs(w) < EPS) { if (w > Scalar(0)) { two_atan_nbyw_by_n = Scalar(PI) / n; } else { two_atan_nbyw_by_n = -Scalar(PI) / n; } } else { two_atan_nbyw_by_n = Scalar(2) * atan(n / w) / n; } } return two_atan_nbyw_by_n * unit_quaternion.vec(); } EIGEN_DEVICE_FUNC static SO3 Exp(Tangent const& phi) { Scalar theta2 = phi.squaredNorm(); Scalar theta = sqrt(theta2); Scalar imag_factor; Scalar real_factor; if (theta < EPS) { Scalar theta4 = theta2 * theta2; imag_factor = Scalar(0.5) - Scalar(1.0/48.0) * theta2 + Scalar(1.0/3840.0) * theta4; real_factor = Scalar(1) - Scalar(1.0/8.0) * theta2 + Scalar(1.0/384.0) * theta4; } else { imag_factor = sin(.5 * theta) / theta; real_factor = cos(.5 * theta); } Quaternion q(real_factor, imag_factor*phi.x(), imag_factor*phi.y(), imag_factor*phi.z()); return SO3(q); } EIGEN_DEVICE_FUNC static Adjoint left_jacobian(Tangent const& phi) { // left jacobian Matrix3 I = Matrix3::Identity(); Matrix3 Phi = SO3::hat(phi); Matrix3 Phi2 = Phi * Phi; Scalar theta2 = phi.squaredNorm(); Scalar theta = sqrt(theta2); Scalar coef1 = (theta < EPS) ? Scalar(1.0/2.0) - Scalar(1.0/24.0) * theta2 : (1.0 - cos(theta)) / theta2; Scalar coef2 = (theta < EPS) ? Scalar(1.0/6.0) - Scalar(1.0/120.0) * theta2 : (theta - sin(theta)) / (theta2 * theta); return I + coef1 * Phi + coef2 * Phi2; } EIGEN_DEVICE_FUNC static Adjoint left_jacobian_inverse(Tangent const& phi) { // left jacobian inverse Matrix3 I = Matrix3::Identity(); Matrix3 Phi = SO3::hat(phi); Matrix3 Phi2 = Phi * Phi; Scalar theta2 = phi.squaredNorm(); Scalar theta = sqrt(theta2); Scalar half_theta = Scalar(.5) * theta ; Scalar coef2 = (theta < EPS) ? Scalar(1.0/12.0) : (Scalar(1) - theta * cos(half_theta) / (Scalar(2) * sin(half_theta))) / (theta * theta); return I + Scalar(-0.5) * Phi + coef2 * Phi2; } EIGEN_DEVICE_FUNC static Eigen::Matrix act_jacobian(Point const& p) { // jacobian action on a point return SO3::hat(-p); } EIGEN_DEVICE_FUNC static Eigen::Matrix act4_jacobian(Point4 const& p) { // jacobian action on a point Eigen::Matrix J = Eigen::Matrix::Zero(); J.template block<3,3>(0,0) = SO3::hat(-p.template segment<3>(0)); return J; } private: Quaternion unit_quaternion; }; #endif