☰
PythonOCC倒角倒圆实战:二维草图与三维实体API详解
2026/10/5 4:42:29 网站建设 项目流程

做CAD自动化、参数化建模、几何算法,或者哪怕只是想快速生成一个带圆角的零件模型来配合后续仿真、渲染甚至数学建模项目,PythonOCC都是绕不开的选择。它本质上是OpenCASCADE(OCCT)这个工业级几何内核的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里两套API:Fillet2d vs Fillet/Chamfer

很多初学者会纠结一个问题:二维倒圆角和三维倒圆角到底是不是同一个函数?答案是否定的。二维环境里,目标对象是Wire或者Face,操作的是顶点(Vertex),本质是把两条相邻边的交角替换成一段圆弧;三维环境里,目标对象是Solid,操作的是边(Edge),本质是在两个相邻面之间生成一个过渡曲面。

拿生活里的例子类比,二维倒圆像是给折线拐角“磨个圆”,结果还是一条平面曲线;三维倒圆则是给砖块的棱“刮一道”,结果是生成一个光滑的过渡曲面。正因为几何维度不同,OpenCASCADE才把API拆成了两个方向。实际操作中,很多人习惯先在二维草图里做好轮廓圆角,再拉伸成三维实体,这样三维部分只需要处理实体棱边的倒角倒圆,逻辑会清晰很多。这也是CAD软件里最常见的建模思路。

1.3 环境准备:安装pythonocc-core的版本坑

PythonOCC的安装不算复杂,但版本坑一定要提。它目前的主线版本是7.x,对应OpenCASCADE 7.x,Python绑定模块名统一从OCC.Core开始。直接pip install pythonocc-core通常不会成功,因为预编译包很少发布到PyPI,所以更靠谱的做法是用conda:

conda create -n occ python=3.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首尾相连成一个Wire,Wire可以围成一个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() # 筛选出顶面(z=20)上的边 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,沿侧面方向切4mm:

from OCC.Core.BRepFilletAPI import BRepFilletAPI_MakeChamfer # 筛选底面(z=0)上的边 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轴拉伸20mm:

from 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或Shell,API不接受。

排查时我一般先用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 倒角倒圆放在建模流程的哪个位置,直接影响成功率

建模流程对倒角倒圆的影响非常大。我的习惯是先把大的形状特征做完,最后再统一做倒角倒圆,并且遵循“先小后大、先远后近”的原则:

  • 先做半径/距离较小的特征,再做较大的特征;
  • 先做不影响后续选边的特征,比如先做内部的孔口倒角,再做外轮廓棱边倒角;
  • 尽量避免在倒角后的形状上继续做依赖原始边的操作。

原因很简单,倒角倒圆本质是“破坏性”操作,它会删除原始棱边并生成新的过渡面,如果后续还要基于原始棱边选择其他特征,往往找不到边。所以参数化流程设计时,倒角倒圆通常是作为最后一道几何工序存在,之后只做显示、导出、网格划分这类下游动作。

我自己在实际项目里会把半径、距离这些参数全部提成变量,放在一个字典里统一管理。这样批量生成系列零件时,只需改一组参数,模型和倒角倒圆结果就会跟着变,不用手改几何代码。这个习惯帮我在做多个规格的零件模板时省了大量时间,强烈建议你也试试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询