A few years ago, most cobots lived in their own little world: a tabletop cell, a vision system, a pick-and-place routine, maybe a light curtain if someone was feeling cautious. Nobody asked hard questions about how that cell reported its state to SCADA because it didn’t really need to. It was an island. Now it isn’t. Cobots are getting wired directly into discrete packaging and assembly lines — feeding cartoners, tending machines, doing final assembly steps alongside traditional servo-driven equipment — and that means they have to speak the same operational language as everything else on the line. For most packaging and consumer goods plants, that language is PackML.
Here’s the uncomfortable part: PackML’s state model was written with PLC-driven, deterministic machines in mind. Idle, Starting, Running, Held, Suspended, Aborted, Stopped, Completing — these states assume a machine that is either doing its job at rated speed or is unambiguously not doing its job. Cobots don’t work that way. They slow down when a person gets close. They pause mid-motion for a force event that isn’t a fault. They sit in states that don’t map to anything in the ISA-88/PackML vocabulary because the vendor SDK was designed by robotics engineers, not packaging engineers. If you integrate a cobot into a PackML-governed line without doing real translation work, you get a cell that reports “Running” constantly — even while it’s crawling at reduced speed, even while it’s paused waiting for a human to clear a zone. That’s not a minor cosmetic problem. It’s bad OEE data, bad changeover tracking, and a line controller that can’t actually reason about what’s happening.
Why the black-box approach doesn’t survive contact with line integration
In a standalone cell, you could get away with treating the cobot as a fixed-cycle-time black box: it’s either producing or it’s down, full stop. Once that cobot is a station inside a PackML-governed line — reporting into an ISA-95-aligned MES, feeding a line-level OEE calculation, participating in a changeover sequence — the black-box model breaks. Line control needs to know not just “is the cobot moving” but “is the cobot doing productive work at its intended rate,” because those are different questions with a cobot in a way they mostly aren’t with a fixed-guarded robot cell.
This matters most in three places: OEE performance-loss accounting, changeover/changeover-time tracking, and fault escalation. If a collaborative slowdown — the cobot reducing speed because a person entered a sensing zone — gets reported as Running, your availability number looks great and your performance number silently eats the loss with no attribution. Nobody can tell from the dashboard whether the line is slow because of a real bottleneck or because an operator keeps wandering into the cobot’s zone during changeovers. You’ve made the data worthless for the exact question you built the dashboard to answer.
Mapping vendor states onto PackML: where it’s easy and where it isn’t
Some of this mapping is genuinely straightforward. Most cobot controllers (whether you’re working with a URCap-style program state, a vendor SDK’s robot state enum, or a safety controller’s status word) expose something like Program Running, Program Paused, Program Stopped, Safeguard Stop, Protective Stop, and Emergency Stop. Those map reasonably well:
- Program Running at full rate → PackML Running.
- Program Stopped / cycle complete → PackML Complete or Idle, depending on where you are in the cycle.
- Emergency Stop → PackML Aborted, unambiguously.
- Operator-initiated pause via HMI → PackML Held, since it’s a deliberate hold within a running mode.
Where it gets genuinely difficult is the collaborative-specific states that don’t have a clean PackML analog:
Force-limited safety stops aren’t “Held”
A protective stop triggered by force/torque sensing — the arm detects unexpected resistance and stops to comply with ISO/TS 15066 collaborative operation requirements — looks superficially like a Held state (production is paused, no fault has been declared, recovery is expected). But treating it as Held is a category error. Held in PackML implies an operator or upstream/downstream condition intentionally paused a healthy machine. A force-limited stop is a safety event, even when it doesn’t rise to E-stop severity. If you map it straight to Held, you bury safety-relevant events inside your routine hold-time statistics, and nobody doing OEE analysis downstream will ever notice that “held” time has a safety signature to it. The more honest approach is to carry the safety context as a sub-state or reason code attached to Held (or, in more conservative implementations, route it through Held with a mandatory “stop reason” tag rather than trying to invent a new top-level PackML state — PackML deliberately doesn’t want to be extended casually, and a proliferation of custom states defeats the purpose of adopting the standard in the first place).
Collaborative slowdown is where “Suspended” gets stretched thin
Speed and separation monitoring — the cobot reducing velocity as a person approaches, per ISO/TS 15066 — is the hardest case of all, because the machine is still producing, just at reduced rate. That’s not Held (nothing stopped) and it’s not cleanly Running either, since your performance-loss accounting needs to know the line is running below rated speed for a reason other than a mechanical or process fault. Some integrators shoehorn this into Suspended, on the logic that Suspended is meant to capture “running but not producing at intended output due to an external condition.” That’s a defensible reading, but it only works if you’re disciplined about not also using Suspended for unrelated starvation/blocking conditions elsewhere on the line — otherwise you’ve merged two different loss categories into one bucket and made root-cause analysis harder, not easier.
Build the OPC UA interface to tell the truth, not to look tidy
The practical fix is architectural, not philosophical. Don’t let the cobot’s native state land directly in the PackML tag as a one-to-one passthrough. Put a thin translation layer — whether that’s logic in the cell’s PLC, a small edge gateway, or logic inside the cobot’s own program — between the vendor SDK/URCap state and the PackML state machine your OPC UA server exposes. That layer should do three things:
- Expose the standard PackML state (Idle, Starting, Running, Held, Suspended, Aborted, Stopped, Complete) as the primary tag, so SCADA/MES and any PackTags-compliant consumer can read it without special-casing your cell.
- Carry a secondary reason-code or sub-state tag — a real, documented enumeration, not a free-text field — that captures the actual cobot condition: force-limited stop, speed-and-separation slowdown, gripper fault, vision retry, operator-initiated pause. This is where the nuance lives without polluting the top-level state model.
- Timestamp state transitions at the source (the cobot controller or its immediate gateway), not at the SCADA poll cycle, so fast collaborative-slowdown events don’t get smeared or missed by a slower OPC UA subscription rate.
This is more engineering work than pointing an OPC UA server at whatever enum the vendor SDK exposes and calling it done. It requires someone — usually the controls engineer, sometimes working with the MES integrator — to sit down with the actual safety and motion documentation for that specific cobot platform and decide, deliberately, how each native state and fault code rolls up into PackML’s eight states plus a reason code. That mapping should be documented once, in a table, and treated as part of the machine’s control documentation the same way you’d document a PLC’s fault code list.
The payoff is data that matches what actually happened on the floor
Do this correctly and your line-level OEE stops lying about performance loss, your changeover tracking can actually distinguish “the cobot is recalibrating a gripper offset” from “an operator is standing in the collaborative zone during setup,” and your maintenance team gets a real signal when force-limited stops start clustering around a specific fixture wear pattern instead of that information disappearing into an undifferentiated Held bucket. Get it wrong — or skip it because the vendor’s out-of-the-box OPC UA server already exposes something PackML-shaped — and you’ve adopted the letter of PackML without the substance: a state machine that technically conforms to the standard’s tag structure while quietly misrepresenting what the collaborative robot is actually doing on your line. As more packaging and assembly lines mix cobots with conventional servo and pneumatic stations, that gap between conformance and honesty is exactly the kind of thing that determines whether your OEE numbers are a management tool or a comforting fiction.
This article was written with the assistance of artificial intelligence. While we aim for accuracy, the information may be incomplete, out of date, or incorrect, and should be independently verified before you rely on it for any decision. It is provided for general information only and does not constitute professional advice.
