日期:2023-07-11 18:01:00浏览量:250
深圳市鑫希田机电有限公司(艾德克尔ADDKA)是集工业自动化零部件产品、互联网、供应链服务于一体的综合性工厂直销+电商平台, 2005年创立于粤港澳大湾区四大中心城市之一 、国家物流枢纽、国际性综合交通枢纽 、国际科技产业创新中心 、中国三大全国性金融中心之一 深圳市,以代理销售日本松下、三菱、ABB等工业自动化传动产品,为使产品质量不断提高,引进一批高素质的专业技术人员,引进100台/套 日本哈马仪滚齿机、精密全自动磨床、热处理设备、CNC加工中心等生产设备;日本TIT精密齿轮检测设备、铬氏硬度仪、维氏硬度仪等,至力研发、生产高效、节能、环保型的工业传动产品。 2020年为适应客户需要,满足客户期望一站式采购工业自动化零部件需求,公司转型升级,旨在构建以“工业部件+工厂+互联网+供应链”为主体的全新工业品营销方式,打造工业自动化零部件自主生产、线上线下交易、服务、技术支持一体化平台。具有高性价比,正品保证,快速出货,免费3D下载等特点。我们多样化的产品种类与充足的库存使客户可以安心、便捷地搜寻和购买到合适的产品。、充足的现货储备,所有产品质量保障, 深圳市鑫希田机电有限公司为客户提供专业的采购服务,节省客户的时间和成本。
本文大部分内容来自《硬石电机控制专题指导手册》
一、引出
1、步进电机速度,是根据输入的脉冲信号的变化来改变的。理论上,给一个脉冲,步进电机就旋转一个步距角。但实际上,如果脉冲信号变化太快,步进电机由于内部的反向电动势的阻尼作用,转子与定子之间的磁反应将跟随不上电信号的变化,将导致堵转和丢步。
2、步进电机运行起来后,如果要立即停止,由于所带负载具有惯性,可能会导致过冲。因此步进电机需要做加减速控制。通常,完成步进电机的加减速时间为300ms以上。
二、分类
加减速算法一般分为两种,一种是加速度不变的梯形算法,该种算法运算简单,可是因为加速度一直不变,因此运动过程不是很平滑,在启动、停止、高速运动段会产生很大的冲击和振动及噪音。速度曲线如下:
还有一种是加速度会改变的“S”型曲线算法,随着运动的阶段,加速度实时变化,使得运动过程更平滑。速度曲线如下:
根据加速度变化规律的不同,“S”型曲线又分为Sin曲线、2次曲线、循环曲线、3次曲线。
总之加减速算法的目的在于,实现当前速度到目标速度的平稳过渡,在这个过程中速度变化得越平滑,则电机运动得也越平滑。在算法选用中,根据自己的运动要求来选择即可。
三、AVR446_Linear speed control of stepper motor文档解析(使用STM32实现)
上图是单片机发送给步进电机驱动器的脉冲信号,我们控制两个脉冲信号之间的时间间隔就可以控制脉冲频率,脉冲频率也就是电机转速。
在stm32单片机中,我们一般使用定时器来产生脉冲,如果stm32主频位72M,我们把定时器36分频,那么该定时器计数一次就是1/2M秒。现在我们得到了时间的基本单位了,我把1/2M秒设为tt,那么上图中的第一个脉冲间隔就表示为c0*tt,第二个脉冲间隔表示为c1*tt。也就是说要用stm32产生上面的波形,我先把定时器比较值设为c0,一次计数完成后再把定时器比较值设为c1,这样就可以产生不同间隔的脉冲了。下面来看看c0、c1......cn的值怎么确定。
下面是电机运动过程中加速度、速度、位置的关系:
电机某个时刻的速度,可以对加速度进行积分求得。因为我们这里是线性加速,加速度为定值,所以加速度乘上时间就是速度了:
电机运动后的位置可以对速度进行积分求得。
上式中,n表示脉冲个数,α表示电机步距角。发送一个脉冲电机移动一个步距角,发送n个脉冲,电机的位置即为nα。
把上式变形,求电机运动到该位置所花时间:
用运动到n+1个脉冲位置所花时间减去tn,就可以得到第n个脉冲的时间间隔,cn*tt。(cn是定时器比较值,tt为定时器计数间隔)
上式中,α是电机固定的,电机加速度是指定的。因此可以直接使用上式计算第一个脉冲间隔、第二个脉冲间隔、第三个脉冲间隔.....然后赋值给stm32定时器的比较值就可以了。可是上式有3个开方,计算会很慢,所以要想办法简化。
使用麦克劳林公式:
把我们的公式带入:
最后简化得到:
接下来讨论一下的加速度的变化:
由前面的
和
得到下面的
和
组合上面两式,得到:
由上式可以知道,当速度ωn不变,加速度与n成反比。即:电机加速到给定最大速度所需要的步数与电机加速度成反比。设加速过程最大速度为ω1,减速过程最大速度为ω2。因为加速过程的最大速度和开始减速的速度相同,即:ω1=ω2,因此:
四、程序思路
1、计算电机最大速度时的定时器计数值
由上面推到的数学模型,我们来实现梯形曲线的运动过程。控制步进电机在给定步数的情况下,速度从零开始加速,到达最大速度后开始匀速运动,运动到一定步数后开始减速,最后减速到零停止。曲线示意图如下:
为了避免使用浮点数运算,把代码中涉及的speed、accel、decel这3个参数都做了放大10倍处理。由上面所说的,stm32定时器的计数频率为ft,基本时间单位为tt,第n个脉冲的时间间隔为cn*tt=cn/ft,步距角是α。
因此,到n个脉冲时转动步距角α,所需时间为cn/ft,即速度ω=α/(cn/ft),cn=α*ft/ω。
当速度值最大等于speed时,脉冲时间间隔min_delay最小。min_delay=cn=10*α*ft/speed。(因为speed放大了10倍,所以分子也要放大10倍,speed的时间单位为0.1rad/sec。)
2、 计算电机第一步的定时器比较值
由前面的公式:
则
乘以0.676是为了修正数据推算过程中的误差。
step_delay=c0=0.676*ft*sqrt(2*α*10/accel)(accel也被放大了10倍,单位为0.1rad/sec^2)
3、根据最大速度和给定步数的不同,加速过程有两种情况:
A、持续加速到最大速度。
B、还没有加速到最大速度就要开始减速。
1)、持续加速到最大速度
上图中,speed、accel、decel、step是已知参数,max_s_lim、accel_lim、decel_val是待求参数。
max_s_lim是加速到最大速度所需步数,由上面的推导公式
则max_s_lim的计算公式为:
max_s_lim = speed^2 / (2*alpha*accel*10)
accel_lim是忽略最大速度speed限制,速度可以一直增大,直到开始减速时的速度,由前面推导的
可以得到n1的计算公式:
n1 = (n1+n2)decel / (accel + decel)
如果max_s_lim
2)、还没有加速到最大速度就要开始减速
如果max_s_lim>accel_lim,说明加速度受限于减速的开始。减速行程decel_val的计算公式为:
4、定时器中断处理
定时器中断产生步进脉冲并且只有在步进电机移动时进入。根据步进电机的4种运行状态
在定时器中断中设置4个状态的状态机。STOP为停止状态,ACCEL为加速状态,RUN为匀速状态(对应最大速度speed)、DECEL为加速状态。
5、定时器比较值计算
步进电机每走一步,下一步的时间间隔必须计算得出。计算结果会包含一个系数和一个余数,为了提高精度,余数要保留并包含到下一次计算当中。
由前面的推导:
已经计算出了第一步的定时器比较值,下一步就可以使用上式计算:
其中,rest为上次计算的余数。new_rest是本次计算的余数,计算出来用于下次计算。
五、代码(来自硬石例程:YSF1_HAL_MOTOR-008.57&42步进电机梯形加减速实现)
1、main函数
int main(void)
{
SystemClock_Config(); //系统时钟初始化
KEY_GPIO_Init(); //按键IO初始化
STEPMOTOR_TIMx_Init(); //定时器初始化,初始化为比较输出模式,当定时器计数值自加到等于比较值就翻转STEP的电平
while (1)
{
if(KEY_StateRead()==KEY_DOWN) //如果按键被按键,就运动一段距离
{
STEPMOTOR_AxisMoveRel(6400*-2, 15000 , 15000 , 1200); //电机开始运行函数
}
}
}
2、电机运行函数STEPMOTOR_AxisMoveRel和中断服务函数
/* 电机旋转方向 */
#define CW 0 // 顺时针
#define CCW 1 // 逆时针
/* 电机运动状态 */
#define STOP 0 // 加减速曲线状态:停止
#define ACCEL 1 // 加减速曲线状态:加速阶段
#define DECEL 2 // 加减速曲线状态:减速阶段
#define RUN 3 // 加减速曲线状态:匀速阶段
/* 电机参数 */
#define SystemClock 72000000 //系统主频
#define TIM_PRESCALER 3 //定时器分频系数
#define T1_FREQ (SystemClock/(TIM_PRESCALER+1)) // 定时器计数频率,ft值
#define FSPR 200 // 步进电机单圈步数
#define MICRO_STEP 32 // 步进电机驱动器细分数
#define SPR (FSPR*MICRO_STEP) // 旋转一圈需要的脉冲数
#define ALPHA ((float)(2*3.14159/SPR)) // 电机步距角,弧度值
/* 电机驱动模块IO控制 */
#define STEPMOTOR_OUTPUT_ENABLE() HAL_GPIO_WritePin(STEPMOTOR_ENA_PORT,STEPMOTOR_ENA_PIN,GPIO_PIN_SET)
#define STEPMOTOR_OUTPUT_DISABLE() HAL_GPIO_WritePin(STEPMOTOR_ENA_PORT,STEPMOTOR_ENA_PIN,GPIO_PIN_RESET)
#define STEPMOTOR_DIR_FORWARD() HAL_GPIO_WritePin(STEPMOTOR_DIR_PORT,STEPMOTOR_DIR_PIN,GPIO_PIN_SET)
#define STEPMOTOR_DIR_REVERSAL() HAL_GPIO_WritePin(STEPMOTOR_DIR_PORT,STEPMOTOR_DIR_PIN,GPIO_PIN_RESET)
typedef struct {
__IO uint8_t run_state ; // 电机旋转状态
__IO uint8_t dir ; // 电机旋转方向
__IO int32_t step_delay; // 下个脉冲周期(时间间隔),启动时为加速度
__IO uint32_t decel_start; // 启动减速位置
__IO int32_t decel_val; // 减速阶段步数
__IO int32_t min_delay; // 最小脉冲周期(最大速度,即匀速段速度)
__IO int32_t accel_count; // 加减速阶段计数值
}speedRampData;
speedRampData srd = {STOP,CW,0,0,0,0,0}; // 加减速曲线变量
__IO int32_t step_position = 0; // 电机当前位置
/**
* 函数功能: 让电机运动给定的步数
* 输入参数: step: 移动的步数 (正数为顺时针,负数为逆时针).
accel 加速度,实际值为accel*0.1*rad/sec^2
decel 减速度,实际值为decel*0.1*rad/sec^2
speed 最大速度,实际值为speed*0.1*rad/sec
* 返 回 值: 无
* 说 明: 以给定的步数移动步进电机,先加速到最大速度,然后在合适位置开始
* 减速至停止,使得整个运动距离为指定的步数。如果加减速阶段很短并且
* 速度很慢,那还没达到最大速度就要开始减速
*/
void STEPMOTOR_AxisMoveRel(__IO int32_t step, __IO uint32_t accel, __IO uint32_t decel, __IO uint32_t speed)
{
__IO uint16_t tim_count;
__IO uint32_t max_s_lim; // 达到最大速度speed时的移动步数
__IO uint32_t accel_lim; // 必须要开始减速时的移动步数。从该值到step之间为减速阶段
if(step < 0) // 如果步数为负数,说明逆时针旋转
{
srd.dir = CCW; // 逆时针方向旋转
STEPMOTOR_DIR_REVERSAL(); // 改变驱动模式的DIR电平
step =-step; // 获取步数绝对值
}
else
{
srd.dir = CW; // 顺时针方向旋转
STEPMOTOR_DIR_FORWARD(); // 改变驱动模式的DIR电平
}
if(step == 1) // 如果步数为1,只移动一步
{
srd.accel_count = -1; // 只移动一步
srd.run_state = DECEL; // 减速状态
srd.step_delay = 1000; // 定时器比较值用固定值1000
}
else if(step != 0) // 如果目标运动步数不为0,大于1
{
// 计算当速度值增加到最大等于speed时,定时器的比较值。
// min_delay=cn=10*α*ft/speed
srd.min_delay = (int32_t)( ((float)(10*ALPHA*T1_FREQ))/speed );
// 计算第一个(c0) 的定时器比较值,其中accel单位为0.1rad/sec^2
// step_delay=c0=0.676*ft*sqrt(2*α*10/accel)
srd.step_delay = (int32_t)( ((float)(0.676*T1_FREQ)) * sqrt(((float)(2*10*ALPHA)) / accel) );
// 计算达到最大速度speed时的移动步数
// max_s_lim = speed^2 / (2*alpha*accel*10)
max_s_lim = (uint32_t)( speed*speed / (((float)(2*ALPHA))*accel*10) );
// 如果max_s_lim=0,说明被四舍五入掉了,改为1
if(max_s_lim == 0){
max_s_lim = 1;
}
// 计算多少步之后必须开始减速
// n1 = (n1+n2)decel / (accel + decel)
accel_lim = (uint32_t)(step*decel/(accel+decel));
// 我们必须加速至少1步才能才能开始减速.
if(accel_lim == 0){
accel_lim = 1;
}
// 计算减速阶段步数,为负数
if(accel_lim <= max_s_lim){ // 如果加速过程不受限于最大速度,即还没加速到最大速度就要开始减速
srd.decel_val = accel_lim - step;
}
else{ // 如果加速过程受限于最大速度,即可以加速到最大速度
srd.decel_val = -(max_s_lim*accel/decel);
}
// 当只剩下一步我们必须减速
if(srd.decel_val == 0){
srd.decel_val = -1;
}
// 计算开始减速时的步数,当电机运动到此就进入减速阶段
srd.decel_start = step + srd.decel_val;
// 如果最大速度很慢,我们就不需要进行加速运动,直接以speed开始转动
if(srd.step_delay <= srd.min_delay){
srd.step_delay = srd.min_delay;
srd.run_state = RUN; // 不必进入加速阶段,直接进入匀速运行阶段
}
else{
srd.run_state = ACCEL; // 否则就要进行加速
}
srd.accel_count = 0; // 复位加速度计数值
}
/* 把计算出的电机第一步的定时器计数值赋值给MCU定时器,并使能定时器,使能电机 */
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR); // 获得定时器当前计数值
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay); // 把当前计数值+比较值,设置翻转值
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_ENABLE); // 使能定时器通道
STEPMOTOR_OUTPUT_ENABLE(); //使能电机驱动模块EN引脚
}
/**
* 函数功能: 定时器中断服务函数
* 说 明: 功能有两个:设置定时器计数值、计算电机下一步的定时器计数值
*/
void STEPMOTOR_TIMx_IRQHandler(void) //定时器中断处理函数
{
__IO uint16_t tim_count=0;
uint16_t new_step_delay=0; // 保存新(下)一个延时周期
__IO static uint16_t last_accel_delay=0; // 加速过程中最后一次延时(脉冲周期).
__IO static uint32_t step_count = 0; // 总移动步数计数器
__IO static int32_t rest = 0; // 记录new_step_delay中的余数,提高下一步计算的精度
__IO static uint8_t i=0; // 定时器使用翻转模式,需要进入两次中断才输出一个完整脉冲
if(__HAL_TIM_GET_IT_SOURCE(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx) !=RESET)
{
__HAL_TIM_CLEAR_IT(&htimx_STEPMOTOR, STEPMOTOR_TIM_IT_CCx); // 清除定时器中断
// 设置比较值
tim_count=__HAL_TIM_GET_COUNTER(&htimx_STEPMOTOR); // 获得定时器当前计数值
__HAL_TIM_SET_COMPARE(&htimx_STEPMOTOR,STEPMOTOR_TIM_CHANNEL_x,tim_count+srd.step_delay); // 把当前计数值+比较值,设置翻转值
i++; // 定时器中断次数计数值,同样的翻转值要使用两次,STEP翻转两次,进两次中断服务函数,这样才是一个完整的脉冲
if(i==2) // 2次,说明已经输出一个完整脉冲
{
i=0; // 清零定时器中断次数计数值
switch(srd.run_state) // 加减速曲线阶段
{
case STOP:
step_count = 0; // 清零步数计数器
rest = 0; // 清零余值
TIM_CCxChannelCmd(STEPMOTOR_TIMx, STEPMOTOR_TIM_CHANNEL_x, TIM_CCx_DISABLE); // 失能定时器
__HAL_TIM_CLEAR_FLAG(&htimx_STEPMOTOR, STEPMOTOR_TIM_FLAG_CCx);
STEPMOTOR_OUTPUT_DISABLE(); // 失能EN引脚
break;
case ACCEL:
step_count++; // 移动步数加1
if(srd.dir==CW)
{
step_position++; // 电机位置加1
}
else
{
step_position--; // 电机位置减1
}
srd.accel_count++; // 加速过程移动步数加1
new_step_delay = srd.step_delay - (((2 *srd.step_delay) + rest)/(4 * srd.accel_count + 1));//计算下一步的比较值
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
if(step_count >= srd.decel_start) // 检查是否应该开始减速
{
srd.accel_count = srd.decel_val; // 加速计数值赋值为减速阶段总步数
srd.run_state = DECEL; // 从下个脉冲开始进入减速阶段
}
else if(new_step_delay <= srd.min_delay) // 检查是否到达期望的最大速度
{
last_accel_delay = new_step_delay; // 保存加速过程中的最后一次比较值,减速阶段会使用一次
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)
rest = 0; // 清零余值
srd.run_state = RUN; // 设置为匀速运行状态
}
break;
case RUN:
step_count++; // 移动步数加1
if(srd.dir==CW)
{
step_position++; // 电机位置加1
}
else
{
step_position--; // 电机位置减1
}
new_step_delay = srd.min_delay; // 使用min_delay(对应最大速度speed)
if(step_count >= srd.decel_start) // 需要开始减速
{
srd.accel_count = srd.decel_val; // 加速计数值赋值为减速阶段总步数
new_step_delay = last_accel_delay;// 加阶段最后的比较值做为减速阶段的起始比较值
srd.run_state = DECEL; // 状态改变为减速
}
break;
case DECEL:
step_count++; // 移动步数加1
if(srd.dir==CW)
{
step_position++; // 电机位置加1
}
else
{
step_position--; // 电机位置减1
}
srd.accel_count++; //减速阶段移动步数加一,它的初始值为减速阶段总步数,是负值
new_step_delay = srd.step_delay - (((2 * srd.step_delay) + rest)/(4 * srd.accel_count + 1)); //计算下一步的比较值
rest = ((2 * srd.step_delay)+rest)%(4 * srd.accel_count + 1);// 计算余数,下次计算补上余数,减少误差
if(srd.accel_count >= 0) //当减速阶段移动步数加到0时,说明减速完成
{
srd.run_state = STOP; //进入停止阶段
}
break;
}
srd.step_delay = new_step_delay; // 为下一步赋值
}
}
}
以上内容为用户投稿,如有侵权请联系我们删除!