3. 工作站编排的核心对象建模
工作站编排系统要解决的第一个工程问题,不是“怎么调度”,而是“调度什么”。如果任务、动作、资源、物料、工位、设备状态和约束条件各自用一套私有数据结构描述,编排器就无法在统一语义下比较、排序和冲突消解。本节的目标是建立一套可落地的统一对象模型,让七类核心对象共享同一套标识、时间、状态和约束表达方式。
一、七类对象的职责边界
先明确每类对象“描述什么、不描述什么”,避免建模时职责重叠:
- 任务(Task):面向业务目标的最小可调度单元,例如“完成工件A的装配”。任务本身不关心由谁执行,只声明目标、输入物料、期望产出和截止时间。
- 动作(Action):任务分解后的可执行原子操作,例如“抓取”“拧紧”“拍照检测”。动作绑定具体设备能力,是编排器真正下发到控制器的粒度。
- 资源(Resource):可被占用的执行能力,包括机器人、夹具、工具、AGV、相机等。资源有容量、可用时间窗和占用状态。
- 物料(Material):被加工、搬运或检测的对象,具有批次、位置、工艺状态和追溯标识。
- 工位(Station):物料停留和动作发生的物理位置,是资源与物料的交汇点,例如装配工位、检测工位、缓存位。
- 设备状态(DeviceState):资源在某一时刻的运行态,如空闲、运行、故障、维护、离线。
- 约束条件(Constraint):限制动作或任务可行性的规则,包括时序约束、资源互斥、工艺顺序、空间可达性和时间窗。
这七类对象不是并列孤立的,而是通过“任务—动作—资源—工位—物料”的执行链和“状态—约束”的可行性判定层耦合在一起。下图给出统一对象模型的关系结构:
二、统一描述方法:共享字段与引用机制
统一建模的关键不是把所有对象塞进一个大结构体,而是让七类对象共享一组基础字段,并通过ID引用建立关系。建议每个对象至少包含以下公共字段:
| 字段 | 含义 | 示例 |
|---|---|---|
| id | 全局唯一标识,前缀区分类型 | TASK-001、ACT-012、RES-RB02 |
| type | 对象类型枚举 | task / action / resource / material / station / state / constraint |
| time_window | 有效时间窗或时间戳 | [t_start, t_end] 或单点时刻 |
| status | 当前状态 | pending / ready / running / done / failed |
| constraints | 引用的约束ID列表 | [CON-003, CON-007] |
| version | 版本号,用于并发更新 | 3 |
对象之间只存ID引用,不嵌套复制。例如动作对象中不直接写入机器人完整信息,只写 resource_ref: "RES-RB02";编排器需要时再查资源表。这样做的工程收益是:资源状态变化只需更新资源对象一处,所有引用它的动作自动获得最新状态,避免数据不一致。
三、约束条件的分类与表达
约束是统一模型中最容易被忽视、却最影响编排正确性的部分。按作用方式可分为四类:
- 时序约束:动作A必须在动作B之前完成,表达为
predecessor: ACT-A, successor: ACT-B。 - 资源互斥:同一资源同一时刻只能被一个动作占用,表达为资源容量
capacity: 1加时间窗不重叠检查。 - 工艺顺序:物料必须经过工位1→工位2→工位3,表达为物料工艺路线
route: [ST-01, ST-02, ST-03]。 - 空间可达性:机器人只能作用于其工作空间内的工位,表达为资源—工位可达矩阵。
约束不直接修改对象状态,而是作为可行性判定函数参与编排决策。编排器在生成候选动作序列时,逐条检查约束是否满足,不满足则剪枝。
四、一个可运行的对象模型示例
下面给出一个装配工作站的简化对象模型片段,展示七类对象如何用统一格式描述。参数为教学示例,非实测数据:
{
"task": {
"id": "TASK-001", "type": "task",
"goal": "完成工件A的螺栓装配",
"material_ref": "MAT-A01",
"deadline": 120.0,
"constraints": ["CON-001", "CON-002"]
},
"action": {
"id": "ACT-012", "type": "action",
"name": "拧紧螺栓M6",
"resource_ref": "RES-RB02",
"station_ref": "ST-01",
"duration": 8.5,
"predecessor": "ACT-011",
"constraints": ["CON-003"]
},
"resource": {
"id": "RES-RB02", "type": "resource",
"category": "robot",
"capacity": 1,
"reachable_stations": ["ST-01", "ST-02"],
"state_ref": "STATE-RB02"
},
"material": {
"id": "MAT-A01", "type": "material",
"batch": "B2024-07",
"location": "ST-01",
"process_state": "待拧紧",
"route": ["ST-01", "ST-02"]
},
"station": {
"id": "ST-01", "type": "station",
"name": "装配工位1",
"resources": ["RES-RB02", "RES-FX01"],
"capacity": 1
},
"device_state": {
"id": "STATE-RB02", "type": "state",
"resource_ref": "RES-RB02",
"value": "idle",
"timestamp": 45.2
},
"constraint": {
"id": "CON-003", "type": "constraint",
"kind": "resource_exclusive",
"target": "RES-RB02",
"expression": "no_overlap(time_window)"
}
}
这个模型可以直接被编排器加载:任务声明目标,动作绑定资源与工位,物料携带工艺路线,设备状态提供实时可用性,约束提供可行性判定规则。编排器遍历动作图,检查约束,输出可执行序列。
五、建模时的常见陷阱
陷阱一:把状态写进资源对象本体。资源对象应只存静态属性(类别、容量、可达工位),动态状态单独用设备状态对象表达。否则每次状态变化都要改资源定义,版本管理会失控。
陷阱二:约束硬编码在动作里。约束应独立成对象,通过ID引用。这样同一条“资源互斥”约束可以复用于多个动作,修改时只改一处。
陷阱三:物料位置与工位状态不同步。物料位置字段和工位占用状态必须由同一事件驱动更新,否则会出现“物料显示在ST-01,但ST-01显示空闲”的矛盾。
实践建议:先用表格把七类对象的字段列全,再写JSON Schema校验。每次编排前跑一次模型一致性检查:所有引用ID是否存在、时间窗是否合法、约束表达式是否可解析。这一步能拦截大部分编排失败。
统一对象模型的价值在于:编排算法不需要为每类对象写特殊分支,只需要一套通用的“查询—检查约束—更新状态”接口。任务、动作、资源、物料、工位、设备状态和约束条件在同一语义空间中被描述和操作,后续的调度、冲突消解和异常恢复才能建立在可靠的数据基础之上。