ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PythonOCC倒角倒圆实战:二维草图与三维实体API详解

PythonOCC倒角倒圆实战:二维草图与三维实体API详解 做CAD自动化、参数化建模、几何算法或者哪怕只是想快速生成一个带圆角的零件模型来配合后续仿真、渲染甚至数学建模项目PythonOCC都是绕不开的选择。它本质上是OpenCASCADEOCCT这个工业级几何内核的Python绑定可以直接用Python操作BRep精确建模的数据结构。今天聊一个基础但特别实用的功能倒角Chamfer和倒圆Fillet并且把二维草图和三维实体两个场景分开讲清楚。很多人第一次用PythonOCC做倒角倒圆时要么找不到对应的API要么API用了但Build失败最后只能干瞪眼。这篇文章会把二维和三维的实现代码、参数逻辑、常见坑一次性过完适合刚接触PythonOCC、或者已经在写几何算法但被倒角倒圆卡住的朋友直接参考。1. 倒角倒圆为什么是建模里的“高频动作”1.1 从去毛刺到减应力倒角倒圆到底解决什么问题倒角倒圆在不同行业里有不同的叫法但本质都是对模型的棱边或拐角做一次“修剪”或“过渡”。二维场景里比如一块钣金件的轮廓直角拐点容易在冲压时产生应力集中也容易刮手所以工程图纸上基本都会标注倒圆角或倒角三维场景里更明显机械零件上的锐边必须做倒角一方面是为了装配时好导入、不划伤配合面另一方面是热处理和表面处理时不容易在尖角处产生裂纹。从几何建模角度看倒角倒圆也是在把“理想几何”往“可制造几何”上靠的一个关键步骤。一个长方体如果不做任何处理所有棱边都是尖锐的这在数学上很干净但实际产品几乎不会这么做。所以无论是做CAD二次开发还是做参数化建模倒角倒圆都是必须掌握的基本功。PythonOCC里对应两套主力API二维用BRepFilletAPI_MakeFillet2d三维用BRepFilletAPI_MakeFillet圆角和BRepFilletAPI_MakeChamfer倒角。搞清楚这几个类的区别后面做复杂模型就不会乱。1.2 PythonOCC里两套APIFillet2d vs Fillet/Chamfer很多初学者会纠结一个问题二维倒圆角和三维倒圆角到底是不是同一个函数答案是否定的。二维环境里目标对象是Wire或者Face操作的是顶点Vertex本质是把两条相邻边的交角替换成一段圆弧三维环境里目标对象是Solid操作的是边Edge本质是在两个相邻面之间生成一个过渡曲面。拿生活里的例子类比二维倒圆像是给折线拐角“磨个圆”结果还是一条平面曲线三维倒圆则是给砖块的棱“刮一道”结果是生成一个光滑的过渡曲面。正因为几何维度不同OpenCASCADE才把API拆成了两个方向。实际操作中很多人习惯先在二维草图里做好轮廓圆角再拉伸成三维实体这样三维部分只需要处理实体棱边的倒角倒圆逻辑会清晰很多。这也是CAD软件里最常见的建模思路。1.3 环境准备安装pythonocc-core的版本坑PythonOCC的安装不算复杂但版本坑一定要提。它目前的主线版本是7.x对应OpenCASCADE 7.xPython绑定模块名统一从OCC.Core开始。直接pip install pythonocc-core通常不会成功因为预编译包很少发布到PyPI所以更靠谱的做法是用condaconda create -n occ python3.10 conda activate occ conda install -c conda-forge pythonocc-core我实测在conda-forge渠道可以正常安装装完以后跑一句验证from OCC.Core.gp import gp_Pnt print(gp_Pnt(0, 0, 0))能输出坐标信息就说明环境OK。要注意Python版本别乱升到太新pythonocc-core的预编译包对不同Python版本支持有滞后我自己用3.10一直很稳3.11以上偶尔会碰到依赖冲突。2. 二维草图中的倒角倒圆从Wire到圆角轮廓2.1 先说清楚二维模型的拓扑Edge、Wire和Vertex二维建模在OpenCASCADE里并不是一个独立模块而是复用BRep拓扑结构。一条线段是一个Edge多个Edge首尾相连成一个WireWire可以围成一个Face。倒角倒圆发生在Vertex上也就是两条Edge的公共交点但API操作的对象却是整个Wire或Face这点容易让人困惑。实际写代码的时候可以先构造四条线段组成一个矩形Wire然后把这个Wire交给BRepFilletAPI_MakeFillet2d去处理。这个类会扫描Wire里的所有Vertex对每个Vertex按你指定的半径做圆角替换。它返回的Shape仍然是一个Wire只不过原来的尖角位置被换成了圆弧边。这里有一个细节如果直接通过BRepBuilderAPI_MakeWire把四条边串起来得到的Wire内部拓扑是正确的但拿TopExp_Explorer去遍历Wire时默认会先遍历Edge不会直接给你Vertex列表。需要先遍历Edge再从每条Edge上取第一个Vertex才能按顺序拿到所有角点。顺序不对会导致圆角加错位置这是我第一次写时踩过的坑。2.2 用MakeFillet2d给矩形四角加圆角下面是一个可以直接运行的二维倒圆角示例。我建一个100×50的矩形四个角做R10圆角from OCC.Core.gp import gp_Pnt from OCC.Core.BRepBuilderAPI import ( BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeWire, ) from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeFillet2d from OCC.Core.TopoDS import topods from OCC.Core.TopExp import TopExp_Explorer from OCC.Core.TopAbs import TopAbs_VERTEX from OCC.Core.BRepTools import BRepTools_WireExplorer p1 gp_Pnt(0, 0, 0) p2 gp_Pnt(100, 0, 0) p3 gp_Pnt(100, 50, 0) p4 gp_Pnt(0, 50, 0) e1 BRepBuilderAPI_MakeEdge(p1, p2).Edge() e2 BRepBuilderAPI_MakeEdge(p2, p3).Edge() e3 BRepBuilderAPI_MakeEdge(p3, p4).Edge() e4 BRepBuilderAPI_MakeEdge(p4, p1).Edge() wire BRepBuilderAPI_MakeWire(e1, e2, e3, e4).Wire() # 遍历闭合线框拿到所有角点 vertices [] wexp BRepTools_WireExplorer(wire) while wexp.More(): edge topods.Edge(wexp.Current()) vexp TopExp_Explorer(edge, TopAbs_VERTEX) if vexp.More(): vertices.append(topods.Vertex(vexp.Current())) wexp.Next() fillet2d BRepFilletAPI_MakeFillet2d(wire) for v in vertices: fillet2d.AddFillet(v, 10.0) fillet2d.Build() rounded_wire fillet2d.Shape()这段代码执行完之后rounded_wire就是一个四角为R10圆弧的闭合线框。如果想把它变成面再拉伸可以用BRepBuilderAPI_MakeFace包一层然后再用BRepPrimAPI_MakePrism拉伸。不过要注意MakeFillet2d传入的Wire必须是闭合且无自交的否则Build阶段会报错。提示AddFillet的第一个参数是TopoDS_Vertex不是坐标点也不是Edge。如果你想对指定某个角做圆角不要用坐标去匹配直接用WireExplorer遍历到的Vertex对象最安全。关于二维倒角BRepFilletAPI_MakeFillet2d里也有AddChamfer方法接受顶点和一个或两个距离参数。签名在不同小版本里略有差异使用前建议先跑一下help定位当前版本的参数形式。我只做了AddFillet的自动化遍历AddChamfer的用法类似区别在于你需要额外指定倒角距离。2.3 二维倒角的坑规则比圆角更严格二维倒角在几何上比圆角更麻烦一点。圆角只需要保证圆弧半径不大于相邻两条边的长度倒角则要看切掉的那段距离是否在两条边的有效范围内。比如一个10mm长的短边你要做5mm倒角沿着两条边各量5mm理论上刚好到顶点但如果两条边的夹角很小切出来的倒角面会非常靠近相邻边的端点Build经常失败。我实际项目里的处理方式是把倒角参数做成一个独立的配置项Build失败就自动把距离值乘以0.8再试一次。如果连续三次失败就说明这个几何位置本身不适合做倒角需要人工介入。这种“降级重试”思路在批量生成零件时非常救命不然一个文件报错就中断整个流程。3. 三维实体上的倒角与倒圆API实测与参数逻辑3.1 MakeFillet沿一条边生成圆角过渡面三维圆角的入口是BRepFilletAPI_MakeFillet它作用在Solid上通过Add方法指定半径和边。半径本质上是圆角过渡面在垂直截面上的圆弧半径半径越大过渡面越宽但也会吃掉更多相邻面的面积所以半径不能无限大。最简单的例子对一个长方体顶面的四条边做R3圆角from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeFillet from OCC.Core.TopExp import TopExp_Explorer from OCC.Core.TopAbs import TopAbs_EDGE from OCC.Core.TopoDS import topods from OCC.Core.BRepAdaptor import BRepAdaptor_Curve box BRepPrimAPI_MakeBox(100, 50, 20).Shape() # 筛选出顶面z20上的边 edges [] exp TopExp_Explorer(box, TopAbs_EDGE) while exp.More(): edge topods.Edge(exp.Current()) adaptor BRepAdaptor_Curve(edge) lo adaptor.FirstParameter() hi adaptor.LastParameter() mid adaptor.Value((lo hi) / 2.0) if abs(mid.Z() - 20.0) 1e-6: edges.append(edge) exp.Next() fillet BRepFilletAPI_MakeFillet(box) for e in edges: fillet.Add(3.0, e) fillet.Build() rounded_box fillet.Shape()这里我用了BRepAdaptor_Curve来取边的中点坐标目的是筛选出顶边。你也可以用BRep_Tool.Curve拿曲线的几何信息但BRepAdaptor_Curve更省事直接给参数范围和中点值。3.2 MakeChamfer等距与不等距倒角的参数逻辑三维倒角入口是BRepFilletAPI_MakeChamfer。它的Add方法有两个常见重载一个是Add(distance, edge)表示沿相邻两个面各切除相同距离的等距倒角另一个是Add(distance1, distance2, edge)表示两个相邻面上切除距离不同。等距倒角是45度斜切不等距倒角更灵活适合用在要求不对称的场合。沿用上面长方体现在对底面四条边做一个不对称倒角沿底面方向切2mm沿侧面方向切4mmfrom OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeChamfer # 筛选底面z0上的边 bottom_edges [] exp TopExp_Explorer(rounded_box, TopAbs_EDGE) while exp.More(): edge topods.Edge(exp.Current()) adaptor BRepAdaptor_Curve(edge) lo adaptor.FirstParameter() hi adaptor.LastParameter() mid adaptor.Value((lo hi) / 2.0) if abs(mid.Z()) 1e-6: bottom_edges.append(edge) exp.Next() chamfer BRepFilletAPI_MakeChamfer(rounded_box) for e in bottom_edges: chamfer.Add(2.0, 4.0, e) chamfer.Build() final_shape chamfer.Shape()这里有一个我在实际项目中反复踩的坑对同一个Solid先做圆角再做倒角不能把两个操作混在同一个对象里。BRepFilletAPI_MakeFillet和BRepFilletAPI_MakeChamfer虽然都接收一个初始Shape但它们是在初始Shape的拓扑基础上添加过渡特征并不是叠加式修改。所以先圆角后倒角倒角操作的输入应该是圆角后的Shape而不是原始的box。上面代码就是先对box做圆角得到rounded_box再对rounded_box做倒角链条是对的。3.3 多条边一起处理Add循环的顺序与效率当需要给很多边倒角倒圆时思路是先把所有Edge对象收集进一个列表然后循环Add最后统一Build。不要在Add一条边之后立刻Build一次这样会反复执行重复的拓扑计算效率低而且很容易因为前一次Build改变了形状导致后一次Add找不到边。批量Add时还有个隐藏问题多条边如果共享同一个顶点或同一条边半径过大时会出现几何退化。比如一个正方体如果对同一个顶点的三条相交边都做大半径圆角那这个顶点附近的三个圆角面会互相冲突OpenCASCADE有时能自动裁切有时直接报错。所以批量操作前最好有一个参数范围检查粗略估计一下最小边长然后控制半径不超过最短边的一半。4. 一个完整示例从二维圆角矩形到三维零件加工4.1 案例目标一块100×50×20的底板前面把二维和三维分开讲了这一节用一个完整案例串起来生成一块100×50×20的底板先做二维轮廓的R10圆角再拉伸成体最后对顶面四周做R3圆角底面做2mm/4mm不对称倒角。这样既体现了二维草图级别的倒角倒圆也展示了三维实体级别的过渡特征实际工作中这种“二维定轮廓、三维做棱边”的组合非常常见。需要说明的是二维圆角矩形拉伸之后底板上表面和下表面的边界已经是圆弧过渡不是直角矩形所以三维圆角/倒角操作的目标是拉伸体侧面的一些棱边。为了让代码清晰我会在二维圆角完成之后单独拉伸成一个实体再在那个实体上选择顶面和底面的边进行操作。4.2 第一步生成二维圆角矩形线框这一步在前面已经写过代码现在把它封装成一个函数方便后续调用def make_rounded_rect_wire(cx, cy, width, height, radius): x0 cx - width / 2.0 y0 cy - height / 2.0 p1 gp_Pnt(x0, y0, 0) p2 gp_Pnt(x0 width, y0, 0) p3 gp_Pnt(x0 width, y0 height, 0) p4 gp_Pnt(x0, y0 height, 0) e1 BRepBuilderAPI_MakeEdge(p1, p2).Edge() e2 BRepBuilderAPI_MakeEdge(p2, p3).Edge() e3 BRepBuilderAPI_MakeEdge(p3, p4).Edge() e4 BRepBuilderAPI_MakeEdge(p4, p1).Edge() wire BRepBuilderAPI_MakeWire(e1, e2, e3, e4).Wire() vertices [] wexp BRepTools_WireExplorer(wire) while wexp.More(): edge topods.Edge(wexp.Current()) vexp TopExp_Explorer(edge, TopAbs_VERTEX) if vexp.More(): vertices.append(topods.Vertex(vexp.Current())) wexp.Next() fillet2d BRepFilletAPI_MakeFillet2d(wire) for v in vertices: fillet2d.AddFillet(v, radius) fillet2d.Build() return fillet2d.Shape()这个函数的好处是参数化后续想改成80×40、R5只要改输入参数就行。对做模板化零件很友好。4.3 第二步拉伸成三维实体得到圆角矩形的Wire后先转成Face再用BRepPrimAPI_MakePrism沿Z轴拉伸20mmfrom OCC.Core.BRepBuilderAPI import BRepBuilderAPI_MakeFace from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakePrism from OCC.Core.gp import gp_Vec rounded_wire make_rounded_rect_wire(0, 0, 100, 50, 10) face BRepBuilderAPI_MakeFace(rounded_wire).Face() solid BRepPrimAPI_MakePrism(face, gp_Vec(0, 0, 20)).Shape()这里一个容易忽略的点是MakeFace要求Wire是闭合的二维圆角后的Wire依然是闭合所以没问题。拉伸出来的Solid顶部和底部都是圆角矩形面侧面由平面和圆柱面拼接而成。到这个阶段零件的“二维倒圆角”已经体现出来了顶面和底面的轮廓是圆角的侧面在四个角上也是圆滑过渡的。4.4 第三步顶面圆角与底面倒角一次性构建接下来对这个Solid继续做三维圆角和倒角。因为solid的顶面已经是圆角矩形顶部棱边中既包含直线边也包含圆弧边如果对它们都统一加R3圆角会改变顶部轮廓的圆弧形状导致几何冲突。为了避免这个问题我选择筛选出底面的四条边做不对称倒角再筛选出侧面上的竖直棱边做小圆角。筛选底面边依然用z坐标接近0来匹配筛选竖直棱边则用方向向量判断这里以边的中点x坐标处于轮廓角点附近为例简化处理。实际项目中建议根据边方向与Z轴平行来判断竖直边from OCC.Core.gp import gp_Dir bottom_edges [] vertical_edges [] exp TopExp_Explorer(solid, TopAbs_EDGE) while exp.More(): edge topods.Edge(exp.Current()) adaptor BRepAdaptor_Curve(edge) lo adaptor.FirstParameter() hi adaptor.LastParameter() mid adaptor.Value((lo hi) / 2.0) if abs(mid.Z()) 1e-6: bottom_edges.append(edge) tangent gp_Dir(adaptor.DN(lo, 1)) # 一阶导作为切向 if abs(abs(tangent.Z()) - 1.0) 1e-6: vertical_edges.append(edge) exp.Next()拿到边列表后分别用MakeChamfer和MakeFillet构建chamfer BRepFilletAPI_MakeChamfer(solid) for e in bottom_edges: chamfer.Add(2.0, 4.0, e) chamfer.Build() solid_with_chamfer chamfer.Shape() fillet BRepFilletAPI_MakeFillet(solid_with_chamfer) for e in vertical_edges: fillet.Add(3.0, e) fillet.Build() final_part fillet.Shape()这里我调整了顺序先倒角后圆角因为底面被倒角后不会影响顶部和侧面的竖直棱边。如果先圆角后倒角竖直棱边的端点位置会被底部倒角波及容易冲突。做完之后final_part就是一个工业感很强的底板零件二维轮廓圆角、三维棱边圆角和倒角全都体现出来了。5. 常见报错与排查技巧实录5.1 “Fillet failed”报错先查这四件事BRepFilletAPI_MakeFillet和MakeChamfer在Build失败时最常见的错误信息就是Fillet failed或BRep_API: command not done。这类报错通常由四类原因引起按概率排序半径/距离太大超过了相邻面的有效范围选择的边是退化边或已经被其他特征修改过多条边之间存在共享顶点特征互相打架输入Shape本身不是Solid而是一个Face或ShellAPI不接受。排查时我一般先用BRepCheck_Analyzer检查输入Shape是否有拓扑问题再逐步缩小半径看是否恢复正常。如果半径减半后成功说明是参数范围问题如果仍然失败大概率是边选择有问题。5.2 顶点和边别搞混拓扑类型决定成败二维倒圆角时AddFillet的第一个参数是Vertex三维倒角时Add的第一个参数是距离第二个参数才是Edge。经常有人把二维的用法带到三维给MakeFillet传了一个Vertex结果报类型错误。还有人在二维里把半径当成第一个参数传给AddFillet也会报错。我在写自动化脚本时会刻意封装成两套函数一套处理Wire一套处理Solid从接口上避免传参搞混。如果项目里有大量几何操作建议做一层类型校验用isinstance或者检查TopoDS_Shape的Type提前拦截错误。5.3 看不到结果怎么办显示和导出STEP的快路径PythonOCC的显示模块在不同环境里表现差异很大纯命令行下直接init_display会弹不出窗口。我通常用两种方式验证结果一是导出STEP文件用FreeCAD或CAD助手打开看二是在Jupyter里用WebGL渲染。导出STEP的代码很简单from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs writer STEPControl_Writer() writer.Transfer(final_part, STEPControl_AsIs) writer.Write(bottom_plate.step)这样导出的STEP可以被主流CAD软件识别方便检查倒角倒圆效果。如果只是想快速看几何拓扑数量也可以通过TopExp_Explorer统计Edge和Face的数量判断Build前后是否符合预期。5.4 倒角倒圆放在建模流程的哪个位置直接影响成功率建模流程对倒角倒圆的影响非常大。我的习惯是先把大的形状特征做完最后再统一做倒角倒圆并且遵循“先小后大、先远后近”的原则先做半径/距离较小的特征再做较大的特征先做不影响后续选边的特征比如先做内部的孔口倒角再做外轮廓棱边倒角尽量避免在倒角后的形状上继续做依赖原始边的操作。原因很简单倒角倒圆本质是“破坏性”操作它会删除原始棱边并生成新的过渡面如果后续还要基于原始棱边选择其他特征往往找不到边。所以参数化流程设计时倒角倒圆通常是作为最后一道几何工序存在之后只做显示、导出、网格划分这类下游动作。我自己在实际项目里会把半径、距离这些参数全部提成变量放在一个字典里统一管理。这样批量生成系列零件时只需改一组参数模型和倒角倒圆结果就会跟着变不用手改几何代码。这个习惯帮我在做多个规格的零件模板时省了大量时间强烈建议你也试试。
返回列表