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.
- curve_generation/__init__.py +9 -0
- curve_generation/bezier_curve.py +131 -0
- curve_generation/bspline_curve.py +271 -0
- curve_generation/cubic_spline.py +128 -0
- curve_generation/curve.py +64 -0
- curve_generation/dubins_curve.py +348 -0
- curve_generation/fem_pos_smooth.py +114 -0
- curve_generation/polynomial_curve.py +226 -0
- curve_generation/reeds_shepp.py +736 -0
- global_planner/__init__.py +3 -0
- global_planner/evolutionary_search/__init__.py +4 -0
- global_planner/evolutionary_search/aco.py +186 -0
- global_planner/evolutionary_search/evolutionary_search.py +87 -0
- global_planner/evolutionary_search/pso.py +356 -0
- global_planner/graph_search/__init__.py +28 -0
- global_planner/graph_search/a_star.py +124 -0
- global_planner/graph_search/d_star.py +291 -0
- global_planner/graph_search/d_star_lite.py +188 -0
- global_planner/graph_search/dijkstra.py +77 -0
- global_planner/graph_search/gbfs.py +78 -0
- global_planner/graph_search/graph_search.py +87 -0
- global_planner/graph_search/jps.py +165 -0
- global_planner/graph_search/lazy_theta_star.py +114 -0
- global_planner/graph_search/lpa_star.py +230 -0
- global_planner/graph_search/s_theta_star.py +133 -0
- global_planner/graph_search/theta_star.py +171 -0
- global_planner/graph_search/voronoi.py +200 -0
- global_planner/sample_search/__init__.py +6 -0
- global_planner/sample_search/informed_rrt.py +152 -0
- global_planner/sample_search/rrt.py +151 -0
- global_planner/sample_search/rrt_connect.py +147 -0
- global_planner/sample_search/rrt_star.py +77 -0
- global_planner/sample_search/sample_search.py +135 -0
- local_planner/__init__.py +19 -0
- local_planner/apf.py +144 -0
- local_planner/ddpg.py +630 -0
- local_planner/dqn.py +687 -0
- local_planner/dwa.py +212 -0
- local_planner/local_planner.py +262 -0
- local_planner/lqr.py +146 -0
- local_planner/mpc.py +214 -0
- local_planner/pid.py +158 -0
- local_planner/rpp.py +147 -0
- python_motion_planning-1.0.dist-info/LICENSE +674 -0
- python_motion_planning-1.0.dist-info/METADATA +873 -0
- python_motion_planning-1.0.dist-info/RECORD +65 -0
- python_motion_planning-1.0.dist-info/WHEEL +5 -0
- python_motion_planning-1.0.dist-info/top_level.txt +4 -0
- utils/__init__.py +19 -0
- utils/agent/__init__.py +0 -0
- utils/agent/agent.py +135 -0
- utils/environment/__init__.py +0 -0
- utils/environment/env.py +134 -0
- utils/environment/node.py +85 -0
- utils/environment/point2d.py +96 -0
- utils/environment/pose2d.py +91 -0
- utils/helper/__init__.py +3 -0
- utils/helper/math_helper.py +65 -0
- utils/planner/__init__.py +0 -0
- utils/planner/control_factory.py +31 -0
- utils/planner/curve_factory.py +29 -0
- utils/planner/planner.py +40 -0
- utils/planner/search_factory.py +51 -0
- utils/plot/__init__.py +0 -0
- 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
|