ARTICLE DETAIL

资讯详情

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

python的先进制造技术工业场景模拟第二十四篇:读取不同硬质合金刀具加工记录,统计每种刀具可加工零件数量,评估刀具耐用度。

python的先进制造技术工业场景模拟第二十四篇:读取不同硬质合金刀具加工记录,统计每种刀具可加工零件数量,评估刀具耐用度。 周三夜班机加车间。这批件干到 180 件刀又崩了刀具管理员老周把几根硬质合金刀摆桌上系统里记的是换刀事件可我想知道同牌号的刀干这个材料到底能撑多少件是 150 件就到头还是能到 220现在全靠老师傅拍脑袋换早了浪费刀换晚了出废品。我打开 MES 的导出表。这表里有什么老周问。每把刀一个编号加工零件时记开始件号、结束件号、加工材料、切削参数、是否崩刃我指着 CSV一把刀一行或者按寿命事件一行。可系统不聚合同型号刀平均能加工几件也不告诉你是后刀面磨损先到还是崩刃先到。我就想问一句老周说WC-Co 细晶的、超细晶的、涂层 PVD 的各统计一下平均寿命件数画个分布再告诉我哪批刀寿命离散大哪批稳。最好能按加工材料分开看钢件和不锈钢不能混着算。所以你要的不是换刀记录表是同型号刀具耐用度统计 寿命分布 离散度评估 异常刀识别对老周点头比如同是 PVD 涂层刀干 45 钢平均 195 件但有一把只干了 80 件就崩了我想把它挑出来看是不是那台机床主轴跳动了。明白了我开 VS Code用 pandas 读刀具加工记录numpy 算寿命件数scipy 做分布检验和置信区间matplotlib 画寿命箱线图威布尔图scikit-learn 聚类找异常刀networkx 把刀具型号→加工材料→机床建关系网看瓶颈。数据自包含——合成 5 类硬质合金刀×多把模拟正常磨损与少量早损下载就能跑。我敲了段原型import pandas as pdlife df.groupby(tool_id).apply(lambda g: g[end_part].max() - g[start_part].min() 1)完整版用 OOP 封好我说一个类管刀具记录加载一个类算单刀寿命一个类做型号级统计一个类做威布尔分析一个类找异常刀一个类画关系网一个类出图。输出每种刀平均件数、寿命分布、早损清单、威布尔斜率。老周凑近看那我以后看报告PVD 涂层干 45 钢均值 195 件威布尔斜率 8.2说明寿命挺集中有一把 82 件崩刃标红关联机床 M3就知道去查 M3 主轴了。对我接话刀具耐用度不是标称值是统计出来的分布。数字孪生里刀是消耗品模型得先有这张统计底表才能做换刀预测。一、实际应用场景真实痛点场景设定多型号硬质合金刀具不同牌号/涂层加工不同材料零件MES 记录每把刀的启用件号、停用件号、加工材料、机床号、停用原因正常磨损/崩刃/崩角/达到寿命。现场只记录事件不提供同型号刀具寿命聚合统计也不区分材料工况导致换刀策略全凭经验。现场原话叙事化我不是舍不得换刀老周说是没数。同是 PVD 涂层刀有的干 210 件有的 160 件就崩系统里就是两条换刀记录没人帮我算均值。有次我按 150 件换结果后面刀都还能用一年白扔几百根另一次按 220 件换出了批振纹废品。后来我把每把刀寿命手记在本子上可本子跟 MES 对不上也分不了材料。核心矛盾换刀事件流水与同型号刀具耐用度统计 寿命分布 威布尔评估 早损异常识别 工况归因之间的断层。需要一个硬质合金刀具耐用度统计与评估程序用pandas 聚合、numpy 算寿命、scipy 做分布拟合与检验、matplotlib 画箱线图/威布尔图、scikit-learn 聚类异常刀、networkx 建工况关系网。二、痛点分析映射到滨州职业学院《先进制造技术》课程模型《先进制造技术》模块 本篇痛点对应数控加工与CAD/CAM技术刀具材料与切削参数优化 硬质合金牌号/涂层耐用度量化统计先进制造技术基础制造过程资源消耗管理 刀具作为关键消耗资源寿命建模FMS与先进生产管理单元级刀具管理与换刀策略 按型号聚合寿命支撑换刀阈值设定智能制造与数字孪生刀具数字孪生 寿命分布→可预测换刀模型底座先进制造新模式成本与质量协同零缺陷 早损识别避免批量振纹废品一句话总结我们需要构建一个硬质合金刀具耐用度统计与评估程序用pandas 按型号聚合scipy 做威布尔拟合scikit-learn 找早损异常networkx 关联工况实现从换刀流水账到可统计、可预测、可归因的刀具寿命模型。三、核心逻辑讲解大白话3.1 问题本质把刀具想成灯泡寿命把每把刀想成一个灯泡* 刀具型号 灯泡品牌细晶 WC-Co / 超细晶 / PVD涂层…* 加工件数 亮了多少小时* 停用原因 怎么灭的自然暗下去正常磨损炸了崩刃* 同型号多把刀 同一品牌一堆灯泡有的亮久点有的短点* 平均件数 品牌标称寿命* 威布尔斜率 这批灯泡齐不齐心斜率大5说明寿命很集中斜率小说明忽长忽短* 早损刀 同批里早早炸掉的那个灯泡得拎出来看是不是装错机床了* 工况关系网 哪种刀在哪种材料哪台机床上最费工业应用*pandas.groupby([tool_grade,material]) 聚合寿命件数*scipy.stats.weibull_min 拟合寿命分布取形状参数 k*scikit-learn 用寿命斜率偏差做早损聚类*networkx 建刀具型号—材料—机床 三层图找高消耗路径3.2 业务逻辑 → 代码映射定义刀具加工记录模型│▼ ToolLogLoader (pandas)导入 CSVtool_id, tool_grade, coating, material, machine,start_part, end_part, stop_reason校验件号连续、去重│▼ ToolLifeCalculator (numpy/pandas)单刀寿命life_parts end_part - start_part 1按刀聚合标记早损│▼ GradeStatistics (pandas/numpy)型号级统计按 (牌号,涂层,材料) 分组均值/中位数/标准差/P10/P90/置信区间│▼ WeibullAnalyzer (scipy)寿命分布weibull_min.fit - 形状k, 尺度ηk5 集中, k2 离散大计算 B10 寿命(10%失效件数)│▼ AnomalyDetector (scikit-learn)早损识别特征[寿命, 偏离同组均值比例]IsolationForest / KMeans 找异常刀│▼ ToolConditionGraph (networkx)工况关系网刀具型号-材料-机床 三层图边权平均消耗件数倒数(越费权重越大)│▼ ToolVisualizer (matplotlib)可视化1. 各型号寿命箱线图2. 威布尔概率图3. 早损刀散点标记4. 工况关系网图5. 寿命直方图标称线│▼ SyntheticToolGenerator (numpy)合成数据5类硬质合金刀每类多把干45钢/不锈钢/铸铁含正常磨损少量早损3.3 为什么用威布尔而不是正态分布* 问题寿命数据有下限 0、右偏正态会算出负寿命且对早损不敏感。* 处理策略威布尔分布是可靠性工程标准模型形状参数直接反映失效模式。* 工程合理性跟刀具厂寿命样本报告口径一致B10 寿命可直接用于换刀策略。3.4 分析前后对比维度 MES换刀记录 本程序同型号均值 手算 自动分组聚合寿命分布 无 威布尔拟合 B10早损识别 靠肉眼 聚类自动标红工况归因 无 networkx 关系网换刀阈值 拍脑袋 P90 B10 双参考四、OOP 代码实现4.1 项目结构tool_life_eval/├── tool_life_eval/│ ├── __init__.py│ ├── log_loader.py # 刀具记录加载│ ├── life_calculator.py # 单刀寿命计算│ ├── grade_statistics.py # 型号级统计│ ├── weibull_analyzer.py # 威布尔分析│ ├── anomaly_detector.py # 早损异常识别│ ├── condition_graph.py # 工况关系网│ ├── visualizer.py # 可视化│ └── synthetic_data.py # 合成数据├── tests/│ ├── __init__.py│ └── test_tool_life.py├── results/│ ├── box_life_by_grade.png # 箱线图│ ├── weibull_probplot.png # 威布尔概率图│ ├── anomaly_scatter.png # 早损散点│ ├── condition_graph.png # 工况关系网│ ├── life_hist.png # 寿命直方图│ ├── grade_life_stats.csv # 型号统计表│ ├── early_failure_list.csv # 早损清单│ └── weibull_report.txt└── run_tool_life.py4.2 核心源码detailssummary/summary刀具加工记录加载器import pandas as pdfrom pathlib import Pathfrom typing import Optionalclass ToolLogLoader:加载刀具加工流水记录STOP_REASONS (normal_wear, chipping, edge_break, reach_life)def __init__(self, filepath: str tool_logs.csv,encoding: str utf-8):self.filepath Path(filepath)self.encoding encodingself._raw: Optional[pd.DataFrame] Nonedef load(self) - pd.DataFrame:if not self.filepath.exists():raise FileNotFoundError(f文件不存在: {self.filepath})self._raw pd.read_csv(self.filepath, encodingself.encoding)rename {}for tgt, al in {tool_id: [tool_id, 刀号, id],tool_grade: [tool_grade, 牌号, grade],coating: [coating, 涂层, coat],material: [material, 加工材料, mat],machine: [machine, 机床, mc],start_part: [start_part, 起始件, sp],end_part: [end_part, 结束件, ep],stop_reason: [stop_reason, 停用原因, reason],}.items():if tgt not in self._raw.columns:for a in al:if a in self._raw.columns:rename[a] tgtbreakself._raw self._raw.rename(columnsrename)req [tool_id, tool_grade, coating, material,machine, start_part, end_part, stop_reason]miss [c for c in req if c not in self._raw.columns]if miss:raise ValueError(f缺少必要列: {miss})for c in [start_part, end_part]:self._raw[c] pd.to_numeric(self._raw[c], errorscoerce)self._raw self._raw.dropna(subset[start_part, end_part])self._raw self._raw[(self._raw[end_part] self._raw[start_part])]self._raw self._raw.sort_values([tool_grade, tool_id]).reset_index(dropTrue)return self._raw.copy()/detailsdetailssummary/summary单刀寿命计算import numpy as npimport pandas as pdfrom typing import Optionalclass ToolLifeCalculator:计算每把刀可加工零件数def __init__(self):passdef per_tool(self, df: pd.DataFrame) - pd.DataFrame:rows []for tool_id, g in df.groupby(tool_id):head g.iloc[0]# 支持一把刀分段加工时取全局起止sp int(g[start_part].min())ep int(g[end_part].max())life ep - sp 1rows.append({tool_id: tool_id,tool_grade: head[tool_grade],coating: head[coating],material: head[material],machine: head[machine],start_part: sp,end_part: ep,life_parts: int(life),stop_reason: g[stop_reason].mode().iloc[0],})out pd.DataFrame(rows)return out.sort_values(tool_id).reset_index(dropTrue)def tag_early_failure(self, life_df: pd.DataFrame,group_keys(tool_grade, coating, material),k_sigma: float 2.0) - pd.DataFrame:按同工况组均值-2σ标记早损df life_df.copy()stats df.groupby(list(group_keys))[life_parts].agg([mean, std]).reset_index()stats[std] stats[std].fillna(0.0)df df.merge(stats, onlist(group_keys), howleft)df[is_early] (df[life_parts] df[mean] - k_sigma * df[std]) | \(df[stop_reason].isin([chipping, edge_break]))df[dev_from_mean] (df[life_parts] - df[mean]).round(2)for c in [mean, std]:df[c] df[c].round(2)return df/detailsdetailssummary/summary型号级耐用度统计import numpy as npimport pandas as pdfrom scipy import statsfrom typing import Optionalclass GradeStatistics:按 (牌号,涂层,材料) 聚合统计def __init__(self, conf: float 0.95):self.conf confdef summarize(self, life_df: pd.DataFrame) - pd.DataFrame:keys [tool_grade, coating, material]g life_df.groupby(keys)[life_parts]n g.count()mean g.mean()med g.median()std g.std(ddof1).fillna(0.0)p10 g.quantile(0.10)p90 g.quantile(0.90)# 均值置信区间rows []for key_tuple in mean.index:sub life_df[(life_df[tool_grade] key_tuple[0]) (life_df[coating] key_tuple[1]) (life_df[material] key_tuple[2])][life_parts].valuesif len(sub) 1:ci stats.t.interval(self.conf, len(sub) - 1,locsub.mean(), scalestats.sem(sub))ci_lo, ci_hi round(ci[0], 1), round(ci[1], 1)else:ci_lo ci_hi round(float(sub.mean()), 1)rows.append({tool_grade: key_tuple[0],coating: key_tuple[1],material: key_tuple[2],n_tools: int(n[key_tuple]),mean_life: round(mean[key_tuple], 1),median_life: round(med[key_tuple], 1),std_life: round(std[key_tuple], 1),p10_life: round(p10[key_tuple], 1),p90_life: round(p90[key_tuple], 1),ci95_lo: ci_lo,ci95_hi: ci_hi,early_fail_cnt: int(((life_df[(life_df[tool_grade] key_tuple[0]) (life_df[coating] key_tuple[1]) (life_df[material] key_tuple[2])][is_early] True).sum()),})return pd.DataFrame(rows).sort_values(mean_life, ascendingFalse).reset_index(dropTrue)/detailsdetailssummary/summary威布尔寿命分析 (scipy)import numpy as npimport pandas as pdfrom scipy import statsfrom typing import Dict, Optionalclass WeibullAnalyzer:对每组寿命做威布尔最小分布拟合def __init__(self):passdef fit_group(self, life_values: np.ndarray) - Dict:x np.asarray(life_values, dtypefloat)x x[x 0]if len(x) 3:return {k: np.nan, eta: np.nan, b10: np.nan}# weibull_min(c, loc, scale), c即形状kc, loc, scale stats.weibull_min.fit(x, floc0)k float(c)eta float(scale)b10 float(eta * np.log(1 / 0.9) ** (1 / k)) if k 0 else np.nanreturn {k: round(k, 3), eta: round(eta, 1), b10: round(b10, 1)}def fit_all_groups(self, life_df: pd.DataFrame) - pd.DataFrame:keys [tool_grade, coating, material]rows []for key_tuple, g in life_df.groupby(keys):res self.fit_group(g[life_parts].values)rows.append({tool_grade: key_tuple[0],coating: key_tuple[1],material: key_tuple[2],weibull_k: res[k],weibull_eta: res[eta],b10_life: res[b10],dispersion: 集中 if (res[k] and res[k] 5)else (离散 if (res[k] and res[k] 2) else 中等),})return pd.DataFrame(rows).sort_values(weibull_k, ascendingFalse).reset_index(dropTrue)def probplot_data(self, life_values: np.ndarray):返回威布尔概率图坐标x np.sort(np.asarray(life_values, dtypefloat))x x[x 0]n len(x)if n 2:return x, np.array([])f (np.arange(1, n 1) - 0.3) / (n 0.4)y np.log(-np.log(1 - f))return x, y/detailsdetailssummary/summary早损异常刀识别 (scikit-learn)import numpy as npimport pandas as pdfrom sklearn.cluster import KMeansfrom sklearn.preprocessing import StandardScalerfrom typing import Optionalclass AnomalyDetector:基于同工况偏离度聚类找早损刀def __init__(self, n_clusters: int 2, random_state: int 42):self.n_clusters n_clustersself.random_state random_stateself.scaler StandardScaler()def detect(self, tagged_df: pd.DataFrame) - pd.DataFrame:df tagged_df.copy()# 特征寿命 偏离均值比例df[dev_ratio] df[dev_from_mean] / (df[mean] 1e-9)X df[[life_parts, dev_ratio]].valuesXs self.scaler.fit_transform(X)k min(self.n_clusters, len(df))km KMeans(n_clustersk, random_stateself.random_state, n_init10)df[cluster] km.fit_predict(Xs)# 寿命低的簇标记为异常簇cmean df.groupby(cluster)[life_parts].mean()low_cluster cmean.idxmin()df[cluster_is_early] df[cluster] low_cluster# 综合标记规则早损 OR 聚类异常簇df[final_early_flag] df[is_early] | df[cluster_is_early]return df.sort_values(life_parts).reset_index(dropTrue)def summary(self, df: pd.DataFrame) - pd.DataFrame:sub df[df[final_early_flag]]return sub[[tool_id, tool_grade, coating, material,machine, life_parts, mean, stop_reason,dev_from_mean]].copy()/detailsdetailssummary/summary工况关系网 (networkx)import networkx as nximport pandas as pdfrom typing import Optionalclass ToolConditionGraph:刀具型号-材料-机床 三层关系网def __init__(self):self.G nx.Graph()def build(self, life_df: pd.DataFrame, stats_df: pd.DataFrame) - nx.Graph:self.G.clear()# 节点for _, r in stats_df.iterrows():gid f{r[tool_grade]}|{r[coating]}self.G.add_node(gid, ntypegrade,mean_lifer[mean_life])self.G.add_node(r[material], ntypematerial)for m in life_df[machine].unique():self.G.add_node(m, ntypemachine)# 边grade-material, 权1/均值寿命(越费越大)for _, r in stats_df.iterrows():gid f{r[tool_grade]}|{r[coating]}w round(1.0 / (r[mean_life] 1e-9), 5)self.G.add_edge(gid, r[material], weightw,mean_lifer[mean_life])# 机床级平均寿命mc life_df.groupby([machine, tool_grade, coating])[life_parts].mean().reset_index()for _, r in mc.iterrows():gid f{r[tool_grade]}|{r[coating]}self.G.add_edge(gid, r[machine],weightround(1.0 / (r[life_parts] 1e-9), 5),mean_liferound(r[life_parts], 1))return self.Gdef bottleneck_paths(self) - pd.DataFrame:找平均寿命最低的 grade-material-machine 组合rows []for u, v, d in self.G.edges(dataTrue):if mean_life in d:rows.append({node_a: u, node_b: v,mean_life: d[mean_life],weight: d[weight]})df pd.DataFrame(rows)if df.empty:return dfreturn df.sort_values(mean_life).reset_index(dropTrue)/detailsdetailssummary/summary可视化import numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport networkx as nxfrom pathlib import Pathplt.rcParams[font.sans-serif] [SimHei, DejaVu Sans]plt.rcParams[axes.unicode_minus] Falseclass ToolVisualizer:def __init__(self, results_dir: str results):self.results_dir Path(results_dir)self.results_dir.mkdir(exist_okTrue)def box_life(self, life_df, stats_df):fig, ax plt.subplots(figsize(14, 6))labels stats_df.apply(lambda r: f{r[tool_grade]}\n{r[coating]}\n{r[material]}, axis1)data []for _, r in stats_df.iterrows():sub life_df[(life_df[tool_grade] r[tool_grade]) (life_df[coating] r[coating]) (life_df[material] r[material])][life_parts].valuesdata.append(sub)ax.boxplot(data, labelslabels, showmeansTrue)ax.set_ylabel(可加工件数, fontsize12)ax.set_title(各型号刀具寿命箱线图, fontsize14, fontweightbold)plt.xticks(rotation30, haright, fontsize8)ax.grid(axisy, alpha0.3)plt.tight_layout()plt.savefig(self.results_dir / box_life_by_grade.png,dpi150, bbox_inchestight)plt.close()def weibull_probplot(self, life_df, weibull_df):fig, ax plt.subplots(figsize(12, 7))for _, w in weibull_df.iterrows():sub life_df[(life_df[tool_grade] w[tool_grade]) (life_df[coating] w[coating]) (life_df[material] w[material])][life_parts].valuesx, y WeibullProbHelper().probplot_data(sub)if len(x) 2:continueax.plot(x, y, .-, labelf{w[tool_grade]}|{w[coating]}|{w[material]} k{w[weibull_k]})ax.set_xscale(log)ax.set_xlabel(寿命件数 (log), fontsize12)ax.set_ylabel(ln(-ln(1-F)), fontsize12)ax.set_title(威布尔概率图, fontsize14, fontweightbold)ax.legend(fontsize7)ax.grid(alpha0.3)plt.tight_layout()plt.savefig(self.results_dir / weibull_probplot.png,dpi150, bbox_inchestight)plt.close()def anomaly_scatter(self, det_df):fig, ax plt.subplots(figsize(12, 6))normal det_df[~det_df[final_early_flag]]early det_df[det_df[final_early_flag]]ax.scatter(normal[mean], normal[life_parts], c#3498DB,label正常刀, s30, alpha0.7)ax.scatter(early[mean], early[life_parts], c#E74C3C,label早损刀, s60, edgecolork)# 参考线 ymeanallx det_df[mean].valuesax.plot(allx, allx, g--, alpha0.5, label同组均值线)for _, r in early.iterrows():ax.annotate(r[tool_id], (r[mean], r[life_parts]),fontsize7, colorred)ax.set_xlabel(同工况组平均寿命(件), fontsize12)ax.set_ylabel(本刀寿命(件), fontsize12)ax.set_title(早损刀具识别散点图, fontsize14, fontweightbold)ax.legend()ax.grid(alpha0.3)plt.tight_layout()plt.savefig(self.results_dir / anomaly_scatter.png,dpi150, bbox_inchestight)plt.close()def condition_graph(self, G):fig, ax plt.subplots(figsize(14, 9))pos nx.spring_layout(G, seed42, k0.6)ncolors []for n, d in G.nodes(dataTrue):t d.get(ntype, grade)ncolors.append({grade: #E74C3C, material: #2ECC71,machine: #3498DB}.get(t, #999))sizes [300 (G.nodes[n].get(mean_life, 50) * 3 if G.nodes[n].get(ntype)grade else 300) for n in G.nodes()]nx.draw_networkx_nodes(G, pos, node_colorncolors,node_sizesizes, axax, alpha0.9)edges [(u, v) for u, v, d in G.edges(dataTrue)]ws [G.edges[u, v][weight] * 8000 for u, v in edges]nx.draw_networkx_edg利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛
返回列表