最完整路径规划实战:13种搜索算法动画对比与实现指南

最完整路径规划实战:13种搜索算法动画对比与实现指南

【免费下载链接】PathPlanning Common used path planning algorithms with animations. 【免费下载链接】PathPlanning 项目地址: https://gitcode.com/gh_mirrors/pa/PathPlanning

引言:你还在为路径规划算法选择发愁吗?

在机器人导航(Robot Navigation)、自动驾驶(Autonomous Driving)和游戏开发(Game Development)等领域,路径规划(Path Planning)是核心技术之一。面对复杂多变的环境,如何快速找到一条最优路径,同时兼顾实时性和鲁棒性,一直是工程师和研究者面临的挑战。

本文将深入剖析13种主流搜索算法(Search Algorithm),通过动画对比和代码实现,帮助你彻底理解各类算法的优缺点及适用场景。读完本文,你将能够:

  • 掌握A*、Dijkstra、D* Lite等经典搜索算法的核心原理
  • 通过动态可视化对比不同算法的搜索过程和路径结果
  • 学会如何根据实际场景选择最合适的路径规划算法
  • 基于PathPlanning项目框架快速实现和测试各类算法

算法全景:搜索算法分类与应用场景

路径搜索算法可以根据不同的标准进行分类,常见的分类方式包括:

按搜索策略分类

mermaid

13种搜索算法特性对比表

算法名称完备性最优性时间复杂度空间复杂度启发函数动态环境适应典型应用场景
BFSO(b^d)O(b^d)网格路径规划
DFSO(b^m)O(bm)迷宫探索
DijkstraO(b^d)O(b^d)静态环境最短路径
Best-FirstO(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* LiteO(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算法的搜索过程可以用以下流程图表示:

mermaid

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)(仅估计代价)

mermaid

3. 动态环境搜索:D* Lite算法的革新

D* Lite算法是一种高效的动态路径规划算法,特别适用于机器人在未知或动态变化环境中的导航。它通过维护一个优先队列和双向指针,能够在环境发生变化时快速重新规划路径。

mermaid

项目实战: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. 实时路径规划:在嵌入式系统中的优化策略

在资源受限的嵌入式系统中实现路径规划算法,需要考虑以下优化策略:

  1. 内存优化

    • 使用稀疏表示法代替稠密网格
    • 实现节点池复用,避免频繁内存分配
    • 采用优先级队列的高效实现(如 Fibonacci 堆)
  2. 计算优化

    • 启发函数预计算和缓存
    • 使用整数运算代替浮点运算
    • 实现算法的增量式更新版本
  3. 精度与性能平衡

    • 动态调整搜索精度和范围
    • 采用多级路径规划策略(全局粗略+局部精细)
# 嵌入式系统优化示例:使用固定点数运算和节点池
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  # 节点池耗尽

算法选择指南:根据场景挑选最优路径规划方案

选择合适的路径规划算法需要考虑多个因素,以下是一个决策流程图,帮助你根据具体场景做出选择:

mermaid

典型应用场景的算法推荐

  1. 自动驾驶车辆路径规划

    • 全局路径规划:A或Bidirectional A
    • 局部避障:D* Lite或Anytime D*
    • 停车场场景:Reeds-Shepp曲线+A*
  2. 移动机器人导航

    • 室内环境:D* Lite或LPA*
    • 未知环境探索:LRTA或RTAA
    • 高动态环境:Anytime D*
  3. 游戏AI路径规划

    • 角色移动:A*或Jump Point Search
    • 大规模场景:Hierarchical A*
    • 实时策略游戏:ARA或简化版A
  4. 无人机路径规划

    • 3D空间路径:3D A或RRT
    • 避障重规划:3D D* Lite
    • 长距离导航:Bidirectional A*

总结与展望:路径规划技术的发展趋势

本文要点回顾

  1. 算法原理:深入解析了13种搜索算法的核心原理、优缺点及适用场景
  2. 实现指南:提供了基于PathPlanning框架的完整实现示例和代码
  3. 性能对比:通过动画和实验数据直观展示了不同算法的表现差异
  4. 应用策略:给出了根据实际场景选择和优化算法的实用指南

路径规划技术的未来发展方向

  1. 深度学习与传统算法融合

    • 使用神经网络学习启发函数,提高A*类算法性能
    • 基于强化学习的路径规划策略,适应复杂动态环境
  2. 多智能体协同路径规划

    • 分布式路径规划算法,解决多机器人协作问题
    • 冲突检测与消解机制,实现高效协同运动
  3. 不确定性环境下的鲁棒规划

    • 概率路径规划算法,处理传感器噪声和环境不确定性
    • 鲁棒优化方法,提高路径对扰动的抵抗能力
  4. 实时高维空间路径规划

    • 基于采样的高效高维空间搜索算法
    • 分层路径规划策略,平衡精度和效率

进一步学习资源

推荐书籍

  1. 《Planning Algorithms》by Steven M. LaValle
  2. 《Autonomous Mobile Robots: Planning and Control》by Roland Siegwart
  3. 《Path Planning for Mobile Robots: A Review》by K. M. Krishna

在线课程

  1. Coursera: "Robotics: Motion Planning and Control"
  2. edX: "Autonomous Navigation for Flying Robots"
  3. Udacity: "Self-Driving Car Engineer Nanodegree"

开源项目

  1. PathPlanning (本文使用的项目): https://gitcode.com/gh_mirrors/pa/PathPlanning
  2. ROS Navigation Stack: http://wiki.ros.org/navigation
  3. OpenRAVE: http://openrave.org/

如果你觉得本文对你有帮助,请点赞、收藏并关注,以便获取更多路径规划和机器人技术相关的深度文章。下一期我们将深入探讨采样-based规划算法(RRT、RRT*等),敬请期待!

mermaid

【免费下载链接】PathPlanning Common used path planning algorithms with animations. 【免费下载链接】PathPlanning 项目地址: https://gitcode.com/gh_mirrors/pa/PathPlanning

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值