3. S型曲线加减速规划

任务目标

在直线插补中实现S型曲线加减速规划,替代传统梯形加减速,消除加速度在启停阶段的突变。通过对比两种规划在相同位移、速度限制下的加速度曲线,验证S型规划对加加速度(Jerk)的约束效果。

核心实现

import numpy as np
import matplotlib.pyplot as plt

def s_curve_profile(s_total, v_max, a_max, j_max, dt=0.001):
    """S型加减速规划(7段式),返回时间序列与运动参数"""
    # 计算临界条件:能否达到最大速度
    s_acc = v_max * v_max / a_max  # 匀加速段所需位移(忽略加加速度段近似)
    # 实际按加加速度约束计算
    t_a = a_max / j_max            # 加加速度段时间
    s_a = j_max * t_a**3           # 单个加加速度段位移(从0加速到a_max)
    # 若两段加加速度位移之和超过总位移的一半,则达不到最大速度
    if 2 * s_a < s_total / 2:
        # 能达到最大速度:7段式
        t_v = (s_total - 2 * (s_a + v_max * t_a)) / v_max
        t_total = 4 * t_a + 2 * t_a + t_v  # 加加速+匀加速+减加速+匀速+加减速+匀减速+减减速
        t_total = 4 * t_a + 2 * (v_max / a_max - t_a) + t_v
    else:
        # 达不到最大速度:仅加加速-减加速-加减速-减减速(无匀速段)
        # 实际简化为三角S型,此处用数值迭代求时间
        t_total = 2 * (2 * s_total / j_max) ** (1/3)
        t_a = t_total / 4
        v_max = j_max * t_a * t_a / 2
        a_max = j_max * t_a
        t_v = 0

    # 生成时间轴
    t = np.arange(0, t_total, dt)
    n = len(t)
    v = np.zeros(n)
    a = np.zeros(n)
    j = np.zeros(n)

    # 7段参数
    t1 = t_a
    t2 = t1 + (v_max / a_max - t_a) if v_max / a_max > t_a else t1
    t3 = t2 + t_a
    t4 = t3 + t_v
    t5 = t4 + t_a
    t6 = t5 + (v_max / a_max - t_a) if v_max / a_max > t_a else t5
    t7 = t6 + t_a

    for i, ti in enumerate(t):
        if ti < t1:
            j[i] = j_max
            a[i] = j_max * ti
            v[i] = 0.5 * j_max * ti**2
        elif ti < t2:
            j[i] = 0
            a[i] = a_max
            v[i] = 0.5 * j_max * t1**2 + a_max * (ti - t1)
        elif ti < t3:
            j[i] = -j_max
            a[i] = a_max - j_max * (ti - t2)
            v[i] = 0.5 * j_max * t1**2 + a_max * (t2 - t1) + a_max * (ti - t2) - 0.5 * j_max * (ti - t2)**2
        elif ti < t4:
            j[i] = 0
            a[i] = 0
            v[i] = v_max
        elif ti < t5:
            j[i] = -j_max
            a[i] = -j_max * (ti - t4)
            v[i] = v_max - 0.5 * j_max * (ti - t4)**2
        elif ti < t6:
            j[i] = 0
            a[i] = -a_max
            v[i] = v_max - 0.5 * j_max * t1**2 - a_max * (ti - t5)
        else:
            j[i] = j_max
            a[i] = -a_max + j_max * (ti - t6)
            v[i] = 0.5 * j_max * (t7 - ti)**2

    s = np.cumsum(v) * dt
    return t, s, v, a, j

def trapezoidal_profile(s_total, v_max, a_max, dt=0.001):
    """梯形加减速:匀加速-匀速-匀减速"""
    t_acc = v_max / a_max
    s_acc = 0.5 * a_max * t_acc**2
    if 2 * s_acc > s_total:
        t_acc = np.sqrt(s_total / a_max)
        v_max = a_max * t_acc
        t_total = 2 * t_acc
        t_v = 0
    else:
        t_v = (s_total - 2 * s_acc) / v_max
        t_total = 2 * t_acc + t_v

    t = np.arange(0, t_total, dt)
    n = len(t)
    v = np.zeros(n)
    a = np.zeros(n)

    for i, ti in enumerate(t):
        if ti < t_acc:
            a[i] = a_max
            v[i] = a_max * ti
        elif ti < t_acc + t_v:
            a[i] = 0
            v[i] = v_max
        else:
            a[i] = -a_max
            v[i] = v_max - a_max * (ti - t_acc - t_v)

    s = np.cumsum(v) * dt
    return t, s, v, a

# 参数设置
s_total = 0.05      # 总位移 50mm
v_max = 0.2         # 最大速度 200mm/s
a_max = 1.0         # 最大加速度 1000mm/s²
j_max = 50.0        # 最大加加速度 50000mm/s³

# 生成两种规划
t_s, s_s, v_s, a_s, j_s = s_curve_profile(s_total, v_max, a_max, j_max)
t_t, s_t, v_t, a_t = trapezoidal_profile(s_total, v_max, a_max)

# 对比图
fig, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
axes[0].plot(t_s, v_s * 1000, 'b-', label='S型速度')
axes[0].plot(t_t, v_t * 1000, 'r--', label='梯形速度')
axes[0].set_ylabel('速度 (mm/s)')
axes[0].legend()
axes[0].grid(True)

axes[1].plot(t_s, a_s * 1000, 'b-', label='S型加速度')
axes[1].plot(t_t, a_t * 1000, 'r--', label='梯形加速度')
axes[1].set_ylabel('加速度 (mm/s²)')
axes[1].set_xlabel('时间 (s)')
axes[1].legend()
axes[1].grid(True)

plt.tight_layout()
plt.show()

# 输出关键指标
print(f"S型总时间: {t_s[-1]:.4f}s, 梯形总时间: {t_t[-1]:.4f}s")
print(f"S型最大|Jerk|: {np.max(np.abs(j_s)):.1f} mm/s³")
print(f"梯形最大|Jerk|: {np.max(np.abs(np.diff(a_t)/0.001)):.1f} mm/s³")

运行与验证

环境依赖:Python 3.8+,numpy,matplotlib。

运行方式:将代码保存为 s_curve_compare.py,执行 python s_curve_compare.py。

验证方法:

  1. 观察速度曲线:S型曲线在启停阶段呈平滑过渡,梯形曲线为直线上升/下降;
  2. 观察加速度曲线:S型加速度连续变化无突变,梯形加速度在启停瞬间存在阶跃;
  3. 终端输出S型最大Jerk为设定值50 mm/s³,梯形Jerk远大于该值(理论上接近无穷大,数值上取决于采样间隔);
  4. 两种规划的总位移均收敛至50mm,可通过 s_s[-1] 与 s_t[-1] 验证。

关键说明

本实现采用7段式S型规划(加加速-匀加速-减加速-匀速-加减速-匀减速-减减速),当位移不足以达到最大速度时自动退化为无匀速段的4段式。梯形规划作为对照组,其加速度在t=0和t=t_acc处存在无限大加加速度(实际表现为阶跃),这正是S型规划要消除的机械冲击源。直线插补中,将规划得到的位置序列 s 按时间间隔发送给伺服即可完成运动控制。