Skip to content

Commit 4902966

Browse files
authored
Merge pull request AtsushiSakai#297 from AtsushiSakai/issue_294
Visibility Graph Path Planner implemenation
2 parents ef39fc2 + 881308b commit 4902966

File tree

10 files changed

+648
-283
lines changed

10 files changed

+648
-283
lines changed

PathPlanning/Dijkstra/dijkstra.py

Lines changed: 85 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,42 @@
1111

1212
show_animation = True
1313

14+
1415
class Dijkstra:
1516

16-
def __init__(self, ox, oy, reso, rr):
17+
def __init__(self, ox, oy, resolution, robot_radius):
1718
"""
1819
Initialize map for a star planning
1920
2021
ox: x position list of Obstacles [m]
2122
oy: y position list of Obstacles [m]
22-
reso: grid resolution [m]
23+
resolution: grid resolution [m]
2324
rr: robot radius[m]
2425
"""
2526

26-
self.reso = reso
27-
self.rr = rr
27+
self.min_x = None
28+
self.min_y = None
29+
self.max_x = None
30+
self.max_y = None
31+
self.x_width = None
32+
self.y_width = None
33+
self.obstacle_map = None
34+
35+
self.resolution = resolution
36+
self.robot_radius = robot_radius
2837
self.calc_obstacle_map(ox, oy)
2938
self.motion = self.get_motion_model()
3039

3140
class Node:
32-
def __init__(self, x, y, cost, pind):
41+
def __init__(self, x, y, cost, parent):
3342
self.x = x # index of grid
3443
self.y = y # index of grid
3544
self.cost = cost
36-
self.pind = pind # index of previous Node
45+
self.parent = parent # index of previous Node
3746

3847
def __str__(self):
39-
return str(self.x) + "," + str(self.y) + "," + str(self.cost) + "," + str(self.pind)
48+
return str(self.x) + "," + str(self.y) + "," + str(
49+
self.cost) + "," + str(self.parent)
4050

4151
def planning(self, sx, sy, gx, gy):
4252
"""
@@ -53,39 +63,40 @@ def planning(self, sx, sy, gx, gy):
5363
ry: y position list of the final path
5464
"""
5565

56-
nstart = self.Node(self.calc_xyindex(sx, self.minx),
57-
self.calc_xyindex(sy, self.miny), 0.0, -1)
58-
ngoal = self.Node(self.calc_xyindex(gx, self.minx),
59-
self.calc_xyindex(gy, self.miny), 0.0, -1)
66+
start_node = self.Node(self.calc_xy_index(sx, self.min_x),
67+
self.calc_xy_index(sy, self.min_y), 0.0, -1)
68+
goal_node = self.Node(self.calc_xy_index(gx, self.min_x),
69+
self.calc_xy_index(gy, self.min_y), 0.0, -1)
6070

61-
openset, closedset = dict(), dict()
62-
openset[self.calc_index(nstart)] = nstart
71+
open_set, closed_set = dict(), dict()
72+
open_set[self.calc_index(start_node)] = start_node
6373

6474
while 1:
65-
c_id = min(openset, key=lambda o: openset[o].cost)
66-
current = openset[c_id]
75+
c_id = min(open_set, key=lambda o: open_set[o].cost)
76+
current = open_set[c_id]
6777

6878
# show graph
6979
if show_animation: # pragma: no cover
70-
plt.plot(self.calc_position(current.x, self.minx),
71-
self.calc_position(current.y, self.miny), "xc")
80+
plt.plot(self.calc_position(current.x, self.min_x),
81+
self.calc_position(current.y, self.min_y), "xc")
7282
# for stopping simulation with the esc key.
73-
plt.gcf().canvas.mpl_connect('key_release_event',
74-
lambda event: [exit(0) if event.key == 'escape' else None])
75-
if len(closedset.keys()) % 10 == 0:
83+
plt.gcf().canvas.mpl_connect(
84+
'key_release_event',
85+
lambda event: [exit(0) if event.key == 'escape' else None])
86+
if len(closed_set.keys()) % 10 == 0:
7687
plt.pause(0.001)
7788

78-
if current.x == ngoal.x and current.y == ngoal.y:
89+
if current.x == goal_node.x and current.y == goal_node.y:
7990
print("Find goal")
80-
ngoal.pind = current.pind
81-
ngoal.cost = current.cost
91+
goal_node.parent = current.parent
92+
goal_node.cost = current.cost
8293
break
8394

8495
# Remove the item from the open set
85-
del openset[c_id]
96+
del open_set[c_id]
8697

8798
# Add it to the closed set
88-
closedset[c_id] = current
99+
closed_set[c_id] = current
89100

90101
# expand search grid based on motion model
91102
for move_x, move_y, move_cost in self.motion:
@@ -94,94 +105,95 @@ def planning(self, sx, sy, gx, gy):
94105
current.cost + move_cost, c_id)
95106
n_id = self.calc_index(node)
96107

97-
if n_id in closedset:
108+
if n_id in closed_set:
98109
continue
99110

100111
if not self.verify_node(node):
101112
continue
102113

103-
if n_id not in openset:
104-
openset[n_id] = node # Discover a new node
114+
if n_id not in open_set:
115+
open_set[n_id] = node # Discover a new node
105116
else:
106-
if openset[n_id].cost >= node.cost:
117+
if open_set[n_id].cost >= node.cost:
107118
# This path is the best until now. record it!
108-
openset[n_id] = node
119+
open_set[n_id] = node
109120

110-
rx, ry = self.calc_final_path(ngoal, closedset)
121+
rx, ry = self.calc_final_path(goal_node, closed_set)
111122

112123
return rx, ry
113124

114-
def calc_final_path(self, ngoal, closedset):
125+
def calc_final_path(self, goal_node, closed_set):
115126
# generate final course
116-
rx, ry = [self.calc_position(ngoal.x, self.minx)], [
117-
self.calc_position(ngoal.y, self.miny)]
118-
pind = ngoal.pind
119-
while pind != -1:
120-
n = closedset[pind]
121-
rx.append(self.calc_position(n.x, self.minx))
122-
ry.append(self.calc_position(n.y, self.miny))
123-
pind = n.pind
127+
rx, ry = [self.calc_position(goal_node.x, self.min_x)], [
128+
self.calc_position(goal_node.y, self.min_y)]
129+
parent = goal_node.parent
130+
while parent != -1:
131+
n = closed_set[parent]
132+
rx.append(self.calc_position(n.x, self.min_x))
133+
ry.append(self.calc_position(n.y, self.min_y))
134+
parent = n.parent
124135

125136
return rx, ry
126137

127138
def calc_position(self, index, minp):
128-
pos = index*self.reso+minp
139+
pos = index * self.resolution + minp
129140
return pos
130141

131-
def calc_xyindex(self, position, minp):
132-
return round((position - minp)/self.reso)
142+
def calc_xy_index(self, position, minp):
143+
return round((position - minp) / self.resolution)
133144

134145
def calc_index(self, node):
135-
return (node.y - self.miny) * self.xwidth + (node.x - self.minx)
146+
return (node.y - self.min_y) * self.x_width + (node.x - self.min_x)
136147

137148
def verify_node(self, node):
138-
px = self.calc_position(node.x, self.minx)
139-
py = self.calc_position(node.y, self.miny)
149+
px = self.calc_position(node.x, self.min_x)
150+
py = self.calc_position(node.y, self.min_y)
140151

141-
if px < self.minx:
152+
if px < self.min_x:
142153
return False
143-
elif py < self.miny:
154+
if py < self.min_y:
144155
return False
145-
elif px >= self.maxx:
156+
if px >= self.max_x:
146157
return False
147-
elif py >= self.maxy:
158+
if py >= self.max_y:
148159
return False
149160

150-
if self.obmap[node.x][node.y]:
161+
if self.obstacle_map[node.x][node.y]:
151162
return False
152163

153164
return True
154165

155166
def calc_obstacle_map(self, ox, oy):
156167

157-
self.minx = round(min(ox))
158-
self.miny = round(min(oy))
159-
self.maxx = round(max(ox))
160-
self.maxy = round(max(oy))
161-
print("minx:", self.minx)
162-
print("miny:", self.miny)
163-
print("maxx:", self.maxx)
164-
print("maxy:", self.maxy)
168+
self.min_x = round(min(ox))
169+
self.min_y = round(min(oy))
170+
self.max_x = round(max(ox))
171+
self.max_y = round(max(oy))
172+
print("min_x:", self.min_x)
173+
print("min_y:", self.min_y)
174+
print("max_x:", self.max_x)
175+
print("max_y:", self.max_y)
165176

166-
self.xwidth = round((self.maxx - self.minx)/self.reso)
167-
self.ywidth = round((self.maxy - self.miny)/self.reso)
168-
print("xwidth:", self.xwidth)
169-
print("ywidth:", self.ywidth)
177+
self.x_width = round((self.max_x - self.min_x) / self.resolution)
178+
self.y_width = round((self.max_y - self.min_y) / self.resolution)
179+
print("x_width:", self.x_width)
180+
print("y_width:", self.y_width)
170181

171182
# obstacle map generation
172-
self.obmap = [[False for i in range(self.ywidth)]
173-
for i in range(self.xwidth)]
174-
for ix in range(self.xwidth):
175-
x = self.calc_position(ix, self.minx)
176-
for iy in range(self.ywidth):
177-
y = self.calc_position(iy, self.miny)
183+
self.obstacle_map = [[False for _ in range(self.y_width)]
184+
for _ in range(self.x_width)]
185+
for ix in range(self.x_width):
186+
x = self.calc_position(ix, self.min_x)
187+
for iy in range(self.y_width):
188+
y = self.calc_position(iy, self.min_y)
178189
for iox, ioy in zip(ox, oy):
179190
d = math.hypot(iox - x, ioy - y)
180-
if d <= self.rr:
181-
self.obmap[ix][iy] = True
191+
if d <= self.robot_radius:
192+
self.obstacle_map[ix][iy] = True
182193
break
183194

184-
def get_motion_model(self):
195+
@staticmethod
196+
def get_motion_model():
185197
# dx, dy, cost
186198
motion = [[1, 0, 1],
187199
[0, 1, 1],
@@ -239,6 +251,7 @@ def main():
239251

240252
if show_animation: # pragma: no cover
241253
plt.plot(rx, ry, "-r")
254+
plt.pause(0.01)
242255
plt.show()
243256

244257

PathPlanning/VisibilityRoadMap/__init__.py

Whitespace-only changes.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
class Geometry:
2+
class Point:
3+
def __init__(self, x, y):
4+
self.x = x
5+
self.y = y
6+
7+
@staticmethod
8+
def is_seg_intersect(p1, q1, p2, q2):
9+
10+
def on_segment(p, q, r):
11+
if ((q.x <= max(p.x, r.x)) and (q.x >= min(p.x, r.x)) and
12+
(q.y <= max(p.y, r.y)) and (q.y >= min(p.y, r.y))):
13+
return True
14+
return False
15+
16+
def orientation(p, q, r):
17+
val = (float(q.y - p.y) * (r.x - q.x)) - (
18+
float(q.x - p.x) * (r.y - q.y))
19+
if val > 0:
20+
return 1
21+
if val < 0:
22+
return 2
23+
return 0
24+
25+
# Find the 4 orientations required for
26+
# the general and special cases
27+
o1 = orientation(p1, q1, p2)
28+
o2 = orientation(p1, q1, q2)
29+
o3 = orientation(p2, q2, p1)
30+
o4 = orientation(p2, q2, q1)
31+
32+
if (o1 != o2) and (o3 != o4):
33+
return True
34+
if (o1 == 0) and on_segment(p1, p2, q1):
35+
return True
36+
if (o2 == 0) and on_segment(p1, q2, q1):
37+
return True
38+
if (o3 == 0) and on_segment(p2, p1, q2):
39+
return True
40+
if (o4 == 0) and on_segment(p2, q1, q2):
41+
return True
42+
43+
return False

0 commit comments

Comments
 (0)