IEC 61131标准入门:五种PLC编程语言与POU设计实战
1. 从梯形图到标准框架为什么工控人绕不开IEC 61131干工控这行的尤其是搞PLC编程的大概率都经历过这样的场景接手一个老项目打开程序一看梯形图写得密密麻麻一个主程序从头到尾几百个网络段变量命名全是M0.0、M0.1、VW100这种注释几乎没有。想改一个逻辑得先把整个程序通读一遍生怕动了哪里引起连锁反应。更头疼的是同一个功能换个品牌的PLC就得重写一遍三菱的FX系列和西门子的S7-1200指令系统完全不一样连定时器的时基单位都不同。这种“一个品牌一套玩法”的局面在IEC 61131标准推广之前是工控行业的常态。IEC 61131是国际电工委员会制定的可编程控制器标准总共分为十个部分涵盖了从设备要求、编程语言、通信接口到安全规范的全方位内容。其中对普通工程师影响最大的是第三部分——编程语言标准。它定义了五种编程语言梯形图LD、功能块图FBD、顺序功能图SFC、指令表IL和结构化文本ST。这五种语言各有各的适用场景不是让你全都用而是给你一个统一的框架让你在不同品牌、不同项目之间有一套共同的“语言规范”。说白了IEC 61131解决的核心问题是标准化和可移植性。你写的程序逻辑如果按照标准来组织换一个支持该标准的PLC平台移植成本会大幅降低。同时标准还引入了程序组织单元POU的概念把程序、功能块、函数分开管理让代码结构更清晰复用性更强。对于刚入行的朋友来说理解这套标准比死记某个品牌的指令表要划算得多——因为指令会过时平台会更替但标准化的编程思维是通用的。这篇文章适合两类人看一类是刚接触PLC编程、还在被各种品牌指令搞得晕头转向的新手另一类是有一定经验、但一直用“野路子”写程序、想系统梳理知识体系的老手。我会从标准的核心框架讲起拆解五种编程语言的适用场景然后落到实操层面讲清楚怎么用标准化的思路去组织一个项目最后分享一些实际调试中踩过的坑和排查技巧。内容会尽量说人话能用生活类比的地方就不堆术语。2. IEC 61131标准的核心框架拆解2.1 标准的十个部分到底管什么IEC 61131标准并不是一本薄薄的小册子它由十个部分组成每个部分管的事情不一样。很多人只知道第三部分讲编程语言其实其他部分同样重要只是离日常编程稍微远一点。第一部分是通用信息定义了标准适用的范围、术语和基本概念。第二部分是设备要求和测试规定了PLC硬件应该满足什么条件比如电源波动范围、抗干扰能力、工作温度等。这部分主要是给硬件厂商看的做工程的了解一下就行不用深究。第三部分是编程语言这是绝大多数工程师关注的重点五种语言的定义、语法规则、数据类型都在这里。第四部分讲用户指南相当于给用户提供一些选型、安装、维护方面的建议。第五部分是通信定义了PLC之间、PLC与上位机之间的通信规范。第六部分是现场总线通信第七部分是模糊控制编程第八部分是编程语言的实现指南第九部分是单滴数字通信接口第十部分是安全相关的功能。对普通工程师来说第三部分和第五部分是重点。第三部分决定了你怎么写程序第五部分决定了你怎么让设备之间对话。其他的部分知道有这么回事就行真正需要的时候再翻。2.2 五种编程语言的定位与选择逻辑五种编程语言不是让你随便挑一个用它们各自有最适合的场景。选错了语言程序写起来别扭维护起来更痛苦。梯形图LD是最直观的语言它的图形符号和继电器控制电路非常相似。常开触点、常闭触点、线圈、定时器、计数器这些元素对电气背景的人来说几乎零学习成本。梯形图适合写逻辑联锁、开关量控制、简单的顺序控制。但它的缺点也很明显处理复杂数学运算和字符串操作时非常吃力程序规模一大网络段多了之后可读性急剧下降。功能块图FBD用方框和连线来表示信号流向特别适合过程控制和模拟量处理。比如PID控制、信号滤波、量程转换这些功能用FBD画出来一目了然。它的思维方式和信号流图很像搞过自动控制的人会觉得很顺手。但FBD对逻辑分支的处理不如梯形图直观复杂的条件判断用FBD画会变成一团乱麻。顺序功能图SFC是专门为顺序控制设计的。它的核心思想是把一个工艺流程拆成若干个“步”每一步执行特定的动作步与步之间通过“转换条件”来切换。比如一个自动灌装线从“等待瓶子到位”到“开始灌装”到“灌装完成”到“瓶子移出”每一步都很清晰。SFC最大的好处是逻辑结构和工作流程一一对应调试的时候一眼就能看出程序卡在哪一步。但SFC不适合写纯组合逻辑也不适合处理复杂的并行分支。指令表IL是一种类似汇编的文本语言一行一条指令从上到下执行。它的优点是执行效率高代码紧凑适合写对扫描周期要求苛刻的小段逻辑。但可读性差维护困难现在新出的PLC平台很多已经不再支持IL了。我的建议是除非维护老项目否则新项目尽量别用IL。结构化文本ST是五种语言里最像高级语言的语法接近Pascal和C。它适合写复杂的数学运算、循环、条件判断、数组操作、字符串处理。比如你要做一个配方管理用ST写一个查表程序几行代码就能搞定用梯形图得画几十个网络段。ST的缺点是对于纯开关量逻辑写出来不如梯形图直观电气背景的人看起来会有点费劲。实际项目中很少只用一种语言。常见的组合是主控流程用SFC搭框架开关量逻辑用LD写模拟量处理用FBD复杂计算用ST。IEC 61131允许在同一个项目里混合使用多种语言这才是它真正强大的地方。2.3 程序组织单元POU的设计哲学POU是IEC 61131里一个非常核心的概念全称是Program Organization Unit。它把代码分成三种类型程序Program、功能块Function Block和函数Function。程序是最高层的POU它直接和PLC的硬件资源绑定比如输入输出映射、全局变量访问。一个项目里可以有多个程序分别对应不同的任务。功能块是有状态的它内部可以有自己的静态变量调用之间状态会保持。比如一个定时器功能块这次调用时计时到了5秒下次调用时它还记得这个状态。函数是无状态的同样的输入必然得到同样的输出它内部不能有静态变量也不能访问全局变量。为什么要分这么细核心目的是复用和封装。你写一个电机控制功能块把启动、停止、过载保护、运行反馈都封装进去下次遇到同类型的电机直接调用这个功能块就行不用把逻辑再画一遍。功能块的内部实现对外部是透明的你只需要知道它的输入输出接口不需要关心里面怎么写的。这就是模块化编程的思想和软件开发里的函数封装是一个道理。我见过很多新手写程序所有逻辑都堆在Main程序里一个文件几千行变量命名混乱没有任何封装。这种程序能跑但没法维护也没法复用。按照IEC 61131的思路应该把功能拆成独立的功能块主程序只负责调用和协调。这样调试的时候可以单独测试每个功能块出问题也容易定位。3. 核心细节解析与实操要点3.1 数据类型别再用M0.0打天下了IEC 61131定义了一套标准的数据类型这是写程序的基础。很多从老式PLC转过来的人习惯用M0.0、M0.1这种位地址来存所有东西不管它是布尔量、整数还是浮点数。这种做法在简单项目里能凑合但项目一复杂就会乱成一锅粥。标准数据类型分为几大类。布尔型BOOL只有TRUE和FALSE两个值用来表示开关状态。整型包括BYTE8位、WORD16位、DWORD32位、LWORD64位以及有符号的SINT、INT、DINT、LINT。实型有REAL32位浮点数和LREAL64位浮点数。还有时间类型TIME、日期类型DATE、字符串类型STRING等。用标准数据类型的好处是第一编译器会帮你做类型检查你把一个INT赋值给BOOL编译器会报错避免了很多低级错误。第二代码可读性大幅提升看到变量名就知道它是什么类型不用去猜M0.0到底存的是什么。第三不同品牌之间的移植更容易因为数据类型是标准化的。实操中有一个坑要注意不同PLC平台对数据类型的实现有细微差别。比如INT的取值范围标准定义是-32768到32767但有些平台可能会用不同的字节序。在做跨平台移植时涉及数据通信的地方要特别小心字节序问题。另外REAL类型的精度问题也要注意浮点数运算会有累积误差做精确计算时要用LREAL或者改用整数运算。3.2 变量作用域全局变量是万恶之源IEC 61131把变量分为全局变量和局部变量。全局变量在整个项目里都能访问局部变量只在定义它的POU内部有效。新手最喜欢用全局变量因为方便哪里需要就在哪里用。但全局变量用多了程序会变得极其脆弱。你改一个全局变量的值可能影响到十几个地方而且你根本不知道哪些地方在用这个变量。调试的时候你发现某个变量值不对但找不到是谁改的因为所有POU都能改它。正确的做法是尽量用局部变量只在必要的时候用全局变量。功能块的输入输出接口用局部变量功能块之间的数据传递通过接口参数来传而不是通过全局变量。这样每个功能块都是独立的你可以单独测试它不用担心它偷偷改了别的什么东西。全局变量只用在几个地方硬件输入输出的映射、系统级的配置参数、需要跨多个程序访问的共享数据。而且全局变量的命名要有规范比如加前缀“g_”表示全局让人一眼就能看出来。3.3 任务配置别让所有代码挤在一个扫描周期里PLC的工作方式是循环扫描读输入、执行程序、写输出然后重复。默认情况下所有程序都在同一个扫描周期里执行。但实际项目中不同任务的实时性要求不一样。比如急停逻辑要求响应时间越短越好而温度PID控制可能几百毫秒执行一次就够了。IEC 61131支持任务配置你可以把不同的程序分配给不同的任务每个任务有自己的执行周期和优先级。比如高速任务周期1毫秒跑急停和安全逻辑中速任务周期10毫秒跑一般的逻辑控制低速任务周期100毫秒跑PID和数据处理。这样做的好处是第一关键逻辑的响应时间有保障不会被慢速任务拖累。第二CPU资源分配更合理不需要把所有代码都塞进一个扫描周期。第三调试的时候可以单独监控每个任务的执行情况。配置任务时要注意优先级设置。高优先级任务可以打断低优先级任务所以高优先级任务的执行时间要尽量短否则会严重影响低优先级任务的执行。一般来说急停和安全逻辑放在最高优先级运动控制次之过程控制再次数据记录和通信放在最低优先级。3.4 功能块实例化同一个功能块可以反复用功能块和函数的区别在于状态。功能块有静态变量每次调用之间状态保持。这意味着你可以在一个程序里多次实例化同一个功能块每个实例有自己独立的状态。举个例子你写了一个电机控制功能块有启动、停止、过载三个输入有运行、故障两个输出内部有一个定时器做启动延时。你在主程序里实例化三次分别控制三台电机。每台电机的定时器是独立的一台电机的启动延时不会影响另一台。实例化的语法在不同平台上有差异。有的平台用“功能块名_实例名”的方式比如Motor_1、Motor_2。有的平台用VAR声明的方式在变量声明区声明实例。不管哪种方式核心思想是一样的每个实例有自己独立的内存空间。这里有一个常见的坑功能块内部的静态变量在PLC断电后是否保持取决于平台的设置。有些平台默认不保持断电后静态变量清零。如果你的功能块依赖静态变量来记录状态断电重启后状态丢失可能会导致问题。解决办法是在功能块里加一个初始化逻辑或者把需要保持的变量映射到保持区。4. 实操过程与核心环节实现4.1 项目结构规划先画图纸再盖房子拿到一个项目不要急着打开编程软件就开始写。先花时间把项目结构规划好后面写代码会顺畅很多。第一步是梳理工艺流程。把设备的动作顺序、联锁关系、异常处理都列出来。比如一个自动装配机上料、定位、压装、检测、下料每个步骤的进入条件、执行动作、退出条件是什么异常情况怎么处理。这一步不需要考虑PLC就用自然语言描述。第二步是划分功能模块。根据工艺流程把功能相近的部分归到一起。比如上料机构、压装机构、检测机构、下料机构每个机构对应一个功能块。每个功能块负责自己的逻辑对外只暴露必要的接口。第三步是定义接口。每个功能块的输入输出是什么输入包括启动、停止、手动/自动切换、参数设定等输出包括运行状态、故障状态、完成信号等。接口定义清楚了功能块之间怎么连接就清楚了。第四步是分配任务。哪些功能块放在高速任务里哪些放在低速任务里。安全相关的逻辑单独放在最高优先级任务里。第五步才是打开编程软件按照规划好的结构去实现。这样做的好处是程序结构清晰每个功能块职责单一调试的时候可以分模块测试出问题容易定位。4.2 用ST写一个配方管理功能块配方管理是工业控制里很常见的需求。比如一台灌装机要灌装不同规格的瓶子每种规格对应不同的灌装量、灌装速度、延时时间。用梯形图写配方管理会很痛苦用ST写就很简单。先定义配方数据结构。假设每个配方有编号、名称、灌装量、灌装速度、延时时间五个字段。用ST定义一个结构体类型TYPE RecipeType : STRUCT RecipeID : INT; RecipeName : STRING[20]; FillVolume : REAL; FillSpeed : REAL; DelayTime : TIME; END_STRUCT END_TYPE然后定义一个配方数组最多存50个配方VAR_GLOBAL RecipeArray : ARRAY[1..50] OF RecipeType; CurrentRecipe : RecipeType; END_VAR写一个函数根据配方编号查找配方FUNCTION FindRecipe : BOOL VAR_INPUT RecipeID : INT; END_VAR VAR i : INT; END_VAR FOR i : 1 TO 50 DO IF RecipeArray[i].RecipeID RecipeID THEN CurrentRecipe : RecipeArray[i]; FindRecipe : TRUE; RETURN; END_IF; END_FOR; FindRecipe : FALSE;这个函数遍历配方数组找到匹配的编号就复制到CurrentRecipe返回TRUE。如果没找到返回FALSE。用ST写这种逻辑代码量少逻辑清晰改起来也方便。如果用梯形图光那个FOR循环就得画好几个网络段而且很容易画错。4.3 用SFC搭一个自动流程框架SFC最适合写顺序控制。以一个简单的自动灌装流程为例步骤是这样的等待瓶子到位、开始灌装、灌装完成、瓶子移出、回到等待。在SFC里每个步骤是一个“步”步与步之间的连线是“转换”。每个步可以关联一个动作转换关联一个条件。初始步是“等待瓶子到位”转换条件是“瓶子到位传感器为TRUE”。满足条件后进入“开始灌装”步这个步的动作是“打开灌装阀”。转换条件是“灌装量达到设定值”。满足后进入“灌装完成”步动作是“关闭灌装阀”。转换条件是“灌装阀关闭到位”。然后进入“瓶子移出”步动作是“启动传送带”。转换条件是“瓶子离开传感器为TRUE”。最后回到“等待瓶子到位”步。SFC的好处是程序结构和工艺流程一一对应调试的时候一眼就能看出当前在哪一步。如果卡住了直接看哪个转换条件没满足就行。而且SFC支持并行分支和选择分支可以处理复杂的流程。实操中要注意SFC的步不要太多一个屏幕能看完最好。步太多的话可以拆成多个SFC用步标志位来协调。另外SFC的转换条件要尽量简单复杂的判断逻辑放在步的动作里或者单独的功能块里。4.4 混合编程让每种语言干它最擅长的事实际项目中我通常这样组合主控流程用SFC开关量联锁用LD模拟量处理用FBD数据处理和通信用ST。比如一个水处理项目SFC负责整个制水流程的步骤切换LD负责泵阀的联锁逻辑比如泵没启动时阀门不能开FBD负责PH值和浊度的PID控制ST负责和上位机的数据通信和报表生成。这种混合编程的方式让每种语言都在它最擅长的领域发挥作用。SFC让流程清晰LD让联锁可靠FBD让模拟量处理直观ST让数据处理高效。而且IEC 61131标准支持在同一个项目里混合使用这些语言不同POU可以用不同的语言实现。需要注意的是混合编程时POU之间的接口要定义清楚。SFC调用LD写的功能块LD功能块调用FBD写的PID功能块数据通过接口参数传递不要用全局变量乱传。另外不同语言的执行顺序要搞清楚SFC的步动作和LD功能块的调用时机要协调好避免出现竞争条件。5. 常见问题与排查技巧实录5.1 程序能下载但运行异常怎么查这是最常见的问题。程序编译通过下载到PLC也没报错但一运行就不对。排查思路是从外到内从硬件到软件。先查硬件。输入输出模块的指示灯状态对不对传感器信号有没有进来执行器有没有动作。用万用表量一下关键点的电压确认硬件没问题。我遇到过好几次程序查了半天最后发现是传感器坏了或者线接错了。硬件没问题再查软件。先看输入映射对不对PLC读到的输入状态和实际传感器状态是否一致。再看输出映射程序里输出为TRUE时对应的输出点有没有动作。如果输入输出映射有问题检查硬件配置和地址分配。输入输出都正常再查逻辑。用编程软件的在线监控功能看关键变量的实时值。从流程的起点开始一步步往后跟。比如SFC流程看当前卡在哪一步那个步的转换条件为什么不满足。转换条件通常是一个或多个布尔量的组合逐个检查这些布尔量的状态。如果逻辑太复杂在线监控看不出来可以用强制功能。把怀疑的变量强制为某个值看程序行为是否符合预期。但强制功能要慎用调试完一定要取消强制否则会留下隐患。5.2 模拟量采集波动大怎么处理模拟量波动是工控现场的老大难问题。4-20mA信号进来程序里读到的值一直在跳PID根本没法调。先排除硬件原因。信号线有没有用屏蔽线屏蔽层有没有单端接地。信号线有没有和动力线走在一起变频器、伺服驱动器这些干扰源离信号线太近。传感器供电是否稳定电源纹波大不大。这些硬件问题不解决软件滤波都是治标不治本。硬件没问题再上软件滤波。最简单的是算术平均滤波连续采N个值求平均。N越大越平滑但响应越慢。一般N取8到16比较合适。代码用ST写很简单FUNCTION_BLOCK AvgFilter VAR_INPUT RawValue : REAL; SampleCount : INT : 8; END_VAR VAR_OUTPUT FilteredValue : REAL; END_VAR VAR Buffer : ARRAY[1..16] OF REAL; Index : INT : 1; Sum : REAL : 0; i : INT; END_VAR Buffer[Index] : RawValue; Sum : 0; FOR i : 1 TO SampleCount DO Sum : Sum Buffer[i]; END_FOR; FilteredValue : Sum / SampleCount; Index : Index 1; IF Index SampleCount THEN Index : 1; END_IF;如果波动还是有规律的高频噪声可以用一阶滞后滤波也叫低通滤波。公式是滤波值 滤波系数 × 本次采样值 (1 - 滤波系数) × 上次滤波值。滤波系数越小滤波效果越强但滞后越大。一般取0.1到0.3之间。5.3 通信丢包或超时排查思路PLC和上位机、PLC和PLC之间的通信偶尔丢一包两包很正常但如果频繁丢包或超时就要排查了。先看物理层。网线接头有没有松动水晶头有没有氧化。网线长度有没有超过100米中间有没有强干扰源。如果是串口通信波特率、数据位、停止位、校验位是否匹配通信线有没有接错。物理层没问题再看协议层。通信双方的协议参数是否一致比如Modbus的从站地址、寄存器地址、数据类型。用抓包工具抓一下通信报文看请求和响应是否匹配。如果请求发出去了但没有响应可能是从站地址错了或者从站没上电。如果响应了但数据不对可能是寄存器地址或数据类型不匹配。还有一个常见问题是通信负载太高。如果通信周期设得太短或者一次请求的数据量太大PLC处理不过来就会超时。解决办法是降低通信频率或者把大数据量拆成多次小数据量请求。另外通信任务不要放在高优先级任务里否则会占用太多CPU时间影响控制逻辑的执行。5.4 常见问题速查表问题现象可能原因排查方法解决措施程序下载后无输出输出映射错误检查硬件配置和地址分配重新配置输出地址模拟量波动大干扰或滤波不足检查屏蔽线和接地加滤波功能块通信频繁超时通信负载过高抓包看请求响应间隔降低通信频率SFC卡在某一步转换条件不满足在线监控转换条件变量检查传感器和逻辑功能块状态丢失静态变量未保持检查变量保持设置映射到保持区任务执行时间过长低优先级任务被抢占监控各任务执行时间调整任务优先级数据类型不匹配隐式类型转换检查变量声明显式转换或修改变量类型5.5 几个容易踩的坑第一个坑定时器的时间基准搞混。不同PLC平台的定时器时间基准不一样有的是1毫秒有的是10毫秒有的是100毫秒。你设一个定时器为100以为是100毫秒结果实际是10秒。移植程序时一定要确认时间基准。第二个坑整数溢出不报错。INT类型的范围是-32768到32767你做一个加法结果超过32767PLC不会报错它会回绕到-32768。这种问题很难查因为程序不报错但结果完全不对。解决办法是预估数值范围该用DINT就用DINT该用REAL就用REAL。第三个坑浮点数比较不能用等号。REAL类型的0.1加0.2不等于0.3因为浮点数有精度误差。比较浮点数时要用一个很小的误差范围比如绝对值小于0.001就认为相等。第四个坑SFC的步动作只执行一次还是持续执行。不同平台的实现不一样。有的平台步动作在进入步时执行一次有的平台步动作在步激活期间持续执行。用之前一定要查手册确认否则逻辑会出错。第五个坑功能块的实例名不能重复。同一个功能块可以实例化多次但每个实例的名字必须唯一。如果两个实例重名编译会报错。命名时最好加上前缀或后缀比如Motor1_Ctrl、Motor2_Ctrl。6. 从标准到实践一些个人体会IEC 61131标准的内容很多但核心思想其实就几条标准化数据类型、模块化程序结构、任务分级管理、多语言混合编程。这几条吃透了剩下的都是细节。我刚开始学PLC的时候也是从梯形图入门的觉得梯形图最直观别的语言都不想碰。后来做了一个大项目程序写到几千行梯形图已经乱得没法维护了才被迫去学ST和SFC。学完之后发现ST写数据处理是真的香SFC写流程是真的清晰。现在我的习惯是能用SFC搭框架就用SFC能用ST写逻辑就用ST梯形图只用来写简单的联锁和手动逻辑。对于新手我的建议是先把梯形图练熟这是基础。然后学ST这是趋势。SFC和FBD看项目需要遇到了再学。IL了解一下就行新项目基本用不上了。还有一点很重要不管用什么语言变量命名一定要规范。我见过太多项目变量名是a、b、c、temp1、temp2过两个月自己都看不懂。好的命名应该能看出变量的用途和类型比如Pump1_Start、Valve2_Open、Temp_PV、Temp_SP。多花几分钟命名后面省几个小时调试。最后说一个实际调试中的小技巧在程序里加一个“调试模式”开关。调试模式下一些关键变量会记录变化历史方便回溯。比如SFC的步切换记录每一步的进入时间和退出时间出问题的时候一看就知道卡在哪一步、卡了多久。这个功能用ST写很简单但调试的时候能省很多时间。