1414
1515show_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
203204def 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