CNC 数据采集与监控 — 车间 MES 数采中枢
行业: 机加工车间 / 精密模具 / 汽车零部件 工位规模: 中型车间 32 台 CNC 集中数采 + 实时监控 + MES 上报 数据规模: 32 台 × 20 点位 × 500 ms ≈ 1 280 点/秒 核心范围: 多协议采集 / 时序数据库 / OEE 实时 / MES 对接 / 刀具寿命 / 云端看板
1. 设备简介
本工站作为车间级的数采中枢, 一台工控机同时接入 32 台不同品牌的 CNC, 按协议循环采集关键工艺指标, 本地落地时序数据库, 并向 MES / Grafana / DarraCloud 分发。
工站参数:
- 设备数: 32 台 CNC (发那科 31i × 12, 三菱 M80 × 8, 海德汉 TNC640 × 4, 广数 GSK × 8)
- 采集点: 主轴 RPM / 负载 / 进给率 / 刀号 / 程序号 / 状态 / 累计件数等 20 点
- 高频周期: 500 ms (RPM / 负载)
- 低频周期: 2 s (程序号 / 刀号)
- 存储: 本地 SQLite + 时序分区压缩, 保留 180 天
- 分发: OPC UA Server / HTTP API / MQTT / DarraCloud
设计目标:
- 32 台全部稳定在线, 单台断线不影响其他
- OEE 指标刷新 ≤ 5 s
- 刀具寿命 80 % / 100 % 两级告警
- 拔网线可断网离线缓存, 恢复后补传 MES
2. 项目模块组成
本案例偏向纯软件 + 弱电, 机械子系统只是既有 CNC, 电气子系统只涉及机柜/交换机/UPS, 软件子系统是主体。
三层责任:
| 层 | 产出 | 关键点 |
|---|---|---|
| 既有 CNC | IP 地址清单 / 协议开通 | CNC 侧开启以太网口与协议服务 |
| 电气 | 机柜 / 交换机 / 网线 | 工业级屏蔽超五类, 全车间网段隔离 |
| 软件 | 协议库 / 数据库 / 看板 | 纯软件实现, 无需数采网关 |
3. 机械装配架构
本案例不涉及 机器人 / 伺服 / 传送带, 仅为机柜与网络装配, 物理拓扑如下:
布线规范:
- 全车间超五类屏蔽网线, 走桥架, 避开变频器动力线 ≥ 30 cm
- 交换机 4 × 10G 上联预留, 32 端口分给 CNC
- UPS 3 kVA 支撑工控机 + 交换机 30 分钟
- 4G 路由作出站备份, 宽带断线时自动切换
4. 程序架构
Darra 的 OB 调度非常适合数采: 每个协议一个独立 OB, 互不阻塞, 某台 CNC 断线仅影响对应协议 OB 的一个 slot。
调度表:
| OB | 周期 | 功能 |
|---|---|---|
| OB10 | 500 ms | FOCAS 采集 (Fanuc 12 台) |
| OB11 | 500 ms | OPC UA Client (海德汉 4 台) |
| OB12 | 500 ms | Modbus TCP (广数 8 台) |
| OB20 | 2 s | MTConnect HTTP (三菱 8 台) |
| OB21 | 2 s | 低频点 (程序号 / 刀号) |
| OB1 | 1 s | OEE 计算 |
| OB30 | 10 s | 批量落库 |
| OB31 | 10 s | MES 推送 |
| OB32 | 30 s | 云端同步 |
5. 硬件选型 BOM
| 序号 | 类别 | 品牌 / 型号 | 规格 | 数量 | 单价 (¥) | 小计 (¥) |
|---|---|---|---|---|---|---|
| 1 | 工控机 | 研华 UNO-2484G | i7-12700 / 32G / 2TB NVMe | 1 | 12 000 | 12 000 |
| 2 | 工业交换机 | 华为 S5720-52X-PWR-LI | 48 口千兆 + 4 × 10G | 1 | 8 500 | 8 500 |
| 3 | KVM 切换器 | 普联 | 2 路 | 1 | 380 | 380 |
| 4 | UPS | 山特 C3KS | 3 kVA 在线式 | 1 | 4 800 | 4 800 |
| 5 | 24″ 值班大屏 | AOC | 显示总览看板 | 2 | 1 200 | 2 400 |
| 6 | 4G 路由器 | 华三 MSR810 | 双 SIM, 备用出站 | 1 | 2 200 | 2 200 |
| 7 | 工业超五类屏蔽网线 | 全车间布线 | 1 | 2 500 | 2 500 | |
| 8 | 机柜 + PDU | 定制 | 22U 带散热风扇 | 1 | 3 200 | 3 200 |
硬件合计: ¥35 980
国产化率 ≈ 95% (工控机/交换机/UPS/4G/机柜均国产)
无需数采网关、无需额外协议转换器, 原 CNC 以太网口直接接入交换机即可。
6. IO 模块配置
6.1 选择 IO 模块 (逻辑从站)
本案例不使用物理 DI/DO, "IO" 指向软件侧逻辑从站 (每台 CNC 视为一个通讯从站)。IDE 采用"通讯从站向导"而非 EtherCAT 从站。
[+] 通讯网络 (车间 TCP)
├─ [+] Fanuc 分组 (FOCAS)
│ ├─ CNC-01 ~ CNC-12 192.168.10.11 ~ .22
├─ [+] 三菱 分组 (MTConnect)
│ ├─ CNC-13 ~ CNC-20 192.168.10.23 ~ .30
├─ [+] 海德汉 分组 (OPC UA)
│ ├─ CNC-21 ~ CNC-24 192.168.10.31 ~ .34
└─ [+] 广数 分组 (Modbus TCP)
└─ CNC-25 ~ CNC-32 192.168.10.35 ~ .42
配置流程 (IDE 通讯从站向导):
- 新建通讯项目 → 添加分组 → 选择协议 (FOCAS / MTConnect / OPC UA / Modbus TCP)
- 分组内 添加设备 → 填写 IP / 端口 / 超时 / 重连策略
- 导入 采集点模板 (IDE 自带各协议常用点 CSV, 可导入)
- 变量映射: 每台设备实例化一个
UDT_CncData, 名称形如cnc01 / cnc02 / ... / cnc32 - 协议级别独立 OB 运行, 单台断线不阻塞其他
6.2 IO 地址分配表
本案例以 UDT 变量替代物理地址, 每台 CNC 有一个实例:
| 变量名 | 协议 | CNC 厂商点位 | 说明 |
|---|---|---|---|
| cnc01.rSpindleRPM | FOCAS 9033 | CNC_RDACTF | 实际主轴转速 |
| cnc01.rSpindleLoad | FOCAS 9068 | CNC_RDSVLOAD | 伺服负载 |
| cnc01.rFeedRate | FOCAS 9022 | CNC_RDSPEED | 进给速度 |
| cnc01.nToolNo | FOCAS 9045 | CNC_RDTOFSR | 刀具号 |
| cnc01.sProgName | FOCAS 9003 | CNC_RDPRGNUM | NC 程序号 |
| cnc01.eMachState | FOCAS 9100 | CNC_STATINFO | 运行状态 |
| cnc01.nAlarmCode | FOCAS 9071 | CNC_RDALMSTAT | 报警号 |
| cnc01.nPartsTotal | FOCAS 9017 | CNC_RDPARAM | 累计件数 |
| cnc13.rSpindleRPM | MTConnect | //Rotary[@name='S']/Velocity | XML XPath |
| cnc21.rSpindleRPM | OPC UA | ns=2;s=Axes.S.ActualVel | NodeId |
| cnc25.rSpindleRPM | Modbus | 40001 | 保持寄存器 |
| cnc25.nAlarmCode | Modbus | 40100 | 报警区 |
6.3 IO 用途分工
数采场景没有物理机器人和安全组, 按数据流向分层:
| 阵营 | 归属 | 含义 |
|---|---|---|
| 采集组 | 所有 cnc01~32 高低频点 | 由各协议 OB 读取 |
| 计算组 | OEE / 刀具寿命 / AI 异常 | OB1_OEE 处理 |
| 分发组 | MES / Grafana / DarraCloud 接口 | OB31/32 向外推送 |
6.4 数据绑定到机器人
本案例无机器人, 该章节对应为"数据绑定到 MES/OPC UA Server":
| 逻辑名 | 绑定目标 | 协议 |
|---|---|---|
UaServer.Cnc01.Spindle.RPM | cnc01.rSpindleRPM | OPC UA Node |
UaServer.Cnc01.Tool.No | cnc01.nToolNo | OPC UA Node |
Grafana.metric.spindle_rpm{id=01} | cnc01.rSpindleRPM | HTTP JSON |
Mes.Equipment[01].OEE | cnc01.rOEE | HTTP POST |
MQTT topic shop/cnc01/state | cnc01.eMachState | MQTT Publish |
IDE OPC UA Server 向导 勾选要发布的变量即可, 无需编码。
7. 核心数据结构
// -------- 单台 CNC 采集数据 --------
TYPE UDT_CncData :
STRUCT
sDevId : STRING[16]; // CNC-01 ~ CNC-32
eBrand : E_CncBrand;
sIp : STRING[16];
eProto : E_CncProtocol;
// 高频 500ms
rSpindleRPM : REAL;
rSpindleLoad : REAL;
rFeedRate : REAL;
rFeedOverride : REAL;
// 低频 2s
nToolNo : INT;
rToolUsedMin : REAL;
rToolLifeMin : REAL;
sProgName : STRING[32];
eMachState : E_MachState;
// 累计
nPartsTotal : DINT;
nPartsGood : DINT;
nAlarmCode : DWORD;
sAlarmText : STRING[80];
// OEE 实时
tRunTimeDay : TIME;
tPlanTimeDay : TIME := T#16h;
rAvailability : REAL;
rPerformance : REAL;
rQuality : REAL;
rOEE : REAL;
// 通讯诊断
bOnline : BOOL;
nErrorCnt : DINT;
tLastOk : DT;
END_STRUCT
END_TYPE
TYPE E_CncBrand : (Fanuc, Mitsubishi, Heidenhain, Gsk); END_TYPE
TYPE E_CncProtocol : (FOCAS, MTConnect, OPCUA, ModbusTcp); END_TYPE
TYPE E_MachState : (Stop := 0, Run := 1, Pause := 2, Alarm := 3); END_TYPE
// -------- 车间总览 --------
TYPE UDT_ShopSummary :
STRUCT
nOnline : INT; // 在线台数
nRunning : INT; // 运行中
nAlarm : INT; // 报警中
rShopOEE : REAL; // 全车间 OEE
nPartsDayTotal : DINT; // 全车间当日产量
END_STRUCT
END_TYPE
VAR_GLOBAL
cnc : ARRAY[1..32] OF UDT_CncData;
shop : UDT_ShopSummary;
END_VAR
8. 核心程序代码
8.1 OB10 — FOCAS 采集 (Fanuc 12 台)
PROGRAM OB10_Fanuc
VAR
i : INT;
hLib : DWORD;
rc : DINT;
END_VAR
FOR i := 1 TO 12 DO
IF cnc[i].eProto <> FOCAS THEN CONTINUE; END_IF;
rc := FOCAS_Connect(cnc[i].sIp, 8193, ADR(hLib));
IF rc <> 0 THEN
cnc[i].bOnline := FALSE;
cnc[i].nErrorCnt := cnc[i].nErrorCnt + 1;
CONTINUE;
END_IF;
// 500 ms 高频点
FOCAS_ReadSpindle(hLib, ADR(cnc[i].rSpindleRPM));
FOCAS_ReadServoLoad(hLib, 0, ADR(cnc[i].rSpindleLoad));
FOCAS_ReadFeed(hLib, ADR(cnc[i].rFeedRate));
FOCAS_ReadFeedOvr(hLib, ADR(cnc[i].rFeedOverride));
FOCAS_ReadStatus(hLib, ADR(cnc[i].eMachState));
FOCAS_ReadAlarm(hLib, ADR(cnc[i].nAlarmCode), ADR(cnc[i].sAlarmText));
cnc[i].bOnline := TRUE;
cnc[i].tLastOk := DT_NOW();
FOCAS_Close(hLib);
END_FOR;
END_PROGRAM
8.2 OB1 — OEE 计算
PROGRAM OB1_OEE
VAR
i : INT;
END_VAR
shop.nOnline := 0;
shop.nRunning := 0;
shop.nAlarm := 0;
shop.rShopOEE := 0.0;
shop.nPartsDayTotal := 0;
FOR i := 1 TO 32 DO
IF NOT cnc[i].bOnline THEN CONTINUE; END_IF;
shop.nOnline := shop.nOnline + 1;
// 可用率 = 运行时间 / 计划时间
IF cnc[i].tPlanTimeDay > T#0s THEN
cnc[i].rAvailability := TIME_TO_REAL(cnc[i].tRunTimeDay)
/ TIME_TO_REAL(cnc[i].tPlanTimeDay);
END_IF;
// 性能率 = 实际件数 / 理论件数 (由 MES 反馈理论节拍)
IF cnc[i].nPartsTotal > 0 THEN
cnc[i].rPerformance := MIN(1.0, cnc[i].nPartsGood * 1.0
/ MAX(1, cnc[i].nPartsTotal));
END_IF;
// 合格率 = 合格 / 总数
IF cnc[i].nPartsTotal > 0 THEN
cnc[i].rQuality := cnc[i].nPartsGood * 1.0 / cnc[i].nPartsTotal;
END_IF;
cnc[i].rOEE := cnc[i].rAvailability
* cnc[i].rPerformance
* cnc[i].rQuality;
// 刀具寿命告警
IF cnc[i].rToolUsedMin / cnc[i].rToolLifeMin >= 1.0 THEN
FB_RaiseAlarm(i, 801, 'Tool life EXPIRED, force change', Fault);
ELSIF cnc[i].rToolUsedMin / cnc[i].rToolLifeMin >= 0.8 THEN
FB_RaiseAlarm(i, 800, 'Tool life 80%, prepare change', Warn);
END_IF;
// 状态统计
IF cnc[i].eMachState = Run THEN shop.nRunning := shop.nRunning + 1; END_IF;
IF cnc[i].eMachState = Alarm THEN shop.nAlarm := shop.nAlarm + 1; END_IF;
shop.rShopOEE := shop.rShopOEE + cnc[i].rOEE;
shop.nPartsDayTotal := shop.nPartsDayTotal + cnc[i].nPartsTotal;
END_FOR;
IF shop.nOnline > 0 THEN
shop.rShopOEE := shop.rShopOEE / shop.nOnline;
END_IF;
END_PROGRAM
8.3 OB30 — 批量落库 (10 s)
PROGRAM OB30_DB
VAR
i : INT;
stmt : SQLITE_Stmt;
END_VAR
stmt.Prepare(
'INSERT INTO cnc_timeseries(dev,ts,rpm,load,feed,state,parts) ' ||
'VALUES(?,?,?,?,?,?,?)');
FOR i := 1 TO 32 DO
IF NOT cnc[i].bOnline THEN CONTINUE; END_IF;
stmt.Bind(1, cnc[i].sDevId);
stmt.Bind(2, DT_NOW());
stmt.Bind(3, cnc[i].rSpindleRPM);
stmt.Bind(4, cnc[i].rSpindleLoad);
stmt.Bind(5, cnc[i].rFeedRate);
stmt.Bind(6, cnc[i].eMachState);
stmt.Bind(7, cnc[i].nPartsTotal);
stmt.Execute();
stmt.Reset();
END_FOR;
stmt.Close();
END_PROGRAM
8.4 OB31 — MES 推送 (带离线缓存)
PROGRAM OB31_Mes
VAR
i : INT;
sJson : STRING[1024];
http : HTTP_Client;
bOk : BOOL;
END_VAR
FOR i := 1 TO 32 DO
IF NOT cnc[i].bOnline THEN CONTINUE; END_IF;
sJson := JSON_Build(cnc[i]); // 内置序列化
bOk := http.Post(
'https://mes.local/api/equip/state',
sJson,
timeout := T#3s);
IF NOT bOk THEN
// 离线缓存到本地 SQLite, 下个周期重试
DB_InsertPending(cnc[i].sDevId, sJson);
END_IF;
END_FOR;
// 补传离线积压
FB_ReplayPending(limit := 500);
END_PROGRAM
9. 运行与调试
9.1 上电步骤
- 合闸 UPS → 工控机 → 启动 Darra Panel
- 执行 通讯诊断: 32 台 CNC ping 全通
- 逐台验证协议握手 (IDE 通讯监视器)
- 观察
shop.nOnline达到 32 - 查看 Grafana 看板, 主轴 RPM 折线持续刷新
9.2 协议调通顺序
建议分批调通, 避免一次全上排查困难:
| 批次 | 协议 | 台数 | 备注 |
|---|---|---|---|
| 1 | Modbus TCP (广数) | 8 | 最简单, 先通 |
| 2 | OPC UA (海德汉) | 4 | 需导入 CNC Nodeset |
| 3 | FOCAS (发那科) | 12 | 需 CNC 侧开通 HSSB/Ethernet |
| 4 | MTConnect (三菱) | 8 | 需 CNC Agent 先启动 |
9.3 OPC UA Server 发布
IDE → OPC UA Server 向导 → 勾选要对外发布的变量:
Root
├── Shop
│ ├── OnlineCount (UInt16)
│ ├── RunningCount (UInt16)
│ └── ShopOEE (Double)
└── Machines
├── CNC-01
│ ├── SpindleRPM (Double)
│ ├── ToolNo (Int16)
│ └── OEE (Double)
├── CNC-02
│ └── ...
MES 使用任意 OPC UA Client 订阅即可。
9.4 告警规则
| 告警 | 条件 | 级别 | 推送 |
|---|---|---|---|
| 通讯断线 | tLastOk 超 30 s | Warn | 值班室大屏 |
| CNC 报警 | nAlarmCode <> 0 | Fault | 微信 + 大屏 |
| 刀具 80% | rToolUsed/rToolLife ≥ 0.8 | Warn | 班组长 |
| 刀具 100% | ≥ 1.0 | Fault | 强制停机 |
| OEE < 40% | 持续 30 分钟 | Info | 工艺师邮件 |
9.5 常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| FOCAS 断线频繁 | CNC HSSB 超时 | 连接保活 10 s |
| MTConnect XPath 错 | CNC 版本差异 | 导入对应 XSD |
| 落库延时 > 1 s | SQLite WAL 关了 | 启用 WAL |
| Grafana 掉点 | HTTP 接口压力 | 改 MQTT/InfluxDB |
10. Darra 功能对照表
| 工站需求 | 实现方式 | IDE 位置 |
|---|---|---|
| 多协议采集 | 内置 FOCAS / MTConnect / OPC UA / Modbus / MQTT | 通讯向导 |
| 时序数据库 | 内置 SQLite + WAL + 分区 | 项目属性 |
| OEE 计算 | FB_Availability/Performance/Quality | 程序模块 |
| 刀具寿命管理 | UDT 字段 + FB_RaiseAlarm | 程序模块 |
| OPC UA Server | 勾选发布变量 | UA Server 向导 |
| Grafana 对接 | HTTP 接口内置 | 通讯向导 |
| 离线缓存 | DB_InsertPending + FB_ReplayPending | 库函数 |
| 云端看板 | DarraCloud 一键 | 项目属性 |
11. 项目总结
数采中枢场景最能体现 Darra 作为"一体化软 PLC"的价值: 以 1 台工控机 + 1 套软件替代传统方案的 "32 个数采网关 + SCADA + 数据库 + OPC Server + MES 适配器", 硬件成本降低约一个数量级。
成果:
- 32 台全在线, 单台断线仅影响对应 slot, 整体不挂
- OEE 刷新 1 s, 数据库写入 800 条/秒
- 180 天历史占用约 45 GB, 2 TB SSD 可容 8 年
- 全部国产硬件, 机柜合计 ¥3.6 w
- MES / Grafana / 云端三条分发路径并行, 任一中断不影响其他
可复用: UDT_CncData / UDT_ShopSummary / 多协议 OB 模板 / OEE 计算 FB / OPC UA Server 发布方式, 可直接迁移到注塑机、压铸机、冲压机、立式加工中心等任意设备数采场景, 替换协议库即可。