[STM32F1] STM32实现编码器电机【速度与位置环闭环控制】

[复制链接]
 楼主| yutingwei 发表于 2023-7-29 16:59 | 显示全部楼层 |阅读模式
一、硬件及接线说明
1.1 硬件平台
控制芯片:STM32F103ZET6

电机驱动:TB6612

电机类型:520编码器电机(12V 110RPM 减速比90)

1.2 接线说明
PWMA —— PE9(TIM1通道1)
STBY —— PF0
AIN1 —— PF1
AIN2 —— PF2
编码器A相 —— PA1(TIM2编码器模式)
编码器B相 —— PA0(TIM2编码器模式)
TIM6:产生1ms定时器中断(无需接线)

 楼主| yutingwei 发表于 2023-7-29 17:01 | 显示全部楼层
二、CUBEMX配置
2.1 新建工程,配置时钟频率为72MHz
5782164c4d5619d86a.png
 楼主| yutingwei 发表于 2023-7-29 17:02 | 显示全部楼层
2.2 配置RCC,使用外部高速晶振
7349564c4d58bf13cf.png
 楼主| yutingwei 发表于 2023-7-29 17:02 | 显示全部楼层
2.3 Debug配置为Serial Wire模式
8273764c4d5a2f29c2.png
 楼主| yutingwei 发表于 2023-7-29 17:02 | 显示全部楼层
2.4 配置GPIO,PF0默认上拉
7849964c4d5af84fd1.png
 楼主| yutingwei 发表于 2023-7-29 17:03 | 显示全部楼层
2.5 配置定时器
1989264c4d5e875d81.png
2884064c4d5ede9207.png

1023364c4d5f3df477.png
配置完成后生成代码
 楼主| yutingwei 发表于 2023-7-29 17:04 | 显示全部楼层
三、代码实现
encoder.c
  1. /* 此文件为编码器电机闭环调试,包括速度环和位置环
  2. * 配置:TIM2(PA0、PA1):编码器模式
  3. *                          TIM1-CH1(PE9):PWM输出
  4. *                         IN1:PF1
  5. *                          IN2: PF2
  6. *              ENABLE: PF0
  7. * 使用方法:1、初始化Motor_Init()
  8. *                                          2、发送电流SetCurrent()
  9. */
  10. #include  "encoder.h"

  11. encoderMotor_t encoderMoto[ENCODER_MOTO_COUNT];                //编码器电机结构体

  12. pid_t Encoder_Motor_Pid_Pos[ENCODER_MOTO_COUNT];  //编码器电机位置环PID结构体
  13. pid_t Encoder_Motor_Pid_Spd[ENCODER_MOTO_COUNT];  //编码器电机速度环PID结构体


  14. void Motor_Init(void)
  15. {
  16.                 HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL);      //开启编码器定时器
  17.                 __HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE);                  //开启编码器定时器更新中断,防溢出处理
  18.                 __HAL_TIM_SET_COUNTER(&htim2, 10000);                //编码器定时器初始值设定为10000
  19.        
  20.                 encoderMoto[0].IOControl.htim_pwm = htim1;
  21.                 encoderMoto[0].IOControl.IN1_Port = GPIOF;
  22.                 encoderMoto[0].IOControl.IN2_Port = GPIOF;
  23.                 encoderMoto[0].IOControl.IN1_Pin = GPIO_PIN_1;
  24.                 encoderMoto[0].IOControl.IN2_Pin = GPIO_PIN_2;
  25. }


  26. //
  27. /* 编码器电机发送电流函数
  28. * motor:编码器电机参数结构体
  29. * val:转动的速度或角度,SPEED最大为110,POSITION一圈为3960
  30. * mode:模式选择:速度环:SPEED
  31. *                                                                  位置环:POSITION
  32. */
  33. void SetCurrent(encoderMotor_t *motor, int32_t val, uint32_t mode)
  34. {
  35.                 float pos_output,spd_output;
  36.                 if(mode == 1)
  37.                         spd_output = pid_calc(&Encoder_Motor_Pid_Spd[0], motor->speed, val);
  38.                 else
  39.                 {
  40.                         pos_output = pid_calc(&Encoder_Motor_Pid_Pos[0], motor->totalAngle, val);
  41.                         spd_output = pid_calc(&Encoder_Motor_Pid_Spd[0], motor->speed, pos_output);
  42.                 }

  43.                 if(spd_output > 0)
  44.                 {
  45.                                 HAL_GPIO_WritePin(motor->IOControl.IN1_Port, motor->IOControl.IN1_Pin, GPIO_PIN_SET);                //控制正反转
  46.                                 HAL_GPIO_WritePin(motor->IOControl.IN2_Port, motor->IOControl.IN2_Pin, GPIO_PIN_RESET);
  47.                                 __HAL_TIM_SET_COMPARE(&motor->IOControl.htim_pwm, TIM_CHANNEL_1, (uint32_t)(spd_output));
  48.                 }
  49.                 else
  50.                 {
  51.                                 HAL_GPIO_WritePin(motor->IOControl.IN1_Port, motor->IOControl.IN1_Pin, GPIO_PIN_RESET);
  52.                                 HAL_GPIO_WritePin(motor->IOControl.IN2_Port, motor->IOControl.IN2_Pin, GPIO_PIN_SET);
  53.                                 __HAL_TIM_SET_COMPARE(&motor->IOControl.htim_pwm, TIM_CHANNEL_1, (uint32_t)(-spd_output));
  54.                 }
  55.        
  56. }

  57. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  58. {
  59.         static int16_t count = 0;
  60.         if(htim->Instance==htim6.Instance)                         //1ms中断
  61.         {
  62.                 count++;
  63.                 if(count >= 10)
  64.                 {
  65.                         count = 0;
  66.                         int16_t pluse = COUNTERNUM - RELOADVALUE/2;                                                                                       
  67.                         encoderMoto[0].totalAngle = pluse + encoderMoto[0].loopNum * RELOADVALUE/2;  
  68.                         encoderMoto[0].speed = (float)(encoderMoto[0].totalAngle - encoderMoto[0].lastAngle)/(4*PLUSE_OF_CIRCLE*RR)*6000;                        //进行速度计算,根据前文所说的,4倍频,编码器13位,减速比30,再乘以6000即为每分钟输出轴多少转
  69.                         encoderMoto[0].lastAngle = encoderMoto[0].totalAngle;         //更新转过的圈数
  70.                 }
  71.         }
  72.         else if(htim->Instance == htim2.Instance)      //如果是编码器更新中断,即10ms内,脉冲数超过了计数范围,需要进行处理
  73.         {
  74.                 if(COUNTERNUM < 10000)        encoderMoto[0].loopNum++;
  75.                 else if(COUNTERNUM > 10000)        encoderMoto[0].loopNum--;
  76.                 __HAL_TIM_SetCounter(&htim2, 10000);             //重新设定初始值                       
  77.         }
  78. }

 楼主| yutingwei 发表于 2023-7-29 17:04 | 显示全部楼层
encoder.h
  1. #ifndef __ENCODER_H
  2. #define __ENCODER_H

  3. #include "tim.h"
  4. #include "gpio.h"
  5. #include "main.h"
  6. #include "stm32_hal_legacy.h"
  7. #include "pid.h"

  8. #define ENCODER_MOTO_COUNT                1                //编码器电机数量
  9. #define RR        90        //电机减速比
  10. #define PLUSE_OF_CIRCLE                11
  11. #define RELOADVALUE         __HAL_TIM_GetAutoreload(&htim2)    //获取自动装载值,本例中为20000
  12. #define COUNTERNUM                 __HAL_TIM_GetCounter(&htim2)        //获取编码器定时器中的计数值


  13. #define MOTOR_1                1

  14. enum{
  15.     POSITION        = 0,
  16.     SPEED         = 1,
  17. };

  18. /* 编码器电机接口定义结构体 */
  19. typedef struct _IOControl
  20. {
  21.         TIM_HandleTypeDef htim_encoder;
  22.         TIM_HandleTypeDef htim_pwm;
  23.         GPIO_TypeDef *IN1_Port;
  24.         GPIO_TypeDef *IN2_Port;
  25.         uint16_t IN1_Pin;
  26.         uint16_t IN2_Pin;
  27. }IOControl_t;

  28. /* 编码器电机参数结构体 */
  29. typedef struct _EncoderMotor{
  30.         int8_t ID;
  31.         int16_t loopNum;          //防超上限
  32.         int32_t lastAngle;        //上1ms转的角度
  33.         int32_t totalAngle;       //总角度
  34.         float speed;              //电机输出轴转速,单位RPM
  35.         float set;
  36.         IOControl_t IOControl;
  37. }encoderMotor_t;

  38. extern encoderMotor_t encoderMoto[ENCODER_MOTO_COUNT];
  39. extern pid_t Encoder_Motor_Pid_Pos[ENCODER_MOTO_COUNT];  //编码器电机位置环PID结构体
  40. extern pid_t Encoder_Motor_Pid_Spd[ENCODER_MOTO_COUNT];  //编码器电机速度环PID结构体

  41. void SetCurrent(encoderMotor_t *motor, int32_t val, uint32_t mode);
  42. void Motor_Init(void);
  43. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

  44. #endif

 楼主| yutingwei 发表于 2023-7-29 17:04 | 显示全部楼层
pid.c
  1. #include "pid.h"


  2. void abs_limit(float *a, float ABS_MAX)
  3. {
  4.     if(*a > ABS_MAX)
  5.         *a = ABS_MAX;
  6.     if(*a < -ABS_MAX)
  7.         *a = -ABS_MAX;
  8. }

  9. void PID_struct_init(pid_t* pid,
  10.                                                                                  uint32_t maxout,
  11.                                                                                  uint32_t intergral_limit,
  12.                                                                                  float         kp,
  13.                                                                                  float         ki,
  14.                                                                            float         kd)
  15. {
  16.     pid->IntegralLimit = intergral_limit;
  17.     pid->MaxOutput = maxout;
  18.    
  19.     pid->p = kp;
  20.     pid->i = ki;
  21.     pid->d = kd;
  22. }


  23. float pid_calc(pid_t* pid, float get, float set)
  24. {
  25.     pid->get = get;
  26.     pid->set = set;
  27.     pid->err = set - get;        /*set - measure,得到偏差*/
  28.    
  29.                 pid->pout = pid->p * pid->err;
  30.                 pid->iout += pid->i * pid->err;
  31.                 pid->dout = pid->d * (pid->err - pid->lastError);
  32.        
  33.                 abs_limit(&(pid->iout), pid->IntegralLimit);  /*积分限幅*/
  34.                 pid->pos_out = pid->pout + pid->iout + pid->dout;
  35.        
  36.                 abs_limit(&(pid->pos_out), pid->MaxOutput);  /*限定输出值的大小*/

  37.    
  38.     /*更新数据*/
  39.     pid->lastError = pid->err;

  40.     return pid->pos_out; /*PID输出*/
  41. }

 楼主| yutingwei 发表于 2023-7-29 17:04 | 显示全部楼层
pid.h
  1. #ifndef __PID_H_
  2. #define __PID_H_

  3. #include "main.h"

  4. typedef struct __pid_t
  5. {
  6.     float p,i,d;
  7.     float err,lastError;        //误差

  8.     float set;        //目标值
  9.     float get;        //测量值
  10.    
  11.     float pout;                //P输出                               
  12.     float iout;                //I输出                                       
  13.     float dout;                //D输出               
  14.    
  15.     float pos_out;                        //本次位置式输出,即 pos_out = pout + iout + dout
  16.     float last_pos_out;                //上次位置式输出
  17.    

  18.     uint32_t MaxOutput;                        //输出限幅
  19.     uint32_t IntegralLimit;                //积分限幅
  20.    

  21. }pid_t;

  22. void abs_limit(float *a, float ABS_MAX);
  23. void PID_struct_init(pid_t* pid,
  24.                                                                                  uint32_t maxout,
  25.                                                                                  uint32_t intergral_limit,
  26.                                                                                  float         kp,
  27.                                                                                  float         ki,
  28.                                                                            float         kd);

  29. float pid_calc(pid_t* pid, float get, float set);

  30. #endif

 楼主| yutingwei 发表于 2023-7-29 17:05 | 显示全部楼层
main.c
  1. #include "encoder.h"
  2. #include "pid.h"

  3. int main(void)
  4. {
  5.     HAL_Init();
  6.     SystemClock_Config();
  7.     MX_GPIO_Init();
  8.     MX_TIM1_Init();
  9.     MX_TIM2_Init();
  10.     MX_TIM6_Init();
  11.     /*以上为cube生成*/
  12.   
  13.         HAL_TIM_Base_Start_IT(&htim6);                       //开启1ms定时器中断
  14.         HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  15.         Motor_Init();
  16.        
  17.         PID_struct_init(&Encoder_Motor_Pid_Pos[0], 100000, 1000, 0.2, 0.0, 0);
  18.         PID_struct_init(&Encoder_Motor_Pid_Spd[0], 1000, 1000, 30, 0.05, 0.01);

  19.     while (1)
  20.     {
  21.                 SetCurrent(&encoderMoto[0], 20, SPEED);       
  22.     }
  23. }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

58

主题

514

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部