【STM32篇】步进电机之S型曲线

这篇具有很好参考价值的文章主要介绍了【STM32篇】步进电机之S型曲线。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用步进电机的S曲线算法的目的是为了使电机缓慢加速到目标转速或从高转速减速到0。防止电机在高转速时立即停止而对电机造成损伤,减少电机的使用寿命。

本文主要讲述S型算法的使用,对于具体的原理,可通过其他博主的文章学习。

【STM32篇】步进电机之S型曲线

图1.S算法加减速图

如图1所示,使用S算法的步进电机运转主要包含S型加速、匀速、S型减速3个阶段。

图中v表示电机转速,t为时间。

S型算法代码如下:

/*
*   period: 指向保存计时器周期值的数组
*   len: 数组长度
*   fre_max: 最大速度,频率值。
*   fre_min: 最小速度,频率值。
*   flexible:  灵活值。调整S曲线
*/
void CalculateSModelLine(uint16_t * period, uint32_t len, uint16_t fre_max, uint16_t fre_min, float flexible)
{
    int32_t i;
    float deno ;
    float melo ;
    float fre ;
    float Fdelt = fre_max-fre_min;
    if(len>MAX_ACC_PULSE) len=MAX_ACC_PULSE;
    for(i=0; i<len; i++){
        melo = flexible * (2.0*i/len-1) ;
        deno = 1.0 / (1 + expf(-melo)); 
        fre = Fdelt * deno + fre_min;
        period[i] = (uint16_t)(TIM_CLOCK/fre);//TIM_CLOCK为定时器频率
    }
}

该函数的速度(频率)为一个脉冲的频率。例如:定时器计数一次为1us,PWM波的脉宽为1000,即一个脉冲时间为1000us=0.001s,频率f=1/t=1000Hz;

根据该函数,我对S型算法的理解与图1有所不同。如下图

【STM32篇】步进电机之S型曲线

图2.步进电机S曲线脉冲图

这里我将X轴看作脉冲数,Y轴看作一个脉冲所需的时间(或频率)。

电机按指定频率运行

如图2所示,①为加速阶段,x越小即一个脉冲的时间越短,转动一个角度的速度越快。②为匀速阶段,速度达到最大值,③为减速阶段。①③对称。

直接使用void CalculateSModelLine(...)函数,可控制电机在一定角度上按S曲线运转,但不可直接控制电机在该角度上运行时间。

拿电机控制云台振动来说,1/16步进模式下,给电机1600个脉冲使云台向上10cm,再给电机1600个脉冲使它往下10cm,反复切换方向,减少脉冲时间,便可增大云台振动频率。

计算过程:

如图2.设电机运行范围为range,频率为frequency,即控制电机的脉冲数为range,总时间为1/frequency。求出最大速度。

设匀速运行阶段一个脉冲的定时器计数值为TIM_min,加速和减速阶段平均计数值为2TIM_min(当然觉得误差大也可改为(TIM_max+TIM_min)/2),不会影响后面的计算。设定S型曲线脉冲数为一定值NUM。

所以有:

(1/frequency)/(1/TIM_CLOCK) = (range -2* MUX)*TIM_min + 2*NUM * 2TIM_min;

TIM_min = TIM_CLOCK / (frequency * (range +2* NUM) );

TIM_min为计数值,1/frequency 为总时间,(1/frequency)/(1/TIM_CLOCK)为总计数值

TIM_min * 1/TIM_CLOCK便是一个脉冲周期,

倒数即为脉冲频率:TIM_CLOCK/TIM_min = frequency * (range +2* MUX);

所以匀速运行时的脉冲频率为frequency * (range +2* NUM)

代码如下:(运行范围小于2*NUM属于没有匀速阶段,只有加速和减速)

/*
    freq:频率HZ,即range个脉冲需要的时间
    range:范围,即脉冲数
    return:最大脉冲频率
*/
uint16_t calculate_S_MaxHZ(uint16_t freq, uint16_t range)
{
    uint32_t maxHz;
    if(freq == 0 || range == 0)
    {
        return 0;
    }
    if(range > MOTOR_S_NUM * 2)
    {
        maxHz = freq  * ( range + MOTOR_S_NUM * 2);
    }
    else
    {
        maxHz = 2 * range * freq ;
    }
    return maxHz;
}

这个函数算出的maxHz,存在这一定误差,freq越大,实际运行时间越长。

【STM32篇】步进电机之S型曲线

图3.配置motor工作 1HZ频率

如图3所示,电机S曲线运行500个脉冲期望时间为1s,而实际时间为0.974s,看似误差不大。

提高工作频率,freq = 4,理论时间为0.25s,实际时间为0.34s,即在规定时间内,电机无法运行到指定目的地。

【STM32篇】步进电机之S型曲线

图4.配置motor工作 4HZ频率

解决办法:

| timer1 - timer2 |为理论时间与实际时间的差值,将其转换为每个脉冲的时间差(均数):

| timer1 - timer2 | / range ;

每个脉冲定时器计数差值(均数)为:| timer1 - timer2 | / range /(1 / TIM_CLOCK)

按理说,实际脉冲与理论脉冲在周期相差一定值,那在实际脉冲上增加或减去相差的时间,总时间就会相等。但这是S曲线脉冲,所求得的最大值脉冲频率并非均值,所以实际时间和理论时间只会慢慢接近。

代码实现:

/*
    freq:频率HZ,即range个脉冲需要的时间
    range:范围,即脉冲数
    maxHz:最大脉冲频率
*/
void calculate_S_PulseFreq(uint16_t freq, uint16_t range)
{
    uint32_t maxHz;
    float timer1,timer2;
    uint32_t Vibrate_Time=0;
    if(freq == 0 || range == 0)
    {
        return ;
    }
    if(range > MOTOR_S_NUM * 2)
    {
        maxHz = freq  * ( range + MOTOR_S_NUM * 2);
    }
    else
    {
        maxHz = 2 * range * freq ;
    }
    //计算准确时间
    while(1)
    {
        Vibrate_Time = 0;
        CalculateSModelLine(motor_S_period, MOTOR_S_NUM, maxHz, min_Hz, 6.0);
        for(uint8_t i=0;i<MOTOR_S_NUM;i++)
        {
            Vibrate_Time += (motor_S_period[i]);
        }
        Vibrate_Time = 2*Vibrate_Time+((range - MOTOR_S_NUM * 2) * motor_S_period[MOTOR_S_NUM - 1]);//总计数
        timer1 = Vibrate_Time*1.0 / (TIMER4_CLOCK);     //实际时间
        timer2 = 1.0/freq;                           //理论所需的时间
        //再计算最大脉冲频率,确保误差在1ms以内
        if(timer1 > timer2)       
        {
            if((timer1-timer2)>0.001)
            {
                maxHz = TIMER4_CLOCK/( TIMER4_CLOCK/maxHz - (timer1-timer2)*TIMER4_CLOCK/range);
            }
            else break;
        }
        else
        {
            if((timer2-timer1)>0.001)
            {
                maxHz = TIMER4_CLOCK/( TIMER4_CLOCK/maxHz + (timer2-timer1)*TIMER4_CLOCK/range);
            }
            else break;
        }
    }
}

在计算脉冲最大频率的函数里直接求出最大脉冲频率,进而慢慢逼近。那问题来了,会不会在循环算S曲线脉冲时,耗费大小时间?

1HZ工作频率频率下:循环3次便可求出最大脉冲频率,总时间误差值在1ms内。

【STM32篇】步进电机之S型曲线
【STM32篇】步进电机之S型曲线

4HZ工作频率下:原实际时间为0.347s,理论时间为0.25s,差值较大。在5次循环中可将误差值减少至1ms以内。

【STM32篇】步进电机之S型曲线
【STM32篇】步进电机之S型曲线

具体情况受电机运行范围和运行频率的影响,在具体还得看使用环境。

当然,也可将频率改为秒单位时间等。

/*
    sTime:运行时间
    range:范围,即脉冲数
    maxHz:最大脉冲频率
*/
void calculate_S_PulseFreq(uint16_t sTime, uint16_t range)
{
    uint32_t maxHz;
    float timer1,timer2;
    uint32_t Vibrate_Time=0;
    if(freq == 0 || range == 0)
    {
        return ;
    }
    if(range > MOTOR_S_NUM * 2)
    {
        maxHz = 1/sTime  * ( range + MOTOR_S_NUM * 2);
    }
    else
    {
        maxHz = 2 * range / sTime ;
    }
    //计算准确时间
    uint8_t k=0;
    while(1)
    {
        k++;
        Vibrate_Time = 0;
        CalculateSModelLine(motor_S_period, MOTOR_S_NUM, maxHz, min_Hz, 6.0);
        for(uint8_t i=0;i<MOTOR_S_NUM;i++)
        {
            Vibrate_Time += (motor_S_period[i]);
        }
        Vibrate_Time = 2*Vibrate_Time+((range - MOTOR_S_NUM * 2) * motor_S_period[MOTOR_S_NUM - 1]);//总计数
        timer1 = Vibrate_Time*1.0 / (TIMER4_CLOCK);     //实际时间
        timer2 = 1.0/freq;                           //理论所需的时间
        //再计算最大脉冲频率,确保误差在1ms以内
        if(timer1 > timer2)       
        {
            if((timer1-timer2)>0.001)
            {

                maxHz = TIMER4_CLOCK/( TIMER4_CLOCK/maxHz - (timer1-timer2)*TIMER4_CLOCK/range);
            }
            else break;
        }
        else
        {
            if((timer2-timer1)>0.001)
            {
                maxHz = TIMER4_CLOCK/( TIMER4_CLOCK/maxHz + (timer2-timer1)*TIMER4_CLOCK/range);
            }
            else break;
        }
    }
}

重新梳理:

2023/5/30

此次为了更清晰的理解计算过程,上述方式同样可行。

如图2所示,将S曲线理解为一个S加速段,匀速段,S减速段运动,将y坐标看做脉冲周期,x轴为脉冲数。

void CalculateSModelLine(uint16_t * period, uint32_t len, uint16_t fre_max, uint16_t fre_min, float flexible);该函数中fre_max和fre_min分别为脉冲最大和最小频率,可设定其中一个参数为已知量,此处将fre_min设为已知量500,即脉冲周期(时间)为1/fre_min = 2ms。(该速度已经很慢了,可根据实际情况设定最小频率)。

计算过程:

  1. 特定频率下电机往返转动。

期望频率freq,期望时间:1/freq;

计算时间 = S运动时间 + 匀速运动时间;

匀速运动脉冲时间:1/fre_max; //可理解为均值

S运动脉冲时间:(1/fre_max + 1/fre_min)/ 2; //可理解为中值

(1/fre_max + 1/fre_min)/ 2 * 2 * len + 1/fre_max * (range - 2* len) = 1/freq ;

【STM32篇】步进电机之S型曲线

两段S曲线运动时间 + 匀速运动时间;

len:一段S曲线的脉冲数

range:总脉冲数

最终结果:fre_max = (freq * fre_min *(range - len))/(fre_min - len * freq);

CalculateSModelLine(period, len, fre_max, fre_min, 6.0);调用该函数,便可计算出S曲线的TIM计数值,结果保存至period中。

代码如下:定义变量有所不同

其中:

#define NUM 100

#define min_Hz 500

#define TIMER2_CLOCK SystemCoreClock/(timer_prescaler_read(TIMER2)+1)

uint16_t motor_S_period[100];

/*
Calculate S model line
计算加减速的S型曲线

param <in>  period        uint32_t*   存放S曲线数据的数组
            len            uint32_t    数组长度
            fre_max        uint32_t     最大频率
            fre_min        uint32_t    最小频率
            float        flexible    S型曲线调整系数,flexible越大,曲线越陡
return        void
*/

void CalculateSModelLine(uint16_t * period, uint32_t len, uint16_t fre_max, uint16_t fre_min, float flexible)
{
    int32_t i;
    float deno ;
    float melo ;
    float fre ;
    float Fdelt = fre_max-fre_min;
    if(len>MAX_ACC_PULSE) len=MAX_ACC_PULSE;
    for(i=0; i<len; i++){
        melo = flexible * (2.0*i/len-1) ;//melo = flexible * (i-len/2) / (len/2); //flexible is (0~10),adjust the S curves
        deno = 1.0 / (1 + expf(-melo)); //expf is a library function of exponential(e)
        fre = Fdelt * deno + fre_min;
        period[i] = (uint16_t)(TIMER2_CLOCK/fre);//(72000000.0 / fre); // 72000000 is the timer drive frequency,64 prescaler
        if(period[i] % 2)
        period[i]=period[i]-1;//化奇为偶
    }
}

/*
    freq:频率HZ,即range个脉冲需要的时间
    range:范围,即脉冲数
    maxHz:最大脉冲频率
*/
void calculate_S_PulseFreq(uint16_t freq, uint16_t range)
{
    uint32_t maxHZ;
    float timer1,timer2;
    uint32_t Vibrate_Time=0;
    
    if(range > NUM * 2)
    {
        /*
        计算过程:计算时间 = 预期时间
        NUM(1/Max_HZ + 1/Min_HZ) + (range - 2 * NUM) * 1/Max_HZ = 1/freq;
        */
        maxHZ = ((freq) * min_Hz * (range - NUM)) / (min_Hz - NUM * freq);
        //计算准确时间
        while(1)
        {
            Vibrate_Time = 0;
            CalculateSModelLine(motor_S_period, NUM, maxHZ, min_Hz, 6.0);
            for(uint16_t i=0;i<NUM;i++)
            {
                Vibrate_Time += (motor_S_period[i]);
            }
            
            Vibrate_Time = 2*Vibrate_Time+((range - NUM * 2) * motor_S_period[NUM - 1]);//总计数
            timer1 = Vibrate_Time*1.0 / (TIMER2_CLOCK);    //实际振动时间
            timer2 = 1.0/(freq);                        //理论所需的时间
            if(timer1 > timer2)
            {
                if((timer1-timer2)>0.001)//实际时间大于理论时间
                {
                    
                    maxHZ = TIMER2_CLOCK/( TIMER2_CLOCK/maxHZ - (timer1-timer2)*TIMER2_CLOCK/range);//增大最大脉冲频率
                }
                else break;
            }
            else
            {
                if((timer2-timer1)>0.001)
                {
                    maxHZ = TIMER2_CLOCK/( TIMER2_CLOCK/maxHZ + (timer2-timer1)*TIMER2_CLOCK/range);//减小最大脉冲频率
                }
                else break;
            }
        }
    }
}

在精确计算时间时,先计算S曲线中的100个脉冲,motor_S_period中保存的数据为定时器计数值(而非直接脉冲频率),随后计算所有脉冲的和,再乘上定时器频率的倒数即为这500个脉冲所用的时间。这个时间和预期的时间是有差别,只能进行进一步的计算才能接近预期值。

这里我的计算是考虑实际时间与预期时间的差值,实际时间大,则可说明在计算fre_max时fre_max值大了,就需要减小。减小多少?这里采取时间差值的平均数。如果实际时间小,那就加上这个差值。

maxHZ = TIMER2_CLOCK/( TIMER2_CLOCK/maxHZ - (timer1-timer2)*TIMER2_CLOCK/range);

对于这个式子,计算的还是频率,TIMER2_CLOCK需要保留。

如果去掉TIMER2_CLOCK,1/maxHZ为最大脉冲时间, (timer1-timer2)/range为差值的平均时间,看似没问题,但这个是用机器语言计算,在计算过程中可能会出现问题。(在实际测试过程中会产生问题)

1/maxHZ为时间,再除以1/TIMER2_CLOCK便可将时间转换为TIM计数值,最后再转换为频率即可。

实际测试:

【STM32篇】步进电机之S型曲线

这里需要电机运行500个脉冲,并在0.25s(4HZ)内完成。

第一次计算S(k=1),实际时间0.166207999s;实际时间小,maxHZ就需要加大,就执行下面语句。

【STM32篇】步进电机之S型曲线

第二次计算S(k=2),此时看到maxHZ由8000变为3417,频率加大了。实际时间也变为0.26465866,也接近0.25了。

【STM32篇】步进电机之S型曲线

第三次计算S(k=3),实际时间0.248768661

【STM32篇】步进电机之S型曲线

第四次计算S(k=4),实际时间0.250145346,这个时间已经在我的预期时间范围(±1ms)内,所以计算已经完成。在使用过程中,担心K值太大,可在上电后立即计算S,以后只需从数组中读取数值给定时器使用即可(写入重装载寄存器中)。

【STM32篇】步进电机之S型曲线

为了方便演示,直接使用串口打印。

【STM32篇】步进电机之S型曲线

如下图,为电机运行时的3个过程。

【STM32篇】步进电机之S型曲线

S曲线的使用

1.电机制定时间运行至目标位置。

main.c


#include "motor.h"
#include "systick.h"
void clock_config(void);
void nvic_config(void);

int main(void)
{
    clock_config();            //配置RCC时钟
    nvic_config();            //配置中断优先级
    MOTOR_Init();            //motor初始化
    MOTOR_ConfigWork(CW,500,1);//顺时针56°
    while(1);
}
void clock_config(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
}
void NVIC_IQR_Confing(uint8_t nvic_IRQChannel,uint8_t nvic_PreemptionPriority, uint8_t nvic_SubPriority)
{
    NVIC_InitTypeDef nvic_Init;
    nvic_Init.NVIC_IRQChannel = nvic_IRQChannel;//中断号
    nvic_Init.NVIC_IRQChannelPreemptionPriority = nvic_PreemptionPriority;//抢占优先级
    nvic_Init.NVIC_IRQChannelSubPriority = nvic_SubPriority;//子优先级
    nvic_Init.NVIC_IRQChannelCmd = ENABLE;//启用中断优先级
    NVIC_Init(&nvic_Init);
}
void nvic_config(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置中断优先级分组
    NVIC_IQR_Confing(TIM4_IRQn,1,0);
}

2.脉冲波形:

3.实验结果:

最后,由于是我在手动计算出来编写的函数,在一些步骤上可能合理度不够,也不简洁,所以各位在浏览文章的时候有更好的方法,也可在评论区中分享分享。希望能帮助到正在学习步进电机的朋友们。如果找到更好的方法,也会慢慢分享到博客上。

欢迎指正!

2023年3月14日文章来源地址https://www.toymoban.com/news/detail-484947.html

到了这里,关于【STM32篇】步进电机之S型曲线的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32控制42步进电机

    首先采用TB6600驱动外接12VDC电源。 STM32选c8t6最小系统板 接线TB6600跟32接线有共阴级接法(把ENA-,DIR-,PUL-与32共地)也有共阳接法(把ENA+,DIR+,PUL+与32共地)。这里选共阴级接法。 判断42步进电机的4根线哪两根是A相(A-和A+),哪两根是B相(B-和B+),方法:将任意两根线短接起来

    2024年02月16日
    浏览(54)
  • 【STM32篇】4988驱动步进电机

    本文介绍的步进电机驱动为毕设所用,学习时也借鉴了其他博主和商家的资料,介绍的都很详细。但对于刚入门的友友萌来说,可能还会存在的一些不解。而我就是被卡在驱动电源上。下文为我对4988驱动的一些理解,希望能帮助到刚学习步进电机的朋友。 4988驱动IC价格便宜

    2024年02月01日
    浏览(57)
  • 基于STM32的步进电机驱动设计

    程序源码提取链接放置文章底部,自行提取即可。 采用STM32驱动28BYJ4步进电机,实现正转反转,完成角度调整。步进电机是一种将电脉冲转化为角位移的执行机构。当步进驱动器接收到一个脉冲信号,它就驱动步进电机按设定的方向转动------一个固定的角度(及步进角)。可以

    2024年02月02日
    浏览(44)
  • stm32f103使用DM542控制42相步进电机的原理和可行性分析

    步进电机是一种能够实现准确位置控制的电机,通常应用于需要高精度定位的场合,例如3D打印机、机器人和自动化系统等。而STM32F103是一种高性能、低功耗的微控制器,它拥有广泛的外设,适合用于控制各种电机。本文将介绍使用STM32F103控制DM542驱动器驱动42相步进电机的原

    2024年02月15日
    浏览(31)
  • 【STM32篇】DRV8425驱动步进电机

    【STM32篇】4988驱动步进电机_hr4988-CSDN博客         在上篇文章中使用了HR4988实现了步进电机的驱动,在实际运用过程,HR4988或者A4988驱动步进电机会存在电机噪音太大的现象。本次将向各位友友介绍一个驱动简单且非常静音的一款步进电机驱动IC。         DRV8424/25 是适

    2024年04月11日
    浏览(42)
  • STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数

    该篇文章中用到的步进电机闭环驱动器为Emm42_V4.0步进电机闭环驱动器。该闭环驱动器自带FOC矢量闭环控制算法,能实现力矩、速度、位置三环控制。 如下图所示,该42步进闭环电机驱动器的A+、A-、B+、B-连接步进电机,通过右侧的使能、脉冲、方向端对步进电机进行驱动控制

    2024年02月01日
    浏览(38)
  • STM32基于CAN总线协议控制步进电机

    如上图所示,实现了以下功能: 1.两块stm32单片机通过CAN控制器与收发器进行半双工通信; 2.stm32主机通过检测按键,切换不同的模式,将不同模式的case值发送给stm32从机; 3.stm32从机根据收到的case值,控制步进电机进行不同的运动操作; 4.OLED用于显示收发内容与按键状态等

    2024年01月19日
    浏览(33)
  • 基于STM32闭环步进电机控制系统设计

    ** 单片机设计介绍,1654基于STM32闭环步进电机控制系统设计(仿真,程序,说明)   基于STM32的闭环步进电机控制系统设计是一种利用STM32微控制器开发的系统,用于实现对步进电机的精确控制。以下是该系统的一般设计概述: STM32微控制器:作为主控芯片,STM32具备高性能

    2024年02月08日
    浏览(31)
  • STM32CubeMX ULN2003步进电机驱动

    一、28BYJ-48 步进电机  28BYJ-48是一款5线单极步进电机,运行电压为5V。 根据数据表,当28BYJ-48电机在全步模式下运行时,每步对应于11.25°的旋转。这意味着每转有32步 (360°/11.25° = 32)。 如上图所示,步距角=5.625°/64 意思就是每64个脉冲步进电机就会转5.625度,因此我们很容易得

    2024年01月18日
    浏览(40)
  • STM32控制四个步进电机(包含运行程序)

    软件: keil 5(其他版本也ok) 硬件: 步进电机(我用的是42)×4;单片机开发板(本例使用STM32F407ZGT6);电机驱动器×4;外部按键;杜邦线。 实验效果: 四个步进电机对应四个外部按键,按下对应的外部按键选择需要控制的电机。 在开发板上按下KEYO,步进电机以500HZ的频率

    2024年04月29日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包