OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5180|回复: 6

STM32F429使用位置式PID算法控制电机转速遇到问题

[复制链接]

2

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
94
金钱
94
注册时间
2019-3-18
在线时间
27 小时
发表于 2019-7-25 11:17:06 | 显示全部楼层 |阅读模式
这几天在调试PID,想用来控制电机速度,用到PWM、定时器编码器模式、带AB相编码盘的直流减速电机;程序是参考某位大佬的,移植过来F429开发板,电机能跑,但是PIM没作用到。由于自己水平还很低,现在还在想哪里出了问题,想着人多力量大,求助一下论坛大佬们,给些建议。下面就是我移植过来的代码:

定时器输出PWM主要代码:
  1. /*****************************************************************************************************************
  2. 函数功能: 实现对定时器的初始化,所用到的定时器为定时器3,通道1和通道2,分别输出PWM波控制电机
  3.           实现编码器对电机速度的获取,并打印到串口助手上

  4. 电机GPIO: TIM3_CH1Handler --> PB4    TIM3_CH2Handler --> PB5

  5. 占空比:   compare1:TIM3通道1的占空比,compare2:TIM3通道2的占空比

  6. 相关解析: 输出的方波周期就是自动重装载值的值(us为单位),占空比 = compare / arr

  7. A4950接线:单片机     A4950模块     
  8.            5V    -->   VCC      
  9.            GND   -->   GND
  10.            PB4   -->   AN1
  11.            GND   -->   AN2
  12.            PB5   -->   BN1
  13.            GND   -->   BN2
  14.                        AOUT1   -->  电机1+
  15.                        AOUT2   -->  电机1-
  16.                        BOUT1   -->  电机2+
  17.                        BOUT2   -->  电机2-
  18.                        VM----->>>>> 外接电源,7.6V---12V,需要共地
  19. *****************************************************************************************************************/
  20. #include "timer.h"
  21. #include "led.h"
  22. #include "encoder.h"

  23. TIM_HandleTypeDef TIM3_PWM_Handler; //定时器3句柄
  24. TIM_OC_InitTypeDef TIM3_CH1Handler; //定时器3通道1句柄
  25. TIM_OC_InitTypeDef TIM3_CH2Handler; //定时器3通道2句柄

  26. //PWM 输出初始化
  27. //arr:自动重装值 psc:时钟预分频数
  28. void TIM3_PWM_Init(u16 arr,u16 psc)
  29. {
  30.     TIM3_PWM_Handler.Instance=TIM3;//定时器3
  31.     TIM3_PWM_Handler.Init.Prescaler= psc;//时钟预分频系数
  32.     TIM3_PWM_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
  33.     TIM3_PWM_Handler.Init.Period=arr;//自动重装载值
  34.     TIM3_PWM_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
  35.     HAL_TIM_PWM_Init(&TIM3_PWM_Handler);
  36.    
  37.     TIM3_CH1Handler.OCMode=TIM_OCMODE_PWM1;//PWM1模式
  38.     TIM3_CH1Handler.Pulse=arr/2;//设置比较值,此值用来确定占空比
  39.     TIM3_CH1Handler.OCPolarity=TIM_OCPOLARITY_LOW;//输出比较极性低
  40.     HAL_TIM_PWM_ConfigChannel(&TIM3_PWM_Handler,&TIM3_CH1Handler,TIM_CHANNEL_1);//配置 TIM3通道1
  41.     HAL_TIM_PWM_ConfigChannel(&TIM3_PWM_Handler,&TIM3_CH1Handler,TIM_CHANNEL_2);//配置 TIM3通道2
  42.     HAL_TIM_PWM_Start(&TIM3_PWM_Handler,TIM_CHANNEL_1);//开启PWM通道1
  43.     HAL_TIM_PWM_Start(&TIM3_PWM_Handler,TIM_CHANNEL_2);//开启PWM通道2
  44. }

  45. //定时器底层驱动,时钟使能,引脚配置
  46. //此函数会被 HAL_TIM_PWM_Init()调用
  47. //htim:定时器句柄
  48. void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
  49. {
  50.     GPIO_InitTypeDef GPIO_Initure;
  51.     __HAL_RCC_TIM3_CLK_ENABLE();//开启TIM3时钟
  52.     __HAL_RCC_GPIOB_CLK_ENABLE();//开启GPIOB时钟
  53.    
  54.     GPIO_Initure.Pin=GPIO_PIN_4 | GPIO_PIN_5;//PB4和PB5
  55.     GPIO_Initure.Mode=GPIO_MODE_AF_PP;//复用为推挽输出
  56.     GPIO_Initure.Pull=GPIO_PULLUP;//上拉
  57.     GPIO_Initure.Speed=GPIO_SPEED_HIGH;//高速
  58.     GPIO_Initure.Alternate=GPIO_AF2_TIM3; //PB 复用为 TIM3_CH1
  59.     HAL_GPIO_Init(GPIOB,&GPIO_Initure);
  60. }


  61. extern long Pulse_Num;
  62. extern float Speed_num;
  63. u8 TimerFlag=0;

  64. void TIM3_IRQHandler(void)
  65. {
  66.     if(__HAL_TIM_GET_FLAG(&TIM3_PWM_Handler, TIM_FLAG_UPDATE) != RESET)//溢出中断
  67.     {
  68.         Speed_num = Pulse_Num;
  69.                 Pulse_Num = 0;
  70.                 TimerFlag=1;
  71.     }
  72.     __HAL_TIM_CLEAR_FLAG(&TIM3_PWM_Handler, TIM_FLAG_UPDATE);//清除中断标志位
  73. }   

  74. //设置TIM3通道1的占空比  
  75. //compare1:比较值
  76. void TIM_SetTIM3Compare1(u32 compare1)
  77. {
  78.     TIM3->CCR1=compare1;
  79. }

  80. //设置TIM3通道2的占空比
  81. //compare2:比较值
  82. void TIM_SetTIM3Compare2(u32 compare2)
  83. {
  84.     TIM3->CCR2=compare2;
  85. }
复制代码
定时器编码器模式主要代码:
  1. /*********************************************************************************************************
  2. 函数功能:定时器4编码器模式检测电机转速

  3. 测速GPIO: A相 --> PD12(TIM4_CH1)   B相 --> PD13(TIM4_CH2)

  4. 电机脉冲: 编码器AB相输出:每转每相各输出360个脉冲
  5. *********************************************************************************************************/
  6. #include "encoder.h"
  7. #include "timer.h"
  8. #include "led.h"

  9. long Interupt_Num=0 ;
  10. long Pulse_Num =0 ;
  11. float Circle_NUm =0;
  12. float Speed_num=0;

  13. TIM_HandleTypeDef TIM4_Handler;//定时器4句柄
  14. TIM_Encoder_InitTypeDef TIM4_Encoder_Handler;//定时器4编码器模式句柄

  15. //编码器模式初始化函数
  16. void TIM4_Encoder_Init(u16 arr, u16 psc)
  17. {
  18.     TIM4_Handler.Instance=TIM4;//选择定时器4
  19.     TIM4_Handler.Init.Prescaler=psc;//时钟预分频系数
  20.     TIM4_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数
  21.     TIM4_Handler.Init.Period=arr;//自动装载值
  22.     TIM4_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
  23.     //HAL_TIM_Base_Init(&TIM4_Handler);
  24.    
  25.     TIM4_Encoder_Handler.EncoderMode=TIM_ENCODERMODE_TI12;
  26.     TIM4_Encoder_Handler.IC1Filter=0;
  27.     TIM4_Encoder_Handler.IC1Polarity=TIM_ICPOLARITY_RISING;
  28.     TIM4_Encoder_Handler.IC1Prescaler=TIM_ICPSC_DIV1;
  29.     TIM4_Encoder_Handler.IC1Selection=TIM_ICSELECTION_DIRECTTI;
  30.    
  31.     TIM4_Encoder_Handler.IC2Filter=0;
  32.     TIM4_Encoder_Handler.IC2Polarity=TIM_ICPOLARITY_RISING;
  33.     TIM4_Encoder_Handler.IC2Prescaler=TIM_ICPSC_DIV1;
  34.     TIM4_Encoder_Handler.IC2Selection=TIM_ICSELECTION_DIRECTTI;
  35.     HAL_TIM_Encoder_Init(&TIM4_Handler, &TIM4_Encoder_Handler);
  36.    
  37.     HAL_TIM_Encoder_Start(&TIM4_Handler, TIM_CHANNEL_ALL);//开启编码器中断
  38.     HAL_TIM_Encoder_Start_IT(&TIM4_Handler, TIM_CHANNEL_ALL);//开启更新中断
  39.     TIM4->CNT=0;
  40.    
  41.     __HAL_TIM_ENABLE(&TIM4_Handler);//使能中断
  42.     __HAL_TIM_ENABLE_IT(&TIM4_Handler, TIM_IT_UPDATE);//使能更新中断
  43. }

  44. //编码器模式回调函数
  45. void HAL_TIM_Encoder_MspInit(TIM_HandleTypeDef *htim)
  46. {
  47.     GPIO_InitTypeDef GPIOD_Initure;
  48.     __HAL_RCC_GPIOD_CLK_ENABLE();
  49.     __HAL_RCC_TIM4_CLK_ENABLE();
  50.    
  51.     GPIOD_Initure.Pin=GPIO_PIN_12 | GPIO_PIN_13;
  52.     GPIOD_Initure.Mode=GPIO_MODE_AF_PP;
  53.     GPIOD_Initure.Pull=GPIO_PULLUP;
  54.     GPIOD_Initure.Speed=GPIO_SPEED_HIGH;
  55.     GPIOD_Initure.Alternate=GPIO_AF2_TIM4;//PD复用为TIM4模式
  56.     HAL_GPIO_Init(GPIOD, &GPIOD_Initure);
  57.    
  58.     HAL_NVIC_SetPriority(TIM4_IRQn,1,3);    //设置中断优先级,抢占优先级1,子优先级3
  59.     HAL_NVIC_EnableIRQ(TIM4_IRQn);          //开启ITM4中断   
  60.     HAL_TIM_Base_Start_IT(&TIM4_Handler); //使能定时器4和定时器4更新中断:TIM_IT_UPDATE   
  61. }

  62. void TIM4_IRQHandler(void)
  63. {
  64.     HAL_TIM_IRQHandler(&TIM4_Handler);
  65. }

  66. //回调函数,定时器中断服务函数调用
  67. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  68. {

  69.     if(htim==(&TIM4_Handler))
  70.     {
  71.         LED1 = !LED1 ;
  72.         if ( (TIM4->CR1&0x0010) == 0 )  //向上计数溢出
  73.         {
  74.             Interupt_Num++ ;
  75.         }
  76.         else //向下计数溢出
  77.         {
  78.             Interupt_Num-- ;
  79.         }               
  80.     }
  81. }

  82. //输出编码器获取值
  83. void Circle_Print()
  84. {  
  85.     if(Interupt_Num ==0 )                        //CNT从0开始,刚开始的时候计算脉冲的方法
  86.     {
  87.         Pulse_Num = TIM4->CNT;
  88.     }               
  89.     else
  90.     {  
  91.         Pulse_Num = Interupt_Num*2000 +TIM4->CNT; //在第一圈完成之后,计算脉冲数多的方法
  92.     }
  93.     //Circle_NUm=(float)Pulse_Num/360;
  94.     printf("Interupt_Num的值是:%ld , 捕获到的总数是:%ld\r\n" , Interupt_Num, Pulse_Num);   
  95. }
复制代码
PID算法代码:
  1. /*************************************************************************************************
  2. 函数功能:位置式PID控制算法的实现

  3. 位置公式:经离散化处理后,PIDout = Kp * e(k) + Ki * ∑(0-->k)e(j) + Kd * [e(k) - e(k-1)],
  4.           Ki = Kp*T/Ti,Kd = Kp*Td/T, Kp为比例系数,Ki为积分系数,Kd为微分系数

  5. 增量公式:经过离散化处理后:PIDout = Kp[e(k) - e(k-1)] + Ki * e(k) + Kd[e(k) - 2e(k-1) + e(k-2)]
  6.           e(k)为本次偏差,e(k-1)为上一次偏差,e(k-2)为上上次偏差

  7. 偏差值  :输出值与设定值之间的差值,用e(k)表示,即:e(k) = S(k) - O(k)

  8. 离散化  :在不改变数据的相对大小的条件下,对数据进行相应的缩小;离散情况下积分其实就是累加过程

  9. 速度闭环:速度闭环控制就是根据单位时间获取的脉冲数测量电机的速度信息,并与目标值进行比较,得到控制偏差,
  10.          然后通过对偏差的比例、积分、微分进行控制,使偏差趋向于0的过程

  11. 参考:   https://blog.csdn.net/qq_25352981/article/details/81007075
  12.          https://blog.csdn.net/kilotwo/article/details/79952530
  13.          https://www.amobbs.com/thread-5043342-1-1.html
  14. *************************************************************************************************/
  15. #include "pid.h"
  16. #include "encoder.h"
  17. #include "timer.h"

  18. void PID_Init(PID_TypeDef * PID)
  19. {
  20.         PID->errorSumDownLim = -188;
  21.         PID->errorSumUpLim = 188;
  22.         PID->PIDoutDownLim = -999;
  23.         PID->PIDoutUpLim = 999;
  24.         PID->errorUpLim = 50;
  25.         PID->errorDownLim = -50;
  26.         PID->delErrorUpLim = 20;                        //试凑值
  27.         PID->delErrorDownLim = -40;
  28. }

  29. //将PID的Kp、Ki、Kd系数简化用x、y、z表示
  30. void PID_SetParameter(PID_TypeDef * PID,float x,float y,float z)
  31. {
  32.         PID->kp=x;
  33.         PID->ki=y;
  34.         PID->kd=z;
  35. }

  36. //简化设定值表示
  37. void PID_SetTargetVal(PID_TypeDef * PID, float x)
  38. {
  39.         PID->TargetVal = x;
  40. }

  41. //简化输出值表示
  42. void PID_SetNowVal(PID_TypeDef * PID, float x)
  43. {
  44.         PID->NowVal = x;
  45. }

  46. //PID计算函数
  47. void PID_Clac(PID_TypeDef * PID)               
  48. {
  49.     //得到偏差,P项的值
  50.         PID->error = PID->TargetVal - PID->NowVal;          //偏差 = 设定 - 当前               
  51.         if(PID->error > PID->errorUpLim)                                        //误差限幅
  52.     {
  53.         PID->error = PID->errorUpLim;
  54.     }               
  55.         else if(PID->error < PID->errorDownLim)
  56.     {
  57.         PID->error = PID->errorDownLim;
  58.     }
  59.                
  60.     //得到偏差之和,I项的值
  61.         PID->errorSum += PID->error;                                                //偏差求和       
  62.         if(PID->errorSum > PID->errorSumUpLim)              //偏差和限幅
  63.     {
  64.         PID->errorSum = PID->errorSumUpLim;
  65.     }                       
  66.         else if(PID->errorSum < PID->errorSumDownLim)
  67.     {
  68.         PID->errorSum = PID->errorSumDownLim;
  69.     }
  70.        
  71.     //得到最近两次偏差之差,D项的值
  72.         PID->delError = PID->error - PID->error1;                        //偏差微分
  73.         PID->error1 = PID->error;                                                        //把本次偏差赋值给上次偏差
  74.         if(PID->delError > PID->delErrorUpLim)                                //偏差微分限幅
  75.     {
  76.         PID->delError = PID->delErrorUpLim;
  77.     }       
  78.         else if(PID->delError < PID->delErrorDownLim)
  79.     {
  80.         PID->delError = PID->delErrorDownLim;
  81.     }
  82.        
  83.     //PID三项分别输出
  84.         PID->Pout = PID->kp * PID->error;                                        //比例输出
  85.         PID->Iout = PID->ki * PID->errorSum;                                //积分输出
  86.         PID->Dout = PID->kd * PID->delError;                                //微分输出
  87.        
  88.     //得到PID项之和,为位置式PID控制算法
  89.         PID->PIDout = PID->Pout + PID->Iout + PID->Dout;        //PID输出       
  90.         if(PID->PIDout > PID->PIDoutUpLim)                  //PID输出限幅
  91.     {
  92.         PID->PIDout = PID->PIDoutUpLim;
  93.     }                       
  94.         else if(PID->PIDout < PID->PIDoutDownLim)
  95.     {
  96.         PID->PIDout = PID->PIDoutDownLim;
  97.     }               
  98. }
复制代码
控制电机转速代码:
  1. #include "motor.h"
  2. #include "timer.h"
  3. #include "encoder.h"
  4. #include "pid.h"

  5. extern float Speed_num;
  6. extern u8 TimerFlag;
  7. PID_TypeDef PID_MOTOR_R;

  8. //u16 val=250;
  9. float Po=3.0f, Io=4.5f, Do=2.0f;
  10. float Target=200.0f;
  11. //最佳Po=3.0f, Io=4.5f, Do=2.0f;

  12. u8 testCnt = 0, testFlag = 0;
  13. float testTargetUpLim = 360;
  14. float testTargetDownLim = 160;

  15. /*------------------------------------------
  16. 函数功能:电机底层驱动函数
  17. 函数说明:
  18. ------------------------------------------*/
  19. void MotorMove(float pwm1)
  20. {
  21.         if(pwm1 >= 0)
  22.         {
  23.                 MOTOR_A = 1;
  24.                 MOTOR_B = 0;
  25.                 TIM_SetTIM3Compare1(pwm1);
  26.         TIM_SetTIM3Compare2(pwm1);
  27.         
  28.         }
  29.         else if(pwm1 < 0)
  30.         {
  31.                 MOTOR_A = 0;
  32.                 MOTOR_B = 1;
  33.                 TIM_SetTIM3Compare1(-pwm1);
  34.         TIM_SetTIM3Compare2(pwm1);
  35.         }
  36. }

  37. void motorCon()
  38. {       
  39.     if(TimerFlag)
  40.     {
  41.                 TimerFlag = 0;
  42.         if(testFlag)
  43.         {
  44.             testCnt++;
  45.             if(testCnt%40==0)
  46.             {
  47.                 Target = testTargetUpLim;
  48.                 testCnt=0;
  49.             }
  50.             else if(testCnt%20==0)
  51.             {
  52.                Target = testTargetDownLim;
  53.             }                       
  54.         }
  55.             
  56.         PID_SetParameter(&PID_MOTOR_R, Po, Io, Do);
  57.         PID_SetTargetVal(&PID_MOTOR_R, Target);
  58.         PID_SetNowVal(&PID_MOTOR_R, Speed_num);
  59.         PID_Clac(&PID_MOTOR_R);
  60.         MotorMove(PID_MOTOR_R.PIDout);
  61.         printf("the PID_MOTOR_R.PIDout is %f\r\n", PID_MOTOR_R.PIDout);
  62.     }      
  63. }

复制代码
主函数代码:
  1. #include "sys.h"
  2. #include "delay.h"
  3. #include "usart.h"
  4. #include "led.h"
  5. #include "timer.h"
  6. #include "encoder.h"
  7. #include "pid.h"
  8. #include "motor.h"

  9. extern PID_TypeDef PID_MOTOR_R;

  10. int main(void)
  11. {
  12.     HAL_Init();                     //初始化HAL库   
  13.     Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
  14.     delay_init(180);
  15.     uart_init(115200);
  16.     LED_Init();
  17.     TIM3_PWM_Init(500-1,90-1);//90M/90=1M 的计数频率,自动重装载为 500那么 PWM 频率为 1M/500=2kHZ
  18.     TIM4_Encoder_Init(2000,0);
  19.    
  20.     Interupt_Num=0 ;
  21.     Pulse_Num =0 ;
  22.     PID_Init(&PID_MOTOR_R);       
  23.         while(1)
  24.         {
  25.         //TIM_SetTIM3Compare1(200);
  26.         //TIM_SetTIM3Compare2(300);
  27.         //Circle_Print();       
  28.         motorCon();
  29.     }   
  30. }
复制代码


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

13

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2019-7-28
在线时间
12 小时
发表于 2019-7-29 21:42:37 | 显示全部楼层
回复 支持 1 反对 0

使用道具 举报

2

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
94
金钱
94
注册时间
2019-3-18
在线时间
27 小时
 楼主| 发表于 2019-7-25 11:27:22 | 显示全部楼层
还有几个不是很懂的问题:
1、电机输出的PWM控制电机转速跟编码器捕获到的脉冲数有没有关系
2、编码器捕获到的脉冲数怎么转化成电机转过圈数,是直接除以360(本电机的线数是360)吗?,调试的时候慢速看,觉得不对。。。
3、怎么把实际测到的值与输入值对应起来?

有空指点指点小白,谢谢大佬们了!
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
8
金钱
8
注册时间
2020-4-21
在线时间
2 小时
发表于 2020-4-21 17:52:59 | 显示全部楼层
帮顶一下子
回复 支持 反对

使用道具 举报

5

主题

62

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
358
金钱
358
注册时间
2018-9-29
在线时间
146 小时
发表于 2020-4-29 14:42:27 | 显示全部楼层
这是我看到的写的最整齐的  代码 没有之一
回复 支持 反对

使用道具 举报

5

主题

62

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
358
金钱
358
注册时间
2018-9-29
在线时间
146 小时
发表于 2020-4-29 14:46:35 | 显示全部楼层
楼主能贴工程吗?很多.H文件看不到 学习一下
回复 支持 反对

使用道具 举报

0

主题

4

帖子

0

精华

初级会员

Rank: 2

积分
168
金钱
168
注册时间
2020-10-26
在线时间
65 小时
发表于 2021-3-26 20:55:34 | 显示全部楼层
胖胖胖胖啊 发表于 2019-7-25 11:27
还有几个不是很懂的问题:
1、电机输出的PWM控制电机转速跟编码器捕获到的脉冲数有没有关系
2、编码器捕 ...

1,没有关系,编码器捕获的信号是另外一个定时器的俩个通道(AB双相捕获)
2,脉冲数/(精度*减速比*倍频数(只有4倍频或者1倍频))
3,实际测到的啥?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-28 07:06

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表