IEC61131-3实战指南:五种PLC编程语言选型、数据类型与项目落地
1. 工控圈绕不开的IEC61131它到底在管什么干了十几年自动化从最早对着三菱FX系列梯形图一根线一根线地画到后来用CODESYS做多轴运动控制中间踩过的坑、返过的工十有八九都跟一个东西有关——IEC61131标准。你可以把它理解成PLC编程界的“普通话”不管你是西门子、三菱、欧姆龙还是国产汇川、台达只要宣称自己符合这个标准那编程的逻辑框架、数据类型、程序结构就有一套通用的规矩可循。这套标准最早是1993年发布的后来不断修订现在大家常说的是IEC61131-3专门管编程语言那一块。说白了它解决的核心问题就一个让不同品牌的PLC编程不至于各说各话。早年间各家搞各家的指令系统你在这家学会的编程思路换到另一家基本要从头来。IEC61131-3把编程语言统一成了五种梯形图LD、功能块图FBD、顺序功能图SFC、指令表IL和结构化文本ST。前两种是图形化的跟继电器逻辑一脉相承电工出身的兄弟上手快SFC适合做流程控制一步一步跟流程图似的IL有点像汇编现在用得少了ST则是高级语言风格写算法、做复杂运算特别顺手。这套标准适合谁来学我的判断是三类人一是刚入行的电气自动化专业毕业生学校里可能只教了梯形图但实际项目里ST和SFC用得非常频繁二是从其他品牌转过来的老手比如从日系转欧系或者从欧系转国产理解标准框架能让你迁移成本大幅降低三是做设备出口的工程师国外客户经常在技术协议里明确要求“编程需符合IEC61131-3”不懂这个连标书都写不利索。我见过太多人把IEC61131当成纯理论去背结果到了现场还是一脸懵。这篇文章我不打算照本宣科地念标准条款而是结合我这些年做过的产线改造、运动控制、过程控制项目把IEC61131-3里真正影响你日常编程的那些点拆开来讲。从数据类型怎么选、程序组织单元怎么划分到五种语言各自适合什么场景、常见坑在哪里再到实际项目里怎么把标准落地成能跑的代码。你看完至少能明白一件事标准不是拿来背的是拿来用的。2. 五种编程语言怎么选别再用梯形图硬扛所有活了2.1 梯形图LD电工逻辑的直系后代梯形图是绝大多数人接触PLC的第一门语言没有之一。它的视觉逻辑跟继电器控制回路几乎一模一样左边是电源轨右边是零线中间一排排触点、线圈、功能块。常开触点、常闭触点、输出线圈这三个元素就能搭出最基础的启保停电路。我刚开始带徒弟的时候第一周就让他们用梯形图写电机正反转互锁写不出来不准碰其他语言。但梯形图的局限也很明显。它擅长处理布尔逻辑和简单的定时计数一旦涉及浮点运算、数组操作、字符串处理梯形图就会变得极其臃肿。我见过一个项目用梯形图算一个PID输出光中间变量就占了三十多行后来改成ST只用了四行。所以我的建议是开关量逻辑、互锁、报警处理用梯形图没问题但涉及模拟量运算、配方管理、通信数据处理趁早换ST。梯形图里有个容易被忽视的细节能流的方向。标准里规定能流只能从左到右、从上到下不能倒流。这意味着你不能把输出线圈放在触点左边也不能让两个线圈互相驱动。有些品牌的编程软件会强制检查有些则允许你“违规”但运行结果不可预期。我踩过一次坑在一个日系PLC上写了一段自锁电路线圈放在触点上方编译通过了但实际运行时输出抖动查了半天才发现是能流方向的问题。2.2 功能块图FBD连续控制的好帮手FBD跟梯形图长得像但思维方式完全不同。梯形图是“电流怎么走”FBD是“信号怎么流”。它用方框表示功能块比如AND、OR、PID、TON方框之间用线连起来信号从输入端流向输出端。做模拟量处理、PID回路、信号滤波的时候FBD比梯形图直观得多。我印象最深的是一个温度控制项目用FBD搭了三路PID每路的设定值、反馈值、输出值一目了然调试的时候直接在线看每个功能块的输入输出哪一路有问题一眼就能定位。后来客户要求增加前馈补偿我直接在PID前面加了一个加法块五分钟改完。如果换成梯形图光是找那些中间变量就得花半小时。FBD的坑在于功能块的执行顺序。标准里没有明确规定同一个网络里功能块的执行顺序不同品牌的编译器处理方式不一样。有的按从左到右、从上到下有的按数据依赖关系。我遇到过一次两个功能块互相引用对方的输出结果一个品牌编译报错另一个品牌编译通过但运行结果跟预期相反。所以在FBD里尽量避免功能块之间的循环依赖如果实在需要加一个延时或者用中间变量隔开。2.3 顺序功能图SFC流程控制的骨架SFC是我个人最喜欢的一种语言尤其适合做设备流程控制。它的核心概念就三个步Step、转换条件Transition、动作Action。设备处于哪个步、满足什么条件跳到下一步、每一步执行什么动作清清楚楚。一条包装线、一个装配工位、一套水处理流程用SFC画出来跟工艺流程图几乎一一对应跟客户评审的时候特别省事。SFC最大的价值在于它强制你把流程拆成离散的状态。很多新手写流程控制喜欢用一堆IF-ELSE嵌套代码写到几百行之后自己都理不清。SFC逼着你把每个稳定状态定义成一个步步与步之间的转换条件必须明确。我做过一个灌装设备项目最初用梯形图写光“等待灌装完成”这个状态就藏在一堆中间继电器里后来改成SFC八个步把整个流程覆盖得干干净净客户看了直接说“这才是我想要的”。但SFC也有坑。步的初始化和复位是新手最容易翻车的地方。设备上电时应该进入哪个步急停之后回到哪个步报警清除后从哪个步继续这些必须在设计阶段就想清楚。我的习惯是上电先进入“初始化步”完成回零、参数加载之后再跳到“待机步”急停触发后跳到“急停步”所有输出强制复位报警清除后根据报警类型决定是回到待机还是继续当前步。这些逻辑用SFC表达特别自然但如果你用梯形图写很容易漏掉某个分支。2.4 指令表IL与结构化文本ST一个在退场一个在崛起IL现在基本可以不用学了。它是类似汇编的文本语言一行一条指令可读性差维护困难。新出的PLC编程软件里IL要么被隐藏了要么直接不支持。我最后一次用IL是十年前在一个老式欧系PLC上之后再也没有碰过。ST则完全相反它是目前工控编程里上升势头最猛的语言。语法类似Pascal和C的结合体支持IF、CASE、FOR、WHILE这些控制结构能定义数组、结构体、自定义功能块。做复杂算法、通信协议解析、数据记录、配方管理ST是唯一的选择。我现在的项目里ST代码占比基本在60%以上梯形图只用来处理急停链和安全逻辑。ST的入门门槛比梯形图高但一旦跨过去效率提升是数量级的。举个例子解析一个Modbus RTU报文用梯形图写可能要几十个网络用ST写就是一个FOR循环加几个IF判断。再比如做PID参数自整定ST里写个数组遍历和极值查找十几行搞定。我建议每个想往上走的PLC工程师至少把ST的IF、CASE、FOR、数组、结构体这五个东西练熟你会发现很多以前觉得麻烦的事情突然变简单了。3. 数据类型与程序结构标准里最容易被跳过但最重要的部分3.1 数据类型选不对程序迟早出问题IEC61131-3定义了一套标准数据类型分三大类基本数据类型BOOL、BYTE、WORD、DWORD、INT、DINT、REAL、TIME等、衍生数据类型数组、结构体、枚举、子范围、泛型数据类型ANY、ANY_NUM等主要用于功能块接口。很多人编程时不太在意数据类型觉得能编译通过就行结果运行起来各种溢出、精度丢失、通信错位。我踩过最典型的一个坑用INT做计数器设备运行了三天计数器到32767之后直接跳回-32768整个逻辑全乱。后来改成DINT问题解决。INT是16位有符号整数范围-32768到32767DINT是32位范围-21亿到21亿。做产量统计、运行时间累计、脉冲计数这些一律用DINT别省那点内存。REAL是32位浮点数精度有限。做PID运算、流量累积、位置控制的时候如果中间变量用REAL多次累加之后误差会累积。我的做法是中间累加用LREAL64位浮点最终输出再转成REAL。有些老型号PLC不支持LREAL那就用DINT做定点运算比如把流量乘以1000变成整数再累加最后显示的时候再除回去。还有一个容易忽视的是TIME类型。标准里TIME的精度是毫秒范围很大。但不同品牌对TIME的实现不一样有的用32位有的用64位。做长延时的时候比如设备运行计时用TIME没问题但做精确定时比如每10ms触发一次任务最好用TON功能块配合循环任务别自己用TIME做减法。3.2 程序组织单元POU把代码切成能复用的块IEC61131-3把程序组织单元分成三类程序Program、功能块Function Block、函数Function。这三者的区别我用一个类比来解释函数像数学公式给同样的输入永远得到同样的输出不记得之前发生过什么功能块像一台设备有内部状态上次调用和这次调用可能结果不同程序像整个车间是顶层调度者。函数适合做纯计算比如求平均值、做单位换算、解析字符串。函数不能有内部状态变量也不能调用功能块。我经常把一些常用的计算封装成函数比如“摄氏度转华氏度”、“流量标定计算”、“CRC校验”放在库里随用随取。功能块适合做有状态的控制比如PID、定时器、计数器、通信端口。功能块有内部变量每次调用会保留上次的状态。PID是最典型的功能块你给它设定值和反馈值它输出控制量同时内部记住积分项和微分项。我见过有人用函数写PID结果每次调用积分项都清零控制效果一塌糊涂。程序是顶层单元一个PLC项目里可以有多个程序分别对应不同的任务。比如“主程序”负责调度“安全程序”负责急停处理“通信程序”负责数据交换。程序之间可以通过全局变量或者直接引用来共享数据。我的经验是先想清楚哪些逻辑需要复用再决定用函数还是功能块。如果一段代码在多个地方出现而且逻辑完全一样就封装成函数如果逻辑一样但需要记住上次状态就封装成功能块。别把所有东西都塞在一个程序里后期维护会想哭。3.3 变量作用域全局变量是万恶之源IEC61131-3里变量分局部变量和全局变量。局部变量在POU内部声明只在该POU内可见全局变量在整个项目里都能访问。很多新手图省事把所有变量都定义成全局的结果程序一大变量名冲突、意外修改、调试困难全来了。我的原则是能用局部变量就用局部变量全局变量只用来做跨POU的数据交换。比如一个功能块内部的中间变量全部声明为局部只有需要跟其他功能块共享的数据比如设定值、状态字、报警字才放到全局变量表里。全局变量命名要有前缀比如g_表示全局fb_表示功能块内部这样一眼就能看出变量的归属。还有一个细节VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT的区别。VAR_INPUT是只读输入VAR_OUTPUT是输出VAR_IN_OUT是引用传递既能读也能写。用VAR_IN_OUT的时候要小心因为它直接操作外部变量如果功能块内部逻辑有bug可能会把外部变量改乱。我一般只在需要返回多个值的时候用VAR_IN_OUT其他情况尽量用VAR_INPUT和VAR_OUTPUT。4. 从标准到落地一个完整项目的编程框架怎么搭4.1 项目拆解先画流程再写代码我接任何一个新项目第一件事不是打开编程软件而是拿纸笔画流程。设备有几个工位、每个工位有几个动作、动作之间的先后关系是什么、异常情况怎么处理这些想清楚了再动手。IEC61131-3的SFC特别适合做这个阶段的工具哪怕最后不用SFC实现用SFC梳理一遍流程也能避免很多逻辑漏洞。举个例子一个简单的搬运机械手动作序列是“原点等待→伸出→夹紧→缩回→旋转→伸出→松开→缩回→旋转回原点”。用SFC画出来就是九个步每个步对应一个动作步与步之间是转换条件。画完之后你会发现有些步可以合并有些转换条件需要加延时或者传感器确认。这个过程花半小时能省掉后面至少两天的调试时间。流程梳理完之后我会把整个项目拆成几个POU主程序负责模式切换和任务调度手动程序负责手动模式下的点动逻辑自动程序用SFC或者ST实现自动流程报警程序处理所有报警的触发、确认、复位通信程序处理跟HMI、上位机、其他设备的数据交换。每个POU各司其职调试的时候可以单独禁用某个POU快速定位问题。4.2 状态机设计用CASE语句实现SFC的等效逻辑有些PLC品牌对SFC的支持不完整或者项目要求必须用ST实现这时候我会用CASE语句写状态机。思路很简单定义一个INT变量step每个值代表一个步CASE语句里根据step的值执行对应的动作动作完成后修改step的值跳到下一步。CASE step OF 0: // 初始化 IF initDone THEN step : 10; END_IF; 10: // 等待启动 IF startBtn AND NOT emergency THEN step : 20; END_IF; 20: // 伸出 cylinderExtend : TRUE; IF extendSensor THEN step : 30; END_IF; 30: // 夹紧 gripperClose : TRUE; IF gripSensor THEN step : 40; END_IF; ... END_CASE;这种写法的好处是逻辑集中、易于调试。在线监控的时候直接看step的值就知道设备在哪个状态比在一堆梯形图里找中间继电器快得多。坏处是如果步数太多CASE语句会很长这时候可以拆成多个CASE或者用状态机功能块。我一般会在每个步里加一个超时定时器比如伸出动作超过3秒还没到位就报警。这个超时逻辑用TON功能块实现每个步配一个超时后跳到报警步。这样设备不会卡在某个步里一直等操作工也能及时知道哪里出了问题。4.3 通信与数据交换标准里没细说但实际绕不开IEC61131-3主要管编程语言和程序结构对通信协议涉及不多。但实际项目里PLC跟HMI、变频器、伺服、上位机、其他PLC的通信占了很大工作量。我的经验是通信程序单独做一个POU用ST写跟逻辑程序解耦。Modbus RTU是最常见的用ST写一个轮询状态机发送请求→等待响应→解析数据→更新变量→下一个从站。每个从站配一个超时计数器连续超时三次就报通信故障。数据解析的时候注意字节序不同品牌的设备对WORD和DWORD的字节排列不一样有的高字节在前有的低字节在前。我一般会写两个函数WordSwap和DwordSwap根据设备手册决定要不要调用。Profinet、EtherCAT这些实时以太网协议一般由PLC的硬件配置工具处理编程层面只需要读写映射好的IO变量。但要注意过程映像区的更新时机有些PLC在循环任务开始时更新输入映像任务结束时更新输出映像如果你在任务中间直接读物理输入可能读到的是旧值。我习惯在程序开头把所有输入变量拷贝到局部变量后面全部用局部变量这样逻辑不受映像区更新时机影响。5. 常见问题与排查技巧实录5.1 数据类型不匹配导致的“灵异现象”现象一个计数器偶尔会跳变比如从100直接跳到-30000多。排查检查计数器变量的数据类型发现是INT设备运行一段时间后超过32767溢出。解决改成DINT问题消失。教训所有可能超过32767的计数、累计、时间变量一律用DINT。现象PID输出偶尔会突然满量程。排查检查PID功能块的输入变量发现反馈值用INT传入但功能块期望REAL隐式转换时精度丢失导致PID误判。解决在传入前用INT_TO_REAL显式转换并做限幅处理。教训功能块接口的数据类型必须严格匹配不要依赖隐式转换。5.2 程序执行顺序引发的逻辑错误现象两个功能块互相引用对方的输出编译通过但运行结果跟预期相反。排查查看编译器的执行顺序规则发现A品牌按从左到右执行B品牌按数据依赖执行。解决在两者之间加一个中间变量打破循环依赖。教训FBD里避免功能块之间的循环引用如果必须用中间变量隔开。现象急停按下后某个输出没有立即断开。排查发现该输出在程序末尾才被写入而急停逻辑在程序开头中间有其他逻辑把它重新置位了。解决把急停逻辑放在程序最后或者用硬件安全继电器直接切断输出。教训安全逻辑要么放在程序最后要么用硬件实现别指望软件能保证实时性。5.3 通信故障排查速查表故障现象可能原因排查方法解决措施通信完全不通物理层断开检查网线、终端电阻、供电重新接线加终端电阻偶尔通信超时波特率不匹配核对双方波特率、数据位、停止位统一通信参数数据错位字节序不一致用已知数据测试观察解析结果调用字节交换函数从站无响应站号冲突逐个从站断开测试修改站号确保唯一通信时断时续电磁干扰检查屏蔽层接地、走线距离加磁环远离动力线5.4 几个我踩过的坑和对应的技巧坑一SFC步的初始化遗漏。设备上电后直接进入自动步但回零还没完成结果机械撞机。技巧上电永远先进入初始化步完成回零、参数加载、通信建立之后再允许进入自动步。坑二ST里的FOR循环没有退出条件。写了一个WHILE循环等传感器信号结果传感器一直没触发程序卡死在循环里看门狗超时。技巧所有WHILE循环必须加超时计数器超过一定次数强制退出并报警。坑三全局变量被意外修改。两个功能块用了同名的全局变量一个写一个读结果数据错乱。技巧全局变量加前缀功能块内部变量加前缀命名规范强制执行。坑四定时器在循环任务里不准。用TON做10ms定时但循环任务周期是20ms定时器实际精度变成20ms。技巧定时精度要求高的场合用硬件中断或者高速计数器别依赖循环任务里的软件定时器。坑五模拟量输入没有滤波。压力传感器信号波动大直接进PID导致输出抖动。技巧模拟量输入先做一阶滤波或者移动平均滤波时间常数根据信号特性调整一般取PID采样周期的3到5倍。6. 工具链与学习路径怎么从入门到能干活6.1 编程软件的选择不同品牌的PLC用不同的编程软件但底层都遵循IEC61131-3。我建议新手先精通一个平台再横向扩展。西门子的TIA Portal、倍福的TwinCAT、CODESYS、三菱的GX Works这四个覆盖了国内大部分项目。TIA Portal生态最全但授权贵TwinCAT和CODESYS对ST和面向对象支持最好适合做复杂算法GX Works在日系设备里用得最多梯形图体验好。我的学习路径是先用一个平台把五种语言都写一遍理解各自的适用场景然后找一个实际项目从流程图开始用SFC或者CASE状态机实现流程用ST写算法用梯形图写安全逻辑最后把常用的功能封装成库比如PID、滤波、通信、报警下次项目直接调用。6.2 仿真与调试技巧大部分编程软件都带仿真功能但仿真跟实际硬件有差异。我的做法是逻辑先在仿真里跑通再下载到硬件上调试。仿真里重点验证流程跳转、报警触发、通信解析这些逻辑硬件上重点调试模拟量精度、通信稳定性、运动控制响应。在线调试的时候善用变量监控表和趋势图。把关键变量加到监控表里实时看数值变化用趋势图记录PID输出和反馈的曲线调参数的时候特别直观。我一般会把所有报警位、状态字、关键模拟量都放到一个监控页面调试的时候一眼就能看出设备状态。6.3 代码规范与版本管理IEC61131-3没有规定代码规范但团队协作必须有。我的规范是变量命名用匈牙利前缀b_布尔、i_整数、r_实数、s_字符串、st_结构体POU命名用动词加名词FB_PID、FC_Average、PRG_Main注释用中文写清楚每个步、每个功能块的作用。版本管理用Git每次修改提交前先编译通过提交信息写清楚改了什么、为什么改。这套规范看起来麻烦但项目大了之后能省大量时间。我见过一个项目三个人同时改代码变量名冲突、逻辑覆盖、版本混乱最后花了整整一周做代码合并。如果一开始就定好规范这些问题根本不会发生。7. 标准之外那些IEC61131-3没告诉你但必须知道的事IEC61131-3管的是编程语言和程序结构但实际项目里还有很多它管不到的地方。比如扫描周期标准里没有规定PLC的扫描周期应该是多少但实际项目里扫描周期直接影响控制精度。我的经验是开关量逻辑扫描周期10ms以内够用模拟量PID控制在20ms左右运动控制需要1ms甚至更短。扫描周期太长控制响应慢太短CPU负荷高可能看门狗超时。再比如内存管理标准里没有规定变量内存怎么分配但实际项目里数组开太大、结构体嵌套太深、字符串操作太频繁都可能导致内存不足或者运行变慢。我的习惯是数组大小按实际需求的1.5倍定义结构体嵌套不超过三层字符串操作尽量用固定长度。还有任务调度IEC61131-3定义了程序组织单元但没有规定任务怎么调度。实际项目里循环任务、事件任务、中断任务的优先级和周期需要仔细设计。我的原则是安全逻辑放最高优先级中断通信放低优先级循环任务PID放中等优先级循环任务HMI刷新放最低优先级。这样保证关键逻辑不受其他任务影响。最后说一个我个人的体会IEC61131-3是底线不是天花板。它保证你的程序在不同品牌之间可移植但不保证你的程序高效、可靠、易维护。真正决定项目质量的是你对工艺的理解、对异常的处理、对细节的把控。标准教你怎么写代码经验教你写好代码。多动手、多踩坑、多总结比背标准条款有用得多。