3. 路径-时间解耦法:将轨迹规划拆分为路径规划与速度规划两个子问题

说实话,我刚入行那会儿,面对轨迹规划问题,总想着一步到位——直接算出一条带时间信息的轨迹。结果呢?要么算法复杂到跑不动,要么解出来的轨迹根本没法用。后来一位老前辈点醒了我:把路径和速度分开考虑。这就是我们今天要聊的路径-时间解耦法。

3.1 为什么要解耦?

你想想看,一个完整的轨迹包含两个维度:空间路径时间进程。如果同时优化这两个维度,问题会变得非常复杂。我曾在某个项目中尝试过直接求解最优轨迹,结果优化变量太多,求解器直接罢工了。

解耦的核心思想很简单:

  • 先规划路径:只关心机器人走什么形状的路线
  • 再规划速度:在固定路径上,决定什么时候加速、什么时候减速

这样做的好处很明显——每个子问题的复杂度都大大降低。说白了,就是把一个硬骨头拆成两块,一块一块啃。

核心观点:路径-时间解耦法不是偷懒,而是一种工程智慧。在大多数工业机器人场景中,这种分治策略已经足够优秀。

3.2 路径规划子问题

路径规划的目标是找到一条从起点到终点的几何曲线。注意,这里我们只关心空间形状,不关心速度。

常见的路径表示方式有:

表示方式 特点 适用场景
多项式曲线 光滑、计算简单 点到点运动
B样条曲线 局部可控、连续性好 复杂路径、避障
贝塞尔曲线 直观、凸包性质好 视觉引导、交互设计
分段线性路径 简单、容易实现 AGV、仓储机器人

我个人习惯用B样条曲线。为什么?因为它的局部可控性太香了。我在做焊接机器人项目时,经常需要微调路径的某一段,B样条只需要调整几个控制点,其他部分完全不受影响。换成多项式曲线?那得重新算一遍。

小技巧:路径规划时,别忘了考虑机器人的运动学约束。比如差速轮机器人不能原地转弯,那就得保证路径曲率连续。

3.3 速度规划子问题

路径定好了,接下来就是速度规划。这一步的目标是:在给定的路径上,确定每个时刻的速度和加速度。

速度规划的核心约束有三个:

  1. 运动学约束:最大速度、最大加速度不能超
  2. 动力学约束:电机扭矩、关节力矩有限
  3. 任务约束:比如末端执行器在抓取时必须低速

我曾经犯过一个错误:只考虑了运动学约束,忽略了动力学。结果路径跑起来,电机直接过载报警。嗯,从那以后我再也不敢忽略动力学了。

常见的速度规划方法:

  • 梯形速度规划:加速-匀速-减速三段式,简单粗暴
  • S形速度规划:加加速度连续,运动更平滑
  • 时间最优速度规划:在约束下尽可能快,适合节拍要求高的场景

注意:速度规划不是越快越好。有时候为了追求时间最优,加速度会频繁切换,导致机器人抖动。我建议在时间最优和运动平滑之间找个平衡点。

3.4 解耦法的完整流程

下面这张图展示了路径-时间解耦法的完整流程:

路径-时间解耦法流程图 输入:起点、终点、约束 步骤1:路径规划 生成几何路径(B样条/多项式等) 步骤2:速度规划 在路径上分配速度/加速度 输出:完整轨迹(路径+速度) 路径规划只关心空间形状,速度规划只关心时间分配,两者解耦后各自独立优化

3.5 代码示例:简单的路径-时间解耦实现

下面是一个Python示例,演示了如何用解耦法生成一条轨迹:

import numpy as np

# ========== 路径规划:生成B样条路径 ==========
def generate_path(waypoints, num_points=100):
    """
    用B样条生成平滑路径
    waypoints: 控制点列表 [(x1,y1), (x2,y2), ...]
    """
    # 简化实现:用三次多项式插值
    t = np.linspace(0, 1, num_points)
    path_x = np.interp(t, np.linspace(0, 1, len(waypoints)), 
                       [p[0] for p in waypoints])
    path_y = np.interp(t, np.linspace(0, 1, len(waypoints)), 
                       [p[1] for p in waypoints])
    return np.column_stack((path_x, path_y))

# ========== 速度规划:梯形速度曲线 ==========
def generate_velocity(path_length, max_v=1.0, max_a=0.5):
    """
    在固定路径长度上生成梯形速度曲线
    path_length: 路径总长度
    """
    # 计算加速段和减速段
    t_acc = max_v / max_a
    d_acc = 0.5 * max_a * t_acc**2
    
    if 2 * d_acc >= path_length:
        # 来不及加速到最大速度,三角形速度曲线
        t_acc = np.sqrt(path_length / max_a)
        t_const = 0
    else:
        # 梯形速度曲线
        d_const = path_length - 2 * d_acc
        t_const = d_const / max_v
    
    total_time = 2 * t_acc + t_const
    return total_time, t_acc, t_const

# ========== 主流程 ==========
waypoints = [(0, 0), (1, 2), (3, 3), (5, 1)]
path = generate_path(waypoints)
path_length = np.sum(np.linalg.norm(np.diff(path, axis=0), axis=1))

total_time, t_acc, t_const = generate_velocity(path_length)
print(f"路径长度: {path_length:.2f}")
print(f"总时间: {total_time:.2f}s")
print(f"加速时间: {t_acc:.2f}s, 匀速时间: {t_const:.2f}s")

经验之谈:实际项目中,路径规划的输出最好用参数化曲线(比如弧长参数化),这样速度规划时可以直接用弧长作为自变量,计算起来特别方便。

3.6 解耦法的优缺点

任何方法都有两面性。解耦法也不例外:

优点 缺点
算法简单,计算量小 无法同时优化路径和速度
模块化设计,便于调试 可能错过全局最优解
适合实时性要求高的场景 路径和速度的耦合被忽略
工程实现成熟,资料丰富 对复杂约束处理能力有限

我记得有一次做协作机器人项目,要求末端执行器在搬运过程中保持姿态稳定。解耦法就有点吃力了——因为路径和速度的耦合在这里很重要。最后我改用了一种迭代优化方法,才勉强满足要求。

所以我的建议是:如果你的场景是典型的点到点运动,或者对实时性要求很高,解耦法绝对够用。但如果你的任务涉及复杂的动力学耦合,那可能需要考虑更高级的方法。

总结一下:路径-时间解耦法是一种经典的工程方法。它把复杂问题拆成两个简单问题,让轨迹规划变得可控、可调、可维护。虽然它不完美,但在绝大多数工业场景中,它是最实用的选择。

专注资料整理