拓冰建站拓冰建站
首页 / 资讯中心 / 正文

C#实现的CNC数据采集系统:从通信到数据集

简介本资源是一套面向工业自动化与智能制造方向个人学习者的CNC数据采集系统实践材料聚焦C#在数控机床实时监控、状态记录与故障诊断中的工程应用。资源包含完整可运行的Windows Forms项目源码含9个核心.cs文件、Fanuc数控系统通信支持库fwlib32.dll等、数据集定义文件xsd/xsc/xss、配置文件App.config等及项目构建元数据csproj/sln共29个文件总大小652KB结构清晰便于理解工业通信、数据库绑定与UI交互的集成逻辑。已有162人下载学习适合具备基础C#和.NET框架知识的学习者通过阅读源码掌握CNC数据采集协议对接如Focas库调用、实时数据可视化、加工参数持久化存储等关键技术环节并借助配套数据集开展真实场景下的数据分析与系统调试训练。 做数据采集这个行当久了你会发现一个很有意思的现象很多工厂买回来的CNC设备联网率低得惊人。哪怕是两三台加工中心的小车间老板也想搞清楚设备到底在干什么、开了几小时、停了几小时、有没有在空转。但这个需求落到技术层面往往就卡在“不知道怎么把机床里的数据拿出来”。我这次分享一个用C#从零搭起来的CNC数据采集系统附带完整的源码结构设计和一套可供算法训练的数据集格式希望能给做车间数字化、MES系统对接或者设备运维的朋友提供参考。先说明这套系统能干什么它能把数控系统以发那科、西门子、三菱常见系统为主内部的运行状态、主轴负载、坐标位置、程序号、报警信息等数据按秒级周期采集下来存入SQL Server数据库同时对外提供统计查询接口。采集下来的历史数据整理成结构化数据集后可以用于OEE计算、设备利用率分析、主轴负载异常预警甚至后续做基于机器学习的故障预测。这个系统适合谁看如果你是上位机开发、MES实施工程师、工厂设备管理员或者正在为“怎么把CNC拉进数字化系统”这件事发愁这篇文章能给你一条能直接落地的路线。下面我按项目拆解的顺序讲尽量把每个关键选择背后的原因也说清楚。1. 项目背景与整体设计思路1.1 车间数据采集的现状和痛点数控机床和普通设备最大的区别是它本身自带一套完整的控制系统系统里存着大量有价值的数据。但大多数机床出厂时这些数据只显示在屏幕上没有对外的接口习惯。工厂里常用的数据收集方式我总结下来无非三种第一种是人工登记。操作工每班结束在纸质表单上填写开机时间、加工数量、异常情况。这种方式的问题不用多说记录不及时、容易漏填、数据可信度差老板拿到手也只能当参考。第二种是加装传感器。在机床外部装电流互感器、震动传感器、三色灯采集器通过外部信号推断机床状态。这种方案不用碰机床系统实施简单但只能读到间接信号拿不到程序名、主轴负载、报警代码这类内部信息想深入分析工艺时明显不够用。第三种就是直接跟数控系统通信。通过网口或者串口走数控系统提供的通讯协议直接读取系统内部变量和状态字。这才是数据采集的正路数据最全、实时性最好、准确度最高但技术门槛也最高。我一开始做这套系统时就是冲着第三种方案去的。原因很现实只有拿到底层数据才能谈得上后续的工艺优化和预测性维护。如果只是采集个三色灯状态那系统的天花板太低了。1.2 为什么用C#而不是其他语言很多做设备采集的工程师习惯用C、LabVIEW或者Python我在这套系统里选了C#主要有四个原因。第一个原因是Windows生态成熟。车间里的上位机、数据服务器绝大多数是Windows系统C#的WinForm/WPF做采集监控界面非常顺手部署也简单一台工控机装上.NET运行时就能跑。第二个原因是通信库丰富。无论是发那科的FOCAS库、西门子的S7协议还是走OPC UAC#都有对应的官方库或成熟开源封装不需要自己从零啃协议栈。对于小团队来说这一点能省下大量开发时间。第三个原因是处理并发和数据库操作顺手。数据采集是典型的高频写入场景一台设备一秒一条甚至一秒多条数据几十台设备并发写入时C#的异步编程模型和成熟的ORM/ADO.NET生态能比较轻松地扛住。第四个原因也很实际C#开发人员好找。车间级的数字化系统通常需要后期持续维护如果用了特别小众的语言或框架人走了项目就黄了。C#在国内工控领域的保有量大后续接手成本低。1.3 整体架构链路这套系统的物理架构不复杂从机床到数据展示分四层设备层CNC机床通过以太网口接入车间交换机。走网络通信的前提是机床得有网口近十年出厂的设备基本都具备老设备可以通过串口转以太网模块解决。采集服务层一台工控机或虚拟机上运行C#编写的Windows服务负责按周期轮询每一台机床的数控系统读取设定好的数据项解析后写入数据库。数据存储层使用SQL Server作为主数据库按天分表存储采集明细数据同时建有设备基础信息表、报警记录表、产量统计表。应用展示层一个WinForm桌面程序和一个Web API接口。WinForm用于现场查看实时状态Web API给MES系统或报表平台提供数据。这个架构是比较常规的工业数据采集方案好处是各层职责清晰采集服务只管数据采集入库展示层只做查询互不干扰。即使某台机床断网或者采集服务重启也不会影响已经入库的数据。2. 采集核心模块源码拆解2.1 通信层封装FOCAS与OPC UA的取舍通信层是整个系统最核心的部分也是最容易踩坑的地方。不同品牌的数控系统提供的通信方式完全不同我把常用方案梳理了一下数控系统品牌常见通信方案C#接入方式备注发那科FOCAS/FOCAS2P/Invoke调用Fwlib32.dll发那科官方通信库功能最全西门子S7协议 / OPC UAS7netplus库 / OPC UA客户端840D sl以上系统支持OPC UA三菱iQAP / CNC通信官方ActiveX或TCP直接解析老设备需要走串口兄弟、马扎克等各自私有协议或OPC UA按厂家SDK对接一般都有现成库或文档我这套系统里主力对接的是发那科系统因为车间里发那科设备的占比最高。FOCAS库的C#调用方式很直接就是通过DllImport导入Fwlib32.dll里的函数。比如建立连接[DllImport(Fwlib32.dll)] private static extern short cnc_allclibhndl3(string ip, ushort port, int timeout, out ushort handle); [DllImport(Fwlib32.dll)] private static extern short cnc_rdspdl(ushort handle, out short load, out short speed); public ushort Connect(string ip, int timeoutMs) { ushort handle; short result cnc_allclibhndl3(ip, 8193, timeoutMs, out handle); if (result ! 0) { throw new Exception($FOCUS连接失败错误码{result}); } return handle; }这里有两个容易踩的坑。第一个是端口号发那科默认的以太网通信端口是8193这个值在官方文档里有明确说明不能随便改。第二个是超时时间如果设备IP不通或者网络有延迟connect函数会阻塞一段时间建议把超时设短一点比如3秒失败后快速重试避免采集线程卡死。对于支持OPC UA的数控系统我封装了一个通用的OPC UA客户端。C#这边可以基于OPC Foundation官方库或者轻量的OpcUaHelper开源库来实现。核心逻辑是配置好EndpointUrl、连接超时和安全策略连接成功后按节点ID批量读取数据项。OPC UA的好处是各家系统支持得越来越普遍而且读到的数据自带标准化语义不用像FOCAS那样自己去查各种参数含义。实际项目里我通常会同时保留两套通信接口通过配置文件选择走哪条通道。这样不同品牌、不同年代的机床可以在同一套采集服务里并存不至于因为通信方式不同而被迫部署多套系统。2.2 数据采集轮询与入库逻辑数据采集的核心是一个定时轮询任务。每台设备注册一个采集工作项系统按设定好的周期通常是1秒到5秒依次执行读取、解析、入库操作。轮询周期不是越短越好。周期太短数控系统通信被频繁请求可能影响机床本身的运行甚至导致系统报警周期太长又拿不到细粒度的运行数据主轴负载的瞬时峰值捕不到。我的经验值是主轴负载、转速、进给速度这类快速变化的数据用1秒或2秒周期坐标位置、刀具号、程序名这类慢变量5秒读一次就够。这个配置可以在采集配置表里针对每台设备单独调整。采集入库的代码逻辑大概是这样public async Task CollectAsync(CancellationToken ct) { while (!ct.IsCancellationRequested) { foreach (var device in _devices) { try { var data await _reader.ReadDeviceDataAsync(device); await _repository.InsertAsync(data); } catch (Exception ex) { _logger.LogError($设备{device.DeviceCode}采集失败{ex.Message}); } } await Task.Delay(_interval, ct); } }这段代码看起来简单但生产环境里需要注意几个细节。首先是并发度问题一台一台顺序采集设备多了以后单轮耗时很长但并行采集又会给机床通信模块造成压力。我最后是采用分组串行、组内并发的方式比如每10台设备一组组内并行执行每组之间串行等待整体采集效率提升明显。入库时我用的批量插入而不是逐条Insert。因为一秒一条数据看起来不多但30台设备连续跑一个月数据量有几百万行。如果逐条Insert数据库事务开销太大后期查询会越来越慢。我封装了一个简单的批量写入方法收集一定条数或一定时间后一次性提交到数据库。2.3 设备状态判定与产量统计逻辑拿到原始数据之后不能直接存库就完事还要做一层关键加工设备状态判定。数控系统的运行状态字能告诉我们当前是自动运行中、手动操作、暂停还是报警但这个状态字含义比较底层直接展示给管理层看不懂。我这边会把状态映射为四个业务状态运行、待机、停机、报警。判定逻辑设计如下运行系统状态为自动运行中或者主轴负载超过设定阈值比如5%且程序正在执行。待机系统上电但未执行程序主轴不转无报警。停机通信中断或机床处于关机状态持续读不到数据。报警读取到报警代码或者系统状态字表示报警。光靠状态字还不够可靠。我曾经遇到过一种情况机床状态字显示“运行中”但实际操作工按了进给保持主轴已经停转了。这时候状态字并不能准确反映加工是否真的在推进。后来我在判定逻辑里加了主轴负载条件只有当主轴负载大于一个阈值时才认为在真实加工。这个阈值不能设死不同加工场景差异很大我在配置表里给每台设备单独设了参数实施时根据实际工况标定。产量统计也是基于状态判定延伸出来的。最靠谱的做法是在加工程序里加宏变量每加工完一件工件就自动计数通过FOCAS或OPC UA读取这个计数变量。但很多现场的加工程序不受控没有统一写宏变量。作为补充系统可以用“主轴负载曲线”来识别加工循环当主轴负载从高负载降到空载并持续几秒后又升上去可以认为完成了一个加工循环。这个方法识别精度不是100%但作为参考数据够用了。3. 数据集构建与清洗3.1 数据字段定义与采集周期设计系统跑起来之后真正值钱的是积累下来的数据。这些数据整理成数据集可以用于各种分析。我在这里把数据集的结构定义一下方便大家直接参考。采集明细表的核心字段如下字段名类型说明采集周期timestampdatetime数据采集时间-device_idvarchar设备编号-status_codeint业务状态1运行、2待机、3停机、4报警事件/每秒spindle_loadfloat主轴负载百分比1sspindle_speedint主轴转速rpm1sfeed_speedint进给速度mm/min1sx_pos / y_pos / z_posfloat三轴绝对坐标mm5sprogram_novarchar当前程序号事件触发tool_noint当前刀具号事件触发alarm_codevarchar报警代码事件触发part_countint当前产量计数事件触发导出的CSV样例大概是这样timestamp,device_id,status_code,spindle_load,spindle_speed,feed_speed,x_pos,y_pos,z_pos,program_no,tool_no,alarm_code 2025-01-18 08:00:01,MC001,1,45.2,8000,1500,123.456,45.678,89.012,O1001,12, 2025-01-18 08:00:02,MC001,1,46.1,8000,1500,123.458,45.679,89.013,O1001,12, 2025-01-18 08:00:03,MC001,1,44.8,8000,1500,123.460,45.680,89.015,O1001,12, 2025-01-18 08:00:30,MC001,4,0.2,0,0,125.300,46.120,92.000,O1001,12,ALM1023有些读者可能奇怪为什么报警信息不是单独一张表实际采集时报警代码是数控系统内部的当前报警状态同一个时刻可能有多条报警。我为了简化处理在明细表里只存了第一个报警代码同时单独建了一张报警记录表专门记录报警发生和消除的时间点。明细表用于趋势分析报警表用于统计停机原因两张表配合使用。3.2 数据清洗的原则和步骤原始采集数据直接拿来用是不行的至少有两类脏数据必须处理。第一类是通信抖动导致的异常值。比如网络闪断又恢复时可能会读到0或者一个极大值。我的处理原则是对物理量设置合理范围主轴负载超过100%或者小于0的直接标记为无效坐标位置突变超过设定范围比如一秒内移动超过100mm明显不合理的保留原始值但添加标记字段分析时按需过滤。这里要特别注意不要轻易把异常值直接删除因为报警和异常本身就是有用的事件信号。我的做法是额外加一列quality_code0表示正常1表示可疑2表示无效。第二类是重复数据和长时间零值区间。机床长时间停机时主轴负载会一直是0这些数据本身有价值但不需要一秒一条存储。我在清洗脚本里加了降采样逻辑连续运行状态下数据按秒保留连续停机状态下压缩为5分钟一条。这样数据集体积能缩小一半以上分析精度基本不受影响。补充说明一下数据清洗不要做成一次性脚本最好是采集服务落库之后定期跑一个清洗流程把清洗结果写入单独的数据集市表。这样做的好处是原始明细数据永远保留后续发现清洗规则有问题可以重新清洗不至于原始数据已经被污染了。3.3 数据集的应用场景数据集建好了能干什么最基础的应用是OEE计算。OEE是设备综合效率公式是OEE 时间开动率 x 性能开动率 x 合格品率。时间开动率来自设备状态数据用实际运行时间除以计划开机时间性能开动率可以通过实际产量和理论节拍估算合格品率一般要对接质检数据如果暂时没有质检系统可以先用良品数除以总产量。有了秒级状态数据时间开动率和设备空闲原因可以自动统计出来比人工记录准确太多。再往深了走数据集可以做设备异常预警。主轴负载是最容易做预测的信号在正常加工循环里主轴负载曲线是有规律的。如果连续多个循环中负载曲线出现整体抬升或者周期性尖刺大概率是刀具磨损或者主轴轴承开始出问题了。我跑过一个简单的方案用历史正常数据训练一个负载基线实时数据跟基线对比偏差超过阈值时产生预警。这个用随机森林或者STL时序分解都能做数据量和质量足够的话效果不错。数据集还可以用于工艺参数分析。比如分析某一类零件加工时切削参数和主轴负载的关系找到负载过高或者震刀风险大的参数组合辅助工艺工程师优化切削参数。这类分析不需要很复杂的算法用统计工具做相关性分析和聚类就能发现不少线索。4. 部署过程中的几个关键问题4.1 网络隔离与网关部署CNC机床所在的车间网络一般不能直接连接到办公网安全上不允许工控网和办公网通常有隔离要求。但数据采集服务往往部署在办公网一侧的服务器上这就产生了一个网络打通的问题。我这次部署采用的方案是加一台工业网关网关一侧接车间设备网另一侧接办公网只开放特定端口的数据转发。网关上加白名单规则除了采集服务所在服务器的IP和端口其他流量一律拒绝。配置简单但安全性提升明显。如果车间设备本身没有以太网口需要走串口转以太网模块。选模块的时候要留个心眼有些廉价模块的透传机制不稳定数据容易丢。我用的方案是买工业级串口服务器工作模式设置为TCP ServerC#采集端作为TCP Client主动连接断线自动重连。这个模式比TCP Client模式稳定得多。4.2 时间同步与数据准确性数据采集中一个容易被忽视的问题是机床时间不一致。数控系统的时钟可能会走偏设备时间和服务器的采集时间对不上会导致时序分析出错。我的做法是采集服务不以机床时间作为数据时间戳统一以服务器本地时间作为标准。也就是读取数据时时间戳用服务器当前时间而不是设备返回的时间。这样所有设备的数据在时间轴上是对齐的。另外机房服务器配置了NTP时间同步确保服务器本身的时间是准的。还有个细节是跨天换班时的数据归属问题。有些工厂的班次不是0点切换比如白班8点到20点夜班20点到次日8点。如果只按自然日统计产量跟实际班次对不上。我这边在产量统计时支持自定义班次时段按班次时段来切分数据和计算产量跟车间考核体系保持一致。4.3 并发采集与系统性能系统上线初期只有3台设备一切顺利。后来扩到30台之后出现了两个明显问题一是采集轮询一圈的时间变长部分设备的数据延迟超过预期二是SQL Server写入压力变大偶尔出现死锁。针对延迟问题我调整了采集调度策略。原来是一台接一台串行采集改成按设备分组并行采集。分组的原则是同一品牌的设备不要放在同一组避免同时向同一类型的数控系统发起通信请求减少系统侧排队。调整后30台设备单轮采集时间从20多秒降到了5秒以内。针对数据库死锁问题主要原因是多条采集线程同时写入同一张表。我改成了单写入队列模式所有线程采集到的数据统一投递到一个内存队列由一个专用后台线程批量落库。队列消费不过来时数据先暂存在本地文件缓存避免内存占用过高。这个方案实施后死锁问题再没出现过。5. 常见问题与排查技巧5.1 通信断断续续很不稳定这是CNC采集项目里最常遇到的问题现象是采集服务日志里经常出现连接失败、读取超时或同一台设备数据时有时无。排查思路分几个方向。先Ping设备IP确认网络层面通不通再检查设备网口和交换机端口是否是百兆/千兆自适应模式有些老机床配的是百兆网卡如果接在千兆交换机上出现双工不匹配就会导致通信不稳定还要确认设备IP是否冲突车间设备IP冲突非常常见一冲突就时好时坏。如果网络没问题再看采集超时设置。FOCAS的connect超时和read超时都必须配置而且要设得合理。超时太短容易误报太长会拖慢轮询。经验值是连接超时3秒、读取超时2秒单次失败后触发重连连续3次失败才判定设备离线。5.2 数据读出来了但明显不对常见的情况是主轴负载一直是0或者一直是100。最先要确认的是读的地址对不对。发那科的系统变量地址不同版本之间可能有细微差别要以现场系统的版本为准。还有一种情况是系统里有数据但C#解析出来是负数或者乱码。这通常是数据类型匹配问题。比如系统返回的是16位有符号整数你按无符号整数解析就会出错。调试时先写一个简单的读取程序把原始返回值打出来再比对系统屏上的实际显示值确认类型和缩放系数都对上了再去做批量解析。5.3 数据库越来越大查询越来越慢采集系统跑几个月后明细表随便就有几千万行。查询时如果没有索引一个简单的按时间范围查询都可能要几十秒。解决思路是分表和索引并重。我这边是按天分表每天一张采集明细表查询时按日期定位到具体表单表数据量控制在几十万行以内查询速度很快。同时建了复合索引device_id timestamp这是最常用的查询条件执行计划能稳定走索引。分表后有一个新问题如果需要跨天查询一个月的数据代码层要做表名拼接和结果合并。我在数据访问层封装了一个GetTableNameByDate方法根据查询日期自动定位表名上层调用无感知。最后再说一个我自己的习惯所有采集字段在中转处理时尽量保留原始值不做过度加工。很多后来发现有用的分析维度都是最初采集时多保留了一个字段才有的。宁可数据多一点也不要等需要的时候再回去找历史记录那时候已经晚了。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门