简介:预测性维护2.0:PhoenixContactPLCnext的振动频谱深度学习是一份聚焦工业设备智能运维的技术文档,面向嵌入式开发、PLC编程与设备维护工程师,尤其适合智能制造、能源管理等领域的技术人员。文档以PhoenixContact PLCnext平台为主线,系统讲解预测性维护从传统模式向数据驱动模式演进的路径,涵盖振动信号产生机理、时域与频域分析、傅里叶变换应用,以及CNN、LSTM等深度学习模型在振动频谱特征提取与故障诊断中的实战方法。全文共26页,采用单个PDF文件打包,压缩包大小仅1.74MB,支持目录章节跳转及阅读器左侧大纲快速定位,内容结构完整、图文清晰,便于按需查阅。目前已有26人学习使用。除基础理论外,文档还提供了Python与Structured Text代码示例、基于PLCnext的软硬件搭建步骤、案例研究与经济性分析,有助于读者在真实场景中快速落地预测性维护方案。
1. 把振动频谱深度学习压进 PLCnext 控制器,才能抓住转瞬即逝的故障特征
在传统预测性维护方案里,振动数据要先上传到云端,再由后端跑谱分析和深度学习模型。这个链路在数据中心没问题,但到了风机塔筒、注塑机机台和大型泵组旁边,抖动网络、时断时续的工业无线和不在同一网段的设备群,都会让“边缘-云端”这条路变脆。Phoenix Contact PLCnext 控制器里跑的是 Linux,支持 Python/C++ 应用容器,本身又有实时 IO 采集能力,意味着可以把频谱计算和深度学习推理放到设备侧完成:传感器采集、滑动窗口 FFT、CNN 分类都在同一个控制器内闭环,故障特征出现后的几十毫秒内就能给出预警。这不是让 PLC 去取代振动分析仪,而是把 PLCnext 当工业边缘节点用,只承担最适合本地处理的这一层。适合正在做预测性维护 2.0 落地、又不想依赖云端推理的自动化与 IT 融合团队。
2. PLCnext 振动频谱预处理:从原始波形到频谱张量
在训练深度学习模型之前,必须先解决一个问题:喂给模型的频谱数据怎么来。在 PLCnext 环境下,振动信号一般通过 IEPE 加速度计进入控制器,接线方式有两种:一种是用支持 IEPE 供电的模拟量输入模块直接接入,另一种是把加速度计接到独立采集终端,再通过 EtherCAT 总线把原始波形同步到 PLCnext。两种方式在程序侧没有差别,后文代码都只需要一个返回一维 float 数组的采集函数。
2.1 采样率、抗混叠与 FFT 窗口的三个关键参数
振动频谱深度学习最容易在第一步埋坑:采样率不够,轴承故障的特征频率被混叠成低频尖峰,模型学得再好也会把缺陷当正常。我一般先把目标频率范围定下来。普通电机和泵组,分析 0~10 kHz 已经覆盖到轴承故障特征频率和主要倍频;要观察齿轮箱啮合频率,至少要分析到 30 kHz。根据奈奎斯特采样定律,采样率取分析带宽的 2.56 倍是工程上稳妥的做法,也就是常见振动分析仪里 25600 Hz 对应 10 kHz 带宽。
FFT 窗口长度直接影响频率分辨率。分辨率的计算公式是 Δf = fs / N,比如 25600 Hz 采样、取 1 秒窗长,N = 25600,频率分辨率只有 1 Hz。滚动轴承的外圈内圈故障特征频率往往不是整数值,1 Hz 分辨率足够识别,但相邻的倍频成分如果靠得太近,就需要更长的窗口。LSTM 这类时间序列模型对长度敏感,而 CNN 对频谱形状敏感,所以窗口长度不能拍脑袋。
下面这张表是我在调试 PLCnext 振动项目时常用的默认参数:
| 设备类型 | 分析带宽 | 采样率 | 窗口长度 | 频率分辨率 |
|---|---|---|---|---|
| 普通电机 | 0~10 kHz | 25.6 kHz | 1 s | 1 Hz |
| 高速主轴 | 0~20 kHz | 51.2 kHz | 2 s | 0.5 Hz |
| 齿轮箱 | 0~30 kHz | 76.8 kHz | 4 s | 0.25 Hz |
需要注意,PLCnext 是 PLC,不是专用采集卡,模拟量采集的实时性受循环周期影响。我一般不在中断里做 FFT,而是把原始波形写到环形缓冲,再放到 Linux 用户态线程里批量处理。这样即使某一段波形因为总线抖动少了 100 个样本,也不至于让整个推理链路崩溃,后面在频谱张量里直接按时间戳对齐即可。
2.2 用 SciPy 在 PLCnext 中实现滑动窗口 FFT
PLCnext 的 Python 运行时一般基于容器部署,常见做法是在 PLCnext Store 或自定义镜像里安装 numpy 和 scipy,再通过控制器网口映射到宿主机。下面这段代码可以在 PLCnext 的 Python 容器里直接运行:
import numpy as np from scipy.signal import welch SAMPLE_RATE = 25600 # 25.6 kHz 采样 WINDOW_SEC = 1.0 # 频谱窗口 1 秒 N_PER_SEG = 4096 N_OVERLAP = N_PER_SEG // 2 def compute_spectrum(ring_buffer): # ring_buffer 是最近 1 秒的原始振动信号 windowed = ring_buffer * np.hanning(ring_buffer.size) freqs, psd = welch( windowed, fs=SAMPLE_RATE, nperseg=N_PER_SEG, noverlap=N_OVERLAP, scaling='density' ) # 只保留 0~10 kHz 部分,后续模型输入固定为 2048 维 mask = freqs <= 10000 return freqs[mask], psd[mask]代码里welch用的是 Welch 平均周期图法,把 1 秒信号切成多个 4096 点小段,分别做 FFT 再平均,能显著降低频域方差。nperseg决定谱的形状,太大则时间平均不足,太小则频率分辨率下降;对 1 秒窗口来说,4096 是兼顾速度和细节的取值。最后用 mask 把 10 kHz 以上的部分去掉,一方面减少计算量,另一方面也避免传感器共振频段干扰模型训练。
得到psd之后,我会将它取对数再做归一化,形成固定维度的一维频谱张量,直接喂给后面的深度学习模型。这个预处理不依赖 PLCnext 的特殊 API,普通 Python 环境也可以跑,方便先在 PC 上把整条链路调通再搬到 PLCnext 里。现场调试时还需要检查一个容易被忽略的点:PLCnext 的容器 CPU 配额如果设得太低,FFT 线程会被系统调度打断,频谱会周期性出现整段噪声。遇到这种情况,我会先调大容器内存限制,再把 FFT 线程绑定到第二个 CPU 核上。
3. 在 PLCnext 中部署 CNN 振动频谱分类模型
题目里说的“振动频谱深度学习”,最直接的落点是对频谱图做分类。深度学习常用的模型有很多,但到了 PLCnext 这种 Arm 架构工业控制器上,模型参数量和推理延迟都要被严格约束。一维 CNN 是这里最合适的选择之一。
3.1 为什么用一维 CNN 而不是 LSTM
LSTM 在时间序列预测里确实很强,但振动频谱已经把时域波形转成了频域能量分布。频谱中的故障特征表现为某些频带的能量集中、边带结构和倍频关系,这些更像是局部模式组合,而不是长距离时序依赖。一维 CNN 的感受野逐层扩大,第一层卷积核看见若干频点,第二层就能组合出边带和倍频结构,这种归纳偏置非常适合频谱张量。
另外,推理延迟在工业环境里是硬指标。LSTM 需要逐步推理,在 PLCnext 的 CPU 上容易出现批次变长导致延迟抖动;CNN 是一次性前向传播,每层都是固定矩阵运算,更容易在规定的扫描周期内完成。我曾在类似配置的 Arm 控制器上做过对比,相同精度的 CNN 比 LSTM 快 5 倍以上,内存占用少一个数量级。对于预测性维护 2.0 的现场场景,我会把深度学习模型分为两条路线:
监督分类路线适用于已知故障样本较多的设备,直接输出“正常、不平衡、轴承外圈故障、轴承内圈故障、松动”五类。频谱残差路线则用于故障样本不足的现场,用自编码器学习正常频谱的流形,再以重建误差作为异常分数。后者在预测性维护 2.0 里越来越常见,因为真实设备上“坏样本”总是很难等。
3.2 在 PC 上训练一个频谱 CNN 并导出 ONNX
训练环境和 PLCnext 运行时环境可以完全分离。我在 PC 上用 PyTorch 训练模型,输入特征是上一章生成的 2048 维对数功率谱。以下是一个足够小的 CNN 结构:
import torch.nn as nn import torch CLASSES = ['normal', 'unbalance', 'bearing_outer', 'bearing_inner', 'looseness'] class VibrationCNN(nn.Module): def __init__(self): super().__init__() # 输入形状:(batch, 1, 2048) 的频谱张量 self.conv = nn.Sequential( nn.Conv1d(1, 16, kernel_size=8, stride=2, padding=3), nn.BatchNorm1d(16), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(16, 32, kernel_size=5, stride=2, padding=2), nn.BatchNorm1d(32), nn.ReLU(), nn.AdaptiveAvgPool1d(8) ) self.head = nn.Linear(32 * 8, len(CLASSES)) def forward(self, x): x = self.conv(x) return self.head(x.flatten(1))这个模型参数量只有两万多,文件大小不到 100 KB。训练时注意把每一类样本按不同时间段的采集记录切分,而不是把一个连续波形切成的相邻窗口同时放进训练集和测试集,否则频谱之间的相关性会让验证指标虚高。训练收敛后用验证集确定早停轮次,再导出 ONNX:
model = VibrationCNN() model.load_state_dict(torch.load("vib_cnn.pt")) model.eval() dummy = torch.randn(1, 1, 2048) torch.onnx.export( model, dummy, "vib_cnn.onnx", input_names=["spectrum"], output_names=["logits"], dynamic_axes={"spectrum": {0: "batch"}}, opset_version=11 )opset_version=11是 ONNX Runtime 兼容性和新算子之间的平衡点,PLCnext 容器里的 ONNX Runtime 不一定支持更高版本。导出的vib_cnn.onnx放进 PLCnext 容器目录后,后续部署只需要加载一次模型,避免每次推理都重复读取文件。
部署方式的选择可以看下表:
| 部署方式 | 模型大小 | 推理延迟参考 | 适用场景 |
|---|---|---|---|
| 原始 ONNX FP32 | ~100 KB | 5~10 ms | 大多数 PLCnext 现场 |
| ONNX int8 量化 | ~30 KB | 2~4 ms | 高速主轴、超大窗口 |
| 自编码器残差模型 | ~80 KB | 8~15 ms | 故障样本不足的未知设备 |
延迟数值受 PLCnext 的 Arm CPU 主频和容器资源配额影响,上表只是我测试过的量级,不是产品指标。量化虽然能降低延迟,但必须用设备的真实频谱数据校准,否则精度损失可能在 5% 以上。
4. 预测性维护 2.0 标定:故障频率、阈值与误报控制
深度学习模型不是一训就完。现场最常见的问题是正常样本远多于故障样本,训练后的模型会倾向于把所有状态判为“正常”。所以落地时必须结合物理标定和动态阈值,把深度学习的输出变成稳定的告警信号。
4.1 用轴承故障特征频率校核 CNN 的分类结果
CNN 输出的概率分布不能直接当告警依据。我建议先用轴承故障特征频率公式做一层物理校核。比如外圈故障频率 BPFO、内圈故障频率 BPFI 由滚动体数量、转频和接触角共同决定,不同设备之间可能接近。当 CNN 把某种频谱判为“轴承外圈故障”时,调试者应该能在频谱图上看到对应的外圈特征频率及其倍频。
| 特征频率 | 近似计算公式 | 诊断意义 |
|---|---|---|
| 转频 f_r | 转速 / 60 | 轴类故障的基础频率 |
| 外圈 BPFO | 0.4 × N × f_r | 外圈缺陷 |
| 内圈 BPFI | 0.6 × N × f_r | 内圈缺陷 |
| 保持架 BTF | 0.4 × f_r | 保持架异常 |
这些公式在精度上不够严格,但用来校核模型输出等级足够了。如果 CNN 高概率输出“内圈故障”,而频谱主峰却集中在外圈特征频率附近,就要怀疑训练样本标注错误,而不是急着调阈值。把特征频率计算写成 PLCnext 上的一个查询函数,每次模型输出后自动对比,可以大大降低“自信误判”。
4.2 频谱残差与 EWMA 动态阈值抑制误报
生产现场转速波动、负载变化都会引起频谱能量整体偏移,固定阈值会频繁报警。常见做法是让模型维护一个“正常频谱”的残差流形:用训练好的自编码器重建当前频谱,重建误差越大,说明越远离正常模式。然后对重建误差做指数加权平均,再叠加 n 倍标准差作为动态阈值。
class AdaptiveThreshold: def __init__(self, alpha=0.02, k=5): self.alpha = alpha self.k = k self.mean = 0.0 self.var = 0.0 def update(self, residual): # residual 来自自编码器重建误差 self.mean = (1 - self.alpha) * self.mean + self.alpha * residual self.var = (1 - self.alpha) * self.var + self.alpha * (residual - self.mean) ** 2 threshold = self.mean + self.k * np.sqrt(self.var) return threshold这里的alpha决定阈值跟踪速度。太大,阈值会跟着故障信号走,异常永远追不上;太小,对设备老化这类缓变漂移不收敛。我一般设 0.02,相当于约 50 个频谱窗口完成一次基线更新。k是标准差倍数,k=5 时误报很少,但早期轻微故障可能漏报。现场先记录两周正常数据得到稳定基线,再根据可接受的最大漏报率去调 k。
参数调整时还要注意 PLCnext 的扫描周期和频谱窗口之间的匹配关系。比如窗口是 1 秒,重叠率 50%,那么每 0.5 秒就产生一条频谱;如果update放在每个频谱都执行,alpha=0.02的等效时间常数只有 25 秒,这对大多数慢变故障已经足够。对冲击性瞬时故障,阈值模块应该主动冻结,避免一次瞬态冲击就把基线拉偏。
5. 在 PLCnext Runtime 里回放历史频谱,验证深度学习模型命中率
模型部署完不是结束,必须验证在 PLCnext 上跑的模型和 PC 上训练时的表现是否一致。最直接的办法是用历史振动数据做离线回放:把现场录制好的原始波形文件放进 PLCnext 容器,重新计算频谱,再让模型逐条推理,与停机检修记录做对照。
5.1 用 ONNX Runtime 批量回放历史数据
PLCnext 容器里加载 ONNX 模型只需要 onnxruntime,推理代码很简短:
import onnxruntime as ort import numpy as np sess = ort.InferenceSession("vib_cnn.onnx", providers=["CPUExecutionProvider"]) def infer_spectrum(psd): input_data = np.expand_dims(psd.astype(np.float32), axis=(0, 1)) logits = sess.run(None, {"spectrum": input_data})[0] probs = softmax(logits[0]) return int(np.argmax(probs)), float(np.max(probs))回放时把每条频谱的时间戳、模型输出、最大概率和频谱文件路径写入 CSV,方便后期和检修记录对照。这里有一个常被忽略的细节:PLCnext 文件系统写入较慢,不要在推理循环里逐条写盘;应该在容器内存里积累成一个列表,等到回放结束后一次性写出。这样既避免 IO 阻塞,又不会漏掉高并发推理时的数据。
5.2 验证命中率时加入未知故障样本
最后一个技巧:不要只用已知故障类别去衡量模型命中率。我会在验证集中故意混入几种训练时从未出现过的故障波形,看模型会给出什么反应。如果模型对未知故障也输出 95% 以上的置信度指向某一个已知类别,说明特征提取鲁棒性不足,后面的维护动作很可能是错误的。
在 PLCnext 推理逻辑里同时输出 softmax 的最大概率,低于 0.75 的状态一律进入“待观察”缓冲,由频谱残差模块决定是否告警。这个方法在多个预测性维护 2.0 项目里都能显著减少“自信误判”,比单纯调阈值更有效。把这种带未知故障样本的回放流程固化成一个自动化脚本,每次模型更新后都跑一遍,就能长期守住告警准确率这条底线。
本文还有配套的精品资源,点击获取