基于残差卷积与双向长短期记忆网络的轴承健康状态监测与剩余使用寿命预测(Python)

发布于 2025-9-25 06:58
浏览
0收藏

算法实现了一个完整的轴承健康状态监测与剩余使用寿命预测解决方案。系统首先通过数据预处理模块加载轴承传感器数据,包括水平振动信号和垂直振动信号,并进行标准化处理和序列化组织,将连续时间序列数据划分为固定长度的数据片段。接着采用残差卷积网络从原始振动信号中提取空间特征,捕捉信号中的局部模式和特征。

然后使用双向长短期记忆网络对时序数据进行建模,充分利用历史信息和未来信息,捕捉传感器数据的长期依赖关系和时间动态特性。

最后通过全连接回归网络将提取的特征映射到剩余使用寿命预测值。系统采用均方根误差作为损失函数,通过AdamW优化器进行模型训练,并提供了完整的训练流程、损失可视化以及预测结果展示功能。

在预测阶段,系统加载训练好的模型对测试数据进行剩余使用寿命预测,并将预测结果与真实值进行可视化对比,评估模型性能。

开始
│
├─ 数据加载与预处理
│   ├─ 读取轴承传感器CSV文件
│   ├─ 数据标准化处理
│   ├─ 创建剩余使用寿命标签
│   └─ 组织为序列数据
│
├─ 模型构建
│   ├─ 残差卷积网络
│   │   ├─ 1维卷积层
│   │   ├─ 实例归一化层
│   │   └─ 残差连接
│   ├─ 双向LSTM网络
│   │   ├─ 前向LSTM层
│   │   ├─ 后向LSTM层
│   │   └─ 激活函数
│   └─ 回归网络
│       ├─ 全连接层
│       ├─ 激活函数
│       └─ 输出层
│
├─ 模型训练
│   ├─ 前向传播
│   ├─ 损失计算(均方根误差)
│   ├─ 反向传播
│   └─ 参数优化(AdamW)
│
├─ 模型评估
│   ├─ 加载测试数据
│   ├─ 模型预测
│   ├─ 结果保存
│   └─ 性能可视化
│
├─ 结果可视化
│   ├─ 训练损失曲线
│   ├─ 预测结果对比
│   └─ 性能分析
│
└─ 结束

第一步进行数据准备与预处理,加载轴承传感器的CSV格式数据文件,包含水平振动信号和垂直振动信号两种传感器数据,对传感器读数进行标准化处理使其符合标准正态分布,创建剩余使用寿命标签从最大值线性递减到0表示设备从全新到完全失效的状态,将连续时间序列数据组织为固定长度的数据序列以便模型处理。

第二步构建深度学习模型架构,实现残差卷积网络包含1维卷积层、实例归一化层和残差连接,用于从原始振动信号中提取空间特征和局部模式;实现双向长短期记忆网络包含前向和后向LSTM层,充分利用历史信息和未来信息,捕捉传感器数据的长期依赖关系和时间动态特性;实现回归网络包含多个全连接层和激活函数,将提取的高级特征映射到剩余使用寿命预测值。

第三步进行模型训练与优化,采用前向传播计算模型预测结果,使用均方根误差作为损失函数评估预测准确性,通过反向传播算法计算梯度,使用AdamW优化器更新模型参数,记录训练过程中的损失变化以便监控模型收敛情况。

第四步进行模型评估与预测,加载测试数据集并进行与训练数据相同的预处理操作,使用训练好的模型对测试数据进行剩余使用寿命预测,保存预测结果和真实值以便后续分析和比较。

第五步进行结果可视化与分析,绘制训练损失曲线展示模型训练过程中的收敛情况,绘制预测结果与真实值的对比曲线直观展示模型性能,分析模型预测准确性和误差分布,评估模型在实际应用中的可行性和可靠性。

# 导入必要的库
import os  # 用于操作系统相关功能,如文件路径操作


# 定义函数获取轴承数据文件夹路径
def get_bearing_paths(root_dir):
    """
    从根目录中查找所有符合命名规范的轴承数据文件夹


    参数:
    root_dir: 根目录路径


    返回:
    bearing_folders: 符合条件的轴承文件夹路径列表
    """
    bearing_folders = []  # 存储轴承文件夹路径的列表


    # 遍历根目录及其所有子目录
    for root, dirs, files in os.walk(root_dir):
        for dir_name in dirs:
            # 检查文件夹名是否以'Bearing'开头
            if dir_name.startswith('Bearing'):
                try:
                    # 提取轴承编号
                    bearing_number = dir_name.split('Bearing')[1]
                    # 将编号分割为主编号和次编号
                    bearing_major, bearing_minor = map(int, bearing_number.split('_'))


                    # 检查编号是否在有效范围内
                    if 1 <= bearing_major <= 3 and 1 <= bearing_minor <= 5:
                        # 将有效路径添加到列表
                        bearing_folders.append(os.path.join(root, dir_name))
                except ValueError:
                    # 名称格式不正确的文件夹忽略
                    continue
    return bearing_folders


# 设置根目录路径
root_directory = "datasets"
# 获取所有轴承数据文件夹路径
bearing_paths = get_bearing_paths(root_directory)


# 打印所有找到的轴承数据路径
for path in bearing_paths:
    print(path)


# 导入数据处理和可视化库
import pandas as pd  # 数据处理和分析库
import os  # 操作系统接口
from tqdm import tqdm  # 进度条显示库


# 设置数据路径
data_path = "datasets/37.5Hz11kN/Bearing2_4/" 
# 获取文件列表
file_list = os.listdir(data_path)


# 按文件名中的数字排序
file_list = sorted(file_list, key=lambda x: int(x.split('.')[0]))


# 创建空DataFrame存储所有数据
df = pd.DataFrame()
# 遍历所有文件并加载数据
for f in tqdm(file_list):
    # 读取CSV文件
    temp = pd.read_csv(data_path + f"{f}")
    # 将数据添加到DataFrame
    df = pd.concat([df, temp], axis=0)
# 重置索引
df.reset_index(drop=True, inplace=True)
# 显示DataFrame
df


# 导入数据可视化库
import matplotlib.pyplot as plt  # 数据可视化库


# 创建图形
plt.figure(figsize=(10, 4))
# 创建水平振动信号子图
plt.subplot(1, 2, 1)
# 绘制水平振动信号
df["Horizontal_vibration_signals"].plot()
# 设置子图标题
plt.title("Horizontal Vibration Signals")


# 创建垂直振动信号子图
plt.subplot(1, 2, 2)
# 绘制垂直振动信号
df["Vertical_vibration_signals"].plot()
# 设置子图标题
plt.title("Vertical Vibration Signals")


# 显示图形
plt.tight_layout()
plt.show()


# 计算数据长度
n = len(df)


# 创建剩余使用寿命(RUL)列,从n-1递减到0
df['RUL'] = range(n-1, -1, -1)


# 计算RUL的最小值和最大值
min_rul = df['RUL'].min()
max_rul = df['RUL'].max()


# 定义RUL归一化函数
def normalize_rul(rul, min_rul, max_rul):
    """将RUL值归一化到0-1范围"""
    return (rul - min_rul) / (max_rul - min_rul)


# 应用归一化函数
df['Normalized_RUL'] = df['RUL'].apply(lambda x: normalize_rul(x, min_rul, max_rul))
# 显示DataFrame
df


# 导入PyTorch相关库
import torch  # PyTorch深度学习框架
import torch.nn as nn  # PyTorch神经网络模块
from torch.utils.data import Dataset, DataLoader  # PyTorch数据加载和处理工具


# 定义轴承数据集类
class BearingDataset(Dataset):
    def __init__(self, vibration, rul, seq_length):
        """
        初始化轴承数据集


        参数:
        vibration: 振动信号数据
        rul: 剩余使用寿命数据
        seq_length: 序列长度
        """
        self.vibration = vibration
        self.rul = rul
        self.seq_length = seq_length


    def __len__(self):
        """返回数据集大小"""
        return len(self.rul) - self.seq_length + 1


    def __getitem__(self, idx):
        """获取指定索引的数据"""
        # 获取序列数据
        x = self.vibration[idx:idx + self.seq_length]
        # 获取对应的RUL值
        y = self.rul[idx + self.seq_length - 1]


        # 转换为PyTorch张量
        x = torch.tensor(x, dtype=torch.float32)
        y = torch.tensor(y, dtype=torch.float32)


        return x, y


# 提取特征和标签
vibration = df[['Horizontal_vibration_signals', 'Vertical_vibration_signals']].values
rul_model = df['Normalized_RUL'].values


# 设置序列长度
seq_length = 128
# 创建数据集
dataset = BearingDataset(vibration, rul_model, seq_length)
# 创建数据加载器
dataloader = DataLoader(dataset, batch_size=1024, shuffle=True)


# 定义残差卷积块
class ResConv1dBlock(nn.Module):
    def __init__(self, channels):
        """
        初始化残差卷积块


        参数:
        channels: 输入输出通道数
        """
        super(ResConv1dBlock, self).__init__()
        # 1维卷积层
        self.conv1 = nn.Conv1d(channels, channels, kernel_size=3, stride=1, padding=1)
        # 实例归一化层
        self.norm1 = nn.InstanceNorm1d(channels)
        # ReLU激活函数
        self.relu = nn.ReLU(inplace=False)


    def forward(self, x):
        """前向传播"""
        # 卷积操作
        out = self.conv1(x)
        # 归一化
        out = self.norm1(out)
        # 残差连接
        out = out + x
        # 激活函数
        out = self.relu(out)
        return out


# 定义双向LSTM模块
class BiLSTM(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, seq_length, device):
        """
        初始化双向LSTM模块


        参数:
        input_size: 输入特征维度
        hidden_size: 隐藏层维度
        num_layers: LSTM层数
        seq_length: 序列长度
        device: 计算设备
        """
        super(BiLSTM, self).__init__()
        self.device = device
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.seq_length = seq_length
        # 双向LSTM层
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, 
                            batch_first=True, bidirectional=True, device=device)
        # ReLU激活函数
        self.relu = nn.ReLU(inplace=False)


    def forward(self, x):
        """前向传播"""
        # 初始化隐藏状态和细胞状态
        h0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(self.device)
        c0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(self.device)
        # LSTM前向传播
        out, _ = self.lstm(x, (h0, c0))
        # 应用激活函数
        out = self.relu(out)
        return out


# 定义回归器模块
class Regressor(nn.Module):
    def __init__(self, input_size, device):
        """
        初始化回归器模块


        参数:
        input_size: 输入特征维度
        device: 计算设备
        """
        super(Regressor, self).__init__()
        # 全连接层
        self.fc1 = nn.Linear(input_size, 64)
        self.fc2 = nn.Linear(64, 16, device=device)
        self.fc3 = nn.Linear(16, 1, device=device)
        # ReLU激活函数
        self.relu = nn.ReLU(inplace=False)


    def forward(self, x):
        """前向传播"""
        # 使用序列的最后一个时间步
        out = self.fc1(x[:, -1, :])
        out = self.relu(out)
        out = self.fc2(out)
        out = self.relu(out)
        out = self.fc3(out)
        return out


# 定义退化模型
class DegradationModel(nn.Module):
    def __init__(self, input_channels=2, hidden_size=64, num_layers=4, seq_length=128, device=torch.device("cuda")):
        """
        初始化退化模型


        参数:
        input_channels: 输入通道数
        hidden_size: 隐藏层维度
        num_layers: LSTM层数
        seq_length: 序列长度
        device: 计算设备
        """
        super(DegradationModel, self).__init__()
        # 残差卷积块
        self.conv = ResConv1dBlock(input_channels)
        # 双向LSTM模块
        self.bilstm = BiLSTM(input_channels, hidden_size, num_layers, seq_length, device)
        # 回归器模块
        self.reg = Regressor(hidden_size*2, device=device)
        self.device = device


    def forward(self, x):
        """前向传播"""
        # 调整维度并应用卷积
        x = self.conv(x.permute(0,2,1))
        # 调整维度并应用双向LSTM
        x = self.bilstm(x.permute(0,2,1))
        # 应用回归器
        out = self.reg(x)
        return out


# 导入优化器库
import torch.optim as optim  # PyTorch优化器模块
from tqdm import tqdm  # 进度条显示库
import torch.nn.functional as F  # PyTorch函数模块


# 设置版本号
ver = "1"


# 初始化模型参数
input_channels = 2  # 输入通道数(水平+垂直振动信号)
hidden_size = 64  # LSTM隐藏层维度
num_layers = 4  # LSTM层数
device = torch.device('cuda')  # 使用GPU设备


# 创建退化模型实例
rul_model = DegradationModel(input_channels, hidden_size, 
                             num_layers, seq_length, device).to(device)


# 定义损失函数
def loss_function(pred, target):
    """计算均方根误差损失"""
    loss = F.mse_loss(pred, target, reduction='mean')
    rmse_loss = torch.sqrt(loss)
    return rmse_loss


# 设置损失函数
criterion = loss_function
# 设置优化器(AdamW)
optimizer = optim.AdamW(rul_model.parameters(), lr=0.001)


# 初始化日志文件
log_file = f'rul_log_{ver}.txt'
with open(log_file, 'w') as f:
    f.write("Training Log\n")


# 设置训练轮数
num_epochs = 1000
# 开始训练循环
for epoch in range(1, num_epochs+1):
    rul_model.train()  # 设置模型为训练模式
    total_loss = 0.0  # 初始化总损失


    # 使用进度条遍历数据加载器
    for batch in tqdm(dataloader, desc=f"Epoch: {epoch}"):
        x, y = batch  # 获取批次数据
        x, y = x.to(device), y.view(-1,1).to(device)  # 移动数据到设备


        # 模型预测
        pred = rul_model(x)


        # 计算损失
        loss = criterion(pred, y)


        # 反向传播和优化
        optimizer.zero_grad()  # 清零梯度
        loss.backward()  # 反向传播
        optimizer.step()  # 更新参数


        total_loss += loss.item()  # 累加损失


    # 计算平均损失
    avg_loss = total_loss / len(dataloader)
    # 创建日志消息
    log_message = f'Epoch {epoch}/{num_epochs}, Loss: {avg_loss}\n'
    print(log_message)


    # 将日志消息写入文件
    with open(log_file, 'a') as f:
        f.write(log_message)


# 保存训练好的模型
torch.save(rul_model.state_dict(), f'./models/rul_model_{ver}.pth')


# 导入可视化库
import matplotlib.pyplot as plt  # 数据可视化库


# 从日志文件加载损失值
log_file = f'rul_log_{ver}.txt'
epochs = []  # 存储轮数
losses = []  # 存储损失值


# 读取日志文件
with open(log_file, 'r') as f:
    lines = f.readlines()[1:]  # 跳过第一行标题
    for line in lines:
        # 解析每行数据
        epoch_info = line.strip().split(',')
        epoch = int(epoch_info[0].split(' ')[1].split('/')[0])
        loss = float(epoch_info[1].split(': ')[1])
        epochs.append(epoch)
        losses.append(loss)


# 可视化损失曲线
plt.figure(figsize=(10, 5))
plt.plot(epochs, losses, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss Over Epochs')
plt.legend()
plt.grid(True)
plt.show()


# 导入必要的库
import torch  # PyTorch深度学习框架
import torch.nn as nn  # PyTorch神经网络模块
import numpy as np  # 数值计算库
import pandas as pd  # 数据处理和分析库
import os  # 操作系统接口
from tqdm import tqdm  # 进度条显示库


# 定义数据加载函数
def load_df(data_path):
    """加载并预处理轴承数据"""
    # 获取文件列表
    file_list = os.listdir(data_path)


    # 按文件名中的数字排序
    file_list = sorted(file_list, key=lambda x: int(x.split('.')[0]))


    # 创建空DataFrame
    df = pd.DataFrame()
    # 遍历所有文件并加载数据
    for f in tqdm(file_list):
        temp = pd.read_csv(data_path + f"{f}")
        df = pd.concat([df, temp], axis=0)
    # 重置索引
    df.reset_index(drop=True, inplace=True)


    # 创建RUL列
    df['RUL'] = range(len(df)-1, -1, -1)


    # 计算RUL的最小值和最大值
    min_rul = df['RUL'].min()
    max_rul = df['RUL'].max()


    # 定义RUL归一化函数
    def normalize_rul(rul, min_rul, max_rul):
        return (rul - min_rul) / (max_rul - min_rul)


    # 应用归一化
    df['Normalized_RUL'] = df['RUL'].apply(lambda x: normalize_rul(x, min_rul, max_rul))


    return df


# 加载测试数据
data_path = "./datasets/37.5Hz11kN/Bearing2_4/" 
df = load_df(data_path)


# 提取特征和标签
vibration = df[['Horizontal_vibration_signals', 'Vertical_vibration_signals']].values
rul = df['Normalized_RUL'].values


# 设置序列长度
seq_length = 128
# 创建数据集
dataset = BearingDataset(vibration, rul, seq_length)
# 创建数据加载器
dataloader = DataLoader(dataset, batch_size=1024, shuffle=False)


# 初始化模型参数
input_channels = 2  # 输入通道数
hidden_size = 64  # LSTM隐藏层维度
num_layers = 4  # LSTM层数
device = torch.device('cuda')  # 使用GPU设备


# 创建模型实例
rul_model = DegradationModel(input_channels, hidden_size, 
                             num_layers, seq_length, device).to(device)


# 加载训练好的模型权重
model_path = f'models/rul_model_{ver}.pth'
rul_model.load_state_dict(torch.load(model_path))
# 设置模型为评估模式
rul_model.eval()


# 初始化预测结果和真实值列表
predicted_ruls = []
true_ruls = []


# 不计算梯度
with torch.no_grad():
    # 使用进度条遍历测试数据
    for batch in tqdm(dataloader, desc="Testing..."):
        features, labels = batch  # 获取批次数据
        features, labels = features.to(device), labels.view(-1,1).to(device)  # 移动数据到设备
        # 模型预测
        pred = rul_model(features)
        # 保存预测结果
        predicted_ruls.extend(pred.cpu().numpy())
        # 保存真实值
        true_ruls.extend(labels.cpu().numpy())


# 转换为NumPy数组并展平
predicted_ruls = np.array(predicted_ruls).flatten()
true_ruls = np.array(true_ruls).flatten()


# 导入可视化库
import numpy as np  # 数值计算库
import matplotlib.pyplot as plt  # 数据可视化库


# 创建图形
plt.figure(figsize=(12, 6))
# 绘制预测的RUL
plt.plot(predicted_ruls, label='Predicted RUL')
# 绘制真实的RUL
plt.plot(true_ruls, label='True RUL')
plt.xlabel('Sample')
plt.ylabel('RUL')
plt.title('Predicted vs True RUL')
plt.legend()
plt.grid(True)
plt.show()

本文转载自​​高斯的手稿​

收藏
回复
举报
回复
相关推荐