基于STM32F407单片机对双直流电机的pid闭环控制(有代码)

直流电机简介

直流电动机是将直流电能转换为机械能的电动机。因其良好的调速性能而在电力拖动中得到广泛应用。直流电动机按励磁方式分为永磁、他励和自励3类,其中自励又分为并励、串励和复励3种。
当直流电源通过电刷向电枢绕组供电时,电枢表面的N极下导体可以流过相同方向的电流,根据左手定则导体将受到逆时针方向的力矩作用;电枢表面S极下部分导体也流过相同方向的电流,同样根据左手定则导体也将受到逆时针方向的力矩作用。这样,整个电枢绕组即转子将按逆时针旋转,输入的直流电能就转换成转子轴上输出的机械能。由定子和转子组成,定子:基座,主磁极,换向极,电刷装置等;转子(电枢):电枢铁心,电枢绕组,换向器,转轴和风扇等。
在这里插入图片描述

PWM调速方法

在步进电机控制系统中可以通过输人PWM波的方法来对步进电动的运动进行控制。PWM波的产生可以通过时钟频率、自动重装值等参数进行设置,从而调节PWM波的占空比和输出频率。
脉冲宽度调制(PWM),是英文“Pulse Width Modulation” 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制, PWM 原理如图:在这里插入图片描述
我们假定定时器工作在向上计数 PWM模式,且当 CNT小于CCRx 时,输出 0,当 CNT大于等于CCRx 时输出 1。那么就可以得到如上的 PWM示意图:当 CNT 值小于 CCRx 的时候, IO 输出低电平(0),当 CNT 值大于等于 CCRx 的时候,IO 输出高电平(1),当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率,这就是 PWM 输出的原理。
通过控制脉冲占空比来改变电机的电枢电压.改变占空比的方法有3种:(1)定宽调频法,这种方法是保持t1不变,只改变t2,这样周期(T或频率)也随之改变;(2)调宽调频法,保持t1不变,而改变t2,这样也使周期T(或频率)改变;(3)定频调宽法,这种方法是使周期(T或频率)不变,而同时改变t1和t2.由于前两利,方法都改变了周期(或频率),当控制频率与系统的固有频率接近时,将会引起振荡,用的比较少,因此本系统用的是定频调宽法.在脉冲作用下,当电机通电时,速度增加.电机断电时,速度逐渐减小.只要按一定规律,改变通断电时间,即可实现对电机的转速控制。

电机选型

电机选的是——德力电机DGX42BLDC4260直流无刷行星减速电机大扭力马达。
在这里插入图片描述
品牌: DELI MOTOR
型号: DGX42BLDC4260
产地: 中国大陆
电源方式: 直流电
电压: 36V
电压: DC 12V/24V
功率: 30W
级数: 1/2/3/4
刹车: 自带线控电磁刹车(蓝色引线)
极数: 24极12对极(即1圈12个脉冲…)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

电机实验平台的搭建

运用STM32F407定时器TIM2和TIM4的PWM功能分别驱动两个直流电机运动;运用串口通信将电机的实时转速反馈到PC上位机上,可利用串口调试助手获取实验数据;通过编码器对电机进行闭环控制,可将电机的实时转速反馈给主控芯片。
在这里插入图片描述

运用STM32F407定时器TIM2和TIM4的PWM功能分别驱动两个直流电机运动

,相关代码如下:

void TIM2_IRQHandler(void)  
{
  static uint32_t last_rising_1 = 0;
	if(TIM2->SR&TIM_FLAG_Update)	// 溢出中断
	{
		// 溢出中断的处理
		CountFlags1++;
	}
	if(TIM2->SR&TIM_FLAG_CC1)
	{
		// 正确捕获
		uint32_t tempccr = TIM2->CCR1;
		uint32_t correctCount = 0;
		if (CountFlags1 == 0)
		{
			correctCount = tempccr - last_rising_1;
			last_rising_1 = tempccr;
		}
		else
		{
			correctCount = 0xffff * CountFlags1  - last_rising_1 + tempccr;
			last_rising_1 = tempccr;
			CountFlags1 = 0;
		}
		if(correctCount != 0)
			MotorFrequency_1 = 8 * 2000000 / correctCount;

	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update|TIM_IT_CC1);	  
}


void TIM4_IRQHandler(void)  
{
	static uint32_t last_rising_2 = 0;
	if(TIM4->SR&TIM_FLAG_Update)	// 溢出中断
	{
		// 溢出中断的处理
		CountFlags2++;
	}
	if(TIM4->SR&TIM_FLAG_CC1)
	{
		// 正确捕获
		uint32_t tempccr = TIM4->CCR1;
		uint32_t correctCount = 0;
		if (CountFlags2 == 0)
		{
			correctCount = tempccr - last_rising_2;
			last_rising_2 = tempccr;
		}
		else
		{
			correctCount = 0xffff * CountFlags2  - last_rising_2 + tempccr;
			last_rising_2 = tempccr;	
			CountFlags2 = 0;
		}
		if(correctCount != 0)
			MotorFrequency_2 = 8 * 2000000 / correctCount;

	}
	TIM_ClearITPendingBit(TIM4, TIM_IT_Update|TIM_IT_CC1);  
}

/*
	函数:定时器2、3、4通道1输入捕获配置,把定时器2、3、4配置成计数器模式,选择外部时钟为外部时钟1模式
*/
void wheel_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_ICInitTypeDef TIM_ICInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
  
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);  //使能GPIOA、GPIOB时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2  | RCC_APB1Periph_TIM4, ENABLE);	//使能TIM2.3.4时钟

	/*TIM2_CH1 -- PA5 /  TIM3_CH1 -- PA6  / TIM4_CH1 -- PB6*/
	GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_DOWN;	

	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_5;  //PA5  
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;  //PB6
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_SetBits(GPIOA, GPIO_Pin_5);	
	GPIO_SetBits(GPIOB, GPIO_Pin_6);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_TIM2);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);


	TIM_TimeBaseStructure.TIM_Period        = 0xffff - 1; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler     = 42-1; 	 //预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	// TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
//	TIM_ICInitStructure.TIM_ICFilter = 0x0f;

	//TIM_TIxExternalClockConfig(TIM2,TIM_TIxExternalCLK1Source_TI1,TIM_ICPolarity_Rising,0);	 //设置为外部时钟模式1
	// TIM_TIxExternalClockConfig(TIM3,TIM_TIxExternalCLK1Source_TI1,TIM_ICPolarity_Rising,0);	//设置为外部时钟模式1
	//TIM_TIxExternalClockConfig(TIM4,TIM_TIxExternalCLK1Source_TI1,TIM_ICPolarity_Rising,0);	 //设置为外部时钟模式1

	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;                //选择输入端IC1映射到TI1上
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;     //上升沿捕获
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV8;           //配置输入分频8
	TIM_ICInitStructure.TIM_ICFilter = 0x11;                        //滤波器长度
	TIM_ICInit(TIM2, &TIM_ICInitStructure);
	TIM_ICInit(TIM4, &TIM_ICInitStructure);

	TIM_ITConfig(TIM2, TIM_IT_Update|TIM_IT_CC1, ENABLE);
	TIM_ITConfig(TIM4, TIM_IT_Update|TIM_IT_CC1, ENABLE);


	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
	NVIC_Init(&NVIC_InitStructure);

	TIM_SetCounter(TIM2, 0);		//计数器2、3、4清零
	// TIM_SetCounter(TIM3, 0);
	TIM_SetCounter(TIM4, 0);

	TIM_Cmd(TIM2, ENABLE); 		//计时器2、3、4使能	
	// TIM_Cmd(TIM3, ENABLE); 		
	TIM_Cmd(TIM4, ENABLE);
}

PWM相关设置:

//TIM9 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void timer9_pwm_init(void)
{		 					 	
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9,ENABLE);  		//TIM9时钟使能    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); 	//使能PORTE时钟	
	
	GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_TIM9); //GPIOE5复用为定时器9
	
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_5;           //GPIOE5
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_AF;        	//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;		//速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      	//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_UP;        	//上拉
	GPIO_Init(GPIOE,&GPIO_InitStructure);              		//初始化PE5
	  
	TIM_TimeBaseStructure.TIM_Prescaler 		= 167;  							//定时器分频,分频后为1MHz
	TIM_TimeBaseStructure.TIM_CounterMode		=	TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseStructure.TIM_Period				=	19;   							//自动重装载值,频率为50KHz
	TIM_TimeBaseStructure.TIM_ClockDivision	=	TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM9,&TIM_TimeBaseStructure);//初始化定时器9
	
	//初始化TIM9 Channel1 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode 			= TIM_OCMode_PWM1; 					//选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	//比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity 	= TIM_OCPolarity_Low; 			//输出极性:TIM输出比较极性低
	TIM_OC1Init(TIM9, &TIM_OCInitStructure);  											//根据T指定的参数初始化外设TIM1 4OC1

	TIM_OC1PreloadConfig(TIM9, TIM_OCPreload_Enable);  //使能TIM9在CCR1上的预装载寄存器
 
  TIM_ARRPreloadConfig(TIM9,ENABLE);//ARPE使能 
	
	TIM_Cmd(TIM9, ENABLE);  //使能TIM9										  
} 

void adjust_lightness(u32 lval)
{
	TIM_SetCompare1(TIM9,lval);
}

电机控制相关代码:

//---------------------------------电机控制相关说明---------------------------------//
//
// 使用定时器产生的PWM控制电机,使用同一个TIM3定时器进行控制启动停止,使用不同的通道
// 控制电机的转速
//     No.1电机:TIM2 通道1.PB4,复用引脚 PB3.转向控制
//     No.2电机:TIM3 通道2.PB5,复用引脚 PC13.转向控制
// 控制思路:
//     通用电机初始化:进行定时器的初始化;
//     个别电机初始化:初始化对应的定时器通道,和引脚
//     分离初始化可以方便的控制不同电机的初始化状态,也方便进行单独控制
//----------------------------------------------------------------------------------//

//1号电机:DIR1--PB3;
//2号电机:DIR2--PC13;
void Motor_dir_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);   //使能PB;PC

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;		//PB3
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;		//PC13
	GPIO_Init(GPIOC, &GPIO_InitStructure);
}
//1号电机:PB4--PWM-->TIM3-CH1;
//2号电机:PB5--PWM-->TIM3-CH2;
void Motor_init(u32 arr)
{
		GPIO_InitTypeDef  GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  		//TIM3时钟使能
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 	//使能PORTB时钟	

	GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_TIM3); //GPIOB4--TIM3-CH1
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_TIM3); //GPIOB5--TIM3-CH2
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_4 | GPIO_Pin_5;           //GPIOB4;5
	GPIO_InitStructure.GPIO_Mode	= GPIO_Mode_AF;        	//复用功能
	GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_100MHz;		//速度100MHz
	GPIO_InitStructure.GPIO_OType   = GPIO_OType_PP;      	//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_DOWN;        	//上拉-->下拉
	GPIO_Init(GPIOB,&GPIO_InitStructure);              		//初始化PB
	  
	TIM_TimeBaseStructure.TIM_Prescaler 	= 2;  							//定时器分频,分频后为1MHz psc
	TIM_TimeBaseStructure.TIM_CounterMode	= TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseStructure.TIM_Period		= arr; //自动重装载值
	TIM_TimeBaseStructure.TIM_ClockDivision	= TIM_CKD_DIV1; 

	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器3

	//初始化TIM3 Channel1;2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode 			= TIM_OCMode_PWM1; 					//选择定时器模式:TIM脉冲宽度调制模式2
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; 	//比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity 	= TIM_OCPolarity_High; 			//输出极性:TIM输出比较极性低-->high
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  											//根据T指定的参数初始化外设TIM3 4OC1
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器

	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  											//根据T指定的参数初始化外设TIM3 4OC1
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器
	TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能 -->TIM3
	//TIM_CtrlPWMOutputs(TIM3,ENABLE);//使能TIM3 MOE使能主输出
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	//TIM_SetCompare1(TIM3,0);
	//TIM_SetCompare2(TIM3,0);
}

//1号电机:DIR1--PB3;
void Motor1(u32 com1,u8 en1,u8 dir1)
{
    //com1 = com1*84/100;
	if (en1 == 1 && dir1 == 1)
	{   
		DIR1Z;
//		printf("dir1 = 1 ok\r\n");
		TIM_SetCompare1(TIM3,com1);
//		printf("en1 = %d \r\n",en1);
	}
    else if(en1 == 1 && dir1 != 1)
    {
		DIR1F;		
//		printf("dir1 = %d\r\n",dir1);
		TIM_SetCompare1(TIM3,com1);
//		printf("en1 = %d \r\n",en1);
    }   
	else
	{
		TIM_SetCompare1(TIM3,0);
//		printf("motor 1 stop\r\n");
	}
}
//2号电机:DIR2--PC13;
void Motor2(u32 com2,u8 en2,u8 dir2)
{
    //com2 = com2*84/100;
    if(en2 == 1 && dir2 == 1)
	{	
		DIR2Z;
//		printf("dir2 = 1 ok\r\n");	
		TIM_SetCompare2(TIM3,com2);
//		printf("en2 = %d \r\n",en2);
	}
	else if(en2 == 1 && dir2 != 1)
	{
		DIR2F;
//		printf("dir2 = %d\r\n",dir2);
		TIM_SetCompare2(TIM3,com2);
//		printf("en2 = %d \r\n",en2);
	}
    else
    {
		TIM_SetCompare2(TIM3,0);
//		printf("motor 2 stop\r\n");
    }
}

实验平台示意图:
在这里插入图片描述
可通过串口通信,获得两个电机的实时转速:
在这里插入图片描述

利用PI控制算法实现保持两个电机转速同步的功能

在这里插入图片描述
假设电机目标转速是tar,假设初时时刻转速为v,那么当前时刻的转速和目标转速之间一定存在一个误差error:
error = tar - v
如果用比例控制算法,就是指转速调整量v和误差error是成正比的。即 :
v=kp×error

假设tar=100,v=80,那么error=20,若kp取0.5,那么t=1时刻,v=20×0.5=10,此时转速调整为90;t=2时刻,v=10×0.5=5,此时转速调整为95……可见,最终转速会达到要求

int Velocity_PID (int v, int tar)
{
  static int kp=1;
  
  static int error,DeltPwm,last_error=0,  integral_error=0;
  
  error = tar - v;
  DeltPwm = kp * error;
  return DeltPwm; 
}

存在问题
单单的比例控制存在着一些不足,其中一点就是 ——稳态误差。

像上述的例子,根据kp取值不同,转速最后都会达到100,但是,电机在实际运动过程中,会存在水的阻力及其他的外界干扰因素。

仍然假设tar=100,v=80,kp取0.5,那么会存在着某种情况,转速经过几次调整仍然是80,并没有改变。因为v=80,则误差error=20. 所以每次转速调整量v=0.5×20=10.同时,假设每次进行转速调整由于各类阻力的存在又会损失10的转速,增加的转速和损失的转速相互抵消,转速将不再变化!!

也就是说,我的目标是100,但是最后转速达到80就不在变化了,且系统已经达到稳定。由此产生的误差就是稳态误差了。
在这里插入图片描述
由实验数据可得,设置目标值为600,如果仅用PI控制,会存在误差。

由此可见,如果仅仅用比例调节,会存在稳态误差,所以必须运用PID控制算法,根据位置式离散PID公式 :
pwm=Kpe(k)+Ki∑e(k)+Kd[e(k)-e(k-1)]
e(k)代表本次偏差
e(k-1)代表上一次的偏差
∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,k;
pwm代表输出

位置闭环控制就是根据编码器的脉冲累加测量电机的位置信息,并与目标值进行比较,得到控制偏差,然后通过对偏差的比例、积分、微分进行控制,使偏差趋向于零的过程。

pid控制电机相关代码:

///**************************************************************************
//函数功能:绝对值函数
//入口参数:int
//返回  值:unsigned int
//**************************************************************************/
////int myabs(int a)
////{ 		   
////	  int temp;
////		if(a<0)  temp=-a;  
////	  else temp=a;
////	  return temp;
////}
///**************************************************************************
//函数功能:位置式PID控制器
//入口参数:编码器测量位置信息,目标位置
//返回  值:电机PWM
//根据位置式离散PID公式 
//pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
//e(k)代表本次偏差 
//e(k-1)代表上一次的偏差  
//∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;
//pwm代表输出
//**************************************************************************/

int Target_1 = 600,Target_2 = 600;
int Direction_1 = 1, Direction_2 = 1; //方向,1正转,0反转
int Enable = 1;


extern int MotorFrequency_1, MotorFrequency_2;

void Motor_Pid_Controller_init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);	//使能TIM3时钟
		
	TIM_TimeBaseStructure.TIM_Period        = 500 - 1; //设定计数器自动重装值
	TIM_TimeBaseStructure.TIM_Prescaler     = 8400 - 1; 	 //预分频器   
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode   = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);	 				//3.允许定时器更新中断
	TIM_Cmd(TIM1, ENABLE);
	
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
	NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_TIM10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	
	
	TIM_SetCounter(TIM1, 0);
}


void TIM1_UP_TIM10_IRQHandler(void)  
{
	static int DeltPwm_1, DeltPwm_2;
  static int LastPwm_1 = 2*700, LastPwm_2 = 2*700;//, TwoPwm_1 = 2*700, TwoPwm_2 = 2*700;//, ThreePwm_1 = 2*700, ThreePwm_2 = 2*700;
  static int PresentPwm_1, PresentPwm_2;
	
	
	if(TIM_GetITStatus(TIM1, TIM_IT_Update)==SET)	// 溢出中断
	{
		{
			DeltPwm_1 = Velocity_PID(MotorFrequency_1,Target_1);
			DeltPwm_2 = Velocity_PID(MotorFrequency_2,Target_2);
			PresentPwm_1 = DeltPwm_1 + LastPwm_1;
			PresentPwm_2 = DeltPwm_2 + LastPwm_2;

			if(PresentPwm_1>2*2500 | PresentPwm_1<0)
			  PresentPwm_1 = LastPwm_1/2 + Target_1 + 700;

			if(PresentPwm_2>2*2500 | PresentPwm_2<0)
			  PresentPwm_2 = LastPwm_2/2 + Target_2 + 700;
		}
	
		Motor1(PresentPwm_1,Enable,Direction_1);
		Motor2(PresentPwm_2,Enable,Direction_2);
		LastPwm_1 = PresentPwm_1;
		LastPwm_2 = PresentPwm_2; 

		printf("r1 = %d,r2 = %d\r\n",MotorFrequency_1,MotorFrequency_2);
		printf("target_1 = %d, pre_pwm_1 = %d, target_2 = %d, pre_pwm_2 = %d\r\n",Target_1,PresentPwm_1,Target_2,PresentPwm_2);
	}
	TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
	// 正常的定时中断处理  
} 


int Velocity_PID (int v, int tar)
{
	static int kp=1;//,ti=100,td_=10;
	static int error,DeltPwm,last_error=0, integral_error=0;
	error = tar - v;
	integral_error += error;
	DeltPwm = kp * error+ kp*integral_error/ti + kp*(error - last_error)/td_;
	last_error = error;
	return DeltPwm; 
}

总结

在这里插入图片描述
PID控制算法中,比例,微分,积分每个项前面都有一个系数。在真正的工程实践中,最难的是如果确定三个项的系数,这就需要大量的实验以及经验来决定了。接下来需要通过不断的尝试和正确的思考,选取合适的系数,实现对电机转速的优良控制。

发布了160 篇原创文章 · 获赞 316 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/ShawnWang1994/article/details/103843306
今日推荐