你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32电机PID速度控制

[复制链接]
STMCU 发布时间:2020-9-16 10:22
2.3.1 解决的问题


解决带编码器直流电机的速度闭环问题。


2.3.2 PID理论

将偏差的比例、积分、微分,通过线性组合构成控制量,用控制量对被控对象进行控制,这样的控制器称为PID控制器。在连续空间中,我们通常探讨模拟PID的控制原理,如图所示:



我们这里用电机速度控制为例,讲解PID控制系统。r(t)为设定电机速度、y(t)为实际电机速度、e(t)=y(t)-r(t)为速度差值作为PID控制器的输入、u(t)为PID控制器的输出,作用到被控对象电机上。根据模拟PID控制器,科学家们也得出了模拟PID控制的公式,如图所示:


其中Kp、Ti、Td,分别为控制器的比例系数、积分系数、微分系数。该理论用在控制的例子比比皆是。但是模拟PID控制系统是在连续空间的上描述的,无法在计算机上用代码实现。于是就有数字PID控制理论,将连续空间的PID控制系统在离散空间上描述。积分变成了求和、微分变成了求斜率,于是就出现数字PID控制系统的理论公式,如图所示:



其中Kp、Ti、Td和上面描述的一样,T为采用周期,ek是本次差值,ek-1上一次的差值,直接通过模拟PID转化的数字PID又叫做位置式PID,该方式的PID的输出直接是控制量,非常不适合经常出现异常的系统,另外一种方式是增量式PID,每次只输出一个正向或者反向的调节量,就算出现异常,也不会产生巨大的影响。具体数学公式如下所示:该方法较多的应用于生产生活中,本论文中电机的速度PID控制当然也不例外。



有了上面的理论基础,开始代码实现的介绍。首先就是明确增量式PID系统的输入、输出、控制对象。将速度的设定值和速度的测得值作为PID控制器的输入参数,PID的输出参数为对PWM的调节偏差,控制对象PWM进而驱动电机达到设定速度。以上内容确定之后,就是PID控制器的代码部分了。其实仔细看看增量式PID就只有一个公式,所以使用代码实现并不困难。如下所示核心代码就这一句。



完成上面的代码,只是完成速度PID的一部分,剩下的是尤为重要的PID参数整定。该整定方法丰富多样,最为准确的是模型计算,但是对于我们做机器人多使用试凑法。虽然需要调节一段时间,但是不需要对机器人进行建模。试凑法一般按照P、I、D的顺序进行调节。


初始时刻将Ki和Kd都设置成0,按照经验设置Kp的初始值,就这样将系统投入运行,由小到大调节Kp。求得满意的曲线之后,若要引入积分作用,将Kp设置成之前的5/6,然后Ki由小到大开始调节。达到满意效果之后,若要引入微分作用,将Kd按照经验调节即可。经过有规律的试凑,最终达到一个我们满意的就行。





2.3.3 代码分享

(1)pid.h

  1. #include "pid.h"


  2. struct pid_uint pid_Task_Letf;
  3. struct pid_uint pid_Task_Right;

  4. /****************************************************************************
  5. *函数名称:PID_Init(void)
  6. *函数功能:初始化PID结构体参数
  7. ****************************************************************************/

  8. void PID_Init(void)
  9. {
  10. //乘以1024原因避免出现浮点数运算,全部是整数运算,这样PID控制器运算速度会更快
  11. /***********************左轮速度pid****************************/
  12.         pid_Task_Letf.Kp = 1024 * 0.5;//0.4
  13.          pid_Task_Letf.Ki = 1024 * 0;        
  14.         pid_Task_Letf.Kd = 1024 * 0.08;
  15.         pid_Task_Letf.Ur = 1024 * 4000;
  16.         pid_Task_Letf.Adjust   = 0;
  17.         pid_Task_Letf.En       = 1;
  18.         pid_Task_Letf.speedSet = 0;
  19.         pid_Task_Letf.speedNow = 0;
  20.         reset_Uk(&pid_Task_Letf);               
  21. /***********************右轮速度pid****************************/
  22.         pid_Task_Right.Kp = 1024 * 0.35;//0.2
  23.          pid_Task_Right.Ki = 1024 * 0;        //不使用积分
  24.         pid_Task_Right.Kd = 1024 * 0.06;
  25.         pid_Task_Right.Ur = 1024 * 4000;
  26.         pid_Task_Right.Adjust   = 0;
  27.         pid_Task_Right.En       = 1;
  28.         pid_Task_Right.speedSet = 0;
  29.         pid_Task_Right.speedNow = 0;
  30.         reset_Uk(&pid_Task_Right);
  31. }

  32. /***********************************************************************************************
  33. 函 数 名:void reset_Uk(PID_Uint *p)
  34. 功    能:初始化U_kk,ekk,ekkk
  35. 说    明:在初始化时调用,改变PID参数时有可能需要调用
  36. 入口参数:PID单元的参数结构体 地址
  37. ************************************************************************************************/

  38. void reset_Uk(struct pid_uint *p)
  39. {
  40.         p->U_kk=0;
  41.         p->ekk=0;
  42.         p->ekkk=0;
  43. }

  44. /***********************************************************************************************
  45. 函 数 名:s32 PID_commen(int set,int jiance,PID_Uint *p)
  46. 功    能:PID计算函数
  47. 说    明:求任意单个PID的控制量
  48. 入口参数:期望值,实测值,PID单元结构体
  49. 返 回 值:PID控制量
  50. ************************************************************************************************/

  51. s32 PID_common(int set,int jiance,struct pid_uint *p)
  52. {
  53.         int ek=0,U_k=0;

  54.         ek=jiance - set;                                                               
  55.         
  56.         U_k=p->U_kk + p->Kp*(ek - p->ekk) + p->Ki*ek + p->Kd*(ek - 2*p->ekk + p->ekkk);
  57.         
  58.         p->U_kk=U_k;
  59.     p->ekkk=p->ekk;
  60.         p->ekk=ek;
  61.         
  62.         if(U_k>(p->Ur))                                                   
  63.                 U_k=p->Ur;
  64.         if(U_k<-(p->Ur))
  65.                 U_k=-(p->Ur);
  66.         
  67.         return U_k>>10;
  68. }

  69. /***********************************************************************************
  70. ** 函数名称 :void Pid_Which(struct pid_uint *pl, struct pid_uint *pr)
  71. ** 函数功能 :pid选择函数              
  72. ***********************************************************************************/

  73. void Pid_Which(struct pid_uint *pl, struct pid_uint *pr)
  74. {
  75.         /**********************左轮速度pid*************************/
  76.         if(pl->En == 1)
  77.         {                                                                        
  78.                 pl->Adjust = -PID_common(pl->speedSet, pl->speedNow, pl);               
  79.         }        
  80.         else
  81.         {
  82.                 pl->Adjust = 0;
  83.                 reset_Uk(pl);
  84.                 pl->En = 2;
  85.         }
  86.         /***********************右轮速度pid*************************/
  87.         if(pr->En == 1)
  88.         {
  89.                 pr->Adjust = -PID_common(pr->speedSet, pr->speedNow, pr);               
  90.         }        
  91.         else
  92.         {
  93.                 pr->Adjust = 0;
  94.                 reset_Uk(pr);
  95.                 pr->En = 2;
  96.         }
  97. }

  98. /*******************************************************************************
  99. * 函数名:Pid_Ctrl(int *leftMotor,int  *rightMotor)
  100. * 描述  :Pid控制
  101. *******************************************************************************/

  102. void Pid_Ctrl(int *leftMotor,int  *rightMotor)
  103. {
  104.         Pid_Which(&pid_Task_Letf, &pid_Task_Right);
  105.         *leftMotor  += pid_Task_Letf.Adjust;
  106.         *rightMotor += pid_Task_Right.Adjust;
  107. }
复制代码


(2)main.c



  1. #include "sys.h"

  2. //====================自己加入的头文件===============================
  3. #include "delay.h"
  4. #include "led.h"
  5. #include "encoder.h"
  6. #include "usart3.h"
  7. #include "timer.h"
  8. #include "pwm.h"
  9. #include "pid.h"
  10. #include "motor.h"
  11. #include <stdio.h>
  12. /*===================================================================
  13. 程序功能:直流减速电机的速度闭环控制测试
  14. 程序编写:公众号:小白学移动机器人
  15. 其他    :如果对代码有任何疑问,可以私信小编,一定会回复的。
  16. =====================================================================
  17. ------------------关注公众号,获得更多有趣的分享---------------------
  18. ===================================================================*/
  19. int leftSpeedNow  =0;
  20. int rightSpeedNow =0;

  21. int leftSpeeSet   = -300;//mm/s
  22. int rightSpeedSet = -300;//mm/s

  23. int main(void)
  24. {

  25.         GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
  26.         GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 启用 SWD
  27.         
  28.         MY_NVIC_PriorityGroupConfig(2);        //=====设置中断分组
  29.         
  30.         delay_init();                            //=====延时函数初始化
  31.         LED_Init();                     //=====LED初始化    程序灯        
  32.         
  33.         usart3_init(9600);              //=====串口3初始化  蓝牙 发送调试信息

  34.         Encoder_Init_TIM2();            //=====初始化编码器1接口
  35.         Encoder_Init_TIM4();            //=====初始化编码器2接口
  36.         
  37.         Motor_Init(7199,0);             //=====初始化PWM 10KHZ,用于驱动电机 如需初始化驱动器接口
  38.         
  39.         TIM3_Int_Init(50-1,7200-1);     //=====定时器初始化 5ms一次中断

  40.         PID_Init();                                                //=====PID参数初始化
  41.         
  42.         while(1)
  43.         {
  44.                 //给速度设定值和实时值赋值
  45.                 pid_Task_Letf.speedSet  = leftSpeeSet;
  46.                 pid_Task_Right.speedSet = rightSpeedSet;
  47.                 pid_Task_Letf.speedNow  = leftSpeedNow;
  48.                 pid_Task_Right.speedNow = rightSpeedNow;
  49.                
  50.                 //执行PID控制函数
  51.                 Pid_Ctrl(&motorLeft,&motorRight);
  52.                
  53.                 //根据PID计算的PWM数据进行设置PWM
  54.                 Set_Pwm(motorLeft,motorRight);
  55.                
  56.                 //打印速度
  57.                 printf("%d,%d\r\n",leftSpeedNow,rightSpeedNow);
  58.         }
  59. }

  60. //5ms 定时器中断服务函数

  61. void TIM3_IRQHandler(void)                            //TIM3中断
  62. {
  63.         if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否
  64.         {
  65.                 TIM_ClearITPendingBit(TIM3, TIM_IT_Update);   //清除TIMx的中断待处理位
  66.                
  67.                 Get_Motor_Speed(&leftSpeedNow,&rightSpeedNow);//计算电机速度
  68.                
  69.                 Led_Flash(100);                               //程序闪烁灯
  70.         }
  71. }
复制代码


2.3.4 总结


以上三篇内容,是关于直流减速电机的PWM控制、速度测量以及最后电机速度的闭环控制。现在对于电机的简单控制基本告一段落,对于做一个ROS小车的电机控制,这里基本是足够的。下面我们会介绍使用IIC+DMP获取MPU6050数据。





收藏 评论0 发布时间:2020-9-16 10:22

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版