1. 从继电器柜到软逻辑:IEC 61131到底解决了谁的痛点
刚入行那会儿,我接手过一个老产线的改造项目。电气柜里密密麻麻的继电器、时间继电器、计数器,接线端子排长得像蜈蚣,排查一个故障得拿着图纸一根线一根线地量。老师傅跟我说,这套逻辑要是用PLC重写,半天就能搞定。那时候我才真正意识到,IEC 61131标准存在的意义,不是给编程添规矩,而是把工业控制逻辑从“硬件接线”的泥潭里拽出来,变成可复用、可移植、可维护的软件资产。
很多人第一次接触PLC编程,脑子里蹦出来的问题是“我该学西门子还是三菱”。但真正该先问的是:这套控制逻辑,换一个品牌的PLC还能不能跑?IEC 61131就是回答这个问题的。它是国际电工委员会制定的可编程控制器编程语言标准,把PLC的编程语言、数据类型、程序组织单元、通信接口等做了一套统一的约定。你可以把它理解成工业控制领域的“普通话”——不管你是哪个厂家的人,说普通话大家都能听懂。
这套标准最早在1993年发布,后来经过多次修订,目前主流参考的是第二版和第三版的内容。它定义了五种编程语言,规定了变量声明方式,还引入了程序组织单元(POU)的概念。说白了,它想让PLC程序像高级语言一样,有函数、有结构体、有命名空间,而不是一堆散乱的梯形图网络堆在一起。
那这套标准具体适合谁来学?我的判断是三类人:一是刚入行的电气自动化工程师,需要建立正确的编程思维框架;二是从传统继电器控制转过来的老师傅,需要理解“软逻辑”和“硬接线”的本质区别;三是做设备出口的团队,因为海外客户经常要求程序符合IEC 61131-3的规范,方便后期维护和二次开发。
接下来我会从标准的核心架构讲起,把五种编程语言、数据类型、POU组织方式逐一拆开,再结合我实际项目中踩过的坑,聊聊怎么从零搭建一个符合标准思维的程序框架。文章会涉及具体代码示例和配置思路,但不会绑定某个特定品牌的软件,因为标准本身是跨平台的。
2. 五种编程语言不是让你全学,而是让你会选
IEC 61131-3定义了五种编程语言,很多人一看就懵了:难道我要把五种都学会?其实不是。这五种语言的设计初衷是针对不同的应用场景和不同的工程师背景,你可以根据项目特点和个人习惯来选择。我见过只用梯形图走天下的老工程师,也见过全程用结构化文本写复杂算法的年轻人,两种做法都没问题,关键是选对场景。
2.1 梯形图(LD):电气工程师的母语
梯形图是PLC编程里最直观的语言,它的符号体系直接继承自继电器控制电路。常开触点、常闭触点、线圈、定时器、计数器,这些元素在梯形图里的画法和电气原理图几乎一一对应。对于从继电器控制转过来的工程师来说,梯形图几乎没有学习门槛——你以前怎么画电路图,现在就怎么画梯形图。
但梯形图有个明显的短板:处理复杂逻辑和数学运算时非常吃力。比如你要做一个PID控制算法,或者对一组数据做排序,用梯形图写出来会像蜘蛛网一样,可读性极差。我个人的经验是,梯形图适合写开关量逻辑、互锁关系、设备启停顺序控制,这些场景下它的直观性是其他语言比不了的。
提示:梯形图里尽量一个网络只做一件事,不要在一个网络里塞太多并联和串联分支。后期排查故障时,网络越简洁,定位越快。
2.2 结构化文本(ST):复杂算法的首选
结构化文本的语法类似Pascal和C,有IF-THEN-ELSE、CASE、FOR、WHILE这些控制结构,支持数组、结构体、函数调用。如果你写过任何一门高级语言,上手ST会非常快。我在做运动控制、过程控制、数据处理这类项目时,基本都用ST来写核心算法。
ST的优势在于表达能力强、代码紧凑。同样一个冒泡排序,梯形图可能要画几十个网络,ST几行就搞定了。但ST的缺点是调试时不如梯形图直观,变量之间的依赖关系需要靠阅读代码来理解。所以我的习惯是:用ST写功能块内部逻辑,用梯形图做顶层调用和状态监控,两者结合使用。
FUNCTION_BLOCK FB_MotorControl VAR_INPUT Start : BOOL; Stop : BOOL; Fault : BOOL; END_VAR VAR_OUTPUT Run : BOOL; END_VAR VAR State : INT := 0; END_VAR CASE State OF 0: // 停止状态 IF Start AND NOT Fault THEN State := 1; END_IF; 1: // 运行状态 Run := TRUE; IF Stop OR Fault THEN State := 0; END_IF; END_CASE;上面这段代码展示了一个简单的电机控制功能块,用CASE语句实现状态机。这种写法在ST里非常自然,换成梯形图就会很别扭。
2.3 功能块图(FBD):信号流一目了然
功能块图用方块表示功能块,用连线表示信号流向。它特别适合过程控制领域,因为过程控制里大量使用PID、滤波、标定、报警这些标准功能块,用FBD连起来就像画信号流图一样清晰。很多DCS系统就是基于FBD的编程思路。
FBD的局限性在于,它不太适合表达复杂的条件分支和循环逻辑。你很难用FBD画出一个带嵌套判断的算法。所以FBD通常和ST配合使用:FBD做顶层信号流,ST写底层功能块。
2.4 顺序功能图(SFC):流程控制的利器
顺序功能图是专门为顺序控制设计的语言。它把控制过程分解成一个个“步”(Step),步与步之间用“转换”(Transition)连接,每个步里可以挂动作(Action)。这种编程方式特别适合包装机械、装配线、注塑机这类有明显工序划分的设备。
SFC最大的好处是逻辑清晰、易于调试。设备卡在哪个步,一眼就能看出来。而且SFC天然支持并行分支和选择分支,可以表达复杂的工序跳转。我做过一个多工位装配设备,用SFC写主流程,每个步里调用ST写的功能块,整个程序结构非常清爽。
2.5 指令表(IL):正在被淘汰的“汇编语言”
指令表是一种低级语言,类似汇编,用操作码和操作数逐条写指令。它是最早的PLC编程语言之一,但现在新项目里已经很少用了。IEC 61131-3第三版已经把IL列为“逐步淘汰”的语言。我的建议是:了解即可,不必深学。除非你维护的是十几年前的老设备,否则没必要花时间在IL上。
| 语言 | 最适合场景 | 学习难度 | 我的使用频率 |
|---|---|---|---|
| 梯形图(LD) | 开关量逻辑、互锁、启停控制 | 低 | 高 |
| 结构化文本(ST) | 算法、数据处理、状态机 | 中 | 高 |
| 功能块图(FBD) | 过程控制、信号流 | 低 | 中 |
| 顺序功能图(SFC) | 顺序流程、工序控制 | 中 | 中 |
| 指令表(IL) | 老设备维护 | 高 | 极低 |
这张表是我个人经验的总结,不是标准的规定。标准只是定义了这五种语言,并没有规定你必须用哪一种。实际项目中,混合使用多种语言才是常态。
3. 数据类型和变量作用域:程序稳定的地基
很多人写PLC程序,变量命名随手就来,M0.0、M0.1、V100、V101,程序写完自己都记不住哪个变量是干什么的。这种习惯在小项目里还能凑合,一旦程序规模上去,维护成本会指数级上升。IEC 61131-3对数据类型和变量作用域有明确的规定,用好这些规定,程序的可读性和稳定性会提升一个档次。
3.1 基本数据类型:别再用WORD走天下了
IEC 61131-3定义了一套标准的数据类型,包括布尔型(BOOL)、整型(SINT/INT/DINT/LINT)、无符号整型(USINT/UINT/UDINT/ULINT)、浮点型(REAL/LREAL)、字节和字(BYTE/WORD/DWORD/LWORD)、字符串(STRING/WSTRING)、时间(TIME/DATE/TOD/DT)等。
我见过很多老程序,所有数值都用WORD或INT,温度用INT,计数用INT,连布尔标志也用INT的0和1表示。这种写法在早期PLC内存紧张的时候可以理解,但现在完全没必要。用对数据类型,好处有三个:一是编译器能帮你做类型检查,减少隐式转换的bug;二是代码可读性更好,看到REAL就知道是模拟量,看到BOOL就知道是开关量;三是不同品牌PLC之间移植时,标准数据类型兼容性更好。
注意:REAL是32位浮点数,精度有限。做累计流量、高精度位置计算时,要考虑用LREAL(64位)或者改用整数运算避免精度损失。我曾经在一个累计流量项目里用REAL累加,跑了三天后发现累计值偏差了0.5%,后来改成DINT按最小单位累加才解决。
3.2 派生数据类型:结构体和数组让程序有“骨架”
除了基本类型,IEC 61131-3还支持派生数据类型,最常用的就是结构体(STRUCT)和数组(ARRAY)。
结构体可以把一组相关的变量打包成一个整体。比如你要描述一台电机的状态,可以定义一个MotorType结构体,里面包含运行状态、故障状态、电流、转速、运行时间等成员。这样你在程序里传递电机数据时,只需要传一个结构体变量,而不是五六个分散的变量。
TYPE MotorType : STRUCT Run : BOOL; Fault : BOOL; Current : REAL; Speed : REAL; RunHours : DINT; END_STRUCT END_TYPE数组则适合处理批量数据,比如多工位设备的工位状态、多路温度采集值等。数组可以和FOR循环配合,用几行代码处理几十路信号,比梯形图里一个个网络画过去效率高得多。
3.3 变量作用域:全局变量是万恶之源
IEC 61131-3把变量分为局部变量和全局变量。局部变量在POU内部声明,只在该POU内可见;全局变量在所有POU中都可以访问。
我的经验是:全局变量越少越好。全局变量多了,程序里任何一个地方都可能修改它,出了问题很难定位。正确的做法是:功能块内部用局部变量,功能块之间通过输入输出接口传递数据。这样每个功能块就像一个黑盒子,你只需要关心它的输入和输出,不需要关心它内部怎么实现。
| 变量类型 | 声明位置 | 可见范围 | 我的使用建议 |
|---|---|---|---|
| 局部变量 | POU内部 | 仅本POU | 优先使用 |
| 全局变量 | 全局变量表 | 所有POU | 仅用于系统级配置 |
| 输入输出变量 | POU接口 | 调用者可见 | 功能块间通信 |
| 临时变量 | POU内部 | 仅本POU,不保持 | 循环计数等 |
这张表的核心思想是封装。把变化限制在功能块内部,功能块之间的接口保持稳定,整个程序的可维护性就会好很多。
4. 程序组织单元:把程序拆成积木,而不是揉成面团
IEC 61131-3把程序组织单元(POU)分为三种:程序(Program)、功能块(Function Block)、函数(Function)。这三种POU的区别,决定了你写的代码是“一次性消耗品”还是“可复用资产”。
4.1 程序、功能块、函数的本质区别
程序是最高层的POU,相当于整个控制程序的入口。一个PLC项目通常有一个主程序,主程序里调用各种功能块和函数。程序可以访问全局变量,也可以有局部变量。
功能块是带记忆的POU。它有输入和输出,内部有静态变量,调用一次执行一次,但内部状态会保持到下一次调用。比如一个定时器功能块,你调用它的时候传入使能信号和设定时间,它会记住当前计时值,下次调用时继续累加。功能块是PLC编程里最核心的复用单元。
函数是不带记忆的POU。同样的输入永远得到同样的输出,内部没有静态变量。比如一个计算平均值的函数,传入数组返回平均值,调用多少次结果都一样。函数适合做纯计算,不适合做状态控制。
| POU类型 | 是否有记忆 | 能否调用功能块 | 典型用途 |
|---|---|---|---|
| 程序 | 有 | 能 | 主控流程 |
| 功能块 | 有 | 能 | 设备控制、状态机 |
| 函数 | 无 | 不能 | 数学计算、类型转换 |
这张表里最关键的一条是:函数不能调用功能块。因为函数没有记忆,而功能块需要记忆,两者在语义上冲突。很多初学者在这里踩坑,写了一个函数,里面想调用一个定时器功能块,编译器直接报错。
4.2 功能块的设计原则:高内聚、低耦合
我写功能块有一个习惯:一个功能块只做一件事。比如电机控制功能块就只管电机的启停和故障处理,不要在里面掺入报警输出、数据记录这些杂七杂八的逻辑。报警和数据记录应该由上层程序或者专门的功能块来处理。
这样做的好处是功能块可以复用。今天做A项目用这个电机控制功能块,明天做B项目还能用,只需要改一下输入输出映射就行。如果功能块里掺了太多业务逻辑,换一个项目就得大改,复用价值就没了。
功能块的接口设计也有讲究。输入变量尽量用标准数据类型,不要用自定义的结构体作为输入,除非这个结构体是通用的。输出变量要明确,不要通过全局变量往外传数据。我见过一个功能块,内部直接修改了十几个全局变量,调用者根本不知道它会改哪些变量,这种功能块就是“定时炸弹”。
4.3 程序组织单元的实际项目结构
以一个典型的设备控制项目为例,我的程序结构通常是这样组织的:
- 主程序(Program):负责模式选择、急停处理、设备整体使能。
- 设备控制功能块(FB):每个设备一个功能块,比如电机、气缸、加热器。
- 工艺功能块(FB):实现具体的工艺逻辑,比如温度PID、位置控制。
- 工具函数(Function):做单位转换、数据滤波、报警判断。
- 通信功能块(FB):处理与上位机、其他PLC的数据交换。
这种分层结构的好处是,每一层只关心自己的职责。主程序不关心电机怎么控制,只关心什么时候调用电机功能块;电机功能块不关心工艺怎么走,只关心自己有没有故障。出了问题,先定位是哪一层的问题,再深入排查,效率比一锅粥的程序高得多。
5. 从标准到落地:我踩过的坑和总结的经验
标准读起来是一回事,实际用起来是另一回事。IEC 61131-3给了你一套框架,但具体怎么落地,每个项目都有不同的答案。下面这几个坑,是我在实际项目中真实踩过的,分享出来希望能帮你少走弯路。
5.1 变量命名:别让三个月后的自己骂现在的你
我刚工作时写程序,变量名都是M0、M1、V100、V101,当时觉得省事。三个月后设备出故障,我拿着程序排查,完全想不起来M37是干什么的。后来我强制自己用有意义的命名,比如Conveyor1_RunCmd、TankLevel_HighAlarm、Motor2_Fault。虽然打字多了一点,但排查故障时省下的时间远远超过打字的时间。
IEC 61131-3没有规定命名规则,但大多数编程环境支持长变量名。我的建议是采用匈牙利命名法加驼峰命名的组合:前缀表示数据类型或用途,后面用驼峰表示具体含义。比如bMotorRun表示布尔型电机运行信号,rTankLevel表示实型罐体液位,fbPID表示功能块实例。这样一眼就能看出变量的类型和用途。
5.2 状态机:比一堆IF-ELSE靠谱得多
很多初学者写顺序控制,喜欢用一堆IF-ELSE嵌套。设备有五个步骤,就写五层IF-ELSE。这种写法在步骤少的时候还能看,步骤一多就变成“箭头型代码”,缩进深得屏幕都放不下。
我的做法是用CASE语句写状态机。把设备的每个步骤定义成一个状态编号,用CASE根据当前状态执行对应的动作,动作完成后切换到下一个状态。这种写法结构清晰,每个状态做什么一目了然,增加步骤也只需要加一个CASE分支。
CASE Step OF 0: // 等待启动 IF Start THEN Step := 10; END_IF; 10: // 气缸伸出 Cylinder_Extend := TRUE; IF Cylinder_Extended THEN Step := 20; END_IF; 20: // 延时 IF Timer_Done THEN Step := 30; END_IF; 30: // 气缸缩回 Cylinder_Extend := FALSE; IF Cylinder_Retracted THEN Step := 0; END_IF; END_CASE;这种状态机写法在ST里非常自然,在梯形图里也可以用步进指令实现。关键是把状态和动作分离,状态决定当前该做什么,动作只负责执行。
5.3 模拟量处理:滤波和标定不能省
模拟量输入是PLC编程里最容易出问题的地方。传感器信号有噪声,直接拿来用会导致控制抖动。我见过一个温度控制项目,热电偶信号没做滤波,温度值在设定值附近来回跳,加热器频繁启停,接触器几个月就坏了。
我的标准做法是:模拟量输入先做滤波,再做标定,最后做报警判断。滤波用一阶低通或者滑动平均,标定把原始值转换成工程单位,报警判断用标定后的值。这三步缺一不可。
滤波参数的选择要看信号特性。温度信号变化慢,滤波时间可以长一点,比如10秒;压力信号变化快,滤波时间要短,比如0.5秒。滤波时间太长会导致响应滞后,太短又起不到滤波效果。我通常会在调试时用趋势图观察,找到一个既能滤掉噪声又不影响响应的平衡点。
5.4 程序版本管理:别再用“最终版”“最终版2”了
PLC程序也需要版本管理。我见过太多工程师用“程序_最终版”“程序_最终版2”“程序_最终版_改”这样的文件名,最后自己都分不清哪个是最新的。
我的做法是:每次下载到PLC的程序,都导出一份存档,文件名包含日期和版本号。比如20250115_v1.3_产线A。同时在程序里加一个版本变量,HMI上可以查看当前运行的版本号。这样现场出问题时,能快速确认PLC里跑的是哪个版本,和存档对比就能知道有没有被改过。
如果团队有条件,可以用Git或者SVN做版本管理。PLC程序虽然是二进制文件,但很多编程环境支持导出为XML或文本格式,这样就可以用版本管理工具跟踪变更了。
5.5 跨品牌移植:标准是底线,但不是万能药
IEC 61131-3的目标之一是提高程序的可移植性,但实际项目中,跨品牌移植远没有标准说的那么轻松。不同厂家对标准的实现有差异,比如数据类型名称可能不同,功能块的接口可能不一样,编译器的扩展语法也各有各的玩法。
我的经验是:核心算法用标准ST写,硬件相关部分用厂家专用功能。比如PID算法、状态机、数据处理这些逻辑,用标准ST写,移植时基本不用改。但硬件配置、通信设置、特殊模块读写这些,每个厂家都不一样,移植时肯定要重写。所以移植前要先评估工作量,别指望一键迁移。
| 移植内容 | 可移植性 | 注意事项 |
|---|---|---|
| 标准ST算法 | 高 | 注意数据类型名称差异 |
| 梯形图逻辑 | 中 | 指令符号可能不同 |
| 硬件配置 | 低 | 必须重写 |
| 通信设置 | 低 | 协议实现有差异 |
| HMI画面 | 低 | 通常绑定厂家 |
这张表是我多次移植项目后的经验总结。核心思想是:把可移植的部分和不可移植的部分分开,可移植的用标准写,不可移植的用厂家专用功能封装起来,换平台时只需要重写封装层。
6. 给不同阶段工程师的学习路径建议
IEC 61131-3的内容很多,一口气全学完不现实。根据我自己的学习经历和带新人的经验,不同阶段的工程师应该有不同的侧重点。
刚入行的新手,先把梯形图和基本指令学扎实。梯形图是PLC编程的基础,开关量逻辑、定时器、计数器这些必须熟练。同时要理解扫描周期、输入输出刷新、变量作用域这些基本概念。这个阶段不要急着学ST和SFC,先把梯形图写顺了再说。
有一两年经验的工程师,开始学结构化文本和功能块设计。ST能帮你处理梯形图搞不定的复杂逻辑,功能块能帮你把代码组织成可复用的模块。这个阶段要多看别人写的功能块,学习接口设计和状态机写法。同时要养成好的命名习惯和注释习惯。
有三到五年经验的工程师,重点研究程序架构和标准化。怎么分层、怎么解耦、怎么设计可复用的功能块库,这些是区分普通工程师和高级工程师的关键。这个阶段可以研究一下IEC 61131-3第三版的新特性,比如面向对象编程的扩展,虽然很多PLC平台还不支持,但了解一下没坏处。
带团队的工程师,要建立团队的编程规范和代码审查机制。变量命名规则、功能块接口标准、版本管理流程,这些看起来是小事,但团队规模上去之后,没有规范会乱成一锅粥。我见过一个团队,五个人写同一个项目,变量命名五花八门,最后整合的时候花了整整一周时间改名字。
最后说一个我自己的体会:标准是死的,项目是活的。IEC 61131-3给了你一套工具,但怎么用取决于你的项目需求和团队习惯。不要为了“符合标准”而把程序写得复杂无比,也不要因为“以前都这么写”而拒绝新的方法。找到适合自己团队的平衡点,才是最重要的。