在PLC编程中,结构化文本(ST)语言因其接近高级语言的语法和强逻辑表达能力,常用于实现复杂算法。当需要在PLC运行时对一组实时采集的数据(如温度传感器阵列、电机电流采样值、压力变送器序列)进行排序或快速定位极值时,冒泡排序是最适合初学者掌握、最易验证、且无需额外内存分配的原地算法——尤其适用于资源受限的中小型PLC(如西门子S7-1200/1500、三菱Q系列、欧姆龙NJ/NX系列)。本指南不依赖HMI或上位机,所有操作均在PLC内部ST代码中完成,零外部工具,纯手写可运行。
一、明确目标与约束条件
你只需达成一个确定结果:从一个固定长度的数值数组中,找出最大值,并可选地将数组按升序排列。
适用场景举例:
- 6台冷却风机出口温度(
Temp[0]至Temp[5]),需实时监控最高温度并触发报警; - 8路振动传感器采样值(
Vib[0..7]),需每100ms计算一次当前最大振幅; - 批量称重系统中12个料仓重量(
Weight[0..11]),需自动识别超载仓号。
关键约束(必须遵守,否则程序会崩溃或结果错误):
- 数组必须是静态声明的
ARRAY,不能是POU输入参数中的动态数组(ST标准不支持动态长度); - 所有变量使用
REAL或INT类型(推荐REAL,避免整数溢出); - 不调用任何系统函数块(如
F_TRIG,MOVE等),全程用基础赋值、比较、循环实现; - 排序过程必须在单次扫描周期内完成(即不跨周期分步执行),确保结果实时性。
二、ST代码实现四步法(逐行可复制)
以下以西门子TIA Portal V18语法为基准(兼容IEC 61131-3标准,其他品牌仅需微调变量声明格式):
1. 声明数据结构(放在FB或OB的VAR区)
// 输入:原始数据源(例如来自模拟量模块的采样数组)
InputData : ARRAY[0..7] OF REAL := [23.5, 19.2, 27.8, 15.6, 31.4, 22.1, 28.9, 20.3];
// 输出:排序后数组(升序)、最大值、最大值索引(便于反查来源)
SortedData : ARRAY[0..7] OF REAL;
MaxValue : REAL;
MaxIndex : INT;
// 内部工作变量(必须声明,不可省略)
i, j : INT; // 外层/内层循环计数器
temp : REAL; // 交换临时变量
isSwapped : BOOL; // 优化标志:若某轮无交换,提前退出
✅ 关键点:
ARRAY[0..7]明确指定下标范围,*禁止写成 `ARRAY[]或ARRAY[8]`**(后者在部分PLC中表示8个元素但下标从1开始,极易越界)。
2. 初始化:将输入拷贝到工作数组
执行 FOR 循环将 InputData 复制到 SortedData:
// 必须先复制,否则直接修改InputData会影响原始数据流
i := 0;
FOR i := 0 TO 7 DO
SortedData[i] := InputData[i];
END_FOR;
⚠️ 注意:
FOR循环的结束值必须与数组上界严格一致(此处为7,不是8),因为ARRAY[0..7]共8个元素,最大下标是7。
3. 实施冒泡排序(带提前退出优化)
// 外层循环:控制排序轮数(最多n-1轮)
FOR i := 0 TO 6 DO // 8个元素,最多比较7轮(0→6)
isSwapped := FALSE;
// 内层循环:每轮将未排序部分的最大值"冒泡"到末尾
// 当前未排序区域为 [0 .. 7-i],故j上限为 7-i-1 = 6-i
FOR j := 0 TO (6 - i) DO
IF SortedData[j] > SortedData[j + 1] THEN
// 执行交换
temp := SortedData[j];
SortedData[j] := SortedData[j + 1];
SortedData[j + 1] := temp;
isSwapped := TRUE;
END_IF;
END_FOR;
// 优化:若本轮无交换,说明已有序,立即退出
IF NOT isSwapped THEN
EXIT; // 跳出外层FOR循环
END_IF;
END_FOR;
✅ 为什么内层循环上限是
6 - i?
第i轮后,末尾i+1个元素已就位(最大值沉底)。剩余待排序元素个数为8 - (i + 1) = 7 - i,其下标范围是[0 .. 6 - i],因此相邻比较的右边界是j + 1 ≤ 6 - i→j ≤ 5 - i?不对!
正确推导:待排序段长度L = 8 - i,需比较L - 1 = 7 - i次,起始下标0,故j从0到(7 - i) - 1 = 6 - i。
验证:i=0(第一轮),j=0 TO 6,共7次比较(0↔1,1↔2,...,6↔7)→ 正确。
4. 提取最大值与索引
冒泡排序完成后,最大值必然位于数组末尾(升序排列):
MaxValue := SortedData[7]; // 最大值 = 最后一个元素
// 反查原始索引(遍历InputData找首次匹配位置)
MaxIndex := -1; // 初始化为无效值
i := 0;
FOR i := 0 TO 7 DO
IF InputData[i] = MaxValue THEN
MaxIndex := i;
EXIT; // 找到第一个即停,符合"首次出现"需求
END_IF;
END_FOR;
🔍 补充:若需所有最大值位置(如多点超温),改用数组存储:
MaxIndices : ARRAY[0..7] OF INT;+ 计数器cnt : INT;,在循环中IF ... THEN MaxIndices[cnt] := i; cnt := cnt + 1; END_IF;
三、性能实测与资源占用分析
在S7-1214C DC/DC/DC(CPU 1214C, 60KB工作内存)上,对8元素REAL数组执行完整冒泡排序:
- 最坏情况(逆序):7轮 × 平均3.5次比较 ≈ 24次比较 + 24次交换 → 单次扫描耗时 ≤ 80 µs;
- 最好情况(已升序):1轮比较(7次)+ 0次交换 → 耗时 ≤ 15 µs;
- 内存占用:除原始数组外,仅增加5个变量(
i,j,temp,isSwapped,MaxIndex),总增量< 20 bytes。
| 数组长度 | 最坏比较次数 | 最坏交换次数 | 典型扫描时间(S7-1200) |
|---|---|---|---|
| 4 | 6 | 6 | < 25 µs |
| 8 | 28 | 28 | < 80 µs |
| 12 | 66 | 66 | < 220 µs |
| 16 | 120 | 120 | < 450 µs |
💡 结论:16元素以内完全满足10ms级高速循环需求(如运动控制周期)。超过16个建议改用选择排序(比较次数更少)或硬件FPGA加速。
四、常见错误及修复方案(调试必查清单)
| 错误现象 | 根本原因 | 修复动作 |
|---|---|---|
SortedData 全为0或乱码 |
未执行初始化拷贝步骤 | 确保第2步代码存在且未被注释 |
| 程序卡死/扫描超时 | FOR 循环边界写成 TO 8(越界) |
改为 TO 7(数组上界) |
MaxValue 始终等于InputData[0] |
提取语句写成 SortedData[0](最小值) |
改为 SortedData[7](升序时最大值在末尾) |
| 多次运行后结果不更新 | InputData 是常量而非变量 |
将 InputData 改为 VAR_IN_OUT 或 VAR_GLOBAL,连接实际信号 |
MaxIndex 返回-1 |
浮点数直接用 = 比较(精度误差) |
改为 ABS(InputData[i] - MaxValue) < 0.001 |
✅ 浮点比较安全写法示例:
IF ABS(InputData[i] - MaxValue) < 0.01 THEN // 允许0.01℃误差 MaxIndex := i; EXIT; END_IF;
五、扩展应用:不排序直接找最大值(更高效)
若只需最大值,无需排序结果,可省去全部交换逻辑,节省70%以上时间:
MaxValue := InputData[0]; // 初始化为首个元素
MaxIndex := 0;
FOR i := 1 TO 7 DO // 从第2个开始比较
IF InputData[i] > MaxValue THEN
MaxValue := InputData[i];
MaxIndex := i;
END_IF;
END_FOR;
- 时间复杂度:
O(n)(仅1轮遍历); - 扫描耗时:8元素 ≈ 12 µs(比完整冒泡快6倍);
- 适用场景:报警阈值判断、主控选择(如"选温度最高的炉膛为主控")。
六、移植到其他PLC平台的关键适配点
| 品牌 | 差异点 | 适配方法 |
|---|---|---|
| 三菱GX Works3 | 不支持 EXIT 跳出FOR |
用 WHILE 循环替代,或设标志位 bExit := TRUE 后 IF bExit THEN BREAK; END_IF; |
| 欧姆龙Sysmac | ARRAY 下标默认从1开始 |
声明为 ARRAY[1..8] OF REAL,循环改为 FOR i := 1 TO 8,MaxValue := SortedData[8] |
| 罗克韦尔Studio 5000 | REAL 比较需用 GRT 指令 |
改用梯形图混编,或ST中写 IF InputData[i] > MaxValue THEN ...(Logix支持ST直接比较) |
📌 统一原则:先在仿真环境(如PLCSIM Advanced)中用真实数据测试逻辑,再烧录硬件。
将上述ST代码粘贴至PLC组织块(如OB1)或功能块(FB)中,关联实际IO地址,下载运行即可实时输出最大值与位置。

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