基于Linux与FOCAS的Fanuc机床数据采集实战:从选型到稳定运行
1. 项目缘起与整体设计思路车间里那台Fanuc系统的加工中心已经跑了快八年平时看状态全靠老师傅站在面板前面盯指示灯刀具磨损、主轴负载异常这些事基本靠经验判断。去年产线要上数据看板第一个硬骨头就是怎么把Fanuc机床的数据稳定采出来。我前后试过三种方案踩了不少坑最后落地的是一套基于Linux工控机加Fanuc官方协议库的采集方案。这篇就把整个测试过程和实现细节摊开讲给同样在做机床数据采集的朋友一个可复现的参考。先说清楚这个项目到底在做什么。Fanuc机床数据采集核心目标是把机床内部的运行状态、加工计数、报警信息、主轴转速、进给速度、坐标位置这些参数以固定频率读取出来再通过本地网络送到上位机或者数据库。Linux在这里扮演的是采集网关的角色负责跑采集程序、做协议转换、缓存数据、断线重连。为什么不用Windows工控机一是Linux长期运行更稳二是资源占用低一台低配的工控机就能跑三是部署和远程维护方便脚本化程度高。适合看这篇内容的人我大致分三类。第一类是做设备联网、MES对接的工程师需要从底层把机床数据拿到手第二类是做工业物联网平台的技术人员关心数据源怎么接第三类是想自己动手做机床监控的爱好者或者小厂技术员预算有限但想把老设备用起来。不管你是哪一类只要机床是Fanuc系统这套思路基本都能套用。方案选型上我对比过三种主流做法。第一种是走Fanuc的FOCAS接口这是官方提供的库功能最全能读的参数最多稳定性也最好缺点是需要拿到对应版本的库文件而且对网络配置有一定要求。第二种是走OPC UA如果机床本身带OPC UA服务或者中间加一个协议转换网关也能拿到数据但老机床基本没有这个功能加网关成本高。第三种是纯靠IO信号或者宏程序输出这种方式能拿到的数据非常有限而且实时性差。综合下来FOCAS是性价比最高的选择尤其适合老机床改造。这里要解释一下为什么FOCAS是首选。FOCAS全称是Fanuc Open CNC API Specification是Fanuc官方开放的一套接口规范底层走的是以太网TCP/IP协议。机床那边相当于一个服务端采集端作为客户端去连接连接建立之后就可以按功能号去请求各种数据。它的优势在于数据颗粒度细比如主轴负载、伺服电流、刀具补偿号这些都能单独读而且读取频率可以做到毫秒级。相比之下宏程序输出只能拿到有限的几个变量还要占用机床的加工时间不适合高频采集。整体架构上我采用的是分层设计。最底层是机床通过网口接入车间交换机中间层是Linux采集网关跑FOCAS客户端程序上层是数据服务负责接收、存储和展示。采集程序用C语言写直接调用FOCAS库这样性能最好资源占用也最低。为什么不选PythonPython调用FOCAS需要做一层封装稳定性和性能都不如C直接调用而且长时间运行容易出现内存问题。当然如果你只是做原型验证Python也能凑合但生产环境我还是推荐C。网络规划这块也有讲究。Fanuc机床的以太网口通常支持两种模式一种是作为普通网口一种是作为FOCAS服务口。需要在机床参数里确认FOCAS功能是否开启对应的端口号一般是8193。采集网关和机床要在同一个网段或者通过路由可达。我建议单独划一个VLAN给设备网避免和办公网混在一起减少广播风暴和IP冲突的风险。IP地址规划上机床用固定IP采集网关也用固定IP不要用DHCP否则断线重连的时候找不到设备。注意在动网络配置之前一定要先确认机床当前有没有在用网口做其他用途比如程序传输或者远程监控。有些机床只有一个网口改了配置可能影响原有功能。2. 核心细节解析与实操要点2.1 FOCAS库的获取与版本匹配FOCAS库的获取是整个项目的第一道门槛。Fanuc官方并不直接对外提供库文件下载通常需要通过机床代理商或者系统集成商渠道获取。库文件分32位和64位两个版本还有不同的版本号比如Fwlib32.dll对应WindowsLinux下则是libfwlib32.so。这里有个关键点库的版本要和机床的CNC系统版本匹配太新的库连老系统可能连不上太老的库又读不到新系统的某些参数。我实际测试中遇到过一个问题手头拿到的库是较新版本连一台老系统的机床时连接能建立但读某些参数返回错误码。后来换了一个稍老版本的库问题就解决了。所以建议在项目初期多准备几个版本的库遇到连接问题先换库试试。另外Linux下的库文件需要放到系统库路径比如/usr/lib或者/usr/local/lib然后执行ldconfig刷新缓存否则程序运行时会报找不到库。# 将库文件复制到系统库目录 sudo cp libfwlib32.so /usr/local/lib/ # 刷新库缓存 sudo ldconfig # 验证库是否被识别 ldconfig -p | grep fwlib编译的时候要链接这个库Makefile里加上-lfwlib32。如果是64位系统但库是32位的还需要安装32位兼容库否则编译能过但运行会报错。这个坑我踩过当时折腾了半天才发现是架构不匹配。2.2 连接建立与心跳维持FOCAS的连接建立流程其实不复杂核心就是调用cnc_allclibhndl3这个函数传入机床IP、端口号、超时时间返回一个句柄。句柄是后续所有操作的入口读数据、写参数、查报警都要靠它。端口号默认是8193但有些机床可能改过需要在机床参数里确认。连接建立之后不能就一直放着不管。Fanuc机床那边有连接超时机制如果长时间没有数据交互可能会主动断开。所以采集程序里要加心跳定期调用一个轻量级的函数比如读一下机床状态或者系统时间保持连接活跃。我一般设置心跳间隔为5秒太频繁没必要太稀疏又容易断。#include fwlib32.h unsigned short handle; short ret; // 建立连接 ret cnc_allclibhndl3(192.168.1.100, 8193, 10, handle); if (ret ! EW_OK) { printf(连接失败错误码: %d\n, ret); return -1; } // 心跳读取机床状态 ODBST stat; ret cnc_statinfo(handle, stat); if (ret ! EW_OK) { printf(心跳失败准备重连\n); cnc_freelibhndl(handle); // 触发重连逻辑 }重连逻辑是必须的。车间环境里网络抖动、机床断电、交换机重启都是常事程序不能一断就挂。我的做法是加一个状态机连接断开后进入重连等待每隔10秒尝试一次连续失败超过一定次数就告警。重连成功后要重新初始化所有需要读取的数据项避免句柄失效导致后续读取全部报错。2.3 数据项选择与读取频率设计Fanuc能读的数据项非常多但并不是所有都需要采。我一般根据业务需求来选常见的分几类。状态类包括运行模式、自动运行状态、急停状态、报警状态计数类包括加工件数、运行时间、切削时间轴类包括各轴坐标、主轴转速、进给速度、主轴负载报警类包括当前报警号和报警信息。读取频率要根据数据变化速度来定。状态类和报警类变化不频繁1秒读一次足够轴坐标和转速变化快可以做到100毫秒甚至更快计数类变化慢5秒读一次也没问题。但要注意读取频率越高对机床通讯资源的占用越大有些老机床的通讯处理能力有限频率太高可能导致响应变慢甚至丢包。我实际测试下来一台机床同时读20个数据项100毫秒周期CPU占用大概在5%左右网络流量很小因为FOCAS协议本身比较精简。但如果同时采集多台机床就要考虑采集网关的负载必要时做多线程或者多进程分发。数据类别典型数据项建议读取周期备注状态类运行模式、急停、报警状态1000ms变化不频繁计数类加工件数、运行时间5000ms变化慢轴类坐标、转速、负载100-200ms变化快按需调整报警类报警号、报警信息1000ms报警发生时需及时捕获参数类刀具补偿、工件坐标按需读取不需要周期采集提示读取频率不是越高越好先确认机床的通讯能力再逐步调整。我一般从500毫秒开始试稳定后再往下压。2.4 数据解析与格式转换FOCAS返回的数据大多是结构体或者数组需要做解析才能变成可读的数值。比如读主轴转速返回的是一个结构体里面包含实际转速和设定转速单位是转每分钟。读坐标返回的是各轴的机械坐标和相对坐标单位是毫米或者英寸取决于机床参数设置。解析的时候要注意字节序和数据类型。FOCAS库已经做了大部分转换但有些字段需要自己处理。比如报警信息返回的是字符串需要按机床的语言设置来解析有些机床是中文有些是英文。还有时间戳FOCAS返回的是机床内部时间和采集端时间可能有偏差建议在采集端统一打时间戳避免时间混乱。数据格式上我一般转成JSON再往上送这样上位机好处理。JSON的字段名要统一规范比如spindle_speed、feed_rate、axis_x_position方便后续对接。如果数据量很大可以考虑用二进制或者MessagePack减少网络开销。// 读取主轴转速示例 ODBSPN spn; ret cnc_rdspindle(handle, 1, spn); if (ret EW_OK) { printf(主轴实际转速: %d\n, spn.data[0]); printf(主轴设定转速: %d\n, spn.data[1]); }3. 实操过程与核心环节实现3.1 环境准备与依赖安装Linux采集网关我选的是Ubuntu Server 20.04这个版本长期支持社区资源多遇到问题好查。硬件配置不用太高我用的是一台低功耗工控机四核处理器、4G内存、64G固态硬盘跑采集程序绰绰有余。如果车间环境温度高、粉尘大建议选无风扇工控机可靠性更好。系统装好之后先做基础配置。关闭不必要的服务比如图形界面、蓝牙、打印服务减少资源占用。设置静态IP和机床同网段。配置防火墙只开放必要的端口采集程序本身不需要对外提供服务所以入站规则可以全部拒绝出站允许到机床IP和上位机IP。# 设置静态IPnetplan方式 sudo nano /etc/netplan/01-netcfg.yaml network: version: 2 ethernets: eth0: addresses: - 192.168.1.200/24 gateway4: 192.168.1.1 nameservers: addresses: [192.168.1.1] # 应用配置 sudo netplan apply依赖库方面除了FOCAS库本身还需要安装编译工具链gcc、make、gdb这些。如果要用到数据库或者消息队列再按需安装。我一般还会装一个tcpdump方便抓包排查网络问题。3.2 采集程序框架搭建采集程序我采用多线程设计主线程负责连接管理和状态监控采集线程负责周期读取数据发送线程负责把数据送到上位机。线程之间用环形缓冲区传递数据避免锁竞争。为什么用环形缓冲区因为采集频率高如果每次读取都直接发送网络抖动会导致采集线程阻塞影响读取周期。有了缓冲区采集线程只管往里面写发送线程慢慢发两边解耦。程序启动流程是这样的先加载配置文件读取机床IP、端口、数据项列表、读取周期这些参数然后初始化FOCAS库建立连接连接成功后启动采集线程和发送线程主线程进入循环定期检查连接状态和线程健康度。如果连接断开主线程负责重连重连成功后通知采集线程恢复工作。// 简化的主循环框架 int main() { load_config(config.json); if (init_focas() ! 0) { printf(FOCAS初始化失败\n); return -1; } if (connect_machine() ! 0) { printf(机床连接失败\n); return -1; } start_collect_thread(); start_send_thread(); while (running) { check_connection(); check_thread_health(); sleep(1); } cleanup(); return 0; }配置文件我用JSON格式方便修改和扩展。里面定义每台机床的IP、端口、需要采集的数据项、每个数据项的读取周期。这样增加机床或者调整数据项的时候不用改代码改配置就行。3.3 关键数据读取实现状态读取是最基础的功能。cnc_statinfo函数返回机床的当前状态包括运行模式、自动运行状态、急停状态、报警状态等。运行模式有编辑、自动、MDI、手动等自动运行状态有停止、暂停、运行中。这些状态对于判断机床是否在加工非常关键。ODBST stat; ret cnc_statinfo(handle, stat); if (ret EW_OK) { // stat.aut 自动运行状态 // stat.run 运行状态 // stat.motion 运动状态 // stat.emergency 急停状态 // stat.alarm 报警状态 }坐标读取用cnc_rdposition或者cnc_rddynamic2前者读绝对坐标后者读动态坐标。动态坐标包含相对坐标、绝对坐标、机械坐标还有剩余移动量。我一般用cnc_rddynamic2一次能拿到更多信息。读取的时候要注意轴号Fanuc的轴号从1开始对应X、Y、Z、A等轴。ODBDY2 dynamic; ret cnc_rddynamic2(handle, -1, sizeof(ODBDY2), dynamic); if (ret EW_OK) { for (int i 0; i dynamic.datano; i) { printf(轴%d 绝对坐标: %d\n, i1, dynamic.pos[i].absolute); printf(轴%d 相对坐标: %d\n, i1, dynamic.pos[i].relative); } }主轴和进给读取用cnc_rdspindle和cnc_rdfeedrate。主轴数据包括实际转速、设定转速、负载、转速倍率等。进给数据包括实际进给速度、设定进给速度、进给倍率。这些数据对于分析加工状态和优化工艺很有价值。报警读取用cnc_alarm2返回当前报警号和报警信息。报警号是数字报警信息是字符串。我一般把报警号和信息一起存下来方便后续做报警统计和分析。报警读取要注意有些报警是瞬时的如果读取周期太长可能漏掉所以报警类数据建议1秒读一次。3.4 数据发送与断线缓存数据发送我走的是TCP长连接上位机开一个服务端采集程序作为客户端连上去。为什么不用MQTTMQTT当然也可以但需要额外部署Broker对于小规模场景来说直接TCP更简单。如果上位机是云平台那MQTT或者HTTP更合适。发送的数据格式是JSON每一条数据包含时间戳、机床ID、数据项名称、数值。上位机收到后解析入库。为了减少网络开销我一般做批量发送比如攒够100条或者间隔1秒发一次而不是每读一条就发一条。断线缓存是必须的。网络不可能永远稳定如果发送失败数据不能丢。我的做法是在本地开一个环形缓冲区发送失败的数据先存进去等连接恢复后再补发。缓冲区大小根据数据量和断线时长来定我一般设置能缓存10分钟的数据。如果断线超过10分钟旧数据就被覆盖优先保证最新数据。// 发送线程伪代码 while (running) { if (is_connected()) { batch buffer_read_batch(100); if (send_data(batch) ! 0) { buffer_write_back(batch); // 发送失败写回缓冲区 reconnect(); } } else { reconnect(); } sleep(1); }注意缓冲区要用环形结构避免内存无限增长。写满之后覆盖最旧的数据保证程序不会因为内存耗尽而崩溃。3.5 测试验证与数据核对程序写完只是第一步测试验证才是重头戏。我一般分三步走。第一步是单机测试连一台机床读几个简单数据项确认能连上、能读到数。第二步是对比测试把采集到的数据和机床面板上显示的数据做对比确认数值一致。第三步是压力测试长时间运行观察稳定性。对比测试的时候要注意机床面板显示的数据和FOCAS读到的数据可能有细微差异比如坐标显示的小数位数不同转速显示有取整。这些是正常的只要在合理范围内就行。如果差异很大就要检查是不是读错了数据项或者单位换算有问题。压力测试我一般跑72小时观察几个指标程序内存占用是否稳定、连接是否频繁断开、数据是否有丢失、CPU占用是否正常。我遇到过一个问题程序跑了两天之后内存缓慢增长最后发现是某个结构体没有释放。这种问题在短时间测试中很难发现必须长时间跑才能暴露。测试阶段测试内容持续时间关注指标单机测试连接、读取基本数据1小时连接成功率、数据正确性对比测试与面板数据核对2小时数值一致性、单位正确性压力测试长时间稳定运行72小时内存、CPU、连接稳定性断线测试模拟网络中断反复多次重连速度、数据完整性4. 常见问题与排查技巧实录4.1 连接失败类问题排查连接失败是最常见的问题表现是cnc_allclibhndl3返回非零错误码。排查思路从物理层往上走。先ping一下机床IP确认网络通不通。如果不通检查网线、交换机端口、IP配置。如果通再telnet一下8193端口确认端口开放。如果端口不通检查机床的FOCAS功能是否开启端口号是否被改过。错误码也能提供线索。比如EW_SOCKET表示套接字错误通常是网络问题EW_HANDLE表示句柄错误可能是连接数超了EW_PARAM表示参数错误可能是IP或者端口传错了。我整理了一个常见错误码对照表方便快速定位。错误码含义常见原因解决方法EW_OK成功--EW_SOCKET套接字错误网络不通、端口未开检查网络和端口EW_HANDLE句柄错误连接数超限、句柄未初始化检查连接数重新初始化EW_PARAM参数错误IP或端口错误核对配置EW_BUSY忙机床通讯繁忙降低读取频率稍后重试EW_DATA数据错误数据项不支持检查数据项是否有效还有一个坑是机床的连接数限制。Fanuc机床通常只允许有限个客户端同时连接比如2个或者4个。如果采集程序没有正常释放句柄连接数被占满新的连接就建不上。所以程序退出的时候一定要调用cnc_freelibhndl释放句柄异常退出的时候也要有清理机制。4.2 数据读取异常类问题数据读取异常的表现是返回错误码或者读到的数值明显不对。常见原因有几个。一是数据项不支持有些老系统不支持某些新功能号读的时候会返回错误。二是读取频率太高机床处理不过来返回忙。三是句柄失效连接断了但程序没发现继续用旧句柄读。排查的时候先降低读取频率试试如果问题消失那就是频率太高。如果还不行换一个数据项读确认是个别数据项的问题还是全部数据项的问题。如果是个别数据项查一下机床手册确认这个数据项在当前系统版本下是否支持。如果是全部数据项那大概率是连接问题检查连接状态。数值不对的情况也要注意。比如坐标读出来是整数但实际有小数这是因为FOCAS返回的是脉冲数需要除以分辨率才能得到实际值。分辨率在机床参数里一般是1微米或者0.1微米。如果不做换算读出来的数就是错的。这个坑我踩过当时读出来的坐标是实际值的1000倍查了半天才发现是单位问题。// 坐标换算示例 // 假设分辨率为1微米FOCAS返回的是脉冲数 double actual_position (double)dynamic.pos[0].absolute / 1000.0; printf(X轴实际坐标: %.3f mm\n, actual_position);4.3 网络与稳定性问题网络问题在车间环境里很常见。车间里大功率设备多电磁干扰强网线如果屏蔽不好容易丢包。我建议用工业级屏蔽网线交换机也用工业级的电源做滤波。如果条件允许光纤收发器更稳抗干扰能力更强。程序稳定性方面除了前面说的内存泄漏还有一个常见问题是线程死锁。多线程程序如果锁用得不好容易出现死锁表现是程序卡住不动。排查死锁可以用gdb attach到进程看各个线程的调用栈。预防死锁的办法是尽量减少锁的使用能用无锁数据结构就用无锁的必须用锁的时候注意加锁顺序一致。还有一个问题是时间同步。采集网关和上位机的时间如果不一致数据的时间戳就会乱。建议在采集网关上装NTP客户端定期和上位机或者时间服务器同步。如果车间没有时间服务器至少保证采集网关和上位机之间同步。提示程序里加日志很重要但日志级别要控制好。调试阶段可以打详细日志生产环境只打错误和警告避免日志文件把磁盘写满。4.4 多机床采集的扩展问题单台机床跑通之后下一步就是多台机床同时采集。这时候要考虑几个问题。一是采集网关的性能一台低配工控机跑10台机床的采集程序CPU和内存占用会明显上升需要评估是否够用。二是网络带宽10台机床的数据量虽然不大但如果都走同一个网口要注意带宽是否足够。三是程序的并发模型是每台机床一个进程还是一个进程多线程处理所有机床。我试过两种方式。每台机床一个进程的方式隔离性好一台机床出问题不影响其他机床但进程间通信和统一管理麻烦。一个进程多线程的方式管理方便但一个线程出问题可能影响整个进程。最后我选的是折中方案一个进程管理多台机床每台机床一个采集线程线程之间独立主线程负责监控和重连。这样既方便管理又有一定的隔离性。多机床场景下配置文件的组织也要注意。我一般按机床分组每台机床一个配置块包含IP、端口、数据项、周期。程序启动的时候遍历配置为每台机床创建采集线程。如果某台机床连接失败不影响其他机床只是记录错误并定期重试。扩展方式优点缺点适用场景单进程单线程简单一台出问题全挂1台机床单进程多线程管理方便线程间可能相互影响2-10台机床多进程隔离性好管理复杂10台以上机床5. 实操心得与后续扩展方向这套方案在车间里跑了大半年整体稳定性还不错平均无故障时间能到一个月以上。中间遇到过几次断线都是网络问题重连机制都正常工作了没有丢数据。采集频率方面我最终定的是状态类1秒、轴类200毫秒、计数类5秒这个配置在机床通讯能力和数据实时性之间取得了平衡。有几个经验我觉得值得分享。第一FOCAS库的版本一定要和机床匹配不要盲目用最新版。第二读取频率不要一开始就设很高先低后高逐步调整。第三断线缓存一定要做而且要做环形覆盖避免内存无限增长。第四日志要打但不要打太多生产环境只打关键信息。第五长时间测试不能省很多问题只有跑久了才会暴露。后续扩展方面有几个方向可以考虑。一是把数据接入时序数据库比如InfluxDB或者TDengine方便做历史查询和趋势分析。二是加规则引擎对采集到的数据做实时判断比如主轴负载超过阈值就告警。三是和MES或者ERP对接把加工计数和工单关联起来。四是做可视化看板让车间管理人员能实时看到机床状态。如果让我重新做一遍我会在架构上做一些调整。比如把采集程序做成插件式数据项读取用配置驱动增加新数据项不用改代码。再比如把数据发送做成可插拔的支持TCP、MQTT、HTTP多种协议适应不同的上位机环境。还有就是加一个本地Web界面方便现场调试和查看状态不用每次都SSH上去看日志。最后说一个实际使用中的小技巧。机床的IP地址有时候会因为维护或者更换硬件而改变如果程序里写死了IP改起来很麻烦。我的做法是在配置文件里用机床名称做标识IP地址单独维护一个映射表程序启动的时候根据名称查IP。这样即使IP变了只要改映射表就行不用动主配置。另外我还会在程序里加一个自动发现功能扫描网段内所有开放8193端口的设备自动识别Fanuc机床减少手工配置的工作量。这套方案不是什么高深的技术核心就是稳定和可靠。工业现场的环境比实验室复杂得多网络抖动、电磁干扰、设备老化都是常态程序必须能扛住这些。我在实际项目里最大的体会是不要追求功能多先把基础的数据采集做稳再考虑扩展。数据采不准、采不全后面做再多分析都是空中楼阁。