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 ()
0 commit comments