STM32实现PID控制器的参数整定与代码模板
在STM32嵌入式控制中,PID控制器是温度、电机转速、平衡小车等闭环系统的核心算法。本文提供一份可直接移植的增量式PID代码模板,并分享工程中行之有效的参数整定方法。
一、增量式PID算法模板
相比位置式PID,增量式PID输出的是控制量的增量(Δu),天然抗积分饱和,且手动/自动切换无冲击。
pid.h
#ifndef __PID_H
#define __PID_H
typedef struct {
float Kp, Ki, Kd; // 比例、积分、微分系数
float Ts; // 采样周期(秒)
float error[3]; // e[k], e[k-1], e[k-2]
float output_min; // 输出下限
float output_max; // 输出上限
float last_output; // u[k-1]
} IncPID_t;
void PID_Init(IncPID_t *pid, float kp, float ki, float kd, float ts);
float PID_Calculate(IncPID_t *pid, float setpoint, float feedback);
void PID_SetOutputLimit(IncPID_t *pid, float min, float max);
#endif
pid.c
#include "pid.h"
void PID_Init(IncPID_t *pid, float kp, float ki, float kd, float ts)
{
pid->Kp = kp;
pid->Ki = ki;
pid->Kd = kd;
pid->Ts = ts;
pid->error[0] = pid->error[1] = pid->error[2] = 0;
pid->last_output = 0;
pid->output_min = -1000;
pid->output_max = 1000;
}
float PID_Calculate(IncPID_t *pid, float setpoint, float feedback)
{
// 1. 更新误差序列
pid->error[2] = pid->error[1];
pid->error[1] = pid->error[0];
pid->error[0] = setpoint - feedback;
// 2. 计算增量 Δu
float delta = pid->Kp * (pid->error[0] - pid->error[1])
+ pid->Ki * pid->Ts * pid->error[0]
+ pid->Kd / pid->Ts * (pid->error[0] - 2*pid->error[1] + pid->error[2]);
// 3. 增量限幅(防突变)
if (delta > 50) delta = 50;
if (delta < -50) delta = -50;
// 4. 累加得到绝对输出
float output = pid->last_output + delta;
// 5. 总输出限幅
if (output > pid->output_max) output = pid->output_max;
if (output < pid->output_min) output = pid->output_min;
pid->last_output = output;
return output;
}
void PID_SetOutputLimit(IncPID_t *pid, float min, float max)
{
pid->output_min = min;
pid->output_max = max;
}
二、STM32定时器调用示例
在1ms定时器中断中调用PID,输出控制PWM占空比:
IncPID_t motor_pid;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3) // 1ms定时器
{
float current_speed = Read_Encoder_Speed(); // 编码器反馈
float pwm_out = PID_Calculate(&motor_pid, target_speed, current_speed);
Set_Motor_PWM((int16_t)pwm_out);
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_TIM3_Init(); // 1ms定时器
MX_TIM1_Init(); // PWM输出
PID_Init(&motor_pid, 1.5f, 0.01f, 0.05f, 0.001f);
PID_SetOutputLimit(&motor_pid, -999, 999);
HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
while (1)
{
// 可通过串口在线修改PID参数
}
}
三、参数整定四步法
第一步:纯比例(P)试探
• Ki=0, Kd=0,Kp从小开始(如0.5)
• 给定阶跃目标,观察响应
• 若不振荡:逐步增大Kp(每次×1.5)
• 若持续等幅振荡:记录临界Kp(Ku)和振荡周期(Tu),然后Kp=0.5×Ku
第二步:加入积分(I)消除静差
• 保持Kp,从Ki=0.001开始逐步增大
• 观察稳态误差是否消失
• 若出现低频摆动:Ki过大,需减小
第三步:加入微分(D)抑制超调
• 保持Kp、Ki,从Kd=0.01开始
• 观察超调量是否减小
• 若出现高频抖动:Kd过大,需减小或滤除噪声
第四步:微调与限幅
• 积分限幅:在PID结构中增加integral_limit,防止积分饱和
• 输出死区:当误差小于阈值时输出置0,防止频繁调节
四、在线调参与调试技巧
4.1 串口透传参数
void Serial_Debug(void)
{
char buf[64];
sprintf(buf, "SP=%.1f FB=%.1f OUT=%.1f\r\n",
target_speed, current_speed, pwm_out);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 10);
}
4.2 在线修改Kp/Ki/Kd
通过串口接收命令:
if (strncmp(rx_buf, "KP:", 3) == 0)
motor_pid.Kp = atof(rx_buf + 3);
4.3 数据可视化
将SP、FB、OUT三个变量通过DAC输出或串口发给上位机(如VOFA+),实时绘制曲线,比看数值直观十倍。
五、常见问题与对策
现象 原因 对策
输出饱和后恢复慢 积分饱和 启用积分限幅或增量式PID
目标附近来回抖动 Kp过大或死区太小 减小Kp,加入5%死区
响应太慢 Kp过小或Ki过大 增大Kp,减小Ki
微分项引入噪声 反馈信号有高频噪声 对反馈做低通滤波后再微分
六、结语
本文提供的增量式PID模板可直接移植到STM32工程,配合定时器中断实现1ms周期的闭环控制。参数整定遵循先P后I再D的顺序,配合串口在线调参与波形观察,可在半小时内完成大部分系统的调优。





