blob: 2c000bf16d1621135a15ae5c3a851fd4d39acc04 [file] [log] [blame]
Austin Schuhc2b08772018-12-19 18:05:06 +11001#ifndef FRC971_CONTROL_LOOPS_DRIVETRAIN_SPLINE_H_
2#define FRC971_CONTROL_LOOPS_DRIVETRAIN_SPLINE_H_
3
4#include "Eigen/Dense"
5
Austin Schuhf49b4e32019-01-13 17:26:58 -08006#include "frc971/control_loops/binomial.h"
7
Austin Schuhc2b08772018-12-19 18:05:06 +11008namespace frc971 {
9namespace control_loops {
10namespace drivetrain {
11
12// Class to hold a spline as a function of alpha. Alpha can only range between
13// 0.0 and 1.0.
Austin Schuhf49b4e32019-01-13 17:26:58 -080014template <int N>
15class NSpline {
Austin Schuhc2b08772018-12-19 18:05:06 +110016 public:
17 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
18
Alex Perry70921502019-01-27 17:31:46 -080019 // Computes the characteristic matrix of a given spline order. This is an
20 // upper triangular matrix rather than lower because our splines are
21 // represented as rows rather than columns.
22 // Each row represents the impact of each point with increasing powers of
23 // alpha. Row i, column j contains the effect point i has with the j'th power
24 // of alpha.
25 static ::Eigen::Matrix<double, N, N> SplineMatrix() {
26 ::Eigen::Matrix<double, N, N> matrix =
27 ::Eigen::Matrix<double, N, N>::Zero();
28
Austin Schuhf49b4e32019-01-13 17:26:58 -080029 for (int i = 0; i < N; ++i) {
30 // Binomial(N - 1, i) * (1 - t) ^ (N - i - 1) * (t ^ i) * P[i]
31 const double binomial = Binomial(N - 1, i);
32 const int one_minus_t_power = N - i - 1;
Alex Perry70921502019-01-27 17:31:46 -080033
34 // Then iterate over the powers of t and add the pieces to the matrix.
Austin Schuhf49b4e32019-01-13 17:26:58 -080035 for (int j = i; j < N; ++j) {
Alex Perry70921502019-01-27 17:31:46 -080036 // j is the power of t we are placing in the matrix.
Austin Schuhf49b4e32019-01-13 17:26:58 -080037 // k is the power of t in the (1 - t) expression that we need to
38 // evaluate.
39 const int k = j - i;
40 const double tscalar =
41 binomial * Binomial(one_minus_t_power, k) * ::std::pow(-1.0, k);
Alex Perry70921502019-01-27 17:31:46 -080042 matrix(i, j) = tscalar;
Austin Schuhf49b4e32019-01-13 17:26:58 -080043 }
44 }
Alex Perry70921502019-01-27 17:31:46 -080045 return matrix;
46 }
47
48 // Computes the matrix to multiply by [1, a, a^2, ...] to evaluate the spline.
49 template <int D>
50 static ::Eigen::Matrix<double, 2, N - D> SplinePolynomial(
51 const ::Eigen::Matrix<double, 2, N> &control_points) {
52 // We use rows for the spline, so the multiplication looks "backwards"
53 ::Eigen::Matrix<double, 2, N> polynomial = control_points * SplineMatrix();
Austin Schuhf49b4e32019-01-13 17:26:58 -080054
55 // Now, compute the derivative requested.
56 for (int i = D; i < N; ++i) {
57 // Start with t^i, and multiply i, i-1, i-2 until you have done this
58 // Derivative times.
59 double scalar = 1.0;
60 for (int j = i; j > i - D; --j) {
61 scalar *= j;
62 }
63 polynomial.template block<2, 1>(0, i) =
64 polynomial.template block<2, 1>(0, i) * scalar;
65 }
66 return polynomial.template block<2, N - D>(0, D);
67 }
68
69 // Computes an order M-1 polynomial matrix in alpha. [1, alpha, alpha^2, ...]
70 template <int M>
71 static ::Eigen::Matrix<double, M, 1> AlphaPolynomial(const double alpha) {
72 ::Eigen::Matrix<double, M, 1> polynomial =
73 ::Eigen::Matrix<double, M, 1>::Zero();
74 polynomial(0) = 1.0;
75 for (int i = 1; i < M; ++i) {
76 polynomial(i) = polynomial(i - 1) * alpha;
77 }
78 return polynomial;
79 }
80
Austin Schuhc2b08772018-12-19 18:05:06 +110081 // Constructs a spline. control_points is a matrix of start, control1,
Austin Schuhf49b4e32019-01-13 17:26:58 -080082 // control2, ..., end.
83 NSpline(::Eigen::Matrix<double, 2, N> control_points)
84 : control_points_(control_points),
85 spline_polynomial_(SplinePolynomial<0>(control_points_)),
86 dspline_polynomial_(SplinePolynomial<1>(control_points_)),
87 ddspline_polynomial_(SplinePolynomial<2>(control_points_)),
88 dddspline_polynomial_(SplinePolynomial<3>(control_points_)) {}
Austin Schuhc2b08772018-12-19 18:05:06 +110089
90 // Returns the xy coordiate of the spline for a given alpha.
91 ::Eigen::Matrix<double, 2, 1> Point(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080092 return spline_polynomial_ * AlphaPolynomial<N>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110093 }
94
95 // Returns the dspline/dalpha for a given alpha.
96 ::Eigen::Matrix<double, 2, 1> DPoint(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080097 return dspline_polynomial_ * AlphaPolynomial<N - 1>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110098 }
99
100 // Returns the d^2spline/dalpha^2 for a given alpha.
101 ::Eigen::Matrix<double, 2, 1> DDPoint(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -0800102 return ddspline_polynomial_ * AlphaPolynomial<N - 2>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +1100103 }
104
105 // Returns the d^3spline/dalpha^3 for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800106 ::Eigen::Matrix<double, 2, 1> DDDPoint(double alpha) const {
107 return dddspline_polynomial_ * AlphaPolynomial<N - 3>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +1100108 }
109
110 // Returns theta for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800111 double Theta(double alpha) const {
112 const ::Eigen::Matrix<double, 2, 1> dp = DPoint(alpha);
113 return ::std::atan2(dp(1), dp(0));
114 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100115
116 // Returns dtheta/dalpha for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800117 double DTheta(double alpha) const {
118 const ::Eigen::Matrix<double, N - 1, 1> alpha_polynomial =
119 AlphaPolynomial<N - 1>(alpha);
120
121 const ::Eigen::Matrix<double, 2, 1> dp =
122 dspline_polynomial_ * alpha_polynomial;
123 const ::Eigen::Matrix<double, 2, 1> ddp =
124 ddspline_polynomial_ * alpha_polynomial.template block<N - 2, 1>(0, 0);
125 const double dx = dp(0);
126 const double dy = dp(1);
127
128 const double ddx = ddp(0);
129 const double ddy = ddp(1);
130
131 return 1.0 / (::std::pow(dx, 2) + ::std::pow(dy, 2)) *
132 (dx * ddy - dy * ddx);
133 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100134
135 // Returns d^2 theta/dalpha^2 for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800136 double DDTheta(double alpha) const {
137 const ::Eigen::Matrix<double, N - 1, 1> alpha_polynomial =
138 AlphaPolynomial<N - 1>(alpha);
139 const ::Eigen::Matrix<double, 2, 1> dp =
140 dspline_polynomial_ * alpha_polynomial;
141 const ::Eigen::Matrix<double, 2, 1> ddp =
142 ddspline_polynomial_ * alpha_polynomial.template block<N - 2, 1>(0, 0);
143 const ::Eigen::Matrix<double, 2, 1> dddp =
144 dddspline_polynomial_ * alpha_polynomial.template block<N - 3, 1>(0, 0);
145 const double dx = dp(0);
146 const double dy = dp(1);
147
148 const double ddx = ddp(0);
149 const double ddy = ddp(1);
150
151 const double dddx = dddp(0);
152 const double dddy = dddp(1);
153
154 const double magdxy2 = ::std::pow(dx, 2) + ::std::pow(dy, 2);
155
156 return -1.0 / (::std::pow(magdxy2, 2)) * (dx * ddy - dy * ddx) * 2.0 *
157 (dy * ddy + dx * ddx) +
158 1.0 / magdxy2 * (dx * dddy - dy * dddx);
159 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100160
Austin Schuhb23f5252019-01-13 21:16:23 -0800161 const ::Eigen::Matrix<double, 2, N> &control_points() const {
162 return control_points_;
163 }
164
Austin Schuhc2b08772018-12-19 18:05:06 +1100165 private:
Austin Schuhf49b4e32019-01-13 17:26:58 -0800166 const ::Eigen::Matrix<double, 2, N> control_points_;
167
168 // Each of these polynomials gets multiplied by [x^(n-1), x^(n-2), ..., x, 1]
169 // depending on the size of the polynomial.
170 const ::Eigen::Matrix<double, 2, N> spline_polynomial_;
171 const ::Eigen::Matrix<double, 2, N - 1> dspline_polynomial_;
172 const ::Eigen::Matrix<double, 2, N - 2> ddspline_polynomial_;
173 const ::Eigen::Matrix<double, 2, N - 3> dddspline_polynomial_;
Austin Schuhc2b08772018-12-19 18:05:06 +1100174};
175
Austin Schuh280996e2019-01-19 17:43:37 -0800176typedef NSpline<6> Spline;
Austin Schuhf49b4e32019-01-13 17:26:58 -0800177
Austin Schuhb23f5252019-01-13 21:16:23 -0800178// Converts a 4 control point spline into
179::Eigen::Matrix<double, 2, 6> Spline4To6(
180 const ::Eigen::Matrix<double, 2, 4> &control_points);
181
Austin Schuh280996e2019-01-19 17:43:37 -0800182template <int N>
183::Eigen::Matrix<double, 2, N> TranslateSpline(
184 const ::Eigen::Matrix<double, 2, N> &control_points,
185 const ::Eigen::Matrix<double, 2, 1> translation) {
186 ::Eigen::Matrix<double, 2, N> ans = control_points;
187 for (size_t i = 0; i < N; ++i) {
188 ans.template block<2, 1>(0, i) += translation;
189 }
190 return ans;
191}
192
Austin Schuhc2b08772018-12-19 18:05:06 +1100193} // namespace drivetrain
194} // namespace control_loops
195} // namespace frc971
196
197#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_SPLINE_H_