2、S形速度规划基础:从梯形速度曲线到S形曲线

做运动控制的人,几乎都绕不开速度规划这个话题。我记得刚入行那会儿,第一个项目就是给一台三轴点胶机写运动程序。当时图省事,直接用了梯形速度曲线。结果呢?机器一启动,胶枪就在起点抖了一下,点出来的胶点歪歪扭扭。后来才明白——梯形曲线在速度切换点上的加速度突变,就是抖动的根源。

今天我们就来聊聊,怎么从梯形曲线进化到S形曲线,以及S形曲线那经典的7段式结构。

2.1 梯形速度曲线的痛点

梯形速度曲线,说白了就是三段:匀加速、匀速、匀减速。它的速度剖面像个梯形,所以得名。

但问题出在哪儿呢?

你想想看,梯形曲线的加速度是阶跃变化的。从静止到加速的瞬间,加速度从0直接跳到A;从加速到匀速的瞬间,加速度又从A直接降到0。这种突变会产生一个物理量——加加速度(Jerk),也就是加速度的变化率。理论上,梯形曲线的Jerk在切换点是无穷大。

核心结论:梯形曲线在速度切换点存在加速度突变,导致加加速度无穷大,这是机械抖动和冲击的根本原因。

我在项目中遇到过一台高速贴片机,用梯形曲线跑的时候,吸嘴在取料位置总会弹跳一下。后来换成S形曲线,弹跳直接消失了。说白了,S形曲线就是给加速度加了一个“缓坡”,让它平滑过渡。

2.2 从梯形到S形:加加速度的引入

S形曲线和梯形曲线最大的区别,就是引入了加加速度(Jerk)这个约束。

梯形曲线只约束了:

  • 最大速度 Vmax
  • 最大加速度 Amax

S形曲线额外约束了:

  • 最大加加速度 Jmax

加加速度的单位是 m/s³,它描述了加速度的变化快慢。Jmax 越小,加速度变化越平缓,运动越柔和,但加减速时间也会变长。

我个人的习惯是,在设备调试阶段先把 Jmax 设得保守一些(比如理论值的 50%),等确认运动平稳后再逐步提高。这样能避免因为Jerk过大导致机械共振。

2.3 S形曲线的7段式结构

S形曲线完整的运动过程,可以拆成7个阶段。我画了一张图,方便你理解:

S形速度规划7段式结构图 S形速度规划 7 段式结构 速度 v 时间 t ① ② ③ ④ ⑤ ⑥ ⑦ 加加速 匀加速 减加速 匀速 加减速 匀减速 减减速 加速度 a 加加速度 j 速度 加速度 加加速度

这7段分别是:

  1. 加加速段:加速度从0开始,以Jmax线性增加。速度呈二次曲线上升。
  2. 匀加速段:加速度达到Amax后保持不变。速度线性上升。
  3. 减加速段:加速度从Amax线性减小到0。速度继续上升,但增速放缓。
  4. 匀速段:加速度为0,速度保持Vmax不变。
  5. 加减速段:加速度从0反向增加到-Amaz。速度开始下降。
  6. 匀减速段:加速度保持-Amaz不变。速度线性下降。
  7. 减减速段:加速度从-Amaz线性减小到0。速度缓慢降到0。

我的经验:在实际项目中,并不是每次都需要完整的7段。如果运动距离很短,可能连匀速段都没有,直接就是“加加速-匀加速-减加速-加减速-匀减速-减减速”的6段结构。甚至更短时,只有“加加速-减加速-加减速-减减速”4段。要学会根据实际行程动态裁剪段数。

2.4 各阶段的数学描述

我们以加加速段为例,看看数学上怎么描述。假设加加速度为 J(常数),初始速度为 v₀,初始加速度为 a₀=0。

加加速段(0 ≤ t ≤ T₁):

  • 加加速度:j(t) = J
  • 加速度:a(t) = J·t
  • 速度:v(t) = v₀ + ½·J·t²
  • 位移:s(t) = v₀·t + ⅙·J·t³

你看,速度是时间的二次函数,位移是三次函数。这就是“S形”名字的由来——速度曲线呈S形弯曲,而不是梯形那样直来直去。

其他段的公式类似,只是初始条件和符号不同。我整理了一张表,方便你对照:

阶段 加速度 a(t) 速度 v(t) 位移 s(t)
① 加加速 J·t v₀ + ½·J·t² v₀·t + ⅙·J·t³
② 匀加速 Amax v₁ + Amax·t v₁·t + ½·Amax·t²
③ 减加速 Amax - J·t v₂ + Amax·t - ½·J·t² v₂·t + ½·Amax·t² - ⅙·J·t³
④ 匀速 0 Vmax Vmax·t
⑤ 加减速 -J·t Vmax - ½·J·t² Vmax·t - ⅙·J·t³
⑥ 匀减速 -Amax v₅ - Amax·t v₅·t - ½·Amax·t²
⑦ 减减速 -Amax + J·t v₆ - Amax·t + ½·J·t² v₆·t - ½·Amax·t² + ⅙·J·t³

注意:上表中的 t 是相对于该段起始时刻的时间偏移,不是全局时间。每段的初始速度 v₀、v₁、v₂ 等,需要根据上一段的结束速度递推计算。我曾经在代码里犯过这个错——直接用全局时间代入公式,结果速度曲线出现了跳变,排查了半天才发现是时间基准搞错了。

2.5 什么时候用S形,什么时候用梯形?

不是所有场景都需要S形曲线。我个人的选择原则是这样的:

场景 推荐曲线 原因
高速高精度定位(如贴片机、数控机床) S形曲线 减少冲击,提高定位精度
重载搬运(如机器人、AGV) S形曲线 避免货物晃动,保护机械结构
简单点位运动(如传送带启停) 梯形曲线 计算简单,实时性高
低速、轻载场景 梯形曲线 冲击小,没必要增加计算量

说白了,S形曲线是用计算量换平滑度。如果你的控制器算力够,我建议默认用S形。毕竟现在MCU的性能越来越强,多算几个三次方程根本不是事儿。

2.6 一个简单的Python实现

最后,我写了一个极简的S形速度规划函数。它只处理加加速到匀速再到减减速的完整7段,但足够你理解核心逻辑了。

import numpy as np

def s_curve_plan(v0, vmax, amax, jmax, total_time):
    """
    简化的S形速度规划(对称7段)
    返回时间数组和速度数组
    """
    # 计算各段时间(假设对称,且能达到最大速度)
    ta = amax / jmax          # 加加速/减加速段时间
    tv = total_time - 4*ta    # 匀速段时间(需大于0)
    
    if tv < 0:
        raise ValueError("总时间太短,无法达到最大速度")
    
    # 时间离散化
    dt = 0.001
    t = np.arange(0, total_time, dt)
    v = np.zeros_like(t)
    
    # 分段计算速度
    for i, ti in enumerate(t):
        if ti < ta:
            # 加加速段
            v[i] = v0 + 0.5 * jmax * ti**2
        elif ti < 2*ta:
            # 匀加速段
            t_local = ti - ta
            v[i] = v0 + 0.5 * jmax * ta**2 + amax * t_local
        elif ti < 3*ta:
            # 减加速段
            t_local = ti - 2*ta
            v[i] = v0 + 0.5 * jmax * ta**2 + amax * ta + amax * t_local - 0.5 * jmax * t_local**2
        elif ti < 3*ta + tv:
            # 匀速段
            v[i] = vmax
        elif ti < 4*ta + tv:
            # 加减速段
            t_local = ti - (3*ta + tv)
            v[i] = vmax - 0.5 * jmax * t_local**2
        elif ti < 5*ta + tv:
            # 匀减速段
            t_local = ti - (4*ta + tv)
            v[i] = vmax - 0.5 * jmax * ta**2 - amax * t_local
        else:
            # 减减速段
            t_local = ti - (5*ta + tv)
            v[i] = vmax - 0.5 * jmax * ta**2 - amax * ta - amax * t_local + 0.5 * jmax * t_local**2
    
    return t, v

# 使用示例
t, v = s_curve_plan(v0=0, vmax=100, amax=500, jmax=2000, total_time=1.0)
print("速度规划完成,速度范围:", v.min(), "~", v.max())

一点补充:上面的代码为了清晰,把每段都写成了显式公式。实际工程中,我更喜欢用状态机的方式来实现——每个周期根据当前阶段和剩余时间,查表或递推计算。这样代码更易维护,也方便处理异常情况(比如中途急停)。

嗯,S形曲线的基础就聊到这儿。你可能会问:如果运动距离很短,达不到最大速度怎么办?或者,怎么在运动过程中实时调整目标位置?这些问题,我们后面会一一拆解。先把7段式的结构吃透,后面的内容就好理解了。