ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C#实战:CNC数据采集系统解析,涵盖FOCAS与OPC UA

2026/8/31 14:50:40 拓冰建站 浏览量
C#实战:CNC数据采集系统解析,涵盖FOCAS与OPC UA 简介本资源是一套面向工业自动化方向个人学习者的CNC数据采集系统实战项目聚焦于Fanuc数控机床的实时状态监控、加工参数记录与基础故障诊断适用于具备C#基础并希望切入智能制造数据采集领域的初学者与进阶开发者。压缩包共29个文件含9个核心C#源码文件如connectmachine.cs、FanucOpe.cs、2个关键动态库fwlib32.dll、fwlibe1.dll用于机床通信、4个备份文件.zbak便于版本回溯以及.sln解决方案、.csproj工程配置、.xsd/.xsc数据集定义等完整开发结构整体仅652KB轻量易读。已有162人下载学习资源结构清晰包含Windows Forms界面实现、DataSet数据建模、Fanuc FOCAS协议调用封装及配置文件管理读者可直接运行调试、理解CNC通信逻辑、掌握工业数据采集系统典型分层设计UI层→业务层→驱动层→数据层是难得的贴近真实产线场景的学习范例。 干数控机床数据采集这行的应该都听过一句话数控系统是黑盒想从里面拿数据每个品牌都有自己的脾气。我这次整理的这套基于C#的CNC数据采集系统是过去几年在车间里实际跑过的代码从FANUC FOCAS通讯到西门子OPC UA接入再到数据集的构建和清洗全都打包整理好了代码和样例数据放在一起不是那种只能看看的Demo是真能拿到车间试运行的完整工程。这套系统能干什么简单说就是通过网线和数控系统建立通讯把机床的坐标、主轴转速、进给倍率、报警信息、当前程序号、运行模式这些数据定时采集出来存到数据库里再给MES、大屏看板或者数据分析平台用。好处是把车间设备的实际运行状态变成结构化数据产能分析、设备OEE、异常追溯都有依据了。适合正在做上位机和MES对接的工程师也适合刚入门工控开发、想研究C#和数控系统通讯的朋友。接下来的内容我尽量按“为什么这么设计、具体怎么实现、坑在哪里”三个层面讲清楚源码里涉及的核心函数我也会逐个拆开说明。1. 先搞清楚这套系统解决什么问题1.1 为什么选择C#来做CNC数据采集选型这件事我在好几个项目里权衡过。有段时间用LabVIEW做采集界面开发确实快但和团队里做MES的同事协作时算法封装、代码管理都不方便。也试过Python采集脚本本身没问题但部署到Windows工控机上打包、注册服务、性能控制总感觉隔了一层。C#在这个场景下的优势非常明确。第一WinForm和WPF开发上位机界面太成熟了表格、趋势图、按钮事件拖拖拽拽就完事车间老师傅用起来也顺手。第二C#和MES、数据库打交道几乎是零成本SQL Server、MySQL、PostgreSQL都有官方驱动Entity Framework或Dapper随便选数据从机床到业务系统的链路非常顺。第三.NET 6之后的跨平台能力让这个系统不再局限于Windows部署到Linux服务器做边缘采集节点也没问题。我自己实测过同样的采集逻辑Windows和Linux跑出来的数据基本一致。有人可能会问C性能不是更好吗是单次API调用确实更快但CNC采集本来就是低频操作几百毫秒采一次C#的托管开销完全可以忽略。真正要命的是开发效率和后续维护C写通讯封装光内存管理和线程同步就得耗掉一半工期这不划算。1.2 整体架构从机床到数据平台的完整链路这套系统的架构不复杂但每一层都有讲究。整个数据流是这样的数控系统FANUC/西门子/三菱 ↓ 网线连接TCP/IP C#采集服务采集线程 缓存队列 上传线程 ↓ MQTT或HTTP上报 数据接收服务网关/消息队列 ↓ 写入 数据库SQL Server / 时序库如InfluxDB ↓ 查询接口 MES / 看板 / 数据分析平台设备层是整个链路的地基。不同数控系统的通讯协议差异很大FANUC走FOCAS协议西门子840D sl走OPC UA或者S7协议三菱走EzSocket还有些国产系统走Modbus TCP。所以采集服务在设计上就必须做成“协议驱动”模式每种系统一个驱动实现对外暴露统一的数据模型上层不用关心底层是哪家设备。采集层是核心。这里要特别注意采集不是简单的“定时读一次数据”而是要有独立的采集线程、数据缓存队列、断线重连机制。不然机床一关机、网线一拔、系统一重启采集服务可能就挂掉了或者数据丢一片。源码里这部分用的是生产者-消费者模型采集线程生产数据放进队列上传线程消费队列往服务端发送两边速率解耦突发数据多的时候队列能暂存一部分不会立刻丢失。传输层我提供了两种方式一种是MQTT适合设备数量多、网络不稳定的场景离线时数据能缓存在客户端另一种是HTTP接口适合内网环境简单、设备数量少的项目。实际车间使用中MQTT明显更稳尤其是采集点和服务器之间要经过交换机、光缆这些环节时MQTT的QoS机制能保证消息不丢。存储层和展示层其实已经超出采集系统本身了但我在源码里加了一个简单的接收端示例建了十几张基础表用户拿到手可以直接跑起来看到数据入库的效果。1.3 采集范围到底包含哪些数据刚开始做CNC采集的时候我犯过一个错误觉得数据越全越好于是把能读的都读了结果采集周期拖得很长现场数据反而失真。后来才总结出CNC数据采集必须按用途分层设计。首先是状态类数据包括机床开关机状态、当前运行模式自动/手动/MDI/编辑、报警状态、程序运行状态。这类数据变化频率低1秒采一次就完全够用主要给OEE统计、设备监控大屏用。其次是坐标类数据X/Y/Z轴的绝对坐标、相对坐标、机械坐标还包括当前刀具号T、主轴转速S、进给速度F。这类数据用于加工过程追溯、工件位置监控、主轴负载分析采集频率可以做到200到500毫秒一次。如果想做轨迹分析或者振动诊断那就需要更高的采样频率但那种场景一般就得用专门的高速数据采集卡了普通TCP通讯做不到。第三是生产类数据比如当前程序号、运行时间、加工件数、循环次数。这些数据是MES最关心的直接影响产能统计1秒采集一次入库后再按分钟、小时粒度聚合。最后是报警类数据报警号、报警内容、报警时间出现报警时立即上报这个不能按固定周期必须采用事件触发方式。我在源码里是单独开了一个轮询线程发现报警状态变化就立刻推送。2. 核心采集源码拆解以FANUC FOCAS为例2.1 FOCAS通讯库的C#封装方法FANUC机床自带的以太网口用的通讯协议是FOCAS官方提供DLL库Focas32.dll通过以太网和伺服软件比如FANUC的Servo Guide共享这套接口。这个DLL不是.NET库是原生C接口所以在C#里必须用DllImport引入。网上很多人写的封装是这么做的[DllImport(Focas32.dll, EntryPoint cnc_allclibhndl3)] private static extern short cnc_allclibhndl3( string ipaddr, ushort port, int timeout, out IntPtr handle);这里有三个关键参数。ipaddr就是机床的IP地址在FANUC系统上通过MDI面板设置一般在“系统”参数里能找到。port固定是8193这个不要改。timeout是连接超时时间单位是毫秒我建议设置3000到5000毫秒太短了车间网络稍有波动就连接失败太长了程序启动时卡住影响体验。返回值也很重要。FOCAS函数返回short类型0表示成功非0表示错误码。常见的错误码比如EW_BUSY说明机床忙EW_PROTOCOL说明协议错误可能版本不匹配。源码里我写了一个统一的错误码解析方法把错误码转成可读的错误信息排查问题的时候非常有用。连接句柄handle是后面所有读取函数的第一个参数一定要声明成类级别的字段不要每次读取都重新连接。FOCAS的机制是建立一次连接后续多次读取用完了再断开。每次读取都重连的话不仅效率低还可能被数控系统判定为异常连接导致IP被锁一段时间。2.2 读取坐标、转速、报警的核心函数说明连接建立之后读取数据就比较直接了。FANUC FOCAS提供了一组读取函数源码里最常用的是这四个。首先是读取坐标函数是cnc_rdposition[DllImport(Focas32.dll)] private static extern short cnc_rdposition( IntPtr handle, short type, out ODBPOS position);type参数表示坐标系类型绝对坐标传-1相对坐标传0机械坐标传1。ODBPOS是输出结构体里面包含每个轴的位置信息和单位。这里有坑ODBPOS结构体里的字段布局必须和DLL里完全一致字段顺序差一点都不行轻则读出错误数据重则内存访问异常崩溃。源码里我是严格按照FOCAS头文件逐一对应排列的你要是自己封装记得找厂商要最新的头文件别在网上随便抄一个结构体版本不对读出来的数据经常是错的。其次是读取主轴速度和进给速度函数是cnc_rdspeed[DllImport(Focas32.dll)] private static extern short cnc_rdspeed( IntPtr handle, out ODBSPEED speed);ODBSPEED输出结构体里面主轴转速单位是转/分进给速度单位是毫米/分注意有的系统能返回实际值有的系统只能返回指令值要看系统具体参数。第三是读取报警信息函数是cnc_rdalm[DllImport(Focas32.dll)] private static extern short cnc_rdalm( IntPtr handle, out ODBALM alm);ODBALM包含了报警号和报警信息报警号是short类型报警信息是字符数组。读取时要注意这个函数在没有报警时会返回0但不代表没有数据必须判断输出结构体里的报警状态字段。第四是读取当前运行模式和程序状态函数是cnc_rdmode和cnc_rdact这两个组合起来能判断机床当前是在自动加工、手动还是编辑状态以及当前执行的是哪个程序号。MES系统做产量统计时主要就是靠这两个函数的返回值来判断当前是不是在正常加工。2.3 线程模型与缓存队列保证数据不丢的底层设计在实际车间环境里网络不会像办公室那么稳定断线重连、数据缓存是刚需。我在源码里把采集服务设计成了三个线程协作的模型你拿到代码能直接看懂。采集线程是核心线程按照配置的采集周期循环调用FOCAS读取函数把原始数据封装成一个统一的数据模型对象塞进ConcurrentQueue缓存队列。这里用ConcurrentQueue而不是普通的Queue是因为多线程并发写队列时不会出现数据错乱省去了手动加锁的麻烦。上传线程负责把队列里的数据拿出来打包成JSON通过MQTT或HTTP推送到服务端。队列在内存里是先进先出的上传线程消费速度跟不上采集线程生产速度时队列会持续增长所以源码里设置了一个最大队列长度默认5000条超过之后丢弃最老的数据并记录日志。实际使用中把采集周期设为500毫秒、上报周期设为1秒队列在正常情况下是稳定在几十条左右基本不会触发丢弃。断线重连机制我是这么做的采集线程在调用FOCAS连接时如果返回错误码不会立刻重试而是进入等待状态过5秒再尝试重连。上传线程检测到MQTT或HTTP连接断开时会暂停出队操作但采集线程不受影响数据仍然在队列里缓存。这样网络恢复后积压的数据会自动补传上去最多可能丢几秒到几十秒的数据但不会因为断网直接导致整个采集服务崩溃。2.4 防止采集影响机床正常运行的边界控制这一点是很多刚入行的工程师容易忽略的。FOCAS虽然主要提供只读接口但采集动作本身仍然可能给数控系统带来负担。我见过有工程师把采集周期调到50毫秒结果机床PMC扫描周期都受影响了操作工直接投诉。我的经验是给采集频率设置一个底线。状态类数据不低于1秒一次坐标类数据不低于200毫秒一次。FOCAS读取函数本身是同步阻塞的一次读取大概会占用系统几百微秒到几毫秒的时间频率太高累积起来的CPU占用是实打实的。还有一个细节是超时控制。FOCAS所有读取函数最好都做超时保护不然机床系统处于忙碌状态时读取函数可能长时间不返回导致采集线程卡住。我在源码里用的是对读取函数做异步调用加超时等待超过3秒就判定为超时重新建立连接这样能避免采集线程被一个异常读取永久阻塞。另外要注意FANUC系统的连接数有限制一般同一时间只允许建立有限几个FOCAS连接通常是几个到十几个具体看系统版本。如果车间里有好几台采集设备同时连一台机床可能后连的会被拒绝。所以开发调试时一定要确认没有其他软件占着连接我在现场就遇到过一次笔记本调试客户端连着机床服务端采集连不进去排查了半天才发现是这个原因。3. 数据集怎么整理才真正能用3.1 数据集字段设计原则与存储格式源码包里附带了一份真实机床采集的样例数据集CSV格式一共包含几十万条记录。这份数据集的字段设计我用了挺长时间打磨既要满足通用性又不能太冗余。最终定下来的核心字段有这些timestamp, device_id, machine_mode, run_status, program_name, spindle_speed, feed_rate, x_pos, y_pos, z_pos, spindle_load, x_load, y_load, z_load, tool_no, alarm_code每行数据都包含一个ISO 8601格式的时间戳这是数据集的灵魂。没有时间戳的数据基本就是废数据因为做时序分析、对齐多设备数据全部依赖时间戳。device_id用来区分不同的机床比如MC01、MC02就是车间里的设备编号。machine_mode对应机床运行模式run_status表示运行或停止状态。坐标和负载数据就是从FOCAS读出来的原始值单位统一为毫米、转/分、百分比。存储格式上原始数据我推荐CSV因为通用、方便各种工具读取也方便做数据清洗。但如果是长期生产环境建议还是导入到时序数据库比如InfluxDB或者SQL Server里查询效率高得多。数据集里这份CSV主要用于建模和算法验证比如做故障预测、刀具磨损识别直接用pandas读进来就能处理。3.2 脏数据清洗与标注方法做过数据分析的人都知道真实采集的数据脏得很。我这份数据集里也包含一些明显的问题记录比如机床在断电关机前坐标值会异常跳变报警发生时主轴负载会突然归一化到0通讯闪断过一小段时间时间戳会有缺口。这些都是真实场景中的典型脏数据正好用来演示如何处理。数据清洗主要做三件事。第一是去重因为网络重传或者采集服务补传可能出现完全重复的两行按时间戳和设备ID去重即可。第二是处理缺失值上下两条记录之间如果时间间隔超过采集周期的3倍就标记为缺失段这种缺失不建议插值因为机床停机期间数据本来就不该有值。第三是过滤异常值坐标值突然跳变超过机床行程范围、主轴转速为负值这些明显不合理的记录直接剔除。标注方面我是按“正常加工”“报警停机”“待机空闲”“关机离线”四个状态给数据集打标签的。做法是结合报警码、运行状态和时间间隔来标。比如alarm_code非0且run_status为停止就标注为报警停机连续一段时间没有任何变化且没有加工动作就标注为待机空闲。标签是单独的label列方便做监督学习。现在这份数据集可以直接用来做三分类、四分类的分类模型训练也可以拿来做无监督的异常检测。3.3 数据集的实际业务应用方向有了这份数据集能做的应用其实挺多。我自己在项目中实现过两个比较典型的场景。第一个是设备OEE自动计算。OEE是设备综合效率等于时间开动率乘以性能开动率乘以合格品率。用数据集的run_status、machine_mode和spindle_speed字段可以精确算出机床的有效运行时间、加工时间和理论加工周期然后自动得出OEE。这套逻辑跑起来每天早上MES系统里就能看到前一天每台设备的OEE报表解决了车间靠人工填日报的低效问题。第二个是主轴负载的异常预警。通过分析主轴负载的时间序列特征尤其是加工同一种零件时的负载曲线可以建立基线模型。当新采集的负载数据和基线偏差超过阈值时系统自动发出预警往往是刀具磨损、切削参数异常的前兆。这个用机器学习方法做不难数据集里的spindle_load字段就是为此准备的。实际上这份数据集还可以用于加工节拍分析、程序执行时间预测、多设备负载均衡调度等场景本质上有持续的时序数据数据科学方面就有发挥空间。4. 源码部署实录从仓库到车间真正跑起来4.1 环境准备与依赖清单拿到源码第一件事不是双击编译而是先把环境准备好。开发机需要安装.NET 6.0 SDK或更高版本我用的是.NET 6直接用Visual Studio 2022或者Rider打开解决方案文件就可以。运行时依赖有三个NuGet包MQTTnet做MQTT客户端、Newtonsoft.Json做JSON序列化、System.Data.SqlClient写数据库。如果连接FANUC机床还需要把Focas32.dll放到程序输出目录里。这个DLL官方不直接提供下载一般由FANUC代理商提供或者从机床厂家的开发资料里获取。没有这个DLL程序编译不报错但运行时会提示找不到Focas32.dll这是因为它是原生库不是NuGet能解决的。网络准备方面采集中间需要保证机床和工控机在同一个局域网内建议直接网线直连或者走车间交换机配置好机床的IP地址、子网掩码并在工控机上用ping命令测一下连通性。4.2 配置项逐条解析源码里有一个appsettings.json配置文件所有重要参数都在里面不用改代码就能适配不同车间。我挑几个关键的说明一下。{ Device: { Id: MC01, Ip: 192.168.0.1, Port: 8193, Protocol: Focas }, Collect: { StatusIntervalMs: 1000, PositionIntervalMs: 500, AlarmIntervalMs: 500, TimeoutMs: 3000 }, Upload: { Mode: MQTT, BrokerIp: 192.168.0.100, BrokerPort: 1883, Topic: cnc/data, IntervalMs: 1000, MaxQueueSize: 5000 } }Device节点配置设备基本信息Ip是机床的IPPort固定8193Protocol字段以后可以扩展比如西门子就改成OpcUa。Collect节点配置采集周期我把状态采集和坐标采集分开配置了因为二者用途不同。Upload节点配置上传方式Mode改成Http就是走HTTP接口上报BrokerIp是MQTT代理服务器地址。这里有个容易踩的坑StatusIntervalMs和PositionIntervalMs如果设置成一样的值会导致每个周期同时读取多类数据单次执行时间变长容易撞上系统忙。建议给状态和坐标分配不同的时间偏移实际上源码里已经做了错峰处理把两个定时器错开了一半周期如果你改配置保持这个思想就好。4.3 运行实测性能和稳定性参考这套系统我在车间的调试场景下跑过一台FANUC 0i MF的加工中心配置和上面的参数差不多。实际运行了一周中间故意拔了几次网线模拟故障表现如下。正常情况下采集线程每500毫秒读一次坐标和转速每1秒读一次状态CPU占用率稳定在百分之三到百分之五之间内存占用不到80MB。上传线程每1秒把队列里的数据打包成JSON发到MQTT一天的原始数据量大概在30万条左右MySQL入库后磁盘占用不到200MB。断网状态下采集线程继续工作队列缓存到上限后丢弃最老的数据并打日志网络恢复后自动补传补传过程中队列占用会明显上升但不会出现内存溢出。报警数据是测试最多的一项。人为触发一次机床报警从报警产生到采集服务收到报警消息再到页面展示出来整个过程耗时不超过1秒满足车间监控的需求。整体来说这套系统在单台机床、单采集节点的规模下非常稳定完全可以作为正式环境的基础框架来用。5. 实战中遇到的坑和排查方法5.1 连接建立失败这是大家最先遇到的问题也是最折磨人的。代码明明照着文档写的但就是连不上机床。我把自己排查这类问题的完整套路整理出来。第一步确认IP地址和端口。机床侧需要在系统面板里设置以太网参数有些FANUC系统还需要额外设置访问权限不开权限的话FOCAS连接会被拒绝。默认端口就是8193不要随便改。第二步用工具验证网络通不通。先ping机床IP能通说明二层三层网络正常。再用telnet测试8193端口是否开放如果telnet能连上说明FOCAS服务在监听。第三步检查FOCAS连接占用。如果已经有一个客户端连着机床新连接很可能失败。把其他测试程序关掉再试或者把机床系统重启一下释放连接。第四步确认Focas32.dll版本。不同的FANUC系统版本可能对应不同的DLL版本老版本DLL连新系统、新版本DLL连老系统都可能返回EW_PROTOCOL错误。5.2 采集数据异常坐标跳变和转速为0采集线程连上了但读出来的数据显示异常这类问题分两种。一种是通讯返回正常但数值不符合常理比如某个轴的坐标突然从100跳到500万这基本可以判定是结构体解析错误字节对齐或者字段长度不对导致读出来的值被错位解读。解决方法是严格对照FANUC头文件检查结构体定义一个字节都不能差。另一种是数据本身为0或者全空但机床面板上明明有正常数值。这种情况先检查采集周期是否设置得太快导致系统还没来得及刷新缓冲区。另外FOCAS读取函数有变量类型的概念不同的变量范围读出的结果不同比如有的系统用毫米有的用脉冲数需要看具体参数设置。5.3 排查技巧速查表与避坑指南我整理了一张问题速查表贴在源码仓库的README里这里把最核心的几个放出来现象可能原因排查方法连接超时IP不对、端口不通、权限未开ping、telnet、检查机床网络参数返回错误码EW_PROTOCOLDLL版本和系统不匹配换对应版本的Focas32.dll坐标读出3128.xxx结构体对齐错误逐字节比对头文件结构体定义一个周期内CPU占用飙升采集间隔设置过短调大采集周期错峰读取队列持续增长上传线程卡住或网络中断检查MQTT连接状态、适当调大队列上限报警漏报报警轮询线程未独立改用独立线程读取报警状态并事件上报数据时间戳不连续网络闪断、补传机制生效接受轻微间隙重点看补传是否成功最后补充一个我自己反复踩的坑采集服务部署成Windows服务后不要用系统默认的“本地系统账户”运行要给它一个独立的账户并且允许“交互式登录”否则有可能读取权限受限而且服务重启后FOCAS连接经常拿不到句柄。这个在开发调试时不容易发现上了生产才知道坑。另外就是数据集的维护不要一次性采完就扔掉建议把每天的原始增量数据归档按月做一次清洗合并。这样时间长了之后你的数据集就会越来越丰富模型训练和算法验证的材料就不会缺。这套源码拿到手里建议先从FANUC设备做起一台机器跑通后再扩展其他协议。协议驱动的架构已经留好接口后面接西门子、三菱都只是加一个驱动的事。我自己的经验是一套能落地的数据采集系统代码只占三成剩下的七成是对设备、对车间、对现场异常的理解这些只能靠摸索积累。希望这份源码和数据集能帮你少走一些弯路。本文还有配套的精品资源点击获取