路径规划与轨迹规划:你真的分得清吗?

很多刚入行的朋友问我:「路径规划和轨迹规划,不就是一回事吗?」
嗯,我当年也这么想。直到第一次在产线上调试AGV,车明明沿着规划好的路径走,却在转弯时翻了——我才意识到,这两者差得远了。

简单说:路径规划只关心「走哪条路」,轨迹规划还要管「怎么走」。
路径是一条几何线,轨迹是这条线上每个点的位置、速度、加速度,甚至加加速度。

核心区别一句话:
路径 = 空间中的一条曲线(几何)
轨迹 = 路径 + 时间参数化(运动学/动力学约束)

1. 几何路径 vs 时间参数化

先看路径。路径规划的输出,通常是一系列离散的路径点,或者一条连续的曲线。比如A*算法找出来的折线段,或者RRT*生成的平滑曲线。它只描述「机器人应该经过哪些位置」,不包含任何时间信息。

但机器人要动起来,必须知道「什么时候到哪个点」。这就是时间参数化——给路径加上时间戳。

举个例子:
路径:从(0,0)到(10,0)的一条直线。
轨迹:t=0s在(0,0),t=2s在(5,0),t=4s在(10,0),且速度保持2.5m/s。

我个人习惯把时间参数化看作「给路径注入灵魂」。没有时间参数,路径只是一具空壳。

实战小技巧:
我在做机械臂轨迹规划时,经常先用RRT*找一条无碰撞路径,再用五次多项式做时间参数化。这样既保证了路径的几何可行性,又保证了运动平滑性。

2. 约束类型:运动学、动力学、环境

为什么轨迹规划比路径规划难?因为约束太多了。我把它分成三类:

约束类型 典型约束 影响
运动学约束 最大速度、最大加速度、关节限位 决定轨迹的平滑性和可达性
动力学约束 最大力矩、电机功率、惯性力 决定轨迹的物理可行性
环境约束 障碍物、地面摩擦、狭窄通道 决定轨迹的安全性和可执行性

运动学约束是最基础的。比如AGV的最大转弯半径,机械臂每个关节的角度范围。这些约束决定了路径的几何形状是否可达。

动力学约束就复杂多了。我记得有一次做四足机器人跳跃规划,路径规划出来的轨迹在运动学上完全可行,但动力学仿真时发现电机力矩不够,机器人直接趴地上了。从那以后,我每次做轨迹规划都会先检查动力学可行性。

环境约束是「隐形杀手」。你以为路径避开了障碍物,但实际运行时,地面摩擦系数变化、传感器噪声、动态障碍物突然出现,都可能让轨迹失效。

我曾经踩过的坑:
做仓储AGV项目时,路径规划避开了所有静态障碍物,但没考虑地面有油渍。结果AGV在转弯时打滑,直接撞上货架。后来我在轨迹规划里加入了地面摩擦系数估计和动态速度调整,才彻底解决这个问题。

3. 路径规划 vs 轨迹规划:一张图看懂

下面这张图是我自己总结的,每次给团队新人培训都会用:

路径规划 vs 轨迹规划:核心对比 路径规划 输入:地图、起点、终点 输出:几何路径(点序列/曲线) 典型算法:A*、RRT、Dijkstra 约束:环境障碍、运动学可达性 轨迹规划 输入:路径 + 时间约束 输出:时间参数化轨迹 典型方法:多项式插值、梯形速度 约束:速度、加速度、力矩 时间参数化 关键差异 • 路径:只关心「能不能到」 • 轨迹:关心「怎么到、多快到、稳不稳」 • 路径规划是轨迹规划的前置步骤 • 轨迹规划必须考虑机器人物理限制 • 实际项目中,两者往往迭代优化

4. 实战中的选择策略

你可能会问:那我到底该用路径规划还是轨迹规划?
答案是:大多数场景下,两者都要用。

我一般这样决策:

  1. 如果机器人运动速度很慢(比如巡检机器人0.5m/s),路径规划基本够用,轨迹规划可以简化成匀速运动。
  2. 如果机器人需要高速运动(比如无人机竞速、机械臂抓取),必须做完整的轨迹规划,否则必然出问题。
  3. 如果环境动态变化(比如人机协作),需要实时重规划,这时候路径规划和轨迹规划要耦合在一起做。

我的个人习惯:
做项目时,我会先跑一遍路径规划,确认几何路径可行。然后立刻做轨迹规划,检查运动学和动力学约束。如果轨迹规划失败,我会返回修改路径规划的参数(比如增加路径平滑度、调整路径点密度)。这样来回迭代几次,基本就能拿到可执行的轨迹。

5. 一个简单的代码示例

下面是一个用Python实现的路径到轨迹转换示例。路径是直线,轨迹用梯形速度曲线参数化:

import numpy as np

# 路径:从(0,0)到(10,0)的直线
path_points = np.array([[0, 0], [10, 0]])

# 轨迹参数
v_max = 2.0  # 最大速度 m/s
a_max = 1.0  # 最大加速度 m/s^2
total_distance = 10.0

# 梯形速度曲线时间计算
t_acc = v_max / a_max          # 加速时间
d_acc = 0.5 * a_max * t_acc**2 # 加速段距离

if 2 * d_acc <= total_distance:
    # 有匀速段
    d_const = total_distance - 2 * d_acc
    t_const = d_const / v_max
    t_total = 2 * t_acc + t_const
else:
    # 没有匀速段(三角形速度曲线)
    t_acc = np.sqrt(total_distance / a_max)
    t_total = 2 * t_acc

print(f"轨迹总时间: {t_total:.2f} s")
print(f"加速时间: {t_acc:.2f} s")

# 生成时间参数化轨迹
dt = 0.01
t = np.arange(0, t_total, dt)
trajectory = []

for ti in t:
    if ti < t_acc:
        # 加速段
        s = 0.5 * a_max * ti**2
        v = a_max * ti
    elif ti < t_total - t_acc:
        # 匀速段
        s = d_acc + v_max * (ti - t_acc)
        v = v_max
    else:
        # 减速段
        t_dec = ti - (t_total - t_acc)
        s = total_distance - 0.5 * a_max * (t_acc - t_dec)**2
        v = a_max * (t_acc - t_dec)
    
    # 线性插值到路径
    ratio = s / total_distance
    x = path_points[0,0] + ratio * (path_points[1,0] - path_points[0,0])
    y = path_points[0,1] + ratio * (path_points[1,1] - path_points[0,1])
    trajectory.append([ti, x, y, v])

trajectory = np.array(trajectory)
print(f"轨迹点数: {len(trajectory)}")

这段代码虽然简单,但包含了轨迹规划的核心思想:把几何路径映射到时间域,同时满足速度和加速度约束。

注意:
实际项目中,路径往往不是直线,而是复杂的曲线。这时候需要先对路径进行弧长参数化,再在弧长域上做时间参数化。我常用的方法是先对路径点做三次样条插值,然后计算弧长,最后用梯形速度曲线映射到时间域。

6. 总结一下

路径规划和轨迹规划的区别,说白了就是「地图」和「导航」的区别。地图告诉你路在哪,导航告诉你什么时候加速、什么时候转弯、什么时候刹车。

做轨迹规划时,一定要时刻记住三个约束:运动学约束决定能不能动,动力学约束决定动得稳不稳,环境约束决定动得安不安全。三者缺一不可。

嗯,这一章就到这里。记住:路径是骨架,轨迹是血肉。只有两者结合,才能让机器人真正动起来。