文章目录
前言
在文章《西门子PLC结构化编程_带前馈控制功能的位置式PID控制器》中,我们讨论了带前馈控制功能的位置式PID实现方法,现在我们将该算法应用到增量式PID中。
一、功能概述
PID控制功能块应具备以下功能:
- 完整的PID实现:包含比例、积分、微分三项计算;
- 防积分饱和:可配置的积分上下限,防止积分项过大;
- 死区控制:防止控制元件频繁抖动;
- 基于过程值的微分:避免设定值变化导致误差过大,造成输出冲击;
- 手动/自动无扰切换:在手动模式下保持积分项,切换时无冲击,同时,将当前PID输出值赋给手动设定值;
- 输出限幅:可配置的输出上下限,保护执行机构;
- 状态监控:提供各项输出用于监控和调试;
- 手动模式:PID控制器取消激活,PID输出值等于手动设定值;
- 半自动模式:PID控制器激活,设定值SP由操作员手动设置;
- 自动模式:PID控制器激活,设定值SP由其它连锁条件计算得出
- Kp:增大使响应加快,但过大会导致振荡;
- Ti:减小使积分作用增强,消除稳态误差;
- Td:增大改善稳定性,抑制超调;
- 可选择控制器正作用/反作用;
- 如果过程是正作用(控制量增加→过程值增加),则控制器应设置为反作用;
- 如果过程是反作用(控制量增加→过程值减少),则控制器应设置为正作用;
- 具有前馈控制功能;
- 位置式PID当前输出 = 位置式PID上一扫描周期输出 + 增量式PID当前输出。
二、公式推导
在文章《西门子PLC结构化编程_带前馈控制功能的位置式PID控制器》中,我们已经推导出了位置式PID公式以及前馈控制计算公式,这里不再赘述。只给出增量式PID计算公式。
增量式PID算法的核心公式为:
Δu(k) = Kp × [e(k) - e(k-1)] + Ki × e(k) + Kd × [e(k) - 2e(k-1) + e(k-2)]
其中:
Δu(k) = 输出变化量(增量)
e(k) = 当前误差
e(k-1) = 上一次误差
e(k-2) = 上两次误差
Ki = Kp × Δt / Ti(积分系数)
Kd = Kp × Td / Δt(微分系数)
三、程序编写
1. 新建自定义数据类型“1_PID_Controller”
图1
2. 新建FB块“FB37_IncPID_Controller_Advanced”
在引脚中添加如下变量:
图2
3. 编写程序
REGION 手动、半自动、自动无扰切换
IF #PID.HMI.HMIManual THEN
#PID.HMI.ManualSetpoint := #PID.HMI.TotalOutput;
END_IF;
IF #PID.HMI.HMIAuto THEN
#PID.HMI.AutoSetpoint := #PID.InPut.SetpointValue;
END_IF;
IF #PID.HMI.HMISemiAuto THEN
#PID.HMI.SemiAutoSetpoint := #PID.InPut.SetpointValue;
END_IF;
END_REGION
REGION 设定值赋值
IF #PID.HMI.Manual_DO THEN
#PID.Temp.OutputTemp := #PID.HMI.ManualSetpoint;
#PID.HMI.TotalOutput := #PID.HMI.ManualSetpoint;
// 在手动模式下保持积分项,防止切换回自动时产生冲击
#PID.Static.IntegralTerm := #PID.Static.IntegralTerm; // 保持不变
#PID.OutPut.ControllerActive := 0;
ELSIF
#PID.HMI.SemiAuto_DO THEN
#PID.InPut.SetpointValue := #PID.HMI.SemiAutoSetpoint;
#PID.OutPut.ControllerActive := 1;
ELSIF
#PID.HMI.Auto_DO THEN
#PID.InPut.SetpointValue := #PID.HMI.AutoSetpoint;
#PID.OutPut.ControllerActive := 1;
END_IF;
END_REGION
REGION 初始化
IF #PID.Static.FirstScan OR #PID.HMI.HMIReset THEN
#PID.OutPut.Output := 0.0;
#PID.OutPut.Error := 0.0;
#PID.OutPut.Proportional := 0.0;
#PID.OutPut.Integral := 0.0;
#PID.OutPut.Derivative := 0.0;
#PID.OutPut.FeedforwardOutput := 0.0;
#PID.Static.PrevError := 0.0;
#PID.Static.PrevPrevError := 0.0;
#PID.Static.LastOutput := 0.0;
#PID.OutPut.IncrementalChange := 0.0;
// #PID.Static.LastFeedforwardInput := 0.0;
// #PID.Static.FeedforwardRateLimit := 0.0;
#PID.Static.FeedforwardDelta := 0.0;
#PID.Static.FirstScan := FALSE;
// 设置初始方向
IF #PID.HMI.ControllerDirection = 0 THEN
#PID.Static.DirectionSign := 1.0; // 正作用
ELSIF
#PID.HMI.ControllerDirection = 1 THEN
#PID.Static.DirectionSign := -1.0; // 反作用
END_IF;
IF #PID.HMI.HMIReset THEN
RETURN; // 重置时立即返回,不执行后续计算
END_IF;
END_IF;
// 时间处理
#PID.Static.DeltaTime := #PID.InPut.ScanTime / 1000.0;
END_REGION
REGION PID计算
IF #PID.OutPut.ControllerActive THEN
// 根据作用方向计算当前误差 e(t) = Direction * (SP - PV)
IF ABS(#PID.InPut.SetpointValue - #PID.HMI.ProcessValue) <= #PID.InPut.DeadValue THEN
#PID.Temp.ErrorTemp := 0; // 死区设置
ELSE
#PID.Temp.ErrorTemp := #PID.Static.DirectionSign * (#PID.InPut.SetpointValue - #PID.HMI.ProcessValue);
END_IF;
#PID.OutPut.Error := #PID.Temp.ErrorTemp;
// 计算积分系数
#PID.Temp.Ki := 0.0;
IF #PID.HMI.Ti > 0.0 AND #PID.Static.DeltaTime > 0.0 THEN
#PID.Temp.Ki := #PID.HMI.Kp * #PID.Static.DeltaTime / #PID.HMI.Ti;
END_IF;
// 计算微分系数
#PID.Temp.Kd := 0.0;
IF #PID.HMI.Td > 0.0 AND #PID.Static.DeltaTime > 0.0 THEN
#PID.Temp.Kd := #PID.HMI.Kp * #PID.HMI.Td / #PID.Static.DeltaTime;
END_IF;
// 计算误差变化量
#PID.Temp.DeltaError := #PID.Temp.ErrorTemp - #PID.Static.PrevError;
#PID.Temp.Delta2Error := #PID.Temp.ErrorTemp - 2 * #PID.Static.PrevError + #PID.Static.PrevPrevError;
// 增量式PID公式: Δu(k) = Kp*(e(k)-e(k-1)) + Ki*e(k) + Kd*(e(k)-2e(k-1)+e(k-2))
#PID.Temp.IncrementalTemp := #PID.HMI.Kp * #PID.Temp.DeltaError + #PID.Temp.Ki * #PID.Temp.ErrorTemp + #PID.Temp.Kd * #PID.Temp.Delta2Error;
// 前馈控制计算
IF #PID.InPut.EnableFeedforward THEN
// 计算前馈输入的变化量(增量式前馈)
#PID.Static.FeedforwardDelta := #PID.InPut.FeedforwardInput - #PID.Static.LastFeedforwardInput;
#PID.Static.LastFeedforwardInput := #PID.InPut.FeedforwardInput;
// 前馈变化率限制(不使用外部前馈处理程序时使用),确保前馈输入的变化不会超过每秒±5单位的速率限制
// #PID.Static.FeedforwardRateLimit := #PID.Static.FeedforwardRateLimit +
// LIMIT(MN := - #PID.InPut.IncrementalMax * 0.5, IN := #PID.Static.FeedforwardDelta, MX := #PID.InPut.IncrementalMax * 0.5);
// 根据前馈类型计算前馈输出
CASE #PID.InPut.FeedforwardType OF
0: // 加法前馈
// #PID.Temp.FeedforwardTemp := #PID.InPut.FeedforwardGain * #PID.Static.FeedforwardRateLimit; // 不使用外部前馈处理程序时使用
#PID.Temp.FeedforwardTemp := #PID.InPut.FeedforwardGain * #PID.Static.FeedforwardDelta;
1: // 乘法前馈
// #PID.Temp.FeedforwardTemp := #PID.Temp.IncrementalTemp * #PID.InPut.FeedforwardGain * #PID.Static.FeedforwardRateLimit / 100.0; // 不使用外部前馈处理程序时使用
#PID.Temp.FeedforwardTemp := #PID.Temp.IncrementalTemp * #PID.InPut.FeedforwardGain * #PID.Static.FeedforwardDelta / 100.0;
ELSE: // 默认加法前馈
// #PID.Temp.FeedforwardTemp := #PID.InPut.FeedforwardGain * #PID.Static.FeedforwardRateLimit; // 不使用外部前馈处理程序时使用
#PID.Temp.FeedforwardTemp := #PID.InPut.FeedforwardGain * #PID.Static.FeedforwardDelta;
END_CASE;
// 前馈输出限幅
#PID.Temp.FeedforwardTemp := LIMIT(MN := - #PID.InPut.IncrementalMax * 0.8, IN := #PID.Temp.FeedforwardTemp, MX := #PID.InPut.IncrementalMax * 0.8);
#PID.OutPut.FeedforwardOutput := #PID.Temp.FeedforwardTemp;
// 总增量 = PID增量 + 前馈增量
#PID.Temp.IncrementalTemp := #PID.Temp.IncrementalTemp + #PID.Temp.FeedforwardTemp;
ELSE
#PID.OutPut.FeedforwardOutput := 0.0;
END_IF;
// 限制增量变化幅度
IF #PID.Temp.IncrementalTemp > #PID.InPut.IncrementalMax THEN
#PID.Temp.IncrementalTemp := #PID.InPut.IncrementalMax;
ELSIF #PID.Temp.IncrementalTemp < - #PID.InPut.IncrementalMax THEN
#PID.Temp.IncrementalTemp := - #PID.InPut.IncrementalMax;
END_IF;
#PID.OutPut.IncrementalChange := #PID.Temp.IncrementalTemp;
// 计算新输出值
#PID.Temp.OutputTemp := #PID.Static.LastOutput + #PID.Temp.IncrementalTemp;
END_IF;
// 输出限幅和抗积分饱和处理
IF #PID.Temp.OutputTemp > #PID.InPut.OutputMax THEN
#PID.Temp.OutputTemp := #PID.InPut.OutputMax;
// 抗积分饱和: 当输出饱和时,停止积分作用
// 对于增量式PID,这通过限制下一次的积分项实现
// #PID.OutPut.IncrementalChange := #PID.InPut.OutputMax - #PID.Static.LastOutput;
ELSIF
#PID.Temp.OutputTemp < #PID.InPut.OutputMin THEN
#PID.Temp.OutputTemp := #PID.InPut.OutputMin;
// 抗积分饱和: 当输出饱和时,停止积分作用
// #PID.OutPut.IncrementalChange := #PID.InPut.OutputMin - #PID.Static.LastOutput;
END_IF;
#PID.OutPut.Output := #PID.Temp.OutputTemp;
#PID.HMI.TotalOutput := #PID.Temp.OutputTemp;
#PID.Static.LastOutput := #PID.Temp.OutputTemp;
// 保存误差历史供下次使用
#PID.Static.PrevPrevError := #PID.Static.PrevError;
#PID.Static.PrevError := #PID.Temp.ErrorTemp;
// 计算各项输出(用于显示和调试)
#PID.OutPut.Proportional := #PID.HMI.Kp * #PID.Temp.DeltaError;
#PID.OutPut.Integral := #PID.Temp.Ki * #PID.Temp.ErrorTemp;
#PID.OutPut.Derivative := #PID.Temp.Kd * #PID.Temp.Delta2Error;
// 更新状态
#PID.OutPut.DirectionStatus := #PID.HMI.ControllerDirection;
END_REGION
REGION 辅助功能
// 按钮自复位
#PID.HMI.HMIManual := #PID.HMI.HMIManual AND NOT #PID.Temp.HMIManualReset;
#PID.HMI.HMISemiAuto := #PID.HMI.HMISemiAuto AND NOT #PID.Temp.HMISemiAutoReset;
#PID.HMI.HMIAuto := #PID.HMI.HMIAuto AND NOT #PID.Temp.HMIAutoReset;
// #PID.HMI.HMIReset := #PID.HMI.HMIReset AND NOT #PID.Temp.HMIResetReset;
// 自动生成复位信号
#PID.Temp.HMIManualReset := #PID.HMI.HMIManual;
#PID.Temp.HMISemiAutoReset := #PID.HMI.HMISemiAuto;
#PID.Temp.HMIAutoReset := #PID.HMI.HMIAuto;
// #PID.Temp.HMIResetReset := #PID.HMI.HMIReset;
END_REGION
总结
本文将前馈控制算法应用于增量式PID控制模型中,可根据实际执行器特性,选择位置式PID模型还是增量式PID模型。