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

步进电机梯形加减速

什么是梯形加减速 

假设该装置使用步进电机实现物体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^2step_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;              /* 为下个(新的)延时(脉冲周期)赋值 */}}
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10.Java---clone+内部类

一次浅拷贝的过程 打印结果: 一次深拷贝的过程 打印结果: 抽象类和接口的区别 外部类&内部类 1.内部类由static修饰时,不可以是变量 这样就是可以的,他就代表一个常量 2.怎么实例化内部类 当然不可以直接实例化啦! 是这么实例化的,看起来比我们平时麻烦了很多呢! …

SpringCloud OpenFeign 服务接口调用

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第四篇&#xff0c;即介绍 Feign 和 OpenFeign 服务接口调用。 二、概述 2.1 Feign 是什么 Feign 是一…

C++程序设计-练手题集合【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下C程序设计中的练手题&#xff0c;以供大家期末复习和考研复习的时候使用。 C程序设计系列文章传送门&#xff1a; 第一章 面向对象基础 第四/五章 函数和类和对象 第六/七/八章 运算符重载/包含与继承/虚函数…

dolphin schedulerAPI调用(二)——创建任务

&#xff08;作者&#xff1a;陈玓玏&#xff09; API文档地址&#xff1a;http://192.168.3.100:21583/dolphinscheduler/swagger-ui/index.html?languagezh_CN&langcn#/task%20definition%20related%20operation/createTaskDefinitionUsingPOST_1 实际使用中&#x…

场的概念---数量场(标量场)和矢量场介绍理解

目录 一、场的概念 二、场的分类 三、数量场&#xff08;标量场&#xff09;的等值面 四、矢量场中的矢量线 矢量线方程推导&#xff1a; 一、场的概念 场在数学上是指一个向量到另一个向量或数的映射。场指物体在空间中的分布情况。场是用空间位置函数来表征的。在物理学…

雾锁王国专用服务器设置方法,基于阿里云1分钟开服!

阿里云雾锁王国服务器搭建教程是基于计算巢服务&#xff0c;3分钟即可成功创建Enshrouded游戏服务器&#xff0c;阿里云8核32G雾锁王国专用游戏服务器90元1个月、271元3个月&#xff0c;阿里云服务器网aliyunfuwuqi.com亲自整理雾锁王国服务器详细搭建教程&#xff1a; 一、前…

全自动内衣洗衣机什么牌子好?热心推荐四款全能硬核的内衣洗衣机

内衣洗衣机这一产品是专为有特殊需求的人士所研发的&#xff0c;其的容量往往都比较小&#xff0c;并且体积也很小巧&#xff0c;安装都非常便捷&#xff0c;作为“家中第二台”补充式洗衣机被很多人推崇&#xff0c;可以作为贴身衣物的专用洗衣机&#xff0c;那么这种内衣洗衣…

Kutools For Excel | 新增 300+ 高级功能

Kutools For Excel 是一个便捷的 Excel 插件&#xff0c;具有 300 多种高级功能&#xff0c;可将各种复杂的任务简化为在 Excel 中的几次单击。 功能强大且用户友好的加载项将为 Excel 用户节省大量工作时间&#xff0c;并大大提高工作效率。支持 Excel 2021 / 2019 / 2016 / …

【Linux】调试工具 - gdb

目录 一、gdb概述&#xff1a; 二、list&#xff08;查看源文件代码&#xff09;&#xff1a; 三、run&#xff08;运行程序&#xff09;&#xff1a; ​四、断点相关操作&#xff1a; 1、查看断点&#xff1a; 2、在指定行设置断点&#xff1a; 3、在函数入口处设置断…

[计算机效率] 便笺的使用

2.4 便笺 便笺程序是一种方便用户记录、查看和编辑便签的简单应用程序。在Windows系统中&#xff0c;便笺通常作为系统自带的实用工具之一&#xff0c;可以帮助用户快速创建、编辑和组织便签&#xff0c;以便随时记录重要的信息、任务或提醒事项。 便笺程序通常具有以下特点&a…

阿里云企业2核4G5M服务器ECS u1性能测评

阿里云服务器ECS u1实例&#xff0c;2核4G&#xff0c;5M固定带宽&#xff0c;80G ESSD Entry盘优惠价格199元一年&#xff0c;性能很不错&#xff0c;CPU采用Intel Xeon Platinum可扩展处理器&#xff0c;购买限制条件为企业客户专享&#xff0c;实名认证信息是企业用户即可&a…

Metasploit(MSF)使用教程(以ms17_010永恒之蓝为例)

一.Metasploit简介&#xff1a; Metasploit就是一个漏洞框架。它的全称叫做The Metasploit Framework&#xff0c;简称MSF。是一个免费、可下载的框架&#xff0c;通过它可以很容易地获取、开发并对计算机软件漏洞实施攻击。它本身附带数2000多个已知软件漏洞的专业级漏洞攻击工…

如何运用惟客数据CDP客户数据平台构建好用户画像?

​惟客数据CDP是一个企业级客户数据资产平台&#xff0c;能够跨平台整合全域客户数据&#xff0c;统一客户身份&#xff0c;实时全景客户画像&#xff0c;基于大数据计算和挖分析提供深度客户洞察&#xff0c;实现精细化运营和精准营销。部署更轻更快&#xff0c;快速实现企业数…

API接口数据集接口pytorch api接口获取数据

API是应用程序的开发接口&#xff0c;在开发程序的时候&#xff0c;我们有些功能可能不需要从到到位去研发&#xff0c;我们可以拿现有的开发出来的功能模块来使用&#xff0c;而这个功能模块&#xff0c;就叫做库(libary)。比如说&#xff1a;要实现数据传输的安全&#xff0c…

10分钟读懂Diffusion:图解Diffusion扩散模型

数据派THU 本文通过图解的方式让大家快速了解 Diffusion 原理。 [ 导读 ]想必大家都听说过——图像领域大火的深度生成模型Diffusion Model&#xff0c;为了让大家快速了解 Diffusion 原理&#xff0c;这篇文章我们通过图解的方式。希望对你有所帮助&#xff0c;让你在学习和应…

3D Gaussian Splatting for Real-Time Radiance Field Rendering(慢慢啃,还是挺复杂的)

三个关键要素 从相机配准的过程中得到的稀疏点云开始&#xff0c;使用3D Gaussian表示场景; 3D Gaussian: 是连续体积辐射场能够防止不必要的空空间优化。对 3D Gaussion进行交叉优化和密度控制: 优化各向异性血方差对场景精确表示。使用快速可视感知渲染算法来进行快速的训练…

CVE-2024-27199 JetBrains TeamCity 身份验证绕过漏洞2

漏洞简介 TeamCity Web 服务器中发现了第二个身份验证绕过漏洞。这种身份验证旁路允许在没有身份验证的情况下访问有限数量的经过身份验证的端点。未经身份验证的攻击者可以利用此漏洞修改服务器上有限数量的系统设置&#xff0c;并泄露服务器上有限数量的敏感信息。 项目官网…

当HR问你:“什么事会让你有成就感”你该怎么回答?【文章底部添加进大学生就业交流群】

当HR问你“什么事会让你有成就感”时&#xff0c;你可以通过以下方式回答&#xff1a; 强调目标实现&#xff1a; 表达你在达成挑战性目标时感到的满足感。举例说明你在过去的工作或项目中如何设定并成功实现了目标。 强调对团队成功的贡献&#xff1a; 谈论你与团队合作取得成…

Caffeine--实现进程缓存

本地进程缓存特点 缓存在日常开发中起着至关重要的作用, 由于存储在内存中, 数据的读取速度非常快,能大量减少对数据库的访问,减少数据库的压力. 缓存分为两类: 分布式缓存, 例如Redis: 优点: 存储容量大, 可靠性更好, 可以在集群间共享缺点: 访问缓存存在网络开销场景: 缓存数…

c++: 引用能否替代指针? 详解引用与指针的区别.

文章目录 前言1. 引用和指针的最大区别:引用不能改变指向2. 引用和指针在底层上面是一样的3. 引用和指针在sizeof面前大小不同4. 有多级指针,没有多级引用5.引用是引用的实体,指针会向后偏移同一个类型的大小 总结 前言 新来的小伙伴如果不知道引用是什么?可以看我的上一篇文…