1. 为什么值得花时间研究 PackML
1.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→Execute |
| STOP | 从 Execute/Starting 等状态 | Execute→Stopping→Stopped |
| HOLD | 从 Execute 状态 | Execute→Holding→Held |
| SUSPEND | 从 Execute 状态 | Execute→Suspending→Suspended |
| RESET | 从 Stopped/Complete/Aborted 状态 | 相应状态→Resetting→Idle |
| ABORT | 任意非 Aborted 状态 | 任意状态→Aborting→Aborted |
| CLEAR | 从 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 对应 Idle,2 对应 Starting,4 对应 Execute,8 对应 Held 等等。这个编码没有完全硬性的统一,但大多数实现是参考 OMAC 给出的推荐值,所以做集成的时候,最好先让对方提供状态编码映射表,别默认按某个顺序直接猜。
PackTag 还讲求“面向对象”,每台设备可以看作一个对象,标签就是对象属性和方法。这样从 PLC 到 SCADA 再到数据库,整个传输链路中每个节点的字段名一致,不用各种改名,排查问题也方便。
模式(Mode)的概念也值得一提。PackML 允许定义最多 16 种模式,常见的有生产模式、手动模式、维护模式、空载模式。模式和状态是两个维度,设备在手动模式下也可能有 Execute 状态,但对应的工艺动作可能只是运转测试,不参与正常生产计数。切换模式时,往往需要先把设备停止或保持在安全状态,再切换。我在实际项目中踩过模式切换的坑,后面会细说。
3. 实操落地:在 PLC 上实现 PackML
3.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”。对接前一定要把对方的编码表拿到手,哪怕麻烦也要先确认,别想当然。我吃过一次亏,设备本地显示 Execute,MES 显示 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。这是正式的推荐指南,内容更严谨,适合碰到细节争议时去查。比如某个状态到底能不能从这个转换到那个,在这份手册里能查到明确的定义。阅读时不用从头到尾逐字啃,当字典查就行。
第三类是厂商的现成库。罗克韦尔、西门子、B&R 等主流 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 带给我的最大额外收获。