1.概览
可以对比不同算法的小动画 PathFinding.js (qiao.github.io)
- 工作空间规划
- 机器人有不同的形状和大小
- 碰撞检测需要了解机器人的几何形状,耗时且难度大
- 我们希望将机器人表示为点,只需要把工作空间转换为配置空间C-obstacle,将原始的空间膨胀,这是一次性的
- C-space R3空间
- C-obstacle 膨胀后的障碍空障碍
- C-space = C-obstacle ∪ C-free
- 图搜索算法的逻辑总是在这个框架下
- 维护一个容器,该容器记录所有准备去访问的节点,同时一个有一个容器记录了所有已经访问的节点,避免形成环
- 这个容器刚开始是空的,我们初始化放入起始节点
- Loop
- 访问一个节点:在容器里根据一种指标弹出一个节点
- 扩展:发现该节点所有可能的邻居节点
- 插入:将这些邻居节点插入到容器中
- End Loop
- 容器已经全空就达到了终止条件
2.BFS和DFS
广度优先搜索和深度优先搜索
- 深度优先算法:总是移除容器中最深层的节点(总是把尝试一直把一条路走到底)
- 首先S进入栈
- S出栈,发现S的周围元素 d e p,S的周围元素 d e p 进入栈
- d出栈,发现d的周围元素 b c e,d的周围元素 b c e 进入栈
- 广度优先算法:总是移除容器中最浅层的节点(一层一层的搜索)
- S 进入队列
- S 弹出队列,发现S的周围元素 d e p,S的周围元素 d e p 进入队列
- p 弹出队列,发现p的周围元素 q,p的周围元素 q 进入队列
- 两种算法DFS总是一条路走到黑,所以它找到的路径不是最短路径
3.贪心算法
- DFS或者BFS是通过先入或后入来判定从容器中拿出哪个节点
- 贪心算法是一种启发式搜索,通过认为定义的”最短“判定从容器中弹出哪个节点(不一定有全局最优解)
4.Dijkstra算法
DFS和BFS都没有考虑边的代价问题,Dijkstra算法如果不考虑边的代价实际上就退化成广度优先算法
- g(n):从起始节点到当前节点n累计的最佳成本
- 更新节点n相邻节点的代价(有可能当前节点n使得它周围的节点m代价降低)
- 保证已扩展/访问的节点开销最小
以下图为例跑一遍流程
- S弹出队列,发现S周围节点 d e p,S的周围节点根据代价排序进入优先级队列 p d e
- p弹出队列,发现p周围节点 q,q的周围节点 q 进入优先级队列
- e弹出队列,发现e周围节点 h r,e的周围节点根据代价排序进入优先级队列 r h
算法伪代码逻辑
- 维护priority queue存储所有已访问的节点
- 将初始节点Xs加入到队列
- 标记 g(Xs)=0,其余的节点代价为无穷大
- Loop
- if the queue is empty, return FALSE; break;
- Remove the node "n" with the lowest g(n) from the priority queue
- mark node "n" as expanded
- if the node "n" is the goal state, return TRUE; break;
- For all unexpanded neighbors "m" of node "n"
- if g(m) = infinite
- if g(m) = g(n) + COSTnm
- push node "m" into queue
- if g(m) > g(n) + COSTnm
- g(m) = g(n) + COSTnm
- if g(m) = infinite
- End Loop
5.A*算法
- 对Dijkstra算法进行优化,加上贪心算法的思想就可以得到A*算法
- h(n):从当前节点n到目标状态的估计最小代价(即目标代价)
- g(n):从起始节点到当前节点n累计的最佳成本
- Weighted A*:f(n):g(n)+εh(n), ε>1:经过节点n,从起始节点到目标节点估计的最小代价
- 如果代价估计大于实际代价,最终找到的路径也不一定是最优路径。ε越大越接近于贪心算法,ε=1就是A*算法,ε=0就是Dijkstra算法
- 算法逻辑与Dijkstra完全相同,但排序将g(n)修改为f(n)
- 以下图为例解释,每个节点的估计代价h已经给出
- 弹出S节点,发现S周围节点a,将a加入优先队列
- 弹出a节点,发现a周围节点b d e,通过计算当前代价和估计代价得到应该按照 d e b 加入优先队列
6.A*算法的优化
6.1 The Best Heuristic
为了得到最优解,我们必须满足估计最小代价和实际最小代价的关系为h(n)≤h*(n),但是如果h(n)远远小于真实最小代价的话,就会导致右图所示的情况,进行很多无效的搜索,因为更小的h(n)使得算法误以为会有一条更有的路径
但实际上这种规格化的网格只能水平走或斜45度走。所以这里不使用欧氏距离,而是使用更标准的算法会更快速的找到目标
6.2 Tie Breaker
因为路径中大量对称的点都会有相同的F(n)值,这些值都需要不断的去遍历进行搜索,这会导致它搜索很多无效的点
核心思想:想办法打破这种平衡,比如计算一个常数p让每个点的F(n)值不太相同
具体做法为F(n) = g(n) + h(n)*(1+p) ,不过这种做法也可能使得h(n)>h*(n),使得我们估计最小代价大于实际最小代价,无法找到最优解
也可以如果有相同的f(n),则我们选取更小的h(n)值弹出容器
或者是我们添加一种倾向性,让他倾向于去行走一条直线,在无障碍的情况下是很好的,但是有障碍的情况下缺不太符合人的直观体会
7. Jump Point Search
先看一下JPS算法和A*算法搜索过程的对比
JPS算法思想1:Look Ahead Rule
- 劣质邻居(inferior neighbors)灰色节点:到达这些节点时,没有x节点的路径更优
- 父节点为4时,如果此时由x到1,它的消耗是4->x->1,不如x->1
- 父节点为4时,如果此时由x到2,它的消耗是4->x->2,不如4->2
- 父节点为4时,如果此时由x到3,它的消耗是4->x->3,和4->2->3相同,经过x的路径没有更优
- 自然邻居(natural neighbors)白色节点:我们只需要考虑通过白色节点
- 强制邻居(Forced Neighbors)黑色节点:墙
JSP算法思想2:Jumping Rules
- 直线规则:如果周围没有障碍物,就是Look Ahead Rule情况1,它只需要继续走直线
- 当他走到y节点时,是Look Ahead Rule情况3
- 对角线规则:如果周围没有障碍物,就是Look Ahead Rule情况2,它可以选择三条路线,优先选择水平,然后选择垂直,最后选择对角线运动。
标签:容器,队列,算法,DFS,BFS,Dijkstra,周围,代价,节点 From: https://www.cnblogs.com/stux/p/18083092