去年下半年帮一家做精密零件的小厂做设备联网,车间里十几台机床,控制系统一半是马扎克的 Smooth 系列,一半是更早的 Smart 系列。老板的需求朴素得不能再朴素:别再让我每天收 Excel 报工表了,我想在办公室屏幕上看到每台机现在到底在不在干活、干的是哪张单。我当时第一反应是问方案商,报回来的单子里 CNC 数据采集单独列了一项,按台数收费,还分基础包和高级包,采集频率越高越贵。这笔钱对小厂来说不算小,于是我把方向调了个头——先去摸清楚这几台设备本身已经开放了什么。结论是:Smooth 和 Smart 这两个系列,很多运行数据其实就在设备自己的 Web 页面和文件系统里躺着,只要不动设备软件、不越权、不破坏原本的加工程序逻辑,完全可以先跑通一套可用的采集链路。这篇就把我这一路踩下来的坑、验证过的字段、以及最后落地的边缘侧架构完整写出来。
1. 先把两代控制系统的"数据出口"摸清楚
动手写代码之前,最浪费时间的错误就是拿一套方案套所有机器。Smooth 和 Smart 虽然都挂着同一个品牌,但它们内部的软件栈、开放程度、页面结构完全是两代人,混着做只会把自己绕进去。
1.1 Smooth 与 Smart 的家底差异
Smooth 系列本质上是一台跑着 Windows 嵌入式系统的工业 PC,屏幕后面有完整的文件系统、有网络服务、有进程管理。这意味着它天然具备"对外说话"的能力:内置 Web 服务、FTP 服务、部分版本还有远程桌面。这些不是为数据采集专门做的,是为了维修、程序传输、远程支持而设计的,但恰恰因为它们是通用服务,我们才能在不动设备的前提下把数据取出来。
Smart 系列相对闭合一些,界面更简化,屏幕尺寸也小,定位是入门机型。它的 Web 能力弱,但文件通道通常更规矩——程序目录、报警历史、加工计数这些往往以文件形式存在,用 FTP 或者共享目录就能拿到。我在现场遇到的一台 Smart,Web 页面只有一个极简的状态页,但 FTP 目录里整整齐齐躺着按日期切分的日志文件,反而比轮询接口更省事。
这里有个经验:不要默认新系统一定好采、老系统一定难采。采集难度取决于这台机器出厂时开了哪些服务,而不是它卖多少钱。我用一张表把两代系统常见的出口列一下,实际现场以扫描结果为准:
| 通道类型 | Smooth 系列 | Smart 系列 | 说明 |
|---|---|---|---|
| 内置 Web 页面 | 常见,功能较全 | 部分机型极简 | 需要浏览器访问验证 |
| HTTP 数据接口 | 多数版本存在 | 较少 | 字段随软件版本变化 |
| FTP 文件服务 | 常见 | 常见 | 用于程序与日志传输 |
| OPC UA | 部分版本、需选件 | 少见 | 有选件时优先用 |
| MTConnect | 需选件授权 | 需选件授权 | 官方路线,稳定但收费 |
| Modbus TCP | 部分机型 | 少见 | 依赖外接模块或选件 |
看这张表就能明白为什么我说"先盘资产":同一批次买回来的机床,因为配置单不同,出口可能完全不一样。我吃过一次亏,第一批五台机器按同一套脚本部署,结果其中两台根本没有对应的接口路径,脚本一直在超时重试,把网关的日志刷满了。
1.2 哪些数据是真的能拿到,哪些别想了
采集清单要务实。我见过太多项目一上来就要"全量数据",最后卡在几个根本拿不到的字段上反复扯皮。按我的经验,从设备自带通道能稳定拿到的是这几类:
- 运行状态:运行、待机、报警、停机,这是最核心的,也是 OEE 的地基。
- 程序信息:当前主程序号、程序名、调用中的子程序。
- 主轴与进给:主轴实际转速、指令转速、进给倍率、快速倍率。
- 轴位置:各轴机械坐标、相对坐标,部分版本有剩余移动量。
- 负载:主轴负载百分比、伺服负载,这个对判断刀具磨损很有用。
- 报警:报警号、报警文本、发生与清除时间。
- 计数与时间:工件计数、通电时间、运行时间、切削时间。
- 刀具:当前刀号、下一刀号、刀补号。
拿不到或者很难拿到的,通常是这几类:真实切削力的物理量、每一刀的微观轨迹、刀具寿命的剩余精确值(很多机型只有到刀库管理层面)、以及和 ERP 工单的强关联——工单号在设备里通常是没有的,得靠程序号或者操作员扫码去映射。这一点必须提前和需求方对齐,否则后面做报表时会发现"良率"根本算不出来,因为设备不知道哪件是废品。
1.3 "免授权"的边界在哪里
标题里的"免授权",我理解得非常具体:不额外采购数据采集选件、不购买第三方采集盒子、不修改设备原厂软件、不绕过任何账号权限,只用设备本身已经提供给使用者的、并且使用方有权限访问的接口。这条线必须划清楚,原因不是道德,而是工程可靠性——任何需要改动设备本体软件的做法,在一次系统升级或一次保修介入后就会全部失效,维护成本会反噬。
我在项目开始时就和设备管理方确认了三件事:一是允许在设备所在网段内部署采集网关;二是允许使用设备已有的账号访问其页面与文件;三是明确采集不得影响加工,一旦发现异常立即停用。这三条写进内部的技术方案里,后面推进起来顺畅很多,因为大家知道边界在哪。反过来说,如果一台设备的管理权限不在你手上,那再怎么"免授权"也不该动,这是前提。
2. 上机之前的资产盘点,比写代码重要十倍
我现在的习惯是:任何一台新机床接入,先花两个小时做盘点,把型号、软件版本、网络配置、开放端口、默认页面全部记录下来,形成一台机器一份的档案。这份档案在半年后设备升级、或者采集突然断流的时候,价值极高。
2.1 型号与软件版本要抄在纸上
先从设备屏幕的"系统信息"或者"版本信息"页面把这几项抄下来:控制器型号全称、软件版本号、可选功能列表、网络参数(IP、掩码、网关、是否 DHCP)。软件版本号尤其关键,因为接口路径和字段在不同小版本之间是会变的,我遇到过同型号两台机器,版本号差了一个小数点,Web 接口的返回结构就不一样。
顺便看一眼网络配置是不是 DHCP。如果是,一定改成静态 IP 或者由网络侧做地址保留。我踩过最无语的一次坑:机床断电重启后从 DHCP 拿到了新地址,采集网关还傻乎乎地朝老地址发请求,日志里全是连接超时,排查了半天以为是设备网络模块坏了。
2.2 端口扫描要挑时间,也要挑力度
端口扫描这件事,我建议只在计划停机或者午休时段做,并且用最温和的参数。控制器的主控 CPU 同时要负责插补和界面刷新,你在它忙着切削的时候做全端口扫描,轻则界面卡顿,重则影响加工节拍。这不是危言耸听,我在一台机器上做高强度扫描时,操作员明确反馈屏幕操作变迟钝了。
温和的扫描方式大致是这样:
# 只扫常见工业端口,-T2 降低速率,-Pn 跳过主机发现 nmap -T2 -Pn -p 21,22,80,443,502,102,4840,5900,8080 192.168.10.21扫出来之后,对开放端口做一次服务识别,确认到底是什么服务在监听:
nmap -T2 -Pn -sV -p 21,80,443,8080 192.168.10.21我的实测结果里,最常见的组合是 21(FTP)加 80(Web)。8080 偶尔出现,通常是另一套管理页面。5900 出现意味着有 VNC 服务,这在远程支持场景里很常见,但它对采集本身价值不大——除非你打算做屏幕图像识别,那属于另一条技术路线,后面会说。
2.3 从默认页面里找内建接口
浏览器打开http://设备IP/,先用设备管理方提供的账号登录。登录之后不要急着翻页面,直接按 F12 打开开发者工具,切到网络面板,然后把界面上的"运行监视""报警一览""程序一览"这些页面挨个点一遍,看每一个请求返回了什么。
这一步是整个流程里信息密度最高的动作。Smooth 系列的监视页面背后往往挂着若干个返回 JSON 的请求,返回体里就是当前的主轴转速、进给速度、程序号、报警列表、坐标值。这些请求的 URL 路径、请求头、以及是否带会话令牌,全部记录下来。我的做法是直接右键"复制为 cURL",粘到文本文件里存着,后面写采集脚本时照着还原。
有几个必须注意的细节:会话令牌通常有超时,页面闲置一段时间后令牌失效,这时候请求会返回登录页的 HTML 而不是 JSON,如果你脚本里不判断,就会把一段 HTML 当成数据解析,然后抛出一堆莫名其妙的异常。另外部分版本对请求频率敏感,连续快速刷新会短暂返回空数据,等几秒又恢复。
3. 走 HTTP 这条路:把页面背后的 JSON 拆开看
Web 接口是我在小批量、多机型场景里最常用的方案,因为它不需要在设备上装任何东西,一个网关对着十几台机器轮询就行。但它也是最脆弱的方案,因为接口不是公开契约,随时可能变。
3.1 一次真实的抓包还原过程
拿一台 Smooth 加工中心举例。登录后点开运行监视页面,网络面板里出现了三个周期性请求,间隔大约一秒。点开第一个看响应,结构大致是这样(字段名我做过脱敏,实际名称随版本变化,请以你自己的抓包为准):
{ "runState": 1, "programName": "O1234", "mainProgram": "O1234", "spindleSpeed": 8420, "spindleCommand": 8500, "feedRate": 1200, "feedOverride": 100, "rapidOverride": 100, "axisPos": {"X": 123.456, "Y": 78.9, "Z": -45.2}, "spindleLoad": 62, "alarmNo": 0, "alarmMsg": "", "toolNo": 12, "nextToolNo": 5, "workCount": 318, "timestamp": "2024-11-06T14:23:51+08:00" }这里每一个字段的含义都值得推敲。runState是个枚举,我在不同机器上见过 0/1/2/3 和 1/2/3/4 两种编码,千万不要凭猜测写死映射关系,正确做法是在现场手动制造各种状态(停机、空跑、切削、报警),每制造一次就抓一次包,把真实值记下来,做成一张对照表。这一步花不了半小时,但能省掉后面几周的报表纠错。
spindleCommand和spindleSpeed的差异很有意思。指令值是你编程写的转速,实际值是编码器反馈的转速。两者差值持续偏大,说明负载重或者皮带打滑;实际值长期为 0 而指令值不为 0,很可能是主轴没转起来或者反馈信号有问题。这个细节在做设备健康度分析时非常好用,比单纯看负载百分比灵敏。
3.2 状态判据要做交叉验证
只靠一个runState判运行状态,误判率会高得让你怀疑人生。原因很现实:操作员把程序调出来、按了循环启动,但机床在等料、在等测量、在等换刀,这时候runState可能显示"运行",但主轴没转、进给没动,实际上不产生任何价值。反过来,有些机型在快速定位阶段runState还是"待机",但轴在动。
我的做法是用多路信号交叉验证,形成一张判据表:
| 目标状态 | 主轴转速 | 进给倍率 | 运行标志 | 报警号 | 轴位移变化 |
|---|---|---|---|---|---|
| 切削中 | >0 | >0 | 运行 | 0 | 持续变化 |
| 空行程 | >0 | >0 | 运行 | 0 | 变化但负载低 |
| 程序暂停 | >0 | 0 | 运行 | 0 | 无变化 |
| 待机 | 0 | 任意 | 待机 | 0 | 无变化 |
| 报警停机 | 0 | 0 | 报警 | >0 | 无变化 |
| 断电/离线 | 无数据 | 无数据 | 无数据 | 无数据 | 无数据 |
最后一行经常被忽略。设备断电时,采集脚本拿到的是连接超时,如果你不单独处理这种情况,脚本会把最后一次读到的状态一直保持下去,于是在报表里这台机器显示"待机"了整整一个夜班,实际上它是关着的。我现在的处理方式是:连续三次轮询失败就标记为"离线",并且专门记一条离线事件,恢复后再记一条上线事件。
3.3 轮询频率和控制器的承受能力
理论上越快越好,实际上有天花板。我做过对比测试,在同一台 Smooth 上以不同间隔请求同一个监视接口:
| 轮询间隔 | 数据完整度 | 控制器界面响应 | 备注 |
|---|---|---|---|
| 200 ms | 高 | 明显变慢 | 不建议,操作员有感知 |
| 500 ms | 高 | 轻微变慢 | 小批量机器可用 |
| 1 s | 够用 | 基本无感 | 我常用的默认值 |
| 5 s | 节拍细节丢失 | 无影响 | 只做产量统计够用 |
| 30 s | 只能看趋势 | 无影响 | 做看板可用 |
结论很明确:1 秒是个比较舒服的平衡点。如果你做的是节拍分析,需要看清每一次换刀、每一次快移,那 500 毫秒是下限。再快就没必要了——机床的机械动作本身就有惯性,200 毫秒以下的采样只是在放大噪声,还会给控制器增加无谓负担。
还有一个隐藏问题:多台设备同时轮询时,网关会瞬间发出大量请求。我一开始用一个循环串行跑,十几台机器跑一圈要十几秒,节拍全乱了。后来改成线程池并发,每台机器一个独立线程,各自维持自己的节奏,问题解决。但并发数要控制,别一上来开五十个线程,网关侧的网络栈和设备的连接数都有上限。
4. 文件与日志通道:被低估的稳定方案
HTTP 接口好用,但一旦设备软件升级,路径可能就变了。相比之下,FTP 这条通道稳定得多,因为文件目录结构通常不会大改。我在几个项目里把 FTP 作为主通道,HTTP 只用来补实时状态,两者互为备份,可靠性明显提升。
4.1 FTP 目录结构通常长什么样
用设备管理方给的账号连上去,先别急着拉文件,用ls -R把整棵树走一遍,看清楚每个目录放什么。常见的几类:
- 加工程序目录:按主程序、子程序分开,文件名就是程序号。
- 日志目录:按日期或按月切分,记录操作、报警、参数变更。
- 报表目录:部分机型会自动生成班次产量报表、加工时间统计。
- 备份目录:参数备份、刀具数据备份。
我一般会先find一遍最近三天的文件,看看哪些在持续更新:
# 用 lftp 更稳,支持被动模式和重试 lftp -u 用户名,密码 192.168.10.21 -e "set ftp:passive-mode on; \ find / -newer /tmp/marker -type f; bye"被动模式这一项要特别提。很多设备内置的 FTP 服务在主动模式下握手会失败,尤其是跨网段访问时。我第一次接一台机器折腾了四十分钟,最后发现就是模式问题。现在的习惯是脚本里默认开被动模式,并且加上重试。
4.2 报警历史和操作日志的价值
实时报警只能告诉你"现在有报警",但报警历史能告诉你"这台机器一天报了几次警、每次持续多久、都是哪几类"。这对设备管理来说价值更大。我从日志里整理过一份某台机器的月度报警分布,发现 70% 的报警集中在气压不足和刀具磨损两类,前者是车间气路问题,后者是刀具管理问题——这两个结论直接推动了现场的改善,比单纯看"报警次数"有用得多。
操作日志还能还原"谁在什么时候改了什么参数"。有一次加工尺寸不稳定,查了半天机械,最后在日志里发现某个班次有人改过进给倍率和刀补,问题一下就定位了。这类信息在做质量追溯时是硬证据。
解析日志的难点在于格式不统一。不同版本的分隔符、时间格式、字段顺序都可能变,所以解析代码要写得宽容:先按行读,再用正则做多模式匹配,匹配不上就原样入库,别直接丢弃。我吃过丢数据的亏——日志解析器遇到一个没见过的格式就整行跳过了,后来做分析时发现某个班次完全空白,白白返工。
4.3 用程序号还原加工节拍
设备不告诉你"这一件什么时候开始、什么时候结束",但你可以推。方法是盯住程序号的变化:当主程序号从无到有,说明开始加工;当程序号回到空或者换成了另一个,说明这一件结束。再结合工件计数的递增,就能把每一件的起止时间标出来。
这里有个坑:程序号在换刀、暂停、复位时不一定变化。我遇到过操作员中途按暂停去处理毛刺,停了二十分钟又继续,程序号一直是同一个。这时候如果只看程序号,就会把这一件算成"加工了二十分钟",节拍严重失真。解决办法是结合前面那张状态判据表,把这二十分钟切成"暂停"段,节拍按实际切削时间算。这也是为什么我一直强调多信号交叉——单靠任何一路都会出错。
5. 状态判定的坑:OEE 一开始几乎一定是错的
所有做设备联网的人都会经历这个阶段:系统上线第一周,报表特别漂亮,可用率 95%,性能 90%。然后车间主任跑过来说,这台机器昨天停了一下午你们怎么没看见。
5.1 报警和停机根本不是一回事
报警是设备自己报出来的异常,停机是设备不产出。这两者的交集远没有想象中大。真实车间里,设备停下来的原因排前几位的通常是:等料、等检验、换程序、调机、午休、没人开机。这些统统不产生报警。
所以状态判定必须建立在"有没有产出"这个物理事实上,而不是"有没有报警"。我的判据顺序是:先看有没有数据(离线直接排除),再看有没有报警(报警→停机),再看主轴和进给(都不动→待机),最后看轴位移和负载(在动→运行)。这一套跑下来,误判率能压到很低,但依然需要人工抽检校准。
5.2 时间戳必须统一,别信设备自己的钟
这是我踩过的最隐蔽的坑。设备的系统时间通常没人维护,跑几个月就偏几分钟,有的甚至连时区都是错的。采集网关记的是网关时间,设备返回的是设备时间,两边一混,做时间序列分析时数据对不齐,节拍算出来忽长忽短。
我的处理原则是:入库统一用网关时间,设备时间单独存一列作为参考。同时推一把设备的时间同步,能在设备侧配置时间服务器的就配上,配不了的至少每周人工校一次。另外,夏令时这种坑在国内不明显,但如果设备出厂设置里时区选错了,日期可能整体差一天,做日报时会出现"今天的产量跑到昨天去了"的诡异现象。
5.3 一次真实的误判复盘
某台车床连续三天报表显示可用率 30%,我一开始怀疑是采集脚本有问题,查了日志发现数据一切正常,状态判定也符合逻辑:主轴转速为 0 的时间占了七成。跑到现场一看,操作员那几天在做小批量试制,编程、对刀、试切占了大半时间,机床确实没在批量切削。
这件事让我意识到,"不切削"不等于"不该算停机"。后来的做法是在状态里单独加一类"调机/试制",由操作员在终端上手动打标,报表里单独统计。这不是技术问题,是管理问题,但技术方案必须给它留出口,否则报表永远和现场对不上,最后没人信。
6. 边缘侧落地:网关选型与采集程序骨架
数据取到了,接下来要落到边缘。这一层的设计目标很明确:不依赖中心服务器、断网能撑住、数据不丢、部署简单。小厂机房条件有限,方案越轻越好。
6.1 网关硬件的取舍
我试过三种形态。工控机(无风扇、宽温)最稳,能跑容器、能接多网段,但成本高、体积大。树莓派这类单板机成本低、部署灵活,跑十几台设备的轮询完全够用,缺点是电源要配好,车间电压波动大的地方容易重启。还有一种是直接用一台旧笔记本,能跑但不可靠,我不推荐进生产环境。
实际选的组合是:每 8 到 12 台设备配一台树莓派级网关,装在电柜附近,用导轨电源供电,走有线网络。网关同时挂两个网段:一个接设备网,一个接办公网,天然形成隔离。程序用 Python 写,跑成系统服务,开机自启,日志按天切分。
6.2 采集程序的最小骨架
框架很简单:配置驱动、并发轮询、统一格式化、发到消息队列。
import json, time, threading import requests from paho.mqtt import client as mqtt DEVICES = [ {"id": "MC-01", "ip": "192.168.10.21", "type": "smooth"}, {"id": "MC-02", "ip": "192.168.10.22", "type": "smart"}, ] def poll(dev, mq): sess = requests.Session() sess.headers.update({"Accept": "application/json"}) path = "/api/status" if dev["type"] == "smooth" else "/status" while True: try: r = sess.get(f"http://{dev['ip']}{path}", timeout=3) if r.status_code == 200 and r.text.strip().startswith("{"): data = r.json() data["dev_id"] = dev["id"] data["gw_ts"] = int(time.time() * 1000) mq.publish(f"cnc/{dev['id']}/status", json.dumps(data), qos=1) dev["fail"] = 0 else: dev["fail"] = dev.get("fail", 0) + 1 except Exception as e: dev["fail"] = dev.get("fail", 0) + 1 if dev.get("fail", 0) >= 3: mq.publish(f"cnc/{dev['id']}/offline", "1", qos=1) time.sleep(1) def main(): mq = mqtt.Client(client_id="gw-shopfloor-01") mq.connect("127.0.0.1", 1883, 60) mq.loop_start() for d in DEVICES: threading.Thread(target=poll, args=(d, mq), daemon=True).start() while True: time.sleep(60)这段代码里有两个细节值得说。一是返回体必须校验是不是以{开头,防止会话失效后把登录页 HTML 当 JSON 解析,我在前面提过这个坑。二是失败计数,连续三次失败才判定离线,避免网络抖动导致的频繁上下线抖动。
6.3 断网缓存与重传
车间的网络没有想象中稳,交换机重启、网线被叉车碰掉都是常事。如果采集程序只做实时转发,一断网那段数据就永远没了。我的做法是在本地写一份 SQLite 缓冲,先落盘再发送,发送成功再删除。这样即使断网一整天,恢复后数据也能补传,代价只是网关的存储卡要选工业级的,别用普通卡,写入寿命不够。
缓冲表结构很简单:一张pending表存原始 JSON 和写入时间,一张sent表存已发送的标记。后台线程定期清理超过七天的已发送记录,防止存储卡被写满。这个设计看着土,但在现场跑了一整年没出过数据丢失。
7. 版本升级之后一定会踩的雷
这套方案能在现场活下来,靠的不是技术多先进,而是对"变"这件事有预期。设备软件会升级,网络会调整,接口会变,这些都不是意外,是必然。
7.1 接口变了怎么办
最典型的一次:设备做了一次系统升级,监视页面重做了,原来那个 JSON 接口的路径还在,但字段名全换了,spindleSpeed变成了嵌套结构。采集脚本没有报错,因为它照样拿到了 JSON,只是所有字段都取不到值,于是报表里主轴转速全是 0,看起来像所有机器都在待机。
这个故障模式最危险的地方在于"静默"。所以我现在强制要求:采集程序对关键字段做非空校验,连续多个周期取不到值就主动告警。宁可误报,也不要静默。另外,升级前后各抓一次包存档,对比字段差异,是排查这类问题最快的办法。
7.2 现场高频故障清单
这几年积累下来,出问题的原因高度集中,我整理成一张表,新项目上线前对着过一遍:
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 全部设备同时离线 | 网关掉电、交换机故障 | 查供电与链路,网关加看门狗 |
| 单台设备频繁上下线 | 网络抖动、网线接触不良 | 换线、固定 IP、延长失败判定阈值 |
| 数据全部为 0 | 会话失效、接口字段变更 | 抓包比对、加字段校验告警 |
| FTP 拉不到文件 | 主动/被动模式、防火墙 | 强制被动模式、放通端口 |
| 设备重启后失联 | DHCP 换地址 | 改静态 IP 或地址保留 |
| 时间对不齐 | 设备时钟漂移、时区错误 | 统一网网关时间、配置时间同步 |
| 报表节拍偏长 | 暂停未切分、程序号未变 | 引入多信号状态判据、手动打标 |
这张表我一般会打印出来贴在调试本上,现场遇到问题先扫一眼,命中率不低。
7.3 关于"长期可维护"的一点私心
这套方案没有任何一处需要改动设备本体,所以它天然具备一个优势:设备该怎么用还怎么用,采集出问题不影响生产。我见过一些项目为了让数据好看,在设备上装了额外的软件或者改动了配置,结果一次保修介入就被要求恢复原状,前面几个月的投入全打水漂。
所以我的原则很死:设备侧零改动,所有逻辑放在边缘网关和中心侧。哪怕因此拿不到某些字段,也不越线。少几个指标,报表还能用;动了设备,风险是全局的。
最后再分享一个我觉得最值钱的习惯:每台设备接进来的时候,我会单独存一份"这台机器长什么样"的档案,包括型号、软件版本、端口、接口路径、字段样例、状态枚举对照表、以及当初抓包的时间。半年后某个采集突然断了,翻出这份档案,五分钟就能判断是设备变了还是网络变了。这个动作不产生任何直接产出,但它在整个项目的生命周期里省下的时间,远超当初记录的十分钟。