Skip to content

Commit 5fd8974

Browse files
authored
Merge pull request AtsushiSakai#180 from zhengzh/master
hybrid a star
2 parents ca267c6 + d8367b5 commit 5fd8974

File tree

3 files changed

+803
-0
lines changed

3 files changed

+803
-0
lines changed

PathPlanning/HybridAStar/a_star.py

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
"""
2+
3+
A* grid based planning
4+
5+
author: Atsushi Sakai(@Atsushi_twi)
6+
Nikos Kanargias ([email protected])
7+
8+
See Wikipedia article (https://en.wikipedia.org/wiki/A*_search_algorithm)
9+
10+
"""
11+
12+
import matplotlib.pyplot as plt
13+
import math
14+
import heapq
15+
16+
# _round = round
17+
# def round(x):
18+
# return int(_round(x))
19+
20+
show_animation = False
21+
22+
class Node:
23+
24+
def __init__(self, x, y, cost, pind):
25+
self.x = x
26+
self.y = y
27+
self.cost = cost
28+
self.pind = pind
29+
30+
def __str__(self):
31+
return str(self.x) + "," + str(self.y) + "," + str(self.cost) + "," + str(self.pind)
32+
33+
34+
def calc_final_path(ngoal, closedset, reso):
35+
# generate final course
36+
rx, ry = [ngoal.x * reso], [ngoal.y * reso]
37+
pind = ngoal.pind
38+
while pind != -1:
39+
n = closedset[pind]
40+
rx.append(n.x * reso)
41+
ry.append(n.y * reso)
42+
pind = n.pind
43+
44+
return rx, ry
45+
46+
47+
def dp_planning(sx, sy, gx, gy, ox, oy, reso, rr):
48+
"""
49+
gx: goal x position [m]
50+
gx: goal x position [m]
51+
ox: x position list of Obstacles [m]
52+
oy: y position list of Obstacles [m]
53+
reso: grid resolution [m]
54+
rr: robot radius[m]
55+
"""
56+
57+
nstart = Node(round(sx / reso), round(sy / reso), 0.0, -1)
58+
ngoal = Node(round(gx / reso), round(gy / reso), 0.0, -1)
59+
ox = [iox / reso for iox in ox]
60+
oy = [ioy / reso for ioy in oy]
61+
62+
obmap, minx, miny, maxx, maxy, xw, yw = calc_obstacle_map(ox, oy, reso, rr)
63+
64+
motion = get_motion_model()
65+
66+
openset, closedset = dict(), dict()
67+
openset[calc_index(ngoal, xw, minx, miny)] = ngoal
68+
pq = []
69+
pq.append((0, calc_index(ngoal, xw, minx, miny)))
70+
71+
72+
while 1:
73+
if not pq:
74+
break
75+
cost, c_id = heapq.heappop(pq)
76+
if c_id in openset:
77+
current = openset[c_id]
78+
closedset[c_id] = current
79+
openset.pop(c_id)
80+
else:
81+
continue
82+
83+
# show graph
84+
if show_animation: # pragma: no cover
85+
plt.plot(current.x * reso, current.y * reso, "xc")
86+
if len(closedset.keys()) % 10 == 0:
87+
plt.pause(0.001)
88+
89+
# Remove the item from the open set
90+
91+
92+
# expand search grid based on motion model
93+
for i, _ in enumerate(motion):
94+
node = Node(current.x + motion[i][0],
95+
current.y + motion[i][1],
96+
current.cost + motion[i][2], c_id)
97+
n_id = calc_index(node, xw, minx, miny)
98+
99+
if n_id in closedset:
100+
continue
101+
102+
if not verify_node(node, obmap, minx, miny, maxx, maxy):
103+
continue
104+
105+
if n_id not in openset:
106+
openset[n_id] = node # Discover a new node
107+
heapq.heappush(pq, (node.cost, calc_index(node, xw, minx, miny)))
108+
else:
109+
if openset[n_id].cost >= node.cost:
110+
# This path is the best until now. record it!
111+
openset[n_id] = node
112+
heapq.heappush(pq, (node.cost, calc_index(node, xw, minx, miny)))
113+
114+
115+
rx, ry = calc_final_path(closedset[calc_index(nstart, xw, minx, miny)], closedset, reso)
116+
117+
return rx, ry, closedset
118+
119+
120+
def calc_heuristic(n1, n2):
121+
w = 1.0 # weight of heuristic
122+
d = w * math.sqrt((n1.x - n2.x)**2 + (n1.y - n2.y)**2)
123+
return d
124+
125+
126+
def verify_node(node, obmap, minx, miny, maxx, maxy):
127+
128+
if node.x < minx:
129+
return False
130+
elif node.y < miny:
131+
return False
132+
elif node.x >= maxx:
133+
return False
134+
elif node.y >= maxy:
135+
return False
136+
137+
if obmap[node.x][node.y]:
138+
return False
139+
140+
return True
141+
142+
143+
def calc_obstacle_map(ox, oy, reso, vr):
144+
145+
minx = round(min(ox))
146+
miny = round(min(oy))
147+
maxx = round(max(ox))
148+
maxy = round(max(oy))
149+
# print("minx:", minx)
150+
# print("miny:", miny)
151+
# print("maxx:", maxx)
152+
# print("maxy:", maxy)
153+
154+
xwidth = round(maxx - minx)
155+
ywidth = round(maxy - miny)
156+
# print("xwidth:", xwidth)
157+
# print("ywidth:", ywidth)
158+
159+
# obstacle map generation
160+
obmap = [[False for i in range(xwidth)] for i in range(ywidth)]
161+
for ix in range(xwidth):
162+
x = ix + minx
163+
for iy in range(ywidth):
164+
y = iy + miny
165+
# print(x, y)
166+
for iox, ioy in zip(ox, oy):
167+
d = math.sqrt((iox - x)**2 + (ioy - y)**2)
168+
if d <= vr / reso:
169+
obmap[ix][iy] = True
170+
break
171+
172+
return obmap, minx, miny, maxx, maxy, xwidth, ywidth
173+
174+
175+
def calc_index(node, xwidth, xmin, ymin):
176+
return (node.y - ymin) * xwidth + (node.x - xmin)
177+
178+
179+
def get_motion_model():
180+
# dx, dy, cost
181+
motion = [[1, 0, 1],
182+
[0, 1, 1],
183+
[-1, 0, 1],
184+
[0, -1, 1],
185+
[-1, -1, math.sqrt(2)],
186+
[-1, 1, math.sqrt(2)],
187+
[1, -1, math.sqrt(2)],
188+
[1, 1, math.sqrt(2)]]
189+
190+
return motion
191+
192+
193+
def main():
194+
print(__file__ + " start!!")
195+
196+
# start and goal position
197+
sx = 10.0 # [m]
198+
sy = 10.0 # [m]
199+
gx = 50.0 # [m]
200+
gy = 50.0 # [m]
201+
grid_size = 2.0 # [m]
202+
robot_size = 1.0 # [m]
203+
204+
ox, oy = [], []
205+
206+
for i in range(60):
207+
ox.append(i)
208+
oy.append(0.0)
209+
for i in range(60):
210+
ox.append(60.0)
211+
oy.append(i)
212+
for i in range(61):
213+
ox.append(i)
214+
oy.append(60.0)
215+
for i in range(61):
216+
ox.append(0.0)
217+
oy.append(i)
218+
for i in range(40):
219+
ox.append(20.0)
220+
oy.append(i)
221+
for i in range(40):
222+
ox.append(40.0)
223+
oy.append(60.0 - i)
224+
225+
if show_animation: # pragma: no cover
226+
plt.plot(ox, oy, ".k")
227+
plt.plot(sx, sy, "xr")
228+
plt.plot(gx, gy, "xb")
229+
plt.grid(True)
230+
plt.axis("equal")
231+
232+
rx, ry, _ = dp_planning(sx, sy, gx, gy, ox, oy, grid_size, robot_size)
233+
234+
if show_animation: # pragma: no cover
235+
plt.plot(rx, ry, "-r")
236+
plt.show()
237+
238+
239+
if __name__ == '__main__':
240+
show_animation = True
241+
main()

PathPlanning/HybridAStar/car.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
2+
import matplotlib.pyplot as plt
3+
from math import sqrt, cos, sin, tan, pi
4+
5+
WB = 3. # rear to front wheel
6+
W = 2. # width of car
7+
LF = 3.3 # distance from rear to vehicle front end
8+
LB = 1.0 # distance from rear to vehicle back end
9+
MAX_STEER = 0.6 #[rad] maximum steering angle
10+
11+
WBUBBLE_DIST = (LF-LB)/2.0
12+
WBUBBLE_R = sqrt(((LF+LB)/2.0)**2+1)
13+
14+
# vehicle rectangle verticles
15+
VRX = [LF, LF, -LB, -LB, LF]
16+
VRY = [W/2,-W/2,-W/2,W/2,W/2]
17+
18+
19+
def check_car_collision(xlist, ylist, yawlist, ox, oy, kdtree):
20+
for x, y, yaw in zip(xlist, ylist, yawlist):
21+
cx = x + WBUBBLE_DIST*cos(yaw)
22+
cy = y + WBUBBLE_DIST*sin(yaw)
23+
24+
ids = kdtree.search_in_distance([cx, cy], WBUBBLE_R)
25+
26+
if not ids:
27+
continue
28+
29+
if not rectangle_check(x, y, yaw,
30+
[ox[i] for i in ids], [oy[i] for i in ids]):
31+
return False # collision
32+
33+
return True # no collision
34+
35+
def rectangle_check(x, y, yaw, ox, oy):
36+
# transform obstacles to base link frame
37+
c, s = cos(-yaw), sin(-yaw)
38+
for iox, ioy in zip(ox, oy):
39+
tx = iox - x
40+
ty = ioy - y
41+
rx = c*tx - s*ty
42+
ry = s*tx + c*ty
43+
44+
if not (rx > LF or rx < -LB or ry > W/2.0 or ry < -W/2.0):
45+
# print (x, y, yaw, iox, ioy, c, s, rx, ry)
46+
return False # no collision
47+
48+
return True # collision
49+
50+
51+
def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
52+
"""Plot arrow."""
53+
if not isinstance(x, float):
54+
for (ix, iy, iyaw) in zip(x, y, yaw):
55+
plot_arrow(ix, iy, iyaw)
56+
else:
57+
plt.arrow(x, y, length * cos(yaw), length * sin(yaw),
58+
fc=fc, ec=ec, head_width=width, head_length=width, alpha=0.4)
59+
# plt.plot(x, y)
60+
61+
def plot_car(x, y, yaw):
62+
car_color = '-k'
63+
c, s = cos(yaw), sin(yaw)
64+
65+
car_outline_x, car_outline_y = [], []
66+
for rx, ry in zip(VRX, VRY):
67+
tx = c*rx-s*ry + x
68+
ty = s*rx+c*ry + y
69+
car_outline_x.append(tx)
70+
car_outline_y.append(ty)
71+
72+
arrow_x, arrow_y, arrow_yaw = c*1.5+x, s*1.5+y, yaw
73+
plot_arrow(arrow_x, arrow_y, arrow_yaw)
74+
75+
plt.plot(car_outline_x, car_outline_y, car_color)
76+
77+
def pi_2_pi(angle):
78+
return (angle + pi) % (2 * pi) - pi
79+
80+
def move(x, y, yaw, distance, steer, L=WB):
81+
x += distance * cos(yaw)
82+
y += distance * sin(yaw)
83+
yaw += pi_2_pi(distance * tan(steer) / L) # distance/2
84+
85+
return x, y, yaw
86+
87+
if __name__ == '__main__':
88+
x, y, yaw = 0., 0., 1.
89+
plt.axis('equal')
90+
plot_car(x, y, yaw)
91+
plt.show()
92+

0 commit comments

Comments
 (0)