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,494 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from interpolatepy.cubic_spline import CubicSpline
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class SplineParameters:
|
|
10
|
+
"""
|
|
11
|
+
Container for spline initialization parameters.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
v0 : float, default=0.0
|
|
16
|
+
Initial velocity constraint at the start of the spline.
|
|
17
|
+
vn : float, default=0.0
|
|
18
|
+
Final velocity constraint at the end of the spline.
|
|
19
|
+
a0 : float, optional
|
|
20
|
+
Initial acceleration constraint at the start of the spline.
|
|
21
|
+
If provided, the first segment will use a quintic polynomial.
|
|
22
|
+
an : float, optional
|
|
23
|
+
Final acceleration constraint at the end of the spline.
|
|
24
|
+
If provided, the last segment will use a quintic polynomial.
|
|
25
|
+
debug : bool, default=False
|
|
26
|
+
If True, prints detailed information about the spline calculation.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
v0: float = 0.0
|
|
30
|
+
vn: float = 0.0
|
|
31
|
+
a0: float | None = None
|
|
32
|
+
an: float | None = None
|
|
33
|
+
debug: bool = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CubicSplineWithAcceleration2(CubicSpline):
|
|
37
|
+
"""
|
|
38
|
+
Cubic spline trajectory planning with initial and final acceleration constraints.
|
|
39
|
+
|
|
40
|
+
This class extends CubicSpline to handle initial and final acceleration constraints
|
|
41
|
+
by using 5th degree polynomials for the first and last segments, as mentioned in
|
|
42
|
+
section 4.4.4 of the paper.
|
|
43
|
+
|
|
44
|
+
The spline consists of cubic polynomial segments for interior segments and
|
|
45
|
+
optional quintic polynomial segments for the first and/or last segment when
|
|
46
|
+
acceleration constraints are specified.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
t_points : array_like
|
|
51
|
+
Array of time points (t0, t1, ..., tn).
|
|
52
|
+
q_points : array_like
|
|
53
|
+
Array of position points (q0, q1, ..., qn).
|
|
54
|
+
params : SplineParameters, optional
|
|
55
|
+
Spline parameters including initial/final velocities and accelerations.
|
|
56
|
+
If None, default parameters will be used.
|
|
57
|
+
|
|
58
|
+
Attributes
|
|
59
|
+
----------
|
|
60
|
+
t_points : ndarray
|
|
61
|
+
Array of time points.
|
|
62
|
+
q_points : ndarray
|
|
63
|
+
Array of position points.
|
|
64
|
+
velocities : ndarray
|
|
65
|
+
Array of velocities at each point.
|
|
66
|
+
t_intervals : ndarray
|
|
67
|
+
Array of time intervals between consecutive points.
|
|
68
|
+
n : int
|
|
69
|
+
Number of segments.
|
|
70
|
+
coefficients : ndarray
|
|
71
|
+
Array of coefficients for each cubic segment.
|
|
72
|
+
a0 : float or None
|
|
73
|
+
Initial acceleration constraint.
|
|
74
|
+
an : float or None
|
|
75
|
+
Final acceleration constraint.
|
|
76
|
+
quintic_coeffs : dict
|
|
77
|
+
Dictionary containing quintic coefficients for first and/or last segments.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
t_points: list[float] | np.ndarray,
|
|
83
|
+
q_points: list[float] | np.ndarray,
|
|
84
|
+
params: SplineParameters | None = None,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""
|
|
87
|
+
Initialize a cubic spline trajectory with optional initial and final accelerations.
|
|
88
|
+
|
|
89
|
+
Parameters
|
|
90
|
+
----------
|
|
91
|
+
t_points : array_like
|
|
92
|
+
Array of time points (t0, t1, ..., tn).
|
|
93
|
+
q_points : array_like
|
|
94
|
+
Array of position points (q0, q1, ..., qn).
|
|
95
|
+
params : SplineParameters, optional
|
|
96
|
+
Spline parameters including initial/final velocities and accelerations.
|
|
97
|
+
If None, default parameters will be used.
|
|
98
|
+
"""
|
|
99
|
+
# Set default parameters if not provided
|
|
100
|
+
if params is None:
|
|
101
|
+
params = SplineParameters()
|
|
102
|
+
|
|
103
|
+
# Initialize the parent class to compute the basic cubic spline
|
|
104
|
+
super().__init__(t_points, q_points, params.v0, params.vn, params.debug)
|
|
105
|
+
|
|
106
|
+
# Store the acceleration constraints
|
|
107
|
+
self.a0 = params.a0
|
|
108
|
+
self.an = params.an
|
|
109
|
+
|
|
110
|
+
# Replace the first and/or last segment with a 5th degree polynomial if needed
|
|
111
|
+
if params.a0 is not None:
|
|
112
|
+
self._replace_first_segment_with_quintic()
|
|
113
|
+
|
|
114
|
+
if params.an is not None:
|
|
115
|
+
self._replace_last_segment_with_quintic()
|
|
116
|
+
|
|
117
|
+
def _replace_first_segment_with_quintic(self) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Replace the first segment with a 5th degree polynomial to satisfy
|
|
120
|
+
initial acceleration constraint.
|
|
121
|
+
|
|
122
|
+
This method computes the coefficients of a quintic polynomial for the first
|
|
123
|
+
segment to satisfy the position, velocity, and acceleration constraints at
|
|
124
|
+
both endpoints of the segment.
|
|
125
|
+
|
|
126
|
+
The quintic polynomial has the form:
|
|
127
|
+
p(tau) = b0 + b1*tau + b2*tau^2 + b3*tau^3 + b4*tau^4 + b5*tau^5
|
|
128
|
+
|
|
129
|
+
With constraints:
|
|
130
|
+
p(0) = q0, p'(0) = v0, p''(0) = a0
|
|
131
|
+
p(T) = q1, p'(T) = v1, p''(T) = a1
|
|
132
|
+
|
|
133
|
+
Where T is the duration of the first segment.
|
|
134
|
+
|
|
135
|
+
The coefficients are stored in self.quintic_coeffs["first"].
|
|
136
|
+
"""
|
|
137
|
+
# Get the time points and positions for the first segment
|
|
138
|
+
t0, t1 = self.t_points[0], self.t_points[1]
|
|
139
|
+
q0, q1 = self.q_points[0], self.q_points[1]
|
|
140
|
+
v0, v1 = self.velocities[0], self.velocities[1]
|
|
141
|
+
a0 = self.a0
|
|
142
|
+
|
|
143
|
+
# Calculate the acceleration at the end of the first segment
|
|
144
|
+
# For a cubic polynomial a(t) = 2*a2 + 6*a3*t
|
|
145
|
+
a1 = 2 * self.coefficients[0, 2] + 6 * self.coefficients[0, 3] * self.t_intervals[0]
|
|
146
|
+
|
|
147
|
+
# Compute the coefficients of the quintic polynomial
|
|
148
|
+
# p(tau) = b0 + b1*tau + b2*tau^2 + b3*tau^3 + b4*tau^4 + b5*tau^5
|
|
149
|
+
# with constraints:
|
|
150
|
+
# p(0) = q0, p'(0) = v0, p''(0) = a0
|
|
151
|
+
# p(T) = q1, p'(T) = v1, p''(T) = a1
|
|
152
|
+
# where T = t1 - t0
|
|
153
|
+
|
|
154
|
+
t_interval = t1 - t0
|
|
155
|
+
|
|
156
|
+
# Set up the system of equations
|
|
157
|
+
a_matrix = np.array(
|
|
158
|
+
[
|
|
159
|
+
[1, 0, 0, 0, 0, 0], # p(0) = q0
|
|
160
|
+
[0, 1, 0, 0, 0, 0], # p'(0) = v0
|
|
161
|
+
[0, 0, 2, 0, 0, 0], # p''(0) = a0
|
|
162
|
+
[
|
|
163
|
+
1,
|
|
164
|
+
t_interval,
|
|
165
|
+
t_interval**2,
|
|
166
|
+
t_interval**3,
|
|
167
|
+
t_interval**4,
|
|
168
|
+
t_interval**5,
|
|
169
|
+
], # p(T) = q1
|
|
170
|
+
[
|
|
171
|
+
0,
|
|
172
|
+
1,
|
|
173
|
+
2 * t_interval,
|
|
174
|
+
3 * t_interval**2,
|
|
175
|
+
4 * t_interval**3,
|
|
176
|
+
5 * t_interval**4,
|
|
177
|
+
], # p'(T) = v1
|
|
178
|
+
[0, 0, 2, 6 * t_interval, 12 * t_interval**2, 20 * t_interval**3], # p''(T) = a1
|
|
179
|
+
]
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
b = np.array([q0, v0, a0, q1, v1, a1])
|
|
183
|
+
|
|
184
|
+
# Solve for the coefficients
|
|
185
|
+
quintic_coeffs = np.linalg.solve(a_matrix, b)
|
|
186
|
+
|
|
187
|
+
# Store the quintic coefficients for later use
|
|
188
|
+
if not hasattr(self, "quintic_coeffs"):
|
|
189
|
+
self.quintic_coeffs = {}
|
|
190
|
+
|
|
191
|
+
self.quintic_coeffs["first"] = quintic_coeffs
|
|
192
|
+
|
|
193
|
+
if self.debug:
|
|
194
|
+
print("\nReplaced first segment with quintic polynomial:")
|
|
195
|
+
print(f" b0 = {quintic_coeffs[0]}")
|
|
196
|
+
print(f" b1 = {quintic_coeffs[1]}")
|
|
197
|
+
print(f" b2 = {quintic_coeffs[2]}")
|
|
198
|
+
print(f" b3 = {quintic_coeffs[3]}")
|
|
199
|
+
print(f" b4 = {quintic_coeffs[4]}")
|
|
200
|
+
print(f" b5 = {quintic_coeffs[5]}")
|
|
201
|
+
|
|
202
|
+
def _replace_last_segment_with_quintic(self) -> None:
|
|
203
|
+
"""
|
|
204
|
+
Replace the last segment with a 5th degree polynomial to satisfy
|
|
205
|
+
final acceleration constraint.
|
|
206
|
+
|
|
207
|
+
This method computes the coefficients of a quintic polynomial for the last
|
|
208
|
+
segment to satisfy the position, velocity, and acceleration constraints at
|
|
209
|
+
both endpoints of the segment.
|
|
210
|
+
|
|
211
|
+
The quintic polynomial has the form:
|
|
212
|
+
p(tau) = b0 + b1*tau + b2*tau^2 + b3*tau^3 + b4*tau^4 + b5*tau^5
|
|
213
|
+
|
|
214
|
+
With constraints:
|
|
215
|
+
p(0) = qn_1, p'(0) = vn_1, p''(0) = an_1
|
|
216
|
+
p(T) = qn, p'(T) = vn, p''(T) = an
|
|
217
|
+
|
|
218
|
+
Where T is the duration of the last segment.
|
|
219
|
+
|
|
220
|
+
The coefficients are stored in self.quintic_coeffs["last"].
|
|
221
|
+
"""
|
|
222
|
+
# Get the time points and positions for the last segment
|
|
223
|
+
tn_1, tn = self.t_points[-2], self.t_points[-1]
|
|
224
|
+
qn_1, qn = self.q_points[-2], self.q_points[-1]
|
|
225
|
+
vn_1, vn = self.velocities[-2], self.velocities[-1]
|
|
226
|
+
an = self.an
|
|
227
|
+
|
|
228
|
+
# Calculate the acceleration at the start of the last segment
|
|
229
|
+
# For a cubic polynomial a(t) = 2*a2
|
|
230
|
+
an_1 = 2 * self.coefficients[-1, 2]
|
|
231
|
+
|
|
232
|
+
# Compute the coefficients of the quintic polynomial
|
|
233
|
+
# p(tau) = b0 + b1*tau + b2*tau^2 + b3*tau^3 + b4*tau^4 + b5*tau^5
|
|
234
|
+
# with constraints:
|
|
235
|
+
# p(0) = qn_1, p'(0) = vn_1, p''(0) = an_1
|
|
236
|
+
# p(T) = qn, p'(T) = vn, p''(T) = an
|
|
237
|
+
# where T = tn - tn_1
|
|
238
|
+
|
|
239
|
+
t_interval = tn - tn_1
|
|
240
|
+
|
|
241
|
+
# Set up the system of equations
|
|
242
|
+
a_matrix = np.array(
|
|
243
|
+
[
|
|
244
|
+
[1, 0, 0, 0, 0, 0], # p(0) = qn_1
|
|
245
|
+
[0, 1, 0, 0, 0, 0], # p'(0) = vn_1
|
|
246
|
+
[0, 0, 2, 0, 0, 0], # p''(0) = an_1
|
|
247
|
+
[
|
|
248
|
+
1,
|
|
249
|
+
t_interval,
|
|
250
|
+
t_interval**2,
|
|
251
|
+
t_interval**3,
|
|
252
|
+
t_interval**4,
|
|
253
|
+
t_interval**5,
|
|
254
|
+
], # p(T) = qn
|
|
255
|
+
[
|
|
256
|
+
0,
|
|
257
|
+
1,
|
|
258
|
+
2 * t_interval,
|
|
259
|
+
3 * t_interval**2,
|
|
260
|
+
4 * t_interval**3,
|
|
261
|
+
5 * t_interval**4,
|
|
262
|
+
], # p'(T) = vn
|
|
263
|
+
[0, 0, 2, 6 * t_interval, 12 * t_interval**2, 20 * t_interval**3], # p''(T) = an
|
|
264
|
+
]
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
b = np.array([qn_1, vn_1, an_1, qn, vn, an])
|
|
268
|
+
|
|
269
|
+
# Solve for the coefficients
|
|
270
|
+
quintic_coeffs = np.linalg.solve(a_matrix, b)
|
|
271
|
+
|
|
272
|
+
# Store the quintic coefficients for later use
|
|
273
|
+
if not hasattr(self, "quintic_coeffs"):
|
|
274
|
+
self.quintic_coeffs = {}
|
|
275
|
+
|
|
276
|
+
self.quintic_coeffs["last"] = quintic_coeffs
|
|
277
|
+
|
|
278
|
+
if self.debug:
|
|
279
|
+
print("\nReplaced last segment with quintic polynomial:")
|
|
280
|
+
print(f" b0 = {quintic_coeffs[0]}")
|
|
281
|
+
print(f" b1 = {quintic_coeffs[1]}")
|
|
282
|
+
print(f" b2 = {quintic_coeffs[2]}")
|
|
283
|
+
print(f" b3 = {quintic_coeffs[3]}")
|
|
284
|
+
print(f" b4 = {quintic_coeffs[4]}")
|
|
285
|
+
print(f" b5 = {quintic_coeffs[5]}")
|
|
286
|
+
|
|
287
|
+
def evaluate(self, t: float | np.ndarray) -> float | np.ndarray:
|
|
288
|
+
"""
|
|
289
|
+
Evaluate the spline at time t.
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
t : float or ndarray
|
|
294
|
+
Time point or array of time points at which to evaluate the spline.
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
float or ndarray
|
|
299
|
+
Position(s) at the specified time(s). Returns a float if a single time
|
|
300
|
+
point is provided, or an ndarray if an array of time points is provided.
|
|
301
|
+
|
|
302
|
+
Notes
|
|
303
|
+
-----
|
|
304
|
+
For time values outside the spline range:
|
|
305
|
+
- If t < t_points[0], returns the position at t_points[0]
|
|
306
|
+
- If t > t_points[-1], returns the position at t_points[-1]
|
|
307
|
+
|
|
308
|
+
For the first and last segments, quintic polynomials are used if acceleration
|
|
309
|
+
constraints were specified. For all other segments, cubic polynomials are used.
|
|
310
|
+
"""
|
|
311
|
+
t = np.atleast_1d(t)
|
|
312
|
+
result = np.zeros_like(t)
|
|
313
|
+
|
|
314
|
+
for i, ti in enumerate(t):
|
|
315
|
+
# Find the segment that contains ti
|
|
316
|
+
if ti <= self.t_points[0]:
|
|
317
|
+
# Before the start of the trajectory
|
|
318
|
+
k = 0
|
|
319
|
+
tau = 0
|
|
320
|
+
elif ti >= self.t_points[-1]:
|
|
321
|
+
# After the end of the trajectory
|
|
322
|
+
k = self.n - 1
|
|
323
|
+
tau = self.t_intervals[k]
|
|
324
|
+
else:
|
|
325
|
+
# Within the trajectory
|
|
326
|
+
# Find the largest k such that t_k <= ti
|
|
327
|
+
k = np.searchsorted(self.t_points, ti, side="right") - 1
|
|
328
|
+
tau = ti - self.t_points[k]
|
|
329
|
+
|
|
330
|
+
# Check if this segment uses a quintic polynomial
|
|
331
|
+
if k == 0 and hasattr(self, "quintic_coeffs") and "first" in self.quintic_coeffs:
|
|
332
|
+
# Use the quintic polynomial for the first segment
|
|
333
|
+
b = self.quintic_coeffs["first"]
|
|
334
|
+
result[i] = (
|
|
335
|
+
b[0]
|
|
336
|
+
+ b[1] * tau
|
|
337
|
+
+ b[2] * tau**2
|
|
338
|
+
+ b[3] * tau**3
|
|
339
|
+
+ b[4] * tau**4
|
|
340
|
+
+ b[5] * tau**5
|
|
341
|
+
)
|
|
342
|
+
elif (
|
|
343
|
+
k == self.n - 1
|
|
344
|
+
and hasattr(self, "quintic_coeffs")
|
|
345
|
+
and "last" in self.quintic_coeffs
|
|
346
|
+
):
|
|
347
|
+
# Use the quintic polynomial for the last segment
|
|
348
|
+
b = self.quintic_coeffs["last"]
|
|
349
|
+
result[i] = (
|
|
350
|
+
b[0]
|
|
351
|
+
+ b[1] * tau
|
|
352
|
+
+ b[2] * tau**2
|
|
353
|
+
+ b[3] * tau**3
|
|
354
|
+
+ b[4] * tau**4
|
|
355
|
+
+ b[5] * tau**5
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
# Use the cubic polynomial for other segments
|
|
359
|
+
a = self.coefficients[k]
|
|
360
|
+
result[i] = a[0] + a[1] * tau + a[2] * tau**2 + a[3] * tau**3
|
|
361
|
+
|
|
362
|
+
return result[0] if len(result) == 1 else result
|
|
363
|
+
|
|
364
|
+
def evaluate_velocity(self, t: float | np.ndarray) -> float | np.ndarray:
|
|
365
|
+
"""
|
|
366
|
+
Evaluate the velocity at time t.
|
|
367
|
+
|
|
368
|
+
Parameters
|
|
369
|
+
----------
|
|
370
|
+
t : float or ndarray
|
|
371
|
+
Time point or array of time points at which to evaluate the velocity.
|
|
372
|
+
|
|
373
|
+
Returns
|
|
374
|
+
-------
|
|
375
|
+
float or ndarray
|
|
376
|
+
Velocity at the specified time(s). Returns a float if a single time
|
|
377
|
+
point is provided, or an ndarray if an array of time points is provided.
|
|
378
|
+
|
|
379
|
+
Notes
|
|
380
|
+
-----
|
|
381
|
+
For time values outside the spline range:
|
|
382
|
+
- If t < t_points[0], returns the velocity at t_points[0]
|
|
383
|
+
- If t > t_points[-1], returns the velocity at t_points[-1]
|
|
384
|
+
|
|
385
|
+
For the first and last segments, derivatives of quintic polynomials are used
|
|
386
|
+
if acceleration constraints were specified. For all other segments, derivatives
|
|
387
|
+
of cubic polynomials are used.
|
|
388
|
+
"""
|
|
389
|
+
t = np.atleast_1d(t)
|
|
390
|
+
result = np.zeros_like(t)
|
|
391
|
+
|
|
392
|
+
for i, ti in enumerate(t):
|
|
393
|
+
# Find the segment that contains ti
|
|
394
|
+
if ti <= self.t_points[0]:
|
|
395
|
+
k = 0
|
|
396
|
+
tau = 0
|
|
397
|
+
elif ti >= self.t_points[-1]:
|
|
398
|
+
k = self.n - 1
|
|
399
|
+
tau = self.t_intervals[k]
|
|
400
|
+
else:
|
|
401
|
+
k = np.searchsorted(self.t_points, ti, side="right") - 1
|
|
402
|
+
tau = ti - self.t_points[k]
|
|
403
|
+
|
|
404
|
+
# Check if this segment uses a quintic polynomial
|
|
405
|
+
if k == 0 and hasattr(self, "quintic_coeffs") and "first" in self.quintic_coeffs:
|
|
406
|
+
# Use the derivative of the quintic polynomial for the first segment
|
|
407
|
+
b = self.quintic_coeffs["first"]
|
|
408
|
+
result[i] = (
|
|
409
|
+
b[1]
|
|
410
|
+
+ 2 * b[2] * tau
|
|
411
|
+
+ 3 * b[3] * tau**2
|
|
412
|
+
+ 4 * b[4] * tau**3
|
|
413
|
+
+ 5 * b[5] * tau**4
|
|
414
|
+
)
|
|
415
|
+
elif (
|
|
416
|
+
k == self.n - 1
|
|
417
|
+
and hasattr(self, "quintic_coeffs")
|
|
418
|
+
and "last" in self.quintic_coeffs
|
|
419
|
+
):
|
|
420
|
+
# Use the derivative of the quintic polynomial for the last segment
|
|
421
|
+
b = self.quintic_coeffs["last"]
|
|
422
|
+
result[i] = (
|
|
423
|
+
b[1]
|
|
424
|
+
+ 2 * b[2] * tau
|
|
425
|
+
+ 3 * b[3] * tau**2
|
|
426
|
+
+ 4 * b[4] * tau**3
|
|
427
|
+
+ 5 * b[5] * tau**4
|
|
428
|
+
)
|
|
429
|
+
else:
|
|
430
|
+
# Use the derivative of the cubic polynomial for other segments
|
|
431
|
+
a = self.coefficients[k]
|
|
432
|
+
result[i] = a[1] + 2 * a[2] * tau + 3 * a[3] * tau**2
|
|
433
|
+
|
|
434
|
+
return result[0] if len(result) == 1 else result
|
|
435
|
+
|
|
436
|
+
def evaluate_acceleration(self, t: float | np.ndarray) -> float | np.ndarray:
|
|
437
|
+
"""
|
|
438
|
+
Evaluate the acceleration at time t.
|
|
439
|
+
|
|
440
|
+
Parameters
|
|
441
|
+
----------
|
|
442
|
+
t : float or ndarray
|
|
443
|
+
Time point or array of time points at which to evaluate the acceleration.
|
|
444
|
+
|
|
445
|
+
Returns
|
|
446
|
+
-------
|
|
447
|
+
float or ndarray
|
|
448
|
+
Acceleration at the specified time(s). Returns a float if a single time
|
|
449
|
+
point is provided, or an ndarray if an array of time points is provided.
|
|
450
|
+
|
|
451
|
+
Notes
|
|
452
|
+
-----
|
|
453
|
+
For time values outside the spline range:
|
|
454
|
+
- If t < t_points[0], returns the acceleration at t_points[0]
|
|
455
|
+
- If t > t_points[-1], returns the acceleration at t_points[-1]
|
|
456
|
+
|
|
457
|
+
For the first and last segments, second derivatives of quintic polynomials are
|
|
458
|
+
used if acceleration constraints were specified. For all other segments, second
|
|
459
|
+
derivatives of cubic polynomials are used.
|
|
460
|
+
"""
|
|
461
|
+
t = np.atleast_1d(t)
|
|
462
|
+
result = np.zeros_like(t)
|
|
463
|
+
|
|
464
|
+
for i, ti in enumerate(t):
|
|
465
|
+
# Find the segment that contains ti
|
|
466
|
+
if ti <= self.t_points[0]:
|
|
467
|
+
k = 0
|
|
468
|
+
tau = 0
|
|
469
|
+
elif ti >= self.t_points[-1]:
|
|
470
|
+
k = self.n - 1
|
|
471
|
+
tau = self.t_intervals[k]
|
|
472
|
+
else:
|
|
473
|
+
k = np.searchsorted(self.t_points, ti, side="right") - 1
|
|
474
|
+
tau = ti - self.t_points[k]
|
|
475
|
+
|
|
476
|
+
# Check if this segment uses a quintic polynomial
|
|
477
|
+
if k == 0 and hasattr(self, "quintic_coeffs") and "first" in self.quintic_coeffs:
|
|
478
|
+
# Use the second derivative of the quintic polynomial for the first segment
|
|
479
|
+
b = self.quintic_coeffs["first"]
|
|
480
|
+
result[i] = 2 * b[2] + 6 * b[3] * tau + 12 * b[4] * tau**2 + 20 * b[5] * tau**3
|
|
481
|
+
elif (
|
|
482
|
+
k == self.n - 1
|
|
483
|
+
and hasattr(self, "quintic_coeffs")
|
|
484
|
+
and "last" in self.quintic_coeffs
|
|
485
|
+
):
|
|
486
|
+
# Use the second derivative of the quintic polynomial for the last segment
|
|
487
|
+
b = self.quintic_coeffs["last"]
|
|
488
|
+
result[i] = 2 * b[2] + 6 * b[3] * tau + 12 * b[4] * tau**2 + 20 * b[5] * tau**3
|
|
489
|
+
else:
|
|
490
|
+
# Use the second derivative of the cubic polynomial for other segments
|
|
491
|
+
a = self.coefficients[k]
|
|
492
|
+
result[i] = 2 * a[2] + 6 * a[3] * tau
|
|
493
|
+
|
|
494
|
+
return result[0] if len(result) == 1 else result
|