TrajoptLib
Library for generating time-optimal trajectories for FRC robots.
Loading...
Searching...
No Matches
spline_helper.hpp
1// Copyright (c) TrajoptLib contributors
2
3#pragma once
4
5#include <array>
6#include <cstddef>
7#include <vector>
8
9#include "trajopt/geometry/pose2.hpp"
10#include "trajopt/geometry/translation2.hpp"
11#include "trajopt/spline/cubic_hermite_spline.hpp"
12#include "trajopt/spline/spline.hpp"
13#include "trajopt/util/symbol_exports.hpp"
14
15namespace trajopt {
16
19class TRAJOPT_DLLEXPORT SplineHelper {
20 public:
28 static std::array<Spline<3>::ControlVector, 2>
30 const Pose2d& start, const std::vector<Translation2d>& interior_waypoints,
31 const Pose2d& end) {
32 double scalar;
33 if (interior_waypoints.empty()) {
34 scalar = 1.2 * start.translation().distance(end.translation());
35 } else {
36 scalar = 1.2 * start.translation().distance(interior_waypoints.front());
37 }
38 const auto initial_control_vector = cubic_control_vector(scalar, start);
39 if (!interior_waypoints.empty()) {
40 scalar = 1.2 * end.translation().distance(interior_waypoints.back());
41 }
42 const auto final_control_vector = cubic_control_vector(scalar, end);
43 return {initial_control_vector, final_control_vector};
44 }
45
60 static std::vector<CubicHermiteSpline> cubic_splines_from_control_vectors(
61 const Spline<3>::ControlVector& start,
62 std::vector<Translation2d> waypoints,
63 const Spline<3>::ControlVector& end) {
64 std::vector<CubicHermiteSpline> splines;
65
66 std::array<double, 2> x_initial = start.x;
67 std::array<double, 2> y_initial = start.y;
68 std::array<double, 2> x_final = end.x;
69 std::array<double, 2> y_final = end.y;
70
71 if (waypoints.size() > 1) {
72 waypoints.emplace(waypoints.begin(),
73 Translation2d{x_initial[0], y_initial[0]});
74 waypoints.emplace_back(Translation2d{x_final[0], y_final[0]});
75
76 // Populate tridiagonal system for clamped cubic
77 /* See:
78 https://www.uio.no/studier/emner/matnat/ifi/nedlagte-emner/INF-MAT4350/h08
79 /undervisningsmateriale/chap7alecture.pdf
80 */
81
82 // Above-diagonal of tridiagonal matrix, zero-padded
83 std::vector<double> a;
84 // Diagonal of tridiagonal matrix
85 std::vector<double> b(waypoints.size() - 2, 4.0);
86 // Below-diagonal of tridiagonal matrix, zero-padded
87 std::vector<double> c;
88 // rhs vectors
89 std::vector<double> dx, dy;
90 // solution vectors
91 std::vector<double> fx(waypoints.size() - 2, 0.0),
92 fy(waypoints.size() - 2, 0.0);
93
94 // populate above-diagonal and below-diagonal vectors
95 a.emplace_back(0);
96 for (size_t i = 0; i < waypoints.size() - 3; ++i) {
97 a.emplace_back(1);
98 c.emplace_back(1);
99 }
100 c.emplace_back(0);
101
102 // populate rhs vectors
103 dx.emplace_back(3 * (waypoints[2].x() - waypoints[0].x()) - x_initial[1]);
104 dy.emplace_back(3 * (waypoints[2].y() - waypoints[0].y()) - y_initial[1]);
105 if (waypoints.size() > 4) {
106 for (size_t i = 1; i <= waypoints.size() - 4; ++i) {
107 // dx and dy represent the derivatives of the internal waypoints. The
108 // derivative of the second internal waypoint should involve the third
109 // and first internal waypoint, which have indices of 1 and 3 in the
110 // waypoints list (which contains ALL waypoints).
111 dx.emplace_back(3 * (waypoints[i + 2].x() - waypoints[i].x()));
112 dy.emplace_back(3 * (waypoints[i + 2].y() - waypoints[i].y()));
113 }
114 }
115 dx.emplace_back(3 * (waypoints[waypoints.size() - 1].x() -
116 waypoints[waypoints.size() - 3].x()) -
117 x_final[1]);
118 dy.emplace_back(3 * (waypoints[waypoints.size() - 1].y() -
119 waypoints[waypoints.size() - 3].y()) -
120 y_final[1]);
121
122 // Compute solution to tridiagonal system
123 thomas_algorithm(a, b, c, dx, &fx);
124 thomas_algorithm(a, b, c, dy, &fy);
125
126 fx.emplace(fx.begin(), x_initial[1]);
127 fx.emplace_back(x_final[1]);
128 fy.emplace(fy.begin(), y_initial[1]);
129 fy.emplace_back(y_final[1]);
130
131 for (size_t i = 0; i < fx.size() - 1; ++i) {
132 // Create the spline.
133 const CubicHermiteSpline spline{{waypoints[i].x(), fx[i]},
134 {waypoints[i + 1].x(), fx[i + 1]},
135 {waypoints[i].y(), fy[i]},
136 {waypoints[i + 1].y(), fy[i + 1]}};
137
138 splines.push_back(spline);
139 }
140 } else if (waypoints.size() == 1) {
141 const double x_deriv =
142 (3 * (x_final[0] - x_initial[0]) - x_final[1] - x_initial[1]) / 4.0;
143 const double y_deriv =
144 (3 * (y_final[0] - y_initial[0]) - y_final[1] - y_initial[1]) / 4.0;
145
146 std::array<double, 2> mid_x_control_vector{waypoints[0].x(), x_deriv};
147 std::array<double, 2> mid_y_control_vector{waypoints[0].y(), y_deriv};
148
149 splines.emplace_back(x_initial, mid_x_control_vector, y_initial,
150 mid_y_control_vector);
151 splines.emplace_back(mid_x_control_vector, x_final, mid_y_control_vector,
152 y_final);
153
154 } else {
155 // Create the spline.
156 const CubicHermiteSpline spline{x_initial, x_final, y_initial, y_final};
157 splines.push_back(spline);
158 }
159
160 return splines;
161 }
162
163 private:
164 static Spline<3>::ControlVector cubic_control_vector(double scalar,
165 const Pose2d& point) {
166 return {{point.x(), scalar * point.rotation().cos()},
167 {point.y(), scalar * point.rotation().sin()}};
168 }
169
177 static void thomas_algorithm(const std::vector<double>& a,
178 const std::vector<double>& b,
179 const std::vector<double>& c,
180 const std::vector<double>& d,
181 std::vector<double>* solution_vector) {
182 auto& f = *solution_vector;
183 size_t N = d.size();
184
185 // Create the temporary vectors
186 // Note that this is inefficient as it is possible to call
187 // this function many times. A better implementation would
188 // pass these temporary matrices by non-const reference to
189 // save excess allocation and deallocation
190 std::vector<double> c_star(N, 0.0);
191 std::vector<double> d_star(N, 0.0);
192
193 // This updates the coefficients in the first row
194 // Note that we should be checking for division by zero here
195 c_star[0] = c[0] / b[0];
196 d_star[0] = d[0] / b[0];
197
198 // Create the c_star and d_star coefficients in the forward sweep
199 for (size_t i = 1; i < N; ++i) {
200 double m = 1.0 / (b[i] - a[i] * c_star[i - 1]);
201 c_star[i] = c[i] * m;
202 d_star[i] = (d[i] - a[i] * d_star[i - 1]) * m;
203 }
204
205 f[N - 1] = d_star[N - 1];
206 // This is the reverse sweep, used to update the solution vector f
207 for (int i = N - 2; i >= 0; i--) {
208 f[i] = d_star[i] - c_star[i] * f[i + 1];
209 }
210 }
211};
212
213} // namespace trajopt
Represents a hermite spline of degree 3.
Definition cubic_hermite_spline.hpp:15
constexpr const Rotation2< T > & rotation() const
Definition pose2.hpp:66
constexpr const T & x() const
Definition pose2.hpp:56
constexpr const T & y() const
Definition pose2.hpp:61
constexpr const Translation2< T > & translation() const
Definition pose2.hpp:51
constexpr const T & cos() const
Definition rotation2.hpp:122
constexpr const T & sin() const
Definition rotation2.hpp:127
Definition spline_helper.hpp:19
static std::vector< CubicHermiteSpline > cubic_splines_from_control_vectors(const Spline< 3 >::ControlVector &start, std::vector< Translation2d > waypoints, const Spline< 3 >::ControlVector &end)
Definition spline_helper.hpp:60
static std::array< Spline< 3 >::ControlVector, 2 > cubic_control_vectors_from_waypoints(const Pose2d &start, const std::vector< Translation2d > &interior_waypoints, const Pose2d &end)
Definition spline_helper.hpp:29
constexpr auto distance(const Translation2< U > &other) const
Definition translation2.hpp:167
Definition spline.hpp:35
std::array< double,(Degree+1)/2 > y
The y components of the control vector.
Definition spline.hpp:40
std::array< double,(Degree+1)/2 > x
The x components of the control vector.
Definition spline.hpp:37