资讯详情

资讯详情

ST代码复用架构:设备模板FB如何一套代码覆盖8种机型?

这个标题看起来平淡但做过非标设备或者产线集成的朋友应该能立刻闻到它的分量。“ST代码复用架构”、“设备模板FB”、“覆盖8种机型”这三个词凑在一起意味着一个团队可能用一份逻辑代码把过去需要维护8套程序的项目压缩成了一个模板加8份参数。这后面省下的不只是工程师的头发还有现场调试的几十个加班夜以及后续机型迭代时的升级成本。先说一下我自己的背景。我之前在好几家做非标自动化设备的公司干过从单机设备到整线集成都有涉及主要用的是Codesys、Twincat这类基于IEC 61131-3的PLC平台ST语言用得比较多。手头这个项目是一个组合型设备一个机架里有8种不同的机型配置从差速器壳体压装到轴承外圈压装从单工位半自动到四工位转盘全自动都有共用一套HMI画面和一套PLC程序框架。今天把这套基于ST的功能块FB复用架构从头到尾拆一遍包括为什么这么设计、FB里到底装了什么、8种机型的差异是怎么被吃掉的以及我们踩过的坑和现在的版本怎么看怎么顺眼。1. 项目背景与架构思路从“复制8份”到“复用1份”做设备程序的人都知道最痛苦的不是第一台设备写程序而是第二台、第三台以及第N台。我们经常遇到的情况是第一款机型开发完了客户找过来说工艺差不多但机构改了一堆轴多了两个压装力从5吨变成8吨转盘变成直线滑台数据要求也不一样。大多数团队的做法是拿上一版程序直接另存为然后改改改完就开始出怪问题。我这个项目其实也走过这个阶段。项目开始时候有6种机型后来客户又加了2种变成了8种。如果按老路子来每一种机型单独一套程序那就是8套程序。每套程序里都有复位逻辑、手动逻辑、自动逻辑、报警逻辑80%的核心代码是一样的却因为最开始没有统一规划各自为政最后变成了一团乱麻。被逼到改不动的时候我在客户现场拍桌子下决心把所有机型的共性逻辑抽出来做成一个通用的设备模板FB差异全部做成参数一套代码8种机型。1.1 为什么非要用ST写FB模板很多人一提到代码复用第一反应是做一个“万能程序”什么功能都往里塞。这其实是误区。真正的复用不是说代码量少了多少而是逻辑模型稳定。IEC 61131-3标准里FB功能块是带内部状态和存储的正好适合描述一台设备的完整行为它有输入指令、参数、输出动作、状态、内部状态当前到哪一步了、有定时器、有计数器还能自己管理自己的报警。ST结构化文本相比梯形图在表达复杂逻辑和数据操作上优势明显。特别是当你需要让一个FB覆盖多种机型时你不可避免要用到数组、结构体、枚举、可变长参数、甚至指针和接口Interface这些在ST里都是正经语法而在梯形图里写这些简直是灾难。再加上FB是支持多实例化的同一个模板你定义10个实例它内部逻辑完全独立互不干扰这天然就是为“一套代码覆盖8种机型”准备的。1.2 架构目标运行时不操心机型变化只改数据我把这个架构的核心目标定成四个字参数驱动。具体来说整个设备程序的运行宏观流程是HMI下发指令 → FB模板接收指令 → FB根据机型参数工位数量、轴配置、顺序决定执行流程 → 输出到具体IO或轴 → 反馈状态给HMI。程序里的每一个动作点在执行之前都会先去查参数表而不是写死在代码里。这样一来不管你是2个工位还是4个工位压装模式是力闭环还是位置闭环扫码枪有还是没有自动上料是振动盘还是皮带线这些问题全部化成了结构体字段和枚举值。现场来了新机型我们根本不用动代码把模板实例化一个新的填好参数表下载就完事了。代码的复用到了这个程度才算真正落地。2. 差异抽象建模8种机型到底差在哪参数怎么定要让一个FB覆盖8种机型首先你得搞清楚这8种机型之间必须存在可被抽象的共同骨架。我们做的这个组合设备8种机型从工艺上看都是“上料-定位-压装-检测-下料”这个闭环区别在于每个环节的执行机制不同。我把它们的差异归纳成五个维度这也是参数表的核心结构。2.1 维度一工位分布与节拍逻辑这是最容易想到的差异。有的机型是单工位定位压装在一个位置完成有的是两工位直线滑台一工位上下料二工位压装还有四工位转盘上料、压装、检测、下料四个位置同时做不同动作。代码里如果不抽象每加一种工位布局就是要加一组状态机。我处理的方式是在参数中定义工位数量、每个工位对应的物理IO通道、动作类型然后在FB内部用一个FOR循环遍历所有工位而不是为每个工位写死一套顺序逻辑。2.2 维度二执行机构的配置同样是压装动作A机型用的是伺服电缸加压力传感器做闭环压装B机型用的是普通气缸到头就行C机型是液压缸加位移传感器靠位置定位。这些执行机构的差异如果做成FB内部的分支判断就会让FB里面全是IF ELSE最终变得不可维护。我的做法是定义一组枚举类型ActuatorTypeACT_SERVO_PRESS、ACT_CYLINDER、ACT_HYDRAULIC然后每个工位增加参数ActuatorType、TargetForce、TargetPosition、SpeedFeedForward等。FB内部统一按“目标位置 目标压力 到达判定方式”三个最终参数来驱动实际PLC指令的差异隐藏在内部实现的函数里通过CASE ActuatorType OF来分发。这样FB对外接口完全统一新增一种执行机构只需要在内部加一个分支。2.3 维度三检测与数据追溯功能8种机型里有的完全没有检测项压装完就完事有的需要压装力位移曲线记录有的要扫码上传MES存压装结果还有的附带气密检测仪要读取检测仪的结果信号。差异极大。不能把这些逻辑全写进FB主流程里否则一个没检测功能的机型也要流程走到“检测”步骤纯属浪费时间。我引入了功能使能掩码的概念。用一个DWORD类型的参数叫FunctionEnableMask每一位代表一个可选功能是否启用bit0扫码bit1曲线记录bit2气密检测bit3自动区分合格品bit4数据上传。FB主流程里只有当对应位为TRUE时才会在流程节点处去调用对应的功能函数。不需要的功能位就是0流程直接跳过不影响节拍。2.4 维度四IO映射与气缸传感器命名规则这是最容易忽略、也最容易在设计阶段就埋雷的差异。8种机型IO点数不一样气缸数量不一样每个气缸的磁性开关位置不一样。如果在FB参数里做“气缸1”、“气缸2”这种代号那跟写死没区别因为你没法从一个“气缸1”的代号知道它到底对应哪个物理IO点。我们的做法是定义一个IO映射结构体数组如下面代码所示。在实例化FB之前由封装层根据配置导入实际的IO地址。FB内部用到第N个气缸时直接调用IO[CylinderIndex].ValveOut和IO[CylinderIndex].FeedbackIn从逻辑名称到物理地址的映射在配置表里完成。这样FB代码和现场接线彻底解耦换一种机型只需要替换IO映射表。TYPE ST_ActuatorIO : STRUCT ValveOut : BYTE; // 阀输出字节地址实际PLC地址映射 ValveBit : BYTE; // 阀输出位 FeedbackIn : BYTE; // 磁性开关反馈字节 FeedbackBit : BYTE; // 磁性开关位 InPosition : BOOL; //可选由外部读入的就位信号 END_STRUCT END_TYPE2.5 维度五配方和工艺参数的粒度8种机型对应的产品型号可能有几十种同一种机型压装不同工件压力目标值、压装速度、保压时间都不一样。这个属于配方Recipe管理层不应该放进FB的核心逻辑里而是放在调用FB的上一层逻辑中。FB本身只认“当前要执行的目标参数”是多少由上一层在启动FB前把目标参数准备好。用大白话说FB就像一个大厨你不能让大厨决定今天做什么菜你只需要在开门营业时把菜单给他。菜单就是工艺参数而菜谱库配方管理是后厨外部的系统跟大厨本身没关系。这个分层思想是我从头到尾一直坚持的。3. FB模板的代码实现结构化设计才是复用灵魂再往下就是实打实的代码了。这个FB模板我取名叫FB_DeviceTemplate主要接口和内部结构我拆成几部分来讲。3.1 接口设计输入输出跟HMI和上层逻辑解耦FB接口设计有个很重要的原则接口应该描述“意图”而不是描述“动作”。什么意思比如你给HMI一个“启动”按钮FB的输入不应该是一个bButtonPressed按钮被按下而应该是一个bStartRequest请求启动。因为将来上位机可能是MES系统自动触发而不一定是人按按钮。接口信号尽量抽象成“请求”、“状态”、“数据”具体触发源由调用者去决定。我这里的主要接口定义如下FUNCTION_BLOCK FB_DeviceTemplate VAR_INPUT bEnable : BOOL; // FB使能不使能时输出清零 bStartRequest : BOOL; // 启动请求上升沿有效 bStopRequest : BOOL; // 停止请求上升沿有效 bResetRequest : BOOL; // 故障复位请求上升沿有效 eOperationMode : E_OPMODE; // 手动/自动/调试 stRecipeParam : ST_RECIPE; // 当前配方参数结构体 stIOMapping : ST_IO_ARRAY;// IO映射表数组 stCommand : ST_CMD; // 附加命令扫码数据、工装信息 END_VAR VAR_OUTPUT bReady : BOOL; // 设备就绪 bRunning : BOOL; // 正在自动运行 bError : BOOL; // 有故障 eStatus : E_STATUS; // 当前状态机状态枚举 stDiagnosis : ST_DIAG; // 诊断信息报警码、位置、时间戳 bCycleComplete : BOOL; // 一个循环完成 END_VAR VAR // 内部状态机当前状态 eState : E_STATUS; // 工位数据由参数结构体实例化 stWork : ST_WORKSTATION_CFG[8]; // 最多支持8个工位 // 内部定时器、计时器 tonDelay : TON; // 数据记录缓冲区 stCurveData : ARRAY[0..999] OF ST_CURVE_POINT; END_VAR有几点要说明第一个输入使用上升沿触发而非电平触发。比如bStartRequest如果直接用BOOL电平那HMI按钮是置位型还是脉冲型就会产生歧义。我在FB内部用R_TRIG做了上升沿检测你只要给一个BOOL变量无论你HMI用置位按钮还是脉冲按钮行为都是一致的消除了上位机侧的边界问题。第二个配方参数不放在FB内部而是作为输入传入这一点前面已说过。但要注意别把配方做成一整个巨大的结构体然后一股脑塞进来最好拆成ST_RECIPE_BASE通用参数节拍时间、逻辑使能字和ST_RECIPE_EXT扩展参数各个工位的压力/位置/速度这样不会让FB参数输入变得臃肿也利于以后增加工艺数据项。第三个输出要带bError必须有但还不能只有它。输出一个stDiagnosis结构体里面包含了报警码、报警文本、报警发生时所在的工作状态和具体工位。这样HMI报警栏就能直接显示“某工位某气缸未到位”而不用工程人员挨个排查。3.2 状态机设计统一骨架下的柔性流转FB内部核心是一个大状态机这是整个模板的脊梁。按IEC 61133-3惯例设备控制的状态通常包括初始化、待机、启动中、运行中、暂停、停止中、完成、故障。我这个模板定义了一个枚举类型E_STATUS具体成员如下TYPE E_STATUS : ( ST_INIT, // 上电初始化 ST_IDLE, // 空闲待机 ST_RESET, // 故障复位执行中 ST_HOME, // 回原点/寻参中 ST_READY, // 已就绪等待循环启动 ST_RUN_CYCLE, // 单循环执行中内部按工位再细分 ST_RUN_AUTO, // 连续自动运行中 ST_STOPPING, // 正常停止中 ST_ABORTING, // 急停/中断中 ST_ERROR, // 故障保持 ST_DONE // 循环完成等待取件 ); END_TYPE你可能注意到我这里没有“暂停暂停”的状态。经验之谈做设备状态机时状态数量越少越不容易出错时间等待和暂停用辅助BOOL和定时器解决不要让状态数量膨胀。否则HMI上要映射状态篮框报警恢复时还要考虑从哪个状态恢复复杂度和bug率指数上升。状态流转的核心规则是外部只有“请求”Start/Stop/Reset在特定状态下才有效。比如bStartRequest在ST_READY状态时才开始执行循环在ST_ERROR状态时什么用都没有。这是为了保护设备不能让你在故障还没复位的情况下又去跑下一轮。3.3 工位级的顺序控制用“分步执行超时监控”吃掉机型差异一个循环内部真正干活的是对工位的顺序控制。如果每一个工位编一套动作顺序那模板内部还是会有8份逻辑只不过套了一个壳而已。要做到真正的复用必须在工位这一层做抽象。我给每个工位定义了一个“步骤动作表”Array of Step Action每个步骤是一个结构体TYPE ST_STEP_ACTION : STRUCT ActuatorId : INT; // 操作哪个执行元件索引到IO映射表 ActionKind : E_ACTION; // 动作类型伸出/收回/压装/保压/等待/测量 TargetPos : REAL; // 目标位置(mm或编码器计数) TargetForce : REAL; // 目标压力(KN或百分比) MaxTime : TIME; // 本步骤超时限制 NextOnCondition : E_COND; // 满足条件触发下一步到位/压力到/时间到 END_STRUCT END_TYPE然后每个工位配置一个步骤表数组一般最多十几步覆盖“夹爪闭合→滑台进→压装→保压→滑台退→夹爪开”这种典型流程。FB内部的主循环执行逻辑非常一致取出当前步骤 → 执行动作 → 轮询完成条件 → 满足则索引加一 → 直到最后一步 → 工位循环完成。这带来的好处是8种机型的动作序列差异最终只体现在“步骤表内容”不同而代码执行的骨架一模一样。就算以后有第9种机型步骤表上午改完配置下午就能上线试机。3.4 超时监控与故障自诊断没有这个模板根本不敢交付做工业设备最怕的就是“设备停了你不知道为什么”。普通逻辑里每个气缸动作用一个定时器监控超时就报警。多机型复用后这个检测不能写死在每段逻辑里否则又变成了只适配一种机型。我的方案是所有步骤在执行前自动使用结构体里的MaxTime生成一个超时定时器。具体做法不是在FB里面为每个动作定义一堆TON实例而是用一个全局TON池动态给每个正在执行的步骤分配一个定时器通道。定时器启动时记录“当前步骤号”和“当前工位”一旦超时诊断结构体里立刻记录“工位2-步骤5-超时动作内容压装”报警文本直接可读。这个设计往小了说是省了代码量往大了说是让一个没有接触过这套程序的调试工程师第一次看到报警信息就能定位到具体机械部分。我见过太多现场设备报警后仪表盘只显示“故障”两个字调试人员拿着万用表从电柜一路量到机械手臂折腾两小时。诊断信息的颗粒度直接决定了售后成本。4. 8种机型的映射适配参数表不是Excel是工程资产FB如果只是挂了个壳内部还是得靠改代码才能适配新机型那毫无意义。要让架构真正支撑8种机型关键在于参数表的组织和管理。我把这层叫“机型映射层”。4.1 机型枚举与配置导入在PLC工程里建立枚举类型E_MODEL_TYPE包含8种机型代号比如MODEL_A1到MODEL_H4。然后在全局变量管理器中建立一个统一命名的配置数据结构TYPE ST_MODEL_PARAMS : STRUCT ModelType : E_MODEL_TYPE; StationCount : INT; CycleTimeLimit : TIME; // 工位配置数组 StationCfg[1..8] : ST_WORKSTATION_CFG; // 机型使能的功能位 FuncMask : DWORD; // 机型适用的配方默认值 DefaultRecipe : ST_RECIPE; END_STRUCT END_TYPE这个结构体是整个8种机型的“皇帝的旨意”。PLC启动时或是HMI上选择了当前机型之后上位机把对应机型的ST_MODEL_PARAMS实例赋值给当前FB实例的配置输入。注意这里有一个“当前机型选择”的概念如果8种机型共用一台PLC同一时刻只能有一种机型激活那配置直接切换即可。如果8种机型是8套独立PLC那就更简单了每套程序里把对应机型参数一填连下载程序都不用换。4.2 HMI侧的机型参数管理把表格做进画面很多团队喜欢用Excel管理参数每次改参数就用U盘导入。我建议有条件的话把参数表直接做到HMI上或者做一个独立的配方管理页面让现场工艺工程师自己改压力目标和速度不需要开PLC程序。这样既减少程序反复下载带来的风险也让“8种机型一套程序”的体验深入到操作层。我们用的方案是在HMI上建立一个“机型配置”页面树形列出所有机型点开某个机型下面按“工位→步骤→动作”的层级列出所有可调整参数。现场把每种机型的标准工艺参数录入一次后续调机改参数直接在HMI上改PLC程序一次下载跑完就不动了。4.3 跨机型调试的数据记录对比同时跑8种机型的调试代码层面唯一共通的语言就是数据。我们在FB里把每个循环的关键数据打时间戳存到缓冲区方便上位机读取。调试阶段对比差异不是拿眼睛看设备跑得快不快而是直接把A机型的压装曲线和B机型的压装曲线放到同一张图上差异一目了然。遇到客户说“我这台设备跑得怎么比上一台慢”直接查曲线数据就知道是节拍差在哪个环节。5. 现场调试实录常见的坑与解决思路架构再好落地都会遇到问题。经过这一轮项目的磨炼我把能想到的坑和对应的解决思路整理出来都是干货。5.1 问题一FB实例之间“互相传染”多实例化FB时最容易踩的坑就是FB内部用了一个全局变量导致两个实例明明应该是独立的行为却牵在一起。一开始写FB时我用了全局的一个定时器数组来做超时控制结果两个机型分别实例化后一个在跑自动另一个在手动调试定时器打架报警信息串了。后来把所有内部底层的辅助变量全部改成了VAR内部私有变量只有跨实例复用的大数据缓冲区才用全局内存并且加上了互斥锁机制。5.2 问题二参数表藏在代码里改动无记录参数表一开始放在全局变量里狗啃了一样改了哪里没记录。后来有一次客户反馈某机型的压装压力不对查了半天发现是某个工程师连PLC时不小心改了全局变量的初始值。血的教训。现在的方案是参数表不写在代码里全部放在电柜内的存储区或者HMI配方系统里PLC程序不存配方值配方值每次启动从外部加载从源头杜绝误改。即使必须写默认值也要在写注释时标注“修改需签名”并且接进配方版本管理流程。5.3 问题三超时时间设置不合理导致误报警多机型覆盖后不同机型同一动作的物理时间差异很大。例如伺服压装快速进给阶段只需要1秒液压缸却需要3秒。如果超时时间在FB里固定写死2秒那液压机型必报警。后来把超时时间放入各步骤的配置表作为工艺参数之一让每种机型自己定义。这个改动很小却把误报警问题根除了。5.4 问题四自动流程走一半手动切换卡死FB主状态机是自动和手动共用的有时现场调试人员在自动循环中把操作模式切到手动FB内部还留在自动状态。结果手动操作完了切回自动FB直接卡死动不了。修正方式是在操作模式切换时强制要求设备必须回到待机状态否则模式切换按钮根本不可用。这个逻辑加在HMI侧而不是FB侧因为FB只管自动流程操作模式的约束属于操作者人机逻辑。5.5 问题五模型选型时FB代码版本迭代的连带问题模板代码本身还在不断完善这是必然的但只要FB内部算法稍有改动所有机型实例都会受影响。适合安排每次FB代码升级先在一台设备上验证记录数据正常后再推送到其余机型。不要在一个休息日直接给整个工厂8台设备同时更新程序一旦有问题你连回滚都要分机型那场面太酸爽了。6. 版本管理与团队协作8种机型容易失控版本管理兜底最后说说团队协作层面。8种机型共用一套程序对版本管理的要求比传统方式高得多。传统的8套程序你改一套影响一台出问题顶多局部返工。现在是1套代码出了问题就是全线停产。这个问题不能靠工程师自觉要靠工具和机制。我们的做法PLC代码采用版本控制每次修改必须记录变更说明。每次发布前在离线仿真环境把8种机型全部跑一遍再出包。现场参数由HMI侧管理跟PLC工程代码分离版本各自独立。架构本身给的是复用能力但只有配上了合理的版本管控这件武器才真正敢放手去用。这也算是我在这个项目里学到的最深刻的一课。现在这套架构运行了大概半年中间又加了一款新型号我们只花了一个下午把新机型的参数录入到HMI配方系统试运行了一下个别特殊动作程序完全没有改动顺利交付。回看当初拍桌子决定重写程序值了。如果你手里也有类似的设备家族产品强烈建议早一点做这种参数驱动的FB模板设计越早做后面的收益越大。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →