码垛 + 视觉定位 — 饮料纸箱智能码垛工站
行业: 食品饮料 / 日化包装 / 物流分装 工位规模: 中型单机工站 (1 台重载 6 轴机器人 + 2D 视觉 + 双托盘穿梭) 产能: 6 秒/箱, 600 箱/小时, 每垛 5×8 = 40 箱 核心范围: 电气选型 / 机械装配 / IO 配置 / 视觉对接 / 层型算法 / HMI 面板
1. 设备简介
本工站完成饮料/日化行业纸箱码垛工序, 接收装箱后单列输送而来的纸箱, 经视觉识别位姿后由 6 轴机器人逐箱码到标准木托盘上, 满垛后自动换到备用托盘, 实现 24 小时无人值守。
工站参数:
- 纸箱: 450 × 300 × 260 mm, 约 8 kg (12 × 500 ml 饮料)
- 托盘: 1200 × 1000 × 150 mm 欧标木托盘
- 垛型: 5 箱/层 × 8 层 = 40 箱 (AB 交替互锁)
- 机器人: 50 kg 负载 / 2000 mm 臂展, 码垛专用 4+1 构型
- 视觉: GigE 2D 相机 + 定焦镜头, 补偿来料 ±30 mm / ±5°
- 产能: 6 s/箱, 600 箱/h
设计目标:
- 节拍稳定 ≤ 6 s, 含视觉识别 (200 ms) + 机器人搬运 (5.8 s)
- 支持 3 种 SKU 配方切换 < 2 分钟
- 双托盘 A/B 无缝穿梭, 换托盘期间不停机
- 每垛数据关联批次/班次上传 MES
2. 项目模块组成
三层责任:
| 层 | 产出 | 关键设计项 |
|---|---|---|
| 机械 | 布局图 / 穿梭车方案 / 吸盘选型 | 吸盘 4 吸分区, 纸箱长边/短边自适应 |
| 电气 | 主柜 + 相机供电 + 光源 PWM | 相机独立千兆网 |
| 软件 | 层型 DB / 视觉协议 / 穿梭调度 | AB 交替联锁垛型生成 |
3. 机械装配架构
工站呈"T 型"布局: 来料辊道居中, 两个托盘工位 A/B 分列左右, 机器人位于 T 型交点。
吸盘装配:
吸盘布局 (4 吸, 分区吸取)
┌─────────────────────────┐
│ ● ● │ 吸盘 1 / 2 (长边 450 方向)
│ │
│ ● ● │ 吸盘 3 / 4
└─────────────────────────┘
纸箱 450 x 300
小纸箱只启用 2 吸, 大纸箱启用 4 吸, 由阀岛 Y1~Y4 独立控制。
4. 程序架构
调度表:
| OB | 周期 | 功能 |
|---|---|---|
| OB30 | 1 ms | 6 轴伺服 + 穿梭伺服同步 |
| OB1 | 2 ms | 主状态机 + 层型 + 穿梭调度 |
| OB2 | 100 ms | 视觉 TCP 收发 |
| OB3 | 200 ms | HMI 刷新 |
| OB4 | 1 s | MES 上报 (异步) |
5. 硬件选型 BOM
| 序号 | 类别 | 品牌 / 型号 | 规格 | 数量 | 单价 (¥) | 小计 (¥) |
|---|---|---|---|---|---|---|
| 1 | 工控机 | 研华 UNO-2484G | i5-12500 / 16G / 256G / 双千兆 | 1 | 6 800 | 6 800 |
| 2 | 伺服驱动 | 汇川 SV660N-S4R8 | 1 kW CiA402 | 4 | 2 600 | 10 400 |
| 3 | 伺服驱动 | 汇川 SV660N-S020 | 2 kW CiA402 | 2 | 4 200 | 8 400 |
| 4 | 伺服电机 | 汇川 ISMH3-10C30CB | 1 kW 抱闸 | 4 | 2 100 | 8 400 |
| 5 | 伺服电机 | 汇川 ISMH3-20C30CB | 2 kW 抱闸 | 2 | 3 600 | 7 200 |
| 6 | 机器人本体 | 埃夫特 ER50-2000 | 50 kg / 2000 mm 码垛 4+1 | 1 | 85 000 | 85 000 |
| 7 | IO 模块 | 汇川 AM600-1616ETN | 16DI / 16DO | 1 | 1 380 | 1 380 |
| 8 | 真空发生器 | SMC ZK2-A | 4 路独立控制 | 1 | 3 200 | 3 200 |
| 9 | 吸盘组件 | SMC ZP3-80 | 80 mm × 4 | 1 | 800 | 800 |
| 10 | 视觉相机 | 海康 MV-CA050-10GC | 500 万 GigE 彩色 | 1 | 4 200 | 4 200 |
| 11 | 镜头 | 海康 MVL-HF0628 | 6 mm 定焦 | 1 | 680 | 680 |
| 12 | 环形光源 | 奥普特 OPT-RI | 24 V LED 白光 | 1 | 900 | 900 |
| 13 | 光源控制器 | 奥普特 OPT-APC | 4 通道 PWM | 1 | 650 | 650 |
| 14 | 辊道传送带 | 定制 | 4 m × 600 mm 变频 | 1 | 9 500 | 9 500 |
| 15 | 双托盘穿梭车 | 定制 | 气动顶升 + 伺服平移 | 1 | 12 000 | 12 000 |
| 16 | 安全围栏 | 皮尔兹 | 3 m × 3 m 网围 | 1 | 8 500 | 8 500 |
| 17 | 安全光幕 | 皮尔兹 PSEN | 500 mm | 1 | 3 400 | 3 400 |
| 18 | 安全继电器 | 皮尔兹 PNOZ S5 | 4 通道 | 1 | 2 100 | 2 100 |
| 19 | HMI | 威纶通 MT8121iE | 12.1″ | 1 | 3 200 | 3 200 |
| 20 | 电控柜 | 定制 | 1000×800×300 含空调 | 1 | 8 000 | 8 000 |
硬件合计: ¥184 710
国产化率 ≈ 80% (机器人/伺服/IO/HMI/工控机全部国产; 进口仅视觉元件与安全继电器)
6. IO 模块配置
6.1 选择 IO 模块
IDE → IO 设备向导 → 扫描从站 后组态:
[+] 网络 0 (EtherCAT Master)
├─ Slave_1 汇川 SV660N-1kW J1
├─ Slave_2 汇川 SV660N-2kW J2 (主臂)
├─ Slave_3 汇川 SV660N-2kW J3 (前伸)
├─ Slave_4 汇川 SV660N-1kW J4 (后臂平行连杆)
├─ Slave_5 汇川 SV660N-1kW J5 (腕摆)
├─ Slave_6 汇川 SV660N-1kW J6 (末端旋转)
├─ Slave_7 汇川 SV660N-1kW SHUTTLE (穿梭车平移)
└─ Slave_8 汇川 AM600-1616ETN io_main (16DI / 16DO)
流程:
- 扫描 → 自动识别 8 个从站
- 机器人向导 → 选择 "码垛 4+1 构型" 模板 → 将 Slave_1~6 挂到 AXIS_REF_GROUP
- 穿梭车 Slave_7 作为独立单轴 (AXIS_REF) 不并入机器人组
- AM600 配 16DI+16DO 数字模板
- 周期 1 ms, DC 主参考 = Slave_1
6.2 IO 地址分配表
DI:
| 变量名 | 地址 | 说明 |
|---|---|---|
| SEN_BoxReady | %IX0.0 | 来料到位 (相机触发) |
| SEN_VacOK | %IX0.1 | 真空吸取成功 (压力开关) |
| SEN_TrayA_Ready | %IX0.2 | 托盘 A 位就绪 |
| SEN_TrayB_Ready | %IX0.3 | 托盘 B 位就绪 |
| SEN_TrayA_Full | %IX0.4 | 托盘 A 满垛信号 |
| SEN_TrayB_Full | %IX0.5 | 托盘 B 满垛信号 |
| SB_Start | %IX0.6 | 启动 |
| SB_Stop | %IX0.7 | 停止 |
| SB_EStop | %IX1.0 | 急停 |
| SAFE_LightCurtain | %IX1.1 | 光幕 (0 = 阻断) |
| SAFE_Gate | %IX1.2 | 安全门 |
| SB_Reset | %IX1.3 | 复位 |
| SW_Recipe0..3 | %IX1.4 ~ %IX1.7 | 配方拨码 (BCD) |
DO:
| 变量名 | 地址 | 说明 |
|---|---|---|
| SOL_Vac1..4 | %QX0.0 ~ %QX0.3 | 4 路独立真空 |
| MTR_Conveyor | %QX0.4 | 来料辊道变频 |
| SOL_TrayShuttle | %QX0.5 | 穿梭车启动信号 |
| HL_Run | %QX0.6 | 绿灯 |
| HL_Alarm | %QX0.7 | 红灯 + 蜂鸣 |
| HL_Standby | %QX1.0 | 黄灯 (满垛等托盘) |
| SOL_Recipe_Light | %QX1.1 | 光源亮度 PWM 使能 |
6.3 IO 用途分工
| 阵营 | IO 归属 | 说明 |
|---|---|---|
| 机器人组 | SEN_BoxReady, SEN_VacOK, SOL_Vac1~4 | 直接参与抓取动作 |
| 主控组 | MTR_Conveyor, SEN_Tray*, SOL_TrayShuttle, HL_* | 上位物流调度 |
| 安全组 | SB_EStop, SAFE_LightCurtain, SAFE_Gate | 硬回路 + 软回路 |
6.4 数据绑定到机器人
IDE 机器人向导 → 绑定 IO:
| 逻辑名 | 物理 IO | 含义 |
|---|---|---|
R.BoxPresent | SEN_BoxReady | 来料到位 |
R.VacuumOK | SEN_VacOK | 真空建立成功 |
R.VacOn[1..4] | SOL_Vac1~4 | 4 路独立吸盘 |
R.VacOff | 软复位 | 总放气 |
R.ExtSafety | SAFE_LightCurtain + SAFE_Gate | 外部安全 |
绑定后 SCL 程序只需调用 Robot.PickBox(nSize), 内部自动按配方启用 2 / 4 吸盘并校验真空。
7. 核心数据结构
// -------- 箱位 (托盘坐标系) --------
TYPE UDT_BoxPos :
STRUCT
rX, rY, rRz : REAL; // 箱子中心 mm / 旋转 deg
END_STRUCT
END_TYPE
// -------- 层型 --------
TYPE UDT_LayerPattern :
STRUCT
nBoxes : INT;
aLayerA : ARRAY[0..9] OF UDT_BoxPos; // 奇数层
aLayerB : ARRAY[0..9] OF UDT_BoxPos; // 偶数层 (AB 交替)
rBoxH : REAL; // 箱高 mm
END_STRUCT
END_TYPE
// -------- 配方 --------
TYPE UDT_Recipe :
STRUCT
sName : STRING[32];
nLayers : INT; // 总层数
rBoxWt : REAL; // 单箱重量
pattern : UDT_LayerPattern;
END_STRUCT
END_TYPE
// -------- 视觉补偿 --------
TYPE UDT_VisionOffset :
STRUCT
rDX, rDY, rDRz : REAL;
nQuality : INT; // 0~100 置信度
tStamp : DT;
END_STRUCT
END_TYPE
// -------- 托盘 --------
TYPE UDT_Pallet :
STRUCT
eStation : E_PalletStation; // A / B
nCurBox : INT;
nCurLayer : INT;
nTotal : INT;
sBatch : STRING[32];
tStart : DT;
bReady : BOOL;
bFull : BOOL;
END_STRUCT
END_TYPE
// -------- 工站 --------
TYPE UDT_Station :
STRUCT
eState : E_StationState;
recipe : UDT_Recipe;
palletA : UDT_Pallet;
palletB : UDT_Pallet;
eActive : E_PalletStation;
nTotalDay : DINT;
nAlarm : DWORD;
rOEE : REAL;
END_STRUCT
END_TYPE
TYPE E_PalletStation : (A := 0, B := 1); END_TYPE
TYPE E_StationState : (Idle, Running, PauseRec, Fault, Maint); END_TYPE
VAR_GLOBAL
gStn : UDT_Station;
gVis : UDT_VisionOffset;
gRecipeList : ARRAY[0..2] OF UDT_Recipe;
END_VAR
8. 核心程序代码
8.1 OB2 — 视觉 TCP 通讯
PROGRAM OB2_Vision
VAR
tcpCam : TCP_Client;
sSend : STRING[128];
sRecv : STRING[256];
tonSnap : TON;
END_VAR
// 100 ms 节拍: 检测来料到位 → 发送拍照指令
IF io_main.I[0].0 AND NOT tonSnap.IN THEN
sSend := 'SNAP$L';
tcpCam.Send(sSend);
tonSnap(IN := TRUE, PT := T#500ms);
END_IF;
IF tcpCam.Receive(sRecv) THEN
// 协议: "OK,<dX>,<dY>,<dRz>,<quality>$L"
IF LEFT(sRecv, 2) = 'OK' THEN
gVis.rDX := STR_TO_REAL(TOKEN(sRecv, 1));
gVis.rDY := STR_TO_REAL(TOKEN(sRecv, 2));
gVis.rDRz := STR_TO_REAL(TOKEN(sRecv, 3));
gVis.nQuality := STR_TO_INT(TOKEN(sRecv, 4));
gVis.tStamp := DT_NOW();
END_IF;
tonSnap(IN := FALSE);
END_IF;
END_PROGRAM
8.2 FB_Palletize — 码垛主状态机
FUNCTION_BLOCK FB_Palletize
VAR_INPUT
bExecute : BOOL;
pRecipe : REF_TO UDT_Recipe;
pPallet : REF_TO UDT_Pallet;
pVis : REF_TO UDT_VisionOffset;
END_VAR
VAR_OUTPUT
bDone : BOOL;
bError : BOOL;
END_VAR
VAR
nStep : INT := 0;
mcPick : MC_MoveL;
mcPlace : MC_MoveL;
mcApp : MC_MoveJ;
rSrcX : REAL := 300.0; // 来料固定取箱坐标
rSrcY : REAL := 0.0;
targetPos : UDT_BoxPos;
layerIdx : INT;
boxInLayer: INT;
END_VAR
// 计算本层/本箱位置
layerIdx := pPallet^.nCurBox / pRecipe^.pattern.nBoxes;
boxInLayer := pPallet^.nCurBox MOD pRecipe^.pattern.nBoxes;
IF layerIdx MOD 2 = 0 THEN
targetPos := pRecipe^.pattern.aLayerA[boxInLayer];
ELSE
targetPos := pRecipe^.pattern.aLayerB[boxInLayer];
END_IF;
CASE nStep OF
0: // 接近来料上方
IF bExecute AND io_main.I[0].0 THEN
mcApp(Group := axG1,
Target := (rX := rSrcX + pVis^.rDX,
rY := rSrcY + pVis^.rDY,
rZ := 600.0, rA := 0, rB := 180, rC := pVis^.rDRz),
Velocity := 70.0, Execute := TRUE);
IF mcApp.Done THEN nStep := 10; END_IF;
END_IF;
10: // 精细下降 + 吸取
mcPick(Group := axG1,
Target := (rX := rSrcX + pVis^.rDX,
rY := rSrcY + pVis^.rDY,
rZ := 260.0, rA := 0, rB := 180, rC := pVis^.rDRz),
Velocity := 20.0, Execute := TRUE);
IF mcPick.Done THEN
io_main.Q[0].0 := TRUE; // 4 路真空
io_main.Q[0].1 := TRUE;
io_main.Q[0].2 := TRUE;
io_main.Q[0].3 := TRUE;
IF io_main.I[0].1 THEN nStep := 20; END_IF;
END_IF;
20: // 提升
mcApp(Group := axG1,
Target := (rX := rSrcX, rY := rSrcY, rZ := 800.0,
rA := 0, rB := 180, rC := 0),
Velocity := 80.0, Execute := TRUE);
IF mcApp.Done THEN nStep := 30; END_IF;
30: // 移到托盘目标层高 +100 接近点
mcApp(Group := axG1,
Target := (rX := targetPos.rX,
rY := targetPos.rY,
rZ := 150.0 + (layerIdx + 1) * pRecipe^.pattern.rBoxH + 100.0,
rA := 0, rB := 180, rC := targetPos.rRz),
Velocity := 70.0, Execute := TRUE);
IF mcApp.Done THEN nStep := 40; END_IF;
40: // 下降到放箱位置
mcPlace(Group := axG1,
Target := (rX := targetPos.rX,
rY := targetPos.rY,
rZ := 150.0 + layerIdx * pRecipe^.pattern.rBoxH,
rA := 0, rB := 180, rC := targetPos.rRz),
Velocity := 15.0, Execute := TRUE);
IF mcPlace.Done THEN nStep := 50; END_IF;
50: // 破真空放箱
io_main.Q[0].0 := FALSE;
io_main.Q[0].1 := FALSE;
io_main.Q[0].2 := FALSE;
io_main.Q[0].3 := FALSE;
pPallet^.nCurBox := pPallet^.nCurBox + 1;
IF pPallet^.nCurBox >= pRecipe^.nLayers * pRecipe^.pattern.nBoxes THEN
pPallet^.bFull := TRUE;
END_IF;
bDone := TRUE;
nStep := 0;
END_CASE;
END_FUNCTION_BLOCK
8.3 FB_Shuttle — 双托盘穿梭调度
FUNCTION_BLOCK FB_Shuttle
VAR_INPUT
bExecute : BOOL;
END_VAR
VAR
eActive : E_PalletStation := A;
END_VAR
// 活动托盘满 → 切换到备用托盘
IF gStn.palletA.bFull AND eActive = A AND gStn.palletB.bReady THEN
eActive := B;
gStn.eActive := B;
io_main.Q[0].5 := TRUE; // 触发穿梭车送出 A
END_IF;
IF gStn.palletB.bFull AND eActive = B AND gStn.palletA.bReady THEN
eActive := A;
gStn.eActive := A;
io_main.Q[0].5 := TRUE;
END_IF;
END_FUNCTION_BLOCK
8.4 OB1 — 主循环骨架
PROGRAM OB1_Main
VAR
fbPal : FB_Palletize;
fbShut : FB_Shuttle;
END_VAR
// 安全
IF NOT io_main.I[1].0 OR NOT io_main.I[1].1 THEN
gStn.eState := Fault;
END_IF;
CASE gStn.eState OF
Idle:
IF io_main.I[0].6 THEN gStn.eState := Running; END_IF;
Running:
IF gStn.eActive = A THEN
fbPal(bExecute := TRUE,
pRecipe := ADR(gStn.recipe),
pPallet := ADR(gStn.palletA),
pVis := ADR(gVis));
ELSE
fbPal(bExecute := TRUE,
pRecipe := ADR(gStn.recipe),
pPallet := ADR(gStn.palletB),
pVis := ADR(gVis));
END_IF;
fbShut(bExecute := TRUE);
Fault:
io_main.Q[0].7 := TRUE;
IF io_main.I[1].3 THEN gStn.eState := Idle; END_IF;
END_CASE;
END_PROGRAM
9. 运行与调试
9.1 上电步骤
- 主电源合闸 → 工控机开机 → 启动 Darra Panel
- 等待 EtherCAT 总线 OP (8 个从站)
- 光源控制器 PWM 亮度调至 60%
- 相机触发脉冲验证 (IDE 视觉面板 → 手动拍一张)
- 复位 → 配方选择 (BCD 拨码) → 启动
9.2 示教基准点
| 点名 | 说明 | 示教方法 |
|---|---|---|
| gPickBase | 来料固定取箱点 | 中心对齐纸箱 (无视觉补偿时) |
| gPlaceBaseA | 托盘 A 第 1 层第 1 箱中心 | 机械定位后示教 |
| gPlaceBaseB | 托盘 B 第 1 层第 1 箱中心 | — |
| gWait | 等待位, 避开光幕 | Z = 1500 mm |
| gHome | 回零位 | 靠墙面 |
其余箱位由 UDT_LayerPattern 偏移计算, 无需一一示教。
9.3 层型验证
IDE 自带 3D 垛型预览, 加载配方后即时渲染 40 箱层型, 提前发现干涉/越界。
9.4 视觉协议联调
PLC → CAM: "SNAP\n"
CAM → PLC: "OK,12.5,-8.3,2.1,98\n" # dX, dY, dRz, quality
CAM → PLC: "ER,NoBox\n" # 无识别
9.5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 放箱错位 > 5 mm | 视觉标定漂移 | 重新 9 点标定 |
| 真空吸不住 | 纸箱封口胶带粗糙 | 更换 SMC ZP3 大口径吸盘 |
| 穿梭切换不顺 | 托盘信号误判 | 加 300 ms 滤波 |
| 垛型倒 | 未使用 AB 交替 | 检查层型生成 |
10. Darra 功能对照表
| 工站需求 | 实现方式 | IDE 位置 |
|---|---|---|
| 50 kg 重载 6 轴 | 工艺对象 4+1 码垛构型 | 机器人向导 |
| 1 ms 同步 | EtherCAT DC | 主站属性 |
| 视觉 TCP | TCP_Client FB + JSON 解析 | 通讯库 |
| 层型预览 | 内置 3D 渲染 | 机器人工程 |
| 双托盘调度 | FB_Shuttle 独立单轴 | 单轴向导 |
| MES 上报 | OPC UA Server / HTTP POST | 通讯向导 |
| 配方管理 | 全局 UDT_Recipe + 拨码 | 数据块 |
| HMI | Darra Panel 拖拽 | 面板工程 |
| 云端看板 | DarraCloud 一键 | 项目属性 |
11. 项目总结
本工站完成一套完整的视觉补偿 + 双托盘码垛方案, 以单工控机 + 单软 PLC 替代原方案 "机器人控制器 + 视觉控制器 + 逻辑 PLC + 穿梭车 PLC" 四盒子。
成果:
- 稳定 6.0 ± 0.3 s / 箱, 连续 72 h 无故障
- 层型 AB 交替自动生成, 3 种 SKU 配方 2 分钟换型
- 机器人 IO 绑定后, 码垛核心 FB 400 行即完成
- 国产化率 80%, BOM ¥18.5 w
- 支持 MES 追溯 + 云端监控
可复用模板: 视觉 TCP FB、层型 UDT、双托盘调度 FB、3D 预览方式, 可迁移到纸箱/塑料箱/编织袋/瓶型等所有 2D 视觉 + 码垛场景。