python-motion-planning 2.0__py3-none-any.whl → 2.0.dev2__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.
Files changed (30) hide show
  1. python_motion_planning/__init__.py +1 -1
  2. python_motion_planning/common/utils/geometry.py +29 -18
  3. python_motion_planning/curve_generator/__init__.py +9 -0
  4. python_motion_planning/curve_generator/bezier_curve.py +131 -0
  5. python_motion_planning/curve_generator/bspline_curve.py +271 -0
  6. python_motion_planning/curve_generator/cubic_spline.py +128 -0
  7. python_motion_planning/curve_generator/curve.py +64 -0
  8. python_motion_planning/curve_generator/dubins_curve.py +348 -0
  9. python_motion_planning/curve_generator/fem_pos_smooth.py +114 -0
  10. python_motion_planning/curve_generator/polynomial_curve.py +226 -0
  11. python_motion_planning/curve_generator/reeds_shepp.py +736 -0
  12. python_motion_planning/path_planner/sample_search/rrt.py +5 -5
  13. python_motion_planning/path_planner/sample_search/rrt_connect.py +2 -2
  14. python_motion_planning/path_planner/sample_search/rrt_star.py +11 -31
  15. {python_motion_planning-2.0.dist-info → python_motion_planning-2.0.dev2.dist-info}/METADATA +15 -15
  16. {python_motion_planning-2.0.dist-info → python_motion_planning-2.0.dev2.dist-info}/RECORD +19 -21
  17. {python_motion_planning-2.0.dist-info → python_motion_planning-2.0.dev2.dist-info}/WHEEL +1 -1
  18. python_motion_planning/traj_optimizer/__init__.py +0 -2
  19. python_motion_planning/traj_optimizer/base_curve_generator.py +0 -53
  20. python_motion_planning/traj_optimizer/curve_generator/__init__.py +0 -2
  21. python_motion_planning/traj_optimizer/curve_generator/point_based/__init__.py +0 -2
  22. python_motion_planning/traj_optimizer/curve_generator/point_based/bspline.py +0 -256
  23. python_motion_planning/traj_optimizer/curve_generator/point_based/cubic_spline.py +0 -115
  24. python_motion_planning/traj_optimizer/curve_generator/pose_based/__init__.py +0 -4
  25. python_motion_planning/traj_optimizer/curve_generator/pose_based/bezier.py +0 -121
  26. python_motion_planning/traj_optimizer/curve_generator/pose_based/dubins.py +0 -355
  27. python_motion_planning/traj_optimizer/curve_generator/pose_based/polynomial.py +0 -197
  28. python_motion_planning/traj_optimizer/curve_generator/pose_based/reeds_shepp.py +0 -606
  29. {python_motion_planning-2.0.dist-info → python_motion_planning-2.0.dev2.dist-info}/licenses/LICENSE +0 -0
  30. {python_motion_planning-2.0.dist-info → python_motion_planning-2.0.dev2.dist-info}/top_level.txt +0 -0
@@ -1,606 +0,0 @@
1
- """
2
- @file: reeds_shepp.py
3
- @author: Yang Haodong, Wu Maojia
4
- @update: 2026.4.12
5
- """
6
- from typing import List, Tuple, Dict, Any
7
- import math
8
-
9
- from python_motion_planning.traj_optimizer.base_curve_generator import BaseCurveGenerator
10
- from python_motion_planning.common.utils.geometry import Geometry
11
-
12
-
13
- class ReedsShepp(BaseCurveGenerator):
14
- """
15
- Class for Reeds-Shepp curve generator.
16
-
17
- Args:
18
- *args: see the parent class.
19
- max_curv: The maximum curvature of the curve.
20
- *args: see the parent class.
21
-
22
- References:
23
- [1] Optimal paths for a car that goes both forwards and backwards
24
-
25
- Examples:
26
- >>> import math
27
- >>> generator = ReedsShepp(step=0.1, max_curv=1.0)
28
- >>> points = [(0.0, 0.0, 0.0), (10.0, 10.0, -math.pi/2), (20.0, 5.0, math.pi/3)]
29
- >>> path, curve_info = generator.generate(points)
30
- >>> print(curve_info['success'])
31
- True
32
- """
33
- def __init__(self, *args,
34
- max_curv: float = 1.0,
35
- **kwargs) -> None:
36
- super().__init__(*args, **kwargs)
37
- self.max_curv = max_curv
38
-
39
- def __str__(self) -> str:
40
- return "Reeds Shepp Curve"
41
-
42
- class _Path:
43
- """
44
- Container for a single Reeds-Shepp path candidate.
45
- """
46
- def __init__(self, lengths: List[float] = None, ctypes: List[str] = None):
47
- self.lengths = lengths if lengths is not None else []
48
- self.ctypes = ctypes if ctypes is not None else []
49
- self.path_length = sum(abs(i) for i in self.lengths)
50
-
51
- def generate(self, points: List[Tuple[float, float, float]]) -> Tuple[List[Tuple[float, float, float]], Dict[str, Any]]:
52
- """
53
- Generate a concatenated Reeds-Shepp curve through a list of poses.
54
-
55
- Args:
56
- points: A list of poses (x, y, yaw) in world frame.
57
-
58
- Returns:
59
- path: A list of (x, y, yaw) waypoints of the generated curve in world frame.
60
- curve_info: A dictionary containing the curve information (success, length).
61
- """
62
- if len(points) < 2:
63
- return [], {"success": False, "length": 0.0}
64
-
65
- path: List[Tuple[float, float, float]] = []
66
- total_length = 0.0
67
- for i in range(len(points) - 1):
68
- best_cost, _, x_list, y_list, yaw_list = self._generate_segment(
69
- points[i], points[i + 1])
70
- if best_cost is None:
71
- return [], {"success": False, "length": 0.0}
72
- total_length += best_cost
73
-
74
- start = 1 if i > 0 else 0
75
- for x, y, yaw in zip(x_list[start:], y_list[start:], yaw_list[start:]):
76
- path.append((float(x), float(y), float(yaw)))
77
-
78
- total_length = float(total_length)
79
-
80
- return path, {"success": True, "length": total_length}
81
-
82
- def _r(self, x: float, y: float) -> Tuple[float, float]:
83
- """
84
- Convert (x, y) to polar coordinates (r, theta).
85
-
86
- Args:
87
- x: x-coordinate value.
88
- y: y-coordinate value.
89
-
90
- Returns:
91
- r, theta: Polar coordinates.
92
- """
93
- return math.hypot(x, y), math.atan2(y, x)
94
-
95
- def _sls(self, x: float, y: float, phi: float):
96
- """
97
- Straight-Left-Straight generation mode.
98
- """
99
- phi = Geometry.regularize_orient(phi)
100
-
101
- if y > 0.0 and 0.0 < phi < math.pi * 0.99:
102
- xd = -y / math.tan(phi) + x
103
- t = xd - math.tan(phi / 2.0)
104
- u = phi
105
- v = math.sqrt((x - xd) ** 2 + y ** 2) - math.tan(phi / 2.0)
106
- return True, t, u, v
107
- if y < 0.0 and 0.0 < phi < math.pi * 0.99:
108
- xd = -y / math.tan(phi) + x
109
- t = xd - math.tan(phi / 2.0)
110
- u = phi
111
- v = -math.sqrt((x - xd) ** 2 + y ** 2) - math.tan(phi / 2.0)
112
- return True, t, u, v
113
- return False, 0.0, 0.0, 0.0
114
-
115
- def _lrl(self, x: float, y: float, phi: float):
116
- """
117
- Left-Right-Left generation mode (L+R-L-).
118
- """
119
- r, theta = self._r(x - math.sin(phi), y - 1.0 + math.cos(phi))
120
-
121
- if r <= 4.0:
122
- u = -2.0 * math.asin(0.25 * r)
123
- t = Geometry.regularize_orient(theta + 0.5 * u + math.pi)
124
- v = Geometry.regularize_orient(phi - t + u)
125
- if t >= 0.0 and u <= 0.0:
126
- return True, t, u, v
127
- return False, 0.0, 0.0, 0.0
128
-
129
- def _lsl(self, x: float, y: float, phi: float):
130
- """
131
- Left-Straight-Left generation mode (L+S+L+).
132
- """
133
- u, t = self._r(x - math.sin(phi), y - 1.0 + math.cos(phi))
134
- if t >= 0.0:
135
- v = Geometry.regularize_orient(phi - t)
136
- if v >= 0.0:
137
- return True, t, u, v
138
- return False, 0.0, 0.0, 0.0
139
-
140
- def _lsr(self, x: float, y: float, phi: float):
141
- """
142
- Left-Straight-Right generation mode (L+S+R+).
143
- """
144
- r, theta = self._r(x + math.sin(phi), y - 1.0 - math.cos(phi))
145
- r = r ** 2
146
- if r >= 4.0:
147
- u = math.sqrt(r - 4.0)
148
- t = Geometry.regularize_orient(theta + math.atan2(2.0, u))
149
- v = Geometry.regularize_orient(t - phi)
150
- if t >= 0.0 and v >= 0.0:
151
- return True, t, u, v
152
- return False, 0.0, 0.0, 0.0
153
-
154
- def _lrlrn(self, x: float, y: float, phi: float):
155
- """
156
- Left-Right(beta)-Left(beta)-Right generation mode (L+R+L-R-).
157
- """
158
- xi = x + math.sin(phi)
159
- eta = y - 1.0 - math.cos(phi)
160
- rho = 0.25 * (2.0 + math.sqrt(xi * xi + eta * eta))
161
-
162
- if rho <= 1.0:
163
- u = math.acos(rho)
164
- t, v = self._cal_tau_omega(u, -u, xi, eta, phi)
165
- if t >= 0.0 and v <= 0.0:
166
- return True, t, u, v
167
- return False, 0.0, 0.0, 0.0
168
-
169
- def _lrlrp(self, x: float, y: float, phi: float):
170
- """
171
- Left-Right(beta)-Left(beta)-Right generation mode (L+R-L-R+).
172
- """
173
- xi = x + math.sin(phi)
174
- eta = y - 1.0 - math.cos(phi)
175
- rho = (20.0 - xi * xi - eta * eta) / 16.0
176
-
177
- if 0.0 <= rho <= 1.0:
178
- u = -math.acos(rho)
179
- if u >= -0.5 * math.pi:
180
- t, v = self._cal_tau_omega(u, u, xi, eta, phi)
181
- if t >= 0.0 and v >= 0.0:
182
- return True, t, u, v
183
- return False, 0.0, 0.0, 0.0
184
-
185
- def _lrsr(self, x: float, y: float, phi: float):
186
- """
187
- Left-Right(pi/2)-Straight-Right generation mode (L+R-S-R-).
188
- """
189
- xi = x + math.sin(phi)
190
- eta = y - 1.0 - math.cos(phi)
191
- rho, theta = self._r(-eta, xi)
192
-
193
- if rho >= 2.0:
194
- t = theta
195
- u = 2.0 - rho
196
- v = Geometry.regularize_orient(t + 0.5 * math.pi - phi)
197
- if t >= 0.0 and u <= 0.0 and v <= 0.0:
198
- return True, t, u, v
199
- return False, 0.0, 0.0, 0.0
200
-
201
- def _lrsl(self, x: float, y: float, phi: float):
202
- """
203
- Left-Right(pi/2)-Straight-Left generation mode (L+R-S-L-).
204
- """
205
- xi = x - math.sin(phi)
206
- eta = y - 1.0 + math.cos(phi)
207
- rho, theta = self._r(xi, eta)
208
-
209
- if rho >= 2.0:
210
- r = math.sqrt(rho * rho - 4.0)
211
- u = 2.0 - r
212
- t = Geometry.regularize_orient(theta + math.atan2(r, -2.0))
213
- v = Geometry.regularize_orient(phi - 0.5 * math.pi - t)
214
- if t >= 0.0 and u <= 0.0 and v <= 0.0:
215
- return True, t, u, v
216
- return False, 0.0, 0.0, 0.0
217
-
218
- def _lrslr(self, x: float, y: float, phi: float):
219
- """
220
- Left-Right(pi/2)-Straight-Left(pi/2)-Right generation mode (L+R-S-L-R+).
221
- """
222
- xi = x + math.sin(phi)
223
- eta = y - 1.0 - math.cos(phi)
224
- r, _ = self._r(xi, eta)
225
-
226
- if r >= 2.0:
227
- u = 4.0 - math.sqrt(r * r - 4.0)
228
- if u <= 0.0:
229
- t = Geometry.regularize_orient(math.atan2((4.0 - u) * xi - 2.0 * eta, -2.0 * xi + (u - 4.0) * eta))
230
- v = Geometry.regularize_orient(t - phi)
231
- if t >= 0.0 and v >= 0.0:
232
- return True, t, u, v
233
- return False, 0.0, 0.0, 0.0
234
-
235
- def _scs(self, x: float, y: float, phi: float) -> List["_Path"]:
236
- """
237
- Straight-Circle-Straight generation mode family (using reflect).
238
- """
239
- paths = []
240
-
241
- flag, t, u, v = self._sls(x, y, phi)
242
- if flag:
243
- paths.append(self._Path(lengths=[t, u, v], ctypes=["S", "L", "S"]))
244
-
245
- flag, t, u, v = self._sls(x, -y, -phi)
246
- if flag:
247
- paths.append(self._Path(lengths=[t, u, v], ctypes=["S", "R", "S"]))
248
-
249
- return paths
250
-
251
- def _ccc(self, x: float, y: float, phi: float) -> List["_Path"]:
252
- """
253
- Circle-Circle-Circle generation mode family (using reflect, timeflip and backwards).
254
- """
255
- paths = []
256
-
257
- flag, t, u, v = self._lrl(x, y, phi)
258
- if flag:
259
- paths.append(self._Path(lengths=[t, u, v], ctypes=["L", "R", "L"]))
260
-
261
- flag, t, u, v = self._lrl(-x, y, -phi)
262
- if flag:
263
- paths.append(self._Path(lengths=[-t, -u, -v], ctypes=["L", "R", "L"]))
264
-
265
- flag, t, u, v = self._lrl(x, -y, -phi)
266
- if flag:
267
- paths.append(self._Path(lengths=[t, u, v], ctypes=["R", "L", "R"]))
268
-
269
- flag, t, u, v = self._lrl(-x, -y, phi)
270
- if flag:
271
- paths.append(self._Path(lengths=[-t, -u, -v], ctypes=["R", "L", "R"]))
272
-
273
- xb = x * math.cos(phi) + y * math.sin(phi)
274
- yb = x * math.sin(phi) - y * math.cos(phi)
275
-
276
- flag, t, u, v = self._lrl(xb, yb, phi)
277
- if flag:
278
- paths.append(self._Path(lengths=[v, u, t], ctypes=["L", "R", "L"]))
279
-
280
- flag, t, u, v = self._lrl(-xb, yb, -phi)
281
- if flag:
282
- paths.append(self._Path(lengths=[-v, -u, -t], ctypes=["L", "R", "L"]))
283
-
284
- flag, t, u, v = self._lrl(xb, -yb, -phi)
285
- if flag:
286
- paths.append(self._Path(lengths=[v, u, t], ctypes=["R", "L", "R"]))
287
-
288
- flag, t, u, v = self._lrl(-xb, -yb, phi)
289
- if flag:
290
- paths.append(self._Path(lengths=[-v, -u, -t], ctypes=["R", "L", "R"]))
291
-
292
- return paths
293
-
294
- def _csc(self, x: float, y: float, phi: float) -> List["_Path"]:
295
- """
296
- Circle-Straight-Circle generation mode family (using reflect, timeflip and backwards).
297
- """
298
- paths = []
299
-
300
- flag, t, u, v = self._lsl(x, y, phi)
301
- if flag:
302
- paths.append(self._Path(lengths=[t, u, v], ctypes=["L", "S", "L"]))
303
-
304
- flag, t, u, v = self._lsl(-x, y, -phi)
305
- if flag:
306
- paths.append(self._Path(lengths=[-t, -u, -v], ctypes=["L", "S", "L"]))
307
-
308
- flag, t, u, v = self._lsl(x, -y, -phi)
309
- if flag:
310
- paths.append(self._Path(lengths=[t, u, v], ctypes=["R", "S", "R"]))
311
-
312
- flag, t, u, v = self._lsl(-x, -y, phi)
313
- if flag:
314
- paths.append(self._Path(lengths=[-t, -u, -v], ctypes=["R", "S", "R"]))
315
-
316
- flag, t, u, v = self._lsr(x, y, phi)
317
- if flag:
318
- paths.append(self._Path(lengths=[t, u, v], ctypes=["L", "S", "R"]))
319
-
320
- flag, t, u, v = self._lsr(-x, y, -phi)
321
- if flag:
322
- paths.append(self._Path(lengths=[-t, -u, -v], ctypes=["L", "S", "R"]))
323
-
324
- flag, t, u, v = self._lsr(x, -y, -phi)
325
- if flag:
326
- paths.append(self._Path(lengths=[t, u, v], ctypes=["R", "S", "L"]))
327
-
328
- flag, t, u, v = self._lsr(-x, -y, phi)
329
- if flag:
330
- paths.append(self._Path(lengths=[-t, -u, -v], ctypes=["R", "S", "L"]))
331
-
332
- return paths
333
-
334
- def _cccc(self, x: float, y: float, phi: float) -> List["_Path"]:
335
- """
336
- Circle-Circle(beta)-Circle(beta)-Circle generation mode family
337
- (using reflect, timeflip and backwards).
338
- """
339
- paths = []
340
-
341
- flag, t, u, v = self._lrlrn(x, y, phi)
342
- if flag:
343
- paths.append(self._Path(lengths=[t, u, -u, v], ctypes=["L", "R", "L", "R"]))
344
-
345
- flag, t, u, v = self._lrlrn(-x, y, -phi)
346
- if flag:
347
- paths.append(self._Path(lengths=[-t, -u, u, -v], ctypes=["L", "R", "L", "R"]))
348
-
349
- flag, t, u, v = self._lrlrn(x, -y, -phi)
350
- if flag:
351
- paths.append(self._Path(lengths=[t, u, -u, v], ctypes=["R", "L", "R", "L"]))
352
-
353
- flag, t, u, v = self._lrlrn(-x, -y, phi)
354
- if flag:
355
- paths.append(self._Path(lengths=[-t, -u, u, -v], ctypes=["R", "L", "R", "L"]))
356
-
357
- flag, t, u, v = self._lrlrp(x, y, phi)
358
- if flag:
359
- paths.append(self._Path(lengths=[t, u, u, v], ctypes=["L", "R", "L", "R"]))
360
-
361
- flag, t, u, v = self._lrlrp(-x, y, -phi)
362
- if flag:
363
- paths.append(self._Path(lengths=[-t, -u, -u, -v], ctypes=["L", "R", "L", "R"]))
364
-
365
- flag, t, u, v = self._lrlrp(x, -y, -phi)
366
- if flag:
367
- paths.append(self._Path(lengths=[t, u, u, v], ctypes=["R", "L", "R", "L"]))
368
-
369
- flag, t, u, v = self._lrlrp(-x, -y, phi)
370
- if flag:
371
- paths.append(self._Path(lengths=[-t, -u, -u, -v], ctypes=["R", "L", "R", "L"]))
372
-
373
- return paths
374
-
375
- def _ccsc(self, x: float, y: float, phi: float) -> List["_Path"]:
376
- """
377
- Circle-Circle(pi/2)-Straight-Circle and Circle-Straight-Circle(pi/2)-Circle
378
- generation mode family (using reflect, timeflip and backwards).
379
- """
380
- paths = []
381
-
382
- flag, t, u, v = self._lrsl(x, y, phi)
383
- if flag:
384
- paths.append(self._Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["L", "R", "S", "L"]))
385
-
386
- flag, t, u, v = self._lrsl(-x, y, -phi)
387
- if flag:
388
- paths.append(self._Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["L", "R", "S", "L"]))
389
-
390
- flag, t, u, v = self._lrsl(x, -y, -phi)
391
- if flag:
392
- paths.append(self._Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["R", "L", "S", "R"]))
393
-
394
- flag, t, u, v = self._lrsl(-x, -y, phi)
395
- if flag:
396
- paths.append(self._Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["R", "L", "S", "R"]))
397
-
398
- flag, t, u, v = self._lrsr(x, y, phi)
399
- if flag:
400
- paths.append(self._Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["L", "R", "S", "R"]))
401
-
402
- flag, t, u, v = self._lrsr(-x, y, -phi)
403
- if flag:
404
- paths.append(self._Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["L", "R", "S", "R"]))
405
-
406
- flag, t, u, v = self._lrsr(x, -y, -phi)
407
- if flag:
408
- paths.append(self._Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["R", "L", "S", "L"]))
409
-
410
- flag, t, u, v = self._lrsr(-x, -y, phi)
411
- if flag:
412
- paths.append(self._Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["R", "L", "S", "L"]))
413
-
414
- xb = x * math.cos(phi) + y * math.sin(phi)
415
- yb = x * math.sin(phi) - y * math.cos(phi)
416
-
417
- flag, t, u, v = self._lrsl(xb, yb, phi)
418
- if flag:
419
- paths.append(self._Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["L", "S", "R", "L"]))
420
-
421
- flag, t, u, v = self._lrsl(-xb, yb, -phi)
422
- if flag:
423
- paths.append(self._Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["L", "S", "R", "L"]))
424
-
425
- flag, t, u, v = self._lrsl(xb, -yb, -phi)
426
- if flag:
427
- paths.append(self._Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["R", "S", "L", "R"]))
428
-
429
- flag, t, u, v = self._lrsl(-xb, -yb, phi)
430
- if flag:
431
- paths.append(self._Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["R", "S", "L", "R"]))
432
-
433
- flag, t, u, v = self._lrsr(xb, yb, phi)
434
- if flag:
435
- paths.append(self._Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["R", "S", "R", "L"]))
436
-
437
- flag, t, u, v = self._lrsr(-xb, yb, -phi)
438
- if flag:
439
- paths.append(self._Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["R", "S", "R", "L"]))
440
-
441
- flag, t, u, v = self._lrsr(xb, -yb, -phi)
442
- if flag:
443
- paths.append(self._Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["L", "S", "L", "R"]))
444
-
445
- flag, t, u, v = self._lrsr(-xb, -yb, phi)
446
- if flag:
447
- paths.append(self._Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["L", "S", "L", "R"]))
448
-
449
- return paths
450
-
451
- def _ccscc(self, x: float, y: float, phi: float) -> List["_Path"]:
452
- """
453
- Circle-Circle(pi/2)-Straight-Circle(pi/2)-Circle generation mode family
454
- (using reflect, timeflip and backwards).
455
- """
456
- paths = []
457
-
458
- flag, t, u, v = self._lrslr(x, y, phi)
459
- if flag:
460
- paths.append(self._Path(lengths=[t, -0.5 * math.pi, u, -0.5 * math.pi, v],
461
- ctypes=["L", "R", "S", "L", "R"]))
462
-
463
- flag, t, u, v = self._lrslr(-x, y, -phi)
464
- if flag:
465
- paths.append(self._Path(lengths=[-t, 0.5 * math.pi, -u, 0.5 * math.pi, -v],
466
- ctypes=["L", "R", "S", "L", "R"]))
467
-
468
- flag, t, u, v = self._lrslr(x, -y, -phi)
469
- if flag:
470
- paths.append(self._Path(lengths=[t, -0.5 * math.pi, u, -0.5 * math.pi, v],
471
- ctypes=["R", "L", "S", "R", "L"]))
472
-
473
- flag, t, u, v = self._lrslr(-x, -y, phi)
474
- if flag:
475
- paths.append(self._Path(lengths=[-t, 0.5 * math.pi, -u, 0.5 * math.pi, -v],
476
- ctypes=["R", "L", "S", "R", "L"]))
477
-
478
- return paths
479
-
480
- def _interpolate(self, mode: str, length: float,
481
- init_pose: Tuple[float, float, float]) -> Tuple[float, float, float]:
482
- """
483
- Planning path interpolation.
484
-
485
- Args:
486
- mode: Motion type, one of {"L", "S", "R"}.
487
- length: Single step motion path length.
488
- init_pose: Initial pose (x, y, yaw).
489
-
490
- Returns:
491
- new_pose: New pose (new_x, new_y, new_yaw) after moving.
492
- """
493
- x, y, yaw = init_pose
494
-
495
- if mode == "S":
496
- new_x = x + length / self.max_curv * math.cos(yaw)
497
- new_y = y + length / self.max_curv * math.sin(yaw)
498
- new_yaw = yaw
499
- elif mode == "L":
500
- new_x = x + (math.sin(yaw + length) - math.sin(yaw)) / self.max_curv
501
- new_y = y - (math.cos(yaw + length) - math.cos(yaw)) / self.max_curv
502
- new_yaw = yaw + length
503
- elif mode == "R":
504
- new_x = x - (math.sin(yaw - length) - math.sin(yaw)) / self.max_curv
505
- new_y = y + (math.cos(yaw - length) - math.cos(yaw)) / self.max_curv
506
- new_yaw = yaw - length
507
- else:
508
- raise NotImplementedError
509
-
510
- return new_x, new_y, new_yaw
511
-
512
- def _generate_segment(self, start_pose: Tuple[float, float, float],
513
- goal_pose: Tuple[float, float, float]):
514
- """
515
- Generate a single Reeds-Shepp curve segment between two poses.
516
-
517
- Args:
518
- start_pose: Initial pose (x, y, yaw).
519
- goal_pose: Target pose (x, y, yaw).
520
-
521
- Returns:
522
- best_cost: Best planning path length in world units.
523
- best_mode: Best motion modes.
524
- x_list: Trajectory of x.
525
- y_list: Trajectory of y.
526
- yaw_list: Trajectory of yaw.
527
- """
528
- sx, sy, syaw = start_pose
529
- gx, gy, gyaw = goal_pose
530
-
531
- dx, dy, dyaw = gx - sx, gy - sy, gyaw - syaw
532
- x = (math.cos(syaw) * dx + math.sin(syaw) * dy) * self.max_curv
533
- y = (-math.sin(syaw) * dx + math.cos(syaw) * dy) * self.max_curv
534
-
535
- planners = [self._scs, self._ccc, self._csc, self._cccc, self._ccsc, self._ccscc]
536
- best_path, best_cost = None, float("inf")
537
-
538
- for planner in planners:
539
- paths = planner(x, y, dyaw)
540
- for path in paths:
541
- if path.path_length < best_cost:
542
- best_path, best_cost = path, path.path_length
543
-
544
- if best_path is None:
545
- return None, None, [], [], []
546
-
547
- points_num = int(best_cost / self.step) + len(best_path.lengths) + 3
548
- x_list = [0.0 for _ in range(points_num)]
549
- y_list = [0.0 for _ in range(points_num)]
550
- yaw_list = [0.0 for _ in range(points_num)]
551
-
552
- i = 0
553
- for mode_, seg_length in zip(best_path.ctypes, best_path.lengths):
554
- d_length = self.step if seg_length > 0.0 else -self.step
555
- current_x, current_y, current_yaw = x_list[i], y_list[i], yaw_list[i]
556
- length = d_length
557
- while abs(length) <= abs(seg_length):
558
- i += 1
559
- current_x, current_y, current_yaw = self._interpolate(
560
- mode_, d_length, (current_x, current_y, current_yaw)
561
- )
562
- x_list[i], y_list[i], yaw_list[i] = current_x, current_y, current_yaw
563
- length += d_length
564
-
565
- i += 1
566
- remainder = seg_length - (length - d_length)
567
- x_list[i], y_list[i], yaw_list[i] = self._interpolate(
568
- mode_, remainder, (x_list[i-1], y_list[i-1], yaw_list[i-1])
569
- )
570
-
571
- if len(x_list) <= 1:
572
- return None, None, [], [], []
573
-
574
- while len(x_list) >= 1 and x_list[-1] == 0.0:
575
- x_list.pop()
576
- y_list.pop()
577
- yaw_list.pop()
578
-
579
- x_list_ = [math.cos(-syaw) * ix + math.sin(-syaw) * iy + sx for (ix, iy) in zip(x_list, y_list)]
580
- y_list_ = [-math.sin(-syaw) * ix + math.cos(-syaw) * iy + sy for (ix, iy) in zip(x_list, y_list)]
581
- yaw_list_ = [Geometry.regularize_orient(iyaw + syaw) for iyaw in yaw_list]
582
-
583
- return best_cost / self.max_curv, best_path.ctypes, x_list_, y_list_, yaw_list_
584
-
585
- def _cal_tau_omega(self, u: float, v: float, xi: float, eta: float, phi: float) -> Tuple[float, float]:
586
- """
587
- Helper to compute (tau, omega) for LRLR-family patterns.
588
-
589
- Args:
590
- u, v: Intermediate angular values from the base pattern.
591
- xi, eta: Coordinates derived from the normalized goal pose.
592
- phi: Normalized goal heading.
593
-
594
- Returns:
595
- tau, omega: Angular values used to complete the pattern.
596
- """
597
- delta = Geometry.regularize_orient(u - v)
598
- A = math.sin(u) - math.sin(delta)
599
- B = math.cos(u) - math.cos(delta) - 1.0
600
-
601
- t1 = math.atan2(eta * A - xi * B, xi * A + eta * B)
602
- t2 = 2.0 * (math.cos(delta) - math.cos(v) - math.cos(u)) + 3.0
603
-
604
- tau = Geometry.regularize_orient(t1 + math.pi) if t2 < 0 else Geometry.regularize_orient(t1)
605
- omega = Geometry.regularize_orient(tau - u + v - phi)
606
- return tau, omega