在结构化文本(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.0、Result > 100.0),结果仍为 NaN 或 INF;更关键的是,多数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中最直接的保护结构,但细节决定成败:
-
比较操作符必须用
<>,不可用!=
IEC 61131-3标准中,<>是唯一合法的“不等于”符号。!=是C语言习惯,在ST编辑器(如TIA Portal、Codesys)中会报编译错误。 -
分母必须声明为浮点类型并初始化
错误写法: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;
- 显式转换:
-
0.0的写法必须带小数点
0是整数常量,0.0才是浮点常量。比较Divisor <> 0在部分PLC中会隐式转换,但存在风险(如某些固件版本对未声明类型的变量转换不一致)。始终使用0.0或0.0e0。 -
分支必须闭合,不可省略
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计数抖动)
- 压力变送器量程0–10 bar,精度0.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;
注意:DIV 和 MOD 对负数结果依赖PLC厂商实现(ISO标准未强制),务必查阅手册确认符号规则。
六、调试与验证方法
-
在线监控强制值
在TIA Portal中,右键变量Divisor→ “强制值”,输入0.0,观察Result是否保持原值/安全值,IsValid是否置FALSE。 -
边界值测试表
在测试用例中覆盖以下分母值:
| Divisor 输入 | 期望行为 | 验证要点 |
|---|---|---|
0.0 |
跳过计算,Result=默认值 | 检查 IsValid=FALSE |
1.0e-7 |
被判定为无效(< ε),同上 | 确认ε阈值生效 |
1.0e-5 |
允许计算,Result≈Numerator×1e5 | 检查无溢出(非INF) |
-0.001 |
正常计算(负分母) | 结果符号正确 |
- 编译器警告检查
启用TIA Portal的“全部警告”级别,确保无W023: Comparison of floating-point values may be unreliable类警告——这提示你可能遗漏了ε判断。
七、反模式警示(必须避免的写法)
| 错误写法 | 风险分析 | 正确替代 |
|---|---|---|
IF Divisor THEN Result := ... |
Divisor 为REAL时,非零即真,但-0.0被误判为假(IEEE 754中-0.0 = 0.0但IF -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前,逐项确认:
- [ ] 分母变量声明为
REAL或LREAL,且初始化非零 - [ ] 所有除法前均有
IF ABS(Divisor) > EPSILON THEN结构 - [ ]
EPSILON值经过工艺参数校核(非随意设1e-10) - [ ]
ELSE分支明确赋值(非留空) - [ ] 函数块调用时传入的
Epsilon参数符合该处精度需求 - [ ] 在线仿真中注入
Divisor=0.0,验证Result不突变、无INF/NaN - [ ] HMI或上位机配置对应报警,当
IsValid=FALSE时触发提示
完成全部勾选,方可投入运行。

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