1. 串级PID原理

速度位置串级PID顾名思义,是速度环与位置环两个环串联一起使用;内环是速度环,外环是位置环,位置pid的输出作为速度pid的输入,把位置pid的输出作为速度pid的目标值去控制。

控制框图如下所示:

2. 代码

本教程的代码是基于【手把手带你用pid算法控制电机】——(2)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)
	{
		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软件
  }

显示实际位置与目标位置的图像,观察图线是不是以一定的斜率上升到一个值就不变化了;原理:位置的导数就是速度,所以位置图像的斜率就表示速度的大小;

斜率不变代表速度大小不变,即速度环效果可以;

脉冲值达到一定值以后不上升,表示位置环效果可以;

同时有这两个现象表示串级PID效果可以。

3. 调参

3.1 速度内环整定

(1)我们要先调整速度内环,保证有阶跃响应的稳定,快速,准确性。

(2)速度环要调到的效果:从速度0到收到一个控制目标,速度迅速达到20;从速度20收到一个控制目标,速度迅速到达0。

(3)±1的偏差为编码器的最小量级,PID控制器是作用于偏差,有偏差后它才能生效,所以属于正常控制范围。

3.2 整合位置外环

因为位置环的输出要作为速度环的速度目标值,所以注意整定位置环参数时,位置环的输出要接近速度环的量级,这样理解:速度环的量级指的就是速度环的目标值指的是每个中断周期内捕捉到的脉冲数值,例如每10ms内电机的脉冲数值是比较小的,所以速度目标值也比较小,所以位置环的参数就也可能比较小。

总之:要根据速度环的需求去整定位置环的参数,并且还要对位置环限幅才能达到应该好的效果。

串级pid效果如下:横坐标为时间,纵坐标有两个,分别是位置目标值和位置实际值,其中实际速度值可以通过实际位置曲线的斜率得以体现,斜率恒定,表示实际速度值恒定。

4. 遇到的问题

理论上,把速度环和位置环单独调好以后,整合起来,串级pid的效果是比较好的,如果内外环参数单独调的时候都是正常的,整合以后电机乱转(没有按理想情况转),十有八九你现在是已经磨了很长一段时间了,你脑袋已经昏了,绝对是你代码写错了,注意位置环和速度环控制函数里输入的参量不要搞错,我就是凌晨3点还在调,没调好,第二天发现是自己函数的输入参量搞错了。

我是速度环的两个参量填错了,导致电机总是乱转,搞得我莫名其妙的。

所以大家移植代码的时候一定要从上到下仔细分析一下代码逻辑,理清楚,避免出现低级错误。

5. 结语

说到最后,想到前言我说了是我本人最近做了一个用到了串级pid的项目,到了本系列教程的最后一节了,也该告诉大家我做的是项目具体是什么。我做了一个智能化电梯,用n20电机正反转模拟电梯的上下行,电机以恒定速度正反转不同圈数代表电梯上升或下降到不同的楼层,并且可以反复持续工作,电机运行都正常,每次误差也不会太大。通过这个项目对速度环,位置环,串级Pid都有了初步的认识。

后面在很多地方都再次用到了速度环、位置环、串级控制等操作,每次实操,我都感觉自己对pid控制算法有新的理解和体会,所以我觉得想一次就把一个知识完全掌握,或者说是掌握的较为全面,是不太可能的;初步学了一个知识以后,一定要在其他项目里多实操,以达到对知识更全面更可靠的理解,而不是只停留在会用,只是对知识一知半解。要知道,往往上爬,真正的高手过招,那都拼的是基础知识和能力!与众君共勉!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐