最完整路径规划实战:13种搜索算法动画对比与实现指南
引言:你还在为路径规划算法选择发愁吗?
在机器人导航(Robot Navigation)、自动驾驶(Autonomous Driving)和游戏开发(Game Development)等领域,路径规划(Path Planning)是核心技术之一。面对复杂多变的环境,如何快速找到一条最优路径,同时兼顾实时性和鲁棒性,一直是工程师和研究者面临的挑战。
本文将深入剖析13种主流搜索算法(Search Algorithm),通过动画对比和代码实现,帮助你彻底理解各类算法的优缺点及适用场景。读完本文,你将能够:
- 掌握A*、Dijkstra、D* Lite等经典搜索算法的核心原理
- 通过动态可视化对比不同算法的搜索过程和路径结果
- 学会如何根据实际场景选择最合适的路径规划算法
- 基于PathPlanning项目框架快速实现和测试各类算法
算法全景:搜索算法分类与应用场景
路径搜索算法可以根据不同的标准进行分类,常见的分类方式包括:
按搜索策略分类
13种搜索算法特性对比表
| 算法名称 | 完备性 | 最优性 | 时间复杂度 | 空间复杂度 | 启发函数 | 动态环境适应 | 典型应用场景 |
|---|---|---|---|---|---|---|---|
| BFS | 是 | 否 | O(b^d) | O(b^d) | 无 | 差 | 网格路径规划 |
| DFS | 否 | 否 | O(b^m) | O(bm) | 无 | 差 | 迷宫探索 |
| Dijkstra | 是 | 是 | O(b^d) | O(b^d) | 无 | 差 | 静态环境最短路径 |
| Best-First | 否 | 否 | O(b^d) | O(b^d) | 有 | 差 | 快速粗略路径 |
| A* | 是 | 是 | O(b^d) | O(b^d) | 有 | 差 | 静态环境最优路径 |
| Bidirectional A* | 是 | 是 | O(b^(d/2)) | O(b^(d/2)) | 有 | 差 | 长距离路径规划 |
| ARA* | 是 | 近似 | O(εb^d) | O(b^d) | 有 | 中 | 实时性要求高的场景 |
| D* | 是 | 是 | O(E) | O(V) | 有 | 好 | 动态障碍物环境 |
| D* Lite | 是 | 是 | O(log n) | O(n) | 有 | 优 | 移动机器人导航 |
| LPA* | 是 | 是 | O(log n) | O(n) | 有 | 优 | 动态路径重规划 |
| LRTA* | 是 | 渐近最优 | O(b^d) | O(b) | 有 | 中 | 未知环境探索 |
| RTAA* | 是 | 近似 | O(1) | O(1) | 有 | 优 | 实时响应系统 |
| Anytime D* | 是 | 近似到最优 | 动态变化 | 动态变化 | 有 | 优 | 高可靠性系统 |
注:b为分支因子,d为解的深度,m为最大深度,ε为次优因子,b为分支因子,n为节点数,E为边数,V为顶点数
核心原理:从基础到进阶的算法解析
1. 无信息搜索:Dijkstra算法深度剖析
Dijkstra算法是一种经典的无信息搜索算法,它通过优先扩展代价最小的节点来找到最优路径。该算法的核心思想是维护一个优先队列,每次从队列中选择代价最小的节点进行扩展。
class Dijkstra(AStar):
"""Dijkstra set the cost as the priority"""
def searching(self):
self.PARENT[self.s_start] = self.s_start
self.g[self.s_start] = 0
self.g[self.s_goal] = math.inf
heapq.heappush(self.OPEN, (0, self.s_start)) # 以代价为优先级
while self.OPEN:
_, s = heapq.heappop(self.OPEN)
self.CLOSED.append(s)
if s == self.s_goal:
break
for s_n in self.get_neighbor(s):
new_cost = self.g[s] + self.cost(s, s_n)
if s_n not in self.g:
self.g[s_n] = math.inf
if new_cost < self.g[s_n]: # 更新代价条件
self.g[s_n] = new_cost
self.PARENT[s_n] = s
heapq.heappush(self.OPEN, (new_cost, s_n)) # 按新代价入队
return self.extract_path(self.PARENT), self.CLOSED
Dijkstra算法的搜索过程可以用以下流程图表示:
2. 有信息搜索:A*算法的核心创新
A*算法通过引入启发函数(Heuristic Function),在Dijkstra算法的基础上进行了优化。它使用f(n) = g(n) + h(n)作为优先级,其中g(n)是从起点到节点n的实际代价,h(n)是从节点n到目标节点的估计代价。
class AStar:
def searching(self):
self.PARENT[self.s_start] = self.s_start
self.g[self.s_start] = 0
self.g[self.s_goal] = math.inf
heapq.heappush(self.OPEN, (self.f_value(self.s_start), self.s_start))
while self.OPEN:
_, s = heapq.heappop(self.OPEN)
self.CLOSED.append(s)
if s == self.s_goal: # 终止条件
break
for s_n in self.get_neighbor(s):
new_cost = self.g[s] + self.cost(s, s_n)
if s_n not in self.g:
self.g[s_n] = math.inf
if new_cost < self.g[s_n]: # 更新代价条件
self.g[s_n] = new_cost
self.PARENT[s_n] = s
heapq.heappush(self.OPEN, (self.f_value(s_n), s_n))
return self.extract_path(self.PARENT), self.CLOSED
def f_value(self, s):
"""f = g + h (g: 实际代价, h: 启发函数值)"""
return self.g[s] + self.heuristic(s)
def heuristic(self, s):
"""计算启发函数值"""
if self.heuristic_type == "manhattan":
return abs(self.s_goal[0] - s[0]) + abs(self.s_goal[1] - s[1])
else: # euclidean
return math.hypot(self.s_goal[0] - s[0], self.s_goal[1] - s[1])
A*算法与Dijkstra算法的关键区别在于优先级函数的选择:
- Dijkstra算法:优先级 = g(n)(实际代价)
- A*算法:优先级 = g(n) + h(n)(实际代价+估计代价)
- Best-First算法:优先级 = h(n)(仅估计代价)
3. 动态环境搜索:D* Lite算法的革新
D* Lite算法是一种高效的动态路径规划算法,特别适用于机器人在未知或动态变化环境中的导航。它通过维护一个优先队列和双向指针,能够在环境发生变化时快速重新规划路径。
项目实战:PathPlanning框架使用指南
1. 环境准备与项目结构
PathPlanning项目提供了一个完整的路径规划算法实现框架,支持2D和3D环境下的多种搜索算法。
项目结构:
PathPlanning/
├── CurvesGenerator/ # 曲线生成算法
├── Sampling_based_Planning/ # 采样-based规划算法
└── Search_based_Planning/ # 搜索-based规划算法
├── Search_2D/ # 2D搜索算法实现
│ ├── ARAstar.py # ARA*算法
│ ├── Anytime_D_star.py # Anytime D*算法
│ ├── Astar.py # A*算法
│ ├── Best_First.py # Best-First算法
│ ├── Bidirectional_a_star.py # 双向A*算法
│ ├── D_star.py # D*算法
│ ├── D_star_Lite.py # D* Lite算法
│ ├── Dijkstra.py # Dijkstra算法
│ ├── LPAstar.py # LPA*算法
│ ├── LRTAstar.py # LRTA*算法
│ ├── RTAAStar.py # RTAA*算法
│ ├── bfs.py # BFS算法
│ ├── dfs.py # DFS算法
│ ├── env.py # 环境定义
│ ├── plotting.py # 可视化工具
│ └── queue.py # 队列数据结构
├── Search_3D/ # 3D搜索算法实现
└── gif/ # 算法动态演示GIF
安装与配置:
# 克隆项目仓库
git clone https://gitcode.com/gh_mirrors/pa/PathPlanning
# 安装依赖
cd PathPlanning
pip install -r requirements.txt
2. 快速上手:实现你的第一个路径规划程序
以下是使用PathPlanning框架实现A*算法路径规划的简单示例:
from Search_2D import Astar, plotting, env
def main():
# 定义起点和终点
s_start = (5, 5) # 起点坐标
s_goal = (45, 25) # 终点坐标
# 创建A*算法实例,使用欧几里得距离作为启发函数
astar = Astar.AStar(s_start, s_goal, "euclidean")
# 创建绘图实例
plot = plotting.Plotting(s_start, s_goal)
# 执行路径搜索
path, visited = astar.searching()
# 动画演示搜索过程和结果路径
plot.animation(path, visited, "A* Algorithm")
if __name__ == '__main__':
main()
3. 算法对比实验:在复杂环境中测试不同算法
为了直观对比不同算法的性能,我们在包含多种障碍物的复杂环境中进行测试:
import time
import numpy as np
import matplotlib.pyplot as plt
from Search_2D import Astar, Dijkstra, Best_First, D_star_Lite
# 定义测试环境和参数
s_start = (5, 5)
s_goal = (45, 45)
obstacle_density = [0.1, 0.2, 0.3, 0.4, 0.5] # 障碍物密度梯度
algorithms = {
"A*": Astar.AStar(s_start, s_goal, "euclidean"),
"Dijkstra": Dijkstra.Dijkstra(s_start, s_goal, "None"),
"Best-First": Best_First.BestFirst(s_start, s_goal, "euclidean"),
"D* Lite": D_star_Lite.DStarLite(s_start, s_goal, "euclidean")
}
# 存储测试结果
results = {name: {"time": [], "path_length": [], "nodes_visited": []} for name in algorithms}
# 执行测试
for density in obstacle_density:
# 创建不同障碍物密度的环境
current_env = env.Env(obstacle_density=density)
for name, algo in algorithms.items():
# 设置当前环境
algo.Env = current_env
algo.obs = current_env.obs
# 记录开始时间
start_time = time.time()
# 执行路径搜索
path, visited = algo.searching()
# 计算指标
execution_time = time.time() - start_time
path_length = sum(algo.cost(path[i], path[i+1]) for i in range(len(path)-1))
# 存储结果
results[name]["time"].append(execution_time)
results[name]["path_length"].append(path_length)
results[name]["nodes_visited"].append(len(visited))
# 绘制结果图表
plt.figure(figsize=(15, 5))
plt.subplot(1, 3, 1)
for name, data in results.items():
plt.plot(obstacle_density, data["time"], marker='o', label=name)
plt.xlabel('Obstacle Density')
plt.ylabel('Execution Time (s)')
plt.title('Algorithm Execution Time vs Obstacle Density')
plt.legend()
plt.subplot(1, 3, 2)
for name, data in results.items():
plt.plot(obstacle_density, data["path_length"], marker='o', label=name)
plt.xlabel('Obstacle Density')
plt.ylabel('Path Length')
plt.title('Path Length vs Obstacle Density')
plt.legend()
plt.subplot(1, 3, 3)
for name, data in results.items():
plt.plot(obstacle_density, data["nodes_visited"], marker='o', label=name)
plt.xlabel('Obstacle Density')
plt.ylabel('Nodes Visited')
plt.title('Nodes Visited vs Obstacle Density')
plt.legend()
plt.tight_layout()
plt.show()
4. 动态环境路径规划:D* Lite算法实战
D* Lite算法特别适用于动态环境,以下是一个障碍物移动时的路径重规划示例:
from Search_2D import D_star_Lite, plotting, env
import time
def dynamic_obstacle_demo():
s_start = (5, 5)
s_goal = (45, 25)
# 初始化D* Lite算法
d_star_lite = D_star_Lite.DStarLite(s_start, s_goal, "euclidean")
plot = plotting.Plotting(s_start, s_goal)
# 初始路径规划
path, visited = d_star_lite.searching()
plot.animation(path, visited, "D* Lite - Initial Path")
# 模拟动态障碍物出现
time.sleep(2) # 等待初始动画完成
# 更新环境:添加新障碍物
new_obstacles = [(20, 15), (21, 15), (22, 15), (23, 15), (24, 15)]
d_star_lite.obs.extend(new_obstacles)
# 重新规划路径
path, visited = d_star_lite.replan()
plot.animation(path, visited, "D* Lite - Path After Obstacle Appearance")
if __name__ == '__main__':
dynamic_obstacle_demo()
高级应用:算法优化与特定场景适配
1. 启发函数设计:提升A*算法性能的关键
A*算法的性能很大程度上依赖于启发函数的选择。一个好的启发函数应该:
- 可采纳性:永远不高估到达目标的实际代价
- 一致性:对于任意节点n和其子节点n',满足h(n) ≤ c(n, n') + h(n')
常见的启发函数实现:
def heuristic(self, s):
"""启发函数设计示例"""
goal = self.s_goal
# 1. 曼哈顿距离 (Manhattan distance)
manhattan = abs(goal[0] - s[0]) + abs(goal[1] - s[1])
# 2. 欧几里得距离 (Euclidean distance)
euclidean = math.hypot(goal[0] - s[0], goal[1] - s[1])
# 3. 切比雪夫距离 (Chebyshev distance)
chebyshev = max(abs(goal[0] - s[0]), abs(goal[1] - s[1]))
# 4. 对角线距离 (Diagonal distance)
dx = abs(goal[0] - s[0])
dy = abs(goal[1] - s[1])
diagonal = dx + dy + (math.sqrt(2) - 2) * min(dx, dy)
# 5. 加权启发函数 (Weighted heuristic)
weighted = 1.2 * euclidean # 大于1的权重会牺牲最优性换取速度
# 返回选择的启发函数值
return euclidean # 默认使用欧几里得距离
2. 处理复杂地形:代价地图与非均匀代价路径规划
在实际应用中,不同区域可能具有不同的通行代价(如平地、山地、水域等)。以下是如何扩展A*算法以支持代价地图:
def cost(self, s_start, s_goal):
"""带代价地图的成本计算"""
if self.is_collision(s_start, s_goal):
return math.inf
# 基础距离成本
distance_cost = math.hypot(s_goal[0] - s_start[0], s_goal[1] - s_start[1])
# 地形成本:根据所在区域的地形类型添加额外成本
terrain_cost = 0
# 检查起点和终点之间的所有点
points = self.get_line_points(s_start, s_goal)
for point in points:
# 根据点的坐标获取地形代价(假设已实现terrain_cost_map)
terrain_type = self.terrain_cost_map.get(point, 1.0)
# 根据地形类型添加不同成本
if terrain_type == "grass":
terrain_cost += 0.5 # 草地代价
elif terrain_type == "sand":
terrain_cost += 1.5 # 沙地代价
elif terrain_type == "mud":
terrain_cost += 3.0 # 泥地代价
elif terrain_type == "water":
terrain_cost += 10.0 # 水域代价,很难通过
# 总代价 = 距离成本 + 地形成本
total_cost = distance_cost + terrain_cost
return total_cost
3. 实时路径规划:在嵌入式系统中的优化策略
在资源受限的嵌入式系统中实现路径规划算法,需要考虑以下优化策略:
-
内存优化:
- 使用稀疏表示法代替稠密网格
- 实现节点池复用,避免频繁内存分配
- 采用优先级队列的高效实现(如 Fibonacci 堆)
-
计算优化:
- 启发函数预计算和缓存
- 使用整数运算代替浮点运算
- 实现算法的增量式更新版本
-
精度与性能平衡:
- 动态调整搜索精度和范围
- 采用多级路径规划策略(全局粗略+局部精细)
# 嵌入式系统优化示例:使用固定点数运算和节点池
class EmbeddedAStar:
def __init__(self, s_start, s_goal, grid_size=100):
self.s_start = self.to_fixed(s_start)
self.s_goal = self.to_fixed(s_goal)
self.grid_size = grid_size
# 节点池 - 预分配内存
self.node_pool = [self.Node() for _ in range(grid_size * grid_size)]
self.pool_index = 0
# 使用整数优先级队列避免浮点数运算
self.OPEN = []
@staticmethod
def to_fixed(point, precision=1000):
"""将浮点数坐标转换为整数固定点数"""
return (int(point[0] * precision), int(point[1] * precision))
class Node:
"""轻量级节点结构"""
__slots__ = ['x', 'y', 'g', 'h', 'parent'] # 固定属性以节省内存
def __init__(self):
self.x = 0
self.y = 0
self.g = 0
self.h = 0
self.parent = None
def get_node(self, x, y):
"""从节点池获取节点,避免动态内存分配"""
if self.pool_index < len(self.node_pool):
node = self.node_pool[self.pool_index]
self.pool_index += 1
node.x = x
node.y = y
node.g = 0
node.h = 0
node.parent = None
return node
return None # 节点池耗尽
算法选择指南:根据场景挑选最优路径规划方案
选择合适的路径规划算法需要考虑多个因素,以下是一个决策流程图,帮助你根据具体场景做出选择:
典型应用场景的算法推荐
-
自动驾驶车辆路径规划:
- 全局路径规划:A或Bidirectional A
- 局部避障:D* Lite或Anytime D*
- 停车场场景:Reeds-Shepp曲线+A*
-
移动机器人导航:
- 室内环境:D* Lite或LPA*
- 未知环境探索:LRTA或RTAA
- 高动态环境:Anytime D*
-
游戏AI路径规划:
- 角色移动:A*或Jump Point Search
- 大规模场景:Hierarchical A*
- 实时策略游戏:ARA或简化版A
-
无人机路径规划:
- 3D空间路径:3D A或RRT
- 避障重规划:3D D* Lite
- 长距离导航:Bidirectional A*
总结与展望:路径规划技术的发展趋势
本文要点回顾
- 算法原理:深入解析了13种搜索算法的核心原理、优缺点及适用场景
- 实现指南:提供了基于PathPlanning框架的完整实现示例和代码
- 性能对比:通过动画和实验数据直观展示了不同算法的表现差异
- 应用策略:给出了根据实际场景选择和优化算法的实用指南
路径规划技术的未来发展方向
-
深度学习与传统算法融合:
- 使用神经网络学习启发函数,提高A*类算法性能
- 基于强化学习的路径规划策略,适应复杂动态环境
-
多智能体协同路径规划:
- 分布式路径规划算法,解决多机器人协作问题
- 冲突检测与消解机制,实现高效协同运动
-
不确定性环境下的鲁棒规划:
- 概率路径规划算法,处理传感器噪声和环境不确定性
- 鲁棒优化方法,提高路径对扰动的抵抗能力
-
实时高维空间路径规划:
- 基于采样的高效高维空间搜索算法
- 分层路径规划策略,平衡精度和效率
进一步学习资源
推荐书籍
- 《Planning Algorithms》by Steven M. LaValle
- 《Autonomous Mobile Robots: Planning and Control》by Roland Siegwart
- 《Path Planning for Mobile Robots: A Review》by K. M. Krishna
在线课程
- Coursera: "Robotics: Motion Planning and Control"
- edX: "Autonomous Navigation for Flying Robots"
- Udacity: "Self-Driving Car Engineer Nanodegree"
开源项目
- PathPlanning (本文使用的项目): https://gitcode.com/gh_mirrors/pa/PathPlanning
- ROS Navigation Stack: http://wiki.ros.org/navigation
- OpenRAVE: http://openrave.org/
如果你觉得本文对你有帮助,请点赞、收藏并关注,以便获取更多路径规划和机器人技术相关的深度文章。下一期我们将深入探讨采样-based规划算法(RRT、RRT*等),敬请期待!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



