周五下午CNC 加工车间工具室。又废了一批活车间主任老周把一摞报废的铝合金壳体推到我面前刀纹明显粗糙度超差。调出系统一看——加工中心 3 号机用的那把 Φ10 立铣刀已经连续切了 6.5 小时。标准寿命是 6 小时。我打开刀具管理系统导出的 CSV 文件。几千行记录字段包括刀具编号、类型立铣刀/面铣刀/钻头/丝锥、安装机床、安装时间、累计加工时间h、标准寿命h、已加工件数、磨损状态正常/注意/警告/报废、最后更新时间。系统里不是有寿命数据吗我问。有老周点开一行但它是按把记录的——每把刀一行显示累计加工时间和标准寿命。我想知道的是哪些刀已经达到寿命了、该换了哪些快到了、要提前准备。系统没有到期刀具清单这个功能——它只会在一把刀报废的时候弹一个报警但那时候活已经废了。所以你现在每次换刀都是凭经验看加工表面对老周苦笑老师傅凭声音和刀纹判断。但新人不行——上次有个新来的一把刀干了 8 小时才发现不对前面 2 小时的活全废了。厂长说必须搞一个刀具更换预警清单每天早会发一份。明白了我打开 VS Code你需要一个程序读取所有刀具的寿命记录拿累计加工时间和标准寿命做比较算出剩余寿命百分比然后筛出两类刀——已经超期的累计 标准和即将到期的剩余 20%。输出一张清单按紧急程度排序。这样每天早会你直接把清单打印出来红色的是必须立刻换的黄色的是本周要准备的。我敲了几行代码import pandas as pdimport matplotlib.pyplot as plt# 读取刀具寿命数据df pd.read_csv(tool_life.csv)# 计算剩余寿命百分比df[remaining_pct] (df[std_life_h] - df[used_h]) / df[std_life_h] * 100df[remaining_pct] df[remaining_pct].clip(lower0)# 筛选到期刀具expired df[df[used_h] df[std_life_h]].copy()expired[priority] 立即更换# 筛选预警刀具剩余 20%warning df[(df[used_h] df[std_life_h]) (df[remaining_pct] 20)].copy()warning[priority] 近期更换# 合并清单replacement_list pd.concat([expired, warning]).sort_values(remaining_pct)print(replacement_list[[tool_id, tool_type, machine, used_h, std_life_h, remaining_pct, priority]])这只是核心逻辑我说完整版会用 OOP 封装一个类管数据加载一个类管数据清洗处理缺失寿命值、统一磨损状态编码一个类管寿命计算和阈值筛选一个类管可视化剩余寿命条形图、类型分布饼图、机床负载热力图。数据自包含——用 numpy 合成一份刀具寿命 CSV读者下载就能跑。老周凑近屏幕所以你这东西就是把几千把刀的用了多久和该用多久比一下——超了的标红快到的标黄排个序打印出来。新人照着清单换刀不会废活了。对。而且还能做趋势分析我补充比如统计各类型刀具的平均实际寿命 vs 标准寿命看看是不是某种刀的设定寿命偏短了——如果是可以跟工艺部门反馈调整标准。刀具管理不是到点就换是用数据证明该不该换。一、实际应用场景真实痛点场景设定CNC 加工车间拥有数十台加工中心和数百把数控刀具每把刀具在管理系统中记录安装时间、累计加工时间、标准寿命、已加工件数、磨损状态等字段。车间工具室需要定期生成刀具更换清单防止刀具超期使用导致工件报废。现场常见痛点刀具数据按把逐条记录行数庞大几百把活跃刀具系统不提供按寿命阈值筛选的到期清单功能依赖操作工凭经验判断新人容易误判导致批量报废。现场原话叙事化我不是不想管刀具寿命老周说每把刀的数据都在系统里累计用了多久、标准寿命是多少清清楚楚。但系统不会告诉我现在有 12 把刀已经超期了、8 把快到了——它只会在一把刀彻底报废的时候弹一个报警。那时候活已经废了报警有什么用我需要的是每天上班前桌上有一张清单哪些刀今天必须换、哪些这周要准备。核心矛盾逐把记录的刀具寿命明细与按阈值筛选的更换预警清单之间的聚合断层。需要一个刀具寿命筛选与更换清单生成程序用pandas 读取并计算剩余寿命用numpy 做阈值判断和统计用matplotlib 绘制剩余寿命分布图输出结构化的刀具更换清单。二、痛点分析映射到滨州职业学院《先进制造技术》课程模型《先进制造技术》模块 本篇痛点对应数控加工与CAD/CAM技术数控刀具系统与寿命管理 刀具寿命模型基于加工时间的刀具磨损规律与寿命阈值设定。先进制造技术基础先进制造系统的维护策略 预防性维护基于寿命数据的刀具更换预警避免突发报废。FMS与先进生产管理FMS 的刀具管理子系统 刀具调度与准备提前生成更换清单减少机床等待时间。智能制造与数字孪生加工过程的数字孪生 刀具磨损状态可视化将寿命数据转化为直观的更换优先级。一句话总结我们需要构建一个刀具寿命记录筛选与更换清单生成程序用pandas 读取并计算剩余寿命百分比用numpy 做阈值筛选用matplotlib 绘制寿命分布图实现从逐把记录到更换预警清单的转化。三、核心逻辑讲解大白话3.1 问题本质把刀具寿命想象成汽车保养提醒把刀具寿命管理想象成汽车的保养提醒系统* 标准寿命 保养周期汽车每 5000 公里换机油刀具每 6 小时加工时间换刀——都是标准寿命。* 累计加工时间 已行驶里程汽车仪表盘显示已跑 4800 公里刀具记录显示已用 5.5 小时。* 剩余寿命百分比 保养倒计时(标准 - 已用) / 标准 × 100%。汽车剩 4% 该保养了刀具剩 10% 该准备了。* 更换清单 4S 店的到期保养清单把所有车按剩余保养里程排序先保养快到周期的——对应车间里先换快到期的刀。工业应用* pandas 计算剩余寿命df[remaining_pct] (df[std_life_h] - df[used_h]) / df[std_life_h] * 100一行代码算出每把刀的剩余百分比。* numpy 阈值筛选np.where(df[used_h] df[std_life_h], 到期, np.where(df[remaining_pct] 20, 预警, 正常))向量化条件判断比逐行 if-else 快几十倍。* matplotlib 条形图横轴刀具编号纵轴剩余寿命百分比红色标到期、黄色标预警、绿色标正常——一张图看清全车间刀具状态。3.2 业务逻辑 → 代码映射定义刀具寿命数据模型│▼ ToolDataLoader (pandas)读取刀具 CSVpd.read_csv(tool_life.csv)解析刀具编号、类型、机床、累计加工时间、标准寿命│▼ DataCleaner (pandas)数据清洗处理缺失寿命值删除或标记统一磨损状态编码正常/注意/警告/报废 → 0/1/2/3│▼ LifeCalculator (pandas/numpy)寿命计算remaining_h std_life_h - used_hremaining_pct remaining_h / std_life_h * 100│▼ ThresholdFilter (numpy)阈值筛选到期used_h std_life_h预警remaining_pct 20%正常其余│▼ ReplacementListGenerator更换清单生成按紧急程度排序到期 → 预警 → 正常输出 CSV 清单│▼ ToolVisualizer (matplotlib)可视化1. 剩余寿命水平条形图按类型着色2. 刀具类型寿命分布箱线图3. 各机床刀具数量与到期数对比│▼ SyntheticDataGenerator (numpy)合成数据生成用 numpy 按类型生成刀具寿命数据模拟不同刀具的寿命分布确保数据自包含、可复现3.3 为什么用向量化条件判断而不是逐行循环* 问题300 把刀每把写if used std: status 到期代码冗长且慢。* 处理策略np.select() 或np.where() 向量化操作一行代码完成全部判断底层 C 加速。* 工程合理性车间可能有上千把刀在册向量化是唯一合理的选择。3.4 分析前后对比维度 人工凭经验判断 Python 程序分析判断依据 听声音、看刀纹主观 累计加工时间 vs 标准寿命客观覆盖范围 只能关注正在用的刀 全部在册刀具预警能力 报废了才发现 剩余 20% 就预警输出形式 感觉这把刀差不多了 按紧急程度排序的更换清单新人友好度 需要多年经验 照着清单换就行四、OOP 代码实现4.1 项目结构cnc_tool_life_manager/├── cnc_tool_life_manager/│ ├── __init__.py│ ├── data_loader.py # 刀具数据加载│ ├── data_cleaner.py # 数据清洗│ ├── life_calculator.py # 寿命计算│ ├── threshold_filter.py # 阈值筛选│ ├── replacement_list.py # 更换清单生成│ ├── visualizer.py # 可视化│ └── synthetic_data.py # 合成数据生成├── tests/│ ├── __init__.py│ └── test_tool_life.py # 单元测试├── results/│ ├── replacement_list.csv # 刀具更换清单│ ├── expired_only.csv # 已到期刀具│ ├── warning_only.csv # 预警刀具│ ├── remaining_life_bar.png # 剩余寿命条形图│ ├── life_boxplot.png # 寿命分布箱线图│ ├── machine_tool_heatmap.png # 机床刀具状态热力图│ └── tool_report.txt # 分析报告└── run_tool_life.py # 主程序入口4.2 核心源码detailssummary/summary数控刀具数据加载器import pandas as pdfrom pathlib import Pathfrom typing import Optionalclass ToolDataLoader:数控刀具数据加载器支持 CSV 格式的刀具寿命记录。def __init__(self, filepath: str, encoding: str utf-8):self.filepath Path(filepath)self.encoding encodingself._raw_df: Optional[pd.DataFrame] Nonedef load(self,tool_id_col: str tool_id,type_col: str tool_type,machine_col: str machine,used_col: str used_h,std_col: str std_life_h,wear_col: str wear_status) - pd.DataFrame:加载刀具寿命数据Parameters----------tool_id_col : str刀具编号列名type_col : str刀具类型列名machine_col : str安装机床列名used_col : str累计加工时间列名std_col : str标准寿命列名wear_col : str磨损状态列名Returns-------pd.DataFrameif not self.filepath.exists():raise FileNotFoundError(f文件不存在: {self.filepath})self._raw_df pd.read_csv(self.filepath, encodingself.encoding)# 列名标准化col_aliases {tool_id_col: [刀具编号, tool_no, tool_id, id],type_col: [刀具类型, type, tool_type, category],machine_col: [机床, machine, mc, cnc],used_col: [已用时间, used, used_h, accumulated_time],std_col: [标准寿命, std_life, std_life_h, standard_life],wear_col: [磨损状态, wear, wear_status, condition],}rename_map {}for target, aliases in col_aliases.items():if target not in self._raw_df.columns:for alias in aliases:if alias in self._raw_df.columns:rename_map[alias] targetbreakif rename_map:self._raw_df self._raw_df.rename(columnsrename_map)return self._raw_df.copy()def get_raw_data(self) - Optional[pd.DataFrame]:return self._raw_df.copy() if self._raw_df is not None else None/detailsdetailssummary/summary刀具数据清洗import numpy as npimport pandas as pdfrom typing import Optionalclass DataCleaner:数控刀具数据清洗器处理缺失值、统一磨损状态编码。# 磨损状态映射WEAR_MAP {正常: 0, Normal: 0, OK: 0, 0: 0,注意: 1, Caution: 1, Monitor: 1, 1: 1,警告: 2, Warning: 2, Warn: 2, 2: 2,报废: 3, Failed: 3, Fail: 3, 3: 3,}def __init__(self):passdef clean(self,df: pd.DataFrame,used_col: str used_h,std_col: str std_life_h,wear_col: str wear_status) - pd.DataFrame:清洗刀具数据Parameters----------df : pd.DataFrame原始数据used_col : str累计加工时间列名std_col : str标准寿命列名wear_col : str磨损状态列名Returns-------pd.DataFrameresult df.copy()# 数值化result[used_col] pd.to_numeric(result[used_col], errorscoerce)result[std_col] pd.to_numeric(result[std_col], errorscoerce)# 处理缺失值删除关键字段为空且无法推断的行result result.dropna(subset[used_col, std_col], howall)# 过滤异常值result result[result[used_col] 0]result result[result[std_col] 0]# 统一磨损状态编码if wear_col in result.columns:result[wear_code] result[wear_col].map(lambda x: self.WEAR_MAP.get(str(x).strip(), -1))else:result[wear_code] -1return result.reset_index(dropTrue)/detailsdetailssummary/summary刀具寿命计算器import numpy as npimport pandas as pdfrom typing import Dict, Optionalclass LifeCalculator:数控刀具寿命计算器计算剩余寿命小时数和百分比。def __init__(self,used_col: str used_h,std_col: str std_life_h):self.used_col used_colself.std_col std_coldef calculate(self, df: pd.DataFrame) - pd.DataFrame:计算剩余寿命Parameters----------df : pd.DataFrame清洗后的数据Returns-------pd.DataFrame新增 remaining_h 和 remaining_pct 列result df.copy()result[remaining_h] (result[self.std_col] - result[self.used_col]).clip(lower0)result[remaining_pct] (result[remaining_h] / result[self.std_col] * 100)return resultdef get_statistics(self, df: pd.DataFrame) - Dict:计算整体寿命统计return {total_tools: len(df),avg_used: df[self.used_col].mean(),avg_std: df[self.std_col].mean(),avg_remaining_pct: df[remaining_pct].mean(),min_remaining_pct: df[remaining_pct].min(),}/detailsdetailssummary/summary阈值筛选器import numpy as npimport pandas as pdfrom typing import Dict, Optionalclass ThresholdFilter:数控刀具阈值筛选器基于寿命阈值筛选到期和预警刀具。STATUS_EXPIRED 到期STATUS_WARNING 预警STATUS_NORMAL 正常def __init__(self,used_col: str used_h,std_col: str std_life_h,remaining_pct_col: str remaining_pct,warning_threshold: float 20.0):self.used_col used_colself.std_col std_colself.remaining_pct_col remaining_pct_colself.warning_threshold warning_thresholddef filter(self, df: pd.DataFrame) - pd.DataFrame:阈值筛选Parameters----------df : pd.DataFrame含寿命计算结果的数据Returns-------pd.DataFrame新增 status 列result df.copy()conditions [result[self.used_col] result[self.std_col],result[self.remaining_pct_col] self.warning_threshold,]choices [self.STATUS_EXPIRED, self.STATUS_WARNING]result[status] np.select(conditions, choices, defaultself.STATUS_NORMAL)return resultdef get_expired(self, df: pd.DataFrame) - pd.DataFrame:获取已到期刀具return df[df[status] self.STATUS_EXPIRED].copy()def get_warning(self, df: pd.DataFrame) - pd.DataFrame:获取预警刀具return df[df[status] self.STATUS_WARNING].copy()def get_normal(self, df: pd.DataFrame) - pd.DataFrame:获取正常刀具return df[df[status] self.STATUS_NORMAL].copy()def summary(self, df: pd.DataFrame) - Dict[str, int]:统计各状态数量return {self.STATUS_EXPIRED: (df[status] self.STATUS_EXPIRED).sum(),self.STATUS_WARNING: (df[status] self.STATUS_WARNING).sum(),self.STATUS_NORMAL: (df[status] self.STATUS_NORMAL).sum(),}/detailsdetailssummary/summary更换清单生成器import numpy as npimport pandas as pdfrom typing import Optionalclass ReplacementListGenerator:刀具更换清单生成器按紧急程度排序生成结构化更换清单。PRIORITY_MAP {到期: 0,预警: 1,正常: 2,}def __init__(self,tool_id_col: str tool_id,type_col: str tool_type,machine_col: str machine):self.tool_id_col tool_id_colself.type_col type_colself.machine_col machine_coldef generate(self, df: pd.DataFrame) - pd.DataFrame:生成更换清单Parameters----------df : pd.DataFrame已筛选和标记状态的数据Returns-------pd.DataFrame按优先级排序的更换清单result df.copy()# 添加优先级result[priority] result[status].map(self.PRIORITY_MAP)# 按优先级和剩余寿命排序result result.sort_values([priority, remaining_pct],ascending[True, True],)# 选择输出列output_cols [self.tool_id_col, self.type_col, self.machine_col,used_h, std_life_h, remaining_h,remaining_pct, status, wear_status,]available_cols [c for c in output_cols if c in result.columns]return result[available_cols].reset_index(dropTrue)def to_csv(self, df: pd.DataFrame, filepath: str) - None:导出为 CSVdf.to_csv(filepath, indexFalse, encodingutf-8-sig)/detailsdetailssummary/summary刀具寿命可视化器import numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom pathlib import Pathfrom typing import Optionalplt.rcParams[font.sans-serif] [SimHei, DejaVu Sans]plt.rcParams[axes.unicode_minus] Falseclass ToolVisualizer:数控刀具寿命可视化STATUS_COLORS {到期: #E74C3C,预警: #F39C12,正常: #2ECC71,}TYPE_COLORS {立铣刀: #3498DB,面铣刀: #9B59B6,钻头: #1ABC9C,丝锥: #E67E22,镗刀: #34495E,球头铣刀: #E91E63,}def __init__(self, results_dir: str results):self.results_dir Path(results_dir)self.results_dir.mkdir(exist_okTrue)def plot_remaining_life_bar(self,df: pd.DataFrame,top_n: int 30,title: str 刀具剩余寿命排行) - None:绘制剩余寿命水平条形图# 取剩余寿命最低的 top_nplot_df df.nsmallest(top_n, remaining_pct).copy()plot_df plot_df.sort_values(remaining_pct, ascendingTrue)fig, ax plt.subplots(figsize(12, max(6, top_n * 0.35)))colors [self.STATUS_COLORS.get(row[status], #BDC3C7)for _, row in plot_df.iterrows()]bars ax.barh(plot_df[tool_id].astype(str),plot_df[remaining_pct],colorcolors,height0.6,)ax.set_xlabel(剩余寿命 (%), fontsize12)ax.set_title(title, fontsize14, fontweightbold)ax.set_xlim(0, 105)# 参考线ax.axvline(x20, color#F39C12, linestyle--,linewidth1, alpha0.7, label预警线 20%)ax.axvline(x0, color#E74C3C, linestyle--,linewidth1, alpha0.7, label到期线 0%)ax.legend(loclower right)# 标注数值for bar, val in zip(bars, plot_df[remaining_pct]):ax.text(bar.get_width() 1,bar.get_y() bar.get_height() / 2,f{val:.1f}%,vacenter,fontsize8,)plt.tight_layout()plt.savefig(self.results_dir / remaining_life_bar.png,dpi150, bbox_inchestight)plt.close()def plot_life_boxplot(self,df: pd.DataFrame,type_col: str tool_type,used_col: str used_h,title: str 各类型刀具寿命分布) - None:绘制各类型刀具寿命分布箱线图types df[type_col].unique()data [df[df[type_col] t][used_col].values for t in types]fig, ax plt.subplots(figsize(10, 6))bp ax.boxplot(data, labelstypes, patch_artistTrue)# 着色for patch, t in zip(bp[boxes], types):patch.set_facecolor(self.TYPE_COLORS.get(t, #BDC3C7))patch.set_alpha(0.7)ax.set_ylabel(累计加工时间 (h), fontsize12)ax.set_title(title, fontsize14, fontweightbold)ax.tick_params(axisx, rotation30)plt.tight_layout()plt.savefig(self.results_dir / life_boxplot.png,dpi150, bbox_inchestight)plt.close()def plot_machine_heatmap(self,df: pd.DataFrame,machine_col: str machine,status_col: str status) - None:绘制各机床刀具状态热力图# 统计每台机床各状态数量cross df.groupby([machine_col, status_col]).size().unstack(fill_value0)if cross.empty:returnfig, ax plt.subplots(figsize(10, max(4, len(cross) * 0.5)))im ax.imshow(cross.values, cmapRdYlGn_r, aspectauto)ax.set_xticks(range(len(cross.columns)))ax.set_xticklabels(cross.columns)ax.set_yticks(range(len(cross.index)))ax.set_yticklabels(cross.index)ax.set_xlabel(状态, fontsize12)ax.set_ylabel(机床, fontsize12)ax.set_title(各机床刀具状态分布, fontsize14, fontweightbold)plt.colorbar(im, axax, label刀具数量)plt.tight_layout()plt.savefig(self.results_dir / machine_tool_heatmap.png,dpi150, bbox_inchestight)plt.close()/detailsdetailssummary/summary合成数据生成器import numpy as npimport pandas as pdfrom pathlib import Pathfrom typing import Optionalclass SyntheticDataGenerator:数控刀具合成数据生成器模拟多类型、多机床的刀具寿命数据。def __init__(self, rng: Optional[np.random.RandomState] None):self.rng rng or np.random.RandomState(42)# 刀具类型配置(名称, 标准寿命均值 h, 标准寿命标准差, 磨损速率均值)self.tool_types [(立铣刀, 6.0, 1.5, 1.0),(面铣刀, 12.0, 2.0, 0.8),(钻头, 4.0, 1.0, 1.2),(丝锥, 3.0, 0.8, 1.5),(镗刀, 20.0, 3.0, 0.5),(球头铣刀, 5.0, 1.2, 1.1),]self.machines [fCNC-{i:02d} for i in range(1, 13)] # 12 台机床def generate(self,n_tools: int 300,output_path: str tool_life.csv) - pd.DataFrame:生成合成刀具寿命数据Parameters----------n_tools : int刀具数量output_path : str输出路径Returns-------pd.DataFramerecords []for i in range(n_tools):# 随机选择刀具类型type_name, std_mean, std_std, wear_rate self.tool_types[self.rng.randint(0, len(self.tool_types))]# 标准寿命正态分布std_life max(1.0, self.rng.normal(std_mean, std_std))# 已用时间取决于磨损速率和随机波动# 模拟部分刀具已超期、部分接近寿命、部分正常usage_factor self.rn利用AI解决实际问题如果你觉得这个工具好用欢迎关注长安牧笛