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