代码

首先是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;

然后对这些参数进行初始化

  1. void PID_param_init()
  2. {
  3. /* 初始化参数 */
  4. pid.target_val=0.0;
  5. pid.actual_val=0.0;
  6. pid.err=0.0;
  7. pid.err_last=0.0;
  8. pid.integral=0.0;
  9. pid.Kp = 0.3;//24
  10. pid.Ki = 0.2;
  11. pid.Kd = 0.0;
  12. #if defined(PID_ASSISTANT_EN)
  13. float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
  14. set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
  15. #endif
  16. }

PID算法的具体实现

  1. float PID_realize(float actual_val)
  2. {
  3. /*计算目标值与实际值的误差*/
  4. pid.err = pid.target_val - actual_val;
  5. pid.integral += pid.err;
  6. /*PID算法实现*/
  7. pid.actual_val = pid.Kp * pid.err +
  8. pid.Ki * pid.integral +
  9. pid.Kd * (pid.err - pid.err_last);
  10. /*误差传递*/
  11. pid.err_last = pid.err;
  12. /*返回当前实际值*/
  13. return pid.actual_val;
  14. }

image.png

在速度控制函数中的调用

  1. /**
  2. * @brief 电机位置式 PID 控制实现(定时调用)
  3. * @param 无
  4. * @retval 无
  5. */
  6. void bldcm_pid_control(void)
  7. {
  8. int32_t speed_actual = get_motor_speed(); // 电机旋转的当前速度
  9. if (bldcm_data.is_enable)
  10. {
  11. float cont_val = 0; // 当前控制值
  12. cont_val = PID_realize(speed_actual);
  13. if (cont_val < 0)
  14. {
  15. cont_val = -cont_val;
  16. bldcm_data.direction = MOTOR_REV;
  17. }
  18. else
  19. {
  20. bldcm_data.direction = MOTOR_FWD;
  21. }
  22. cont_val = (cont_val > PWM_PERIOD_COUNT) ? PWM_PERIOD_COUNT : cont_val; // 上限处理
  23. set_bldcm_speed(cont_val);
  24. #ifdef PID_ASSISTANT_EN
  25. set_computer_value(SEND_FACT_CMD, CURVES_CH1, &speed_actual, 1); // 给通道 1 发送实际值
  26. #else
  27. printf("实际值:%d, 目标值:%.0f,控制值: %.0f\n", speed_actual, get_pid_target(), cont_val);
  28. #endif
  29. }
  30. }

在定时回调函数中的调用

  1. /**
  2. * @brief 定时器更新中断回调函数
  3. * @param htim:定时器句柄
  4. * @retval 无
  5. */
  6. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
  7. {
  8. if(htim == (&htimx_hall))
  9. {
  10. if (motor_drive.timeout++ > 100) // 有一次在产生更新中断前霍尔传感器没有捕获到值
  11. {
  12. printf("堵转超时\r\n");
  13. motor_drive.timeout = 0;
  14. LED1_ON; // 点亮LED1表示堵转超时停止
  15. /* 堵转超时停止 PWM 输出 */
  16. hall_disable(); // 禁用霍尔传感器接口
  17. stop_pwm_output(); // 停止 PWM 输出
  18. set_bldcm_disable();
  19. motor_drive.speed = 0;
  20. }
  21. }
  22. else if(htim == (&TIM_TimeBaseStructure))
  23. {
  24. bldcm_pid_control();
  25. }
  26. }

这个函数主要在定时器中断中调用,定时器配置为每 20ms 中断一次,PID 算法每 20ms 执行一次,这也就是算法的周期。

串口打印结果

image.png

在调大P之后很明显,收敛更快,近似于直线
image.png