目录
问题来源
CAM(计算机辅助制造)路径规划
在CAM软件中规划加工路径时,在粗加工阶段一般追求加工路径尽可能平滑高效,减少不必要的刀路弯折、跳刀。而当零件上存在的孔、槽等凹陷特征时,可能出现在粗加工阶段不必要的局部细小刀路,影响加工效率。如下图所示:
在精加工阶段,当零件上有孔、槽特征时,同样会出现不必要的跳刀,而当刀路连续运行时,加工效率更高。如以下图所示,蓝色的切削刀路被孔洞截断,进而出现较多红色的跳刀:
针对该问题,一个解决方向是,向刀具路径规划算法提出极高的要求——在路径规划阶段即可自动跳过此类特征。但该要求往往过于苛刻,甚至成为一个难以完备解决的难题。而另一个方向则是将此类凹陷的洞特征“补平”——即应用CAD的补面技术构造补洞面片,补洞后,将得到如下刀路:
“补”一个由N个边界围成的区域(Filling n-sided regions)是CAD领域的一个经典问题,针对由两条边界(2-sided)、三条边界(3-sided)、四条边界(4-sided)以及由N条边界(N-sided)围成的区域填充问题,已有充分的技术研究相关研究,并且已在商业三维建模引擎实现,例如ACIS中的Cover。
N边域解决方法
对于单个拓扑面上的N边域这一简单场景,可以直接基于拓扑关系填充或删除N边域(Open CASCADE 获取面的内外环线)。
而复杂场景下的N边域填充主要有以下三种方法:
- 细分方法(Subdivision)
通过递归细分初始多边形 P 0 P_0 P0,即由 P i − 1 P_{i-1} Pi−1求解 P i P_i Pi,到近似生成光滑曲面。处理N边孔洞时,需解决非四边形面的填充问题。 - 多面片法(Multiple Patches)
将N边域分割为三角形或四边形子区域,用标准曲面片填充,但需确保内部光滑性,其中解决子区域之间的连续性问题是一个挑战,如保持 C 1 , G 2 C^1, G^2 C1,G2连续。 - 单面片法(Single Patches)
直接使用单一曲面片填充N边域,避免多面片的光顺性问题,但面临非矩形参数域和形状控制的挑战。这种方法一般也叫做N边曲面(N-Sided Surface)。
如何进一步地定义N边曲面 N-Sided Surface?
N边曲面是一个由N条彼此相连的边界( E 0 , E 1 , E 2 , . . . , E n − 1 E_0, E_1, E_2, ..., E_{n-1} E0,E1,E2,...,En−1)围成的、可通过指定点或边并满足连续性要求约束地单张曲面,指定的约束条件可以是:
- 点约束:指定空间一个点,使N边面通过该点。进一步地,当点位于某曲面上时,可以约束通过该曲面上的点( G 0 G^0 G0)、与曲面上该点相切( G 1 G^1 G1)、与曲面上该点曲率半径相等( G 2 G^2 G2);
- 非边界约束:指定空间上的一条边,使得N边曲面通过该边,并满足连续性要求;
- 边界约束:当边属于相邻面的边界时,可以添加与相邻面的 G 0 / G 1 / G 2 G^0/G^1/G^2 G0/G1/G2连续性约束。
本文尝试通过Open CASCADE算法尝试构造N边曲面。
Open CASCADE 实现N边面
BRepFill_Filling
是 OCC 中实现N边曲面的接口,属于拓扑层面的算法,其底层核心求解算法由GeomPlate
模块提供。GeomPlate
模块提供用于构建板状(plate)曲面的高级曲面建模技术,除了可应用于N边曲面的生成之外,还可以用于过渡圆角曲面的构造。
这里不深入涉及GeomPlate
底层数学原理,仅简要介绍一下BRepFill_Filling
在应用层面的基本逻辑。
整体运行流程
BRepFill_Filling
本质上是构造一个约束NURBS曲面,约束类型可分为点约束、边约束。
算法整体运行过程如下图所示:
关键算法
连接约束边界的缝隙
自由面的作用是辅助构造边界约束中不完整的边界,不是约束的一部分。当边界约束的边界不封闭时,将以自由面为基础,在边界缝隙处构造一段曲线来缝合边界。如下图所示,红色表示约束边界、绿色则是根据自由面构造的缝合曲线段:
约束求解: 边界 + 非边界 + 点
对边界/非边界约束、点/线约束进行前处理,构造为Plate_Plate
约束数据,并进行求解。
其中,构造初始平面目的是为了接下来在平面上(或参数域内)处理点/线约束,初始曲面也可以由外部提供。算法构造的初始平面如下图灰色面所示:
从这里可以看推测,由于后续步骤包括约束边界向初始平面进行投影等操作,当投影所得多边形存在自相交时,算法将难以继续执行。即构造N边面时所选定的边界至少在某个投影方向上不存在自相交。
约束求解后得到的GeomPlate_Surface
是一个参数化曲面,还需进一步逼近为NURBS曲面。
薄板曲面逼近
逼近算法AdvApp2Var_ApproxAFunc2Var
以分段多项式逼近的方式,将约束求解所得薄板曲面GeomPlate_Surface
转换为NURBS曲面Geom_BSplineSurface
。
拓扑构造
逼近时,求解区域会适当大于输入的约束边界。构造拓扑面时,基于输入边界线在N边面上构造新的边界。效果如下图所示:
这里所谓新的边界是相对于N边面而言的,即在输入Edge的基础上,新增构造了一条位于N边面上的边界曲线BRep_CurveOnSurface
,串联所有的边界曲线即可确定N边曲面的外环。
应该注意,虽然在约束求解时,算法可自动缝补输入边界的缝隙。但在构造最终拓扑面时,并未恰当的处理缝隙的问题,当输入的约束边界不封闭,将得到拓扑异常的结果。因此,应仔细检查,确保输入边界是封闭的。
关于拓扑边界的概念可参考如下示意图(其中2D curve P2
可视为新构造的边界),详细可参考OCC关方文档。
由于N边面新构造边界来源于输入边界,二者存在天然的拓扑联系(具有相同的TopoDS_TShape
),因此后续可以轻松将N边曲面与输入边界所在曲面进行缝合。
运行效果
边界约束
以若干条相连边作为N边曲面的边界、与边所在的曲面保持
G
0
/
G
1
/
G
2
G^0/G^1/G^2
G0/G1/G2连续性要求。如下图所示,相同的边界(图中红色线框)、选择不同的相邻面,设置连续性为
G
1
G^1
G1,得到不同的N边曲面:
点约束(空间任意)
以空间中任意一点作为点约束,使得N边曲面通过指定点。如下图所示,不同位置的点所得到的N边曲面:
点约束(曲面上的点)
以曲面上一点为约束,使得N边曲面与点所在曲面保持
G
0
/
G
1
/
G
2
G^0/G^1/G^2
G0/G1/G2连续,如下图所示,:
鼠标所指曲面上的点与N边曲面分别为
G
0
/
G
1
/
G
2
G^0/G^1/G^2
G0/G1/G2连续。
曲面变形
通过空间点约束,实现曲面通过点变形,如下图所示:
曲面桥接
在两个面之间构造光滑衔接的过渡曲面
示例代码
const std::vector<TopoDS_Shape>& bnd_edges = ...;
BRepFill_Filling fill;
// 边界约束
for (size_t i = 1; i < bnd_edges.size(); i += 2)
{
TopoDS_Shape edge = bnd_edges[i - 1];
TopoDS_Shape face = bnd_edges[i];
if (TopAbs_EDGE == edge.ShapeType() && TopAbs_FACE == face.ShapeType())
{
// nothing to do
}
else if (TopAbs_EDGE == face.ShapeType() && TopAbs_FACE == edge.ShapeType())
{
std::swap(edge, face);
}
else
{
continue;
}
fill.Add(TopoDS::Edge(edge), TopoDS::Face(face), GeomAbs_G1);
}
// 非边界约束
const std::vector<TopoDS_Shape>& free_edges = ...;
for (const auto& fe : free_edges)
{
if (TopAbs_EDGE != fe.ShapeType())
{
continue;
}
TopoDS_Edge e = TopoDS::Edge(fe);
fill.Add(e, GeomAbs_G1, false);
}
// 面上点约束
const std::vector<TopoDS_Shape>& faces = ...;
for (const auto& fe : faces)
{
if (TopAbs_FACE != fe.ShapeType())
{
continue;
}
TopoDS_Face f = TopoDS::Face(fe);
Handle(Geom_Surface) surf = BRep_Tool::Surface(f);
if (!surf)
{
continue;
}
const double u = ...;
const double v = ...;
GeomAbs_Shape con;
switch (...)
{
case 0: con = GeomAbs_C0; break;
case 1: con = GeomAbs_G1; break;
case 2: con = GeomAbs_G2; break;
default: con = GeomAbs_G1; break;
}
fill.Add(u, v, f, con);
break;
}
// 自由点约束
gp_Pnt pnt = ...;
fill.Add(pnt);
fill.Build();
if (!fill.IsDone())
{
// "N边面构造错误:Build 异常";
}
TopoDS_Face result_face = fill.Face();
...
原文链接:https://blog.youkuaiyun.com/Mechanicoder/article/details/145809854