TBM滚刀状态识别:CNN/LSTM/GRU/SVM与随机森林模型对比
简介面向滚刀磨损状态识别需求资源包提供一整套基于机器学习方法的项目源码与配套数据集。项目覆盖卷积神经网络、长短期记忆网络、门控循环单元、支持向量机、随机森林等多种算法从数据预处理、特征生成到模型训练与测试均有完整脚本实现。压缩包内共有十五个文件以八个脚本和四个数据文件为核心另含特征可视化图片、说明文档及版本忽略配置整体大小约二点五四兆字节结构精简便于直接运行和二次扩展。数据集选用铣刀一号作为训练样本铣刀四号和六号作为测试样本每把刀具均包含初期磨损、正常磨损、急剧磨损三类样本可系统比较不同模型在时间序列分类任务上的鲁棒性与准确率。已有二百八十人浏览学习适合智能监测领域的研究者、工程师或高年级学生借助源码快速复现实验流程并根据自身数据调整模型结构或评估策略为实际产线刀具状态识别提供参考实现。1. 滚刀状态识别到底在解决什么问题TBM 施工中最烧钱的停机原因某施工段刀盘掘进到第 12 环时异常停机拆开检查发现边缘滚刀偏磨到只剩刀毂刀圈早就该换了。换一把滚刀加上停机时间直接损失按小时计算这还没算刀盘拆装的安全风险。滚刀状态识别这个方向就是用刀盘振动、推进压力、扭矩这些能在线测的信号在滚刀彻底报废前预判出「偏磨」「崩刃」「卡阻」等状态把被动拆机变成主动换刀。标题里的 CNN、LSTM、GRU、SVM、随机森林本质上是在同一条数据流水线上做模型选型对比哪些模型适合小样本哪些模型能吃到时序特征哪些模型部署时最省心。适合正在做机械设备状态监测、故障诊断课题或者负责盾构/TBM 施工设备管理的工程师参考。2. 先把数据底子打好滚刀状态数据的采集、切分与预处理滚刀状态识别做得成不成七成取决于数据剩下三成才是模型。很多项目拿到源码跑不动、跑起来精度又很低问题基本都出在数据切分和标签对齐上。这一章把采集方案、滑窗切分和特征工程一次讲透。2.1 滚刀状态数据从哪来振动、推进参数与换刀记录的标签对齐滚刀状态识别的原始信号通常来自刀盘附近安装的振动加速度传感器以及液压系统自带的推进压力、刀盘扭矩、贯入度、转速等运行参数。常见做法是采集多个通道刀盘振动 X/Y/Z 轴加速度、推进油缸压力、刀盘扭矩。采样率方面振动信号常见 1kHz 到 20kHz推进压力和扭矩这类慢变信号可以降到 10Hz 到 50Hz合并时统一重采样。标签来源是换刀记录。每把滚刀从装上新刀圈到被拆下来更换会有一个完整生命周期施工单位在换刀时会写状态描述比如「偏磨」「崩刃」「正常磨损」。这些记录就是分类标签的真值。工程上最稳的对齐逻辑是按刀号和时间戳把换刀记录映射回传感器时间轴每个时刻的振动特征对应一个滚刀状态标签。import pandas as pd import numpy as np # 换刀记录示例每把刀的安装与拆卸时间、状态标签 tool_records pd.DataFrame({ tool_id: [1, 1, 2, 2], install_time: [2024-03-01 08:00:00, 2024-04-01 08:00:00, 2024-03-05 08:00:00, 2024-04-10 08:00:00], remove_time: [2024-03-28 08:00:00, 2024-04-20 08:00:00, 2024-04-01 08:00:00, 2024-04-25 08:00:00], state_label: [normal, abnormal_wear, partial_wear, normal] }) # 振动信号长表time, tool_id, vib_x, vib_y, vib_z, thrust, torque sensor_df pd.read_csv(vibration_log.csv, parse_dates[time]) # 把传感器每一秒的读数按刀号与时间区间打上状态标签 sensor_df[state_label] None for _, rec in tool_records.iterrows(): mask ((sensor_df[time] rec[install_time]) (sensor_df[time] rec[remove_time]) (sensor_df[tool_id] rec[tool_id])) sensor_df.loc[mask, state_label] rec[state_label] sensor_df sensor_df.dropna(subset[state_label])install_time和remove_time必须精确到分钟级因为滚刀磨损是渐进过程但换刀瞬间信号会突变。标签对齐时最怕两种错误一是换刀记录本身漏填二是把同一把刀在多个刀位间的换装时间混淆。所以我在实际项目中会让施工方额外提供一个「拆刀日期 装机日期 刀位号」的表格按三个字段联合对齐而不是只靠一个时间戳。2.2 滑动窗口切分与特征工程把时间序列变成模型能吃的样本滚刀振动信号是非平稳时间序列不能把一整段几小时的数据直接喂给模型。常规做法是用滑动窗口切成长度固定的样本窗口长度取振动信号单圈旋转的整数倍比如 256 点、512 点或 1024 点。窗口重叠率常见 50%用来做数据增强但重叠样本在训练集和验证集之间会造成数据泄漏后面避坑章节会专门讲。def sliding_window(data, window_size512, step_size256): 滑窗切分默认 50% 重叠。返回样本数组和对应时间戳。 samples [] timestamps [] for start in range(0, len(data) - window_size 1, step_size): end start window_size samples.append(data[start:end]) timestamps.append(data.index[end]) return np.stack(samples), np.array(timestamps) # 假设 sensor_df 已按时间排序且只保留数值列 feature_cols [vib_x, vib_y, vib_z, thrust, torque] values sensor_df[feature_cols].values win_size 512 step 256 win_data, _ sliding_window(values, window_sizewin_size, step_sizestep)window_size512对应大约 0.5 秒的振动数据1kHz 采样足以覆盖滚刀转一圈产生的冲击周期。step_size越大样本数越少训练越快但会漏掉状态切换的边沿我一般先保持 50% 重叠模型效果稳定后再加大步长减少冗余样本。特征工程方面常见的做法是先算时域统计特征再用传统模型包括均方根值、峭度、峰值因子、方差、峰峰值、频域质心。峭度对崩刃和偏磨这类冲击性状态很敏感正常磨损振动幅值平稳峭度接近 3出现崩刃时脉冲冲击增多峭度会显著上升。这个特征在滚刀状态的早期筛选里几乎是必看的。from scipy.stats import kurtosis from scipy.fft import rfft, rfftfreq def extract_features_from_windows(win_data, sample_rate1000): 每个滑窗样本提取一组统计特征。 features [] for w in win_data: rms np.sqrt(np.mean(w ** 2, axis0)) kurt kurtosis(w, axis0) peak np.max(np.abs(w), axis0) peak_factor peak / (rms 1e-8) # 频域质心功率谱重心 spectrum np.abs(rfft(w, axis0)) ** 2 freqs rfftfreq(w.shape[0], d1 / sample_rate) centroid np.sum(freqs[:, None] * spectrum, axis0) / (np.sum(spectrum, axis0) 1e-8) features.append(np.concatenate([rms, kurt, peak_factor, centroid])) return np.array(features)extract_features_from_windows把每个窗口的多通道时域信号压缩成一组低维统计特征这组特征可以直接喂给 SVM 和随机森林。rms反映振动能量kurt反映冲击性peak_factor反映瞬时冲击强度centroid反映信号频谱重心位置。参数里最容易忽略的是1e-8的平滑项窗口内如果有一段停机静默RMS 接近 0峰值因子会爆炸成无穷大不加平滑项传统模型会直接翻车。2.3 数据平衡与标签噪声处理滚刀样本永远是不均衡的滚刀状态数据天然不均衡正常运行样本可能占七成以上崩刃和卡阻样本稀少偏偏这些稀少状态才是安全上最要命的。直接拿原始样本训练模型会学成「什么都不报也能有 70% 正确率」。处理办法有两个方向数据层面做欠采样或过采样算法层面给模型加类别权重。from sklearn.model_selection import train_test_split from sklearn.utils.class_weight import compute_class_weight labels np.array(sensor_df[state_label].values) # 按滑窗后的样本对齐标签这里假设 win_labels 已生成 win_labels labels[:win_data.shape[0]] # 计算每个类别的权重少数类自动获得更高权重 class_weight compute_class_weight(class_weightbalanced, classesnp.unique(win_labels), ywin_labels) weight_dict dict(zip(np.unique(win_labels), class_weight)) print(类别权重:, weight_dict) # 分层切分保证每个状态在训练和验证集中都有 X_train, X_val, y_train, y_val train_test_split( win_data, win_labels, test_size0.2, stratifywin_labels, random_state42)compute_class_weight用balanced模式自动对少数类加权崩刃样本如果只占 3%权重可能到 10 倍以上。切分用stratify保持各类别比例一致避免验证集里偏偏缺了「崩刃」这个关键类别。对于 SVM 和随机森林直接把weight_dict传给模型对于 CNN 和 LSTM在损失函数里加一个类别权重张量即可。标签噪声主要来自换刀记录的人工填写。施工人员可能在繁忙时段把「偏磨」随手写成「正常磨损」我遇到过一批数据里同一个刀位连续三次记录都是「正常」但振动信号的峭度曲线明显有异常抬升。处理方式是在标签对齐后用无监督聚类做一遍离群检查把特征分布明显异常但标签为正常的样本挑出来逐条复核而不是盲信人工记录。3. 用 SVM 和随机森林打基线传统模型在小样本上的价值滚刀状态识别的落地场景里样本量常常只有几百到几千个窗口这时候深度模型容易过拟合传统模型反而更稳。SVM 和随机森林在这个任务上的定位是先跑通、拿到可解释的基线再决定要不要上深度学习。3.1 为什么先跑传统模型小样本下深度模型的翻车概率更高一个常见误区是拿到源码直接跑 CNN结果训练集 99%验证集 60%还找不到原因。滚刀状态数据集往往只有几十把刀的生命周期数据滑窗后虽有几千个样本但同一把刀不同时段的样本高度相关有效独立样本数远小于表面数量。SVM 的几何间隔机制在样本量小的时候泛化能力更好随机森林的袋外估计能直接反映泛化误差这两个模型都能在几分钟内给出一个诚实的基线结果。如果传统模型已经能到 90% 以上的 F1深度模型的价值就要重新评估。我见过不少项目CNN 把准确率从 93% 提到 95%但训练和部署成本翻了好几倍施工方根本不愿意换。反过来如果基线只有 75%说明特征工程或标签对齐本身有问题这时先解决问题再上深度模型才有意义。3.2 SVM 的核函数选择与参数RBF 核是滚刀数据的默认起点SVM 在滚刀状态识别里用 RBF 核是默认选择因为振动特征在高维空间里往往有复杂的非线性边界线性核表现一般。两个关键参数C误分类惩罚和gammaRBF 核宽度。C越大越容易过拟合gamma越大决策边界越复杂。from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import make_pipeline from sklearn.model_selection import GridSearchCV # 标准化很重要SVM 对特征尺度极其敏感 svm_pipe make_pipeline( StandardScaler(), SVC(kernelrbf, class_weightbalanced, probabilityTrue) ) param_grid { svc__C: [0.1, 1, 10, 100], svc__gamma: [0.001, 0.01, 0.1, 1] } grid GridSearchCV(svm_pipe, param_grid, cv5, scoringf1_macro, n_jobs-1) grid.fit(X_train_features, y_train) print(最优参数:, grid.best_params_)StandardScaler必不可少振动特征里 RMS 的量级可能是峭度的几十倍不标准化会让 SVM 的训练过程被大数值特征主导。probabilityTrue是为了后面能取预测概率做决策阈值调整虽然会多点计算开销但在滚刀状态识别里值得。f1_macro作为打分指标比默认准确率靠谱因为类别不均衡会让准确率虚高。C和gamma的搜索范围按经验值给数据量小的时候从网格中心开始即可不必追求过细。3.3 随机森林的树数量、深度与特征重要性解释随机森林在这个任务上的优势是不用做特征标准化、对噪声鲁棒、能输出特征重要性方便跟施工方解释「模型在靠哪些信号做判断」。这在工程验收时特别好用现场工程师愿意相信「振动峭度」这种物理含义明确的特征。from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators200, max_depth12, min_samples_leaf4, class_weightbalanced, random_state42, n_jobs-1 ) rf.fit(X_train_features, y_train) val_acc rf.score(X_val_features, y_val) print(f验证集准确率: {val_acc:.4f}) # 特征重要性排序辅助向业务方解释模型 feat_names feature_cols [rms, kurtosis, peak_factor, centroid] importance sorted(zip(feat_names, rf.feature_importances_), keylambda x: x[1], reverseTrue) for name, imp in importance[:6]: print(f{name}: {imp:.4f})n_estimators200足够稳定再往上对精度的提升很有限只会拖慢训练。max_depth12配合min_samples_leaf4是控制过拟合的组合让每棵树不要长得太深叶子节点也别太碎。class_weightbalanced和 SVM 里同理。特征重要性输出后你会发现峭度和频域质心通常排在前列推进压力又比振动信号更能看出磨损后期的推力补偿——因为刀圈磨短后维持同样贯入度需要更大推力。4. 用 CNN、LSTM、GRU 做时序识别深度模型的实现与参数取舍传统模型跑通之后如果样本量足够通常滑窗后达到上万个样本就可以上深度模型。CNN、LSTM、GRU 在滚刀状态识别上各有分工选择标准不是「哪个模型更高级」而是「信号里的判别特征在时间轴上怎么分布」。4.1 CNN 提取局部冲击特征、LSTM 捕捉长程依赖两种视角的分工滚刀旋转过程中每把刀与掌子面接触会产生周期性冲击冲击波形在时间轴上只占几个毫秒。这种局部模式恰好是卷积神经网络的强项一维卷积核扫过振动波形在局部窗口内识别冲击形状、间隔和幅值模式。崩刃和偏磨的差异往往就体现在冲击脉冲的形态上。而磨损是一个缓慢的渐变过程当前时刻的振动特征取决于过去数十分钟到数小时的演化趋势。LSTM 和 GRU 存在的意义是捕捉这种长程依赖某个状态特征在时间轴上反复出现、逐渐增强形成序列模式。所以一个合理的搭配是CNN 在前层提取局部冲击特征GRU 在后层建模状态随时间的变化趋势。纯 CNN 对渐变类状态容易误判纯 LSTM 又容易在局部冲击细节上丢信息。4.2 用 PyTorch 搭一个一维 CNN 分类器结构与关键参数一维 CNN 在滚刀数据上的输入形状和图像不一样输入是[batch, channels, seq_len]channels对应振动和压力通道数seq_len是窗口长度。下面的结构参考了常见的一维残差卷积方案但做了简化适配。import torch import torch.nn as nn class CnnToolState(nn.Module): 一维 CNN 滚刀状态分类器。输入: [B, C, L] 输出: [B, n_class] def __init__(self, n_channels5, n_class5, n_filters64): super().__init__() self.features nn.Sequential( nn.Conv1d(n_channels, n_filters, kernel_size7, padding3), nn.BatchNorm1d(n_filters), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2), nn.Conv1d(n_filters, n_filters * 2, kernel_size5, padding2), nn.BatchNorm1d(n_filters * 2), nn.ReLU(inplaceTrue), nn.MaxPool1d(kernel_size2), nn.AdaptiveAvgPool1d(1) ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(n_filters * 2, n_class) ) def forward(self, x): features self.features(x) return self.classifier(features.flatten(1)) model CnnToolState(n_channels5, n_class5) print(model)kernel_size7在 1kHz 采样率下对应 7 毫秒的感知范围刚好能覆盖一次滚刀冲击脉冲的主峰。padding3保证卷积后长度不变避免边界信息丢失。AdaptiveAvgPool1d(1)把任意长度的序列池化到 1 维这样窗口长度变化时不需要改全连接层。Dropout(0.5)放在全连接前滚刀数据量小全连接层是最容易过拟合的位置。训练时把原始多通道窗口直接作为输入不需要手动提取统计特征CNN 会在卷积层里自己学到冲击模式。但要注意输入数据要按通道做归一化不能直接喂原始电压值。4.3 GRU 与 LSTM 的取舍参数更少、收敛更快但别指望永远更好LSTM 和 GRU 的结构差异在工程上直接体现为GRU 少了输出门参数量大约少三分之一。在滚刀这种中等规模的数据集上GRU 收敛更快过拟合风险更低是性价比更高的选择。但 LSTM 在序列特别长、依赖关系跨很长距离时上限更高所以两者都值得跑一遍以实验结果为准。class GruToolState(nn.Module): GRU 序列分类器。输入: [B, L, C] 输出: [B, n_class] def __init__(self, input_size5, hidden_size64, n_layers2, n_class5, dropout0.3): super().__init__() self.gru nn.GRU(input_size, hidden_size, n_layers, batch_firstTrue, dropoutdropout, bidirectionalTrue) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(hidden_size * 2, n_class) ) def forward(self, x): out, _ self.gru(x) # 取最后一个时间步的输出 last_out out[:, -1, :] return self.classifier(last_out) model GruToolState(input_size5, hidden_size64, n_layers2, n_class5)batch_firstTrue让输入维度是[B, L, C]而不是[L, B, C]减少写代码时维度搞反的概率。bidirectionalTrue会让 GRU 同时看到过去和未来但代价是延迟预测时不能用流式推理需要等整个窗口采集完才能给出结果。hidden_size64对滚刀数据的窗口长度几百到上千点来说够用加大到 128 通常不会提升精度只会增加计算量。n_layers2是常见的折中单层欠拟合三层以上在小数据集上几乎必然过拟合。训练时 LSTM/GRU 比 CNN 更敏感学习率和梯度裁剪必须配好。常见配置是 AdamW 优化器配学习率1e-3加 CosineAnnealing 学习率衰减同时用clip_grad_norm_把梯度范数限制在 1.0 以内防止长序列的梯度爆炸把模型权重冲乱。optimizer torch.optim.AdamW(model.parameters(), lr1e-3, weight_decay1e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max30) clip_value 1.0 for epoch in range(30): for batch_x, batch_y in train_loader: optimizer.zero_grad() logits model(batch_x) loss criterion(logits, batch_y) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), clip_value) optimizer.step() scheduler.step()weight_decay1e-4做 L2 正则化配合clip_grad_norm_(..., 1.0)是 LSTM/GRU 训练不顺时的第一道解药。T_max30对应训练轮数学习率会在 30 轮内从1e-3余弦降到接近 0。如果损失曲线在前几轮震荡不降优先检查学习率和输入归一化而不是换成更复杂的模型结构。5. 模型对比与避坑指南五种模型在滚刀数据上的真实差距模型对比不是把五个模型各自跑一遍取最高分而是要在同一套数据划分、同一套预处理、同一套评价指标下做公平比较。这章先讲对比实验怎么设计再列出滚刀状态识别里最常见的几个坑都是实际项目中踩过的。5.1 对比实验怎么设计才公平划分方式、评价指标与混淆矩阵公平对比的前提是大家用完全相同的训练集和验证集。先固定数据切分再把数据集保存下来五个模型共用同一份train_set.npz和val_set.npz而不是每次跑模型时重新随机切分。最稳妥的做法是先写好一个数据加载函数把窗口切分、标签对齐、标准化参数全部固定然后作为公共模块被五个模型调用。评价指标不能只看准确率。滚刀状态识别里崩刃漏报的代价远高于误报所以重点看每个类别的召回率尤其是崩刃和卡阻。用一个加权 F1 做总指标配合混淆矩阵看具体哪里出错。混淆矩阵里如果偏磨样本大量被预测成正常说明模型对该状态的特征不敏感这时优先调整类别权重或补充样本而不是换模型。状态类别样本占比误判后果优先关注的指标正常65%~75%无直接后果特异性正常磨损10%~20%换刀时机偏晚召回率偏磨5%~10%刀圈偏磨报废召回率崩刃2%~5%刀圈碎裂、伤及刀盘召回率卡阻1%~3%整机停机召回率 精确率这张表是滚刀状态识别项目的核心。越往下的状态样本越少、后果越重评价指标就越要向召回率倾斜。模型对比时只看accuracy或者只看宏平均 F1 都会掩盖这个关键问题要把混淆矩阵打印出来逐类核对。5.2 数据泄漏的三个常见来源随机切分、重叠窗口、提前归一化数据泄漏是状态识别项目里精度虚高的头号原因。表现是训练集和验证集都 98%部署到现场直接掉到 70% 以下。第一个来源是随机切分同一个时间段的相邻窗口被分到了训练集和验证集两侧模型等于见过答案。修复方式是按时间顺序切分前 80% 训练、后 20% 验证。第二个来源是重叠窗口。用 50% 重叠切分后某个窗口的一部分数据在它的相邻窗口里也出现如果相邻窗口被分到另一侧信息就泄漏了。第三个来源是归一化参数标准化时用全体数据的均值和方差等于验证集的信息提前参与了训练。正确做法是只对训练集计算均值方差再用同一套参数归一化验证集。scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train.reshape( X_train.shape[0], -1)).reshape(X_train.shape) # 验证集用训练集的统计量不能重新 fit X_val_scaled scaler.transform(X_val.reshape( X_val.shape[0], -1)).reshape(X_val.shape)5.3 实测踩坑记录滚刀状态识别最常见的五个问题现象一训练集准确率 99%验证集只有 60%。原因和解决随机切分造成了时间序列泄漏。解决方法是改成按时间顺序划分保证验证集只包含训练集时间之后的数据。我在项目里吃过这个亏后来干脆把切分方式写成固定规则任何模型都只能用它。现象二偏磨状态召回率只有 30%模型把所有样本都判成正常。原因和解决类别不平衡正常样本占到七成模型学会了偷懒。解决方法是给少数类加高权重或者对少数类做 SMOTE 过采样。注意 SMOTE 只适用于特征向量不能直接用于原始时间序列。现象三LSTM 训练过程损失震荡跑 50 轮还不收敛。原因和解决学习率太高长序列的梯度爆炸。解决方法是把学习率降到3e-4加上梯度裁剪clip_value1.0同时检查输入序列是否做了归一化。这个坑几乎每个用 LSTM 的人都踩过GRU 虽然稍好一些也一样需要这些配置。现象四SVM 训练等了几个小时还没结束。原因和解决直接把原始波形拉平喂给 SVM特征维度上千SVM 核矩阵计算量爆炸。解决方法是先用统计特征降到几十维再跑 SVM。这就是第二章为什么要做特征工程传统模型和深度模型在数据准备上完全不是一回事。现象五离线验证 93%上现场掉到 75%。原因和解决训练集和现场数据的分布不一致换了一批传感器、换了个安装位置、推进速度不同振动幅值和频段都变了。解决的常见做法是重新采集一段现场数据做微调或者做简单的域自适应把现场数据的均值方差对齐到训练集分布。最彻底的办法是建立「一项目一模型」的迭代流程每个新的施工段用少量新数据校准一次。6. 从离线实验到现场可用模型部署与持续迭代技巧模型在 Jupyter Notebook 里跑通只是第一步现场能用才是终点。部署时不要把单个窗口的预测结果直接当作换刀依据那样误报率会高到施工方不敢用。我习惯的做法是滑动窗口推流 多数投票模型每处理一个窗口输出一个状态但最终判定取最近 N 个窗口中投票数最多的结果。N 一般取 5 到 10既避免单次误判又能在一个窗口对应的半秒到一秒内及时反应。模型导出用 ONNX Runtime 比较省心PyTorch 模型可以直接转 ONNX推理时不依赖训练框架现场工控机上装个轻量运行时就能跑。量化方面int8 量化后模型体积能缩小到原来的四分之一在振动信号这种容错率还可以的场景里精度损失通常小于两个百分点。再训练的节奏很关键。滚刀状态识别不能训一次用到项目结束每把刀的生命周期、每段地层的岩性变化都会让数据分布漂移。常见做法是每周把新增的换刀记录和对应信号增量标注一次用新数据对模型做二次微调而不是重新全量训练。这样模型能跟上掌子面岩层的变化换刀判断的准确率才能维持住。我在最新一个项目上的习惯是模型版本号和换刀记录一一对应每次现场反馈误判就把那条样本单独存下来。积累到一定量后统一复查看是标签错了还是特征被新工况覆盖了。先查数据、再调模型这个顺序能省掉大量无效调参时间。希望帮到你。本文还有配套的精品资源点击获取