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

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个回答

所属标签

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