☰
彻底搞懂IEC 61131标准:PLC五种编程语言与实战架构
2026/10/24 11:40:36 网站建设 项目流程

开头

做了这么多年工业自动化,经常有刚入行的同事拿着PLC的编程手册问我:梯形图里这个指令到底什么意思?为什么这个功能块在国产PLC里和进口PLC里长得不一样?怎么同样的程序,换个PLC型号就跑不起来了?

这些问题背后,其实都指向同一个根源——对IEC 61131标准不熟悉。这个标准说白了就是给PLC编程立的规矩,从硬件结构、编程语言到通信方式都有明确规范。你只要吃透它,再去看任何品牌的PLC,都会发现它们只是长相不同,骨架和逻辑是相通的。

这篇东西就是我基于IEC 61131标准和PLC编程基础知识做的一次完整梳理。我不打算讲成学院派那种论文风格,而是按实际项目里你可能遇到的顺序来:先把这个标准到底管什么拆清楚,再讲PLC扫周期这些底层机制,接着给你五种编程语言各自的特点和选型思路,最后结合我现场调试踩过的坑,聊聊程序架构和常见的疑难问题。适合刚接触PLC的电气工程师、自学工控的爱好者,还有那些被五花八门的PLC品牌绕晕、想一次性建立系统认知的人。

代码我会用几个主流品牌的通用写法来举例,讲原理的地方尽量避免绑定某个品牌,这样才能让你真正建立起迁移能力——换品牌、换平台不慌。

1. 项目核心拆解:IEC 61131标准的全景图谱

1.1 标准家族:不止是编程语言这一件事

很多人一听到IEC 61131,第一反应就是“那个PLC编程语言标准”。这么说没错,但不全面。IEC 61131其实是国际电工委员会制定的一个系列标准,面向工业可编程控制器的方方面面,目前完整的分部包括:

分部编号核心内容
第1部分IEC 61131-1通用信息,包括术语定义、基本特性
第2部分IEC 61131-2硬件要求与测试,涉及I/O电气特性、EMC等
第3部分IEC 61131-3编程语言与编程模型,最核心的分部
第4部分IEC 61131-4用户指南,偏工程应用指导
第5部分IEC 61131-5通信与服务规约
第6部分IEC 61131-6功能安全相关
第7部分IEC 61131-7模糊控制编程
第8部分IEC 61131-8编程语言应用的指导方针,在第3部分基础上做应用指引

在实际工程里,我们打交道最多的是第3部分,也就是IEC 61131-3。它定义了PLC软件模型、通信模型和五种编程语言,是每一款PLC组态软件底层逻辑的蓝本。第2部分关心的是硬件选型时的电气指标,比如数字量输入是漏型还是源型、模拟量通道的分辨率,这块更接近电气设计的工作范围。

这里想特别说一句:很多自学的朋友只看编程手册,从来不翻IEC标准原文,这也能学会,但遇到奇怪的问题容易抓瞎。举个例子,手册里说某个指令“在当前扫描周期立即生效”,你如果不清楚标准中软件模型的设计,就不理解为什么有的变量读写是即时的、有的是排队等候的。所以我的建议是,IEC 61131-3的原文(网上很容易找到英文版或中文翻译版)值得花时间精读一遍,尤其第2章、第3章,做底层逻辑的扫盲。

1.2 为什么IEC 61131-3是各品牌PLC的"共同语言"

以前PLC市场是百家争鸣的,各家编程方式差异巨大,同样一个“电机启动控制”,在A家的梯形图是基于触点和线圈的,在B家是基于逻辑块的,在C家甚至要用BASIC写一小段脚本来实现。而IEC 61131-3出现后,逐渐统一了软件模型和语言框架。

要注意,“统一”不等于“一模一样”。各个PLC品牌都宣称自己符合IEC 61131-3,但细节上有各自的方言。比如IEC规定变量有全局变量和局部变量之分,2003年以前的老标准还区分“直接表示法”(比如%I0.0这种)和“符号表示法”。但实际用下来,有的PLC组态软件里你根本看不到%I这种写法,全靠符号名,另外一些保守派平台则两种都支持。

我对这个现象的理解是:IEC 61131-3更像一个语法和架构的公约数,保证你学了一家的梯形图后,换到另一家能快速迁移,不至于从零再来。它规定了五类编程语言以及它们之间的组合方式(比如梯形图里嵌结构化文本表达式、结构化文本里调用功能块),同时也允许厂商做自己的扩展指令集。所以你不需要纠结“为什么这个指令别家没有”,只要记住:标准框架保证你“掌握底层规律”,扩展指令决定你要“短时间适应具体平台”。

1.3 两种经典编程模型:文本类与图形类

IEC 61131-3把五种语言分成了两类:文本类语言——指令表(IL)和结构化文本(ST);图形类语言——梯形图(LD)、功能块图(FBD)和顺序功能图(SFC)。

图形类语言直观、容易排查,电气工程师尤其是从继电器控制电路转行过来的,对梯形图天生亲近;功能块图适合做闭环控制算法、逻辑连接复杂的场合;顺序功能图在描述多步骤流程时非常给力(瓶装线灌装、自动化装配线的节拍逻辑)。文本类语言更接近高级编程语言,在数学运算、复杂算法、大量重复逻辑的场景下,效率极高。我后面会专门用一节把五种语言逐一拆开。

2. PLC运行机制:扫描周期与软元件体系

2.1 扫描周期是PLC的"心跳"

不能跳过扫描周期来谈PLC编程,因为它决定了你写的程序每一个循环里做了什么、什么时候做。

传统PLC的核心工作方式是循环扫描,每个扫描周期大致包含三个阶段:读输入(把物理输入点状态采入映像区)、执行用户程序(按顺序扫描执行你的逻辑)、写输出(把输出映像区的状态刷新到物理输出端)。这个过程循环往复,就是扫描周期。如果程序里还有通信任务、自诊断任务,这些通常会穿插在周期中,或者在周期末统一处理。

我实际测过一个中型PLC项目:程序量大概2000条结构化文本,带四个模拟量闭环PID回路,整个扫描周期大约在8毫秒到12毫秒之间。其中输入采样大概0.5毫秒,程序执行占大头,输出刷新不到1毫秒。然而如果加了人机界面通信和现场总线轮询,扫描周期会被拉长到几十毫秒,甚至超过100毫秒——这在高速控制场景下是非常要命的。

提示:扫描周期决定了系统实时性的上限。对毫秒级高速控制(比如飞剪切断、张力控制),优先考虑带中断功能或独立运动控制通道的PLC,别把关键动作放到普通逻辑里。

理解扫描周期是排查一堆“灵异事件”的关键。比如你写了一段代码,当某个输入点闪烁一次时,累计计数加1。但你如果是在输出刷新后用普通线圈去锁存这个信号,往往计数会丢掉,因为脉冲比扫描周期还要短,没被程序捕捉到。正确的做法是用“沿检测指令”——检测上升沿/下降沿,或者用中断输入模块。我第一次做设备时就在这上面栽过跟头:客户反馈计数器一天下来少了好几十个。后来用示波器量了那个接近开关的输出脉冲,发现最短的只有0.5毫秒,而当时PLC扫描周期是5毫秒,三个周期内输入信号就消失了。

这个教训很直观:PLC不是万能的,它的IO采样是一次扫描周期快照,不是连续实时流。

2.2 软元件体系:I、Q、M、T、C、D到底怎么用

很多老派PLC教材把内部存储器说成“软元件”,这说法虽然老,但很形象。以主流品牌为例,常见的存储区是这样的:

符号名称作用使用注意
I输入映像区存放物理输入点状态程序里只读
Q输出映像区存放物理输出点状态程序里可读可写,但最终要在周期末刷新
M内部中间继电器区存放中间逻辑状态数量通常很大,安全起见把变量名起清楚
T定时器区计时功能不同分辨率:1ms/10ms/100ms
C计数器区计数功能分普通计数和高速计数两类
D数据寄存器存放数值数据可保存模拟量、计算中间值、配方等

这个体系对理解变量声明很有帮助。到了符合IEC 61131-3的现代PLC平台中,变量更像高级语言里的命名变量——你直接声明一个“B00_Start”是BOOL类型,“PID_INT_ValveOut”是REAL类型,根本不用操心它落在哪个物理区。但底层还是那个映射表:布尔变量会落在M区,数值变量会分配到D区或数据块区。

所以哪怕用高级命名,也别丢掉底层概念,否则做底层通信时(比如用Modbus读写保持寄存器)会懵:我怎么把DB块里的值对应到Modbus地址上去?其实组态软件都提供了地址映射关系,你要做的只是把变量按合理的方式排列好,别频繁改动变量声明导致地址错位。

2.3 通信对扫描机制的影响

现代PLC没有通信独立存在的。EtherNet/IP、PROFINET、Modbus TCP、CANopen、串口,都是扫描周期之外的异步任务。当PLC在执行程序时,通信任务由独立的通信芯片或多个核心任务调度,但数据交换通常还是通过缓冲区。因此当你用PLC做现场总线主站时,会看到总线上从站的输入数据有一点点延迟——这是由接口刷新周期和总线循环时间共同决定的。

这对编程意味着什么?我会这么处理:对于跨通信的数据一致性要求高的数据,统一在主程序开头一次性进行读写缓冲区映射,不要在程序中间反复跨通信区读写;通信故障标志要加“去抖”延时,避免瞬间断连引发误动作。这些都是实战中血泪换回来的经验。

3. 五种编程语言:选型思路与实战用法

3.1 梯形图:继电器电路的数字化身

梯形图对电气出身的人最友好。老式继电器控制电路里,一个自锁回路需要中间继电器、接触器辅助触点、按钮常开常闭互锁;而梯形图里就是一条线圈输出指令,配合并联的常开触点实现同样的功能。

我第一次带新人时,给他的任务就是把一张传统继电器回路图纸改成梯形图。他照着图一个一个触点翻译,最后做好后功能正常,但程序冗余度很高。原因是他把继电器电路里为了省触点而做的复杂逻辑也1:1搬过来了。实际上PLC内部逻辑资源几乎是无限的,并联串联完全自由,不需要考虑“触点数量不够”的问题。从这个角度说:梯形图的精髓是按逻辑关系来组织,而不是按电气原理图来抄。

使用梯形图时要注意几点:

  • 程序扫描方向是从左到右、从上到下。输出线圈放在最右侧,不能把线圈放在中间当中间节点。
  • 同一输出线圈在一个扫描周期里不要被多个条件重复赋值,这种“双线圈”问题很容易埋雷,后面PLC按最后一个赋值结果刷新,前面写的就白写了。
  • 梯形图适合逻辑控制,但遇到PID、复杂数组运算,就很吃力了。这种时候要么用功能块,要么混用ST。

3.2 功能块图:把算法当积木搭

FBD的直观性不比梯形图差,但它更多体现了“数据流”的思想。每个功能块有输入、输出,信号从左向右流动,各个块之间连起来形成一个信号处理链路。

在模拟量处理上,FBD特别好用。比如把传感器4~20mA信号变成工程量值,典型链路是:读取原始值→量程转换→滤波→标定→输出。每个环节用对应的功能块(SCALING、FILTER、CALIBRATE),连线搭好,逻辑清清楚楚。如果在FBD里看到用户程序,能很快定位是哪一段算法出了问题。

选FBD要注意:

  • 功能块连线要养成“源点输出接目标输入”的习惯,避免拉出很乱的连线。
  • 相同类型的功能块如果出现多个实例,注意每个实例的独立内存,不能想当然共用。
  • 函数块图在大型复杂程序里层级一深就容易乱,适当把子功能封装成子例程或自定义功能块,别把几十个块全铺在同一层。

3.3 结构化文本:算法复杂度的最优解

如果你要处理大量数据、写复杂算法、做数组循环,ST几乎是唯一的高效选择。它的语法和Pascal、C有类似的地方,支持IF/ELSE、CASE、FOR、WHILE、REPEAT这些控制结构,支持数组、结构体,还能声明函数(FUN)和功能块(FB)。

我们举个传感器校准的例子,用ST写就很丝滑:

FUNCTION SensorScale : REAL VAR_INPUT rawValue : INT; // PLC模拟量输入寄存器原始值(0~32767) rawMin : INT; // 量程下限对应的AD值 rawMax : INT; // 量程上限对应的AD值 engMin : REAL; // 工程量下限(比如压力0 MPa) engMax : REAL; // 工程量上限(比如压力10 MPa) END_VAR VAR clamped : INT; END_VAR IF rawValue < rawMin THEN clamped := rawMin; ELSIF rawValue > rawMax THEN clamped := rawMax; ELSE clamped := rawValue; END_IF; SensorScale := engMin + (REAL(clamped) - REAL(rawMin)) / (REAL(rawMax) - REAL(rawMin)) * (engMax - engMin);

这段代码做的事:把AD原始值限制在有效量程内,再线性映射到工程单位。老式做法可能要用十几个梯形图指令块加上一堆中间变量,而ST只需要一次数学运算。可维护性也好,后面要改量程,只需要改引脚参数。

写ST要养成结构化的习惯。变量命名要统一规范,比如输入信号前缀是In_,输出是Out_,中间量是Temp_,定时器是T_。我看过太多工程代码,变量名像a1、b2、temp这种,三个月后自己都看不懂。写代码时把注释写起来,也可以像高级语言一样在关键分支里加“状态跳转说明”。

3.4 指令表:旧日王者,今日配角

IL的感觉很像汇编,一条指令一行操作码加操作数。它很紧凑、效率高,但可读性差,现在新项目中越来越少直接使用了。我自己的经验是:在入门阶段可以花少量时间读懂IL,因为某些老设备维护时你会遇到;但新程序完全可以用ST替代,ST能表达的IL几乎都能表达,且结构更清楚。

如果哪天真要维护IL写的程序,别硬啃,直接把IL转成FBD或LD视图(很多组态软件有这种转换功能),看着图形去理解逻辑,效率高很多。

3.5 顺序功能图:多步骤流程的"剧本"

SFC把一个大流程切成多个“步”,步之间有“转移条件”。只要转换条件满足,流程就从当前步切到下一步。每一步可以关联要执行的动作,比如置位输出、启动定时器、调用功能块。这个思想特别适合描述带有明确节拍、步骤、条件的生产流程——比如一个工件从定位、夹紧、钻孔、检测到放行的全自动装配单元,拿SFC写出来就是一张工艺流程圖,调试直观。

SFC有几个要特别当心的点:

  • 流程切步的瞬间,被跳过的步里面相关输出要执行复位逻辑,不然设备保持上一个状态容易出事故。建议在新的流程程序里写“步激活时的输出动作+步退出时的复位动作”,分开定义。
  • 多分支SFC,也就是选择性分支和并发分支,需要小心初始化默认状态,防止一上电就双分支同时跑。
  • 实际工程中我通常只用SFC做高层的工艺管理,把每个具体动作逻辑放到梯形图或ST子程序里,SFC只管流程状态机的切步条件。

4. 从零搭一个工程:程序结构、变量规划与状态机思想

4.1 主程序 + 子程序 + 功能块的分层组织法

进入实际工程时,不要把自己的代码全堆在主程序里,否则几百条程序叠在一起,排查问题非常痛苦。

我惯用的分层方式是:

层级职责示例
主程序(PRG)调度、IO刷新、流程总控调用初始化子程序、调用状态机
功能块(FB)封装一个独立的控制功能电机控制块、PID调节块、报警管理块
函数(FUN)无记忆功能的算法计算模拟量缩放、工程单位换算
子程序(子例程)一组相关的执行逻辑手自动切换逻辑、故障复位逻辑

这样规划之后,主程序可能只有几十行,而每个功能块有几百行,但每个块的输入输出接口一目了然,排查问题直接定位到对应块,不用全局乱搜。

4.2 做一个电机控制功能块:从接口定义到实现

我们拿一个最常见的交流电机启停控制来实战。这个FB的输入有手动/自动切换、手动启动命令、手动停止命令、自动启动命令、故障信号;输出有电机运行反馈、故障状态、报警信息。

FUNCTION_BLOCK FB_MotorCtrl VAR_INPUT i_bEnable : BOOL; // 功能块使能 i_bManStart : BOOL; // 手动启动 i_bManStop : BOOL; // 手动停止 i_bAutoStart: BOOL; // 自动启动 i_bFault : BOOL; // 外部故障信号 END_VAR VAR_OUTPUT o_bRun : BOOL; // 电机运行输出 o_bAlarm : BOOL; // 报警指示 END_VAR VAR r_qRun : RS; // 锁存运行命令 r_qFault : RS; // 锁存故障 END_VAR // 故障锁存处理 r_qFault.SET1 := i_bFault; IF NOT i_bFault THEN r_qFault.RESET1 := TRUE; END_IF; o_bAlarm := r_qFault.Q1; // 运行逻辑:手动优先,故障强制停止 IF o_bAlarm THEN r_qRun.RESET1 := TRUE; ELSIF i_bEnable THEN IF i_bManStart OR i_bAutoStart THEN r_qRun.SET1 := TRUE; ELSIF i_bManStop THEN r_qRun.RESET1 := TRUE; END_IF; END_IF; o_bRun := r_qRun.Q1;

这个块体现了几个关键思想:

  • 故障用锁存器保存,故障消失后还要一个单独的“复位”动作才能清除。为什么?如果故障瞬断闪了一下,设备不会因为故障消失而自己恢复运行,避免安全隐患。
  • 手动启动和自动启动共用同一个运行锁存器,内部不需要区分当前手自动模式。设备启动条件一旦满足,就是一个高电平命令。
  • 停止逻辑用RS触发器复位端,优先级高过启动。这确保停止和故障在任何情况下都能打断启动状态。

4.3 状态机是流程控制的灵魂

之前说的SFC,核心其实就是状态机思想。就算你完全用ST或LD写程序,也应该在脑子里建立状态机的视角。

我习惯这样实现一个设备的主状态机:

TYPE Enum_State : ( IDLE, // 待机 INIT, // 初始化 RUNNING, // 运行 PAUSED, // 暂停 FAULT, // 故障 FINISHED // 完成 ); END_TYPE VAR e_State : Enum_State := IDLE; e_NextState : Enum_State; END_VAR CASE e_State OF IDLE: IF i_bStart THEN e_State := INIT; END_IF; INIT: // 执行初始化动作,比如复位轴、清报警 IF bInitDone THEN e_State := RUNNING; END_IF; RUNNING: IF i_bPause THEN e_State := PAUSED; ELSIF bCycleDone THEN e_State := FINISHED; END_IF; PAUSED: IF NOT i_bPause THEN e_State := RUNNING; END_IF; FAULT: // 等待故障复位 IF i_bReset AND NOT o_bAlarm THEN e_State := IDLE; END_IF; FINISHED: IF i_bReset THEN e_State := IDLE; END_IF; END_CASE;

写状态机有几个原则:

  • 每个状态里,绝对不要把输出动作和切状态条件混在一起写,而是要分清“进入状态做什么”“状态中持续做什么”“退出状态做什么”。
  • 所有状态都要有出口。最怕的状态是停在某个状态里出不来,要么卡在等待一个永远不满足的条件,要么在状态内部死循环。
  • 设计状态转换条件时,要加入超时监控。越复杂的设备越要给你的每个状态加最长停留时间,超时就触发报警,能帮你省掉大量排查时间。比如某状态逻辑上50毫秒就该跳走,但实际停在那里2秒,这绝对是现场问题信号。

4.4 全局变量还是局部变量:一个取舍问题

IEC 61131-3里,变量有VAR、VAR_INPUT、VAR_OUTPUT、VAR_IN_OUT、VAR_GLOBAL之分。功能块和函数内部有局部变量区,主程序也有局部区;全局变量则存在一个全局符号表中。

我的经验是:尽量把数据封装在功能块内部,全局变量只放跨模块的通信信号和IO映射。为什么?全局变量一旦滥用,程序运行一段时间后,你不知道谁在什么时候改过它,很容易出现莫名其妙的逻辑跳动。这在多人在一个工程里协作开发时空前致命。

但也不能极端到全部用局部变量。比如几个功能块确实需要共享一个参数(比如整条线的模式选择),那这个就应该设计为全局变量,并在程序启动时集中一次赋值。这相当于把这些公共信号看成总线,需要统一管理。

5. 常见问题与排查技巧实录

5.1 双线圈冲突:最隐蔽的定时炸弹

双线圈问题在前面反复提到。简单说就是同一个布尔输出,在主程序的不同位置被赋值了多次。由于PLC逐行扫描的特性,最终输出值是最后一个赋值语句的值。前面赋的值如果原本想的是“置位”,最后被后一条“复位”覆盖,设备就动作异常了。

排查经验:一旦发现设备某个输出不知道怎么该亮还是该灭,先在组态软件的交叉引用视图里搜该变量,看有没有多个输出线圈引用。这种情况救了我好几次。修复方式是:把所有对这个输出的控制条件汇总到一个位置,集中一次赋值,或者拆成多个中间量最后做一个“或”逻辑合并。

5.2 扫描周期和时序:为什么我的计数抖动或者动作慢半拍

开头已经提到过这个问题,再做一点扩展。PLC的输入采样是一次性“快照”,在一个扫描周期内,程序多次读取同一个输入点,得到的都是同一个值,除非使用立即读指令。这样保证了程序运行过程中的逻辑一致性,但同时意味着信号变化不及时。

如果你用普通输入点检测高速脉冲(比如编码器A相输出),扫描周期可能会导致严重的计数丢步。解决路径:

  • 使用PLC的高速计数通道,由硬件计数,和扫描周期完全独立,程序周期性读取计数值即可。
  • 对于普通的按钮输入,加延时去抖。一般PLC自带数字滤波功能,设置在1ms到10ms,足以滤掉机械触点抖动。用软件延时去抖时要注意,如果放在主程序里做,需确保每次只加一次延时逻辑,不要多处累加。
  • 输出设备如果响应太快或太慢,需要先确认是程序中逻辑导致的延迟还是输出模块本身的响应时间。测量方法是用PLC的输出监视位加一个测试脉冲,实际量一下物理输出点。

5.3 模拟量信号跳动:先排查硬件还是先排查程序

模拟量抖动是个大课题。我见过新手上来就写滤波程序,其实问题出在屏蔽层没接地。反过来,也有工程师在现场排查了半天接线、看了示波器,最后发现是程序没有滤波,传感器的微小波动被直接当作有效值用了。

正确姿势是分步排查:

  1. 先用万用表/示波器在PLC端子处量原始信号,看信号本身是否稳定。
  2. 如果信号本身不稳定,优先检查接地、屏蔽、走线,避免信号线与动力线平行过长距离。
  3. 如果信号本身稳定但PLC读上来的值跳,检查模拟量模块的滤波设置和转换速率。
  4. 再做软件处理:一阶惯性滤波或移动平均。注意滤波时间常数不能设太大,否则系统响应变慢,闭环控制会出现震荡。

下面给一个ST的一阶惯性滤波功能块代码,这种写法在各类温控、压力闭环里很常用:

FUNCTION_BLOCK FB_FilterPT1 VAR_INPUT rwIn : REAL; // 原始信号 rTau : REAL; // 滤波时间常数(秒),实际约等于时间常数 rTs : REAL; // 采样周期(秒),一般设成和扫描周期一致 rPrevOut : REAL; // 上一周期输出值(必须在功能块外部保存) END_VAR VAR_OUTPUT rwFiltered : REAL; // 滤波后的信号 END_VAR VAR factor : REAL; END_VAR factor := rTau / (rTs + rTau); rwFiltered := factor * rPrevOut + (1.0 - factor) * rwIn;

如果更多个周期递归,考虑改成移动平均或FIR滤波。但PT1模型已经能应付80%的工业现场需求。

5.4 定时器精度不够:工艺时间怎么老对不上

不同品牌、不同分辨率的定时器,应用场景差别很大。10ms定时器在小时序控制里完全够用,但如果你在做一个高速贴标机需要精确到毫秒级控制,就得使用1ms定时器或者直接用系统时钟(在ST里可以直接读毫秒运行时间),甚至用系统实时性高的硬件定时中断。

另外注意,普通定时器是基于扫描周期来累计的,不是独立时钟。如果扫描周期不稳定,比如通信任务密集占CPU,定时器计时会偏慢。对精度要求高的场合,尽量用专用定时中断或读系统时间戳做差值的方案。

举个例子,我做过一个包装线上的气缸动作时序控制。工艺要求从触发信号到气缸伸出到位后开始下一动作,精确到10ms以内。按扫描周期10ms估算,光计时误差就可能到20ms。后来改成用中断模块读取专用定时器计数,逻辑处理放在中断任务里,才达到时序要求。所以不要以为任何时间控制都能交给PLC的普通定时器就完事了。

5.5 浮点数陷阱:PLC里的REAL不用等于上层软件里的FLOAT

IEC 61131-3里REAL类型默认是32位浮点,对应C语言的float。很多工程师从C++团队转来工控,下意识认为实数都是64位双精度,结果在精度敏感场景(比如长时间累计流量用作贸易结算)吃了大亏。

经验是:单精度浮点在10的7次方级别之内能精确表示到个位,超出这个范围很容易出现精度误差。大量累计计数器如果数值很大还频繁加减,要用LREAL(64位)或者干脆使用整数累加,或者把累计值拆成整数部分和小数部分分别处理。

还有灰度映射的问题:模拟量模块读出来的原始值有符号整型/无符号整型/单极性/双极性之分。转换时务必统一为REAL再计算,避免符号扩展搞错方向,导致负工程量换算错误。我见过锅炉液位显示负值的闹剧,说到底就是16位有符号整型转32位浮点时符号位处理错误。

下面是一个稳妥的工程量转换函数模板:

FUNCTION F_ScaleAnalog : REAL VAR_INPUT nRaw : INT; // 原始AD值 nRawMin : INT; // 对应量程下限 nRawMax : INT; // 对应量程上限 fEngMin : REAL; // 工程量下限 fEngMax : REAL; // 工程量上限 END_VAR VAR fRatio : REAL; END_VAR fRatio := (REAL(nRaw) - REAL(nRawMin)) / (REAL(nRawMax) - REAL(nRawMin)); F_ScaleAnalog := fEngMin + fRatio * (fEngMax - fEngMin);

5.6 程序丢失、备份和文档:比技术参数更重要的工程习惯

最后说一个最容易让工程师崩溃的问题:PLC程序丢了。

我见过不止一个现场:设备调试完成、正常生产了两年,突然某天PLC电池没电或者存储卡坏了,CPU里程序全空,而负责当初调试的人已经离职,技术资料只有一张纸的接线图,程序忘了从设备里上载备份。最后只能按设备动作倒推恢复程序,耗时以周计算。

工程规范上,每个项目必须在关键节点做上载备份,至少保留三处:本机工程文件、现场存储U盘/SD卡、项目文件服务器。程序里加规范注释,让接手的人能看懂。

另外,很多PLC支持在线修改程序,这是强大的功能,但也是隐患。我在修改在线程序时一定会第一时间备份原始版本,修改完后再次上载备份,标注好版本号、修改内容、修改人、日期。这个习惯看起来慢,但日积月累,在项目交接、故障排查时会让你省十倍的时间。

结尾

这些年在工控现场摸爬滚打,最大的体会是:PLC编程看着是个软件活,实际上是硬件、工艺、时序和软件的综合艺术。IEC 61131标准是骨架,扫描周期是心跳,五种编程语言是工具,而真正让一个工程师值钱的,是面对现场复杂工况时那种“知其然,更知其所以然”的定力。

如果你现在还在纠结该主学梯形图还是结构化文本,我的建议是:以梯形图入门,用ST解决复杂算法和数据处理,功能块图用来梳理信号流,SFC管好工艺流程。不要迷信单一语言,真正的项目往往是混合编程。每一款PLC厂家的培训手册都要看,但心里有一根IEC 61131的准绳,你就不会迷失。

最后再分享一个小习惯:每调试完一个项目,把当初踩过的坑、用过的代码片段整理成一个自己的“模板库”。下次做类似设备时,你会发现自己不光快了,而且稳。工控这行,经验就是最大的财富,而把经验变成可复用的资产,是每一个工程师都值得做的事。

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

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

立即咨询