电机速度曲线规划2:S形速度曲线设计与实现

  电机驱动是很常见的应用,在很多系统中我们都会碰到需要改变电机的速度以实现相应的控制功能,这就涉及到电机速度曲线规划的问题。在这篇中我们就来简单讨论一下电机的S型曲线规划的问题。

1、基本原理

  S型速度曲线控制算法是工业控制领域另一种常用的加减速控制策略,S型曲线很好的克服了T型曲线加速度不连续的问题。

  S型曲线实际就是实现一个加速度的T型变化过程,具体来说就是加速度增加、加速度恒定、将速度减小的过程。在整个速度调节规程中,加速度是连续变化的,而反映到速度的变化就是一条平滑的S型曲线。如下图所示:

  这是比较常见的S曲线的形式,其函数表达式如下:

  这一函数是这类函数的一个特例,其并不具有普遍性。在我们应用中,我们可能会需要根据应用的要求对S型曲线在横轴和纵轴两个方向平移或拉伸。所以这一函数更为普遍的描述如下:

  在这一表达式中,A表示在纵轴方向的平移,B表示在纵轴方向的拉伸,a表示在横轴方向的拉伸,b表示在横轴方向的平移。具体反映到函数图形上就是如下图所示:

  那么究竟如何使用这一S曲线函数来对电机进行调速呢?我们考虑到,所谓电机调速实际就是电机速度与运行时间之间存在一定的函数关系。很显然,纵轴就是电机速度,横轴就是运行时间。于是我们就可以得到电机的S型速度曲线的函数关系如下:

  可能大家会发现,这个速度曲线的函数似乎与前面的数学函数有些许差别。这是为了更好的适应调速的区别。在数学上,数轴都是对称的,但在速度调节过程中,速度和时间都不可能存在负数的情况,所以我们需要对其进行平移。但是平移之后,S曲线的图形就不对称了,所以我们以整个调速过程的调速时间的中间点为轴就是对称的了,所以就有了上述的函数表达式。

2、设计与实现

  我们已经简单描述了S型速度规划曲线的数学原理及应用表达式。接下来我们来考虑怎么实现它。

  考虑到在同一个驱动器中可能因为应用场景的需要存在多条的速度规划曲线。所以我们以基于对象的思路来考虑它,这样我们在更换不同的曲线就只需要更换不同的曲线实力就可以了。所以我们先来分析一下曲线对象的属性和操作。

  鉴于前面的分析,我们认为作为一个调速曲线对象至少要记录:开始调速时的初始速度、当前速度、目标速度、加速度、最大速度、最小速度、调速时间、调速时间跨度、曲线类型以及S曲线拉伸度等,我们将这些记为对象的属性。据此我们可以定义电机速度曲线的对象类型为:

/* 定义电机速度曲线对象 */
typedef struct CurveObject {float startSpeed;    //开始调速时的初始速度float currentSpeed;   //当前速度float targetSpeed;   //目标速度float stepSpeed;    //加速度float speedMax;     //最大速度float speedMin;     //最小速度uint32_t aTimes;    //调速时间uint32_t maxTimes;   //调速跨度SpeedCurveType curveMode;  //曲线类型float flexible;     //S曲线拉伸度
}CurveObjectType;

  我们已经定义了一个速度曲线对象类型,接下来我们就来分析如何实现一个S型调速曲线。我们已经描述过,速度其实就是时间的函数,根据我们前面分析的速度时间的函数表达式,我们实现如下:

void (*pCalCurve[])(CurveObjectType *curve)={CalCurveNone,CalCurveTRAP,CalCurveSPTA};/* 电机曲线加减速操作-------------------------------------------------------- */
void MotorVelocityCurve(CurveObjectType *curve)
{float temp=0;if(curve->targetSpeed>curve->speedMax){curve->targetSpeed=curve->speedMax;}if(curve->targetSpeed<curve->speedMin){curve->targetSpeed=curve->speedMin;}if((fabs(curve->currentSpeed-curve->startSpeed)<=curve->stepSpeed)&&(curve->maxTimes==0)){if(curve->startSpeed<curve->speedMin){curve->startSpeed=curve->speedMin;}temp=fabs(curve->targetSpeed-curve->startSpeed);temp=temp/curve->stepSpeed;curve->maxTimes=(uint32_t)(temp)+1;curve->aTimes=0;}if(curve->aTimes<curve->maxTimes){pCalCurve[curve->curveMode](curve);curve->aTimes++;}else{curve->currentSpeed=curve->targetSpeed;curve->maxTimes=0;curve->aTimes=0;}
}/*S型曲线速度计算*/
static void CalCurveSPTA(CurveObjectType *spta)
{float power=0.0;float speed=0.0;power=(2*((float)spta->aTimes)-((float)spta->maxTimes))/((float)spta->maxTimes);power=(0.0-spta->flexible)*power;speed=1+expf(power);speed=(spta->targetSpeed-spta->startSpeed)/speed;spta->currentSpeed=speed+spta->startSpeed;if(spta->currentSpeed>spta->speedMax){spta->currentSpeed=spta->speedMax;}if(spta->currentSpeed<spta->speedMin){spta->currentSpeed=spta->speedMin;}
}

  在这个实现中,我们出于更普遍的实用性考虑,将各种曲线的相同操作集成在一起,然后将它们差异的部分通过曲线类型属性以回调函数的方式集成。

3、应用与验证

  我们实现了S型电机速度规划曲线的基本设计与实现。接下来,我们就是用这一调速曲线来实现一个电机调速的实例。我们定义速度规划曲线时,是及与对象的思想来实现的,所以我们先声明一条曲线对象实例。

  CurveObjectType curve; //电机调速曲线

  在声明了这一曲线对象后,我们需要对其初始化赋值才能正确的使用。大多数的属性直接根据应用对象的要求给予初始值就可以了。需要注意的是曲线类型这一属性,这将决定使用什么样的速度规划曲线。该属性为SpeedCurveType枚举,该枚举定义如下:

/* 定义电机速度曲线类型枚举 */
typedef enum SpeedCurve {CURVE_NONE=0,  //直启CURVE_TRAP=1,  //梯形曲线CURVE_SPTA=2  //S型曲线
}SpeedCurveType;

  在这里我们需要将曲线类型初始化为T形速度规划曲线。具体操作如下:

  curve.curveMode=CURVE_SPTA;

  初始化完成之后就可以使用曲线对象来实现电机速度的调节了。使用也很简单,只要按一定的时间周期调用我们前面实现的MotorVelocityCurve函数就可以实现整个调速过程。具体如下:

  MotorVelocityCurve(&curve);

  在每次调速开始之前都需要设置取下的开始速度和目标速度,这样函数就会按照设定的起始速度和目标速度实现速度调整。

4、小结

  S型速度曲线将整个运动过程划分为7个阶段,即加加速度段、匀加速度段、减加速度段、匀速段、加减速度段、匀减速度段和减减速度段,不同阶段速度衔接处加速度是连续的,且加速度的变化率可控,克服了梯形速度曲线中存在的加速度突变的不利影响,这是S型曲线的一大优势。

  但是相比于T型速度曲线,S型曲线的计算量要大很多,实现起来也比T型曲线更为复杂。不过这些在现在的处理系统中都不再是问题。在使用过程中需要关注参数设定,特别是拉伸系数,必须根据应用场合需要仔细调整。如果太小,中间调速过程会变平坦,起始和结束阶段则会相对变化较快;如果太大,其实和结束过程会变得比较平坦,但中间部分会变化较快。具体就要看应用需求了。

欢迎关注:

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

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

相关文章

USB应用开发笔记之一:STM32上实现USB主机读写U盘

在项目应用中&#xff0c;经常会有对外交换数据的需求。USB接口读写U盘无疑是一种颇为方便的选择。在这一篇中&#xff0c;我们就来讨论如何在STM32上实现USB主机读写U盘文件的方法。 1、应用概述 在我们的产品上有这样一个需求&#xff0c;希望通过大容量的U盘存取数据。我们…

Modbus协议栈综合实例设计

自我们开源了我们的Modbus协议栈之后&#xff0c;就一直有朋友来信说希望提供示例。这次我们整理了几个例子以供参考。 1、应用实例规划 在这次的实例中&#xff0c;我们使用的目标板拥有一个以太网接口、一个RS232串行接口和一个RS485串行接口&#xff0c;所以我们规划实现&a…

ThreadX应用开发笔记之二:移植ThreadX到STM32H7平台

前面我们将ThreadX成功移植到了STM32F4平台&#xff0c;但这只是我们的部分应用。我们希望将ThreadX的优势发挥到我们的更多应用中&#xff0c;所以在这一篇中我们就来实现将ThreadX移植到STM32H7平台中。 1、前期准备 在开始将ThreadX移植到STM32H7平台之前&#xff0c;我们需…

外设驱动库开发笔记31:S-Modlue远红外气体传感器驱动

在气体分析类产品中&#xff0c;我们经常会用到远红外气体传感器。我们就在碳氢类气体成分分析中使用了S-Modlue远红外气体传感器。接下来&#xff0c;我们将讨论S-Modlue远红外气体传感器驱动的设计与实现。 1、功能概述 S-MODULE EVO 使用非分散红外检测技术NDIR&#xff0c…

外设驱动库开发笔记32:HLPM025K3 PM2.5传感器驱动

现在人们对大气环境及室内环境都比较关注。PM2.5在生活中也是常见的词汇。在有些产品中就要求检测PM2.5的数值。检测PM2.5的手段多种多样&#xff0c;在要求不高时我们通常可以采用激光模块。在这一篇中&#xff0c;我们将讨论HLPM025K3 PM2.5传感器驱动的设计与实现。 1、功能…

外设驱动库开发笔记33:LCD1602液晶显示屏驱动

LCD1602是一种工业字符型液晶&#xff0c;能够同时显示16x02即32个字符。LCD1602液晶显示的原理是利用液晶的物理特性&#xff0c;通过电压对其显示区域进行控制&#xff0c;即可以显示出图形。在这一章我们就来讨论LCD1602液晶显示屏驱动的设计与实现。 1、功能概述 LCD1602液…

滤波器开发之四:基于算术平均的中值滤波器

在信号采集系统中&#xff0c;除了我们感兴趣的数据外&#xff0c;难免会有一些来自于环境的干扰信号。但我们总希望我们得到的数据是纯净而真实的&#xff0c;为了达到这个目标&#xff0c;我们不得不想办法去除这些干扰信号&#xff0c;于是滤波器就成为我们必不可少的帮手。…

LwIP应用开发笔记之十一:LwIP带操作系统UDP服务器

我们已经实现了在FreeRTOS系统上的LwIP的移植工作&#xff0c;但只是简单的在系统平台上跑了起来。我们还希望能做更多的事情&#xff0c;这一节我们就在FreeRTOS系统上实现基于LwIP的UDP服务器。 1、UDP协议简述 UDP协议全称是用户数据报协议&#xff0c;在网络中它与TCP协议…

滤波器开发之五:基于算术平均的限幅滤波器

通过AD采集数据时&#xff0c;我们总是希望采集到的数据是纯净而真实的&#xff0c;而实际上环境中存在太多的干扰信号&#xff0c;为了让我们得到的数据尽可能地接近实际值&#xff0c;我们需要降低这些干扰信号的影响。所以软件实现的数字滤波器应运而生&#xff0c;这一篇我…

外设驱动库开发笔记34:OLED显示屏驱动

现在OLED显示屏在嵌入式系统中应用的越来越多。对于一些显示信息不太复杂&#xff0c;以显示信息为主的需求&#xff0c;我们一般会选择OLED显示屏。在这一篇中&#xff0c;我们将讨论OLED显示屏驱动的设计与实现。 1、功能概述 从使用的情况来说&#xff0c;较为常用的是0.96…

外设驱动库开发笔记35:迪文触摸屏驱动

有些时候嵌入式系统也需要显示更为复杂的图形&#xff0c;需要更丰富的数据展示。为此&#xff0c;我们需要更大&#xff0c;色彩更丰富&#xff0c;带触屏的显示屏&#xff0c;当然性价比更高就最好了。在我们的项目中遇到此类需求&#xff0c;我们有时会选择DWIN触摸屏。在本…

快速实现一个室内空气质量检测仪

冬天我们大多会关闭门窗&#xff0c;而依靠暖通空调设备来维持室内温度。而在保证居室温度的同时&#xff0c;我们也希望保持居室内大气环境的健康度。鉴于此&#xff0c;我们设计了一个简单的室内空气质量检测器。 1、系统概述 我们依靠暖通空调设备来维持室内温度、湿度和通…

外设驱动库开发笔记36:NTC负温度系数热电阻测温驱动

在嵌入式产品中&#xff0c;温度检测非常常见。在成本比较敏感而精度要求较低时&#xff0c;NTC电阻是个不错的选择。在这一篇中&#xff0c;我们将讨论如何和设计并实现一个通用的NTC驱动&#xff0c;以便在后续的项目中更方便的复用。 1、功能概述 NTC是指随温度上升电阻呈指…

外设驱动库开发笔记37:S1336-5BQ光敏二极管作为光度计驱动

光敏二极管能够实现很多应用&#xff0c;用于光度检测即是其一。我们在一些产品中就曾使用S1336-5BQ光敏二极管进行光度值检测。所以在本篇中&#xff0c;我们将讨论如何设计并实现S1336-5BQ光敏二极管用于光度检测的驱动。 1、功能概述 根据相关的资料&#xff0c;光电二极管…

PID控制器改进笔记之六:改进PID控制器之参数设定

前面我们发布了一系列PID控制器相关的文章&#xff0c;包括经典PID控制器以及参数自适应的PID控制器。这一系列PID控制器虽说实现了主要功能&#xff0c;也在实际使用中取得了良好效果&#xff0c;但还有很多的细节部分可以改进以提高性能和灵活性。这篇中我们来讨论改进PID控制…

软件设计开发笔记1:基于状态机的程序设计

在编码实现的过程中&#xff0c;我们会经常使用到条件判断结构&#xff0c;而且使用起来很方便。但是在需要转移的状态比较多&#xff0c;或是条件比较复杂时&#xff0c;我们就可能需要很长的条件判断结构来处理。不过&#xff0c;过于复杂的条件判断结构会给代码的编写和维护…

外设驱动库开发笔记38:RTD热电阻测温驱动

我们已经讨论过多种温度检测方式&#xff0c;但我们尚未关注热电阻温度检测&#xff0c;但热电阻测温在工业环境中是非常常见的。尽管有很多集成的数字式的热电阻接口元器件&#xff0c;但这些器件不但成本较高&#xff0c;灵活性也大打折扣。所以我们有时会使用更简单灵活的电…

外设驱动库开发笔记39:按键操作驱动

按键在我们的项目中是经常使用到的组件。一般来说&#xff0c;我们都是在用到按键时直接针对编码&#xff0c;但这样每次都做很多重复性的工作。所以在这里我们考虑做一般性抽象得到一个可应用于按键操作的通用性驱动程序。 1、功能概述 按键操作在我们的产品种经常用到&#…

外设驱动库开发笔记40:AT25xxx外部存储器驱动

我们在前面开发过AT24CXX系列EEPROM存储器&#xff0c;它使用的是I2C接口。不过有时候我们也会使用SPI接口的EEPROM存储器。在这一篇我们将来讨论AT25XXX系列EEPROM存储器的驱动设计、实现及使用。 1、功能概述 AT25XXX系列EEPROM存储器采用SPI接口&#xff0c;因其操作简单且…

闪存中的键值对:无文件系统 minINI

许多嵌入式系统应用需要以持久的方式存储某种数据&#xff1a;校准值、设置或日志信息。对于较少的数据量&#xff0c;使用外部存储器或文件系统是一种过度大材小用。在许多系统中&#xff0c;我使用minINI以“ini-file”的方式存储键值解析&#xff0c;但它需要使用某种文件系…