PID 控制器
Darra PLC 内置 PID 功能块 (PIDController.cs),实现比例-积分-微分闭环控制,适用于温度、压力、流量、速度等连续过程控制。
PID 原理
PID 控制器根据设定值与反馈值的偏差,计算控制输出:
输出 = Kp × e(t) + Ki × ∫e(t)dt + Kd × de(t)/dt
e(t) = 设定值 - 反馈值
| 参数 | 作用 | 增大效果 | 减小效果 |
|---|---|---|---|
| Kp (比例) | 响应速度 | 快速响应,可能振荡 | 平稳但缓慢 |
| Ki (积分) | 消除稳态误差 | 误差快速归零,可能超调 | 稳态误差残留 |
| Kd (微分) | 抑制超调 | 阻尼增强,抗干扰 | 超调增大 |
功能块接口
FUNCTION_BLOCK FB_PID
VAR_INPUT
bEnable : BOOL; // 使能
rSetpoint : REAL; // 设定值 (SP)
rFeedback : REAL; // 反馈值 (PV)
rKp : REAL; // 比例增益
rKi : REAL; // 积分增益
rKd : REAL; // 微分增益
rOutMin : REAL; // 输出下限
rOutMax : REAL; // 输出上限
bManual : BOOL; // 手动模式
rManualOut : REAL; // 手动输出值
bReset : BOOL; // 复位积分器
END_VAR
VAR_OUTPUT
rOutput : REAL; // 控制输出 (CV)
rError : REAL; // 当前偏差
bAtSetpoint : BOOL; // 到达设定值 (偏差在死区内)
bLimited : BOOL; // 输出已限幅
END_VAR
温度控制示例
PROGRAM OB30 // 10ms 周期中断
VAR
fbPID_Temp : FB_PID;
rActTemp : REAL; // 实际温度 (°C)
rSetTemp : REAL; // 设定温度 (°C)
rHeaterPwr : REAL; // 加热功率 (%)
END_VAR
// 读取温度传感器 (PT100, 4-20mA → 0-200°C)
rActTemp := INT_TO_REAL(%IW100 - 4000) / 16000.0 * 200.0;
// 设定值来自 HMI
rSetTemp := %MD200;
// PID 计算
fbPID_Temp(
bEnable := TRUE,
rSetpoint := rSetTemp,
rFeedback := rActTemp,
rKp := 5.0,
rKi := 0.8,
rKd := 1.2,
rOutMin := 0.0,
rOutMax := 100.0,
bManual := %MX100.0, // HMI 手动切换
rManualOut := %MD104 // HMI 手动输出
);
// 输出到加热器 (PWM 或模拟量)
rHeaterPwr := fbPID_Temp.rOutput;
%QW200 := REAL_TO_INT(rHeaterPwr * 327.67); // 0-100% → 0-32767
END_PROGRAM
抗积分饱和 (Anti-Windup)
当输出达到限幅时,积分器会继续累积导致超调。PID 功能块内置抗积分饱和机制:
- 条件积分: 输出饱和时暂停积分累积
- 回算法: 将超出限幅的部分反馈到积分器中衰减
自动切换
手动/自动模式切换时,积分器自动跟踪手动输出值,实现无扰切换 (Bumpless Transfer)。
输出限幅
// 输出限幅确保安全
fbPID(
rOutMin := 0.0, // 最小输出 (如: 加热器不能负功率)
rOutMax := 100.0 // 最大输出 (如: 100% 功率上限)
);
参数整定建议
Ziegler-Nichols 法
- 将 Ki 和 Kd 设为 0
- 逐步增大 Kp 直到系统产生等幅振荡
- 记录临界增益 Ku 和振荡周期 Tu
- 按下表计算参数:
| 控制器 | Kp | Ki | Kd |
|---|---|---|---|
| P | 0.5×Ku | 0 | 0 |
| PI | 0.45×Ku | 1.2×Kp/Tu | 0 |
| PID | 0.6×Ku | 2×Kp/Tu | Kp×Tu/8 |
实际调参
Ziegler-Nichols 法给出初始参数,实际应用中需要根据系统响应微调。建议使用 IDE 的趋势曲线面板 (TrendPanel) 观察设定值、反馈值和输出值的实时波形进行调参。
常见应用
| 应用 | 典型参数范围 | 周期 |
|---|---|---|
| 温度控制 | Kp=2 | 100ms~1s |
| 压力控制 | Kp=1 | 10ms~100ms |
| 流量控制 | Kp=0.5 | 10ms~50ms |
| 速度控制 | Kp=0.1 | 1ms~10ms |
自整定 (Auto-Tuning)
Darra PID 内置三种自整定算法:
继电式自整定 (Relay-based)
经典 Åström-Hägglund 方法, 用继电器方式激励系统产生极限环:
VAR
fbPID : FB_PID;
fbTune : FB_PID_AutoTune;
END_VAR
// 启动自整定
fbTune(
bStart := bBtn_StartTune,
rSetpoint := rSetTemp,
rFeedback := rActTemp,
rRelayAmplitude := 10.0, // 继电幅度 (% 输出)
rHysteresis := 0.5, // 回差 (输入单位)
iMaxCycles := 5, // 最大振荡周期数
tTimeout := T#10m
);
// 自整定完成后写入 PID
IF fbTune.bDone THEN
fbPID.rKp := fbTune.rKp;
fbPID.rKi := fbTune.rKi;
fbPID.rKd := fbTune.rKd;
END_IF;
阶跃响应法
系统稳态时给一个阶跃输入, 根据响应曲线计算参数:
fbTune(
eMethod := E_TuneMethod.STEP_RESPONSE,
rStepSize := 20.0, // 阶跃幅度
tSettleTime := T#2m // 观察时间
);
频率扫描法
依次施加不同频率的正弦扰动, 绘制伯德图找出频率响应, 参数更精确但耗时:
fbTune(
eMethod := E_TuneMethod.FREQ_SWEEP,
rFreqMin := 0.01,
rFreqMax := 10.0,
iPointsPerDecade := 10
);
自整定安全
- 自整定期间系统会在设定值附近来回振荡, 确保振荡幅度不会超出安全范围
- 不要在生产运行时做自整定, 应在设备调试期或停机维护时进行
- 对温度等缓变系统, 自整定可能需要 10~30 分钟
- 自整定失败时 (未收敛), PID 参数保持不变, 手动调参
前馈控制 (Feed Forward)
前馈在已知扰动的情况下提前补偿, 不用等偏差出现:
FUNCTION_BLOCK FB_PID_FF
VAR_INPUT
rSetpoint : REAL;
rFeedback : REAL;
rDisturbance : REAL; // 测量到的扰动
rFFGain : REAL := 0.5;
// ... 其他 PID 参数
END_VAR
VAR_OUTPUT
rOutput : REAL;
END_VAR
// PID 闭环
fbPID(rSetpoint := rSetpoint, rFeedback := rFeedback, ...);
// 前馈补偿
rFeedForward := rDisturbance * rFFGain;
// 合成输出
rOutput := LIMIT(rOutMin, fbPID.rOutput + rFeedForward, rOutMax);
END_FUNCTION_BLOCK
典型应用: 温度控制中, 冷料投入量已知 → 提前增加加热功率, 不用等温度掉下来再补。
死区 (Deadband)
偏差在死区内时不调整输出, 避免输出抖动:
VAR
rError : REAL;
rDeadband : REAL := 0.5; // ±0.5 死区
END_VAR
rError := rSetpoint - rFeedback;
IF ABS(rError) < rDeadband THEN
rError := 0.0; // 死区内置零
END_IF;
// 用 rError 做 PID 计算
死区用途:
- 抑制抖动: 输出不会在设定值附近频繁震荡
- 保护执行器: 减少阀门 / 变频器的动作次数, 延长寿命
- 节能: 避免无意义的小幅度调节
反风上 (Anti-Windup)
积分器饱和防护, 防止积分项积累导致超调:
方法 1: 条件积分 (Conditional Integration)
输出饱和时停止积分累积:
// 计算积分项
IF NOT (fbPID.bLimited AND fbPID.rError * SIGN(rOutput) > 0.0) THEN
rIntegral := rIntegral + rError * rKi * dt;
END_IF;
方法 2: 回算法 (Back Calculation)
将饱和差值反馈到积分器:
// 回算
rExcess := fbPID.rRawOutput - fbPID.rOutput; // 超出限幅的部分
rIntegral := rIntegral - rExcess * rKt; // 反向衰减
Darra PID 默认启用回算法, 参数 rKt 默认 1 / T_track = 1 / (rKp * 0.5)。
级联 PID (Cascaded)
多个 PID 串联, 外环设定值 = 内环输出:
┌──────────────────┐
│ SP_Temperature │ ← HMI 温度设定
└────────┬─────────┘
↓
┌────────┴─────────┐ ← 外环 (慢, 100ms)
│ PID_Temp │
│ 反馈: 温度传感器│
└────────┬─────────┘
↓ (输出作为内环设定值)
┌────────┴─────────┐
│ SP_Flow │
└────────┬─────────┘
↓
┌────────┴─────────┐ ← 内环 (快, 10ms)
│ PID_Flow │
│ 反馈: 流量传感器│
└────────┬─────────┘
↓
阀门开度输出
ST 实现:
// 外环: 温度控制
PROGRAM OB30_100ms // 100ms 周期
VAR
fbPID_Temp : FB_PID;
END_VAR
fbPID_Temp(
rSetpoint := rSetTemp,
rFeedback := rActTemp,
rKp := 2.0, rKi := 0.3, rKd := 0.5,
rOutMin := 0.0, rOutMax := 100.0
);
// 外环输出 = 流量设定值 (传递到内环)
rSetFlow := fbPID_Temp.rOutput;
END_PROGRAM
// 内环: 流量控制
PROGRAM OB30_10ms // 10ms 周期
VAR
fbPID_Flow : FB_PID;
END_VAR
fbPID_Flow(
rSetpoint := rSetFlow, // 来自外环
rFeedback := rActFlow,
rKp := 5.0, rKi := 2.0, rKd := 0.1,
rOutMin := 0.0, rOutMax := 100.0
);
// 最终输出到阀门
%QW200 := REAL_TO_INT(fbPID_Flow.rOutput * 327.67);
END_PROGRAM
级联 PID 的优势:
- 快速抑制扰动: 内环快速响应流量扰动, 外环负责长期温度稳定
- 分离时间常数: 内外环独立调参, 内快外慢
- 限幅更灵活: 外环可直接限制内环设定值上下限
在线调参
PID 调参建议在 趋势图 中观察实时响应:
- 配置趋势记录:
rSetpoint,rFeedback,rOutput,rError - 采样 10~100ms (根据被控对象时间常数)
- 先调 Kp (从 0 开始递增, 直到出现轻微振荡)
- 加 Ki 消除稳态误差 (从小值开始, 避免超调)
- 加 Kd 抑制超调 (如不需要可保持 0)
- 整定期间趋势图记录前后对比
排错表
| 问题 | 原因 | 解决 |
|---|---|---|
| 输出持续饱和 | Kp 太大 / 限幅太紧 | 减小 Kp 或放宽限幅 |
| 稳态有残差 | 未启用积分 | 增大 Ki |
| 震荡剧烈 | Kp 太大 / Kd 不够 | 减小 Kp 或增大 Kd |
| 响应缓慢 | Kp 太小 | 增大 Kp |
| 换方向超调 | 积分饱和 | 确认抗积分饱和已启用 |
| 手动→自动切换跳变 | 积分未跟踪 | 启用 Bumpless Transfer |
| 输出抖动 | 未用死区 | 启用死区 rDeadband |
最佳实践
- 先比例, 再积分, 最后微分: 按此顺序调参
- Ki 一定要限幅: 防止积分饱和引起超调
- 微分项加滤波: 原始 Kd 对噪声敏感, 建议用
Kd / (1 + s*tau)滤波 - OB 周期稳定: PID 放 OB30 (周期中断), 不要放 OB1
- 级联优于单环: 复杂系统用级联结构, 比单环 PID 更稳
- 自整定在调试期做, 生产时别动
- 趋势图长期监控: 24h 记录便于排查偶发问题