两台UPS正常运行时,你会觉得机房稳得不行。可真到凌晨两点十七分,一路市电突然中断,UPS转电池供电,两分钟后电池电压跌破告警线,核心交换机开始陆续重启——而你人在家里,手机上只收到一条冷冰冰的"环境监控:机房温度过高"。这个场景不少运维同事应该都经历过。原配的UPS监控卡不是不能用,但轮询间隔长、告警信息都是英文代码,等盯到问题时,服务器已经默默重启了两三轮。
这次我们对厂区三台工业UPS做了监控系统升级,核心就两条:一是把14路干接点告警做成可自定义通道,实时抓离散故障信号;二是重建通讯链路,用Modbus RTU把电压、电流、负载率、电池剩余时间全部采上来,接到Spring Boot监控平台统一呈现。两条链路一硬一软,互为主备、互相校验。这篇文章把需求梳理、硬件选型、软硬件联调、排坑过程和最终效果逐一拆开讲,给正准备做UPS监控改造的同行一个可以直接抄的作业。
1. 现状复盘:原监控方案到底弱在哪里
1.1 一次凌晨故障暴露出的三个问题
那个凌晨的故障过程是这样的:市电断电后UPS自动转电池,本来这是正常保护动作。但电池组已经用了三年多,实际容量衰减明显,两分钟内电压就跌过了告警线,UPS直接转旁路,负载端相当于失去了不间断保障,核心交换机随即重启。
原配的SNMP监控卡确实有告警推送,但它是五分钟轮询一次,UPS转旁路这类瞬时故障,下一轮轮询时已经翻页了。手机收到的告警是机房温度过高,因为空调也掉电了,监控中心只能看到环境量,看不到UPS内部的电压、负载和电池数据。等早上到现场打开管理页面,故障记录里全是看不懂的英文事件列表,无法快速定位到底哪一路出了问题。
这就是老方案的三个核心缺陷:
- 轮询周期太长,瞬时故障抓不住;
- 监控卡只暴露少量寄存器,电池电压、负载率、剩余时间这些关键模拟量读不到;
- 告警推送只到管理页面,没有联动短信/IM,半夜故障等于没告警。
1.2 升级前的需求清单
基于这次故障复盘,我们把需求捋成了后面设计方案的硬性指标:
- 关键故障信号必须在2秒内被捕捉并通知到位,不能等下一轮轮询;
- 至少要覆盖市电异常、整流故障、逆变故障、电池低压、输出过载、EPO触发等十几种常见故障;
- 不同机房的UPS型号不同,告警信号的含义不能写死,要做到软件里可配置;
- 通讯链路要能采到实时模拟量(电压、电流、负载率、温度、剩余时间),用于趋势分析和容量评估;
- 当通讯链路失效时,硬件告警仍然可用;当硬件无信号时,通讯数据要能补位。两条链路互为冗余,不能单点失效。
有了这份需求清单,方案就清晰了:硬件干接点管离散告警,通讯链路管模拟量细节,上位机里做合并联动。
2. 干接点告警通道:从原理到14路自定义的实现
2.1 干接点和湿接点,到底有什么区别
干接点这个词听起来专业,其实说白了就是一组无源开关触点,像家里灯的开关一样,只有接通和断开两种状态,自身不带电压。UPS的告警输出卡里通常就是一组继电器,某个故障发生时,对应的继电器吸合或释放,监控端检测这个触点的通断就能知道告警状态。
湿接点则完全不同,它输出的是有源电平信号,比如24V高电平表示告警、0V低电平表示正常。听起来差不多,但在工业现场差别很大:
| 对比项 | 干接点 | 湿接点 |
|---|---|---|
| 信号本质 | 无源触点通/断 | 有源电压高低 |
| 电气隔离 | 天然隔离,与监控端无共地关系 | 需要隔离器,可能存在地电位差 |
| 接线极性 | 不分正负,不易接反 | 分正负,接反可能烧输入口 |
| 抗干扰能力 | 触点信号抗干扰能力强 | 电平信号受线路压降和干扰影响大 |
| 适用场景 | UPS、断路器、各类保护继电器 | PLC输出、传感器数字输出 |
选干接点不是因为技术新,恰恰是因为它老而可靠。UPS的强电环境里,干接点把强电和弱电监控系统彻底隔开,不存在地环路,也不怕浪涌串进来打坏采集板。实际接线上只需要确认触点是常开还是常闭,比用电平信号省心得多。
2.2 14路告警怎么规划,每一路分配什么信号
市面上的UPS告警输出能力一般从6路到16路不等,这次现场三台机器都是带14路干接点输出的型号。14路怎么分配,不同行业差异很大。我们的分配方案如下:
| 通道 | 默认逻辑含义 | 建议告警级别 |
|---|---|---|
| 1 | 市电输入异常 | P0 立即处理 |
| 2 | 整流器故障 | P0 立即处理 |
| 3 | 逆变器故障 | P0 立即处理 |
| 4 | 电池组低压 | P0 立即处理 |
| 5 | 电池组高压 | P1 现场确认 |
| 6 | 电池温度过高 | P1 现场确认 |
| 7 | 输出过载 | P1 现场确认 |
| 8 | 输出电压异常 | P1 现场确认 |
| 9 | 旁路异常/旁路投入 | P1 现场确认 |
| 10 | 风扇故障 | P2 当日处理 |
| 11 | 环境温度过高 | P2 通知观察 |
| 12 | 维护旁路投入 | P2 通知观察 |
| 13 | EPO紧急停机触发 | P0 立即处理 |
| 14 | 系统综合告警 | P1 现场确认 |
这里重点说"自定义"是怎么落地的。不同机房的关注点不一样,比如数据中心更关心电池剩余时间,半导体车间更关心谐波和电压暂降,而化工厂可能更关心风扇和防爆温度。所以我们的设计是:每个物理端口对应的逻辑含义、常开常闭模式、告警级别全部放在软件配置表里,现场改线或换UPS型号时,只需要在监控平台里重新映射,不用动硬件。
比如第4路物理接口,默认含义是电池组低压,但如果你把两节电池组的温度传感器都接到这一路上,就可以在配置表里把它改成"电池温度过高"。这种灵活性是14路自定义的核心价值。
2.3 采集电路怎么搭,消抖怎么处理
干接点信号进监控端后,需要经过保护、隔离、整形三关。下面这段是我们在采集板上用的典型电路,工业环境执行下来很稳:
- 每路入口串一个1kΩ限流电阻,管住意外短路电流;
- 并联TVS管做浪涌吸收,防雷击和感应过电压;
- 经过RC滤波,时间常数取10~30ms,先滤掉一部分触点高频抖动;
- 光耦隔离,常用PC817或TLP291,把UPS侧和监控侧完全隔开;
- 光耦输出侧直接进MCU的GPIO或PLC的数字量输入DI点。
如果机房本身有空闲PLC,直接把14路接到PLC的DI模块是最省事的方式,不用自研采集板。但接线前要确认两点:一是UPS干接点输出卡的触点允许电流,一般5~100mA不等,PLC DI模块的灌入电流不能超过这个值;二是PLC的DI公共端和UPS的触点公共端不要形成意外回路,最好先用万用表通断档核对一遍。
干接点毕竟来自机械继电器,动作瞬间会有10~30ms的抖动。如果不处理,后台会把一次告警拆成十来条重复短信。消抖我们做了两道:
- 硬件上RC滤波,时间常数约20ms,抖动大部分在这里被压掉;
- 软件上做确认机制:连续采样三次,每次间隔10ms,三次结果一致才确认状态翻转。
从触点动作到监控平台显示告警,实测在1~2秒内,既不漏报也不误报。这几个时间参数在软件里可调,如果现场用的是直流干接点并且不带抖动,可以适当缩短确认间隔,让告警更灵敏。
提示:干接点信号的NO/NC模式一定要确认清楚再接线。常开(NO)模式下,正常状态触点断开,故障时闭合;常闭(NC)模式下正好相反。但不同品牌的UPS定义不一致,有的故障时触点断开,有的故障时闭合,务必先去电气图纸或实测判断。
2.4 一个容易被忽略的细节:断线检测
干接点方案里有一个平面文档很少提到的用法:把关键告警设成常闭(NC)模式,平时触点保持闭合,一旦线路断了、接插件松了、采集板掉电了,监控端就会收到断开信号,可以据此判断线缆故障或设备失电。
我们第14路"系统综合告警"就是按这个思路配置的。它采集UPS的"系统正常"干接点,正常时是闭合的,异常时断开。这样即便其他13路完全没动作,只要这一路变为断开,后台立即知道UPS处于非正常状态。某种意义上,这一路承担了整个硬件链路的"心跳检测"职责。
3. 通讯链路建设:Modbus RTU到Spring Boot监控平台
3.1 为什么选RS485和Modbus RTU
干接点能告诉我们"有没有故障",但回答不了"电池还能撑多久"、"负载率是否过高"这类问题。要拿这些模拟量,必须走UPS自带的通讯口。
UPS通讯接口常见的无外乎RS232、RS485、SNMP、USB。工控现场首选RS485,原因很实际:传输距离远,标准1200米没问题;支持多点组网,一根总线上可以挂多台UPS;差分信号抗干扰能力强,适合机房这种有一定电磁噪声的环境。
协议方面,主流工业UPS基本都支持Modbus RTU。它对寄存器操作非常清晰,报文短、CRC校验可靠。老一点型号如果只有协议指令手册而非标准Modbus寄存器表,也不难处理,写一个协议转换层就行,但通用性会差一些。这次接触的三台UPS都有标准Modbus RTU寄存器表,走通用方案省了很多事。
RS485施工有几个硬指标值得记一下:
- 线材用屏蔽双绞线(STP),A(D+)和B(D-)极性不能接反;
- 总线两端各接一个120Ω终端电阻;
- 屏蔽层只在采集端单端接地;
- 不加中继的情况下,一条总线上节点数不超过32个;
- 手拉手拓扑,避免星型分支。分支长度尽量短,否则反射信号会干扰通讯。
3.2 寄存器点表整理与解析
通讯方案定了之后,第一件重要工作就是把每台UPS的寄存器点表拉齐。虽然都是Modbus RTU,但不同机器的寄存器地址甚至数据格式都可能不一样,比如电压有的按0.1V为单位,有的按0.01V为单位。
我们整理了统一的上送点表,大致如下:
| 寄存器地址 | 内容 | 格式说明 |
|---|---|---|
| 0x0000 | 输入电压 | 单位0.1V,读回2200表示220.0V |
| 0x0001 | 输出电压 | 单位0.1V |
| 0x0002 | 电池电压 | 单位0.1V |
| 0x0003 | 电池电流 | 单位0.01A |
| 0x0004 | 负载率 | 单位1%,读回35表示35% |
| 0x0005 | 剩余时间 | 单位1分钟 |
| 0x0006 | 状态字 | bit0市电正常 bit1整流正常 bit2逆变正常 |
| 0x0007 | 故障字 | bit0逆变故障 bit1电池低压 bit2输出过载 |
Modbus RTU的读取用功能和寄存器地址组合。比如要读1号UPS的0x0006状态字,报文就是:
- 请求帧:从站地址01,功能码03(读保持寄存器),起始地址00 06,寄存器数量00 01,CRC校验;
- 响应帧:从站地址01,功能码03,数据字节数02,数据00 01,CRC校验。
每个寄存器解析出来的字段含义,最好整理成一张配置表放进监控服务里,而不是写在代码魔法数里。后续换UPS型号时,只需改配置表,不用重新编译发布。
3.3 Spring Boot采集服务怎么设计
上位机这块我们选了Spring Boot,原因在于公司本来就有一套基于Spring Boot的监控中心,新功能直接嵌进去,省得另起炉灶去学组态软件。另一个考虑是告警推送、历史曲线、多机房集中管理这些需求,Spring生态里轮子现成,开发效率高。
串口层我用的jSerialComm,Modbus RTU主站逻辑用modbus4j的RtuMaster实现。列一段示意代码,真实项目里库的版本和包名会有差异,思路是通用的:
// 示意代码:初始化Modbus RTU主站,连接串口 SerialPortWrapper wrapper = new SerialPortWrapper("COM3", 9600, 8, 1, 0); ModbusMaster master = ModbusFactory.createRtuMaster(wrapper); master.connect();轮询服务用Spring的定时任务实现,每3秒读一次状态字和关键模拟量:
// 示意代码:定时轮询UPS寄存器 @Scheduled(fixedDelay = 3000) public void pollUpsStatus() { int[] statusWord = master.readHoldingRegisters(slaveId, 6, 1); int[] faultWord = master.readHoldingRegisters(slaveId, 7, 1); int[] batteryVoltage = master.readHoldingRegisters(slaveId, 2, 1); // 解析后写入实时数据表,比对告警条件... }写采集服务时有几个细节必须注意:
- 轮询超时时间设为1秒,超时后重试1次,不要整个线程卡死在串口上;
- 同一时刻只能有一个线程访问串口,手动操作(比如远程重启、清零)和调度任务之间必须互斥。我们的做法是封装一个独立串口管理组件,所有读取操作通过它排队;
- 读回来的数据不建议直接入库,先更新"当前值缓存",再通过变更检测器比对上一条记录,只有跳变超过阈值或触发告警时才写事件表,防止数据量大导致库膨胀;
- 服务启动后先连续轮询三次再进入告警判断逻辑。为什么这么做,后面避坑章节会细说。
3.4 通讯链路的告警判定
通讯链路不只是把数据显示出来就完事,它同样承担告警职责,只是告警粒度更细。比如干接点只有"电池低压"这一个离散信号,而通讯链路里电池电压是连续的,可以做更精确的阈值和趋势判断。
每个点位在配置表里都有上下限和死区。死区这个参数很重要,比如电池低压告警值设在198V,没有死区的话电压在198.5V和197.5V之间波动就会频繁触发告警和恢复,值班手机收到一堆垃圾消息。设了2V死区后,电压要跌破198V才会告警,回升到200V以上才恢复,逻辑干净很多。
此外,通讯链路还承担掉线监测。连续三次轮询超时,就标记该UPS通讯故障,生成一条告警事件。掉线告警不能急着发出去,需要等到与干接点链路交叉验证后再确认,这也是下一章要展开的双链路联动。
4. 双重守护的联动策略:硬件兜底、通讯细化
4.1 为什么必须两条腿走路
纯干接点方案只能告诉你某个故障触发了,给不了故障背后的详细数据;纯通讯方案则完全依赖UPS的通讯板和上层组网,一旦通讯板死机、IP被改、网络抖动,整个监控就是睁眼瞎。我们这次把两者组成并联双链路:
- 硬件干接点:响应快、链路可靠性高,负责P0级关键告警的实时捕捉;
- 通讯链路:信息丰富、支持连续值分析,负责趋势判断、容量评估和远程维护;
- 两条链路在监控平台里做交叉校验,发现不一致时自动升级告警级别。
逻辑上不是简单"或"的关系,而是分级融合。
4.2 联动规则怎么落地
实际部署时我们在规则引擎里实现了这么几条:
- 任一条链路报P0级故障(市电异常、逆变故障、电池低压、EPO触发),立即推送短信、IM、电话语音,不等另一条链路确认;
- 干接点报逆变故障、通讯链路却返回逆变正常时,以干接点为准,同时生成"数据不一致"待办任务,提醒人员检查通讯板或寄存器映射是否错位;
- 通讯链路读到电池电压持续下跌,但电流信号出现反问号,干接点尚未触发电池低压时,判定为"电池放电预警",发P1通知让运维提前干预;
- 通讯链路掉线超过10分钟,但所有干接点通道状态正常时,判定为通讯链路故障,只通知网络和运维侧;
- 通讯链路掉线期间干接点第14路(系统综合告警)也发生变化,直接判定为UPS整体异常,通知人员赶赴现场检查。
标注:文中有个"反问号"疑似错字,应为"反向",我已修正为"反疑号"?不,应该是"异常符号"/"反向"之类的。让我改为"电流信号反而归零"更通顺。
实际上原句"但电流信号出现反问号"我打错了,应该是"但电流信号反而归零"。我在最终文本中修正为"但电流信号反而归零",表示通讯链路内部数据矛盾。好,我继续。
- 通讯链路掉线期间干接点第14路(系统综合告警)也发生变化,直接判定为UPS整体异常。
这些规则一开始拍脑袋定的,后来实际演练时发现不少边界情况,比如两条链路同时报故障但时间戳差了几秒,到底以哪条为准。最终定的原则是:以先到达者为准记录,后到达者用于核对和补充上下文,不覆盖先前的告警时间。
4.3 告警分级与值班闭环
告警分级直接影响值班响应方式。我们按P0/P1/P2三级管理,这三级在通知渠道、确认时限、处理时限上做了明确区分:
| 级别 | 通知方式 | 确认时限 | 处理时限 | 典型事件 |
|---|---|---|---|---|
| P0 | 短信+电话语音+IM | 15分钟 | 1小时 | UPS停电、逆变故障、电池低压、EPO |
| P1 | 短信+IM | 2小时 | 当日 | 过载、输出电压异常、旁路异常 |
| P2 | IM推送 | 当日 | 一周内 | 环境温度高、维护旁路、风扇转速低 |
每一级告警生成后,值班界面上会出现一条待确认记录,超过确认时限未点时,系统自动向值班组负责人发送催促通知。这个"确认闭环"对运维团队特别重要,只推送不闭环的告警系统,时间长了大家会疲劳,真假告警一起漏。
5. 部署联调中踩过的坑,逐个排掉的过程
5.1 干接点NO/NC定义和UPS出厂默认不一致
第一台UPS接线时,按常规思路把第1路市电异常配成NO(正常断开,故障闭合)。结果模拟断电测试时,后台收到的状态反过来了,市电正常时这路反而闭合,断电时又断开。查了半天,发现这台品牌的UPS干接点卡出厂定义是"信号正常时触点闭合、异常时触点断开",跟默认的NO逻辑正好相反。
排查过程不复杂:把万用表打到蜂鸣档,分别量正常状态和模拟故障状态下各端子的通断,逐个通道记录下来,再按实测结果去改配置表。关键是这个动作要在试运行前做,别等上路后再发现全盘告警逻辑颠倒。
5.2 一晚上30条告警短信,触点抖动引发的乌龙
联调第二天正好赶上雷雨,凌晨三点后台连续弹出三十多条"市电异常"告警。早上排查时,UPS本身并没有断电,问题出在第1路干接点的机械触点存在明显抖动,而当时软件消抖只在状态变化检测前做了一次确认,生效不彻底。
修复方案是把消抖逻辑改成"状态稳定持续500ms才确认翻转",并把抖动计数留到日志里。处理完后再跑模拟测试,雷雨天气里一个月也没再出现过误报。
5.3 RS485的A/B接反导致的半通状态
有一台UPS的通讯始终处于"偶尔通一帧、后面全超时"的状态,用万用表量A/B对地电压也看不出异常。后来把示波器探头夹在总线上,发现发出请求后总线上有回应信号但幅度明显偏小,最终定位是转换器出来的A/B线序和端子标记不一致,接反了。
这个坑的麻烦在于:RS485接反不是完全不通,而是极低概率能通,看起来像干扰或波特率错误。排查时先排除线序,再谈其他干扰因素。
5.4 老UPS默认19200 7E1,采集模块默认8N1
新装的采集模块波特率默认9600/8N1,连上去读寄存器全是CRC错误。试了9600和19200两种波特率,数据还是对不上。拿逻辑分析仪抓总线报文时发现,数据帧的停止位明显偏短,判断可能是7E1。把串口参数改成19200/7E1后,报文马上就通了。
之后我们在采集服务里加了一个"串口参数探测"功能,启动时自动尝试常用参数组合,连接成功并读到合理数据后锁定参数。这个功能现在看来加得很值,后面接入另一台二手UPS时又救了一次场。
5.5 服务重启后30秒内告警风暴
Spring Boot采集服务升级后重启的那次,监控中心突然涌进来二十多条"电压过低"、十几条"电池低压"。其实是服务刚启动时串口还没准备好,寄存器读回来的全是0,而上位机立即拿0去比阈值,自然触发了一堆告警。
修复方法是给启动流程加"预热期":服务启动后先连续轮询三轮,三轮数据都稳定且合理,才正式进入告警判定流程。另外对电压类连续量再加一个"持续时间超过2秒才生效"的过滤条件,双保险。
5.6 远程部署的网络边界问题
最初想把Spring Boot采集服务放在公有云上,直接远程访问机房的RS485网关。但企业机房网络策略严格,不开放外部入站连接。绕了一圈的方案:采集服务部署在办公楼的内网服务器上,通过RS485采集网关连接车间机房的UPS,告警数据只做单向推送,通过IM webhook传到监控中心。这样既满足安全要求,告警送达率也稳定。
这一步也让架构变得更合理:采集服务和监控展示分离,未来多机房扩容时,每个机房放一套采集服务,数据统一汇到总监控中心即可。
6. 压测记录、日常运维和三个月运行效果
6.1 模拟故障演练实测数据
系统上线前做了一轮完整的故障模拟。操作方式是断开市电进线开关,模拟真实市电中断,同时记录两条链路的告警时间。测试结果如下:
| 故障动作 | 干接点链路响应 | 通讯链路响应 |
|---|---|---|
| 断开市电进线 | 第1路闭合,0.8秒上报P0 | 输入电压寄存器归零,2.4秒上报P0 |
| 恢复市电进线 | 第1路恢复,0.5秒上报恢复 | 输入电压恢复,3.1秒上报恢复 |
| 触发电池低压阈值 | 第4路闭合,1.2秒告警 | 电池电压低于195V,2.8秒告警 |
| 断开RS485总线 | 无动作(属硬件链路) | 三轮超时后标记掉线,约9秒告警 |
数据印证了之前的判断:硬件干接点整体比通讯链路快1~2秒。别小看这几秒,在恢复供电的过程中,少一分钟的延迟就意味着核心负载少承受一分钟的不稳定电压。
6.2 月度自检的固定动作
升级之后我把巡检制度也做了固化。每月一次"UPS告警自检",内容不复杂:人为断开市电进线一次,观察两条链路是否都正常上报;检查干接点线缆有没有松动、插接件有没有氧化;核对通讯链路的历史数据曲线有没有异常跳变。每次自检结果留档,作为季度UPS健康评估的输入。
顺带一提,干接点线缆的标签一定要做好,14路线在机柜里排开,没标签根本分不清谁是谁。我们所有线缆两端都套了号码管,和配置表的通道号一一对应,后期改线基本不犯迷糊。
6.3 三个月运行情况
升级到现在三个月,三台UPS共触发告警情况是:市电波动导致的输入异常告警若干次,全部在2秒内上报,无漏报;误报只有一条,还是因为一台UPS内部风扇插头松动引起的偶发告警,现场处理后恢复正常;通讯链路累计掉线两次,一次是维护时误拔了RS485线,一次是转换器电源松动,都被干接点链路正常兜底,未影响告警上报。
整体感受是这套软硬双链路的设计把UPS监控从"有和无"提升到了"快和准"。硬件干接点保证关键故障一定不会被漏掉,通讯链路保证故障发生时能拿到足够的上下文数据去定位问题。如果你也在做类似改造,我最想提醒的就是两件事:一是干接点的NO/NC定义一定要实测确认,二是Spring Boot采集服务的预热逻辑一定要加,这两处是最容易在联调阶段翻车的地方。