做泸州这一带的工控项目,我打交道最多的就是酒厂灌装线、化工厂的辅机控制、非标装配设备这些场景。说实话,很多同行梯形图画得很溜,什么自锁互锁、定时器计数器,信手拈来。但你要是问他PLC结构化编程怎么落地,十有八九会回你一句“能跑就行”。可现实是,程序越来越大,客户改需求越来越勤,今天加一个延时,明天换一个动作顺序,伸手去改那些上千行的老梯形图时,你就知道什么叫“牵一发动全身”。这篇帖子就专门聊聊PLC结构化编程的核心实现方法,不讲虚的,直接讲我在实际项目里怎么拆程序、怎么写功能块、怎么处理顺启逆停、联锁互锁、PID这些经典逻辑,也会把下载通讯、现场调试踩过的坑一并倒出来。适合谁看?刚入门想进阶的PLC新手、天天跑非标现场的调试工程师,以及被老代码折磨得想重构的设备维护电工。
1. 结构化编程究竟解决什么问题
1.1 先说说那些“能跑但不敢动”的老程序
我接过一个包装设备的改造项目,原程序是别人用梯形图写的,所有逻辑全塞在一个主程序里,从第一行到最后一千多行,中间穿插着二三十个定时器。客户提了个小需求:某两个动作之间加3秒延时。我打开程序一看,这个定时器被三处地方引用,复位的条件又分散在另外两个网段里。我盯着交叉引用看了半个小时,最后只能小心翼翼地在原逻辑上再叠一层条件。程序是跑起来了,但我心里很明白,这团乱麻下个月再改一次,可能就彻底理不清了。
这就是没有结构化编程的典型症状。所谓结构化,说白了就是让程序的每一块逻辑都有清晰的边界,每个功能都封装成独立的模块,数据流是可见的,而不是把所有东西焊死在一个大网段里。梯形图不是不能用,但你要把梯形图当成“函数内部的具体实现”,而不是当成整个程序的全部。
1.2 结构化不是文件多,而是边界清楚
有些初学者以为结构化编程就是把程序分成好几个程序块,各自写一段完事。这个理解太浅了。真正的结构化是三层分离:
第一层是任务层,也就是PLC扫描周期里哪些程序块被调用、以什么顺序调用,这是大脑;第二层是逻辑层,每个设备域或者每个工艺步骤对应一个功能块、函数或程序,这是手脚;第三层是数据层,全局变量、共享数据块、背景数据块各司其职,这是神经系统。
我做一个项目,最先画出来的不是梯形图,而是一张功能清单。把设备分成几个域:润滑系统、主轴系统、冷却系统、报警系统。然后每个域分配一个功能块,域与域之间的交互只通过明确的接口变量实现,内部怎么改不影响外部。
这样做的直接好处是:今天客户改了主轴启动延时,我只需要打开主轴那个功能块,改里面的TON定时器参数,其他域的逻辑碰都不碰。交叉引用干干净净,排查问题的时候打开“调用结构”一看就知道谁在调用谁。
1.3 你的施工现场适不适合结构化
我遇到过不少维修电工,他们觉得结构化是工程师“装样子”,自己用梯形图照样修设备。但我说句实话,如果你的设备只有一台电机加两个限位,那确实随便怎么写都行。可只要设备动作超过五六个,顺序控制里面有分支条件,又牵扯到变频器、软启动器、多台电机联锁,你不做结构化,调试的时候就是灾难。
非标项目尤其如此,因为非标设备的工艺经常变。客户今天说润滑电机先跑,明天说主轴和润滑一起跑。结构化编程的核心优势就是让这种改动从“改一整片逻辑”变成“改一个参数”。这个优势在调试现场会放大十倍。
2. 程序块设计与变量规划
2.1 FB、FC、程序块到底怎么选
IEC 61131-3里规定了几种程序组织单元,我们日常用最多的就是功能块FB、函数FC和程序PRG。很多新手分不清,我打个比方。
FC(函数)就像一个计算器,你给它两个数,它给你一个结果,中间不记任何账。典型用途是模拟量工程量换算、数值比较这些纯粹的计算逻辑。
FB(功能块)不一样,它自带记忆。你调用一个电机控制FB,这个FB内部记住了电机当前是运行还是停止,记住了累计运行时间。每次调用这个FB,PLC都会给它分配一个专门存数据的背景数据块,就像每个工人进车间领一个属于自己的工具箱,工具用完放回自己柜子,互不干扰。电机启停控制、PID调节、顺序控制步进器,都应该做成FB。
PRG(程序)就是任务层执行的主干,它负责调用各个FB,决定调用顺序。我习惯在PRG里只做“调用”和“数据交换”,不写具体工艺。工艺逻辑全部下放到FB里,PRG干净得像一张排班表。
2.2 命名规范,是写给三个月后的自己看的
代码写得好不好,不看当时多聪明,看三个月后你还能不能看懂。我刚入门的时候给变量起名也是随心所欲,什么A1、B2、T1,到了调试现场报警了,对着触摸屏上的“A1报警”一脸懵。后来我痛定思痛,给自己定了一套命名规范,一直用到现在。
变量前缀统一加数据类型标识,DI开头是数字量输入,DO开头是数字量输出,AI是模拟量输入,AO是模拟量输出,V是内部变量,FB是功能块实例名。然后紧跟设备名称和信号含义,比如DI_Pump1_Run表示1号泵的运行反馈,DO_Pump1_Start是1号泵的启动命令。
功能块实例命名用“FB_设备名_序号”,比如FB_Motor_Lube是润滑电机控制块。交叉引用列表按前缀一过滤,整台设备的输入输出一目了然,不用在几百个变量名里大海捞针。
2.3 用结构体把设备打包成一个抽屉
做非标项目时,设备往往有规律性。三台软启动电机,参数和IO结构几乎一样;六台输送辊道,每台都有运行反馈、故障信号、启动命令。如果每个信号都单独建一个变量,程序会膨胀得厉害。
我习惯用STRUCT结构体把设备打包。比如建一个电机数据类型Motor_Type,里面包含启动命令、停止命令、运行反馈、故障信号、电流值、启动延时时间、停止延时时间。然后在全局变量表里声明M1、M2、M3都是Motor_Type类型。这样调用电机控制FB的时候,FB的接口直接对应Motor_Type里的各个元素,代码简洁到像在读说明书。
模拟量也是一样,AI_Temp_Type结构体里放原始值、工程量上限、工程量下限、滤波系数、当前值。程序里引用某个温度点,直接写AI_Temp_BoilWork.Current,含义清清楚楚。结构化编程到这一步,基本算是从“画电路图”进化到“写软件架构”了。
3. 核心逻辑的结构化实现方法
3.1 用一个总允许条件做全局联锁
老式梯形图里最常见的问题,就是每个运动设备各自写一堆互锁触点,这个阀没关那个泵就不能开,那个泵没停这个阀就不能关。条件分散在全程序各个角落,现场改一个传感器,你根本不知道它影响了多少设备。
我现在的做法是做一个“全局允许”条件块。急停按钮状态、安全门开关、液压站压力正常、气压正常、变频器无故障,这些信号汇总到一个变量里,叫RunEnable。所有运动FB在输出动作之前,第一件事就检查RunEnable。任何一个安全条件不满足,整台设备的运动输出全部被封锁,没有任何死角。
这个汇总逻辑我放在一个专用的FC里,输入是所有安全信号,输出是RunEnable。谁需要看这个条件,直接引用接口,不用关心内部是怎么组合的。新加一个安全条件,只需要改FC内部的一行逻辑,全设备联动生效。
3.2 顺启逆停用状态机拆解
来自热词里的经典问题:“润滑电动机开始运行,3s后主轴电机运行;系统停止,主轴电机先停,4s后润滑电动机停止”。这种顺序控制在老程序里通常写成无数个定时器和辅助继电器的组合,而且还容易出问题:如果主轴还没停,润滑就停了,或者中途急停之后整个时序错乱。
结构化做法是定义一个状态机。状态有四个:空闲S_IDLE、润滑运行S_LUB、主轴延时启动S_DELAY、全速运行S_RUN。用WORD或者枚举类型保存当前所在状态。启动按钮按下去,状态从S_IDLE跳到S_LUB,同时启动TON定时器3秒。定时器到点,状态进入S_RUN。停止按钮按下去,状态从S_RUN回到S_DELAY(此时主轴停,润滑保持),再启动一个4秒的TON。定时器到点,状态回到S_IDLE。
主轴电机的输出等于当前状态在S_RUN或S_DELAY区间;润滑电机的输出等于当前状态不等于S_IDLE。这样不管中间怎么跳变,状态机的输出永远和状态一致,不存在梯形图里“线圈被重复驱动”的问题。中间来了急停,直接把状态强制回S_IDLE,所有输出统一复位,绝对不会出现半吊子状态。
3.3 定时器链替代满天飞的定时器
老程序里定时器一多,管理就是噩梦。而且同一个定时器被多个条件复位,很难查出是谁在搞鬼。结构化编程里,我习惯把顺序控制里的定时器收敛到状态机内部,甚至收敛到FB内部。
比如一拖三软启动器场景,三个电机只能有一个投入运行,且电机切换需要先停止原电机,等待软启动器内部放电完,再启动目标电机。这个逻辑我封装成一个软启动管理FB,内部维护三个电机的状态位,以及一个间隔延时调整定时器。切换时先复位原启动命令,定时5秒,5秒后置位新目标启动命令。外部逻辑只需要告诉这个FB“我要切换到几号泵”,剩下的时序全部由FB内部完成。
这种做法的好处是:现场客户说“间隔时间太长了,改成3秒”,我只需要改FB内部TON的PT值。三台电机改一台和改三台一样,一个参数搞定。这就是结构化的核心红利。
3.4 PID控制温差大波动大的排查思路
热词里“PLC温度PID波动温差大如何调节”也是个高频问题。PID控制我同样建议封装成FB,里面包含设定值、反馈值、输出值、比例系数、积分时间、微分时间、手动自动切换、输出上限下限。这样每个温控回路都是一个独立实例,面板操作员可以在触摸屏上在线调参。
实际遇到温差波动大的情况,我基本按这四步排查:第一步看传感器采样位置,是不是离加热源太近,或者探头碰到容器壁了,反馈本身就有假波动;第二步看执行机构,阀门或者加热器有没有卡涩、回差是不是过大,输出往下调一点,温度没反应,再调一点突然过冲,这种多半是执行机构问题;第三步把PID参数降回来,先设成纯比例控制,慢慢加积分,微分在温度系统里我很少用,因为温度惯性大,微分容易把噪音放大;第四步检查扫描周期和输出周期是否匹配,温度回路不需要每个扫描周期都执行PID运算,可以间隔几百毫秒跑一次。
这里有一个很常见的坑:模拟量滤波不当。工程师看到温度波动大,第一时间加滤波系数,结果滤波时间常数太大,反馈值被磨平了,PID看到的是一个严重滞后的信号,于是输出越调越猛,温差更大。我在FB里加了两个滤波档位,一档是原始值,一档是滤波值,调试时可以分别监视,到底波动是真波动还是假波动,一目了然。
3.5 报警与故障处理也走结构化
做一个完整的设备控制程序,报警逻辑往往占了一半以上。老程序里报警就是每个故障点一个线圈,触摸屏上一个个点位去关联,改起来烦死。
结构化之后,我把报警分成两级。第一级是安全级报警,急停、电机过载、安全门打开,这类报警直接参与联锁,任何情况下都能切断输出。第二级是提示级报警,比如某个温度过高、某个液位偏低、通信中断,提示操作员注意,但不立刻停机。
我用一个报警管理FB,接口传入报警编号、报警文本、当前信号状态、报警级别。FB内部负责上升沿捕捉、报警锁定、复位逻辑。这样梯形图里只需要把每个故障信号接到FB的管脚上,不用每个报警都画一段自锁。触摸屏那边需要报警列表,直接映射FB里的报警字。
4. 下载、通讯和在线调试里的坑
4.1 台达PLC下载程序的适配细节
有热词问“台达PLC怎么下载程序”,其实台达的软件分两类,老款用WPLSoft,新款(比如DVP-ES3系列)用ISPSoft。第一次下载程序最容易被卡住的点,不是软件操作,而是通讯参数不匹配。
我用ISPSoft下载,先看通讯设置里有没有选对COM口或者网口。用USB转串口线时,电脑设备管理器里显示的COM端口号必须和软件里选的一致,驱动没装好最常见的现象就是软件提示“无法连接PLC”。然后看PLC站号,默认站号是1,软件里站点设置也得是1。如果用网口通讯下载,IP地址必须和PLC设置在同一网段,比如PLC是192.168.1.10,电脑就得配192.168.1.x。
还有一个小技巧:有些台达PLC程序下载前必须把PLC切到STOP状态,下载完再切回RUN。软件里有“RUN后自动下载”的选项,勾上之后程序会自动切换,但前提是PLC的RUN/STOP开关没有硬性掰到STOP档位。以前有同行在配电柜里掰着开关下载,怎么都连不上,最后发现是自己把STOP档位锁死了,这类低级错误其实很常见。
4.2 InoProShop怎么设置PLC端口号,AMS NetId是什么
汇川、信捷有很多型号用的是Codesys内核,编程软件叫InoProShop或者类似IDE。这类软件里“设置PLC端口号”跟传统PLC不一样,它默认监听端口是11740,但这玩意儿一般不用动,你真正要配的是网络连接和AMSNetId。
热词里说“建立连接需要目标PLC的AMSNetId(6字节网络标识符)和端口号”,很多人卡在这一步,因为找不到这个ID在哪里。拿InoProShop举例,你新建或者打开工程后,在左侧设备树里找到PLC设备,右键属性,弹出来的对话框中有一个“网络”或者“通讯”相关页签,里面会显示一个类似“192.168.1.10.1.1”的六段地址,这就是AMSNetId。前四段是IP地址,后两段是设备在三层路由里的标识。
我经常遇到的情况是:软件扫描网络找不到PLC,但我知道它的IP。这时候不用扫描,直接在连接配置里手动填IP和AMSNetId。前提是电脑能ping通PLC,如果ping不通,检查网线、IP、防火墙,还有一个容易被忽略的是Windows防火墙拦截了Codesys的通讯端口。把防火墙里对应软件的入站规则放行,多半就能连上了。
4.3 西门子S7-200 SMART搜索不到CPU的排除思路
热词里“STEP 7 Micro/WIN SMART连接PLC后搜索找不到CPU,通过添加IP地址可以连接上”是个超典型问题。S7-200 SMART用Micro/WIN SMART软件时,点“查找CPU”按钮扫描不到,但手动进去填IP却能连,说明PLC本身没问题,问题出在搜索通道或者网卡配置上。
排查顺序:第一步,电脑网卡IP地址要和PLC同一个网段,PLC默认是192.168.1.20之类的,电脑设成192.168.1.x。第二步,PG/PC接口选择正确,Micro/WIN SMART顶部下拉框选TCP/IP对应的网卡,别选成虚拟网卡或者别的。第三步,检查防火墙,很多杀毒软件会把西门子的扫描广播包拦掉,通信不上就会扫描失败。第四步,试试软件菜单里的“通讯”界面,点“查找CPU”之前先把“远程”勾选上,不然扫描范围可能只局限当前网段。
从实际经验看,90%的搜索不到都是因为电脑上装了多个网卡,Micro/WIN SMART默认选错了网卡在扫描,搜了个寂寞。手动添加IP能连上,原理就是绕过了自动扫描这个过程,直接走TCP点对点连。
4.4 博途与模拟屏不兼容、PLCSIM Advanced密码报错
热词里还有一个“博途PLC与模拟屏不兼容”。这个要看具体现象:如果是TIA Portal组态的PLC,和威纶通、昆仑通态这类第三方屏幕通过S7协议通信时偶尔会有兼容问题,常见原因是屏幕固件里的S7协议版本太老,或者PLC侧没有开放PUT/GET通信。
我用博途给第三方屏幕做S7通信时,习惯在PLC的防护与安全设置里把“允许来自远程对象的PUT/GET通信访问”勾上。如果不勾,第三方屏作为客户端直接读不到PLC数据。屏幕侧需要正确填写PLC的IP地址、机架号、插槽号,S7-1200默认是0号机架1号插槽,S7-1500是0号机架1号插槽还是2号插槽要看一下具体型号。如果屏幕型号实在太老,就得换协议,比如用Modbus TCP转发,或者升级屏幕固件。
“S7-PLCSIM Advanced下载程序在线检查保护机密PLC组态数据的密码时出错”,这个我一直记着。现象明明就是本地仿真,还要密码,其实是因为原项目在PLC的防护设置里激活了块保护或者组态保护。PLCSIM Advanced模拟的是一个虚拟PLC,它读取了你的保护设置,下载时会去校验密码。解决方法是:回到博途项目,在PLC属性里检查“防护与安全”,要么取消块保护,要么设置一个访问级别为完全访问且无密码的配置,然后重新编译硬件配置再下载。仿真器里的PLC型号和版本也要和真实目标型号严格对应,我曾经用版本不一致的仿真器拉一个V4.5的项目,各种莫名报错,换成匹配版本就好了。
5. 非标项目从下载程序到稳定运行的调试清单
5.1 上电前的检查环节,别急着下载
每次做非标设备调试,甲方越催,越容易跳过检查直接上电。我吃过教训,现在给自己定了一条铁律:先静态查线再上电,先检查IO再下载程序。具体清单我用表格固定在调试记录本里,项目再急也不省这一步。
| 序号 | 检查项 | 操作要点 |
|---|---|---|
| 1 | 电源确认 | 开关电源输出电压、PLC电源端子正负极、急停回路接线是否常闭串联 |
| 2 | 数字量输入 | 用万用表逐个核对输入点,确认传感器信号类型是PNP还是NPN,和模块输入类型匹配 |
| 3 | 数字量输出 | 逐一强制输出,确认接触器、指示灯接线正确,摸清每个地址实际带载的设备 |
| 4 | 模拟量信号 | 检查4-20mA还是0-10V,变送器供电是否接正确,信号线是否用屏蔽层单端接地 |
| 5 | 通信线缆 | RS485的A/B是否接反,终端电阻是否设置,网线的水晶头压接有没有问题 |
| 6 | 逻辑复核 | 对照图纸和IO表,确认PLC地址分配和图纸一致 |
这些检查听着笨,但能省掉后面80%的调试时间。很多看似是程序问题,最后发现其实是NPN和PNP接反了、模拟量正负极反了这种低级但致命的错误。
5.2 空载跑逻辑,用强制和监视把每个条件过一遍
上电之后不要马上带载运行。先切到手动模式或者检修模式,把所有执行机构的负载断开(比如把接触器输出端的负载线拆了),只给PLC信号,验证程序逻辑是否正确。
我的习惯是打开梯形图在线监视,然后按照“启动条件→运行状态→停止条件”的顺序,逐条强制输入信号,观察输出变化是否符合预期。一个字一个字的查,不偷懒。比如润滑电机控制FB,先看启动按钮是否能在无故障状态下置位运行,再看反转置“运行反馈”信号,确认FB没有卡在等待反馈的死循环里,最后强制故障信号,确认联锁切断和报警触发都正常。
强制的时候要记住一条:强制完成后必须逐个取消,全部恢复正常状态再测试下一个条件。否则带着一堆强制信号跑程序,后面出现什么怪现象都分不清是程序问题还是强制残留问题。这个坑我印象极深,有一次调试三菱PLC,前一晚强制了一个X点忘了取消,第二天整个动作顺序全乱,排查了半天才发现是远程IO模块上的强制指示灯一直在亮。
5.3 带载调试的顺序,从慢到快逐步逼近工艺要求
空载逻辑一旦确认没问题,接下来就进入带载调试。带载调试的核心原则是两个:先单机后联动,先低速后速度到额定的和生产节拍一致。
拿一个润滑电机加主轴电机的系统举例。第一步单机启动润滑电机,观察电流、稳定声音、空载转速。确认没问题后,手动启动主轴电机,验证变频器参数是否合适。第二步做联动测试,在自动模式下按启动按钮,确认3秒延时后主轴启动,停止时主轴先停、4秒后润滑停。这个过程中我会盯着触摸屏上的状态字文本,从“润滑运行”跳到“主轴延时启动”再跳到“全速运行”,每一步的切换条件和定时时间是否精准。
带载调试过程中,如果发现机械动作有顿挫、冲击,不要急着去调PLC程序里的时间参数。先看机械原因,比如气缸有没有缓冲、变频器加减速时间是否太陡。机械问题和程序问题往往混在一起,要在趋势图里同时看“输出命令”和“实际反馈”,才能判断偏差出在哪一侧。
5.4 交验前的报警测试和断电恢复测试
程序带载跑顺以后,还有两道关必须过:报警测试和断电恢复测试。
报警测试是把每个报警信号逐一接地或者接24V触发一遍,确认触摸屏上有对应的报警文本弹出,同时联锁动作正确。我用电子表统计一遍每个报警从触发到屏上显示的时间,如果个别报警延迟超过2秒,就要检查PLC扫描周期、报警FB的调用频率和屏幕的刷新周期是否匹配。
断电恢复测试特别考验结构化设计的功底。老程序里经常出现断电后数据块里的中间状态没初始化,重新上电后设备直接从半截流程开始跑,非常危险。结构化编程里,设备域FB的初始化逻辑里必须包含上电默认状态判断,原则上上电后所有工艺状态都要回到S_IDLE空闲态,输出全部复位,除非有专人确认安全后才能重新启动。我习惯在FB内部加一个“初始化完成”标志位,背景数据块掉电保持区只在“格式化数据”这种无关紧要的地方用,工艺状态字坚决不给保持。
6. 现场高频问题速查表
做这行久了,你会发现大家遇到的问题高度相似。我把这些年现场经常遇到的情况整理成一个速查表,遇到问题先照这个表过一遍,能省很多冤枉时间。
| 现象 | 常见原因 | 处理思路 |
|---|---|---|
| 台达PLC连不上下载软件 | 串口COM号选错、站号不符、PLC在RUN状态 | 核对设备管理器COM号,设置同站号,软件勾选下载前切STOP |
| InoProShop扫描不到设备 | 不同网段、防火墙拦截、AMSNetId不对 | 电脑pingPLC IP,放行防火墙,手动填AMSNetId建立连接 |
| S7-200 SMART搜索不到CPU | 选错网卡、不在同一网段、被防火墙拦 | 切换PG/PC接口为对应网卡,设置同网段,勾选远程 |
| PLC温度PID温差波动大 | 滤波太强导致反馈滞后、执行机构回差大、采样位置不典型 | 先判断真实波动还是假波动,恢复纯P控制,逐个加I,D慎用 |
| 博途PLC连第三方模拟屏读不到数据 | GET/PUT未开放、机架/插槽填错 | CPU防护设置勾选允许PUT/GET访问,确认机架槽号 |
| PLCSIM Advanced下载报密码错误 | 项目启用了块保护或组态保护 | PLC属性里取消保护或设置完全访问,重新编译下载 |
| 一拖三软启电机切换时跳闸 | 原电机没完全停就启动新电机、软启动器放电未完成 | 在FB内部延长切换间隔,设定先复位原启动、延时后再置位新启动 |
| 三菱PLC在线监视但程序不刷新 | 监控模式下没有执行“写入模式”、PLC处于RUN但未下载完整程序 | 检查程序是否已下载到PLC内存,切到监控模式看实时状态 |
| 变频器和PLC用485通信乱码 | 波特率、数据位、校验位约定不一致,A/B线接反 | 全部从站统一为9600-8-N-1之类的参数,按说明书确认线序 |
| 程序里定时器互相干扰 | 同一个定时器被多处复位或引用 | 将顺序控制里的定时器收敛到状态机内部,避免跨网段引用 |
7. 最后再说几句我的体会
这几年在泸州周边做设备升级,碰到最多的不是花哨的控制算法,而是工厂老师傅手写的一堆“能跑但看不懂”的老程序。我给其中一条生产线做过整体重构,把原来一千多行梯形图拆成了二十多个功能块,光梳理功能清单就花了两天半,但后面改工艺的时候对方说太省事了,以前改一个动作要提心吊胆一整天,现在十五分钟就验收完。
我的建议很简单:想学结构化编程,不需要等什么大项目练手,把你手头那台只有几个电机的旧设备先拿来重构一遍,拆出输入输出表,建好结构体,做几个FB,把顺序控制改成状态机。跑通了,你就真正理解结构化编程的核心实现方法了,后面再做复杂的非标项目,思路完全是另一个层面。
结构化不是炫技,它是给设备的整个生命周期降低维护成本。每一行代码,都值得为三个月后那个满头大汗的自己考虑考虑。