资讯动态

PLC泵轮换控制:基于SCL的累计运行时间均衡算法解析

发布时间:2026/9/20 18:53:14 来源:尧图企业网站定制
简介TIA博途水处理项目中开启累计运行时间最短泵的SCL程序示例是一份面向PLC工程师与自动化初学者的技术文档。资源聚焦多台水泵的均衡调度解决因固定启停导致设备磨损不均的工程问题。文档基于西门子TIA博途环境使用SCL结构化文本语言完整讲解如何统计每台泵的运行秒数、通过冒泡排序找出累计时间最短的设备并在满足启动条件时按设定数量优先开启。还覆盖故障泵自动补位、停止条件判断、OB1调用FB及仿真验证等关键环节。文档以docx格式呈现共1个文件压缩包大小1.02MB适合快速下载阅读。目前已有453人学习。读者可从中获得完整的泵组数据结构定义、1秒脉冲计时方法、排序算法实现思路及仿真实例能直接迁移到实际水处理或类似多设备轮换控制项目中有效均衡负载、延长设备寿命。1. 项目背景与需求分析水处理项目里泵的启停控制几乎是每天都要打交道的事。最近在TIA博途TIA Portal里写了一段SCL程序专门用来解决一个很典型的问题现场有多台同规格泵工艺要求每次只需要开一台但为了均衡磨损、避免某台泵长期运行而其他泵长期闲置必须自动选择“累计运行时间最短”的那台泵来启动。这个需求在自来水厂、污水提升泵站、循环水系统里非常常见。但“选择最短运行时间”听起来简单真正落地到PLC程序里会牵扯到几个实际问题什么叫“累计运行时间”从哪里来怎么存断电会不会丢选泵时要不要排除故障泵、检修泵、手动状态泵多台泵时间完全一样时怎么办会不会总选第一台程序放在FC里还是FB里数据怎么组织才不乱这篇文章就把我实际项目中的一套写法完整拆开讲SCL代码可以直接抄到博途里用也会把我在调试中踩过的坑和优化思路一并说明。1.1 为什么要写这样一段程序很多小项目最初的写法是“泵1开8小时切换到泵2开8小时”这是定时轮换粗暴但有效。但它有个明显问题如果某台泵因为工艺原因频繁启停或者某段时间负荷特别大运行时间会快速增长——定时轮换根本不管这个时间长了就会出现“一台泵已经跑了上万小时另一台泵才跑了几千小时”的严重不均衡。按累计运行时间最短来选泵本质上是把“轮换”从时间维度变成“状态维度”谁用得少这次就先用谁。这个策略特别适合恒压供水、污水处理进水提升这类多泵并联、单台即可满足工况的场景。所以这段程序的价值不只是“抄一段代码”而是帮你在项目里建立起一套可维护的设备轮换机制。1.2 项目实际的设备构成以我做的这套水处理系统为例现场配置了4台提升泵全部为同功率、同扬程的离心泵由变频器驱动。控制系统选用西门子S7-1200固件V4.5以上编程环境为TIA Portal V17程序语言主体为SCL。4台泵的控制分三种模式手动、自动、远程。手动模式下直接就地启停PLC只做状态监视自动模式下由液位/压力联锁自动启停远程模式下由上位机WinCC或第三方组态下发启停指令。选泵程序只在“自动”模式下生效——这一点至关重要否则操作员在手动模式开泵都会被程序干扰。2. 数据结构设计与程序整体思路写SCL和写LAD/FBD最大的区别在于SCL更接近高级语言数据结构设计直接影响程序的清晰度和可扩展性。我强烈建议在博途里用自定义数据类型UDT来组织每台泵的数据而不是建四个独立的DB块。这样以后从4台泵扩展到8台泵只需改循环边界程序主体一行不用动。2.1 定义泵的数据结构UDT打开博途在PLC数据类型里新建一个UDT命名为Pump_DataType内部包含以下变量变量名数据类型说明AutoModeBool自动模式允许标志来自转换开关FaultBool故障信号热继电器/变频器故障汇总RunningBool泵运行反馈接触器辅助触点或变频器运行信号ReadyBool就绪状态综合判断无故障、非检修、允许启动AccumRunTimeTime累计运行时间StartCountInt累计启动次数辅助排序用StartCmdBool启动指令输出StopCmdBool停止指令输出ManualRunningBool手动模式运行标志用于时间累计的硬逻辑把时间类型选为Time而不是Int秒是为了在WinCC上显示“xx天xx小时xx分”时不需额外换算。但注意S7-1200的Time类型最大值约24天8小时如果你的泵常年不间断运行建议用ULInt存储秒数或者用两个Int分别存高低字。我这个项目里泵不是连续运行所以Time够用。2.2 全局DB的结构新建全局DBHMI_PumpDB不设置优化访问取消勾选“优化的块访问”方便HMI直接读取。内部建立一个数组Pumps : ARRAY[1..4] OF Pump_DataType为什么用数组因为SCL里的FOR循环需要下标遍历。如果每个泵单独建变量你就得写四遍几乎一样的比较代码代码量翻四倍出错的概率也高得多。数组UDT是这类多设备均衡控制的标准解法。另外单独定义一个系统级变量PumpStartRequest : Bool // 来自液位或压力的自动启动请求 SelectedPumpIndex : Int // 最终选中泵的编号0表示没有选中 TimeCompareEnable : Bool // 比较使能在程序启动初期或检修后强制重新选择2.3 程序的总体框架我的做法是在OB1里周期调用三个FB按顺序执行时间累计块FB_Timer每秒累加正在运行的泵的运行时间。选泵逻辑块FB_Selection当有启动请求时遍历所有泵选时间最短的。命令输出块FB_Command把选中泵的StartCmd置位停止其他泵。这个分层结构有一个好处任何一个功能需要修改只需要动对应那一个FB不会牵连其他逻辑。比如你想把时间累计从每秒一次改成每十秒一次只动FB_Timer选泵逻辑完全不受影响。三个FB的接口最好都做在背景DB里生成多重背景也可以但新手不要一上来就用多重背景调试时层层嵌套反而看不清楚。我这个项目直接拉了三个独立背景DB简单直白。3. SCL代码实现与逐段讲解这一部分进入正题。下面给出的代码是我整理过的精简版去掉了一些HMI联锁和报警细节核心逻辑完整可以直接测试。3.1 运行时间累计程序FB_Timer这一段程序在OB1里应该被条件调用建议用一个1秒时基的定时器或者Cycle_Time为1秒的OB块。最简单的方式是先做一个1Hz的时钟#Clock1s : NOT #Clock1s; // 放在一个每100ms执行一次的程序段里取反5次即1s然后在FB_Timer中// 确保程序只在泵真实运行时累计时间 FOR #i : 1 TO 4 DO IF #Pumps[#i].Running OR #Pumps[#i].ManualRunning THEN // Time类型累加1秒直接加T#1S #Pumps[#i].AccumRunTime : #Pumps[#i].AccumRunTime T#1S; // 按天小时分钟显示更方便但存储仍是秒 END_IF; // 运行次数统计检测上升沿 IF #Pumps[#i].Running AND NOT #RunStatusOld[#i] THEN #Pumps[#i].StartCount : #Pumps[#i].StartCount 1; END_IF; #RunStatusOld[#i] : #Pumps[#i].Running; END_FOR;这里有个细节Running信号是泵启动完成后的反馈而不是命令输出。如果你用命令输出做累计会出现泵实际上没转起来但时间照样在涨的假数据后期统计完全失真。我见过好几个项目栽在这里。关于掉电保持AccumRunTime必须存放在掉电保持区。在优化访问的DB里右键变量属性 → 保持性选择“保持”。或者直接在非优化DB里编译后勾选保持性。S7-1200掉电后默认保持性变量会写入保持存储器但如果你的DB是“优化的块访问”必须在变量属性里显式设置。千万别忘了这个否则现场一断电累计时间全部清零整套选泵逻辑等于白做。3.2 选泵程序FB_Selection这是核心中的核心。思路是遍历所有泵把不满足条件的剔除然后比较累计时间找出最小值对应的泵号。注意这里必须同时考虑“泵现在没运行”的条件。为什么因为如果某台泵正在运行它显然不可能被再次选中但它的时间肯定也在涨你不排除它下一轮启动时可能误以为它在等待状态。判断条件AutoMode为True自动状态Fault为False无故障Running为False当前处于停止状态Ready为True就绪包括变频器远方状态等ManualRunning为False防止有人在就地手动开着程序又去选它。核心代码如下#SelectedPumpIndex : 0; #MinTime : T#49D; // 初值给一个很大的时间49天模拟Int溢出可根据需要调整 #MinStartCount : 32767; FOR #i : 1 TO 4 DO // 筛选条件自动、无故障、停止状态、就绪 IF #Pumps[#i].AutoMode AND NOT #Pumps[#i].Fault AND NOT #Pumps[#i].Running AND #Pumps[#i].Ready AND NOT #Pumps[#i].ManualRunning THEN // 第一优先级累计运行时间短者优先 IF #Pumps[#i].AccumRunTime #MinTime THEN #MinTime : #Pumps[#i].AccumRunTime; #SelectedPumpIndex : #i; #MinStartCount : #Pumps[#i].StartCount; ELSIF #Pumps[#i].AccumRunTime #MinTime THEN // 时间相同启动次数少者优先避免永远选第一台 IF #Pumps[#i].StartCount #MinStartCount THEN #MinStartCount : #Pumps[#i].StartCount; #SelectedPumpIndex : #i; END_IF; END_IF; END_IF; END_FOR; // 输出到全局变量供命令输出块使用 #PumpSelected : #SelectedPumpIndex;这一段代码里我故意做了“时间相同走StartCount二次排序”的处理。这是项目中最容易忽略的一点。4台新泵刚投运累计时间全是0如果只按时间比较PLC的FOR循环每次都是或结果每次都会选中下标最小的1号泵其他几台泵永远没有机会启动。加了StartCount后才真正实现了“谁启动次数少谁先上”的均衡。3.3 启动命令输出FB_Command选中泵之后还需要把启动指令给到对应的泵同时停止其他泵防止多台泵同时启动造成工艺超调。这里的逻辑分为两层第一层自动启动指令生成。用一个液位或压力的联锁信号作为启动请求PumpStartRequest当该信号有效并且当前没有泵在运行将SelectedPumpIndex传给输出。FOR #i : 1 TO 4 DO IF #PumpStartRequest TRUE AND #SelectedPumpIndex #i THEN #Pumps[#i].StartCmd : TRUE; ELSE #Pumps[#i].StartCmd : FALSE; END_IF; END_FOR;第二层自动停止其他泵。如果系统处于自动状态且当前运行的泵不是被选中的泵则给出停止信号。但在实际项目中不要直接无条件停泵——至少加一个“允许停止”条件防止因为扫描周期内PumpStartRequest有抖动而发生频繁启停。更稳妥的做法只有当被选泵已经运行成功Running反馈到位后才停止另一台泵。顺序是“先启新泵确认反馈后再停旧泵”。这段做联动切换时才需要本次只讲单台启动不展开。3.4 完整程序块调用OB1里的调用关系FB_Timer.TimerExecute(Clock1s : #Clock1s); FB_Selection.SelectionExecute(PumpStartRequest : 液位联锁信号, PumpSelected HMI_PumpDB.SelectedPumpIndex); FB_Command.CommandExecute(PumpSelected : HMI_PumpDB.SelectedPumpIndex);每个FB的INOUT接口都指向同一个HMI_PumpDB里的Pumps数组这样数据一致性好也便于HMI统一监视。4. 调试与现场问题排查代码写出来是一回事现场调好是另一回事。这部分讲一下我在这类项目上实际碰到的几个问题和解决办法供大家参考。4.1 泵的Running反馈抖动导致时间异常累计项目初期泵的接触器辅助触点用的是硬接点在启停瞬间存在触点抖动也就是几毫秒内的通断反复。如果时间累计的扫描周期恰好落在抖动区间会出现“泵明明还没启动运行时间却加了十几秒”的假数据。排查思路在FB_Timer里给Running信号增加延时确认例如持续3个扫描周期为TRUE才认定运行。也可以用定时器让反馈稳定后再计时。我的做法是增加一段滤波先用TON延时接通10ms再用TON断开延时300ms确保信号稳定后再参与逻辑运算。4.2 手动模式与自动模式切换后选泵错误系统刚送电时所有泵处于手动模式操作员可能就地手动启动了2号泵。此时如果切换自动模式FB_Selection会遍历四台泵把所有泵都视为“未运行”然后根据时间找出某台泵选中发出启动命令实际上2号泵还在手动运行现场就成了两台泵同时转。解决方案在判断条件里增加ManualRunning互锁也就是在3.2节代码中已经体现的NOT ManualRunning。同时还需要在AutoMode切至TRUE时强制执行一次全停清除历史启动命令。4.3 累计时间保持不变或异常跳变检查Time类型溢出问题。S7-1200的Time最大值约24天8小时如果泵连续运行超过这个值变量会回绕到负值导致所有比较逻辑全部错乱。我在项目中把时间改成秒数存储在ULInt中同时在HMI上用“秒转天数”的脚本显示。虽然UI稍微复杂一点但彻底规避了溢出。如果你不想改存储类型至少要在比较前做绝对值处理并在HMI显示时截取低两位不过这种方案容易在中间量上踩坑不建议。4.4 四台泵时间差太大时选泵策略失效如果系统已经运行了两年期间一直用定时轮换没有均衡策略四台泵的累计时间可能差异很大比如1号泵8000小时2号泵5000小时3号泵2000小时4号泵1500小时。此时选最短时间的泵是正确的但你会发现最短的那台泵一旦被选中运行一段时间后它的时间逐渐追上第二短的之后系统会立刻切换目标。这样可能导致泵频繁切换——每隔一两个小时就切一次太频繁了对接触器和变频器都不好。我的做法是加入“最小切换间隔”死区一旦选中某台泵至少运行N小时比如24小时后才允许下次重新选择。实现手段是增加一个运行计时器#LockPumpIndex : #SelectedPumpIndex; #LockTime : T#0MS; // 每次选中后LockTime开始累计 IF #LockPumpIndex 0 AND #Pumps[#LockPumpIndex].Running THEN #LockTime : #LockTime T#1S; END_IF; // 在FB_Selection中若LockTime T#24H直接维持原选择不执行新选择这套死区逻辑虽然简单但对延长电气设备寿命有明显效果。4.5 HMI显示与报表统计最后提一下HMI侧。SCL程序算出来的AccumRunTime是Time类型在WinCC里显示需要转换。博途自带的格式不支持直接显示“天:小时:分钟”我一般做两个Tag一个是原始Time值另一个是字符串转换结果。转换逻辑同样用SCL写一个FC不过这部分看项目预算如果上位机用的是第三方组态也可以直接在组态里做转换。5. 扩展思路与经验总结这套“选累计时间最短的泵”的逻辑不仅适用于泵也适用于压缩机、风机、搅拌机凡是多台设备并联且需要轮换场景的都能套用。你只需要把泵的UDT改成“设备UDT”把逻辑里的关键字换一下即可。另外值得注意的是选泵逻辑不应独立于工艺联锁。我在上面代码里只用了PumpStartRequest一个启动请求实际项目中它应该来自液位上限/压力下限/流量低等信号的与或组合。一旦液位达到停泵阈应无条件停止所有自动泵程序要另加一个高优先级停止逻辑。如果未来项目需要扩展到8台泵建议把ARRAY[1..4]改成ARRAY[1..8]FOR循环上限同步调整程序无需其他改动。这就是UDT数组结构的最大收益。再分享一个调试技巧在所有FB的INOUT接口上挂一个“模拟测试开关”。当它置位时程序不执行真实输出只计算并保存SelectedPumpIndex和中间比较结果。这样在离线仿真或点动测试时可以快速验证选泵算法是否正确而不会真的启停设备。这个开关也能在甲方验收演示时帮上大忙。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价