ps:2022更新

pid详细解释
一般很少用增量式,都是用位置式,下文增量式可以不看

本文分为几个部分:
1.编码器
2.定时器输入捕获(把定时器初始化为编码器模式)
3.pid闭环控速度
编码器配置

编码器

1.概述
在这里插入图片描述
传感器-> 角速度或角位移------转化为------->电数字脉冲
2.原理
在这里插入图片描述
在这里插入图片描述
四倍频技术
在这里插入图片描述
注意:
开始的时候一直很疑惑的一个点就是这边四倍频,如果按照这样操作,那么一个方波计数器将计数2次,而且ab两相那么就是计数4次,但是我们只出现了一个方波却记了4个,所以这边有问题。

//解决方案1:经过学长讲解,下文中TIM2配置编码器时候,psc=0x0需要改为0x03,这样能达到4分频,也只有在这种情况下才能准确计算出单位时间内的方波数量//(边沿触发cnt计数,分频没用)

定时器各参数含义
解决方案2:在下文Read_Encoder()函数中,将这句代码

case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;

改成

case 2:  Encoder_TIM=( (short)TIM2 -> CNT)/4;  TIM2 -> CNT=0;break;

转速计算方法:用捕获值(一秒内输出的脉冲数)/编码器线数(转速一圈输出脉冲数)/电机减数比(内部电机转动圈数与电机输出轴转动圈数比,即减速齿轮比)

2.定时器输入捕获(把定时器初始化为编码器模式)

编码器定时器配置

void Encoder_Init_TIM2(void)
{
	RCC->APB1ENR|=1<<0;     //TIM2时钟使能
	RCC->APB2ENR|=1<<2;    //使能PORTA时钟
	GPIOA->CRL&=0XFFFFFF00;//PA0 PA1    为编码器的ab相
	GPIOA->CRL|=0X00000044;//浮空输入
	/* 把定时器初始化为编码器模式 */ 
	TIM2->PSC = 0x0;//预分频器
	TIM2->ARR = ENCODER_TIM_PERIOD-1;//设定计数器自动重装值 65535    
  TIM2->CCMR1 |= 1<<0;          //输入模式,IC1FP1映射到TI1上
  TIM2->CCMR1 |= 1<<8;          //输入模式,IC2FP2映射到TI2上
  TIM2->CCER |= 0<<1;           //IC1不反向
  TIM2->CCER |= 0<<5;           //IC2不反向
	TIM2->SMCR |= 3<<0;	          //SMS='011' 所有的输入均在上升沿和下降沿有效
	TIM2->CR1 |= 0x01;    //CEN=1,使能定时器
}
void Encoder_Init_TIM4(void)
{
	RCC->APB1ENR|=1<<2;     //TIM4时钟使能
	RCC->APB2ENR|=1<<3;    //使能PORTb时钟
	GPIOB->CRL&=0X00FFFFFF;//PB6 PB7
	GPIOB->CRL|=0X44000000;//浮空输入
	/* 把定时器初始化为编码器模式 */ 
	TIM4->PSC = 0x0;//预分频器
	TIM4->ARR = ENCODER_TIM_PERIOD-1;//设定计数器自动重装值 
  TIM4->CCMR1 |= 1<<0;          //输入模式,IC1FP1映射到TI1上
  TIM4->CCMR1 |= 1<<8;          //输入模式,IC2FP2映射到TI2上
  TIM4->CCER |= 0<<1;           //IC1不反向
  TIM4->CCER |= 0<<5;           //IC2不反向
	TIM4->SMCR |= 3<<0;	          //SMS='011' 所有的输入均在上升沿和下降沿有效
	TIM4->CR1 |= 0x01;    //CEN=1,使能定时器
}

3.pid闭环控速度

在main函数中调用

  Timer3_Init(99,7199);//=====10MS进一次中断服务函数,中断服务函数在control.c

定时器3配置函数

void Timer3_Init(u16 arr,u16 psc)  
{  
	RCC->APB1ENR|=1<<1; //时钟使能    
 	TIM3->ARR=arr;      //设定计数器自动重装值   
	TIM3->PSC=psc;      //预分频器7200,得到10Khz的计数时钟
	TIM3->DIER|=1<<0;   //允许更新中断				
	TIM3->DIER|=1<<6;   //允许触发中断	   
	TIM3->CR1|=0x01;    //使能定时器
	MY_NVIC_Init(1,3,TIM3_IRQn,2);
}  

定时器3中断服务函数,其中用到的函数在下文实现

int Target_velocity=50;  //设定速度控制的目标速度为50个脉冲每10ms
int TIM3_IRQHandler(void)  
{    
	if(TIM3->SR&0X0001)//10ms定时中断
	{   
		  TIM3->SR&=~(1<<0);   //===清除定时器1中断标志位		 
		  Encoder=Read_Encoder(2);//===读取编码器的值,M法测速,输出为每10ms的脉冲数
  		Led_Flash(100);        //===LED闪烁;指示单片机正常运行	
		  Moto1=Incremental_PI(Encoder,Target_velocity);  //===速度PI控制器
	  	Xianfu_Pwm();  //===PWM限幅
    	Set_Pwm(Moto1);  //===赋值给PWM寄存器  
	}       	
	 return 0;	  
} 

读定时器cnt计数函数

int Read_Encoder(u8 TIMX)
{
    int Encoder_TIM;    
   switch(TIMX)
	 {
	   case 2:  Encoder_TIM= (short)TIM2 -> CNT;  TIM2 -> CNT=0;break;
		 case 3:  Encoder_TIM= (short)TIM3 -> CNT;  TIM3 -> CNT=0;break;	
		 case 4:  Encoder_TIM= (short)TIM4 -> CNT;  TIM4 -> CNT=0;break;	
		 default:  Encoder_TIM=0;
	 }
		return Encoder_TIM;
}

增量式PID控制函数

/**************************************************************************
函数功能:增量PI控制器
入口参数:编码器测量值,目标速度
返回  值:电机PWM
根据增量式离散PID公式 
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
e(k)代表本次偏差 
e(k-1)代表上一次的偏差  以此类推 
pwm代表增量输出
在我们的速度控制闭环系统里面,只使用PI控制
pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
**************************************************************************/
int Incremental_PI (int Encoder,int Target)
{ 	
   float Kp=20,Ki=30;	
	 static int Bias,Pwm,Last_bias;
	 Bias=Encoder-Target;                //计算偏差
	 Pwm+=Kp*(Bias-Last_bias)+Ki*Bias;   //增量式PI控制器
	 Last_bias=Bias;	                   //保存上一次偏差 
	 return Pwm;                         //增量输出
}

位置式PID控制

/**************************************************************************
函数功能:位置式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 Position_PID (int Encoder,int Target)
{ 	
	 float Position_KP=80,Position_KI=0.1,Position_KD=500;
	 static float Bias,Pwm,Integral_bias,Last_Bias;
	 Bias=Encoder-Target;                                  //计算偏差
	 Integral_bias+=Bias;	                                 //求出偏差的积分
	 Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias);       //位置式PID控制器
	 Last_Bias=Bias;                                       //保存上一次偏差 
	 return Pwm;                                           //增量输出
}
/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:PWM
返回  值:无
**************************************************************************/
void Set_Pwm(int moto1)
{
			if(moto1>0)			AIN2=1,			AIN1=0;
			else 	          AIN2=0,			AIN1=1;
			PWMA=myabs(moto1);
}

/**************************************************************************
函数功能:限制PWM赋值 
入口参数:无
返回  值:无
**************************************************************************/
void Xianfu_Pwm(void)
{	
	  int Amplitude=7100;    //===PWM满幅是7200 限制在7100
    if(Moto1<-Amplitude) Moto1=-Amplitude;	
		if(Moto1>Amplitude)  Moto1=Amplitude;	
}

源码:

电机速度闭环控制
电机位置闭环控制

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐