周五白班,精加工工位巡检。
“这批轴套,前50件还亮得像镜子,到第80件开始发雾,第120件三坐标一量,Ra从0.8飘到1.6了,”数控操作工老肖拿指甲刮了刮工件表面,“以前换刀按件数定,比如150件换,可实际跟材料批次、切深、转速都有关,刀尖磨钝是慢慢爬的,粗糙度也是跟着爬的,等发现就超差了。”
我接上导出的刀补日志、工序参数、每件的表面粗糙度抽检表。
“这里面有啥?”我问。
“粗加工刀轨、精加工进给F、主轴转速S、每件的Ra实测、刀具半径补偿值都有,”老肖说,“但系统只给单件结果,不模拟‘刀尖后刀面磨损VB怎么随件数爬’‘磨损怎么映射到残留高度’‘Ra怎么逐步劣化’。想换种材料,又得跑一炉看趋势。”
“最亏的是中段,”老肖补一句,“50到100件之间,Ra从0.8爬到1.2,还没超差,质检按批检就漏了,到120件直接报废几件。排产还按150件换刀,等于拿超差换产能。”
“我就想干一件事,”老肖说,“给粗精工序参数+刀具初始状态,模拟VB磨损逐件累积,反推残留高度,算出每件Ra曲线,标出从哪件开始进临界区、哪件必须换刀,像个小刀具寿命仿真器,不用跑一炉。”
“数控不是看程序跑没跑,”我接话,“是看‘刀尖每走一件磨掉多少、残留高度怎么涨、Ra曲线怎么斜着爬’。用 numpy 做磨损递推,pandas 管工序件表,scipy 算残留高度与Ra映射,matplotlib 画磨损-Ra双轴曲线+刀纹云图,networkx 建工序-刀具-粗糙度关联,sklearn 做超差预警分类。”
“对,”老肖点头,“要能说清‘第1件VB=0.02mm Ra0.78,第82件VB=0.12mm Ra1.21进临界,第118件VB=0.18mm Ra1.62超差,建议第110件换刀;主因是精加工进给+VB耦合’。”
“OOP 封好,”我开工程,“工序加载器、刀具磨损模型、残留高度映射、粗糙度递推器、超差分类器、可视化器,合成多批次多材料数据,下载就能跑。”
敲了行原型:
# 目标: 粗精工序 → 刀具VB磨损递推 → 残留高度 → Ra逐件劣化
# 方法: 磨损累积模型 + 几何残留高度 + RF超差分类 + 关联图
老肖凑近看:“那以后看报告:VB磨损曲线+Ra双轴,残留高度随件数变化,刀纹热力图,工序-刀具-粗糙度网络,超差预测散点。新批次上机前先跑,黄线就是换刀点。”
“对,”我接话,“数控仿真不是‘画个刀轨’,是‘提前看见刀尖哪件开始钝、表面哪件开始毛’。数字孪生里挂这个刀具看板,就是操作工的‘换刀闹钟’。”
一、实际应用场景(真实痛点)
场景设定:数控车/铣精加工轴套类零件,粗加工去量、精加工保表面质量。刀具后刀面磨损 VB 随加工件数逐步累积,残留高度随之增大,Ra 从 0.8μm 逐步爬到超差。现场按固定件数换刀,未耦合磨损-粗糙度动态关系,中段劣化被批检漏掉。
现场原话(叙事化):
“不是程序编错了,”老肖说,“是刀尖钝得悄无声息。前50件手摸都滑,到80件看着就发雾,三坐标一量Ra1.3,批检还按10件抽1件,正好抽到的都还行。”
“最亏的是换刀策略,”老肖说,“定死150件换,可这批料硬点,110件就超了,后面8件全废。等于用超差换那40件的产能。”
核心矛盾:“固定件数换刀+单件Ra抽检” 与 “VB磨损递推+残留高度映射+Ra逐件曲线+超差预警+关联图” 之间的断层。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》课程模块 本篇痛点对应
数控加工与CAD/CAM技术:粗精加工、刀具磨损、切削参数、表面粗糙度、残留高度 VB递推+Ra劣化+换刀点
先进制造技术基础:几何精度、表面质量评价、材料去除机理 残留高度→Ra映射
FMS与先进生产管理:刀具寿命管理、换刀策略、在制品质量追踪 动态换刀点+质量追踪
智能制造与数字孪生:刀具状态数字映射、粗糙度看板 磨损/ Ra挂孪生
先进制造新模式:工艺知识库(材料-参数-刀具寿命库) 磨损模型复用
一句话总结:我们需要一个“粗精工序参数→刀具VB磨损递推→残留高度→Ra逐件曲线→超差预警+关联图”程序,实现从“固定件数换刀”到“按磨损状态动态换刀”的闭环。
三、核心逻辑讲解(大白话)
3.1 问题本质:把刀尖想成“慢慢变圆的铅笔头”
把精加工刀尖想成削铅笔的刀,刚磨完是尖的,削出来的面光;用久了变圆,削出来的面就有一道道棱:
* VB磨损 = 刀尖后刀面磨掉的宽度,单位mm,从0.02慢慢爬到0.2
* 残留高度 = 刀尖是圆弧,走刀后工件表面留下的微小波峰,刀尖越钝波峰越高
* Ra = 这些波峰波谷平均出来的粗糙度值
* 粗加工 = 先挖掉大部分料,不管光洁度
* 精加工 = 吃刀浅、走刀密,靠刀尖圆弧保光洁度
* 固定换刀 = 数件数,不管刀钝没钝
* 动态仿真 = 每加工一件,VB加一点,残留高度算一次,Ra跟一次,画成曲线
3.2 业务逻辑 → 代码映射
输入工序参数+刀具初始状态
│
▼ ProcessParamLoader (pandas)
读取表:
件号, 工序(粗/精), 主轴转速S, 进给F, 切深ap,
刀尖圆弧半径re, 材料硬度, 初始VB
│
▼ ToolWearModel (numpy)
VB磨损递推:
ΔVB_i = k * (ap*F/S) * 材料系数 * (1+已磨损放大项)
VB_i = VB_{i-1} + ΔVB_i
粗加工磨损权重高, 精加工慢但敏感
│
▼ ResidualHeightModel (scipy + numpy)
残留高度:
h = f(F, re, VB) 刀尖变钝→等效re增大
理论: h ≈ F²/(8*(re+VB))
│
▼ RoughnessPropagator (numpy + scipy)
Ra递推:
Ra ≈ C * h^0.8 + 刀具振动项
逐件输出Ra序列
分级: 优(Ra≤0.8) / 良(≤1.6) / 临界(1.2~1.6) / 超差(>1.6)
│
▼ OverLimitClassifier (sklearn)
超差预警:
特征: VB, Ra, 件号, 材料硬度, F, ap
标签: 安全/临界/超差
RF三分类 + 5折宏F1
预测“还有几件到超差”
│
▼ CncVisualizer (matplotlib + networkx)
可视化:
1. VB磨损+Ra双Y轴曲线(标换刀线)
2. 残留高度随件数曲线
3. 刀纹表面热力图(模拟不同VB下刀纹)
4. 工序-刀具-粗糙度关联网络
5. 超差预测vs实际散点
6. 单件Ra分布箱线(分段)
│
▼ SyntheticCncBatch (numpy)
合成数据:
多批次×多材料, 机制: 硬料VB爬得快, F大残留高, VB耦合放大
3.3 为什么不能“固定件数换刀”
视角 问题
按件数换刀 漏掉材料硬度差异
单件Ra抽检 中段劣化被平均掉
看程序参数 看不到刀尖变圆
VB递推 每件磨多少都算
残留高度 刀钝→波峰涨可量化
Ra曲线 斜爬过程全看见
RF预警 提前报“还有N件超差”
3.4 分析前后对比
维度 传统方式 本程序
换刀依据 150件固定 VB=0.18动态换刀(第110件)
劣化发现 超差后返工 第82件进临界即预警
中段质量 批检漏掉 逐件Ra曲线可见
主因分析 凭经验 VB+进给耦合量化
知识沉淀 老师傅记忆 材料-参数-寿命库
四、OOP 代码实现
4.1 项目结构
cnc_wear_roughness/
├── cnc_wear_roughness/
│ ├── __init__.py
│ ├── process_param_loader.py # 工序参数加载
│ ├── tool_wear_model.py # VB磨损递推(numpy)
│ ├── residual_height_model.py # 残留高度(scipy)
│ ├── roughness_propagator.py # Ra递推
│ ├── over_limit_classifier.py # 超差分类(sklearn)
│ ├── cnc_visualizer.py # 可视化
│ └── synthetic_cnc_batch.py # 合成批次
├── tests/
│ ├── __init__.py
│ └── test_cnc.py
├── results/
│ ├── wear_ra_curve.png
│ ├── residual_height_curve.png
│ ├── tool_mark_heatmap.png
│ ├── process_tool_network.png
│ ├── overrun_pred_scatter.png
│ ├── ra_boxplot_stage.png
│ ├── cnc_detail.csv
│ └── cnc_report.txt
└── run_cnc.py
4.2 核心源码
<details>
<summary></summary>
"""数控工序参数加载器。"""
import pandas as pd
from pathlib import Path
class ProcessParamLoader:
"""加载粗精工序参数与刀具初始状态。"""
def __init__(self, filepath: str = "cnc_process.csv",
encoding: str = "utf-8"):
self.filepath = Path(filepath)
self.encoding = encoding
def load(self) -> pd.DataFrame:
if not self.filepath.exists():
raise FileNotFoundError(self.filepath)
df = pd.read_csv(self.filepath, encoding=self.encoding)
req = ["part_no", "op", "spindle_s", "feed_f", "ap",
"tool_re", "material_hb", "vb_init"]
miss = [c for c in req if c not in df.columns]
if miss:
raise ValueError(f"缺列: {miss}")
for c in req[1:]:
df[c] = pd.to_numeric(df[c], errors="coerce")
return df.dropna(subset=req).reset_index(drop=True)
def summary(self, df: pd.DataFrame) -> str:
s = f"计划件数: {len(df)}\n"
s += f"精加工转速S: {df[df.op=='finish']['spindle_s'].iloc[0]}rpm\n"
s += f"精加工进给F: {df[df.op=='finish']['feed_f'].iloc[0]}mm/r\n"
s += f"刀尖圆弧re: {df['tool_re'].iloc[0]}mm\n"
s += f"材料硬度: {df['material_hb'].iloc[0]}HB\n"
s += f"初始VB: {df['vb_init'].iloc[0]}mm"
return s.rstrip()
</details>
<details>
<summary></summary>
"""刀具后刀面磨损VB递推 (numpy)。"""
import numpy as np
from dataclasses import dataclass
@dataclass
class WearResult:
vb: np.ndarray
dvb: np.ndarray
class ToolWearModel:
"""
VB累积模型(教学级):
ΔVB = k * (ap*F/S) * 材料系数 * (1+0.5*VB/VB_ref)
粗加工权重1.0, 精加工权重0.35(吃刀浅但敏感)
已磨损刀具摩擦加剧, 磨损加速(自放大)
"""
def __init__(self, k: float = 1.2e-4, vb_ref: float = 0.2):
self.k = k
self.vb_ref = vb_ref
def simulate(self, n_parts: int, op: np.ndarray,
s: float, f: float, ap: np.ndarray,
material_hb: float, vb_init: float) -> WearResult:
vb = np.zeros(n_parts)
dvb = np.zeros(n_parts)
mat_coef = 0.6 + material_hb / 200.0 # 硬料磨损快
for i in range(n_parts):
w = 1.0 if op[i] == "rough" else 0.35
base = self.k * (ap[i] * f / max(s, 1)) * mat_coef * w
amp = 1.0 + 0.5 * (vb[i-1] / self.vb_ref if i > 0 else 0.0)
d = base * amp
if i == 0:
vb[i] = vb_init + d
else:
vb[i] = vb[i-1] + d
dvb[i] = d
return WearResult(vb, dvb)
</details>
<details>
<summary></summary>
"""残留高度模型 (scipy + numpy)。"""
import numpy as np
from dataclasses import dataclass
@dataclass
class ResidualResult:
h_res: np.ndarray
class ResidualHeightModel:
"""
刀尖圆弧+磨损等效放大:
re_eff = re + VB (刀钝等效圆弧变大)
h = F^2 / (8 * re_eff) (几何残留高度)
"""
def __init__(self):
pass
def compute(self, f: float, re: float,
vb: np.ndarray) -> ResidualResult:
re_eff = re + vb
h = (f ** 2) / (8.0 * re_eff)
return ResidualResult(h)
</details>
<details>
<summary></summary>
"""Ra粗糙度递推 (numpy + scipy)。"""
import numpy as np
from dataclasses import dataclass
from scipy.ndimage import gaussian_filter1d
@dataclass
class RoughnessResult:
ra: np.ndarray
grade: np.ndarray
class RoughnessPropagator:
"""
Ra ≈ C * h^0.8 + 微振动项
分级:
excellent: Ra<=0.8
good: <=1.6
critical: 1.2~1.6
overrun: >1.6
"""
def __init__(self, c: float = 0.9, vib: float = 0.05):
self.c = c
self.vib = vib
def propagate(self, h_res: np.ndarray,
n_parts: int) -> RoughnessResult:
ra = self.c * (h_res ** 0.8) * 1000.0 + self.vib * np.arange(n_parts)/n_parts
ra = gaussian_filter1d(ra, sigma=1.0) # 平滑工艺噪声
grade = np.array([self._grade(r) for r in ra])
return RoughnessResult(ra, grade)
def _grade(self, r: float) -> str:
if r <= 0.8:
return "excellent"
if r <= 1.2:
return "good"
if r <= 1.6:
return "critical"
return "overrun"
</details>
<details>
<summary></summary>
"""超差预警分类 (sklearn RF)。"""
import numpy as np
import pandas as pd
from typing import Dict
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score, KFold
class OverLimitClassifier:
"""安全/临界/超差 三分类 + 剩余件数预测。"""
def __init__(self, random_state: int = 42):
self.random_state = random_state
self.model_ = None
self.feat = ["vb", "ra", "part_idx", "material_hb", "feed_f", "ap"]
def fit(self, df: pd.DataFrame, y: np.ndarray):
self.model_ = RandomForestClassifier(
n_estimators=300, max_depth=6, min_samples_leaf=2,
random_state=self.random_state, n_jobs=-1)
self.model_.fit(df[self.feat].values, y)
return self
def feature_importance(self) -> Dict:
imp = dict(zip(self.feat, self.model_.feature_importances_))
return dict(sorted(imp.items(), key=lambda x: x[1], reverse=True))
def cross_validate(self, df: pd.DataFrame, y: np.ndarray) -> Dict:
kf = KFold(n_splits=5, shuffle=True, random_state=self.random_state)
sc = cross_val_score(self.model_, df[self.feat].values, y,
cv=kf, scoring="f1_macro")
return {"f1_macro_mean": float(sc.mean()), "f1_macro_std": float(sc.std())}
def predict(self, df: pd.DataFrame) -> np.ndarray:
return self.model_.predict(df[self.feat].values)
</details>
<details>
<summary></summary>
"""数控可视化 (matplotlib + networkx)。"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pathlib import Path
import networkx as nx
plt.rcParams["font.sans-serif"] = ["SimHei", "WenQuanYi Micro Hei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False
GRADE_COLOR = {"excellent":"#27AE60","good":"#2ECC71",
"critical":"#F39C12","overrun":"#E74C3C"}
class CncVisualizer:
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def wear_ra_curve(self, idx, vb, ra, change_point, overrun_point):
fig, ax1 = plt.subplots(figsize=(12, 6))
ax1.plot(idx, vb*1000, color="#2C3E50", lw=1.8, label="VB磨损(μm)")
ax1.set_xlabel("加工件数", fontsize=12)
ax1.set_ylabel("VB磨损 (μm)", color="#2C3E50", fontsize=12)
ax2 = ax1.twinx()
for i, g in enumerate(["excellent","good","critical","overrun"]):
m = ra == g
if m.any():
ax2.scatter(idx[m], ra[m], c=GRADE_COLOR[g], s=18,
label=f"Ra-{g}", zorder=5)
ax2.set_ylabel("Ra (μm)", color="#E67E22", fontsize=12)
ax1.axvline(change_point, color="#F39C12", ls="--", lw=2,
label=f"临界点#{change_point}")
ax1.axvline(overrun_point, color="#E74C3C", ls="--", lw=2,
label=f"超差点#{overrun_point}")
ax1.set_title("刀具VB磨损 + Ra粗糙度双轴曲线",
fontsize=13, fontweight="bold")
ax1.grid(alpha=0.25)
lines1, labels1 = ax1.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(lines1+lines2, labels1+labels2, fontsize=8,
loc="upper left", ncol=2)
plt.tight_layout()
plt.savefig(self.results_dir/"wear_ra_curve.png",
dpi=150, bbox_inches="tight")
plt.close()
def residual_curve(self, idx, h_res):
fig, ax = plt.subplots(figsize=(11, 5))
ax.plot(idx, h_res*1000, color="#8E44AD", lw=1.8)
ax.fill_between(idx, 0, h_res*1000, color="#8E44AD", alpha=0.1)
ax.set_xlabel("加工件数", fontsize=12)
ax.set_ylabel("残留高度 (μm)", fontsize=12)
ax.set_title("残留高度随件数变化(刀钝→波峰涨)",
fontsize=13, fontweight="bold")
ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"residual_height_curve.png",
dpi=150, bbox_inches="tight")
plt.close()
def tool_mark_heatmap(self, vb_end, re, f, n_grid=80):
fig, ax = plt.subplots(figsize=(9, 5))
# 模拟刀纹: 横向走刀, 纵向波峰随VB
x = np.linspace(0, 5, n_grid)
y = np.linspace(0, 1, 20)
X, Y = np.meshgrid(x, y)
re_eff = re + vb_end
wave = (f**2)/(8*re_eff)*1000 * np.sin(2*np.pi*X/0.05)**2
im = ax.imshow(wave, aspect="auto", cmap="copper",
extent=[0,5,0,1], origin="lower")
ax.set_xlabel("工件周向展开 (mm)", fontsize=12)
ax.set_ylabel("轴向 (mm)", fontsize=12)
ax.set_title(f"刀纹表面形貌(VB={vb_end*1000:.0f}μm时)",
fontsize=13, fontweight="bold")
plt.colorbar(im, ax=ax, label="高度(μm)")
plt.tight_layout()
plt.savefig(self.results_dir/"tool_mark_heatmap.png",
dpi=150, bbox_inches="tight")
plt.close()
def process_network(self, imp: Dict):
fig, ax = plt.subplots(figsize=(11, 8))
G = nx.DiGraph()
G.add_node("Ra超差", kind="target")
for n in ["VB磨损","残留高度","进给F","切深ap","材料硬度","件号"]:
G.add_node(n, kind="factor")
key_map = {"vb":"VB磨损","ra":"Ra超差","feed_f":"进给F","ap":"切深ap",
"material_hb":"材料硬度","part_idx":"件号",
"h_res":"残留高度"}
for k,v in imp.items():
if k in ("vb","ra"):
continue
G.add_edge(key_map.get(k,k), "Ra超差", weight=v)
G.add_edge("VB磨损","残留高度", weight=0.9)
G.add_edge("残留高度","Ra超差", weight=0.9)
G.add_edge("进给F","残留高度", weight=0.5)
pos = nx.spring_layout(G, seed=42)
cmap = ["#E74C3C" if n=="Ra超差" else "#3498DB" for n in G.nodes()]
nx.draw_networkx_nodes(G,pos,node_color=cmap,node_size=3200,
alpha=0.9,ax=ax)
nx.draw_networkx_edges(G,pos,arrowstyle="-|>",arrowsize=20,
edge_color="#555",width=2,ax=ax)
nx.draw_networkx_labels(G,pos,font_size=11,ax=ax,
font_color="white",font_weight="bold")
ax.set_title("工序-刀具-粗糙度关联网络", fontsize=14, fontweight="bold")
ax.axis("off")
plt.tight_layout()
plt.savefig(self.results_dir/"process_tool_network.png",
dpi=150, bbox_inches="tight")
plt.close()
def pred_scatter(self, y_true, y_pred):
fig, ax = plt.subplots(figsize=(8, 8))
labels = ["安全","临界","超差"]
ct = np.array([labels.index(y) for y in y_true])
cp = np.array([labels.index(y) for y in y_pred])
ax.scatter(ct, cp, c="#27AE60", s=60, edgecolors="k", alpha=0.8)
ax.plot([-0.5,2.5],[-0.5,2.5],"r--",lw=2,label="理想")
ax.set_xticks([0,1,2]); ax.set_xticklabels(labels)
ax.set_yticks([0,1,2]); ax.set_yticklabels(labels)
ax.set_xlabel("实际状态", fontsize=12)
ax.set_ylabel("预测状态", fontsize=12)
ax.set_title("超差预警 预测vs实际", fontsize=13, fontweight="bold")
ax.legend(fontsize=10); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"overrun_pred_scatter.png",
dpi=150, bbox_inches="tight")
plt.close()
def ra_boxplot(self, idx, ra, stages):
fig, ax = plt.subplots(figsize=(10, 6))
data = [ra[(idx>=s[0])&(idx<=s[1])] for s in stages]
labels = [f"{s[0]}-{s[1]}件" for s in stages]
bp = ax.boxplot(data, labels=labels, patch_artist=True)
for patch, g in zip(bp["boxes"], ["excellent","good","critical","overrun"]):
patch.set_facecolor(GRADE_COLOR[g]); patch.set_alpha(0.7)
ax.set_ylabel("Ra (μm)", fontsize=12)
ax.set_xlabel("加工阶段", fontsize=12)
ax.set_title("分段Ra分布箱线图", fontsize=13, fontweight="bold")
ax.grid(alpha=0.25)
plt.tight_layout()
plt.savefig(self.results_dir/"ra_boxplot_stage.png",
dpi=150, bbox_inches="tight")
plt.close()
</details>
<details>
<summary></summary>
"""合成数控加工批次数据。"""
import numpy as np
import pandas as pd
from pathlib import Path
from typing import Optional
class SyntheticCncBatch:
"""
多批次×多材料
机制:
硬料VB爬得快
精加工F大→残留高
VB自放大
"""
def __init__(self, rng: Optional[np.random.RandomState] = None):
self.rng = rng or np.random.RandomState(42)
def generate(self, out_path: str = "cnc_process.csv",
n_parts: int = 130,
material_hb: float = 180.0,
feed_f: float = 0.08,
spindle_s: float = 3000.0,
tool_re: float = 0.4,
vb_init: float = 0.02) -> pd.DataFrame:
rows = []
for i in range(n_parts):
# 前5件粗加工, 其余精加工
op = "rough" if i < 5 else "finish"
ap = 0.8 if op == "rough" else 0.1
rows.append({
"part_no": f"P{i+1:03d}",
"op": op,
"spindle_s": spindle_s,
"feed_f": feed_f,
"ap": ap,
"tool_re": tool_re,
"material_hb": material_hb,
"vb_init": vb_init,
})
df = pd.DataFrame(rows)
Path(out_path).parent.mkdir(parents=True, exist_ok=True)
df.to_csv(out_path, index=False, encoding="utf-8")
return df
</details>
<details>
<summary></summary>
"""
数控粗精工序仿真: 刀具磨损累积 → 残留高度 → Ra逐步劣化
================================================================================
课程映射(滨州职业学院《先进制造技术》):
数控加工与CAD/CAM:粗精加工/刀具磨损/残留高度/表面粗糙度
先进制造技术基础:几何精度/表面质量评价
FMS与先进生产管理:刀具寿命管理/动态换刀
智能制造与数字孪生:刀具状态看板
先进制造新模式:材料-参数-寿命知识库
技术栈(严格):
pandas / numpy # 工序表/磨损递推
scipy # 平滑/几何处理
scikit-learn # RF超差分类
matplotlib / networkx# 双轴曲线+关联图
"""
import sys, os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import numpy as np
import pandas as pd
from pathlib import Path
from cnc_wear_roughness.process_param_loader import ProcessParamLoader
from cnc_wear_roughness.tool_wear_model import ToolWearModel
from cnc_wear_roughness.residual_height_model import ResidualHeightModel
from cnc_wear_roughness.roughness_propagator import RoughnessPropagator
from cnc_wear_roughness.over_limit_classifier import OverLimitClassifier
from cnc_wear_roughness.cnc_visualizer import CncVisualizer
from cnc_wear_roughness.synthetic_cnc_batch import SyntheticCncBatch
def main():
print("=" * 70)
print("数控粗精工序仿真: 刀具磨损→残留高度→Ra逐步劣化")
print("=" * 70)
results_dir = Path("results"); results_dir.mkdir(exist_ok=True)
# 1. 合成
print("\n[1/8] 生成工序批次...")
gen = SyntheticCncBatch(rng=np.random.RandomState(42))
df = gen.generate("cnc_process.csv", n_parts=130,
material_hb=185.0, feed_f=0.08,
spindle_s=3000.0, tool_re=0.4, vb_init=0.02)
print(f" 130件, 前5件粗加工, 材料185HB, 精加工F=0.08mm/r")
# 2. 加载
print("\n[2/8] 加载工序参数...")
df = ProcessParamLoader("cnc_process.csv").load()
print(ProcessParamLoader().summary(df))
n = len(df)
op = df["op"].values
s = df["spindle_s"].iloc[0]
f = df["feed_f"].iloc[0]
ap = df["ap"].values
re = df["tool_re"].iloc[0]
hb = df["material_hb"].iloc[0]
vb0 = df["vb_init"].iloc[0]
idx = np.arange(1, n+1)
# 3. VB磨损递推
print("\n[3/8] 刀具VB磨损递推...")
wear = ToolWearModel(k=1.2e-4, vb_ref=0.2).simulate(
n, op, s, f, ap, hb, vb0)
print(f" 首件VB={wear.vb[0]*1000:.1f}μm, "
f"末件VB={wear.vb[-1]*1000:.1f}μm")
# 4. 残留高度
print("\n[4/8] 残留高度计算...")
res = ResidualHeightModel().compute(f, re, wear.vb)
print(f" 首件残留高度={res.h_res[0]*1000:.3f}μm, "
f"末件={res.h_res[-1]*1000:.3f}μm")
# 5. Ra递推
print("\n[5/8] Ra粗糙度递推...")
rp = RoughnessPropagator(c=0.9, vib=0.05)
rr = rp.propagate(res.h_res, n)
ra = rr.ra
# 定位关键点
critical_idx = int(np.argmax(ra > 1.2)) + 1
overrun_idx = int(np.argmax(ra > 1.6)) + 1
# 建议换刀
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!