22 KiB
工业自动化生产线管控系统 详细设计方案
文档信息
- 版本:V2.4(最终生产版·固定槽位与批量补料)
- 日期:2026-05-01
- 状态:可交付开发
目录
- 项目背景与目标
- 核心设计理念
- 系统总体架构
- 事件总线与一致性模型
- 工艺路线定义
- 数据库设计
- 工件生命周期与状态管理
- 调度器三层架构设计
- 任务(Task)状态机与生命周期
- 工单管理详细设计
- 工站接口设计
- 机器人子调度器与手持工具
- 超时与心跳机制
- 运行期状态校验
- 断电恢复与事件重放
- Redis数据结构设计
- 暂存台槽位管理与批量补料
- 与外部系统交互
- 关键技术选型
- 部署与扩展性
- 附录A:PLC信号与事件映射表
1. 项目背景与目标
1.1 项目背景
本系统面向一条多品种小批量柔性生产线,核心设备包括:
- 4台CNC加工中心(CNC_1~4)
- 高压清洗机(双槽位,关门批处理,清洗时间2~3分钟/件)
- 内窥镜检测滚筒线(自动检测分流)
- 热能去毛刺机
- 除锈清洗线(5位FIFO流水线)
- 激光打标机(无上下料位,手持作业)
- 地轨机器人(唯一搬运设备)
- 10个接驳台(各9槽位)
- 随行暂存台(8个固定槽位)
- 抽检台(1号槽位用于异常工件暂存)
可加工的工件类型及机床绑定关系:
| 基本类型 | 子型号 | 绑定机床 |
|---|---|---|
| A6VM107 | B99414, B99487 | CNC_3, CNC_4 |
| A6VM160 | B99464, B99488 | CNC_1, CNC_2 |
| A6VM200 | B99259 | CNC_1 |
1.2 建设目标
- 工件全生命周期实时追踪与可视化
- 事件驱动 + 状态机自动调度,无需预编排任务图
- 多品种混流生产,灵活应对异常(插单、故障、返工)
- 新增工件类型或设备仅需配置,不改核心代码
- 工业级健壮性:防重复派单、防状态回退、超时心跳、分级断电恢复
2. 核心设计理念
2.1 工艺状态机驱动
每种工件定义静态工艺路线(状态机),工件实例仅保留当前工序指针、状态、上下文及物理位置。绝不预生成任务实体,所有前进步骤均由事件触发实时决策。
2.2 事件驱动 + Redis 单一真相源
- Redis 是运行时唯一状态源,所有调度决策仅查询 Redis。
- PostgreSQL 仅作持久化日志和审计存储,不参与在线决策。
- PLC 信号是输入,不是状态源。
- 每个状态变更均产生不可变事件,全局唯一 ID,实体版本号防乱序,所有处理器幂等。
2.3 调度三层解耦
调度引擎拆分为 任务生成 → 约束过滤 → 策略选择,各层独立可测,易于扩展。
2.4 机器人子调度器
调度引擎只下发抽象运输任务(取、放工件),机器人控制器自行完成路径规划、手持工具调用、重试。
2.5 工单与调度过滤
工单暂停仅修改工单自身状态,调度时通过约束过滤层排除,不污染工件状态。
2.6 暂存台固定槽位策略
暂存台提供 8 个固定槽位,工件在生产周期内独占一个槽位直到下料。补料策略:首次尽量装满(8件),后续当空位数超过 4 个时,一次性批量补满。
2.7 工业级健壮性
- 实体版本号防并发和状态回退
- 原子抢占防重复派单
- Task 状态机跟踪生命周期,支持超时与重试
- 设备心跳监控在线状态
- 分级断电恢复策略
3. 系统总体架构
本系统为 单体应用,采用分层模块化设计,打包为单个可执行文件,内部按职责划分包(package),通过接口通信。
┌──────────────────────────────────────────────────────┐
│ 人工终端 (React) │
└──────────────────────┬───────────────────────────────┘
│ WebSocket / HTTP
┌──────────────────────▼───────────────────────────────┐
│ go-zero HTTP Server (API层) │
│ ┌─────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │工单管理 │ │看板/监控 │ │人工交互接口 │ │
│ └────┬────┘ └────┬─────┘ └────────┬───────────┘ │
│ │ │ │ │
│ ┌────▼────────────▼────────────────▼─────────────┐ │
│ │ 业务逻辑层 (Service) │ │
│ │ ┌────────────┐ ┌───────────┐ ┌──────────┐ │ │
│ │ │工单服务 │ │调度引擎 │ │状态管理 │ │ │
│ │ └────────────┘ └───────────┘ └──────────┘ │ │
│ └───────────────────────┬───────────────────────┘ │
│ │ │
│ ┌───────────────────────▼───────────────────────┐ │
│ │ 基础设施层 (Infra) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────────┐ │ │
│ │ │事件总线 │ │PLC适配器 │ │机器人通信 │ │ │
│ │ │(Redis │ │(OPC-UA/ │ │(TCP/寄存器)│ │ │
│ │ │ Streams) │ │ Modbus) │ │ │ │ │
│ │ └──────────┘ └──────────┘ └────────────┘ │ │
│ └───────────────────────┬───────────────────────┘ │
│ │ │
│ Redis (SSOT) PostgreSQL (持久化) │
└──────────────────────────────────────────────────────┘
4. 事件总线与一致性模型
4.1 事件结构
type Event struct {
ID string // UUIDv7
EntityID string // 如 "job:J123"
EntityVersion int // 事件基于的实体版本
Type string // 事件类型
Source string // 来源模块/设备
Timestamp time.Time
Payload map[string]interface{}
}
4.2 原子更新与版本控制
- 每个实体在 Redis Hash 中存储
version(整数)。 - 应用事件时,使用 Lua 脚本原子校验:当前版本必须等于事件携带版本,否则拒绝(过期事件)。
- 校验通过后更新状态并递增版本。
4.3 写入路径
- 生成事件对象(携带当前版本)。
- 发布到 Redis Stream (XADD)。
- 消费协程调用
StateManager.ApplyEvent执行 Lua 脚本更新 Redis。 - 异步写入 PostgreSQL。
4.4 幂等与重放
- 实体记录
last_event_id,重复事件直接跳过。 - 断电恢复时可从 PG
event_log按顺序重放事件,安全重建 Redis 状态。
5. 工艺路线定义
5.1 工序类型
| 类型 | 说明 | 资源需求 |
|---|---|---|
| LOAD | 上料并首次扫码 | 机器人+接驳台 |
| BUFFER_STAGE | 暂存台积聚等待 | 暂存台特定槽位(分配后固定) |
| MACHINING | CNC加工 | CNC_1~4(可限定) |
| WASH | 高压清洗(成对关门) | 高压清洗机 |
| INSPECTION | 内窥镜检测 | 检测滚筒线 |
| JUDGE | 检测结果判定 | 无 |
| RUST_WASH | 除锈清洗 | 除锈清洗线(FIFO) |
| FINAL_SCAN | 最终扫码(手持) | 扫码器 |
| LASER_MARK | 激光打标(手持) | 激光打标机 |
| DECISION | 抽检/下料分支 | 机器人 |
5.2 首次扫码特殊规则
机器人从接驳台抓取工件后直接手持扫码:
- 成功 → 分配暂存台空闲槽位,放入,工件进入
BUFFER_STAGE(状态ON_BUFFER)。 - 失败 → 工件放回原接驳台槽位,状态
SUSPENDED,原因SCAN_FAILED,人工介入。
5.3 示例工艺路线(A6VM107)
{
"recipe_id": "R-A6VM107",
"steps": [
{"step_id":"10","type":"LOAD","tool":"SCANNER","next":"20","branches":{"FAIL":"_RETURN_BAY"}},
{"step_id":"20","type":"BUFFER_STAGE","next":"30"},
{"step_id":"30","type":"MACHINING","allowed":["CNC_3","CNC_4"],"next":"40","step_timeout":600},
{"step_id":"40","type":"UNLOAD","next":"50"},
{"step_id":"50","type":"WASH","next":"60","step_timeout":300},
{"step_id":"60","type":"UNLOAD","next":"70"},
{"step_id":"70","type":"INSPECTION","next":"80"},
{"step_id":"80","type":"JUDGE","branches":{"PASS":"90","FAIL":"900"}},
{"step_id":"90","type":"UNLOAD","next":"100"},
{"step_id":"100","type":"DEBURR","next":"110","step_timeout":300},
{"step_id":"110","type":"UNLOAD","next":"120"},
{"step_id":"120","type":"RUST_WASH","next":"130"},
{"step_id":"130","type":"UNLOAD","next":"140"},
{"step_id":"140","type":"FINAL_SCAN","tool":"SCANNER","next":"150","branches":{"FAIL":"145"}},
{"step_id":"145","type":"LASER_MARK","tool":"LASER","next":"150"},
{"step_id":"150","type":"DECISION","branches":{"SAMPLE":"200","NORMAL":"210"}},
{"step_id":"200","type":"PLACE_SAMPLING"},
{"step_id":"210","type":"PLACE_AGV"}
]
}
_RETURN_BAY表示放回原接驳台槽位并挂起。
6. 数据库设计
6.1 基础主数据表
product_type
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(20) PK | A6VM107, A6VM160, A6VM200 |
| name | varchar(50) | |
| recipe_id | varchar(20) FK | |
| is_active | boolean |
recipe
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(20) PK | |
| name | varchar(50) | |
| version | int | |
| create_time | timestamptz |
recipe_step
| 字段 | 类型 | 说明 |
|---|---|---|
| step_id | varchar(10) PK | |
| recipe_id | varchar(20) FK | |
| step_name | varchar(50) | |
| step_type | varchar(20) | 工序类型 |
| resource_type | varchar(50) | 所需工站类型 |
| tool_type | varchar(20) | 手持工具类型 |
| allowed_resources | jsonb | 允许的设备列表 |
| processing_params | jsonb | 工时等 |
| next_step_default | varchar(10) | |
| next_step_branches | jsonb | 分支映射 |
| step_timeout | int | 超时秒数 |
| description | text |
equipment
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(20) PK | |
| type_code | varchar(20) | |
| name | varchar(50) | |
| status | varchar(20) | 从Redis推导,此字段仅作离线参考 |
| slot_count | int | |
| ip_address | varchar(45) | |
| location | varchar(50) |
6.2 业务数据表
work_order
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(30) PK | |
| product_type_id | varchar(20) FK | |
| quantity | int | |
| source_bay_slot_id | varchar(30) FK | |
| status | varchar(20) | CREATED/IN_PROGRESS/PAUSED/COMPLETED/CANCELLED |
| context | jsonb | |
| created_time | timestamptz | |
| updated_time | timestamptz | |
| completed_time | timestamptz |
job
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(30) PK | |
| work_order_id | varchar(30) FK | |
| product_type_id | varchar(20) FK | |
| recipe_id | varchar(20) FK | |
| current_step_id | varchar(10) FK | |
| status | varchar(20) | 见7.1 |
| position_type | varchar(20) | ON_EQUIPMENT/ON_BUFFER/IN_HAND |
| position_ref_id | varchar(50) | 位置引用(含暂存台槽位ID) |
| context | jsonb | 子型号、扫码数据等 |
| priority | int | |
| suspended_reason | text | |
| version | int | 事件版本控制 |
| last_event_id | varchar(36) | |
| created_time | timestamptz | |
| last_updated | timestamptz |
job_step_instance
| 字段 | 类型 | 说明 |
|---|---|---|
| id | bigserial PK | |
| job_id | varchar(30) FK | |
| step_id | varchar(10) FK | |
| attempt_no | int | |
| status | varchar(20) | IN_PROGRESS/COMPLETED/FAILED |
| equipment_id | varchar(20) | |
| start_time | timestamptz | |
| end_time | timestamptz | |
| result | jsonb |
task
| 字段 | 类型 | 说明 |
|---|---|---|
| id | varchar(36) PK | UUID |
| job_id | varchar(30) FK | |
| type | varchar(30) | TRANSPORT/HANDHELD_SCAN… |
| status | varchar(20) | CREATED/DISPATCHED/RUNNING/SUCCESS/FAILED/TIMEOUT |
| from_pos | jsonb | |
| to_pos | jsonb | |
| assigned_robot | varchar(20) | |
| started_time | timestamptz | |
| completed_time | timestamptz | |
| result | jsonb |
task_log / event_log 略。
7. 工件生命周期与状态管理
7.1 Job 状态
| 状态 | 含义 | 转换示例 |
|---|---|---|
| CREATED | 已创建,在接驳台 | → IN_HANDLING |
| IN_HANDLING | 机器人操作中 | → ON_BUFFER(扫码成功) / SUSPENDED(扫码失败) |
| PROCESSING | 设备加工中 | → WAITING_UNLOAD |
| WAITING_UNLOAD | 加工完成待取走 | → IN_HANDLING |
| ON_BUFFER | 固定在暂存台槽位上 | → IN_HANDLING |
| WAITING_DECISION | 等待判定(检测后) | → COMPLETED / SCRAPPED / ON_BUFFER |
| COMPLETED | 正常下线,释放暂存台槽位 | 终态 |
| SCRAPPED | 报废,释放暂存台槽位 | 终态 |
| SUSPENDED | 异常暂停(扫码失败等) | → 恢复 |
7.2 暂存台槽位生命周期
- 扫码成功后分配一个空闲槽位(编号1~8),写入
position_ref_id。 - 工件全程独占该槽位,直到进入 COMPLETED 或 SCRAPPED 状态后释放。
8. 调度器三层架构设计
8.0 调度触发机制(事件驱动)
- 状态管理器在原子更新 Redis 后,将新变为
ON_BUFFER或WAITING_UNLOAD的工件 ID 推入 Redis Sorted Setsched:ready_jobs。 - 调度引擎阻塞在
ZPOPMAX上,仅处理变动工件,无全局扫描。 - 处理完若无可行任务,工件放回
sched:ready_jobs等待下一次事件。
8.1 任务生成层
根据 job.current_step 和 recipe_step 生成候选动作列表。
8.2 约束过滤层
过滤设备不可用、工单暂停、工件暂停、工具锁冲突、批处理未满、超时挂起等。
8.3 策略选择层
优先级排序:
- 紧急转移(WAITING_UNLOAD)
- 成组装卸(高压清洗机)
- 设备上料
- 暂存台批量补料(空位 > 4 触发)
- 手持作业
- 普通搬运
8.4 原子抢占
Lua 脚本原子地将工件状态从 ON_BUFFER 改为 IN_HANDLING,成功则创建 Task。
9. 任务(Task)状态机与生命周期
CREATED → DISPATCHED → RUNNING → SUCCESS
↓ ↓
FAILED TIMEOUT
- CREATED:调度生成
- DISPATCHED:已下发机器人
- RUNNING:机器人开始执行
- SUCCESS/FAILED/TIMEOUT:终态
- FAILED 可重试,TIMEOUT 则工件回退。
10. 工单管理详细设计
- 创建:选择接驳台槽位、工件类型(可指定子型号),生成工单和 N 个 Job。
- 暂停/恢复:工单状态改为 PAUSED,过滤层排除;恢复改回 IN_PROGRESS。
- 单工件暂存:直接设置 Job 状态为 SUSPENDED。
- 返工:修改
current_step,新增job_step_instance。
11. 工站接口设计
type Station interface {
ID() string
Type() string
GetStatus() StationStatus // IDLE, BUSY, WAITING, FAULT
CanAccept(jobType string) bool
Execute(cmd StationCommand) (AssignedSlot string, error)
OnEvent(event StationEvent)
}
设备占用通过 Redis 推导,不冗余存储。
12. 机器人子调度器与手持工具
调度引擎下发抽象任务,机器人控制器自行完成运动控制、工具调用(通过 HandheldTool 接口),工具用 Redis 锁互斥。
13. 超时与心跳机制
- 工序超时:
step_timeout超时自动挂起。 - 任务超时:Task 创建后超时回退。
- 设备心跳:
equipment:{id}:heartbeatTTL 5s,丢失则过滤。
14. 运行期状态校验
周期性抽查 PLC 实际工件 vs Redis 逻辑,不一致则挂起报警。
15. 断电恢复与事件重放
15.1 恢复分级
| 级别 | 条件 | 措施 |
|---|---|---|
| L1 自动 | CREATED/ON_BUFFER/WAITING_RESOURCE | 直接恢复 |
| L2 半自动 | PROCESSING 且 PLC 确认完成 | 转 WAITING_UNLOAD 自动恢复 |
| L3 人工 | IN_HANDLING/关门中/位置不明 | 人工确认处置 |
15.2 重放
从 PG 事件流按顺序重放,版本号与幂等保证正确性。
16. Redis 数据结构设计
job:{id}Hash:状态、位置、版本、last_event_idequipment:{id}Hash:设备状态equipment:{id}:heartbeatString TTLtask:{id}Hash:任务状态sched:ready_jobsSorted Set:按优先级排队的就绪工件buffer:slots:bitmapString:8位 Bitmap 表示暂存台槽位占用tool:lock:{type}String TTL:手持工具锁
17. 暂存台槽位管理与批量补料
17.1 槽位分配与释放
- 暂存台共 8 个槽位,编号 1~8,通过 Redis Bitmap 管理。
- 分配:首次扫码成功后,寻找第一个空闲槽位(位值为0),置1,记录槽位号到 job 的
position_ref_id。 - 占用期:工件在生产全程独占该槽位,即使被取走去加工,槽位依然保持占用。
- 释放:工件状态变为 COMPLETED 或 SCRAPPED 时,对应 Bitmap 位置 0。
17.2 批量补料规则
- 冷启动:系统启动时若暂存台空位 = 8,触发连续上料,尽可能装满 8 件。
- 运行中触发:每次槽位释放(工件下线)后,检查 Bitmap 中 0 的个数。若 空位数 > 4,立即触发批量补料。
- 补料数量:N = min(空位数, 接驳台上待上料工件数),目标是将暂存台补满至 8。
- 实现:状态管理器在处理工件终态事件后调用
BufferManager.CheckAndRefill(),若满足条件则向调度引擎提交高优先级批量上料命令(优先级 4),生成一串“抓取→扫码→入暂存台”任务,连续执行直至补满或无料可上。
18. 与外部系统交互
- AGV:托盘到位/成品送出通过事件通知。
- MES(可选):接收工单,上报履历。
- 人工终端:工单、抽检、暂停/恢复、断电恢复看板。
19. 关键技术选型
| 组件 | 技术 |
|---|---|
| 后端语言 | Go 1.23.4 |
| 框架 | go-zero v1.3.8(单体) |
| ORM | ent v0.14.5 |
| 数据库 | PostgreSQL |
| 缓存/状态/事件 | Redis (SSOT, Streams, Lua) |
| PLC 通信 | OPC-UA / Modbus TCP |
| 前端 | React 19 + Semi Design + Zustand + Vite 7 |
20. 部署与扩展性
- 单体二进制部署,依赖 Redis + PostgreSQL。
- 模块接口解耦,未来可拆分微服务。
- 新增工件/设备仅需数据库配置。
附录A:PLC信号与事件映射表
A.1 状态信号(PLC → 系统)
| PLC信号 | 地址 | 类型 | 系统事件 |
|---|---|---|---|
| CNCDone1~4 | M1140.0~3 | bool | MACHINE_TASK_COMPLETE |
| WasherDone | M1144.0 | bool | MACHINE_TASK_COMPLETE |
| DeburrDone | M1145.0 | bool | MACHINE_TASK_COMPLETE |
| CleaningDone | M1146.0 | bool | MACHINE_TASK_COMPLETE |
| MeasureOK/NG | M1148.0/1 | bool | INSPECTION_PASS/FAIL |
| SamplingOK/NG | M1151.0/1 | bool | SAMPLING_PASS/FAIL |
A.2 指令信号(系统 → PLC)
| 指令 | 地址 | 参数 | 任务类型 |
|---|---|---|---|
| FetchWorkpieceFromDock | M1000 | 类型,接驳台号,槽号 | PICK |
| PlaceWorkpieceToDock | M1010 | 类型,接驳台号,槽号 | PLACE |
| FetchWorkpieceFromTempStation | M1020 | 类型,暂存位 | PICK |
| PlaceWorkpieceToTempStation | M1030 | 类型,暂存位 | PLACE |
| PlaceWorkpieceToMachine | M1040 | 类型,设备ID,槽位 | LOAD |
| FetchWorkpieceFromMachine | M1050 | 类型,设备ID,槽位 | UNLOAD |
| ExchangeWorkpieceToMachine | M1060 | 类型,设备ID,槽位 | EXCHANGE |
| PlaceWorkpieceToRollingLine | M1070 | 类型 | LOAD |
| FetchWorkpieceFromRollingLine | M1080 | 类型 | UNLOAD |
| PlaceWorkpieceToSamplingStation | M1090 | 类型 | PLACE |
| FetchWorkpieceFromSamplingStation | M1100 | 类型 | PICK |
| MoveWorkpieceToMarkingStation | M1110 | 类型 | HANDHELD_MARK |
| StartScanCode | M1130 | 类型,轮次 | HANDHELD_SCAN |
本方案覆盖了从事件驱动、状态机、调度引擎到现场PLC信号的全部细节,可直接指导开发团队进行实现。