CWRU轴承数据预处理实战:1000采样长度构建4分类任务的3个关键步骤

CWRU轴承数据预处理实战:1000采样长度构建4分类任务的3个关键步骤

工业设备故障诊断领域中,滚动轴承的健康状态监测一直是工程师们关注的焦点。凯斯西储大学(CWRU)轴承数据集作为该领域的基准数据,为研究者提供了丰富的振动信号样本。本文将深入探讨如何通过三个关键步骤,将原始振动信号转化为适合深度学习模型处理的标准化输入。

1. 数据加载与初步探索

任何数据分析项目的第一步都是理解数据本身。CWRU数据集采用Matlab格式存储,包含驱动端(DE)、风扇端(FE)和基础(BA)三个位置的加速度计数据。让我们先看看如何正确加载这些数据:

import scipy.io as sio import numpy as np def load_matfile(filepath): """加载CWRU的.mat格式数据文件""" mat_data = sio.loadmat(filepath) de_data = mat_data['DE'].flatten() # 驱动端振动信号 fe_data = mat_data['FE'].flatten() # 风扇端振动信号 time_series = mat_data['time'].flatten() # 时间序列 rpm = mat_data['RPM'][0][0] # 转速信息 return de_data, fe_data, time_series, rpm

数据加载后,我们需要进行初步的可视化分析。振动信号通常表现出周期性特征,通过观察波形可以初步判断轴承状态:

import matplotlib.pyplot as plt def plot_vibration_signal(signal, title): plt.figure(figsize=(12, 4)) plt.plot(signal[:2000]) # 展示前2000个采样点 plt.title(title) plt.xlabel('采样点') plt.ylabel('振幅') plt.grid() plt.show() # 示例:绘制正常状态和内圈故障信号的对比 normal_signal = load_matfile('normal.mat')[0] fault_signal = load_matfile('inner_race_fault.mat')[0] plot_vibration_signal(normal_signal, '正常轴承振动信号') plot_vibration_signal(fault_signal, '内圈故障振动信号')

通过对比不同状态的信号波形,我们可以观察到故障信号通常具有更明显的冲击特征。下表展示了CWRU数据集中四种典型状态的波形特征:

状态类型波形特征典型周期幅值范围
正常平稳,随机振动无固定周期±0.5g
内圈故障周期性冲击100-150采样点±2.0g
外圈故障周期性冲击80-120采样点±1.8g
滚动体故障非严格周期冲击150-200采样点±1.5g

提示:在实际工程中,建议先对数据进行标准化处理(如Z-score标准化),以避免不同传感器量纲带来的影响。

2. 采样窗口选择与特征提取

采样窗口长度的选择是数据预处理中最关键的决策之一。CWRU数据集中,振动信号的典型周期约为100-200个采样点。选择1000作为窗口长度主要基于以下考虑:

  1. 覆盖完整故障周期:确保窗口能包含至少5-10个完整的故障冲击周期
  2. 计算效率平衡:过长的窗口会增加计算负担,过短则可能丢失关键特征
  3. 模型输入要求:适合大多数CNN网络的输入尺寸

实现固定长度采样的Python代码如下:

def segment_signal(signal, window_size=1000, step=500): """将长信号分割为固定长度的样本段""" segments = [] for start in range(0, len(signal)-window_size, step): segment = signal[start:start+window_size] segments.append(segment) return np.array(segments) # 对四类信号进行采样 normal_segments = segment_signal(normal_signal) inner_segments = segment_signal(load_matfile('inner_race_fault.mat')[0]) outer_segments = segment_signal(load_matfile('outer_race_fault.mat')[0]) ball_segments = segment_signal(load_matfile('ball_fault.mat')[0])

特征提取是提升模型性能的关键。除了原始振动信号外,我们可以计算时域和频域特征:

from scipy import stats from scipy.fft import fft def extract_features(segment): """提取时域和频域特征""" features = {} # 时域特征 features['mean'] = np.mean(segment) features['std'] = np.std(segment) features['skewness'] = stats.skew(segment) features['kurtosis'] = stats.kurtosis(segment) # 频域特征 fft_vals = np.abs(fft(segment))[:500] # 取前500个频点 features['freq_mean'] = np.mean(fft_vals) features['freq_std'] = np.std(fft_vals) features['dominant_freq'] = np.argmax(fft_vals) return features

下表对比了不同故障类型的典型特征值范围:

特征指标正常范围内圈故障范围外圈故障范围滚动体故障范围
峰度3-515-3010-208-15
标准差0.1-0.30.5-1.20.4-1.00.3-0.8
主频成分随机分布转速相关频率固定频率点多个谐波频率

3. 标签构建与数据平衡

四分类任务的标签构建需要考虑类别平衡问题。CWRU数据集中各类样本数量不均,直接使用可能导致模型偏差。以下是处理方案:

def create_balanced_dataset(segments_list, labels_list, samples_per_class=1400): """创建平衡的数据集""" balanced_data = [] balanced_labels = [] for segments, label in zip(segments_list, labels_list): # 随机选择指定数量的样本 indices = np.random.choice(len(segments), samples_per_class, replace=len(segments)<samples_per_class) balanced_data.extend(segments[indices]) balanced_labels.extend([label]*samples_per_class) # 打乱顺序 shuffle_idx = np.random.permutation(len(balanced_data)) return np.array(balanced_data)[shuffle_idx], np.array(balanced_labels)[shuffle_idx] # 定义标签 labels = [0, 1, 2, 3] # 0:正常, 1:内圈故障, 2:外圈故障, 3:滚动体故障 segments_list = [normal_segments, inner_segments, outer_segments, ball_segments] # 创建平衡数据集 X, y = create_balanced_dataset(segments_list, labels)

数据增强是解决样本不足的有效手段。对于振动信号,我们可以采用以下增强方法:

def augment_signal(segment, noise_level=0.05): """通过添加噪声和时移进行数据增强""" # 添加高斯噪声 noise = np.random.normal(0, noise_level*np.std(segment), len(segment)) augmented = segment + noise # 随机时移 shift = np.random.randint(-50, 50) augmented = np.roll(augmented, shift) return augmented # 示例:对单个样本进行增强 original = X[0] augmented = augment_signal(original)

注意:增强后的数据应保持原始信号的物理特性,避免过度扭曲导致模型学习到虚假特征。

4. 数据保存与模型输入准备

完成上述步骤后,我们需要将处理好的数据保存为模型可直接读取的格式:

def save_processed_data(X, y, save_dir='processed_data'): """保存处理后的数据和标签""" import os if not os.path.exists(save_dir): os.makedirs(save_dir) np.save(os.path.join(save_dir, 'X.npy'), X) np.save(os.path.join(save_dir, 'y.npy'), y) print(f"数据已保存至{save_dir}目录") # 保存数据 save_processed_data(X, y)

对于深度学习模型,我们通常需要将数据分为训练集和测试集:

from sklearn.model_selection import train_test_split def create_train_test_split(X, y, test_size=0.3): """创建训练集和测试集""" X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=test_size, stratify=y, random_state=42) # 为CNN模型添加通道维度 X_train = X_train[..., np.newaxis] X_test = X_test[..., np.newaxis] return X_train, X_test, y_train, y_test # 创建数据集 X_train, X_test, y_train, y_test = create_train_test_split(X, y)

最终得到的数据规格应满足以下要求:

  • 输入形状:(样本数, 1000, 1)
  • 输出形状:(样本数,)
  • 类别分布:每个类别在训练集和测试集中比例相同

通过这三个关键步骤,我们成功将原始的振动信号转化为适合深度学习模型处理的结构化数据。在实际项目中,这种标准化的预处理流程可以显著提高模型的训练效率和最终性能。