blob: ab38980ab29c5561c2a0c2912391a8d0c1761698 [file] [log] [blame]
Austin Schuh941b46d2018-12-19 18:06:05 +11001#include "frc971/control_loops/drivetrain/distance_spline.h"
2
Austin Schuha6e7b212019-01-20 13:53:01 -08003#include "aos/logging/logging.h"
Austin Schuh941b46d2018-12-19 18:06:05 +11004#include "frc971/control_loops/drivetrain/spline.h"
James Kuszmaul75a18c52021-03-10 22:02:07 -08005#include "glog/logging.h"
Austin Schuh941b46d2018-12-19 18:06:05 +11006
7namespace frc971 {
8namespace control_loops {
9namespace drivetrain {
10
James Kuszmaul75a18c52021-03-10 22:02:07 -080011::std::vector<float> DistanceSpline::BuildDistances(size_t num_alpha) {
Austin Schuh280996e2019-01-19 17:43:37 -080012 num_alpha = num_alpha == 0 ? 100 * splines_.size() : num_alpha;
James Kuszmaul75a18c52021-03-10 22:02:07 -080013 ::std::vector<float> distances;
Austin Schuh280996e2019-01-19 17:43:37 -080014 distances.push_back(0.0);
Austin Schuh941b46d2018-12-19 18:06:05 +110015
Austin Schuha6e7b212019-01-20 13:53:01 -080016 if (splines_.size() > 1) {
17 // We've got a multispline to follow!
Ethan Wing20da69a2021-10-13 20:37:36 -070018 // Confirm that the ends line up to the correct number of derivatives.
Austin Schuha6e7b212019-01-20 13:53:01 -080019 for (size_t i = 1; i < splines_.size(); ++i) {
20 const Spline &spline0 = splines_[i - 1];
21 const Spline &spline1 = splines_[i];
22
23 const ::Eigen::Matrix<double, 2, 1> end0 = spline0.Point(1.0);
24 const ::Eigen::Matrix<double, 2, 1> start1 = spline1.Point(0.0);
25
26 if (!end0.isApprox(start1, 1e-6)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070027 AOS_LOG(ERROR,
28 "Splines %d and %d don't line up. [%f, %f] != [%f, %f]\n",
29 static_cast<int>(i - 1), static_cast<int>(i), end0(0, 0),
30 end0(1, 0), start1(0, 0), start1(1, 0));
Austin Schuha6e7b212019-01-20 13:53:01 -080031 }
32
33 const ::Eigen::Matrix<double, 2, 1> dend0 = spline0.DPoint(1.0);
34 const ::Eigen::Matrix<double, 2, 1> dstart1 = spline1.DPoint(0.0);
35
36 if (!dend0.isApprox(dstart1, 1e-6)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070037 AOS_LOG(
38 ERROR,
Ethan Wing20da69a2021-10-13 20:37:36 -070039 "Splines %d and %d don't line up in the first derivative. [%f, "
Austin Schuha6e7b212019-01-20 13:53:01 -080040 "%f] != [%f, %f]\n",
41 static_cast<int>(i - 1), static_cast<int>(i), dend0(0, 0),
42 dend0(1, 0), dstart1(0, 0), dstart1(1, 0));
43 }
44
45 const ::Eigen::Matrix<double, 2, 1> ddend0 = spline0.DDPoint(1.0);
46 const ::Eigen::Matrix<double, 2, 1> ddstart1 = spline1.DDPoint(0.0);
47
48 if (!ddend0.isApprox(ddstart1, 1e-6)) {
Austin Schuhf257f3c2019-10-27 21:00:43 -070049 AOS_LOG(
50 ERROR,
Ethan Wing20da69a2021-10-13 20:37:36 -070051 "Splines %d and %d don't line up in the second derivative. [%f, "
Austin Schuha6e7b212019-01-20 13:53:01 -080052 "%f] != [%f, %f]\n",
53 static_cast<int>(i - 1), static_cast<int>(i), ddend0(0, 0),
54 ddend0(1, 0), ddstart1(0, 0), ddstart1(1, 0));
55 }
56 }
57 }
58
Austin Schuh280996e2019-01-19 17:43:37 -080059 const double dalpha =
60 static_cast<double>(splines_.size()) / static_cast<double>(num_alpha - 1);
Austin Schuh941b46d2018-12-19 18:06:05 +110061 double last_alpha = 0.0;
Austin Schuh280996e2019-01-19 17:43:37 -080062 for (size_t i = 1; i < num_alpha; ++i) {
Austin Schuh941b46d2018-12-19 18:06:05 +110063 const double alpha = dalpha * i;
Austin Schuh280996e2019-01-19 17:43:37 -080064 distances.push_back(distances.back() +
65 GaussianQuadrature5(
66 [this](double alpha) {
67 const size_t spline_index = ::std::min(
68 static_cast<size_t>(::std::floor(alpha)),
69 splines_.size() - 1);
70 return this->splines_[spline_index]
71 .DPoint(alpha - spline_index)
72 .norm();
73 },
74 last_alpha, alpha));
Austin Schuh941b46d2018-12-19 18:06:05 +110075 last_alpha = alpha;
76 }
Austin Schuh280996e2019-01-19 17:43:37 -080077 return distances;
Austin Schuh941b46d2018-12-19 18:06:05 +110078}
79
James Kuszmaul75a18c52021-03-10 22:02:07 -080080std::vector<Spline> FlatbufferToSplines(const MultiSpline *fb) {
81 CHECK_NOTNULL(fb);
82 const size_t spline_count = fb->spline_count();
83 CHECK_EQ(fb->spline_x()->size(), static_cast<size_t>(spline_count * 5 + 1));
84 CHECK_EQ(fb->spline_y()->size(), static_cast<size_t>(spline_count * 5 + 1));
85 std::vector<Spline> splines;
86 for (size_t ii = 0; ii < spline_count; ++ii) {
87 Eigen::Matrix<double, 2, 6> points;
88 for (int jj = 0; jj < 6; ++jj) {
89 points(0, jj) = fb->spline_x()->Get(ii * 5 + jj);
90 points(1, jj) = fb->spline_y()->Get(ii * 5 + jj);
91 }
92 splines.emplace_back(Spline(points));
93 }
94 return splines;
95}
96
Austin Schuh280996e2019-01-19 17:43:37 -080097DistanceSpline::DistanceSpline(::std::vector<Spline> &&splines, int num_alpha)
98 : splines_(::std::move(splines)), distances_(BuildDistances(num_alpha)) {}
99
100DistanceSpline::DistanceSpline(const Spline &spline, int num_alpha)
101 : splines_({spline}), distances_(BuildDistances(num_alpha)) {}
102
James Kuszmaul75a18c52021-03-10 22:02:07 -0800103DistanceSpline::DistanceSpline(const MultiSpline *fb, int num_alpha)
104 : splines_(FlatbufferToSplines(fb)),
105 distances_(BuildDistances(num_alpha)) {}
106
107// TODO(james): Directly use the flatbuffer vector for accessing distances,
108// rather than doing this redundant copy.
109DistanceSpline::DistanceSpline(const fb::DistanceSpline &fb)
110 : splines_(FlatbufferToSplines(fb.spline())),
111 distances_(CHECK_NOTNULL(fb.distances())->begin(),
112 fb.distances()->end()) {}
113
114flatbuffers::Offset<fb::DistanceSpline> DistanceSpline::Serialize(
115 flatbuffers::FlatBufferBuilder *fbb,
116 flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Constraint>>>
117 constraints) const {
118 if (splines_.empty()) {
119 return {};
120 }
121 const size_t num_points = splines_.size() * 5 + 1;
122 float *spline_x_vector = nullptr;
123 float *spline_y_vector = nullptr;
124 const flatbuffers::Offset<flatbuffers::Vector<float>> spline_x_offset =
125 fbb->CreateUninitializedVector(num_points, &spline_x_vector);
126 const flatbuffers::Offset<flatbuffers::Vector<float>> spline_y_offset =
127 fbb->CreateUninitializedVector(num_points, &spline_y_vector);
128 CHECK_NOTNULL(spline_x_vector);
129 CHECK_NOTNULL(spline_y_vector);
130 spline_x_vector[0] = splines_[0].control_points()(0, 0);
131 spline_y_vector[0] = splines_[0].control_points()(1, 0);
132 for (size_t spline_index = 0; spline_index < splines_.size();
133 ++spline_index) {
134 for (size_t point = 1; point < 6u; ++point) {
135 spline_x_vector[spline_index * 5 + point] =
136 splines_[spline_index].control_points()(0, point);
137 spline_y_vector[spline_index * 5 + point] =
138 splines_[spline_index].control_points()(1, point);
139 }
140 }
141 MultiSpline::Builder multi_spline_builder(*fbb);
142 multi_spline_builder.add_spline_count(splines_.size());
143 multi_spline_builder.add_spline_x(spline_x_offset);
144 multi_spline_builder.add_spline_y(spline_y_offset);
145 multi_spline_builder.add_constraints(constraints);
146 const flatbuffers::Offset<MultiSpline> multi_spline_offset =
147 multi_spline_builder.Finish();
148 const flatbuffers::Offset<flatbuffers::Vector<float>> distances_offset =
149 fbb->CreateVector(distances_);
150 fb::DistanceSpline::Builder spline_builder(*fbb);
151 spline_builder.add_spline(multi_spline_offset);
152 spline_builder.add_distances(distances_offset);
153 return spline_builder.Finish();
154}
155
Austin Schuh941b46d2018-12-19 18:06:05 +1100156::Eigen::Matrix<double, 2, 1> DistanceSpline::DDXY(double distance) const {
Austin Schuh280996e2019-01-19 17:43:37 -0800157 const AlphaAndIndex a = DistanceToAlpha(distance);
158 const ::Eigen::Matrix<double, 2, 1> dspline_point =
159 splines_[a.index].DPoint(a.alpha);
160 const ::Eigen::Matrix<double, 2, 1> ddspline_point =
161 splines_[a.index].DDPoint(a.alpha);
Austin Schuh941b46d2018-12-19 18:06:05 +1100162
163 const double squared_norm = dspline_point.squaredNorm();
164
165 return ddspline_point / squared_norm -
Austin Schuhd749d932020-12-30 21:38:40 -0800166 dspline_point *
167 (dspline_point(0) * ddspline_point(0) +
168 dspline_point(1) * ddspline_point(1)) /
Austin Schuh941b46d2018-12-19 18:06:05 +1100169 ::std::pow(squared_norm, 2);
170}
171
172double DistanceSpline::DDTheta(double distance) const {
Austin Schuh280996e2019-01-19 17:43:37 -0800173 const AlphaAndIndex a = DistanceToAlpha(distance);
Austin Schuh941b46d2018-12-19 18:06:05 +1100174
175 // TODO(austin): We are re-computing DPoint here even worse
Austin Schuh280996e2019-01-19 17:43:37 -0800176 const ::Eigen::Matrix<double, 2, 1> dspline_point =
177 splines_[a.index].DPoint(a.alpha);
178 const ::Eigen::Matrix<double, 2, 1> ddspline_point =
179 splines_[a.index].DDPoint(a.alpha);
Austin Schuh941b46d2018-12-19 18:06:05 +1100180
Austin Schuh280996e2019-01-19 17:43:37 -0800181 const double dtheta = splines_[a.index].DTheta(a.alpha);
182 const double ddtheta = splines_[a.index].DDTheta(a.alpha);
Austin Schuh941b46d2018-12-19 18:06:05 +1100183
184 const double squared_norm = dspline_point.squaredNorm();
185
Austin Schuhd749d932020-12-30 21:38:40 -0800186 return ddtheta / squared_norm - dtheta *
187 (dspline_point(0) * ddspline_point(0) +
188 dspline_point(1) * ddspline_point(1)) /
189 ::std::pow(squared_norm, 2);
Austin Schuh941b46d2018-12-19 18:06:05 +1100190}
191
Austin Schuh280996e2019-01-19 17:43:37 -0800192DistanceSpline::AlphaAndIndex DistanceSpline::DistanceToAlpha(
193 double distance) const {
Austin Schuh941b46d2018-12-19 18:06:05 +1100194 if (distance <= 0.0) {
Austin Schuh280996e2019-01-19 17:43:37 -0800195 return {0, 0.0};
Austin Schuh941b46d2018-12-19 18:06:05 +1100196 }
197 if (distance >= length()) {
Austin Schuh280996e2019-01-19 17:43:37 -0800198 return {splines_.size() - 1, 1.0};
Austin Schuh941b46d2018-12-19 18:06:05 +1100199 }
200
201 // Find the distance right below our number using a binary search.
202 size_t after = ::std::distance(
203 distances_.begin(),
204 ::std::lower_bound(distances_.begin(), distances_.end(), distance));
205 size_t before = after - 1;
206 const double distance_step_size =
Austin Schuh280996e2019-01-19 17:43:37 -0800207 (splines_.size() / static_cast<double>(distances_.size() - 1));
Austin Schuh941b46d2018-12-19 18:06:05 +1100208
Austin Schuh280996e2019-01-19 17:43:37 -0800209 const double alpha = (distance - distances_[before]) /
210 (distances_[after] - distances_[before]) *
211 distance_step_size +
212 static_cast<double>(before) * distance_step_size;
213 const size_t index = static_cast<size_t>(::std::floor(alpha));
214
215 return {index, alpha - index};
Austin Schuh941b46d2018-12-19 18:06:05 +1100216}
217
218} // namespace drivetrain
219} // namespace control_loops
220} // namespace frc971