InterpolatePy 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- interpolatepy/__init__.py +8 -0
- interpolatepy/b_spline.py +737 -0
- interpolatepy/b_spline_approx.py +544 -0
- interpolatepy/b_spline_cubic.py +444 -0
- interpolatepy/b_spline_interpolate.py +515 -0
- interpolatepy/b_spline_smooth.py +639 -0
- interpolatepy/c_s_smoot_search.py +177 -0
- interpolatepy/c_s_smoothing.py +611 -0
- interpolatepy/c_s_with_acc1.py +643 -0
- interpolatepy/c_s_with_acc2.py +494 -0
- interpolatepy/cubic_spline.py +486 -0
- interpolatepy/double_s.py +580 -0
- interpolatepy/frenet_frame.py +245 -0
- interpolatepy/linear.py +107 -0
- interpolatepy/polynomials.py +451 -0
- interpolatepy/simple_paths.py +281 -0
- interpolatepy/trapezoidal.py +613 -0
- interpolatepy/tridiagonal_inv.py +96 -0
- interpolatepy/version.py +5 -0
- interpolatepy-1.0.0.dist-info/METADATA +415 -0
- interpolatepy-1.0.0.dist-info/RECORD +24 -0
- interpolatepy-1.0.0.dist-info/WHEEL +5 -0
- interpolatepy-1.0.0.dist-info/licenses/LICENSE +21 -0
- interpolatepy-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import matplotlib.pyplot as plt
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from interpolatepy.tridiagonal_inv import solve_tridiagonal
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CubicSpline:
|
|
8
|
+
"""
|
|
9
|
+
Cubic spline trajectory planning implementation.
|
|
10
|
+
|
|
11
|
+
This class implements the cubic spline algorithm described in the document.
|
|
12
|
+
It generates a smooth trajectory passing through specified waypoints with
|
|
13
|
+
continuous velocity and acceleration profiles.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
t_points : list or numpy.ndarray
|
|
18
|
+
List or array of time points (t0, t1, ..., tn)
|
|
19
|
+
q_points : list or numpy.ndarray
|
|
20
|
+
List or array of position points (q0, q1, ..., qn)
|
|
21
|
+
v0 : float, optional
|
|
22
|
+
Initial velocity at t0. Default is 0.0
|
|
23
|
+
vn : float, optional
|
|
24
|
+
Final velocity at tn. Default is 0.0
|
|
25
|
+
debug : bool, optional
|
|
26
|
+
Whether to print debug information. Default is False
|
|
27
|
+
|
|
28
|
+
Attributes
|
|
29
|
+
----------
|
|
30
|
+
t_points : numpy.ndarray
|
|
31
|
+
Array of time points
|
|
32
|
+
q_points : numpy.ndarray
|
|
33
|
+
Array of position points
|
|
34
|
+
v0 : float
|
|
35
|
+
Initial velocity
|
|
36
|
+
vn : float
|
|
37
|
+
Final velocity
|
|
38
|
+
debug : bool
|
|
39
|
+
Debug flag
|
|
40
|
+
n : int
|
|
41
|
+
Number of segments (n = len(t_points) - 1)
|
|
42
|
+
t_intervals : numpy.ndarray
|
|
43
|
+
Time intervals between consecutive points
|
|
44
|
+
velocities : numpy.ndarray
|
|
45
|
+
Velocities at each waypoint
|
|
46
|
+
coefficients : numpy.ndarray
|
|
47
|
+
Polynomial coefficients for each segment
|
|
48
|
+
|
|
49
|
+
Notes
|
|
50
|
+
-----
|
|
51
|
+
The cubic spline ensures C2 continuity (continuous position, velocity, and
|
|
52
|
+
acceleration) across all waypoints.
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> import numpy as np
|
|
57
|
+
>>> # Define waypoints
|
|
58
|
+
>>> t_points = [0, 1, 2, 3]
|
|
59
|
+
>>> q_points = [0, 1, 0, 1]
|
|
60
|
+
>>> # Create spline
|
|
61
|
+
>>> spline = CubicSpline(t_points, q_points)
|
|
62
|
+
>>> # Evaluate at specific time
|
|
63
|
+
>>> spline.evaluate(1.5)
|
|
64
|
+
>>> # Plot the trajectory
|
|
65
|
+
>>> spline.plot()
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
t_points: list[float] | np.ndarray,
|
|
71
|
+
q_points: list[float] | np.ndarray,
|
|
72
|
+
v0: float = 0.0,
|
|
73
|
+
vn: float = 0.0,
|
|
74
|
+
debug: bool = False,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Initialize a cubic spline trajectory.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
t_points : list or numpy.ndarray
|
|
82
|
+
List or array of time points (t0, t1, ..., tn)
|
|
83
|
+
q_points : list or numpy.ndarray
|
|
84
|
+
List or array of position points (q0, q1, ..., qn)
|
|
85
|
+
v0 : float, optional
|
|
86
|
+
Initial velocity at t0. Default is 0.0
|
|
87
|
+
vn : float, optional
|
|
88
|
+
Final velocity at tn. Default is 0.0
|
|
89
|
+
debug : bool, optional
|
|
90
|
+
Whether to print debug information. Default is False
|
|
91
|
+
|
|
92
|
+
Raises
|
|
93
|
+
------
|
|
94
|
+
ValueError
|
|
95
|
+
If t_points and q_points have different lengths
|
|
96
|
+
If t_points are not strictly increasing
|
|
97
|
+
"""
|
|
98
|
+
# Ensure inputs are numpy arrays
|
|
99
|
+
self.t_points = np.array(t_points, dtype=float)
|
|
100
|
+
self.q_points = np.array(q_points, dtype=float)
|
|
101
|
+
self.v0 = float(v0)
|
|
102
|
+
self.vn = float(vn)
|
|
103
|
+
self.debug = debug
|
|
104
|
+
|
|
105
|
+
# Check input validity
|
|
106
|
+
if len(self.t_points) != len(self.q_points):
|
|
107
|
+
raise ValueError("Time points and position points must have the same length")
|
|
108
|
+
|
|
109
|
+
if not np.all(np.diff(self.t_points) > 0):
|
|
110
|
+
raise ValueError("Time points must be strictly increasing")
|
|
111
|
+
|
|
112
|
+
# Compute time intervals
|
|
113
|
+
self.n = len(self.t_points) - 1
|
|
114
|
+
self.t_intervals = np.diff(self.t_points)
|
|
115
|
+
|
|
116
|
+
# Compute intermediate velocities and coefficients
|
|
117
|
+
self.velocities = self._compute_velocities()
|
|
118
|
+
self.coefficients = self._compute_coefficients()
|
|
119
|
+
|
|
120
|
+
def _compute_velocities(self) -> np.ndarray:
|
|
121
|
+
"""
|
|
122
|
+
Compute the velocities at intermediate points by solving
|
|
123
|
+
the tridiagonal system described in the document.
|
|
124
|
+
|
|
125
|
+
This method implements the mathematical formulation from pages 4-5 of the document,
|
|
126
|
+
corresponding to equation (17). It sets up and solves the tridiagonal system A*v = c
|
|
127
|
+
to find the intermediate velocities, which ensures C2 continuity of the spline.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
numpy.ndarray
|
|
132
|
+
Array of velocities [v0, v1, ..., vn]
|
|
133
|
+
|
|
134
|
+
Notes
|
|
135
|
+
-----
|
|
136
|
+
The tridiagonal system is formed as follows:
|
|
137
|
+
|
|
138
|
+
Matrix A:
|
|
139
|
+
[2(T0+T1) T0 0 ... 0 ]
|
|
140
|
+
[T2 2(T1+T2) T1 0 ... ]
|
|
141
|
+
[0 T3 2(T2+T3) T2 ... ]
|
|
142
|
+
[... ... ... ... ... ]
|
|
143
|
+
[0 ... 0 Tn-2 2(Tn-3+Tn-2) Tn-3]
|
|
144
|
+
[0 ... 0 0 Tn-1 2(Tn-2+Tn-1)]
|
|
145
|
+
|
|
146
|
+
Vector c:
|
|
147
|
+
c_i = 3/(T_i*T_{i+1}) * [T_i^2*(q_{i+2}-q_{i+1}) + T_{i+1}^2*(q_{i+1}-q_i)]
|
|
148
|
+
"""
|
|
149
|
+
n = self.n
|
|
150
|
+
t_intervals = self.t_intervals
|
|
151
|
+
q = self.q_points
|
|
152
|
+
|
|
153
|
+
# Create the tridiagonal matrix A as shown in the document:
|
|
154
|
+
# Matrix A has the following structure:
|
|
155
|
+
# [2(T0+T1) T0 0 ... 0 ]
|
|
156
|
+
# [T2 2(T1+T2) T1 0 ... ]
|
|
157
|
+
# [0 T3 2(T2+T3) T2 ... ]
|
|
158
|
+
# [... ... ... ... ... ]
|
|
159
|
+
# [0 ... 0 Tn-2 2(Tn-3+Tn-2) Tn-3]
|
|
160
|
+
# [0 ... 0 0 Tn-1 2(Tn-2+Tn-1)]
|
|
161
|
+
|
|
162
|
+
if n == 1:
|
|
163
|
+
# Special case: only one segment
|
|
164
|
+
# We know v0 and vn, so no need to solve system
|
|
165
|
+
return np.array([self.v0, self.vn])
|
|
166
|
+
|
|
167
|
+
# Create the right-hand side vector c from equation (17) in the document
|
|
168
|
+
# c_i = 3/(T_i*T_{i+1}) * [T_i^2*(q_{i+2}-q_{i+1}) + T_{i+1}^2*(q_{i+1}-q_i)]
|
|
169
|
+
rhs = np.zeros(n - 1)
|
|
170
|
+
|
|
171
|
+
for i in range(n - 1):
|
|
172
|
+
rhs[i] = (
|
|
173
|
+
3
|
|
174
|
+
/ (t_intervals[i] * t_intervals[i + 1])
|
|
175
|
+
* (
|
|
176
|
+
t_intervals[i] ** 2 * (q[i + 2] - q[i + 1])
|
|
177
|
+
+ t_intervals[i + 1] ** 2 * (q[i + 1] - q[i])
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Adjust the right-hand side to account for known boundary velocities v0 and vn
|
|
182
|
+
# These adjustments come from moving the terms with known velocities to the RHS
|
|
183
|
+
if n > 1:
|
|
184
|
+
# First equation: subtract T_1*v_0 from RHS
|
|
185
|
+
rhs[0] -= t_intervals[1] * self.v0
|
|
186
|
+
|
|
187
|
+
# Last equation: subtract T_{n-2}*v_n from RHS
|
|
188
|
+
rhs[-1] -= t_intervals[-2] * self.vn
|
|
189
|
+
|
|
190
|
+
# Solve the system for the intermediate velocities v1, ..., v(n-1)
|
|
191
|
+
# Case n=2 means we have exactly one intermediate velocity to solve (1x1 system)
|
|
192
|
+
if n == 2: # noqa: PLR2004
|
|
193
|
+
# Special case: only one intermediate velocity to solve for
|
|
194
|
+
# Simple division is sufficient (1x1 system)
|
|
195
|
+
main_diag_value = 2 * (t_intervals[0] + t_intervals[1])
|
|
196
|
+
v_intermediate = rhs / main_diag_value
|
|
197
|
+
else:
|
|
198
|
+
# Instead of building the full matrix, extract the diagonal elements for the
|
|
199
|
+
# tridiagonal solver
|
|
200
|
+
|
|
201
|
+
# Main diagonal: 2(T_i + T_{i+1})
|
|
202
|
+
main_diag = np.zeros(n - 1)
|
|
203
|
+
for i in range(n - 1):
|
|
204
|
+
main_diag[i] = 2 * (t_intervals[i] + t_intervals[i + 1])
|
|
205
|
+
|
|
206
|
+
# Lower diagonal: T_{i+1} (first element not used in solve_tridiagonal)
|
|
207
|
+
lower_diag = np.zeros(n - 1)
|
|
208
|
+
for i in range(1, n - 1):
|
|
209
|
+
lower_diag[i] = t_intervals[i + 1]
|
|
210
|
+
|
|
211
|
+
# Upper diagonal: T_i (last element not used in solve_tridiagonal)
|
|
212
|
+
upper_diag = np.zeros(n - 1)
|
|
213
|
+
for i in range(n - 2):
|
|
214
|
+
upper_diag[i] = t_intervals[i]
|
|
215
|
+
|
|
216
|
+
# Print debug information only if debug is enabled
|
|
217
|
+
if self.debug:
|
|
218
|
+
print("\nTridiagonal Matrix components:")
|
|
219
|
+
print("Main diagonal:", main_diag)
|
|
220
|
+
print("Lower diagonal:", lower_diag)
|
|
221
|
+
print("Upper diagonal:", upper_diag)
|
|
222
|
+
print("Right-hand side vector:", rhs)
|
|
223
|
+
|
|
224
|
+
# Solve the tridiagonal system using the Thomas algorithm
|
|
225
|
+
v_intermediate = solve_tridiagonal(lower_diag, main_diag, upper_diag, rhs)
|
|
226
|
+
|
|
227
|
+
# Print the intermediate velocities if debug is enabled
|
|
228
|
+
if self.debug:
|
|
229
|
+
print("\nIntermediate velocities v_1 to v_{n-1}:")
|
|
230
|
+
print(v_intermediate)
|
|
231
|
+
|
|
232
|
+
# Construct the full velocity array by including the known boundary velocities
|
|
233
|
+
velocities = np.zeros(n + 1)
|
|
234
|
+
velocities[0] = self.v0 # Initial velocity (given)
|
|
235
|
+
velocities[1:-1] = v_intermediate # Intermediate velocities (computed)
|
|
236
|
+
velocities[-1] = self.vn # Final velocity (given)
|
|
237
|
+
|
|
238
|
+
# Print the complete velocity vector if debug is enabled
|
|
239
|
+
if self.debug:
|
|
240
|
+
print("\nComplete velocity vector v:")
|
|
241
|
+
print(velocities)
|
|
242
|
+
|
|
243
|
+
return velocities
|
|
244
|
+
|
|
245
|
+
def _compute_coefficients(self) -> np.ndarray:
|
|
246
|
+
"""
|
|
247
|
+
Compute the coefficients for each cubic polynomial segment.
|
|
248
|
+
|
|
249
|
+
For each segment k, we compute:
|
|
250
|
+
- ak0: constant term
|
|
251
|
+
- ak1: coefficient of (t-tk)
|
|
252
|
+
- ak2: coefficient of (t-tk)^2
|
|
253
|
+
- ak3: coefficient of (t-tk)^3
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
numpy.ndarray
|
|
258
|
+
Array of shape (n, 4) containing the coefficients for each segment
|
|
259
|
+
where n is the number of segments
|
|
260
|
+
|
|
261
|
+
Notes
|
|
262
|
+
-----
|
|
263
|
+
The cubic polynomial for segment k is defined as:
|
|
264
|
+
q(t) = ak0 + ak1*(t-tk) + ak2*(t-tk)^2 + ak3*(t-tk)^3
|
|
265
|
+
|
|
266
|
+
The coefficients are computed to ensure position, velocity, and
|
|
267
|
+
acceleration continuity at the waypoints.
|
|
268
|
+
"""
|
|
269
|
+
n = self.n
|
|
270
|
+
t_intervals = self.t_intervals
|
|
271
|
+
q = self.q_points
|
|
272
|
+
v = self.velocities
|
|
273
|
+
|
|
274
|
+
coeffs = np.zeros((n, 4))
|
|
275
|
+
|
|
276
|
+
for k in range(n):
|
|
277
|
+
coeffs[k, 0] = q[k] # ak0 = qk
|
|
278
|
+
coeffs[k, 1] = v[k] # ak1 = vk
|
|
279
|
+
|
|
280
|
+
# Compute ak2 and ak3
|
|
281
|
+
coeffs[k, 2] = (1 / t_intervals[k]) * (
|
|
282
|
+
(3 * (q[k + 1] - q[k]) / t_intervals[k]) - 2 * v[k] - v[k + 1]
|
|
283
|
+
)
|
|
284
|
+
coeffs[k, 3] = (1 / t_intervals[k] ** 2) * (
|
|
285
|
+
(2 * (q[k] - q[k + 1]) / t_intervals[k]) + v[k] + v[k + 1]
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# Print detailed coefficient calculation if debug is enabled
|
|
289
|
+
if self.debug:
|
|
290
|
+
print(f"\nCoefficient calculation for segment {k}:")
|
|
291
|
+
print(f" ak0 = {coeffs[k, 0]}")
|
|
292
|
+
print(f" ak1 = {coeffs[k, 1]}")
|
|
293
|
+
print(f" ak2 = {coeffs[k, 2]}")
|
|
294
|
+
print(f" ak3 = {coeffs[k, 3]}")
|
|
295
|
+
|
|
296
|
+
return coeffs
|
|
297
|
+
|
|
298
|
+
def evaluate(self, t: float | np.ndarray) -> float | np.ndarray:
|
|
299
|
+
"""
|
|
300
|
+
Evaluate the spline at time t.
|
|
301
|
+
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
t : float or numpy.ndarray
|
|
305
|
+
Time point or array of time points
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
float or numpy.ndarray
|
|
310
|
+
Position(s) at the specified time(s)
|
|
311
|
+
|
|
312
|
+
Examples
|
|
313
|
+
--------
|
|
314
|
+
>>> # Evaluate at a single time point
|
|
315
|
+
>>> spline.evaluate(1.5)
|
|
316
|
+
>>> # Evaluate at multiple time points
|
|
317
|
+
>>> spline.evaluate(np.linspace(0, 3, 100))
|
|
318
|
+
"""
|
|
319
|
+
t = np.atleast_1d(t)
|
|
320
|
+
result = np.zeros_like(t)
|
|
321
|
+
|
|
322
|
+
for i, ti in enumerate(t):
|
|
323
|
+
# Find the segment that contains ti
|
|
324
|
+
if ti <= self.t_points[0]:
|
|
325
|
+
# Before the start of the trajectory
|
|
326
|
+
k = 0
|
|
327
|
+
tau = 0
|
|
328
|
+
elif ti >= self.t_points[-1]:
|
|
329
|
+
# After the end of the trajectory
|
|
330
|
+
k = self.n - 1
|
|
331
|
+
tau = self.t_intervals[k]
|
|
332
|
+
else:
|
|
333
|
+
# Within the trajectory
|
|
334
|
+
# Find the largest k such that t_k <= ti
|
|
335
|
+
k = np.searchsorted(self.t_points, ti, side="right") - 1
|
|
336
|
+
tau = ti - self.t_points[k]
|
|
337
|
+
|
|
338
|
+
# Evaluate the polynomial
|
|
339
|
+
a = self.coefficients[k]
|
|
340
|
+
result[i] = a[0] + a[1] * tau + a[2] * tau**2 + a[3] * tau**3
|
|
341
|
+
|
|
342
|
+
return result[0] if len(result) == 1 else result
|
|
343
|
+
|
|
344
|
+
def evaluate_velocity(self, t: float | np.ndarray) -> float | np.ndarray:
|
|
345
|
+
"""
|
|
346
|
+
Evaluate the velocity at time t.
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
t : float or numpy.ndarray
|
|
351
|
+
Time point or array of time points
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
float or numpy.ndarray
|
|
356
|
+
Velocity at the specified time(s)
|
|
357
|
+
|
|
358
|
+
Examples
|
|
359
|
+
--------
|
|
360
|
+
>>> # Evaluate velocity at a single time point
|
|
361
|
+
>>> spline.evaluate_velocity(1.5)
|
|
362
|
+
>>> # Evaluate velocity at multiple time points
|
|
363
|
+
>>> spline.evaluate_velocity(np.linspace(0, 3, 100))
|
|
364
|
+
"""
|
|
365
|
+
t = np.atleast_1d(t)
|
|
366
|
+
result = np.zeros_like(t)
|
|
367
|
+
|
|
368
|
+
for i, ti in enumerate(t):
|
|
369
|
+
# Find the segment that contains ti
|
|
370
|
+
if ti <= self.t_points[0]:
|
|
371
|
+
# Before the start of the trajectory
|
|
372
|
+
k = 0
|
|
373
|
+
tau = 0
|
|
374
|
+
elif ti >= self.t_points[-1]:
|
|
375
|
+
# After the end of the trajectory
|
|
376
|
+
k = self.n - 1
|
|
377
|
+
tau = self.t_intervals[k]
|
|
378
|
+
else:
|
|
379
|
+
# Within the trajectory
|
|
380
|
+
k = np.searchsorted(self.t_points, ti, side="right") - 1
|
|
381
|
+
tau = ti - self.t_points[k]
|
|
382
|
+
|
|
383
|
+
# Evaluate the derivative of the polynomial
|
|
384
|
+
a = self.coefficients[k]
|
|
385
|
+
result[i] = a[1] + 2 * a[2] * tau + 3 * a[3] * tau**2
|
|
386
|
+
|
|
387
|
+
return result[0] if len(result) == 1 else result
|
|
388
|
+
|
|
389
|
+
def evaluate_acceleration(self, t: float | np.ndarray) -> float | np.ndarray:
|
|
390
|
+
"""
|
|
391
|
+
Evaluate the acceleration at time t.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
t : float or numpy.ndarray
|
|
396
|
+
Time point or array of time points
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
float or numpy.ndarray
|
|
401
|
+
Acceleration at the specified time(s)
|
|
402
|
+
|
|
403
|
+
Examples
|
|
404
|
+
--------
|
|
405
|
+
>>> # Evaluate acceleration at a single time point
|
|
406
|
+
>>> spline.evaluate_acceleration(1.5)
|
|
407
|
+
>>> # Evaluate acceleration at multiple time points
|
|
408
|
+
>>> spline.evaluate_acceleration(np.linspace(0, 3, 100))
|
|
409
|
+
"""
|
|
410
|
+
t = np.atleast_1d(t)
|
|
411
|
+
result = np.zeros_like(t)
|
|
412
|
+
|
|
413
|
+
for i, ti in enumerate(t):
|
|
414
|
+
# Find the segment that contains ti
|
|
415
|
+
if ti <= self.t_points[0]:
|
|
416
|
+
# Before the start of the trajectory
|
|
417
|
+
k = 0
|
|
418
|
+
tau = 0
|
|
419
|
+
elif ti >= self.t_points[-1]:
|
|
420
|
+
# After the end of the trajectory
|
|
421
|
+
k = self.n - 1
|
|
422
|
+
tau = self.t_intervals[k]
|
|
423
|
+
else:
|
|
424
|
+
# Within the trajectory
|
|
425
|
+
k = np.searchsorted(self.t_points, ti, side="right") - 1
|
|
426
|
+
tau = ti - self.t_points[k]
|
|
427
|
+
|
|
428
|
+
# Evaluate the second derivative of the polynomial
|
|
429
|
+
a = self.coefficients[k]
|
|
430
|
+
result[i] = 2 * a[2] + 6 * a[3] * tau
|
|
431
|
+
|
|
432
|
+
return result[0] if len(result) == 1 else result
|
|
433
|
+
|
|
434
|
+
def plot(self, num_points: int = 1000) -> None:
|
|
435
|
+
"""
|
|
436
|
+
Plot the spline trajectory along with its velocity and acceleration profiles.
|
|
437
|
+
|
|
438
|
+
Parameters
|
|
439
|
+
----------
|
|
440
|
+
num_points : int, optional
|
|
441
|
+
Number of points to use for plotting. Default is 1000
|
|
442
|
+
|
|
443
|
+
Returns
|
|
444
|
+
-------
|
|
445
|
+
None
|
|
446
|
+
Displays the plot using matplotlib
|
|
447
|
+
|
|
448
|
+
Notes
|
|
449
|
+
-----
|
|
450
|
+
This method creates a figure with three subplots showing:
|
|
451
|
+
1. Position trajectory with waypoints
|
|
452
|
+
2. Velocity profile
|
|
453
|
+
3. Acceleration profile
|
|
454
|
+
|
|
455
|
+
The original waypoints are marked with red circles on the position plot.
|
|
456
|
+
"""
|
|
457
|
+
t_min, t_max = self.t_points[0], self.t_points[-1]
|
|
458
|
+
t = np.linspace(t_min, t_max, num_points)
|
|
459
|
+
|
|
460
|
+
q = self.evaluate(t)
|
|
461
|
+
v = self.evaluate_velocity(t)
|
|
462
|
+
a = self.evaluate_acceleration(t)
|
|
463
|
+
|
|
464
|
+
_fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(10, 8), sharex=True)
|
|
465
|
+
|
|
466
|
+
# Position plot
|
|
467
|
+
ax1.plot(t, q, "b-", linewidth=2)
|
|
468
|
+
ax1.plot(self.t_points, self.q_points, "ro", markersize=8)
|
|
469
|
+
ax1.set_ylabel("Position")
|
|
470
|
+
ax1.grid(True)
|
|
471
|
+
ax1.set_title("Cubic Spline Trajectory")
|
|
472
|
+
|
|
473
|
+
# Velocity plot
|
|
474
|
+
ax2.plot(t, v, "g-", linewidth=2)
|
|
475
|
+
ax2.plot(self.t_points, self.velocities, "ro", markersize=6)
|
|
476
|
+
ax2.set_ylabel("Velocity")
|
|
477
|
+
ax2.grid(True)
|
|
478
|
+
|
|
479
|
+
# Acceleration plot
|
|
480
|
+
ax3.plot(t, a, "r-", linewidth=2)
|
|
481
|
+
ax3.set_ylabel("Acceleration")
|
|
482
|
+
ax3.set_xlabel("Time")
|
|
483
|
+
ax3.grid(True)
|
|
484
|
+
|
|
485
|
+
plt.tight_layout()
|
|
486
|
+
plt.show()
|