647 lines
24 KiB
Python
647 lines
24 KiB
Python
"""
|
||
模具刀路设计与G代码生成模块
|
||
|
||
架构:
|
||
1. ToolLibrary - 刀具库与切削参数管理
|
||
2. CuttingParamsCalculator - 切削参数自动计算
|
||
3. RoughingToolpathGenerator - 粗加工刀路生成
|
||
4. FinishingToolpathGenerator - 精加工刀路生成
|
||
5. GCodePostProcessor - G代码后处理器
|
||
6. MoldCAMDesigner - 模具CAM综合设计器
|
||
|
||
加工策略:
|
||
- 粗加工:Z层等高粗加工(自适应清根)
|
||
- 半精加工:等高线铣削
|
||
- 精加工:平行铣削/螺旋铣削/等高线精加工
|
||
- 清角:笔式清角
|
||
- 钻孔:冷却水路/顶针孔/螺丝孔
|
||
"""
|
||
|
||
from typing import Dict, List, Any, Optional, Tuple
|
||
import math
|
||
from utils.logger import get_logger
|
||
|
||
logger = get_logger(__name__)
|
||
|
||
|
||
class ToolLibrary:
|
||
"""刀具库"""
|
||
|
||
TOOLS = {
|
||
"endmill_20mm": {
|
||
"type": "endmill", "diameter": 20.0, "flute_length": 60.0,
|
||
"cutting_edges": 4, "material": "carbide",
|
||
"corner_radius": 0.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 100, "feed_per_tooth": 0.15,
|
||
"axial_depth": 10.0, "radial_depth": 15.0
|
||
}
|
||
},
|
||
"endmill_16mm": {
|
||
"type": "endmill", "diameter": 16.0, "flute_length": 50.0,
|
||
"cutting_edges": 4, "material": "carbide",
|
||
"corner_radius": 0.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 120, "feed_per_tooth": 0.12,
|
||
"axial_depth": 8.0, "radial_depth": 12.0
|
||
}
|
||
},
|
||
"endmill_10mm": {
|
||
"type": "endmill", "diameter": 10.0, "flute_length": 35.0,
|
||
"cutting_edges": 4, "material": "carbide",
|
||
"corner_radius": 0.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 130, "feed_per_tooth": 0.10,
|
||
"axial_depth": 5.0, "radial_depth": 8.0
|
||
}
|
||
},
|
||
"endmill_6mm": {
|
||
"type": "endmill", "diameter": 6.0, "flute_length": 22.0,
|
||
"cutting_edges": 3, "material": "carbide",
|
||
"corner_radius": 0.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 140, "feed_per_tooth": 0.06,
|
||
"axial_depth": 3.0, "radial_depth": 4.0
|
||
}
|
||
},
|
||
"ballnose_10mm": {
|
||
"type": "ballnose", "diameter": 10.0, "flute_length": 30.0,
|
||
"cutting_edges": 2, "material": "carbide",
|
||
"corner_radius": 5.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 150, "feed_per_tooth": 0.08,
|
||
"axial_depth": 0.5, "radial_depth": 1.0
|
||
}
|
||
},
|
||
"ballnose_6mm": {
|
||
"type": "ballnose", "diameter": 6.0, "flute_length": 22.0,
|
||
"cutting_edges": 2, "material": "carbide",
|
||
"corner_radius": 3.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 160, "feed_per_tooth": 0.06,
|
||
"axial_depth": 0.3, "radial_depth": 0.5
|
||
}
|
||
},
|
||
"ballnose_3mm": {
|
||
"type": "ballnose", "diameter": 3.0, "flute_length": 12.0,
|
||
"cutting_edges": 2, "material": "carbide",
|
||
"corner_radius": 1.5,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 180, "feed_per_tooth": 0.03,
|
||
"axial_depth": 0.15, "radial_depth": 0.3
|
||
}
|
||
},
|
||
"ballnose_1mm": {
|
||
"type": "ballnose", "diameter": 1.0, "flute_length": 5.0,
|
||
"cutting_edges": 2, "material": "carbide",
|
||
"corner_radius": 0.5,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 200, "feed_per_tooth": 0.01,
|
||
"axial_depth": 0.05, "radial_depth": 0.1
|
||
}
|
||
},
|
||
"drill_8mm": {
|
||
"type": "drill", "diameter": 8.0, "flute_length": 50.0,
|
||
"cutting_edges": 2, "material": "carbide",
|
||
"corner_radius": 0.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 80, "feed_per_tooth": 0.10,
|
||
"axial_depth": 50.0, "radial_depth": 0.0
|
||
}
|
||
},
|
||
"drill_5mm": {
|
||
"type": "drill", "diameter": 5.0, "flute_length": 35.0,
|
||
"cutting_edges": 2, "material": "carbide",
|
||
"corner_radius": 0.0,
|
||
"speeds_feeds": {
|
||
"cutting_speed": 90, "feed_per_tooth": 0.08,
|
||
"axial_depth": 35.0, "radial_depth": 0.0
|
||
}
|
||
},
|
||
}
|
||
|
||
MOLD_STEEL = {
|
||
"P20": {"hardness_hrc": 30, "cutting_speed_factor": 1.0, "feed_factor": 1.0},
|
||
"718H": {"hardness_hrc": 35, "cutting_speed_factor": 0.85, "feed_factor": 0.9},
|
||
"NAK80": {"hardness_hrc": 38, "cutting_speed_factor": 0.75, "feed_factor": 0.85},
|
||
"S136": {"hardness_hrc": 50, "cutting_speed_factor": 0.5, "feed_factor": 0.7},
|
||
"H13": {"hardness_hrc": 48, "cutting_speed_factor": 0.55, "feed_factor": 0.75},
|
||
"Al7075": {"hardness_hrc": 15, "cutting_speed_factor": 2.0, "feed_factor": 1.5},
|
||
}
|
||
|
||
@classmethod
|
||
def get_tool(cls, tool_id: str) -> Optional[Dict]:
|
||
return cls.TOOLS.get(tool_id)
|
||
|
||
@classmethod
|
||
def select_roughing_tool(cls, cavity_volume_mm3: float,
|
||
min_corner_radius: float = 0.0,
|
||
steel: str = "P20") -> Dict:
|
||
"""根据型腔体积和最小圆角选择粗加工刀具"""
|
||
if cavity_volume_mm3 > 500000:
|
||
tool_id = "endmill_20mm"
|
||
elif cavity_volume_mm3 > 100000:
|
||
tool_id = "endmill_16mm"
|
||
elif cavity_volume_mm3 > 20000:
|
||
tool_id = "endmill_10mm"
|
||
else:
|
||
tool_id = "endmill_6mm"
|
||
|
||
tool = cls.TOOLS[tool_id].copy()
|
||
steel_props = cls.MOLD_STEEL.get(steel, cls.MOLD_STEEL["P20"])
|
||
|
||
tool["speeds_feeds"] = cls._adjust_for_steel(tool["speeds_feeds"], steel_props)
|
||
tool["tool_id"] = tool_id
|
||
return tool
|
||
|
||
@classmethod
|
||
def select_finishing_tool(cls, surface_quality: str = "standard",
|
||
min_corner_radius: float = 0.0,
|
||
steel: str = "P20") -> Dict:
|
||
"""根据表面质量要求选择精加工刀具"""
|
||
if surface_quality == "mirror":
|
||
tool_id = "ballnose_3mm" if min_corner_radius <= 3 else "ballnose_6mm"
|
||
elif surface_quality == "fine":
|
||
tool_id = "ballnose_6mm" if min_corner_radius <= 6 else "ballnose_10mm"
|
||
else:
|
||
tool_id = "ballnose_10mm"
|
||
|
||
tool = cls.TOOLS[tool_id].copy()
|
||
steel_props = cls.MOLD_STEEL.get(steel, cls.MOLD_STEEL["P20"])
|
||
|
||
tool["speeds_feeds"] = cls._adjust_for_steel(tool["speeds_feeds"], steel_props)
|
||
tool["tool_id"] = tool_id
|
||
return tool
|
||
|
||
@classmethod
|
||
def _adjust_for_steel(cls, speeds_feeds: Dict, steel_props: Dict) -> Dict:
|
||
"""根据模具钢调整切削参数"""
|
||
adjusted = speeds_feeds.copy()
|
||
adjusted["cutting_speed"] *= steel_props["cutting_speed_factor"]
|
||
adjusted["feed_per_tooth"] *= steel_props["feed_factor"]
|
||
adjusted["axial_depth"] *= steel_props["feed_factor"]
|
||
adjusted["radial_depth"] *= steel_props["feed_factor"]
|
||
return adjusted
|
||
|
||
|
||
class CuttingParamsCalculator:
|
||
"""切削参数计算器"""
|
||
|
||
@staticmethod
|
||
def calculate_spindle_speed(cutting_speed_m_min: float, tool_diameter: float) -> int:
|
||
"""N = (1000 × Vc) / (π × D)"""
|
||
if tool_diameter <= 0:
|
||
return 1000
|
||
rpm = (1000 * cutting_speed_m_min) / (math.pi * tool_diameter)
|
||
return int(min(max(rpm, 500), 24000))
|
||
|
||
@staticmethod
|
||
def calculate_feed_rate(spindle_speed: int, feed_per_tooth: float,
|
||
cutting_edges: int) -> float:
|
||
"""F = N × fz × z"""
|
||
return spindle_speed * feed_per_tooth * cutting_edges
|
||
|
||
@staticmethod
|
||
def calculate_mrr(feed_rate: float, axial_depth: float,
|
||
radial_depth: float) -> float:
|
||
"""材料去除率 Q = ae × ap × F / 1000 (cm³/min)"""
|
||
return axial_depth * radial_depth * feed_rate / 1000
|
||
|
||
@staticmethod
|
||
def estimate_machining_time(toolpath_length: float, feed_rate: float,
|
||
rapid_distance: float = 0,
|
||
rapid_speed: float = 15000) -> float:
|
||
"""估算加工时间(分钟)"""
|
||
cutting_time = toolpath_length / feed_rate / 60 if feed_rate > 0 else 0
|
||
rapid_time = rapid_distance / rapid_speed / 60 if rapid_speed > 0 else 0
|
||
return cutting_time + rapid_time
|
||
|
||
@classmethod
|
||
def calculate_all(cls, tool: Dict) -> Dict:
|
||
"""计算完整切削参数"""
|
||
sf = tool["speeds_feeds"]
|
||
rpm = cls.calculate_spindle_speed(sf["cutting_speed"], tool["diameter"])
|
||
feed = cls.calculate_feed_rate(rpm, sf["feed_per_tooth"], tool["cutting_edges"])
|
||
mrr = cls.calculate_mrr(feed, sf["axial_depth"], sf["radial_depth"])
|
||
|
||
return {
|
||
"tool_id": tool.get("tool_id", "unknown"),
|
||
"tool_type": tool["type"],
|
||
"tool_diameter": tool["diameter"],
|
||
"spindle_speed_rpm": rpm,
|
||
"feed_rate_mm_min": round(feed, 1),
|
||
"axial_depth_mm": sf["axial_depth"],
|
||
"radial_depth_mm": sf["radial_depth"],
|
||
"material_removal_rate_cm3_min": round(mrr, 2),
|
||
"cutting_speed_m_min": round(sf["cutting_speed"], 1),
|
||
}
|
||
|
||
|
||
class RoughingToolpathGenerator:
|
||
"""粗加工刀路生成器"""
|
||
|
||
def generate_z_level_roughing(self, stock_bbox: Dict, cavity_bbox: Dict,
|
||
tool: Dict, cutting_params: Dict,
|
||
stock_allowance: float = 0.5) -> Dict[str, Any]:
|
||
"""
|
||
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]
|
||
z_max = cavity_bbox.get("max", [0, 0, 0])[2]
|
||
total_depth = z_max - z_min
|
||
|
||
axial_depth = cutting_params["axial_depth_mm"]
|
||
num_levels = max(1, math.ceil(total_depth / axial_depth))
|
||
|
||
actual_depth = total_depth / num_levels
|
||
|
||
stepover = cutting_params["radial_depth_mm"]
|
||
|
||
levels = []
|
||
for i in range(num_levels):
|
||
z_level = z_max - (i + 1) * actual_depth + stock_allowance
|
||
levels.append({
|
||
"z": round(z_level, 2),
|
||
"depth": round(actual_depth, 2),
|
||
"level_index": i + 1,
|
||
})
|
||
|
||
toolpath_length = self._estimate_roughing_length(
|
||
cavity_bbox, num_levels, stepover
|
||
)
|
||
|
||
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||
)
|
||
|
||
return {
|
||
"strategy": "z_level_roughing",
|
||
"tool": cutting_params,
|
||
"levels": levels,
|
||
"num_levels": num_levels,
|
||
"stepover": stepover,
|
||
"stock_allowance": stock_allowance,
|
||
"total_depth": round(total_depth, 2),
|
||
"estimated_toolpath_length": round(toolpath_length, 1),
|
||
"estimated_time_min": round(machining_time, 1),
|
||
"approach_type": "helical_ramp",
|
||
"ramp_angle": 2.0,
|
||
}
|
||
|
||
def _estimate_roughing_length(self, cavity_bbox: Dict, num_levels: int,
|
||
stepover: float) -> float:
|
||
"""估算粗加工刀路总长度"""
|
||
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
||
width = dims[0]
|
||
length = dims[1]
|
||
|
||
passes_per_level = max(1, int(width / stepover))
|
||
length_per_pass = length
|
||
length_per_level = passes_per_level * length_per_pass * 1.1
|
||
|
||
return length_per_level * num_levels
|
||
|
||
|
||
class FinishingToolpathGenerator:
|
||
"""精加工刀路生成器"""
|
||
|
||
def generate_parallel_finishing(self, cavity_bbox: Dict, tool: Dict,
|
||
cutting_params: Dict,
|
||
stepover: float = 0.3,
|
||
angle: float = 0.0) -> Dict[str, Any]:
|
||
"""
|
||
平行铣削精加工
|
||
|
||
Args:
|
||
cavity_bbox: 型腔边界框
|
||
tool: 刀具参数
|
||
cutting_params: 切削参数
|
||
stepover: 步距 mm
|
||
angle: 加工角度
|
||
|
||
Returns:
|
||
精加工刀路方案
|
||
"""
|
||
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
||
width = dims[0]
|
||
length = dims[1]
|
||
|
||
num_passes = max(1, int(width / stepover) + 1)
|
||
|
||
surface_roughness = self._estimate_surface_roughness(
|
||
tool["diameter"], stepover
|
||
)
|
||
|
||
toolpath_length = num_passes * length * 1.05
|
||
|
||
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||
)
|
||
|
||
return {
|
||
"strategy": "parallel_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),
|
||
"cutting_direction": "one_way",
|
||
"stepover_type": "scallop",
|
||
}
|
||
|
||
def generate_contour_finishing(self, cavity_bbox: Dict, tool: Dict,
|
||
cutting_params: Dict,
|
||
z_step: float = 0.5) -> Dict[str, Any]:
|
||
"""
|
||
等高线精加工
|
||
|
||
Args:
|
||
cavity_bbox: 型腔边界框
|
||
tool: 刀具参数
|
||
cutting_params: 切削参数
|
||
z_step: Z方向步距 mm
|
||
|
||
Returns:
|
||
等高线精加工方案
|
||
"""
|
||
z_min = cavity_bbox.get("min", [0, 0, 0])[2]
|
||
z_max = cavity_bbox.get("max", [0, 0, 0])[2]
|
||
total_depth = z_max - z_min
|
||
|
||
num_levels = max(1, int(total_depth / z_step) + 1)
|
||
|
||
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
||
perimeter = 2 * (dims[0] + dims[1])
|
||
|
||
toolpath_length = num_levels * perimeter * 1.1
|
||
|
||
machining_time = CuttingParamsCalculator.estimate_machining_time(
|
||
toolpath_length, cutting_params["feed_rate_mm_min"]
|
||
)
|
||
|
||
return {
|
||
"strategy": "contour_finishing",
|
||
"tool": cutting_params,
|
||
"z_step": z_step,
|
||
"num_levels": num_levels,
|
||
"estimated_toolpath_length": round(toolpath_length, 1),
|
||
"estimated_time_min": round(machining_time, 1),
|
||
}
|
||
|
||
def _estimate_surface_roughness(self, tool_diameter: float,
|
||
stepover: float) -> float:
|
||
"""估算表面粗糙度 Ra"""
|
||
if tool_diameter <= 0:
|
||
return 1.0
|
||
r = tool_diameter / 2
|
||
h = stepover ** 2 / (8 * r) if r > 0 else stepover
|
||
return h * 0.25
|
||
|
||
|
||
class GCodePostProcessor:
|
||
"""G代码后处理器"""
|
||
|
||
def __init__(self, controller: str = "fanuc"):
|
||
self.controller = controller
|
||
self.dialects = {
|
||
"fanuc": {
|
||
"rapid": "G00", "linear": "G01",
|
||
"cw_arc": "G02", "ccw_arc": "G03",
|
||
"absolute": "G90", "incremental": "G91",
|
||
"tool_change": "M06", "spindle_on": "M03",
|
||
"spindle_off": "M05", "coolant_on": "M08",
|
||
"coolant_off": "M09", "program_end": "M30",
|
||
"length_comp": "G43", "xy_plane": "G17",
|
||
"cancel_comp": "G40", "cancel_canned": "G80",
|
||
},
|
||
"siemens": {
|
||
"rapid": "G00", "linear": "G01",
|
||
"cw_arc": "G02", "ccw_arc": "G03",
|
||
"absolute": "G90", "incremental": "G91",
|
||
"tool_change": "M06", "spindle_on": "M03",
|
||
"spindle_off": "M05", "coolant_on": "M08",
|
||
"coolant_off": "M09", "program_end": "M30",
|
||
"length_comp": "G43", "xy_plane": "G17",
|
||
"cancel_comp": "G40", "cancel_canned": "G80",
|
||
},
|
||
}
|
||
|
||
def generate_gcode(self, operations: List[Dict],
|
||
program_number: int = 1000,
|
||
program_name: str = "MOLD_CAVITY") -> str:
|
||
"""
|
||
生成完整G代码程序
|
||
|
||
Args:
|
||
operations: 加工操作列表
|
||
program_number: 程序号
|
||
program_name: 程序名
|
||
|
||
Returns:
|
||
G代码字符串
|
||
"""
|
||
d = self.dialects.get(self.controller, self.dialects["fanuc"])
|
||
lines = []
|
||
|
||
lines.append(f"%")
|
||
lines.append(f"O{program_number} ({program_name})")
|
||
lines.append(f"{d['xy_plane']} {d['cancel_comp']} {d['cancel_canned']} {d['absolute']}")
|
||
lines.append(f"G54")
|
||
lines.append("")
|
||
|
||
for op_idx, op in enumerate(operations):
|
||
strategy = op.get("strategy", "unknown")
|
||
tool_info = op.get("tool", {})
|
||
tool_id = tool_info.get("tool_id", "T01")
|
||
tool_num = op_idx + 1
|
||
|
||
lines.append(f"(=== 操作 {tool_num}: {strategy} ===)")
|
||
|
||
tool_type = tool_info.get("tool_type", "endmill")
|
||
tool_dia = tool_info.get("tool_diameter", 10)
|
||
lines.append(f"(刀具: {tool_type} D{tool_dia:.1f}mm)")
|
||
|
||
lines.append(f"T{tool_num:02d} {d['tool_change']}")
|
||
lines.append(f"{d['length_comp']} H{tool_num:02d} Z100.0")
|
||
|
||
rpm = tool_info.get("spindle_speed_rpm", 3000)
|
||
lines.append(f"S{rpm} {d['spindle_on']}")
|
||
|
||
lines.append(f"{d['rapid']} X0 Y0 Z10.0")
|
||
lines.append(f"{d['coolant_on']}")
|
||
lines.append("")
|
||
|
||
feed = tool_info.get("feed_rate_mm_min", 500)
|
||
levels = op.get("levels", [])
|
||
|
||
if strategy == "z_level_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)}")
|
||
lines.append(f"{d['linear']} X50.0 Y30.0 F{feed}")
|
||
lines.append(f"{d['linear']} X-50.0 Y30.0")
|
||
lines.append(f"{d['linear']} X-50.0 Y-30.0")
|
||
lines.append(f"{d['linear']} X50.0 Y-30.0")
|
||
lines.append(f"{d['rapid']} Z10.0")
|
||
lines.append("")
|
||
|
||
elif strategy in ("parallel_finishing", "contour_finishing"):
|
||
num_passes = op.get("num_passes", 10)
|
||
stepover = op.get("stepover", 0.3)
|
||
|
||
for i in range(num_passes):
|
||
y = i * stepover - 30
|
||
lines.append(f"{d['linear']} Z-5.0 F{int(feed * 0.3)}")
|
||
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("")
|
||
|
||
else:
|
||
lines.append(f"(策略 {strategy} 的刀路数据)")
|
||
lines.append("")
|
||
|
||
lines.append(f"{d['coolant_off']}")
|
||
lines.append(f"{d['spindle_off']}")
|
||
lines.append(f"{d['rapid']} Z100.0")
|
||
lines.append("")
|
||
|
||
lines.append(f"{d['coolant_off']}")
|
||
lines.append(f"{d['spindle_off']}")
|
||
lines.append(f"G28 G91 Z0")
|
||
lines.append(f"G28 G91 X0 Y0")
|
||
lines.append(f"{d['program_end']}")
|
||
lines.append(f"%")
|
||
|
||
return "\n".join(lines)
|
||
|
||
|
||
class MoldCAMDesigner:
|
||
"""模具CAM综合设计器"""
|
||
|
||
def __init__(self):
|
||
self.tool_lib = ToolLibrary()
|
||
self.params_calc = CuttingParamsCalculator()
|
||
self.roughing_gen = RoughingToolpathGenerator()
|
||
self.finishing_gen = FinishingToolpathGenerator()
|
||
self.post_processor = GCodePostProcessor()
|
||
|
||
def design_mold_cam(self, cavity_bbox: Dict, stock_bbox: Dict,
|
||
mold_steel: str = "P20",
|
||
surface_quality: str = "standard",
|
||
controller: str = "fanuc",
|
||
program_number: int = 1000) -> Dict[str, Any]:
|
||
"""
|
||
综合设计模具CAM方案
|
||
|
||
Args:
|
||
cavity_bbox: 型腔边界框
|
||
stock_bbox: 毛坯边界框
|
||
mold_steel: 模具钢材料
|
||
surface_quality: 表面质量要求
|
||
controller: 数控系统
|
||
program_number: 程序号
|
||
|
||
Returns:
|
||
完整的CAM方案
|
||
"""
|
||
logger.info(f"开始模具CAM设计: 钢材={mold_steel}, 质量={surface_quality}")
|
||
|
||
roughing_tool = ToolLibrary.select_roughing_tool(
|
||
self._estimate_cavity_volume(cavity_bbox),
|
||
steel=mold_steel
|
||
)
|
||
roughing_params = CuttingParamsCalculator.calculate_all(roughing_tool)
|
||
|
||
finishing_tool = ToolLibrary.select_finishing_tool(
|
||
surface_quality=surface_quality,
|
||
steel=mold_steel
|
||
)
|
||
finishing_params = CuttingParamsCalculator.calculate_all(finishing_tool)
|
||
|
||
roughing_op = self.roughing_gen.generate_z_level_roughing(
|
||
stock_bbox, cavity_bbox, roughing_tool, roughing_params
|
||
)
|
||
|
||
finishing_op = self.finishing_gen.generate_parallel_finishing(
|
||
cavity_bbox, finishing_tool, finishing_params
|
||
)
|
||
|
||
operations = [roughing_op, finishing_op]
|
||
|
||
gcode = self.post_processor.generate_gcode(
|
||
operations, program_number=program_number
|
||
)
|
||
|
||
total_time = (
|
||
roughing_op.get("estimated_time_min", 0) +
|
||
finishing_op.get("estimated_time_min", 0)
|
||
)
|
||
|
||
result = {
|
||
"operations": operations,
|
||
"tools": {
|
||
"roughing": roughing_params,
|
||
"finishing": finishing_params,
|
||
},
|
||
"gcode": gcode,
|
||
"gcode_lines": len(gcode.split("\n")),
|
||
"summary": {
|
||
"total_operations": len(operations),
|
||
"total_estimated_time_min": round(total_time, 1),
|
||
"mold_steel": mold_steel,
|
||
"surface_quality": surface_quality,
|
||
"controller": controller,
|
||
},
|
||
"recommendations": self._generate_cam_recommendations(
|
||
roughing_op, finishing_op, mold_steel
|
||
),
|
||
}
|
||
|
||
logger.info(f"CAM设计完成: {len(operations)} 个工序, "
|
||
f"预计 {total_time:.1f} 分钟")
|
||
|
||
return result
|
||
|
||
def _estimate_cavity_volume(self, cavity_bbox: Dict) -> float:
|
||
"""估算型腔体积"""
|
||
dims = cavity_bbox.get("dimensions", [100, 100, 50])
|
||
return dims[0] * dims[1] * dims[2]
|
||
|
||
def _generate_cam_recommendations(self, roughing: Dict, finishing: Dict,
|
||
steel: str) -> List[str]:
|
||
"""生成CAM建议"""
|
||
recs = []
|
||
|
||
roughing_time = roughing.get("estimated_time_min", 0)
|
||
if roughing_time > 120:
|
||
recs.append("粗加工时间较长,建议使用更大直径刀具或增加切削深度")
|
||
|
||
finishing_roughness = finishing.get("surface_roughness_ra", 0)
|
||
if finishing_roughness > 0.8:
|
||
recs.append("表面粗糙度偏高,建议减小步距或使用更小直径球头刀")
|
||
|
||
if steel in ("S136", "H13"):
|
||
recs.append(f"高硬度钢材({steel}),建议使用涂层刀具并降低切削速度")
|
||
recs.append("建议增加半精加工工序减少精加工余量")
|
||
|
||
recs.append("加工前需确认工件坐标系零点位置")
|
||
recs.append("首件加工建议降低进给率20%进行试切")
|
||
|
||
return recs
|