1. 为什么值得花时间研究 PackML1.1 设备集成时最痛的那几件事做包装自动化这些年真正让我下决心去研究 PackML 的不是标准文档里那些高深的状态图而是去年集成三条包装线时被折腾出来的血泪。三台设备分别来自三个不同厂商一台叫 Run一台叫 AUTO另一台状态变量里直接写了个 99 表示运行中。MES 那边要求每个工位上报状态我只好一个一个设备去对点翻了三天的手册写了一堆判断逻辑到最后还有两台设备的报警状态对不上。这种乱象在包装行业太常见了。PLC 程序是每个工程师按自己习惯写的状态命名五花八门状态值定义随心所欲有的用整数有的用字符串有的干脆把状态放在 HMI 的文本列表里。设备内部怎么跑只有原厂自己清楚一旦做产线级集成、做数据采集、做 OEE 统计麻烦就全来了。PackML 就是冲着这个痛点来的。它的全称是 Packaging Machine Language由 OMAC 组织推动后来被纳入 ISA-TR88.00.02 标准体系本质上是一套包装机械状态建模和接口规范。它把一台机器内部“在干什么”“处于什么状态”“接受什么命令”用统一的方式定义出来再配上一套标准化的标签结构让设备之间、设备与上层系统之间能够用同一种语言对话。1.2 PackML 到底解决了什么问题先说最直观的收益状态统一。以前每台设备的状态列表是各写各的现在按 PackML 定义任何一台包装设备都跑不出那十几个标准状态。MES、SCADA、OEE 系统只需要认这一套状态即可不用再为每家设备单独写翻译层。再说命令统一。启动、停止、保持、恢复、复位、中止这些操作在任何 PackML 设备上的语义是一致的。操作员换了一条产线不需要重新学习一套操作习惯因为按钮逻辑、状态反馈、互锁条件都遵循同样的框架。还有数据结构的统一。PackML 定义了标准的标签集合包括状态、命令、管理信息设备端把这些标签暴露出来上层直接读取和写入就行。我在实际项目里最深的体会是有了 PackML设备集成工作从“考古式沟通”变成了“按标准对接”效率不是一个量级。PackML 不是强制每个设备内部必须怎么编程而是约定对外呈现的模型和接口。内部你用梯形图、结构化文本、SFC随便只要对外是 PackML 那套状态机和标签别人就能低成本接入。这个思路很务实也是它能被广泛接受的原因。1.3 这篇笔记适合谁看如果你正在做包装设备、灌装线、装盒机、贴标机、码垛机或者任何和包装自动化相关的项目这篇内容值得花十分钟读完。尤其是下面这几类人被多厂商设备集成折磨过的项目工程师、调试工程师需要把设备状态送给 MES/SCADA 的自动化工程师做设备标准化的设备厂商、OEM 厂商的技术负责人想搞懂状态机和设备建模的 PLC 程序员我会从概念拆解、落地实操、踩坑记录三个角度来写尽量避免教科书式的大段理论以实际能落地的思路为主。2. PackML 核心概念拆解2.1 状态机模型一台设备到底有多少种状态PackML 的状态机模型是它的核心来源是 ISA-TR88.00.02 和 IEC 61512 标准。整套状态机包括 15 个标准状态和 7 个标准命令。状态之间不是随便切换的必须遵循模型定义的转换路径。这看起来约束很多但正是这种约束让所有设备的行为可以预期。我习惯把这 15 个状态按照“设备在干嘛”分成四组来记分组状态说明工作状态Execute设备正在正常执行生产任务转换状态Starting、Completing、Holding、Held、Suspending、Suspended、Resetting、Stopping、Stopped、Clearing、Aborting、Aborted设备从一个稳定状态向另一个稳定状态过渡的过程空闲状态Idle设备已准备好接受启动命令但还没有开始运行停止状态Stopped设备处于停止状态可能有未完成的批次或需要复位这套状态机设计得比较细致实际项目里并不一定每个状态都用上比如 Suspending/Suspended 和 Holding/Held 的区别很多团队干脆只实现其中一组。但理解全部状态的含义还是必要的因为你不知道哪台设备的协议里会出现什么状态。简单说一下几个容易混淆的状态Held 和 Stopped 的区别Held 是暂停设备还在工作条件下随时可以恢复执行Stopped 是停止可能需要重新走复位和启动流程。Aborted 和其他状态的区别Aborted 表示设备发生了严重异常必须经过 Clearing 清理流程才能回到可运行状态。Completing/Complete 表示一个批次或一份订单已经生产完毕设备正在收尾。状态机是设备这台“演员”的剧本。状态就是角色命令就是上台下台的指令只有剧本统一了不同的演员才能毫无障碍地搭戏。2.2 命令集怎样驱动状态机运转状态不会自己变必须由命令来驱动。PackML 定义了 7 个标准命令RESET、START、STOP、HOLD、SUSPEND、ABORT、CLEAR。另外还有一组对应 HMI 操作习惯的扩展命令比如 UNSUSPEND但核心就上面这七个。命令和状态转换的关系我用一张表整理过实际编程时就是照着这张表做判断命令触发条件目标转换START从 Idle 或 Stopped 状态且已复位Idle→Starting→ExecuteSTOP从 Execute/Starting 等状态Execute→Stopping→StoppedHOLD从 Execute 状态Execute→Holding→HeldSUSPEND从 Execute 状态Execute→Suspending→SuspendedRESET从 Stopped/Complete/Aborted 状态相应状态→Resetting→IdleABORT任意非 Aborted 状态任意状态→Aborting→AbortedCLEAR从 Aborted 状态Aborted→Clearing→Stopped/Idle注意几个细节。RESET 和 CLEAR 的区别在于RESET 是把设备从停止/完成状态恢复到空闲准备状态比如产品换型后清理完成按一下复位就可以接受下一次启动命令CLEAR 是针对故障状态的设备发生了 ABORT状态跑到 Aborted 以后必须先 CLEAR 把故障信息清除、把设备机构恢复到安全位然后才能进入 Stopped 或 Idle。每个命令都有允许的“前状态”不是任何时候都能按。如果设备在 Execute 状态按 HOLD 是有效的但如果设备已经在 Idle 状态按 HOLD那就是错误命令控制器要么拒绝执行要么报警提示操作员。这一点在做 HMI 按钮使能逻辑时非常重要。2.3 PackTag 标签结构设备对外说的“共同语言”状态机和命令定义的是行为框架真正对接 MES、SCADA 时还要看数据怎么交换。PackML 配套定义了 PackTag 规范也就是一套标准的标签集合用来描述设备状态、命令、模式、数据。PackTag 不是死板的单点标签而是一组按功能划分的结构化标签大致包括状态类标签当前状态、状态变化时间戳、前一状态等命令类标签操作员或上层系统写入的命令管理类标签设备模式、单元编号、产品 ID、当前批次号等数据类标签计数、速度、温度等任意工艺数据状态类标签的值一般按整数编码对应 15 个状态。比如 1 对应 Idle2 对应 Starting4 对应 Execute8 对应 Held 等等。这个编码没有完全硬性的统一但大多数实现是参考 OMAC 给出的推荐值所以做集成的时候最好先让对方提供状态编码映射表别默认按某个顺序直接猜。PackTag 还讲求“面向对象”每台设备可以看作一个对象标签就是对象属性和方法。这样从 PLC 到 SCADA 再到数据库整个传输链路中每个节点的字段名一致不用各种改名排查问题也方便。模式Mode的概念也值得一提。PackML 允许定义最多 16 种模式常见的有生产模式、手动模式、维护模式、空载模式。模式和状态是两个维度设备在手动模式下也可能有 Execute 状态但对应的工艺动作可能只是运转测试不参与正常生产计数。切换模式时往往需要先把设备停止或保持在安全状态再切换。我在实际项目中踩过模式切换的坑后面会细说。3. 实操落地在 PLC 上实现 PackML3.1 方案选型用图还是用文本实现状态机概念理解了紧接着的问题就是代码怎么写。PackML 是模型框架不是具体实现方式所以同一个状态机你可以用梯形图写、用结构化文本写、用 SFC 写甚至用一堆 JMP/CALL 指令硬写。但不同方案维护难度差别很大。我自己的经验不建议用梯形图去实现整台设备的状态机。梯形图适合表达逻辑互锁和 I/O 控制但表达状态转换关系时非常吃力状态多了以后程序就是一团乱麻。也不建议用一堆互相跳转的 JMP 指令因为状态跳转路径不直观调试时想看清‘现在为什么卡住了’特别费劲。比较实用的做法有两种第一种用结构化文本ST配合枚举和 CASE 语句。思路是定义状态枚举和命令枚举写一个状态机功能块内部用 CASE 处理当前状态每个状态里判断哪些命令有效、执行哪些动作。这种方式优点是逻辑集中、边界清晰一套代码可以复制到多台设备适合设备标准化。第二种用 SFC。SFC 本身就是为顺序控制设计的和 PackML 的状态转换天然匹配每一步对应一个状态转换条件对应命令和工艺条件。缺点是 SFC 在复杂逻辑分支时有时反而不如 ST 直接而且各家 PLC 的 SFC 实现风格差异较大跨平台复用难度高。我个人倾向用 ST 写一个通用状态机功能块把状态逻辑和工艺逻辑分开。状态机只负责状态转换工艺逻辑通过进入动作、退出动作的接口调用外部功能块。这样设备之间的差异全部被隔离在工艺逻辑那一层状态机本身几乎不用改。3.2 搭一个基础的状态机骨架这里给一个最小可用的 ST 代码骨架思路可以参考。不需要完整工程代码核心是把状态机的骨骼搭出来。先定义类型TYPE PackML_State : ( State_Idle, State_Starting, State_Execute, State_Holding, State_Held, State_Suspending, State_Suspended, State_Completing, State_Complete, State_Resetting, State_Stopping, State_Stopped, State_Aborting, State_Aborted, State_Clearing ) : State_Idle; END_TYPE TYPE PackML_Command : ( Cmd_None, Cmd_Start, Cmd_Stop, Cmd_Hold, Cmd_Suspend, Cmd_Reset, Cmd_Abort, Cmd_Clear ) : Cmd_None; END_TYPE然后是功能块的主循环用 CASE 处理状态转换。拿 Execute 状态举例CASE currentState OF State_Execute: // 执行工艺动作 bMachineRunning : TRUE; // 接收命令做状态转换判断 IF cmd Cmd_Hold THEN currentState : State_Holding; // 触发保持动作比如暂停进料 bHoldRequested : TRUE; ELSIF cmd Cmd_Stop THEN currentState : State_Stopping; bStopRequested : TRUE; ELSIF cmd Cmd_Abort THEN currentState : State_Aborting; bAbortRequested : TRUE; END_IF;注意一个关键点在状态机里命令一般是“边沿触发”也就是命令从无到有的那一下才有效而不是电平持续有效。否则操作员一直按住 HOLD 按钮状态机可能反复执行进入保持的动作。所以功能块内部会做上升沿检测通常用上一个扫描周期的命令值做比较。这个骨架跑起来以后先别急着挂整条产线的工艺先做一件事把状态值、命令值、触发标志全部暴露出来连一个简单的 HMI 页面手动发命令验证状态转换是否正确。等到转换关系验证没问题再开始往各个状态里填工艺动作。3.3 状态转换的“进入动作”和“退出动作”状态机本身只解决“状态怎么跳”真正让设备干活的是状态切换时执行的动作。这里有个非常重要的设计原则把动作挂在进入动作和退出动作上而不是挂在状态内部一直执行。什么叫挂在状态内部就是某些程序把电机启动、气缸动作直接写在 State_Execute 里每个扫描周期都去执行一次。这样做在简单的逻辑里问题不大但一旦动作有时序要求比如必须在进入 Execute 之后的第一个周期启动电机第二秒打开阀门第三秒才允许投料那直接写在状态内部就非常难控制还容易在状态反复切换时重复执行。我习惯把动作按作用分成三类进入动作进入某个状态的瞬间执行包括启动设备、置位标志、记录时间戳持续动作状态保持期间每个扫描周期都执行比如速度闭环控制、看门狗刷新退出动作离开某个状态的瞬间执行包括停止电机、复位标志、保存计数举个例子设备收到 START 命令后从 Idle 进入 Starting这时启动进料皮带条件满足后再切到 Execute此时开始计数收到 STOP 命令进入 Stopping这时关闭进料阀等待相关机构停下来进入 Stopped 时把当前批次号归档。每一步动作挂在对应的边缘上状态机就非常清爽。为了让这套逻辑可维护我给每个状态都定义了显式的进入/退出动作功能块接口状态机核心代码里不写任何具体工艺只做状态跳转。设备和设备之间的差异全部通过外部功能块的参数隔离出去。实际用下来新增一台设备时状态机代码基本不用动只需要填工艺动作调试时间能砍掉一多半。3.4 和 HMI 对接时的状态显示与命令按钮状态机在 PLC 里跑起来了下一个问题就是 HMI 怎么显示、怎么操作。这块看起来很基础但恰恰是体现 PackML 价值最明显的地方。HMI 上显示状态最省事的方式是拿状态整数直接做个文本列表映射。但这样做会有一个问题状态文本是固定的换了一台设备可能有些状态根本不存在但文本选项还在那里。更好的做法是让 HMI 从 PLC 读取一组“可用状态”和“当前状态”然后动态生成显示文本。这等于把状态机定义也拿到了 HMI 侧两边始终是同一个模型。命令按钮的做法也需要注意使能逻辑。建议在 PLC 侧实现一个“命令允许矩阵”根据当前状态输出当前允许执行的命令列表HMI 直接读取这个列表来禁用或启用按钮。比如当前状态是 Execute那么允许的按钮是 Hold、Stop、Suspend、Abort当前状态是 Aborted那么只允许 Clear其他按钮全部置灰。这样操作员不会误触无效命令也符合安全习惯。还有一个实战小技巧HMI 上除了显示当前状态我还习惯把状态的“进入时间”显示出来。有些设备在 Starting 状态卡了很久操作员一看就知道出问题了比让维护人员去翻 PLC 抓着强得多。4. 实际项目中踩过的坑和排查技巧4.1 状态卡在 Starting、Stopping 这类过渡状态这个问题是 PackML 落地时最容易遇到的。设备从 Execute 收到 STOP 命令后状态跳到 Stopping然后就再也不动了。查程序发现 Stopping 后面的转换条件一直不满足导致状态机永远停在中途状态。最常见的原因有三个一是停止动作本身没有完成标志比如电机虽然停了但是没有反馈信号二是停止过程依赖某些异步动作比如气缸缩回需要时间程序却没等反馈就死等下一个条件三是启动条件永远不满足比如安全门微动开关没信号但程序在 Starting 状态里一直等到门关上才算成功。排查方法其实不复杂前提是状态机的每个转换条件要能被外部监视。我给每个条件都做了命名标志位比如 bCond_MotorStopComplete、bCond_SafetyDoorClosed然后在 HMI 上加了一个“状态转换监视页”把每个过渡状态的转换条件实时显示出来。谁为 0、谁为 1一眼看清比拿万用表去量端子快太多。如果你发现自己总是在调试时翻条件那说明这个状态机的可观测性设计失败了。状态机的好处除了统一还有一个很容易忽略的优势它天然适合做可视化监控。每个状态、每个转换条件都是明牌这比传统散点逻辑调试时靠猜强多了。4.2 手动模式切回自动模式时的状态冲突设备做维护时维修工经常要把设备切到手动模式手动点动某个电机、气缸然后退出维护模式切回自动。但这个过程中状态机最容易被搞乱。举个例子设备在 Execute 状态切到维护模式状态机却还停留在 Execute 上维修工手动动作了几次再切回自动程序还以为是正常的 Execute 状态计数继续累加工艺也直接往下跑。这个行为会导致产量数据混乱甚至设备在机构还没复位的情况下就重新投入生产。我的处理办法是模式切换时强制状态机先回到受控状态。从运行模式切到维护模式必须先触发 STOP 或 HOLD让状态机离开 Execute 进入 Stopped 或 Held维护模式下面的手动动作和状态机解耦手动点动直接控制输出不经过状态机从维护模式切回自动模式时状态机要求当前状态必须是 Stopped 或 Idle然后操作员重新按复位、启动。一句话总结手动模式是状态机的“外部世界”手动动作结束后必须重新走启动流程不允许手动干预后直接无缝回到自动运行的等级。这个原则执行到位可以减少一大半模式切换相关的诡异故障。4.3 和 MES 上报的状态不一致设备本地状态机已经运行正常但 MES 显示的状态却和设备实际状态对不上。这个坑我排查过好几次最后发现多数问题出在上报时机和上报内容上。一种情况是 MES 轮询周期太长设备已经经过了 Execute、Holding、Held、Execute 这么一圈MES 恰好只采到了中间的短暂状态画面上显示设备在暂停实际上早就恢复了。这种情况建议把上报从“周期轮询”改成“变化边沿触发”状态一变就立刻上报一条带时间戳的记录SCADA 再根据记录重建状态历史。另一种情况是状态编码不统一。前面提到过PackML 的状态编码没有全球统一甲方让你发的“1”可能对应乙方说的“Execute”也可能对应“Idle”。对接前一定要把对方的编码表拿到手哪怕麻烦也要先确认别想当然。我吃过一次亏设备本地显示 ExecuteMES 显示 Stopped最后发现是状态编码差了一位。还有一个容易被忽略的点状态上报时要带上质量戳。设备故障、MES 断线、PLC 停机这些异常情况下上报的状态值没有意义。带一个 Quality 字段比如 0 表示正常、3 表示不可信能帮 MES 侧提前识别坏数据而不是把错误状态存入数据库里污染报表。4.4 状态机和工艺逻辑耦合导致的维护灾难这个坑不是一次性的而是长期积累出来的。前期项目紧为了让设备动起来直接在状态机里塞了大量工艺逻辑某状态里判断了多少次计数、调用了哪个配方、甚至温度 PID 都写在状态机里。刚开始跑得挺好后来工艺要求调整想改状态机里的温度控制结果一改状态跳转也受影响最后整个设备逻辑变成了一座泥潭。好的 PackML 实现一定是状态机和工艺逻辑分层分得很干净的。状态机只做状态跳转和命令分析工艺逻辑通过接口挂载进去互不干扰。状态机是一个“骨架”工艺逻辑是“血肉”两者分开事后才改得动。我在后期重构时把状态机的每一个状态都做成了标准接口状态内部不出现任何具体设备名。比如不写“启动切刀电机”而是写“调用输出控制功能块 bPara_CutterRun”切刀这个细节放到外部配置。好处是下次换一台不同型号的设备这套状态机代码可以原样复用只需要重新映射工艺接口。这套思路做下来即便是新人接手也不用怕把设备状态机改成一锅粥。5. 学习 PackML 的资料路线和进阶方向5.1 先从哪份资料啃起PackML 的资料不算多但深度学习路径还是比较清晰的。我第一次啃的时候走了不少弯路这里给后来者捋一条比较顺的路线。第一份要看的是 OMAC 发布的 PackML 指南。这份材料虽然不是正式标准但内容通俗有很多图示和实例适合作为入门。它会把状态机的来龙去脉、状态转换的过程、PackTag 的结构讲清楚建议先通读一遍抓住整体框架再往下钻。第二份是 ISA-TR88.00.02-2015。这是正式的推荐指南内容更严谨适合碰到细节争议时去查。比如某个状态到底能不能从这个转换到那个在这份手册里能查到明确的定义。阅读时不用从头到尾逐字啃当字典查就行。第三类是厂商的现成库。罗克韦尔、西门子、BR 等主流 PLC 厂商都提供 PackML 相关的库手册或示例工程。西门子没有官方的固定 PackML 库但社区里有实现可以参考。找一个你看得顺眼的例子把状态机代码逐行读懂比自己从零琢磨效率高很多。5.2 PackML 和 ISA-88 批处理是什么关系学习 PackML 的过程中一定会碰到 ISA-88 这个标准。PackML 在某种程度上可以说是 ISA-88 思想在包装机械领域的落地分支。ISA-88 定义了批处理的物理模型过程单元、设备单元、设备模块、控制模块以及状态模型和配方管理。PackML 沿用了其中状态模型的内核又针对包装机的特点进行了简化。理解这层关系对做设备标准化有帮助。比如 PackML 中的模式、命令、状态机设计思路和 ISA-88 的工序模型是一脉相承的。如果你之前做过批产系统的 S88 模型再看 PackML 会非常顺手。反过来如果你先学了 PackML再去接触 ISA-88 的批量控制也会发现很多概念似曾相识。我个人的学习体会是不要只停留在“会实现 PackML 状态机”这个层面要理解它背后“统一模型、统一接口、统一数据”的设计哲学。这样才能在不同品牌 PLC、不同设备类型之间灵活迁移而不是死记硬背几个状态名。5.3 从 PackML 走向设备数字化PackML 现在的发展方向已经不只是解决设备内部状态统一的问题了。随着 OPC UA 的普及PackML 规范和 OPC UA 的配套规范也出来了直接把 PackML 的标签结构和状态机映射到 OPC UA 的信息模型上设备可以通过 OPC UA 服务器直接暴露标准化的状态和命令接口上位系统只要支持这个规范就能即插即用地对接设备。这对设备数字化非常有意义。以前做一套产线数字化需要为每台设备写一个数据采集适配器现在只要设备支持 PackML/OPC UA采集系统就能自动识别设备状态、产出数据、报警信息。数据采集、OEE 计算、远程运维、预测维护全都建立在这套标准接口之上。如果你所在的企业有设备出口或集团统一信息化项目建议尽早把 PackML 作为设备控制器的标准配置。哪怕客户当时没有要求提前把状态机和标签按标准做后续接任何系统都从容很多也能省下大量“补标准”的钱。6. 学习和落地 PackML 的一些心得回看我自己的学习和落地过程有一个体会特别深PackML 最难的地方不是状态机怎么实现而是让整个团队接受“先定义模型再写逻辑”的工作方式。很多工程师习惯了一上来就写代码设备能跑就行觉得状态机是多余的前置工作。但真正到了多设备集成、长期维护、系统对接的时候那点前置成本换来的收益是十倍百倍的。我个人建议所有新上的包装设备项目不管客户有没有提要求内部尽量按 PackML 的框架来做。不需要一开始就把 15 个状态全做出来可以先从 Idle、Starting、Execute、Stopping、Stopped、Aborting、Aborted、Clearing 这几个最核心的状态开始跑顺了再补充 Holding、Suspending 这些扩展状态。这样既不会因为模型太复杂影响项目进度又保留了后续扩展的余地。最后分享一个实际用下来的小技巧在状态机功能块外面包一层“运行日志”记录功能记录每一次状态转换的时间、来源状态、目标状态、触发命令。这些日志是排查现场问题最犀利的武器。以前设备出故障大家在现场猜原因现在直接翻日志能精确到毫秒级看到设备从哪个状态因为哪条命令变成了哪个状态问题往往十几分钟就能定位。这一点是 PackML 带给我的最大额外收获。