x
This commit is contained in:
@@ -17,7 +17,7 @@ DEFAULT_CAM_PREFERENCES = {
|
|||||||
"mold_steel": "P20",
|
"mold_steel": "P20",
|
||||||
"surface_quality": "standard",
|
"surface_quality": "standard",
|
||||||
"controller": "fanuc",
|
"controller": "fanuc",
|
||||||
"include_gcode": False,
|
"include_gcode": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -503,7 +503,7 @@ class BaseMoldGenerator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
logger.info("回退方案型腔/型芯分离完成")
|
logger.info("回退方案型腔/型芯分离完成")
|
||||||
return cavity or mold_block, core
|
return cavity, core
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"分模回退方案失败: {e}")
|
logger.error(f"分模回退方案失败: {e}")
|
||||||
|
|||||||
@@ -244,44 +244,41 @@ class RoughingToolpathGenerator:
|
|||||||
tool: Dict, cutting_params: Dict,
|
tool: Dict, cutting_params: Dict,
|
||||||
stock_allowance: float = 0.5) -> Dict[str, Any]:
|
stock_allowance: float = 0.5) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
Z层等高粗加工
|
Z层等高粗加工 — 生成真实刀路坐标点。
|
||||||
|
|
||||||
策略:从顶面逐层向下铣削,每层切深为 axial_depth
|
策略:从顶面逐层向下铣削,每层在型腔边界框内往复走刀。
|
||||||
|
|
||||||
Args:
|
|
||||||
stock_bbox: 毛坯边界框
|
|
||||||
cavity_bbox: 型腔边界框
|
|
||||||
tool: 刀具参数
|
|
||||||
cutting_params: 切削参数
|
|
||||||
stock_allowance: 精加工余量 mm
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
粗加工刀路方案
|
|
||||||
"""
|
"""
|
||||||
z_min = cavity_bbox.get("min", [0, 0, 0])[2]
|
cx_min, cy_min, cz_min = cavity_bbox.get("min", [0, 0, 0])
|
||||||
z_max = cavity_bbox.get("max", [0, 0, 0])[2]
|
cx_max, cy_max, cz_max = cavity_bbox.get("max", [0, 0, 0])
|
||||||
total_depth = z_max - z_min
|
total_depth = cz_max - cz_min
|
||||||
|
|
||||||
axial_depth = cutting_params["axial_depth_mm"]
|
axial_depth = cutting_params["axial_depth_mm"]
|
||||||
num_levels = max(1, math.ceil(total_depth / axial_depth))
|
num_levels = max(1, math.ceil(total_depth / axial_depth))
|
||||||
|
|
||||||
actual_depth = total_depth / num_levels
|
actual_depth = total_depth / num_levels
|
||||||
|
|
||||||
stepover = cutting_params["radial_depth_mm"]
|
stepover = cutting_params["radial_depth_mm"]
|
||||||
|
|
||||||
levels = []
|
levels = []
|
||||||
|
path_points = []
|
||||||
|
|
||||||
for i in range(num_levels):
|
for i in range(num_levels):
|
||||||
z_level = z_max - (i + 1) * actual_depth + stock_allowance
|
z_level = cz_max - (i + 1) * actual_depth + stock_allowance
|
||||||
|
rounded_z = round(z_level, 2)
|
||||||
levels.append({
|
levels.append({
|
||||||
"z": round(z_level, 2),
|
"z": rounded_z,
|
||||||
"depth": round(actual_depth, 2),
|
"depth": round(actual_depth, 2),
|
||||||
"level_index": i + 1,
|
"level_index": i + 1,
|
||||||
})
|
})
|
||||||
|
layer_points = self._generate_roughing_layer_points(
|
||||||
|
x_min=cx_min, x_max=cx_max,
|
||||||
|
y_min=cy_min, y_max=cy_max,
|
||||||
|
z=rounded_z,
|
||||||
|
stepover=stepover,
|
||||||
|
)
|
||||||
|
path_points.extend(layer_points)
|
||||||
|
|
||||||
toolpath_length = self._estimate_roughing_length(
|
toolpath_length = self._estimate_roughing_length(
|
||||||
cavity_bbox, num_levels, stepover
|
cavity_bbox, num_levels, stepover
|
||||||
)
|
)
|
||||||
|
|
||||||
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||||||
toolpath_length, cutting_params["feed_rate_mm_min"]
|
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||||||
)
|
)
|
||||||
@@ -298,8 +295,35 @@ class RoughingToolpathGenerator:
|
|||||||
"estimated_time_min": round(machining_time, 1),
|
"estimated_time_min": round(machining_time, 1),
|
||||||
"approach_type": "helical_ramp",
|
"approach_type": "helical_ramp",
|
||||||
"ramp_angle": 2.0,
|
"ramp_angle": 2.0,
|
||||||
|
"path_points": path_points,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _generate_roughing_layer_points(
|
||||||
|
x_min: float, x_max: float,
|
||||||
|
y_min: float, y_max: float,
|
||||||
|
z: float, stepover: float,
|
||||||
|
) -> List[List[float]]:
|
||||||
|
"""为单个 Z 层生成往复走刀坐标点序列。"""
|
||||||
|
points = []
|
||||||
|
y_positions = []
|
||||||
|
y = y_min
|
||||||
|
while y <= y_max + 0.001:
|
||||||
|
y_positions.append(round(y, 2))
|
||||||
|
y += stepover
|
||||||
|
|
||||||
|
for idx, y_pos in enumerate(y_positions):
|
||||||
|
if idx % 2 == 0:
|
||||||
|
# 正向走刀: x_min → x_max
|
||||||
|
points.append([round(x_min, 2), y_pos, z])
|
||||||
|
points.append([round(x_max, 2), y_pos, z])
|
||||||
|
else:
|
||||||
|
# 反向走刀: x_max → x_min
|
||||||
|
points.append([round(x_max, 2), y_pos, z])
|
||||||
|
points.append([round(x_min, 2), y_pos, z])
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
def _estimate_roughing_length(self, cavity_bbox: Dict, num_levels: int,
|
def _estimate_roughing_length(self, cavity_bbox: Dict, num_levels: int,
|
||||||
stepover: float) -> float:
|
stepover: float) -> float:
|
||||||
"""估算粗加工刀路总长度"""
|
"""估算粗加工刀路总长度"""
|
||||||
@@ -313,6 +337,70 @@ class RoughingToolpathGenerator:
|
|||||||
|
|
||||||
return length_per_level * num_levels
|
return length_per_level * num_levels
|
||||||
|
|
||||||
|
def generate_core_roughing(self, stock_bbox: Dict, core_bbox: Dict,
|
||||||
|
tool: Dict, cutting_params: Dict,
|
||||||
|
stock_allowance: float = 0.5) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
型芯粗加工 — 从外轮廓向内逐层等高铣削。
|
||||||
|
|
||||||
|
型芯是凸出的几何体,策略是从毛坯外边界向内逐层收缩切削,
|
||||||
|
每层保持同一 Z 高度,环绕型芯轮廓走刀。
|
||||||
|
"""
|
||||||
|
sx_min, sy_min, sz_min = stock_bbox.get("min", [0, 0, 0])
|
||||||
|
sx_max, sy_max, sz_max = stock_bbox.get("max", [0, 0, 0])
|
||||||
|
cx_min, cy_min, cz_min = core_bbox.get("min", [0, 0, 0])
|
||||||
|
cx_max, cy_max, cz_max = core_bbox.get("max", [0, 0, 0])
|
||||||
|
|
||||||
|
axial_depth = cutting_params["axial_depth_mm"]
|
||||||
|
radial_depth = cutting_params["radial_depth_mm"]
|
||||||
|
total_depth = cz_max - cz_min
|
||||||
|
num_levels = max(1, math.ceil(total_depth / axial_depth))
|
||||||
|
|
||||||
|
levels = [{
|
||||||
|
"z": round(cz_max - (i + 1) * total_depth / num_levels, 2),
|
||||||
|
"depth": round(total_depth / num_levels, 2),
|
||||||
|
"level_index": i + 1,
|
||||||
|
} for i in range(num_levels)]
|
||||||
|
|
||||||
|
path_points = []
|
||||||
|
margin = 1.0
|
||||||
|
for level in levels:
|
||||||
|
z = level["z"]
|
||||||
|
outer_xmin, outer_xmax = sx_min + margin, sx_max - margin
|
||||||
|
outer_ymin, outer_ymax = sy_min + margin, sy_max - margin
|
||||||
|
num_contours = max(1, int(min(outer_xmax - cx_max, outer_ymax - cy_max) / radial_depth))
|
||||||
|
for c in range(num_contours):
|
||||||
|
inset = c * radial_depth
|
||||||
|
x0 = max(cx_min, outer_xmin + inset)
|
||||||
|
x1 = min(cx_max, outer_xmax - inset)
|
||||||
|
y0 = max(cy_min, outer_ymin + inset)
|
||||||
|
y1 = min(cy_max, outer_ymax - inset)
|
||||||
|
contour = [
|
||||||
|
[round(x0, 2), round(y0, 2), z],
|
||||||
|
[round(x1, 2), round(y0, 2), z],
|
||||||
|
[round(x1, 2), round(y1, 2), z],
|
||||||
|
[round(x0, 2), round(y1, 2), z],
|
||||||
|
]
|
||||||
|
path_points.extend(contour)
|
||||||
|
|
||||||
|
dims = core_bbox.get("dimensions", [100, 100, 50])
|
||||||
|
toolpath_length = num_levels * 2 * (dims[0] + dims[1]) * 2.0
|
||||||
|
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||||||
|
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"strategy": "core_roughing",
|
||||||
|
"tool": cutting_params,
|
||||||
|
"levels": levels,
|
||||||
|
"num_levels": num_levels,
|
||||||
|
"stepover": radial_depth,
|
||||||
|
"stock_allowance": stock_allowance,
|
||||||
|
"estimated_toolpath_length": round(toolpath_length, 1),
|
||||||
|
"estimated_time_min": round(machining_time, 1),
|
||||||
|
"path_points": path_points,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class FinishingToolpathGenerator:
|
class FinishingToolpathGenerator:
|
||||||
"""精加工刀路生成器"""
|
"""精加工刀路生成器"""
|
||||||
@@ -322,30 +410,36 @@ class FinishingToolpathGenerator:
|
|||||||
stepover: float = 0.3,
|
stepover: float = 0.3,
|
||||||
angle: float = 0.0) -> Dict[str, Any]:
|
angle: float = 0.0) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
平行铣削精加工
|
平行铣削精加工 — 生成真实刀路坐标点。
|
||||||
|
|
||||||
Args:
|
在型腔顶部沿 Y 方向往复走刀,覆盖整个型腔 XY 区域。
|
||||||
cavity_bbox: 型腔边界框
|
|
||||||
tool: 刀具参数
|
|
||||||
cutting_params: 切削参数
|
|
||||||
stepover: 步距 mm
|
|
||||||
angle: 加工角度
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
精加工刀路方案
|
|
||||||
"""
|
"""
|
||||||
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
||||||
|
cx_min, cy_min, cz_min = cavity_bbox.get("min", [0, 0, 0])
|
||||||
|
cx_max, cy_max, cz_max = cavity_bbox.get("max", [0, 0, 0])
|
||||||
width = dims[0]
|
width = dims[0]
|
||||||
length = dims[1]
|
length = dims[1]
|
||||||
|
finish_z = cz_max - 0.1 # 型腔顶面
|
||||||
|
|
||||||
num_passes = max(1, int(width / stepover) + 1)
|
num_passes = max(1, int(width / stepover) + 1)
|
||||||
|
path_points = []
|
||||||
|
|
||||||
|
y = cy_min
|
||||||
|
for i in range(num_passes):
|
||||||
|
y_pos = round(y + i * stepover, 2)
|
||||||
|
if y_pos > cy_max:
|
||||||
|
y_pos = round(cy_max, 2)
|
||||||
|
if i % 2 == 0:
|
||||||
|
path_points.append([round(cx_min, 2), y_pos, finish_z])
|
||||||
|
path_points.append([round(cx_max, 2), y_pos, finish_z])
|
||||||
|
else:
|
||||||
|
path_points.append([round(cx_max, 2), y_pos, finish_z])
|
||||||
|
path_points.append([round(cx_min, 2), y_pos, finish_z])
|
||||||
|
|
||||||
surface_roughness = self._estimate_surface_roughness(
|
surface_roughness = self._estimate_surface_roughness(
|
||||||
tool["diameter"], stepover
|
tool["diameter"], stepover
|
||||||
)
|
)
|
||||||
|
|
||||||
toolpath_length = num_passes * length * 1.05
|
toolpath_length = num_passes * length * 1.05
|
||||||
|
|
||||||
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||||||
toolpath_length, cutting_params["feed_rate_mm_min"]
|
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||||||
)
|
)
|
||||||
@@ -361,6 +455,7 @@ class FinishingToolpathGenerator:
|
|||||||
"estimated_time_min": round(machining_time, 1),
|
"estimated_time_min": round(machining_time, 1),
|
||||||
"cutting_direction": "one_way",
|
"cutting_direction": "one_way",
|
||||||
"stepover_type": "scallop",
|
"stepover_type": "scallop",
|
||||||
|
"path_points": path_points,
|
||||||
}
|
}
|
||||||
|
|
||||||
def generate_contour_finishing(self, cavity_bbox: Dict, tool: Dict,
|
def generate_contour_finishing(self, cavity_bbox: Dict, tool: Dict,
|
||||||
@@ -411,6 +506,62 @@ class FinishingToolpathGenerator:
|
|||||||
h = stepover ** 2 / (8 * r) if r > 0 else stepover
|
h = stepover ** 2 / (8 * r) if r > 0 else stepover
|
||||||
return h * 0.25
|
return h * 0.25
|
||||||
|
|
||||||
|
def generate_3d_surface_finishing(self, cavity_bbox: Dict, tool: Dict,
|
||||||
|
cutting_params: Dict,
|
||||||
|
stepover: float = 0.5,
|
||||||
|
angle: float = 0.0) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Ballnose 三维曲面精加工 — Z 随形走刀。
|
||||||
|
|
||||||
|
在型腔边界框内,沿 Y 方向逐行走刀,Z 高度按余弦曲线随 Y 变化,
|
||||||
|
模拟曲面轮廓加工。适用于球头刀对型腔底面的随形精加工。
|
||||||
|
"""
|
||||||
|
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
||||||
|
cx_min, cy_min, cz_min = cavity_bbox.get("min", [0, 0, 0])
|
||||||
|
cx_max, cy_max, cz_max = cavity_bbox.get("max", [0, 0, 0])
|
||||||
|
width = dims[0]
|
||||||
|
length = dims[1]
|
||||||
|
depth = dims[2]
|
||||||
|
mid_y = (cy_min + cy_max) / 2.0
|
||||||
|
|
||||||
|
num_passes = max(1, int(width / stepover) + 1)
|
||||||
|
path_points = []
|
||||||
|
|
||||||
|
y = cy_min
|
||||||
|
for i in range(num_passes):
|
||||||
|
y_pos = round(y + i * stepover, 2)
|
||||||
|
if y_pos > cy_max:
|
||||||
|
y_pos = round(cy_max, 2)
|
||||||
|
# Z 随 Y 位置按余弦曲线变化,模拟中心最深、边缘最浅的曲面
|
||||||
|
ratio = (y_pos - mid_y) / max((cy_max - cy_min) / 2, 1.0)
|
||||||
|
z_depth = cz_max - depth * 0.85 * (1.0 - min(abs(ratio), 1.0))
|
||||||
|
z_finish = round(max(cz_min + 0.1, z_depth), 2)
|
||||||
|
if i % 2 == 0:
|
||||||
|
path_points.append([round(cx_min, 2), y_pos, z_finish])
|
||||||
|
path_points.append([round(cx_max, 2), y_pos, z_finish])
|
||||||
|
else:
|
||||||
|
path_points.append([round(cx_max, 2), y_pos, z_finish])
|
||||||
|
path_points.append([round(cx_min, 2), y_pos, z_finish])
|
||||||
|
|
||||||
|
surface_roughness = self._estimate_surface_roughness(tool["diameter"], stepover)
|
||||||
|
toolpath_length = num_passes * length * 1.1
|
||||||
|
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||||||
|
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"strategy": "3d_surface_finishing",
|
||||||
|
"tool": cutting_params,
|
||||||
|
"stepover": stepover,
|
||||||
|
"angle": angle,
|
||||||
|
"num_passes": num_passes,
|
||||||
|
"surface_roughness_ra": round(surface_roughness, 3),
|
||||||
|
"estimated_toolpath_length": round(toolpath_length, 1),
|
||||||
|
"estimated_time_min": round(machining_time, 1),
|
||||||
|
"note": "3D 曲面随形加工,Z 高度沿 Y 方向余弦变化",
|
||||||
|
"path_points": path_points,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class GCodePostProcessor:
|
class GCodePostProcessor:
|
||||||
"""G代码后处理器"""
|
"""G代码后处理器"""
|
||||||
@@ -487,31 +638,66 @@ class GCodePostProcessor:
|
|||||||
|
|
||||||
feed = tool_info.get("feed_rate_mm_min", 500)
|
feed = tool_info.get("feed_rate_mm_min", 500)
|
||||||
levels = op.get("levels", [])
|
levels = op.get("levels", [])
|
||||||
|
path_points = op.get("path_points", [])
|
||||||
|
|
||||||
if strategy == "z_level_roughing" and levels:
|
if strategy == "z_level_roughing" and levels:
|
||||||
for level in levels:
|
for level in levels:
|
||||||
z = level["z"]
|
z = level["z"]
|
||||||
lines.append(f"(--- Z层 {level['level_index']}: Z={z:.2f} ---)")
|
lines.append(f"(--- Z层 {level['level_index']}: Z={z:.2f} ---)")
|
||||||
lines.append(f"{d['linear']} Z{z:.2f} F{int(feed * 0.5)}")
|
lines.append(f"{d['linear']} Z{z:.2f} F{int(feed * 0.5)}")
|
||||||
lines.append(f"{d['linear']} X50.0 Y30.0 F{feed}")
|
# 筛选当前 Z 层的刀路点
|
||||||
lines.append(f"{d['linear']} X-50.0 Y30.0")
|
layer_points = [p for p in path_points if abs(p[2] - z) < 0.01]
|
||||||
lines.append(f"{d['linear']} X-50.0 Y-30.0")
|
if layer_points:
|
||||||
lines.append(f"{d['linear']} X50.0 Y-30.0")
|
for pt in layer_points:
|
||||||
|
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} F{feed}")
|
||||||
|
else:
|
||||||
|
# 无精细刀路点时退化为矩形扫面
|
||||||
|
cx_min = min(p[0] for p in path_points) if path_points else -50.0
|
||||||
|
cx_max = max(p[0] for p in path_points) if path_points else 50.0
|
||||||
|
cy_min = min(p[1] for p in path_points) if path_points else -30.0
|
||||||
|
cy_max = max(p[1] for p in path_points) if path_points else 30.0
|
||||||
|
lines.append(f"{d['linear']} X{cx_max:.2f} Y{cy_min:.2f} F{feed}")
|
||||||
|
lines.append(f"{d['linear']} X{cx_min:.2f} Y{cy_min:.2f}")
|
||||||
|
lines.append(f"{d['linear']} X{cx_min:.2f} Y{cy_max:.2f}")
|
||||||
|
lines.append(f"{d['linear']} X{cx_max:.2f} Y{cy_max:.2f}")
|
||||||
lines.append(f"{d['rapid']} Z10.0")
|
lines.append(f"{d['rapid']} Z10.0")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
elif strategy in ("parallel_finishing", "contour_finishing"):
|
elif strategy in ("parallel_finishing", "contour_finishing"):
|
||||||
num_passes = op.get("num_passes", 10)
|
finish_z = path_points[0][2] if path_points else -5.0
|
||||||
stepover = op.get("stepover", 0.3)
|
lines.append(f"{d['linear']} Z{finish_z:.2f} F{int(feed * 0.3)}")
|
||||||
|
if path_points:
|
||||||
for i in range(num_passes):
|
for pt in path_points:
|
||||||
y = i * stepover - 30
|
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} F{feed}")
|
||||||
lines.append(f"{d['linear']} Z-5.0 F{int(feed * 0.3)}")
|
else:
|
||||||
lines.append(f"{d['linear']} X50.0 Y{y:.2f} F{feed}")
|
num_passes = op.get("num_passes", 10)
|
||||||
lines.append(f"{d['linear']} X-50.0 Y{y:.2f}")
|
stepover = op.get("stepover", 0.3)
|
||||||
|
for i in range(num_passes):
|
||||||
|
y = i * stepover - 30
|
||||||
|
lines.append(f"{d['linear']} X50.0 Y{y:.2f} F{feed}")
|
||||||
|
lines.append(f"{d['linear']} X-50.0 Y{y:.2f}")
|
||||||
lines.append(f"{d['rapid']} Z5.0")
|
lines.append(f"{d['rapid']} Z5.0")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
|
elif strategy == "core_roughing" and levels:
|
||||||
|
for level in levels:
|
||||||
|
z = level["z"]
|
||||||
|
lines.append(f"(--- 型芯Z层 {level['level_index']}: Z={z:.2f} ---)")
|
||||||
|
lines.append(f"{d['linear']} Z{z:.2f} F{int(feed * 0.5)}")
|
||||||
|
layer_points = [p for p in path_points if abs(p[2] - z) < 0.01]
|
||||||
|
for pt in layer_points:
|
||||||
|
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} F{feed}")
|
||||||
|
lines.append(f"{d['rapid']} Z10.0")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
elif strategy == "3d_surface_finishing":
|
||||||
|
lines.append(f"(--- 3D 曲面精加工,球头刀随形 ---)")
|
||||||
|
if path_points:
|
||||||
|
for pt in path_points:
|
||||||
|
lines.append(f"{d['linear']} X{pt[0]:.2f} Y{pt[1]:.2f} Z{pt[2]:.2f} F{feed}")
|
||||||
|
lines.append(f"{d['rapid']} Z10.0")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
lines.append(f"(策略 {strategy} 的刀路数据)")
|
lines.append(f"(策略 {strategy} 的刀路数据)")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
@@ -540,25 +726,50 @@ class MoldCAMDesigner:
|
|||||||
self.roughing_gen = RoughingToolpathGenerator()
|
self.roughing_gen = RoughingToolpathGenerator()
|
||||||
self.finishing_gen = FinishingToolpathGenerator()
|
self.finishing_gen = FinishingToolpathGenerator()
|
||||||
self.post_processor = GCodePostProcessor()
|
self.post_processor = GCodePostProcessor()
|
||||||
|
self._collision_detector = None
|
||||||
|
self._toolpath_optimizer = None
|
||||||
|
self._edm_designer = None
|
||||||
|
self._machining_simulator = None
|
||||||
|
|
||||||
|
def _get_collision_detector(self):
|
||||||
|
if self._collision_detector is None:
|
||||||
|
from moldinsight.core.mold_machining import CollisionDetector
|
||||||
|
self._collision_detector = CollisionDetector()
|
||||||
|
return self._collision_detector
|
||||||
|
|
||||||
|
def _get_toolpath_optimizer(self):
|
||||||
|
if self._toolpath_optimizer is None:
|
||||||
|
from moldinsight.core.mold_machining import ToolpathOptimizer
|
||||||
|
self._toolpath_optimizer = ToolpathOptimizer()
|
||||||
|
return self._toolpath_optimizer
|
||||||
|
|
||||||
|
def _get_edm_designer(self):
|
||||||
|
if self._edm_designer is None:
|
||||||
|
from moldinsight.core.mold_machining import EDMElectrodeDesigner
|
||||||
|
self._edm_designer = EDMElectrodeDesigner()
|
||||||
|
return self._edm_designer
|
||||||
|
|
||||||
|
def _get_machining_simulator(self):
|
||||||
|
if self._machining_simulator is None:
|
||||||
|
from moldinsight.core.mold_machining import MachiningSimulator
|
||||||
|
self._machining_simulator = MachiningSimulator()
|
||||||
|
return self._machining_simulator
|
||||||
|
|
||||||
def design_mold_cam(self, cavity_bbox: Dict, stock_bbox: Dict,
|
def design_mold_cam(self, cavity_bbox: Dict, stock_bbox: Dict,
|
||||||
mold_steel: str = "P20",
|
mold_steel: str = "P20",
|
||||||
surface_quality: str = "standard",
|
surface_quality: str = "standard",
|
||||||
controller: str = "fanuc",
|
controller: str = "fanuc",
|
||||||
program_number: int = 1000) -> Dict[str, Any]:
|
program_number: int = 1000,
|
||||||
|
core_bbox: Optional[Dict] = None,
|
||||||
|
undercut_regions: Optional[List[Dict]] = None) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
综合设计模具CAM方案
|
综合设计模具CAM方案(型腔 + 型芯 + 3D 曲面 + EDM + 仿真)。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cavity_bbox: 型腔边界框
|
cavity_bbox: 型腔边界框
|
||||||
stock_bbox: 毛坯边界框
|
stock_bbox: 毛坯边界框
|
||||||
mold_steel: 模具钢材料
|
core_bbox: 型芯边界框(可选,提供则生成型芯刀路)
|
||||||
surface_quality: 表面质量要求
|
undercut_regions: 倒扣区域列表(可选,提供则生成 EDM 方案)
|
||||||
controller: 数控系统
|
|
||||||
program_number: 程序号
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
完整的CAM方案
|
|
||||||
"""
|
"""
|
||||||
logger.info(f"开始模具CAM设计: 钢材={mold_steel}, 质量={surface_quality}")
|
logger.info(f"开始模具CAM设计: 钢材={mold_steel}, 质量={surface_quality}")
|
||||||
|
|
||||||
@@ -584,6 +795,79 @@ class MoldCAMDesigner:
|
|||||||
|
|
||||||
operations = [roughing_op, finishing_op]
|
operations = [roughing_op, finishing_op]
|
||||||
|
|
||||||
|
# 型芯粗加工(提供 core_bbox 时)
|
||||||
|
core_op = None
|
||||||
|
if core_bbox:
|
||||||
|
core_op = self.roughing_gen.generate_core_roughing(
|
||||||
|
stock_bbox, core_bbox, roughing_tool, roughing_params
|
||||||
|
)
|
||||||
|
operations.append(core_op)
|
||||||
|
|
||||||
|
# 3D 曲面精加工(ballnose 随形)
|
||||||
|
surface_op = self.finishing_gen.generate_3d_surface_finishing(
|
||||||
|
cavity_bbox, finishing_tool, finishing_params
|
||||||
|
)
|
||||||
|
operations.append(surface_op)
|
||||||
|
|
||||||
|
# 刀路优化与碰撞检测
|
||||||
|
manufacturing_warnings = self._generate_cam_recommendations(
|
||||||
|
roughing_op, finishing_op, mold_steel
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
optimizer = self._get_toolpath_optimizer()
|
||||||
|
for op in operations:
|
||||||
|
pts = op.get("path_points", [])
|
||||||
|
if pts and len(pts) >= 2:
|
||||||
|
optimized = optimizer.optimize_toolpath(
|
||||||
|
pts,
|
||||||
|
{"feed_rate_mm_min": op.get("tool", {}).get("feed_rate_mm_min", 500)},
|
||||||
|
stock_bbox,
|
||||||
|
)
|
||||||
|
if optimized and isinstance(optimized, dict):
|
||||||
|
op["optimized"] = True
|
||||||
|
op["optimization_metadata"] = {
|
||||||
|
"original_point_count": optimized.get("original_point_count"),
|
||||||
|
"corner_slowdowns": optimized.get("corner_slowdowns"),
|
||||||
|
"recommendations": optimized.get("recommendations"),
|
||||||
|
}
|
||||||
|
|
||||||
|
detector = self._get_collision_detector()
|
||||||
|
tool_rep = {"diameter": roughing_tool["diameter"], "flute_length": roughing_tool["flute_length"]}
|
||||||
|
collision_result = detector.check_toolpath_safety(
|
||||||
|
roughing_op.get("path_points", [[0, 0, 50]]),
|
||||||
|
tool_rep,
|
||||||
|
stock_bbox,
|
||||||
|
)
|
||||||
|
if collision_result and not collision_result.get("is_safe", True):
|
||||||
|
manufacturing_warnings.append(
|
||||||
|
f"碰撞检测发现 {collision_result.get('total_issues', 0)} 处潜在碰撞风险,请人工复核安全平面"
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug(f"刀路优化/碰撞检测跳过(非致命): {exc}")
|
||||||
|
|
||||||
|
# EDM 电极设计
|
||||||
|
edm_result = None
|
||||||
|
if undercut_regions:
|
||||||
|
try:
|
||||||
|
edm = self._get_edm_designer()
|
||||||
|
edm_result = edm.design_electrodes(
|
||||||
|
undercut_regions=undercut_regions,
|
||||||
|
cavity_bbox=cavity_bbox,
|
||||||
|
material="copper",
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug(f"EDM 电极设计跳过(非致命): {exc}")
|
||||||
|
|
||||||
|
# 加工仿真
|
||||||
|
simulation_result = None
|
||||||
|
try:
|
||||||
|
sim = self._get_machining_simulator()
|
||||||
|
simulation_result = sim.simulate_machining(operations, stock_bbox)
|
||||||
|
if simulation_result and simulation_result.get("gouging_detected"):
|
||||||
|
manufacturing_warnings.append("仿真检测到过切风险,请人工复核刀路")
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug(f"加工仿真跳过(非致命): {exc}")
|
||||||
|
|
||||||
gcode = self.post_processor.generate_gcode(
|
gcode = self.post_processor.generate_gcode(
|
||||||
operations, program_number=program_number
|
operations, program_number=program_number
|
||||||
)
|
)
|
||||||
@@ -608,9 +892,9 @@ class MoldCAMDesigner:
|
|||||||
"surface_quality": surface_quality,
|
"surface_quality": surface_quality,
|
||||||
"controller": controller,
|
"controller": controller,
|
||||||
},
|
},
|
||||||
"recommendations": self._generate_cam_recommendations(
|
"recommendations": manufacturing_warnings,
|
||||||
roughing_op, finishing_op, mold_steel
|
"edm": edm_result,
|
||||||
),
|
"simulation": simulation_result,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(f"CAM设计完成: {len(operations)} 个工序, "
|
logger.info(f"CAM设计完成: {len(operations)} 个工序, "
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ class CAMBundleService:
|
|||||||
mold_steel=mold_steel,
|
mold_steel=mold_steel,
|
||||||
surface_quality=surface_quality,
|
surface_quality=surface_quality,
|
||||||
controller=controller,
|
controller=controller,
|
||||||
|
core_bbox=cavity_bbox, # MVP: 型芯 bbox 复用型腔 bbox
|
||||||
|
undercut_regions=scheme.get("undercut_regions") if scheme else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
process_plan = self._build_process_plan(cam_result.get("operations", []))
|
process_plan = self._build_process_plan(cam_result.get("operations", []))
|
||||||
@@ -55,6 +57,26 @@ class CAMBundleService:
|
|||||||
if include_gcode:
|
if include_gcode:
|
||||||
bundle["gcode"] = cam_result.get("gcode", "")
|
bundle["gcode"] = cam_result.get("gcode", "")
|
||||||
bundle["gcode_lines"] = cam_result.get("gcode_lines", 0)
|
bundle["gcode_lines"] = cam_result.get("gcode_lines", 0)
|
||||||
|
|
||||||
|
# 可选:EDM 电极方案
|
||||||
|
edm = cam_result.get("edm")
|
||||||
|
if edm and edm.get("electrodes"):
|
||||||
|
bundle["edm"] = {
|
||||||
|
"electrode_count": edm.get("total_electrode_count", 0),
|
||||||
|
"total_volume_cm3": edm.get("total_volume_cm3", 0),
|
||||||
|
"material": edm.get("material", "copper"),
|
||||||
|
"recommendations": edm.get("recommendations", []),
|
||||||
|
}
|
||||||
|
|
||||||
|
# 可选:加工仿真摘要
|
||||||
|
sim = cam_result.get("simulation")
|
||||||
|
if sim:
|
||||||
|
bundle["simulation"] = {
|
||||||
|
"removed_percent": sim.get("total_volume_removed_percent", 0),
|
||||||
|
"gouging_detected": bool(sim.get("gouging_detected")),
|
||||||
|
"quality": sim.get("quality_assessment", {}),
|
||||||
|
}
|
||||||
|
|
||||||
return bundle
|
return bundle
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -494,7 +494,7 @@ class ProcessingService:
|
|||||||
"generated_at": datetime.now().isoformat(),
|
"generated_at": datetime.now().isoformat(),
|
||||||
"schemes": {},
|
"schemes": {},
|
||||||
}
|
}
|
||||||
components = ["cavity", "core", "parting_surface"]
|
components = ["cavity", "core", "parting_surface", "product", "a_plate", "b_plate"]
|
||||||
|
|
||||||
for scheme_id, cavity_data in export_shapes.items():
|
for scheme_id, cavity_data in export_shapes.items():
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user