【Take you through the pid algorithm to control the motor】——(2) PID speed loop

Table of contents

foreword

 1. PID speed loop principle

2. Code

2.1 main.c

2.2 pid.c

2.4 pid.h

2.5 main.h

 3. pid tuning

 According to the timer interrupt and PWM situation I configured, the motor rotation result obtained by adjusting the speed PID coefficient to the following value is more appropriate.

 Velocity_KP=4.5,Velocity_KI=0.1,Velocity_KD=0; 

 4. Summary


foreword

1. This series of tutorials is based on the hal library development of the smallest system board of stm32f103c8t6. It will guide you to learn the speed loop, position loop and speed position cascaded pid using the Pid algorithm in the most easy-to-understand way.

2. The idea of ​​publishing this series of Pid series tutorials is that I participated in a competition some time ago and needed to use the function of cascading Pids, but I found that the tutorials on the Internet were scattered and did not focus on explaining these three together. Functional, or the written content is not simple and direct enough, and it will be very difficult for students with poor foundation to learn. In order to take everyone to master the ability to use pid to control the motor with the most intuitive steps and the simplest code, I plan to publish a complete and easy-to-understand series of tutorials, which also serves as a summary of what I have learned some time ago.

3. The Pid algorithm involves a lot of content, and it is worth our in-depth research to discover its beauty. The study of the Pid algorithm is not only a theoretical understanding, but also a practice to realize the function of controlling the speed and position. It is also a science to adjust the parameters after transplanting the pid algorithm! Hahaha, so mastering the pid is not an easy task, but I have realized the cascaded pid, please believe that I will lead everyone to realize it bit by bit from the beginning to the end!

The function to be realized in this chapter is to use the pid speed loop to control the motor. The final effect is that the motor rotates at the set speed, and the speed response is fast, the size is accurate, and the anti-interference ability is strong.

Everyone will learn:

(1) Use the PID speed loop to control the motor;

(2) How to adjust the parameters of the pid algorithm;

Before studying this chapter, it is recommended that you finish the study first: 【Take you through the pid algorithm to control the motor】——(1) The use of encoder motor and 0.96-inch OLED display

The content of this article is written on the basis of the previous code.

 1. PID speed loop principle

The speed loop uses an incremental PID algorithm. What is an incremental PID is in my article: Definition and application of positional Pid and incremental Pid

It has already been explained clearly, so I won't make redundant explanations here.

2. Code

2.1 main.c

(1) In order to adjust the pid parameters conveniently, we can use the vofa host computer; cubmx configures a serial port, here is serial port 1, and the baud rate configuration is 115200, so the baud rate I configured in the serial port assistant is also 115200 .

(2) I have also redirected the serial port 1 here, so that it is more convenient to print data on the host computer.

/* USER CODE BEGIN PV */
int16_t  speed,encoder_counter;

//float Position_KP=0.18,Position_KI=0.002,Position_KD=0; //位置PID系数
float Velocity_KP=4.5,Velocity_KI=0.1,Velocity_KD=0; //速度PID系数
int Encoder,Target_Velocity=30;
int Moto,Position_Moto;//电机PWM变量
int limit_a;
//int Position,Target_Position=850; //位置和目标位置自己设定


/* USER CODE END PV */
/* USER CODE BEGIN 0 */

/**
 * @function: void GET_NUM(void)
 * @description: 使用STM32编码器模式,读取编码器产生的脉冲值
 * @param {*} 
 * @return {*}
 */
void GET_NUM(void)
{
	encoder_counter=(short) __HAL_TIM_GET_COUNTER(&htim3);
	__HAL_TIM_SET_COUNTER(&htim3,0);//将编码器模式的定时器清零
}

/**
 * @function:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
 * @description: 定时器中断回调函数,0.1S中断一次并计算转速,将电机转速以及编码器产生的脉冲数显示在OLED屏上
 * @param {TIM_HandleTypeDef *htim} 
 * @return {*}
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim==&htim1)
	{
		//key_flag=1;
		GET_NUM();//得到所记录的脉冲数 
		//Position+=encoder_counter;
		//Position_Moto = Position_PID(Position,Target_Position);
		//limit_a=Xianfu(Position_Moto,myabs(Target_Velocity));
		//Moto = Incremental_PI(encoder_counter,limit_a);
		Moto = Incremental_PI(encoder_counter,Target_Velocity);
		
		Set_Pwm(Moto);
		
		//Key_scan();
		 //speed=(float)encoder_counter/2040/0.1;//转速为n,r/s  脉冲数转化为速度
		 //OLED_Showdecimal(0,4,speed,2,2,12,0);//在特定位置显示2位整数+2位小数的电机转速
	}
}

/*串口重定向*/
 #ifdef __GNUC__
     #define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
 #else
     #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
 #endif /* __GNUC__*/
 
 /******************************************************************
     *@brief  Retargets the C library printf  function to the USART.
     *@param  None
     *@retval None
 ******************************************************************/
 PUTCHAR_PROTOTYPE
 {
     HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);
     return ch;
 }
 
 
/* USER CODE END 0 */
/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		printf("%d,%d\n",encoder_counter,Target_Velocity);    //输出编码器的值(实际值)和目标值到vofa软件
  }

2.2 pid.c

#include "main.h"


/*******************
限幅函数
********************/
int Xianfu(int value,int Amplitude)
{
	int temp;
	if(value>Amplitude) temp = Amplitude;
	else if(value<-Amplitude) temp = -Amplitude;
	else temp = value;
	return temp;
}

/******************
函数功能:取绝对值
入口参数:int
返回值  :int
******************/
int myabs(int a)
{
	int temp;
	if(a<0)  temp=-a;
	else temp=a;
	return temp;
}

//float Err=0,last_err=0,next_err=0,pwm=0,add=0,p=0.9,i=0.3,d=0;

//int16_t myabs(int a)  //绝对值函数,传入进来的a为测得的速度speed
//{ 		   
//	  int temp;
//		if(a<0)  temp=-a;  
//	  else temp=a;
//	  return temp;
//}

//void pwm_control()//pid限幅函数
//{
//    if(pwm>999)
//        pwm=999;
//    if(pwm<0)
//        pwm=0;
//}

//float pid1(int16_t speed1,float tar1)//pid算法
//{
//    speed1=myabs(speed1);
//    Err=tar1-speed1;
//    add=p*(Err-last_err)+i*(Err)+d*(Err+next_err-2*last_err);
//    pwm+=add;
//    pwm_control();
//    next_err=last_err;
//    last_err=Err;
//    return pwm;
//}


/**************************************************************
函数功能:增量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)
//增量式的pi控制应该如上所示
****************************************************************/
int Incremental_PI (int Encoder,int Target)
{ 	
	 static float Bias,Pwm,Last_bias,next_bias;//bias=Err
	Encoder=myabs(Encoder);
	 Bias=Target-Encoder;//计算偏差
	// Integral_bias+=Bias;
	// Integral_bias=Xianfu(Integral_bias,5000);  
	 Pwm+=(Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias+Velocity_KD*(Bias+next_bias-2*Last_bias));   //增量式PI控制器
	 next_bias = Last_bias;
	 Last_bias=Bias;	                                  
//	if(Pwm>999)Pwm=999;
//	if(Pwm<-999)Pwm=-999;
	Xianfu(Pwm,99);
	 return Pwm;                                          
}


/*********************************************************************
函数功能:位置式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 position,int target)
{ 	
	 static float Bias,Pwm,Integral_bias,Last_Bias;
	 Bias=target-position;                                
	 Integral_bias+=Bias;	                            
	 Integral_bias=Xianfu(Integral_bias,myabs(Target_Velocity));
	 Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias);//位置式PID控制器
	 Last_Bias=Bias;                                      
	
	if(Pwm>10000)Pwm=10000;
	if(Pwm<-10000)Pwm=-10000;
	return Pwm;                                           
}


void Set_Pwm(int moto)
{
	if(moto<0) 
	{MOTOR_BACK;}
	else      
	{MOTOR_GO;}
	PWM_SetCompare1(moto);
	
	
	
}


2.4 pid.h

#ifndef __PID_H
#define __PID_H

//float pid1(int16_t speed1,float tar1);
int Xianfu(int value,int Amplitude);
int myabs(int a);
int Incremental_PI (int Encoder,int Target);
int Position_PID (int position,int target);
void Set_Pwm(int moto);
#endif

2.5 main.h

/* USER CODE BEGIN Includes */
#include "oled.h"
#include "oledfont.h"
#include "pwm.h"
#include "pid.h"
#include "stdio.h"
/* USER CODE END Includes */
/* USER CODE BEGIN EFP */
extern float Position_KP,Position_KI,Position_KD;
extern float Velocity_KP,Velocity_KI,Velocity_KD;
extern int16_t encoder_counter;
extern int Target_Velocity,Target_Position;

#define MOTOR_GO HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, AIN2_Pin, GPIO_PIN_SET)
#define MOTOR_BACK HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, AIN2_Pin, GPIO_PIN_RESET)
/* USER CODE END EFP */

 3. pid tuning

step:

(1) The values ​​of p, i, and d of the speed loop are first assigned to 0, and the value of p (proportional coefficient) is adjusted first, and only the values ​​of p, i are adjusted in the whole process. Print the pulse number encoder_counter (actual value) and target value Target_Velocity obtained by the encoder into vofa, observe the trend of encoder_counter value close to Target_Velocity, increase the p value if the response is too slow, and the proportional coefficient is to construct the linear relationship between input and output of.

(2) When the encoder_counter value is close to but not exceeding the Target_Velocity, the p value is almost adjusted. At this time, the encoder_counter value is closer to the Target_Velocity by increasing the i value, and the i value is generally added in units of 0.001.

(3) Results: Finally, when the encoder_counter value can respond to the Target_Velocity value relatively quickly, and when the wheel is given resistance, the wheel will not stop rotating, and the two values ​​​​of p and i are almost adjusted.

 According to the timer interrupt and PWM situation I configured, the motor rotation result obtained by adjusting the speed PID coefficient to the following value is more appropriate.

 Velocity_KP=4.5,Velocity_KI=0.1,Velocity_KD=0; 

You can read this article for specific tuning effects and tutorials:

Detailed explanation of PID tuning parameters with pictures and texts

 4. Summary

PID is a control algorithm. After mastering how to use the encoder motor , the learning of the speed loop is actually adding a speed control to the motor. In layman's terms, it is just adding a few lines of code, which is not difficult.

Code words are not easy, I hope friends who like it don't forget to like + bookmark + follow , your affirmation is the driving force for my creation.

欢迎大家积极交流,本文未经允许谢绝转载!!!

Guess you like

Origin blog.csdn.net/weixin_62261692/article/details/129963872