接手这套污水站自动化改造的时候,柜子里还是老式继电器逻辑,仪表信号靠硬接线拉到控制室,运行人员说“每天光看液位跑上跑下就够累了”。甲方提了几个硬条件:改造期间尽量不停产,自控系统要足够简单可靠,电工能看懂能维护,预算还不能太离谱。盘了一圈方案之后,我最终选了施耐德TM218系列PLC来开发整套污水处理程序,从工艺拆解、程序架构、功能块编写一直做到现场调试投运。
这篇文章准备把整套开发的思路和踩过的坑完整讲一遍,重点不是劝你买哪个品牌,而是分享污水项目里TM218程序怎么组织、模拟量怎么滤波、PID怎么调、Modbus轮询怎么排雷。如果你正在憋一个污水站自控项目,或者手里已经有一台TM218不知道怎么下手,这篇文章值得从头看完。
1. 为什么是TM218:先盘IO和数据量,再谈控制器选型
做污水自控项目有个比较容易犯的毛病:还没理清现场有多少个点,就先想着上大型PLC或者干脆推DCS。结果项目做下来,光编程软件授权、现场调试时间和技术门槛就把利润吃光了。所以我选硬件的习惯是反着来的——先把IO点数、通讯需求、控制复杂度盘清楚,再回头挑控制器,这样TM218的优势会非常明显。
1.1 待改造污水站的规模与IO清单
我当时面对的这个污水站,处理规模不算大,但麻雀虽小五脏俱全:进水区有粗细格栅、提升泵,生化段有曝气池、回流泵,后端有加药间、沉淀池、消毒渠和污泥脱水间。全部点位数盘下来大概是这个规模:
| 信号类型 | 数量 | 典型点位 |
|---|---|---|
| 数字量输入DI | 约42点 | 手自动旋钮、电机故障、格栅限位、液位开关 |
| 数字量输出DO | 约30点 | 接触器控制、格栅启停、电磁阀、报警灯 |
| 模拟量输入AI | 约18点 | 液位计4-20mA、DO仪、pH计、流量计 |
| 模拟量输出AO | 约8点 | 提升泵变频给定、鼓风机变频给定、加药泵给定 |
这个规模放在整个污水自控领域里属于典型的“中小型水站”。程序逻辑的复杂程度并不在于点位数多,而在于模拟量信号占比高、工艺联锁关系多、设备轮换要求细致。TM218这种经济型PLC的算力去处理这些数据绰绰有余,真正花钱的地方是模拟量扩展模块和抗干扰措施。
1.2 TM218与其他型号的对比与取舍
很多人容易混淆施耐德这代经济型PLC的定位。TM218属于Modicon家族,编程软件是SoMachine Basic(新版叫EcoStruxure Machine Expert Basic),它和M221定位接近,但TM218更贴近老Twido升级的场景,而M241/M251属于Machine Expert平台,功能更强但是编程方式完全不同。
| 型号 | 编程软件 | 程序容量 | 通讯能力 | 适合场景 |
|---|---|---|---|---|
| M218/TM218 | SoMachine Basic | 较小 | 串口Modbus RTU为主 | 中小型逻辑控制、模拟量比例调节 |
| M221 | SoMachine Basic | 稍大 | 串口+可选以太网 | 小型自动化系统 |
| M241 | Machine Expert | 大 | 以太网Modbus TCP | 设备联网、运动控制 |
| M251 | Machine Expert | 大 | 以太网为主 | 分布式IO、复杂通讯 |
当时也考虑过M241,最后否掉的原因是:这台污水站没有运动控制需求,上位机只需通过Modbus采集数据,TM218本体带串口再做简单扩展就够用了。更关键的是一线维护电工对SoMachine Basic的梯形图风格更熟悉,后续维护不容易抓瞎。
1.3 编程软件和固件版本必须先统一
这里有个细节特别提醒:TM218的SoMachine Basic软件版本和PLC固件版本一定要提前对齐。我见过有人拿新版软件写好程序,结果现场PLC固件偏老,下载时报错或者下载后部分指令运行异常。
所以项目启动时就要做三件事:一是确认软件版本统一用同一个版本号,团队内部不要一人一个版本;二是检查PLC固件版本,必要时用官方固件升级工具先升级再开发;三是把出厂默认IP地址和串口参数记录下来,防止后面调试时忘记密码或参数冲突。升级固件有风险,操作前务必确认整个项目里没有正在运行的设备,否则停电升级过程中设备和通讯中断很容易引发连锁反应。
2. 工艺段拆解:程序里的每段逻辑都能在池子里找到对应物
PLC程序不是凭空写的,每一段逻辑背后都对应水池里的一个工艺设备或者一个检测仪表。我把整个污水站的工艺拆成三段来设计程序:进水提升段、生化曝气段、加药消毒段,这样每段程序都有明确的“物理归属”,调试时也能快速定位问题。
2.1 提升泵与格栅:液位联锁、轮换和错峰启动
进水提升泵是污水站的第一道关口,控制逻辑的核心是液位联锁。我习惯设定三档液位:停泵液位0.3米、启泵液位1.2米、溢流报警液位3.0米。当液位低于停泵液位时,所有提升泵必须停止防止空转;液位超过启泵液位时,按轮换策略启动一台泵;如果液位继续上涨超过设定值,说明来水量大或者泵故障,直接联动启动第二台泵并触发报警。
水泵轮换这个逻辑看着简单,实际写的时候有个坑:单纯按“上次运行的是几号泵就切到另一台”来做,一旦其中一台泵故障,程序就不知道该选谁了。所以轮换功能块必须把“故障旁路”设计进去——优先选择运行时间短的可用泵,而不是死板地交替。还有一个重要细节是错峰启动:多台泵不能同时合闸,否则冲击电流会把变压器母线电压拉低,甚至导致变频器欠压跳闸。我通常在第二台泵启动前强制加入5秒延时。
格栅控制相对简单,一般按时间间隔运行:每隔15分钟运行5分钟,或者根据格栅前后液位差连续运行。程序里我加了“格栅持续运行超时报警”,避免渣多的时候格栅被卡住却没人发现。
2.2 曝气池溶解氧:连续调节PID和它的特殊脾气
生化段的鼓风机曝气控制是整台TM218程序里最值得花心思的部分。很多人以为溶解氧越高越好,实际上曝气量过大不仅浪费电能,还会使活性污泥过度氧化导致解体。DO仪的目标值一般工艺上给到1.5到2.5mg/L,程序要做的是让鼓风机频率自动跟在这个目标附近。
实际工程里我采用的是PID连续调节方式:DO仪4-20mA信号作为过程值,变频器给定作为输出。TM218在SoMachine Basic里提供PID功能块,但使用中有个很关键的量纲统一问题——仪表信号、工程量、百分比输出必须先统一到0到100%的范围内,否则PID参数完全没法整定。我习惯先做模拟量滤波处理,再做工程量转换,最后才送进PID功能块。
这里有一个经验:DO仪的响应滞后非常明显,探头浸在水里测量的是溶解氧在水中的扩散平衡,这个物理过程本身就慢。所以PID参数里微分基本不用,比例也不能太激进,积分时间要放到十分钟甚至更长的量级,否则鼓风机频率会来回大幅波动,进水池里看到的是溶解氧曲线像锯齿一样上下跳动。调试时先手动给定鼓风机频率,等溶解氧稳定在一个平台后再切自动,一边观察曲线一边逐步加大积分作用,这样整定效率最高。
2.3 加药消毒:前馈为主、反馈修正为辅
加药间的程序控制对象主要是PAC(聚合氯化铝)混凝剂和次氯酸钠消毒剂。这类控制的难点在于仪表反馈严重滞后——余氯仪和pH计的实际测量值比投加动作晚了几分钟,如果只依靠反馈PID,系统会一直震荡。
我的做法是采用“前馈+反馈”的组合结构:前馈部分由进水流量信号直接计算基础投加量,流量大了加药量跟着等比例增大;反馈部分用余氯或pH实测值和目标值的偏差做缓慢修正,并且给修正量加上限幅,防止大范围超调。这套逻辑看起来不复杂,但它同时解决了响应速度和稳态精度两个问题。
程序里我还会把加药泵的“冲程调节”和“频率调节”分开考虑。变频器负责流量粗调,计量泵冲程负责精度微调,PLC输出0到10V或4到20mA信号时一定要确认泵的输入接口类型,很多现场的加药泵本身自带PID功能,如果不把它的内部PID屏蔽掉,两套调节器互相较劲,输出的加药流量曲线会非常难看。
3. 程序骨架和核心模块:从任务划分到一段能跑的ST代码
工艺逻辑清楚了,接下来说程序本身怎么组织。TM218这种小型PLC没有特别复杂的实时任务调度,但SoMachine Basic提供了任务分段和程序段管理功能,合理规划会让程序的可读性和可维护性提升一个台阶,尤其是后面现场改了十几次之后,你会发现当初的规划有多重要。
3.1 任务划分与变量规划
我习惯把程序按功能分成六个程序段:安全联锁段、模拟量输入处理段、工艺控制段、PID调节段、报警处理段、通讯刷新段。注意并不是每个程序段都要用一个独立任务,而是在主循环里按顺序调用,但安全联锁段必须放在最前面,保证任何情况下急停、高液位禁止启动这类条件最先被执行。
变量规划方面,TM218支持符号变量和掉电保持区。我强烈建议不要在程序里到处直接写%MW软元件地址,原因很简单:后期插入一条指令会导致地址重排,排查起来非常费劲。我通常是一张IO映射表对应一组符号变量,比如“LV101_TANK1_LEVEL”对应液位计“LT-101”的工程量,这样程序读起来就和工艺流程完全一致。掉电保持区只放运行模式、泵累计运行时间、PID输出保持值这类必须断电记忆的数据,而模拟量瞬时值、中间计算值一概不放,保持区放太多会加速内部存储器损耗。
3.2 模拟量工程转换、滤波与断线诊断
污水站的模拟量信号质量普遍堪忧,探头长期泡在污泥里,信号波动大是常态。所以我把模拟量处理做成一个规范化模板,每个AI通道都由三段组成:原始码值转毫安、毫安转工程量、断线判断。
SoMachine Basic里读模拟量原始值通常是整数,扩展模块的量程可能是0到4095或者0到32767,这取决于模块型号和配置,所以程序里不要写死。我一般这样处理:
(* 假设原始值 rawValue 来自模拟量通道 *) mAValue := (rawValue * 16.0) / 32767.0 + 4.0; IF mAValue < 3.6 THEN wireBreak := TRUE; (* 信号断开或者低于有效范围 *) engValue := 0.0; ELSIF mAValue > 20.0 THEN engValue := 100.0; (* 超满量程,按故障处理 *) ELSE wireBreak := FALSE; engValue := (mAValue - 4.0) * (rangeHigh - rangeLow) / 16.0 + rangeLow; END_IF;为什么先转成毫安再转工程量?因为断线判断直接依赖电流值,4到20mA信号低于3.6mA基本可以断定线路断了或者仪表失电,这时候直接把对应设备的联锁带上故障状态,比等到工程量变成负数再处理要可靠得多。
滤波我采用一阶惯性滤波,公式很简单:本次滤波值等于上次滤波值加上一个滤波系数乘以本次原始值和上次值的差。滤波系数我习惯取0.2左右,DO仪这种慢响应信号可以再小一点,但流量计这种需要较快响应的信号不要滤波太深,否则调节系统会变得迟钝。注意:程序里所有模拟量滤波系数应该做成可调整变量,方便现场调试时根据实际信号波动情况调节。
3.3 水泵轮换功能块
水泵轮换是污水站最常见的逻辑,我用结构化文本风格写了一个功能块,核心思想是:记录当前运行泵号,累计运行时间到设定值就切换;当前泵故障时自动切换到另一台。
PUMP1_CMD := FALSE; PUMP2_CMD := FALSE; IF NOT ENABLE THEN CURRENT_PUMP := 1; RUN_HOURS := 0; ELSIF LEVEL >= PUMP_ON_LEVEL THEN IF RUN_HOURS >= SWITCH_INTERVAL THEN RUN_HOURS := 0; CURRENT_PUMP := 3 - CURRENT_PUMP; END_IF; IF CURRENT_PUMP = 1 AND NOT PUMP1_FAULT THEN PUMP1_CMD := TRUE; ELSIF CURRENT_PUMP = 2 AND NOT PUMP2_FAULT THEN PUMP2_CMD := TRUE; ELSE (* 当前泵故障,立即切换到另一台 *) CURRENT_PUMP := 3 - CURRENT_PUMP; RUN_HOURS := 0; END_IF; ELSIF LEVEL <= PUMP_OFF_LEVEL THEN PUMP1_CMD := FALSE; PUMP2_CMD := FALSE; RUN_HOURS := 0; END_IF;这里面有个小技巧:CURRENT_PUMP := 3 - CURRENT_PUMP在只有两台泵的场景下实现1和2之间的互相切换,比IF判断更简洁。运行时间累计不要用浮点数在主循环里累加,会积累误差,我一般用一个秒脉冲信号,每秒对整数计数器加一,满3600秒就是一个小时,再用运算指令把小时数累加到运行时间变量里。
实际变电站调试时还要注意一个细节:泵的启动命令发出后,接触器吸合需要时间,程序要在发出命令后延时几百毫秒再检测泵的运行反馈信号,否则会误报“启动失败”,这种假报警会让操作人员对系统失去信任。
3.4 报警自保持与首出记录
污水站程序的报警处理推荐采用“条件触发、自保持、人工确认复位”的模式。直接原因是:很多报警条件是波动的,比如液位断续超过警戒线几秒钟,如果报警输出跟着条件瞬间消失,运行人员还没走到控制柜前报警就没影子了,更没法排查原因。
自保持报警的实现逻辑是:报警条件满足时置位一个内部保持位,该保持位只有在“复位按钮”按下且报警条件已经不成立时才复位。这样报警一直保持到人工确认为止,符合污水站运维习惯。首出记录则更进一步:用几个特殊寄存器记录第一次触发的报警号,不会被后续报警覆盖,对事故分析非常有用。
4. TM218在污水调试中遇到的三个硬核问题
程序写完了,真正的考验才刚刚开始。现场调试阶段我遇到了三个比较有代表性的问题,一个是模拟量干扰,一个是Modbus通讯拥堵,还有一个是掉电恢复后的数据丢失。这些问题都不是程序语法层面的,而是工程环境下的综合问题,单独拿出来讲讲排查思路。
4.1 现场模拟量漂移:测稳定、读跳动,屏蔽问题排查全过程
生化池的DO仪信号进PLC之后,上位机显示的溶解氧数值在0.2到8.7mg/L之间无规律跳动,工艺人员一看这曲线直接摇头。我第一步先用万用表串在信号回路里测量,结果发现毫安值是稳定的,大约稳定在4.75mA,换算成溶解氧应该在0.5mg/L左右。这说明仪表本身输出的信号没有大问题,问题出在信号进PLC这一段的传输或模数转换环节。
第二步做通道测试:把PLC模拟量输入端子直接短接,程序读数应该在4毫安附近且稳定。实测确实稳定,说明模拟量模块本身没有问题。然后把万用表从中间断开,信号线重新接上,读数又开始跳动。这就基本锁定问题在信号线的抗干扰上。
检查发现信号电缆和变频器输出电缆走在同一个桥架里,屏蔽层只在仪表端接了地,PLC端悬空。我把信号线从桥架里单独抽出来穿管,屏蔽层改为PLC侧单端接地,仪表侧屏蔽层悬空(或者按仪表厂家要求接),再在PLC侧串入信号隔离器,最终读数稳定在0.48到0.55mg/L。后来我总结了污水站模拟量敷设的三条铁律:动力线和信号线必须分开走桥架;模拟量屏蔽电缆的屏蔽层只能在控制室侧单端接地;变频器比较多的时候,4-20mA信号直接加隔离器比什么滤波都省心。
4.2 Modbus轮询拥堵:一个站卡死全链路告警
这套系统里有十来台带Modbus RTU通讯的仪表,流量计、余氯仪、浊度仪、pH计全走串口总线。刚开始调试时发现一个奇怪现象:只要某一台仪表断电或者接线松动,整条总线上所有仪表的数据全部读不到,而且HMI上连着报一大堆“通讯失败”。
排查思路是这样的:先用串口调试工具把PLC断开,只留仪表在总线上,逐个读取能正常通讯。接回去之后再逐台恢复仪表供电,发现当接上第6台设备时,前面几台也开始响应超时。问题在于这台仪表的通讯参数(波特率或者校验方式)和总线配置不一致,它在总线上不断发送错误帧,把整个Modbus链路干扰了。因为Modbus RTU是半双工主从协议,从站收到错误请求后可能在错误的时间点回复数据,主站就会一直等或者收到乱码。
解决的措施分三层:第一层是重新核对所有仪表的通讯参数,确保波特率、数据位、校验位完全一致;第二层是在程序里给每次读写请求设置200毫秒超时,连续3次失败就把对应站标记为离线并跳过,不占用后续轮询时间;第三层是在HMI上做一个通讯状态画面,每个仪表的离线标志单独显示,避免一个站点掉线导致所有数据都显示为0。程序里的超时处理用了定时器和指令忙标志配合,在SoMachine Basic里把功能块写成带状态判断的形式,而不是一条指令死等。
4.3 掉电恢复与首次扫描:上电不等于初始化
现场调试阶段有一次做断电恢复试验,结果发现PLC重新上电后,清水池补水泵没有按照自动模式重新启动,而手动状态却恢复了。排查后发现我把“自动/手动模式”这个变量放在了非保持区,断电后变量值清零,程序默认回到了手动状态,自然没法自动启动。
这个问题非常典型。解决思路是:把所有“运行模式”“泵累计时间”“PID输出保持值”都映射到PLC的掉电保持区。TM218通过SoMachine Basic的设置界面可以指定保持区范围,我在项目里用了一个专门的变量分区,每个关键变量在定义时都明确是否保持。另外,程序里用了首次扫描逻辑来执行初始化:第一次扫描时把一些常量写入保持变量,比如把PID输出保持值强制为0,防止上电瞬间PID积分累计值导致设备猛动。
还有一个周围人经常忽略的细节:HMI和PLC同时上电时,HMI启动往往比PLC快,此时HMI读到的寄存器数据可能是0值,如果画面程序把“数据为0”直接解释成实际值,设备状态就会误显示。我的习惯是让PLC里做一个每秒加一的心跳变量,HMI判断这个心跳是否在变化,超过3秒不变化才显示通讯中断,绝不能用“读数为0”来判断仪表信号正常。这个习惯在污水项目里救了我好几次。
5. 从仿真验证到柜内测试,再到现场投运的完整清单
程序写完到现场以后,很多人急着合闸试设备,我理解这种心情,但经验告诉我,越急越容易出乱子。一套规范的项目交付流程应该是:离线模拟、柜内点对点测试、现场单机调试、空载联调、带载联动、工艺闭环,每一步都有明确的检查项。
5.1 离线模拟与柜内点对点测试的差异体验
SoMachine Basic自带模拟运行功能,可以在电脑上模拟PLC的扫描循环来验证内部逻辑。但它模拟不了硬接线,也模拟不了外部仪表的真实信号。所以我只是把离线模拟当成程序功能的“冒烟测试”:确认没有死循环、变量没有乱跳、各程序段能正常执行。
柜内点对点测试才是最花时间的环节。我的做法是准备一根短接线,专门用来短接数字量输入点;准备一个信号发生器,专门用来模拟4-20mA信号;输出点则直接用万用表量端子电压。每测一个点就在IO表上打一个勾,同时确认程序内部能看到对应的变量翻转。这里有个很容易踩的坑:柜内测试用的IO表和现场实际接线表不一致,图纸改了没同步。所以建议柜内测试阶段就拿着最终版的电气接线图来对,后面现场出问题少一半。
5.2 现场投运安全测试:必须花半天做的几项试验
单机调试完成后的空载联动和带载联动阶段,有几项安全测试必须认真做,这半天时间不能省:
- 急停回路测试:按下急停,所有输出点必须在规定时间内全部断开,这个按照规定频率测试多次。
- 高液位联锁测试:用信号发生器模拟液位超过报警值,确认对应的泵确实停止且报警面板亮灯。
- 模拟量断线保护测试:直接拔掉某台仪表的信号线,确认程序能检测断线并冻结相关设备动作,而不是出现虚构的0值。
- 断电恢复测试:运行状态下直接断掉PLC和所有设备电源,再恢复送电,观察每台设备能不能按照预期进入自动状态,电机是否错峰重启。
- 手动自动切换测试:确认在手动模式下不会触发自动启动命令,自动模式下不会被调试遗留的强制信号干扰。
这些试验结果要记录成表格并存档,后面运营阶段再遇到类似现象,整理问题时就有依据。
5.3 把这套程序沉淀成模板,下次复用只改参数
项目交付后,我把这套程序的框架整理成了内部模板。模板分三层:硬件配置层、数据映射层、工艺逻辑层。硬件配置层记录模块型号和地址分配;数据映射层把物理IO地址与工艺符号对应起来;工艺逻辑层则写成通用性很强的功能块,液位联锁值、PID参数、轮换时间间隔全部做成了变量。
这样做的最大好处是:下次做类似规模的污水处理项目时,不需要从零写程序,只需要复制模板工程,重新映射IO清单,再根据现场仪表量程修改参数。连调试时最花时间的PID整定,也可以把上次项目的初始参数作为起步值,现场微调即可。
我把PID参数、液位联锁定值、轮换时间等工艺参数全部做到HMI的“工艺参数”画面上,授权给运营人员调整。这样运维人员平时只需要看着画面修改数值,不需要碰梯形图,更不需要懂PLC程序。对一个长期运行的污水站来说,这比炫耀程序技巧实用得多。
最后分享一个这项目完成后的小体会:一套好的污水站PLC程序,不是功能块写得有多深奥,而是让运行人员觉得“这设备挺好用”。控制系统的价值不是替人做所有决定,而是把风险降到最低,把操作空间留给真正懂工艺的人。后续再写类似项目,我大概率还会选择TM218这个级别的控制器,但会根据污水站的规模适当增加以太网扩展模块,给上位机留出更宽的通讯入口。