1. 项目背景与需求分析
在工业自动化控制领域,多电机协同工作场景非常普遍。以水处理厂为例,通常需要根据工艺需求启动多台水泵,但直接同时启动所有电机会对电网造成巨大冲击,同时也不利于设备寿命管理。传统解决方案是采用固定顺序启动,但这种方式无法均衡设备磨损。
我在某污水处理厂项目中就遇到过这个问题:12台污水提升泵需要根据水位变化动态启停,原先的固定顺序启动导致前几台泵机械密封过早磨损。于是我们决定开发一个基于运行时间排序的智能启动功能块,这正是本文要分享的内容。
2. 技术方案设计
2.1 核心算法选择
为什么选择冒泡排序而不是更高效的快速排序?在工控领域有几点特殊考量:
- 泵的数量通常不超过20台,O(n²)的时间复杂度完全可以接受
- 冒泡排序实现简单,SCL代码可读性强
- 每次扫描周期只需完成少量数据交换,对PLC扫描周期影响小
实际测试表明,对10台泵排序在S7-1200 PLC上仅增加约0.3ms扫描时间。
2.2 数据结构设计
采用索引数组(IndexArray)而非直接排序运行时间数组的优势:
- 保持原始运行时间数据不变,便于其他功能调用
- 只需交换整型索引值,比交换DINT类型的时间值效率更高
- 扩展性强,后续可轻松增加其他排序维度(如优先级)
3. 功能块实现详解
3.1 接口定义优化
原始代码的接口可以进一步优化为:
scl复制FUNCTION_BLOCK "FB_TimeSortStart"
VAR_INPUT
{Attribute 'S7_m_c' := 'true'}
TotalPumps : INT := 10; // 带掉电保持功能
StartPumps : INT;
RunTime : ARRAY[1..MAX_PUMPS] OF DINT;
Reset : BOOL := FALSE; // 新增复位功能
END_VAR
VAR_OUTPUT
StartFlags : ARRAY[1..MAX_PUMPS] OF BOOL;
SortComplete : BOOL;
END_VAR
VAR
CONSTANT
MAX_PUMPS : INT := 20; // 最大支持20台
END_VAR
//...其余变量同前
END_VAR
改进点:
- 升级为功能块(FB)便于状态保持
- 增加MAX_PUMPS常量方便修改
- 添加复位功能和完成标志
- 直接输出启动标志数组
3.2 排序算法实现技巧
实际项目中我优化过的冒泡排序实现:
scl复制// 带提前退出优化的冒泡排序
FOR i := 1 TO TotalPumps - 1 DO
bSwap := FALSE;
FOR j := 1 TO TotalPumps - i DO
IF RunTime[IndexArray[j]] > RunTime[IndexArray[j + 1]] THEN
temp := IndexArray[j];
IndexArray[j] := IndexArray[j + 1];
IndexArray[j + 1] := temp;
bSwap := TRUE;
END_IF;
END_FOR;
IF NOT bSwap THEN
EXIT; // 提前退出
END_IF;
END_FOR;
这个优化可以减少不必要的循环次数,实测在已排序情况下可减少40%执行时间。
4. 工程应用实践
4.1 典型应用场景
- 水泵站控制:根据液位信号自动启动运行时间最短的N台泵
- 空压机轮换:多台空压机按累计运行时间均衡负载
- 照明控制:厂房照明灯具按使用时间排序开启
4.2 实际项目参数设置
在某净水厂项目中的典型配置:
scl复制// 实例化功能块
#TimeSortStart_DB(
TotalPumps := 8, // 8台加压泵
StartPumps := #iStartCount, // 根据压力PID输出计算
RunTime := #aPumpRuntime,
Reset := #bReset
);
// 启动控制
IF #TimeSortStart_DB.SortComplete THEN
#aStartCmd := #TimeSortStart_DB.StartFlags;
END_IF;
4.3 性能优化建议
- 对于超过15台设备的情况,建议改用选择排序
- 运行时间建议每10分钟记录一次,而非持续更新
- 在OB35循环中断中调用,确保定时执行
5. 常见问题排查
5.1 问题现象:排序结果异常
可能原因及解决方案:
- 运行时间单位不一致:确保所有时间值同为秒或毫秒
- 数组越界:检查TotalPumps值是否小于等于数组上限
- 数据类型错误:RunTime数组必须为DINT类型
5.2 问题现象:功能块无输出
检查步骤:
- 确认SortComplete标志是否为TRUE
- 检查StartPumps值是否大于0
- 监控IndexArray数组是否完成排序
6. 功能扩展方向
6.1 多条件排序
扩展支持优先级+运行时间复合排序:
scl复制IF #aPriority[IndexArray[j]] <> #aPriority[IndexArray[j + 1]] THEN
bSwap := #aPriority[IndexArray[j]] > #aPriority[IndexArray[j + 1]];
ELSE
bSwap := RunTime[IndexArray[j]] > RunTime[IndexArray[j + 1]];
END_IF
6.2 停机排序功能
新增停机排序功能,优先停止运行时间长的设备:
scl复制IF #bStartMode THEN
// 正常启动排序
ELSE
// 反向排序停机
bSwap := RunTime[IndexArray[j]] < RunTime[IndexArray[j + 1]];
END_IF
6.3 可视化监控
在WinCC中增加排序过程可视化:
- 显示各设备运行时间柱状图
- 用不同颜色标注即将启动的设备
- 记录每次排序结果到数据库
在实际项目中应用这个功能块后,某污水厂的泵组运行时间差异从原来的300%降低到15%以内,设备维护周期显著延长。一个意外收获是,这个算法后来还被客户应用到空压机组的控制中,同样取得了很好的效果。