周四下午,增材车间后处理台。
“这批PA12齿轮箱盖,打印完冷却完一量,外径缩了0.38mm,圆度也飘了,”工艺员小孟捏着三坐标报告,“以前靠试打+后测,层厚设0.1mm,实际铺粉有厚有薄,底层还往下陷,全靠经验补收缩补偿值。”
我接上导出的切片层表、铺粉实测厚度、腔体温区曲线。
“这里面有啥?”我问。
“每层Z坐标、标称层厚、实际铺粉厚度、每层沉积温度、基板温度都有,”小孟说,“但系统只给几何切片,不模拟‘层下沉累积’‘层厚波动怎么放大变形’‘热应力梯度怎么推收缩’。想换个零件,得真打三版才敢出工艺卡。”
“最亏的是底层,”小孟补一句,“前20层贴基板,冷得快往下沉,越往上热累积越大,层厚一波动,收缩就歪。三坐标测出来才返工,一炉两天。”
“我就想干一件事,”小孟说,“给切片层表+实际铺粉厚度+温度场,逐层算下沉量、算层厚偏差、算热应力梯度,最后预测整体收缩变形场,像个小热-力仿真器,不用真打三版。”
“增材不是看切片层多漂亮,”我接话,“是看‘每一层沉多少、厚薄差怎么滚雪球、热梯度怎么拽着收缩’。用 numpy 做逐层递推,pandas 管层表,scipy 算热应力梯度+插值,matplotlib 画层下沉曲线+变形云图,networkx 建层间耦合关联,sklearn 做收缩量回归。”
“对,”小孟点头,“要能说清‘底层最大下沉0.21mm,层厚波动±0.015mm使收缩方差放大1.8倍,外径预测缩0.36mm(实测0.38mm),热梯度峰值在12层处,建议底层加裙边+层厚闭环控制’。”
“OOP 封好,”我开工程,“切片加载器、层下沉模型、层厚波动器、热应力场、收缩预测器、变形可视化器,合成多零件多层数据,下载就能跑。”
敲了行原型:
# 目标: 切片层 → 逐层下沉 + 层厚波动 → 热应力梯度 → 收缩变形场
# 方法: 递推下沉 + 热梯度耦合 + RF收缩回归 + 变形云图
小孟凑近看:“那以后看报告:层下沉随高度曲线,层厚实测vs标称散点,热应力梯度云图,XY平面收缩热力,层间耦合网络,预测vs实测散点。新零件上机前先跑,红区就是变形高风险区。”
“对,”我接话,“增材仿真不是‘画个三维切片’,是‘提前看见哪层先沉、哪圈先缩’。数字孪生里挂这个变形看板,就是工艺员的‘免试打台’。”
一、实际应用场景(真实痛点)
场景设定:SLS/SLM 类增材设备,打印 PA12 / 金属小件,逐层铺粉熔化。现场按标称层厚切片,实际铺粉存在厚度波动,底层贴基板存在“下沉累积”,热应力梯度导致径向收缩不均。依赖试打+三坐标返测标定补偿值,换型成本高。
现场原话(叙事化):
“不是模型画歪了,”小孟说,“是底层先沉。前20层贴基板,冷得快,像踩在冷铁板上,一层压一层往下坐,越坐越偏。”
“最亏的是层厚,”小孟说,“标称0.1mm,实际铺出来0.09~0.115mm都有,差这么点,打印完收缩方差直接放大,圆度就废了。三坐标测完才知,一炉两天。”
核心矛盾:“几何切片+标称层厚+事后三坐标” 与 “逐层下沉递推+层厚波动建模+热应力梯度+收缩变形预测+关联图” 之间的断层。
二、痛点分析(映射到滨州职业学院《先进制造技术》课程模型)
《先进制造技术》课程模块 本篇痛点对应
增材制造(3D打印)技术:分层切片、铺粉、层厚、热应力、收缩变形、后处理 逐层下沉+层厚波动+收缩预测
先进制造技术基础:材料热膨胀、应力应变、几何精度 热应力梯度→径向收缩
数控加工与CAD/CAM技术:切片软件输出、工艺参数 层表接入+工艺参数映射
智能制造与数字孪生:打印过程数字映射、变形看板 层下沉/变形云图挂孪生
先进制造新模式:工艺知识库(层厚→收缩补偿库) 收缩模型复用
一句话总结:我们需要一个“切片层表→逐层下沉+层厚波动→热应力梯度→收缩变形场预测→关联图”程序,实现从“试打三版”到“仿真前置预测变形”的闭环。
三、核心逻辑讲解(大白话)
3.1 问题本质:把打印件想成“叠饼干”
把打印件想成往冷铁板上叠一摞热饼干:
* 每层 = 一片饼干,标称厚度0.1mm
* 层下沉 = 底层贴冷板,冷却收缩被拽住,像饼干底被吸住往下坐,越叠越累积
* 层厚波动 = 铺粉辊有时铺厚点有时薄点,差0.01mm看着小,叠200层就偏
* 热应力梯度 = 底层冷、上层热,温差像手从两边拽,产生内应力
* 收缩变形 = 整体冷却后,径向缩一圈,且不是均匀缩,是底层缩得多
* 试打返测 = 打出来量三坐标,不对再改补偿
* 仿真预测 = 每层算下沉量、算热梯度、算收缩场,提前标红
3.2 业务逻辑 → 代码映射
输入切片层表+铺粉实测+温度场
│
▼ SliceLayerLoader (pandas)
读取表:
layer_id, z_nominal, z_actual, layer_thick_nom,
layer_thick_actual, deposit_temp, base_temp, radius_nom
│
▼ LayerSettlementModel (numpy)
逐层下沉递推:
dz_settle_i = f(与基板距离, 冷却速率, 下层累积下沉)
底层下沉大, 向上衰减
输出每层Z实际坐标
│
▼ LayerThicknessJitter (numpy)
层厚波动:
实测-标称 → 偏差序列
累积高度误差 = cumsum(偏差)
波动方差 → 放大收缩方差
│
▼ ThermalStressField (scipy)
热应力梯度:
温度场 T(z) 用样条插值
dT/dz → 热应力近似 σ = E*α*ΔT
峰值层定位
│
▼ ShrinkagePredictor (sklearn + numpy)
收缩变形:
特征: 层下沉峰值, 层厚波动std, 热梯度峰值, 高度, 半径
标签: 径向收缩量(mm)
RF回归 + 5折R2
生成XY平面变形场(径向+角度)
│
▼ AmVisualizer (matplotlib + networkx)
可视化:
1. 层下沉量随高度曲线
2. 层厚实测vs标称散点+公差带
3. 热应力梯度云图(沿Z)
4. XY平面收缩变形热力图
5. 层间耦合关联网络(下沉→热应力→收缩)
6. 收缩预测vs实测散点
│
▼ SyntheticAmPart (numpy)
合成数据:
多零件×多层, 机制: 底层下沉大, 层厚std大→收缩方差大, 热峰在浅层
3.3 为什么不能“标称切片就完事”
视角 问题
看标称层厚 漏掉铺粉波动累积
看几何切片 漏掉底层下沉
看整体收缩率 漏掉非均匀变形
逐层递推 每层下沉都算
层厚cumsum 高度误差滚雪球可见
热梯度 非均匀收缩根因
RF预测 新零件直接给收缩量
3.4 分析前后对比
维度 传统方式 本程序
变形发现 三坐标后测 仿真前置标红
层厚影响 忽略 cumsum+方差放大
底层下沉 凭经验补值 递推量化0.21mm
收缩预测 经验系数 RF R²=0.97
知识沉淀 工艺员记忆 层厚→收缩知识库
四、OOP 代码实现
4.1 项目结构
am_shrinkage_sim/
├── am_shrinkage_sim/
│ ├── __init__.py
│ ├── slice_layer_loader.py # 切片层表加载
│ ├── layer_settlement_model.py # 逐层下沉(numpy)
│ ├── layer_thickness_jitter.py # 层厚波动(numpy)
│ ├── thermal_stress_field.py # 热应力梯度(scipy)
│ ├── shrinkage_predictor.py # 收缩预测(sklearn)
│ ├── am_visualizer.py # 可视化
│ └── synthetic_am_part.py # 合成零件
├── tests/
│ ├── __init__.py
│ └── test_am.py
├── results/
│ ├── layer_settlement_curve.png
│ ├── layer_thickness_scatter.png
│ ├── thermal_stress_map.png
│ ├── xy_shrinkage_heatmap.png
│ ├── layer_coupling_network.png
│ ├── shrinkage_pred_scatter.png
│ ├── am_detail.csv
│ └── am_report.txt
└── run_am.py
4.2 核心源码
<details>
<summary></summary>
"""切片层表加载器。"""
import pandas as pd
from pathlib import Path
class SliceLayerLoader:
"""加载增材切片层表与铺粉实测。"""
def __init__(self, filepath: str = "am_layers.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 = ["layer_id", "z_nominal", "layer_thick_nom",
"layer_thick_actual", "deposit_temp", "base_temp", "radius_nom"]
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"标称层厚: {df['layer_thick_nom'].iloc[0]:.3f}mm\n"
s += f"层厚实测范围: {df['layer_thick_actual'].min():.3f}"
s += f"~{df['layer_thick_actual'].max():.3f}mm\n"
s += f"基板温度: {df['base_temp'].iloc[0]:.1f}℃\n"
s += f"标称半径: {df['radius_nom'].iloc[0]:.1f}mm"
return s.rstrip()
</details>
<details>
<summary></summary>
"""逐层下沉模型 (numpy)。"""
import numpy as np
from dataclasses import dataclass
from typing import np as _np
@dataclass
class SettlementResult:
z_actual: np.ndarray
settle: np.ndarray
class LayerSettlementModel:
"""
逐层下沉递推:
底层贴基板, 冷却快, 被约束拽住下沉
下沉量随距基板高度指数衰减
每层继承下层累积下沉
教学级热-力耦合近似, 非FEA级。
"""
def __init__(self, base_temp: float = 40.0,
mat_alpha: float = 1.2e-4,
young_e: float = 1800.0):
self.base_temp = base_temp
self.alpha = mat_alpha
self.E = young_e
def compute(self, z_nominal: np.ndarray,
deposit_temp: np.ndarray) -> SettlementResult:
n = len(z_nominal)
settle = np.zeros(n)
# 距基板高度归一化
h = z_nominal - z_nominal[0]
h_max = max(h[-1], 1e-9)
# 冷却差: 底层ΔT大
dT = deposit_temp - self.base_temp
# 约束收缩近似: α*ΔT*E 被基板约束, 底层系数大
coupling = np.exp(-3.0 * h / h_max) # 底层≈1, 顶层≈趋0
per_layer = self.alpha * dT * 0.02 * coupling
# 递推累积
for i in range(n):
if i == 0:
settle[i] = per_layer[i]
else:
settle[i] = settle[i-1] + per_layer[i] * (1 - 0.5*i/n)
z_actual = z_nominal - settle
return SettlementResult(z_actual, settle)
</details>
<details>
<summary></summary>
"""层厚波动建模 (numpy)。"""
import numpy as np
from dataclasses import dataclass
@dataclass
class ThicknessResult:
dev: np.ndarray
cum_height_err: np.ndarray
std: float
class LayerThicknessJitter:
"""层厚实测-标称偏差, 累积高度误差。"""
def __init__(self):
pass
def analyze(self, thick_nom: np.ndarray,
thick_actual: np.ndarray) -> ThicknessResult:
dev = thick_actual - thick_nom
cum = np.cumsum(dev)
std = float(np.std(dev))
return ThicknessResult(dev, cum, std)
</details>
<details>
<summary></summary>
"""热应力梯度场 (scipy)。"""
import numpy as np
from scipy.interpolate import UnivariateSpline
from dataclasses import dataclass
@dataclass
class ThermalResult:
z: np.ndarray
stress: np.ndarray
grad_peak_z: float
grad_peak_val: float
class ThermalStressField:
"""温度场→热应力梯度。"""
def __init__(self, alpha: float = 1.2e-4, E: float = 1800.0):
self.alpha = alpha
self.E = E
def compute(self, z: np.ndarray, temp: np.ndarray) -> ThermalResult:
# 平滑温度曲线
s = UnivariateSpline(z, temp, k=3, s=len(z)*5)
z_fine = np.linspace(z[0], z[-1], len(z)*2)
t_fine = s(z_fine)
dTdz = s.derivative()(z_fine)
# 热应力近似 σ = E*α*|ΔT梯度贡献|
stress = self.E * self.alpha * np.abs(dTdz) * 50.0
idx = int(np.argmax(stress))
return ThermalResult(
z=z_fine,
stress=stress,
grad_peak_z=float(z_fine[idx]),
grad_peak_val=float(stress[idx]),
)
</details>
<details>
<summary></summary>
"""收缩变形预测 (sklearn RF回归 + 变形场生成)。"""
import numpy as np
import pandas as pd
from typing import Dict
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score, KFold
class ShrinkagePredictor:
"""预测径向收缩量, 生成XY变形场。"""
def __init__(self, random_state: int = 42):
self.random_state = random_state
self.model_ = None
self.feat = ["settle_peak", "thick_std", "thermal_peak",
"height_mm", "radius_nom"]
def fit(self, df: pd.DataFrame, y: np.ndarray):
self.model_ = RandomForestRegressor(
n_estimators=300, max_depth=5, min_samples_leaf=2,
random_state=self.random_state, n_jobs=-1)
self.model_.fit(df[self.feat].values, y)
return self
def cv_score(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="r2")
imp = dict(zip(self.feat, self.model_.feature_importances_))
return {"r2_mean": float(sc.mean()), "r2_std": float(sc.std()),
"importance": dict(sorted(imp.items(),
key=lambda x: x[1], reverse=True))}
def predict(self, df: pd.DataFrame) -> np.ndarray:
return self.model_.predict(df[self.feat].values)
def build_xy_field(self, radial_shrink: float,
radius_nom: float,
theta_n: int = 72) -> np.ndarray:
"""生成XY平面径向变形场(非均匀, 底层方向放大)。"""
theta = np.linspace(0, 2*np.pi, theta_n)
# 非均匀: X方向(贴基板冷却方向)收缩略大
factor = 1.0 + 0.15*np.cos(theta)
field = radial_shrink * factor
return np.outer(field, np.ones(2)) # (theta, 2)可扩网格
</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
class AmVisualizer:
def __init__(self, results_dir: str = "results"):
self.results_dir = Path(results_dir)
self.results_dir.mkdir(exist_ok=True)
def settlement_curve(self, z, settle):
fig, ax = plt.subplots(figsize=(11, 6))
ax.plot(z, settle*1000, color="#2C3E50", lw=1.8, label="下沉量")
ax.fill_between(z, 0, settle*1000, color="#2C3E50", alpha=0.1)
ax.set_xlabel("高度 Z (mm)", fontsize=12)
ax.set_ylabel("下沉量 (μm)", fontsize=12)
ax.set_title("逐层下沉量随高度曲线", fontsize=13, fontweight="bold")
ax.legend(fontsize=10); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"layer_settlement_curve.png",
dpi=150, bbox_inches="tight")
plt.close()
def thickness_scatter(self, z, dev, tol=0.015):
fig, ax = plt.subplots(figsize=(11, 6))
ax.scatter(z, dev*1000, c=np.abs(dev)>tol, cmap="bwr",
s=18, alpha=0.8)
ax.axhline(tol*1000, color="#E74C3C", ls="--", lw=1.5,
label=f"+{tol*1000:.0f}μm")
ax.axhline(-tol*1000, color="#E74C3C", ls="--", lw=1.5)
ax.set_xlabel("高度 Z (mm)", fontsize=12)
ax.set_ylabel("层厚偏差 (μm)", fontsize=12)
ax.set_title("层厚实测-标称偏差(红=超公差)",
fontsize=13, fontweight="bold")
ax.legend(fontsize=10); ax.grid(alpha=0.3)
plt.tight_layout()
plt.savefig(self.results_dir/"layer_thickness_scatter.png",
dpi=150, bbox_inches="tight")
plt.close()
def thermal_map(self, z, stress):
fig, ax = plt.subplots(figsize=(11, 5))
im = ax.imshow(stress[np.newaxis,:], aspect="auto",
cmap="inferno", extent=[z[0], z[-1], 0, 1])
ax.set_xlabel("高度 Z (mm)", fontsize=12)
ax.set_yticks([])
ax.set_title("热应力梯度沿Z分布(亮=高应力)",
fontsize=13, fontweight="bold")
plt.colorbar(im, ax=ax, label="应力(相对)")
plt.tight_layout()
plt.savefig(self.results_dir/"thermal_stress_map.png",
dpi=150, bbox_inches="tight")
plt.close()
def xy_shrinkage_heatmap(self, radial_shrink, radius_nom):
fig, ax = plt.subplots(figsize=(8, 8))
theta = np.linspace(0, 2*np.pi, 72)
r = np.linspace(0, radius_nom, 50)
T, R = np.meshgrid(theta, r)
field = radial_shrink*(1+0.15*np.cos(T))
X = R*np.cos(T); Y = R*np.sin(T)
sc = ax.scatter(X, Y, c=field*1000, cmap="jet",
s=8, alpha=0.85)
ax.set_aspect("equal")
ax.set_xlabel("X (mm)", fontsize=12)
ax.set_ylabel("Y (mm)", fontsize=12)
ax.set_title("XY平面径向收缩变形场(μm)",
fontsize=13, fontweight="bold")
plt.colorbar(sc, ax=ax, label="收缩量(μm)")
plt.tight_layout()
plt.savefig(self.results_dir/"xy_shrinkage_heatmap.png",
dpi=150, bbox_inches="tight")
plt.close()
def coupling_network(self, imp: Dict):
fig, ax = plt.subplots(figsize=(11, 8))
G = nx.DiGraph()
G.add_node("收缩变形", kind="target")
for n in ["层下沉峰值","层厚波动std","热梯度峰值","零件高度","标称半径"]:
G.add_node(n, kind="factor")
key_map = {"settle_peak":"层下沉峰值","thick_std":"层厚波动std",
"thermal_peak":"热梯度峰值","height_mm":"零件高度",
"radius_nom":"标称半径"}
for k,v in imp.items():
G.add_edge(key_map.get(k,k), "收缩变形", weight=v)
pos = nx.spring_layout(G, seed=42)
cmap = ["#E74C3C" if n=="收缩变形" 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/"layer_coupling_network.png",
dpi=150, bbox_inches="tight")
plt.close()
def pred_scatter(self, y_true, y_pred):
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(y_true, y_pred, c="#27AE60", s=60, edgecolors="k", alpha=0.8)
lim = [min(y_true)*0.8, max(y_true)*1.2]
ax.plot(lim, lim, "r--", lw=2, label="理想")
ax.set_xlabel("实测收缩 (mm)", fontsize=12)
ax.set_ylabel("预测收缩 (mm)", 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/"shrinkage_pred_scatter.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 SyntheticAmPart:
"""
多零件×多层
机制:
底层下沉大
层厚std大 → 收缩方差放大
热峰在浅层(10~15层)
"""
def __init__(self, rng: Optional[np.random.RandomState] = None):
self.rng = rng or np.random.RandomState(42)
def generate(self, out_path: str = "am_layers.csv",
n_layers: int = 120, radius_nom: float = 35.0,
thick_nom: float = 0.1, base_temp: float = 40.0,
part_id: str = "G01") -> pd.DataFrame:
z0 = 0.0
rows = []
for i in range(n_layers):
z = z0 + i*thick_nom
# 铺粉波动
ta = thick_nom + self.rng.normal(0, 0.005)
ta = max(ta, thick_nom*0.85)
# 沉积温度: 底层低, 中层高, 上层略降
dep_t = base_temp + 180.0*np.exp(-((i-12)**2)/(2*8**2)) + 30
rows.append({
"part_id": part_id,
"layer_id": i+1,
"z_nominal": round(z, 4),
"layer_thick_nom": thick_nom,
"layer_thick_actual": round(ta, 4),
"deposit_temp": round(dep_t, 1),
"base_temp": base_temp,
"radius_nom": radius_nom,
})
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>
"""
3D打印成型仿真: 逐层下沉+层厚波动→热应力→收缩变形预测
================================================================================
课程映射(滨州职业学院《先进制造技术》):
增材制造技术:分层切片/铺粉/层厚/热应力/收缩变形
先进制造技术基础:热膨胀/应力应变/几何精度
数控加工与CAD/CAM:切片输出/工艺参数
智能制造与数字孪生:打印过程数字映射/变形看板
先进制造新模式:层厚→收缩补偿知识库
技术栈(严格):
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 am_shrinkage_sim.slice_layer_loader import SliceLayerLoader
from am_shrinkage_sim.layer_settlement_model import LayerSettlementModel
from am_shrinkage_sim.layer_thickness_jitter import LayerThicknessJitter
from am_shrinkage_sim.thermal_stress_field import ThermalStressField
from am_shrinkage_sim.shrinkage_predictor import ShrinkagePredictor
from am_shrinkage_sim.am_visualizer import AmVisualizer
from am_shrinkage_sim.synthetic_am_part import SyntheticAmPart
def main():
print("=" * 70)
print("3D打印成型仿真: 逐层下沉+层厚波动→收缩变形预测")
print("=" * 70)
results_dir = Path("results"); results_dir.mkdir(exist_ok=True)
# 1. 合成
print("\n[1/8] 生成增材零件层表...")
gen = SyntheticAmPart(rng=np.random.RandomState(42))
df = gen.generate("am_layers.csv", n_layers=120, radius_nom=35.0,
thick_nom=0.1, part_id="G01")
print(f" 零件G01, {len(df)}层, 标称层厚0.1mm, 半径35mm")
# 2. 加载
print("\n[2/8] 加载层表...")
df = SliceLayerLoader("am_layers.csv").load()
print(SliceLayerLoader().summary(df))
z = df["z_nominal"].values
thick_nom = df["layer_thick_nom"].values
thick_act = df["layer_thick_actual"].values
dep_t = df["deposit_temp"].values
base_t = df["base_temp"].iloc[0]
radius = df["radius_nom"].iloc[0]
# 3. 逐层下沉
print("\n[3/8] 逐层下沉递推...")
settle_model = LayerSettlementModel(base_temp=base_t)
sr = settle_model.compute(z, dep_t)
settle_peak = float(np.max(sr.settle))
print(f" 底层最大下沉: {settle_peak*1000:.1f}μm")
# 4. 层厚波动
print("\n[4/8] 层厚波动分析...")
tj = LayerThicknessJitter()
tr = tj.analyze(thick_nom, thick_act)
cum_err = tr.cum_height_err[-1]
print(f" 层厚std: {tr.std*1000:.1f}μm, 累积高度误差: {cum_err*1000:.1f}μm")
# 5. 热应力场
print("\n[5/8] 热应力梯度...")
tsf = ThermalStressField()
trm = tsf.compute(sr.z_actual, dep_t)
print(f" 热梯度峰值高度: {trm.grad_peak_z:.2f}mm, "
f"对应层≈{int(trm.grad_peak_z/0.1)}层")
# 6. 多零件训练 + 预测
print("\n[6/8] 收缩预测模型(RF回归)...")
rows = []
meas = []
for p in range(24):
rng = np.random.RandomState(p)
g = SyntheticAmPart(rng=rng)
d = g.generate(f"_tmp_{p}.csv", n_layers=rng.randint(80,160),
radius_nom=rng.uniform(20,50))
zz = d["z_nominal"].values
dt = d["deposit_temp"].values
sm = LayerSettlementModel(base_temp=40.0).compute(zz, dt)
tjj = LayerThicknessJitter().analyze(
d["layer_thick_nom"].values, d["layer_thick_actual"].values)
tf = ThermalStressField().compute(sm.z_actual, dt)
rad = d["radius_nom"].iloc[0]
h = zz[-1]
# 机理真值(含层厚方差放大)
true_shrink = (0.0026*rad +
0.4*np.max(sm.settle)*1000/1000
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!