当前位置:   article > 正文

电机应用开发-直流有刷电机速度环控制实现_直流电机速度环设计

直流电机速度环设计

目录

直流有刷电机速度环控制实现

硬件设计

直流电机速度环控制-位置式PID实现

编程要点

配置基本定时器6产生定时中断来执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

编写位置式PID算法

直流电机速度环控制-增量式PID实现

编程要点

配置基本定时器6产生定时中断来执行PID运算

配置定时器1输出PWM控制电机

配置定时器3读取编码器的计数值

编写增量式PID算法


直流有刷电机速度环控制实现

硬件设计

可选:L298N电机驱动板、野火MOS搭建的驱动板。

直流电机速度环控制-位置式PID实现

编程要点

配置基本定时器产生定时中断来执行PID运算

配置定时器输出PWM控制电机

配置定时器读取编码器的计数值

编写位置式PID算法

编写速度控制函数

增加上位机曲线观察相关代码

编写按键控制代码

配置基本定时器6产生定时中断来执行PID运算
  1. TIM_HandleTypeDef TIM_TimeBaseStructure;
  2. /**
  3. * @brief 初始化基本定时器定时,默认50ms产生一次中断
  4. * @param 无
  5. * @retval 无
  6. */
  7. void TIMx_Configuration(void)
  8. {
  9. HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 3);
  10. HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
  11. __TIM6_CLK_ENABLE();
  12. TIM_TimeBaseStructure.Instance = TIM6;
  13. TIM_TimeBaseStructure.Init.Period = 50 * 50 - 1;
  14. TIM_TimeBaseStructure.Init.Prescaler = 1680 - 1;
  15. TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
  16. TIM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  17. HAL_TIM_Base_Init(&TIM_TimeBaseStructure);
  18. // 开启定时器更新中断
  19. HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);
  20. uint32_t temp = (__HAL_TIM_GET_AUTORELOAD(&TIM_TimeBaseStructure) + 1) / 50.0; // 计算周期,单位ms
  21. set_computer_value(SEND_PERIOD_CMD, CURVES_CH1, &temp, 1); // 给通道 1 发送目标值
  22. }
  23. /**
  24. * @brief 定时器更新事件回调函数
  25. * @param 无
  26. * @retval 无
  27. */
  28. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  29. {
  30. if (htim == (&TIM_TimeBaseStructure))
  31. {
  32. motor_pid_control(); // 每50ms执行一次PID运算
  33. }
  34. }

配置定时器1输出PWM控制电机
  1. TIM_HandleTypeDef DCM_TimeBaseStructure;
  2. /**
  3. * @brief 初始化控制通用定时器
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Motor_TIMx_Configuration(void)
  8. {
  9. GPIO_InitTypeDef GPIO_InitStruct;
  10. TIM_OC_InitTypeDef TIM_OCInitStructure;
  11. __HAL_RCC_GPIOA_CLK_ENABLE();
  12. __TIM1_CLK_ENABLE();
  13. // PA8--PWM_TIM_CH1
  14. GPIO_InitStruct.Pin = GPIO_PIN_8;
  15. GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  16. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  17. GPIO_InitStruct.Alternate = PWM_TIM_GPIO_AF;
  18. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  19. // PA9--PWM_TIM_CH2
  20. GPIO_InitStruct.Pin = GPIO_PIN_9;
  21. HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  22. // TIM1 66.7us一次周期
  23. DCM_TimeBaseStructure.Instance = TIM1;
  24. DCM_TimeBaseStructure.Init.Period = 5600 - 1;
  25. DCM_TimeBaseStructure.Init.Prescaler = 1 - 1;
  26. DCM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;
  27. DCM_TimeBaseStructure.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  28. HAL_TIM_PWM_Init(&DCM_TimeBaseStructure);
  29. /*PWM模式配置*/
  30. TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1;
  31. TIM_OCInitStructure.Pulse = 0;
  32. TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
  33. TIM_OCInitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  34. TIM_OCInitStructure.OCIdleState = TIM_OCIDLESTATE_SET;
  35. TIM_OCInitStructure.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  36. /*配置PWM通道*/
  37. HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_1);
  38. /*开始输出PWM*/
  39. HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_1);
  40. /*配置PWM通道*/
  41. HAL_TIM_PWM_ConfigChannel(&DCM_TimeBaseStructure, &TIM_OCInitStructure, TIM_CHANNEL_2);
  42. /*开始输出PWM*/
  43. HAL_TIM_PWM_Start(&DCM_TimeBaseStructure, TIM_CHANNEL_2);
  44. }
  45. /**
  46. * @brief 设置TIM通道的占空比
  47. * @param channel 通道 (1,2,3,4)
  48. * @param compare 占空比
  49. * @note 无
  50. * @retval 无
  51. */
  52. void TIM1_SetPWM_pulse(uint32_t channel, int compare)
  53. {
  54. switch (channel)
  55. {
  56. case TIM_CHANNEL_1:
  57. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_1, compare);
  58. break;
  59. case TIM_CHANNEL_2:
  60. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_2, compare);
  61. break;
  62. case TIM_CHANNEL_3:
  63. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_3, compare);
  64. break;
  65. case TIM_CHANNEL_4:
  66. __HAL_TIM_SET_COMPARE(&DCM_TimeBaseStructure, TIM_CHANNEL_4, compare);
  67. break;
  68. }
  69. }

配置定时器3读取编码器的计数值
  1. /* 定时器溢出次数 */
  2. __IO int16_t Encoder_Overflow_Count = 0;
  3. TIM_HandleTypeDef TIM_EncoderHandle;
  4. /**
  5. * @brief 编码器接口初始化
  6. * @param 无
  7. * @retval 无
  8. */
  9. void Encoder_Init(void)
  10. {
  11. GPIO_InitTypeDef GPIO_InitStruct = {0};
  12. TIM_Encoder_InitTypeDef Encoder_ConfigStructure;
  13. __HAL_RCC_GPIOC_CLK_ENABLE();
  14. __HAL_RCC_TIM3_CLK_ENABLE();
  15. // PC6--TIM3_CH1
  16. GPIO_InitStruct.Pin = GPIO_PIN_6;
  17. GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
  18. GPIO_InitStruct.Pull = GPIO_PULLUP;
  19. GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  20. GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
  21. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  22. // PC7--TIM3_CH2
  23. GPIO_InitStruct.Pin = GPIO_PIN_7;
  24. HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
  25. // TIM3 0.78ms一个周期
  26. TIM_EncoderHandle.Instance = TIM3;
  27. TIM_EncoderHandle.Init.Prescaler = 0;
  28. TIM_EncoderHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
  29. TIM_EncoderHandle.Init.Period = 65535;
  30. TIM_EncoderHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  31. TIM_EncoderHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  32. /* 设置编码器倍频数 */
  33. Encoder_ConfigStructure.EncoderMode = TIM_ENCODERMODE_TI12;
  34. Encoder_ConfigStructure.IC1Polarity = TIM_ICPOLARITY_RISING;
  35. Encoder_ConfigStructure.IC1Selection = TIM_ICSELECTION_DIRECTTI;
  36. Encoder_ConfigStructure.IC1Prescaler = TIM_ICPSC_DIV1;
  37. Encoder_ConfigStructure.IC1Filter = 0;
  38. Encoder_ConfigStructure.IC2Polarity = TIM_ICPOLARITY_RISING;
  39. Encoder_ConfigStructure.IC2Selection = TIM_ICSELECTION_DIRECTTI;
  40. Encoder_ConfigStructure.IC2Prescaler = TIM_ICPSC_DIV1;
  41. Encoder_ConfigStructure.IC2Filter = 0;
  42. HAL_TIM_Encoder_Init(&TIM_EncoderHandle, &Encoder_ConfigStructure);
  43. /* 清零计数器 */
  44. __HAL_TIM_SET_COUNTER(&TIM_EncoderHandle, 0);
  45. /* 清零中断标志位 */
  46. __HAL_TIM_CLEAR_IT(&TIM_EncoderHandle, TIM_IT_UPDATE);
  47. /* 使能定时器的更新事件中断 */
  48. __HAL_TIM_ENABLE_IT(&TIM_EncoderHandle, TIM_IT_UPDATE);
  49. /* 设置更新事件请求源为:计数器溢出 */
  50. __HAL_TIM_URS_ENABLE(&TIM_EncoderHandle);
  51. /* 设置中断优先级 */
  52. HAL_NVIC_SetPriority(TIM3_IRQn, 0, 1);
  53. /* 使能定时器中断 */
  54. HAL_NVIC_EnableIRQ(TIM3_IRQn);
  55. /* 使能编码器接口 */
  56. HAL_TIM_Encoder_Start(&TIM_EncoderHandle, TIM_CHANNEL_ALL);
  57. }
  58. /**
  59. * @brief 定时器更新事件回调函数
  60. * @param 无
  61. * @retval 无
  62. */
  63. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  64. {
  65. if (htim == (&TIM_EncoderHandle))
  66. {
  67. /* 判断当前计数器计数方向 */
  68. if (__HAL_TIM_IS_TIM_COUNTING_DOWN(&TIM_EncoderHandle))
  69. { /* 下溢 */
  70. Encoder_Overflow_Count--;
  71. }
  72. else
  73. { /* 上溢 */
  74. Encoder_Overflow_Count++;
  75. }
  76. }
  77. }

编写位置式PID算法
  1. typedef struct
  2. {
  3. float target_val; // 目标值
  4. float actual_val; // 实际值
  5. float err; // 定义偏差值
  6. float err_last; // 定义上一个偏差值
  7. float Kp,Ki,Kd; // 定义比例、积分、微分系数
  8. float integral; // 定义积分值
  9. }_pid; // 位置式PID
  10. _pid pid;
  11. /**
  12. * @brief PID参数初始化
  13. * @note 无
  14. * @retval 无
  15. */
  16. void PID_param_init(void)
  17. {
  18. /* 初始化参数 */
  19. pid.target_val = 100.0;
  20. pid.actual_val = 0.0;
  21. pid.err = 0.0;
  22. pid.err_last = 0.0;
  23. pid.integral = 0.0;
  24. pid.Kp = 5.0;
  25. pid.Ki = 2.0;
  26. pid.Kd = 0.0;
  27. float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
  28. set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
  29. }
  30. /**
  31. * @brief 设置目标值
  32. * @param val 目标值
  33. * @note 无
  34. * @retval 无
  35. */
  36. void set_pid_target(float temp_val)
  37. {
  38. pid.target_val = temp_val; // 设置当前的目标值
  39. }
  40. /**
  41. * @brief 获取目标值
  42. * @param 无
  43. * @note 无
  44. * @retval 目标值
  45. */
  46. float get_pid_target(void)
  47. {
  48. return pid.target_val; // 设置当前的目标值
  49. }
  50. /**
  51. * @brief 设置比例、积分、微分系数
  52. * @param p:比例系数 P
  53. * @param i:积分系数 i
  54. * @param d:微分系数 d
  55. * @note 无
  56. * @retval 无
  57. */
  58. void set_p_i_d(float p, float i, float d)
  59. {
  60. pid.Kp = p; // 设置比例系数 P
  61. pid.Ki = i; // 设置积分系数 I
  62. pid.Kd = d; // 设置微分系数 D
  63. }
  64. /**
  65. * @brief PID算法实现
  66. * @param actual_val:实际值
  67. * @note 无
  68. * @retval 通过PID计算后的输出
  69. */
  70. float PID_realize(float actual_val)
  71. {
  72. /*计算目标值与实际值的误差*/
  73. pid.err = pid.target_val - actual_val;
  74. /*误差累积*/
  75. pid.integral += pid.err;
  76. /*PID算法实现*/
  77. pid.actual_val = pid.Kp * pid.err + pid.Ki * pid.integral + pid.Kd * (pid.err - pid.err_last);
  78. /*误差传递*/
  79. pid.err_last = pid.err;
  80. /*返回当前实际值*/
  81. return pid.actual_val;
  82. }
  83. /**
  84. * @brief 电机位置式 PID 控制实现(定时调用)
  85. * @param 无
  86. * @retval 无
  87. */
  88. void motor_pid_control(void)
  89. {
  90. if (is_motor_en == 1) // 电机在使能状态下才进行控制处理
  91. {
  92. float cont_val = 0; // 当前控制值
  93. static __IO int32_t Capture_Count = 0; // 当前时刻总计数值
  94. static __IO int32_t Last_Count = 0; // 上一时刻总计数值
  95. int32_t actual_speed = 0; // 实际测得速度
  96. /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD */
  97. Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
  98. // 编码器物理分辨率为16,初始化配置后为4倍,即16*4
  99. // 减速电机减速比为30,50为基本定时器周期
  100. // 转速 = 里程/时间,单位为多少转/min。
  101. /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数 */
  102. actual_speed = ((float)(Capture_Count - Last_Count) / 16 * 4 / 30) / (50 / 1000.0 / 60.0);
  103. /* 记录当前总计数值,供下一时刻计算使用 */
  104. Last_Count = Capture_Count;
  105. cont_val = PID_realize(actual_speed); // 进行 PID 计算
  106. if (cont_val > 0) // 判断电机方向
  107. {
  108. set_motor_direction(MOTOR_FWD);
  109. }
  110. else
  111. {
  112. cont_val = -cont_val;
  113. set_motor_direction(MOTOR_REV);
  114. }
  115. cont_val = (cont_val > 5500) ? 5500: cont_val; // 速度上限处理
  116. set_motor_speed(cont_val); // 设置 PWM 占空比
  117. set_computer_value(SEND_FACT_CMD, CURVES_CH1, &actual_speed, 1); // 给通道 1 发送实际值
  118. }
  119. }

直流电机速度环控制-增量式PID实现

编程要点

配置基本定时器产生定时中断来执行PID运算

配置定时器输出PWM控制电机

配置定时器读取编码器的计数值

编写增量式PID算法

编写速度控制函数

增加上位机曲线观察相关代码

编写按键控制代码

配置基本定时器6产生定时中断来执行PID运算

同上

配置定时器1输出PWM控制电机

同上

配置定时器3读取编码器的计数值

同上

编写增量式PID算法
  1. typedef struct
  2. {
  3. float target_val; // 目标值
  4. float actual_val; // 实际值
  5. float err; // 定义当前偏差值
  6. float err_next; // 定义下一个偏差值
  7. float err_last; // 定义最后一个偏差值
  8. float Kp, Ki, Kd; // 定义比例、积分、微分系数
  9. }_pid; // 速度环PID
  10. _pid pid;
  11. /**
  12. * @brief PID参数初始化
  13. * @note 无
  14. * @retval 无
  15. */
  16. void PID_param_init()
  17. {
  18. /* 初始化参数 */
  19. pid.target_val = 100;
  20. pid.actual_val = 0.0;
  21. pid.err = 0.0;
  22. pid.err_last = 0.0;
  23. pid.err_next = 0.0;
  24. pid.Kp = 6;
  25. pid.Ki = 2.5;
  26. pid.Kd = 0;
  27. float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
  28. set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
  29. }
  30. /**
  31. * @brief 设置目标值
  32. * @param val 目标值
  33. * @note 无
  34. * @retval 无
  35. */
  36. void set_pid_target(float temp_val)
  37. {
  38. pid.target_val = temp_val; // 设置当前的目标值
  39. }
  40. /**
  41. * @brief 获取目标值
  42. * @param 无
  43. * @note 无
  44. * @retval 目标值
  45. */
  46. float get_pid_target(void)
  47. {
  48. return pid.target_val; // 设置当前的目标值
  49. }
  50. /**
  51. * @brief 设置比例、积分、微分系数
  52. * @param p:比例系数 P
  53. * @param i:积分系数 i
  54. * @param d:微分系数 d
  55. * @note 无
  56. * @retval 无
  57. */
  58. void set_p_i_d(float p, float i, float d)
  59. {
  60. pid.Kp = p; // 设置比例系数 P
  61. pid.Ki = i; // 设置积分系数 I
  62. pid.Kd = d; // 设置微分系数 D
  63. }
  64. /**
  65. * @brief PID算法实现
  66. * @param actual_val:实际值
  67. * @note 无
  68. * @retval 通过PID计算后的输出
  69. */
  70. float PID_realize(float actual_val)
  71. {
  72. /*计算目标值与实际值的误差*/
  73. pid.err = pid.target_val - actual_val;
  74. /*PID算法实现*/
  75. pid.actual_val += pid.Kp * (pid.err - pid.err_next) + pid.Ki * pid.err + pid.Kd * (pid.err - 2 * pid.err_next + pid.err_last);
  76. /*传递误差*/
  77. pid.err_last = pid.err_next;
  78. pid.err_next = pid.err;
  79. /*返回当前实际值*/
  80. return pid.actual_val;
  81. }
  82. /**
  83. * @brief 电机增量式 PID 控制实现(定时调用)
  84. * @param 无
  85. * @retval 无
  86. */
  87. void motor_pid_control(void)
  88. {
  89. if (is_motor_en == 1) // 电机在使能状态下才进行控制处理
  90. {
  91. static float cont_val = 0; // 当前控制值
  92. static __IO int32_t Capture_Count = 0; // 当前时刻总计数值
  93. static __IO int32_t Last_Count = 0; // 上一时刻总计数值
  94. int32_t actual_speed = 0; // 实际测得速度
  95. /* 当前时刻总计数值 = 计数器值 + 计数溢出次数 * ENCODER_TIM_PERIOD */
  96. Capture_Count = __HAL_TIM_GET_COUNTER(&TIM_EncoderHandle) + (Encoder_Overflow_Count * 65535);
  97. // 编码器物理分辨率为16,初始化配置后为4倍,即16*4
  98. // 减速电机减速比为30,50为基本定时器周期
  99. // 转速 = 里程/时间,单位为多少转/min。
  100. /* 转轴转速 = 单位时间内的计数值 / 编码器总分辨率 * 时间系数 */
  101. actual_speed = ((float)(Capture_Count - Last_Count) / 16 * 4 / 30) / (GET_BASIC_TIM_PERIOD() / 1000.0 / 60.0);
  102. /* 记录当前总计数值,供下一时刻计算使用 */
  103. Last_Count = Capture_Count;
  104. cont_val = PID_realize(actual_speed); // 进行 PID 计算
  105. if (cont_val > 0) // 判断电机方向
  106. {
  107. set_motor_direction(MOTOR_FWD);
  108. }
  109. else
  110. {
  111. cont_val = -cont_val;
  112. set_motor_direction(MOTOR_REV);
  113. }
  114. cont_val = (cont_val > 5500) ? 5500: cont_val; // 速度上限处理
  115. set_motor_speed(cont_val); // 设置 PWM 占空比
  116. set_computer_value(SEND_FACT_CMD, CURVES_CH1, &actual_speed, 1); // 给通道 1 发送实际值
  117. }
  118. }

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

闽ICP备14008679号