
1. 项目全貌与需求拆解1.1 为什么会有openrig做工业设备监控这么多年一个绕不开的痛点就是每套系统都是定制项目。钻机、修井机、泥浆泵这些大型装备表面上看都是标准设备但到了现场一摸底每个井队的仪表配置都不一样有的用老式模拟量传感器有的用Modbus RTU采集器还有的上了OPC UA网关。以前的做法是给每个现场单独写一套采集程序来一台设备就写一遍联调既费人力又难维护。开源的通用监控平台倒是不少但大多是围绕IT基础设施设计的拿到工业现场用要改很多东西。openrig这个项目我最初的定位很明确做一套专门面向钻机装备的开放数据采集与状态监控框架。它不绑定具体硬件品牌不预设固定点位表而是提供一套从传感器到数据库再到可视化的完整链路让现场工程师能通过改配置文件而不是改代码就能接入一个新的井场。项目的名字也很直白open代表开放rig就是钻机合起来就是开放的钻机监控。如果你正在做装备物联网改造、设备数据上云或者只是想给车间的几台老设备做个数字化台账openrig的思路都可以直接借鉴。1.2 核心需求与目标用户在动手写代码之前我先梳理了几个硬性需求这些需求直接决定了架构选型的方向协议多样性要兜得住现场既有传统4-20mA模拟量也有Modbus RTU/TCP、PLC的S7协议还可能有个别设备只提供HTTP接口。采集层必须能做到一站接入不能为了一个特殊协议单独搭一套服务。点位配置要能动态生效井队换设备、加传感器是家常便饭如果每改一个点位都要改代码重新发布项目根本运营不下去。点位表必须外置最好用表格或配置文件就能维护。断网不能丢数据井场网络环境不稳定卫星链路经常抖动。边缘侧必须有一层本地缓存网络恢复后再自动补传。报警要能分级不是所有越限都要弹窗设备轻度波动只需要记录严重到可能停机才需要推送消息。报警规则需要可配置。展示层要快、要直观现场值班室看的是实时状态和趋势办公室看的是日报统计数据展示要开箱即用别让用户自己写前端。这套需求下来适合参考openrig的读者大致有三类一是装备制造企业的售后技术团队需要给客户提供设备运行数据报告二是油田、矿山现场的自动化工程师想把分散的传感器统一收拢到一个平台三是做工业物联网创业的开发者需要一个稳定的底座作为二次开发基础。1.3 为什么不用现成的商用平台市面上其实有不少成熟的组态软件和工业物联网平台用起来省心但有两个绕不过去的坎一是按点位收费一个中大型井场动辄几百个点位年费不是小数目二是闭源黑盒后期想接入一个平台没支持的自定义协议只能走厂商排期现场根本等不起。openrig选择走开源路径核心价值在于可掌控。协议解析可以自己扩展数据库结构完全开放告警规则可以按客户要求随意调整。当然开源不等于所有东西都要自己造轮子底层仍然依赖成熟的生态组件比如用EMQX做消息接入、用TDengine存时序数据、用Grafana出图表这些后来都被证明是靠谱的选择。提示如果项目周期极短、点位少于50个、现场没有特别奇怪的协议用现成商用平台反而效率更高。openrig的价值在设备数量多、协议杂、需要长期演进的情况下才真正显现。2. 系统架构与数据链路设计2.1 整体拓扑数据怎么从传感器流到大屏把openrig拆开看数据流是一条非常清晰的单向链路传感器信号 - 采集网关 - 消息总线 - 数据处理 - 数据库 - API/可视化。最底层的传感器和执行器通过有线或无线方式接到采集网关。网关这里有两种角色如果现场已经有成熟的PLC或RTUopenrig负责从这些控制器里取数如果是什么控制器都没有的裸传感器网关就直接承担IO采集任务。取到的原始数据统一转成标准消息格式推送到内部的EMQX消息总线。这一层是整个架构的关键解耦点往后不管是做实时计算还是做历史存储都只是订阅/消费消息的问题不会反过来影响采集端。数据处理服务从消息总线拉取数据后做清洗、单位换算、阈值判断再决定写库还是触发告警。最终Grafana从TDengine查询数据做可视化同时提供一个RESTful API给第三方系统调取。这张图的重要之处在于每个环节都有明确的界面谁出问题排查谁互不干扰。2.2 采集层设计一个驱动框架吃掉所有协议采集层是openrig里工程量最大的模块也可以说是最体现设计功力的地方。我没有为每种协议单独写一个采集程序而是抽象了一个驱动框架每个协议实现统一接口对外暴露start、stop、read三个方法框架负责管理驱动的生命周期、数据上报速率和异常重连。# 采集驱动统一接口示例 class BaseDriver: def __init__(self, config): self.config config self.running False def start(self): raise NotImplementedError def stop(self): raise NotImplementedError def read(self): raise NotImplementedErrorModbus RTU驱动、S7驱动、OPC UA驱动、HTTP轮询驱动都继承这个基类。新增一种协议时只需要实现这三个方法再写一个对应的配置文件加载器就能被框架直接管理。这样设计的主要原因在于采集层是这类项目变更最频繁的部分把变化隔离在驱动内部主体框架就能保持稳定。另一个设计细节是上报速率不能按秒一刀切。温度、液位这种缓变量5秒采一次已经足够振动、泵压这种需要捕捉瞬态变化的量可能需要500毫秒采一次。openrig的点位配置里有一个独立的sampling字段驱动框架按照点位配置分组调度保证慢变量不浪费带宽快变量不丢失细节。2.3 中间消息层为什么选EMQX而不是自研队列采集端和数据端之间放一个消息中间件这个决定是我在整个项目里最坚持的没有之一。早期版本为了部署简单我图省事让采集服务直接写数据库结果踩了大坑一旦数据库连接不稳定采集线程全被阻塞一批数据直接丢在内存里。后来改成所有数据先进消息队列采集服务只负责往队列里推数据处理服务再从队列里拉两边彻底脱钩哪怕数据库重启消息也原封不动地积压在队列里恢复后继续消费。具体中间件选型时我在RabbitMQ、Kafka、EMQX之间做过对比。最终选EMQX是基于三个考虑一是它原生支持MQTT协议而MQTT是工业网关最普遍的对外协议采集服务直接订阅远端MQTT数据源非常顺滑二是它支持共享订阅可以方便地做消费者水平扩展三是它在嵌入式设备上的生态比较成熟很多传感器厂家出厂就支持MQTT上报。下表是当时的选型对比给同样在纠结的人一个参考能力维度RabbitMQKafkaEMQX工业设备接入友好度一般需适配AMQP差客户端较重高原生支持MQTT断线消息积压能力中等强基于分区日志强支持离线消息边缘部署资源占用中等较高低适合小机器水平扩展一般极强强支持集群注意选择EMQX之后记得开启保留消息和离线消息功能。现场网关经常掉线重连没有这两个功能设备一断一合期间的数据就找不回来了。2.4 存储方案时序数据与时序数据库的匹配工业设备监控产生的数据99%是带有时间戳的数值序列这类数据用传统关系型数据库存会很尴尬写入频率高导致IO瓶颈数据量大后查询变慢还要自己实现数据降采样和过期清理。openrig选择TDengine来做时序存储。第一眼看中它是因为超级表模型非常契合钻机场景——每台钻机建一张超级表不同井队的设备作为子表挂在其下查询时可以自动按时间维度做聚合分片。比如一台钻机上上百个点位写进同一张超级表的不同子表里按时间段拉取对比趋势时一条SQL就够了性能和写法都省心。点位实时值并没有直接写时序库而是放在Redis里做最新值缓存。因为大屏展示页会高频轮询每个点位的当前值如果每次都去查TDengine虽然也能查到但会把时序库的查询压力拉高。用一个主动推送的机制把最新值更新到Redis前端界面直接读Redis实时性有保障历史查询才走时序库。3. 核心功能模块的落地实现3.1 点位配置管理Excel驱动一切点位配置是openrig最贴近现场的一项设计。现场自动化工程师不一定熟悉编程但几乎都会用Excel。为了方便他们独立维护点位表openrig设计了一套基于Excel的配置方式一张点位清单包含区域、设备、点位名称、数据类型、驱动类型、寄存器地址、采样周期、报警阈值等字段启动加载时自动解析生成对应的采集任务和存储结构。# 配置目录结构示意 config/ ├── devices/ │ ├── drilling_rig_01.yaml │ └── mud_pump_01.yaml ├── drivers/ │ ├── modbus_rtu.yaml │ └── s7_comm.yaml └── alarm_rules/ └── default_rules.yaml之所以用YAML作为Excel机制的底层载体是因为YAML天然适合表达层级关系一个点位有哪些属性、属于哪个分组、挂在哪个设备下一目了然比Excel更简洁。实际工作流是现场工程师在Excel里维护点位表改完后通过Web上传页面导入后端校验合格后自动生成设备配置实现热更新不需要重启服务。这里有个实际踩过的坑点位名称一定要设置成英文字段加中文描述双份字段。数据库列名和映射用英文界面上显示用中文。如果直接在数据库字段里用中文后期做报表、对接第三方系统时编码问题会搞得人想砸电脑。3.2 数据解析与质量处理数据从驱动读到到最终写入时序库中间要经过一个数据质量管道。这个管道包含三个环节格式标准化、单位换算、无效值剔除。格式标准化解决的是同一物理量不同表达的问题。比如压力这个参数有的设备上报的单位是kPa有的设备是MPa有的甚至直接给一个数字量纲写在描述里。管道里内置一个单位字典按点位配置对原始值做换算统一转成国际单位存储。无效值剔除主要针对三类常见情况传感器断线时返回的负极大值、接线松动导致的数据跳变、设备停机时上报的无意义数据。剔除的逻辑不是简单的丢弃而是给数据打质量标签正常数据标记为good异常标记为bad或uncertain。这个标签会一直跟随数据进入时序库查询时可以根据需要过滤。这样设计的原因是有些时候现场的坏数据恰恰是判断设备故障的线索简单丢弃反而会让后续分析失去依据。3.3 告警引擎分级分渠道推送告警模块的触发逻辑其实不复杂难点在减少无效打扰。一开始我设计成超限就推送结果现场一天能收几百条微信消息值班长直接把通知关了真正的险情反而被淹没。升级后的告警规则引入两个概念持续时间与恢复延迟。持续时间是指越限状态必须维持多少秒才触发告警用于屏蔽瞬时尖峰恢复延迟是指告警解除后至少要等待多久才能再次触发同一告警防止设备在阈值边缘反复震荡导致告警风暴。-- 告警规则表示例 -- threshold: 报警阈值 -- duration: 持续时间单位秒 -- delay: 恢复延迟单位秒 CREATE TABLE alarm_rule ( id INTEGER PRIMARY KEY, device_id VARCHAR(64), point_id VARCHAR(64), alarm_level VARCHAR(16), operator VARCHAR(8), threshold FLOAT, duration INT DEFAULT 5, delay INT DEFAULT 120 );告警级别按严重程度分为三级提示、警告、紧急。提示级只记录不推送用于趋势关注警告级推送值班室和班组群紧急级直接触发电话语音告警和现场声光报警联动。分级之后值班人员的精力才真正聚焦在高风险事件上。3.4 可视化看板不只是画曲线openrig的可视化层基于Grafana二次开发但不只是换个Logo做几个面板那么简单。针对钻机场景我在看板上做了三块核心内容第一块是设备总览用一张钻机简化示意图标注关键点位哪个位置传感器有问题直接在图上变色闪烁比翻列表直观得多。第二块是报警统计按班组、按时间段、按设备类型三个维度展示报警次数和处置时长这个看板是我在项目上线后应现场管理要求加的因为考核人员需要量化每个班组对报警的响应效率。第三块是运行趋势对比支持任意选两台设备把相同点位的时间序列叠加显示这个功能在比对两台同型号泥浆泵的运行状态时非常好用。数据返回路径上我优化了两个点一是历史趋势查询的冷数据走TDengine的预计算聚合默认按原始精度存储按分钟、小时、天自动降采样二是页面上的实时卡片都走WebSocket推送不用轮询浏览器端负载更低数据刷新也几乎无延迟。4. 实际操作从零部署一套openrig平台4.1 服务器选型与环境准备以3台钻机、大约600个点位接入为例我建议的最低配置是4核8G内存100G SSD硬盘单节点部署全部服务。为什么是这个规格因为EMQX和TDengine本身都是轻量级服务内存大头其实在Grafana和数据处理服务上600个点位按5秒一个采集周期算消息吞吐量也才每秒几百条单节点完全扛得住。操作系统建议用Ubuntu 22.04 LTS或Debian 12原因是这两个系统的软件源里能直接装到较新的Docker版本后面部署容器化服务省心。openrig的部署是基于Docker Compose的所有服务都容器化主机上只需要装好Docker和Compose插件。# 安装基础环境以Ubuntu为例 sudo apt update sudo apt install -y docker.io docker-compose-plugin sudo systemctl enable --now docker提示生产环境一定不要用Docker的host网络模式跑TDengine会绕开容器网络隔离端口冲突和权限问题会让你浪费大量排查时间。建议用compose文件里的网络配置让服务间通过服务名相互访问。4.2 配置文件详解整个openrig的部署配置集中在docker-compose.yaml和.env两个文件里。docker-compose.yaml定义了6个核心服务下面逐个说明配置意图emqx消息中间件映射1883端口用于采集网关接入18083为Web管理界面开启离线消息和持久化会话。tdengine时序数据库数据目录挂载到宿主机磁盘必须持久化否则容器重建就丢光历史数据。redis最新值缓存和告警去重使用追加appendonly配置确保重启不丢。collector采集服务既是MQTT消费者也是Modbus/PLC采集发起端环境变量里指定EMQX地址。processor数据处理服务消费数据消息执行清洗、单位换算、告警判定写时序库。grafana可视化服务内置数据源和看板模板第一次启动自动加载配置。# docker-compose.yaml 核心片段 services: emqx: image: emqx/emqx:5.4.1 environment: EMQX_ALLOW_ANONYMOUS: true EMQX_PERSISTENT_SESSION: true ports: - 1883:1883 - 18083:18083 volumes: - emqx_data:/opt/emqx/data tdengine: image: tdengine/tdengine:3.2.3.0 volumes: - td_data:/var/lib/taos environment: TAOS_NUM_OF_CNODES: 1 grafana: image: grafana/grafana:10.2.0 environment: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD} volumes: - grafana_data:/var/lib/grafana ports: - 3000:3000.env文件里存放公共配置包括数据库账号密码、告警邮箱、企业微信机器人Webhook地址还有平台自身的监听端口。密码和密钥建议用环境变量注入而不是写死在Compose文件里防止配置信息泄露到代码仓库。4.3 数据接入与点位导入流程平台跑起来之后最关键的步骤是接入第一台设备的点位。首先在设备管理页面录入钻机的基本信息和通信参数比如IP、端口、从站地址。然后准备点位Excel模板模板表头包含区域编码、设备编码、点位名称、点位编码、驱动类型、寄存器地址、数据类型、单位、采样周期、告警级别、告警阈值、持续时间。这些字段不要随意改后端校验器会做字段名严格匹配。上传之后立即校验三步驱动类型是否在已安装列表里、寄存器地址格式是否符合协议规则、告警阈值与数据类型是否兼容。校验通过后点下发配置按钮配置会自动分发到采集服务并热加载生效。验证接入是否成功的标志不仅仅是能看到实时数值更重要的是看两条记录一条是采集驱动日志里的点位加载成功一条是数据处理日志里的首次数据落库。看到这两条就说明链路彻底打通了。接着去Grafana的探针页面确认数值的物理合理性——这个步骤很多新手容易漏掉监控平台跑起来显示一个爆炸数据往往不是设备真的爆炸而是点位映射错位或量纲没换算。5. 部署上线后的常见问题与排查实录5.1 设备离线不是真离线是心跳机制不够健壮项目上线第一周就遇到一个诡异问题大屏上总有一两台设备显示离线但现场人员过去看设备明明在正常运转本地仪表盘数值也在跳动。排查后发现根因不在设备侧而在openrig的心跳判断机制。最初的判断标准是超过30秒没有收到该设备的任何消息就判定离线但现场有部分点位上报周期原本就设置为60秒慢变量点位多的设备整体数据上报频率自然偏低于是被误判。修复方案是区分点位活跃和设备活跃两个概念。只要该设备下任意一个点位在30秒内有数据就认为设备在线只有全部点位都沉默超过心跳阈值才判定离线。同时在采集网关侧增加一个设备级心跳消息由网关每隔10秒主动上报不依赖业务点位数据。这样既避免误报又能真正确认网络链路是否通畅。5.2 数据曲线频繁跳尖峰是滤波参数没调部署初期现场反馈泥浆泵的压力曲线每隔十几分钟就出现一个尖锐脉冲从正常的25MPa瞬间跳到35MPa又落回。第一反应是压力变送器质量问题换了一个新变送器后现象依旧。后来排查到问题出在Modbus采集驱动的时间戳逻辑上。驱动取数时使用了采集程序处理时刻作为数据时间戳而Modbus RTU链路上如果一条报文在传输层重发了两次采集程序可能把这次重发的旧数据也当成新数据上报了旧数据的真实发生时刻跟程序处理时刻对不上写入时序库后表现为一个虚假的瞬时尖峰。修复方法是在驱动里引入数据版本号机制。每台从站设备维护一个上次成功获取值的寄存器和时间戳新上报的数据必须同时满足寄存器地址匹配和数值变化时间有效两个条件才被接受否则丢弃并记一条质量标签。同时在前端展示滤波器上配置一阶惯性滤波设置时间常数为0.5秒双管齐下后曲线恢复平滑。5.3 告警风暴把值班手机震麻了有一口井在交接班期间连续收到170多条报警消息值班手机什么都干不了。打开告警日志一看是同一个低压告警点在阈值线上下反复穿越触发、恢复、再触发几秒钟一轮循环。告警引擎里的持续时间和恢复延迟已经生效了为什么还会这样后来发现恢复延迟参数默认是120秒但对于某些快变点位设备本身在阈值附近波动恢复还没到时间又冲上去计数器永远在重置。针对这类点位我把报警规则做了自动化抑制当一个点位在5分钟内触发超过5次同类告警自动进入静默模式只记录事件不推送消息直到连续10分钟数据恢复正常才解除静默。本质是加了一个简单的告警熔断机制而不是一味调大延迟。上线后告警量直接降了两个数量级同时真正重要的事故报警一条没漏。5.4 历史数据跨天查询时段错乱值班长报告一个很影响信任度的BUG前一天23点到次日凌晨1点的数据在日报曲线里显示到了8点之后的位置。检查数据库存储后发现时间戳本身没有问题问题出在展示层的时区配置。TDengine服务默认使用系统时区而Grafana默认使用UTC时区。数据写入TDengine时带的是北京时间的时间戳但显示时被Grafana按UTC转换整整偏移了8小时。修复方式是统一约定所有服务一律使用UTC时间戳存储展示层由Grafana自身的时区设置做转换。把Grafana的默认时区改成Asia/ShanghaiTDengine容器挂载宿主机的/etc/localtime后跨天查询再没出过时区错位。注意时区问题在容器化部署里非常隐蔽因为你单独看任何一层日志时间都是对的放在一起就乱了。建议在架构图阶段就明确时区约定不要等到用户反馈之后再补。6. 一些让我改变做法的实际经历项目进入稳定运行期后我重新审视了整个架构有两点感受特别深。第一点监控平台的真正难点不是技术而是流程。数据链路搭建、点位配置、告警规则这些技术工作一周就能完成。但要让现场值班人员真正信任这套系统、把报警当成一回事花了将近两个月。中间经历了一次误报漏报的信任危机后来靠每天与现场联合复盘报警日志、按他们的反馈调整阈值才逐步建立信任。第二点自动补数据不是万能的。早期为了追求数据完整性在网络恢复后按时间戳补传积压数据。但后来发现补传的旧数据会覆盖时序库里的真实空缺导致统计报表与实际生产记录有出入。我的应对方案是把补传数据打上backfill标签默认不参与日报统计只有在管理员确认需要纳入趋势分析时才手动放开。这比无脑补传要稳妥得多。最后分享一个配置小心得在用Excel维护点位表时一定养成每次修改后同步导出存档的习惯。openrig支持配置版本回滚但前提是你得先把配置快照存下来。我记得有一次现场同事误操作把整张点位表清空了如果没有前一天导出的备份几百个点位的信息就得一个一个重新录入。从那以后我写了个定时任务每天晚上自动把设备配置和点位配置打包备份到对象存储安全感一下就上来了。openrig这个项目的价值不在于它做出了多惊艳的功能而在于它把工业设备数据接入这件事从项目级定制变成了配置式交付。如果你手头正好有设备需要做监控按这个思路搭一套省下的时间一定对得起折腾的功夫。