1 # 版本1.2,2018—04—09 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 # print(map_grid) 22 map_grid[3, 3:8] = 0 23 map_grid[3:10, 7] = 0 24 map_grid[10, 3:8] = 0 25 map_grid[17, 13:17] = 0 26 map_grid[10:17, 13] = 0 27 map_grid[10, 13:17] = 0 28 map_grid[5, 2] = 7 29 map_grid[15, 15] = 5 30 # 画出定义的栅格地图 31 32 # plt.imshow(map_grid, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 33 # plt.colorbar() 34 # xlim(-1, 20) # 设置x轴范围 35 # ylim(-1, 20) # 设置y轴范围 36 # my_x_ticks = numpy.arange(0, 20, 1) 37 # my_y_ticks = numpy.arange(0, 20, 1) 38 # plt.xticks(my_x_ticks) 39 # plt.yticks(my_y_ticks) 40 # plt.grid(True) 41 # plt.show() 42 43 44 class AStar(object): 45 """ 46 创建一个A*算法类 47 """ 48 49 def __init__(self): 50 """ 51 初始化 52 """ 53 self.f = 0 54 self.g = 0 55 self.last_point = numpy.array([]) 56 self.current_point = numpy.array([]) # 当前目标点不断取得更新 57 self.open = numpy.array([[], []]) # 先创建一个空的open表 58 self.closed = numpy.array([[], []]) # 先创建一个空的closed表 59 # self.taboo = numpy.array([[], []]) # 创建一个禁忌表,用于放置不再搜索的点 60 self.start = numpy.array([5, 2]) # 起点坐标 61 self.goal = numpy.array([15, 15]) # 终点坐标 62 self.record_direction = numpy.array([[], [], [], []]) # 记录坐标点和方向 63 self.best_path_array = numpy.array([[], []]) # 回溯路径表 64 self.point_f = numpy.array([[], [], []]) # 记录每个点的f值 65 66 def h_value_tem(self, cur_p): 67 """ 68 计算拓展节点和终点的h值 69 :param cur_p:子搜索节点坐标 70 :return: 71 """ 72 h = (cur_p[0] - 15) ** 2 + (cur_p[1] - 15) ** 2 73 h = numpy.sqrt(h) # 计算h 74 return h 75 76 def g_value(self): 77 """ 78 累计的g值 79 :param x: 80 :param y: 81 :return: 82 """ 83 self.g = self.g + self.g_value_tem(self.current_point, self.last_point) 84 85 def g_value_tem(self, chl_p, cu_p): 86 """ 87 计算拓展节点和父节点的g值 88 其实也可以直接用1或者1.414代替 89 :param chl_p:子节点坐标 90 :param cu_p:父节点坐标,也就是self.current_point 91 :return:返回子节点到父节点的g值,但不是全局g值 92 """ 93 g1 = cu_p[0] - chl_p[0] 94 g2 = cu_p[1] - chl_p[1] 95 g = g1 ** 2 + g2 ** 2 96 g = numpy.sqrt(g) 97 return g 98 99 def f_value_tem(self, chl_p, cu_p): 100 """ 101 求出的是临时g值和h值的和,还需加上累计g值得到全局f值 102 :param chl_p: 父节点坐标 103 :param cu_p: 子节点坐标 104 :return: 105 """ 106 f = self.g_value_tem(chl_p, cu_p) + self.h_value_tem(cu_p) 107 return f 108 109 def min_f(self): 110 """ 111 找出open中f值最小的节点坐标,记录为current_point 112 :return:返回open表中最小值的位置索引和在map_grid中的坐标 113 对撞墙后的处理方式是,随机选择一个方向进行搜索 114 并且将open列表清零,不然一直是死循环 115 这种处理方式以后待改进!!!--1.1 116 1.2更新: 117 建立方向指针 118 """ 119 tem_f = [] # 创建一个记录f值的临时列表 120 for i in range(self.open.shape[1]): 121 # 计算拓展节点的全局f值 122 f_value = self.f_value_tem(self.current_point, self.open[:, i]) + self.g 123 tem_f.append(f_value) 124 index = tem_f.index(min(tem_f)) # 返回最小值索引 125 location = self.open[:, index] # 返回最小值坐标 126 print('打印位置索引和地图坐标:') 127 print(index, location) 128 return index, location 129 130 def child_point(self, x): 131 """ 132 拓展的子节点坐标 133 :param x: 父节点坐标 134 :return: 子节点存入open表,返回值是每一次拓展出的子节点数目,用于撞墙判断 135 当搜索的节点撞墙后,如果不加处理,会陷入死循环 136 """ 137 # tem_open = numpy.array([[], []]) # 统计拓展节点的临时数组 138 # tem_open_shape = 0 # 统计临时数组的长度 139 # 开始遍历周围8个节点 140 for j in range(-1, 2, 1): 141 for q in range(-1, 2, 1): 142 if j == 0 and q == 0: # 搜索到父节点去掉 143 continue 144 m = numpy.array([x[0] + j, x[1] + q]) 145 146 # print(map_grid[int(x[0] + j), int(x[1] + q)]) 147 if map_grid[int(m[0]), int(m[1])] == 0: # 搜索到障碍物去掉 148 continue 149 if m[0] < 0 or m[0] > 19 or m[1] < 0 or m[1] > 19: # 搜索点出了边界去掉 150 continue 151 152 self.direction(x, m) # 每产生一个子节点,记录一次方向 153 # 在closed表中,则去掉搜索点 154 b = self.judge_location(m, self.closed) 155 if b == 1: 156 numpy.delete(self.record_direction, -1, axis=1) 157 continue 158 159 # 在open表中,则去掉搜索点,但是需要更新方向指针和self.g值 160 # 而且只需要计算并更新self.g即可,此时建立一个比较g值的函数 161 a = self.judge_location(m, self.open) 162 if a == 1: 163 # 此时删除上面记录的方向并重新记录 164 numpy.delete(self.record_direction, -1, axis=1) 165 # self.direction(self.last_point, m) 166 167 continue 168 169 # # 在taboo表中,则去掉搜索点 170 # c = self.judge_location(x, j, q, self.taboo) 171 # if c == 1: 172 # continue 173 174 # tem_open = numpy.c_[tem_open, m] # 175 176 # tem_open_shape = tem_open.shape[1] # 求出tem_open的长度 177 178 self.open = numpy.c_[self.open, m] # 搜索出的子节点加入open 179 # print('打印第一次循环后的open:') 180 # print(self.open) 181 # return tem_open_shape 182 183 def judge_location(self, m, list_co): 184 """ 185 判断拓展点是否在open表或者closed表中 186 :return: 187 """ 188 jud = 0 189 for i in range(list_co.shape[1]): 190 191 if m[0] == list_co[0, i] and m[1] == list_co[1, i]: 192 193 jud = jud + 1 194 else: 195 jud = jud 196 # if a != 0: 197 # continue 198 return jud 199 200 def direction(self, father_point, son_point): 201 """ 202 建立每一个节点的方向,便于在closed表中选出最佳路径 203 非常重要的一步,不然画出的图像参考1.1版本 204 x记录子节点和父节点的x轴变化 205 y记录子节点和父节点的y轴变化 206 如(0,1)表示子节点在父节点的方向上变化0和1 207 :return: 208 """ 209 x = son_point[0] - father_point[0] 210 y = son_point[1] - father_point[1] 211 xy = [son_point[0], son_point[1], x, y] 212 self.record_direction = numpy.c_[self.record_direction, xy] 213 # return x, y 214 215 # def g_compare(self, father, son, son_grandson): 216 # """ 217 # g值比较函数 218 # :param father: 219 # :param son: 220 # :param son_grandson: 221 # :return: 222 # """ 223 # pass 224 225 def path_backtrace(self): 226 """ 227 回溯closed表中的最短路径 228 :return: 229 """ 230 best_path = [15, 15] # 回溯路径的初始化 231 self.best_path_array = numpy.array([[15], [15]]) 232 j = 0 233 while j <= self.record_direction.shape[1]: 234 for i in range(self.record_direction.shape[1]): 235 if best_path[0] == self.record_direction[0][i] and best_path[1] == self.record_direction[1][i]: 236 x = self.record_direction[0][i]-self.record_direction[2][i] 237 y = self.record_direction[1][i]-self.record_direction[3][i] 238 best_path = [x, y] 239 self.best_path_array = numpy.c_[self.best_path_array, best_path] 240 break # 如果已经找到,退出本轮循环,减少耗时 241 else: 242 continue 243 j = j+1 244 # return best_path_array 245 246 def main(self): 247 """ 248 main函数 249 :return: 250 """ 251 self.open = numpy.column_stack((self.open, self.start)) # 起点放入open 252 self.current_point = self.start # 起点放入当前点,作为父节点 253 # self.closed 254 ite = 1 255 while ite <= 200: 256 257 # open列表为空,退出 258 if self.open.shape[1] == 0: 259 print('没有搜索到路径!') 260 return 261 262 self.last_point = self.current_point # 上一个目标点不断取得更新 263 264 index, self.current_point = self.min_f() # 判断open表中f值 265 print('检验第%s次当前点坐标' % ite) 266 print(self.current_point) 267 268 # 选取open表中最小f值的节点作为best,放入closed表 269 self.closed = numpy.c_[self.closed, self.current_point] 270 271 if self.current_point[0] == 15 and self.current_point[1] == 15: # 如果best是目标点,退出 272 print('搜索成功!') 273 return 274 275 # tem_open_shape = self.child_point(self.current_point) # 生成子节点并判断数目 276 self.child_point(self.current_point) # 生成子节点并判断数目 277 278 self.open = delete(self.open, index, axis=1) # 删除open中最优点 279 280 # if tem_open_shape == 0: 281 # self.closed = delete(self.closed, -1, axis=1) # 删除closed中不合理的点 282 # self.taboo = numpy.c_[self.taboo, self.current_point] # 将撞墙点加入禁忌表 283 # self.current_point = last_point 284 # 285 # continue 286 287 self.g_value() 288 289 # print(self.open) 290 291 ite = ite+1 292 293 294 class MAP(object): 295 """ 296 画出地图 297 """ 298 def draw_init_map(self): 299 """ 300 画出起点终点图 301 :return: 302 """ 303 plt.imshow(map_grid, 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_path_open(self, a): 315 """ 316 画出open表中的坐标点图 317 :return: 318 """ 319 map_open = copy.deepcopy(map_grid) 320 for i in range(a.closed.shape[1]): 321 x = a.closed[:, i] 322 323 map_open[int(x[0]), int(x[1])] = 1 324 325 plt.imshow(map_open, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 326 # plt.colorbar() 327 xlim(-1, 20) # 设置x轴范围 328 ylim(-1, 20) # 设置y轴范围 329 my_x_ticks = numpy.arange(0, 20, 1) 330 my_y_ticks = numpy.arange(0, 20, 1) 331 plt.xticks(my_x_ticks) 332 plt.yticks(my_y_ticks) 333 plt.grid(True) 334 # plt.show() 335 336 def draw_path_closed(self, a): 337 """ 338 画出closed表中的坐标点图 339 :return: 340 """ 341 print('打印closed长度:') 342 print(a.closed.shape[1]) 343 map_closed = copy.deepcopy(map_grid) 344 for i in range(a.closed.shape[1]): 345 x = a.closed[:, i] 346 347 map_closed[int(x[0]), int(x[1])] = 5 348 349 plt.imshow(map_closed, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 350 # plt.colorbar() 351 xlim(-1, 20) # 设置x轴范围 352 ylim(-1, 20) # 设置y轴范围 353 my_x_ticks = numpy.arange(0, 20, 1) 354 my_y_ticks = numpy.arange(0, 20, 1) 355 plt.xticks(my_x_ticks) 356 plt.yticks(my_y_ticks) 357 plt.grid(True) 358 # plt.show() 359 360 def draw_direction_point(self, a): 361 """ 362 从终点开始,根据记录的方向信息,画出搜索的路径图 363 :return: 364 """ 365 print('打印direction长度:') 366 print(a.record_direction.shape[1]) 367 map_direction = copy.deepcopy(map_grid) 368 for i in range(a.best_path_array.shape[1]): 369 x = a.best_path_array[:, i] 370 371 map_direction[int(x[0]), int(x[1])] = 6 372 373 plt.imshow(map_direction, cmap=plt.cm.hot, interpolation='nearest', vmin=0, vmax=10) 374 # plt.colorbar() 375 xlim(-1, 20) # 设置x轴范围 376 ylim(-1, 20) # 设置y轴范围 377 my_x_ticks = numpy.arange(0, 20, 1) 378 my_y_ticks = numpy.arange(0, 20, 1) 379 plt.xticks(my_x_ticks) 380 plt.yticks(my_y_ticks) 381 plt.grid(True) 382 383 def draw_three_axes(self, a): 384 """ 385 将三张图画在一个figure中 386 :return: 387 """ 388 plt.figure() 389 ax1 = plt.subplot(221) 390 391 ax2 = plt.subplot(222) 392 ax3 = plt.subplot(223) 393 ax4 = plt.subplot(224) 394 plt.sca(ax1) 395 self.draw_init_map() 396 plt.sca(ax2) 397 self.draw_path_open(a) 398 plt.sca(ax3) 399 self.draw_path_closed(a) 400 plt.sca(ax4) 401 self.draw_direction_point(a) 402 403 plt.show() 404 405 406 if __name__ == '__main__': 407 408 a1 = AStar() 409 a1.main() 410 a1.path_backtrace() 411 m1 = MAP() 412 m1.draw_three_axes(a1)