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,321 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DUBINS_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
|
+
import matplotlib.patches as patches
|
|
13
|
+
from scipy.spatial.transform import Rotation as Rot
|
|
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
|
+
import CurvesGenerator.dubins_path as dubins
|
|
20
|
+
import CurvesGenerator.draw as draw
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Node:
|
|
24
|
+
def __init__(self, x, y, yaw):
|
|
25
|
+
self.x = x
|
|
26
|
+
self.y = y
|
|
27
|
+
self.yaw = yaw
|
|
28
|
+
self.parent = None
|
|
29
|
+
self.cost = 0.0
|
|
30
|
+
self.path_x = []
|
|
31
|
+
self.path_y = []
|
|
32
|
+
self.paty_yaw = []
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DubinsRRTStar:
|
|
36
|
+
def __init__(self, sx, sy, syaw, gx, gy, gyaw, vehicle_radius, step_len,
|
|
37
|
+
goal_sample_rate, search_radius, iter_max):
|
|
38
|
+
self.s_start = Node(sx, sy, syaw)
|
|
39
|
+
self.s_goal = Node(gx, gy, gyaw)
|
|
40
|
+
self.vr = vehicle_radius
|
|
41
|
+
self.step_len = step_len
|
|
42
|
+
self.goal_sample_rate = goal_sample_rate
|
|
43
|
+
self.search_radius = search_radius
|
|
44
|
+
self.iter_max = iter_max
|
|
45
|
+
self.curv = 1
|
|
46
|
+
|
|
47
|
+
self.env = env.Env()
|
|
48
|
+
self.utils = utils.Utils()
|
|
49
|
+
|
|
50
|
+
self.fig, self.ax = plt.subplots()
|
|
51
|
+
self.delta = self.utils.delta
|
|
52
|
+
self.x_range = self.env.x_range
|
|
53
|
+
self.y_range = self.env.y_range
|
|
54
|
+
self.obs_circle = self.obs_circle()
|
|
55
|
+
self.obs_boundary = self.env.obs_boundary
|
|
56
|
+
self.utils.update_obs(self.obs_circle, self.obs_boundary, [])
|
|
57
|
+
|
|
58
|
+
self.V = [self.s_start]
|
|
59
|
+
self.path = None
|
|
60
|
+
|
|
61
|
+
def planning(self):
|
|
62
|
+
|
|
63
|
+
for i in range(self.iter_max):
|
|
64
|
+
print("Iter:", i, ", number of nodes:", len(self.V))
|
|
65
|
+
rnd = self.Sample()
|
|
66
|
+
node_nearest = self.Nearest(self.V, rnd)
|
|
67
|
+
new_node = self.Steer(node_nearest, rnd)
|
|
68
|
+
|
|
69
|
+
if new_node and not self.is_collision(new_node):
|
|
70
|
+
near_indexes = self.Near(self.V, new_node)
|
|
71
|
+
new_node = self.choose_parent(new_node, near_indexes)
|
|
72
|
+
|
|
73
|
+
if new_node:
|
|
74
|
+
self.V.append(new_node)
|
|
75
|
+
self.rewire(new_node, near_indexes)
|
|
76
|
+
|
|
77
|
+
if i % 5 == 0:
|
|
78
|
+
self.draw_graph()
|
|
79
|
+
|
|
80
|
+
last_index = self.search_best_goal_node()
|
|
81
|
+
|
|
82
|
+
path = self.generate_final_course(last_index)
|
|
83
|
+
print("get!")
|
|
84
|
+
px = [s[0] for s in path]
|
|
85
|
+
py = [s[1] for s in path]
|
|
86
|
+
plt.plot(px, py, '-r')
|
|
87
|
+
plt.pause(0.01)
|
|
88
|
+
plt.show()
|
|
89
|
+
|
|
90
|
+
def draw_graph(self, rnd=None):
|
|
91
|
+
plt.cla()
|
|
92
|
+
# for stopping simulation with the esc key.
|
|
93
|
+
plt.gcf().canvas.mpl_connect('key_release_event',
|
|
94
|
+
lambda event: [exit(0) if event.key == 'escape' else None])
|
|
95
|
+
for node in self.V:
|
|
96
|
+
if node.parent:
|
|
97
|
+
plt.plot(node.path_x, node.path_y, "-g")
|
|
98
|
+
|
|
99
|
+
self.plot_grid("dubins rrt*")
|
|
100
|
+
plt.plot(self.s_start.x, self.s_start.y, "xr")
|
|
101
|
+
plt.plot(self.s_goal.x, self.s_goal.y, "xr")
|
|
102
|
+
plt.grid(True)
|
|
103
|
+
self.plot_start_goal_arrow()
|
|
104
|
+
plt.pause(0.01)
|
|
105
|
+
|
|
106
|
+
def plot_start_goal_arrow(self):
|
|
107
|
+
draw.Arrow(self.s_start.x, self.s_start.y, self.s_start.yaw, 2, "darkorange")
|
|
108
|
+
draw.Arrow(self.s_goal.x, self.s_goal.y, self.s_goal.yaw, 2, "darkorange")
|
|
109
|
+
|
|
110
|
+
def generate_final_course(self, goal_index):
|
|
111
|
+
print("final")
|
|
112
|
+
path = [[self.s_goal.x, self.s_goal.y]]
|
|
113
|
+
node = self.V[goal_index]
|
|
114
|
+
while node.parent:
|
|
115
|
+
for (ix, iy) in zip(reversed(node.path_x), reversed(node.path_y)):
|
|
116
|
+
path.append([ix, iy])
|
|
117
|
+
node = node.parent
|
|
118
|
+
path.append([self.s_start.x, self.s_start.y])
|
|
119
|
+
return path
|
|
120
|
+
|
|
121
|
+
def calc_dist_to_goal(self, x, y):
|
|
122
|
+
dx = x - self.s_goal.x
|
|
123
|
+
dy = y - self.s_goal.y
|
|
124
|
+
return math.hypot(dx, dy)
|
|
125
|
+
|
|
126
|
+
def search_best_goal_node(self):
|
|
127
|
+
dist_to_goal_list = [self.calc_dist_to_goal(n.x, n.y) for n in self.V]
|
|
128
|
+
goal_inds = [dist_to_goal_list.index(i) for i in dist_to_goal_list if i <= self.step_len]
|
|
129
|
+
|
|
130
|
+
safe_goal_inds = []
|
|
131
|
+
for goal_ind in goal_inds:
|
|
132
|
+
t_node = self.Steer(self.V[goal_ind], self.s_goal)
|
|
133
|
+
if t_node and not self.is_collision(t_node):
|
|
134
|
+
safe_goal_inds.append(goal_ind)
|
|
135
|
+
|
|
136
|
+
if not safe_goal_inds:
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
min_cost = min([self.V[i].cost for i in safe_goal_inds])
|
|
140
|
+
for i in safe_goal_inds:
|
|
141
|
+
if self.V[i].cost == min_cost:
|
|
142
|
+
return i
|
|
143
|
+
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
def rewire(self, new_node, near_inds):
|
|
147
|
+
for i in near_inds:
|
|
148
|
+
near_node = self.V[i]
|
|
149
|
+
edge_node = self.Steer(new_node, near_node)
|
|
150
|
+
if not edge_node:
|
|
151
|
+
continue
|
|
152
|
+
edge_node.cost = self.calc_new_cost(new_node, near_node)
|
|
153
|
+
|
|
154
|
+
no_collision = ~self.is_collision(edge_node)
|
|
155
|
+
improved_cost = near_node.cost > edge_node.cost
|
|
156
|
+
|
|
157
|
+
if no_collision and improved_cost:
|
|
158
|
+
self.V[i] = edge_node
|
|
159
|
+
self.propagate_cost_to_leaves(new_node)
|
|
160
|
+
|
|
161
|
+
def choose_parent(self, new_node, near_inds):
|
|
162
|
+
if not near_inds:
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
costs = []
|
|
166
|
+
for i in near_inds:
|
|
167
|
+
near_node = self.V[i]
|
|
168
|
+
t_node = self.Steer(near_node, new_node)
|
|
169
|
+
if t_node and not self.is_collision(t_node):
|
|
170
|
+
costs.append(self.calc_new_cost(near_node, new_node))
|
|
171
|
+
else:
|
|
172
|
+
costs.append(float("inf")) # the cost of collision node
|
|
173
|
+
min_cost = min(costs)
|
|
174
|
+
|
|
175
|
+
if min_cost == float("inf"):
|
|
176
|
+
print("There is no good path.(min_cost is inf)")
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
min_ind = near_inds[costs.index(min_cost)]
|
|
180
|
+
new_node = self.Steer(self.V[min_ind], new_node)
|
|
181
|
+
|
|
182
|
+
return new_node
|
|
183
|
+
|
|
184
|
+
def calc_new_cost(self, from_node, to_node):
|
|
185
|
+
d, _ = self.get_distance_and_angle(from_node, to_node)
|
|
186
|
+
return from_node.cost + d
|
|
187
|
+
|
|
188
|
+
def propagate_cost_to_leaves(self, parent_node):
|
|
189
|
+
for node in self.V:
|
|
190
|
+
if node.parent == parent_node:
|
|
191
|
+
node.cost = self.calc_new_cost(parent_node, node)
|
|
192
|
+
self.propagate_cost_to_leaves(node)
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def get_distance_and_angle(node_start, node_end):
|
|
196
|
+
dx = node_end.x - node_start.x
|
|
197
|
+
dy = node_end.y - node_start.y
|
|
198
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
199
|
+
|
|
200
|
+
def Near(self, nodelist, node):
|
|
201
|
+
n = len(nodelist) + 1
|
|
202
|
+
r = min(self.search_radius * math.sqrt((math.log(n)) / n), self.step_len)
|
|
203
|
+
|
|
204
|
+
dist_table = [(nd.x - node.x) ** 2 + (nd.y - node.y) ** 2 for nd in nodelist]
|
|
205
|
+
node_near_ind = [ind for ind in range(len(dist_table)) if dist_table[ind] <= r ** 2]
|
|
206
|
+
|
|
207
|
+
return node_near_ind
|
|
208
|
+
|
|
209
|
+
def Steer(self, node_start, node_end):
|
|
210
|
+
sx, sy, syaw = node_start.x, node_start.y, node_start.yaw
|
|
211
|
+
gx, gy, gyaw = node_end.x, node_end.y, node_end.yaw
|
|
212
|
+
maxc = self.curv
|
|
213
|
+
|
|
214
|
+
path = dubins.calc_dubins_path(sx, sy, syaw, gx, gy, gyaw, maxc)
|
|
215
|
+
|
|
216
|
+
if len(path.x) <= 1:
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
node_new = Node(path.x[-1], path.y[-1], path.yaw[-1])
|
|
220
|
+
node_new.path_x = path.x
|
|
221
|
+
node_new.path_y = path.y
|
|
222
|
+
node_new.path_yaw = path.yaw
|
|
223
|
+
node_new.cost = node_start.cost + path.L
|
|
224
|
+
node_new.parent = node_start
|
|
225
|
+
|
|
226
|
+
return node_new
|
|
227
|
+
|
|
228
|
+
def Sample(self):
|
|
229
|
+
delta = self.utils.delta
|
|
230
|
+
|
|
231
|
+
if random.random() > self.goal_sample_rate:
|
|
232
|
+
return Node(random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
233
|
+
random.uniform(self.y_range[0] + delta, self.y_range[1] - delta),
|
|
234
|
+
random.uniform(-math.pi, math.pi))
|
|
235
|
+
else:
|
|
236
|
+
return self.s_goal
|
|
237
|
+
|
|
238
|
+
@staticmethod
|
|
239
|
+
def Nearest(nodelist, n):
|
|
240
|
+
return nodelist[int(np.argmin([(nd.x - n.x) ** 2 + (nd.y - n.y) ** 2
|
|
241
|
+
for nd in nodelist]))]
|
|
242
|
+
|
|
243
|
+
def is_collision(self, node):
|
|
244
|
+
for ox, oy, r in self.obs_circle:
|
|
245
|
+
dx = [ox - x for x in node.path_x]
|
|
246
|
+
dy = [oy - y for y in node.path_y]
|
|
247
|
+
dist = np.hypot(dx, dy)
|
|
248
|
+
|
|
249
|
+
if min(dist) < r + self.delta:
|
|
250
|
+
return True
|
|
251
|
+
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
def animation(self):
|
|
255
|
+
self.plot_grid("dubins rrt*")
|
|
256
|
+
self.plot_arrow()
|
|
257
|
+
plt.show()
|
|
258
|
+
|
|
259
|
+
def plot_arrow(self):
|
|
260
|
+
draw.Arrow(self.s_start.x, self.s_start.y, self.s_start.yaw, 2.5, "darkorange")
|
|
261
|
+
draw.Arrow(self.s_goal.x, self.s_goal.y, self.s_goal.yaw, 2.5, "darkorange")
|
|
262
|
+
|
|
263
|
+
def plot_grid(self, name):
|
|
264
|
+
|
|
265
|
+
for (ox, oy, w, h) in self.obs_boundary:
|
|
266
|
+
self.ax.add_patch(
|
|
267
|
+
patches.Rectangle(
|
|
268
|
+
(ox, oy), w, h,
|
|
269
|
+
edgecolor='black',
|
|
270
|
+
facecolor='black',
|
|
271
|
+
fill=True
|
|
272
|
+
)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
for (ox, oy, r) in self.obs_circle:
|
|
276
|
+
self.ax.add_patch(
|
|
277
|
+
patches.Circle(
|
|
278
|
+
(ox, oy), r,
|
|
279
|
+
edgecolor='black',
|
|
280
|
+
facecolor='gray',
|
|
281
|
+
fill=True
|
|
282
|
+
)
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
plt.plot(self.s_start.x, self.s_start.y, "bs", linewidth=3)
|
|
286
|
+
plt.plot(self.s_goal.x, self.s_goal.y, "gs", linewidth=3)
|
|
287
|
+
|
|
288
|
+
plt.title(name)
|
|
289
|
+
plt.axis("equal")
|
|
290
|
+
|
|
291
|
+
@staticmethod
|
|
292
|
+
def obs_circle():
|
|
293
|
+
obs_cir = [
|
|
294
|
+
[10, 10, 3],
|
|
295
|
+
[15, 22, 3],
|
|
296
|
+
[22, 8, 2.5],
|
|
297
|
+
[26, 16, 2],
|
|
298
|
+
[37, 10, 3],
|
|
299
|
+
[37, 23, 3],
|
|
300
|
+
[45, 15, 2]
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
return obs_cir
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def main():
|
|
307
|
+
sx, sy, syaw = 5, 5, np.deg2rad(90)
|
|
308
|
+
gx, gy, gyaw = 45, 25, np.deg2rad(0)
|
|
309
|
+
goal_sample_rate = 0.1
|
|
310
|
+
search_radius = 50.0
|
|
311
|
+
step_len = 30.0
|
|
312
|
+
iter_max = 250
|
|
313
|
+
vehicle_radius = 2.0
|
|
314
|
+
|
|
315
|
+
drrtstar = DubinsRRTStar(sx, sy, syaw, gx, gy, gyaw, vehicle_radius, step_len,
|
|
316
|
+
goal_sample_rate, search_radius, iter_max)
|
|
317
|
+
drrtstar.planning()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == '__main__':
|
|
321
|
+
main()
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DYNAMIC_RRT_2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import math
|
|
9
|
+
import copy
|
|
10
|
+
import numpy as np
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
import matplotlib.patches as patches
|
|
13
|
+
|
|
14
|
+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
|
|
15
|
+
"/../../Sampling_based_Planning/")
|
|
16
|
+
|
|
17
|
+
from Sampling_based_Planning.rrt_2D import env, plotting, utils
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Node:
|
|
21
|
+
def __init__(self, n):
|
|
22
|
+
self.x = n[0]
|
|
23
|
+
self.y = n[1]
|
|
24
|
+
self.parent = None
|
|
25
|
+
self.flag = "VALID"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Edge:
|
|
29
|
+
def __init__(self, n_p, n_c):
|
|
30
|
+
self.parent = n_p
|
|
31
|
+
self.child = n_c
|
|
32
|
+
self.flag = "VALID"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DynamicRrt:
|
|
36
|
+
def __init__(self, s_start, s_goal, step_len, goal_sample_rate, waypoint_sample_rate, iter_max):
|
|
37
|
+
self.s_start = Node(s_start)
|
|
38
|
+
self.s_goal = Node(s_goal)
|
|
39
|
+
self.step_len = step_len
|
|
40
|
+
self.goal_sample_rate = goal_sample_rate
|
|
41
|
+
self.waypoint_sample_rate = waypoint_sample_rate
|
|
42
|
+
self.iter_max = iter_max
|
|
43
|
+
self.vertex = [self.s_start]
|
|
44
|
+
self.vertex_old = []
|
|
45
|
+
self.vertex_new = []
|
|
46
|
+
self.edges = []
|
|
47
|
+
|
|
48
|
+
self.env = env.Env()
|
|
49
|
+
self.plotting = plotting.Plotting(s_start, s_goal)
|
|
50
|
+
self.utils = utils.Utils()
|
|
51
|
+
self.fig, self.ax = plt.subplots()
|
|
52
|
+
|
|
53
|
+
self.x_range = self.env.x_range
|
|
54
|
+
self.y_range = self.env.y_range
|
|
55
|
+
self.obs_circle = self.env.obs_circle
|
|
56
|
+
self.obs_rectangle = self.env.obs_rectangle
|
|
57
|
+
self.obs_boundary = self.env.obs_boundary
|
|
58
|
+
self.obs_add = [0, 0, 0]
|
|
59
|
+
|
|
60
|
+
self.path = []
|
|
61
|
+
self.waypoint = []
|
|
62
|
+
|
|
63
|
+
def planning(self):
|
|
64
|
+
for i in range(self.iter_max):
|
|
65
|
+
node_rand = self.generate_random_node(self.goal_sample_rate)
|
|
66
|
+
node_near = self.nearest_neighbor(self.vertex, node_rand)
|
|
67
|
+
node_new = self.new_state(node_near, node_rand)
|
|
68
|
+
|
|
69
|
+
if node_new and not self.utils.is_collision(node_near, node_new):
|
|
70
|
+
self.vertex.append(node_new)
|
|
71
|
+
self.edges.append(Edge(node_near, node_new))
|
|
72
|
+
dist, _ = self.get_distance_and_angle(node_new, self.s_goal)
|
|
73
|
+
|
|
74
|
+
if dist <= self.step_len:
|
|
75
|
+
self.new_state(node_new, self.s_goal)
|
|
76
|
+
|
|
77
|
+
path = self.extract_path(node_new)
|
|
78
|
+
self.plot_grid("Dynamic_RRT")
|
|
79
|
+
self.plot_visited()
|
|
80
|
+
self.plot_path(path)
|
|
81
|
+
self.path = path
|
|
82
|
+
self.waypoint = self.extract_waypoint(node_new)
|
|
83
|
+
self.fig.canvas.mpl_connect('button_press_event', self.on_press)
|
|
84
|
+
plt.show()
|
|
85
|
+
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def on_press(self, event):
|
|
91
|
+
x, y = event.xdata, event.ydata
|
|
92
|
+
if x < 0 or x > 50 or y < 0 or y > 30:
|
|
93
|
+
print("Please choose right area!")
|
|
94
|
+
else:
|
|
95
|
+
x, y = int(x), int(y)
|
|
96
|
+
print("Add circle obstacle at: s =", x, ",", "y =", y)
|
|
97
|
+
self.obs_add = [x, y, 2]
|
|
98
|
+
self.obs_circle.append([x, y, 2])
|
|
99
|
+
self.utils.update_obs(self.obs_circle, self.obs_boundary, self.obs_rectangle)
|
|
100
|
+
self.InvalidateNodes()
|
|
101
|
+
|
|
102
|
+
if self.is_path_invalid():
|
|
103
|
+
print("Path is Replanning ...")
|
|
104
|
+
path, waypoint = self.replanning()
|
|
105
|
+
|
|
106
|
+
print("len_vertex: ", len(self.vertex))
|
|
107
|
+
print("len_vertex_old: ", len(self.vertex_old))
|
|
108
|
+
print("len_vertex_new: ", len(self.vertex_new))
|
|
109
|
+
|
|
110
|
+
plt.cla()
|
|
111
|
+
self.plot_grid("Dynamic_RRT")
|
|
112
|
+
self.plot_vertex_old()
|
|
113
|
+
self.plot_path(self.path, color='blue')
|
|
114
|
+
self.plot_vertex_new()
|
|
115
|
+
self.vertex_new = []
|
|
116
|
+
self.plot_path(path)
|
|
117
|
+
self.path = path
|
|
118
|
+
self.waypoint = waypoint
|
|
119
|
+
else:
|
|
120
|
+
print("Trimming Invalid Nodes ...")
|
|
121
|
+
self.TrimRRT()
|
|
122
|
+
|
|
123
|
+
plt.cla()
|
|
124
|
+
self.plot_grid("Dynamic_RRT")
|
|
125
|
+
self.plot_visited(animation=False)
|
|
126
|
+
self.plot_path(self.path)
|
|
127
|
+
|
|
128
|
+
self.fig.canvas.draw_idle()
|
|
129
|
+
|
|
130
|
+
def InvalidateNodes(self):
|
|
131
|
+
for edge in self.edges:
|
|
132
|
+
if self.is_collision_obs_add(edge.parent, edge.child):
|
|
133
|
+
edge.child.flag = "INVALID"
|
|
134
|
+
|
|
135
|
+
def is_path_invalid(self):
|
|
136
|
+
for node in self.waypoint:
|
|
137
|
+
if node.flag == "INVALID":
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
def is_collision_obs_add(self, start, end):
|
|
141
|
+
delta = self.utils.delta
|
|
142
|
+
obs_add = self.obs_add
|
|
143
|
+
|
|
144
|
+
if math.hypot(start.x - obs_add[0], start.y - obs_add[1]) <= obs_add[2] + delta:
|
|
145
|
+
return True
|
|
146
|
+
|
|
147
|
+
if math.hypot(end.x - obs_add[0], end.y - obs_add[1]) <= obs_add[2] + delta:
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
o, d = self.utils.get_ray(start, end)
|
|
151
|
+
if self.utils.is_intersect_circle(o, d, [obs_add[0], obs_add[1]], obs_add[2]):
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
return False
|
|
155
|
+
|
|
156
|
+
def replanning(self):
|
|
157
|
+
self.TrimRRT()
|
|
158
|
+
|
|
159
|
+
for i in range(self.iter_max):
|
|
160
|
+
node_rand = self.generate_random_node_replanning(self.goal_sample_rate, self.waypoint_sample_rate)
|
|
161
|
+
node_near = self.nearest_neighbor(self.vertex, node_rand)
|
|
162
|
+
node_new = self.new_state(node_near, node_rand)
|
|
163
|
+
|
|
164
|
+
if node_new and not self.utils.is_collision(node_near, node_new):
|
|
165
|
+
self.vertex.append(node_new)
|
|
166
|
+
self.vertex_new.append(node_new)
|
|
167
|
+
self.edges.append(Edge(node_near, node_new))
|
|
168
|
+
dist, _ = self.get_distance_and_angle(node_new, self.s_goal)
|
|
169
|
+
|
|
170
|
+
if dist <= self.step_len:
|
|
171
|
+
self.new_state(node_new, self.s_goal)
|
|
172
|
+
path = self.extract_path(node_new)
|
|
173
|
+
waypoint = self.extract_waypoint(node_new)
|
|
174
|
+
print("path: ", len(path))
|
|
175
|
+
print("waypoint: ", len(waypoint))
|
|
176
|
+
|
|
177
|
+
return path, waypoint
|
|
178
|
+
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
def TrimRRT(self):
|
|
182
|
+
for i in range(1, len(self.vertex)):
|
|
183
|
+
node = self.vertex[i]
|
|
184
|
+
node_p = node.parent
|
|
185
|
+
if node_p.flag == "INVALID":
|
|
186
|
+
node.flag = "INVALID"
|
|
187
|
+
|
|
188
|
+
self.vertex = [node for node in self.vertex if node.flag == "VALID"]
|
|
189
|
+
self.vertex_old = copy.deepcopy(self.vertex)
|
|
190
|
+
self.edges = [Edge(node.parent, node) for node in self.vertex[1:len(self.vertex)]]
|
|
191
|
+
|
|
192
|
+
def generate_random_node(self, goal_sample_rate):
|
|
193
|
+
delta = self.utils.delta
|
|
194
|
+
|
|
195
|
+
if np.random.random() > goal_sample_rate:
|
|
196
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
197
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
198
|
+
|
|
199
|
+
return self.s_goal
|
|
200
|
+
|
|
201
|
+
def generate_random_node_replanning(self, goal_sample_rate, waypoint_sample_rate):
|
|
202
|
+
delta = self.utils.delta
|
|
203
|
+
p = np.random.random()
|
|
204
|
+
|
|
205
|
+
if p < goal_sample_rate:
|
|
206
|
+
return self.s_goal
|
|
207
|
+
elif goal_sample_rate < p < goal_sample_rate + waypoint_sample_rate:
|
|
208
|
+
return self.waypoint[np.random.randint(0, len(self.waypoint) - 1)]
|
|
209
|
+
else:
|
|
210
|
+
return Node((np.random.uniform(self.x_range[0] + delta, self.x_range[1] - delta),
|
|
211
|
+
np.random.uniform(self.y_range[0] + delta, self.y_range[1] - delta)))
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def nearest_neighbor(node_list, n):
|
|
215
|
+
return node_list[int(np.argmin([math.hypot(nd.x - n.x, nd.y - n.y)
|
|
216
|
+
for nd in node_list]))]
|
|
217
|
+
|
|
218
|
+
def new_state(self, node_start, node_end):
|
|
219
|
+
dist, theta = self.get_distance_and_angle(node_start, node_end)
|
|
220
|
+
|
|
221
|
+
dist = min(self.step_len, dist)
|
|
222
|
+
node_new = Node((node_start.x + dist * math.cos(theta),
|
|
223
|
+
node_start.y + dist * math.sin(theta)))
|
|
224
|
+
node_new.parent = node_start
|
|
225
|
+
|
|
226
|
+
return node_new
|
|
227
|
+
|
|
228
|
+
def extract_path(self, node_end):
|
|
229
|
+
path = [(self.s_goal.x, self.s_goal.y)]
|
|
230
|
+
node_now = node_end
|
|
231
|
+
|
|
232
|
+
while node_now.parent is not None:
|
|
233
|
+
node_now = node_now.parent
|
|
234
|
+
path.append((node_now.x, node_now.y))
|
|
235
|
+
|
|
236
|
+
return path
|
|
237
|
+
|
|
238
|
+
def extract_waypoint(self, node_end):
|
|
239
|
+
waypoint = [self.s_goal]
|
|
240
|
+
node_now = node_end
|
|
241
|
+
|
|
242
|
+
while node_now.parent is not None:
|
|
243
|
+
node_now = node_now.parent
|
|
244
|
+
waypoint.append(node_now)
|
|
245
|
+
|
|
246
|
+
return waypoint
|
|
247
|
+
|
|
248
|
+
@staticmethod
|
|
249
|
+
def get_distance_and_angle(node_start, node_end):
|
|
250
|
+
dx = node_end.x - node_start.x
|
|
251
|
+
dy = node_end.y - node_start.y
|
|
252
|
+
return math.hypot(dx, dy), math.atan2(dy, dx)
|
|
253
|
+
|
|
254
|
+
def plot_grid(self, name):
|
|
255
|
+
|
|
256
|
+
for (ox, oy, w, h) in self.obs_boundary:
|
|
257
|
+
self.ax.add_patch(
|
|
258
|
+
patches.Rectangle(
|
|
259
|
+
(ox, oy), w, h,
|
|
260
|
+
edgecolor='black',
|
|
261
|
+
facecolor='black',
|
|
262
|
+
fill=True
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
for (ox, oy, w, h) in self.obs_rectangle:
|
|
267
|
+
self.ax.add_patch(
|
|
268
|
+
patches.Rectangle(
|
|
269
|
+
(ox, oy), w, h,
|
|
270
|
+
edgecolor='black',
|
|
271
|
+
facecolor='gray',
|
|
272
|
+
fill=True
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
for (ox, oy, r) in self.obs_circle:
|
|
277
|
+
self.ax.add_patch(
|
|
278
|
+
patches.Circle(
|
|
279
|
+
(ox, oy), r,
|
|
280
|
+
edgecolor='black',
|
|
281
|
+
facecolor='gray',
|
|
282
|
+
fill=True
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
plt.plot(self.s_start.x, self.s_start.y, "bs", linewidth=3)
|
|
287
|
+
plt.plot(self.s_goal.x, self.s_goal.y, "gs", linewidth=3)
|
|
288
|
+
|
|
289
|
+
plt.title(name)
|
|
290
|
+
plt.axis("equal")
|
|
291
|
+
|
|
292
|
+
def plot_visited(self, animation=True):
|
|
293
|
+
if animation:
|
|
294
|
+
count = 0
|
|
295
|
+
for node in self.vertex:
|
|
296
|
+
count += 1
|
|
297
|
+
if node.parent:
|
|
298
|
+
plt.plot([node.parent.x, node.x], [node.parent.y, node.y], "-g")
|
|
299
|
+
plt.gcf().canvas.mpl_connect('key_release_event',
|
|
300
|
+
lambda event:
|
|
301
|
+
[exit(0) if event.key == 'escape' else None])
|
|
302
|
+
if count % 10 == 0:
|
|
303
|
+
plt.pause(0.001)
|
|
304
|
+
else:
|
|
305
|
+
for node in self.vertex:
|
|
306
|
+
if node.parent:
|
|
307
|
+
plt.plot([node.parent.x, node.x], [node.parent.y, node.y], "-g")
|
|
308
|
+
|
|
309
|
+
def plot_vertex_old(self):
|
|
310
|
+
for node in self.vertex_old:
|
|
311
|
+
if node.parent:
|
|
312
|
+
plt.plot([node.parent.x, node.x], [node.parent.y, node.y], "-g")
|
|
313
|
+
|
|
314
|
+
def plot_vertex_new(self):
|
|
315
|
+
count = 0
|
|
316
|
+
|
|
317
|
+
for node in self.vertex_new:
|
|
318
|
+
count += 1
|
|
319
|
+
if node.parent:
|
|
320
|
+
plt.plot([node.parent.x, node.x], [node.parent.y, node.y], color='darkorange')
|
|
321
|
+
plt.gcf().canvas.mpl_connect('key_release_event',
|
|
322
|
+
lambda event:
|
|
323
|
+
[exit(0) if event.key == 'escape' else None])
|
|
324
|
+
if count % 10 == 0:
|
|
325
|
+
plt.pause(0.001)
|
|
326
|
+
|
|
327
|
+
@staticmethod
|
|
328
|
+
def plot_path(path, color='red'):
|
|
329
|
+
plt.plot([x[0] for x in path], [x[1] for x in path], linewidth=2, color=color)
|
|
330
|
+
plt.pause(0.01)
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def main():
|
|
334
|
+
x_start = (2, 2) # Starting node
|
|
335
|
+
x_goal = (49, 24) # Goal node
|
|
336
|
+
|
|
337
|
+
drrt = DynamicRrt(x_start, x_goal, 0.5, 0.1, 0.6, 5000)
|
|
338
|
+
drrt.planning()
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
if __name__ == '__main__':
|
|
342
|
+
main()
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Environment for rrt_2D
|
|
3
|
+
@author: damminhtien
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Env:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.x_range = (0, 50)
|
|
10
|
+
self.y_range = (0, 30)
|
|
11
|
+
self.obs_boundary = self.obs_boundary()
|
|
12
|
+
self.obs_circle = self.obs_circle()
|
|
13
|
+
self.obs_rectangle = self.obs_rectangle()
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def obs_boundary():
|
|
17
|
+
obs_boundary = [
|
|
18
|
+
[0, 0, 1, 30],
|
|
19
|
+
[0, 30, 50, 1],
|
|
20
|
+
[1, 0, 50, 1],
|
|
21
|
+
[50, 1, 1, 30]
|
|
22
|
+
]
|
|
23
|
+
return obs_boundary
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def obs_rectangle():
|
|
27
|
+
obs_rectangle = [
|
|
28
|
+
[14, 12, 8, 2],
|
|
29
|
+
[18, 22, 8, 3],
|
|
30
|
+
[26, 7, 2, 12],
|
|
31
|
+
[32, 14, 10, 2]
|
|
32
|
+
]
|
|
33
|
+
return obs_rectangle
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def obs_circle():
|
|
37
|
+
obs_cir = [
|
|
38
|
+
[7, 12, 3],
|
|
39
|
+
[46, 20, 2],
|
|
40
|
+
[15, 5, 2],
|
|
41
|
+
[37, 7, 3],
|
|
42
|
+
[37, 23, 3]
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
return obs_cir
|