路径规划基础:图搜索算法与路径生成

大家好,我是你们的机器人运动规划讲师。今天我们来聊聊路径规划里最基础、也最核心的一块内容——图搜索算法。说实话,这部分内容我每次讲都觉得特别亲切,因为我在做第一个机器人项目时,就是被这几个算法“折磨”过来的。但搞懂了它们,后面的路就好走了。

一、图搜索的核心思想

路径规划,说白了就是“从A点到B点,怎么走最合适”。我们通常把环境抽象成一张图,图里有节点(位置)和边(可通行路径)。然后算法就在这张图上找一条最优路径。

我个人习惯把图搜索分为两大类:

  • 确定性搜索:比如Dijkstra、A*,它们知道整个地图信息,属于“全局规划”。
  • 随机采样搜索:比如RRT,它不知道全局地图,边探索边规划,适合高维空间。

你想想看,如果地图很小,用Dijkstra就挺好。但如果地图很大,或者维度很高(比如机械臂的关节空间),那Dijkstra就扛不住了。这时候RRT就派上用场了。

二、Dijkstra算法:最朴素的“地毯式搜索”

Dijkstra算法是图搜索的“老大哥”。它的思路很简单:从起点开始,一层一层往外扩展,直到找到终点。每次扩展都选当前代价最小的节点。

我在项目中遇到过一个问题:用Dijkstra规划一条10公里长的路径,地图网格是0.1米分辨率,结果跑了整整3分钟才出结果。为什么?因为它把整个地图都搜了一遍,很多无关区域也搜了。

核心要点:Dijkstra保证找到最短路径,但效率低。适合小规模地图或对最优性要求极高的场景。

伪代码大概长这样:

function Dijkstra(Graph, start, goal):
    dist = {start: 0}
    prev = {}
    queue = priority_queue([(0, start)])
    
    while queue not empty:
        current = queue.pop()
        if current == goal:
            return reconstruct_path(prev, goal)
        for neighbor in Graph.neighbors(current):
            new_dist = dist[current] + cost(current, neighbor)
            if new_dist < dist.get(neighbor, inf):
                dist[neighbor] = new_dist
                prev[neighbor] = current
                queue.push((new_dist, neighbor))
    return None

避坑指南:我曾经在项目中直接用Dijkstra处理动态障碍物,结果路径频繁重规划,机器人走走停停。后来我改用D* Lite(动态Dijkstra变种),才解决了这个问题。

三、A*算法:带“方向感”的搜索

A*算法是Dijkstra的升级版。它加了一个启发式函数h(n),用来估计当前节点到终点的距离。这样搜索就有了“方向感”,不会盲目乱搜。

启发式函数怎么选?我建议:

  • 网格地图:用曼哈顿距离(四方向移动)或欧几里得距离(八方向移动)
  • 路网地图:用直线距离
  • 高维空间:用欧几里得距离,但要注意维度灾难

你想想看,如果启发式函数h(n)永远小于真实距离,A*保证找到最优路径。如果h(n)大于真实距离,A*会更快,但可能不是最优。这个权衡在实际项目中很常见。

function AStar(Graph, start, goal):
    open_set = priority_queue([(0, start)])
    g_score = {start: 0}
    f_score = {start: heuristic(start, goal)}
    
    while open_set not empty:
        current = open_set.pop()
        if current == goal:
            return reconstruct_path(came_from, current)
        for neighbor in Graph.neighbors(current):
            tentative_g = g_score[current] + cost(current, neighbor)
            if tentative_g < g_score.get(neighbor, inf):
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g
                f_score[neighbor] = tentative_g + heuristic(neighbor, goal)
                open_set.push((f_score[neighbor], neighbor))
    return None

注意:A*的启发式函数设计不好,会导致搜索效率下降。我曾经见过一个团队用欧几里得距离做启发式,但地图是四方向网格,结果搜索范围比Dijkstra还大。嗯,这里要注意:启发式函数要和实际移动方式匹配。

四、RRT算法:随机探索的“冒险家”

RRT(Rapidly-exploring Random Tree)算法和前面两个完全不同。它不依赖全局地图,而是通过随机采样来探索空间。每次随机选一个点,然后从最近的已有节点向这个点生长一段距离。

我记得第一次用RRT做机械臂规划时,看到它像藤蔓一样在关节空间里“乱窜”,心里直打鼓。但后来发现,它确实能绕过各种障碍物,找到一条可行路径。

RRT的核心步骤:

  1. 随机采样一个点q_rand
  2. 找到树上离q_rand最近的节点q_near
  3. 从q_near向q_rand方向生长一步,得到q_new
  4. 检查q_new到q_near的路径是否碰撞
  5. 如果无碰撞,将q_new加入树中
  6. 重复直到q_new接近目标
function RRT(start, goal, environment):
    tree = [start]
    while not reached_goal(tree, goal):
        q_rand = random_sample(environment)
        q_near = nearest_neighbor(tree, q_rand)
        q_new = steer(q_near, q_rand, step_size)
        if not collision(q_near, q_new, environment):
            tree.append(q_new)
            if distance(q_new, goal) < threshold:
                return extract_path(tree, start, q_new)
    return None

个人经验:RRT的步长设置很关键。步长太大,容易跳过狭窄通道;步长太小,树生长太慢。我一般设成地图尺寸的1/20到1/10,然后根据实际效果微调。

五、路径生成与离散化

算法找到的路径通常是一系列节点(离散点)。但机器人不能直接沿着这些点走,因为:

  • 节点之间是直线连接,不够平滑
  • 节点密度可能不均匀
  • 机器人有运动学约束(比如不能原地转弯)

所以我们需要做两件事:

  1. 路径离散化:把路径上的节点插值成更密集的点序列。比如每0.1米一个点。
  2. 路径平滑:用样条曲线(如B样条、贝塞尔曲线)对离散点进行拟合,生成连续平滑的轨迹。

我建议用三次B样条做平滑,因为它能保证C2连续(加速度连续),而且计算量不大。代码实现大概是这样:

import numpy as np
from scipy.interpolate import splprep, splev

def smooth_path(path_points, num_points=100):
    # path_points: Nx2 或 Nx3 的路径点
    path_array = np.array(path_points)
    # 参数化
    tck, u = splprep([path_array[:,0], path_array[:,1]], s=0)
    # 生成平滑点
    u_new = np.linspace(0, 1, num_points)
    smooth_x, smooth_y = splev(u_new, tck)
    return np.column_stack((smooth_x, smooth_y))

避坑指南:我曾经直接用A*输出的路径点驱动机器人,结果机器人走到拐角时剧烈抖动,差点撞墙。后来加了平滑处理,问题就解决了。记住:路径规划的输出只是“建议”,真正执行前一定要做平滑和插值。

六、知识体系总览

下面这张图总结了本章的核心内容,我画成了流程图,方便你理解各个算法之间的关系和应用场景。

路径规划基础:图搜索算法知识体系 路径规划算法 确定性搜索 随机采样搜索 Dijkstra A* RRT RRT* 小规模/静态环境 高维/动态环境 路径离散化 → 样条平滑 → 轨迹生成 全局已知地图 未知/部分已知地图

这张图把本章的知识点串起来了。你可以看到,确定性搜索和随机采样搜索是两条不同的技术路线,各有各的适用场景。而不管用哪种算法,最后都要经过路径离散化和平滑,才能变成机器人能执行的轨迹。

总结一下:Dijkstra适合小地图、追求最优;A*适合中等地图、需要效率;RRT适合高维空间、复杂障碍。实际项目中,我经常把A*和RRT结合起来用——先用A*做全局规划,再用RRT做局部避障。效果还不错。

好了,这一章的内容就到这里。路径规划是机器人运动控制的基石,搞懂了这些算法,后面学轨迹优化、避障策略就会轻松很多。记住我的一句话:没有最好的算法,只有最合适的算法。选算法时,一定要考虑你的地图大小、维度、实时性要求。

可可精品资料库