Skip to content

File rotation.hpp

File List > Ennui > math > rotation.hpp

Go to the documentation of this file

#pragma once

#include <math.h>

#include <Eigen/Dense>

#include "ennui_types.hpp"

namespace ennui {
namespace math {

inline Matrix3x3 R3_to_so3(const Vector3 &x) {
  Matrix3x3 R{                       //
              {0.00, -x[2], +x[1]},  //
              {+x[2], 0.00, -x[0]},  //
              {-x[1], +x[0], 0.00}};
  return R;
}

inline Matrix3x3 euler_finite_rotation(
    const Eigen::Ref<const Matrix3x3> &OmegaNormed, double xnorm) {
  return Matrix3x3::Identity() + sin(xnorm) * OmegaNormed +
         (1 - cos(xnorm)) * (OmegaNormed * OmegaNormed);
}

inline Matrix3x3 euler_finite_rotation_approx(
    const Eigen::Ref<const Matrix3x3> &Omega, double xnorm) {
  return Matrix3x3::Identity() + (1 - xnorm * xnorm / 6) * Omega +
         (0.5 - xnorm * xnorm / 24) * (Omega * Omega);
}

inline Matrix3x3 R3_to_SO3(ConstRefVector3 &x) {
  const double xnorm = sqrt(x.dot(x));
  Matrix3x3 Omega;
  Matrix3x3 rslt = Matrix3x3::Identity();
  if (xnorm > 1e-6) {
    // Standard form (Eq. 5.73)
    Omega.noalias() = R3_to_so3(x / xnorm);
    rslt.noalias() = euler_finite_rotation(Omega, xnorm);
  } else {
    // Small angle approximation (Eq. 5.72), without sinc
    Omega.noalias() = R3_to_so3(x);
    rslt.noalias() = euler_finite_rotation_approx(Omega, xnorm);
  }
  return rslt;
}

inline Matrix3x3 R4_to_SO3(const Vector4 &q) {
  // Get indices for state components
  const auto vect_idx = Eigen::seqN(1, 3);

  const double qwt = 1.0 / sqrt(q.dot(q));
  const double qs = qwt * q[0];
  const Vector3 qv = qwt * q(vect_idx);

  // Eq. 117 [sola_quaternion_2017]
  return (qs * qs - qv.dot(qv)) * Matrix3x3::Identity() +
         2 * qv * qv.transpose() + 2 * qs * R3_to_so3(qv);
}

inline Matrix3x3 normalize_SO3(const Matrix3x3 R) {
  // Perform SVD
  Eigen::JacobiSVD<Matrix3x3> svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV);

  // Reconstruct the normalized rotation matrix
  return svd.matrixU() * svd.matrixV().transpose();
}

inline Matrix3x3 normalize_SO3_Groves(const Matrix3x3 R) {
  const double d01 = R.row(0).dot(R.row(1));
  const double d02 = R.row(0).dot(R.row(2));
  const double d12 = R.row(1).dot(R.row(2));
  Matrix3x3 rslt;
  rslt.row(0) = R.row(0) - 0.5 * d01 * R.row(1) - 0.5 * d02 * R.row(2);
  rslt.row(1) = -0.5 * d01 * R.row(0) + R.row(1) - 0.5 * d12 * R.row(2);
  rslt.row(2) = -0.5 * d02 * R.row(0) - 0.5 * d12 * R.row(1) + R.row(2);
  for (int i = 0; i < 3; i++) {
    rslt.row(i) = rslt.row(i).stableNormalized();
  }
  return rslt;
}

inline Matrix3x3 mean_attitude_update(
    const Eigen::Ref<const Matrix3x3> &alpha_cross, double alpha_norm) {
  return Matrix3x3::Identity() +
         ((1 - cos(alpha_norm)) / (alpha_norm * alpha_norm)) * alpha_cross +
         (1 - sin(alpha_norm) / alpha_norm) / (alpha_norm * alpha_norm) *
             alpha_cross * alpha_cross;
}

inline Matrix3x3 mean_attitude_update_approx(
    const Eigen::Ref<const Matrix3x3> &alpha_cross, double alpha_norm) {
  return Matrix3x3::Identity() +
         (0.5 - alpha_norm * alpha_norm / 24.0) * alpha_cross +
         (1.0 / 6.0 - alpha_norm * alpha_norm / 120.0) * alpha_cross *
             alpha_cross;
}

}  // namespace math
}  // namespace ennui