blob: 6d84d5341b3b193c43146f33b2b1fa2e42ac7a4f [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"
Philipp Schrader790cb542023-07-05 21:06:52 -07007
James Kuszmaule32fa932021-05-11 21:38:16 -07008#include "frc971/analysis/in_process_plotter.h"
Austin Schuhc2b08772018-12-19 18:05:06 +11009
10DEFINE_bool(plot, false, "If true, plot");
11
Stephan Pleinesf63bde82024-01-13 15:59:33 -080012namespace frc971::control_loops::drivetrain::testing {
Austin Schuhc2b08772018-12-19 18:05:06 +110013
James Kuszmaule32fa932021-05-11 21:38:16 -070014std::string TestName() {
15 const ::testing::TestInfo *info =
Philipp Schrader790cb542023-07-05 21:06:52 -070016 ::testing::UnitTest::GetInstance()->current_test_info();
17 return std::string(info->test_case_name()) + "." + std::string(info->name());
James Kuszmaule32fa932021-05-11 21:38:16 -070018}
19
Austin Schuhc2b08772018-12-19 18:05:06 +110020// Test fixture with a spline from 0, 0 to 1, 1
21class SplineTest : public ::testing::Test {
James Kuszmaule32fa932021-05-11 21:38:16 -070022 public:
23 static void SetUpTestSuite() {
24 if (FLAGS_plot) {
25 plotter_ = std::make_unique<analysis::Plotter>();
26 }
27 }
28
29 static void TearDownTestSuite() {
30 if (FLAGS_plot) {
31 plotter_->Spin();
32 }
33 }
Philipp Schrader790cb542023-07-05 21:06:52 -070034
Austin Schuhc2b08772018-12-19 18:05:06 +110035 protected:
36 SplineTest()
Austin Schuhb23f5252019-01-13 21:16:23 -080037 : control_points_((::Eigen::Matrix<double, 2, 4>() << 0.0, 0.5, 0.5, 1.0,
38 0.0, 0.0, 1.0, 1.0)
39 .finished()),
40 spline4_(control_points_),
James Kuszmaule32fa932021-05-11 21:38:16 -070041 spline6_(Spline4To6(control_points_)) {
42 if (FLAGS_plot) {
43 CHECK(plotter_);
44 plotter_->Title(TestName());
45 }
46 }
47 ~SplineTest() {}
48
49 void TearDown() override {
50 if (FLAGS_plot) {
51 plotter_->Publish();
52 }
53 }
54
55 static std::unique_ptr<analysis::Plotter> plotter_;
Austin Schuhb23f5252019-01-13 21:16:23 -080056
57 ::Eigen::Matrix<double, 2, 4> control_points_;
58 NSpline<4> spline4_;
59 NSpline<6> spline6_;
Austin Schuhc2b08772018-12-19 18:05:06 +110060};
61
James Kuszmaule32fa932021-05-11 21:38:16 -070062std::unique_ptr<analysis::Plotter> SplineTest::plotter_;
63
Austin Schuhc2b08772018-12-19 18:05:06 +110064// Tests that the derivitives of xy integrate back up to the position.
65TEST_F(SplineTest, XYIntegral) {
66 ::std::vector<double> alphas_plot;
67 ::std::vector<double> x_plot;
68 ::std::vector<double> y_plot;
69 ::std::vector<double> ix_plot;
70 ::std::vector<double> iy_plot;
71 ::std::vector<double> dx_plot;
72 ::std::vector<double> dy_plot;
73 ::std::vector<double> idx_plot;
74 ::std::vector<double> idy_plot;
75
76 const int num_points = 10000;
Austin Schuhb23f5252019-01-13 21:16:23 -080077 ::Eigen::Matrix<double, 2, 1> point = spline6_.Point(0.0);
78 ::Eigen::Matrix<double, 2, 1> dpoint = spline6_.DPoint(0.0);
79 ::Eigen::Matrix<double, 2, 1> ddpoint = spline6_.DDPoint(0.0);
Austin Schuhc2b08772018-12-19 18:05:06 +110080
81 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
82 for (int i = 0; i < num_points; ++i) {
83 const double alpha =
84 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
Austin Schuhb23f5252019-01-13 21:16:23 -080085 const ::Eigen::Matrix<double, 2, 1> expected_point = spline6_.Point(alpha);
Austin Schuhd749d932020-12-30 21:38:40 -080086 const ::Eigen::Matrix<double, 2, 1> expected_dpoint =
87 spline6_.DPoint(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110088 const ::Eigen::Matrix<double, 2, 1> expected_ddpoint =
Austin Schuhb23f5252019-01-13 21:16:23 -080089 spline6_.DDPoint(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110090
91 alphas_plot.push_back(alpha);
92 x_plot.push_back(expected_point(0));
93 y_plot.push_back(expected_point(1));
94 ix_plot.push_back(point(0));
95 iy_plot.push_back(point(1));
96 dx_plot.push_back(expected_dpoint(0));
97 dy_plot.push_back(expected_dpoint(1));
98 idx_plot.push_back(dpoint(0));
99 idy_plot.push_back(dpoint(1));
100
101 EXPECT_LT((point - expected_point).norm(), 1e-2) << ": At alpha " << alpha;
Austin Schuhd749d932020-12-30 21:38:40 -0800102 EXPECT_LT((dpoint - expected_dpoint).norm(), 1e-2)
103 << ": At alpha " << alpha;
104 EXPECT_LT((ddpoint - expected_ddpoint).norm(), 1e-2)
105 << ": At alpha " << alpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100106
107 // We need to record the starting state without integrating.
108 if (i == 0) {
109 continue;
110 }
111
112 point += dpoint * dalpha;
113 dpoint += ddpoint * dalpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800114 ddpoint += spline6_.DDDPoint(alpha) * dalpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100115 }
116
117 // Conditionally plot the functions and their integrals to aid debugging.
118 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700119 plotter_->AddFigure("Spline Attributes Over Alpha");
120 plotter_->AddLine(alphas_plot, x_plot, "X");
121 plotter_->AddLine(alphas_plot, ix_plot, "Integrated X");
122 plotter_->AddLine(alphas_plot, y_plot, "Y");
123 plotter_->AddLine(alphas_plot, iy_plot, "Integrated Y");
124 plotter_->AddLine(alphas_plot, dx_plot, "dX");
125 plotter_->AddLine(alphas_plot, idx_plot, "Integrated dX");
126 plotter_->AddLine(alphas_plot, dy_plot, "dY");
127 plotter_->AddLine(alphas_plot, idy_plot, "Integrated dY");
128 plotter_->XLabel("Spline Alpha");
129 plotter_->YLabel("X/Y (m), dX, dY (m / alpha)");
Austin Schuh122dbaa2023-02-20 17:39:08 -0800130 plotter_->Publish();
Austin Schuhc2b08772018-12-19 18:05:06 +1100131
James Kuszmaule32fa932021-05-11 21:38:16 -0700132 plotter_->AddFigure("X/Y Plot of Spline Path");
133 plotter_->AddLine(x_plot, y_plot, "spline");
134 plotter_->XLabel("X (m)");
135 plotter_->YLabel("Y (m)");
Austin Schuh122dbaa2023-02-20 17:39:08 -0800136 plotter_->Publish();
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
Stephan Pleinesf63bde82024-01-13 15:59:33 -0800258} // namespace frc971::control_loops::drivetrain::testing