【手把手带你用pid算法控制电机】——(3)PID位置环

目录

前言

1. PID位置环原理

2. 代码

3. 调参 


前言

1、该系列教程是基于stm32f103c8t6最小系统板的hal库开发,用最通俗易懂的方式手把手带你学会使用Pid算法的速度环、位置环以及速度位置串级pid。

2、出这一期Pid系列教程的想法是前段时间我参加了一个比赛,要用到串级Pid的功能,可是我发现网上的教程要么是零零散散的,没有集中一起讲解这三个功能的,要么就是写的内容不够简单直接,基础没那么好的同学学起来会很吃力。为了用最直观的步骤和最简单的代码带大家一起掌握使用pid控制电机的能力,我打算出一期完整的、通俗易懂的系列教程,同时也作为对我前段时间学习内容的总结。

3、Pid算法涉及到的内容很多,值得我们去深入地研究去发现它的美,pid算法的学习不仅仅是理论上的理解,要去实践,实现把速度、位置控制好的功能。移植好pid算法以后的调参也是一门学问呢!哈哈哈,所以掌握pid并不是一件容易的事情,不过我已经把串级pid实现了,请相信我会带着大家一起从头到尾一点一点实现!

本章节要实现的功能是使用pid位置环控制电机,最终达到的效果是:电机可以转动到特定的位置,要求位置准确,抗干扰能力强(转到目的角度以后,用手很难转动电机)。

大家会学会:使用PID位置环控制电机;

学习本章前建议大家先学习完:【手把手带你用pid算法控制电机】——(1)编码器电机和0.96寸OLED显示屏的使用

【手把手带你用pid算法控制电机】——(2)PID速度环

在学会速度环的基础上,位置环就很容易掌握了;

1. PID位置环原理

位置环用的是位置式PID算法,什么是位置式PID在我的这篇文章:位置式Pid和增量式Pid的定义及应用

已经讲的比较清楚,这里总结就是一句话:位置环原理就是将每次进入中断以后采集到的脉冲值累加,得到的值就是电机转动了多少位置的反映;通过设置位置目标值,通过PID算法就可以实现将电机转到固定的目标位置。

2. 代码

本教程的代码是基于【手把手带你用pid算法控制电机】——(2)PID速度环

不需要再写代码,只需要在那篇文章的代码中改一些地方就行。

改下面这些代码,把关于位置式PID的代码取消注释,有关速度环PID的代码注释。

/* 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);
		Moto =Xianfu(Position_Moto,(这里填PWM满幅));
		Set_Pwm(Moto);
		
		 //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",Position,Target_Position);    //输出编码器的值(实际值)和目标值到vofa软件
  }

3. 调参 

我调出来的参数是

float Position_KP=0.18,Position_KI=0.002,Position_KD=0; //位置PID系数

pid参数的整定需要实际情况实际操作,注意:

(1)速度环的pid参数与位置环的pid参数不是同一组参数,算法也有所区别;

(2)但是它俩都是pid算法,那p、i、d各参数值的作用是一样的,调参的方法是一样的,最后效果都是要实际值去靠近目标值 ,越接近说明参数调的越好。

(3)位置环一般不用d值。

码字不易,希望喜欢的小伙伴别忘了点赞+收藏+关注,你们的肯定就是我创作的动力。

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

猜你喜欢

转载自blog.csdn.net/weixin_62261692/article/details/130252025