blob: 6498fb6a5a93f554f0bb91713ab22e1ffe91a9ef [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
12namespace frc971 {
13namespace control_loops {
14namespace drivetrain {
15namespace testing {
16
James Kuszmaule32fa932021-05-11 21:38:16 -070017std::string TestName() {
18 const ::testing::TestInfo *info =
Philipp Schrader790cb542023-07-05 21:06:52 -070019 ::testing::UnitTest::GetInstance()->current_test_info();
20 return std::string(info->test_case_name()) + "." + std::string(info->name());
James Kuszmaule32fa932021-05-11 21:38:16 -070021}
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 }
Philipp Schrader790cb542023-07-05 21:06:52 -070037
Austin Schuhc2b08772018-12-19 18:05:06 +110038 protected:
39 SplineTest()
Austin Schuhb23f5252019-01-13 21:16:23 -080040 : control_points_((::Eigen::Matrix<double, 2, 4>() << 0.0, 0.5, 0.5, 1.0,
41 0.0, 0.0, 1.0, 1.0)
42 .finished()),
43 spline4_(control_points_),
James Kuszmaule32fa932021-05-11 21:38:16 -070044 spline6_(Spline4To6(control_points_)) {
45 if (FLAGS_plot) {
46 CHECK(plotter_);
47 plotter_->Title(TestName());
48 }
49 }
50 ~SplineTest() {}
51
52 void TearDown() override {
53 if (FLAGS_plot) {
54 plotter_->Publish();
55 }
56 }
57
58 static std::unique_ptr<analysis::Plotter> plotter_;
Austin Schuhb23f5252019-01-13 21:16:23 -080059
60 ::Eigen::Matrix<double, 2, 4> control_points_;
61 NSpline<4> spline4_;
62 NSpline<6> spline6_;
Austin Schuhc2b08772018-12-19 18:05:06 +110063};
64
James Kuszmaule32fa932021-05-11 21:38:16 -070065std::unique_ptr<analysis::Plotter> SplineTest::plotter_;
66
Austin Schuhc2b08772018-12-19 18:05:06 +110067// Tests that the derivitives of xy integrate back up to the position.
68TEST_F(SplineTest, XYIntegral) {
69 ::std::vector<double> alphas_plot;
70 ::std::vector<double> x_plot;
71 ::std::vector<double> y_plot;
72 ::std::vector<double> ix_plot;
73 ::std::vector<double> iy_plot;
74 ::std::vector<double> dx_plot;
75 ::std::vector<double> dy_plot;
76 ::std::vector<double> idx_plot;
77 ::std::vector<double> idy_plot;
78
79 const int num_points = 10000;
Austin Schuhb23f5252019-01-13 21:16:23 -080080 ::Eigen::Matrix<double, 2, 1> point = spline6_.Point(0.0);
81 ::Eigen::Matrix<double, 2, 1> dpoint = spline6_.DPoint(0.0);
82 ::Eigen::Matrix<double, 2, 1> ddpoint = spline6_.DDPoint(0.0);
Austin Schuhc2b08772018-12-19 18:05:06 +110083
84 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
85 for (int i = 0; i < num_points; ++i) {
86 const double alpha =
87 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
Austin Schuhb23f5252019-01-13 21:16:23 -080088 const ::Eigen::Matrix<double, 2, 1> expected_point = spline6_.Point(alpha);
Austin Schuhd749d932020-12-30 21:38:40 -080089 const ::Eigen::Matrix<double, 2, 1> expected_dpoint =
90 spline6_.DPoint(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110091 const ::Eigen::Matrix<double, 2, 1> expected_ddpoint =
Austin Schuhb23f5252019-01-13 21:16:23 -080092 spline6_.DDPoint(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +110093
94 alphas_plot.push_back(alpha);
95 x_plot.push_back(expected_point(0));
96 y_plot.push_back(expected_point(1));
97 ix_plot.push_back(point(0));
98 iy_plot.push_back(point(1));
99 dx_plot.push_back(expected_dpoint(0));
100 dy_plot.push_back(expected_dpoint(1));
101 idx_plot.push_back(dpoint(0));
102 idy_plot.push_back(dpoint(1));
103
104 EXPECT_LT((point - expected_point).norm(), 1e-2) << ": At alpha " << alpha;
Austin Schuhd749d932020-12-30 21:38:40 -0800105 EXPECT_LT((dpoint - expected_dpoint).norm(), 1e-2)
106 << ": At alpha " << alpha;
107 EXPECT_LT((ddpoint - expected_ddpoint).norm(), 1e-2)
108 << ": At alpha " << alpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100109
110 // We need to record the starting state without integrating.
111 if (i == 0) {
112 continue;
113 }
114
115 point += dpoint * dalpha;
116 dpoint += ddpoint * dalpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800117 ddpoint += spline6_.DDDPoint(alpha) * dalpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100118 }
119
120 // Conditionally plot the functions and their integrals to aid debugging.
121 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700122 plotter_->AddFigure("Spline Attributes Over Alpha");
123 plotter_->AddLine(alphas_plot, x_plot, "X");
124 plotter_->AddLine(alphas_plot, ix_plot, "Integrated X");
125 plotter_->AddLine(alphas_plot, y_plot, "Y");
126 plotter_->AddLine(alphas_plot, iy_plot, "Integrated Y");
127 plotter_->AddLine(alphas_plot, dx_plot, "dX");
128 plotter_->AddLine(alphas_plot, idx_plot, "Integrated dX");
129 plotter_->AddLine(alphas_plot, dy_plot, "dY");
130 plotter_->AddLine(alphas_plot, idy_plot, "Integrated dY");
131 plotter_->XLabel("Spline Alpha");
132 plotter_->YLabel("X/Y (m), dX, dY (m / alpha)");
Austin Schuh122dbaa2023-02-20 17:39:08 -0800133 plotter_->Publish();
Austin Schuhc2b08772018-12-19 18:05:06 +1100134
James Kuszmaule32fa932021-05-11 21:38:16 -0700135 plotter_->AddFigure("X/Y Plot of Spline Path");
136 plotter_->AddLine(x_plot, y_plot, "spline");
137 plotter_->XLabel("X (m)");
138 plotter_->YLabel("Y (m)");
Austin Schuh122dbaa2023-02-20 17:39:08 -0800139 plotter_->Publish();
Austin Schuhc2b08772018-12-19 18:05:06 +1100140 }
141}
142
143// Tests that the derivitives of theta integrate back up to the angle.
144TEST_F(SplineTest, ThetaIntegral) {
145 ::std::vector<double> alphas_plot;
146 ::std::vector<double> theta_plot;
147 ::std::vector<double> itheta_plot;
148 ::std::vector<double> dtheta_plot;
149 ::std::vector<double> idtheta_plot;
150
151 const int num_points = 10000;
Austin Schuhb23f5252019-01-13 21:16:23 -0800152 double theta = spline6_.Theta(0.0);
153 double dtheta = spline6_.DTheta(0.0);
Austin Schuhc2b08772018-12-19 18:05:06 +1100154
155 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
156 for (int i = 0; i < num_points; ++i) {
157 const double alpha =
158 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
Austin Schuhb23f5252019-01-13 21:16:23 -0800159 const double expected_theta = spline6_.Theta(alpha);
160 const double expected_dtheta = spline6_.DTheta(alpha);
Austin Schuhc2b08772018-12-19 18:05:06 +1100161
162 alphas_plot.push_back(alpha);
163 theta_plot.push_back(expected_theta);
164 itheta_plot.push_back(theta);
165 dtheta_plot.push_back(expected_dtheta);
166 idtheta_plot.push_back(dtheta);
167
168 EXPECT_NEAR(expected_theta, theta, 1e-2) << ": At alpha " << alpha;
169 EXPECT_NEAR(expected_dtheta, dtheta, 1e-2) << ": At alpha " << alpha;
170
171 // We need to record the starting state without integrating.
172 if (i == 0) {
173 continue;
174 }
175
176 theta += dtheta * dalpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800177 dtheta += spline6_.DDTheta(alpha) * dalpha;
Austin Schuhc2b08772018-12-19 18:05:06 +1100178 }
179
180 // Conditionally plot the functions and their integrals to aid debugging.
181 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700182 plotter_->AddFigure("Heading Plot");
183 plotter_->AddLine(alphas_plot, theta_plot, "theta");
184 plotter_->AddLine(alphas_plot, itheta_plot, "Integrated theta");
185 plotter_->AddLine(alphas_plot, dtheta_plot, "dtheta");
186 plotter_->AddLine(alphas_plot, idtheta_plot, "Integrated dtheta");
187 plotter_->XLabel("Alpha");
188 plotter_->YLabel("Theta (rad), Dtheta (rad / alpha)");
Austin Schuhc2b08772018-12-19 18:05:06 +1100189 }
190}
191
Austin Schuhb23f5252019-01-13 21:16:23 -0800192// Tests that a 4 point spline has the same points as a 6 point spline built
193// with Spline4To6.
194TEST_F(SplineTest, FourToSixSpline) {
195 const int num_points = 10000;
196
197 ::std::vector<double> alphas_plot;
198 ::std::vector<double> x_plot;
199 ::std::vector<double> y_plot;
200
201 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
202 for (int i = 0; i < num_points; ++i) {
203 const double alpha = dalpha * static_cast<double>(i);
204
205 const ::Eigen::Matrix<double, 2, 1> expected_point = spline4_.Point(alpha);
206 const ::Eigen::Matrix<double, 2, 1> expected_dpoint =
207 spline4_.DPoint(alpha);
208 const ::Eigen::Matrix<double, 2, 1> expected_ddpoint =
209 spline4_.DDPoint(alpha);
210 const ::Eigen::Matrix<double, 2, 1> expected_dddpoint =
211 spline4_.DDDPoint(alpha);
212
213 const ::Eigen::Matrix<double, 2, 1> point = spline6_.Point(alpha);
214 const ::Eigen::Matrix<double, 2, 1> dpoint = spline6_.DPoint(alpha);
215 const ::Eigen::Matrix<double, 2, 1> ddpoint = spline6_.DDPoint(alpha);
216 const ::Eigen::Matrix<double, 2, 1> dddpoint = spline6_.DDDPoint(alpha);
217
218 alphas_plot.push_back(alpha);
219 x_plot.push_back(point(0));
220 y_plot.push_back(point(1));
221
222 EXPECT_LT((point - expected_point).norm(), 1e-9) << ": At alpha " << alpha;
Austin Schuhd749d932020-12-30 21:38:40 -0800223 EXPECT_LT((dpoint - expected_dpoint).norm(), 1e-9)
224 << ": At alpha " << alpha;
225 EXPECT_LT((ddpoint - expected_ddpoint).norm(), 1e-9)
226 << ": At alpha " << alpha;
227 EXPECT_LT((dddpoint - expected_dddpoint).norm(), 1e-9)
228 << ": At alpha " << alpha;
Austin Schuhb23f5252019-01-13 21:16:23 -0800229 }
230
231 // Conditionally plot the functions and their integrals to aid debugging.
232 if (FLAGS_plot) {
James Kuszmaule32fa932021-05-11 21:38:16 -0700233 plotter_->AddFigure("Spline X/Y");
234 plotter_->AddLine(alphas_plot, x_plot, "X");
235 plotter_->AddLine(alphas_plot, y_plot, "Y");
236 plotter_->XLabel("Alpha");
237 plotter_->YLabel("X/Y (m)");
Austin Schuhb23f5252019-01-13 21:16:23 -0800238
239 ::std::vector<double> control4x;
240 ::std::vector<double> control4y;
241 ::std::vector<double> control6x;
242 ::std::vector<double> control6y;
243 for (int i = 0; i < 4; ++i) {
244 control4x.push_back(spline4_.control_points()(0, i));
245 control4y.push_back(spline4_.control_points()(1, i));
246 }
247 for (int i = 0; i < 6; ++i) {
248 control6x.push_back(spline6_.control_points()(0, i));
249 control6y.push_back(spline6_.control_points()(1, i));
250 }
251
James Kuszmaule32fa932021-05-11 21:38:16 -0700252 plotter_->AddFigure("Spline Path Control Points Comparison");
253 plotter_->AddLine(x_plot, y_plot, "Spline");
254 plotter_->AddLine(control4x, control4y, "4-Spline Control Points");
255 plotter_->AddLine(control6x, control6y, "6-Spline Control Points");
256 plotter_->XLabel("X (m)");
257 plotter_->YLabel("Y (m)");
Austin Schuhb23f5252019-01-13 21:16:23 -0800258 }
259}
260
Austin Schuhc2b08772018-12-19 18:05:06 +1100261} // namespace testing
262} // namespace drivetrain
263} // namespace control_loops
264} // namespace frc971