blob: 3e40e3f46f3d1b040164b2ddfd3fd06024c94515 [file] [log] [blame]
#ifndef AOS_CONTROLS_QUATERNION_UTILS_H_
#define AOS_CONTROLS_QUATERNION_UTILS_H_
#include "Eigen/Dense"
#include "Eigen/Geometry"
#include "absl/log/check.h"
#include "absl/log/log.h"
namespace frc971::controls {
// Function to compute the quaternion average of a bunch of quaternions. Each
// column in the input matrix is a quaternion (optionally scaled by it's
// weight).
template <int SM>
inline Eigen::Matrix<double, 4, 1> QuaternionMean(
Eigen::Matrix<double, 4, SM> input) {
// Algorithm to compute the average of a bunch of quaternions:
// http://www.acsu.buffalo.edu/~johnc/ave_quat07.pdf
Eigen::Matrix<double, 4, 4> m = input * input.transpose();
Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>> solver;
solver.compute(m);
Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>>::EigenvectorsType
eigenvectors = solver.eigenvectors();
Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>>::EigenvalueType eigenvalues =
solver.eigenvalues();
int max_index = 0;
double max_eigenvalue = 0.0;
for (int i = 0; i < 4; ++i) {
const double eigenvalue = std::abs(eigenvalues(i, 0));
if (eigenvalue > max_eigenvalue) {
max_eigenvalue = eigenvalue;
max_index = i;
}
}
// Assume that there shouldn't be any imaginary components to the eigenvector.
// I can't prove this is true, but everyone else seems to assume it...
// TODO(james): Handle this more rigorously.
for (int i = 0; i < 4; ++i) {
CHECK_LT(eigenvectors(i, max_index).imag(), 1e-4)
<< eigenvectors(i, max_index);
}
return eigenvectors.col(max_index).real().normalized();
}
// Converts from a quaternion to a rotation vector, where the rotation vector's
// direction represents the axis to rotate around and its magnitude represents
// the number of radians to rotate.
Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
const Eigen::Matrix<double, 4, 1> &X);
inline Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
const Eigen::Quaternion<double> &X) {
return ToRotationVectorFromQuaternion(X.coeffs());
}
// Converts from a rotation vector to a quaternion. If you supply max_angle_cap,
// then the rotation vector's magnitude will be clipped to be no more than
// max_angle_cap before being converted to a quaternion.
Eigen::Matrix<double, 4, 1> ToQuaternionFromRotationVector(
const Eigen::Matrix<double, 3, 1> &X,
const double max_angle_cap = std::numeric_limits<double>::infinity());
// Creates a rotational velocity vector to be integrated.
//
// omega is the rotational velocity vector in body coordinates.
// q is a matrix with the compononents of the quaternion in it.
//
// Returns dq / dt
inline Eigen::Vector4d QuaternionDerivative(Eigen::Vector3d omega,
const Eigen::Vector4d &q_matrix) {
// See https://www.ashwinnarayan.com/post/how-to-integrate-quaternions/ for
// another resource on quaternion integration and derivitives.
Eigen::Quaternion<double> q(q_matrix);
Eigen::Quaternion<double> omega_q;
omega_q.w() = 0.0;
omega_q.vec() = 0.5 * omega;
Eigen::Quaternion<double> deriv = q * omega_q;
return deriv.coeffs();
}
// d QuaternionDerivative / d omega
Eigen::Matrix<double, 4, 3> QuaternionDerivativeDerivitive(
const Eigen::Vector4d &q_matrix);
inline Eigen::Matrix<double, 4, 3> QuaternionDerivativeDerivitive(
const Eigen::Quaternion<double> &q) {
return QuaternionDerivativeDerivitive(q.coeffs());
}
} // namespace frc971::controls
#endif // AOS_CONTROLS_QUATERNION_UTILS_H_