1.2

  1 # 版本1.220180409
  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         如(01)表示子节点在父节点的方向上变化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)
原文地址:https://www.cnblogs.com/yangmingustb/p/8783278.html