blob: 088c699909dc8aee347c7395b456a0024a739d4f [file] [log] [blame]
Brian Silvermandac0a4b2020-06-23 17:03:09 -07001#include "aos/controls/quaternion_utils.h"
2
3#include "Eigen/Dense"
4#include "Eigen/Geometry"
5
6namespace aos {
7namespace controls {
8
9Eigen::Matrix<double, 4, 1> ToQuaternionFromRotationVector(
10 const Eigen::Matrix<double, 3, 1> &X, const double max_angle_cap) {
11 const double unclipped_angle = X.norm();
12 const double angle_scalar =
13 (unclipped_angle > max_angle_cap) ? max_angle_cap / unclipped_angle : 1.0;
14 const double angle = unclipped_angle * angle_scalar;
15 const double half_angle = angle * 0.5;
16
17 const double half_angle_squared = half_angle * half_angle;
18
19 // sin(x)/x = 1
20 double sinx_x = 1.0;
21
22 // - x^2/3!
23 double value = half_angle_squared / 6.0;
24 sinx_x -= value;
25
26 // + x^4/5!
27 value = value * half_angle_squared / 20.0;
28 sinx_x += value;
29
30 // - x^6/7!
31 value = value * half_angle_squared / (6.0 * 7.0);
32 sinx_x -= value;
33
34 // + x^8/9!
35 value = value * half_angle_squared / (8.0 * 9.0);
36 sinx_x += value;
37
38 // - x^10/11!
39 value = value * half_angle_squared / (10.0 * 11.0);
40 sinx_x -= value;
41
42 // + x^12/13!
43 value = value * half_angle_squared / (12.0 * 13.0);
44 sinx_x += value;
45
46 // - x^14/15!
47 value = value * half_angle_squared / (14.0 * 15.0);
48 sinx_x -= value;
49
50 // + x^16/17!
51 value = value * half_angle_squared / (16.0 * 17.0);
52 sinx_x += value;
53
54 // To plot the residual in matplotlib, run:
55 // import numpy
56 // import scipy
57 // from matplotlib import pyplot
58 // x = numpy.arange(-numpy.pi, numpy.pi, 0.01)
59 // pyplot.plot(x, 1 - x**2 / scipy.misc.factorial(3) +
60 // x**4 / scipy.misc.factorial(5) -
61 // x**6 / scipy.misc.factorial(7) +
62 // x**8 / scipy.misc.factorial(9) -
63 // x ** 10 / scipy.misc.factorial(11) +
64 // x ** 12 / scipy.misc.factorial(13) -
65 // x ** 14 / scipy.misc.factorial(15) +
66 // x ** 16 / scipy.misc.factorial(17) -
67 // numpy.sin(x) / x)
68
69 const double scalar = sinx_x * 0.5;
70
71 Eigen::Matrix<double, 4, 1> result;
72 result.block<3, 1>(0, 0) = X * scalar * angle_scalar;
73 result(3, 0) = std::cos(half_angle);
74 return result;
75}
76
77inline Eigen::Matrix<double, 4, 1> MaybeFlipX(
78 const Eigen::Matrix<double, 4, 1> &X) {
79 if (X(3, 0) < 0.0) {
80 return -X;
81 } else {
82 return X;
83 }
84}
85
86Eigen::Matrix<double, 3, 1> ToRotationVectorFromQuaternion(
87 const Eigen::Matrix<double, 4, 1> &X) {
88 // TODO(austin): Verify we still need it.
89 const Eigen::Matrix<double, 4, 1> corrected_X = MaybeFlipX(X);
90 const double half_angle =
91 std::atan2(corrected_X.block<3, 1>(0, 0).norm(), corrected_X(3, 0));
92
93 const double half_angle_squared = half_angle * half_angle;
94
95 // TODO(austin): We are doing a division at the end of this. Do the taylor
96 // series expansion of x/sin(x) instead to avoid this.
97
98 // sin(x)/x = 1
99 double sinx_x = 1.0;
100
101 // - x^2/3!
102 double value = half_angle_squared / 6.0;
103 sinx_x -= value;
104
105 // + x^4/5!
106 value = value * half_angle_squared / 20.0;
107 sinx_x += value;
108
109 // - x^6/7!
110 value = value * half_angle_squared / (6.0 * 7.0);
111 sinx_x -= value;
112
113 // + x^8/9!
114 value = value * half_angle_squared / (8.0 * 9.0);
115 sinx_x += value;
116
117 // - x^10/11!
118 value = value * half_angle_squared / (10.0 * 11.0);
119 sinx_x -= value;
120
121 // + x^12/13!
122 value = value * half_angle_squared / (12.0 * 13.0);
123 sinx_x += value;
124
125 // - x^14/15!
126 value = value * half_angle_squared / (14.0 * 15.0);
127 sinx_x -= value;
128
129 // + x^16/17!
130 value = value * half_angle_squared / (16.0 * 17.0);
131 sinx_x += value;
132
133 const double scalar = 2.0 / sinx_x;
134
135 return corrected_X.block<3, 1>(0, 0) * scalar;
136}
137
138} // namespace controls
139} // namespace aos