Skip to content

Commit 3073a02

Browse files
committed
fix bug and huge refactoring
1 parent 193ff61 commit 3073a02

File tree

2 files changed

+390
-361
lines changed

2 files changed

+390
-361
lines changed

PathPlanning/AStar/a_star.py

Lines changed: 184 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -14,190 +14,191 @@
1414

1515
show_animation = True
1616

17-
class Node:
18-
19-
def __init__(self, x, y, cost, pind):
20-
self.x = x # index of grid
21-
self.y = y # index of grid
22-
self.cost = cost
23-
self.pind = pind
24-
25-
def __str__(self):
26-
return str(self.x) + "," + str(self.y) + "," + str(self.cost) + "," + str(self.pind)
27-
28-
def a_star_planning(sx, sy, gx, gy, ox, oy, reso, rr):
29-
"""
30-
A star path search
31-
32-
input:
33-
sx: start x position [m]
34-
sy: start y position [m]
35-
gx: goal x position [m]
36-
gx: goal x position [m]
17+
class AStarPlanner:
18+
19+
def __init__(self, ox, oy, reso, rr):
20+
"""
21+
Intialize map for a star planning
22+
3723
ox: x position list of Obstacles [m]
3824
oy: y position list of Obstacles [m]
3925
reso: grid resolution [m]
4026
rr: robot radius[m]
41-
42-
output:
43-
rx: x position list of the final path
44-
ry: y position list of the final path
45-
"""
46-
47-
ox = [iox / reso for iox in ox]
48-
oy = [ioy / reso for ioy in oy]
49-
50-
obmap, minx, miny, maxx, maxy, xw, yw = calc_obstacle_map(ox, oy, reso, rr)
51-
52-
motion = get_motion_model()
53-
54-
nstart = Node(calc_xyindex(sx, minx, reso), calc_xyindex(sy, minx, reso), 0.0, -1)
55-
ngoal = Node(calc_xyindex(gx, minx, reso), calc_xyindex(gy, minx, reso), 0.0, -1)
56-
57-
openset, closedset = dict(), dict()
58-
openset[calc_index(nstart, xw, minx, miny)] = nstart
59-
60-
while 1:
61-
c_id = min(
62-
openset, key=lambda o: openset[o].cost + calc_heuristic(ngoal, openset[o]))
63-
current = openset[c_id]
64-
65-
# show graph
66-
if show_animation: # pragma: no cover
67-
plt.plot(calc_position(current.x, minx, reso), calc_position(current.y, miny, reso), "xc")
68-
if len(closedset.keys()) % 10 == 0:
69-
plt.pause(0.001)
70-
71-
if current.x == ngoal.x and current.y == ngoal.y:
72-
print("Find goal")
73-
ngoal.pind = current.pind
74-
ngoal.cost = current.cost
75-
break
76-
77-
# Remove the item from the open set
78-
del openset[c_id]
79-
80-
# Add it to the closed set
81-
closedset[c_id] = current
82-
83-
# expand search grid based on motion model
84-
for i, _ in enumerate(motion):
85-
node = Node(current.x + motion[i][0],
86-
current.y + motion[i][1],
87-
current.cost + motion[i][2], c_id)
88-
n_id = calc_index(node, xw, minx, miny)
89-
90-
if n_id in closedset:
91-
continue
92-
93-
if not verify_node(node, obmap, minx, miny, maxx, maxy, reso):
94-
continue
95-
96-
if n_id not in openset:
97-
openset[n_id] = node # Discover a new node
98-
else:
99-
if openset[n_id].cost >= node.cost:
100-
# This path is the best until now. record it!
101-
openset[n_id] = node
102-
103-
rx, ry = calc_final_path(ngoal, closedset, reso, minx, miny)
104-
105-
return rx, ry
106-
107-
108-
109-
def calc_final_path(ngoal, closedset, reso, minx, miny):
110-
# generate final course
111-
rx, ry = [calc_position(ngoal.x, minx, reso)], [calc_position(ngoal.y, miny, reso)]
112-
pind = ngoal.pind
113-
while pind != -1:
114-
n = closedset[pind]
115-
rx.append(calc_position(n.x, minx, reso))
116-
ry.append(calc_position(n.y, miny, reso))
117-
pind = n.pind
118-
119-
return rx, ry
120-
121-
122-
123-
def calc_heuristic(n1, n2):
124-
w = 1.0 # weight of heuristic
125-
d = w * math.sqrt((n1.x - n2.x)**2 + (n1.y - n2.y)**2)
126-
return d
127-
128-
def calc_position(index, minp, reso):
129-
return index*reso+minp
130-
131-
def calc_xyindex(position, minp, reso):
132-
return round((position - minp)/reso)
133-
134-
def verify_node(node, obmap, minx, miny, maxx, maxy, reso):
135-
136-
px = calc_position(node.x, minx, reso)
137-
py = calc_position(node.y, miny, reso)
138-
139-
if px < minx:
140-
return False
141-
elif py < miny:
142-
return False
143-
elif px >= maxx:
144-
return False
145-
elif py >= maxy:
146-
return False
147-
148-
if obmap[node.x][node.y]:
149-
return False
150-
151-
return True
152-
153-
154-
def calc_obstacle_map(ox, oy, reso, rr):
155-
156-
minx = round(min(ox))
157-
miny = round(min(oy))
158-
maxx = round(max(ox))
159-
maxy = round(max(oy))
160-
print("minx:", minx)
161-
print("miny:", miny)
162-
print("maxx:", maxx)
163-
print("maxy:", maxy)
164-
165-
xwidth = round(maxx - minx)
166-
ywidth = round(maxy - miny)
167-
print("xwidth:", xwidth)
168-
print("ywidth:", ywidth)
169-
170-
# obstacle map generation
171-
obmap = [[False for i in range(ywidth)] for i in range(xwidth)]
172-
for ix in range(xwidth):
173-
x = ix + minx
174-
for iy in range(ywidth):
175-
y = iy + miny
176-
for iox, ioy in zip(ox, oy):
177-
d = math.sqrt((iox - x)**2 + (ioy - y)**2)
178-
if d <= rr:
179-
obmap[ix][iy] = True
180-
break
181-
182-
return obmap, minx, miny, maxx, maxy, xwidth, ywidth
183-
184-
185-
def calc_index(node, xwidth, xmin, ymin):
186-
return (node.y - ymin) * xwidth + (node.x - xmin)
187-
188-
189-
def get_motion_model():
190-
# dx, dy, cost
191-
motion = [[1, 0, 1],
192-
[0, 1, 1],
193-
[-1, 0, 1],
194-
[0, -1, 1],
195-
[-1, -1, math.sqrt(2)],
196-
[-1, 1, math.sqrt(2)],
197-
[1, -1, math.sqrt(2)],
198-
[1, 1, math.sqrt(2)]]
199-
200-
return motion
27+
"""
28+
29+
self.reso = reso
30+
self.rr = rr
31+
self.calc_obstacle_map(ox, oy)
32+
self.motion = self.get_motion_model()
33+
34+
class Node:
35+
def __init__(self, x, y, cost, pind):
36+
self.x = x # index of grid
37+
self.y = y # index of grid
38+
self.cost = cost
39+
self.pind = pind
40+
41+
def __str__(self):
42+
return str(self.x) + "," + str(self.y) + "," + str(self.cost) + "," + str(self.pind)
43+
44+
def planning(self, sx, sy, gx, gy):
45+
"""
46+
A star path search
47+
48+
input:
49+
sx: start x position [m]
50+
sy: start y position [m]
51+
gx: goal x position [m]
52+
gx: goal x position [m]
53+
54+
output:
55+
rx: x position list of the final path
56+
ry: y position list of the final path
57+
"""
58+
59+
nstart = self.Node(self.calc_xyindex(sx, self.minx),
60+
self.calc_xyindex(sy, self.miny), 0.0, -1)
61+
ngoal = self.Node(self.calc_xyindex(gx, self.minx),
62+
self.calc_xyindex(gy, self.miny), 0.0, -1)
63+
64+
openset, closedset = dict(), dict()
65+
openset[self.calc_index(nstart)] = nstart
66+
67+
while 1:
68+
c_id = min(
69+
openset, key=lambda o: openset[o].cost + self.calc_heuristic(ngoal, openset[o]))
70+
current = openset[c_id]
71+
72+
# show graph
73+
if show_animation: # pragma: no cover
74+
plt.plot(self.calc_position(current.x, self.minx),
75+
self.calc_position(current.y, self.miny), "xc")
76+
if len(closedset.keys()) % 10 == 0:
77+
plt.pause(0.001)
78+
79+
if current.x == ngoal.x and current.y == ngoal.y:
80+
print("Find goal")
81+
ngoal.pind = current.pind
82+
ngoal.cost = current.cost
83+
break
84+
85+
# Remove the item from the open set
86+
del openset[c_id]
87+
88+
# Add it to the closed set
89+
closedset[c_id] = current
90+
91+
# expand search grid based on motion model
92+
for i, _ in enumerate(self.motion):
93+
node = self.Node(current.x + self.motion[i][0],
94+
current.y + self.motion[i][1],
95+
current.cost + self.motion[i][2], c_id)
96+
n_id = self.calc_index(node)
97+
98+
if n_id in closedset:
99+
continue
100+
101+
if not self.verify_node(node):
102+
continue
103+
104+
if n_id not in openset:
105+
openset[n_id] = node # Discover a new node
106+
else:
107+
if openset[n_id].cost >= node.cost:
108+
# This path is the best until now. record it!
109+
openset[n_id] = node
110+
111+
rx, ry = self.calc_final_path(ngoal, closedset)
112+
113+
return rx, ry
114+
115+
def calc_final_path(self, ngoal, closedset):
116+
# generate final course
117+
rx, ry = [self.calc_position(ngoal.x, self.minx)], [
118+
self.calc_position(ngoal.y, self.miny)]
119+
pind = ngoal.pind
120+
while pind != -1:
121+
n = closedset[pind]
122+
rx.append(self.calc_position(n.x, self.minx))
123+
ry.append(self.calc_position(n.y, self.miny))
124+
pind = n.pind
125+
126+
return rx, ry
127+
128+
def calc_heuristic(self, n1, n2):
129+
w = 1.0 # weight of heuristic
130+
d = w * math.sqrt((n1.x - n2.x)**2 + (n1.y - n2.y)**2)
131+
return d
132+
133+
def calc_position(self, index, minp):
134+
pos = index*self.reso+minp
135+
return pos
136+
137+
def calc_xyindex(self, position, minp):
138+
return round((position - minp)/self.reso)
139+
140+
def calc_index(self, node):
141+
return (node.y - self.miny) * self.xwidth + (node.x - self.minx)
142+
143+
def verify_node(self, node):
144+
px = self.calc_position(node.x, self.minx)
145+
py = self.calc_position(node.y, self.miny)
146+
147+
if px < self.minx:
148+
return False
149+
elif py < self.miny:
150+
return False
151+
elif px >= self.maxx:
152+
return False
153+
elif py >= self.maxy:
154+
return False
155+
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 i in range(self.ywidth)]
179+
for i in range(self.xwidth)]
180+
for ix in range(self.xwidth):
181+
x = self.calc_position(ix, self.minx)
182+
for iy in range(self.ywidth):
183+
y = self.calc_position(iy, self.miny)
184+
for iox, ioy in zip(ox, oy):
185+
d = math.sqrt((iox - x)**2 + (ioy - y)**2)
186+
if d <= self.rr:
187+
self.obmap[ix][iy] = True
188+
break
189+
190+
def get_motion_model(self):
191+
# dx, dy, cost
192+
motion = [[1, 0, 1],
193+
[0, 1, 1],
194+
[-1, 0, 1],
195+
[0, -1, 1],
196+
[-1, -1, math.sqrt(2)],
197+
[-1, 1, math.sqrt(2)],
198+
[1, -1, math.sqrt(2)],
199+
[1, 1, math.sqrt(2)]]
200+
201+
return motion
201202

202203

203204
def main():
@@ -208,7 +209,7 @@ def main():
208209
sy = 10.0 # [m]
209210
gx = 50.0 # [m]
210211
gy = 50.0 # [m]
211-
grid_size = 1.0 # [m]
212+
grid_size = 2.0 # [m]
212213
robot_radius = 1.0 # [m]
213214

214215
# set obstable positions
@@ -239,7 +240,8 @@ def main():
239240
plt.grid(True)
240241
plt.axis("equal")
241242

242-
rx, ry = a_star_planning(sx, sy, gx, gy, ox, oy, grid_size, robot_radius)
243+
a_star = AStarPlanner(ox, oy, grid_size, robot_radius)
244+
rx, ry = a_star.planning(sx, sy, gx, gy)
243245

244246
if show_animation: # pragma: no cover
245247
plt.plot(rx, ry, "-r")

0 commit comments

Comments
 (0)