ST怎么写除法保护:IF Divisor <> 0.0 THEN Result := Numerator / Divisor; END_IF;

发布于 2026-03-15 11:16:56 · 浏览 5 次 · 评论 0 条

在结构化文本(ST)编程中,除法运算看似简单,但若不加保护,极易引发运行时错误——最典型的是除零异常。该异常在PLC(可编程逻辑控制器)中不会抛出传统软件的“报错对话框”,而是导致:数值结果为 NaN(非数字)、±INF(正/负无穷),或更严重地触发硬件级看门狗超时、CPU停机、程序跳转至故障组织块(如OB80/OB86),最终造成产线停机。因此,“IF Divisor <> 0.0 THEN Result := Numerator / Divisor; END_IF;”不是可选技巧,而是电气自动化系统安全运行的强制性基础防线


一、为什么必须做除法保护?从PLC底层机制说起

PLC执行ST代码时,其运算单元遵循IEC 61131-3标准定义的浮点数行为,底层依赖IEEE 754单精度(REAL)或双精度(LREAL)格式。当分母为0.0时:

  • Numerator / 0.0(Numerator ≠ 0) → 结果为 +INF-INF(取决于被除数符号)
  • 0.0 / 0.0 → 结果为 NaN(Not a Number)

这两种值具有传染性:一旦参与后续计算(如 Result + 1.0Result > 100.0),结果仍为 NaNINF;更关键的是,多数PLC厂商(西门子S7-1200/1500、罗克韦尔ControlLogix、倍福TwinCAT)默认不触发运行时中断,程序继续执行,但输出值已完全失真。

例如,在恒压供水系统中,PID输出需根据压力偏差与流量反馈做归一化处理:

PressureRatio := (Setpoint - ActualPressure) / MaxPressureDeviation;

MaxPressureDeviation 因传感器断线变为0.0,PressureRatio 立即变为 INF,导致PID输出饱和,变频器全速运行,水泵过载跳闸——而PLC日志中可能仅显示“无错误”,排查耗时数小时。

因此,保护的本质不是防止语法错误,而是阻断无效数据进入控制链路


二、基础写法详解:IF Divisor <> 0.0 THEN ... END_IF

该语句是ST中最直接的保护结构,但细节决定成败:

  1. 比较操作符必须用 <>,不可用 !=
    IEC 61131-3标准中,<> 是唯一合法的“不等于”符号。!= 是C语言习惯,在ST编辑器(如TIA Portal、Codesys)中会报编译错误。

  2. 分母必须声明为浮点类型并初始化
    错误写法:

    VAR
        Divisor : INT; // 整型无法直接与0.0比较
    END_VAR
    IF Divisor <> 0.0 THEN ... // 编译失败:类型不匹配

    正确写法(三选一):

    • 显式转换:IF REAL(Divisor) <> 0.0 THEN Result := Numerator / REAL(Divisor); END_IF;
    • 统一声明为REAL:Divisor : REAL := 1.0;
    • 使用LREAL提升精度(推荐用于高精度场景):Divisor : LREAL := 1.0;
  3. 0.0 的写法必须带小数点
    0 是整数常量,0.0 才是浮点常量。比较 Divisor <> 0 在部分PLC中会隐式转换,但存在风险(如某些固件版本对未声明类型的变量转换不一致)。始终使用 0.00.0e0

  4. 分支必须闭合,不可省略 END_IF
    ST是强结构化语言,IF 必须配对 END_IF。遗漏将导致编译失败或逻辑范围意外扩大。


三、进阶保护:应对浮点精度陷阱

工业现场传感器信号常含噪声,经滤波后分母可能趋近于0但不等于0(如 1.2e-12)。此时 Divisor <> 0.0 为真,但 Numerator / Divisor 会产生极大值(溢出),等效于未保护。

解决方案:引入最小有效阈值(ε)

// 定义阈值(根据工艺要求设定)
CONST
    EPSILON : REAL := 1.0e-6; // 允许的最小绝对值
END_CONST

// 保护逻辑
IF ABS(Divisor) > EPSILON THEN
    Result := Numerator / Divisor;
ELSE
    Result := 0.0; // 或预设安全值,如限幅值、前次有效值
END_IF;
  • ABS() 函数取绝对值,避免负数分母被忽略
  • EPSILON 值需结合场景确定:
    • 压力变送器量程0–10 bar,精度0.1% → ε可设 1e-3
    • 编码器计数差值(整型转REAL)→ ε设 0.5(防止±1计数抖动)

✅ 关键原则:阈值必须大于浮点计算的相对误差。IEEE 754单精度REAL的机器精度约为 1.19e-7,故 1e-6 是安全下限。


四、工程化封装:自定义函数块(FB)实现复用

重复编写保护逻辑易出错且维护困难。应封装为可重用函数块:

FUNCTION_BLOCK DIV_PROTECT
VAR_INPUT
    Numerator : REAL;
    Divisor   : REAL;
    Epsilon   : REAL := 1.0e-6; // 默认阈值
END_VAR
VAR_OUTPUT
    Result    : REAL;
    IsValid   : BOOL; // 标识计算是否有效
END_VAR
VAR
    AbsDivisor : REAL;
END_VAR

AbsDivisor := ABS(Divisor);
IF AbsDivisor > Epsilon THEN
    Result := Numerator / Divisor;
    IsValid := TRUE;
ELSE
    Result := 0.0;
    IsValid := FALSE;
END_IF;

调用方式(在主程序OB1中):

// 实例化
fbDivProtect1 : DIV_PROTECT;

// 执行
fbDivProtect1(
    Numerator := TemperatureError,
    Divisor   := TimeConstant,
    Epsilon   := 1.0e-3
);
TemperatureRate := fbDivProtect1.Result;
IF NOT fbDivProtect1.IsValid THEN
    // 触发报警:TimeConstant异常
    AlarmCode := 105;
END_IF;

优势:

  • 同一逻辑复用数十处,修改阈值只需改FB内部一处
  • IsValid 输出便于上层做故障诊断与冗余切换
  • 支持不同精度需求(通过输入Epsilon动态调整)

五、特殊场景强化策略

场景1:分母来自模拟量输入(AI模块)

传感器断线时,AI通道可能输出最大值(如16#7FFF)或固定故障码(如西门子SM1231的16#8000)。此时不能只判断 <> 0.0

// 西门子S7-1200示例:检测AI通道状态字
IF AI_Channel.StatusWord AND 16#0001 = 0 THEN // 位0=0表示无故障
    IF ABS(AI_Channel.Value) > 1.0e-3 THEN // 排除噪声
        Result := ProcessValue / AI_Channel.Value;
    ELSE
        Result := LastValidResult; // 保持上一次有效值
    END_IF;
ELSE
    Result := 0.0;
    Alarm_AI_Fault := TRUE;
END_IF;

场景2:需要除法结果参与安全逻辑(如SIL2系统)

IEC 61508要求安全功能失效概率≤1e-7/小时。此时必须双重保护:

// 主计算通道
IF ABS(Divisor) > EPSILON THEN
    MainResult := Numerator / Divisor;
    MainValid := TRUE;
ELSE
    MainValid := FALSE;
END_IF;

// 冗余校验通道(算法独立,如查表法近似)
RedundantResult := LookupTable_Approx(Numerator, Divisor);

// 交叉验证
IF MainValid AND ABS(MainResult - RedundantResult) < 0.1 THEN
    SafeOutput := MainResult;
ELSE
    SafeOutput := SAFETY_DEFAULT; // 如0.0或急停值
    TriggerSafetyEvent();
END_IF;

场景3:整数除法(DIV)与取模(MOD)保护

ST中整数除法 Numerator DIV Divisor 同样需防零:

IF Divisor <> 0 THEN
    Quotient := Numerator DIV Divisor;
    Remainder := Numerator MOD Divisor;
ELSE
    Quotient := 0;
    Remainder := Numerator;
END_IF;

注意:DIVMOD 对负数结果依赖PLC厂商实现(ISO标准未强制),务必查阅手册确认符号规则。


六、调试与验证方法

  1. 在线监控强制值
    在TIA Portal中,右键变量 Divisor → “强制值”,输入 0.0,观察 Result 是否保持原值/安全值,IsValid 是否置FALSE。

  2. 边界值测试表
    在测试用例中覆盖以下分母值:

Divisor 输入 期望行为 验证要点
0.0 跳过计算,Result=默认值 检查 IsValid=FALSE
1.0e-7 被判定为无效(< ε),同上 确认ε阈值生效
1.0e-5 允许计算,Result≈Numerator×1e5 检查无溢出(非INF)
-0.001 正常计算(负分母) 结果符号正确
  1. 编译器警告检查
    启用TIA Portal的“全部警告”级别,确保无 W023: Comparison of floating-point values may be unreliable 类警告——这提示你可能遗漏了ε判断。

七、反模式警示(必须避免的写法)

错误写法 风险分析 正确替代
IF Divisor THEN Result := ... Divisor 为REAL时,非零即真,但-0.0被误判为假(IEEE 754中-0.0 = 0.0IF -0.0 THEN为FALSE) 始终用 <> 0.0> EPSILON
IF Divisor <> 0 THEN ... 0 是INT,与REAL比较需隐式转换,部分旧版固件转换异常 统一用 0.0
Result := Numerator / MAX(1.0, ABS(Divisor)); 表面规避除零,但扭曲数学意义(如本该为100的结果变成100/1=100,但分母0.001时变成100/1=100,失去精度) 用条件分支,不篡改分母
在FC中返回未初始化Result 若IF条件不满足,Result保持上电初始值(可能为随机内存值) 必须在IF外赋默认值:<br>Result := 0.0;<br>IF ... THEN Result := ...; END_IF;

八、性能与资源考量

  • 执行时间:一次 ABS() + 一次浮点比较 + 条件跳转 ≈ 0.2μs(S7-1500),远低于典型扫描周期(10ms),无性能负担。
  • 代码体积:增加约4行ST代码,占用RAM可忽略。
  • 实时性:所有操作在单个扫描周期内完成,无延迟。

结论:保护带来的可靠性收益远超微乎其微的资源消耗。


九、行业规范引用

  • IEC 61131-3 Ed.3 (2013) 第3.3.3节明确:“浮点运算应考虑特殊值(INF, NaN)的传播,并在必要时提供防护逻辑。”
  • ISA-88 Part 1(批量控制)要求:“所有基于测量值的计算必须包含传感器有效性验证。”
  • 汽车OEM通用规范(如VW 80000)强制规定:“涉及除法的控制算法,分母有效性检查覆盖率必须100%。”

未实施保护即视为不符合功能安全基本要求。


十、终极检查清单(部署前必做)

在将含除法的ST代码下载至PLC前,逐项确认:

  • [ ] 分母变量声明为 REALLREAL,且初始化非零
  • [ ] 所有除法前均有 IF ABS(Divisor) > EPSILON THEN 结构
  • [ ] EPSILON 值经过工艺参数校核(非随意设 1e-10
  • [ ] ELSE 分支明确赋值(非留空)
  • [ ] 函数块调用时传入的 Epsilon 参数符合该处精度需求
  • [ ] 在线仿真中注入 Divisor=0.0,验证 Result 不突变、无 INF/NaN
  • [ ] HMI或上位机配置对应报警,当 IsValid=FALSE 时触发提示

完成全部勾选,方可投入运行。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文