blob: e5498111144b45e508ee220fbafda2beed0f5fef [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
Austin Schuhf49b4e32019-01-13 17:26:58 -080019 // Computes the matrix to multiply by [1, a, a^2, ...] to evaluate the spline.
20 template <int D>
21 static ::Eigen::Matrix<double, 2, N - D> SplinePolynomial(
22 const ::Eigen::Matrix<double, 2, N> &control_points) {
23 ::Eigen::Matrix<double, 2, N> polynomial =
24 ::Eigen::Matrix<double, 2, N>::Zero();
25 // Compute this in pieces. Start by the coefficients of each control point.
26 for (int i = 0; i < N; ++i) {
27 // Binomial(N - 1, i) * (1 - t) ^ (N - i - 1) * (t ^ i) * P[i]
28 const double binomial = Binomial(N - 1, i);
29 const int one_minus_t_power = N - i - 1;
30 // Then iterate over the powers of t and add the pieces to the polynomial
31 // matrix.
32 for (int j = i; j < N; ++j) {
33 // j is the power of t we are placing in the polynomial matrix.
34 // k is the power of t in the (1 - t) expression that we need to
35 // evaluate.
36 const int k = j - i;
37 const double tscalar =
38 binomial * Binomial(one_minus_t_power, k) * ::std::pow(-1.0, k);
39 polynomial.template block<2, 1>(0, j) +=
40 control_points.template block<2, 1>(0, i) * tscalar;
41 }
42 }
43
44 // Now, compute the derivative requested.
45 for (int i = D; i < N; ++i) {
46 // Start with t^i, and multiply i, i-1, i-2 until you have done this
47 // Derivative times.
48 double scalar = 1.0;
49 for (int j = i; j > i - D; --j) {
50 scalar *= j;
51 }
52 polynomial.template block<2, 1>(0, i) =
53 polynomial.template block<2, 1>(0, i) * scalar;
54 }
55 return polynomial.template block<2, N - D>(0, D);
56 }
57
58 // Computes an order M-1 polynomial matrix in alpha. [1, alpha, alpha^2, ...]
59 template <int M>
60 static ::Eigen::Matrix<double, M, 1> AlphaPolynomial(const double alpha) {
61 ::Eigen::Matrix<double, M, 1> polynomial =
62 ::Eigen::Matrix<double, M, 1>::Zero();
63 polynomial(0) = 1.0;
64 for (int i = 1; i < M; ++i) {
65 polynomial(i) = polynomial(i - 1) * alpha;
66 }
67 return polynomial;
68 }
69
Austin Schuhc2b08772018-12-19 18:05:06 +110070 // Constructs a spline. control_points is a matrix of start, control1,
Austin Schuhf49b4e32019-01-13 17:26:58 -080071 // control2, ..., end.
72 NSpline(::Eigen::Matrix<double, 2, N> control_points)
73 : control_points_(control_points),
74 spline_polynomial_(SplinePolynomial<0>(control_points_)),
75 dspline_polynomial_(SplinePolynomial<1>(control_points_)),
76 ddspline_polynomial_(SplinePolynomial<2>(control_points_)),
77 dddspline_polynomial_(SplinePolynomial<3>(control_points_)) {}
Austin Schuhc2b08772018-12-19 18:05:06 +110078
79 // Returns the xy coordiate of the spline for a given alpha.
80 ::Eigen::Matrix<double, 2, 1> Point(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080081 return spline_polynomial_ * AlphaPolynomial<N>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110082 }
83
84 // Returns the dspline/dalpha for a given alpha.
85 ::Eigen::Matrix<double, 2, 1> DPoint(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080086 return dspline_polynomial_ * AlphaPolynomial<N - 1>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110087 }
88
89 // Returns the d^2spline/dalpha^2 for a given alpha.
90 ::Eigen::Matrix<double, 2, 1> DDPoint(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080091 return ddspline_polynomial_ * AlphaPolynomial<N - 2>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110092 }
93
94 // Returns the d^3spline/dalpha^3 for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -080095 ::Eigen::Matrix<double, 2, 1> DDDPoint(double alpha) const {
96 return dddspline_polynomial_ * AlphaPolynomial<N - 3>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110097 }
98
99 // Returns theta for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800100 double Theta(double alpha) const {
101 const ::Eigen::Matrix<double, 2, 1> dp = DPoint(alpha);
102 return ::std::atan2(dp(1), dp(0));
103 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100104
105 // Returns dtheta/dalpha for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800106 double DTheta(double alpha) const {
107 const ::Eigen::Matrix<double, N - 1, 1> alpha_polynomial =
108 AlphaPolynomial<N - 1>(alpha);
109
110 const ::Eigen::Matrix<double, 2, 1> dp =
111 dspline_polynomial_ * alpha_polynomial;
112 const ::Eigen::Matrix<double, 2, 1> ddp =
113 ddspline_polynomial_ * alpha_polynomial.template block<N - 2, 1>(0, 0);
114 const double dx = dp(0);
115 const double dy = dp(1);
116
117 const double ddx = ddp(0);
118 const double ddy = ddp(1);
119
120 return 1.0 / (::std::pow(dx, 2) + ::std::pow(dy, 2)) *
121 (dx * ddy - dy * ddx);
122 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100123
124 // Returns d^2 theta/dalpha^2 for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800125 double DDTheta(double alpha) const {
126 const ::Eigen::Matrix<double, N - 1, 1> alpha_polynomial =
127 AlphaPolynomial<N - 1>(alpha);
128 const ::Eigen::Matrix<double, 2, 1> dp =
129 dspline_polynomial_ * alpha_polynomial;
130 const ::Eigen::Matrix<double, 2, 1> ddp =
131 ddspline_polynomial_ * alpha_polynomial.template block<N - 2, 1>(0, 0);
132 const ::Eigen::Matrix<double, 2, 1> dddp =
133 dddspline_polynomial_ * alpha_polynomial.template block<N - 3, 1>(0, 0);
134 const double dx = dp(0);
135 const double dy = dp(1);
136
137 const double ddx = ddp(0);
138 const double ddy = ddp(1);
139
140 const double dddx = dddp(0);
141 const double dddy = dddp(1);
142
143 const double magdxy2 = ::std::pow(dx, 2) + ::std::pow(dy, 2);
144
145 return -1.0 / (::std::pow(magdxy2, 2)) * (dx * ddy - dy * ddx) * 2.0 *
146 (dy * ddy + dx * ddx) +
147 1.0 / magdxy2 * (dx * dddy - dy * dddx);
148 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100149
Austin Schuhb23f5252019-01-13 21:16:23 -0800150 const ::Eigen::Matrix<double, 2, N> &control_points() const {
151 return control_points_;
152 }
153
Austin Schuhc2b08772018-12-19 18:05:06 +1100154 private:
Austin Schuhf49b4e32019-01-13 17:26:58 -0800155 const ::Eigen::Matrix<double, 2, N> control_points_;
156
157 // Each of these polynomials gets multiplied by [x^(n-1), x^(n-2), ..., x, 1]
158 // depending on the size of the polynomial.
159 const ::Eigen::Matrix<double, 2, N> spline_polynomial_;
160 const ::Eigen::Matrix<double, 2, N - 1> dspline_polynomial_;
161 const ::Eigen::Matrix<double, 2, N - 2> ddspline_polynomial_;
162 const ::Eigen::Matrix<double, 2, N - 3> dddspline_polynomial_;
Austin Schuhc2b08772018-12-19 18:05:06 +1100163};
164
Austin Schuh280996e2019-01-19 17:43:37 -0800165typedef NSpline<6> Spline;
Austin Schuhf49b4e32019-01-13 17:26:58 -0800166
Austin Schuhb23f5252019-01-13 21:16:23 -0800167// Converts a 4 control point spline into
168::Eigen::Matrix<double, 2, 6> Spline4To6(
169 const ::Eigen::Matrix<double, 2, 4> &control_points);
170
Austin Schuh280996e2019-01-19 17:43:37 -0800171template <int N>
172::Eigen::Matrix<double, 2, N> TranslateSpline(
173 const ::Eigen::Matrix<double, 2, N> &control_points,
174 const ::Eigen::Matrix<double, 2, 1> translation) {
175 ::Eigen::Matrix<double, 2, N> ans = control_points;
176 for (size_t i = 0; i < N; ++i) {
177 ans.template block<2, 1>(0, i) += translation;
178 }
179 return ans;
180}
181
Austin Schuhc2b08772018-12-19 18:05:06 +1100182} // namespace drivetrain
183} // namespace control_loops
184} // namespace frc971
185
186#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_SPLINE_H_