西门子PLC结构化编程_带前馈控制功能的增量式PID控制器

增量式PID与前馈控制实现


前言

在文章《西门子PLC结构化编程_带前馈控制功能的位置式PID控制器》中,我们讨论了带前馈控制功能的位置式PID实现方法,现在我们将该算法应用到增量式PID中。


一、功能概述

PID控制功能块应具备以下功能:

  1. 完整的PID实现:包含比例、积分、微分三项计算;
  2. 防积分饱和:可配置的积分上下限,防止积分项过大;
  3. 死区控制:防止控制元件频繁抖动;
  4. 基于过程值的微分:避免设定值变化导致误差过大,造成输出冲击;
  5. 手动/自动无扰切换:在手动模式下保持积分项,切换时无冲击,同时,将当前PID输出值赋给手动设定值;
  6. 输出限幅:可配置的输出上下限,保护执行机构;
  7. 状态监控:提供各项输出用于监控和调试;
  8. 手动模式:PID控制器取消激活,PID输出值等于手动设定值;
  9. 半自动模式:PID控制器激活,设定值SP由操作员手动设置;
  10. 自动模式:PID控制器激活,设定值SP由其它连锁条件计算得出
  11. Kp:增大使响应加快,但过大会导致振荡;
  12. Ti:减小使积分作用增强,消除稳态误差;
  13. Td:增大改善稳定性,抑制超调;
  14. 可选择控制器正作用/反作用;
  15. 如果过程是正作用(控制量增加→过程值增加),则控制器应设置为反作用;
  16. 如果过程是反作用(控制量增加→过程值减少),则控制器应设置为正作用;
  17. 具有前馈控制功能;
  18. 位置式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模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值