做过大型生产线的朋友,应该都体会过那种“一收一放之间全是隐痛”的状态:六十多个工位的程序打开以后,光搜索“电机启动”就能搜出三四十段逻辑,每段的定时器不同、保护方式不同、复位方式也不同。设备一报警,电气同事得像考古一样在几十个程序块里翻监控点。我自己后来做了一套基于西门子S7-1500、S7-1200平台的模板程序,版本固定在博途 V15.1,把电机和气缸这类标准化设备全部抽象成功能块,下面把整个设计思路、核心代码框架和联调阶段踩过的坑完整拆出来,适合正在做非标自动化线体、多工位装配线、汽车零部件产线或电池模组线的工程师参考。
我要先说明白一个观点:模板程序不是把代码复制粘贴到每个工位就算完事,而是把“电机”“气缸”这些出现频率最高的设备,封装成带参数、带状态、带故障处理的标准功能块。同一台设备,一份逻辑,通过背景DB和工艺参数去区分现场实例。这样做的直接收益是:新工位接入不再需要动PLC逻辑,只需要复制DB、填参数、对点表、做画面。文章后面所有内容都是围绕这个思路展开,会同时覆盖S7-1500和S7-1200的差异、V15.1的版本坑、电机与气缸功能块的接口设计、以及联调时真正的排错链路。
1. 大型线体程序为什么要做“模板化”而不是逐工位写逻辑
1.1 一套线体几十个工位,最怕的是“改一处要翻十个程序块”
老式做法里,很多设备程序是“一个工位复制一份,然后改地址、改参数”。这种方式的隐患在项目前期不明显,等设备进场、开始联调,问题就集中爆发了。你改了一个工位的安全联锁逻辑,却发现隔壁工位用的还是旧逻辑;你优化了某个气缸的超时时间,却发现同类气缸在另外三个工位没有同步。最典型的是故障排查:同一类设备在程序里出现了几十个不同的写法,每个写法都有自己的定时器、自己的中间变量、自己的复位方式,找问题只能靠人肉检索。
大型生产线对程序的要求从来不是“能跑就行”,而是“可维护、可追溯、可复制”。所谓模板化,本质上就是把设备动作抽象成标准功能块,让每台设备只存在一份逻辑源码。设备之间的差异通过参数和实例化数据来体现,代码本身不再跟着设备数量膨胀。比如一台三相异步电机和一个变频器控制的风机,控制的“骨架”是一样的:启动命令、停止命令、运行反馈、故障反馈、复位、状态输出。差异只是控制方式、超时时间、是否参与联锁,这些都属于参数范畴,不应该进入逻辑层面。
1.2 模板化的本质:把设备动作抽象成可复用的功能块
在西门子博途里,“功能块”就是FB(Function Block)。FB跟FC最大的区别在于:FB每次调用都自带一个背景数据块(Instance DB),用于保存这次调用内部的运行状态、定时器、计数器和故障锁存信息。也就是说,同一个FB可以被不同的背景DB实例化很多次,每次实例的数据互不干扰,但代码只有一套。
这是我建议大型线体全部使用FB而不是FC的根本原因。电机需要累计运行时间、需要锁存故障、需要记住自己处于自动还是手动状态;气缸需要判断当前所在位置、需要记录上一次动作是否超时、需要屏蔽报警。这些“记忆”必须落在背景DB里。如果用FC,要么把所有状态变量做成庞大的全局DB去人工对齐,要么每次调用都用临时变量导致状态丢失。模板化程序里,设备状态必须跟设备实例绑定,FB的背景DB机制正好满足这一点。
1.3 版本15.1在这个方案里的位置
很多现场工程师问过我:为什么不用V16、V17,非要用V15.1?答案很现实:产线客户的技术团队固化在V15.1,备件库里还有旧固件的CPU,老项目的库文件也都是V15.1导出的。从工程落地角度讲,V15.1对S7-1200和S7-1500的支持非常完整,优化访问、符号寻址、Variant、全局库、HMI Faceplate这些模板化需要的核心特性都成熟可用。V15.1还有一个好处:它相对稳定,不像后面几个版本频繁更新补丁,团队多人协作时不容易出现“他打开的版本跟我不一样”这种扯皮事。
当然选V15.1也要付出代价,那就是版本兼容性边界非常清晰:低版本项目可以尝试升级到V15.1,但用V16或更高版本保存过的项目,V15.1基本打不开。这一点放到后面专门讲,属于模板程序在扩散到多台设备之前必须先确认的死规则。
2. 模板程序的骨架:分层、命名和UDT
2.1 程序怎么分层才不会被“工艺变更”打乱
模板程序的分层决定了后续维护成本。我的做法是把程序分成四层:HMI与全局数据层、工艺调度层、设备控制层、IO映射层。OB1只做循环调度,不写具体工艺;工艺调度层的FB负责一个工位的节拍、互锁和顺序逻辑;设备控制层的FB就是本文核心的电机功能块和气缸功能块,每个FB只负责单台设备;IO映射层用全局DB把物理地址统一映射成工程符号。
举个例子:设备层有一个“顶升电机”(代号M102),设备控制层的FB只关心“我有启动命令、我有运行反馈、我是否超时”,它完全不知道这个电机的输入点接在哪个机架哪个槽位。IO映射层负责把实际输入字节的某一位,映射成M102的运行反馈符号。这么做的好处非常明显:电气图纸变更时只要改IO映射层,设备FB不需要重新下载;反过来说,FB内部逻辑优化升级,也不会影响已经完成的IO对点表。
命名规范是模板化最容易忽视却又最影响使用体验的部分。我常用的规则是:设备类前缀——电机用M开头加工位号和功能码,气缸用C开头,阀岛用V开头;DB块统一加DB_前缀;全局数据块加g_前缀。设备符号尽量让人一眼看懂是干什么的,比如“M102_LiftMotor_RunFb”表示102工位顶升电机的运行反馈。一套线体几百个变量,如果没有统一命名,人到了现场根本分不清哪个是输入哪个是输出。
2.2 UDT是设备的“数据契约”
UDT(用户自定义类型)在模板程序里扮演的是“数据契约”的角色。电机和气缸的工艺参数、当前状态、传感器配置,都应该定义成结构体。这样做的好处是:设备FB的接口可以统一接收一个UDT类型的参数,后续加字段不会导致接口大改;HMI做标准面板时,画面的变量连接也可以直接指向UDT成员,复用性极高。
下面给一个电机参数UDT的参考结构:
| 字段名 | 数据类型 | 说明 |
|---|---|---|
| DevName | String[12] | 设备工程名称 |
| CtrlType | Int | 0=直启,1=变频器模拟量,2=PN通讯控制 |
| RunTimeout | Time | 启动命令发出后等待反馈的最长时间 |
| StopTimeout | Time | 停止命令发出后反馈消失的最长等待时间 |
| EnableCurrentAlm | Bool | 是否启用电流异常报警 |
| CurrentLimit | Real | 电流上限,用于模拟量采集判断 |
| EnableJog | Bool | 是否允许点动 |
| DevComment | String[20] | 设备用途备注 |
气缸的UDT类似,但多了传感器极性、单双电控配置、到位超时时间这些字段。关键原则是:UDT里的字段必须保持稳定,上线之后尽量不要删字段或改类型,字段含义可以在注释里补充,但结构不要大动。因为UDT一旦变动,所有引用它的DB都需要重新编译下载,这在现场是伤筋动骨的事。
2.3 实例化方式:多重背景和独立背景DB怎么选
FB在工程里有两种常见的实例化方式:一种是每个FB调用在程序块列表里生成独立的背景DB;另一种是嵌套到某个工位FB里,作为该工位FB的“多重背景”使用。我个人的建议是:大型线体的电机和气缸单独建背景DB,不要让它们全部嵌套在工位FB里。原因很简单,独立背景DB便于在线监视,调试时单个设备的数据一目了然,而且坏了可以单独下载,不必把整个工位都牵连进去。
S7-1200的情况要特殊一点。它的内存比1500小,背景DB数量太多可能吃紧,所以1200侧常用“工位FB嵌套多重背景”的做法。比如一个工位FB内部声明了“M102_LiftMotor : FB_Motor;”和“C102_ClampCyl : FB_Cylinder;”,这样工位里的所有设备在一个大FB内部运行,背景数据跟着工位走。对1200来说,程序块数量明显减少,管理更紧凑。1500侧内存足够,我则更倾向于把每个设备的FB直接实例化,让维护人员能快速按设备号定位背景DB。
3. 电机功能块:从启停控制到故障锁存的完整设计
3.1 接口先冻结,功能块才敢复用
功能块接口设计是模板程序里最重要的决策,因为它直接决定后续复用成本。接口一旦发布,就等于跟所有调用它的程序块签署了“数据契约”,后面再想加一个输入引脚、改一个输出类型,会牵连几十处调用点全部重新编译下载。所以我在设计电机FB接口时,把需求分成三类:命令类、反馈类、参数类。
命令类包括自动启动、自动停止、复位、手动使能、手动启动、点动;反馈类包括运行反馈、外部故障反馈(热继/变频器故障)、模拟量电流反馈;参数类包括控制方式、反馈超时时间、是否屏蔽报警。下面是我常用的电机FB接口表:
| 接口 | 方向 | 类型 | 说明 |
|---|---|---|---|
| i_Start | In | Bool | 自动启动命令 |
| i_Stop | In | Bool | 自动停止命令 |
| i_Reset | In | Bool | 故障复位命令 |
| i_ManualEn | In | Bool | 手动模式使能 |
| i_ManualCmd | In | Bool | 手动运行命令 |
| i_CtrlType | In | Int | 控制方式选择 |
| i_FeedbackRun | In | Bool | 运行反馈 |
| i_FeedbackFault | In | Bool | 外部故障输入 |
| i_RunTimeout | In | Time | 反馈超时时间,默认5秒 |
| q_TripOut | Out | Bool | 给接触器或变频器使能输出 |
| q_Running | Out | Bool | 综合运行状态 |
| q_Fault | Out | Bool | 故障状态 |
| q_FaultCode | Out | Int | 故障编号 |
| q_RunTimeAcc | InOut | Real | 累计运行时间(小时) |
接口里的“q_RunTimeAcc”我用InOut类型而不是静态变量,是为了把累计运行时间放到外部DB里做统计,方便报表和保养提醒,避免保养数据随着背景DB下载被冲掉。
3.2 一个电机FB要覆盖直启、变频和通讯三种控制方式
电机在实际产线上的控制方式五花八门,直接接触器启停的有,变频器模拟量调速的有,PN通讯读报文控制字的有。模板程序不能只服务最简场景,还要预留扩展空间。我的做法是用i_CtrlType参数分流:0表示直启,输出布尔量q_TripOut直接给接触器;1表示变频器模拟量控制,除了q_TripOut做使能,还多输出一个速度设定值(模拟量或整数);2表示走PN通讯,控制字从通讯DB读取,功能块默认让出执行权,只做状态监视和故障判断。
为什么这样设计?因为产线调试时经常遇到“这台电机先按直启跑起来,后面换变频器”的情况。如果功能块不区分控制方式,以后换硬件等于重写逻辑。用参数分流后,改动只是一个INT类型参数,PLC逻辑一行都不用动。这也是模板程序的核心价值:把大概率发生的变更,提前变成参数而不是代码。
下面给一段SCL骨架,说明电机控制逻辑的基本结构。注意V15.1里FB的局部变量访问需要加“#”前缀。
FUNCTION_BLOCK "FB_Motor" { S7_Optimized_Access := 'TRUE' } VAR_INPUT i_Start : Bool; i_Stop : Bool; i_Reset : Bool; i_ManualEn : Bool; i_ManualCmd : Bool; i_CtrlType : Int := 0; i_FeedbackRun : Bool; i_FeedbackFault : Bool; i_RunTimeout : Time := T#5S; END_VAR VAR_OUTPUT q_TripOut : Bool; q_Running : Bool; q_Fault : Bool; q_FaultCode : Int; END_VAR VAR tonRunCheck : TON; FaultLock : Bool; END_VAR BEGIN // 复位故障锁存 IF #i_Reset THEN #FaultLock := FALSE; #q_FaultCode := 0; END_IF; // 外部故障直接锁存 IF #i_FeedbackFault THEN #FaultLock := TRUE; #q_FaultCode := 100; END_IF; // 手动模式 IF #i_ManualEn THEN #q_TripOut := #i_ManualCmd; ELSE // 自动模式:启动优先,停止优先,故障优先 #q_TripOut := ((#q_TripOut AND NOT #i_Stop) OR #i_Start) AND NOT #FaultLock; END_IF; // 运行反馈超时检查:命令已发出但反馈一直不来 #tonRunCheck(IN := #q_TripOut AND NOT #i_FeedbackRun, PT := #i_RunTimeout); IF #tonRunCheck.Q AND NOT #FaultLock THEN #FaultLock := TRUE; #q_FaultCode := 101; END_IF; // 综合状态 #q_Running := #i_FeedbackRun; #q_Fault := #FaultLock; END_FUNCTION_BLOCK这段代码没有写成大型生产线的完整工艺控制,但已经把模板化的核心逻辑表达清楚了:状态锁存、命令优先级、超时判断。实际项目里,我还会在FB里加入累计运行时间、电机启动次数统计、首次故障记录,用来支撑设备健康管理。
3.3 反馈不一致、外部热故障和累计运行时间的处理
电机类设备的常见故障不是“抱死”这么简单,更多是“命令和反馈不一致”。启动命令已经给出,接触器吸合了,但运行反馈没来,可能是辅助触点断线、可能是接触器没吸、可能是反馈回路保险丝烧了。反之,停止命令给出后反馈仍然存在,可能是接触器触点粘连,也可能是反馈回路上有其他回路串电。这两种情况都需要在FB里做时间窗口判断,不能一看到反馈不对就立刻锁死,给现场留一个小缓冲,避免触点抖动造成误报。
热继或变频器故障我从外部接入i_FeedbackFault,在FB内部做“故障锁存”。所谓锁存,意思是就算外部故障信号瞬间消失,FB里仍然保持故障状态,直到操作员按下复位并满足复位条件。这个设计在现场非常必要,否则变频器瞬间过载一下,故障还没看清历史就消失了,找根因极其困难。复位逻辑上务必增加一个“复位不允许条件”:如果外部故障仍然存在,复位命令不生效。多数PLC工程师在成熟项目里都会做这个处理,模板程序里更要做进去。
累计运行时间推荐用实时时钟配合中断OB做,每满一小时累加一次。这样做比功能块内部用时间继电器轮询累加更准,也省资源。数据放到外部数据块后,HMI保养页面直接读取,做预防性维护很方便。
3.4 调用示例和参数化建议
电机FB在OB1或者工位FB里的调用非常直观。如果你走独立背景DB路线,直接拖拽实例然后填背景DB名称;如果走多重背景,则在工位FB内部声明实例变量。现场调试时,我更推荐独立背景DB,因为HMI的监控画面可以直接拖变量,观察故障码、手动切换、复位按钮的位置一目了然。
参数化建议有三条。第一,i_RunTimeout别设太短,尤其首次通电时接触器吸合有延迟,现场测试下来直启电机一般2秒内反馈必到,我的默认值设5秒,变频器启动慢的设到8秒。第二,如果电机是变频器控制的,q_TripOut千万不要理解成“上电接触器”,在变频器模式下q_TripOut更多是“运行使能”,速度给定走专用输出,这个语义差异要在功能块注释里写清楚。第三,所有参数默认值设在FB接口上,而不是散落在各个背景DB里,这样新实例一拖出来就自带合理默认值,大幅减少配置遗漏。
4. 气缸功能块:单双电控、到位判断和超时报警
4.1 气缸FB的数据模型:命令、反馈和位置状态
气缸是自动化产线上数量和电机一样庞大的设备。它的控制逻辑看起来比电机简单——伸出、缩回、两个磁开、一个电磁阀——但真到大型线体里,气缸的标准写法反而更容易乱,因为换向阀有单电控和双电控的差别,传感器有常开常闭的差别,动作超时报警的判断时机也有讲究。
气缸功能块需要维护三个核心概念:命令、反馈、状态。命令就是“我要伸出/我要缩回”;反馈是磁开关的到位信号;状态则是FB计算出来的位置信息。我习惯用一个整数表示气缸当前位置:0表示缩回到位,1表示伸出到位,2表示处于中间过程。这个位置状态对HMI画面特别有用,调试时一屏就能看清所有气缸现在在哪里,不用逐个去看布尔量。
气缸FB的接口设计相对简洁,但有一个字段特别容易被忽略:传感器的有效电平。磁性开关有常开、常闭两种连接方式,还有NPN和PNP之分。如果不做开关量反转参数,气缸接线改一下极性,程序里的到位判断就全部反了。我为此专门加了i_ReverseSensor参数,默认FALSE;现场传感器是常闭接法时置TRUE,FB内部自动对反馈取反。
4.2 超时报警不能只看“有没有到位”,要看“该不该到位”
这是气缸功能块设计里最容易踩坑的点。不少同事写气缸控制,会直接写“如果反馈信号消失超过5秒就报警”。但这样写有问题:气缸本来就该缩回时,你伸出到位信号一直消失是正常的,不能报“没到位”的错。正确的做法是判断“命令期望状态”和“实际到位状态”是否一致。命令指向伸出,超过设定时间还没到伸出到位;或者命令指向缩回,超过设定时间还没到缩回到位,这两种情况才算真正超时故障。
中间态的处理也要写进逻辑。双电控阀在没有动作时两个线圈都断电,气缸停在中间位置。这时候伸出和缩回两个反馈都没有,如果逻辑不小心,会把“停在中间”当成故障。我的做法是:只有存在“主动动作命令”和“到位信号不匹配”时才启动超时定时器;没有任何动作命令时,即使不在两个端点也不判故障,只把位置状态置为“中间”。
下面给一个气缸FB的核心逻辑片段:
FUNCTION_BLOCK "FB_Cylinder" VAR_INPUT i_Extend : Bool; i_Retract : Bool; i_Reset : Bool; i_ManualEn : Bool; i_ManualExtend : Bool; i_ManualRetract : Bool; i_FbExtend : Bool; i_FbRetract : Bool; i_ReverseSensor : Bool := FALSE; i_DoubleSolenoid : Bool := TRUE; i_TimeOut : Time := T#3S; i_MaskExtAlm : Bool := FALSE; i_MaskRetAlm : Bool := FALSE; END_VAR VAR_OUTPUT q_ValveExtend : Bool; q_ValveRetract : Bool; q_Position : Int := 0; q_Fault : Bool; q_FaultCode : Int; END_VAR VAR fbExt : Bool; fbRet : Bool; tonExt : TON; tonRet : TON; FaultLock : Bool; END_VAR BEGIN // 传感器极性反转处理 #fbExt := #i_FbExtend XOR #i_ReverseSensor; #fbRet := #i_FbRetract XOR #i_ReverseSensor; // 复位锁存 IF #i_Reset THEN #FaultLock := FALSE; #q_FaultCode := 0; END_IF; // 手动优先 IF #i_ManualEn THEN #q_ValveExtend := #i_ManualExtend; #q_ValveRetract := #i_ManualRetract; ELSE // 双电控阀:两个输出,互斥处理 #q_ValveExtend := #i_Extend AND NOT #i_Retract AND NOT #FaultLock; #q_ValveRetract := #i_Retract AND NOT #i_Extend AND NOT #FaultLock; END_IF; // 伸出到位超时判定:命令要求伸出,但就是不到伸出位 #tonExt(IN := #q_ValveExtend AND NOT #fbExt AND NOT #i_MaskExtAlm, PT := #i_TimeOut); IF #tonExt.Q THEN #FaultLock := TRUE; #q_FaultCode := 201; END_IF; // 缩回到位超时判定 #tonRet(IN := #q_ValveRetract AND NOT #fbRet AND NOT #i_MaskRetAlm, PT := #i_TimeOut); IF #tonRet.Q THEN #FaultLock := TRUE; #q_FaultCode := 202; END_IF; // 位置状态计算:0缩回 1伸出 2中间 IF NOT #fbExt AND #fbRet THEN #q_Position := 0; ELSIF #fbExt AND NOT #fbRet THEN #q_Position := 1; ELSE #q_Position := 2; END_IF; #q_Fault := #FaultLock; END_FUNCTION_BLOCK单电控阀的场合,实际只有一个阀线圈,得电伸出、失电缩回。这时把q_ValveRetract当成内部中间变量不使用,只引出q_ValveExtend给输出点。我加i_DoubleSolenoid参数就是为了在外围接线图上区分这两种情况。如果功能块里已经做了互锁扩展,那双电控阀就完全按照代码执行。
4.3 传感器极性、防抖和调试屏蔽
气缸磁开关在产线现场非常容易出问题,主要原因是机械振动导致磁开关松动、位置偏移,或者线缆破损。模板程序里需要给反馈信号加一点滤波,否则气缸动作到位瞬间,磁开关信号会跳几下,把超时报警误触发。我的经验是:到位信号用“延迟接通”做防抖,一般20~50毫秒就够;而报警判定则要比防抖时间再长一些,比如0.5秒,因为气缸动作本身有缓冲过程,到位瞬间存在挤压反弹,不能立刻判定故障。
调试屏蔽功能也一定要做。产线联调阶段,经常出现气缸本体还没装传感器、或者传感器还在货路上的情况。没有屏蔽参数,这些气缸一动作就报警,整个自动流程根本跑不起来。所以我在气缸FB里放了i_MaskExtAlm和i_MaskRetAlm,作用就是暂时关闭该方向的到位报警。现场调试时允许屏蔽,但设备交付验收前必须逐台解除屏蔽,并把屏蔽状态作为一个诊断共享块记录到报表里,防止带病交付。
4.4 手动/自动模式下的Fault复位逻辑
气缸故障的复位跟电机类似,但有一个细节不一样:电机是“停止后等待复位”,气缸则经常是“复位的同时要回退到原位”。比如顶升气缸在自动中途超时报警,工艺上往往要求先手动缩回,再把所有气缸恢复到初始位置,最后才能按复位。所以气缸功能块的复位不能只清FB内部锁存,还要配合工艺层做“状态恢复确认”。我在项目里是让气缸FB的复位输出经过工艺调度FB确认之后才下发,防止操作员过早按复位导致气缸从中间位置开始跑,撞坏工装。
这个“复位不允许条件”在FB内部只做基本检查:外部故障条件如果仍然成立,复位无效;至于“气缸是否已在安全位置”这种工艺联锁,不应该放在设备层FB里,应该由上一层工艺逻辑控制。
5. TIA Portal V15.1实战:版本、库、下载和兼容性
5.1 版本15.1的兼容性边界:向上打不开,向下要谨慎
模板程序固定到V15.1之后,最怕遇到的问题就是版本跨来跨去。V15.1能打开V14、V15的项目,打开后需要做一次升级编译,这在我的项目里都比较顺利。但从V16开始创建或保存过的项目,V15.1基本打不开。哪怕只是一个程序块被别人用V16打开并保存过,回头再拿回V15.1环境,都会提示“项目由较高版本创建”。所以团队协作有一条死规矩:模板库必须存放在V15.1单独环境里,谁要用高版本打开,先复制副本,弄完以后不要回写原有库文件。
库文件的版本属性和项目版本一样存在兼容问题。V15.1导入低版本的库问题不大,但V16版本导出的全局库放进V15.1几乎必然报错。我处理的办法是:模板程序库在V15.1里维护一个“发布版”,每次功能块接口升级,都在发布说明里写清楚变更内容和影响的背景DB,然后全公司统一升级,不允许个人在项目里私改库里的母版FB。否则时间一长,每个项目里的FB看起来同名,实际上逻辑都不一样,模板就名存实亡了。
5.2 优化访问与符号化:模板化程序的地基
V15.1对S7-1200/1500默认启用“优化块访问”,意思是背景DB里的变量没有固定物理地址,通过符号直接访问。模板程序强烈建议全部使用优化访问。原因很直接:模板里设备功能块的背景DB要反复复制,如果用了非优化访问,一旦DB里的变量顺序变化,地址全乱,HMI画面和上位机监控点全部需要重新关联。优化访问下,变量位置对用户透明,HMI通过符号连接,程序怎么加变量都不怕地址漂移。
当然非优化访问也有一席之地:和老设备通讯、文本显示屏、或者第三方网关需要绝对地址时,单独建立一个非优化访问的数据块做“地址交换区”。我的原则是:功能块内部一律优化访问,需要看物理地址的点,在IO映射层或专用的通讯缓冲DB里暴露出来,不把设备FB的干净结构给破坏掉。
5.3 Variant参数:一个FB吃下任意长度的数组
V15.1里Variant数据类型是非常好用的万能参数。你可以在FB里定义一个Variant类型的输入,让它接收任意数据类型的DB、数组或单个变量。比如在模板程序的“设备数据导出”功能块里,我定义了一个Variant参数,调用方可以把任意工位的设备参数DB塞进去,功能块内部用类型判断和自动循环处理,不需要为每类设备单独写一个导出程序。
使用Variant要特别注意两点。第一,V15.1里S7-1200也支持Variant,但对Variant的处理性能不如S7-1500,大数据阵列频繁访问时可能会拉高扫描周期,1200侧慎用。第二,Variant是“鸭子类型”,运行前不会做严格的参数类型检查,一旦传入类型和内部期望不一致,可能在运行时输出奇怪的错误码。我的经验是:Variant函数内部第一件事就是做类型断言,类型不匹配就返回专门错误码,然后让调用方去查,不要在函数中间毫无预兆地异常退出。
5.4 在线下载的“一致性”提示和接口冻结策略
V15.1在线下载时,程序改动会导致软件给出大量一致性提示。最典型的是:修改了电机FB的接口,所有调用它的工位FB全部变成“不一致”,需要重新编译并下载。如果这时候只是改了一个引脚注释,也会触发这种牵连。大型线体几十个工位,动不动全部下载,现场连调试机器都带不动。我踩过几次坑之后,总结出一条看似保守但极其有效的策略:功能块接口在设计评审后“冻结”,上线后只允许追加引脚,不允许删除和改类型。
所谓追加,就是新增的引脚放在接口区最后面,并且赋予默认值。这样旧的背景DB和调用块大概率不会因为“缺少新引脚”而报错,在线下载影响范围小。如果确实要改一个已经用过的引脚类型,尽量新建一个功能块版本,通过库的版本升级去切换,而不要在项目里原地改。这个策略不仅救了维护阶段的实际工作量,也倒逼团队在项目前期把所有工艺需求想清楚。
6. 联调阶段的高发问题与排查链路
6.1 电机有输出指令但接触器不吸合
现场最经典的故障之一是:HMI下发启动,电机FB的q_TripOut已经为TRUE,监控软件也看到布尔量变1了,但接触器就是不吸合。这种情况的排查链路不能从PLC程序入手,而要从输出回路往里查。我一般按这个顺序:先看PLC输出模块的指示灯是否亮;如果亮,量输出端子有无电压;有电压,查中间继电器线圈是否有电;有电,查中间继电器触点是否导通;导通,再看接触器线圈接线两端有没有电压;最后查接触器本身是否卡滞或烧毁。
这里有个经验:十次这种故障里,至少有三四次是中间继电器插脚氧化或者插座松动,触点看着在动,实际阻值很大,驱动不了接触器。还有一次是PLC输出端接线鼻子压到了线皮,万用表量端子有电压,一接负载就掉压。模板程序帮不上这些硬件问题的忙,但能帮上的最大忙是:让你能快速确认“命令已经发到输出点了”,剩下的事交给万用表。
6.2 气缸“到位超时”报警的完整排查顺序
气缸到位超时是联调阶段报警排行榜常客。这类报警的排查顺序,务必要从“氧气有没有供上”开始。气源没开、气路快插接头没插到位、减压阀压力调低了,都会导致气缸动作极慢甚至不动。先看压力表、听排气声,比看程序更高效。
气源正常后,再看节流阀和缓冲。节流阀调太小,气缸虽然慢但能到位,只是时间超过了设置值;缓冲太紧则气缸在行程末端直接卡住。第三步才轮到传感器:磁开关是否松动移位、线缆是否接触不良、NPN和PNP是否选错。最后如果这些全查完没问题,才回头查程序里的传感器极性和屏蔽位设置。很多时候,现场工程师一看到报警就打开PLC程序改超时时间,改到10秒、20秒,结果把真正的机械问题掩盖掉了。我的原则是:报警时间超过5秒的,这一条记录必须重点复查,不允许简单拉长时间掩盖问题。
6.3 通讯控制方式的常见坑:从模拟量到PN报文
走通讯控制的电机,问题往往不在FB逻辑,而在报文对不上。模拟量控制最经典的问题:PLC输出端组态成0~10V,变频器侧拨码或参数设成4~20mA,结果输出5V时变频器完全没反应,输出10V时电机全速甚至超速。排查时先确认模块接线、量程卡位置、变频器模拟量输入类型三者一致。
PN通讯控制则要核对控制字和状态字的位定义。V15.1里的标准报文和厂商自定义协议有区别,电机功能块里读到的状态字第3位可能不是“运行就绪”,而是别的含义。我用了个笨办法:联调阶段让电机先手动运行,然后在PLC在线表里看整个PZD报文逐位变化,根据实际信号反推报文含义,最后在FB内部做好位映射。宁可前期多花半小时确认,也不要盲目按照手册假设,因为不同厂家的属性定义在标准报文上不一定完全一致。
另外,通讯控制的FB设计里,建议把“通讯心跳”也纳入故障判断。很多变频器内部有通讯超时默认定时器,但PLC侧不一定感知到。在电机FB里加一个报文刷新时间戳监控,超过设定时间没收到状态字,就判通讯中断并锁存故障,这样可以避免变频器已经跳停了,PLC却还傻乎乎输出运行命令的情况。
结语:模板程序是越用越值钱的长期资产
模板程序做起来比单写一套程序要多花不少心思,但它是越用越值钱的东西。我自己做这套电机和气缸功能块的过程中,最大的体会不是写代码本身,而是“接口冻结”和“命名规范”这两件事必须在一开始就坚持。哪怕前期多开三次评审会,也比后期几十个工位全改一遍要划算。另外,V15.1环境下版本管理需要格外手稳,库文件、项目文件、程序块版本,谁动了都要记录在案,不然模板程序散落在各个工程师电脑里,最终还是会退化成“每个工位一种写法”。这条路上没有捷径,把接口定稳、把UDT定准、把下载策略定严,后面每个新项目都能从这份模板里直接受益。