Skip to content

Commit 2b31650

Browse files
1 parent 60e9e8f commit 2b31650

File tree

2 files changed

+131
-56
lines changed

2 files changed

+131
-56
lines changed

PathPlanning/RRT/rrt.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,15 @@ def __init__(self, x, y):
3232
self.path_y = []
3333
self.parent = None
3434

35-
def __init__(self, start, goal, obstacle_list, rand_area,
36-
expand_dis=3.0, path_resolution=0.5, goal_sample_rate=5, max_iter=500):
35+
def __init__(self,
36+
start,
37+
goal,
38+
obstacle_list,
39+
rand_area,
40+
expand_dis=3.0,
41+
path_resolution=0.5,
42+
goal_sample_rate=5,
43+
max_iter=500):
3744
"""
3845
Setting Parameter
3946
@@ -75,8 +82,10 @@ def planning(self, animation=True):
7582
if animation and i % 5 == 0:
7683
self.draw_graph(rnd_node)
7784

78-
if self.calc_dist_to_goal(self.node_list[-1].x, self.node_list[-1].y) <= self.expand_dis:
79-
final_node = self.steer(self.node_list[-1], self.end, self.expand_dis)
85+
if self.calc_dist_to_goal(self.node_list[-1].x,
86+
self.node_list[-1].y) <= self.expand_dis:
87+
final_node = self.steer(self.node_list[-1], self.end,
88+
self.expand_dis)
8089
if self.check_collision(final_node, self.obstacle_list):
8190
return self.generate_final_course(len(self.node_list) - 1)
8291

@@ -108,6 +117,8 @@ def steer(self, from_node, to_node, extend_length=float("inf")):
108117
if d <= self.path_resolution:
109118
new_node.path_x.append(to_node.x)
110119
new_node.path_y.append(to_node.y)
120+
new_node.x = to_node.x
121+
new_node.y = to_node.y
111122

112123
new_node.parent = from_node
113124

@@ -130,17 +141,19 @@ def calc_dist_to_goal(self, x, y):
130141

131142
def get_random_node(self):
132143
if random.randint(0, 100) > self.goal_sample_rate:
133-
rnd = self.Node(random.uniform(self.min_rand, self.max_rand),
134-
random.uniform(self.min_rand, self.max_rand))
144+
rnd = self.Node(
145+
random.uniform(self.min_rand, self.max_rand),
146+
random.uniform(self.min_rand, self.max_rand))
135147
else: # goal point sampling
136148
rnd = self.Node(self.end.x, self.end.y)
137149
return rnd
138150

139151
def draw_graph(self, rnd=None):
140152
plt.clf()
141153
# for stopping simulation with the esc key.
142-
plt.gcf().canvas.mpl_connect('key_release_event',
143-
lambda event: [exit(0) if event.key == 'escape' else None])
154+
plt.gcf().canvas.mpl_connect(
155+
'key_release_event',
156+
lambda event: [exit(0) if event.key == 'escape' else None])
144157
if rnd is not None:
145158
plt.plot(rnd.x, rnd.y, "^k")
146159
for node in self.node_list:
@@ -167,8 +180,8 @@ def plot_circle(x, y, size, color="-b"): # pragma: no cover
167180

168181
@staticmethod
169182
def get_nearest_node_index(node_list, rnd_node):
170-
dlist = [(node.x - rnd_node.x) ** 2 + (node.y - rnd_node.y)
171-
** 2 for node in node_list]
183+
dlist = [(node.x - rnd_node.x)**2 + (node.y - rnd_node.y)**2
184+
for node in node_list]
172185
minind = dlist.index(min(dlist))
173186

174187
return minind
@@ -184,7 +197,7 @@ def check_collision(node, obstacleList):
184197
dy_list = [oy - y for y in node.path_y]
185198
d_list = [dx * dx + dy * dy for (dx, dy) in zip(dx_list, dy_list)]
186199

187-
if min(d_list) <= size ** 2:
200+
if min(d_list) <= size**2:
188201
return False # collision
189202

190203
return True # safe
@@ -202,20 +215,14 @@ def main(gx=6.0, gy=10.0):
202215
print("start " + __file__)
203216

204217
# ====Search Path with RRT====
205-
obstacleList = [
206-
(5, 5, 1),
207-
(3, 6, 2),
208-
(3, 8, 2),
209-
(3, 10, 2),
210-
(7, 5, 2),
211-
(9, 5, 2),
212-
(8, 10, 1)
213-
] # [x, y, radius]
218+
obstacleList = [(5, 5, 1), (3, 6, 2), (3, 8, 2), (3, 10, 2), (7, 5, 2),
219+
(9, 5, 2), (8, 10, 1)] # [x, y, radius]
214220
# Set Initial parameters
215-
rrt = RRT(start=[0, 0],
216-
goal=[gx, gy],
217-
rand_area=[-2, 15],
218-
obstacle_list=obstacleList)
221+
rrt = RRT(
222+
start=[0, 0],
223+
goal=[gx, gy],
224+
rand_area=[-2, 15],
225+
obstacle_list=obstacleList)
219226
path = rrt.planning(animation=show_animation)
220227

221228
if path is None:

PathPlanning/RRTStar/rrt_star.py

Lines changed: 100 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212

1313
import matplotlib.pyplot as plt
1414

15-
sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
16-
"/../RRT/")
15+
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../RRT/")
1716

1817
try:
1918
from rrt import RRT
@@ -33,15 +32,17 @@ def __init__(self, x, y):
3332
super().__init__(x, y)
3433
self.cost = 0.0
3534

36-
def __init__(self, start, goal, obstacle_list, rand_area,
35+
def __init__(self,
36+
start,
37+
goal,
38+
obstacle_list,
39+
rand_area,
3740
expand_dis=30.0,
3841
path_resolution=1.0,
3942
goal_sample_rate=20,
4043
max_iter=300,
41-
connect_circle_dist=50.0
42-
):
43-
super().__init__(start, goal, obstacle_list,
44-
rand_area, expand_dis, path_resolution, goal_sample_rate, max_iter)
44+
connect_circle_dist=50.0,
45+
search_until_max_iter=False):
4546
"""
4647
Setting Parameter
4748
@@ -51,35 +52,46 @@ def __init__(self, start, goal, obstacle_list, rand_area,
5152
randArea:Random Sampling Area [min,max]
5253
5354
"""
55+
super().__init__(start, goal, obstacle_list, rand_area, expand_dis,
56+
path_resolution, goal_sample_rate, max_iter)
5457
self.connect_circle_dist = connect_circle_dist
5558
self.goal_node = self.Node(goal[0], goal[1])
59+
self.search_until_max_iter = search_until_max_iter
5660

57-
def planning(self, animation=True, search_until_max_iter=True):
61+
def planning(self, animation=True):
5862
"""
5963
rrt star path planning
6064
61-
animation: flag for animation on or off
62-
search_until_max_iter: search until max iteration for path improving or not
65+
animation: flag for animation on or off .
6366
"""
6467

6568
self.node_list = [self.start]
6669
for i in range(self.max_iter):
6770
print("Iter:", i, ", number of nodes:", len(self.node_list))
6871
rnd = self.get_random_node()
6972
nearest_ind = self.get_nearest_node_index(self.node_list, rnd)
70-
new_node = self.steer(self.node_list[nearest_ind], rnd, self.expand_dis)
73+
new_node = self.steer(self.node_list[nearest_ind], rnd,
74+
self.expand_dis)
75+
near_node = self.node_list[nearest_ind]
76+
new_node.cost = near_node.cost + \
77+
math.hypot(new_node.x-near_node.x,
78+
new_node.y-near_node.y)
7179

7280
if self.check_collision(new_node, self.obstacle_list):
7381
near_inds = self.find_near_nodes(new_node)
74-
new_node = self.choose_parent(new_node, near_inds)
75-
if new_node:
82+
node_with_updated_parent = self.choose_parent(
83+
new_node, near_inds)
84+
if node_with_updated_parent:
85+
self.rewire(node_with_updated_parent, near_inds)
86+
self.node_list.append(node_with_updated_parent)
87+
else:
7688
self.node_list.append(new_node)
77-
self.rewire(new_node, near_inds)
7889

79-
if animation and i % 5 == 0:
90+
if animation:
8091
self.draw_graph(rnd)
8192

82-
if (not search_until_max_iter) and new_node: # check reaching the goal
93+
if ((not self.search_until_max_iter)
94+
and new_node): # if reaches goal
8395
last_index = self.search_best_goal_node()
8496
if last_index is not None:
8597
return self.generate_final_course(last_index)
@@ -93,6 +105,21 @@ def planning(self, animation=True, search_until_max_iter=True):
93105
return None
94106

95107
def choose_parent(self, new_node, near_inds):
108+
"""
109+
Computes the cheapest point to new_node contained in the list
110+
near_inds and set such a node as the parent of new_node.
111+
Arguments:
112+
--------
113+
new_node, Node
114+
randomly generated node with a path from its neared point
115+
There are not coalitions between this node and th tree.
116+
near_inds: list
117+
Indices of indices of the nodes what are near to new_node
118+
119+
Returns.
120+
------
121+
Node, a copy of new_node
122+
"""
96123
if not near_inds:
97124
return None
98125

@@ -113,14 +140,18 @@ def choose_parent(self, new_node, near_inds):
113140

114141
min_ind = near_inds[costs.index(min_cost)]
115142
new_node = self.steer(self.node_list[min_ind], new_node)
116-
new_node.parent = self.node_list[min_ind]
117143
new_node.cost = min_cost
118144

119145
return new_node
120146

121147
def search_best_goal_node(self):
122-
dist_to_goal_list = [self.calc_dist_to_goal(n.x, n.y) for n in self.node_list]
123-
goal_inds = [dist_to_goal_list.index(i) for i in dist_to_goal_list if i <= self.expand_dis]
148+
dist_to_goal_list = [
149+
self.calc_dist_to_goal(n.x, n.y) for n in self.node_list
150+
]
151+
goal_inds = [
152+
dist_to_goal_list.index(i) for i in dist_to_goal_list
153+
if i <= self.expand_dis
154+
]
124155

125156
safe_goal_inds = []
126157
for goal_ind in goal_inds:
@@ -139,17 +170,48 @@ def search_best_goal_node(self):
139170
return None
140171

141172
def find_near_nodes(self, new_node):
173+
"""
174+
1) defines a ball centered on new_node
175+
2) Returns all nodes of the three that are inside this ball
176+
Arguments:
177+
---------
178+
new_node: Node
179+
new randomly generated node, without collisions between
180+
its nearest node
181+
Returns:
182+
-------
183+
list
184+
List with the indices of the nodes inside the ball of
185+
radius r
186+
"""
142187
nnode = len(self.node_list) + 1
143188
r = self.connect_circle_dist * math.sqrt((math.log(nnode) / nnode))
144-
# if expand_dist exists, search vertices in a range no more than expand_dist
145-
if hasattr(self, 'expand_dis'):
189+
# if expand_dist exists, search vertices in a range no more than
190+
# expand_dist
191+
if hasattr(self, 'expand_dis'):
146192
r = min(r, self.expand_dis)
147-
dist_list = [(node.x - new_node.x) ** 2 +
148-
(node.y - new_node.y) ** 2 for node in self.node_list]
149-
near_inds = [dist_list.index(i) for i in dist_list if i <= r ** 2]
193+
dist_list = [(node.x - new_node.x)**2 + (node.y - new_node.y)**2
194+
for node in self.node_list]
195+
near_inds = [dist_list.index(i) for i in dist_list if i <= r**2]
150196
return near_inds
151197

152198
def rewire(self, new_node, near_inds):
199+
"""
200+
For each node in near_inds, this will check if it is cheaper to
201+
arrive to them from new_node.
202+
In such a case, this will re-assign the parent of the nodes in
203+
near_inds to new_node.
204+
Parameters:
205+
----------
206+
new_node, Node
207+
Node randomly added which can be joined to the tree
208+
209+
near_inds, list of uints
210+
A list of indices of the self.new_node which contains
211+
nodes within a circle of a given radius.
212+
Remark: parent is designated in choose_parent.
213+
214+
"""
153215
for i in near_inds:
154216
near_node = self.node_list[i]
155217
edge_node = self.steer(new_node, near_node)
@@ -161,7 +223,12 @@ def rewire(self, new_node, near_inds):
161223
improved_cost = near_node.cost > edge_node.cost
162224

163225
if no_collision and improved_cost:
164-
self.node_list[i] = edge_node
226+
near_node.x = edge_node.x
227+
near_node.y = edge_node.y
228+
near_node.cost = edge_node.cost
229+
near_node.path_x = edge_node.path_x
230+
near_node.path_y = edge_node.path_y
231+
near_node.parent = edge_node.parent
165232
self.propagate_cost_to_leaves(new_node)
166233

167234
def calc_new_cost(self, from_node, to_node):
@@ -192,10 +259,12 @@ def main():
192259
] # [x,y,size(radius)]
193260

194261
# Set Initial parameters
195-
rrt_star = RRTStar(start=[0, 0],
196-
goal=[6, 10],
197-
rand_area=[-2, 15],
198-
obstacle_list=obstacle_list)
262+
rrt_star = RRTStar(
263+
start=[0, 0],
264+
goal=[6, 10],
265+
rand_area=[-2, 15],
266+
obstacle_list=obstacle_list,
267+
expand_dis=1)
199268
path = rrt_star.planning(animation=show_animation)
200269

201270
if path is None:
@@ -206,10 +275,9 @@ def main():
206275
# Draw final path
207276
if show_animation:
208277
rrt_star.draw_graph()
209-
plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')
278+
plt.plot([x for (x, y) in path], [y for (x, y) in path], 'r--')
210279
plt.grid(True)
211-
plt.pause(0.01) # Need for Mac
212-
plt.show()
280+
plt.show()
213281

214282

215283
if __name__ == '__main__':

0 commit comments

Comments
 (0)