pathplanning 0.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.
- CurvesGenerator/__init__.py +1 -0
- CurvesGenerator/bezier_path.py +145 -0
- CurvesGenerator/bspline_curve.py +80 -0
- CurvesGenerator/cubic_spline.py +262 -0
- CurvesGenerator/draw.py +66 -0
- CurvesGenerator/dubins_path.py +351 -0
- CurvesGenerator/quartic_polynomial.py +43 -0
- CurvesGenerator/quintic_polynomial.py +143 -0
- CurvesGenerator/reeds_shepp.py +720 -0
- Sampling_based_Planning/__init__.py +1 -0
- Sampling_based_Planning/rrt_2D/__init__.py +1 -0
- Sampling_based_Planning/rrt_2D/adaptively_informed_trees.py +0 -0
- Sampling_based_Planning/rrt_2D/advanced_batch_informed_trees.py +0 -0
- Sampling_based_Planning/rrt_2D/batch_informed_trees.py +402 -0
- Sampling_based_Planning/rrt_2D/dubins_rrt_star.py +321 -0
- Sampling_based_Planning/rrt_2D/dynamic_rrt.py +342 -0
- Sampling_based_Planning/rrt_2D/env.py +45 -0
- Sampling_based_Planning/rrt_2D/extended_rrt.py +251 -0
- Sampling_based_Planning/rrt_2D/fast_marching_trees.py +220 -0
- Sampling_based_Planning/rrt_2D/informed_rrt_star.py +305 -0
- Sampling_based_Planning/rrt_2D/plotting.py +115 -0
- Sampling_based_Planning/rrt_2D/queue.py +62 -0
- Sampling_based_Planning/rrt_2D/rrt.py +114 -0
- Sampling_based_Planning/rrt_2D/rrt_connect.py +156 -0
- Sampling_based_Planning/rrt_2D/rrt_sharp.py +0 -0
- Sampling_based_Planning/rrt_2D/rrt_star.py +184 -0
- Sampling_based_Planning/rrt_2D/rrt_star_smart.py +311 -0
- Sampling_based_Planning/rrt_2D/utils.py +138 -0
- Sampling_based_Planning/rrt_3D/ABIT_star3D.py +148 -0
- Sampling_based_Planning/rrt_3D/BIT_star3D.py +353 -0
- Sampling_based_Planning/rrt_3D/FMT_star3D.py +192 -0
- Sampling_based_Planning/rrt_3D/__init__.py +1 -0
- Sampling_based_Planning/rrt_3D/dynamic_rrt3D.py +239 -0
- Sampling_based_Planning/rrt_3D/env3D.py +148 -0
- Sampling_based_Planning/rrt_3D/extend_rrt3D.py +102 -0
- Sampling_based_Planning/rrt_3D/informed_rrt_star3D.py +234 -0
- Sampling_based_Planning/rrt_3D/plot_util3D.py +167 -0
- Sampling_based_Planning/rrt_3D/queue.py +86 -0
- Sampling_based_Planning/rrt_3D/rrt3D.py +72 -0
- Sampling_based_Planning/rrt_3D/rrt_connect3D.py +187 -0
- Sampling_based_Planning/rrt_3D/rrt_star3D.py +107 -0
- Sampling_based_Planning/rrt_3D/rrt_star_smart3D.py +0 -0
- Sampling_based_Planning/rrt_3D/utils3D.py +416 -0
- Search_based_Planning/Search_3D/Anytime_Dstar3D.py +245 -0
- Search_based_Planning/Search_3D/Astar3D.py +119 -0
- Search_based_Planning/Search_3D/Dstar3D.py +192 -0
- Search_based_Planning/Search_3D/DstarLite3D.py +219 -0
- Search_based_Planning/Search_3D/LP_Astar3D.py +194 -0
- Search_based_Planning/Search_3D/LRT_Astar3D.py +87 -0
- Search_based_Planning/Search_3D/RTA_Astar3D.py +78 -0
- Search_based_Planning/Search_3D/__init__.py +1 -0
- Search_based_Planning/Search_3D/bidirectional_Astar3D.py +115 -0
- Search_based_Planning/Search_3D/env3D.py +147 -0
- Search_based_Planning/Search_3D/plot_util3D.py +155 -0
- Search_based_Planning/Search_3D/queue.py +154 -0
- Search_based_Planning/Search_3D/utils3D.py +359 -0
- Search_based_Planning/__init__.py +1 -0
- Search_based_Planning/plan2d/__init__.py +17 -0
- Search_based_Planning/plan2d/anytime_dstar.py +314 -0
- Search_based_Planning/plan2d/anytime_repairing_astar.py +220 -0
- Search_based_Planning/plan2d/astar.py +225 -0
- Search_based_Planning/plan2d/best_first_search.py +67 -0
- Search_based_Planning/plan2d/bidirectional_astar.py +229 -0
- Search_based_Planning/plan2d/breadth_first_search.py +69 -0
- Search_based_Planning/plan2d/depth_first_search.py +69 -0
- Search_based_Planning/plan2d/dijkstra.py +68 -0
- Search_based_Planning/plan2d/dstar.py +304 -0
- Search_based_Planning/plan2d/dstar_lite.py +239 -0
- Search_based_Planning/plan2d/learning_realtime_astar.py +230 -0
- Search_based_Planning/plan2d/lifelong_planning_astar.py +254 -0
- Search_based_Planning/plan2d/plan2d_facade.py +494 -0
- Search_based_Planning/plan2d/realtime_adaptive_astar.py +234 -0
- Search_based_Planning/plan2d/run.py +55 -0
- Search_based_Planning/plan2d/utils/__init__.py +1 -0
- Search_based_Planning/plan2d/utils/env.py +48 -0
- Search_based_Planning/plan2d/utils/plotting.py +258 -0
- Search_based_Planning/plan2d/utils/queue.py +62 -0
- pathplanning-0.1.0.dist-info/LICENSE +21 -0
- pathplanning-0.1.0.dist-info/METADATA +354 -0
- pathplanning-0.1.0.dist-info/RECORD +86 -0
- pathplanning-0.1.0.dist-info/WHEEL +5 -0
- pathplanning-0.1.0.dist-info/top_level.txt +4 -0
- pathplanningv2/__init__.py +29 -0
- pathplanningv2/api.py +18 -0
- pathplanningv2/registry.py +107 -0
- pathplanningv2/search2d.py +15 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""
|
|
2
|
+
INFORMED_RRT_STAR 2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import math
|
|
9
|
+
import random
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
from scipy.spatial.transform import Rotation as Rot
|
|
13
|
+
import matplotlib.patches as patches
|
|
14
|
+
|
|
15
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
|
|
16
|
+
"/../../Sampling_based_Planning/")
|
|
17
|
+
|
|
18
|
+
from Sampling_based_Planning.rrt_2D import env, plotting, utils
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Node:
|
|
22
|
+
def __init__(self, n):
|
|
23
|
+
self.x = n[0]
|
|
24
|
+
self.y = n[1]
|
|
25
|
+
self.parent = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class IRrtStar:
|
|
29
|
+
def __init__(self, x_start, x_goal, step_len,
|
|
30
|
+
goal_sample_rate, search_radius, iter_max):
|
|
31
|
+
self.x_start = Node(x_start)
|
|
32
|
+
self.x_goal = Node(x_goal)
|
|
33
|
+
self.step_len = step_len
|
|
34
|
+
self.goal_sample_rate = goal_sample_rate
|
|
35
|
+
self.search_radius = search_radius
|
|
36
|
+
self.iter_max = iter_max
|
|
37
|
+
|
|
38
|
+
self.env = env.Env()
|
|
39
|
+
self.plotting = plotting.Plotting(x_start, x_goal)
|
|
40
|
+
self.utils = utils.Utils()
|
|
41
|
+
|
|
42
|
+
self.fig, self.ax = plt.subplots()
|
|
43
|
+
self.delta = self.utils.delta
|
|
44
|
+
self.x_range = self.env.x_range
|
|
45
|
+
self.y_range = self.env.y_range
|
|
46
|
+
self.obs_circle = self.env.obs_circle
|
|
47
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
48
|
+
self.obs_boundary = self.env.obs_boundary
|
|
49
|
+
|
|
50
|
+
self.V = [self.x_start]
|
|
51
|
+
self.X_soln = set()
|
|
52
|
+
self.path = None
|
|
53
|
+
|
|
54
|
+
def init(self):
|
|
55
|
+
cMin, theta = self.get_distance_and_angle(self.x_start, self.x_goal)
|
|
56
|
+
C = self.RotationToWorldFrame(self.x_start, self.x_goal, cMin)
|
|
57
|
+
xCenter = np.array([[(self.x_start.x + self.x_goal.x) / 2.0],
|
|
58
|
+
[(self.x_start.y + self.x_goal.y) / 2.0], [0.0]])
|
|
59
|
+
x_best = self.x_start
|
|
60
|
+
|
|
61
|
+
return theta, cMin, xCenter, C, x_best
|
|
62
|
+
|
|
63
|
+
def planning(self):
|
|
64
|
+
theta, dist, x_center, C, x_best = self.init()
|
|
65
|
+
c_best = np.inf
|
|
66
|
+
|
|
67
|
+
for k in range(self.iter_max):
|
|
68
|
+
if self.X_soln:
|
|
69
|
+
cost = {node: self.Cost(node) for node in self.X_soln}
|
|
70
|
+
x_best = min(cost, key=cost.get)
|
|
71
|
+
c_best = cost[x_best]
|
|
72
|
+
|
|
73
|
+
x_rand = self.Sample(c_best, dist, x_center, C)
|
|
74
|
+
x_nearest = self.Nearest(self.V, x_rand)
|
|
75
|
+
x_new = self.Steer(x_nearest, x_rand)
|
|
76
|
+
|
|
77
|
+
if x_new and not self.utils.is_collision(x_nearest, x_new):
|
|
78
|
+
X_near = self.Near(self.V, x_new)
|
|
79
|
+
c_min = self.Cost(x_nearest) + self.Line(x_nearest, x_new)
|
|
80
|
+
self.V.append(x_new)
|
|
81
|
+
|
|
82
|
+
# choose parent
|
|
83
|
+
for x_near in X_near:
|
|
84
|
+
c_new = self.Cost(x_near) + self.Line(x_near, x_new)
|
|
85
|
+
if c_new < c_min:
|
|
86
|
+
x_new.parent = x_near
|
|
87
|
+
c_min = c_new
|
|
88
|
+
|
|
89
|
+
# rewire
|
|
90
|
+
for x_near in X_near:
|
|
91
|
+
c_near = self.Cost(x_near)
|
|
92
|
+
c_new = self.Cost(x_new) + self.Line(x_new, x_near)
|
|
93
|
+
if c_new < c_near:
|
|
94
|
+
x_near.parent = x_new
|
|
95
|
+
|
|
96
|
+
if self.InGoalRegion(x_new):
|
|
97
|
+
if not self.utils.is_collision(x_new, self.x_goal):
|
|
98
|
+
self.X_soln.add(x_new)
|
|
99
|
+
# new_cost = self.Cost(x_new) + self.Line(x_new, self.x_goal)
|
|
100
|
+
# if new_cost < c_best:
|
|
101
|
+
# c_best = new_cost
|
|
102
|
+
# x_best = x_new
|
|
103
|
+
|
|
104
|
+
if k % 20 == 0:
|
|
105
|
+
self.animation(x_center=x_center, c_best=c_best, dist=dist, theta=theta)
|
|
106
|
+
|
|
107
|
+
self.path = self.ExtractPath(x_best)
|
|
108
|
+
self.animation(x_center=x_center, c_best=c_best, dist=dist, theta=theta)
|
|
109
|
+
plt.plot([x for x, _ in self.path], [y for _, y in self.path], '-r')
|
|
110
|
+
plt.pause(0.01)
|
|
111
|
+
plt.show()
|
|
112
|
+
|
|
113
|
+
def Steer(self, x_start, x_goal):
|
|
114
|
+
dist, theta = self.get_distance_and_angle(x_start, x_goal)
|
|
115
|
+
dist = min(self.step_len, dist)
|
|
116
|
+
node_new = Node((x_start.x + dist * math.cos(theta),
|
|
117
|
+
x_start.y + dist * math.sin(theta)))
|
|
118
|
+
node_new.parent = x_start
|
|
119
|
+
|
|
120
|
+
return node_new
|
|
121
|
+
|
|
122
|
+
def Near(self, nodelist, node):
|
|
123
|
+
n = len(nodelist) + 1
|
|
124
|
+
r = 50 * math.sqrt((math.log(n) / n))
|
|
125
|
+
|
|
126
|
+
dist_table = [(nd.x - node.x) ** 2 + (nd.y - node.y) ** 2 for nd in nodelist]
|
|
127
|
+
X_near = [nodelist[ind] for ind in range(len(dist_table)) if dist_table[ind] <= r ** 2 and
|
|
128
|
+
not self.utils.is_collision(nodelist[ind], node)]
|
|
129
|
+
|
|
130
|
+
return X_near
|
|
131
|
+
|
|
132
|
+
def Sample(self, c_max, c_min, x_center, C):
|
|
133
|
+
if c_max < np.inf:
|
|
134
|
+
r = [c_max / 2.0,
|
|
135
|
+
math.sqrt(c_max ** 2 - c_min ** 2) / 2.0,
|
|
136
|
+
math.sqrt(c_max ** 2 - c_min ** 2) / 2.0]
|
|
137
|
+
L = np.diag(r)
|
|
138
|
+
|
|
139
|
+
while True:
|
|
140
|
+
x_ball = self.SampleUnitBall()
|
|
141
|
+
x_rand = np.dot(np.dot(C, L), x_ball) + x_center
|
|
142
|
+
if self.x_range[0] + self.delta <= x_rand[0] <= self.x_range[1] - self.delta and \
|
|
143
|
+
self.y_range[0] + self.delta <= x_rand[1] <= self.y_range[1] - self.delta:
|
|
144
|
+
break
|
|
145
|
+
x_rand = Node((x_rand[(0, 0)], x_rand[(1, 0)]))
|
|
146
|
+
else:
|
|
147
|
+
x_rand = self.SampleFreeSpace()
|
|
148
|
+
|
|
149
|
+
return x_rand
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def SampleUnitBall():
|
|
153
|
+
while True:
|
|
154
|
+
x, y = random.uniform(-1, 1), random.uniform(-1, 1)
|
|
155
|
+
if x ** 2 + y ** 2 < 1:
|
|
156
|
+
return np.array([[x], [y], [0.0]])
|
|
157
|
+
|
|
158
|
+
def SampleFreeSpace(self):
|
|
159
|
+
delta = self.delta
|
|
160
|
+
|
|
161
|
+
if np.random.random() > self.goal_sample_rate:
|
|
162
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
163
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
164
|
+
|
|
165
|
+
return self.x_goal
|
|
166
|
+
|
|
167
|
+
def ExtractPath(self, node):
|
|
168
|
+
path = [[self.x_goal.x, self.x_goal.y]]
|
|
169
|
+
|
|
170
|
+
while node.parent:
|
|
171
|
+
path.append([node.x, node.y])
|
|
172
|
+
node = node.parent
|
|
173
|
+
|
|
174
|
+
path.append([self.x_start.x, self.x_start.y])
|
|
175
|
+
|
|
176
|
+
return path
|
|
177
|
+
|
|
178
|
+
def InGoalRegion(self, node):
|
|
179
|
+
if self.Line(node, self.x_goal) < self.step_len:
|
|
180
|
+
return True
|
|
181
|
+
|
|
182
|
+
return False
|
|
183
|
+
|
|
184
|
+
@staticmethod
|
|
185
|
+
def RotationToWorldFrame(x_start, x_goal, L):
|
|
186
|
+
a1 = np.array([[(x_goal.x - x_start.x) / L],
|
|
187
|
+
[(x_goal.y - x_start.y) / L], [0.0]])
|
|
188
|
+
e1 = np.array([[1.0], [0.0], [0.0]])
|
|
189
|
+
M = a1 @ e1.T
|
|
190
|
+
U, _, V_T = np.linalg.svd(M, True, True)
|
|
191
|
+
C = U @ np.diag([1.0, 1.0, np.linalg.det(U) * np.linalg.det(V_T.T)]) @ V_T
|
|
192
|
+
|
|
193
|
+
return C
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def Nearest(nodelist, n):
|
|
197
|
+
return nodelist[int(np.argmin([(nd.x - n.x) ** 2 + (nd.y - n.y) ** 2
|
|
198
|
+
for nd in nodelist]))]
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def Line(x_start, x_goal):
|
|
202
|
+
return math.hypot(x_goal.x - x_start.x, x_goal.y - x_start.y)
|
|
203
|
+
|
|
204
|
+
def Cost(self, node):
|
|
205
|
+
if node == self.x_start:
|
|
206
|
+
return 0.0
|
|
207
|
+
|
|
208
|
+
if node.parent is None:
|
|
209
|
+
return np.inf
|
|
210
|
+
|
|
211
|
+
cost = 0.0
|
|
212
|
+
while node.parent:
|
|
213
|
+
cost += math.hypot(node.x - node.parent.x, node.y - node.parent.y)
|
|
214
|
+
node = node.parent
|
|
215
|
+
|
|
216
|
+
return cost
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def get_distance_and_angle(node_start, node_end):
|
|
220
|
+
dx = node_end.x - node_start.x
|
|
221
|
+
dy = node_end.y - node_start.y
|
|
222
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
223
|
+
|
|
224
|
+
def animation(self, x_center=None, c_best=None, dist=None, theta=None):
|
|
225
|
+
plt.cla()
|
|
226
|
+
self.plot_grid("Informed rrt*, N = " + str(self.iter_max))
|
|
227
|
+
plt.gcf().canvas.mpl_connect(
|
|
228
|
+
'key_release_event',
|
|
229
|
+
lambda event: [exit(0) if event.key == 'escape' else None])
|
|
230
|
+
|
|
231
|
+
for node in self.V:
|
|
232
|
+
if node.parent:
|
|
233
|
+
plt.plot([node.x, node.parent.x], [node.y, node.parent.y], "-g")
|
|
234
|
+
|
|
235
|
+
if c_best != np.inf:
|
|
236
|
+
self.draw_ellipse(x_center, c_best, dist, theta)
|
|
237
|
+
|
|
238
|
+
plt.pause(0.01)
|
|
239
|
+
|
|
240
|
+
def plot_grid(self, name):
|
|
241
|
+
|
|
242
|
+
for (ox, oy, w, h) in self.obs_boundary:
|
|
243
|
+
self.ax.add_patch(
|
|
244
|
+
patches.Rectangle(
|
|
245
|
+
(ox, oy), w, h,
|
|
246
|
+
edgecolor='black',
|
|
247
|
+
facecolor='black',
|
|
248
|
+
fill=True
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
for (ox, oy, w, h) in self.obs_rectangle:
|
|
253
|
+
self.ax.add_patch(
|
|
254
|
+
patches.Rectangle(
|
|
255
|
+
(ox, oy), w, h,
|
|
256
|
+
edgecolor='black',
|
|
257
|
+
facecolor='gray',
|
|
258
|
+
fill=True
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
for (ox, oy, r) in self.obs_circle:
|
|
263
|
+
self.ax.add_patch(
|
|
264
|
+
patches.Circle(
|
|
265
|
+
(ox, oy), r,
|
|
266
|
+
edgecolor='black',
|
|
267
|
+
facecolor='gray',
|
|
268
|
+
fill=True
|
|
269
|
+
)
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
plt.plot(self.x_start.x, self.x_start.y, "bs", linewidth=3)
|
|
273
|
+
plt.plot(self.x_goal.x, self.x_goal.y, "rs", linewidth=3)
|
|
274
|
+
|
|
275
|
+
plt.title(name)
|
|
276
|
+
plt.axis("equal")
|
|
277
|
+
|
|
278
|
+
@staticmethod
|
|
279
|
+
def draw_ellipse(x_center, c_best, dist, theta):
|
|
280
|
+
a = math.sqrt(c_best ** 2 - dist ** 2) / 2.0
|
|
281
|
+
b = c_best / 2.0
|
|
282
|
+
angle = math.pi / 2.0 - theta
|
|
283
|
+
cx = x_center[0]
|
|
284
|
+
cy = x_center[1]
|
|
285
|
+
t = np.arange(0, 2 * math.pi + 0.1, 0.1)
|
|
286
|
+
x = [a * math.cos(it) for it in t]
|
|
287
|
+
y = [b * math.sin(it) for it in t]
|
|
288
|
+
rot = Rot.from_euler('z', -angle).as_dcm()[0:2, 0:2]
|
|
289
|
+
fx = rot @ np.array([x, y])
|
|
290
|
+
px = np.array(fx[0, :] + cx).flatten()
|
|
291
|
+
py = np.array(fx[1, :] + cy).flatten()
|
|
292
|
+
plt.plot(cx, cy, ".b")
|
|
293
|
+
plt.plot(px, py, linestyle='--', color='darkorange', linewidth=2)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def main():
|
|
297
|
+
x_start = (18, 8) # Starting node
|
|
298
|
+
x_goal = (37, 18) # Goal node
|
|
299
|
+
|
|
300
|
+
rrt_star = IRrtStar(x_start, x_goal, 1, 0.10, 12, 1000)
|
|
301
|
+
rrt_star.planning()
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
if __name__ == '__main__':
|
|
305
|
+
main()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plotting tools for Sampling-based algorithms
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import matplotlib.patches as patches
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from . import env
|
|
11
|
+
except ImportError: # pragma: no cover - script execution fallback
|
|
12
|
+
import env
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Plotting:
|
|
16
|
+
def __init__(self, x_start, x_goal):
|
|
17
|
+
self.xI, self.xG = x_start, x_goal
|
|
18
|
+
self.env = env.Env()
|
|
19
|
+
self.obs_bound = self.env.obs_boundary
|
|
20
|
+
self.obs_circle = self.env.obs_circle
|
|
21
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
22
|
+
|
|
23
|
+
def animation(self, nodelist, path, name, animation=False):
|
|
24
|
+
self.plot_grid(name)
|
|
25
|
+
self.plot_visited(nodelist, animation)
|
|
26
|
+
self.plot_path(path)
|
|
27
|
+
|
|
28
|
+
def animation_connect(self, V1, V2, path, name):
|
|
29
|
+
self.plot_grid(name)
|
|
30
|
+
self.plot_visited_connect(V1, V2)
|
|
31
|
+
self.plot_path(path)
|
|
32
|
+
|
|
33
|
+
def plot_grid(self, name):
|
|
34
|
+
fig, ax = plt.subplots()
|
|
35
|
+
|
|
36
|
+
for (ox, oy, w, h) in self.obs_bound:
|
|
37
|
+
ax.add_patch(
|
|
38
|
+
patches.Rectangle(
|
|
39
|
+
(ox, oy), w, h,
|
|
40
|
+
edgecolor='black',
|
|
41
|
+
facecolor='black',
|
|
42
|
+
fill=True
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
for (ox, oy, w, h) in self.obs_rectangle:
|
|
47
|
+
ax.add_patch(
|
|
48
|
+
patches.Rectangle(
|
|
49
|
+
(ox, oy), w, h,
|
|
50
|
+
edgecolor='black',
|
|
51
|
+
facecolor='gray',
|
|
52
|
+
fill=True
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
for (ox, oy, r) in self.obs_circle:
|
|
57
|
+
ax.add_patch(
|
|
58
|
+
patches.Circle(
|
|
59
|
+
(ox, oy), r,
|
|
60
|
+
edgecolor='black',
|
|
61
|
+
facecolor='gray',
|
|
62
|
+
fill=True
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
plt.plot(self.xI[0], self.xI[1], "bs", linewidth=3)
|
|
67
|
+
plt.plot(self.xG[0], self.xG[1], "gs", linewidth=3)
|
|
68
|
+
|
|
69
|
+
plt.title(name)
|
|
70
|
+
plt.axis("equal")
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def plot_visited(nodelist, animation):
|
|
74
|
+
if animation:
|
|
75
|
+
count = 0
|
|
76
|
+
for node in nodelist:
|
|
77
|
+
count += 1
|
|
78
|
+
if node.parent:
|
|
79
|
+
plt.plot([node.parent.x, node.x], [node.parent.y, node.y], "-g")
|
|
80
|
+
plt.gcf().canvas.mpl_connect('key_release_event',
|
|
81
|
+
lambda event:
|
|
82
|
+
[exit(0) if event.key == 'escape' else None])
|
|
83
|
+
if count % 10 == 0:
|
|
84
|
+
plt.pause(0.001)
|
|
85
|
+
else:
|
|
86
|
+
for node in nodelist:
|
|
87
|
+
if node.parent:
|
|
88
|
+
plt.plot([node.parent.x, node.x], [node.parent.y, node.y], "-g")
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def plot_visited_connect(V1, V2):
|
|
92
|
+
len1, len2 = len(V1), len(V2)
|
|
93
|
+
|
|
94
|
+
for k in range(max(len1, len2)):
|
|
95
|
+
if k < len1:
|
|
96
|
+
if V1[k].parent:
|
|
97
|
+
plt.plot([V1[k].x, V1[k].parent.x], [V1[k].y, V1[k].parent.y], "-g")
|
|
98
|
+
if k < len2:
|
|
99
|
+
if V2[k].parent:
|
|
100
|
+
plt.plot([V2[k].x, V2[k].parent.x], [V2[k].y, V2[k].parent.y], "-g")
|
|
101
|
+
|
|
102
|
+
plt.gcf().canvas.mpl_connect('key_release_event',
|
|
103
|
+
lambda event: [exit(0) if event.key == 'escape' else None])
|
|
104
|
+
|
|
105
|
+
if k % 2 == 0:
|
|
106
|
+
plt.pause(0.001)
|
|
107
|
+
|
|
108
|
+
plt.pause(0.01)
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def plot_path(path):
|
|
112
|
+
if len(path) != 0:
|
|
113
|
+
plt.plot([x[0] for x in path], [x[1] for x in path], '-r', linewidth=2)
|
|
114
|
+
plt.pause(0.01)
|
|
115
|
+
plt.show()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import collections
|
|
2
|
+
import heapq
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class QueueFIFO:
|
|
6
|
+
"""
|
|
7
|
+
Class: QueueFIFO
|
|
8
|
+
Description: QueueFIFO is designed for First-in-First-out rule.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self.queue = collections.deque()
|
|
13
|
+
|
|
14
|
+
def empty(self):
|
|
15
|
+
return len(self.queue) == 0
|
|
16
|
+
|
|
17
|
+
def put(self, node):
|
|
18
|
+
self.queue.append(node) # enter from back
|
|
19
|
+
|
|
20
|
+
def get(self):
|
|
21
|
+
return self.queue.popleft() # leave from front
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class QueueLIFO:
|
|
25
|
+
"""
|
|
26
|
+
Class: QueueLIFO
|
|
27
|
+
Description: QueueLIFO is designed for Last-in-First-out rule.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self.queue = collections.deque()
|
|
32
|
+
|
|
33
|
+
def empty(self):
|
|
34
|
+
return len(self.queue) == 0
|
|
35
|
+
|
|
36
|
+
def put(self, node):
|
|
37
|
+
self.queue.append(node) # enter from back
|
|
38
|
+
|
|
39
|
+
def get(self):
|
|
40
|
+
return self.queue.pop() # leave from back
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class QueuePrior:
|
|
44
|
+
"""
|
|
45
|
+
Class: QueuePrior
|
|
46
|
+
Description: QueuePrior reorders elements using value [priority]
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self):
|
|
50
|
+
self.queue = []
|
|
51
|
+
|
|
52
|
+
def empty(self):
|
|
53
|
+
return len(self.queue) == 0
|
|
54
|
+
|
|
55
|
+
def put(self, item, priority):
|
|
56
|
+
heapq.heappush(self.queue, (priority, item)) # reorder s using priority
|
|
57
|
+
|
|
58
|
+
def get(self):
|
|
59
|
+
return heapq.heappop(self.queue)[1] # pop out the smallest item
|
|
60
|
+
|
|
61
|
+
def enumerate(self):
|
|
62
|
+
return self.queue
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RRT_2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import math
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from . import env, plotting, utils
|
|
11
|
+
except ImportError: # pragma: no cover - script execution fallback
|
|
12
|
+
import env
|
|
13
|
+
import plotting
|
|
14
|
+
import utils
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Node:
|
|
18
|
+
def __init__(self, n):
|
|
19
|
+
self.x = n[0]
|
|
20
|
+
self.y = n[1]
|
|
21
|
+
self.parent = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Rrt:
|
|
25
|
+
def __init__(self, s_start, s_goal, step_len, goal_sample_rate, iter_max):
|
|
26
|
+
self.s_start = Node(s_start)
|
|
27
|
+
self.s_goal = Node(s_goal)
|
|
28
|
+
self.step_len = step_len
|
|
29
|
+
self.goal_sample_rate = goal_sample_rate
|
|
30
|
+
self.iter_max = iter_max
|
|
31
|
+
self.vertex = [self.s_start]
|
|
32
|
+
|
|
33
|
+
self.env = env.Env()
|
|
34
|
+
self.plotting = plotting.Plotting(s_start, s_goal)
|
|
35
|
+
self.utils = utils.Utils()
|
|
36
|
+
|
|
37
|
+
self.x_range = self.env.x_range
|
|
38
|
+
self.y_range = self.env.y_range
|
|
39
|
+
self.obs_circle = self.env.obs_circle
|
|
40
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
41
|
+
self.obs_boundary = self.env.obs_boundary
|
|
42
|
+
|
|
43
|
+
def planning(self):
|
|
44
|
+
for i in range(self.iter_max):
|
|
45
|
+
node_rand = self.generate_random_node(self.goal_sample_rate)
|
|
46
|
+
node_near = self.nearest_neighbor(self.vertex, node_rand)
|
|
47
|
+
node_new = self.new_state(node_near, node_rand)
|
|
48
|
+
|
|
49
|
+
if node_new and not self.utils.is_collision(node_near, node_new):
|
|
50
|
+
self.vertex.append(node_new)
|
|
51
|
+
dist, _ = self.get_distance_and_angle(node_new, self.s_goal)
|
|
52
|
+
|
|
53
|
+
if dist <= self.step_len and not self.utils.is_collision(node_new, self.s_goal):
|
|
54
|
+
self.new_state(node_new, self.s_goal)
|
|
55
|
+
return self.extract_path(node_new)
|
|
56
|
+
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
def generate_random_node(self, goal_sample_rate):
|
|
60
|
+
delta = self.utils.delta
|
|
61
|
+
|
|
62
|
+
if np.random.random() > goal_sample_rate:
|
|
63
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
64
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
65
|
+
|
|
66
|
+
return self.s_goal
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def nearest_neighbor(node_list, n):
|
|
70
|
+
return node_list[int(np.argmin([math.hypot(nd.x - n.x, nd.y - n.y)
|
|
71
|
+
for nd in node_list]))]
|
|
72
|
+
|
|
73
|
+
def new_state(self, node_start, node_end):
|
|
74
|
+
dist, theta = self.get_distance_and_angle(node_start, node_end)
|
|
75
|
+
|
|
76
|
+
dist = min(self.step_len, dist)
|
|
77
|
+
node_new = Node((node_start.x + dist * math.cos(theta),
|
|
78
|
+
node_start.y + dist * math.sin(theta)))
|
|
79
|
+
node_new.parent = node_start
|
|
80
|
+
|
|
81
|
+
return node_new
|
|
82
|
+
|
|
83
|
+
def extract_path(self, node_end):
|
|
84
|
+
path = [(self.s_goal.x, self.s_goal.y)]
|
|
85
|
+
node_now = node_end
|
|
86
|
+
|
|
87
|
+
while node_now.parent is not None:
|
|
88
|
+
node_now = node_now.parent
|
|
89
|
+
path.append((node_now.x, node_now.y))
|
|
90
|
+
|
|
91
|
+
return path
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def get_distance_and_angle(node_start, node_end):
|
|
95
|
+
dx = node_end.x - node_start.x
|
|
96
|
+
dy = node_end.y - node_start.y
|
|
97
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def main():
|
|
101
|
+
x_start = (2, 2) # Starting node
|
|
102
|
+
x_goal = (49, 24) # Goal node
|
|
103
|
+
|
|
104
|
+
rrt = Rrt(x_start, x_goal, 0.5, 0.05, 10000)
|
|
105
|
+
path = rrt.planning()
|
|
106
|
+
|
|
107
|
+
if path:
|
|
108
|
+
rrt.plotting.animation(rrt.vertex, path, "RRT", True)
|
|
109
|
+
else:
|
|
110
|
+
print("No Path Found!")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
if __name__ == '__main__':
|
|
114
|
+
main()
|