伺服电机、步进电机通用的S曲线及梯形加减速控制源码,十分经典,有中文注释及实现原理说明
伺服电机、步进电机通用的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轴在急停时不震得像个筛子。
(代码节选自实际工程,测试数据取自某雕刻机控制板,为防泄密已做参数混淆)
