电机应用-步进电机进阶驱动

步进电机梯形加减速

什么是梯形加减速 

假设该装置使用步进电机实现物体X的移动,系统要求从A点出发,到B点停止,移动的时间越短越好系统稳定

        根据步进电机的特性,最大程度加大电机转速(提高脉冲频率),则到达B点的时间就越短,但是如果施加脉冲频率过高,超过了步进电机的最小启动频率,则会造成电机内部的反向电动势的阻尼作用,转子与定子之间的磁反应将跟随不上电信号的变化,导致堵转或者丢步,滑块连动都没动。

        所以要求在电机启动时需要一个低速,但为了实现系统要求,在启动之后慢慢的升高速度,实现一个加速的过程,那如果在达到终点前一直在加速,就又有另外的一个问题,到达终点时速度太快就会导致刹不住,出现过冲现象,为了防止这个现象我们需要在达到终点前的一段距离提前减速,使得滑块到达终点时速度刚好为0,即从最高速度一直减至停止。

        可以将这个过程描述为:梯形加减速。

1、在OA加速过程中,由低于步进电机的启动频率开始启动(模型中由0启动),以固定的加速度增加速度到目标值。

2、在AB匀速过程中,以最大速度匀速运动。

3、在BC减速过程中,以固定的加速度减小速度到0。

这种算法是一种在加速过程和减速过程中加速度不变的匀变速控制算法。

梯形加减速的原理分析

梯形加减速的原理:控制脉冲频率来控制速度。控制脉冲个数来控制位移。

梯形加减速的算法实现

推导过程复杂,可自行百度。

梯形加减速整体流程

定时器中断处理流程:

定时器中断产生脉冲并且只有在步进电机移动时进入,四个不同运动的状态,分别是stop--accel--run--decel--stop。

每一个速度变化的阶段的划分是前面计算得出的对应脉冲个数,当脉冲个数到达限制则启动切换即可,这种操作可以通过状态机在定时器中断中实现。

在定时器中断实现状态机功能:

当应用程序启动或者步进电机停止状态机处于STOP状态。当输入移动步数建立计算完成,一个新状态被置位同时定时器的中断被使能。

当运行的步数超过1步状态机进入ACCEL状态,如果只移动1步,那么状态直接变为DECEL,因为只移动一步是不需要加速的。

当状态变为ACCEL,应用程序一直加速步进电机达到期望的最大速度,这是状态变为RUN,或者减速必须开始,状态变为DECEL。

当状态变为RUN时,步进电机会持续保持最大速度speed旋转,直到必须开始减速然后把状态改变为DECEL。它会一直保持DECEL状态并一直减速到期望的步数并且速度为0。然后状态变为STOP。

中断流程图:

步进电机梯形加减速实验

硬件资源:TIM8_CH3(PI7)、DIR(PB2)、EN(PF11)。

涉及梯形加减速实现部分:

#define TIM_FREQ            168000000U                      /* 定时器主频 */
#define MAX_STEP_ANGLE      0.225                           /* 最小步距(1.8/MICRO_STEP) */
#define PAI                 3.1415926                       /* 圆周率*/
#define FSPR                200                             /* 步进电机单圈步数 */
#define MICRO_STEP          8                               /* 步进电机驱动器细分数 */
#define T1_FREQ             (TIM_FREQ/84)                   /* 频率ft值 */
#define SPR                 (FSPR*MICRO_STEP)               /* 旋转一圈需要的脉冲数 */

/* 数学常数 */

#define ALPHA               ((float)(2*PAI/SPR))            /* α= 2*pi/spr */
#define A_T_x10             ((float)(10*ALPHA*T1_FREQ))
#define T1_FREQ_148         ((float)((T1_FREQ*0.676)/10))   /* 0.676为误差修正值 */
#define A_SQ                ((float)(2*100000*ALPHA))
#define A_x200              ((float)(200*ALPHA))            /* 2*10*10*a/10 */

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;

enum STA
{
    STOP = 0,                                               /* 加减速曲线状态:停止*/
    ACCEL,                                                  /* 加减速曲线状态:加速阶段*/
    DECEL,                                                  /* 加减速曲线状态:减速阶段*/
    RUN                                                     /* 加减速曲线状态:匀速阶段*/
};

enum DIR
{
 CCW = 0,                                                   /* 逆时针 */ 
 CW                                                         /* 顺时针 */
};

enum EN
{
 EN_ON = 0,                                                 /* 失能脱机引脚 */
 EN_OFF                                                     /* 使能脱机引脚 使能后电机停止旋转 */
};

/********************************************梯形加减速***********************************************/
speedRampData g_srd               = {STOP,CW,0,0,0,0,0};  /* 加减速变量 */
__IO int32_t  g_step_position     = 0;                    /* 当前位置 */
__IO uint8_t  g_motion_sta        = 0;                    /* 是否在运动?0:停止,1:运动 */
__IO uint32_t g_add_pulse_count   = 0;                    /* 脉冲个数累计 */

/*
 * @brief       生成梯形运动控制参数
 * @param       step:移动的步数 (正数为顺时针,负数为逆时针).
 * @param       accel  加速度,实际值为accel*0.1*rad/sec^2  10倍并且2个脉冲算一个完整的周期
 * @param       decel  减速度,实际值为decel*0.1*rad/sec^2
 * @param       speed  最大速度,实际值为speed*0.1*rad/sec
 * @retval      无
 */
void create_t_ctrl_param(int32_t step, uint32_t accel, uint32_t decel, uint32_t speed)
{
    __IO uint16_t tim_count;        /* 达到最大速度时的步数*/
    __IO uint32_t max_s_lim;        /* 必须要开始减速的步数(如果加速没有达到最大速度)*/
    __IO uint32_t accel_lim;
	
    if(g_motion_sta != STOP)        /* 只允许步进电机在停止的时候才继续*/
        return;
	
    if(step < 0)                    /* 步数为负数 */
    {   
        g_srd.dir = CCW;            /* 逆时针方向旋转 */
        ST3_DIR(CCW);
        step =-step;                /* 获取步数绝对值 */
    }
    else
    {
        g_srd.dir = CW;             /* 顺时针方向旋转 */
        ST3_DIR(CW);
    }

    if(step == 1)                   /* 步数为1 */
    {
        g_srd.accel_count = -1;     /* 只移动一步 */
        g_srd.run_state = DECEL;    /* 减速状态. */
        g_srd.step_delay = 1000;    /* 默认速度 */
    }
    else if(step != 0)              /* 如果目标运动步数不为0*/
    {
        /*设置最大速度极限, 计算得到min_delay用于定时器的计数器的值 min_delay = (alpha / t)/ w*/
        g_srd.min_delay = (int32_t)(A_T_x10 /speed); //匀速运行时的计数值

        /* 通过计算第一个(c0) 的步进延时来设定加速度,其中accel单位为0.1rad/sec^2
         step_delay = 1/tt * sqrt(2*alpha/accel)
         step_delay = ( tfreq*0.676/10 )*10 * sqrt( (2*alpha*100000) / (accel*10) )/100 */
        
        g_srd.step_delay = (int32_t)((T1_FREQ_148 * sqrt(A_SQ / accel))/10); /* c0 */

        max_s_lim = (uint32_t)(speed * speed/ (A_x200 * accel / 10));  /* 计算多少步之后达到最大速度的限制 max_s_lim = speed^2 / (2*alpha*accel) */

        if(max_s_lim == 0)                                      /* 如果达到最大速度小于0.5步,我们将四舍五入为0,但实际我们必须移动至少一步才能达到想要的速度 */
        {
            max_s_lim = 1;
        }
        accel_lim = (uint32_t)(step*decel/(accel+decel));       /* 这里不限制最大速度 计算多少步之后我们必须开始减速 n1 = (n1+n2)decel / (accel + decel) */

        if(accel_lim == 0)                                      /* 不足一步 按一步处理*/
        {
            accel_lim = 1;
        }
		
        if(accel_lim <= max_s_lim)                              /* 加速阶段到不了最大速度就得减速。。。使用限制条件我们可以计算出减速阶段步数 */
        {
            g_srd.decel_val = accel_lim - step;                 /* 减速段的步数 */
        }
        else
        {
            g_srd.decel_val = -(max_s_lim*accel/decel);         /* 减速段的步数 */
        }
		
        if(g_srd.decel_val == 0)                                /* 不足一步 按一步处理 */
        {
            g_srd.decel_val = -1;
        }
        g_srd.decel_start = step + g_srd.decel_val;             /* 计算开始减速时的步数 */
        
        if(g_srd.step_delay <= g_srd.min_delay)                 /* 如果一开始c0的速度比匀速段速度还大,就不需要进行加速运动,直接进入匀速 */
        {
            g_srd.step_delay = g_srd.min_delay;
            g_srd.run_state = RUN;
        }
        else  
        {
            g_srd.run_state = ACCEL;
        }
        g_srd.accel_count = 0;                                  /* 复位加减速计数值 */
    }
	
    g_motion_sta = 1;                                           /* 电机为运动状态 */
    ST3_EN(EN_ON);
    tim_count=__HAL_TIM_GET_COUNTER(&g_atimx_handle);
    __HAL_TIM_SET_COMPARE(&g_atimx_handle, ATIM_TIMX_PWM_CH3, tim_count + g_srd.step_delay / 2);  	/* 设置定时器比较值 */
    HAL_TIM_OC_Start_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH3);                                 		/* 使能定时器通道 */
}

状态机实现:

/**
  * @brief  定时器比较中断
  * @param  htim:定时器句柄指针
  * @note   无
  * @retval 无
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    __IO uint32_t tim_count = 0;
    __IO uint32_t tmp = 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 (htim->Instance == TIM8)
    {
        tim_count = __HAL_TIM_GET_COUNTER(&g_atimx_handle);
        tmp = tim_count + g_srd.step_delay / 2;             /* 整个C值里边是需要翻转两次的所以需要除以2 */
        __HAL_TIM_SET_COMPARE(&g_atimx_handle, ATIM_TIMX_PWM_CH3, tmp);

        i++;                                                /* 定时器中断次数计数值 */

        if (i == 2)                                         /* 2次,说明已经输出一个完整脉冲 */
        {
            i = 0;                                          /* 清零定时器中断次数计数值 */

            switch (g_srd.run_state)                        /* 加减速曲线阶段 */
            {
                case STOP:
                    step_count = 0;                             /* 清零步数计数器 */
                    rest = 0;                                   /* 清零余值 */
                    /* 关闭通道*/
                    HAL_TIM_OC_Stop_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH3);
                    ST3_EN(EN_OFF);
                    g_motion_sta = 0;                           /* 电机为停止状态  */
                    break;

                case ACCEL:
                    g_add_pulse_count++;                        /* 只用于记录相对位置转动了多少度 */
                    step_count++;                               /* 步数加1*/

                    if (g_srd.dir == CW)
                    {
                        g_step_position++;                      /* 绝对位置加1  记录绝对位置转动多少度*/
                    }
                    else
                    {
                        g_step_position--;                      /* 绝对位置减1*/
                    }

                    g_srd.accel_count++;                        /* 加速计数值加1*/
					/* 计算新(下)一步脉冲周期(时间间隔) */
                    new_step_delay = g_srd.step_delay - (((2 * g_srd.step_delay) + rest) / (4 * g_srd.accel_count + 1)); 
					/* 计算余数,下次计算补上余数,减少误差 */
                    rest = ((2 * g_srd.step_delay) + rest) % (4 * g_srd.accel_count + 1);                            

                    if (step_count >= g_srd.decel_start)        /* 检查是否到了需要减速的步数 */
                    {
                        g_srd.accel_count = g_srd.decel_val;    /* 加速计数值为减速阶段计数值的初始值 */
                        g_srd.run_state = DECEL;                /* 下个脉冲进入减速阶段 */
                    }
                    else if (new_step_delay <= g_srd.min_delay) 
                    {	/* 检查是否到达期望的最大速度 计数值越小速度越快,当你的速度和最大速度相等或更快就进入匀速*/
                        last_accel_delay = new_step_delay;      /* 保存加速过程中最后一次延时(脉冲周期)*/
                        new_step_delay = g_srd.min_delay;       /* 使用min_delay(对应最大速度speed)*/
                        rest = 0;                               /* 清零余值 */
                        g_srd.run_state = RUN;                  /* 设置为匀速运行状态 */
                    }

                    break;

                case RUN:
                    g_add_pulse_count++;
                    step_count++;                               /* 步数加1 */

                    if (g_srd.dir == CW)
                    {
                        g_step_position++;                      /* 绝对位置加1 */
                    }
                    else
                    {
                        g_step_position--;                      /* 绝对位置减1*/
                    }

                    new_step_delay = g_srd.min_delay;           /* 使用min_delay(对应最大速度speed)*/

                    if (step_count >= g_srd.decel_start)        /* 需要开始减速 */
                    {
                        g_srd.accel_count = g_srd.decel_val;    /* 减速步数做为加速计数值 */
                        new_step_delay = last_accel_delay;      /* 加阶段最后的延时做为减速阶段的起始延时(脉冲周期) */
                        g_srd.run_state = DECEL;                /* 状态改变为减速 */
                    }

                    break;

                case DECEL:
                    step_count++;                               /* 步数加1 */
                    g_add_pulse_count++;

                    if (g_srd.dir == CW)
                    {
                        g_step_position++;                      /* 绝对位置加1 */
                    }
                    else
                    {
                        g_step_position--;                      /* 绝对位置减1 */
                    }

                    g_srd.accel_count++;
					/* 计算新(下)一步脉冲周期(时间间隔) */
                    new_step_delay = g_srd.step_delay - (((2 * g_srd.step_delay) + rest) / (4 * g_srd.accel_count + 1)); 
					/* 计算余数,下次计算补上余数,减少误差 */
                    rest = ((2 * g_srd.step_delay) + rest) % (4 * g_srd.accel_count + 1);                               

                    /* 检查是否为最后一步 */
                    if (g_srd.accel_count >= 0)                 /* 判断减速步数是否从负值加到0是的话 减速完成 */
                    {
                        g_srd.run_state = STOP;
                    }

                    break;
            }

            g_srd.step_delay = new_step_delay;              /* 为下个(新的)延时(脉冲周期)赋值 */
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/448206.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【脚本玩漆黑的魅影】全自动对战宫殿

文章目录 原理主要代码全部代码 原理 对战宫殿是让宠物自己打&#xff0c;不需要我们选技能&#xff0c;所以用来刷对战点数很合适。 需要准备三个主力。 主要是根据屏幕截图进行各种操作。 1&#xff0c;外面的对话&#xff0c;除了选自由级以外&#xff0c;其他都是直接点…

第十一个实验:数组和簇的混用,线性

实验内容: 输入5个元素的一维数组 显示每一个元素的值,索引和奇偶类型 第一步:新建项目 第二步:编程 创建一维数组,一共五个元素 ​​​ 选择数组索引部件 判断元素的奇偶性,把元素值,该元素索引和奇偶特性组成簇 复制4份,每一个元素一份

(day 2)JavaScript学习笔记(基础之变量、常量和注释)

概述 这是我的学习笔记&#xff0c;记录了JavaScript的学习过程&#xff0c;我是有一些Python基础的&#xff0c;因此在学习的过程中不自觉的把JavaScript的代码跟Python代码做对比&#xff0c;以便加深印象。我本人学习软件开发纯属个人兴趣&#xff0c;大学所学的专业也非软件…

MySQL技能树学习

MySQL三大范式&#xff1a; 第一范式主要是确保数据表中每个字段的值必须具有原子性&#xff0c;也就是说数据表中每个字段的值为不可再次拆分的最小数据单元。 第二范式是指在第一范式的基础上&#xff0c;确保数据表中除了主键之外的每个字段都必须依赖主键。 第三范式是在…

【设计模式】享元模式的使用场景及与其他共享技术的对比

文章目录 1.概述2.享元模式2.1.核心概念2.2.实现案例2.2.1.内部状态实现2.2.2.外部状态实现 2.3.更多场景 3.享元模式的一些对比3.1.与缓存的区别3.2.与池化技术的区别 4.总结 1.概述 享元模式&#xff08;Flyweight Pattern&#xff09;是一种非常常用的结构型设计模式&#…

实在TARS大模型斩获多项重磅大奖,AI领域实力认可

近日&#xff0c;实在智能TARS&#xff08;塔斯&#xff09;大模型凭借在多个垂直行业场景的优秀落地应用案例&#xff0c;以及AIGC领域的深耕和技术积累&#xff0c;荣获多项重磅大奖。 TARS大模型是是实在智能基于在自然语言处理&#xff08;NLP&#xff09;领域深厚的技术积…

MySQL常见的索引类型介绍

我将为您详细讲解 MySQL 中常见的索引类型&#xff0c;以及它们的使用场景、特点、区别和优势。索引是提高数据库查询性能的关键工具&#xff0c;它可以加速数据检索速度&#xff0c;减少服务器的负担。在 MySQL 中&#xff0c;索引类型主要包括 B-Tree 索引、哈希索引、全文索…

Linux进程概念僵尸进程孤儿进程

文章目录 一、什么是进程二、进程的状态三、Linux是如何做的&#xff1f;3.1 R状态3.2 S状态3.3 D状态3.4 T状态3.5 t状态3.6 X状态3.7 Z状态 四、僵尸进程4.1 僵尸进程危害 五、孤儿进程 一、什么是进程 对于进程理解来说&#xff0c;在Windows上是也可以观察到的&#xff0c…

Java线程的6种状态

线程在生命周期中并不是固定处于某一个状态而是随着代码的执行在不同状态之间切换。 NEW&#xff1a;初始状态&#xff0c;线程被创建出来但没有被调用start()RUNNABLE&#xff1a;运行状态&#xff0c;线程被调用了start()等待运行的状态BLOCKED&#xff1a;阻塞状态&#xf…

新手如何快速上手学习单片机?

读者朋友能容我&#xff0c;不使博文负真心 新开专栏&#xff0c;期待与诸君共享精彩 个人主页&#xff1a;17_Kevin-CSDN博客 专栏&#xff1a;《单片机》 学习单片机是一个有趣且有挑战性的过程。单片机是一种微控制器&#xff0c;广泛应用于各种电子设备和嵌入式系统中。在这…

开源向量数据库介绍

在开源矢量数据库的世界里&#xff0c;有些名字因其性能、灵活性和健壮性而脱颖而出。 1. Milvus Milvus 由 Zilliz 推出&#xff0c;是一款高度可定制的开源矢量数据库&#xff0c;在处理大规模数据方面大放异彩。由于其出色的可扩展性&#xff0c;当你需要处理大量数据时&a…

Python对头发二维建模(考虑风力、重力)

目录 一、背景 二、代码 一、背景 数值方法被用于创建电影、游戏或其他媒体中的计算机图形。例如&#xff0c;生成“逼真”的烟雾、水或爆炸等动画。本文内容是对头发的模拟&#xff0c;要求考虑重力、风力的影响。 假设&#xff1a; 1、人的头部是一个半径为10厘米的球体。…

python学习28

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

llc的基波分析法

对于我们之前分析的 LLC等效谐振电路的分析&#xff0c;其实我们发现分析的并不是完整的方波输入&#xff0c;而是用正弦波来分的 那么为何用基波来分析呢&#xff0c;因为对于方波而言&#xff0c;根据傅里叶级数它是可以分解成基波、 1次、3次、5次.......等各种奇次谐波的入…

《ElementPlus 与 ElementUI 差异集合》el-input 和 el-button 属性 size 有变化

差异 element-ui el-input 和 el-button 中&#xff0c;属性size 值是 medium / small / minielement-plus el-input 和 el-button 中&#xff0c;属性size 值是 ‘large’ | ‘default’ | ‘small’&#xff1b; 如果你是自动升级&#xff0c;Vue3 系统会有如下警告“ el-b…

NLP深入学习:结合源码详解 BERT 模型(一)

文章目录 1. 前言2. BERT 关键流程2.1 整体流程2.2 Pre-training&#xff08;预训练&#xff09;2.2.1 Masked Language Model (MLM)2.2.2 Next Sentence Prediction (NSP) 2.3 Fine-tuning&#xff08;微调&#xff09; 3. 总结4. 参考 1. 前言 BERT&#xff08;Bidirectiona…

访问一次网站的全过程

目录 流程图&#xff1a; 一、应用层开始 1. 在浏览器输入https://www.baidu.com 2. DNS获取IP地址 3. 根据HTTP协议生成HTTP请求报文 应用层结束 二、传输层开始 4. TCP三次握手 传输层结束 三、网络层开始 5. IP寻址 6. ARP协议获取MAC地址 网络层结束 四、数据…

Linux中三次握手,四次挥手状态图,端口复用 半关闭状态,心跳包

tcp三次握手和四次挥手状态图&#xff1a; 为什么需要2MSL&#xff1a; 原因1&#xff1a;让四次挥手过程更加可靠&#xff0c;确保最后一个发送给对方的ACK到达&#xff1b;若对方没有收到ACK应答&#xff0c;对方会再次发送FIN请求关闭&#xff0c;此时在2MSL时间内被动关闭…

实际应用中运放里多余的引脚怎么处理?

实际应用中运放里多余的引脚怎么处理&#xff1f;-电子发烧友网 (elecfans.com)

Python实现图片(合并)转PDF

在日常的工作和学习过程当中,我相信很多人遇到过这样一个很普通的需求,就是将某一个图片转为PDF或者是将多个图片合并到一个PDF文件。但是,在苦苦搜寻一圈之后发现要么要下载软件,下载了还要注册,注册了还要VIP,甚至SVIP才能实现这样的需求! 今天,我带大家把这个功能打…