blob: 9aa3325b748588ad55e256561a744218fe14db59 [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"
7#include "third_party/matplotlib-cpp/matplotlibcpp.h"
8
9DEFINE_bool(plot, false, "If true, plot");
10
11namespace frc971 {
12namespace control_loops {
13namespace drivetrain {
14namespace testing {
15
16// Test fixture with a spline from 0, 0 to 1, 1
17class SplineTest : public ::testing::Test {
18 protected:
19 SplineTest()
20 : spline_((::Eigen::Matrix<double, 2, 4>() << 0.0, 0.5, 0.5, 1.0, 0.0,
21 0.0, 1.0, 1.0)
22 .finished()) {}
23 Spline spline_;
24};
25
26// Tests that the derivitives of xy integrate back up to the position.
27TEST_F(SplineTest, XYIntegral) {
28 ::std::vector<double> alphas_plot;
29 ::std::vector<double> x_plot;
30 ::std::vector<double> y_plot;
31 ::std::vector<double> ix_plot;
32 ::std::vector<double> iy_plot;
33 ::std::vector<double> dx_plot;
34 ::std::vector<double> dy_plot;
35 ::std::vector<double> idx_plot;
36 ::std::vector<double> idy_plot;
37
38 const int num_points = 10000;
39 ::Eigen::Matrix<double, 2, 1> point = spline_.Point(0.0);
40 ::Eigen::Matrix<double, 2, 1> dpoint = spline_.DPoint(0.0);
41 ::Eigen::Matrix<double, 2, 1> ddpoint = spline_.DDPoint(0.0);
42
43 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
44 for (int i = 0; i < num_points; ++i) {
45 const double alpha =
46 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
47 const ::Eigen::Matrix<double, 2, 1> expected_point = spline_.Point(alpha);
48 const ::Eigen::Matrix<double, 2, 1> expected_dpoint = spline_.DPoint(alpha);
49 const ::Eigen::Matrix<double, 2, 1> expected_ddpoint =
50 spline_.DDPoint(alpha);
51
52 alphas_plot.push_back(alpha);
53 x_plot.push_back(expected_point(0));
54 y_plot.push_back(expected_point(1));
55 ix_plot.push_back(point(0));
56 iy_plot.push_back(point(1));
57 dx_plot.push_back(expected_dpoint(0));
58 dy_plot.push_back(expected_dpoint(1));
59 idx_plot.push_back(dpoint(0));
60 idy_plot.push_back(dpoint(1));
61
62 EXPECT_LT((point - expected_point).norm(), 1e-2) << ": At alpha " << alpha;
63 EXPECT_LT((dpoint - expected_dpoint).norm(), 1e-2) << ": At alpha "
64 << alpha;
65 EXPECT_LT((ddpoint - expected_ddpoint).norm(), 1e-2) << ": At alpha "
66 << alpha;
67
68 // We need to record the starting state without integrating.
69 if (i == 0) {
70 continue;
71 }
72
73 point += dpoint * dalpha;
74 dpoint += ddpoint * dalpha;
75 ddpoint += spline_.DDDPoint(alpha) * dalpha;
76 }
77
78 // Conditionally plot the functions and their integrals to aid debugging.
79 if (FLAGS_plot) {
80 matplotlibcpp::figure();
81 matplotlibcpp::plot(alphas_plot, x_plot, {{"label", "x"}});
82 matplotlibcpp::plot(alphas_plot, ix_plot, {{"label", "ix"}});
83 matplotlibcpp::plot(alphas_plot, y_plot, {{"label", "y"}});
84 matplotlibcpp::plot(alphas_plot, iy_plot, {{"label", "iy"}});
85 matplotlibcpp::plot(alphas_plot, dx_plot, {{"label", "dx"}});
86 matplotlibcpp::plot(alphas_plot, idx_plot, {{"label", "idx"}});
87 matplotlibcpp::plot(alphas_plot, dy_plot, {{"label", "dy"}});
88 matplotlibcpp::plot(alphas_plot, idy_plot, {{"label", "idy"}});
89 matplotlibcpp::legend();
90
91 matplotlibcpp::show();
92 }
93}
94
95// Tests that the derivitives of theta integrate back up to the angle.
96TEST_F(SplineTest, ThetaIntegral) {
97 ::std::vector<double> alphas_plot;
98 ::std::vector<double> theta_plot;
99 ::std::vector<double> itheta_plot;
100 ::std::vector<double> dtheta_plot;
101 ::std::vector<double> idtheta_plot;
102
103 const int num_points = 10000;
104 double theta = spline_.Theta(0.0);
105 double dtheta = spline_.DTheta(0.0);
106
107 const double dalpha = 1.0 / static_cast<double>(num_points - 1);
108 for (int i = 0; i < num_points; ++i) {
109 const double alpha =
110 1.0 * static_cast<double>(i) / static_cast<double>(num_points - 1);
111 const double expected_theta = spline_.Theta(alpha);
112 const double expected_dtheta = spline_.DTheta(alpha);
113
114 alphas_plot.push_back(alpha);
115 theta_plot.push_back(expected_theta);
116 itheta_plot.push_back(theta);
117 dtheta_plot.push_back(expected_dtheta);
118 idtheta_plot.push_back(dtheta);
119
120 EXPECT_NEAR(expected_theta, theta, 1e-2) << ": At alpha " << alpha;
121 EXPECT_NEAR(expected_dtheta, dtheta, 1e-2) << ": At alpha " << alpha;
122
123 // We need to record the starting state without integrating.
124 if (i == 0) {
125 continue;
126 }
127
128 theta += dtheta * dalpha;
129 dtheta += spline_.DDTheta(alpha) * dalpha;
130 }
131
132 // Conditionally plot the functions and their integrals to aid debugging.
133 if (FLAGS_plot) {
134 matplotlibcpp::figure();
135 matplotlibcpp::plot(alphas_plot, theta_plot, {{"label", "theta"}});
136 matplotlibcpp::plot(alphas_plot, itheta_plot, {{"label", "itheta"}});
137 matplotlibcpp::plot(alphas_plot, dtheta_plot, {{"label", "dtheta"}});
138 matplotlibcpp::plot(alphas_plot, idtheta_plot, {{"label", "idtheta"}});
139 matplotlibcpp::legend();
140
141 matplotlibcpp::show();
142 }
143}
144
145} // namespace testing
146} // namespace drivetrain
147} // namespace control_loops
148} // namespace frc971