python-motion-planning 1.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.
Files changed (65) hide show
  1. curve_generation/__init__.py +9 -0
  2. curve_generation/bezier_curve.py +131 -0
  3. curve_generation/bspline_curve.py +271 -0
  4. curve_generation/cubic_spline.py +128 -0
  5. curve_generation/curve.py +64 -0
  6. curve_generation/dubins_curve.py +348 -0
  7. curve_generation/fem_pos_smooth.py +114 -0
  8. curve_generation/polynomial_curve.py +226 -0
  9. curve_generation/reeds_shepp.py +736 -0
  10. global_planner/__init__.py +3 -0
  11. global_planner/evolutionary_search/__init__.py +4 -0
  12. global_planner/evolutionary_search/aco.py +186 -0
  13. global_planner/evolutionary_search/evolutionary_search.py +87 -0
  14. global_planner/evolutionary_search/pso.py +356 -0
  15. global_planner/graph_search/__init__.py +28 -0
  16. global_planner/graph_search/a_star.py +124 -0
  17. global_planner/graph_search/d_star.py +291 -0
  18. global_planner/graph_search/d_star_lite.py +188 -0
  19. global_planner/graph_search/dijkstra.py +77 -0
  20. global_planner/graph_search/gbfs.py +78 -0
  21. global_planner/graph_search/graph_search.py +87 -0
  22. global_planner/graph_search/jps.py +165 -0
  23. global_planner/graph_search/lazy_theta_star.py +114 -0
  24. global_planner/graph_search/lpa_star.py +230 -0
  25. global_planner/graph_search/s_theta_star.py +133 -0
  26. global_planner/graph_search/theta_star.py +171 -0
  27. global_planner/graph_search/voronoi.py +200 -0
  28. global_planner/sample_search/__init__.py +6 -0
  29. global_planner/sample_search/informed_rrt.py +152 -0
  30. global_planner/sample_search/rrt.py +151 -0
  31. global_planner/sample_search/rrt_connect.py +147 -0
  32. global_planner/sample_search/rrt_star.py +77 -0
  33. global_planner/sample_search/sample_search.py +135 -0
  34. local_planner/__init__.py +19 -0
  35. local_planner/apf.py +144 -0
  36. local_planner/ddpg.py +630 -0
  37. local_planner/dqn.py +687 -0
  38. local_planner/dwa.py +212 -0
  39. local_planner/local_planner.py +262 -0
  40. local_planner/lqr.py +146 -0
  41. local_planner/mpc.py +214 -0
  42. local_planner/pid.py +158 -0
  43. local_planner/rpp.py +147 -0
  44. python_motion_planning-1.0.dist-info/LICENSE +674 -0
  45. python_motion_planning-1.0.dist-info/METADATA +873 -0
  46. python_motion_planning-1.0.dist-info/RECORD +65 -0
  47. python_motion_planning-1.0.dist-info/WHEEL +5 -0
  48. python_motion_planning-1.0.dist-info/top_level.txt +4 -0
  49. utils/__init__.py +19 -0
  50. utils/agent/__init__.py +0 -0
  51. utils/agent/agent.py +135 -0
  52. utils/environment/__init__.py +0 -0
  53. utils/environment/env.py +134 -0
  54. utils/environment/node.py +85 -0
  55. utils/environment/point2d.py +96 -0
  56. utils/environment/pose2d.py +91 -0
  57. utils/helper/__init__.py +3 -0
  58. utils/helper/math_helper.py +65 -0
  59. utils/planner/__init__.py +0 -0
  60. utils/planner/control_factory.py +31 -0
  61. utils/planner/curve_factory.py +29 -0
  62. utils/planner/planner.py +40 -0
  63. utils/planner/search_factory.py +51 -0
  64. utils/plot/__init__.py +0 -0
  65. utils/plot/plot.py +274 -0
@@ -0,0 +1,736 @@
1
+ """
2
+ @file: reeds_shepp.py
3
+ @breif: Reeds shepp curve generation
4
+ @author: Winter
5
+ @update: 2023.7.26
6
+ """
7
+ import math
8
+ import numpy as np
9
+
10
+ from python_motion_planning.utils import Plot
11
+ from .curve import Curve
12
+
13
+ class ReedsShepp(Curve):
14
+ """
15
+ Class for Reeds shepp curve generation.
16
+
17
+ Parameters:
18
+ step (float): Simulation or interpolation size
19
+ max_curv (float): The maximum curvature of the curve
20
+
21
+ Examples:
22
+ >>> from python_motion_planning.curve_generation import ReedsShepp
23
+ >>> points = [(0, 0, 0), (10, 10, -90), (20, 5, 60)]
24
+ >>> generator = ReedsShepp(step, max_curv)
25
+ >>> generator.run(points)
26
+
27
+ References:
28
+ [1] Optimal paths for a car that goes both forwards and backwards
29
+ """
30
+ def __init__(self, step: float, max_curv: float) -> None:
31
+ super().__init__(step)
32
+ self.max_curv = max_curv
33
+
34
+ def __str__(self) -> str:
35
+ return "Reeds Shepp Curve"
36
+
37
+ def R(self, x, y):
38
+ """
39
+ Return the polar coordinates (r, theta) of the point (x, y)
40
+ i.e. rcos(theta) = x; rsin(theta) = y
41
+
42
+ Parameters:
43
+ x (float): x-coordinate value
44
+ y (float): y-coordinate value
45
+
46
+ Returns:
47
+ r, theta (float): Polar coordinates
48
+
49
+ """
50
+ r = math.hypot(x, y)
51
+ theta = math.atan2(y, x)
52
+
53
+ return r, theta
54
+
55
+ def M(self, theta):
56
+ """
57
+ Truncate the angle to the interval of -π to π.
58
+
59
+ Parameters:
60
+ theta (float): Angle value
61
+
62
+ Returns:
63
+ theta (float): Truncated angle value
64
+ """
65
+ return self.pi2pi(theta)
66
+
67
+ class Path:
68
+ """
69
+ class for Path element
70
+ """
71
+ def __init__(self, lengths: list = [], ctypes: list = [], x: list = [],
72
+ y: list = [], yaw: list = [], dirs: list = []):
73
+ self.lengths = lengths # lengths of each part of path (+: forward, -: backward)
74
+ self.ctypes = ctypes # type of each part of the path
75
+ self.path_length = sum([abs(i) for i in lengths]) # total path length
76
+ self.x = x # x-coordinate value of curve
77
+ self.y = y # y-coordinate value of curve
78
+ self.yaw = yaw # yaw value of curve
79
+ self.dirs = dirs # direction value of curve (1: forward, -1: backward)
80
+
81
+ def SLS(self, x: float, y: float, phi: float):
82
+ """
83
+ Straight-Left-Straight generation mode.
84
+ """
85
+ phi = self.M(phi)
86
+
87
+ if y > 0.0 and 0.0 < phi < math.pi * 0.99:
88
+ xd = -y / math.tan(phi) + x
89
+ t = xd - math.tan(phi / 2.0)
90
+ u = phi
91
+ v = math.sqrt((x - xd) ** 2 + y ** 2) - math.tan(phi / 2.0)
92
+ return True, t, u, v
93
+ elif y < 0.0 and 0.0 < phi < math.pi * 0.99:
94
+ xd = -y / math.tan(phi) + x
95
+ t = xd - math.tan(phi / 2.0)
96
+ u = phi
97
+ v = -math.sqrt((x - xd) ** 2 + y ** 2) - math.tan(phi / 2.0)
98
+ return True, t, u, v
99
+
100
+ return False, 0.0, 0.0, 0.0
101
+
102
+ def LRL(self, x: float, y: float, phi: float):
103
+ """
104
+ Left-Right-Left generation mode. (L+R-L-)
105
+ """
106
+ r, theta = self.R(x - math.sin(phi), y - 1.0 + math.cos(phi))
107
+
108
+ if r <= 4.0:
109
+ u = -2.0 * math.asin(0.25 * r)
110
+ t = self.M(theta + 0.5 * u + math.pi)
111
+ v = self.M(phi - t + u)
112
+
113
+ if t >= 0.0 and u <= 0.0:
114
+ return True, t, u, v
115
+
116
+ return False, 0.0, 0.0, 0.0
117
+
118
+ def LSL(self, x: float, y: float, phi: float):
119
+ """
120
+ Left-Straight-Left generation mode. (L+S+L+)
121
+ """
122
+ u, t = self.R(x - math.sin(phi), y - 1.0 + math.cos(phi))
123
+
124
+ if t >= 0.0:
125
+ v = self.M(phi - t)
126
+ if v >= 0.0:
127
+ return True, t, u, v
128
+
129
+ return False, 0.0, 0.0, 0.0
130
+
131
+ def LSR(self, x: float, y: float, phi: float):
132
+ """
133
+ Left-Straight-Right generation mode. (L+S+R+)
134
+ """
135
+ r, theta = self.R(x + math.sin(phi), y - 1.0 - math.cos(phi))
136
+ r = r ** 2
137
+
138
+ if r >= 4.0:
139
+ u = math.sqrt(r - 4.0)
140
+ t = self.M(theta + math.atan2(2.0, u))
141
+ v = self.M(t - phi)
142
+
143
+ if t >= 0.0 and v >= 0.0:
144
+ return True, t, u, v
145
+
146
+ return False, 0.0, 0.0, 0.0
147
+
148
+ def LRLRn(self, x: float, y: float, phi: float):
149
+ """
150
+ Left-Right(beta)-Left(beta)-Right generation mode. (L+R+L-R-)
151
+ """
152
+ xi = x + math.sin(phi)
153
+ eta = y - 1.0 - math.cos(phi)
154
+ rho = 0.25 * (2.0 + math.sqrt(xi * xi + eta * eta))
155
+
156
+ if rho <= 1.0:
157
+ u = math.acos(rho)
158
+ t, v = self._calTauOmega(u, -u, xi, eta, phi)
159
+ if t >= 0.0 and v <= 0.0:
160
+ return True, t, u, v
161
+
162
+ return False, 0.0, 0.0, 0.0
163
+
164
+ def LRLRp(self, x: float, y: float, phi: float):
165
+ """
166
+ Left-Right(beta)-Left(beta)-Right generation mode. (L+R-L-R+)
167
+ """
168
+ xi = x + math.sin(phi)
169
+ eta = y - 1.0 - math.cos(phi)
170
+ rho = (20.0 - xi * xi - eta * eta) / 16.0
171
+
172
+ if 0.0 <= rho <= 1.0:
173
+ u = -math.acos(rho)
174
+ if u >= -0.5 * math.pi:
175
+ t, v = self._calTauOmega(u, u, xi, eta, phi)
176
+ if t >= 0.0 and v >= 0.0:
177
+ return True, t, u, v
178
+
179
+ return False, 0.0, 0.0, 0.0
180
+
181
+ def LRSR(self, x: float, y: float, phi: float):
182
+ """
183
+ Left-Right(pi/2)-Straight-Right generation mode. (L+R-S-R-)
184
+ """
185
+ xi = x + math.sin(phi)
186
+ eta = y - 1.0 - math.cos(phi)
187
+ rho, theta = self.R(-eta, xi)
188
+
189
+ if rho >= 2.0:
190
+ t = theta
191
+ u = 2.0 - rho
192
+ v = self.M(t + 0.5 * math.pi - phi)
193
+ if t >= 0.0 and u <= 0.0 and v <= 0.0:
194
+ return True, t, u, v
195
+
196
+ return False, 0.0, 0.0, 0.0
197
+
198
+ def LRSL(self, x: float, y: float, phi: float):
199
+ """
200
+ Left-Right(pi/2)-Straight-Left generation mode. (L+R-S-L-)
201
+ """
202
+ xi = x - math.sin(phi)
203
+ eta = y - 1.0 + math.cos(phi)
204
+ rho, theta = self.R(xi, eta)
205
+
206
+ if rho >= 2.0:
207
+ r = math.sqrt(rho * rho - 4.0)
208
+ u = 2.0 - r
209
+ t = self.M(theta + math.atan2(r, -2.0))
210
+ v = self.M(phi - 0.5 * math.pi - t)
211
+ if t >= 0.0 and u <= 0.0 and v <= 0.0:
212
+ return True, t, u, v
213
+
214
+ return False, 0.0, 0.0, 0.0
215
+
216
+ def LRSLR(self, x: float, y: float, phi: float):
217
+ """
218
+ Left-Right(pi/2)-Straight-Left(pi/2)-Right generation mode. (L+R-S-L-R+)
219
+ """
220
+ xi = x + math.sin(phi)
221
+ eta = y - 1.0 - math.cos(phi)
222
+ r, _ = self.R(xi, eta)
223
+
224
+ if r >= 2.0:
225
+ u = 4.0 - math.sqrt(r * r - 4.0)
226
+ if u <= 0.0:
227
+ t = self.M(math.atan2((4.0 - u) * xi - 2.0 * eta, -2.0 * xi + (u - 4.0) * eta))
228
+ v = self.M(t - phi)
229
+
230
+ if t >= 0.0 and v >= 0.0:
231
+ return True, t, u, v
232
+
233
+ return False, 0.0, 0.0, 0.0
234
+
235
+ def SCS(self, x: float, y: float, phi: float):
236
+ """
237
+ # 2
238
+ Straight-Circle-Straight generation mode(using reflect).
239
+
240
+ Parameters:
241
+ x (float): x of goal position
242
+ y (float): y of goal position
243
+ phi (float): goal orientation
244
+
245
+ Returns:
246
+ paths (list): Available paths
247
+ """
248
+ paths = []
249
+
250
+ flag, t, u, v = self.SLS(x, y, phi)
251
+ if flag:
252
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["S", "L", "S"]))
253
+
254
+ flag, t, u, v = self.SLS(x, -y, -phi)
255
+ if flag:
256
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["S", "R", "S"]))
257
+
258
+ return paths
259
+
260
+ def CCC(self, x: float, y: float, phi: float):
261
+ """
262
+ # 8
263
+ Circle-Circle-Circle generation mode(using reflect, timeflip and backwards).
264
+
265
+ Parameters:
266
+ x (float): x of goal position
267
+ y (float): y of goal position
268
+ phi (float): goal orientation
269
+
270
+ Returns:
271
+ paths (list): Available paths
272
+ """
273
+ paths = []
274
+
275
+ # L+R-L-
276
+ flag, t, u, v = self.LRL(x, y, phi)
277
+ if flag:
278
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["L", "R", "L"]))
279
+
280
+ # timefilp: L-R+L+
281
+ flag, t, u, v = self.LRL(-x, y, -phi)
282
+ if flag:
283
+ paths.append(self.Path(lengths=[-t, -u, -v], ctypes=["L", "R", "L"]))
284
+
285
+ # reflect: R+L-R-
286
+ flag, t, u, v = self.LRL(x, -y, -phi)
287
+ if flag:
288
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["R", "L", "R"]))
289
+
290
+ # timeflip + reflect: R-L+R+
291
+ flag, t, u, v = self.LRL(-x, -y, phi)
292
+ if flag:
293
+ paths.append(self.Path(lengths=[-t, -u, -v], ctypes=["R", "L", "R"]))
294
+
295
+ # backwards
296
+ xb = x * math.cos(phi) + y * math.sin(phi)
297
+ yb = x * math.sin(phi) - y * math.cos(phi)
298
+
299
+ # backwards: L-R-L+
300
+ flag, t, u, v = self.LRL(xb, yb, phi)
301
+ if flag:
302
+ paths.append(self.Path(lengths=[v, u, t], ctypes=["L", "R", "L"]))
303
+
304
+ # backwards + timefilp: L+R+L-
305
+ flag, t, u, v = self.LRL(-xb, yb, -phi)
306
+ if flag:
307
+ paths.append(self.Path(lengths=[-v, -u, -t], ctypes=["L", "R", "L"]))
308
+
309
+ # backwards + reflect: R-L-R+
310
+ flag, t, u, v = self.LRL(xb, -yb, -phi)
311
+ if flag:
312
+ paths.append(self.Path(lengths=[v, u, t], ctypes=["R", "L", "R"]))
313
+
314
+ # backwards + timeflip + reflect: R+L+R-
315
+ flag, t, u, v = self.LRL(-xb, -yb, phi)
316
+ if flag:
317
+ paths.append(self.Path(lengths=[-v, -u, -t], ctypes=["R", "L", "R"]))
318
+
319
+ return paths
320
+
321
+ def CSC(self, x: float, y: float, phi: float):
322
+ """
323
+ # 8
324
+ Circle-Straight-Circle generation mode(using reflect, timeflip and backwards).
325
+
326
+ Parameters:
327
+ x (float): x of goal position
328
+ y (float): y of goal position
329
+ phi (float): goal orientation
330
+
331
+ Returns:
332
+ paths (list): Available paths
333
+ """
334
+ paths = []
335
+
336
+ # L+S+L+
337
+ flag, t, u, v = self.LSL(x, y, phi)
338
+ if flag:
339
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["L", "S", "L"]))
340
+
341
+ # timefilp: L-S-L-
342
+ flag, t, u, v = self.LSL(-x, y, -phi)
343
+ if flag:
344
+ paths.append(self.Path(lengths=[-t, -u, -v], ctypes=["L", "S", "L"]))
345
+
346
+ # reflect: R+S+R+
347
+ flag, t, u, v = self.LSL(x, -y, -phi)
348
+ if flag:
349
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["R", "S", "R"]))
350
+
351
+ # timeflip + reflect: R-S-R-
352
+ flag, t, u, v = self.LSL(-x, -y, phi)
353
+ if flag:
354
+ paths.append(self.Path(lengths=[-t, -u, -v], ctypes=["R", "S", "R"]))
355
+
356
+ # L+S+R+
357
+ flag, t, u, v = self.LSR(x, y, phi)
358
+ if flag:
359
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["L", "S", "R"]))
360
+
361
+ # timefilp: L-S-R-
362
+ flag, t, u, v = self.LSR(-x, y, -phi)
363
+ if flag:
364
+ paths.append(self.Path(lengths=[-t, -u, -v], ctypes=["L", "S", "R"]))
365
+
366
+ # reflect: R+S+L+
367
+ flag, t, u, v = self.LSR(x, -y, -phi)
368
+ if flag:
369
+ paths.append(self.Path(lengths=[t, u, v], ctypes=["R", "S", "L"]))
370
+
371
+ # timeflip + reflect: R+S+l-
372
+ flag, t, u, v = self.LSR(-x, -y, phi)
373
+ if flag:
374
+ paths.append(self.Path(lengths=[-t, -u, -v], ctypes=["R", "S", "L"]))
375
+
376
+ return paths
377
+
378
+ def CCCC(self, x: float, y: float, phi: float):
379
+ """
380
+ # 8
381
+ Circle-Circle(beta)-Circle(beta)-Circle generation mode
382
+ (using reflect, timeflip and backwards).
383
+
384
+ Parameters:
385
+ x (float): x of goal position
386
+ y (float): y of goal position
387
+ phi (float): goal orientation
388
+
389
+ Returns:
390
+ paths (list): Available paths
391
+ """
392
+ paths = []
393
+
394
+ # L+R+L-R-
395
+ flag, t, u, v = self.LRLRn(x, y, phi)
396
+ if flag:
397
+ paths.append(self.Path(lengths=[t, u, -u, v], ctypes=["L", "R", "L", "R"]))
398
+
399
+ # timefilp: L-R-L+R+
400
+ flag, t, u, v = self.LRLRn(-x, y, -phi)
401
+ if flag:
402
+ paths.append(self.Path(lengths=[-t, -u, u, -v], ctypes=["L", "R", "L", "R"]))
403
+
404
+ # reflect: R+L+R-L-
405
+ flag, t, u, v = self.LRLRn(x, -y, -phi)
406
+ if flag:
407
+ paths.append(self.Path(lengths=[t, u, -u, v], ctypes=["R", "L", "R", "L"]))
408
+
409
+ # timeflip + reflect: R-L-R+L+
410
+ flag, t, u, v = self.LRLRn(-x, -y, phi)
411
+ if flag:
412
+ paths.append(self.Path(lengths=[-t, -u, u, -v], ctypes=["R", "L", "R", "L"]))
413
+
414
+ # L+R-L-R+
415
+ flag, t, u, v = self.LRLRp(x, y, phi)
416
+ if flag:
417
+ paths.append(self.Path(lengths=[t, u, u, v], ctypes=["L", "R", "L", "R"]))
418
+
419
+ # timefilp: L-R+L+R-
420
+ flag, t, u, v = self.LRLRp(-x, y, -phi)
421
+ if flag:
422
+ paths.append(self.Path(lengths=[-t, -u, -u, -v], ctypes=["L", "R", "L", "R"]))
423
+
424
+ # reflect: R+L-R-L+
425
+ flag, t, u, v = self.LRLRp(x, -y, -phi)
426
+ if flag:
427
+ paths.append(self.Path(lengths=[t, u, u, v], ctypes=["R", "L", "R", "L"]))
428
+
429
+ # timeflip + reflect: R-L+R+L-
430
+ flag, t, u, v = self.LRLRp(-x, -y, phi)
431
+ if flag:
432
+ paths.append(self.Path(lengths=[-t, -u, -u, -v], ctypes=["R", "L", "R", "L"]))
433
+
434
+ return paths
435
+
436
+ def CCSC(self, x: float, y: float, phi: float):
437
+ """
438
+ # 16
439
+ Circle-Circle(pi/2)-Straight-Circle and Circle-Straight-Circle(pi/2)-Circle
440
+ generation mode (using reflect, timeflip and backwards).
441
+
442
+ Parameters:
443
+ x (float): x of goal position
444
+ y (float): y of goal position
445
+ phi (float): goal orientation
446
+
447
+ Returns:
448
+ paths (list): Available paths
449
+ """
450
+ paths = []
451
+
452
+ # L+R-(pi/2)S-L-
453
+ flag, t, u, v = self.LRSL(x, y, phi)
454
+ if flag:
455
+ paths.append(self.Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["L", "R", "S", "L"]))
456
+
457
+ # timefilp: L-R+(pi/2)S+L+
458
+ flag, t, u, v = self.LRSL(-x, y, -phi)
459
+ if flag:
460
+ paths.append(self.Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["L", "R", "S", "L"]))
461
+
462
+ # reflect: R+L-(pi/2)S-R-
463
+ flag, t, u, v = self.LRSL(x, -y, -phi)
464
+ if flag:
465
+ paths.append(self.Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["R", "L", "S", "R"]))
466
+
467
+ # timeflip + reflect: R-L+(pi/2)S+R+
468
+ flag, t, u, v = self.LRSL(-x, -y, phi)
469
+ if flag:
470
+ paths.append(self.Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["R", "L", "S", "R"]))
471
+
472
+ # L+R-(pi/2)S-R-
473
+ flag, t, u, v = self.LRSR(x, y, phi)
474
+ if flag:
475
+ paths.append(self.Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["L", "R", "S", "R"]))
476
+
477
+ # timefilp: L-R+(pi/2)S+R+
478
+ flag, t, u, v = self.LRSR(-x, y, -phi)
479
+ if flag:
480
+ paths.append(self.Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["L", "R", "S", "R"]))
481
+
482
+ # reflect: R+L-(pi/2)S-L-
483
+ flag, t, u, v = self.LRSR(x, -y, -phi)
484
+ if flag:
485
+ paths.append(self.Path(lengths=[t, -0.5 * math.pi, u, v], ctypes=["R", "L", "S", "L"]))
486
+
487
+ # timeflip + reflect: R-L+(pi/2)S+L+
488
+ flag, t, u, v = self.LRSR(-x, -y, phi)
489
+ if flag:
490
+ paths.append(self.Path(lengths=[-t, 0.5 * math.pi, -u, -v], ctypes=["R", "L", "S", "L"]))
491
+
492
+ # backwards
493
+ xb = x * math.cos(phi) + y * math.sin(phi)
494
+ yb = x * math.sin(phi) - y * math.cos(phi)
495
+
496
+ # backwards: L-S-R-(pi/2)L+
497
+ flag, t, u, v = self.LRSL(xb, yb, phi)
498
+ if flag:
499
+ paths.append(self.Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["L", "S", "R", "L"]))
500
+
501
+ # backwards + timefilp: L+S+R+(pi/2)L-
502
+ flag, t, u, v = self.LRSL(-xb, yb, -phi)
503
+ if flag:
504
+ paths.append(self.Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["L", "S", "R", "L"]))
505
+
506
+ # backwards + reflect: R-S-L-(pi/2)R+
507
+ flag, t, u, v = self.LRSL(xb, -yb, -phi)
508
+ if flag:
509
+ paths.append(self.Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["R", "S", "L", "R"]))
510
+
511
+ # backwards + timefilp + reflect: R+S+L+(pi/2)R-
512
+ flag, t, u, v = self.LRSL(-xb, -yb, phi)
513
+ if flag:
514
+ paths.append(self.Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["R", "S", "L", "R"]))
515
+
516
+ # backwards: R-S-R-(pi/2)L+
517
+ flag, t, u, v = self.LRSR(xb, yb, phi)
518
+ if flag:
519
+ paths.append(self.Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["R", "S", "R", "L"]))
520
+
521
+ # backwards + timefilp: R+S+R+(pi/2)L-
522
+ flag, t, u, v = self.LRSR(-xb, yb, -phi)
523
+ if flag:
524
+ paths.append(self.Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["R", "S", "R", "L"]))
525
+
526
+ # backwards + reflect: L-S-L-(pi/2)R+
527
+ flag, t, u, v = self.LRSR(xb, -yb, -phi)
528
+ if flag:
529
+ paths.append(self.Path(lengths=[v, u, -0.5 * math.pi, t], ctypes=["L", "S", "L", "R"]))
530
+
531
+ # backwards + timefilp + reflect: L+S+L+(pi/2)R-
532
+ flag, t, u, v = self.LRSR(-xb, -yb, phi)
533
+ if flag:
534
+ paths.append(self.Path(lengths=[-v, -u, 0.5 * math.pi, -t], ctypes=["L", "S", "L", "R"]))
535
+
536
+ return paths
537
+
538
+ def CCSCC(self, x: float, y: float, phi: float):
539
+ """
540
+ # 4
541
+ Circle-Circle(pi/2)-Straight--Circle(pi/2)-Circle generation mode (using reflect, timeflip and backwards).
542
+
543
+ Parameters:
544
+ x (float): x of goal position
545
+ y (float): y of goal position
546
+ phi (float): goal orientation
547
+
548
+ Returns:
549
+ paths (list): Available paths
550
+ """
551
+ paths = []
552
+
553
+ # L+R-(pi/2)S-L-(pi/2)R+
554
+ flag, t, u, v = self.LRSLR(x, y, phi)
555
+ if flag:
556
+ paths.append(self.Path(lengths=[t, -0.5 * math.pi, u, -0.5 * math.pi, v], ctypes=["L", "R", "S", "L", "R"]))
557
+
558
+ # timefilp: L-R+(pi/2)S+L+(pi/2)R-
559
+ flag, t, u, v = self.LRSLR(-x, y, -phi)
560
+ if flag:
561
+ paths.append(self.Path(lengths=[-t, 0.5 * math.pi, -u, 0.5 * math.pi, -v], ctypes=["L", "R", "S", "L", "R"]))
562
+
563
+ # reflect: R+L-(pi/2)S-R-(pi/2)L+
564
+ flag, t, u, v = self.LRSLR(x, -y, -phi)
565
+ if flag:
566
+ paths.append(self.Path(lengths=[t, -0.5 * math.pi, u, -0.5 * math.pi, v], ctypes=["R", "L", "S", "R", "L"]))
567
+
568
+ # timefilp + reflect: R-L+(pi/2)S+R+(pi/2)L-
569
+ flag, t, u, v = self.LRSLR(-x, -y, phi)
570
+ if flag:
571
+ paths.append(self.Path(lengths=[-t, 0.5 * math.pi, -u, 0.5 * math.pi, -v], ctypes=["R", "L", "S", "R", "L"]))
572
+
573
+ return paths
574
+
575
+ def interpolate(self, mode: str, length: float, init_pose: tuple):
576
+ """
577
+ Planning path interpolation.
578
+
579
+ Parameters:
580
+ mode (str): motion, e.g., L, S, R
581
+ length (float): Single step motion path length
582
+ init_pose (tuple): Initial pose (x, y, yaw)
583
+
584
+ Returns:
585
+ new_pose (tuple): New pose (new_x, new_y, new_yaw) after moving
586
+ """
587
+ x, y, yaw = init_pose
588
+
589
+ if mode == "S":
590
+ new_x = x + length / self.max_curv * math.cos(yaw)
591
+ new_y = y + length / self.max_curv * math.sin(yaw)
592
+ new_yaw = yaw
593
+ elif mode == "L":
594
+ new_x = x + (math.sin(yaw + length) - math.sin(yaw)) / self.max_curv
595
+ new_y = y - (math.cos(yaw + length) - math.cos(yaw)) / self.max_curv
596
+ new_yaw = yaw + length
597
+ elif mode == "R":
598
+ new_x = x - (math.sin(yaw - length) - math.sin(yaw)) / self.max_curv
599
+ new_y = y + (math.cos(yaw - length) - math.cos(yaw)) / self.max_curv
600
+ new_yaw = yaw - length
601
+ else:
602
+ raise NotImplementedError
603
+
604
+ return new_x, new_y, new_yaw
605
+
606
+ def generation(self, start_pose: tuple, goal_pose: tuple):
607
+ """
608
+ Generate the Reeds Shepp Curve.
609
+
610
+ Parameters:
611
+ start_pose (tuple): Initial pose (x, y, yaw)
612
+ goal_pose (tuple): Target pose (x, y, yaw)
613
+
614
+ Returns:
615
+ best_cost (float): Best planning path length
616
+ best_mode: Best motion modes
617
+ x_list (list): Trajectory of x
618
+ y_list (list): Trajectory of y
619
+ yaw_list (list): Trajectory of yaw
620
+ """
621
+ sx, sy, syaw = start_pose
622
+ gx, gy, gyaw = goal_pose
623
+
624
+ # coordinate transformation
625
+ dx, dy, dyaw = gx - sx, gy - sy, gyaw - syaw
626
+ x = (math.cos(syaw) * dx + math.sin(syaw) * dy) * self.max_curv
627
+ y = (-math.sin(syaw) * dx + math.cos(syaw) * dy) * self.max_curv
628
+
629
+ # select the best motion
630
+ planners = [self.SCS, self.CCC, self.CSC, self.CCCC, self.CCSC, self.CCSCC]
631
+ best_path, best_cost = None, float("inf")
632
+
633
+ for planner in planners:
634
+ paths = planner(x, y, dyaw)
635
+ for path in paths:
636
+ if path.path_length < best_cost:
637
+ best_path, best_cost = path, path.path_length
638
+
639
+ # interpolation
640
+ points_num = int(best_cost / self.step) + len(best_path.lengths) + 3
641
+ x_list = [0.0 for _ in range(points_num)]
642
+ y_list = [0.0 for _ in range(points_num)]
643
+ yaw_list = [0.0 for _ in range(points_num)]
644
+
645
+ i = 0
646
+ for mode_, seg_length in zip(best_path.ctypes, best_path.lengths):
647
+ # path increment
648
+ d_length = self.step if seg_length > 0.0 else -self.step
649
+ x, y, yaw = x_list[i], y_list[i], yaw_list[i]
650
+ # current path length
651
+ length = d_length
652
+ while abs(length) <= abs(seg_length):
653
+ i += 1
654
+ x_list[i], y_list[i], yaw_list[i] = self.interpolate(mode_, length, (x, y, yaw))
655
+ length += d_length
656
+ i += 1
657
+ x_list[i], y_list[i], yaw_list[i] = self.interpolate(mode_, seg_length, (x, y, yaw))
658
+
659
+ # failed
660
+ if len(x_list) <= 1:
661
+ return None, None, [], [], []
662
+
663
+ # remove unused data
664
+ while len(x_list) >= 1 and x_list[-1] == 0.0:
665
+ x_list.pop()
666
+ y_list.pop()
667
+ yaw_list.pop()
668
+
669
+ # coordinate transformation
670
+ x_list_ = [math.cos(-syaw) * ix + math.sin(-syaw) * iy + sx for (ix, iy) in zip(x_list, y_list)]
671
+ y_list_ = [-math.sin(-syaw) * ix + math.cos(-syaw) * iy + sy for (ix, iy) in zip(x_list, y_list)]
672
+ yaw_list_ = [self.pi2pi(iyaw + syaw) for iyaw in yaw_list]
673
+
674
+ return best_cost / self.max_curv, best_path.ctypes, x_list_, y_list_, yaw_list_
675
+
676
+ def run(self, points: list):
677
+ """
678
+ Running both generation and animation.
679
+
680
+ Parameters:
681
+ points (list[tuple]): path points
682
+ """
683
+ assert len(points) >= 2, "Number of points should be at least 2."
684
+ import matplotlib.pyplot as plt
685
+
686
+ # generation
687
+ path_x, path_y, path_yaw = [], [], []
688
+ for i in range(len(points) - 1):
689
+ _, _, x_list, y_list, yaw_list = self.generation(
690
+ (points[i][0], points[i][1], np.deg2rad(points[i][2])),
691
+ (points[i + 1][0], points[i + 1][1], np.deg2rad(points[i + 1][2])))
692
+
693
+ for j in range(len(x_list)):
694
+ path_x.append(x_list[j])
695
+ path_y.append(y_list[j])
696
+ path_yaw.append(yaw_list[j])
697
+
698
+ # animation
699
+ plt.figure("curve generation")
700
+ # # static
701
+ # plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
702
+ # for x, y, theta in points:
703
+ # Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')
704
+
705
+ # dynamic
706
+ plt.ion()
707
+ for i in range(len(path_x)):
708
+ plt.clf()
709
+ plt.gcf().canvas.mpl_connect('key_release_event',
710
+ lambda event: [exit(0) if event.key == 'escape' else None])
711
+ plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
712
+ for x, y, theta in points:
713
+ Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')
714
+ Plot.plotCar(path_x[i], path_y[i], path_yaw[i], 1.5, 3, "black")
715
+ plt.axis("equal")
716
+ plt.title(str(self))
717
+ plt.draw()
718
+ plt.pause(0.001)
719
+
720
+ plt.axis("equal")
721
+ plt.title(str(self))
722
+ plt.show()
723
+
724
+
725
+ def _calTauOmega(self, u, v, xi, eta, phi):
726
+ delta = self.M(u - v)
727
+ A = math.sin(u) - math.sin(delta)
728
+ B = math.cos(u) - math.cos(delta) - 1.0
729
+
730
+ t1 = math.atan2(eta * A - xi * B, xi * A + eta * B)
731
+ t2 = 2.0 * (math.cos(delta) - math.cos(v) - math.cos(u)) + 3.0
732
+
733
+ tau = self.M(t1 + math.pi) if t2 < 0 else self.M(t1)
734
+ omega = self.M(tau - u + v - phi)
735
+
736
+ return tau, omega