【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
目前机器人常用的搜路算法主要有这么几种,迪杰斯特拉算法、a*算法、贪心算法。每一种算法都有自己的场景和优势,可以灵活选择。但一般来说,客户的场景不算很复杂的话,搜路算法越简单越好,只要能达到最终的目标即可。对于特别复杂的场景,建议也不要通过底层算法的变更来解决业务的问题,这反而是得不偿失的。所以说,这三种算法,如果没有特别原因的话,最好都实现一下,这样方便fae的同学现场部署和实施。搜路算法本身只是一个拓扑算法,它帮助我们分析了目的地本身是否可达,但是机器人能不能过去,这就两说了。
下面,我们就看下a*算法是怎么实现的。
a*算法的核心其实就是F=G+H。其中F是总代价,G是起始点到当前点的代价,H是当前点到目标点的代价。两者加在一起,就是每次选择新插入点的标准。
a*算法的伪代码流程一般是这样的,
1)将开始点设置为p;
2)p点插入到封闭集当中;
3)搜寻p的所有邻接点,如果邻接点没有在开放集或者封闭集之中,则计算该点的F值,设置该邻接点的parent为p,将临界点插入到开放集当中;
4)判断开放集是否为空,如果为空,则搜路失败,否则继续;
5)从开放集挑出F数值最小的点,作为寻路的下一步起始点;
6)判断该点是否是终点,如果是,结束查找,否则继续;
7)跳转到3继续执行。
整个a*算法还是不算太复杂的。需要注意的地方只有一处,那就是3)中如果发现邻接点已经在开放集中,那需要重新计算它的G值。一旦发现当前G值更小,则需要同步更新parent、G值和F值。
算法的整个过程参考了一本ROS参考书上的python代码。大家可以实际下载下来查看一下效果。代码是用python编写,需要安装matplotlib库。
- import matplotlib.pyplot as plt
- import math
-
- class Node:
- def __init__(self, x, y, parent, cost, index):
- self.x =x
- self.y = y
- self.parent = parent
- self.cost = cost
- self.index = index
-
- def calc_path(goaln, closeset):
- rx,ry = [goaln.x], [goaln.y]
- print(closeset[-1])
- parentn = closeset[-1]
-
- while parentn != None:
- rx.append(parentn.x)
- ry.append(parentn.y)
- parentn = parentn.parent
- return rx, ry
-
- def astar_plan(sx, sy, gx, gy):
- ox,oy,xwidth,ywidth = map_generation()
-
- plt.figure('Astar algorithm')
- plt.plot(ox, oy, 'ks')
- plt.plot(sx, sy, 'bs')
- plt.plot(gx, gy, 'ro')
-
- motion = motion_model()
- openset, closeset = dict(), dict()
- sidx = sy*xwidth + sx
- gidx = gy*xwidth + gx
-
- starn = Node(sx, sy, None, 0, sidx)
- goaln = Node(gx, gy, None, 0, gidx)
- openset[sidx] = starn
- while 1:
- c_id = min(openset,key=lambda o:openset[o].cost + h_cost(openset[o], goaln))
- curnode = openset[c_id]
-
- if curnode.x == goaln.x and curnode.y == goaln.y:
- print('find goal')
- closeset[-1] = curnode
- break
- else:
- closeset[c_id] = curnode
- plt.plot(curnode.x, curnode.y, 'gx')
- if len(openset.keys())%10 == 0:
- plt.pause(0.01)
- del openset[c_id]
-
- for j in range(len(motion)):
- newnode = Node(curnode.x + motion[j][0],
- curnode.y + motion[j][1],
- curnode,
- curnode.cost + motion[j][2],
- c_id)
- n_id = index_calc(newnode, xwidth)
-
- if n_id in closeset:
- continue
-
- if node_verify(newnode, ox, oy):
- continue
-
- if n_id not in openset:
- openset[n_id] = newnode
- else:
- if openset[n_id].cost >= newnode.cost:
- openset[n_id] = newnode
-
- px,py = calc_path(goaln, closeset)
- return px,py
-
-
- def map_generation():
- ox,oy=[],[]
- for i in range(60):
- ox.append(i)
- oy.append(0)
-
- for i in range(60):
- ox.append(i)
- oy.append(60)
-
- for i in range(60):
- ox.append(0)
- oy.append(i)
-
- for i in range(60):
- ox.append(60)
- oy.append(i)
-
- for i in range(25):
- ox.append(i)
- oy.append(20)
-
- for i in range(40):
- ox.append(35)
- oy.append(i)
-
- for i in range(40):
- ox.append(50)
- oy.append(60-i)
-
- minx = min(ox)
- miny = min(oy)
- maxx = max(ox)
- maxy = max(oy)
- xwidth = maxx-minx
- ywidth = maxy-miny
- return ox,oy,xwidth,ywidth
-
- def motion_model():
- motion= [[1,0,1],
- [1,1,math.sqrt(2)],
- [1,-1,math.sqrt(2)],
- [0,1,1],
- [0,-1,1],
- [-1,1,math.sqrt(2)],
- [-1,0,1],
- [-1,-1,math.sqrt(2)]
- ]
- return motion
-
- def h_cost(node, goal):
- w = 1.0
- h = w * (abs(goal.x-node.x) + abs(goal.y-node.y))
- return h
-
- def index_calc(node, xwid):
- n_id = node.y * xwid + node.x
- return n_id
-
- def node_verify(node, ox, oy):
- if(node.x, node.y) in zip(ox, oy):
- return True
- else:
- return False
-
- def main():
- sx,sy=15,15
- gx,gy=55,50
- rx,ry=astar_plan(sx,sy,gx,gy)
- print(rx,ry)
- plt.plot(rx,ry,'r-',linewidth=3)
- plt.show()
-
- if __name__ == '__main__':
- main()
代码中motion_model表示了当前点周围8个点的行驶代价;node_verify则是判断当前点是否在障碍物上;astar_plan是所有算法真正的入口;而map_generation则构建了一个基本的搜寻场景。
运行效果如下所示,供大家参考。直接用python3 astar.py运行即可,
