Skip to content

Commit 50b5b96

Browse files
authored
Merge pull request AtsushiSakai#312 from Guilyx/dfs
Depth-First Search
2 parents b896fb1 + 6c97785 commit 50b5b96

File tree

1 file changed

+254
-0
lines changed

1 file changed

+254
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
"""
2+
3+
Depth-First grid planning
4+
5+
author: Erwin Lejeune (@spida_rwin)
6+
7+
See Wikipedia article (https://en.wikipedia.org/wiki/Depth-first_search)
8+
9+
"""
10+
11+
import math
12+
13+
import matplotlib.pyplot as plt
14+
15+
show_animation = True
16+
17+
18+
class DepthFirstSearchPlanner:
19+
20+
def __init__(self, ox, oy, reso, rr):
21+
"""
22+
Initialize grid map for Depth-First planning
23+
24+
ox: x position list of Obstacles [m]
25+
oy: y position list of Obstacles [m]
26+
reso: grid resolution [m]
27+
rr: robot radius[m]
28+
"""
29+
30+
self.reso = reso
31+
self.rr = rr
32+
self.calc_obstacle_map(ox, oy)
33+
self.motion = self.get_motion_model()
34+
35+
class Node:
36+
def __init__(self, x, y, cost, pind, parent):
37+
self.x = x # index of grid
38+
self.y = y # index of grid
39+
self.cost = cost
40+
self.pind = pind
41+
self.parent = parent
42+
43+
def __str__(self):
44+
return str(self.x) + "," + str(self.y) + "," + str(
45+
self.cost) + "," + str(self.pind)
46+
47+
def planning(self, sx, sy, gx, gy):
48+
"""
49+
Depth First search
50+
51+
input:
52+
sx: start x position [m]
53+
sy: start y position [m]
54+
gx: goal x position [m]
55+
gy: goal y position [m]
56+
57+
output:
58+
rx: x position list of the final path
59+
ry: y position list of the final path
60+
"""
61+
62+
nstart = self.Node(self.calc_xyindex(sx, self.minx),
63+
self.calc_xyindex(sy, self.miny), 0.0, -1, None)
64+
ngoal = self.Node(self.calc_xyindex(gx, self.minx),
65+
self.calc_xyindex(gy, self.miny), 0.0, -1, None)
66+
67+
open_set, closed_set = dict(), dict()
68+
open_set[self.calc_grid_index(nstart)] = nstart
69+
70+
while 1:
71+
if len(open_set) == 0:
72+
print("Open set is empty..")
73+
break
74+
75+
current = open_set.pop(list(open_set.keys())[-1])
76+
c_id = self.calc_grid_index(current)
77+
78+
# show graph
79+
if show_animation: # pragma: no cover
80+
plt.plot(self.calc_grid_position(current.x, self.minx),
81+
self.calc_grid_position(current.y, self.miny), "xc")
82+
# for stopping simulation with the esc key.
83+
plt.gcf().canvas.mpl_connect('key_release_event',
84+
lambda event: [exit(
85+
0) if event.key == 'escape' else None])
86+
plt.pause(0.01)
87+
88+
if current.x == ngoal.x and current.y == ngoal.y:
89+
print("Find goal")
90+
ngoal.pind = current.pind
91+
ngoal.cost = current.cost
92+
break
93+
94+
# expand_grid search grid based on motion model
95+
for i, _ in enumerate(self.motion):
96+
node = self.Node(current.x + self.motion[i][0],
97+
current.y + self.motion[i][1],
98+
current.cost + self.motion[i][2], c_id, None)
99+
n_id = self.calc_grid_index(node)
100+
101+
# If the node is not safe, do nothing
102+
if not self.verify_node(node):
103+
continue
104+
105+
if n_id not in closed_set:
106+
open_set[n_id] = node
107+
closed_set[n_id] = node
108+
node.parent = current
109+
110+
rx, ry = self.calc_final_path(ngoal, closed_set)
111+
return rx, ry
112+
113+
def calc_final_path(self, ngoal, closedset):
114+
# generate final course
115+
rx, ry = [self.calc_grid_position(ngoal.x, self.minx)], [
116+
self.calc_grid_position(ngoal.y, self.miny)]
117+
n = closedset[ngoal.pind]
118+
while n is not None:
119+
rx.append(self.calc_grid_position(n.x, self.minx))
120+
ry.append(self.calc_grid_position(n.y, self.miny))
121+
n = n.parent
122+
123+
return rx, ry
124+
125+
def calc_grid_position(self, index, minp):
126+
"""
127+
calc grid position
128+
129+
:param index:
130+
:param minp:
131+
:return:
132+
"""
133+
pos = index * self.reso + minp
134+
return pos
135+
136+
def calc_xyindex(self, position, min_pos):
137+
return round((position - min_pos) / self.reso)
138+
139+
def calc_grid_index(self, node):
140+
return (node.y - self.miny) * self.xwidth + (node.x - self.minx)
141+
142+
def verify_node(self, node):
143+
px = self.calc_grid_position(node.x, self.minx)
144+
py = self.calc_grid_position(node.y, self.miny)
145+
146+
if px < self.minx:
147+
return False
148+
elif py < self.miny:
149+
return False
150+
elif px >= self.maxx:
151+
return False
152+
elif py >= self.maxy:
153+
return False
154+
155+
# collision check
156+
if self.obmap[node.x][node.y]:
157+
return False
158+
159+
return True
160+
161+
def calc_obstacle_map(self, ox, oy):
162+
163+
self.minx = round(min(ox))
164+
self.miny = round(min(oy))
165+
self.maxx = round(max(ox))
166+
self.maxy = round(max(oy))
167+
print("minx:", self.minx)
168+
print("miny:", self.miny)
169+
print("maxx:", self.maxx)
170+
print("maxy:", self.maxy)
171+
172+
self.xwidth = round((self.maxx - self.minx) / self.reso)
173+
self.ywidth = round((self.maxy - self.miny) / self.reso)
174+
print("xwidth:", self.xwidth)
175+
print("ywidth:", self.ywidth)
176+
177+
# obstacle map generation
178+
self.obmap = [[False for _ in range(self.ywidth)]
179+
for _ in range(self.xwidth)]
180+
for ix in range(self.xwidth):
181+
x = self.calc_grid_position(ix, self.minx)
182+
for iy in range(self.ywidth):
183+
y = self.calc_grid_position(iy, self.miny)
184+
for iox, ioy in zip(ox, oy):
185+
d = math.hypot(iox - x, ioy - y)
186+
if d <= self.rr:
187+
self.obmap[ix][iy] = True
188+
break
189+
190+
@staticmethod
191+
def get_motion_model():
192+
# dx, dy, cost
193+
motion = [[1, 0, 1],
194+
[0, 1, 1],
195+
[-1, 0, 1],
196+
[0, -1, 1],
197+
[-1, -1, math.sqrt(2)],
198+
[-1, 1, math.sqrt(2)],
199+
[1, -1, math.sqrt(2)],
200+
[1, 1, math.sqrt(2)]]
201+
202+
return motion
203+
204+
205+
def main():
206+
print(__file__ + " start!!")
207+
208+
# start and goal position
209+
sx = 10.0 # [m]
210+
sy = 10.0 # [m]
211+
gx = 50.0 # [m]
212+
gy = 50.0 # [m]
213+
grid_size = 2.0 # [m]
214+
robot_radius = 1.0 # [m]
215+
216+
# set obstable positions
217+
ox, oy = [], []
218+
for i in range(-10, 60):
219+
ox.append(i)
220+
oy.append(-10.0)
221+
for i in range(-10, 60):
222+
ox.append(60.0)
223+
oy.append(i)
224+
for i in range(-10, 61):
225+
ox.append(i)
226+
oy.append(60.0)
227+
for i in range(-10, 61):
228+
ox.append(-10.0)
229+
oy.append(i)
230+
for i in range(-10, 40):
231+
ox.append(20.0)
232+
oy.append(i)
233+
for i in range(0, 40):
234+
ox.append(40.0)
235+
oy.append(60.0 - i)
236+
237+
if show_animation: # pragma: no cover
238+
plt.plot(ox, oy, ".k")
239+
plt.plot(sx, sy, "og")
240+
plt.plot(gx, gy, "xb")
241+
plt.grid(True)
242+
plt.axis("equal")
243+
244+
dfs = DepthFirstSearchPlanner(ox, oy, grid_size, robot_radius)
245+
rx, ry = dfs.planning(sx, sy, gx, gy)
246+
247+
if show_animation: # pragma: no cover
248+
plt.plot(rx, ry, "-r")
249+
plt.pause(0.01)
250+
plt.show()
251+
252+
253+
if __name__ == '__main__':
254+
main()

0 commit comments

Comments
 (0)