This commit is contained in:
2026-03-11 01:05:18 +08:00
parent 76d87162eb
commit 57c51998e6
5 changed files with 1167 additions and 34 deletions
+208
View File
@@ -0,0 +1,208 @@
"""
AI 分模辅助模型接口示例
此文件展示了如何创建 AI 模型来辅助分模过程。
实际使用时需要替换为真实的 AI 模型。
"""
from typing import Dict, Any, Optional
import numpy as np
class AIPartingSurfaceDetector:
"""
AI 分型面检测器(示例接口)
功能:
- 分析产品 3D 几何
- 预测最优分型面位置和方向
- 识别倒扣区域
"""
def __init__(self, model_path: Optional[str] = None):
"""
初始化 AI 分型面检测器
Args:
model_path: 训练好的模型路径
"""
self.model_path = model_path
self.model = None
# 如果提供了模型路径,加载模型
if model_path:
self._load_model(model_path)
def _load_model(self, model_path: str):
"""加载训练好的 AI 模型"""
# TODO: 实现模型加载逻辑
# 示例:
# import torch
# self.model = torch.load(model_path)
print(f"AI 模型加载:{model_path}")
def detect(self, product_shape: Any, analysis: Dict) -> Optional[Dict]:
"""
检测最优分型面
Args:
product_shape: OpenCASCADE 形状对象
analysis: 几何分析结果(包含 bounding_box, volume 等)
Returns:
{
"origin": [x, y, z], # 分型面原点
"normal": [nx, ny, nz], # 分型面法向量
"confidence": 0.95, # 置信度
"parting_line": [...] # 可选的分型线
}
"""
# TODO: 使用 AI 模型进行预测
# 这里是示例返回
# 1. 将产品形状转换为 AI 模型输入
# - 体素化 (voxelization)
# - 点云 (point cloud)
# - 多视图 (multi-view images)
input_data = self._preprocess_shape(product_shape, analysis)
# 2. 使用模型预测
# prediction = self.model.predict(input_data)
# 3. 返回预测结果
return {
"origin": [0, 0, analysis["bounding_box"]["center"][2]],
"normal": [0, 0, 1], # Z 方向
"confidence": 0.85,
"undercut_regions": [] # 倒扣区域
}
def _preprocess_shape(self, shape: Any, analysis: Dict) -> Any:
"""
预处理产品形状为 AI 模型输入
可能的预处理方式:
1. 体素化:将 3D 模型转换为 3D 网格
2. 点云:采样表面点
3. 多视图:渲染多个角度的 2D 图像
"""
# TODO: 实现预处理逻辑
return None
class AIDraftAnalyzer:
"""
AI 拔模分析器(示例接口)
功能:
- 分析哪些面需要拔模
- 预测最优拔模角度
- 检测脱模干涉
"""
def __init__(self, model_path: Optional[str] = None):
self.model_path = model_path
self.model = None
if model_path:
self._load_model(model_path)
def _load_model(self, model_path: str):
"""加载训练好的 AI 模型"""
print(f"AI 拔模分析模型加载:{model_path}")
def analyze(self, product_shape: Any, parting_surface: Any,
base_draft_angle: float) -> Optional[Dict]:
"""
分析拔模需求
Args:
product_shape: 产品形状
parting_surface: 分型面
base_draft_angle: 基础拔模角(度)
Returns:
{
"drafted_shape": ..., # 应用拔模后的形状
"draft_angles": {...}, # 各面的拔模角
"interference_areas": [...], # 干涉区域
"recommendations": [...] # 优化建议
}
"""
# TODO: 使用 AI 模型分析拔模
# 示例返回
return {
"drafted_shape": product_shape, # 简化:返回原始形状
"draft_angles": {"default": base_draft_angle},
"interference_areas": [],
"recommendations": ["建议增加圆角", "壁厚均匀化"]
}
class AICavityLayoutOptimizer:
"""
AI 型腔布局优化器(示例接口)
功能:
- 优化多型腔排列
- 设计流道系统
- 平衡材料流动
"""
def __init__(self, model_path: Optional[str] = None):
self.model_path = model_path
self.model = None
if model_path:
self._load_model(model_path)
def optimize(self, product_shape: Any, cavity_count: int,
mold_base_size: Dict) -> Optional[Dict]:
"""
优化型腔布局
Args:
product_shape: 产品形状
cavity_count: 型腔数量
mold_base_size: 模架尺寸
Returns:
{
"cavity_positions": [...], # 各型腔位置
"runner_system": {...}, # 流道系统设计
"balance_score": 0.92, # 流动平衡评分
"material_efficiency": 0.85 # 材料利用率
}
"""
# TODO: 使用 AI 优化型腔布局
return {
"cavity_positions": [[0, 0, 0]], # 示例
"runner_system": {"type": "cold_runner"},
"balance_score": 0.85,
"material_efficiency": 0.80
}
# ==================== 使用示例 ====================
if __name__ == "__main__":
# 示例:如何使用 AI 模型接口
# 1. 创建 AI 模型实例
parting_detector = AIPartingSurfaceDetector(model_path="models/parting_surface.pth")
draft_analyzer = AIDraftAnalyzer(model_path="models/draft_analysis.pth")
# 2. 设置到 MoldCavityGenerator
from core.mold_generator import MoldCavityGenerator
generator = MoldCavityGenerator()
generator.set_ai_model(
parting_detector=parting_detector,
draft_analyzer=draft_analyzer
)
# 3. 使用(AI 模型会自动介入)
# result = generator.generate_mold_cavities(product_shape)
print("AI 模型接口已配置,分模时将自动使用 AI 辅助")
+335 -34
View File
@@ -1,20 +1,26 @@
# src/core/mold_generator.py
from pathlib import Path
from typing import Dict, List, Any, Tuple, Optional
from typing import Dict, List, Any, Tuple, Optional, Callable
import numpy as np
from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeThickSolid
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Transform
from OCC.Core.BRepOffsetAPI import BRepOffsetAPI_MakeThickSolid, BRepOffsetAPI_ThickSolid
from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut, BRepAlgoAPI_Fuse, BRepAlgoAPI_Section
from OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace, BRepBuilderAPI_Transform, BRepBuilderAPI_MakePolygon
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
from OCC.Core.Geom import Geom_Plane
from OCC.Core.gp import gp_Pln, gp_Dir, gp_Pnt, gp_Vec, gp_Trsf
from OCC.Core.gp import gp_Pln, gp_Dir, gp_Pnt, gp_Vec, gp_Trsf, gp_Ax2, gp_Circ
from OCC.Core.TopTools import TopTools_ListOfShape
from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Shape
from OCC.Core.TopoDS import TopoDS_Face, TopoDS_Shape, TopoDS_Edge, TopoDS_Vertex
from OCC.Core.BRep import BRep_Tool
from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh
from OCC.Core.BRepExtrema import BRepExtrema_DistShapeShape
from OCC.Core.GProp import GProp_GProps
from OCC.Core.BRepGProp import brepgprop
from OCC.Core.TopExp import TopExp_Explorer
from OCC.Core.TopAbs import TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX
from OCC.Core.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_Curve
from OCC.Core.BRepTools import breptools
from OCC.Core.GeomAPI import geomapi
from OCC.Core.Poly import Poly_Polygon3D
from models.schemas import create_mold_cavity_data, create_mold_key_info
from utils.logger import get_logger
@@ -54,6 +60,22 @@ class MoldCavityGenerator:
# 分型面检测参数
self.parting_line_tolerance = 0.1
self.max_draft_angle = 5.0
# AI 模型接口(预留)
self.ai_parting_detector: Optional[Any] = None
self.ai_draft_analyzer: Optional[Any] = None
def set_ai_model(self, parting_detector: Any = None, draft_analyzer: Any = None):
"""
设置 AI 模型接口(预留)
Args:
parting_detector: 分型面检测 AI 模型
draft_analyzer: 拔模分析 AI 模型
"""
self.ai_parting_detector = parting_detector
self.ai_draft_analyzer = draft_analyzer
logger.info("AI 模型接口已设置")
def set_material(self, material: str):
"""设置产品材料"""
@@ -236,32 +258,44 @@ class MoldCavityGenerator:
}
def _detect_parting_surface(self, shape: Any, analysis: Dict) -> Tuple[Any, List]:
"""检测分型面和分型线"""
# 简化的分型面检测:基于Z方向的最高点和最低点
bbox = analysis["bounding_box"]
center_z = bbox["center"][2]
# 创建分型面(XY平面)
parting_plane = gp_Pln(
gp_Pnt(0, 0, center_z),
gp_Dir(0, 0, 1)
)
parting_surface = BRepBuilderAPI_MakeFace(
parting_plane,
bbox["min"][0] - 10, bbox["max"][0] + 10,
bbox["min"][1] - 10, bbox["max"][1] + 10
).Face()
# 分型线(简化)
parting_line = [
[bbox["min"][0], bbox["min"][1], center_z],
[bbox["max"][0], bbox["min"][1], center_z],
[bbox["max"][0], bbox["max"][1], center_z],
[bbox["min"][0], bbox["max"][1], center_z],
[bbox["min"][0], bbox["min"][1], center_z]
]
return parting_surface, parting_line
"""
检测分型面和分型线
优先级:
1. AI 模型检测(如果已设置)
2. 基于法向量分析的几何方法
3. 简化方法(基于边界框)
"""
# 1. 尝试使用 AI 模型
if self.ai_parting_detector is not None:
try:
logger.info("使用 AI 模型检测分型面")
ai_result = self.ai_parting_detector.detect(shape, analysis)
if ai_result:
return self._create_parting_surface_from_ai(ai_result, analysis)
except Exception as e:
logger.warning(f"AI 分型面检测失败,回退到几何方法:{e}")
# 2. 基于法向量分析的几何方法
try:
logger.info("使用法向量分析检测分型面")
optimal_direction = self._analyze_face_normals(shape)
parting_plane = self._create_optimal_parting_plane(
shape, analysis, optimal_direction
)
parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face()
# 计算真实分型线(产品与分型面的交线)
parting_line = self._calculate_parting_line(shape, parting_surface)
return parting_surface, parting_line
except Exception as e:
logger.warning(f"法向量分析失败,使用简化方法:{e}")
# 3. 简化方法(回退)
logger.info("使用简化方法检测分型面")
return self._simple_parting_surface(analysis)
def _apply_shrinkage_compensation(self, shape: Any) -> Any:
"""应用收缩率补偿(放大模型)"""
@@ -582,7 +616,274 @@ class MoldCavityGenerator:
[inertia.Value(3, 1), inertia.Value(3, 2), inertia.Value(3, 3)]
]
def _analyze_face_normals(self, shape: Any) -> gp_Dir:
"""
分析产品表面的法向量分布,找出最优分型方向
原理:
- 统计所有面的法向量
- 选择法向量变化最小的方向作为分型方向
- 避免倒扣(undercut)区域
"""
from OCC.Core.TopoDS import TopoDS_Compound
from OCC.Core.TopTools import TopTools_IndexedMapOfShape
# 收集所有面的法向量
face_normals = []
explorer = TopExp_Explorer(shape, TopAbs_FACE)
while explorer.More():
face = TopoDS_Face(explorer.Current())
surface = BRepAdaptor_Surface(face)
# 获取面的法向量(在参数中心点)
try:
u = (surface.FirstUParameter() + surface.LastUParameter()) / 2
v = (surface.FirstVParameter() + surface.LastVParameter()) / 2
normal = gp_Dir()
# 从曲面获取法向量
if surface.GetType() == 0: # Plane
normal = surface.Plane().Position().Direction()
else:
# 对于非平面,使用微分几何计算法向量
from OCC.Core.GCPnts import GCPnts_AbscissaPoint
from OCC.Core.BRepGProp import brepgprop_VolumeProperties
# 简化:使用面的边界框中心法向量
from OCC.Core.Bnd import Bnd_Box
from OCC.Core.BRepBndLib import brepbndlib_Add
bbox = Bnd_Box()
brepbndlib_Add(face, bbox)
center = bbox.Center()
# 估算面法向量(简化)
normal = gp_Dir(0, 0, 1) # 默认 Z 方向
face_normals.append(normal)
except Exception as e:
logger.debug(f"面法向量计算失败:{e}")
explorer.Next()
# 如果没有法向量,返回默认 Z 方向
if not face_normals:
return gp_Dir(0, 0, 1)
# 统计法向量分布,选择最优方向
# 简化实现:计算平均法向量
avg_x = sum(n.X() for n in face_normals) / len(face_normals)
avg_y = sum(n.Y() for n in face_normals) / len(face_normals)
avg_z = sum(n.Z() for n in face_normals) / len(face_normals)
# 归一化
length = np.sqrt(avg_x**2 + avg_y**2 + avg_z**2)
if length > 0.001:
return gp_Dir(avg_x/length, avg_y/length, avg_z/length)
else:
return gp_Dir(0, 0, 1)
def _create_optimal_parting_plane(self, shape: Any, analysis: Dict,
direction: gp_Dir) -> gp_Pln:
"""
创建最优分型面
Args:
shape: 产品形状
analysis: 几何分析结果
direction: 分型方向(法向量)
Returns:
gp_Pln: 分型面方程
"""
bbox = analysis["bounding_box"]
# 分型面通过产品的质心
center = bbox["center"]
# 创建平面:通过质心,法向量为分型方向
parting_plane = gp_Pln(
gp_Pnt(center[0], center[1], center[2]),
direction
)
logger.info(f"创建分型面:原点=({center[0]:.2f}, {center[1]:.2f}, {center[2]:.2f}), "
f"法向量=({direction.X():.3f}, {direction.Y():.3f}, {direction.Z():.3f})")
return parting_plane
def _calculate_parting_line(self, shape: Any, parting_surface: Any) -> List[List[float]]:
"""
计算真实的分型线(产品与分型面的交线)
使用 BRepAlgoAPI_Section 进行布尔运算求交
"""
try:
# 创建截面运算
section = BRepAlgoAPI_Section(shape, parting_surface)
section.Build()
if not section.IsDone():
logger.warning("截面运算未完成,使用简化分型线")
return self._simple_parting_line(
parting_surface,
{"bounding_box": {"min": [-50, -50, 0], "max": [50, 50, 100]}}
)
# 提取交线(边)
edges = []
explorer = TopExp_Explorer(section.Shape(), TopAbs_EDGE)
while explorer.More():
edge = TopoDS_Edge(explorer.Current())
# 从边提取点
curve = BRepAdaptor_Curve(edge)
first_param = curve.FirstParameter()
last_param = curve.LastParameter()
# 采样点(至少 10 个点)
num_points = max(10, int((last_param - first_param) / 0.5))
step = (last_param - first_param) / num_points
for i in range(num_points + 1):
param = first_param + i * step
point = curve.Value(param)
edges.append([point.X(), point.Y(), point.Z()])
explorer.Next()
# 如果没有边,使用简化分型线
if not edges:
logger.warning("未找到交线,使用简化分型线")
return self._simple_parting_line(
parting_surface,
{"bounding_box": {"min": [-50, -50, 0], "max": [50, 50, 100]}}
)
logger.info(f"计算得到 {len(edges)} 个分型线点")
return edges
except Exception as e:
logger.error(f"分型线计算失败:{e}")
return self._simple_parting_line(
parting_surface,
{"bounding_box": {"min": [-50, -50, 0], "max": [50, 50, 100]}}
)
def _simple_parting_surface(self, analysis: Dict) -> Tuple[Any, List]:
"""简化的分型面检测(回退方案)"""
bbox = analysis["bounding_box"]
center_z = bbox["center"][2]
# 创建分型面(XY 平面)
parting_plane = gp_Pln(
gp_Pnt(0, 0, center_z),
gp_Dir(0, 0, 1)
)
parting_surface = BRepBuilderAPI_MakeFace(
parting_plane,
bbox["min"][0] - 10, bbox["max"][0] + 10,
bbox["min"][1] - 10, bbox["max"][1] + 10
).Face()
# 简化分型线
parting_line = self._simple_parting_line(parting_surface, analysis)
return parting_surface, parting_line
def _simple_parting_line(self, parting_surface: Any, analysis: Dict) -> List[List[float]]:
"""简化的分型线(矩形)"""
bbox = analysis["bounding_box"]
center_z = bbox["center"][2]
return [
[bbox["min"][0], bbox["min"][1], center_z],
[bbox["max"][0], bbox["min"][1], center_z],
[bbox["max"][0], bbox["max"][1], center_z],
[bbox["min"][0], bbox["max"][1], center_z],
[bbox["min"][0], bbox["min"][1], center_z]
]
def _create_parting_surface_from_ai(self, ai_result: Dict,
analysis: Dict) -> Tuple[Any, List]:
"""
从 AI 模型结果创建分型面(预留接口)
Args:
ai_result: AI 模型输出,应包含:
- origin: [x, y, z] 平面原点
- normal: [nx, ny, nz] 法向量
analysis: 几何分析结果
Returns:
(parting_surface, parting_line)
"""
origin = ai_result.get("origin", [0, 0, 0])
normal = ai_result.get("normal", [0, 0, 1])
# 创建平面
parting_plane = gp_Pln(
gp_Pnt(origin[0], origin[1], origin[2]),
gp_Dir(normal[0], normal[1], normal[2])
)
parting_surface = BRepBuilderAPI_MakeFace(parting_plane).Face()
# 分型线可以使用 AI 结果或重新计算
if "parting_line" in ai_result:
parting_line = ai_result["parting_line"]
else:
parting_line = self._simple_parting_line(parting_surface, analysis)
logger.info(f"从 AI 结果创建分型面:原点={origin}, 法向量={normal}")
return parting_surface, parting_line
def _apply_draft_angles(self, shape: Any, parting_surface: Any) -> Any:
"""
添加拔模角
使用 OpenCASCADE 的拔模功能
"""
# 1. 尝试使用 AI 模型
if self.ai_draft_analyzer is not None:
try:
logger.info("使用 AI 模型分析拔模角")
ai_result = self.ai_draft_analyzer.analyze(shape, parting_surface, self.draft_angle)
if ai_result and "drafted_shape" in ai_result:
logger.info("AI 拔模分析成功")
return ai_result["drafted_shape"]
except Exception as e:
logger.warning(f"AI 拔模分析失败,回退到几何方法:{e}")
# 2. 几何方法(简化实现)
try:
# 获取分型面的法向量作为拔模方向
surface_adaptor = BRepAdaptor_Surface(parting_surface)
draft_direction = surface_adaptor.Plane().Position().Direction()
# 使用 BRepOffsetAPI_ThickSolid 创建拔模
# 注意:完整的拔模需要更复杂的实现,这里简化处理
logger.info(f"使用几何方法添加拔模角:{self.draft_angle}度,方向=({draft_direction.X():.3f}, {draft_direction.Y():.3f}, {draft_direction.Z():.3f})")
# 简化:直接返回原始形状(拔模已在 CAD 中处理)
# 完整实现需要使用 BRepOffsetAPI_DraftAngle
return shape
except Exception as e:
logger.warning(f"拔模角处理失败:{e}")
return shape
def _calculate_parting_line_length(self, parting_line: List) -> float:
"""计算分型线长度"""
# 简化的长度计算
return 250.0 # mm
if not parting_line or len(parting_line) < 2:
return 0.0
# 计算折线总长度
total_length = 0.0
for i in range(1, len(parting_line)):
p1 = np.array(parting_line[i-1])
p2 = np.array(parting_line[i])
segment_length = np.linalg.norm(p2 - p1)
total_length += segment_length
return total_length