☰
从“能运行”到“好维护”:评估PLC程序质量的五个关键维度
2026/10/12 2:34:18 网站建设 项目流程

几年前我到某现场处理设备停机故障,设备停产,产线负责人守在旁边直跺脚。我打开PLC在线监控,看到的画面至今印象深刻:主程序块里从上到下铺了三百多个网络,触点、线圈、定时器、比较指令全挤在一起,变量名清一色是M0.0、M10.3这类内部地址,全程序唯一的注释只有开头的四个字:"程序能跑"。设备确实能跑,而且已经稳定运行了大半年——问题是,这大半年里没有任何人敢动它,连厂里的老电工都绕着走,一有小毛病就只能干等厂家来收费处理。

那一刻我特别想问问当初写程序的人:能运行,真的就等于写得好吗?干PLC这行这些年,我越来越确信这是一个值得每个工控人都认真回答的问题。程序能跑,只是及格线;好读、好改、好排查、好扩展,才是真正的交付质量。这篇文章我就从这个话题展开,把这几年在现场看到的好程序、烂程序以及我自己重构项目的真实经验,一次说清楚。不管你是刚入行的新手,还是正被"祖宗程序"折磨的维护工程师,应该都能找到点有用的东西。

1. "能运行"这个标准,低得远远超乎你的想象

1.1 一个"没人敢动"的程序,就是失败的开始

前面说的那台设备,后来花了两天才找到停机原因:一个气缸的磁性开关老化,信号时有时无,程序把抖动的信号当成正常到位,下一个工位就提前动作,产品卡死。按理说这种故障十分钟就能定位,但因为整个流程都靠一串定时器互相衔接,没有人能从上万条逻辑关系里理出"到底是谁先动了谁"。最后是厂家工程师带着源程序,对着在线监控一帧一帧看,才确认了问题。

这就是典型的"能跑"型程序。它平时不出错,所以所有人都以为它很好;但它一旦出错,整个现场没有任何人能处理。一台设备的设计寿命通常是十年以上,程序写出来只花三个月,却要被后面无数人读十年。只要一次修改需要别人花三天去读懂你三个月前"随手写的逻辑",这台设备就已经开始亏钱了。判断PLC程序好坏的第一标准,不是今天能不能跑,而是明天出了问题,接手的工程师能不能在合理时间内看懂它。

1.2 所谓"能跑",只验证了"正常情况下的正常路径"

很多人验收程序时,习惯按这样的流程走:按启动键,观察气缸动作,确认节拍正确,然后签字。这套流程验证的其实只有一件事——在输入信号完全符合预期、且执行顺序完全正确的前提下,输出能不能按顺序出来。严格来说,它只覆盖了整个程序逻辑的一个极小子集。

那没被验证的是什么?是断电重启后程序能不能回到安全初始位;是传感器闪断、信号抖动时会不会误动作;是两个工位同时请求同一个输出时谁先谁后;是操作员在自动运行中突然按了手动按钮,程序会不会自己"精神分裂";是气缸卡住超时没到位,程序有没有能力报警而不是闷头继续走。这些场景任何一个处理不好,平时看着"能跑"的程序,到了关键时刻就是事故。我见过一台设备因为断电重启后内部标志位没复位,恢复运行的第一件事就是让压装气缸直接压下来——幸好那次没有人在设备里面作业。

1.3 程序质量的账,最后都是拿别人的时间在付

我也理解有些工程师为什么觉得"能跑就行"。现场工期紧,甲方催得急,能把节拍跑顺已经花掉大半精力,哪还有空做结构化?这个心情我太懂了。但从项目全生命周期的账本上看,这个选择往往不划算:一开始省下的几十个小时,会在设备十几年的服役期里,以"每次维护多花几小时"的方式连本带利地还回去。机器只要卖出去,程序就要被无数人读、改、查,今天欠下的可维护性债,明天一定会有人替你还。

提示:我判断一段程序质量,最喜欢用的拷问是——"如果写这程序的人明天消失了,换一个完全陌生的人来维护,这个项目还能转得动吗?"答案越心虚,程序的质量就越差。

2. 一眼识别"能跑但很危险"的典型坏味道

2.1 所有逻辑全堆在主程序块里

我打开过不少"能跑"的程序,最常见的一类特征就是:不管设备有几个工位、几根轴、多少个动作,全部逻辑都堆在主程序块里。从第一行到几百行全是网络,中间没有功能块、没有子程序、没有注释分隔。这种程序在编辑器里打开的那一刻,在线监控就是一片密密麻麻的绿色,你想找一个气缸的线圈得翻半天,想搞明白动作之间的先后关系,得自己手写流程图。

之所以说它危险,不只是因为难看。把所有逻辑放在一个块里,意味着每次扫描都要把全部逻辑扫一遍,程序规模大了扫描周期会被拖长;更麻烦的是,这种写法天然拒绝复用——第二台一样的设备想复制,只能整体复制然后全文修改,改错一个地址就多一处隐患。正确的做法是按工艺单元拆功能块,每个机械单元一个FB,主程序只负责调度和报警汇总。

2.2 变量名全是"天书",符号寻址形同虚设

另一个高发坏味道是变量命名。M0.0、M10.3、M20.5……内部继电器满天飞,整个程序几十个M地址,没有一个名字能告诉你它代表什么。更夸张的版本是,同一个含义的信号在不同的网络里用了两个不同的M地址,因为写的人自己都忘了前面已经用过。这种程序有一个通病:谁都不敢删变量,因为根本不知道有没有别的地方在用;谁要加一个中间状态,得先花半天确认哪个地址是空的。

现在的PLC平台几乎都支持符号编程,IO点、内部标志位、定时器实例都能起有意义的名字。为什么还有人用裸地址?多半是早期习惯,或者从老设备移植程序图省事。但省事的代价是,整段逻辑的可读性归零。哪怕是梯形图里一个简单的自锁,如果线圈叫"M100"而不是"b_FeedRunning",三个月后连作者自己都要重新推断意图。

2.3 硬编码的时间常数与"裸奔"的定时器

第三种坏味道藏得更深——逻辑里的时间、位置、速度全用魔法数字写死。比如某个动作的等待时间直接写了T#3S,既不说明为什么是3秒,也不放在统一参数区。如果工艺工程师告诉你"等待时间要改成4秒",你得满程序找这个3秒出现在哪几处,找到漏改一个,设备就多了一个"神秘故障"。这种参数应该全部集中声明,用一个有含义的常量名替代。

更常见的问题是定时器不会用。我见过大量把TON当单稳态用的代码:输入一直为真,定时器就一直计时并保持输出,完全不考虑"只要条件成立就一直输出"是否为设计意图。正确使用需要配合边沿检测,让定时器只在触发沿启动一次,同时每个状态切换时清掉上次的残留,避免程序"记得"上一轮的旧状态。

2.4 报警和急停处理只做到"刚好能跑"

最让我不放心的坏味道,是报警逻辑和急停逻辑"刚好够用"。所谓刚好够用,就是急停按下时输出能断电,复位后设备又能自动重新跑起来——至于急停复位后需不需要操作员手动确认、传感器故障有没有报警提示、气缸超时有没有报错文本、气压不足时会不会继续动作,一概不管。这种程序长期运行下来,出故障时现场工程师只能靠看灯猜,猜来猜去把好端端的设备调坏的事,我没少见。

注意:凡是涉及安全回路的逻辑,我在程序里至少要求做到三层——硬件回路硬接线保护、PLC程序里安全输入优先判断、操作界面上给出明确报警和复位流程。宁可多写十行,不能少写一行。

2.5 复制粘贴式编程,改一处漏三处

最后一个典型特征是代码复用靠复制粘贴。一条产线三个工位,逻辑几乎一样,只是地址不同,于是写程序的人直接把第一个工位的网络复制两次,换换地址就完事。当时看确实省事,后来工艺变了,改第一个工位时忘了第三个——这种因为单工位修改引起的连锁丢逻辑,是现场最常见的隐性故障源之一,而且极难排查,因为程序本身看起来"正常得很"。

这几个特征单独出现一个还好,凑在一起基本就是后期维护的灾难现场。我自己的习惯是,接任何程序先做一遍"坏味道检查",把这些问题逐项过一遍,再决定是修补还是重构。

3. 我给PLC程序打分的五个核心维度

3.1 可读性:陌生工程师10分钟内能不能讲清流程

可读性不是"有没有注释"这么简单,而是整体结构是否表达出了工艺逻辑。一段好程序,应该能让一个从没接触过这台设备的工程师,在导航式的结构里花10分钟把主流程讲清楚:主程序调用哪些功能块、每个功能块管理哪个单元、状态之间怎么切换。为了达到这一点,我写程序时会刻意让"结构"自己说话——功能块的名字就是设备单元名,状态的枚举就是工艺动作名,看完块列表就相当于看完了工艺流程。

3.2 健壮性:异常发生时它选择"安全停"还是"乱动"

健壮性考察的是程序在最坏情况下的行为。信号抖动、传感器失效、气缸卡滞、断气断电,这些早晚要来的异常,好程序会明确"停下来、保持安全位置、大声报警";差程序会"继续执行,直到撞上什么东西"。我评判健壮性时会特意做几个破坏性测试:把传感器信号人为断开,把两个按钮同时按住,按下急停再松开,断电重启,看程序会不会给出混乱动作。能跑过这一轮的,我才敢说它健壮。

3.3 可维护性:改一个需求要动几个地方

工艺是活的,今天改个节拍、明天加个检测,都很正常。可维护性就是衡量"改一个需求到底要动几个地方"的指标。如果改一个参数要翻遍五个网络才能改全,这个程序的维护性就不及格;如果把它收敛到一个功能块的一个接口变量上,维护性就是满分。这也是为什么我坚持"一个机械单元一个FB"——需求变化时,你知道该去哪里改,而且只改一边。

3.4 可扩展性:新增一台设备时是搭积木还是推倒重来

产线扩产能是常有的事。程序的可扩展性决定了这时候你是轻松加实例,还是痛苦地复制改造。用功能块封装后,新增一台设备基本上等于新建一个功能块实例、分配好IO、设定参数;如果是堆代码式写法,每加一台设备都是对整个程序的一次"手术",风险指数级上升。我接过的项目里,凡是当年用功能块设计的,后续升级都顺顺利利;凡是当年堆代码的,基本都在第三年就攒够了重写的理由。

3.5 可验证性:出问题时定位时间是用分钟还是用天

最后一个维度常常被忽略:程序出了故障,别人能不能快速定位。评估标准是时间——一个报警出现后,维护人员能不能在几分钟内判断出"是哪个传感器、哪一步、什么原因"。这需要两层功夫:一是程序里有完整的分级报警系统,而不是只有一个笼统的"设备故障"灯;二是状态机设计让当前状态一目了然,在线监控打开就能看到卡在哪一步。解决不了可验证性的程序,不管平时跑得多顺,本质上都是盲盒。

下面这张表是我自己常用的评分参考,分享给同行做个对照:

维度好程序的表现差程序的表现
可读性陌生工程师一看块列表就懂工艺原作者本人也要猜半天
健壮性传感器抖动时保持安全位并报警信号一抖就乱动
可维护性改一个参数只动一处改一个需求翻五个网络
可扩展性加设备等于新建实例加设备等于全文大改
可验证性报警直接指向根因只能看灯猜原因

4. 真实重构案例:一条包装线从"能跑"到"好维护"

4.1 接手时看到的现场

某项目的包装线设备,投产六年,程序由已经离职的工程师从头到尾手写,全部逻辑挤在主程序块里,四百多个网络,靠内存位和定时器的交织完成整条线的节拍。甲方准备扩产,要在原有线体上增加一个封箱工位。原厂家报价很高,于是他们找到我们做评估。我打开程序的时候,就看到了前面说的几乎所有坏味道:裸地址、魔法时间、无报警、复制粘贴式工位逻辑。

更麻烦的是,甲方自己的电气工程师根本不敢改任何一个网络,因为他们试过——曾有一次只是把某个定时器的时间从2秒改成3秒,结果后面三个工位的动作全部错位,最后靠备份恢复。这就是典型的"能跑但不能动"的程序。

4.2 诊断结果:三宗罪

我花了两个晚上把整个程序读了一遍,结论很明确,三宗罪。第一,节拍控制全部建立在定时器链上,前一个定时器动作触发后一个,根本没有状态概念,任何一个信号抖动就能让整条链错位;第二,没有任何故障报警,气缸到位超时、气压不足这类问题全部静默,设备坏了全靠人蹲在旁边看;第三,手动模式形同虚设,只能全自动跑,维护时想单动一个气缸得靠短接IO,危险且痛苦。

4.3 重构思路:状态机打底 + 功能块拆分 + 接口标准化

跟甲方沟通后确定方案:不修修补补,直接重构。核心思路有三个。第一,整个工艺流程改写为状态机,用枚举状态表达当前处于哪个工艺步骤,每个状态只做一件事,状态迁移条件清晰写在CASE里,这样在线监控时一眼就能看出设备卡在哪。第二,把每个工艺单元封装成独立的功能块,气缸、电机、传感器都作为块内部资源,对外只暴露启动请求、完成信号、故障信号这几个接口。第三,所有参数(时间、速度、偏移量)集中到参数区,用有意义的常量名引用。

重构后主程序的结构是这样的:

PROGRAM Main // 各工艺单元功能块实例化调用 fbFeedUnit(bRunReq := xFeedStart, xWorkAtPos := xFeedDone, oRunCmd => oFeedMotor, oAlarm => bAlarmFeed); fbClampUnit(bRunReq := xWorkReady, xClampDone := xClampPos, oClampCmd => oClampCylinder, oAlarm => bAlarmClamp); fbPackUnit(bRunReq := xClampReady, xPackDone := xPackDoneSignal, oPackCmd => oPackCylinder, oAlarm => bAlarmPack); // 报警汇总与显示 fbAlarmMgr.Scan(); END_PROGRAM

单个单元内部的核心节拍,我用状态机实现,结构大致是这样:

CASE stCycle OF S_IDLE: // 待机,只有收到启动请求才离开 IF bRunReq THEN stCycle := S_FEED_RUN; END_IF; S_FEED_RUN: // 进给到位后进入夹紧等待 oRunCmd := TRUE; IF xWorkAtPos THEN stCycle := S_CLAMP_WAIT; END_IF; S_CLAMP_WAIT: // 到位后清零定时器,重新计时 tonClamp(IN := xWorkAtPos, PT := tClampTime); IF tonClamp.Q THEN oClampCmd := TRUE; stCycle := S_CLAMP_DONE; END_IF; S_CLAMP_DONE: // 输出完成信号,等待流程推进 bStepDone := TRUE; IF bFlowRestart THEN stCycle := S_IDLE; END_IF; END_CASE;

这段代码看起来平平无奇,但它的意义在于:每一步都只有一个出口,当前状态和迁移条件全部显式化,不会再出现"上一个定时器到点就触发下一个"这种多米诺骨牌式逻辑。故障发生时,在线监控里直接就能看见状态字停在哪一步,配合监控功能,几分钟内就能定位问题。补充一点,实际写代码时,进入某个状态后的第一个扫描周期要给定时器的IN一个FALSE信号完成复位,或者调用定时器的复位方法,否则上一次的残留时间会让定时器在状态进入瞬间直接置位。这个小细节,现场吃过亏的都懂。

4.4 重构的成本与收益

重构也不是没有代价。方案确认、点表整理、程序重写、现场联调,前前后后花了将近两周,中间还占用了一天半的停机窗口。但效果相当明显:新增封箱工位总共只花了两天时间——新建一个功能块实例,映射IO,加入主程序调度,完事。如果沿用老代码做同样的事,甲方工程师自己估算过,至少一周起步,而且未必搞得定。从那以后,甲方自己的电气工程师也敢改程序了,这是我觉得最大的收获——程序交出去,不是为了让人不敢碰,而是为了让维护的人敢碰、能碰、碰得放心。

4.5 这次重构踩过的坑

几个坑值得提醒。第一个坑是重构过程中"顺手优化"工艺参数。重构的目标是等行为替换,不是改良工艺,把"原来等3秒现在等2.5秒"这种想法全部憋住,否则出了问题,你根本分不清是新逻辑的bug还是工艺调整的锅。第二个坑是手动模式与自动模式的中间状态。重构时我先把手动模式做得扎实,每个气缸都能单独手动操作,再处理自动逻辑,否则联调时连回初始位都困难。第三个坑是报警分级,一开始我把所有报警都做成同一优先级,现场一个传感器闪断就把维护人员叫过来,后来才按"提示、警告、急停"三级分好,反馈立刻清爽了很多。

5. 把"写得好"变成日常习惯的六个动作

5.1 从第一个触点开始就定好命名规范

与其等项目写完再补注释,不如从写第一行逻辑开始就按规范命名。我习惯用前缀区分类型:DI用"i_",DO用"o_",AI用"ai_",内部标志用"b_",定时器实例用"ton_",再加清晰的英文或拼音缩写。符号寻址是现代PLC最基本的功能,放弃它等于主动放弃可维护性。同时,IO点表必须和电气图纸一一对应,程序里每一个IO都有据可查,这是所有后续工作的地基。

5.2 功能块拆分:一个FB只做一件事

拆功能块的标准不是"代码量多就拆",而是"这个块是否对应一个独立的物理单元或功能"。一个气缸的往复、一台电机的启停、一个工位的完整动作,都可以是一个FB。每个FB的接口越少越好,尽量不要在FB里直接访问全局变量,而是把需要的信号作为输入参数传进来,把结果作为输出参数传出去。这样的块,放到哪个项目里都能复用,而不是和当前项目焊死在一起。

5.3 报警、故障和手动模式永远优先实现

我写程序的顺序从来不是"先把自动流程跑通再补报警",而是反过来:先搭好报警框架、急停处理、手动操作,再实现自动流程。这样做的原因是,自动流程越复杂,越需要一个稳定的"安全底座"做支撑;万一联调时出问题,手动模式能让我快速控制每个执行机构,报警能让我立刻知道哪里不对。顺序反了,后期补报警等于重新梳理一半逻辑,成本高得多。

5.4 注释只写"为什么",不写"做了什么"

"启动传送带"这种注释纯属浪费,因为任何人看到线圈名都知道它在启动传送带。真正值钱的是"为什么":"传送带需提前0.5秒启动,因为堆料检测传感器有响应延迟,防止堵料"。这样的注释记录的是写程序时踩过的坑、算过的账,是给未来的自己和其他维护者的情报。我在重构案例里反复强调参数要集中、常量要命名,其实也是在让"为什么"从代码本身就能读出来。

5.5 程序也要版本管理

很多老项目根本没有程序版本的概念,现场改完直接覆盖下载,几个月后连自己都分不清哪个版本在跑。我现在的习惯是:每次改动前,先把当前程序完整导出并注明日期与改动原因;每完成一个阶段,存一次归档;关键设备还会把不同版本的差异说明放在同目录下的表格里。这样做一次只要几分钟,但能避免无数次"改崩了却找不到上一个版本"的惨剧。有条件的话,建议把项目文件纳入统一的版本管理目录,配合注释规范化,效果更好。

5.6 定期给自己做一次Code Review

最后一个习惯,可能也是性价比最高的一个:项目写完,放两个星期,然后假装自己是一个从没接触过这个项目的陌生维护工程师,只依据IO点表和工艺描述,重新去读一遍自己的代码。凡是需要停下来猜的地方,就是需要返工的地方。这个方法我用过很多次,每次都能在交付前发现至少三四处"当时觉得没问题,回头一看满脸问号"的角落。与其让甲方在现场发现,不如自己先当那个"接盘侠"。

我自己这些年最大的转变,就是不再把"能跑"当作交付标准。判断一段程序好坏的时刻,不是设备第一次转起来那一刻,而是半年后、一年后,当报警响起、当设备扩容、当写程序的人已经不在现场,接手的人能不能在最短时间里搞明白这台机器的脾气。能运行,是这个职业的底线;写得好,才是这个职业真正的交付物。最后分享一个我一直在用的小习惯:每次按下下载按钮之前,先问自己一句——"如果明天我消失了,这套程序有人能看懂吗?"答案让你安心,再点确定。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询