刀具磨损状态识别:信号处理与机器学习实战指南

📅 发布时间:2026/10/10 5:38:31
刀具磨损状态识别:信号处理与机器学习实战指南
简介这份资源面向机械加工监测、工业信号处理与深度学习方向的开发者及学生围绕刀具磨损状态识别这一典型工业场景提供基于一维卷积神经网络与长短期记忆网络的完整机器学习实践方案。压缩包共15个文件约2.62MB以8个Python脚本为核心覆盖数据合并、特征生成、CNN与LSTM模型构建及测试等环节并配有4个CSV数据文件、1张结果图与README说明便于直接运行与复现。资源已有395人学习下载适合希望将时序深度学习落地到设备状态监测的读者。通过脚本与数据可掌握1DCNN提取局部特征、LSTM建模长期依赖并完成铣刀磨损分类的完整流程理解数据预处理、模型训练与测试集评估的衔接方式为工业刀具磨损实时识别提供可参考的实现思路与排错基础。1. 刀具磨损状态识别从听声音到看火花机器学习到底能替老师傅做什么车间里最贵的从来不是机床是那个能听声辨磨损的老师傅。他退休那天整条线的良品率掉了三个点——这事我见过不止一次。刀具磨损状态识别要解决的就是把这种手感变成可复现的信号处理加机器学习流水线主轴电流、振动、声发射、切削力哪一路信号先动特征怎么提模型怎么选最后怎么在产线上跑起来。它适合两类人一类是设备工程师手里有数据但不知道怎么建模另一类是算法工程师模型调得溜但不懂切削工艺的边界在哪。这篇不聊虚的从信号采集一路讲到模型上线中间该踩的坑一个不落。2. 先搞清楚刀具磨损到底磨损了什么信号选型与标签定义2.1 后刀面磨损带 VB 才是你要预测的目标刀具磨损不是单一现象。前刀面月牙洼、后刀面磨损带、微崩刃、塑性变形四种失效模式对应的信号特征完全不同。工业标准 ISO 8688 里定义的 VB 值——后刀面磨损带宽度——是最常用的量化指标。粗加工场景下 VB 超过 0.3mm 通常就要换刀精加工可能 0.15mm 就得换。问题在于产线上你不可能每切一刀就把刀拆下来测 VB。所以标签的获取方式决定了整个项目的可行性。常见做法有三种离线测量每加工若干件后停机用工具显微镜测 VB。数据质量最高但采样稀疏一个刀具全生命周期可能只有 10~20 个标签点。间接标注用已标注的工件表面粗糙度反推磨损等级。粗糙度超差就标磨损但这里有个滞后——粗糙度恶化时磨损往往已经比较严重了。分段离散化把连续 VB 值分成初期磨损/正常磨损/急剧磨损三段做分类而非回归。这是工程上最务实的做法因为换刀决策本身就是离散的。我一般建议新手从三分类做起。回归看起来更高级但 VB 的测量噪声和标签稀疏性会让回归模型的 R² 难看到你想砸键盘。2.2 四路信号的性价比排序不是所有信号都值得采。按投入产出比排信号类型传感器成本安装难度对磨损敏感度产线适用性主轴电流极低驱动器自带无需安装中极好振动加速度低简单磁吸高好声发射中需耦合极高一般切削力测力仪极高需改装夹具极高差主轴电流是性价比之王。伺服驱动器本身就输出电流信号不用额外装传感器采样率要求也不高1~10kHz 足够。振动信号敏感度高但安装位置很讲究——装在主轴端和装在工件端特征差异能大到让你怀疑是不是同一把刀。声发射对微崩刃特别灵但切削液一浇上去信噪比就崩。测力仪精度最好但那个价格和安装复杂度除非是实验室项目产线上基本不用想。我的建议主轴电流 振动双通道起步。电流做粗判振动做精判融合之后效果比单通道稳得多。2.3 采样参数怎么定别让混叠毁了你的一切采样率、采样时长、采样触发方式这三个参数定错了后面特征工程做得再花哨都是白搭。import numpy as np # 采样参数配置示例 FS 10000 # 采样率 10kHz覆盖振动信号主要频段 DURATION 0.5 # 每次采样 0.5 秒覆盖至少 3 个主轴旋转周期 TRIGGER_MODE spindle_on # 主轴启动后延迟触发避开启停瞬态 # 抗混叠滤波器截止频率必须小于 FS/2 ANTI_ALIAS_CUTOFF 4000 # 留 1kHz 余量给滤波器滚降 # 每次走刀采集的样本数 n_samples int(FS * DURATION) print(f每次采样点数: {n_samples}) # 5000 点 # 主轴转速 3000rpm 50Hz0.5秒覆盖 25 个旋转周期 spindle_rpm 3000 rev_per_sample (spindle_rpm / 60) * DURATION print(f覆盖旋转周期数: {rev_per_sample}) # 25 个周期采样率的选择有个经验公式至少是关注频率的 2.5 倍。刀具磨损相关的特征频率通常在 1~5kHz 范围取决于齿数和转速所以 10kHz 采样率是安全线。采样时长要覆盖至少 3 个主轴旋转周期否则每转之间的波动会让你误判。触发方式一定要避开启停瞬态——主轴刚启动那 0.2 秒的电流冲击和正常切削完全不是一回事混进去就是给模型喂噪声。注意如果采样率不够又没加抗混叠滤波器高频噪声会折叠到低频你看到的特征频率可能是假的。这个坑我在三个项目里见过每次都是查了半天才发现是混叠。3. 特征工程时域、频域、时频域哪个先上3.1 时域特征最快出结果的起点时域特征计算快、物理意义明确是验证数据质量的第一道关。常用的就那几个均值、方差、均方根、峰值因子、峭度、偏度。import numpy as np from scipy.stats import kurtosis, skew def time_domain_features(signal): 提取时域特征输入为一维振动或电流信号 features {} features[mean] np.mean(signal) features[std] np.std(signal) features[rms] np.sqrt(np.mean(signal**2)) features[peak] np.max(np.abs(signal)) # 峰值因子峰值/RMS对冲击性磨损敏感 features[crest_factor] features[peak] / (features[rms] 1e-10) # 峭度正常磨损时接近3微崩刃时飙升 features[kurtosis] kurtosis(signal) features[skewness] skew(signal) # 波形因子RMS/整流均值 features[shape_factor] features[rms] / (np.mean(np.abs(signal)) 1e-10) return features # 模拟一段振动信号 np.random.seed(42) normal_signal np.random.normal(0, 1, 5000) worn_signal np.random.normal(0, 1.5, 5000) np.random.exponential(0.5, 5000) feat_normal time_domain_features(normal_signal) feat_worn time_domain_features(worn_signal) for k in feat_normal: print(f{k:15s} 正常: {feat_normal[k]:8.4f} 磨损: {feat_worn[k]:8.4f})峭度是最值得盯的指标。正常切削时振动信号接近高斯分布峭度约等于 3一旦出现微崩刃或严重磨损信号里出现冲击成分峭度能飙到 5 以上。峰值因子同理对早期磨损比 RMS 更敏感。但时域特征有个致命问题它们对切削参数变化同样敏感。你改了进给量RMS 也会变模型分不清是磨损还是参数调整。所以时域特征通常只做粗筛不能单独用。3.2 频域特征主轴转频和齿通过频率的谐波变化频域分析的核心逻辑是刀具磨损会改变切削力波形进而改变频谱中谐波的幅值分布。主轴转频 f_r rpm/60齿通过频率 f_t f_r × 齿数。磨损加剧时f_t 及其谐波的幅值通常上升而高频段能量占比也会变化。from scipy.fft import fft, fftfreq def freq_domain_features(signal, fs, spindle_rpm, n_teeth): 提取频域特征重点关注转频和齿通过频率的谐波 n len(signal) yf np.abs(fft(signal))[:n//2] / n xf fftfreq(n, 1/fs)[:n//2] f_r spindle_rpm / 60 # 主轴转频 f_t f_r * n_teeth # 齿通过频率 features {} # 转频及其前3阶谐波幅值 for i in range(1, 4): idx np.argmin(np.abs(xf - f_r * i)) features[famp_fr_{i}x] yf[idx] # 齿通过频率及其前3阶谐波幅值 for i in range(1, 4): idx np.argmin(np.abs(xf - f_t * i)) features[famp_ft_{i}x] yf[idx] # 高频段能量占比2kHz high_mask xf 2000 features[high_freq_ratio] np.sum(yf[high_mask]**2) / (np.sum(yf**2) 1e-10) # 频谱重心 features[spectral_centroid] np.sum(xf * yf) / (np.sum(yf) 1e-10) return features feats freq_domain_features(normal_signal, fs10000, spindle_rpm3000, n_teeth4) for k, v in feats.items(): print(f{k:20s}: {v:.6f})频域特征比时域稳因为它对切削参数的变化没那么敏感——转速变了你跟踪的频率也跟着变比值关系保持稳定。但前提是你得知道转速和齿数。如果产线上转速经常调要么实时读 PLC 的转速值要么用阶次分析代替固定频率分析。高频段能量占比是我最常用的单一频域指标。磨损加剧时切削从剪切逐渐变成摩擦挤压高频成分明显增多。这个指标在多个项目里都排进了特征重要度前五。3.3 时频域小波包分解怎么选基函数和层数时频域方法适合非平稳信号——变转速、变切深的场景。小波包分解是工程上最常用的但基函数和分解层数的选择经常让人纠结。import pywt import numpy as np def wavelet_packet_features(signal, waveletdb4, level3): 小波包分解提取各频带能量占比 wp pywt.WaveletPacket(datasignal, waveletwavelet, modesymmetric, maxlevellevel) features {} # 获取所有叶子节点 nodes [node.path for node in wp.get_level(level, natural)] energies [] for node_path in nodes: node_data wp[node_path].data energy np.sum(node_data**2) energies.append(energy) total_energy sum(energies) 1e-10 for i, e in enumerate(energies): features[fwp_energy_band_{i}] e / total_energy return features # db4 对切削振动信号通常表现较好 wp_feats wavelet_packet_features(normal_signal, waveletdb4, level3) print(f频带数量: {len(wp_feats)}) # 2^3 8 个频带 for k, v in list(wp_feats.items())[:4]: print(f{k:25s}: {v:.4f})基函数选择有个实用原则db4 到 db8 之间试别用 haar。haar 太粗糙频带泄漏严重db 系列阶数太高又容易过拟合。分解层数看你的采样率和关注频段——3 层分解 10kHz 采样信号每个频带宽度约 625Hz够用了。层数太多每个频带的样本点太少能量估计不稳定。注意小波包分解后的特征维度是 2^level3 层就是 8 维4 层 16 维。别一上来就搞 5 层32 维特征扔进分类器小样本下必过拟合。4. 模型选型与训练从随机森林到一维 CNN 的实战对比4.1 小样本先上随机森林和 SVM刀具磨损数据有个特点标签贵。你不可能为了训练模型把几百把刀都切废。所以大多数实际项目里标注样本可能就几百到几千条。这个量级下随机森林和 SVM 比深度学习稳得多。from sklearn.ensemble import RandomForestClassifier from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.model_selection import cross_val_score, StratifiedKFold import numpy as np # 假设 X 是特征矩阵 (n_samples, n_features)y 是标签 (0/1/2) # 这里用模拟数据演示流程 np.random.seed(42) n_samples 600 n_features 20 X np.random.randn(n_samples, n_features) # 模拟三类初期磨损、正常磨损、急剧磨损 y np.random.choice([0, 1, 2], sizen_samples, p[0.3, 0.5, 0.2]) # 随机森林管道 rf_pipe Pipeline([ (scaler, StandardScaler()), (clf, RandomForestClassifier( n_estimators200, # 树的数量200 通常够用 max_depth8, # 限制深度防过拟合 min_samples_leaf5, # 叶节点最少样本数 class_weightbalanced,# 类别不平衡时自动加权 random_state42 )) ]) # SVM 管道 svm_pipe Pipeline([ (scaler, StandardScaler()), (clf, SVC( kernelrbf, C10, # 正则化参数越大越容易过拟合 gammascale, # RBF 核宽度 class_weightbalanced, probabilityTrue )) ]) cv StratifiedKFold(n_splits5, shuffleTrue, random_state42) rf_scores cross_val_score(rf_pipe, X, y, cvcv, scoringf1_macro) svm_scores cross_val_score(svm_pipe, X, y, cvcv, scoringf1_macro) print(f随机森林 F1-macro: {rf_scores.mean():.4f} ± {rf_scores.std():.4f}) print(fSVM F1-macro: {svm_scores.mean():.4f} ± {svm_scores.std():.4f})随机森林的优势是特征重要度直接可看能告诉你哪个信号、哪个特征在起作用。SVM 在小样本高维下表现好但核函数和 C 值调起来比较玄学。我的经验是先跑随机森林看特征重要度砍掉不重要的特征再上 SVM 精调。这样比一上来就 SVM 网格搜索快得多。4.2 一维 CNN 什么时候值得上一维 CNN 适合两种场景一是原始信号直接输入不做手工特征二是样本量足够大至少几千条以上。它的优势是能自动学习时序模式省去特征工程的麻烦。但代价是需要更多数据和算力。import tensorflow as tf from tensorflow.keras import layers, models def build_1d_cnn(input_length, n_classes): 一维 CNN 用于原始振动信号分类 model models.Sequential([ # 第一层卷积提取局部波形特征 layers.Conv1D(32, kernel_size15, strides2, paddingsame, activationrelu, input_shape(input_length, 1)), layers.BatchNormalization(), layers.MaxPooling1D(pool_size2), # 第二层卷积组合局部特征 layers.Conv1D(64, kernel_size7, strides1, paddingsame, activationrelu), layers.BatchNormalization(), layers.MaxPooling1D(pool_size2), # 第三层卷积高层抽象 layers.Conv1D(128, kernel_size3, strides1, paddingsame, activationrelu), layers.GlobalAveragePooling1D(), # 替代 Flatten减少参数 layers.Dropout(0.5), # 防过拟合 layers.Dense(64, activationrelu), layers.Dense(n_classes, activationsoftmax) ]) model.compile( optimizertf.keras.optimizers.Adam(learning_rate1e-3), losssparse_categorical_crossentropy, metrics[accuracy] ) return model model build_1d_cnn(input_length5000, n_classes3) model.summary()几个关键参数第一层 kernel_size 要覆盖至少一个齿通过周期15 是在 10kHz 采样、3000rpm、4 齿条件下的经验值。GlobalAveragePooling 替代 Flatten 能大幅减少参数小样本下更稳。Dropout 0.5 是标配别省。注意一维 CNN 对输入归一化很敏感。原始振动信号幅值范围可能从 0.1 到 50不做归一化训练 loss 会震荡得你怀疑人生。用训练集的均值和标准差做 z-score验证集和测试集用同样的参数。4.3 类别不平衡急剧磨损样本太少怎么办实际数据里急剧磨损阶段通常只占整个寿命的 10%~15%三分类下这个类可能只有几十条样本。直接训练模型会倾向于忽略这个类。处理方式按优先级排class_weightbalanced最简单sklearn 和 Keras 都支持先试这个。SMOTE 过采样对特征空间做插值适合特征工程后的表格数据。但别对原始信号做 SMOTE插值出来的信号没有物理意义。focal loss深度学习场景下替代交叉熵让模型关注难分类样本。调整决策阈值不做重采样在预测阶段根据业务需求调整分类阈值。比如宁可误报也不漏报急剧磨损。from imblearn.over_sampling import SMOTE from collections import Counter # 假设 X_train, y_train 已经准备好 print(f原始分布: {Counter(y_train)}) smote SMOTE( sampling_strategynot majority, # 只对少数类过采样 k_neighbors5, # 近邻数小样本时调小 random_state42 ) X_resampled, y_resampled smote.fit_resample(X_train, y_train) print(f过采样后分布: {Counter(y_resampled)})SMOTE 的 k_neighbors 在少数类样本少于 10 条时要调小否则找不到足够的近邻。另外SMOTE 必须在训练集上做验证集和测试集保持原始分布——否则你评估出来的指标是虚高的。5. 避坑与排查那些让模型在产线上翻车的细节5.1 现象实验室 F1 0.95产线上不到 0.6原因训练数据和产线数据的分布不一致。最常见的是切削参数变了——实验室固定转速进给产线上根据工件材料自动调整。另一个原因是传感器安装位置或耦合方式变了。解决在特征层面做域适应。简单做法是加入切削参数作为特征转速、进给、切深让模型学会在参数变化下判断磨损。进阶做法是用对抗域适应但那个工程量就大了。最务实的做法是产线换型时采集少量新数据做微调哪怕每类只有 20 条fine-tune 一下效果就能拉回来。5.2 现象模型把换刀后初期磨损误判为急剧磨损原因新刀刚换上时后刀面有微观粗糙度振动信号特征和急剧磨损有相似之处——都是高频能量偏高。模型没学过刀具寿命阶段这个上下文。解决加入时间特征。最简单的做法是记录每把刀的累计切削时间或累计切削长度作为额外特征输入。这样模型能区分新刀的初期磨损和老刀的急剧磨损。如果产线有刀具管理系统直接读刀具寿命数据没有的话用主轴功率积分估算累计切削量。5.3 现象交叉验证分数很高但换一批数据就崩原因数据泄漏。最常见的是同一次走刀的数据被随机分到了训练集和测试集。同一次走刀的样本高度相关模型相当于在背答案。解决按走刀或按刀具做分组划分而不是随机划分。sklearn 的 GroupKFold 就是干这个的。from sklearn.model_selection import GroupKFold import numpy as np # groups 标记每个样本属于哪一次走刀或哪一把刀 groups np.repeat(np.arange(30), 20) # 30 次走刀每次 20 个样本 X np.random.randn(600, 20) y np.random.choice([0, 1, 2], size600) gkf GroupKFold(n_splits5) scores [] for train_idx, test_idx in gkf.split(X, y, groupsgroups): # 确保同一次走刀的数据不会同时出现在训练和测试集 assert len(set(groups[train_idx]) set(groups[test_idx])) 0 # 这里做训练和评估 scores.append(0.85) # 占位 print(f分组交叉验证平均分: {np.mean(scores):.4f})5.4 现象模型推理延迟太高跟不上产线节拍原因用了太深的 CNN 或太多特征。产线节拍可能只有几秒模型推理必须在几百毫秒内完成。解决先看特征维度。如果用了小波包 5 层分解特征维度 32加上时域频域总共可能上百维。用随机森林做特征选择砍到 15~20 维推理时间能降一个数量级。如果必须用 CNN考虑模型量化或知识蒸馏。但大多数产线场景梯度提升树 20 维特征就够用了推理时间在 10ms 以内。5.5 现象振动信号里混进了工频干扰原因传感器供电或信号线屏蔽不好50Hz 工频串进来了。这个干扰会污染低频段特征。解决硬件上检查屏蔽和接地。软件上做 50Hz 陷波滤波。from scipy.signal import iirnotch, filtfilt def remove_powerline_noise(signal, fs10000, freq50, Q30): 50Hz 工频陷波 b, a iirnotch(freq, Q, fs) return filtfilt(b, a, signal) # 如果工频谐波也明显在 100Hz、150Hz 也做陷波 cleaned remove_powerline_noise(normal_signal, fs10000, freq50)Q 值选 30 左右太窄了滤波效果不够太宽了会把附近的有效频率也吃掉。陷波之后一定要用 filtfilt 做零相位滤波否则引入的相位延迟会让时域特征偏移。6. 上线前的最后一道关用混淆矩阵和置信度校准做验收模型训练完别只看准确率。产线上最怕的是模型很自信地判错。验收阶段我必做两件事混淆矩阵细看和置信度校准。混淆矩阵要按业务代价加权看。把急剧磨损判成正常磨损的代价设为 10正常磨损判成急剧磨损的代价设为 1——前者是漏报后者是误报。漏报导致刀具崩刃、工件报废甚至机床损坏误报只是提前换刀浪费一点刀具寿命。两者代价差一个数量级。from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay import numpy as np # 假设 y_true, y_pred 是测试集上的真实标签和预测标签 y_true np.array([0, 1, 2, 0, 1, 2, 0, 1, 2, 0]) y_pred np.array([0, 1, 2, 0, 1, 0, 0, 2, 2, 0]) cm confusion_matrix(y_true, y_pred) # 代价矩阵cm[i,j] 表示真实为 i 预测为 j 的代价 cost_matrix np.array([ [0, 1, 5], # 真实初期磨损判成正常代价1判成急剧代价5 [1, 0, 10], # 真实正常磨损判成初期代价1判成急剧代价10 [20, 5, 0] # 真实急剧磨损判成初期代价20判成正常代价5 ]) total_cost np.sum(cm * cost_matrix) print(f加权总代价: {total_cost}) print(f混淆矩阵:\n{cm})置信度校准用可靠性图看。如果模型说90% 概率是急剧磨损那在所有说 90% 的样本里实际急剧磨损的比例应该接近 90%。如果差很远说明模型过度自信需要做 Platt scaling 或 isotonic regression 校准。from sklearn.calibration import CalibratedClassifierCV # 对已训练的分类器做概率校准 calibrated_clf CalibratedClassifierCV( base_estimatorrf_pipe, # 用你训练好的模型 methodisotonic, # 样本少时用 sigmoid cv3 ) # calibrated_clf.fit(X_train, y_train) # 之后用 calibrated_clf.predict_proba 获取校准后的概率校准之后产线上可以设一个灰区置信度低于 0.7 的样本不自动决策转人工确认。这样既保证了自动化率又避免了高代价的误判。我自己的习惯是每次模型上线前一定找产线老师傅看一遍混淆矩阵。他看一眼就能告诉你哪个错判最要命比任何指标都直接。这个习惯帮我省了至少两次重大翻车。希望帮到你。本文还有配套的精品资源点击获取