4. 贝塞尔曲线路径生成:利用控制点生成平滑的AGV行驶路径

做AGV控制这么多年,我越来越觉得路径平滑是个被低估的活儿。很多人觉得,只要给出一串路径点,机器人照着走就行了。但实际跑起来,你会发现——车在拐弯处一顿一顿的,电机嗡嗡响,货物晃来晃去。说白了,就是路径不够“顺”。

那怎么让路径变顺呢?我个人习惯用贝塞尔曲线。这玩意儿在图形学里用得很多,但在AGV路径规划里,它同样好用。你想想看,我们只需要几个控制点,就能生成一条光滑的曲线,机器人沿着它走,加减速自然,转向流畅。

核心思想:用少量控制点“牵引”出一条平滑路径,而不是让机器人硬生生地从一个点直线走到另一个点。

4.1 什么是贝塞尔曲线?

贝塞尔曲线本质上是一个参数方程。给定一组控制点 P₀, P₁, ..., Pₙ,我们可以通过一个参数 t(从 0 到 1)计算出曲线上每个点的位置。

最常见的是一阶(线性)、二阶(二次)和三阶(三次)贝塞尔曲线。对于AGV路径规划,我建议用三阶贝塞尔曲线。为什么?因为它的四个控制点能提供足够的“弯曲自由度”,又不会太复杂。

公式长这样:

B(t) = (1-t)³·P₀ + 3·(1-t)²·t·P₁ + 3·(1-t)·t²·P₂ + t³·P₃

其中 t ∈ [0, 1]
P₀ 是起点,P₃ 是终点
P₁ 和 P₂ 是中间控制点

嗯,这里要注意:P₁ 和 P₂ 并不在路径上,它们只是“拉”着曲线往某个方向走。控制点离得越远,曲线就越“鼓”。

4.2 控制点怎么选?—— 我在项目中的经验

我记得第一次做贝塞尔路径时,踩了个大坑。我随便给了四个控制点,结果曲线直接穿过了障碍物区域。后来我总结了一套选点原则:

  • 起点和终点:就是AGV当前的位置和目标位置。这两个点必须落在路径上。
  • 中间控制点:用来控制曲线的“走向”。我一般把 P₁ 放在起点前进方向的延长线上,P₂ 放在终点后退方向的延长线上。
  • 距离控制:P₁ 离 P₀ 的距离,我通常取路径总长度的 20%~30%。太短了曲线拐弯太急,太长了曲线会“甩出去”。

小技巧:如果你想让AGV在转弯时更平稳,可以把 P₁ 和 P₂ 的距离设得稍微大一点。这样曲线会更“舒展”,AGV的角速度变化也更平缓。

4.3 贝塞尔曲线的离散化

贝塞尔曲线是连续的,但AGV的控制系统需要离散的点。所以我们要对曲线进行采样。我一般按固定步长取 t 值,比如 t = 0, 0.05, 0.1, ..., 1.0,这样得到 21 个路径点。

代码实现很简单:

// 三阶贝塞尔曲线离散化
function generateBezierPath(P0, P1, P2, P3, steps = 20) {
    let path = [];
    for (let i = 0; i <= steps; i++) {
        let t = i / steps;
        let oneMinusT = 1 - t;
        
        let x = oneMinusT*oneMinusT*oneMinusT * P0.x 
              + 3*oneMinusT*oneMinusT*t * P1.x 
              + 3*oneMinusT*t*t * P2.x 
              + t*t*t * P3.x;
              
        let y = oneMinusT*oneMinusT*oneMinusT * P0.y 
              + 3*oneMinusT*oneMinusT*t * P1.y 
              + 3*oneMinusT*t*t * P2.y 
              + t*t*t * P3.y;
              
        path.push({x, y});
    }
    return path;
}

你看,代码其实不复杂。但实际项目中,我还会加一个步骤——检查生成的路径点是否与障碍物碰撞。如果碰撞,就调整控制点位置,重新生成。

4.4 多段贝塞尔曲线拼接

一条贝塞尔曲线只能处理一段路径。如果AGV要跑很长的距离,比如从仓库A区到B区,中间要拐好几个弯,那就需要多段曲线拼接起来。

我曾经在做一个大型仓储项目时,就遇到了这个问题。路径总长200多米,中间有12个转弯点。我用了12段三阶贝塞尔曲线,每段之间保证连续性。

拼接的关键点:

  • 位置连续:上一段的终点 = 下一段的起点。这个自然满足。
  • 切线方向连续:上一段在终点处的切线方向,必须等于下一段在起点处的切线方向。否则AGV会在拼接点处“急转弯”。

怎么保证切线连续?很简单:让上一段的 P₂(倒数第二个控制点)、上一段的 P₃(终点)、下一段的 P₀(起点)、下一段的 P₁ 这四个点共线。

注意:如果只要求位置连续,不要求切线连续,AGV在拼接点处会有一个速度方向的突变。这会导致电机瞬间加速,轻则抖动,重则触发急停保护。我吃过这个亏,后来就老老实实做切线连续了。

4.5 贝塞尔曲线 vs 其他路径生成方法

我整理了一个对比表,方便你选型:

方法 优点 缺点 适用场景
贝塞尔曲线 控制点少,计算快,曲线平滑 全局调整不方便,局部修改影响整体 短距离路径、转弯路径
B样条曲线 局部修改不影响整体,更灵活 控制点较多,计算稍复杂 长距离复杂路径
多项式插值 精确经过所有点 容易产生振荡(龙格现象) 点少且要求精确通过
直线+圆弧 实现简单,符合运动学 路径不够平滑,曲率不连续 简单场景、低速AGV

我个人习惯:如果路径点少于10个,用贝塞尔曲线;如果多于10个,用B样条。但不管用哪种,最终都要做碰撞检测和曲率检查。

4.6 知识体系结构图

下面这张图,是我梳理的贝塞尔曲线路径生成的核心逻辑。你看一眼就能明白整个流程:

贝塞尔曲线路径生成核心流程 输入:起点/终点/控制点 步骤1:选择贝塞尔曲线阶数(推荐三阶) 步骤2:离散化采样(t从0到1步进) 步骤3:碰撞检测 + 曲率检查 输出:平滑路径点序列 关键参数 • 控制点距离:20%~30% • 采样步长:0.05 • 曲率上限:0.1~0.3 • 碰撞阈值:≥10cm • 切线连续:共线约束 • 多段拼接:首尾衔接

4.7 避坑指南

最后,分享几个我踩过的坑:

  • 控制点太近:我曾经把 P₁ 和 P₂ 放得离 P₀ 和 P₃ 太近,结果曲线几乎变成直线,转弯处还是急弯。后来我规定控制点距离至少是路径长度的15%。
  • 忽略曲率约束:AGV有最小转弯半径。如果贝塞尔曲线的曲率超过了AGV的物理极限,车是转不过来的。一定要在生成路径后检查曲率。
  • 采样点太少:如果只采10个点,AGV在点与点之间走直线,曲线效果就没了。我一般采20~30个点,既保证平滑,又不占用太多内存。

嗯,贝塞尔曲线这块儿,说白了就是“用控制点牵着路径走”。你多试几次,找到手感了,就能生成让AGV跑得又稳又顺的路径。


专注资料整理