代码
首先是PID的结构体参数
typedef struct
{
float target_val; //目标值
float actual_val; //实际值
float err; //定义偏差值
float err_last; //定义上一个偏差值
float Kp,Ki,Kd; //定义比例、积分、微分系数
float integral; //定义积分值
}_pid;
然后对这些参数进行初始化
void PID_param_init()
{
/* 初始化参数 */
pid.target_val=0.0;
pid.actual_val=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.integral=0.0;
pid.Kp = 0.3;//24
pid.Ki = 0.2;
pid.Kd = 0.0;
#if defined(PID_ASSISTANT_EN)
float pid_temp[3] = {pid.Kp, pid.Ki, pid.Kd};
set_computer_value(SEND_P_I_D_CMD, CURVES_CH1, pid_temp, 3); // 给通道 1 发送 P I D 值
#endif
}
PID算法的具体实现
float PID_realize(float actual_val)
{
/*计算目标值与实际值的误差*/
pid.err = pid.target_val - actual_val;
pid.integral += pid.err;
/*PID算法实现*/
pid.actual_val = pid.Kp * pid.err +
pid.Ki * pid.integral +
pid.Kd * (pid.err - pid.err_last);
/*误差传递*/
pid.err_last = pid.err;
/*返回当前实际值*/
return pid.actual_val;
}
在速度控制函数中的调用
/**
* @brief 电机位置式 PID 控制实现(定时调用)
* @param 无
* @retval 无
*/
void bldcm_pid_control(void)
{
int32_t speed_actual = get_motor_speed(); // 电机旋转的当前速度
if (bldcm_data.is_enable)
{
float cont_val = 0; // 当前控制值
cont_val = PID_realize(speed_actual);
if (cont_val < 0)
{
cont_val = -cont_val;
bldcm_data.direction = MOTOR_REV;
}
else
{
bldcm_data.direction = MOTOR_FWD;
}
cont_val = (cont_val > PWM_PERIOD_COUNT) ? PWM_PERIOD_COUNT : cont_val; // 上限处理
set_bldcm_speed(cont_val);
#ifdef PID_ASSISTANT_EN
set_computer_value(SEND_FACT_CMD, CURVES_CH1, &speed_actual, 1); // 给通道 1 发送实际值
#else
printf("实际值:%d, 目标值:%.0f,控制值: %.0f\n", speed_actual, get_pid_target(), cont_val);
#endif
}
}
在定时回调函数中的调用
/**
* @brief 定时器更新中断回调函数
* @param htim:定时器句柄
* @retval 无
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == (&htimx_hall))
{
if (motor_drive.timeout++ > 100) // 有一次在产生更新中断前霍尔传感器没有捕获到值
{
printf("堵转超时\r\n");
motor_drive.timeout = 0;
LED1_ON; // 点亮LED1表示堵转超时停止
/* 堵转超时停止 PWM 输出 */
hall_disable(); // 禁用霍尔传感器接口
stop_pwm_output(); // 停止 PWM 输出
set_bldcm_disable();
motor_drive.speed = 0;
}
}
else if(htim == (&TIM_TimeBaseStructure))
{
bldcm_pid_control();
}
}
这个函数主要在定时器中断中调用,定时器配置为每 20ms 中断一次,PID 算法每 20ms 执行一次,这也就是算法的周期。
串口打印结果
在调大P之后很明显,收敛更快,近似于直线