步进电机的速度控制

引出

        这篇主要讨论一下步进电机在速度控制时发现的一个现象,不知道有没有人遇见,就是电机在慢速的调速时,会发现速度变得慢,变速的时间周期长,例如从0rpm变成200rpm,在这个过程中可能需要的时间比较长;步进电机在快速的调速中机会发现速度变化的特别快,例如当前是800rpm加速到1000rpm,就会瞬间加速完成。

        上面所说的其实就是,明明设置的加速度是一样的,但为什么加速度在实际过程中不一样,我理解的是步进电机在速度变化时,与伺服电机不太相同,步进电机有一定的惯性,速度过快时,再进行变速就会出现加速度很高,较短时间就能完成变速,但是想到减速时也是这个特性,所以上面这个说法就不太确定了。所以有了解的大神可以帮忙解答。

解决

        但是我也想了个简单的办法,解决一下这个问题,就是通过在速度变化过程中当前速度变化,进行简单的调整加速度。完全通过逻辑代码加上调试来实现。(后续附上全部代码)

1、首先我需要有一个简单的梯形变速函数

梯形变速就是利用相同的加减系数进行自加或自减。

在这里利用了步进电机的转/分钟当做变速的转速参数,所以需要一个转速转换为ARR值的函数来改变PWM的频率。(因为计算存在无限循环小数,所以误差比较大)

//--------------------------------------------------------------------------------------
/**
  * 函    数:转速(rev/min)转换成ARR的值,在进行速度变换操作
  * 参    数:Current_RPM 当前转速(rev/min)
  * 返 回 值:转换后的ARR的值
  */
uint32_t RPM_SetARR(uint32_t Current_RPM)
{
	//直接返回ARR重装载寄存器的值
	uint32_t ARR_Value = (TIM_Hz / (Init_SetPSC + 1)) / ((Current_RPM * Motor_SetSpeed) / 60);
	return  ARR_Value;
}

 接下来是是加减速的函数

#define				GPIO_Pin_Motor_A				GPIO_Pin_3						//输出搅拌电机的PWM引脚
#define				TIM_Hz							72000000							//时钟频率为72MHz
#define				Init_SetPSC						72 - 1							//初始化的PSC预分频的值
#define				Motor_SetSpeed					1600								//步进电机的脉冲设置(实际上步进电机设置的是1600Pulse/rev)
#define				Init_SetARR						5000 - 1						   //初始化ARR的值



//-----------------------------------加速减速函数-----------------------------------
/**
  * 函    数:加速减速函数(保证启动和停机的减震)(通过ARR改变PWM的频率进行调速)
  * 参    数:Initial_Velocity 起始速度(ARR的值)
  * 参    数:Target_Velocity 目标速度(ARR的值)
  * 参	 数:Speed_Ratio 转速倍数(1为++的变换,2为+=2的变换)
  * 返 回 值:无
  */
void Speed_Transformation(uint16_t Initial_Velocity, uint16_t Target_Velocity, uint8_t Speed_Ratio)
{
	//计算出速度对应的ARR值
	Initial_Velocity += 30;  //给一个20转每分钟的初始速度,来提高加减速效率
	int Temp_Target_Velocity = Target_Velocity;  //保存输入的目标速度
	
	uint16_t Temp_InitSpeed_ARR = RPM_SetARR(Initial_Velocity);
	uint16_t Temp_TargetSpeed_ARR = RPM_SetARR(Target_Velocity);
	
	uint16_t Temp_Velocity_ARR = 0;  //中间变量来进行递加并赋值给ARR
	
	Motor_SetENA(0);  //电机使能
	
	//如果目标速度为0(就是想要设置为停机)
	if(Target_Velocity == 0)
	{
		Temp_Velocity_ARR = Temp_InitSpeed_ARR;
		int Motor_RPM = 0;
		while(Temp_Velocity_ARR < RPM_SetARR(30))
		{
			PWM_SetSetARR_PUL(Temp_Velocity_ARR += Speed_Ratio);  //中间变量递加减速
//			Delay_ms(2);
			
			OLED_ShowNum(4,11,Temp_Velocity_ARR,4);  //测试打印
			
			Motor_RPM = ARR_SetRPM(Temp_Velocity_ARR);
			unsigned char  buf1[64];
			OLED_ShowNum(4,11,Motor_RPM,5);  //测试打印
			sprintf((char *)buf1,"page0.n4.val=%d",Motor_RPM);
			HMI_Sends((char *)buf1);
			HMISendb(0xff);//结束符
		}
		unsigned char  buf1[64];
		OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
		sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
		HMI_Sends((char *)buf1);
		HMISendb(0xff);//结束符
		PWM_SetCompare_TIM2_CH4(0);  //占空比设置为0,电机停止转动
	}
	else
	{
		//如果是加速的话
		int Motor_RPM = 0;
		if(Initial_Velocity < Target_Velocity)
		{
			Temp_Velocity_ARR = Temp_InitSpeed_ARR;
			if(Initial_Velocity == 0)
			{
				Temp_Velocity_ARR = Init_SetARR;
				PWM_SetCompare_TIM2_CH4((Init_SetARR + 1) / 2);  //占空比设置为50%,电机停止转动
			}
			while(Temp_Velocity_ARR > Temp_TargetSpeed_ARR)
			{
				PWM_SetSetARR_PUL(Temp_Velocity_ARR -= Speed_Ratio);  //ARR的值减到目标速度的ARR值时
//				Delay_ms(2);
			
				OLED_ShowNum(4,11,Temp_Velocity_ARR,4);  //测试打印
				
				Motor_RPM = ARR_SetRPM(Temp_Velocity_ARR);
				unsigned char  buf1[64];
				OLED_ShowNum(4,11,Motor_RPM,5);  //测试打印
				sprintf((char *)buf1,"page0.n4.val=%d",Motor_RPM);
				HMI_Sends((char *)buf1);
				HMISendb(0xff);//结束符
			}
			unsigned char  buf1[64];
			OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
			sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
			HMI_Sends((char *)buf1);
			HMISendb(0xff);//结束符
		}
		//如果是减速的话
		else if(Initial_Velocity > Target_Velocity)
		{
			Temp_Velocity_ARR = Temp_InitSpeed_ARR;
			while(Temp_Velocity_ARR < Temp_TargetSpeed_ARR)
			{
				PWM_SetSetARR_PUL(Temp_Velocity_ARR += Speed_Ratio);	 //ARR的值加到目标速度的ARR值时
//				Delay_ms(2);
			
				OLED_ShowNum(4,11,Temp_Velocity_ARR,4);  //测试打印
				
				Motor_RPM = ARR_SetRPM(Temp_Velocity_ARR);
				unsigned char  buf1[64];
				OLED_ShowNum(4,11,Motor_RPM,5);  //测试打印
				sprintf((char *)buf1,"page0.n4.val=%d",Motor_RPM);
				HMI_Sends((char *)buf1);
				HMISendb(0xff);//结束符
			
			}
			unsigned char  buf1[64];
			OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
			sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
			HMI_Sends((char *)buf1);
			HMISendb(0xff);//结束符
		}
	}
}

速度小于30转/分钟时,变速太慢了,所以就让步进电机的最低速度变为30转/分钟。

Initial_Velocity += 30;  //给一个20转每分钟的初始速度,来提高加减速效率

以下代码是因为使用了串口屏,进行数据上报到串口屏上(不重要)

unsigned char  buf1[64];
OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
HMI_Sends((char *)buf1);
HMISendb(0xff);//结束符

2、有了上面函数就可以进行逻辑调用了

//-------------------------------------电机控制函数---------------------------------------
/**
  * 函    数:电机控制函数
  * 参    数:无
  * 返 回 值:无
  *           主要完成收到设置电机速度的数据后,调用梯形加减速的函数处理。
  *           发现在小于200转/分钟时,电机的速度变化过慢,所以需要进行处理,提高加速度
  *           在大于200转/分钟时,电机的加速度过高,所以也需要处理,降低加速度。
  */
void Motor_Control(void)
{	
	if(Motor_Threshold > Motor_RPM)  //加速
	{
		if(Motor_RPM < 200)  													//当前速度小于200rpm时
		{
			Speed_Transformation(Motor_RPM,200,6);  						//快速加到200rpm
			Motor_RPM = 200;  													//更新当前转速
			Speed_Transformation(Motor_RPM,Motor_Threshold,1);  		//高于200rpm的部分要降低加速度进行加速
			Motor_RPM = Motor_Threshold;  									//加速完成后更新当前速度
		}
		else																			//当前速度大于等于200rpm时
		{
			Speed_Transformation(Motor_RPM,Motor_Threshold,1);			//较小加速度的进行加速
			Motor_RPM = Motor_Threshold;										//更新当前速度
		}
	}
	else  //减速
	{
		if(Motor_Threshold > 200)												//目标速度大于200rpm时
		{
			Speed_Transformation(Motor_RPM,Motor_Threshold,1);			//较慢加速度进行减速
			Motor_RPM = Motor_Threshold;										//更新当前速度
		}
		else																			//目标速度小于等于200rpm时
		{
			Speed_Transformation(Motor_RPM,200,1);							//先将速度进行小加速度的减速
			Motor_RPM = 200;														//将200的速度更新给当前速度
			Speed_Transformation(Motor_RPM,Motor_Threshold,6);			//剩下的速度减速过慢,所以就用x6的加速度进行减速
			Motor_RPM = Motor_Threshold;										//最后再将当前速度更新
		}
	}
}

注释比较详细,当我需要进行电机变速时,可以直接调用上面这个函数,因为里面的两个电机速度Motor_Threshold(目标速度)和Motor_RPM(当前速度)是全局变量,测试问题不大,可以使用。(后面附上我的步进电机全部的配置文件,主函数循环可以直接循环调用电机控制函数,就不写了)

3、完整文件

#include "stm32f10x.h"                  // Device header
#include "Motor.h"
#include "OLED.h"
#include "OLED_Font.h"
#include "Delay.h"
#include "Serial.h"
#include <stdio.h>

#define				GPIO_Pin_Motor_A				GPIO_Pin_3						//输出搅拌电机的PWM引脚
#define				TIM_Hz							72000000							//时钟频率为72MHz
#define				Init_SetPSC						72 - 1							//初始化的PSC预分频的值
#define				Motor_SetSpeed					1600								//步进电机的脉冲设置(实际上步进电机设置的是1600Pulse/rev)
#define				Init_SetARR						5000 - 1						   //初始化ARR的值

//--------------------------------------------------------------------------------------
/**
  * 函    数:搅拌电机PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void Motor_PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA和GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_Motor_A;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA2引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;					 //定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	/*1500个脉冲每分钟,步进电机设置为1600Pulse/rev,大约每分钟一圈(误差角度为22.5度)*/
	TIM_TimeBaseInitStructure.TIM_Period = Init_SetARR;             //计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = Init_SetPSC;          //预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/ 
	TIM_OCInitTypeDef TIM_OCInitStructure;									 //定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);                         //结构体初始化,若结构体没有完整赋值
	                                                                //则最好执行此函数,给结构体所有成员都赋一个默认值
	                                                                //避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               //输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;        //输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;										 //初始的CCR值(占空比)
	
	//TIM2的4通道
	TIM_OC4Init(TIM2, &TIM_OCInitStructure);                        //将结构体变量交给TIM_OC4Init,配置TIM2的输出比较通道4
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);														 //使能TIM2,定时器开始运行
}

//-------------------------------------------------------------------------------------
/**
  * 函    数:单独设置占空比(停机开机时使用的函数)
  * 参    数:Compare 设置的占空比
  * 返 回 值:无
  */
void PWM_SetCompare_TIM2_CH4(uint16_t Compare)
{
	TIM_SetCompare4(TIM2, Compare);
}

//-------------------------------------------------------------------------------
/**
  * 函    数:搅拌电机PWM设置ARR重装在寄存器,改变频率从而改变电机的转速(占空比是ARR的50%)
  * 参    数:ARR_Value 写入重装载寄存器的数值
  * 返 回 值:无
  */
void PWM_SetSetARR_PUL(uint16_t ARR_Value)
{
	TIM_SetAutoreload(TIM2,ARR_Value);
	TIM_SetCompare4(TIM2, ARR_Value / 2);
}

//---------------------------------------------------------------------------------
/**
  * 函    数:转速(rev/min)转换成ARR的值,在进行速度变换操作
  * 参    数:Current_RPM 当前转速(rev/min)
  * 返 回 值:转换后的ARR的值
  */
uint32_t RPM_SetARR(uint32_t Current_RPM)
{
	//直接返回ARR重装载寄存器的值
	uint32_t ARR_Value = (TIM_Hz / (Init_SetPSC + 1)) / ((Current_RPM * Motor_SetSpeed) / 60);
	return  ARR_Value;
}

//-------------------------------------------------------------------------------------
/**
  * 函    数:ARR转成转速
  * 参    数:ARR的值
  * 返 回 值:转换后的转速
  */
uint32_t ARR_SetRPM(uint32_t Current_ARR)
{
	//直接返回ARR重装载寄存器的值
	uint32_t RPM_Value = ((TIM_Hz / (Init_SetPSC + 1) / Current_ARR) * 60) / Motor_SetSpeed;
	return  RPM_Value;
}

//-------------------------------------------------------------------------------------
/**
  * 函    数:加速减速函数(保证启动和停机的减震)(通过ARR改变PWM的频率进行调速)
  * 参    数:Initial_Velocity 起始速度(ARR的值)
  * 参    数:Target_Velocity 目标速度(ARR的值)
  * 参	 数:Speed_Ratio 转速倍数(1为++的变换,2为+=2的变换)
  * 返 回 值:无
  */
void Speed_Transformation(uint16_t Initial_Velocity, uint16_t Target_Velocity, uint8_t Speed_Ratio)
{
	//计算出速度对应的ARR值
	Initial_Velocity += 30;  //给一个20转每分钟的初始速度,来提高加减速效率
	int Temp_Target_Velocity = Target_Velocity;  //保存输入的目标速度
	
	uint16_t Temp_InitSpeed_ARR = RPM_SetARR(Initial_Velocity);
	uint16_t Temp_TargetSpeed_ARR = RPM_SetARR(Target_Velocity);
	
	uint16_t Temp_Velocity_ARR = 0;  //中间变量来进行递加并赋值给ARR
	
	Motor_SetENA(0);  //电机使能
	
	//如果目标速度为0(就是想要设置为停机)
	if(Target_Velocity == 0)
	{
		Temp_Velocity_ARR = Temp_InitSpeed_ARR;
		int Motor_RPM = 0;
		while(Temp_Velocity_ARR < RPM_SetARR(30))
		{
			PWM_SetSetARR_PUL(Temp_Velocity_ARR += Speed_Ratio);  //中间变量递加减速
//			Delay_ms(2);
			
			OLED_ShowNum(4,11,Temp_Velocity_ARR,4);  //测试打印
			
			Motor_RPM = ARR_SetRPM(Temp_Velocity_ARR);
			unsigned char  buf1[64];
			OLED_ShowNum(4,11,Motor_RPM,5);  //测试打印
			sprintf((char *)buf1,"page0.n4.val=%d",Motor_RPM);
			HMI_Sends((char *)buf1);
			HMISendb(0xff);//结束符
		}
		unsigned char  buf1[64];
		OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
		sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
		HMI_Sends((char *)buf1);
		HMISendb(0xff);//结束符
		PWM_SetCompare_TIM2_CH4(0);  //占空比设置为0,电机停止转动
	}
	else
	{
		//如果是加速的话
		int Motor_RPM = 0;
		if(Initial_Velocity < Target_Velocity)
		{
			Temp_Velocity_ARR = Temp_InitSpeed_ARR;
			if(Initial_Velocity == 0)
			{
				Temp_Velocity_ARR = Init_SetARR;
				PWM_SetCompare_TIM2_CH4((Init_SetARR + 1) / 2);  //占空比设置为50%,电机停止转动
			}
			while(Temp_Velocity_ARR > Temp_TargetSpeed_ARR)
			{
				PWM_SetSetARR_PUL(Temp_Velocity_ARR -= Speed_Ratio);  //ARR的值减到目标速度的ARR值时
//				Delay_ms(2);
			
				OLED_ShowNum(4,11,Temp_Velocity_ARR,4);  //测试打印
				
				Motor_RPM = ARR_SetRPM(Temp_Velocity_ARR);
				unsigned char  buf1[64];
				OLED_ShowNum(4,11,Motor_RPM,5);  //测试打印
				sprintf((char *)buf1,"page0.n4.val=%d",Motor_RPM);
				HMI_Sends((char *)buf1);
				HMISendb(0xff);//结束符
			}
			unsigned char  buf1[64];
			OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
			sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
			HMI_Sends((char *)buf1);
			HMISendb(0xff);//结束符
		}
		//如果是减速的话
		else if(Initial_Velocity > Target_Velocity)
		{
			Temp_Velocity_ARR = Temp_InitSpeed_ARR;
			while(Temp_Velocity_ARR < Temp_TargetSpeed_ARR)
			{
				PWM_SetSetARR_PUL(Temp_Velocity_ARR += Speed_Ratio);	 //ARR的值加到目标速度的ARR值时
//				Delay_ms(2);
			
				OLED_ShowNum(4,11,Temp_Velocity_ARR,4);  //测试打印
				
				Motor_RPM = ARR_SetRPM(Temp_Velocity_ARR);
				unsigned char  buf1[64];
				OLED_ShowNum(4,11,Motor_RPM,5);  //测试打印
				sprintf((char *)buf1,"page0.n4.val=%d",Motor_RPM);
				HMI_Sends((char *)buf1);
				HMISendb(0xff);//结束符
			
			}
			unsigned char  buf1[64];
			OLED_ShowNum(4,11,Target_Velocity,5);  //测试打印
			sprintf((char *)buf1,"page0.n4.val=%d",Temp_Target_Velocity);
			HMI_Sends((char *)buf1);
			HMISendb(0xff);//结束符
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值