FancySafeBot 0.0.1
A safe robotics library
Loading...
Searching...
No Matches
fsb_spline.h
1#ifndef FSB_SPLINE_H
2#define FSB_SPLINE_H
3
4#include <array>
5#include <cstdint>
6#include "fsb_trajectory_types.h"
7
8namespace fsb
9{
10
11using SplineCoeffs = std::array<Real, 4U>;
12
13enum class SplineEndCondition : uint8_t
14{
15 Natural = 0U,
16 Clamped = 1U
17};
18
19enum class SplineError : uint8_t
20{
21 SUCCESS = 0U,
22 INVALID_STEP_SIZE = 1U,
23 NOT_ENOUGH_POINTS = 2U,
24 TOO_MANY_POINTS = 3U,
25 NOT_GENERATED = 4U,
26 SINGULAR_SYSTEM = 5U
27};
28
29template <size_t MaxPoints>
30class Spline final : public SegmentScalar
31{
32public:
33 Spline() = default;
34
35 template <size_t NumPoints>
36 [[nodiscard]] SplineError generate(const Real step_size,
37 const std::array<Real, NumPoints>& points,
38 const SplineEndCondition end_condition = SplineEndCondition::Natural,
39 const Real start_slope = 0.0,
40 const Real end_slope = 0.0)
41 {
42 if (NumPoints > MaxPoints)
43 {
44 return SplineError::TOO_MANY_POINTS;
45 }
46 if (NumPoints < 2U)
47 {
48 return SplineError::NOT_ENOUGH_POINTS;
49 }
50 if (step_size <= FSB_TOL)
51 {
52 return SplineError::INVALID_STEP_SIZE;
53 }
54
55 const SplineBuildParameters parameters = {NumPoints, step_size, end_condition,
56 start_slope, end_slope};
57 const SplineBuildResult result = build_spline(points, parameters);
58 if (result.error == SplineError::SUCCESS)
59 {
60 m_start_time = 0.0;
61 m_step_size = step_size;
62 m_duration = static_cast<Real>(NumPoints - 1U) * step_size;
63 m_end_condition = end_condition;
64 m_start_slope = start_slope;
65 m_end_slope = end_slope;
66 for (size_t i = 0U; i < NumPoints; ++i)
67 {
68 m_points[i] = points[i];
69 }
70 m_num_points = NumPoints;
71 m_spline = result.spline;
72 }
73
74 return result.error;
75 }
76
77 [[nodiscard]] TrajState evaluate(Real t_eval) const override
78 {
79 if (m_num_points < 2U)
80 {
81 return {};
82 }
83
84 Real t_local = t_eval - m_start_time;
85 if (t_local < 0.0)
86 {
87 t_local = 0.0;
88 }
89 if (t_local > m_duration)
90 {
91 t_local = m_duration;
92 }
93
94 const Real t_index = t_local / m_step_size;
95 const auto k = static_cast<size_t>(t_index);
96 const size_t max_segment = m_num_points - 2U;
97 const auto k_clamped = (k > max_segment) ? max_segment : k;
98
99 const Real dt = t_index - static_cast<Real>(k_clamped);
100 const Real a = m_spline[k_clamped][0];
101 const Real b = m_spline[k_clamped][1];
102 const Real c = m_spline[k_clamped][2];
103 const Real d = m_spline[k_clamped][3];
104
105 const Real inv_h = 1.0 / m_step_size;
106 const Real inv_h2 = inv_h * inv_h;
107 const Real inv_h3 = inv_h2 * inv_h;
108
109 const Real position = (((a * dt) + b) * dt + c) * dt + d;
110 const Real velocity = (((3.0 * a * dt) + (2.0 * b)) * dt + c) * inv_h;
111 const Real acceleration = ((6.0 * a * dt) + (2.0 * b)) * inv_h2;
112 const Real jerk = (6.0 * a) * inv_h3;
113
114 return {position, velocity, acceleration, jerk};
115 }
116
117 [[nodiscard]] TrajState get_final_state() const override
118 {
119 return evaluate(get_final_time());
120 }
121
122 [[nodiscard]] TrajState get_initial_state() const override
123 {
124 return evaluate(m_start_time);
125 }
126
127 [[nodiscard]] Real get_start_time() const override
128 {
129 return m_start_time;
130 }
131
132 [[nodiscard]] Real get_duration() const override
133 {
134 return m_duration;
135 }
136
137 [[nodiscard]] Real get_final_time() const override
138 {
139 return m_start_time + m_duration;
140 }
141
142private:
143 struct SplineBuildParameters
144 {
145 size_t num_points = 0U;
146 Real step_size = 0.0;
147 SplineEndCondition end_condition = SplineEndCondition::Natural;
148 Real start_slope = 0.0;
149 Real end_slope = 0.0;
150 };
151
152 struct SplineBuildResult
153 {
154 SplineError error = SplineError::SUCCESS;
155 std::array<Real, MaxPoints> second_derivative = {};
156 std::array<SplineCoeffs, MaxPoints> spline = {};
157 };
158
159 template <size_t NumPoints>
160 [[nodiscard]] static SplineBuildResult build_spline(
161 const std::array<Real, NumPoints>& points, const SplineBuildParameters& parameters)
162 {
163 SplineBuildResult result = {};
164
165 if (parameters.end_condition == SplineEndCondition::Natural)
166 {
167 result = build_natural_spline(points, parameters);
168 }
169 else
170 {
171 result = build_clamped_spline(points, parameters);
172 }
173
174 return result;
175 }
176
187 template <size_t NumPoints>
188 [[nodiscard]] static SplineBuildResult build_natural_spline(
189 const std::array<Real, NumPoints>& points, const SplineBuildParameters& parameters)
190 {
191 SplineBuildResult result = {};
192
193 if (parameters.num_points < 2U)
194 {
195 result.error = SplineError::NOT_ENOUGH_POINTS;
196 return result;
197 }
198
199 if (parameters.num_points > 2U)
200 {
201 std::array<Real, MaxPoints> c_prime = {};
202 std::array<Real, MaxPoints> d_prime = {};
203
204 c_prime[1] = 1.0 / 4.0;
205 d_prime[1] = 6.0 * (points[2] - (2.0 * points[1]) + points[0]) / 4.0;
206
207 for (size_t i = 2U; i < (parameters.num_points - 1U); ++i)
208 {
209 const Real rhs = 6.0 * (points[i + 1U] - (2.0 * points[i]) + points[i - 1U]);
210 const Real denom = 4.0 - c_prime[i - 1U];
211 c_prime[i] = 1.0 / denom;
212 d_prime[i] = (rhs - d_prime[i - 1U]) / denom;
213 }
214
215 result.second_derivative[parameters.num_points - 2U] = d_prime[parameters.num_points - 2U];
216 for (size_t i = parameters.num_points - 2U; i > 1U; --i)
217 {
218 result.second_derivative[i - 1U] = d_prime[i - 1U]
219 - (c_prime[i - 1U] * result.second_derivative[i]);
220 }
221 }
222
223 for (size_t k = 0U; k < (parameters.num_points - 1U); ++k)
224 {
225 const Real m0 = result.second_derivative[k];
226 const Real m1 = result.second_derivative[k + 1U];
227 const Real y0 = points[k];
228 const Real y1 = points[k + 1U];
229
230 result.spline[k][0] = (m1 - m0) / 6.0;
231 result.spline[k][1] = m0 / 2.0;
232 result.spline[k][2] = (y1 - y0) - ((2.0 * m0 + m1) / 6.0);
233 result.spline[k][3] = y0;
234 }
235
236 return result;
237 }
238
245 template <size_t NumPoints>
246 [[nodiscard]] static SplineBuildResult build_clamped_spline(
247 const std::array<Real, NumPoints>& points, const SplineBuildParameters& parameters)
248 {
249 SplineBuildResult result = {};
250
251 if (parameters.num_points < 2U)
252 {
253 result.error = SplineError::NOT_ENOUGH_POINTS;
254 return result;
255 }
256
257 const size_t n = parameters.num_points - 1U;
258
259 std::array<Real, MaxPoints> lower = {};
260 std::array<Real, MaxPoints> diag = {};
261 std::array<Real, MaxPoints> upper = {};
262 std::array<Real, MaxPoints> rhs = {};
263
264 const Real start_slope_scaled = parameters.start_slope * parameters.step_size;
265 const Real end_slope_scaled = parameters.end_slope * parameters.step_size;
266
267 diag[0] = 2.0;
268 upper[0] = 1.0;
269 rhs[0] = 6.0 * ((points[1] - points[0]) - start_slope_scaled);
270
271 for (size_t i = 1U; i < n; ++i)
272 {
273 lower[i] = 1.0;
274 diag[i] = 4.0;
275 upper[i] = 1.0;
276 rhs[i] = 6.0 * (points[i + 1U] - (2.0 * points[i]) + points[i - 1U]);
277 }
278
279 lower[n] = 1.0;
280 diag[n] = 2.0;
281 rhs[n] = 6.0 * (end_slope_scaled - (points[n] - points[n - 1U]));
282
283 for (size_t i = 1U; i <= n; ++i)
284 {
285 if ((diag[i - 1U] > -FSB_TOL) && (diag[i - 1U] < FSB_TOL))
286 {
287 result.error = SplineError::SINGULAR_SYSTEM;
288 return result;
289 }
290
291 const Real w = lower[i] / diag[i - 1U];
292 diag[i] -= w * upper[i - 1U];
293 rhs[i] -= w * rhs[i - 1U];
294 }
295
296 if ((diag[n] > -FSB_TOL) && (diag[n] < FSB_TOL))
297 {
298 result.error = SplineError::SINGULAR_SYSTEM;
299 return result;
300 }
301 result.second_derivative[n] = rhs[n] / diag[n];
302
303 for (size_t i = n; i > 0U; --i)
304 {
305 if ((diag[i - 1U] > -FSB_TOL) && (diag[i - 1U] < FSB_TOL))
306 {
307 result.error = SplineError::SINGULAR_SYSTEM;
308 return result;
309 }
310 result.second_derivative[i - 1U] =
311 (rhs[i - 1U] - (upper[i - 1U] * result.second_derivative[i])) / diag[i - 1U];
312 }
313
314 for (size_t k = 0U; k < n; ++k)
315 {
316 const Real m0 = result.second_derivative[k];
317 const Real m1 = result.second_derivative[k + 1U];
318 const Real y0 = points[k];
319 const Real y1 = points[k + 1U];
320
321 result.spline[k][0] = (m1 - m0) / 6.0;
322 result.spline[k][1] = m0 / 2.0;
323 result.spline[k][2] = (y1 - y0) - ((2.0 * m0 + m1) / 6.0);
324 result.spline[k][3] = y0;
325 }
326
327 return result;
328 }
329
330private:
331 Real m_start_time = 0.0;
332 Real m_step_size = 0.0;
333 Real m_duration = 0.0;
334 SplineEndCondition m_end_condition = SplineEndCondition::Natural;
335 Real m_start_slope = 0.0;
336 Real m_end_slope = 0.0;
337
338 std::array<Real, MaxPoints> m_points = {};
339 std::array<SplineCoeffs, MaxPoints> m_spline = {};
340 size_t m_num_points = 0U;
341};
342
343}
344
345#endif // FSB_SPLINE_H
Abstract class for segments.
Definition fsb_trajectory_types.h:67
Definition fsb_spline.h:31
Real get_start_time() const override
Get start time of the segment.
Definition fsb_spline.h:127
TrajState evaluate(Real t_eval) const override
Evaluate the segment at a given time.
Definition fsb_spline.h:77
TrajState get_final_state() const override
Get final state of the segment.
Definition fsb_spline.h:117
Real get_duration() const override
Get duration of the segment.
Definition fsb_spline.h:132
TrajState get_initial_state() const override
Get initial state of the segment.
Definition fsb_spline.h:122
Real get_final_time() const override
Get final time of the segment.
Definition fsb_spline.h:137
@ SUCCESS
Successful operation.
Trajectory scalar motion state.
Definition fsb_trajectory_types.h:20