首页/文章/ 详情

基于知识-数据驱动物理信息神经网络的数字孪生铣削智能刀具磨损监测模型

2小时前浏览0

本期分享发表于一区Top期刊MSSP的论文,基于知识-数据驱动物理信息神经网络的数字孪生铣削智能刀具磨损监测模型。针对现有刀具磨损监测模型主要从平均意义上考虑磨损过程约束,无法严格保证其符合潜在物理机制。文章面向铣削过程的数字孪生智能监测,提出一种融合知识-数据驱动的物理信息神经网络的 TWM 模型。在考虑磨损演化过程单调性的基础上,将磨损率变化的潜在机理作为硬物理约束,用于构建物理信息损失。并通过引入具有明确物理意义的知识偏差,引导神经网络学习过程,确保模型输出映射到严格遵循物理机理的值域中,从而提高物理一致性。

论文链接:通过点击最左下角的阅读原文进行在线阅读及下载。

论文基本信息

论文题目: An intelligent tool wear monitoring model based on knowledge-data-driven physical-informed neural network for digital twin milling

论文期刊: Mechanical Systems and Signal Processing

论文时间:2025年

作者:  Xiaohui Fang(a,b,c), Qinghua Song(a,b,c,d*), Xiaojuan Wang(a,b,c), Zhenyang Li(a,b,c), Haifeng Ma(a,b,c,d), Zhanqiang Liu(a,b,c,d,e)

机构:

a School of Mechanical Engineering, Shandong University, Jinan 250061, PR China

b Shandong Key Laboratory of High Performance Tools and System, Jinan 250061, PR China

c Key Laboratory of High Efficiency and Clean Mechanical Manufacture of Ministry of Education, Jinan 250061, PR China

d State Key Laboratory of Advanced Equipment and Technology for Metal Forming, Shandong University, Jinan 250061, PR China

e School of Mechanical, Electrical & Information Engineering, Shandong University, Weihai 264209, PR China

论文下载:https://doi.org/10.1016/j.ymssp.2025.112736

作者邮箱: ssinghua@sdu.edu.cn

摘要

数字孪生可在加工过程中提供高精度数据融合、实时状态预测和优化决策支持。可靠的刀具磨损监测(Tool Wear Monitoring, TWM)模型对于保证数字孪生系统中的数据准确性、提高磨损状态评估可靠性至关重要。然而,现有监测模型主要从平均意义上考虑磨损过程约束,无法严格保证其符合潜在物理机制。针对这一问题,本文面向铣削过程的数字孪生智能监测,提出一种融合知识-数据驱动的物理信息神经网络(Physics-Informed Neural Network, PINN)的 TWM 模型。在考虑磨损演化过程单调性的基础上,本文将磨损率变化的潜在机理作为硬物理约束,用于构建物理信息损失。通过引入具有明确物理意义的知识偏差,引导神经网络学习过程,确保模型输出映射到严格遵循物理机理的值域中,从而提高物理一致性。三种不同加工条件下的铣削实验结果表明,与传统神经网络相比,所提出的 PINN 模型具有更高精度与更强泛化能力,平均 RMSE 分别降低 11.87%、10.35% 和 15.04%。此外,在最优模型离线训练完成后,从在线数据处理到得到磨损输出的响应时间在 2 s 内,而 PINN 模型本身的计算时间处于毫秒量级。该 PINN 模型可为数字孪生智能监测系统提供低延迟响应的实时刀具磨损数据,并与决策支持系统集成,促进数字孪生系统的迭代优化,实现智能预测与维护。

关键词: 数字孪生;刀具磨损监测;物理信息神经网络;多层感知机;长短期记忆网络

目录

1 引言
2 相关工作
    2.1 刀具磨损监测模型

    2.2 物理信息神经网络

 3 方法
3.1 多层感知机与长短期记忆网络
 
3.2 PINN 基本理论
 
3.3 基于 PINN 的刀具磨损监测  
3.4 混合模型与纯数据驱动模型的精度对比  
3.5 残差分析与路径依赖效应  
3.6 采样比例对模型精度的影响  
 4 实验验证与分析
   4.1 案例 1
 
   4.2 案例 2
 
   4.3  案例 3
 
5 讨论
 
    5.1 PINN 关键参数比较  
    5.2 数字孪生场景中的有效性评估  
6 结论  
 注:本文只选中原论文部分进行分享,若想进一步拜读,请下载原论文。
 小编能力有限,如有翻译不恰之处,请多多指正~  

 

1 引言

 

作为实现智能制造的一种有效途径,数字孪生凭借先进的仿真和预测能力,能够在智能制造环境中实现自主执行监测、产品设计优化、生产过程控制、故障诊断与维护 [1,2]。数字孪生将物理实体与其数字虚拟模型相结合,并基于实时数据持续交互 [3],从而能够提供更准确的预测。

在数控机床加工中,数字孪生的主要应用是建立刀具、夹具等硬件的虚拟模型,同时对传感器感知到的实时数据进行处理与分析。这使得数字孪生驱动的 TWM、刀具故障预警、预测性维护、工件颤振分析和实时误差补偿成为可能。Liu 等 [4] 利用设备和环境的基本属性构建车间静态虚拟模型,并结合生产线实时监测数据与动态逻辑信息构建动态虚拟模型,从而通过数字孪生驱动的神经网络模型预测工件表面粗糙度。Awasthi 等 [5] 在数字孪生架构下研究了用于预测功率和切削力的最优传感器组合,并采用 K 近邻算法进行刀具故障监测。Liu 等 [6] 测量主轴和夹具的振动信号,利用提取的频率响应特征构建虚拟刀具健康模型,并采用支持向量机(Support Vector Machine, SVM)模型诊断刀具状态。

对物理环境状态的感知和识别,是实现数字孪生虚实映射数据交互的前提。此外,构建可靠的监测模型能够为监测系统提供准确反馈数据,从而增强对设备诊断和工艺参数调整的支持。对于 TWM 而言,准确的监测模型可帮助操作人员制定更有效的预防性维护计划,避免因设备故障导致停机。过去几十年中,TWM 方法已从传统单传感器信号分析和简单数学模型,发展到多传感器信息融合与神经网络方法。Lin 等 [7] 将刀具磨损表示为钻削力与扭矩的函数,并指出进给率会显著影响切削力信号。El-Wardany 等 [8] 将振动信号特征作为评估刀具磨损的准则。多传感器信息融合能够更全面地表征系统状态 [9]。利用多传感器信息融合和神经网络的 TWM 技术已得到成功开发与验证 [10,11]。以神经网络为代表的机器学习模型,由于其复杂非线性结构,能够有效捕捉刀具磨损过程中的隐含非线性关系,因而非常适合处理复杂刀具磨损特征,并与自动化监测系统集成。在神经网络模型基础上引入先验物理约束以提高物理一致性,已受到越来越多关注。然而,现有集成物理信息的神经网络模型在不同加工条件下重构物理信息时仍面临挑战。同时,由于缺少强制性物理定律约束,预测输出可能偏离磨损机理。

本文提出一种面向数字孪生铣削智能监测系统的 TWM 模型,该模型融合知识驱动与数据驱动的物理信息神经网络。不同于现有方法,本文通过引入物理知识定义刀具磨损的物理信息损失,并将其集成到精简高效、无冗余分支的神经网络模块中。本文提出耦合损失函数以引导训练过程,挖掘刀具磨损过程中的隐含先验知识,并将其作为先验信息增强模型对系统物理规律的理解。该模型在保证精度的同时兼顾计算效率和可移植性。本文主要创新点和贡献如下。

  1. 从多传感信号中提取多域特征,并对特征域相关性分布与特征相关系数分布进行综合分析。利用 Kendall、Spearman 与最大互信息相关性分析获得加权刀具磨损相关系数,筛选高相关特征,以提高监测模型可靠性,并在精度与计算效率之间取得平衡。
  2. 基于刀具磨损过程的广义 PDE,提出刀具磨损 PINN 模型,并将控制方程作为硬约束知识嵌入模型。在考虑磨损演化单调性的基础上,通过分析潜在磨损率机理,引入具有物理意义的知识偏差来引导神经网络学习过程,持续修正预测误差,并确保模型输出映射到严格遵循物理机理的域内。该模型实现了数据驱动算法与物理知识的深度融合,提高了物理一致性。
  3. 通过三组具有不同加工条件、材料和刀具类型的铣削实验以及一系列消融实验,验证所提方法的有效性、泛化性和实时性。所提出的刀具磨损 PINN 模型能够以低延迟响应保证加工过程中的及时反馈,并通过引入物理知识获得能够反映实际磨损过程的预测结果。结合数字孪生系统,该模型可为智能制造提供可靠的决策支持。

本文其余部分安排如下:第 2 节介绍与 TWM 和 PINN 相关的研究工作;第 3 节首先回顾两种典型传统神经网络模型的输出预测过程,然后介绍 PINN 的基本框架,并重点阐述基于所提 PINN 模型的 TWM 实现过程;第 4 节通过铣削实验和消融实验验证所提方法;第 5 节讨论影响 PINN 模型性能的关键参数,并评估所提 PINN 模型的有效性;第 6 节给出结论。

   

2 相关工作

 

2.1 刀具磨损监测模型

目前,TWM 模型主要包括物理模型、数据驱动模型和混合模型,如图 1 所示。

 

图 1. TWM 模型示意图。

物理模型通常通过分析试验数据建立包含工艺参数的经验方程。Slamani 等 [12] 提出一种考虑切削速度和铣削力的经验刀具磨损指标模型。Zhang 等 [13] 提出一种考虑不同阶段磨损率的通用经验模型,并以模型系数变化表征磨损过程。Palmai 等 [14] 研究了磨损过程中的热激活扩散机制,并建立了考虑温度效应的物理磨损模型。然而,实际切削条件多变,物理模型缺乏适应性和灵活性 [15],因此难以推广。

近年来,数据驱动模型已成为推动 TWM 高度自动化和智能化诊断发展的关键力量。直接测量方法通过显微镜、轮廓仪等测量工具获取磨损图像,并利用图像处理算法或智能特征提取技术实现 TWM。Lins 等 [16] 面向信息物理系统开发了一种 TWM 装置,能够可靠地提供换刀时间。然而,机器视觉测量容易受到材料表面光洁度和环境光照条件限制,且监测成本通常较高 [17,18]。

间接测量方法利用传感器采集加工信号,并采用机器学习方法或统计驱动方法进行建模。机器学习方法从加工信号中提取多域特征,并采用 Kendall 分析 [19]、Spearman 分析 [20] 和最大互信息分析 [21] 等方法获得与刀具磨损相关的特征,再将其输入人工智能模型进行训练。Qin 等 [22] 将多域特征输入神经网络,并结合 SVM 实现磨损值预测。Korkmaz 等 [23] 通过分析磨损图像特征,使用多层感知机(Multi-layer Perceptron, MLP)预测刀具磨损。Marani 等 [24] 将电流信号输入长短期记忆(Long Short-Term Memory, LSTM)模型预测刀具磨损。He 等 [25] 将原始温度信号输入神经网络,并在损失函数中引入 Kullback-Leibler 散度惩罚因子。Ma 等 [26] 将 CNN 和 LSTM 串联,分别捕捉空间特征和时间特征以进行磨损预测。然而,机器学习方法可解释性较弱,并且缺乏对预测结果不确定性的量化。

统计数据驱动方法是数据驱动方法的一个子集。该方法通过分析数据分布特征和趋势建立 TWM 模型,其不确定性量化能力有助于决策者更有效地管理风险。Song 等 [27] 建立了用于 TWM 的多核高斯过程(Gaussian Process, GP)模型,并采用 Adam 算法进行超参数优化,将由堆叠多层降噪自编码器提取的高相关特征输入模型。Ma 等 [28] 使用 GP 建立刀具退化模型,并通过粒子滤波优化模型参数。然而,即使是同类型设备,其退化模式也可能不一致,因此统计数据驱动方法的泛化能力较弱 [29,30]。

混合模型融合多种方法,可弥补单一方法的局限。首先,通过集成机器学习与统计过程,可结合二者的数据处理能力和不确定性量化能力。Nguyen 等 [31] 采用 ANFIS 定义工艺参数与磨损之间的数学关系,并结合 GP 提供置信区间。Li 等 [32] 提出一种双阶段 SVM 框架,在离线训练中学习刀具退化特征与磨损之间的关系,并结合粒子滤波在线估计退化特征。Li 等 [33] 将 CNN 和 LSTM 串联作为机器学习模型,其输出值作为三阶段 Wiener 过程的观测值。其次,通过引入物理模型生成的二级数据源,可提高物理一致性并降低数据依赖性 [34-36]。最后,通过引入刀具磨损过程的物理知识,如磨损变化的单调性 [37,38] 和非负性 [39],可引导模型训练过程。然而,当加工条件发生变化时,物理模型必须重构,这需要由高可靠性和强泛化性的物理模型生成新的数据源。此外,仅考虑磨损变化的单调性和非负性,只能保证预测在平均意义上符合磨损规律,而不是实施严格约束,因此在某些时刻仍可能偏离磨损机理。

2.2 物理信息神经网络

将控制方程、物理定律和工程理论融入模型训练过程,可获得精确且物理一致的结果。物理信息神经网络最早由 Raissi 等 [40] 提出,其将物理约束以偏微分方程(Partial Differential Equations, PDE)的形式嵌入神经网络,用于模拟系统行为,或根据观测数据反演 PDE 的未知参数。PINN 主要应用于材料力学 [41]、流体力学 [42] 和医学 [43] 等领域。Kissas 等 [44] 利用动脉血流速度、血管壁位移与压力信息引导神经网络训练,用于预测动脉血压。Du 等 [45] 提出一种基于 PINN 的模型估计管道中混油浓度分布,并通过引入混油输送过程和扩散机制的控制方程引导训练过程。

3 方法

本节首先回顾两种典型传统神经网络——多层感知机和长短期记忆神经网络——的预测输出计算过程;随后介绍考虑 PDE 一般形式的 PINN 基本框架;最后重点阐述基于 PINN 的 TWM 实现过程,这是本文的关键组成部分。

3.1 多层感知机与长短期记忆网络

多层感知机(MLP)通过层间加权和非线性变换逐层从输入数据中提取特征,最终生成输出。通过反向传播算法,MLP 能够高效更新权重和偏置。假设 MLP 共有    层,其中空间坐标     与时间坐标      作为输入:

 
 

在输出层中,使用非线性激活函数可能会限制输出范围或变换形式,导致输出信息损失;尤其在回归问题中,这会对预测精度产生不利影响。因此,输出层采用线性映射生成 MLP 的最终结果     :

 
 

其中,     表示非线性激活函数,     和     分别为权重和偏置。

长短期记忆网络(LSTM)具有更复杂的结构,它通过门控机制调节信息流,从而更高效地捕捉长序列数据中的长期依赖关系。首先,将空间坐标    和时间坐标      转换为适合输入 LSTM 的特征向量:

 
 

其中,     表示组合空间坐标与时间坐标的函数。

双向 LSTM 是标准 LSTM 的增强形式,如图 2 所示。双向 LSTM 同时考虑历史数据和未来时刻信息,因此双向 LSTM 的输出 (h_t) 可表示为:

 

图 2. 双向 LSTM 结构。

 
 
 
 

其中,     和      分别为对应于      时刻前向隐藏层状态     与后向隐藏状态      的权重,     表示    时刻隐藏层状态的偏置。

随后,将双向 LSTM 的输出连接到全连接层,以生成最终结果     :

 
 

其中,     和      分别表示全连接层的权重和偏置。

3.2 PINN 基本理论

将物理知识作为附加约束引入神经网络模型,如微分方程与守恒定律,可帮助模型避免过拟合训练数据,并促进其收敛到全局最优解。考虑一般时空 PDE:

 
 

其中,     表示空间变量,     表示时间变量,    表示空间域,     表示时间域,     表示作用于      的偏微分算子,     表示边界算子。

对于实际物理系统,PINN 使用神经网络逼近 PDE。通过微调参数     ,神经网络输出逐渐接近真实解。神经网络与物理约束之间的差异可表示为残差向量:

 
 

其中,     表示神经网络输出,     表示可训练参数,包括权重和偏置。

PINN 不仅考虑单一数据驱动方法中输入与输出之间的复杂映射关系,还集成物理方程以逼近 PDE 解。图 3 展示了物理信息神经网络的结构示意图,包括神经网络与附加损失项。PINN 总损失由数据损失     、物理信息损失     、边界条件损失     和初始条件损失      组成。

 

图 3. 物理信息神经网络结构示意图。

 
 
 

其中,    、    、     和      表示权重系数;    、    、     和      分别表示真实值数量、PDE 残差点数量、边界点数量和初始值点数量;     表示真实值,     表示实际初始值。

训练过程中的数据损失通常被归类为监督学习任务,因为它依赖已知数据;而物理信息损失可被视为无监督学习任务,因为它通过求解 PDE 和物理定律构造,不需要外部标签。对于时空域中的给定输入,神经网络利用其非线性逼近能力与高维数据处理能力平衡所有损失项,并通过寻找最优权重    和偏置    最小化公式 (10) 中的损失函数:

 
 

在 PINN 模型中,自动微分(Automatic Differentiation, AD)用于计算偏微分算子。AD 的核心机制是通过链式法则将导数从输出节点反向传播到输入节点,逐步计算每个变量的梯度。梯度是表示参数调整方向与幅值的向量。获得梯度后,可更新参数,以逐步修正违反物理定律的预测结果。

 
 

其中,    表示学习率,    表示对参数    的梯度运算,    表示迭代步长。

3.3 基于 PINN 的刀具磨损监测

本文提出一种基于 PINN 模型的 TWM 实现流程,包括数据采集、数据预处理、特征分析以及基于 PINN 的模型构建,如图 4 所示。首先,从 CNC 机床获取刀具全寿命周期样本数据,包括多传感器信号与磨损值。随后处理信号异常值,并进行数据降维。通过相关性分析和特征提取技术识别刀具磨损的高相关特征,从而提高预测结果透明度并降低决策风险。最后,构建并训练最优 PINN 模型以实现 TWM。高质量 TWM 依赖全面可靠的传感器数据、有效降噪以及具有物理意义的特征提取,但其效果最终仍受模型学习能力和泛化能力制约。因此,基于 PINN 的模型构建是决定监测性能的核心部分。

 

图 4. 基于 PINN 的 TWM 框架。

随着刀具磨损增加,刀具的主要特征是刃口逐渐变钝,这会导致刀具在有效工作时间内完成的切削量减少。学者们提出了多种经验模型来解释刀具磨损演化,例如指数模型 [12],以及对数多项式与指数的混合模型 [13,46]。这些模型将刀具磨损描述为时间的一元函数。

然而,仅将刀具磨损演化建模为时间的一元函数,无法在没有实时监测数据的情况下准确捕捉磨损变化。随着刀具磨损加剧,刀具表面会出现材料疲劳、软化、微裂纹形成以及氧化物等现象。此外,多传感器数据包含反映刀具磨损状态的多维复杂特征,这些特征通常具有时间依赖性。均方根、标准差和方差等常用特征经常用于建立刀具磨损映射关系 [47,48]。

因此,为更准确地建模实际刀具性能演化,引入实时加工过程监测数据可提高精度。本文将刀具磨损表示为多源相关特征的函数,并采用神经网络    对其建模。

 
 

其中,    表示刀具磨损预测值;    表示对应的方差、均方根等多维信号特征;    表示时间向量;    )表示多维输入与刀具磨损之间的映射关系。

刀具磨损率是单位时间内发生刀具磨损的速率。随着刀具磨损值增加,磨损率可能因磨损机理变化而改变。磨损率受切削力、切削过程振动以及切削时间等因素影响。刀具磨损率经验模型 [49] 记为    ,可表示为:

 
 

其中,    为摩擦速度,    、    和    为工件与刀具材料属性,    表示接触力,可由比例系数    与刀具磨损值    近似表示,    的计算如下:

 
 

   可进一步按下式计算:

 
 

加工过程中的材料属性通常不易获得,因此刀具磨损率的显式形式未知。受公式 (15) 和公式 (17) 启发,本文建立一个更一般的非线性动力学函数,用于描述刀具磨损率:

 
 

函数    描述由参数    构造的刀具磨损率显式 PDE。当仅考虑    时,公式 (15) 可视为公式 (18) 的特例。其中,    和    分别表示    对    与    的一阶偏导数,    和    分别表示    对    与      的二阶偏导数。本文仅关注一阶偏导数的影响,而忽略高阶偏导数。一方面,一阶偏导数直接对应磨损发展速率,能够充分捕捉刀具磨损的主要动态特征。另一方面,二阶导数等高阶导数表征磨损率变化,在某些情况下可提供额外信息。然而在实际应用中,高阶导数并不能带来显著优势,反而会增加计算复杂度和内存需求,并使 PINN 模型收敛更加困难。

本文采用具有可训练参数    的神经网络    对    建模,并利用 AD 机制计算    和    。同时,构建具有可训练参数    的神经网络    对函数    建模,使其能够从给定的    、    及其他偏导数中学习刀具磨损机理,并进一步推断磨损率变化。

在公式 (14) 的基础上引入公式 (18),等价于引入具有物理意义的知识偏差来引导学习过程,从而约束磨损演化规律并降低预测不确定性。通过    和    建立具有 PDE 残差    的 PINN 模型:

 
 

其中,    表示神经网络    关于    的偏导数。

基于上述分析,需要确保每个切削样本遵循正确的磨损演化规律。因此,物理信息损失项可表示为:

 
 

其中,    表示样本总数,    表示样本索引。

对于刀具磨损而言,刀具磨损随切削次数增加而增加这一原则是公认的物理知识。然而,由于测量系统噪声导致磨损信号随机波动,预测刀具磨损值可能呈现非单调变化。为解决这一问题,本文引入单调性损失以消除物理不一致性。若预测磨损值下降,则会产生非零损失,其大小等于差值。单调性损失计算如下:

 
 

其中,    表示第    次加工操作的预测磨损值。

对于监督学习任务,为逼近从输入到目标输出的函数,常用数据损失函数为均方误差:

 
 

其中,    表示第    次加工操作的实际磨损值。

基于上述所有损失项,基于 PINN 的 TWM 总损失函数如公式 (23) 所示。PINN 具体流程见算法 1。

 
 

其中,    和    分别为物理信息损失项和单调性损失项的权重系数。本文中,    和    分别设为 1 和 0.5。

 

图 5. HMoTP 铣削实验装置与加工参数。

算法 1:物理信息神经网络

 

4 实验验证与分析

本节将采用本文研究团队于 2024 年开发的 HMoTP 刀具磨损数据集 [50]、PHM 2010 数据集 [51] 和 NASA Ames 数据集 [52]。首先进行数据预处理和特征分析;随后将物理知识融入 MLP 与 LSTM 两种传统神经网络架构,构建基于 MLP 的 PINN 模型(称为 M-PINN)和基于 LSTM 的 PINN 模型(称为 L-PINN)。通过三组不同条件下的铣削实验评估这两种模型的性能。所提方法在配备 Intel Core i5-12400 CPU、主频 2.5 GHz 的计算机系统上实现。

4.1 案例 1

4.1.1 实验设置

HMoTP 铣削实验在 DMU 70 V 机床上进行,其    、    和    轴行程分别为 710 mm、520 mm 和 520 mm。工件为 Ti6Al4V 材料薄壁件,尺寸为 150 mm × 100 mm × 5 mm。所用刀具为直径 14 mm 的可拆卸立铣刀;与整体式铣刀不同,该刀具可在不拆除刀柄的情况下保留刀柄。采用便携式数字显微镜测量刀具磨损值,以减小安装误差并避免对试验结果产生不利影响。详细实验装置和切削参数如图 5 所示。采集    、    、    、    、    、    和    七个通道的信号数据,采样频率为 5 kHz。

HMoTP 铣削实验获得了三把刀具(T1、T2 和 T3)的全寿命周期数据,每把刀具完成 100 次切削,共得到 300 组切削样本。切削信号及对应磨损标签以 CSV 格式保存。HMoTP 铣削实验的刀具磨损值变化如图 6 所示。

 

图 6. HMoTP 铣削实验刀具磨损值变化。

4.1.2 特征提取与选择

切削信号中存在大量噪声和异常值,因此有必要消除异常值以减小其对信号的影响。本文采用 Hampel 滤波方法,通过移动局部范围检测异常值并用中位数替换。首先计算局部范围内数据的中位数,随后利用中位数绝对偏差(Median Absolute Deviation, MAD)表示数据离散程度。假设信号局部范围内有一组数据     ,其对应中位数为    ,则 MAD 可由下式计算:

 
 

其中,    表示对数据取中位数的操作。

最后,若,则将    判定为异常值并替换为    ;否则,    保持不变。判定过程如下:

 
 

其中,    表示倍数阈值参数。 此外,在处理长时间序列时,数据量较大,直接输入模型会增加训练复杂度。滑动窗口方法可通过分段降低数据维度,同时在一定程度上平滑噪声信号并保留主要趋势信息。滑动窗口方法原理如图 7 所示。在保证处理后数据长度统一为    的前提下,假设传感器数据长度为    ,窗口大小为    ,则滑动时间步长    可表示为:

 
 

其中,    表示向下取整操作。

 

图 7. 滑动窗口方法原理。

信号中的部分信息可能与刀具磨损无关,若直接输入这些信息,冗余数据会增加模型处理时间,从而降低效率和精度。具有物理意义的特征可提高决策可解释性。本文从多个特征域中提取特征,见附录 A。每个特征域提取 14 个特征,因此每个通道共有 42 个特征。特征选择和特征提取通常是相互补充的步骤。若不进行特征选择,冗余特征会阻碍模型准确学习数据与目标变量之间的关系。本文综合采用三种相关性分析方法,重点评估变量之间的顺序关系、单调关系和非线性关系,以提高相关性分析的全面性与可靠性。加权特征相关系数    的计算公式如下:

 
 

其中,    、    和    分别为 Kendall 分析、Spearman 分析和最大互信息分析结果。

采用 Hampel 滤波方法去除异常值,并利用滑动窗口方法降低数据维度后,HMoTP 铣削实验所有通道的特征域相关性分布如图 8(a) 所示。可以观察到,与刀具磨损高度相关的大多数特征位于时域和频域。对于时频域,HMoTP 铣削实验中的    通道仅表现出少量高度相关特征,且贡献相对较低。为进一步可视化所有通道特征相关系数的分布范围,图 8(b) 给出了 HMoTP 铣削实验所有通道特征相关系数的四分位数、极值和中位数。可以看出,在 HMoTP 铣削实验中,    和    通道中大多数特征的相关程度较高,相关系数集中在 0.1 到 0.4 范围内。

 

图 8. 所有通道相关性分布:(a)特征域相关性分布;(b)特征相关系数分布。

从所有通道中选取加权相关系数最高的前 16 个信号特征,如 mnp、mav 和 acc,并将特征值转换到统一尺度,以避免不同特征量级对模型性能产生影响。采用 Min-Max 归一化方法将取值限制在 0 到 1 范围内。HMoTP 铣削实验相关系数最高的前 16 个特征如图 9 所示。可以观察到,所选特征与刀具磨损之间存在较强正相关。

 

图 9. 前 16 个归一化特征,括号中数值表示对应特征的加权相关系数。

4.1.3 预测结果

为验证所提出物理信息神经网络在 TWM 中的有效性,本文构建 M-PINN 和 L-PINN,并分别与传统神经网络 MLP 和 LSTM 进行比较。所提模型与传统神经网络使用相同超参数,即 batch size、学习率、dropout、激活函数和优化器。然而,传统神经网络不考虑物理信息损失项,因此只包含单一神经网络    ,不包含    ,这表明上述比较等价于消融实验。所提模型与传统神经网络的结构和关键参数设置分别如表 1 和表 2 所示。训练过程中,两把刀具的数据样本用于训练,剩余刀具的数据样本用于测试,并将 20% 的训练样本作为验证集。因此,对于 HMoTP 铣削实验,训练、验证和测试样本数分别为 160、40 和 100。与特征缩放方法类似,本文将标签值限制在 0 到 1 范围内,以加快模型收敛。

表 1. 所提模型与传统神经网络的结构。

 

表 2. 所提模型与传统神经网络的参数设置。

 

以高相关特征和时间为输入,M-PINN 与 L-PINN 在 HMoTP 铣削实验中的磨损预测结果分别如图 10(a) 和图 10(b) 所示。预测磨损值分布在对角线附近,说明模型性能较好。为保证公平比较,PINN 模型和传统神经网络采用相同超参数。汇总刀具磨损预测结果后,所提 M-PINN 与传统神经网络 MLP 的预测结果散点图如图 11(a) 所示,所提 L-PINN 与传统神经网络 LSTM 的预测结果散点图如图 11(b) 所示。与基准模型相比,所提 PINN 模型的预测值更接近真实值,离散程度更小,预测结果更稳定。

 

图 10. HMoTP 铣削实验预测结果:(a)M-PINN;(b)L-PINN。

 

图 11. 基于 HMoTP 铣削实验三把刀具样本评估的结果散点图:(a)M-PINN 与 MLP 对比;(b)L-PINN 与 LSTM 对比。

为尽量减少随机变化和极端情况对结果的影响,本文进行了 10 次重复计算。以 RMSE 作为比较指标,所提模型与传统神经网络的预测结果如图 12 所示。在每幅图中,蓝色实线表示 10 次计算的平均 RMSE,红色虚线表示 10 次计算的中位 RMSE。值得注意的是,所提模型在测试样本上表现更好,其平均 RMSE 和中位 RMSE 通常低于传统神经网络。误差分布相对集中,表明 PINN 模型稳定性增强。PINN 模型将数据驱动算法与物理知识深度融合,不仅拟合输入与输出之间的映射关系,还在预测过程中严格遵循物理机理,持续修正预测误差,并更准确地捕捉磨损演化的复杂动态行为。此外,控制方程会基于实时监测信号持续更新,并可扩展到新的工况,从而保证 PINN 模型的鲁棒性。

 

图 12. HMoTP 铣削实验中 PINN 模型与传统神经网络 10 次重复计算的比较结果:(a)M-PINN 与 MLP 对比;(b)L-PINN 与 LSTM 对比。

尽管传统神经网络考虑了磨损演化过程的单调性,但其只能在平均意义上符合物理规律。在考虑磨损演化单调性的基础上,本文将控制方程作为硬约束嵌入 PINN 模型。该方法引入具有物理意义的知识偏差来引导神经网络学习过程,确保模型输出映射到严格遵循物理机理的值域中,从而降低预测不确定性并提高物理一致性。此外,通过具体定量指标,即消融实验结果,对 PINN 模型与传统神经网络的性能进行比较,如表 3 和表 4 所示。在 12 个评价指标中,所提 PINN 模型始终优于传统神经网络。与传统神经网络相比,所提 PINN 模型最大可使 RMSE 降低 11.87%。

表 3. M-PINN 消融实验结果。

 

表 4. L-PINN 消融实验结果。

 

图 13(a) 和图 13(b) 分别展示了所提 PINN 模型与传统神经网络的训练损失和验证损失变化。与传统神经网络相比,PINN 模型不仅最小化数据损失,即预测值与实际值之间的误差,还最小化与控制方程相关的误差。由于物理损失项引入附加约束,PINN 模型必须在数据拟合与遵循物理规律之间取得平衡,因此训练过程中的总体损失值会增加。当验证集中出现训练过程中未遇到的状态时,PINN 模型仍可通过遵循物理知识进行预测,获得更加一致的预测结果,从而提升验证集上的整体性能。

 

图 13. M-PINN 与 MLP 的损失变化:(a)训练损失;(b)验证损失。

4.2 案例 2

4.2.1 实验设置

PHM 2010 铣削实验采用 Röders Tech RFM760 机床进行,工件为矩形不锈钢块(HRC52),刀具为钨 carbide 三刃球头铣刀。详细实验装置和切削参数如图 14 所示。采集七个通道    、    、    、    、    、    和 AE 的信号数据,采样频率为 50 kHz。

 

图 14. PHM2010 铣削实验装置与加工参数。

PHM 2010 铣削实验使用三把刀具(C1、C4 和 C6),每把刀具经历 315 次切削的完整寿命周期,共获得 945 组切削样本。切削信号及对应磨损标签以 CSV 格式保存。PHM 2010 铣削实验的刀具磨损值变化如图 15 所示。

 

图 15. PHM 2010 铣削实验刀具磨损值变化。

4.2.2 特征提取与选择

经过噪声去除和数据降维后,PHM 2010 铣削实验所有通道的特征域相关性分布如图 16(a) 所示。与 HMoTP 铣削实验类似,与刀具磨损强相关的特征主要位于时域和频域,而时频域特征贡献相对较小。在时频域中,仅    、    和    通道中的少数特征表现出强相关性。此外,如图 16(b) 所示,在 PHM 2010 铣削实验中,除 AE 通道外,其他 6 个通道均包含范围较宽的相关特征。与 HMoTP 铣削实验相比,相关系数分布在 0.6 到 0.9 范围内的比例更大。

 

图 16. 所有通道相关性分布:(a)特征域相关性分布;(b)特征相关系数分布。

与 HMoTP 铣削实验类似,通过综合分析三种相关性方法,识别出相关性最高的前 16 个特征,如图 17 所示。与 HMoTP 铣削实验相比,PHM 2010 铣削实验特征变化更加平缓,且前 16 个特征的加权相关系数均高于 0.9。

 

图 17. 前 16 个归一化特征,括号中数值表示对应特征的加权相关系数。

4.2.3 预测结果

采用与表 1 和表 2 相同的网络结构和参数,M-PINN 与 L-PINN 在 PHM 2010 铣削实验中的预测结果如图 18 所示。所提模型整体预测性能较强。由于后期刀具磨损特征变化较大,预测结果存在轻微波动。通过 10 次重复计算获得的平均和中位预测误差如图 19 所示。类似地,所提模型的平均和中位预测误差始终低于传统神经网络。刀具磨损过程的广义 PDE 适用于整个磨损演化过程,可对神经网络预测过程施加硬约束。通过基于磨损率演化机理持续修正模型预测误差,预测结果保持明确的物理意义,使模型决策逻辑更加符合工业实践。

 

图 18. PHM 2010 铣削实验预测结果:(a)M-PINN;(b)L-PINN。

 

图 19. PHM 2010 铣削实验中 PINN 模型与传统神经网络 10 次重复计算的比较结果:(a)M-PINN 与 MLP 对比;(b)L-PINN 与 LSTM 对比。

此外,消融实验的具体指标用于量化引入物理信息损失后预测精度的提升,如表 5 和表 6 所示。在 PHM 2010 铣削实验中,引入物理信息损失使 RMSE 最大降低 10.35%。网络层    起到惩罚机制作用。若预测结果偏离磨损机理,网络将通过反向传播更新权重,确保最终输出遵循物理规律。

表 5. M-PINN 消融实验结果。

 

表 6. L-PINN 消融实验结果。

 

为更好地展示所提方法的有效性,本文将其监测精度与基于 PHM 2010 数据集发表的模型进行比较,如图 20 所示。与 SSAE-BDT [22] 和 Hybrid information-stacked LSTM [53] 相比,所提方法在 RMSE 和 MAE 指标上均取得最优结果。与 SSAE-BP [22] 相比,两种方法的 MAE 指标相同,所提方法的 RMSE 指标略高。尽管如此,值得注意的是,所提方法无需集成多个神经网络模块即可取得令人满意的结果。现有研究往往过度强调精度,导致融合模型包含多分支和多模块,参数调节复杂,参数数量增加,模型可解释性降低。不同于这些黑箱机器学习架构,所提方法基于物理知识从实际磨损机理出发获得预测结果,这对于数字孪生系统中的决策制定和故障诊断尤为重要。

 

图 20. 不同模型的评价指标(将预测结果转换回原始尺度后计算测试样本平均指标):(a)所提方法;(b)SSAE-BDT [22];(c)SSAE-BP [22];(d)Hybrid information-stacked LSTM [53]。

4.3 案例 3

4.3.1 实验设置

切削力信号已被证实与刀具磨损高度相关 [7]。然而,使用测力仪不仅会限制工作空间,还会改变机床的静、动态刚度,不符合加工过程监测要求 [9]。为进一步验证模型在不同切削条件下的泛化能力,以及在真实工业环境中缺少切削力信号时的可靠性,本文采用 NASA Ames 铣削数据集作为基础。

NASA Ames 铣削实验在 MC-510 V 加工中心上进行。切削刀具为直径 70 mm、六齿面铣刀。工件尺寸为 483 mm × 178 mm × 51 mm。主轴和工作台上均安装振动传感器与 AE 传感器,电流传感器用于测量主轴的交流和直流分量。图 21 给出了 NASA Ames 铣削实验装置和切削参数,记录六个通道    、    、    、    、AC 和 DC)信号数据,采样频率为 100 kHz。

 

图 21. NASA Ames 铣削实验装置与切削参数。

本文选择四把刀具(M2、M3、M11 和 M12)的全寿命周期样本。它们加工的工件均为铸铁,但进给率参数变化对模型性能提出了更严格要求。加工信号以 MAT 格式存储,对应磨损标签以 CSV 格式存储。NASA Ames 铣削实验刀具磨损值变化如图 22 所示。

 

图 22. NASA Ames 铣削实验刀具磨损值变化。

4.3.2 特征提取与选择

经过噪声去除和数据降维后,选择相关性最高的 16 个特征,如图 23 所示。在 NASA Ames 铣削实验中,与案例 1 和案例 2 相比,AE 信号特征表现出更强相关性。在所有提取特征中,AE 信号特征占总数的 62.5%。在刀具磨损后期,切削刃变钝,产生缺口和裂纹。这些缺陷会导致瞬时信号波动、特征剧烈变化,甚至出现与预期刀具磨损进程相偏离的下降趋势 [54]。

 

图 23. 前 16 个归一化特征,括号中数值表示对应特征的加权相关系数。

4.3.3 预测结果

不同于案例 1 和案例 2,本实验中不同刀具之间的切削条件发生变化。M-PINN 和 L-PINN 在 NASA Ames 铣削实验中的预测结果如图 24 所示。结果表明,即使没有直接测量的切削力信号,所提模型仍保持较高预测精度。这不仅降低了监测系统的硬件要求,也增强了其在复杂加工场景中的适用性,使其更适合实际工业条件。

 

图 24. NASA Ames 铣削实验预测结果:(a)M-PINN;(b)L-PINN。

类似地,表 7 和表 8 给出的消融实验结果与未引入物理信息损失项时的结果进行比较。在 NASA Ames 铣削实验中,引入物理信息损失使 RMSE 最大降低 15.04%。尽管该实验样本量相对较小,但由于具有物理引导机制,在传统数据驱动方法受限时,所提出的 PINN 模型仍保持较高预测精度。

表 7. M-PINN 消融实验结果。

 

表 8. L-PINN 消融实验结果。

 

5 讨论

5.1 PINN 关键参数比较

以案例 1 为例,当特征数量为 4、10、16 和 22 时,模型性能指标如图 25 所示。本文中,当特征数量为 16 时,预测精度达到最高。若输入特征数量过少,模型可能因信息丰富度不足而出现欠拟合;相反,若特征数量过多,例如 22 个,可能由于过拟合而限制模型精度并导致性能下降。

 

图 25. 不同特征数量下的 RMSE 指标。

在平衡损失函数中,各损失项的权重对于增强模型性能至关重要。物理信息损失和单调性损失权重系数的适当比例,可确保数据拟合与物理一致性之间达到最优平衡。图 26 给出了 M-PINN 和 L-PINN 模型在不同权重系数下的性能指标。

 

图 26. 不同权重系数下的 RMSE 指标。

如图 26(c) 与图 26(a) 对比所示,引入物理信息损失项后预测精度显著提高。当单调性损失项权重系数    保持不变时,将物理信息损失项权重系数    从 0.5 增加到 1 可提升模型性能,如图 26(c) 相对于图 26(b) 所示。与    ,    相比,组合    ,    可获得更大的精度提升,说明物理信息损失项比单调性损失项具有更显著影响。虽然同时引入物理信息损失和单调性损失相比单一损失项可提高性能,但过大的权重(例如     ,    )会使精度相对于纯数据驱动损失函数(    ,    )降低 19.74%。这表明过度强调物理一致性可能导致反向传播过程中的物理损失项梯度主导训练,从而抑制数据损失梯度,阻碍模型有效学习数据分布。

优化器的作用是根据损失函数调整网络参数以最小化损失。以刀具 T1 为测试样本,图 27 展示了所提出 M-PINN 模型在 Adam、RMSprop 和 SGD 三种优化器下训练过程中的物理信息损失。Adam 优化器的物理信息损失快速且稳定下降,与 RMSprop 和 SGD 相比具有更快收敛速度和更小波动,表明物理信息损失项被有效集成到优化过程中。Adam 优化器能够稳定更新参数,有效缓解梯度振荡问题。

 

图 27. 不同优化器下训练过程中的物理信息损失。

激活函数向神经网络引入非线性。tanh、sigmoid 和 ReLU 是三种常见激活函数。本文比较了 M-PINN 模型在 tanh、sigmoid 和 ReLU 三种激活函数下训练过程中的物理信息损失,如图 28 所示。结果表明,tanh 激活函数对应的物理信息损失下降最快,说明 tanh 激活函数更有效地处理物理信息损失的非线性变化。tanh 激活函数的对称性和较宽激活范围有助于所提 M-PINN 模型捕捉物理方程解。

 

图 28. 不同激活函数下训练过程中的物理信息损失。

进一步通过 10 次重复计算获得 Adam、RMSprop 和 SGD 三种优化器以及 tanh、sigmoid 和 ReLU 三种激活函数的预测结果,分别如图 29(a) 和图 29(b) 所示。在每幅图中,蓝色实线表示 10 次计算的平均 RMSE,红色实线表示 10 次计算的中位 RMSE。结果表明,以 Adam 作为优化器的预测结果平均 RMSE 分别比 RMSprop 和 SGD 降低 16.75% 和 46.84%。与 sigmoid 和 ReLU 函数相比,采用 tanh 作为激活函数的预测结果平均 RMSE 分别降低 29.82% 和 17.67%。

 

图 29. 不同优化器和激活函数下 10 次重复预测结果:(a)Adam、RMSprop 和 SGD 对比;(b)tanh、sigmoid 和 ReLU 对比。

5.2 数字孪生场景中的有效性评估

本文从三个方面评估所提 PINN 模型在数字孪生过程监测中的有效性:实时性、物理信息通用性以及真实加工环境中的传感器需求。首先,基于状态空间模型 [32] 构建刀具磨损过程的物理信息模型。参数估计过程会增加计算负担,并可能在实时数字孪生监测中造成延迟。所提 PINN 模型总响应时间小于 2 s,计算时间处于毫秒量级,适合长期 TWM。

其次,当引入物理模型以增强样本标签的物理信息表征时 [34-36],关键在于评估该模型能否全面表征磨损过程。所提 PINN 模型基于刀具磨损过程的广义 PDE,其物理信息约束机制具有通用适用性。

第三,现有物理信息模型 [35,36] 尚未评估在真实工业环境中缺少切削力信号时的可靠性。所提 PINN 模型通过涉及多种材料和切削条件的三组加工实验验证了其泛化能力,证明其在无切削力信号的真实工业环境中具有适用性。

表 9 给出了所提 PINN 模型与传统神经网络的计算时间,包括训练过程以及磨损值输出响应。响应时间指从在线数据处理到磨损预测输出的完整过程。与 PINN 模型相比,传统神经网络训练时间更短。在训练过程中,PINN 模型必须调整与物理信息相关的神经网络,同时根据实时监测信号动态调整控制方程参数。损失函数集成了数据损失项、单调性损失项和物理信息损失项。为了在数据拟合与遵循物理规律之间取得平衡,训练期间不可避免地增加计算开销,因此需要额外时间进行权重调整。然而,训练过程是离线完成的,训练好的最优 PINN 模型被集成到数字孪生系统中。处理单个样本的响应时间在 2 s 内,其中数据预处理耗时 1.7405 s,特征提取耗时 0.0608 s,模型计算平均耗时 0.0062 s。因此,尽管 PINN 模型训练时间较长,其在精度和可靠性方面的优势十分显著。对于要求高精度和实时性的工业应用而言,这种额外计算成本对于提高预测精度和保证物理一致性是合理的。

表 9. PINN 模型与传统神经网络训练时间和响应时间比较。

 

基于高保真虚拟加工过程建模、多源异构数据大规模同步采集、加工设备和工件状态智能感知以及加工参数智能搜索等关键技术,本文开发了面向铣削过程的数字孪生智能监测系统。该系统包含六个基本组成部分:物理空间、虚拟模型、数据传输模块、孪生信息数据库、实时监测系统和决策支持系统,使机床能够在智能制造环境中实现自主监测与智能决策。基于数字孪生的加工过程实时监测框架如图 30 所示。

 

图 30. 基于数字孪生的加工过程实时监测框架。

物理空间: 物理空间是数字孪生技术的基础,包含机床、工件、刀具和控制系统等实际加工环境。

虚拟模型: 虚拟模型从几何、物理、行为和规则四个维度构建。它依赖物理建模和数值仿真等建模技术,准确再现加工过程中的物理行为,包括刀具磨损、工件变形和加工误差。

数据传输模块: 数据传输模块通过物联网技术和专用通信协议连接数字孪生的各组成部分,实现低延迟的双向信息交换与反馈。

孪生信息数据库: 孪生信息数据库存储静态数据,如刀具编号和机床类型,以及动态数据,包括实时传感器数据、加工过程信息和设备维护记录。此外,它还支持实时数据记录和信息共享。

实时监测系统: TWM 模型、切削力监测模型和温度监测模型等智能模型被集成到实时监测系统中,以实现加工过程的实时预测和异常检测。利用基于 PINN 的 TWM 模型,系统可显示实时刀具磨损状态,在需要换刀前进行预警,并直接为后续维护决策提供信息,促进制造过程优化与迭代。

决策支持系统: 基于监测模型的数据分析结果,决策支持系统通过优化算法或基于知识的推理,为操作人员提供优化建议和决策支持。

这六个组成部分通过实时数据流和信息反馈机制相互连接,形成协同的闭环反馈系统。采用模块化设计,刀具磨损监测 PINN 模型被集成为功能模块,并通过统一数据接口与实时监测系统交互,实现高效协同。在多机床或多刀具环境中,单机模型可通过部署分布式数字孪生扩展为支持跨设备协同的系统。不同节点的学习和预测过程可通过云计算架构协调,以应对大规模制造系统中的复杂挑战。

6 结论

本文面向铣削过程数字孪生智能监测,提出一种融合知识-数据驱动物理信息神经网络的监测模型。TWM 是预防性维护管理的基础,而磨损演化遵循特定物理规律。在考虑磨损演化单调性的基础上,本文分析了磨损率变化的潜在机理。进一步引入具有物理意义的知识偏差来引导神经网络学习过程,确保模型输出映射到严格遵循物理机理的值域中,从而提高物理一致性。

所提模型解决了基于机器学习的纯数据驱动算法不考虑物理约束、从而导致违反物理原则的局限。基于 HMoTP、PHM 2010 和 NASA Ames 三组不同加工材料与切削条件下的铣削实验,本文获得了 40 个模型性能评价指标,并对所有所提模型进行了优化。消融实验结果表明,引入物理信息损失后,平均 RMSE 指标分别降低 11.87%、10.35% 和 15.04%。此外,在离线训练最优模型后,总响应时间小于 2 s,模型计算时间处于毫秒量级。

所提模型的可靠性、泛化性和实时性为铣削过程中的数字孪生智能监测奠定了坚实基础。其低延迟能力确保系统能够在动态铣削过程中持续跟踪刀具磨损状态,并及时向操作人员提供反馈。

为进一步增强 PINN 模型在复杂数字孪生系统中的适用性和可扩展性,未来研究将集成多任务学习框架,以同时预测刀具磨损、切削力和温度等关键变量,实现复杂条件下多物理场协同建模。此外,通过引入强化学习,模型可在实时监测系统中进行自优化,使其适应更复杂的动态环境,并通过探索未知工况持续提升预测能力。 

编辑:Tina

校核:李正平、陈凯歌、曹希铭、赵学功、白亮、任超、海洋、陈宇航、Kira、Leo、王金、赵诚、肖鑫鑫、张优

该文资料搜集自网络,仅用作学术分享,不做商业用途,若侵权,后台联系小编进行删除

来源:故障诊断与python学习
ACTMechanicalSystem振动疲劳非线性化学通用python通信云计算海洋裂纹参数优化理论化机Electric材料控制
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2026-10-15
最近编辑:2小时前
故障诊断与python学习
硕士 签名征集中
获赞 87粉丝 153文章 340课程 0
点赞
收藏
作者推荐

初学者入门教程||手把手教你划分常见数据集,并跑通诊断过程

对于刚接触智能故障诊断的同学来说,真正困难的往往不是某一个模型或某一段代码,而是不知道一项完整的研究应该从哪里开始。面对数据集、信号处理、CNN、LSTM、Transformer、迁移学习、小样本学习和模型可解释性等大量概念,初学者很容易陷入无从下手困境。为此,本系列将围绕故障诊断研究的完整流程,推出一组面向初学者的实战教程,从最基础的数据集制作开始,逐步介绍常见深度学习网络、信号处理方法,以及迁移学习、小样本学习、模型可解释性和物理信息融合等进阶内容。本系列将以基础概念梳理和方法介绍为主,并结合典型故障诊断任务说明不同方法的基本思路、主要特点和适用场景。部分内容还将提供相应的数据处理或模型代码,帮助大家进一步了解方法的实现形式,为后续学习和实践提供参考。作为本系列的第一期,本文首先从数据集处理与划分讲起。公开故障数据集通常以 MAT、CSV、TXT 等不同格式保存,内部变量名称、信号长度、类别标签和工况组织方式也各不相同。很多初学者下载数据后,首先遇到的问题并不是如何搭建复杂网络,而是看不懂文件中保存了什么数据,也不知道如何从连续信号中提取有效变量、制作固定长度样本,并划分训练集、验证集和测试集。本期将结合多个常用故障诊断数据集,介绍原始文件的读取方式、信号变量与类别信息,并演示从连续信号提取、滑动窗口切割、标签制作到数据集保存的完整流程。不合理划分可能造成的数据泄漏问题,完成本期的数据集制作后,本文还将以LSTM故障分类模型为例,展示数据的输入维度、批次维度、时间步和特征维度如何与网络结构对应。通过本期内容,希望帮助初学者真正弄清楚:原始数据是什么样的、训练样本是怎样得到的,以及处理后的数据如何进入深度学习网络。推文干货很多。本文配套代码&精华速览(简洁概括版),已同步至小红书:(ID:故障诊断与python学习),需要的小伙伴可以自取💡~目录1 引言2 西储大学数据集 2.1 数据集文件介绍 2.2 数据集提取划分 2.3 故障分类示例 2.4 结果演示3 华中科技大学轴承数据集 3.1 数据集文件介绍 3.2 数据集提取划分 3.3 故障分类示例 3.4 结果演示4 江南大学轴承数据集 4.1 数据集文件介绍 4.2 数据集提取划分 4.3 故障分类示例 4.4 结果演示5 渥太华轴承数据集 5.1 数据集文件介绍 5.2 数据集提取划分 5.3 故障分类示例 5.4 结果演示6 WT-行星齿轮箱数据集 6.1 数据集文件介绍 6.2 数据集提取划分 6.3 故障分类示例 6.4 结果演示注:小编能力有限,如有不恰之处,请多多指正~1 引言对于故障诊断方向而言,数据处理是开展深度学习研究的第一步。公开故障数据集通常包含振动、电流、转速和负载等监测信息,但不同数据集在文件格式、信号长度、变量名称和标签组织方式上存在较大差异。很多初学者下载数据集后,首先遇到的问题并不是如何搭建复杂模型,而是看不懂文件中保存了什么数据,也不知道应该如何读取、切割和划分样本。在完整的故障诊断流程中,原始连续信号通常不能直接用于模型训练,而是需要经过文件读取、有效变量提取、滑动窗口切割、类别标签制作以及训练集、验证集和测试集划分等步骤,最终转换为神经网络能够接收的数据形式。尤其需要注意的是,如果先对连续信号随机切片,再随机划分训练集和测试集,相邻甚至重叠的样本可能同时进入不同数据子集,从而造成数据泄漏,使模型测试结果虚高。本期将结合多个常用故障诊断数据集,介绍不同数据文件的内部结构、信号变量和标签信息,并梳理从原始文件到模型输入样本的完整处理流程。在完成数据处理后,本文还将以LSTM故障分类模型为例,展示数据的输入维度、批次维度、时间步和特征维度如何与网络结构对应。通过本期内容,希望帮助初学者真正弄清楚:原始数据是什么样的、训练样本是怎样得到的,以及处理后的数据如何进入深度学习网络。2 西储大学轴承数据集2.1 数据集文件介绍西储大学轴承数据集有4个文件,如图1所示,分别是1)Normal Baseline Data(正常基线数据集)2)12k Drive End Bearing Fault Data(12k 驱动端轴承故障数据)3)48k Drive End Bearing Fault Data(48k 驱动端轴承故障数据)4)Fan-End Bearing Fault Data(风扇端轴承故障数据)。图1 西储大学轴承数据集下载打开文件2.1.1. 正常基线数据集西储大学Normal Baseline Data,对应文件夹打开如图2所示,其中不同文件夹代表不同转速,我们以转速1797rpm(每分钟转数)为例,打开看到Normal.mat文件,用Matlab软件打开,如图3所示,其中X097_DE_time代表驱动端加速度数据,X097_FE_time代表风扇端加速度数据,X097RPM是基座数据。图2 西储大学Normal Baseline Data文件图3 Matlab打开Normal.mat文件2.1.2 12k驱动端轴承故障数据西储大学12k Drive End Bearing Fault Data,对应文件夹打开后不同子文件夹命名代表不同转速(与正常基线数据集一样),我们以转速1730为例,打开如图4所示。以0.007-OuterRace3数据为例,用Matlab软件打开,如图5所示,其中X147_BA_time代表基座加速度数据,X147_DE_time代表驱动端加速度数据,X147_FE_time代表风扇端加速度数据,X147RPM是基座数据。(其他文件逻辑类似,这里不一一赘述)图4 12k Drive End Bearing Fault Data文件图5 Matlab打开0.007-OuterRace3.mat文件2.3 数据集提取划分本文选取1730rpm工况数据,其中故障数据选取12k驱动端轴承故障为例,共有10类状态数据如图6所示。 图6 本文中西储大学轴承实验数据 在CWRU 轴承故障数据集的制作过程中,我们首先通过统一配置读取 10 类 .mat 振动信号,并检查文件、变量名、信号长度及异常值;随后不是先滑窗再随机划分,而是先将每类原始连续信号按 6:2:2 划分为训练、验证和测试区域,并在相邻区域之间设置隔离段,以避免重叠窗口导致训练集与测试集发生数据泄漏;之后仅利用训练区域计算可选的全局 Z-score 归一化参数,再分别对三个区域进行长度为 1024、步长为 512 的滑动窗口采样,其中只随机打乱训练集;最后将训练集、验证集、测试集、类别名称、归一化参数、样本数量及数据制作配置统一保存到一个 cwru_dataset.pt 文件中,并重新加载该文件,对张量形状、数据类型和标签范围进行校验,从而得到一个结构清晰、可复现且可直接供后续 LSTM 等模型训练使用的数据集。(代码如下,也可点击左下角阅读原文获取)from __future__ import annotationsfrom pathlib import Pathimport randomfrom typing import Dict, List, Tupleimport numpy as npimport torchfrom scipy.io import loadmat# -------------------------# 1. 基础配置# -------------------------SEED = 100DATA_DIR = Path(r"G:\学习\故障诊断\整理\一维\CWRU\experimental_data")OUTPUT_FILE = Path("cwru_dataset.pt")MAX_POINTS = 120_000WINDOW_SIZE = 1_024STRIDE = 512SPLIT_RATIOS = (0.6, 0.2, 0.2)# 在相邻数据区域之间留出隔离点,进一步减弱边界附近样本的相关性。# 因为是先分区再滑窗,即使设为 0 也不会发生采样点直接重叠。GAP_POINTS = WINDOW_SIZE# 可选值:"train_global_zscore" 或 "none"NORMALIZATION = "none"CLASS_SPECS = [ {"name": "Normal", "label": 0, "file": "Normal.mat", "key": "X100_DE_time"}, {"name": "0.007-Ball", "label": 1, "file": "0.007-Ball.mat", "key": "X121_DE_time"}, {"name": "0.007-InnerRace", "label": 2, "file": "0.007-InnerRace.mat", "key": "X108_DE_time"}, {"name": "0.007-OuterRace6", "label": 3, "file": "0.007-OuterRace6.mat", "key": "X133_DE_time"}, {"name": "0.014-Ball", "label": 4, "file": "0.014-Ball.mat", "key": "X188_DE_time"}, {"name": "0.014-InnerRace", "label": 5, "file": "0.014-InnerRace.mat", "key": "X172_DE_time"}, {"name": "0.014-OuterRace6", "label": 6, "file": "0.014-OuterRace6.mat", "key": "X200_DE_time"}, {"name": "0.021-Ball", "label": 7, "file": "0.021-Ball.mat", "key": "X225_DE_time"}, {"name": "0.021-InnerRace", "label": 8, "file": "0.021-InnerRace.mat", "key": "X212_DE_time"}, {"name": "0.021-OuterRace6", "label": 9, "file": "0.021-OuterRace6.mat", "key": "X237_DE_time"},]def set_seed(seed: int) -> None: random.seed(seed) np.random.seed(seed) torch.manual_seed(seed)set_seed(SEED)print(f"数据目录:{DATA_DIR}")print(f"输出文件:{OUTPUT_FILE.resolve()}")## 2. 数据读取与检查##每个类别由一条配置记录描述,避免多个平行列表顺序错位。加载时会检查文件、MAT 键名、信号长度以及非有限值def load_signal(data_dir: Path, spec: Dict, max_points: int | None) -> np.ndarray: file_path = data_dir / spec["file"] if not file_path.exists(): raise FileNotFoundError(f"找不到数据文件:{file_path}") mat = loadmat(file_path) key = spec["key"] if key not in mat: available = [name for name in mat.keys() if not name.startswith("__")] raise KeyError( f"文件 {file_path.name} 中不存在变量 {key}。可用变量:{available}" ) signal = np.asarray(mat[key], dtype=np.float32).reshape(-1) if max_points is not None: signal = signal[:max_points] if signal.size < WINDOW_SIZE: raise ValueError( f"{spec['name']} 的有效长度为 {signal.size},小于窗口长度 {WINDOW_SIZE}" ) if not np.isfinite(signal).all(): raise ValueError(f"{spec['name']} 中存在 NaN 或 Inf") return signalsignals: Dict[int, np.ndarray] = {}for spec in CLASS_SPECS: signal = load_signal(DATA_DIR, spec, MAX_POINTS) signals[spec["label"]] = signal print(f"{spec['label']:>2} | {spec['name']:<20} | length={len(signal)}")## 3. 先划分原始连续信号,再分别滑窗#原代码将所有重叠窗口混合后随机切分,训练集与测试集可能共享大量相同采样点。本版本先对每条原始信号划分连续区域,再在各区域内部生成窗口。#隔离区不会进入任何集 合,用于减弱分界点附近的高度相似性。def validate_split_ratios(ratios: Tuple[float, float, float]) -> None: if len(ratios) != 3: raise ValueError("SPLIT_RATIOS 必须包含训练、验证、测试三个比例") if any(r <= 0 for r in ratios): raise ValueError("三个划分比例都必须大于 0") if not np.isclose(sum(ratios), 1.0): raise ValueError(f"划分比例之和必须为 1,当前为 {sum(ratios)}")def split_contiguous_signal( signal: np.ndarray, ratios: Tuple[float, float, float], gap_points: int,) -> Dict[str, np.ndarray]: validate_split_ratios(ratios) if gap_points < 0: raise ValueError("gap_points 不能为负数") n = len(signal) train_end = int(n * ratios[0]) val_end = int(n * (ratios[0] + ratios[1])) regions = { "train": signal[:train_end], "val": signal[min(train_end + gap_points, n):val_end], "test": signal[min(val_end + gap_points, n):], } for split_name, region in regions.items(): if len(region) < WINDOW_SIZE: raise ValueError( f"{split_name} 区域长度 {len(region)} 小于窗口长度 {WINDOW_SIZE}。" "请减小 GAP_POINTS、WINDOW_SIZE,或增加原始数据长度。" ) return regionsregions_by_class: Dict[int, Dict[str, np.ndarray]] = { label: split_contiguous_signal(signal, SPLIT_RATIOS, GAP_POINTS) for label, signal in signals.items()}for spec in CLASS_SPECS: lengths = {k: len(v) for k, v in regions_by_class[spec["label"]].items()} print(f"{spec['name']:<20} -> {lengths}")## 4. 仅使用训练区域计算归一化参数#默认采用所有训练区域共同计算的全局均值与标准差。这样不会读取验证集和测试集统计量,同时保留不同故障类别之间的相对幅值关系。若不需要归一化,将 `NORMALIZATION` 改为 `"none"`。def fit_normalizer( regions: Dict[int, Dict[str, np.ndarray]], method: str,) -> Dict[str, float | str]: if method == "none": return {"method": "none", "mean": 0.0, "std": 1.0} if method != "train_global_zscore": raise ValueError(f"不支持的归一化方法:{method}") train_values = np.concatenate( [class_regions["train"] for class_regions in regions.values()] ).astype(np.float64, copy=False) mean = float(train_values.mean()) std = float(train_values.std()) if std < 1e-12: raise ValueError("训练数据标准差接近 0,无法执行 Z-score 标准化") return {"method": method, "mean": mean, "std": std}def apply_normalizer(signal: np.ndarray, normalizer: Dict[str, float | str]) -> np.ndarray: if normalizer["method"] == "none": return signal.astype(np.float32, copy=False) mean = float(normalizer["mean"]) std = float(normalizer["std"]) return ((signal - mean) / std).astype(np.float32, copy=False)normalizer = fit_normalizer(regions_by_class, NORMALIZATION)print("归一化配置:", normalizer)## 5. 生成窗口并组装张量#只打乱训练集。验证集和测试集保持确定顺序,便于复现和定位错误样本。def make_windows(signal: np.ndarray, window_size: int, stride: int) -> np.ndarray: if window_size <= 0 or stride <= 0: raise ValueError("window_size 和 stride 必须为正整数") if len(signal) < window_size: raise ValueError("信号长度小于窗口长度") windows = np.lib.stride_tricks.sliding_window_view(signal, window_size)[::stride] return np.ascontiguousarray(windows, dtype=np.float32)def build_split( regions: Dict[int, Dict[str, np.ndarray]], split_name: str, window_size: int, stride: int, normalizer: Dict[str, float | str],) -> Tuple[torch.Tensor, torch.Tensor, Dict[int, int]]: x_parts: List[np.ndarray] = [] y_parts: List[np.ndarray] = [] counts: Dict[int, int] = {} for spec in CLASS_SPECS: label = spec["label"] normalized = apply_normalizer(regions[label][split_name], normalizer) windows = make_windows(normalized, window_size, stride) x_parts.append(windows) y_parts.append(np.full(len(windows), label, dtype=np.int64)) counts[label] = len(windows) x = np.concatenate(x_parts, axis=0) y = np.concatenate(y_parts, axis=0) # 训练集随机打乱;验证集和测试集保持稳定顺序。 if split_name == "train": rng = np.random.default_rng(SEED) order = rng.permutation(len(y)) x, y = x[order], y[order] return torch.from_numpy(x), torch.from_numpy(y), countsprepared = {}count_report = {}for split_name in ("train", "val", "test"): x, y, counts = build_split( regions_by_class, split_name, WINDOW_SIZE, STRIDE, normalizer, ) prepared[split_name] = {"x": x, "y": y} count_report[split_name] = counts print(f"{split_name:>5}: x={tuple(x.shape)}, y={tuple(y.shape)}")## 6. 保存单一数据文件#保存内容包括三个数据集、类别名称、归一化参数、数据制作配置以及每类样本数量。训练 Notebook 不再依赖 CSV 或 Joblib 中间文件。dataset_bundle = { "train": prepared["train"], "val": prepared["val"], "test": prepared["test"], "class_names": [spec["name"] for spec in CLASS_SPECS], "class_specs": CLASS_SPECS, "normalizer": normalizer, "sample_counts_by_class": count_report, "config": { "seed": SEED, "max_points": MAX_POINTS, "window_size": WINDOW_SIZE, "stride": STRIDE, "split_ratios": SPLIT_RATIOS, "gap_points": GAP_POINTS, "normalization": NORMALIZATION, },}torch.save(dataset_bundle, OUTPUT_FILE)print(f"已保存:{OUTPUT_FILE.resolve()}")print(f"文件大小:{OUTPUT_FILE.stat().st_size / 1024 / 1024:.2f} MB")## 7. 保存后校验#重新读取文件并检查形状、数据类型和标签范围。该单元用于发现保存过程或路径配置问题。def safe_torch_load(path: Path): try: return torch.load(path, map_location="cpu", weights_only=False) except TypeError: # 兼容较旧版本 PyTorch return torch.load(path, map_location="cpu")loaded = safe_torch_load(OUTPUT_FILE)num_classes = len(loaded["class_names"])for split_name in ("train", "val", "test"): x = loaded[split_name]["x"] y = loaded[split_name]["y"] assert x.dtype == torch.float32 assert y.dtype == torch.int64 assert x.ndim == 2 and x.shape[1] == WINDOW_SIZE assert len(x) == len(y) assert int(y.min()) >= 0 and int(y.max()) < num_classes print( f"{split_name:>5}: samples={len(y)}, " f"x_dtype={x.dtype}, y_range=[{int(y.min())}, {int(y.max())}]" )print("数据文件校验通过。")2.4故障分类示例在数据集处理划分完之后,我们使用LSTM 网络用于演示轴承故障分类过程,首先读取并校验已经制作好的 CWRU 训练集、验证集和测试集,将每个长度为 1024 的一维振动信号按照 FRAME_SIZE 自动重塑为 LSTM 所需的三维时序输入,例如由 [batch, 1024] 转换为 [batch, 32, 32];随后通过多层隐藏维度依次为 256、128 和 64 的 LSTM 提取时序特征,选取最后一层最后一个时间步的输出,并经过全连接层、ReLU、Dropout 和最终分类层得到各故障类别的预测结果;训练阶段使用交叉熵损失和 Adam 优化器,并结合梯度裁剪、验证集评估、早停机制和最佳模型参数保存,提高训练稳定性并防止过拟合;训练结束后重新加载验证准确率最高的模型,在测试集上输出分类报告和混淆矩阵,同时提取最后一层 LSTM 特征进行 t-SNE 可视化,此外还可通过 torchinfo.summary 查看各层输出形状和模型参数量,从而形成从数据加载、模型构建、训练验证到测试分析的完整故障诊断流程。(代码如下,也可点击左下角阅读原文获取)from __future__ import annotations"""纯堆叠 LSTM 故障分类完整代码数据要求--------默认读取由“CWRU数据集制作_重构版.ipynb”生成的 cwru_dataset.pt。文件结构应包含: bundle["train"]["x"], bundle["train"]["y"] bundle["val"]["x"], bundle["val"]["y"] bundle["test"]["x"], bundle["test"]["y"] bundle["class_names"] bundle["config"]["window_size"]模型结构--------一维信号 -> 自动分帧 -> 多层堆叠 LSTM -> 最后时间步 -> 全连接分类头说明----1. 特征提取部分只使用 LSTM,不使用 CNN、注意力、LayerNorm 或帧编码器。2. 通过 FRAME_SIZE 控制输入重塑方式。3. 验证集和测试集不打乱、不丢弃最后一个批次。4. 保存真正的最佳 state_dict,测试前重新加载。5. 不保存多余的中间数据文件;默认也不保存图片。"""import copyimport osimport randomimport timefrom pathlib import Pathfrom typing import Dict, Optional, Sequence, Tupleimport matplotlib.pyplot as pltimport numpy as npimport torchimport torch.nn as nnfrom sklearn.manifold import TSNEfrom sklearn.metrics import ( ConfusionMatrixDisplay, classification_report, confusion_matrix,)from torch.utils.data import DataLoader, TensorDataset# ============================================================# 1. 参数配置# ============================================================SEED = 100# 数据文件:由数据集制作代码生成DATASET_FILE = Path("cwru_dataset.pt")# 仅保存一个最佳模型检查点OUTPUT_DIR = Path("lstm_outputs")CHECKPOINT_FILE = OUTPUT_DIR / "best_stacked_lstm_checkpoint.pt"# 数据加载BATCH_SIZE = 32NUM_WORKERS = 0 # Windows/Jupyter 环境建议设为 0CPU_NUM_THREADS = min(4, os.cpu_count() or 1)# 训练参数EPOCHS = 100LEARNING_RATE = 1e-3WEIGHT_DECAY = 0.0PATIENCE = 15 # 设为 0 表示关闭早停GRAD_CLIP_NORM = 5.0 # 设为 None 表示关闭梯度裁剪# 模型参数# 对于长度为 1024 的信号:# FRAME_SIZE=32 -> [B, 32, 32]# FRAME_SIZE=16 -> [B, 64, 16]# FRAME_SIZE=64 -> [B, 16, 64]FRAME_SIZE = 32HIDDEN_SIZES = [256, 128,64]CLASSIFIER_DIM = 64DROPOUT_RATE = 0.5# 结果展示SHOW_TORCHINFO = TrueRUN_TSNE = TrueSAVE_FIGURES = False # 默认只显示图片,不保存图片文件DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")# ============================================================# 2. 通用工具# ============================================================def set_seed(seed: int) -> None: """固定随机种子,尽量提高实验可复现性。""" random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed(seed) torch.cuda.manual_seed_all(seed) # 确定性设置可能略微降低运行速度 torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = Falsedef safe_torch_load(path: Path, map_location: str | torch.device = "cpu"): """兼容不同 PyTorch 版本的 torch.load。""" try: return torch.load( path, map_location=map_location, weights_only=False, ) except TypeError: return torch.load(path, map_location=map_location)def validate_dataset_bundle(bundle: dict) -> None: """检查数据文件是否包含训练所需字段。""" required_splits = ("train", "val", "test") for split_name in required_splits: if split_name not in bundle: raise KeyError(f"数据文件缺少字段:{split_name}") split = bundle[split_name] if "x" not in split or "y" not in split: raise KeyError( f"数据文件中的 {split_name} 必须包含 x 和 y" ) x = split["x"] y = split["y"] if not isinstance(x, torch.Tensor): raise TypeError(f"{split_name}.x 必须是 torch.Tensor") if not isinstance(y, torch.Tensor): raise TypeError(f"{split_name}.y 必须是 torch.Tensor") if x.ndim != 2: raise ValueError( f"{split_name}.x 应为 [样本数, 信号长度]," f"实际形状为 {tuple(x.shape)}" ) if y.ndim != 1: raise ValueError( f"{split_name}.y 应为一维标签," f"实际形状为 {tuple(y.shape)}" ) if len(x) != len(y): raise ValueError( f"{split_name} 的样本数与标签数不一致:" f"{len(x)} != {len(y)}" ) if len(x) == 0: raise ValueError(f"{split_name} 中没有样本") if "class_names" not in bundle: raise KeyError("数据文件缺少 class_names")def create_data_loader( x: torch.Tensor, y: torch.Tensor, batch_size: int, shuffle: bool, seed: int,) -> DataLoader: """创建 DataLoader。""" dataset = TensorDataset(x.float(), y.long()) generator = None if shuffle: generator = torch.Generator() generator.manual_seed(seed) return DataLoader( dataset, batch_size=batch_size, shuffle=shuffle, drop_last=False, num_workers=NUM_WORKERS, pin_memory=(DEVICE.type == "cuda"), generator=generator, )# ============================================================# 3. 纯堆叠 LSTM 分类模型# ============================================================class StackedLSTMClassifier(nn.Module): """ 多层堆叠 LSTM 故障分类模型。 输入 ---- x: [batch_size, signal_length] 模型内部自动重塑为 ------------------ [batch_size, time_steps, frame_size] 其中 ---- time_steps = signal_length // frame_size 特征提取部分只由多个 LSTM 层组成。 """ def __init__( self, frame_size: int, hidden_sizes: Sequence[int], num_classes: int, classifier_dim: int = 64, dropout_rate: float = 0.5, ) -> None: super().__init__() if frame_size <= 0: raise ValueError("frame_size 必须为正整数") if not hidden_sizes: raise ValueError("hidden_sizes 不能为空") if any(size <= 0 for size in hidden_sizes): raise ValueError("hidden_sizes 中的维度必须为正整数") if num_classes <= 1: raise ValueError("num_classes 必须大于 1") if not 0.0 <= dropout_rate < 1.0: raise ValueError("dropout_rate 必须位于 [0, 1)") self.frame_size = int(frame_size) self.hidden_sizes = list(hidden_sizes) # 各层隐藏维度可以不同,因此使用 ModuleList 分别构建。 self.lstm_layers = nn.ModuleList() current_input_dim = self.frame_size for hidden_dim in self.hidden_sizes: self.lstm_layers.append( nn.LSTM( input_size=current_input_dim, hidden_size=hidden_dim, num_layers=1, batch_first=True, ) ) current_input_dim = hidden_dim # 分类头不承担时序特征提取,只负责类别映射。 self.classifier = nn.Sequential( nn.Linear(self.hidden_sizes[-1], classifier_dim), nn.ReLU(), nn.Dropout(dropout_rate), nn.Linear(classifier_dim, num_classes), ) def reshape_signal(self, x: torch.Tensor) -> torch.Tensor: """把一维信号自动划分成多个连续小帧。""" if x.ndim != 2: raise ValueError( "模型期望输入形状为 [batch_size, signal_length]," f"实际输入形状为 {tuple(x.shape)}" ) signal_length = x.size(1) if signal_length % self.frame_size != 0: raise ValueError( f"信号长度 {signal_length} 不能被 " f"FRAME_SIZE={self.frame_size} 整除" ) # -1 自动计算时间步数。 return x.reshape(x.size(0), -1, self.frame_size) def forward_features(self, x: torch.Tensor) -> torch.Tensor: """ 返回最后一层 LSTM 最后一个时间步的特征。 该特征可用于 t-SNE 或其他特征分析。 """ sequence = self.reshape_signal(x) for lstm_layer in self.lstm_layers: sequence, _ = lstm_layer(sequence) return sequence[:, -1, :] def forward(self, x: torch.Tensor) -> torch.Tensor: features = self.forward_features(x) logits = self.classifier(features) return logits# =========================# 创建纯堆叠 LSTM 模型# =========================NUM_CLASSES=10SIGNAL_LENGTH=1024model = StackedLSTMClassifier( frame_size=FRAME_SIZE, hidden_sizes=HIDDEN_SIZES, num_classes=NUM_CLASSES, classifier_dim=CLASSIFIER_DIM, dropout_rate=DROPOUT_RATE).to(DEVICE)# =========================# 查看网络结构及参数量# 输入形状:[batch_size, signal_length]# 模型内部会将 [32, 1024] 重塑为 [32, 32, 32]# =========================from torchinfo import summarysummary( model, input_size=(BATCH_SIZE, SIGNAL_LENGTH), device=str(DEVICE))# ============================================================# 4. 单轮训练与验证# ============================================================def run_epoch( model: nn.Module, loader: DataLoader, criterion: nn.Module, optimizer: Optional[torch.optim.Optimizer] = None,) -> Tuple[float, float]: """ 运行一个训练轮次或验证轮次。 optimizer 不为 None:训练模式 optimizer 为 None:验证/测试模式 """ is_training = optimizer is not None model.train(is_training) total_loss = 0.0 total_correct = 0 total_samples = 0 for signals, labels in loader: signals = signals.to(DEVICE, non_blocking=True) labels = labels.to(DEVICE, non_blocking=True) if is_training: optimizer.zero_grad(set_to_none=True) with torch.set_grad_enabled(is_training): logits = model(signals) loss = criterion(logits, labels) if is_training: loss.backward() if GRAD_CLIP_NORM is not None: nn.utils.clip_grad_norm_( model.parameters(), max_norm=GRAD_CLIP_NORM, ) optimizer.step() current_batch_size = labels.size(0) total_loss += loss.item() * current_batch_size total_correct += ( logits.argmax(dim=1) == labels ).sum().item() total_samples += current_batch_size if total_samples == 0: raise RuntimeError("DataLoader 中没有可用样本") average_loss = total_loss / total_samples accuracy = total_correct / total_samples return average_loss, accuracy# ============================================================# 5. 完整训练流程# ============================================================def train_model( model: nn.Module, train_loader: DataLoader, val_loader: DataLoader, criterion: nn.Module, optimizer: torch.optim.Optimizer, epochs: int, patience: int, checkpoint_file: Path, model_config: Dict, class_names: Sequence[str], dataset_config: Dict,) -> Tuple[Dict[str, list], int, float]: """训练模型,并保存验证集准确率最高的参数。""" history = { "train_loss": [], "train_accuracy": [], "val_loss": [], "val_accuracy": [], } best_state = copy.deepcopy(model.state_dict()) best_val_accuracy = float("-inf") best_epoch = 0 epochs_without_improvement = 0 start_time = time.time() for epoch in range(1, epochs + 1): train_loss, train_accuracy = run_epoch( model=model, loader=train_loader, criterion=criterion, optimizer=optimizer, ) with torch.no_grad(): val_loss, val_accuracy = run_epoch( model=model, loader=val_loader, criterion=criterion, optimizer=None, ) history["train_loss"].append(train_loss) history["train_accuracy"].append(train_accuracy) history["val_loss"].append(val_loss) history["val_accuracy"].append(val_accuracy) print( f"Epoch {epoch:03d}/{epochs:03d} | " f"train loss={train_loss:.6f}, " f"acc={train_accuracy:.4f} | " f"val loss={val_loss:.6f}, " f"acc={val_accuracy:.4f}" ) if val_accuracy > best_val_accuracy: best_val_accuracy = val_accuracy best_epoch = epoch best_state = copy.deepcopy(model.state_dict()) epochs_without_improvement = 0 else: epochs_without_improvement += 1 if patience > 0 and epochs_without_improvement >= patience: print(f"连续 {patience} 轮验证准确率未提升,提前停止训练。") break # 将内存中的模型恢复到真正的最佳状态。 model.load_state_dict(best_state) checkpoint_file.parent.mkdir(parents=True, exist_ok=True) checkpoint = { "model_state_dict": best_state, "model_config": model_config, "best_epoch": best_epoch, "best_val_accuracy": best_val_accuracy, "history": history, "class_names": list(class_names), "dataset_config": dataset_config, "seed": SEED, } torch.save(checkpoint, checkpoint_file) elapsed = time.time() - start_time print("-" * 80) print(f"训练耗时:{elapsed:.2f} 秒") print(f"最佳轮次:{best_epoch}") print(f"最佳验证准确率:{best_val_accuracy:.4f}") print(f"最佳模型已保存:{checkpoint_file.resolve()}") return history, best_epoch, best_val_accuracy# ============================================================# 6. 加载最佳模型# ============================================================def load_best_model( checkpoint_file: Path, device: torch.device,) -> Tuple[StackedLSTMClassifier, dict]: """从检查点重新构造并加载最佳模型。""" checkpoint = safe_torch_load( checkpoint_file, map_location=device, ) model = StackedLSTMClassifier( **checkpoint["model_config"] ).to(device) model.load_state_dict( checkpoint["model_state_dict"] ) model.eval() return model, checkpoint# ============================================================# 7. 测试预测与中间特征提取# ============================================================def predict_with_features( model: StackedLSTMClassifier, loader: DataLoader,) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """返回真实标签、预测标签以及 LSTM 特征。""" true_labels = [] predicted_labels = [] feature_list = [] model.eval() with torch.no_grad(): for signals, labels in loader: signals = signals.to(DEVICE, non_blocking=True) features = model.forward_features(signals) logits = model.classifier(features) predictions = logits.argmax(dim=1) true_labels.append(labels.cpu().numpy()) predicted_labels.append(predictions.cpu().numpy()) feature_list.append(features.cpu().numpy()) return ( np.concatenate(true_labels), np.concatenate(predicted_labels), np.concatenate(feature_list), )# ============================================================# 8. 结果绘图# ============================================================from sklearn.preprocessing import StandardScalerfrom matplotlib.colors import LinearSegmentedColormapdef plot_training_history( history: Dict[str, list], save_figures: bool = False,) -> None: """ 在同一张画布中绘制训练过程。 左图:训练损失和验证损失; 右图:训练准确率和验证准确率。 """ epochs = np.arange( 1, len(history["train_loss"]) + 1, ) # 中文字体设置 plt.rcParams["font.sans-serif"] = [ "Microsoft YaHei", "SimHei", "Arial Unicode MS", "DejaVu Sans", ] plt.rcParams["axes.unicode_minus"] = False # 创建一行两列子图 fig, axes = plt.subplots( 1, 2, figsize=(14, 6), dpi=150, ) # 为防止训练轮数较多时标记过于密集, # 大约每隔总轮数的1/20显示一个标记 marker_interval = max(1, len(epochs) // 20) # ======================================================== # 左图:训练损失和验证损失 # ======================================================== axes[0].plot( epochs, history["train_loss"], label="Train loss", color="blue", linewidth=1.6, marker="^", markersize=5, markevery=marker_interval, ) axes[0].plot( epochs, history["val_loss"], label="Validation loss", color="red", linewidth=1.6, marker="+", markersize=7, markevery=marker_interval, ) axes[0].set_xlabel( "Epochs", fontsize=12, ) axes[0].set_ylabel( "Loss", fontsize=12, ) axes[0].set_title( "Training and Validation Loss", fontsize=15, pad=12, ) axes[0].tick_params( axis="both", labelsize=10, ) axes[0].legend( fontsize=11, frameon=True, ) axes[0].grid( True, linestyle="--", linewidth=0.6, alpha=0.3, ) axes[0].set_axis below(True) # ======================================================== # 右图:训练准确率和验证准确率 # ======================================================== axes[1].plot( epochs, history["train_accuracy"], label="Train accuracy", color="orange", linewidth=1.6, marker="o", markersize=5, markevery=marker_interval, ) axes[1].plot( epochs, history["val_accuracy"], label="Validation accuracy", color="green", linewidth=1.6, marker="*", markersize=7, markevery=marker_interval, ) axes[1].set_xlabel( "Epochs", fontsize=12, ) axes[1].set_ylabel( "Accuracy", fontsize=12, ) axes[1].set_title( "Training and Validation Accuracy", fontsize=15, pad=12, ) axes[1].tick_params( axis="both", labelsize=10, ) axes[1].legend( fontsize=11, frameon=True, ) axes[1].grid( True, linestyle="--", linewidth=0.6, alpha=0.3, ) axes[1].set_axis below(True) # 准确率若采用0~1表示,可固定纵轴范围 axes[1].set_ylim(0.0, 1.02) # 去掉顶部和右侧边框,使图像更加简洁 for ax in axes: ax.spines["top"].set_visible(False) ax.spines["right"].set_visible(False) # 自动调整子图间距 fig.tight_layout() # 保存整张训练过程图 if save_figures: OUTPUT_DIR.mkdir( parents=True, exist_ok=True, ) fig.savefig( OUTPUT_DIR / "training_history.png", dpi=300, bbox_inches="tight", ) plt.show()def plot_confusion_matrix( y_true: np.ndarray, y_pred: np.ndarray, class_names: Sequence[str], save_figures: bool = False,) -> None: """ 绘制学术风格混淆矩阵。 颜色表示每个真实类别内部的预测百分比, 单元格同时显示样本数量和所占百分比。 """ labels = list(range(len(class_names))) # 原始计数混淆矩阵 cm = confusion_matrix( y_true, y_pred, labels=labels, ) # 按真实类别进行行归一化 row_sums = cm.sum(axis=1, keepdims=True) cm_percent = np.divide( cm, row_sums, out=np.zeros_like(cm, dtype=float), where=row_sums != 0, ) * 100.0 figure_width = max(9, len(class_names) * 0.9) figure_height = max(8, len(class_names) * 0.8) fig, ax = plt.subplots( figsize=(figure_width, figure_height) ) # 自定义浅蓝色学术风格色图 academic_blue = LinearSegmentedColormap.from_list( "academic_blue", [ "#F8FBFD", "#E5F1F7", "#C7E1ED", "#98C5DB", "#68A5C8", ], ) image = ax.imshow( cm_percent, interpolation="nearest", cmap=academic_blue, vmin=0, vmax=100, aspect="equal", ) # 颜色条 colorbar = fig.colorbar( image, ax=ax, fraction=0.046, pad=0.04, ) colorbar.set_label( "Percentage (%)", rotation=90, labelpad=12, ) colorbar.outline.set_linewidth(0.6) # 坐标轴刻度 ax.set_xticks(np.arange(len(class_names))) ax.set_yticks(np.arange(len(class_names))) ax.set_xticklabels( class_names, rotation=45, ha="right", ) ax.set_yticklabels(class_names) ax.set_xlabel("Predicted label") ax.set_ylabel("True label") ax.set_title("Confusion matrix") # 单元格标注:数量 + 百分比 threshold = 60.0 for row_index in range(cm.shape[0]): for column_index in range(cm.shape[1]): count = cm[row_index, column_index] percentage = cm_percent[row_index, column_index] text_color = ( "white" if percentage >= threshold else "#263746" ) ax.text( column_index, row_index, f"{count}\n({percentage:.1f}%)", ha="center", va="center", fontsize=8.5, color=text_color, ) # 绘制浅色单元格边界 ax.set_xticks( np.arange(-0.5, len(class_names), 1), minor=True, ) ax.set_yticks( np.arange(-0.5, len(class_names), 1), minor=True, ) ax.grid( which="minor", color="white", linestyle="-", linewidth=1.2, ) ax.tick_params( which="minor", bottom=False, left=False, ) ax.tick_params( axis="both", which="major", length=0, ) fig.tight_layout() if save_figures: OUTPUT_DIR.mkdir( parents=True, exist_ok=True, ) fig.savefig( OUTPUT_DIR / "confusion_matrix.png", dpi=300, bbox_inches="tight", ) plt.show()def plot_tsne( features: np.ndarray, labels: np.ndarray, class_names: Sequence[str], save_figures: bool = False,) -> None: """ 使用最后一层 LSTM 特征绘制学术风格 t-SNE 图。 """ sample_count = len(features) if sample_count < 6: print("测试样本过少,跳过 t-SNE。") return # perplexity 必须小于样本数量 perplexity = min( 30, max(5, (sample_count - 1) // 3), ) # 特征标准化,减小不同特征尺度对 t-SNE 的影响 standardized_features = StandardScaler().fit_transform( features ) # t-SNE降维 embedding = TSNE( n_components=2, perplexity=perplexity, init="pca", learning_rate="auto", random_state=SEED, ).fit_transform(standardized_features) fig, ax = plt.subplots(figsize=(9, 7)) # 使用Matplotlib的标准分类配色 color_map = plt.get_cmap( "tab10", len(class_names), ) for class_index, class_name in enumerate(class_names): mask = labels == class_index if not np.any(mask): continue ax.scatter( embedding[mask, 0], embedding[mask, 1], s=36, alpha=0.82, color=color_map(class_index), edgecolors="white", linewidths=0.45, label=class_name, zorder=3, ) ax.set_xlabel("t-SNE dimension 1") ax.set_ylabel("t-SNE dimension 2") ax.set_title("t-SNE visualization of final LSTM features") # 设置网格位于散点下方 ax.set_axis below(True) ax.grid( True, linestyle="--", linewidth=1, alpha=0.35, ) # 去除顶部和右侧边框 ax.spines["top"].set_visible(True) ax.spines["right"].set_visible(True) # 减弱左侧和底部边框 ax.spines["left"].set_linewidth(0.8) ax.spines["bottom"].set_linewidth(0.8) # 图例放在图像右侧,避免遮挡散点 ax.legend( loc="center left", bbox_to_anchor=(1.02, 0.5), fontsize=8.5, frameon=False, borderaxespad=0.0, ) ax.margins(0.06) fig.tight_layout() if save_figures: OUTPUT_DIR.mkdir( parents=True, exist_ok=True, ) fig.savefig( OUTPUT_DIR / "tsne_features.png", dpi=300, bbox_inches="tight", ) plt.show() plt.show()# ============================================================# 9. 主程序# ============================================================def main() -> None: set_seed(SEED) if DEVICE.type == "cpu": torch.set_num_threads(CPU_NUM_THREADS) print(f"运行设备:{DEVICE}") if DEVICE.type == "cpu": print(f"PyTorch CPU 线程数:{torch.get_num_threads()}") if not DATASET_FILE.exists(): raise FileNotFoundError( f"找不到数据文件:{DATASET_FILE.resolve()}\n" "请先运行数据集制作代码生成 cwru_dataset.pt。" ) bundle = safe_torch_load( DATASET_FILE, map_location="cpu", ) validate_dataset_bundle(bundle) class_names = list(bundle["class_names"]) num_classes = len(class_names) train_x = bundle["train"]["x"].float() train_y = bundle["train"]["y"].long() val_x = bundle["val"]["x"].float() val_y = bundle["val"]["y"].long() test_x = bundle["test"]["x"].float() test_y = bundle["test"]["y"].long() signal_length = int(train_x.shape[1]) if val_x.shape[1] != signal_length or test_x.shape[1] != signal_length: raise ValueError("训练、验证和测试集的信号长度不一致") if signal_length % FRAME_SIZE != 0: raise ValueError( f"信号长度 {signal_length} 不能被 " f"FRAME_SIZE={FRAME_SIZE} 整除" ) all_labels = torch.cat([train_y, val_y, test_y]) if all_labels.min().item() < 0: raise ValueError("标签中存在负数") if all_labels.max().item() >= num_classes: raise ValueError( "标签最大值超过 class_names 对应的类别数" ) time_steps = signal_length // FRAME_SIZE print("-" * 80) print(f"类别数量:{num_classes}") print(f"类别名称:{class_names}") print(f"单个样本长度:{signal_length}") print( f"LSTM 输入形状:[batch, {time_steps}, {FRAME_SIZE}]" ) print( f"训练/验证/测试样本数:" f"{len(train_x)}/{len(val_x)}/{len(test_x)}" ) train_loader = create_data_loader( train_x, train_y, batch_size=BATCH_SIZE, shuffle=True, seed=SEED, ) val_loader = create_data_loader( val_x, val_y, batch_size=BATCH_SIZE, shuffle=False, seed=SEED, ) test_loader = create_data_loader( test_x, test_y, batch_size=BATCH_SIZE, shuffle=False, seed=SEED, ) model_config = { "frame_size": FRAME_SIZE, "hidden_sizes": HIDDEN_SIZES, "num_classes": num_classes, "classifier_dim": CLASSIFIER_DIM, "dropout_rate": DROPOUT_RATE, } model = StackedLSTMClassifier( **model_config ).to(DEVICE) parameter_count = sum( parameter.numel() for parameter in model.parameters() if parameter.requires_grad ) print("-" * 80) print(model) print(f"可训练参数量:{parameter_count:,}") if SHOW_TORCHINFO: try: from torchinfo import summary summary( model, input_size=(BATCH_SIZE, signal_length), device=str(DEVICE), ) except ImportError: print( "未安装 torchinfo,跳过网络结构摘要。" "可执行:pip install torchinfo" ) except Exception as exc: print(f"torchinfo 摘要生成失败:{exc}") criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam( model.parameters(), lr=LEARNING_RATE, weight_decay=WEIGHT_DECAY, ) history, best_epoch, best_val_accuracy = train_model( model=model, train_loader=train_loader, val_loader=val_loader, criterion=criterion, optimizer=optimizer, epochs=EPOCHS, patience=PATIENCE, checkpoint_file=CHECKPOINT_FILE, model_config=model_config, class_names=class_names, dataset_config=bundle.get("config", {}), ) plot_training_history( history, save_figures=SAVE_FIGURES, ) # 测试前重新从磁盘加载最佳模型,避免误用最后一轮模型。 best_model, checkpoint = load_best_model( CHECKPOINT_FILE, DEVICE, ) print("-" * 80) print( f"已加载最佳模型:epoch={checkpoint['best_epoch']}, " f"val_acc={checkpoint['best_val_accuracy']:.4f}" ) y_true, y_pred, test_features = predict_with_features( best_model, test_loader, ) test_accuracy = float(np.mean(y_true == y_pred)) print("-" * 80) print(f"测试样本数:{len(y_true)}") print(f"测试准确率:{test_accuracy:.4f}") print() print( classification_report( y_true, y_pred, labels=list(range(num_classes)), target_names=class_names, digits=4, zero_division=0, ) ) plot_confusion_matrix( y_true, y_pred, class_names, save_figures=SAVE_FIGURES, ) if RUN_TSNE: plot_tsne( test_features, y_true, class_names, save_figures=SAVE_FIGURES, )2.5结果演示训练过程如图所示,经过连续15轮准确率未提升,提前停止训练。图7 西储大学轴承数据集训练过程准确率曲线和loss曲线如图所示,随着epoch增加,准确率不断提高,loss曲线也不断平稳。图8 西储大学训练过程中loss和准确率曲线对测试集使用保存的最佳模型验证,结果如图所示,代码计算了每个类别的不同实验指标,进一步分析了模型对不同故障的识别能力。图9 西储大学测试集实验结果混淆矩阵结果如图所示,其中纵轴表示数据的真实标签,横轴表示诊断标签,显然此次实验对正常标签、0.007-Ball、0.014-OuterRace6三类状态存在轻微误差。对应t-SNE图如图所示。图10 西储大学测试集混淆矩阵图11 西储大学测试集T-SNE图3 华中科技大学轴承数据集3.1数据集文件介绍华中科技大学轴承数据集小编之前详细介绍过(点击即达),这里只介绍下载后的文件夹及数据分布,如图所示,下载后主要有四个文件夹,其中打开原始数据文件夹如图12所示。图12 华中科技大学轴承数据集下载打开文件 图13 华中科技大学原始数据文件原始数据文件包括36个文件(9个状态乘以4个工作条件),每个文件为Excel格式。例如,文件名“0.5X_B_65Hz”表示在65Hz工作条件下发生滚动体中度故障,其中0.5X表示中度故障。故障状态用以下代码表示:H:健康I:内圈故障O:外圈故障B:球故障C:组合故障例如,“O_80Hz”表示在80Hz工作条件下,外圈重度故障。我们以B_65Hz文件为例,打开如图所示,其中所需的振动信号数据分别在3(X方向传感器)、4(Y方向传感器)、5(Z方向的传感器)列的23行以后。图14 打开B_65Hz文件3.2数据集提取划分本文选取65Hz工况的轴承数据,共9类状态数据文件,分别为:H_65Hz.xls、0.5X_B_65Hz.xls、0.5X_C_65Hz.xls 、0.5X_I_65Hz.xls、0.5X_O_65Hz.xls、B_65Hz.xls、C_65Hz.xls、I_65Hz.xls、O_65Hz.xls。在HUST轴承故障数据集的制作过程中,首先按照类别配置读取各个数据文件,跳过前22行并提取第3列振动信号(X方向传感器),将信号转换为 float32 一维数组并检查文件、类别标签、信号长度及异常值;随后对每类连续信号先按 6:2:2 划分训练、验证和测试区域,并在相邻区域间设置隔离段,以避免重叠滑窗造成数据泄漏;之后仅使用训练区域拟合可选的全局 Z-score 归一化参数,再以长度1024、步长512分别进行滑动窗口采样,其中只随机打乱训练集;最后将三个数据集的样本、标签、类别名称、归一化参数、样本数量和制作配置统一保存为 .pt 文件,并重新加载检查张量维度、数据类型、标签范围及 NaN/Inf,从而形成一套完整、可追溯且可直接供 LSTM 等故障分类模型使用的数据预处理流程。(注:与西储大学轴承数据集制作代码仅有数据集加载路径及方式区别,并且预处理的轴承故障数据集由cwru_dataset.pt变成HUST_dataset.pt。这里不再给出代码,可点击左下角阅读原文获取)3.3故障分类示例故障分类示例中,仍采用LSTM网络进行分类,其中只需将加载的预处理的轴承故障数据集由cwru_dataset.pt变成HUST_dataset.pt即可。这里不再给出代码,可点击左下角阅读原文获取 3.4结果演示 训练过程如图所示,经过连续15轮准确率未提升,提前停止训练。 图15 华中科技大学轴承数据集训练过程 准确率曲线和loss曲线如图所示,随着epoch增加,准确率不断提高,loss曲线也不断平稳。 图16 华中科技大学训练过程中loss和准确率曲线 对测试集使用保存的最佳模型验证,结果如图所示,代码计算了每个类别的不同实验指标,进一步分析了模型对不同故障的识别能力。 图17 华中科技大学测试集实验结果 混淆矩阵结果如图所示,其中纵轴表示数据的真实标签,横轴表示诊断标签,显然此次实验对0.5X_B、0.5X_C、O三类状态存在轻微误差。对应t-SNE图如图所示。 图18 华中科技大学测试集混淆矩阵 图19 华中科技大学测试集T-SNE图 4 渥太华轴承数据集4.1数据集文件介绍 渥太华轴承数据集小编之前详细介绍过(点击即达),这里只介绍下载后的文件夹及数据分布,如图所示,下载后主要有4个文件夹,其中打开原始数据文件夹如图所示,有5个文件夹分别代表五种健康状态:健康、内圈故障、外圈故障、球故障、复合故障。 图20 渥太华轴承数据集下载打开文件 以2Data collected from a bearing with inner race fault文件夹(内圈故障)为例,打开发现里面有A、B、C、D4个工况,每种工况有3个文件,共12个文件,其中A代表升速,B代表减速,C代表升速-减速,D代表减速-升速。 图21 打开Data collected from a bearing with inner race fault文件 Matlab打开I-A-1(升速工况的内圈故障),如图所示,其中‘Channel_1’是由加速度计测量的振动数据,也是实验常提取的数据。‘Channel_2’是由编码器测量的旋转速度数据。 图22 渥太华轴承数据集打开I-A-1文件 4.2数据集划分 本文选取升速工况数据作为实验数据,进行数据集划分,如图所示。 图23 本文中渥太华轴承实验数据 在渥太华轴承故障数据集的制作过程中,首先读取 H-A-1、B-A-1、I-A-1、O-A-1、C-A-1 五个 MAT 文件中的 Channel_1 振动信号,并为每类截取前150万个连续数据点;随后先按6:2:2的比例将各类信号连续划分为训练集、验证集和测试集,并在相邻区域之间设置1024点隔离区,避免重叠样本跨集 合造成数据泄漏;之后采用长度1024、步长512的滑动窗口生成样本,仅对训练集进行随机打乱,最后将数据、标签、类别名称、样本数量及相关配置统一保存为 Ottawa_dataset.pt,并重新加载检查数据形状、类型、标签范围和异常值,形成可直接用于深度学习模型训练的标准数据集。(点击左下角阅读原文获取) 4.3 故障分类 代码流程与逻辑与西储大学、华中科技大学相似,这里不再赘述。 (点击左下角阅读原文获取) 4.4 结果演示 训练过程如图所示,经过连续15轮准确率未提升,提前停止训练,结果显示模型对不通故障识别较差(渥太华是变工况数据,训练集测试集差距较大)。 图24 渥太华轴承数据集训练过程 准确率曲线和loss曲线如图所示,随着epoch增加,loss曲线和准确率准确率曲线变化较小。 图25 渥太华训练过程中loss和准确率曲线 对测试集使用保存的最佳模型验证,结果如图所示,代码计算了每个类别的不同实验指标,进一步分析了模型对不同故障的识别能力。 图26 渥太华测试集实验结果 混淆矩阵结果如图所示,其中纵轴表示数据的真实标签,横轴表示诊断标签,显然此次实验效果很差,需要采取针对变工况的方法或者其他模型。对应t-SNE图如图所示,总体模型对不同故障特征提取较差。 图27 渥太华测试集混淆矩阵 图28 渥太华测试集T-SNE图 5 江南大学轴承数据集 5.1数据集文件夹介绍 江南大学数据集文件如图所示,其中包含600rpm、800rpm、1000rpm3种不同转速工况,每种工况有4个健康状态分别为ib(内圈故障)、ob(外圈故障)、tb(滚动体故障)、n(正常),总共12个文件,例如ib600_2代表600/rpm工况的内圈故障。 图29 江南大学轴承数据集下载打开文件 我们以ib600-2为例,打开文件如图所示,第一列即为所需数据。 图30 打开ib600-2文件 5.2 数据集提取划分 本文选取600rpm转速工况的轴承数据作为故障分类的实验数据,如图所示。 图31 江南大学本文实验轴承数据集 在JNU轴承故障数据集的制作过程中,首先按照类别配置读取4个 CSV 文件,将每个文件中的连续信号展开为一维数组、截取指定长度并检查异常值,同时打印原始形状和前几个数据;随后不是先滑窗再随机划分,而是先将每类连续信号按 6:2:2 划分为训练、验证和测试区域,并在相邻区域之间设置隔离段,以避免重叠窗口造成数据泄漏;之后仅使用训练区域拟合可选的归一化参数,再以长度1024、步长512分别进行滑动窗口采样,其中只随机打乱训练集;最后将训练集、验证集、测试集、类别名称、样本数量、归一化参数和数据制作配置统一保存为 JNU_dataset.pt,并重新加载检查数据形状、类型、标签范围及 NaN/Inf,从而形成完整、规范且可复现的 JNU 故障分类数据预处理流程。5.3故障分类 代码流程与逻辑与西储大学、华中科技大学相似,这里不再赘述。 5.4 结果演示 训练过程如图所示,经过连续15轮准确率未提升,提前停止训练。 图32 江南大学轴承数据集训练过程 准确率曲线和loss曲线如图所示,随着epoch增加,准确率不断提高,训练集loss曲线逐渐平稳但是验证集loss曲线波动很大。 图33 江南大学训练过程中loss和准确率曲线 对测试集使用保存的最佳模型验证,结果如图所示,代码计算了每个类别的不同实验指标,进一步分析了模型对不同故障的识别能力。 图34 江南大学测试集实验结果 混淆矩阵结果如图所示,其中纵轴表示数据的真实标签,横轴表示诊断标签,显然此次实验对四类状态均存在误差。对应t-SNE图如图所示,总体模型对不同故障特征提取不是太好。 图35 江南大学测试集混淆矩阵 图36 江南大学测试集T-SNE图 6 WT齿轮箱数据集 WT齿轮箱承数据集小编之前详细介绍过(点击即达),这里只介绍下载后的文件夹及数据分布,如图所示,下载后有多个文件夹,文件夹代表不同健康状态:broken-断齿、healthy-健康、missing_tooth-缺齿、root_crack-齿根裂纹、wear-齿轮磨损。 打开broken数据文件夹,其中1和2分布代表两次安装下的采集数据。打开1文件夹。发现有多个工况的断齿数据集,其中B1_20代表20Hz转速下采集的齿轮箱数据集。matlab打开如图所示。其中前两列分别是X、Y方向的振动信号。 图37 WT齿轮箱数据集下载打开文件 图38 打开broken数据文件夹 图39 打开B1_20文件 4.2数据集划分 本文选取35Hz转速工况数据作为实验数据,进行数据集划分,如图所示。 图40 本文WT齿轮箱实验数据集 在WT齿轮箱故障数据集的制作过程中,首先按照类别配置读取 N1_35、B1_35、M1_35、R1_35、W1_35 五个文件,从变量名中包含 Data 的数据项中提取第一列振动信号,并截取每类前120000个连续数据点,同时检查信号长度、异常值并打印原始形状和前几个数据;随后对每类信号先按 6:2:2 的比例连续划分为训练、验证和测试区域,并在相邻区域之间保留隔离段,以避免重叠滑窗造成数据泄漏;之后采用长度1024、步长512分别生成样本,仅随机打乱训练集,最后将训练集、验证集、测试集、类别名称、样本数量、归一化参数和制作配置统一保存为 WT_dataset.pt,并重新加载校验数据形状、类型、标签范围及 NaN/Inf,从而形成完整、规范且可复现的 WT 故障分类数据预处理流程。4.3 故障分类 代码流程与逻辑与西储大学、华中科技大学相似,这里不再赘述。 4.4 结果演示 训练过程如图所示,经过连续15轮准确率未提升,提前停止训练,结果显示模型对不同故障识别较差,且训练集表现不错,存在过拟合。 图41 WT齿轮箱轴承数据集训练过程 准确率曲线和loss曲线如图所示,随着epoch增加,准确率准确率曲线升高,训练集loss曲线逐渐平稳,但验证集loss曲线波动很大。 图42 WT齿轮箱训练过程中loss和准确率曲线 对测试集使用保存的最佳模型验证,结果如图所示,代码计算了每个类别的不同实验指标,进一步分析了模型对不同故障的识别能力。 图43 WT齿轮箱测试集实验结果 混淆矩阵结果如图所示,其中纵轴表示数据的真实标签,横轴表示诊断标签,显然此次实验对五类状态均存在误差。对应t-SNE图如图所示,总体模型对不同故障特征提取不是太好。 图44 WT齿轮箱测试集混淆矩阵 图45 WT齿轮箱测试集T-SNE图 编辑:Leo校核:李正平、陈凯歌、曹希铭、Tina、白亮、任超、海洋、赵栓栓、Kira、陈宇航、王金、赵诚、肖鑫鑫、张优该文资料搜集自网络,仅用作学术分享,不做商业用途,若侵权,后台联系小编进行删除来源:故障诊断与python学习

未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈