4、梯形加减速算法:梯形曲线数学模型、速度规划、位置计算、C语言实现与参数调优

各位工程师朋友,咱们今天聊聊步进电机驱动里最经典、也最实用的一个算法——梯形加减速。说实话,我入行那会儿,第一个独立负责的项目就是搞一个贴片机的送料轴。当时不懂什么加减速,直接给电机发固定频率的脉冲,结果呢?电机要么丢步,要么震动得像要散架。后来才明白,没有速度规划,步进电机就是个“倔驴”。

梯形加减速,说白了就是让电机启动时慢慢加速,快到目标速度时匀速跑,快停的时候再慢慢减速。这个曲线画出来像个梯形,所以叫这名。它不像S形曲线那么平滑,但胜在简单、计算量小、容易调。在大多数工业场景里,它完全够用。

4.1 梯形曲线的数学模型

我们先看数学。梯形曲线分三个阶段:加速段匀速段减速段

假设我们设定:

  • 起始速度 \( v_0 \)(通常为0)
  • 目标速度 \( v_{max} \)
  • 加速度 \( a \)(正值)
  • 减速度 \( d \)(正值,通常等于 \( a \))
  • 总位移 \( S \)

那么加速段时间 \( t_a = \frac{v_{max} - v_0}{a} \),减速段时间 \( t_d = \frac{v_{max} - v_0}{d} \)。

这里有个关键点:并不是所有运动都能达到目标速度。如果总位移太短,电机刚加速就要减速,形成“三角形”曲线。我遇到过不少新手,上来就设一个很高的目标速度,结果电机根本没时间跑起来,反而更慢。

核心公式:

加速段位移:\( S_a = v_0 t_a + \frac{1}{2} a t_a^2 \)

匀速段位移:\( S_c = v_{max} \times t_c \)

减速段位移:\( S_d = v_{max} t_d - \frac{1}{2} d t_d^2 \)

总位移:\( S = S_a + S_c + S_d \)

嗯,这里要注意,如果 \( S_a + S_d > S \),说明没有匀速段。这时候需要重新计算一个实际能达到的最高速度 \( v_{peak} \)。

4.2 速度规划:什么时候加速,什么时候减速

速度规划的核心就一句话:根据剩余距离决定当前该加速还是减速

我个人习惯用“查表法”来做速度规划。提前算好每个脉冲周期对应的速度值,存成一个数组。运行时直接查表,省去实时计算的开销。这在资源有限的MCU上特别实用。

举个例子,假设我们要走1000步,目标速度是2000步/秒,加速度是500步/秒²。那么:

  1. 先算加速到2000步/秒需要4秒,对应4000步?不对,这里单位要统一。实际上步进电机的速度单位是“步/秒”,加速度是“步/秒²”。
  2. 加速段步数 = \( \frac{v_{max}^2}{2a} = \frac{2000^2}{2 \times 500} = 4000 \)步。
  3. 总步数才1000步,显然达不到2000步/秒。所以实际最高速度要重新算。

这种“先判断能不能达到目标速度”的步骤,我建议放在运动开始前做。我在一个项目中吃过亏,运行时才判断,结果每次加减速都有延迟,导致定位不准。

避坑指南:我曾经在速度规划时忽略了“减速度不等于加速度”的情况。比如有些负载在减速时惯性大,需要更小的减速度。如果你设的减速度太大,电机可能会过冲。所以,我建议把加速度和减速度分开配置,别偷懒。

4.3 位置计算:每个脉冲该什么时候发

位置计算,说白了就是决定每个脉冲的间隔时间。步进电机每收到一个脉冲,就走一步。所以速度越快,脉冲间隔越短。

梯形加减速的位置计算,通常用“定时器比较法”。我们用一个定时器产生PWM波,每个PWM周期更新一次比较值。比较值就是当前速度对应的定时器计数值。

公式很简单:

定时器计数值 = 定时器时钟频率 / 当前速度

比如定时器时钟是1MHz,当前速度是1000步/秒,那么计数值就是1000。也就是说,每1000个时钟周期发一个脉冲。

在加速阶段,每次更新时,计数值逐渐减小(速度变快)。在减速阶段,计数值逐渐增大(速度变慢)。匀速阶段,计数值保持不变。

这里有个细节:计数值必须是整数。所以实际速度会有量化误差。我一般建议用“累加余数法”来补偿。就是每次计算时,把小数部分累加起来,累到超过1就多减一个计数值。这样平均速度更准。

4.4 C语言实现:一个可用的梯形加减速模块

下面我给出一段我常用的梯形加减速代码框架。它不依赖具体硬件,只做逻辑计算。

// 梯形加减速状态机
typedef enum {
    ACCEL,   // 加速
    CRUISE,  // 匀速
    DECEL,   // 减速
    STOP     // 停止
} MotionState;

typedef struct {
    int32_t target_pos;      // 目标位置(步数)
    int32_t current_pos;     // 当前位置
    float speed;             // 当前速度(步/秒)
    float max_speed;         // 目标速度
    float accel;             // 加速度(步/秒²)
    float decel;             // 减速度
    float step_per_sec;      // 定时器频率
    MotionState state;       // 当前状态
} TrapezoidMotion;

// 初始化
void Trapezoid_Init(TrapezoidMotion *m, float max_speed, float accel, float decel) {
    m->max_speed = max_speed;
    m->accel = accel;
    m->decel = decel;
    m->speed = 0;
    m->current_pos = 0;
    m->state = STOP;
}

// 每次定时器中断调用此函数,返回下一个脉冲的定时器计数值
uint32_t Trapezoid_Update(TrapezoidMotion *m) {
    if (m->state == STOP) return 0;

    // 计算剩余距离
    int32_t remaining = m->target_pos - m->current_pos;

    // 判断是否需要减速
    float decel_dist = (m->speed * m->speed) / (2 * m->decel);
    if (remaining <= decel_dist && m->state != DECEL) {
        m->state = DECEL;
    }

    // 根据状态更新速度
    switch (m->state) {
        case ACCEL:
            m->speed += m->accel / m->step_per_sec;
            if (m->speed >= m->max_speed) {
                m->speed = m->max_speed;
                m->state = CRUISE;
            }
            break;
        case CRUISE:
            // 速度不变
            break;
        case DECEL:
            m->speed -= m->decel / m->step_per_sec;
            if (m->speed <= 0) {
                m->speed = 0;
                m->state = STOP;
                return 0;
            }
            break;
        default:
            break;
    }

    // 更新位置
    m->current_pos++;

    // 计算定时器计数值
    if (m->speed > 0) {
        return (uint32_t)(m->step_per_sec / m->speed);
    } else {
        return 0;
    }
}

这段代码我用了很多年,核心逻辑没变过。你可能会问:为什么不用浮点数?嗯,这里用浮点数是为了清晰。实际产品中,我建议全部转成定点数,避免浮点运算的开销。

4.5 参数调优:让电机跑得又稳又快

参数调优是梯形加减速里最考验经验的部分。我总结了几条原则:

参数 调大影响 调小影响 我的建议
加速度 启动快,但可能丢步 启动慢,但运行平稳 从电机额定扭矩的80%开始试
目标速度 效率高,但震动大 效率低,但噪音小 不要超过电机自振频率的1/3
减速度 刹车快,但可能过冲 刹车慢,定位时间长 通常比加速度小10%-20%

调优步骤我一般这样走:

  1. 先调加速度:从较小的值开始,逐渐增大,直到电机出现丢步或异响,然后回调10%。
  2. 再调目标速度:在加速度确定后,逐步提高目标速度,观察电机震动和噪音。找到那个“临界点”。
  3. 最后调减速度:让电机跑一段长距离,看停止位置是否准确。如果过冲,就减小减速度。

警告:千万不要在电机运行时修改加减速参数!我见过有人为了“动态调整”,在运动过程中改了加速度,结果电机直接失步,撞坏了机械限位。要改参数,先让电机停下来。

另外,有个小技巧:用示波器看电机电流波形。如果电流波形在加速阶段出现“塌陷”,说明加速度太大,电机扭矩不够。如果波形平滑,说明参数合理。这个方法比听声音更靠谱。

好了,梯形加减速就讲到这里。下节课我们聊聊更平滑的S形加减速,那个曲线更优雅,但计算量也更大。到时候我会对比两者的适用场景,帮你做选择。