blob: f60eea58b4ea97f05e6c7d5722b3ba57d2ce58fd [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.
14// TODO(austin): Need to be able to represent splines which have more than 2
15// control points at some point. Or splines chained together. This is close
16// enough for now.
Austin Schuhf49b4e32019-01-13 17:26:58 -080017template <int N>
18class NSpline {
Austin Schuhc2b08772018-12-19 18:05:06 +110019 public:
20 EIGEN_MAKE_ALIGNED_OPERATOR_NEW
21
Austin Schuhf49b4e32019-01-13 17:26:58 -080022 // Computes the matrix to multiply by [1, a, a^2, ...] to evaluate the spline.
23 template <int D>
24 static ::Eigen::Matrix<double, 2, N - D> SplinePolynomial(
25 const ::Eigen::Matrix<double, 2, N> &control_points) {
26 ::Eigen::Matrix<double, 2, N> polynomial =
27 ::Eigen::Matrix<double, 2, N>::Zero();
28 // Compute this in pieces. Start by the coefficients of each control point.
29 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;
33 // Then iterate over the powers of t and add the pieces to the polynomial
34 // matrix.
35 for (int j = i; j < N; ++j) {
36 // j is the power of t we are placing in the polynomial matrix.
37 // 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);
42 polynomial.template block<2, 1>(0, j) +=
43 control_points.template block<2, 1>(0, i) * tscalar;
44 }
45 }
46
47 // Now, compute the derivative requested.
48 for (int i = D; i < N; ++i) {
49 // Start with t^i, and multiply i, i-1, i-2 until you have done this
50 // Derivative times.
51 double scalar = 1.0;
52 for (int j = i; j > i - D; --j) {
53 scalar *= j;
54 }
55 polynomial.template block<2, 1>(0, i) =
56 polynomial.template block<2, 1>(0, i) * scalar;
57 }
58 return polynomial.template block<2, N - D>(0, D);
59 }
60
61 // Computes an order M-1 polynomial matrix in alpha. [1, alpha, alpha^2, ...]
62 template <int M>
63 static ::Eigen::Matrix<double, M, 1> AlphaPolynomial(const double alpha) {
64 ::Eigen::Matrix<double, M, 1> polynomial =
65 ::Eigen::Matrix<double, M, 1>::Zero();
66 polynomial(0) = 1.0;
67 for (int i = 1; i < M; ++i) {
68 polynomial(i) = polynomial(i - 1) * alpha;
69 }
70 return polynomial;
71 }
72
Austin Schuhc2b08772018-12-19 18:05:06 +110073 // Constructs a spline. control_points is a matrix of start, control1,
Austin Schuhf49b4e32019-01-13 17:26:58 -080074 // control2, ..., end.
75 NSpline(::Eigen::Matrix<double, 2, N> control_points)
76 : control_points_(control_points),
77 spline_polynomial_(SplinePolynomial<0>(control_points_)),
78 dspline_polynomial_(SplinePolynomial<1>(control_points_)),
79 ddspline_polynomial_(SplinePolynomial<2>(control_points_)),
80 dddspline_polynomial_(SplinePolynomial<3>(control_points_)) {}
Austin Schuhc2b08772018-12-19 18:05:06 +110081
82 // Returns the xy coordiate of the spline for a given alpha.
83 ::Eigen::Matrix<double, 2, 1> Point(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080084 return spline_polynomial_ * AlphaPolynomial<N>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110085 }
86
87 // Returns the dspline/dalpha for a given alpha.
88 ::Eigen::Matrix<double, 2, 1> DPoint(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080089 return dspline_polynomial_ * AlphaPolynomial<N - 1>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110090 }
91
92 // Returns the d^2spline/dalpha^2 for a given alpha.
93 ::Eigen::Matrix<double, 2, 1> DDPoint(double alpha) const {
Austin Schuhf49b4e32019-01-13 17:26:58 -080094 return ddspline_polynomial_ * AlphaPolynomial<N - 2>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110095 }
96
97 // Returns the d^3spline/dalpha^3 for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -080098 ::Eigen::Matrix<double, 2, 1> DDDPoint(double alpha) const {
99 return dddspline_polynomial_ * AlphaPolynomial<N - 3>(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +1100100 }
101
102 // Returns theta for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800103 double Theta(double alpha) const {
104 const ::Eigen::Matrix<double, 2, 1> dp = DPoint(alpha);
105 return ::std::atan2(dp(1), dp(0));
106 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100107
108 // Returns dtheta/dalpha for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800109 double DTheta(double alpha) const {
110 const ::Eigen::Matrix<double, N - 1, 1> alpha_polynomial =
111 AlphaPolynomial<N - 1>(alpha);
112
113 const ::Eigen::Matrix<double, 2, 1> dp =
114 dspline_polynomial_ * alpha_polynomial;
115 const ::Eigen::Matrix<double, 2, 1> ddp =
116 ddspline_polynomial_ * alpha_polynomial.template block<N - 2, 1>(0, 0);
117 const double dx = dp(0);
118 const double dy = dp(1);
119
120 const double ddx = ddp(0);
121 const double ddy = ddp(1);
122
123 return 1.0 / (::std::pow(dx, 2) + ::std::pow(dy, 2)) *
124 (dx * ddy - dy * ddx);
125 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100126
127 // Returns d^2 theta/dalpha^2 for a given alpha.
Austin Schuhf49b4e32019-01-13 17:26:58 -0800128 double DDTheta(double alpha) const {
129 const ::Eigen::Matrix<double, N - 1, 1> alpha_polynomial =
130 AlphaPolynomial<N - 1>(alpha);
131 const ::Eigen::Matrix<double, 2, 1> dp =
132 dspline_polynomial_ * alpha_polynomial;
133 const ::Eigen::Matrix<double, 2, 1> ddp =
134 ddspline_polynomial_ * alpha_polynomial.template block<N - 2, 1>(0, 0);
135 const ::Eigen::Matrix<double, 2, 1> dddp =
136 dddspline_polynomial_ * alpha_polynomial.template block<N - 3, 1>(0, 0);
137 const double dx = dp(0);
138 const double dy = dp(1);
139
140 const double ddx = ddp(0);
141 const double ddy = ddp(1);
142
143 const double dddx = dddp(0);
144 const double dddy = dddp(1);
145
146 const double magdxy2 = ::std::pow(dx, 2) + ::std::pow(dy, 2);
147
148 return -1.0 / (::std::pow(magdxy2, 2)) * (dx * ddy - dy * ddx) * 2.0 *
149 (dy * ddy + dx * ddx) +
150 1.0 / magdxy2 * (dx * dddy - dy * dddx);
151 }
Austin Schuhc2b08772018-12-19 18:05:06 +1100152
Austin Schuhb23f5252019-01-13 21:16:23 -0800153 const ::Eigen::Matrix<double, 2, N> &control_points() const {
154 return control_points_;
155 }
156
Austin Schuhc2b08772018-12-19 18:05:06 +1100157 private:
Austin Schuhf49b4e32019-01-13 17:26:58 -0800158 const ::Eigen::Matrix<double, 2, N> control_points_;
159
160 // Each of these polynomials gets multiplied by [x^(n-1), x^(n-2), ..., x, 1]
161 // depending on the size of the polynomial.
162 const ::Eigen::Matrix<double, 2, N> spline_polynomial_;
163 const ::Eigen::Matrix<double, 2, N - 1> dspline_polynomial_;
164 const ::Eigen::Matrix<double, 2, N - 2> ddspline_polynomial_;
165 const ::Eigen::Matrix<double, 2, N - 3> dddspline_polynomial_;
Austin Schuhc2b08772018-12-19 18:05:06 +1100166};
167
Austin Schuh280996e2019-01-19 17:43:37 -0800168typedef NSpline<6> Spline;
Austin Schuhf49b4e32019-01-13 17:26:58 -0800169
Austin Schuhb23f5252019-01-13 21:16:23 -0800170// Converts a 4 control point spline into
171::Eigen::Matrix<double, 2, 6> Spline4To6(
172 const ::Eigen::Matrix<double, 2, 4> &control_points);
173
Austin Schuh280996e2019-01-19 17:43:37 -0800174template <int N>
175::Eigen::Matrix<double, 2, N> TranslateSpline(
176 const ::Eigen::Matrix<double, 2, N> &control_points,
177 const ::Eigen::Matrix<double, 2, 1> translation) {
178 ::Eigen::Matrix<double, 2, N> ans = control_points;
179 for (size_t i = 0; i < N; ++i) {
180 ans.template block<2, 1>(0, i) += translation;
181 }
182 return ans;
183}
184
Austin Schuhc2b08772018-12-19 18:05:06 +1100185} // namespace drivetrain
186} // namespace control_loops
187} // namespace frc971
188
189#endif // FRC971_CONTROL_LOOPS_DRIVETRAIN_SPLINE_H_