ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python机器学习实现滚刀状态识别:从振动信号到刀具健康度预警

Python机器学习实现滚刀状态识别:从振动信号到刀具健康度预警 简介这份资源面向机械加工与智能制造方向的本科生、研究生及算法初学者提供一套基于机器学习的滚刀状态识别完整项目可用于毕业设计、课程设计或期末大作业。项目以Python实现覆盖CNN、LSTM、GRU、SVM、随机森林等多种模型帮助读者对比不同算法在滚刀磨损与状态分类任务上的表现并理解从数据到模型评估的完整流程。压缩包共15个文件约2.63MB包含8个py源码文件、4个csv数据集、1个png结果图、1个md说明文档及gitignore等辅助文件源码与数据配套齐全便于直接运行和二次修改。目前已有233人学习下载说明该方案在同类选题中具有一定参考价值。读者可获得可复现的模型训练与测试脚本、多组实验数据、特征生成与数据合并工具以及清晰的目录结构适合新手快速上手并完成高质量项目。1. 从振动信号到刀具健康度滚刀状态识别到底在识别什么盾构机在掘进时滚刀承受着极高的推力与扭矩刀圈磨损、崩刃、偏磨几乎是必然事件。问题在于滚刀装在刀盘内部人无法直接观察等到出渣异常或推力突变再停机检查往往已经连带损伤了刀座和主轴承。Python基于机器学习的滚刀状态识别要解决的就是利用掘进过程中采集的振动、推力、扭矩、转速等信号自动判断当前滚刀处于正常、磨损还是失效状态把事后抢修变成事前预警。这个方向适合三类人做盾构/TBM状态监测的工程技术人员、手里有工业信号数据想做分类的算法工程师、以及拿这个题目做课程设计或毕业设计的学生。它的核心难点不在模型本身而在于信号怎么切、特征怎么提、标签怎么标。CNN、LSTM、GRU、SVM、随机森林这几类模型各有适用边界选错了不是精度差一点而是根本训不起来。下面按数据准备、特征工程、模型实现、避坑、进阶验证的顺序把整条链路拆开讲清楚。2. 数据准备与标签体系滚刀状态识别的地基2.1 信号采集方案与采样参数怎么定滚刀状态识别的原始数据通常来自刀盘上的三向加速度传感器、主驱动系统的推力/扭矩变送器以及刀盘转速编码器。常见做法是把加速度传感器安装在刀盘背面靠近滚刀的位置采样率不低于 10 kHz因为滚刀磨损引起的冲击成分集中在高频段采样率低了会直接混叠。推力、扭矩、转速这类缓变量用 100 Hz 左右采集即可它们反映的是整体负载趋势不需要高频。我一般会把数据按“掘进段”切分而不是按固定时间窗口切。原因很简单盾构机停机、管片拼装、空转阶段的信号和正常掘进完全不是一个分布混在一起会让模型学到“停机正常”这种假规律。具体做法是用推力阈值和转速阈值联合判断掘进段只保留两者同时高于设定下限的时间区间。import numpy as np import pandas as pd def extract_drilling_segments(df, thrust_colthrust, rpm_colrpm, thrust_th8000, rpm_th1.0, min_len500): 从原始时序中提取有效掘进段 df: 包含推力、转速、振动等列的DataFrame thrust_th: 推力下限(kN)低于此值视为非掘进 rpm_th: 转速下限(r/min) min_len: 最短掘进段长度过滤碎片段 mask (df[thrust_col] thrust_th) (df[rpm_col] rpm_th) segments [] start None for i, m in enumerate(mask): if m and start is None: start i elif not m and start is not None: if i - start min_len: segments.append(df.iloc[start:i]) start None if start is not None and len(df) - start min_len: segments.append(df.iloc[start:]) return segments这段代码的逻辑是先构造布尔掩码再扫描连续为真的区间。thrust_th和rpm_th需要根据具体机型标定不能照搬我见过直接套用别人阈值导致有效段被砍掉一半的情况。min_len的作用是过滤掉推进系统抖动产生的碎片段一般取 500 到 2000 个采样点。2.2 标签怎么标三类状态还是四类状态标签体系直接决定模型能不能落地。最常见的分法是三类正常、磨损、失效。但实际项目中我更倾向于四类把“磨损”拆成“轻度磨损”和“重度磨损”因为轻度磨损阶段是维护窗口重度磨损已经接近失效两者的处置策略完全不同。如果样本量实在不够退回三类也能用。标签来源一般有两种一是停机开仓检查时人工记录滚刀状态再回标到对应掘进段二是用刀盘扭矩和推力的比值做半自动标注再人工复核。第一种准确但成本高第二种快但噪声大。我的经验是先用第二种做粗标再用第一种做校验两者不一致的样本单独拿出来看往往能发现传感器故障或标注错误。注意标签的时间对齐非常关键。开仓检查的时刻和实际磨损发生的时刻之间有时间差如果直接把检查结果标到检查前最后一段数据上会引入系统性偏差。常见做法是往前推一个换刀周期取该周期中后段的数据作为该状态的样本。2.3 数据集划分不能随机打乱时序信号分类最容易翻车的地方就是数据集划分。很多人习惯用train_test_split随机打乱这在滚刀状态识别里是错的。同一个掘进段切出来的多个窗口高度相关随机打乱会让训练集和测试集共享同一段信号测试精度虚高到 99%一上真实数据就崩。正确做法是按掘进段划分整个掘进段要么进训练集要么进测试集绝不交叉。如果有多台设备的掘进数据更好的是按设备划分留一台设备的数据做测试这样能验证模型的跨设备泛化能力。from sklearn.model_selection import GroupShuffleSplit def split_by_segment(X, y, groups, test_size0.3, seed42): 按掘进段分组划分避免同段信号泄漏 groups: 每个样本所属的掘进段ID gss GroupShuffleSplit(n_splits1, test_sizetest_size, random_stateseed) train_idx, test_idx next(gss.split(X, y, groupsgroups)) return X[train_idx], X[test_idx], y[train_idx], y[test_idx]groups数组记录每个样本来自哪个掘进段GroupShuffleSplit保证同一组的样本不会同时出现在训练集和测试集。这个细节看起来小但它决定了你报出来的精度是真的还是假的。3. 特征工程与模型选型CNN、LSTM、GRU、SVM、随机森林各管什么3.1 时域频域特征提取与 CNN 的输入构造传统机器学习模型吃的是手工特征深度学习模型可以直接吃原始信号或时频图。对 SVM 和随机森林我一般提取以下几类特征时域的均值、方差、峰值、峭度、裕度因子频域的谱重心、谱熵、主频幅值以及小波包分解后各频带的能量比。峭度对冲击成分特别敏感滚刀崩刃时峭度会明显上升这个特征几乎必选。对 CNN常见做法是把原始振动信号做短时傅里叶变换得到时频图或者直接用小波变换得到尺度图然后当成图像输入。CNN 的卷积核在时频图上滑动能自动学到局部冲击模式省去了手工设计特征的麻烦。输入尺寸一般是 64×64 或 128×128 的时频图太小会丢失频率分辨率太大则训练慢且容易过拟合。import numpy as np from scipy.stats import kurtosis, skew from scipy.fft import rfft, rfftfreq def extract_features(signal, fs10000): 提取单个窗口的时域与频域特征 signal: 一维振动信号 fs: 采样率 feats {} feats[mean] np.mean(signal) feats[std] np.std(signal) feats[peak] np.max(np.abs(signal)) feats[kurtosis] kurtosis(signal) feats[skewness] skew(signal) feats[rms] np.sqrt(np.mean(signal**2)) feats[crest] feats[peak] / (feats[rms] 1e-8) # 频域 spec np.abs(rfft(signal)) freqs rfftfreq(len(signal), 1/fs) feats[spec_centroid] np.sum(freqs * spec) / (np.sum(spec) 1e-8) p spec / (np.sum(spec) 1e-8) feats[spec_entropy] -np.sum(p * np.log(p 1e-8)) feats[main_freq_amp] np.max(spec) return featscrest是峰值因子对早期磨损比较敏感spec_entropy反映频谱的集中程度滚刀正常时能量集中在少数频带磨损后频谱展宽熵值上升。这些特征喂给随机森林和 SVM通常能拿到不错的基线精度。3.2 LSTM 与 GRU 处理时序依赖的差异LSTM 和 GRU 都是循环网络区别在于门控结构。LSTM 有三个门输入门、遗忘门、输出门参数量大适合长序列建模GRU 只有两个门重置门、更新门参数少训练快在数据量不大时往往表现更好。滚刀状态识别里单个样本窗口通常几百到几千个点序列长度不算特别长GRU 的性价比通常更高。我一般会先跑 GRU 做基线如果欠拟合再换 LSTM。两者在滚刀磨损这种渐变过程上的差异不会特别大真正影响精度的是序列长度和滑动步长的设置。序列太短模型看不到磨损的渐变趋势序列太长训练慢且容易梯度消失。import torch import torch.nn as nn class GRUClassifier(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, num_classes4, dropout0.3): super().__init__() self.gru nn.GRU(input_size, hidden_size, num_layers, batch_firstTrue, dropoutdropout, bidirectionalTrue) self.fc nn.Sequential( nn.Linear(hidden_size * 2, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, num_classes) ) def forward(self, x): # x: (batch, seq_len, input_size) out, _ self.gru(x) out out[:, -1, :] # 取最后一个时间步 return self.fc(out)bidirectionalTrue让模型同时看前向和后向信息对离线分析很有效如果是实时预警必须改成单向否则会用到未来信息。hidden_size从 64 起步数据量大再往上加。dropout在循环层和全连接层都加防止过拟合。3.3 SVM 与随机森林小样本下的稳妥选择当标注样本只有几百条时深度学习容易过拟合SVM 和随机森林反而是更稳的选择。SVM 用 RBF 核在高维特征空间里找最大间隔超平面对小样本高维特征很友好随机森林靠多棵决策树投票对特征尺度不敏感还能输出特征重要性方便回溯哪些特征在起作用。SVM 的关键参数是C和gamma。C大偏向硬间隔容易过拟合C小偏向软间隔可能欠拟合。gamma控制 RBF 核的作用范围太大导致每个样本只影响自己太小则模型过于平滑。我一般用网格搜索在C[0.1,1,10,100]、gamma[0.001,0.01,0.1,1]里找配合 5 折分组交叉验证。from sklearn.svm import SVC from sklearn.ensemble import RandomForestClassifier from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV, GroupKFold def build_svm_pipeline(): return Pipeline([ (scaler, StandardScaler()), (svm, SVC(kernelrbf, probabilityTrue)) ]) def tune_svm(X, y, groups): param_grid { svm__C: [0.1, 1, 10, 100], svm__gamma: [0.001, 0.01, 0.1, 1] } gkf GroupKFold(n_splits5) gs GridSearchCV(build_svm_pipeline(), param_grid, cvgkf, scoringf1_macro, n_jobs-1) gs.fit(X, y, groupsgroups) return gs.best_params_, gs.best_score_StandardScaler对 SVM 是必须的因为 RBF 核基于距离特征尺度不统一会让大尺度特征主导。随机森林不需要标准化但n_estimators建议不低于 200max_depth要限制否则每棵树长到纯叶节点噪声全被学进去。4. 训练流程与调参从能跑到跑好4.1 类别不平衡的处理实际数据里正常状态样本远多于失效样本直接训练会让模型偏向多数类。常见做法有三种一是对少数类过采样比如 SMOTE二是对多数类欠采样三是在损失函数里给少数类更高权重。我一般优先用类别权重因为它不改变数据分布实现也简单。from sklearn.utils.class_weight import compute_class_weight import numpy as np def get_class_weights(y): classes np.unique(y) weights compute_class_weight(balanced, classesclasses, yy) return dict(zip(classes, weights)) # PyTorch 中使用 # criterion nn.CrossEntropyLoss(weighttorch.tensor(list(weights.values())))compute_class_weight的balanced模式按样本数反比给权重少数类权重高。注意权重别设得太极端否则模型会对少数类过拟合验证集上少数类精度上去了但误报率也飙升。4.2 学习率与早停策略深度学习模型训练时学习率是最关键的参数。我一般用 1e-3 起步配合ReduceLROnPlateau验证损失连续几个 epoch 不降就砍半。早停的耐心值设 10 到 15 个 epoch太小会过早停止太大则浪费时间。from torch.optim.lr_scheduler import ReduceLROnPlateau optimizer torch.optim.Adam(model.parameters(), lr1e-3, weight_decay1e-4) scheduler ReduceLROnPlateau(optimizer, modemin, factor0.5, patience5, min_lr1e-6) # 训练循环中 # scheduler.step(val_loss)weight_decay加一点 L2 正则对循环网络防止过拟合有帮助。min_lr设 1e-6避免学习率降到零后模型完全不动。4.3 评价指标别只看准确率滚刀状态识别里漏报失效的代价远大于误报。所以评价指标要看召回率和 F1尤其是失效类的召回率。混淆矩阵能直观看出模型把哪些状态搞混了常见的是轻度磨损和重度磨损混在一起这时候要么合并类别要么加更多区分性特征。指标含义滚刀场景下的关注点准确率整体正确比例类别不平衡时会虚高宏平均F1各类F1平均反映少数类表现失效类召回失效样本被检出比例直接关系安全误报率正常被判为异常比例影响停机成本5. 避坑与排查滚刀状态识别里最容易翻车的五件事5.1 测试精度 99% 但现场一塌糊涂现象离线交叉验证精度接近满分部署到新设备上准确率掉到 60% 以下。原因数据集划分时同一掘进段的窗口被分到了训练集和测试集模型记住了这段信号的“指纹”而不是学到了通用模式。解决改用按掘进段或按设备分组划分GroupShuffleSplit或GroupKFold确保同组样本不跨集。5.2 模型把停机段判成正常现象混淆矩阵里正常类召回很高但现场发现模型对停机后的重启段误判严重。原因训练数据里混入了停机、空转、管片拼装阶段的信号这些段被标成了正常模型学到了“低推力正常”。解决在数据预处理阶段就用推力、转速阈值把非掘进段剔除只保留有效掘进段参与训练和推理。5.3 LSTM 训练损失不降现象训练几个 epoch 后损失卡在某个值不动准确率也不涨。原因常见的有三种——输入没有归一化、序列太长导致梯度消失、学习率太大导致震荡。解决先对输入做 z-score 标准化把序列长度从几千降到几百学习率从 1e-3 降到 1e-4 试。如果还不行换 GRU 或减小hidden_size。5.4 SVM 在小样本上过拟合现象训练集精度 100%验证集只有 70%。原因C设得太大模型追求硬间隔把噪声也拟合了。解决把C从 100 降到 1 或 0.1同时用GroupKFold做交叉验证选参不要用普通 KFold。另外检查特征维度是不是远大于样本数如果是先做特征选择降维。5.5 特征重要性全是同一个特征现象随机森林输出的特征重要性里某一个特征占了 80% 以上。原因这个特征可能和标签有间接泄漏比如某个特征只在特定设备上取值不同而标签恰好也按设备分。解决检查该特征在不同设备、不同掘进段上的分布如果它和标签的相关性来自设备差异而非状态差异就要剔除或做设备归一化。6. 进阶验证跨设备泛化与在线推理的落地技巧模型在单台设备上跑通只是第一步真正要落地得验证跨设备泛化能力。我的做法是留一台设备的全部数据做测试其余设备做训练。如果跨设备精度掉得厉害说明模型学到的特征和设备强相关这时候要么做迁移学习要么在特征层面做设备归一化比如用该设备的正常段均值方差做标准化。在线推理时滑动窗口的步长和缓冲策略很关键。步长太小推理频率高但计算浪费步长太大预警延迟高。我一般取窗口长度的 1/4 到 1/2 作为步长配合一个长度为 5 的投票缓冲连续 3 个窗口判为异常才触发预警这样能压掉大部分偶发误报。from collections import deque class OnlinePredictor: def __init__(self, model, window_size1024, step256, vote_len5, vote_th3): self.model model self.window_size window_size self.step step self.buffer deque(maxlenvote_len) self.vote_th vote_th def update(self, new_samples): new_samples: 新到达的采样点 # 维护滑动窗口凑够一个窗口就推理 # 实际实现中需要维护一个全局缓冲区 pass def predict(self, window): # window: (1, seq_len, 1) prob self.model(window) pred prob.argmax(dim-1).item() self.buffer.append(pred) if len(self.buffer) self.buffer.maxlen: abnormal sum(1 for p in self.buffer if p ! 0) if abnormal self.vote_th: return 1 # 触发预警 return 0投票缓冲的vote_th设 3 意味着 5 个窗口里至少 3 个判异常才报警这个值可以根据现场对误报的容忍度调整。容忍度低就设 4容忍度高就设 2。我自己的习惯是先在历史数据上回放统计不同vote_th下的误报率和漏报率再选一个平衡点。最后说一个我踩过的坑模型上线后不要锁死要留一个反馈通道。现场每次开仓检查的结果都回标到系统里定期用新数据微调模型。滚刀磨损模式和地质条件强相关一段时间的模型放到另一段地质里可能就不准了。这个反馈闭环比任何调参技巧都管用。希望帮到你。本文还有配套的精品资源点击获取
返回列表