当前位置:   article > 正文

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

速度环pid代码

目录

前言

 1. PID速度环原理

2. 代码

2.1 main.c

2.2 pid.c

2.4 pid.h

2.5 main.h

 3. pid调参

 按照我配置的定时器中断和PWM情况,将速度PID系数调成以下值得到的电机转动结果比较合适。

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

 4. 小结


前言

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

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

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

本章节要实现的功能是使用pid速度环控制电机,最终达到的效果是电机以设定的速度转动,且速度响应快,大小准确,抗干扰能力强。

大家会学会:

(1)使用PID速度环控制电机;

(2)如何调pid算法的参数;

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

本文内容是在前面代码的基础上进行编写的。

 1. PID速度环原理

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

已经讲的比较清楚,这里不做多余阐述。

2. 代码

2.1 main.c

(1)为了方便调pid的参数,我们可以使用vofa上位机;cubmx配置一个串口,这边使用的是串口1,波特率配置的是115200,所以串口助手中我配置的波特率也是115200。

(2)我这里也将串口1重定向了,这样在上位机上打印数据比较方便。

  1. /* USER CODE BEGIN PV */
  2. int16_t speed,encoder_counter;
  3. //float Position_KP=0.18,Position_KI=0.002,Position_KD=0; //位置PID系数
  4. float Velocity_KP=4.5,Velocity_KI=0.1,Velocity_KD=0; //速度PID系数
  5. int Encoder,Target_Velocity=30;
  6. int Moto,Position_Moto;//电机PWM变量
  7. int limit_a;
  8. //int Position,Target_Position=850; //位置和目标位置自己设定
  9. /* USER CODE END PV */
  1. /* USER CODE BEGIN 0 */
  2. /**
  3. * @function: void GET_NUM(void)
  4. * @description: 使用STM32编码器模式,读取编码器产生的脉冲值
  5. * @param {*}
  6. * @return {*}
  7. */
  8. void GET_NUM(void)
  9. {
  10. encoder_counter=(short) __HAL_TIM_GET_COUNTER(&htim3);
  11. __HAL_TIM_SET_COUNTER(&htim3,0);//将编码器模式的定时器清零
  12. }
  13. /**
  14. * @function:void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  15. * @description: 定时器中断回调函数,0.1S中断一次并计算转速,将电机转速以及编码器产生的脉冲数显示在OLED屏上
  16. * @param {TIM_HandleTypeDef *htim}
  17. * @return {*}
  18. */
  19. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  20. {
  21. if(htim==&htim1)
  22. {
  23. //key_flag=1;
  24. GET_NUM();//得到所记录的脉冲数
  25. //Position+=encoder_counter;
  26. //Position_Moto = Position_PID(Position,Target_Position);
  27. //limit_a=Xianfu(Position_Moto,myabs(Target_Velocity));
  28. //Moto = Incremental_PI(encoder_counter,limit_a);
  29. Moto = Incremental_PI(encoder_counter,Target_Velocity);
  30. Set_Pwm(Moto);
  31. //Key_scan();
  32. //speed=(float)encoder_counter/2040/0.1;//转速为n,r/s 脉冲数转化为速度
  33. //OLED_Showdecimal(0,4,speed,2,2,12,0);//在特定位置显示2位整数+2位小数的电机转速
  34. }
  35. }
  36. /*串口重定向*/
  37. #ifdef __GNUC__
  38. #define PUTCHAR_PROTOTYPE int _io_putchar(int ch)
  39. #else
  40. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
  41. #endif /* __GNUC__*/
  42. /******************************************************************
  43. *@brief Retargets the C library printf function to the USART.
  44. *@param None
  45. *@retval None
  46. ******************************************************************/
  47. PUTCHAR_PROTOTYPE
  48. {
  49. HAL_UART_Transmit(&huart1, (uint8_t *)&ch,1,0xFFFF);
  50. return ch;
  51. }
  52. /* USER CODE END 0 */
  1. /* USER CODE BEGIN WHILE */
  2. while (1)
  3. {
  4. /* USER CODE END WHILE */
  5. /* USER CODE BEGIN 3 */
  6. printf("%d,%d\n",encoder_counter,Target_Velocity); //输出编码器的值(实际值)和目标值到vofa软件
  7. }

2.2 pid.c

  1. #include "main.h"
  2. /*******************
  3. 限幅函数
  4. ********************/
  5. int Xianfu(int value,int Amplitude)
  6. {
  7. int temp;
  8. if(value>Amplitude) temp = Amplitude;
  9. else if(value<-Amplitude) temp = -Amplitude;
  10. else temp = value;
  11. return temp;
  12. }
  13. /******************
  14. 函数功能:取绝对值
  15. 入口参数:int
  16. 返回值 :int
  17. ******************/
  18. int myabs(int a)
  19. {
  20. int temp;
  21. if(a<0) temp=-a;
  22. else temp=a;
  23. return temp;
  24. }
  25. //float Err=0,last_err=0,next_err=0,pwm=0,add=0,p=0.9,i=0.3,d=0;
  26. //int16_t myabs(int a) //绝对值函数,传入进来的a为测得的速度speed
  27. //{
  28. // int temp;
  29. // if(a<0) temp=-a;
  30. // else temp=a;
  31. // return temp;
  32. //}
  33. //void pwm_control()//pid限幅函数
  34. //{
  35. // if(pwm>999)
  36. // pwm=999;
  37. // if(pwm<0)
  38. // pwm=0;
  39. //}
  40. //float pid1(int16_t speed1,float tar1)//pid算法
  41. //{
  42. // speed1=myabs(speed1);
  43. // Err=tar1-speed1;
  44. // add=p*(Err-last_err)+i*(Err)+d*(Err+next_err-2*last_err);
  45. // pwm+=add;
  46. // pwm_control();
  47. // next_err=last_err;
  48. // last_err=Err;
  49. // return pwm;
  50. //}
  51. /**************************************************************
  52. 函数功能:增量PI控制器
  53. 入口参数:编码器测量值,目标速度
  54. 返回 值:电机PWM
  55. 根据增量式离散PID公式
  56. pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)+Kd[e(k)-2e(k-1)+e(k-2)]
  57. e(k)代表本次偏差
  58. e(k-1)代表上一次的偏差 以此类推
  59. pwm代表增量输出
  60. 在我们的速度控制闭环系统里面,只使用PI控制
  61. pwm+=Kp[e(k)-e(k-1)]+Ki*e(k)
  62. //增量式的pi控制应该如上所示
  63. ****************************************************************/
  64. int Incremental_PI (int Encoder,int Target)
  65. {
  66. static float Bias,Pwm,Last_bias,next_bias;//bias=Err
  67. Encoder=myabs(Encoder);
  68. Bias=Target-Encoder;//计算偏差
  69. // Integral_bias+=Bias;
  70. // Integral_bias=Xianfu(Integral_bias,5000);
  71. Pwm+=(Velocity_KP*(Bias-Last_bias)+Velocity_KI*Bias+Velocity_KD*(Bias+next_bias-2*Last_bias)); //增量式PI控制器
  72. next_bias = Last_bias;
  73. Last_bias=Bias;
  74. // if(Pwm>999)Pwm=999;
  75. // if(Pwm<-999)Pwm=-999;
  76. Xianfu(Pwm,99);
  77. return Pwm;
  78. }
  79. /*********************************************************************
  80. 函数功能:位置式PID控制器
  81. 入口参数:编码器测量位置信息,目标位置
  82. 返回 值:电机PWM
  83. 根据位置式离散PID公式
  84. pwm=Kp*e(k)+Ki*∑e(k)+Kd[e(k)-e(k-1)]
  85. e(k)代表本次偏差
  86. e(k-1)代表上一次的偏差
  87. ∑e(k)代表e(k)以及之前的偏差的累积和;其中k为1,2,,k;
  88. pwm代表输出
  89. ********************************************************************/
  90. int Position_PID (int position,int target)
  91. {
  92. static float Bias,Pwm,Integral_bias,Last_Bias;
  93. Bias=target-position;
  94. Integral_bias+=Bias;
  95. Integral_bias=Xianfu(Integral_bias,myabs(Target_Velocity));
  96. Pwm=Position_KP*Bias+Position_KI*Integral_bias+Position_KD*(Bias-Last_Bias);//位置式PID控制器
  97. Last_Bias=Bias;
  98. if(Pwm>10000)Pwm=10000;
  99. if(Pwm<-10000)Pwm=-10000;
  100. return Pwm;
  101. }
  102. void Set_Pwm(int moto)
  103. {
  104. if(moto<0)
  105. {MOTOR_BACK;}
  106. else
  107. {MOTOR_GO;}
  108. PWM_SetCompare1(moto);
  109. }

2.4 pid.h

  1. #ifndef __PID_H
  2. #define __PID_H
  3. //float pid1(int16_t speed1,float tar1);
  4. int Xianfu(int value,int Amplitude);
  5. int myabs(int a);
  6. int Incremental_PI (int Encoder,int Target);
  7. int Position_PID (int position,int target);
  8. void Set_Pwm(int moto);
  9. #endif

2.5 main.h

  1. /* USER CODE BEGIN Includes */
  2. #include "oled.h"
  3. #include "oledfont.h"
  4. #include "pwm.h"
  5. #include "pid.h"
  6. #include "stdio.h"
  7. /* USER CODE END Includes */
  1. /* USER CODE BEGIN EFP */
  2. extern float Position_KP,Position_KI,Position_KD;
  3. extern float Velocity_KP,Velocity_KI,Velocity_KD;
  4. extern int16_t encoder_counter;
  5. extern int Target_Velocity,Target_Position;
  6. #define MOTOR_GO HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, AIN2_Pin, GPIO_PIN_SET)
  7. #define MOTOR_BACK HAL_GPIO_WritePin(GPIOA, AIN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(GPIOB, AIN2_Pin, GPIO_PIN_RESET)
  8. /* USER CODE END EFP */

 3. pid调参

步骤:

(1)速度环p、i、d各值先赋为0,先调p值(比例系数),整个过程只调p、i的值即可。将编码器获取到的脉冲数encoder_counter(实际值)和目标值Target_Velocity打印到vofa中去,观察encoder_counter值靠近Target_Velocity的趋势,响应太慢则加大p值,比例系数是构建输入与输出的线性关系的。

(2)当encoder_counter值靠近但不超过Target_Velocity时,p值就差不多调好了,此时通过调大i值来使encoder_counter值更加靠近Target_Velocity,i值一般以0.001为单位来加。

(3)结果:最后encoder_counter值可以比较快地响应到Target_Velocity值时,并且给轮子阻力时,轮子不会停止转动,则p、i两值就调的差不多了。

 按照我配置的定时器中断和PWM情况,将速度PID系数调成以下值得到的电机转动结果比较合适。

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

具体调参的效果和教程大家可以阅读一下这篇文章:

图文详解PID调参

 4. 小结

pid就是一个控制算法,在掌握如何使用编码器电机以后,速度环的学习实际上就是在电机上加一个速度控制罢了,通俗的说就是加几行代码,难度不大。

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/694976
推荐阅读
相关标签
  

闽ICP备14008679号