blob: ca4d128a0446c86d8153824241486a20bb1f3443 [file] [log] [blame]
Austin Schuhc2b08772018-12-19 18:05:06 +11001#include "frc971/control_loops/drivetrain/spline.h"
2
3#include <vector>
4
5#include "gflags/gflags.h"
6#include "gtest/gtest.h"
James Kuszmaule32fa932021-05-11 21:38:16 -07007#include "frc971/analysis/in_process_plotter.h"
Austin Schuhc2b08772018-12-19 18:05:06 +11008
9DEFINE_bool(plot, false, "If true, plot");
10
11namespace frc971 {
12namespace control_loops {
13namespace drivetrain {
14namespace testing {
15
James Kuszmaule32fa932021-05-11 21:38:16 -070016std::string TestName() {
17 const ::testing::TestInfo *info =
18 ::testing::UnitTest::GetInstance()->current_test_info();
19 return std::string(info->test_case_name()) + "." +
20 std::string(info->name());
21}
22
Austin Schuhc2b08772018-12-19 18:05:06 +110023// Test fixture with a spline from 0, 0 to 1, 1
24class SplineTest : public ::testing::Test {
James Kuszmaule32fa932021-05-11 21:38:16 -070025 public:
26 static void SetUpTestSuite() {
27 if (FLAGS_plot) {
28 plotter_ = std::make_unique<analysis::Plotter>();
29 }
30 }
31
32 static void TearDownTestSuite() {
33 if (FLAGS_plot) {
34 plotter_->Spin();
35 }
36 }
Austin Schuhc2b08772018-12-19 18:05:06 +110037 protected:
38 SplineTest()
Austin Schuhb23f5252019-01-13 21:16:23 -080039 : control_points_((::Eigen::Matrix<double, 2, 4>() << 0.0, 0.5, 0.5, 1.0,
40 0.0, 0.0, 1.0, 1.0)
41 .finished()),
42 spline4_(control_points_),
James Kuszmaule32fa932021-05-11 21:38:16 -070043 spline6_(Spline4To6(control_points_)) {
44 if (FLAGS_plot) {
45 CHECK(plotter_);
46 plotter_->Title(TestName());
47 }
48 }
49 ~SplineTest() {}
50
51 void TearDown() override {
52 if (FLAGS_plot) {
53 plotter_->Publish();
54 }
55 }
56
57 static std::unique_ptr<analysis::Plotter> plotter_;
Austin Schuhb23f5252019-01-13 21:16:23 -080058
59 ::Eigen::Matrix<double, 2, 4> control_points_;
60 NSpline<4> spline4_;
61 NSpline<6> spline6_;
Austin Schuhc2b08772018-12-19 18:05:06 +110062};
63
James Kuszmaule32fa932021-05-11 21:38:16 -070064std::unique_ptr<analysis::Plotter> SplineTest::plotter_;
65
Austin Schuhc2b08772018-12-19 18:05:06 +110066// Tests that the derivitives of xy integrate back up to the position.
67TEST_F(SplineTest, XYIntegral) {
68 ::std::vector<double> alphas_plot;
69 ::std::vector<double> x_plot;
70 ::std::vector<double> y_plot;
71 ::std::vector<double> ix_plot;
72 ::std::vector<double> iy_plot;
73 ::std::vector<double> dx_plot;
74 ::std::vector<double> dy_plot;
75 ::std::vector<double> idx_plot;
76 ::std::vector<double> idy_plot;
77
78 const int num_points = 10000;
Austin Schuhb23f5252019-01-13 21:16:23 -080079 ::Eigen::Matrix<double, 2, 1> point = spline6_.Point(0.0);
80 ::Eigen::Matrix<double, 2, 1> dpoint = spline6_.DPoint(0.0);
81 ::Eigen::Matrix<double, 2, 1> ddpoint = spline6_.DDPoint(0.0);
Austin Schuhc2b08772018-12-19 18:05:06 +110082
83 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
84 for (int i = 0; i < num_points; ++i) {
85 const double alpha =
86 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
Austin Schuhb23f5252019-01-13 21:16:23 -080087 const ::Eigen::Matrix<double, 2, 1> expected_point = spline6_.Point(alpha);
Austin Schuhd749d932020-12-30 21:38:40 -080088 const ::Eigen::Matrix<double, 2, 1> expected_dpoint =
89 spline6_.DPoint(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110090 const ::Eigen::Matrix<double, 2, 1> expected_ddpoint =
Austin Schuhb23f5252019-01-13 21:16:23 -080091 spline6_.DDPoint(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110092
93 alphas_plot.push_back(alpha);
94 x_plot.push_back(expected_point(0));
95 y_plot.push_back(expected_point(1));
96 ix_plot.push_back(point(0));
97 iy_plot.push_back(point(1));
98 dx_plot.push_back(expected_dpoint(0));
99 dy_plot.push_back(expected_dpoint(1));
100 idx_plot.push_back(dpoint(0));
101 idy_plot.push_back(dpoint(1));
102
103 EXPECT_LT((point - expected_point).norm(), 1e-2) << ": At alpha " << alpha;
Austin Schuhd749d932020-12-30 21:38:40 -0800104 EXPECT_LT((dpoint - expected_dpoint).norm(), 1e-2)
105 << ": At alpha " << alpha;
106 EXPECT_LT((ddpoint - expected_ddpoint).norm(), 1e-2)
107 << ": At alpha " << alpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100108
109 // We need to record the starting state without integrating.
110 if (i == 0) {
111 continue;
112 }
113
114 point += dpoint * dalpha;
115 dpoint += ddpoint * dalpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800116 ddpoint += spline6_.DDDPoint(alpha) * dalpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100117 }
118
119 // Conditionally plot the functions and their integrals to aid debugging.
120 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700121 plotter_->AddFigure("Spline Attributes Over Alpha");
122 plotter_->AddLine(alphas_plot, x_plot, "X");
123 plotter_->AddLine(alphas_plot, ix_plot, "Integrated X");
124 plotter_->AddLine(alphas_plot, y_plot, "Y");
125 plotter_->AddLine(alphas_plot, iy_plot, "Integrated Y");
126 plotter_->AddLine(alphas_plot, dx_plot, "dX");
127 plotter_->AddLine(alphas_plot, idx_plot, "Integrated dX");
128 plotter_->AddLine(alphas_plot, dy_plot, "dY");
129 plotter_->AddLine(alphas_plot, idy_plot, "Integrated dY");
130 plotter_->XLabel("Spline Alpha");
131 plotter_->YLabel("X/Y (m), dX, dY (m / alpha)");
Austin Schuhc2b08772018-12-19 18:05:06 +1100132
James Kuszmaule32fa932021-05-11 21:38:16 -0700133 plotter_->AddFigure("X/Y Plot of Spline Path");
134 plotter_->AddLine(x_plot, y_plot, "spline");
135 plotter_->XLabel("X (m)");
136 plotter_->YLabel("Y (m)");
Austin Schuhc2b08772018-12-19 18:05:06 +1100137 }
138}
139
140// Tests that the derivitives of theta integrate back up to the angle.
141TEST_F(SplineTest, ThetaIntegral) {
142 ::std::vector<double> alphas_plot;
143 ::std::vector<double> theta_plot;
144 ::std::vector<double> itheta_plot;
145 ::std::vector<double> dtheta_plot;
146 ::std::vector<double> idtheta_plot;
147
148 const int num_points = 10000;
Austin Schuhb23f5252019-01-13 21:16:23 -0800149 double theta = spline6_.Theta(0.0);
150 double dtheta = spline6_.DTheta(0.0);
Austin Schuhc2b08772018-12-19 18:05:06 +1100151
152 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
153 for (int i = 0; i < num_points; ++i) {
154 const double alpha =
155 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
Austin Schuhb23f5252019-01-13 21:16:23 -0800156 const double expected_theta = spline6_.Theta(alpha);
157 const double expected_dtheta = spline6_.DTheta(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +1100158
159 alphas_plot.push_back(alpha);
160 theta_plot.push_back(expected_theta);
161 itheta_plot.push_back(theta);
162 dtheta_plot.push_back(expected_dtheta);
163 idtheta_plot.push_back(dtheta);
164
165 EXPECT_NEAR(expected_theta, theta, 1e-2) << ": At alpha " << alpha;
166 EXPECT_NEAR(expected_dtheta, dtheta, 1e-2) << ": At alpha " << alpha;
167
168 // We need to record the starting state without integrating.
169 if (i == 0) {
170 continue;
171 }
172
173 theta += dtheta * dalpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800174 dtheta += spline6_.DDTheta(alpha) * dalpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100175 }
176
177 // Conditionally plot the functions and their integrals to aid debugging.
178 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700179 plotter_->AddFigure("Heading Plot");
180 plotter_->AddLine(alphas_plot, theta_plot, "theta");
181 plotter_->AddLine(alphas_plot, itheta_plot, "Integrated theta");
182 plotter_->AddLine(alphas_plot, dtheta_plot, "dtheta");
183 plotter_->AddLine(alphas_plot, idtheta_plot, "Integrated dtheta");
184 plotter_->XLabel("Alpha");
185 plotter_->YLabel("Theta (rad), Dtheta (rad / alpha)");
Austin Schuhc2b08772018-12-19 18:05:06 +1100186 }
187}
188
Austin Schuhb23f5252019-01-13 21:16:23 -0800189// Tests that a 4 point spline has the same points as a 6 point spline built
190// with Spline4To6.
191TEST_F(SplineTest, FourToSixSpline) {
192 const int num_points = 10000;
193
194 ::std::vector<double> alphas_plot;
195 ::std::vector<double> x_plot;
196 ::std::vector<double> y_plot;
197
198 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
199 for (int i = 0; i < num_points; ++i) {
200 const double alpha = dalpha * static_cast<double>(i);
201
202 const ::Eigen::Matrix<double, 2, 1> expected_point = spline4_.Point(alpha);
203 const ::Eigen::Matrix<double, 2, 1> expected_dpoint =
204 spline4_.DPoint(alpha);
205 const ::Eigen::Matrix<double, 2, 1> expected_ddpoint =
206 spline4_.DDPoint(alpha);
207 const ::Eigen::Matrix<double, 2, 1> expected_dddpoint =
208 spline4_.DDDPoint(alpha);
209
210 const ::Eigen::Matrix<double, 2, 1> point = spline6_.Point(alpha);
211 const ::Eigen::Matrix<double, 2, 1> dpoint = spline6_.DPoint(alpha);
212 const ::Eigen::Matrix<double, 2, 1> ddpoint = spline6_.DDPoint(alpha);
213 const ::Eigen::Matrix<double, 2, 1> dddpoint = spline6_.DDDPoint(alpha);
214
215 alphas_plot.push_back(alpha);
216 x_plot.push_back(point(0));
217 y_plot.push_back(point(1));
218
219 EXPECT_LT((point - expected_point).norm(), 1e-9) << ": At alpha " << alpha;
Austin Schuhd749d932020-12-30 21:38:40 -0800220 EXPECT_LT((dpoint - expected_dpoint).norm(), 1e-9)
221 << ": At alpha " << alpha;
222 EXPECT_LT((ddpoint - expected_ddpoint).norm(), 1e-9)
223 << ": At alpha " << alpha;
224 EXPECT_LT((dddpoint - expected_dddpoint).norm(), 1e-9)
225 << ": At alpha " << alpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800226 }
227
228 // Conditionally plot the functions and their integrals to aid debugging.
229 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700230 plotter_->AddFigure("Spline X/Y");
231 plotter_->AddLine(alphas_plot, x_plot, "X");
232 plotter_->AddLine(alphas_plot, y_plot, "Y");
233 plotter_->XLabel("Alpha");
234 plotter_->YLabel("X/Y (m)");
Austin Schuhb23f5252019-01-13 21:16:23 -0800235
236 ::std::vector<double> control4x;
237 ::std::vector<double> control4y;
238 ::std::vector<double> control6x;
239 ::std::vector<double> control6y;
240 for (int i = 0; i < 4; ++i) {
241 control4x.push_back(spline4_.control_points()(0, i));
242 control4y.push_back(spline4_.control_points()(1, i));
243 }
244 for (int i = 0; i < 6; ++i) {
245 control6x.push_back(spline6_.control_points()(0, i));
246 control6y.push_back(spline6_.control_points()(1, i));
247 }
248
James Kuszmaule32fa932021-05-11 21:38:16 -0700249 plotter_->AddFigure("Spline Path Control Points Comparison");
250 plotter_->AddLine(x_plot, y_plot, "Spline");
251 plotter_->AddLine(control4x, control4y, "4-Spline Control Points");
252 plotter_->AddLine(control6x, control6y, "6-Spline Control Points");
253 plotter_->XLabel("X (m)");
254 plotter_->YLabel("Y (m)");
Austin Schuhb23f5252019-01-13 21:16:23 -0800255 }
256}
257
Austin Schuhc2b08772018-12-19 18:05:06 +1100258} // namespace testing
259} // namespace drivetrain
260} // namespace control_loops
261} // namespace frc971