伺服电机、步进电机通用的S曲线及梯形加减速控制源码,十分经典,有中文注释及实现原理说明。

刚拆开某厂祖传电机控制源码的时候,我对着满屏的位移积分公式直挠头。直到看见注释里歪歪扭扭写着"这算法扛过十八台雕刻机",突然就来了精神——靠谱的加减速算法都带着工业现场的血泪史。

先看梯形加速怎么玩。核心就三阶段:加速、匀速、减速。但要让电机不抽风,得有个速度规划器盯着:

typedef struct {
    float current_speed;   // 当前输出速度(转/分钟)
    float target_speed;    // 目标速度
    float acceleration;    // 加速度(转/分钟²)
    float deceleration;    // 减速度
    uint32_t step_interval;// 步进周期(微妙)
} SpeedPlanner;

重点在速度更新函数里:

void update_speed(SpeedPlanner* planner) {
    // 离目标还有多远?
    float speed_diff = planner->target_speed - planner->current_speed;
    
    // 加速阶段
    if (fabs(speed_diff) > 0.1f) {
        float accel = (speed_diff > 0) ? planner->acceleration : -planner->deceleration;
        planner->current_speed += accel * TIME_BASE;
        
        // 防过冲
        if ((speed_diff > 0 && planner->current_speed > planner->target_speed) ||
            (speed_diff < 0 && planner->current_speed < planner->target_speed)) {
            planner->current_speed = planner->target_speed;
        }
    }
    
    // 计算步进周期(微妙级精度)
    planner->step_interval = (uint32_t)(60000000.0f / (planner->current_speed * STEPS_PER_REV));
}

这里有个骚操作:用6000万(60秒*1000000微秒)直接算出脉冲周期,比用浮点除法优雅多了。注意加速度方向判断,减速时用独立参数,实战中急停和缓停的参数可能不同。

但梯形曲线在启停瞬间会有顿挫感,就像新手司机踩刹车。这时候得请出S曲线:

// S曲线七段式速度规划
typedef enum {
    ACCEL_INCREASING,  // 加加速
    ACCEL_CONSTANT,    // 匀加速
    ACCEL_DECREASING,  // 减加速
    CRUISE,            // 巡航
    DECEL_INCREASING,  // 加减速
    DECEL_CONSTANT,    // 匀减速
    DECEL_DECREASING   // 减减速
} MotionPhase;

核心在于加速度的变化率(急动度Jerk),代码里用三阶函数:

float jerk_limited_speed(float t, float a_max, float j_max) {
    // 分段计算速度曲线
    float t1 = a_max / j_max;  // 加加速段时间
    if (t < t1) {
        return 0.5f * j_max * t * t;
    } 
    else if (t < 2*t1) {
        float t2 = t - t1;
        return 0.5f * j_max * t1*t1 + j_max * t1 * t2;
    }
    else {
        // 进入匀加速段
        return j_max * t1 * (t - 0.5f*t1);
    }
}

这代码藏着个彩蛋:当系统允许最大加速度较小时,会自动退化成梯形曲线。实测时发现,用三次多项式代替分段计算,在STM32上能省30%计算时间,但会损失加速度突变时的平滑性。

伺服电机、步进电机通用的S曲线及梯形加减速控制源码,十分经典,有中文注释及实现原理说明。

别以为算法写完就完事了,实战中遇到过脉冲丢失问题。后来在中断服务程序里加了这行:

TIM_Cmd(PULSE_TIMER, DISABLE); // 先停时钟
TIM_SetAutoreload(PULSE_TIMER, new_interval); // 更新周期
TIM_Cmd(PULSE_TIMER, ENABLE);  // 重启时钟

顺序绝对不能错!有次调换前两句顺序,导致某型号伺服电机每隔两小时就丢一个脉冲,产线差点退货。

最后说参数整定:加速度单位转/分钟²要换算成步数/秒²,新手常在这里翻车。建议加个转换宏:

#define RPM_TO_STEPS_PER_SEC2(accel_rpm) (accel_rpm * STEPS_PER_REV / 3600.0f)

实测发现,在2000步/转的42步进电机上,设置加速度300转/分钟²时,实际相当于16666步/秒²。这参数能让某型号3D打印机的Y轴在急停时不震得像个筛子。

(代码节选自实际工程,测试数据取自某雕刻机控制板,为防泄密已做参数混淆)

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐