PID 控制器设计:从原理到实现

系列:电机控制系列 - 第 9 篇 目标平台:STM32F407ZGT6 阅读时间:30 分钟

前言

PID 是电机控制的灵魂

  • 电流环 PID → 控制电流
  • 速度环 PID → 控制转速
  • 位置环 PID → 控制位置

本篇目标

  • ✅ 理解 PID 原理
  • ✅ 掌握参数整定方法
  • ✅ 实现抗积分饱和

一、PID 控制器原理

1.1 连续 PID

          ┌─────────────────────────────┐
          │                             │
          │   u(t) = Kp·e(t) + Ki·∫e(t)dt + Kd·de(t)/dt
          │    └──┬──┘   └───┬───┘   └───┬───┘
          │      P         I           D
          │
 e(t) ────┴──────────────────────────────────────> u(t)
 误差                                              输出

三项作用

公式作用特点
PKp·e(t)快速响应比例调节
IKi·∫e(t)dt消除稳态误差积分累积
DKd·de(t)/dt抑制超调微分预测

1.2 离散 PID

位置式 PID

 u(k) = Kp·e(k) + Ki·Σe(k)·Ts + Kd·(e(k)-e(k-1))/Ts

增量式 PID(推荐):

 Δu(k) = u(k) - u(k-1)
       = Kp·(e(k)-e(k-1)) + Ki·e(k)·Ts + Kd·(e(k)-2e(k-1)+e(k-2))/Ts

优点

  • ✅ 计算量小
  • ✅ 无冲击切换
  • ✅ 适合增量式编码器

二、PID 代码实现

2.1 基础 PID 结构体

 /**
  * @brief PID 控制器
  */
 typedef struct {
     float Kp;              // 比例增益
     float Ki;              // 积分增益
     float Kd;              // 微分增益
     
     float integral;        // 积分累积
     float prev_error;      // 上次误差
     float prev_prev_error; // 上上次误差
     
     float integral_limit;  // 积分限幅
     float output_limit;    // 输出限幅
     
     float Ts;              // 采样周期(s)
 } PID_t;

2.2 位置式 PID

 /**
  * @brief 位置式 PID 计算
  */
 float PID_Position(PID_t *pid, float error) {
     // P 项
     float p_term = pid->Kp * error;
     
     // I 项
     pid->integral += error * pid->Ts;
     
     // 积分限幅(抗饱和)
     if (pid->integral > pid->integral_limit) {
         pid->integral = pid->integral_limit;
     } else if (pid->integral < -pid->integral_limit) {
         pid->integral = -pid->integral_limit;
     }
     
     float i_term = pid->Ki * pid->integral;
     
     // D 项
     float d_term = pid->Kd * (error - pid->prev_error) / pid->Ts;
     
     // 更新历史
     pid->prev_error = error;
     
     // 输出
     float output = p_term + i_term + d_term;
     
     // 输出限幅
     if (output > pid->output_limit) {
         output = pid->output_limit;
     } else if (output < -pid->output_limit) {
         output = -pid->output_limit;
     }
     
     return output;
 }

2.3 增量式 PID

 /**
  * @brief 增量式 PID 计算
  */
 float PID_Increment(PID_t *pid, float error) {
     // 计算增量
     float delta_u = pid->Kp * (error - pid->prev_error) +
                     pid->Ki * error * pid->Ts +
                     pid->Kd * (error - 2.0f * pid->prev_error + pid->prev_prev_error) / pid->Ts;
     
     // 更新历史
     pid->prev_prev_error = pid->prev_error;
     pid->prev_error = error;
     
     // 累积输出
     static float output = 0;
     output += delta_u;
     
     // 输出限幅
     if (output > pid->output_limit) {
         output = pid->output_limit;
     } else if (output < -pid->output_limit) {
         output = -pid->output_limit;
     }
     
     return output;
 }

三、抗积分饱和

3.1 积分饱和问题

现象

 误差持续存在 → 积分累积过大 → 输出饱和 → 响应变慢

例子

  • 电机堵转 → 误差持续 → 积分很大
  • 堵转解除 → 积分仍很大 → 电机飞车

3.2 抗饱和方法

方法 1:积分限幅(已实现)

 // 积分累积不超过 ±integral_limit
 if (pid->integral > pid->integral_limit) {
     pid->integral = pid->integral_limit;
 }

方法 2:条件积分

/**
 * @brief 条件积分(误差小时才积分)
 */
void PID_Conditional_Integral(PID_t *pid, float error) {
    // 误差在 ±epsilon 内才积分
    float epsilon = 1.0f;

    if (fabsf(error) < epsilon) {
        pid->integral += error * pid->Ts;
    }
}

方法 3:积分分离

/**
 * @brief 积分分离(启动时禁用积分)
 */
void PID_Integral_Separation(PID_t *pid, float error, float threshold) {
    // 误差大于阈值时,清零积分
    if (fabsf(error) > threshold) {
        pid->integral = 0;
    } else {
        pid->integral += error * pid->Ts;
    }
}

方法 4:抗饱和反馈

/**
 * @brief 抗饱和反馈
 */
float PID_Anti_Windup(PID_t *pid, float error, float output) {
    // 计算饱和误差
    float saturation_error = output - pid->output_limit;

    // 反馈到积分
    float K_anti = 0.5f;  // 抗饱和增益
    pid->integral -= K_anti * saturation_error * pid->Ts;

    return output;
}

四、PID 参数整定

4.1 试凑法

步骤

1. Ki = 0, Kd = 0,逐步增大 Kp
   → 直到系统开始震荡

2. 减小 Kp 到震荡消失
   → Kp_final = 0.6 × Kp_osc

3. 增大 Ki
   → 消除稳态误差

4. 增大 Kd(可选)
   → 减小超调

4.2 临界比例度法

步骤

1. Ki = 0, Kd = 0
2. 增大 Kp 直到系统临界震荡
3. 记录:
   - Kp_crit:临界 Kp
   - T_crit:震荡周期

4. 计算参数:
   Kp = 0.6 × Kp_crit
   Ki = Kp / (0.5 × T_crit)
   Kd = Kp × 0.125 × T_crit

4.3 继电反馈法

/**
 * @brief 继电反馈整定
 */
void PID_Relay_Tuning(void) {
    // 施加继电控制
    float h = 1.0f;  // 继电幅值

    while (1) {
        float error = ref - feedback;

        if (error > 0) {
            output = h;
        } else {
            output = -h;
        }

        // 测量震荡周期 T_crit
        // ...

        // 计算 PID 参数
        float Kp = 0.6f * Kp_crit;
        float Ki = Kp / (0.5f * T_crit);
        float Kd = Kp * 0.125f * T_crit;
    }
}

五、电机 PID 应用

5.1 电流环 PID

// 电流环(快速,kHz 级)
PID_t pid_id = {
    .Kp = 2.0f,
    .Ki = 200.0f,    // 大 Ki,快速消除误差
    .Kd = 0.0f,
    .Ts = 0.00005f,  // 20kHz → 50us
    .integral_limit = 50.0f,
    .output_limit = 24.0f  // 母线电压
};

PID_t pid_iq = {
    .Kp = 2.0f,
    .Ki = 200.0f,
    .Kd = 0.0f,
    .Ts = 0.00005f,
    .integral_limit = 50.0f,
    .output_limit = 24.0f
};

5.2 速度环 PID

// 速度环(中等,100Hz 级)
PID_t pid_speed = {
    .Kp = 0.5f,
    .Ki = 10.0f,
    .Kd = 0.01f,     // 小 Kd,抑制超调
    .Ts = 0.01f,     // 100Hz → 10ms
    .integral_limit = 10.0f,
    .output_limit = 10.0f  // 最大电流
};

5.3 位置环 PID

// 位置环(慢速,10Hz 级)
PID_t pid_position = {
    .Kp = 10.0f,
    .Ki = 1.0f,
    .Kd = 0.1f,
    .Ts = 0.1f,      // 10Hz → 100ms
    .integral_limit = 5.0f,
    .output_limit = 100.0f  // 最大速度
};

六、总结

PID 核心要点

  1. P:快速响应
  2. I:消除稳态误差
  3. D:抑制超调
  4. 抗积分饱和很重要!

下一篇FOC 控制框架

最后修改:2026 年 03 月 14 日
如果觉得我的文章对你有用,请随意赞赏