1 # 版本1.3,2018—04—11 2 # 所有節點的g值並沒有初始化為無窮大 3 # 當兩個子節點的f值一樣時,程序選擇最先搜索到的一個作為父節點加入closed 4 # 對相同數值的不同對待,導致不同版本的A*算法找到等長的不同路徑 5 # 最后closed表中的節點很多,如何找出最優的一條路徑 6 # 撞牆之后產生較多的節點會加入closed表,此時開始刪除closed表中不合理的節點,1.1版本的思路 7 # 1.2版本思路,建立每一個節點的方向指針,指向f值最小的上個節點 8 # 參考《無人駕駛概論》、《基於A*算法的移動機器人路徑規划》王淼馳,《人工智能及應用》魯斌 9 10 11 import numpy 12 from pylab import * 13 import copy 14 15 # 定義一個含有障礙物的20×20的柵格地圖 16 # 10表示可通行點 17 # 0表示障礙物 18 # 7表示起點 19 # 5表示終點 20 map_grid = numpy.full((20, 20), int(10), dtype=numpy.int8) 21 map_grid[3, 3:8] = 0 22 map_grid[3:10, 7] = 0 23 map_grid[10, 3:8] = 0 24 map_grid[17, 13:17] = 0 25 map_grid[10:17, 13] = 0 26 map_grid[10, 13:17] = 0 27 map_grid[5, 2] = 7 28 map_grid[15, 15] = 5 29 30 31 class AStar(object): 32 """ 33 創建一個A*算法類 34 """ 35 36 def __init__(self): 37 """ 38 初始化 39 """ 40 # self.g = 0 # g初始化為0 41 self.start = numpy.array([5, 2]) # 起點坐標 42 self.goal = numpy.array([15, 15]) # 終點坐標 43 self.open = numpy.array([[], [], [], [], [], []]) # 先創建一個空的open表, 記錄坐標,方向,g值,f值 44 self.closed = numpy.array([[], [], [], [], [], []]) # 先創建一個空的closed表 45 self.best_path_array = numpy.array([[], []]) # 回溯路徑表 46 47 def h_value_tem(self, son_p): 48 """ 49 計算拓展節點和終點的h值 50 :param son_p:子搜索節點坐標 51 :return: 52 """ 53 h = (son_p[0] - self.goal[0]) ** 2 + (son_p[1] - self.goal[1]) ** 2 54 h = numpy.sqrt(h) # 計算h 55 return h 56 57 # def g_value_tem(self, son_p, father_p): 58 # """ 59 # 計算拓展節點和父節點的g值 60 # 其實也可以直接用1或者1.414代替 61 # :param son_p:子節點坐標 62 # :param father_p:父節點坐標,也就是self.current_point 63 # :return:返回子節點到父節點的g值,但不是全局g值 64 # """ 65 # g1 = father_p[0] - son_p[0] 66 # g2 = father_p[1] - son_p[1] 67 # g = g1 ** 2 + g2 ** 2 68 # g = numpy.sqrt(g) 69 # return g 70 71 def g_accumulation(self, son_point, father_point): 72 """ 73 累計的g值 74 :return: 75 """ 76 g1 = father_point[0] - son_point[0] 77 g2 = father_point[1] - son_point[1] 78 g = g1 ** 2 + g2 ** 2 79 g = numpy.sqrt(g) + father_point[4] # 加上累計的g值 80 return g 81 82 def f_value_tem(self, son_p, father_p): 83 """ 84 求出的是臨時g值和h值加上累計g值得到全局f值 85 :param father_p: 父節點坐標 86 :param son_p: 子節點坐標 87 :return:f 88 """ 89 f = self.g_accumulation(son_p, father_p) + self.h_value_tem(son_p) 90 return f 91 92 def child_point(self, x): 93 """ 94 拓展的子節點坐標 95 :param x: 父節點坐標 96 :return: 子節點存入open表,返回值是每一次拓展出的子節點數目,用於撞牆判斷 97 當搜索的節點撞牆后,如果不加處理,會陷入死循環 98 """ 99 # 開始遍歷周圍8個節點 100 for j in range(-1, 2, 1): 101 for q in range(-1, 2, 1): 102 103 if j == 0 and q == 0: # 搜索到父節點去掉 104 continue 105 m = [x[0] + j, x[1] + q] 106 print(m) 107 if m[0] < 0 or m[0] > 19 or m[1] < 0 or m[1] > 19: # 搜索點出了邊界去掉 108 continue 109 110 if map_grid[int(m[0]), int(m[1])] == 0: # 搜索到障礙物去掉 111 continue 112 113 114 115 record_g = self.g_accumulation(m, x) 116 record_f = self.f_value_tem(m, x) # 計算每一個節點的f值 117 118 x_direction, y_direction = self.direction(x, m) # 每產生一個子節點,記錄一次方向 119 120 para = [m[0], m[1], x_direction, y_direction, record_g, record_f] # 將參數匯總一下 121 print(para) 122 123 # 在open表中,則去掉搜索點,但是需要更新方向指針和self.g值 124 # 而且只需要計算並更新self.g即可,此時建立一個比較g值的函數 125 a, index = self.judge_location(m, self.open) 126 if a == 1: 127 # 說明open中已經存在這個點 128 129 if record_f <= self.open[5][index]: 130 self.open[5][index] = record_f 131 self.open[4][index] = record_g 132 self.open[3][index] = y_direction 133 self.open[2][index] = x_direction 134 135 continue 136 137 # 在closed表中,則去掉搜索點 138 b, index2 = self.judge_location(m, self.closed) 139 if b == 1: 140 141 if record_f <= self.closed[5][index2]: 142 self.closed[5][index2] = record_f 143 self.closed[4][index2] = record_g 144 self.closed[3][index2] = y_direction 145 self.closed[2][index2] = x_direction 146 self.closed = numpy.delete(self.closed, index2, axis=1) 147 self.open = numpy.c_[self.open, para] 148 continue 149 150 self.open = numpy.c_[self.open, para] # 參數添加到open中 151 print(self.open) 152 153 def judge_location(self, m, list_co): 154 """ 155 判斷拓展點是否在open表或者closed表中 156 :return:返回判斷是否存在,和如果存在,那么存在的位置索引 157 """ 158 jud = 0 159 index = 0 160 for i in range(list_co.shape[1]): 161 162 if m[0] == list_co[0, i] and m[1] == list_co[1, i]: 163 164 jud = jud + 1 165 166 index = i 167 break 168 else: 169 jud = jud 170 # if a != 0: 171 # continue 172 return jud, index 173 174 def direction(self, father_point, son_point): 175 """ 176 建立每一個節點的方向,便於在closed表中選出最佳路徑 177 非常重要的一步,不然畫出的圖像參考1.1版本 178 x記錄子節點和父節點的x軸變化 179 y記錄子節點和父節點的y軸變化 180 如(0,1)表示子節點在父節點的方向上變化0和1 181 :return: 182 """ 183 x = son_point[0] - father_point[0] 184 y = son_point[1] - father_point[1] 185 return x, y 186 187 def path_backtrace(self): 188 """ 189 回溯closed表中的最短路徑 190 :return: 191 """ 192 best_path = [15, 15] # 回溯路徑的初始化 193 self.best_path_array = numpy.array([[15], [15]]) 194 j = 0 195 while j <= self.closed.shape[1]: 196 for i in range(self.closed.shape[1]): 197 if best_path[0] == self.closed[0][i] and best_path[1] == self.closed[1][i]: 198 x = self.closed[0][i]-self.closed[2][i] 199 y = self.closed[1][i]-self.closed[3][i] 200 best_path = [x, y] 201 self.best_path_array = numpy.c_[self.best_path_array, best_path] 202 break # 如果已經找到,退出本輪循環,減少耗時 203 else: 204 continue 205 j = j+1 206 # return best_path_array 207 208 def main(self): 209 """ 210 main函數 211 :return: 212 """ 213 best = self.start # 起點放入當前點,作為父節點 214 h0 = self.h_value_tem(best) 215 init_open = [best[0], best[1], 0, 0, 0, h0] # 將方向初始化為(0,0),g_init=0,f值初始化h0 216 self.open = numpy.column_stack((self.open, init_open)) # 起點放入open,open初始化 217 218 ite = 1 # 設置迭代次數小於200,防止程序出錯無限循環 219 while ite <= 1000: 220 221 # open列表為空,退出 222 if self.open.shape[1] == 0: 223 print('沒有搜索到路徑!') 224 return 225 226 self.open = self.open.T[numpy.lexsort(self.open)].T # open表中最后一行排序(聯合排序) 227 228 # 選取open表中最小f值的節點作為best,放入closed表 229 230 best = self.open[:, 0] 231 print('檢驗第%s次當前點坐標*******************' % ite) 232 print(best) 233 self.closed = numpy.c_[self.closed, best] 234 235 if best[0] == 15 and best[1] == 15: # 如果best是目標點,退出 236 print('搜索成功!') 237 return 238 239 self.child_point(best) # 生成子節點並判斷數目 240 print(self.open) 241 self.open = numpy.delete(self.open, 0, axis=1) # 刪除open中最優點 242 243 # print(self.open) 244 245 ite = ite+1 246 247 248 class MAP(object): 249 """ 250 畫出地圖 251 """ 252 def draw_init_map(self): 253 """ 254 畫出起點終點圖 255 :return: 256 """ 257 plt.imshow(map_grid, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 258 # plt.colorbar() 259 xlim(-1, 20) # 設置x軸范圍 260 ylim(-1, 20) # 設置y軸范圍 261 my_x_ticks = numpy.arange(0, 20, 1) 262 my_y_ticks = numpy.arange(0, 20, 1) 263 plt.xticks(my_x_ticks) 264 plt.yticks(my_y_ticks) 265 plt.grid(True) 266 # plt.show() 267 268 def draw_path_open(self, a): 269 """ 270 畫出open表中的坐標點圖 271 :return: 272 """ 273 map_open = copy.deepcopy(map_grid) 274 for i in range(a.closed.shape[1]): 275 x = a.closed[:, i] 276 277 map_open[int(x[0]), int(x[1])] = 1 278 279 plt.imshow(map_open, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 280 # plt.colorbar() 281 xlim(-1, 20) # 設置x軸范圍 282 ylim(-1, 20) # 設置y軸范圍 283 my_x_ticks = numpy.arange(0, 20, 1) 284 my_y_ticks = numpy.arange(0, 20, 1) 285 plt.xticks(my_x_ticks) 286 plt.yticks(my_y_ticks) 287 plt.grid(True) 288 # plt.show() 289 290 def draw_path_closed(self, a): 291 """ 292 畫出closed表中的坐標點圖 293 :return: 294 """ 295 print('打印closed長度:') 296 print(a.closed.shape[1]) 297 map_closed = copy.deepcopy(map_grid) 298 for i in range(a.closed.shape[1]): 299 x = a.closed[:, i] 300 301 map_closed[int(x[0]), int(x[1])] = 5 302 303 plt.imshow(map_closed, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 304 # plt.colorbar() 305 xlim(-1, 20) # 設置x軸范圍 306 ylim(-1, 20) # 設置y軸范圍 307 my_x_ticks = numpy.arange(0, 20, 1) 308 my_y_ticks = numpy.arange(0, 20, 1) 309 plt.xticks(my_x_ticks) 310 plt.yticks(my_y_ticks) 311 plt.grid(True) 312 # plt.show() 313 314 def draw_direction_point(self, a): 315 """ 316 從終點開始,根據記錄的方向信息,畫出搜索的路徑圖 317 :return: 318 """ 319 print('打印direction長度:') 320 print(a.best_path_array.shape[1]) 321 map_direction = copy.deepcopy(map_grid) 322 for i in range(a.best_path_array.shape[1]): 323 x = a.best_path_array[:, i] 324 325 map_direction[int(x[0]), int(x[1])] = 6 326 327 plt.imshow(map_direction, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 328 # plt.colorbar() 329 xlim(-1, 20) # 設置x軸范圍 330 ylim(-1, 20) # 設置y軸范圍 331 my_x_ticks = numpy.arange(0, 20, 1) 332 my_y_ticks = numpy.arange(0, 20, 1) 333 plt.xticks(my_x_ticks) 334 plt.yticks(my_y_ticks) 335 plt.grid(True) 336 337 def draw_three_axes(self, a): 338 """ 339 將三張圖畫在一個figure中 340 :return: 341 """ 342 plt.figure() 343 ax1 = plt.subplot(221) 344 345 ax2 = plt.subplot(222) 346 ax3 = plt.subplot(223) 347 ax4 = plt.subplot(224) 348 plt.sca(ax1) 349 self.draw_init_map() 350 plt.sca(ax2) 351 self.draw_path_open(a) 352 plt.sca(ax3) 353 self.draw_path_closed(a) 354 plt.sca(ax4) 355 self.draw_direction_point(a) 356 357 plt.show() 358 359 360 if __name__ == '__main__': 361 362 a1 = AStar() 363 a1.main() 364 a1.path_backtrace() 365 m1 = MAP() 366 m1.draw_three_axes(a1)
