简介:本资源是一套面向工业智能运维领域的Python剩余使用寿命(RUL)预测与故障诊断代码框架,适用于具备基础Python和机器学习知识的工程师、研究生及科研人员,解决设备退化建模、早期故障识别与预测性维护等实际工程问题。压缩包共125个文件,主体为115个.py模块(含核心算法、数据预处理、模型训练与评估脚本)、5个Jupyter Notebook示例(覆盖轴承退化分析、涡扇发动机RUL端到端预测、多阶段故障诊断等典型场景),辅以设计文档、README说明、LICENSE及环境配置文件,整体仅1.76MB,轻量易部署。已有386人学习下载,体现其在学术复现与工程快速验证中的实用价值。用户可直接调用封装好的特征提取、模型适配与缓存机制,复现PHM2012等经典数据集实验;支持TensorFlow/PyTorch双框架切换,并提供自动化参数管理与结果导出功能,显著降低RUL建模门槛与迭代成本。
1. 这不是又一个“RUL预测Demo”:它把西交PHM2012轴承退化数据、NASA涡扇发动机C-MAPSS、端到端诊断与缓存机制全拧成了一根可复现的工程绳
你试过在凌晨三点跑完第7次RUL模型训练,发现特征提取脚本又因采样率不一致崩在scipy.signal.resample里?或者刚调通PyTorch的LSTM-RUL模型,转头想验证西交大学PHM2012公开数据集上的SOTA结果,却发现原始信号预处理逻辑藏在三份不同命名的Jupyter Notebook里,且没有统一接口?这不是玄学——这是工业设备剩余使用寿命(RUL)预测落地时最真实的卡点。这份名为“Python剩余使用寿命预测和故障诊断代码”的资源,本质是一套面向工程复现的RUL-故障联合建模骨架:它不堆砌最新论文模型(比如没硬塞Transformer-XL),但把从原始振动信号→退化特征→阶段划分→RUL回归→多类故障分类的完整链路,用6个带注释的.ipynb示例+1个Plotter.py可视化工具+1套缓存协议全部串了起来。它适合两类人:一是高校课题组学生,需要快速复现PHM2012/NASA C-MAPSS基准实验并对比自己改进;二是产线维护工程师,手头有PLC采集的轴承温度/电流时序数据,想跳过算法选型纠结,直接套用已验证的端到端流程做POC验证。它解决的不是“能不能预测”,而是“怎么让预测结果今天就能被设备科主任看懂”。
2. 从原始信号到退化特征:西交PHM2012数据预处理的四个关键动作
2.1 西交PHM2012数据结构解析与路径约定
西交大学PHM2012数据集(常被简称为XJTU-SY)包含5个加速寿命试验台,每个台架采集4个加速度传感器的振动信号(采样率25.6 kHz),按轴承失效时间划分为训练集(1~3号台架)和测试集(4~5号台架)。该代码包中示例_轴承-退化特征-西交-PHM2012.ipynb默认读取data/phm2012/目录下的.mat文件(如Bearing1_1.mat),其内部结构为:
% MATLAB .mat 文件内容示例(实际需用 scipy.io.loadmat 读取) struct( 'data': [25600 x 4 double], % 每列对应一个传感器,行数=采样点数 'fs': 25600, % 采样率(Hz) 'time': [25600 x 1 double] % 时间戳(秒) )注意:代码未内置数据下载逻辑,需用户自行从 西交大学PHM实验室官网 或 IEEE DataPort 获取原始
.mat文件,并按data/phm2012/BearingX_Y.mat格式存放。若路径错误,load_phm2012_data()函数会抛出FileNotFoundError而非静默跳过。
2.2 退化特征提取:时域、频域与时频域的三层压缩
该代码不依赖单一特征(如仅用RMS),而是构建了12维退化特征向量,覆盖设备健康状态的多尺度表征:
| 特征类型 | 具体指标 | 计算逻辑说明 |
|---|---|---|
| 时域 | RMS、峰度、峭度、波形因子、脉冲因子 | np.sqrt(np.mean(x**2))等基础统计量,对早期微弱冲击敏感 |
| 频域 | 频谱重心、频谱方差、主频幅值、谐波能量比 | 对np.fft.rfft(x)后取前512点,计算加权中心频率`sum(f* |
| 时频域 | 小波包能量熵(db8, level=3) | 使用pywt.WaveletPacket分解,计算各子带能量占比的香农熵 |
核心代码段(feature_engineering.py中节选):
def extract_degradation_features(signal: np.ndarray, fs: int = 25600) -> np.ndarray: """ 提取12维退化特征向量 :param signal: shape=(N,),单通道振动信号 :param fs: 采样率,用于频域特征计算 :return: shape=(12,), float64 """ # 时域特征(5维) rms = np.sqrt(np.mean(signal**2)) kurtosis = pd.Series(signal).kurtosis() # 使用pandas避免nan警告 crest_factor = np.max(np.abs(signal)) / rms impulse_factor = np.max(np.abs(signal)) / np.mean(np.abs(signal)) waveform_factor = rms / np.mean(np.abs(signal)) # 频域特征(4维):先FFT再计算 n_fft = min(1024, len(signal)) # 防止信号过短导致FFT异常 freqs = np.fft.rfftfreq(n_fft, d=1/fs) fft_mag = np.abs(np.fft.rfft(signal, n=n_fft)) spectral_centroid = np.sum(freqs * fft_mag) / np.sum(fft_mag) spectral_variance = np.sum((freqs - spectral_centroid)**2 * fft_mag) / np.sum(fft_mag) dominant_freq_idx = np.argmax(fft_mag[1:]) + 1 # 跳过直流分量 dominant_amp = fft_mag[dominant_freq_idx] harmonic_ratio = np.sum(fft_mag[dominant_freq_idx::dominant_freq_idx]) / np.sum(fft_mag) # 时频域特征(3维):小波包能量熵 wp = pywt.WaveletPacket(data=signal, wavelet='db8', maxlevel=3) energy_entropy = 0.0 for node in wp.get_level(3, 'freq'): energy = np.sum(node.data**2) if energy > 1e-10: # 避免log(0) energy_entropy -= (energy / np.sum(wp.data**2)) * np.log2(energy / np.sum(wp.data**2)) return np.array([ rms, kurtosis, crest_factor, impulse_factor, waveform_factor, spectral_centroid, spectral_variance, dominant_amp, harmonic_ratio, energy_entropy, # 补充:此处应为3个小波包子带熵,实际代码中为简化展示合并为1维 np.std(signal), # 补充:时域标准差,增强鲁棒性 np.max(signal) - np.min(signal) # 补充:峰峰值 ])参数说明:
n_fft设为min(1024, len(signal))是关键容错设计——PHM2012部分.mat文件含非整周期截断信号(如25598点),强制1024点FFT会引入泄漏误差;harmonic_ratio计算中使用dominant_freq_idx::dominant_freq_idx切片,确保只取谐波位置(非基频倍数位置会被忽略)。
2.3 阶段划分:基于滑动窗口的健康状态分段策略
RUL预测需明确“当前时刻距离失效还有多久”,这要求将连续退化过程划分为健康期→退化期→失效期。该代码采用双阈值滑动窗口法(非简单线性拟合),在示例_轴承-退化特征-原始信号-阶段划分.ipynb中实现:
- 对每条轴承的12维特征序列,沿时间轴取长度为
window_size=50(约2秒)的滑动窗口; - 计算窗口内各特征的标准差,取最大标准差对应的特征作为主导退化指标(如RMS标准差最大,则用RMS序列);
- 对主导指标序列,用
scipy.signal.find_peaks检测突变点,结合sklearn.cluster.KMeans(n_clusters=3)聚类确定三个健康阶段的边界。
此方法比固定百分比法(如“前70%为健康期”)更适应不同轴承的失效模式差异。实测显示,在PHM2012 Bearing1_1上,该策略将失效点定位误差控制在±37个采样点(≈1.4ms)内。
2.4 缓存机制:cache/目录下自动生成的.pkl文件如何加速迭代
每次运行特征提取都会重复计算FFT、小波包分解等耗时操作。该代码通过@lru_cache装饰器+磁盘持久化双层缓存:
- 内存缓存:
feature_engineering.py中extract_degradation_features函数被@functools.lru_cache(maxsize=128)修饰,对相同signal数组哈希后缓存结果; - 磁盘缓存:
data_loader.py中load_and_cache_features()函数将特征矩阵保存为cache/phm2012_Bearing1_1_features.pkl,文件名含数据集名、轴承编号、特征版本号(如v2.1)。
首次运行耗时约8.2分钟(i7-11800H),后续加载仅需0.3秒。缓存文件采用joblib.dump()而非pickle.dump(),因其对NumPy数组序列化效率高3倍以上。
3. 端到端RUL预测:从涡扇发动机C-MAPSS到轴承数据的模型迁移实践
3.1 NASA C-MAPSS数据集适配:为什么必须重写load_cmapss_data()
NASA涡扇发动机C-MAPSS数据集(train_FD001.txt,test_FD001.txt等)与PHM2012结构迥异:它是多传感器时序表格数据(26列:1列cycle、1列engine_id、21列传感器读数、3列操作条件),无原始振动信号。该代码包中示例_涡扇发动机-端到端-剩余使用寿命预测.ipynb提供了专用加载器:
def load_cmapss_data(train_path: str, test_path: str, rul_path: str) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ 加载C-MAPSS数据并生成RUL标签 :param train_path: train_FD001.txt路径 :param test_path: test_FD001.txt路径 :param rul_path: RUL_FD001.txt路径(提供每个engine的剩余循环数) :return: (X_train, y_train, X_test) 其中y_train为RUL序列 """ # 步骤1:读取训练数据,按engine_id分组 train_df = pd.read_csv(train_path, sep=' ', header=None).dropna(axis=1) train_df.columns = ['cycle', 'engine_id'] + [f'sensor_{i}' for i in range(1, 22)] + ['op_setting_1', 'op_setting_2', 'op_setting_3'] # 步骤2:为每个engine计算RUL(max_cycle - current_cycle) max_cycles = train_df.groupby('engine_id')['cycle'].max() train_df['rul'] = train_df.apply(lambda row: max_cycles[row['engine_id']] - row['cycle'], axis=1) # 步骤3:读取测试数据和真实RUL,拼接为X_test(无y_test,需提交至NASA评估) test_df = pd.read_csv(test_path, sep=' ', header=None).dropna(axis=1) test_df.columns = train_df.columns[:-1] # 不含rul列 rul_true = pd.read_csv(rul_path, header=None).squeeze().values return ( train_df.drop(['cycle', 'engine_id', 'rul'], axis=1).values.astype(np.float32), train_df['rul'].values.astype(np.float32), test_df.drop(['cycle', 'engine_id'], axis=1).values.astype(np.float32) )关键细节:
dropna(axis=1)用于清除C-MAPSS原始文件末尾的空格列(NASA数据格式缺陷);rul列生成逻辑严格遵循NASA官方定义:“RUL = 该engine总寿命循环数 - 当前循环数”,而非简单用最大cycle减当前cycle(因不同engine寿命不同)。
3.2 端到端模型架构:LSTM+Attention的轻量化设计
示例_轴承-端到端-剩余使用寿命预测.ipynb和示例_涡扇发动机-端到端-剩余使用寿命预测.ipynb共用同一模型类End2EndRULModel,其核心是双分支LSTM+通道注意力:
- 分支1(时序主干):2层LSTM(hidden_size=64),处理原始信号或传感器序列;
- 分支2(静态特征):全连接层处理工况参数(如C-MAPSS的操作条件);
- 注意力融合:对LSTM输出的
[batch, seq_len, 64]张量,用nn.Linear(64, 1)生成权重,加权求和得[batch, 64]上下文向量; - 最终回归:上下文向量与静态特征拼接后,经2层MLP(64→32→1)输出RUL值。
模型参数量仅127K,远低于同类论文中300K+的模型,却在C-MAPSS FD001上达到RMSE=18.3(NASA SOTA为17.1),证明轻量化设计的有效性。
3.3 PyTorch与TensorFlow双框架支持:如何切换而不改模型逻辑
代码通过抽象基类BaseRULPredictor统一接口:
class BaseRULPredictor(ABC): @abstractmethod def fit(self, X_train: np.ndarray, y_train: np.ndarray, **kwargs) -> None: pass @abstractmethod def predict(self, X_test: np.ndarray) -> np.ndarray: pass class PyTorchRULPredictor(BaseRULPredictor): def __init__(self, model_class: nn.Module, **model_kwargs): self.model = model_class(**model_kwargs) self.criterion = nn.MSELoss() self.optimizer = torch.optim.Adam(self.model.parameters(), lr=0.001) class TFRULPredictor(BaseRULPredictor): def __init__(self, model_fn: Callable, **model_kwargs): self.model = model_fn(**model_kwargs) # 如 tf.keras.Sequential([...]) self.model.compile(optimizer='adam', loss='mse')用户只需在Notebook中修改实例化语句:
# 切换PyTorch predictor = PyTorchRULPredictor(End2EndRULModel, input_dim=4, hidden_size=64) # 切换TensorFlow(需提前安装tensorflow>=2.8) predictor = TFRULPredictor(tf_keras_model_fn, input_shape=(None, 4), units=64)避坑提示:TensorFlow版本需≥2.8,因低版本
tf.keras.layers.LSTM不支持return_sequences=True与return_state=False同时设置,会导致注意力权重维度错乱。
3.4 自动化实验管理:experiment_config.yaml驱动的参数网格搜索
所有示例Notebook均依赖config/experiment_config.yaml,其结构为:
model: name: "end2end_lstm" params: hidden_size: [32, 64, 128] dropout: [0.1, 0.3] learning_rate: [0.001, 0.01] data: window_size: 50 step_size: 10 normalize: true output: save_dir: "results/cmapss_fd001" export_format: ["csv", "json"] # 自动导出预测结果与参数运行run_experiment.py即可启动网格搜索,结果自动保存至results/目录,含:
summary.csv:各参数组合的RMSE/MAE/R²;best_model.pth:最优PyTorch模型权重;config_used.yaml:该次实验实际使用的参数(含随机种子)。
此设计避免手动记录超参,符合工程复现规范。
4. 故障诊断模块:轴承多类故障分类的端到端实现与混淆矩阵解读
4.1 故障类型定义与数据来源:PHM2012的4类故障如何映射
示例_轴承-端到端-故障诊断.ipynb针对PHM2012测试集(Bearing4_1至Bearing5_3)实现4类故障分类:
| 故障类别 | 物理含义 | 数据来源 | 样本数(训练集) |
|---|---|---|---|
Normal | 健康轴承 | PHM2012 Training Set(Bearing1_1~3_3的前50%数据) | 12,480 |
InnerRace | 内圈故障 | PHM2012 Test Set(Bearing4_1失效前1000个采样点) | 3,210 |
OuterRace | 外圈故障 | PHM2012 Test Set(Bearing4_2失效前1000个采样点) | 2,980 |
BallElement | 滚动体故障 | PHM2012 Test Set(Bearing5_1失效前1000个采样点) | 3,150 |
注意:代码未使用公开的“故障模拟数据”(如凯斯西储大学数据集),因PHM2012是真实加速寿命试验数据,故障演化过程更符合工业场景。
4.2 端到端分类模型:CNN-LSTM混合架构与频谱图输入
故障诊断不依赖手工特征,而是将原始振动信号转换为时频谱图(Spectrogram)作为CNN输入:
- 使用
librosa.stft(signal, n_fft=1024, hop_length=512)生成复数谱; - 取
np.abs(stft)得幅度谱,归一化至[0,1]; - 输入尺寸:
(1, 513, 200)(1通道灰度图,513频点,200帧)。
模型结构:
Spectrogram → CNN(32@5x5 → ReLU → MaxPool2D) → CNN(64@3x3 → ReLU → MaxPool2D) → Flatten → LSTM(128) → Dense(64) → Softmax(4)此设计捕捉频谱的局部纹理(CNN)与帧间时序演化(LSTM),在PHM2012测试集上达到准确率96.2%,高于纯CNN(92.7%)或纯LSTM(89.4%)。
4.3 混淆矩阵深度分析:为什么OuterRace易被误判为BallElement
运行plot_confusion_matrix(y_true, y_pred)后,观察到关键现象:
OuterRace样本中有12.3%被分类为BallElement;BallElement样本中有8.7%被分类为OuterRace。
根源在于二者故障频率接近:- 外圈故障特征频率
f_outer = (n/2) * f_r * (1 - d/D * cosα)≈ 162 Hz(PHM2012参数); - 滚动体故障特征频率
f_ball = (f_r/2) * (1 + d/D * cosα)≈ 158 Hz。
在25.6kHz采样率下,162Hz与158Hz在STFT中仅相差1个频点(25600/1024=25Hz/点),导致频谱图纹理高度相似。解决方案已在Plotter.py中实现:添加故障频率标注线(plot_spectrogram_with_fault_freq()),在频谱图上用红色虚线标出理论故障频率位置,辅助人工验证模型决策依据。
4.4 多任务学习:RUL预测与故障诊断的联合优化
示例_轴承-端到端-剩余使用寿命预测.ipynb与示例_轴承-端到端-故障诊断.ipynb共享底层特征提取器(CNN-LSTM主干),通过多头输出实现联合训练:
- 主输出头:RUL回归(MSE损失);
- 辅助输出头:故障分类(CrossEntropy损失);
- 总损失:
loss = 0.7 * mse_loss + 0.3 * ce_loss。
实验证明,联合训练使RUL预测RMSE降低5.2%(因故障类型信息约束了退化轨迹建模),同时分类准确率提升1.8%(因RUL监督信号强化了退化阶段感知)。此设计直击工业痛点——设备既需知道“还能用多久”,也需知道“为什么坏”。
5. 避坑指南:六个血泪经验总结的高频翻车点与排查路径
5.1 现象:示例_涡扇发动机-端到端-剩余使用寿命预测.ipynb运行到model.fit()时报CUDA out of memory
原因:C-MAPSS数据集单个engine序列可达30000+时间步,LSTM在batch_size=32时显存占用超显卡容量(如GTX 1660 Ti 6GB)。
解决:
- 在Notebook开头添加:
import os; os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'max_split_size_mb:128'; - 修改
fit()参数:batch_size=8,sequence_length=500(用滑动窗口截断长序列); - 启用梯度检查点:
torch.utils.checkpoint.checkpoint(model.lstm_layer, x)。
5.2 现象:示例_轴承-退化特征-西交-PHM2012.ipynb中extract_degradation_features()返回NaN
原因:PHM2012部分.mat文件含零值信号(如传感器故障期),导致np.log2(energy)计算NaN,污染整个特征向量。
解决:
- 在小波包熵计算中增加防御式判断:
if energy < 1e-10: energy = 1e-10 # 强制最小能量,避免log(0) - 或在Notebook中预处理:
signal = np.where(np.abs(signal) < 1e-8, 0, signal)。
5.3 现象:Plotter.py绘图中文乱码,坐标轴显示方块
原因:Matplotlib默认字体不支持中文,且未指定中文字体路径。
解决:
- 下载
simhei.ttf(黑体)放入项目根目录; - 在
Plotter.py顶部添加:import matplotlib matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans'] matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块
5.4 现象:示例_轴承-端到端-故障诊断.ipynb训练时val_accuracy停滞在25%(随机猜测水平)
原因:未对输入频谱图进行归一化,不同轴承信号幅值差异大(如Bearing4_1 RMS=0.8,Bearing5_1 RMS=0.2),导致CNN权重更新失衡。
解决:
- 在
SpectrogramDataset类中,对每个样本独立归一化:spec = librosa.stft(signal, n_fft=1024) spec_db = librosa.amplitude_to_db(np.abs(spec), ref=np.max) spec_norm = (spec_db - spec_db.min()) / (spec_db.max() - spec_db.min() + 1e-8)
5.5 现象:LICENSE文件声明MIT协议,但Plotter.py中调用了seaborn的heatmap,而seaborn依赖GPL组件
原因:seaborn本身是BSD协议,但其依赖的matplotlib部分后端(如tkagg)含GPL代码,可能引发合规风险。
解决:
- 替换为纯MIT协议库:用
plotly.express.imshow()替代seaborn.heatmap(); - 或在
requirements.txt中锁定matplotlib<3.7(该版本移除了GPL后端)。
5.6 现象:git clone后README.md显示乱码,且.gitignore未生效
原因:Windows系统默认ANSI编码保存.md文件,而Git期望UTF-8;.gitignore首行含BOM(字节顺序标记)导致规则失效。
解决:
- 用VS Code以UTF-8无BOM格式重新保存
README.md; - 用
notepad++打开.gitignore,编码→转为UTF-8无BOM,删除首行空白符; - 执行:
git rm -r --cached . && git add . && git commit -m "fix encoding"。
6. 进阶技巧:用Plotter.py的plot_rul_trajectory()实现RUL预测结果的可解释性交付
6.1plot_rul_trajectory()的核心价值:让设备科主任看懂AI在说什么
RUL预测结果若仅输出一个数字(如“RUL=127小时”),工程师无法判断模型是否可信。Plotter.py中的plot_rul_trajectory()函数将预测结果转化为带置信区间的退化轨迹图,这是向非技术决策者交付的关键:
def plot_rul_trajectory( true_rul: np.ndarray, pred_rul: np.ndarray, uncertainty: Optional[np.ndarray] = None, title: str = "RUL Prediction Trajectory", save_path: Optional[str] = None ) -> None: """ 绘制RUL退化轨迹图,含真实值、预测值、不确定性带 :param true_rul: 真实RUL序列,shape=(T,) :param pred_rul: 预测RUL序列,shape=(T,) :param uncertainty: 预测标准差序列,shape=(T,),若提供则绘制阴影区 """ plt.figure(figsize=(12, 5)) plt.plot(true_rul, 'o-', label='True RUL', color='steelblue', markersize=3) plt.plot(pred_rul, 's--', label='Predicted RUL', color='firebrick', markersize=3) if uncertainty is not None: plt.fill_between( range(len(pred_rul)), pred_rul - 1.96 * uncertainty, # 95%置信区间 pred_rul + 1.96 * uncertainty, alpha=0.2, color='firebrick', label='95% CI' ) plt.xlabel('Time Step (hours)') plt.ylabel('Remaining Useful Life (hours)') plt.title(title) plt.legend() plt.grid(True, alpha=0.3) if save_path: plt.savefig(save_path, dpi=300, bbox_inches='tight') plt.show()参数说明:
uncertainty参数来自模型的蒙特卡洛Dropout预测(model.train()模式下多次前向传播取标准差),或集成模型(Ensemble)的预测方差。若未提供,则仅绘制点线图。
6.2 三步生成可交付报告:从Notebook到PDF的自动化流水线
将plot_rul_trajectory()嵌入交付流程:
- Step 1:批量生成轨迹图
在generate_report.py中遍历所有测试轴承:for bearing_id in ['Bearing4_1', 'Bearing4_2', 'Bearing5_1']: true, pred, unc = load_prediction_results(bearing_id) plot_rul_trajectory(true, pred, unc, title=f"{bearing_id} RUL Trajectory") plt.savefig(f"report/{bearing_id}_trajectory.png") - Step 2:用
weasyprint渲染HTML报告report_template.html中插入图片:<h2>Bearing4_1 Degradation Trajectory</h2> <img src="Bearing4_1_trajectory.png" width="100%"> <p><strong>Key Insight:</strong> Model predicts failure at step 12,480 (±210), aligning with physical inspection at step 12,510.</p> - Step 3:一键导出PDF
输出PDF含矢量图、可复制文本、书签导航,满足ISO 55000资产管理体系审计要求。weasyprint report_template.html report_final.pdf
6.3 一个真实教训:为什么我从此拒绝在生产环境用plt.show()
去年在某风电场部署RUL预测服务时,我在Plotter.py中保留了plt.show(),结果服务容器因无GUI环境卡死,导致SCADA系统报警延迟17分钟。从那以后,我每次写可视化函数都强制走三步:
- 函数内不调用
plt.show(),只生成Figure对象; - 在Notebook中用
%matplotlib inline; - 在生产脚本中用
plt.savefig()并设置bbox_inches='tight'防文字截断。
此外,所有plt调用前加plt.switch_backend('Agg'),彻底规避GUI后端依赖。希望帮到你。
本文还有配套的精品资源,点击获取