blob: e96f5f10f56fa3bf4a303b0d3fc43991810c3aac [file] [log] [blame]
Brian Silvermandac0a4b2020-06-23 17:03:09 -07001#ifndef AOS_CONTROLS_QUATERNION_UTILS_H_
2#define AOS_CONTROLS_QUATERNION_UTILS_H_
3
4#include "Eigen/Dense"
5#include "Eigen/Geometry"
6#include "glog/logging.h"
7
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -08008namespace frc971::controls {
Brian Silvermandac0a4b2020-06-23 17:03:09 -07009
10// Function to compute the quaternion average of a bunch of quaternions. Each
11// column in the input matrix is a quaternion (optionally scaled by it's
12// weight).
13template <int SM>
14inline Eigen::Matrix<double, 4, 1> QuaternionMean(
15 Eigen::Matrix<double, 4, SM> input) {
16 // Algorithm to compute the average of a bunch of quaternions:
17 // http://www.acsu.buffalo.edu/~johnc/ave_quat07.pdf
18
19 Eigen::Matrix<double, 4, 4> m = input * input.transpose();
20
21 Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>> solver;
22 solver.compute(m);
23
24 Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>>::EigenvectorsType
25 eigenvectors = solver.eigenvectors();
26 Eigen::EigenSolver<Eigen::Matrix<double, 4, 4>>::EigenvalueType eigenvalues =
27 solver.eigenvalues();
28
29 int max_index = 0;
30 double max_eigenvalue = 0.0;
31 for (int i = 0; i < 4; ++i) {
32 const double eigenvalue = std::abs(eigenvalues(i, 0));
33 if (eigenvalue > max_eigenvalue) {
34 max_eigenvalue = eigenvalue;
35 max_index = i;
36 }
37 }
38
39 // Assume that there shouldn't be any imaginary components to the eigenvector.
40 // I can't prove this is true, but everyone else seems to assume it...
41 // TODO(james): Handle this more rigorously.
42 for (int i = 0; i < 4; ++i) {
43 CHECK_LT(eigenvectors(i, max_index).imag(), 1e-4)
44 << eigenvectors(i, max_index);
45 }
46 return eigenvectors.col(max_index).real().normalized();
47}
48
49// Converts from a quaternion to a rotation vector, where the rotation vector's
50// direction represents the axis to rotate around and its magnitude represents
51// the number of radians to rotate.
52Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
53 const Eigen::Matrix<double, 4, 1> &X);
54
55inline Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
56 const Eigen::Quaternion<double> &X) {
57 return ToRotationVectorFromQuaternion(X.coeffs());
58}
59
60// Converts from a rotation vector to a quaternion. If you supply max_angle_cap,
61// then the rotation vector's magnitude will be clipped to be no more than
62// max_angle_cap before being converted to a quaternion.
63Eigen::Matrix<double, 4, 1> ToQuaternionFromRotationVector(
64 const Eigen::Matrix<double, 3, 1> &X,
65 const double max_angle_cap = std::numeric_limits<double>::infinity());
66
milind-ue53bf552021-12-11 14:42:00 -080067// Creates a rotational velocity vector to be integrated.
68//
69// omega is the rotational velocity vector in body coordinates.
70// q is a matrix with the compononents of the quaternion in it.
71//
72// Returns dq / dt
73inline Eigen::Vector4d QuaternionDerivative(Eigen::Vector3d omega,
74 const Eigen::Vector4d &q_matrix) {
75 // See https://www.ashwinnarayan.com/post/how-to-integrate-quaternions/ for
76 // another resource on quaternion integration and derivitives.
77 Eigen::Quaternion<double> q(q_matrix);
78
79 Eigen::Quaternion<double> omega_q;
80 omega_q.w() = 0.0;
81 omega_q.vec() = 0.5 * omega;
82
83 Eigen::Quaternion<double> deriv = q * omega_q;
84 return deriv.coeffs();
85}
86
87// d QuaternionDerivative / d omega
88Eigen::Matrix<double, 4, 3> QuaternionDerivativeDerivitive(
89 const Eigen::Vector4d &q_matrix);
90inline Eigen::Matrix<double, 4, 3> QuaternionDerivativeDerivitive(
91 const Eigen::Quaternion<double> &q) {
92 return QuaternionDerivativeDerivitive(q.coeffs());
93}
94
Stephan Pleinesd99b1ee2024-02-02 20:56:44 -080095} // namespace frc971::controls
Brian Silvermandac0a4b2020-06-23 17:03:09 -070096
97#endif // AOS_CONTROLS_QUATERNION_UTILS_H_