Files
cadSet/SimpleCADAPI/skills/simplecadapi/references/inverse_engineer/brep-reverse-engineering.md
T

762 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# STEP BREP 逆向工程方法
本文记录一种面向精确 BREP 的 STEP 逆向工作流。目标不是做出“看起来相似”的模型,而是恢复一个满足以下硬约束的可编辑参数模型:
1. 几何点集一致;
2. BREP 拓扑一致;
3. 解析曲面的参数一致;
4. 边界曲线和关键参数范围一致;
5. 模型能够稳定重建和回放。
当多种特征树都满足这些硬约束时,再用工程师的建模习惯筛选:优先对称、整数或标准尺寸、少量草图、稳定基准、清晰特征依赖和容易修改的参数。
## 1. 先区分五种“一致”
STEP 逆向中最容易犯的错误,是把不同层次的一致混为一谈。
### 1.1 外观一致
截图、网格或渲染结果相似。它只能用于快速发现大方向错误,不能用于验收。
截图看不出:
- 两个相邻面是否已合并;
- 一个圆柱面是整圆还是两个半圆;
- 隐藏孔、沉孔深度和内部台阶;
- 半径的微小差异;
- BREP 邻接关系;
- 曲面的参数原点和方向。
### 1.2 数值摘要一致
包围盒、体积、表面积、质心一致。这比外观更强,但仍不充分。不同实体可能碰巧拥有相同的体积和包围盒。
用途:
- 快速检查单位和坐标系;
- 定位哪个特征导致体积误差;
- 验证孔、槽、台阶的总体材料增减;
- 在迭代过程中排除明显错误。
### 1.3 几何点集一致
两个实体占据完全相同的空间。对实体可用双向布尔差集验证:
```text
A - B 的体积 = 0
B - A 的体积 = 0
```
这是几何一致的硬检查,但它不保证拓扑一致。例如一个完整圆柱面和两个半圆柱面可以表示相同点集。
### 1.4 BREP 拓扑一致
面、边、顶点数量相同,并且面—边—顶点邻接图同构。
仅比较数量还不够。两个模型都可能有 20 个面,但连接方式不同。可靠做法是给图节点添加几何标签,再检查图同构:
- 面标签:类型、面积、几何中心、半径、轴线、球心等;
- 边标签:类型、长度、几何中心、半径、轴线等;
- 顶点标签:三维坐标;
- 图边:Face—Edge 和 Edge—Vertex 关联。
### 1.5 参数化表示一致
同一个几何载体仍可能具有不同参数表示:
- 圆柱轴方向相反;
- 平面原点不同;
- 圆弧起始角不同;
- UV 范围平移或反向;
- 一条整圆边与两条半圆边;
- 相同孔通过不同特征顺序产生不同的曲面参数原点。
有些参数差异只是非唯一表示,不应误判为几何差异;有些则是建模历史的有效证据。必须结合设计语义判断。
## 2. 检查手段速查表
| 检查手段 | 最适合回答的问题 | 能提供的直觉 | 不能单独证明什么 |
|---|---|---|---|
| STEP 实体类型计数 | 文件主要由什么几何组成 | 是机械解析体、自由曲面还是混合模型 | 导入后的唯一拓扑数量 |
| BREP 有效性 | 文件能否作为可靠实体继续运算 | 是否存在开壳、坏边、容差问题 | 几何是否符合设计意图 |
| 唯一 Face/Edge/Vertex 数量 | 拓扑复杂度和分段程度 | 圆是否被拆弧、面是否被切分 | 邻接关系是否一致 |
| 拓扑出现次数 | 面如何重复引用边和顶点 | 是否像闭合流形、是否有非流形连接 | 两模型是否同构 |
| 包围盒 | 单位、整体尺寸和主方向 | 哪个方向可能是厚度或长度 | 内部孔槽和局部形状 |
| 体积/面积/质心 | 材料增减是否正确 | 漏掉的是大特征还是表面细节 | 几何点集和拓扑一致 |
| 解析面类型统计 | 特征家族是否正确 | 孔、倒角、圆角、自由曲面来源 | 每个面的具体位置和连接 |
| 面参数与包围盒 | 单个特征的尺寸和位置 | 半径、轴线、台阶、角度、深度 | 建模历史唯一解 |
| 边类型与端点 | 草图分段和切点 | 整圆/半圆、45 度线、圆角切线 | 隐藏面的完整形状 |
| 面-边-顶点邻接图 | BREP 连接结构是否一致 | 哪个局部特征造成分段差异 | 几何点集必然一致 |
| 双向布尔差集 | 两实体是否占据相同空间 | 几何已经完成,可转向拓扑检查 | 面分段和参数化一致 |
| 多视图截图 | 大方向和明显特征是否正确 | 最快发现轴向、孔位、台阶错误 | 精确尺寸、内部结构和拓扑 |
| 物理截面 | 隐藏结构和不同深度轮廓 | 沉孔、槽、壁厚、局部层级 | 未采样位置必然一致 |
| 截面 XOR | 候选差异出现在哪个区域 | 直接定位下一步应修改的特征 | 连续空间上的严格零差异 |
| UV/曲线参数范围 | 参数历史和特征顺序 | 工具从哪个基准面开始、何时被后续特征裁剪 | 非唯一坐标架必然相同 |
| 模型图回放 | 建模过程能否稳定复现 | 是否依赖交互状态或临时对象 | 回放结果与目标自动一致 |
推荐检查顺序是从便宜到昂贵:
```text
有效性和摘要
-> 解析几何清点
-> 多视图和少量关键截面
-> 面边参数与邻接
-> 候选构建
-> 双向差集
-> 图同构
-> 参数历史
-> 回放
```
不要一开始就对所有点做高密度分类,也不要一开始就逐个比较 STEP 文本实体。前者成本高且信息增量有限,后者会被导出器编号和非唯一参数化干扰。
## 3. 推荐的分阶段工作流
### 3.1 阶段 A:建立不可变基准
在写建模代码前,先从原始 STEP 导出一份机器可读报告。可以先用文本清点快速了解文件组成:
```bash
rg -o '^[[:space:]]*#[0-9]+[[:space:]]*=[[:space:]]*[A-Z0-9_]+' model.step \
| sed -E 's/.*=[[:space:]]*//' \
| sort \
| uniq -c \
| sort -nr
```
这能快速发现 `ADVANCED_FACE`、`PLANE`、`CYLINDRICAL_SURFACE`、`B_SPLINE_SURFACE_WITH_KNOTS` 等实体,但它只是 STEP 表示层清点。`EDGE_CURVE` 数量不一定等于内核导入后的唯一 Edge 数量;退化边、缝边和表示方式都会造成差异。最终基准必须来自导入后的 BREP。
机器可读报告至少包括:
- STEP 是否能读取;
- 转移根数量;
- BREP 是否有效;
- Solid、Shell、Face、Edge、Vertex 数量;
- 唯一拓扑数量和拓扑出现次数;
- 包围盒、体积、表面积、质心;
- 面类型统计;
- 边类型统计;
- 每个面的面积、UV 范围、解析曲面参数和邻接边;
- 每条边的类型、参数范围和邻接面。
唯一数量和出现次数必须分开:
```text
unique_edges BREP 中不同的边对象数量
edge_occurrences 遍历所有面时边被引用的总次数
```
在闭合流形实体中,一条普通边通常被两个面引用,因此出现次数通常接近唯一边数的两倍。
正式实现位于:
```text
src/simplecadapi/inverse_engineer/brep/inspect.py
```
Python API:
```python
from simplecadapi.inverse_engineer import brep
report = brep.inspect_step(path="model.step")
report.write_json("model-report.json")
```
CLI:
```bash
uv run simplecad-brep inspect model.step -o model-report.json
```
### 基础属性代码片段
```python
from OCP.BRepCheck import BRepCheck_Analyzer
from OCP.BRepGProp import BRepGProp
from OCP.BRepBndLib import BRepBndLib
from OCP.Bnd import Bnd_Box
from OCP.GProp import GProp_GProps
assert BRepCheck_Analyzer(shape).IsValid()
bbox = Bnd_Box()
BRepBndLib.AddOptimal_s(shape, bbox)
print("bbox", bbox.Get())
volume_props = GProp_GProps()
BRepGProp.VolumeProperties_s(shape, volume_props)
print("volume", volume_props.Mass())
print("center", volume_props.CentreOfMass().Coord())
area_props = GProp_GProps()
BRepGProp.SurfaceProperties_s(shape, area_props)
print("area", area_props.Mass())
```
### 3.2 阶段 B:恢复坐标语义
不要根据 `X/Y/Z` 名称直接假设左右、前后和高度。
应综合使用:
- 包围盒三个跨度;
- 主成分分析;
- 平面和圆柱轴线分布;
- 对称平面拟合;
- 正交视图和典型截面;
- 机械功能,例如孔轴通常对应装配或加工方向。
坐标语义判断错误会导致整个特征解释反转。比如一个方向上的 U 形截面可能是左右对称结构,也可能被错误解释为前后凹槽。
### 3.3 阶段 C:做解析几何清点
解析面类型是最有价值的“建模指纹”之一。
| 几何类型 | 常见来源 | 可恢复的信息 |
|---|---|---|
| Plane | 拉伸端面、切割平面、倒角 | 基准方向、台阶位置、厚度、角度 |
| Cylinder | 孔、轴、拉伸圆弧、直边圆角 | 半径、轴线、深度、圆弧分段 |
| Sphere | 三条圆角边交汇、球切割 | 圆角半径、特征交汇方式 |
| Torus | 圆弧轮廓的边圆角、环形过渡 | 主半径、次半径、圆角中心 |
| Cone | 锥孔、沉头、拔模 | 锥角、轴线、顶点 |
| BSpline | 放样、填充、自由曲面 | 次数、节点、控制点、连续性 |
例如:
```text
4 个 R1 Sphere
4 个 (R2, R1) Torus
14 个 R1 Cylinder
```
通常不是手工拼出的 22 个面,而是一次连续局部 R1 圆角的结果。解析面组合比截图更能揭示特征操作。
### 读取解析曲面参数
```python
from OCP.BRepAdaptor import BRepAdaptor_Surface
surface = BRepAdaptor_Surface(face, True)
kind = surface.GetType()
cylinder = surface.Cylinder()
print("radius", cylinder.Radius())
print("axis", cylinder.Axis().Direction().Coord())
print("origin", cylinder.Location().Coord())
sphere = surface.Sphere()
print("radius", sphere.Radius())
print("center", sphere.Location().Coord())
torus = surface.Torus()
print("major", torus.MajorRadius())
print("minor", torus.MinorRadius())
```
### 3.4 阶段 D:从平面和边恢复尺寸关系
不要只读包围盒。每个解析面的平面方程、轴线和边界端点能够恢复:
- 台阶高度;
- 壁厚;
- 孔深;
- 孔距;
- 对称位置;
- 45 度倒角;
- 圆角切点;
- 草图中的直线方程。
例如一组斜面端点满足:
```text
x + |z| = 1.25
```
这比“看起来大约 45 度”更强,说明草图很可能使用了对称 45 度约束。
### 读取边端点和类型
```python
from OCP.BRepAdaptor import BRepAdaptor_Curve
curve = BRepAdaptor_Curve(edge)
first = curve.FirstParameter()
last = curve.LastParameter()
p0 = curve.Value(first)
p1 = curve.Value(last)
print("type", curve.GetType())
print("start", p0.Coord())
print("end", p1.Coord())
if "Circle" in str(curve.GetType()):
circle = curve.Circle()
print("radius", circle.Radius())
print("center", circle.Location().Coord())
```
### 3.5 阶段 E:建立直观图像,但只让它承担合适的职责
推荐同时生成:
1. 等轴视图;
2. 三个正交视图;
3. 关键高度或轴向位置的占用截面;
4. 目标/候选叠加图;
5. XOR 差异图。
多视图适合发现:
- 坐标方向错误;
- 漏掉大特征;
- 孔位错误;
- 台阶方向错误;
- 圆角范围过大;
- 草图轮廓错误。
截面图适合发现:
- 内孔层级;
- 沉孔深度;
- 不同高度处的轮廓变化;
- 隐藏槽;
- 两实体的局部差异。
正式实现:
```text
src/simplecadapi/inverse_engineer/brep/render.py
src/simplecadapi/inverse_engineer/brep/slices.py
```
渲染图片需要可选依赖:
```bash
uv sync --extra inverse-engineer
```
Python API:
```python
from simplecadapi.inverse_engineer import brep
brep.render_step_views(step_path="candidate.step", output_path="candidate-views.png")
comparison = brep.compare_step_slices(
target_path="target.step",
candidate_path="candidate.step",
output_path="slice-overlay.png",
)
```
CLI:
```bash
uv run simplecad-brep render candidate.step candidate-views.png
uv run simplecad-brep slices target.step candidate.step slice-overlay.png
# 默认自动检查联合包围盒的 X/Y/Z 中心截面;特征边界应显式追加:
uv run simplecad-brep slices target.step candidate.step feature-slices.png \
--slice xz:-1.6 --slice xz:4.0 --slice xy:0
```
截面分类示例:
```python
from OCP.BRepClass3d import BRepClass3d_SolidClassifier
from OCP.TopAbs import TopAbs_IN, TopAbs_ON
from OCP.gp import gp_Pnt
classifier = BRepClass3d_SolidClassifier()
classifier.Load(shape)
classifier.Perform(gp_Pnt(x, y, z), 1e-8)
inside = classifier.State() in (TopAbs_IN, TopAbs_ON)
```
默认截面只用于快速建立直觉。逆向时应进一步选择穿过特征边界、台阶高度、沉孔肩部、对称面和疑似差异区域的物理截面。
注意:逐点 BREP 分类较慢。截面图是诊断工具,应控制分辨率;几何硬验收应使用布尔差集,而不是靠提高图片分辨率。
### 3.6 阶段 F:提出最小特征假设
先写出一个人类可读的候选特征树,不要马上编码大量细节。
好的候选通常包含:
- 一个主草图和一次拉伸;
- 对称约束或镜像;
- 少量孔、槽、台阶;
- 局部圆角和倒角;
- 清晰的加工顺序。
每个参数都应能由原始 BREP 的证据解释。例如:
```text
R1 来自 14 个 R1 圆柱、4 个 R1 球和 4 个次半径 R1 的圆环
R3.25 来自沉孔圆柱
R1.7 来自贯穿孔圆柱
X=3.25 来自台阶平面
Z=±6 来自对称孔轴
```
避免在没有证据时使用:
- 随机小偏移;
- 大量补偿布尔体;
- 任意样条;
- 网格拟合;
- 按最终面逐面拼壳。
### 3.7 阶段 G:按特征逐步落地
每一步都打印少量有意义的基准:
```python
print(
"ground opening fillet",
body.get_volume(),
len(body.get_faces()),
len(body.get_edges()),
)
```
推荐顺序:
1. 主轮廓;
2. 主拉伸;
3. 大型切除或并集;
4. 台阶;
5. 孔;
6. 圆角和倒角,或根据几何指纹调整到正确历史位置;
7. 输出 STEP;
8. 生成报告和截图;
9. 对比;
10. 只修改造成当前差异的特征。
不要一次添加所有特征。否则拓扑不一致时无法知道是哪一步造成的。
## 4. 从差异反推建模历史
### 4.1 面数少但几何相同:检查草图分段
典型情况:
```text
目标:每个孔壁由两个半圆柱面组成
候选:每个孔壁是一个完整圆柱面
```
二者几何点集相同,但拓扑不同。原因通常不是布尔算法随机拆面,而是草图边界不同:
```text
完整圆边拉伸 -> 一个圆柱侧面
两条半圆弧闭合拉伸 -> 两个半圆柱侧面
```
因此,拓扑差异常常能反推草图实体数量。
### 4.2 面数多且出现额外平面:检查错误的布尔拆分
把一个圆柱工具拆成左右两个半圆实体分别切除,会得到两个半圆柱面,但也会引入径向分隔平面。
这是一条重要经验:
> 想得到分段侧面,应在草图边上分段,而不是把实体工具分成多个带内部平面的实体。
### 4.3 几何和拓扑相同但 UV 不同:检查特征顺序
典型沉孔:
```text
方案 A:先沉孔,再从肩部开始切小孔
方案 B:先切贯穿小孔,再切大沉孔
```
两者最终几何可能相同,但小孔圆柱支撑面的参数原点不同。
如果目标小孔的圆柱原点位于入口平面,UV 还保留了被沉孔切掉的参数区间,这通常说明:
```text
先贯穿孔
再沉孔
```
特征顺序不仅影响工程可读性,也会影响 STEP 中保留的曲面参数。
### 4.4 球面和圆环面组合:检查三维圆角交汇
二维草图圆角后拉伸,常产生圆柱面;对三维实体的多条相交边做圆角,会进一步产生球面和圆环面。
因此:
- 只有圆柱圆角面:可能是二维圆角轮廓拉伸;
- 同时出现球面和圆环面:更像三维成组选边圆角;
- 球面半径和圆环次半径相同:通常直接给出圆角半径。
## 5. 严格验收方法
### 5.1 双向布尔差集
```python
from OCP.BRepAlgoAPI import BRepAlgoAPI_Cut
from OCP.BRepGProp import BRepGProp
from OCP.GProp import GProp_GProps
def cut_volume(a, b):
operation = BRepAlgoAPI_Cut(a, b)
operation.SetFuzzyValue(1e-9)
operation.Build()
props = GProp_GProps()
BRepGProp.VolumeProperties_s(operation.Shape(), props)
return props.Mass()
assert cut_volume(target, candidate) < 1e-9
assert cut_volume(candidate, target) < 1e-9
```
这证明实体点集相同,但仍需拓扑检查。
### 5.2 几何标记的邻接图同构
正式实现位于:
```text
src/simplecadapi/inverse_engineer/brep/compare.py
```
Python API:
```python
from simplecadapi.inverse_engineer import brep
comparison = brep.compare_steps(target_path="target.step", candidate_path="candidate.step")
assert comparison.hard_gate_passed
```
CLI:
```bash
uv run simplecad-brep compare target.step candidate.step -o comparison.json
```
报告应同时满足:
```text
same_geometric_point_set = true
geometry_labelled_incidence_graph_isomorphic = true
```
### 规范化非唯一参数
比较几何载体时,不要直接比较平面原点或轴向符号。
平面可规范为:
```text
单位法向 n
有符号距离 d = n dot p
```
轴线可规范为:
```text
单位方向 d,统一符号
原点到世界原点的最近点 p - d(d dot p)
```
这样可以识别“同一个平面但原点不同”和“同一根轴但方向相反”。
### 5.3 参数化表示检查
完成几何和拓扑硬检查后,再检查:
- UV 范围;
- 圆柱、圆锥、圆环坐标架;
- 圆弧参数范围;
- B 样条次数、节点、重数、控制点和权重;
- 边方向和面方向;
- 曲面原点是否反映预期特征顺序。
不要把所有参数差异都当成错误。先问:
1. 这是非唯一坐标架选择吗?
2. 它是否改变几何点集?
3. 它是否改变 BREP 邻接?
4. 它是否揭示不同的草图分段或特征顺序?
5. 用户要求的是精确 BREP,还是 STEP 文本字节一致?
STEP 实体编号和文本顺序通常不属于 BREP 本体,不应作为几何逆向的默认验收项。
### 5.4 回放验收
逆向模型不能只在当前 Python 进程中成功。应记录模型图,重新播放,再对回放结果执行相同检查。
```python
import simplecadapi as scad
with scad.GraphSession(graph_id="reverse_part") as session:
body = build_body()
scad.capture_result(value=body)
payload = scad.export_model_json(session)
rebuilt = scad.replay_model_json(payload)[0]
scad.export_step(rebuilt, "replayed.step")
```
需要分别比较:
```text
原始 vs 直接构建
原始 vs 回放构建
直接构建 vs 回放构建
```
## 6. 高效迭代策略
### 6.1 用差异类型决定下一步
| 当前差异 | 优先检查 |
|---|---|
| 包围盒错误 | 坐标系、单位、基准位置 |
| 体积差很大 | 漏掉主切除/并集、特征方向错误 |
| 体积接近但面积差大 | 圆角、倒角、孔壁或隐藏台阶 |
| 几何相同但面数少 | 草图边分段不足、同域面被合并 |
| 几何相同但面数多 | 多余切割平面、重复布尔、错误拆工具 |
| Sphere/Torus 数量错误 | 圆角选边范围或圆角时序 |
| Cylinder 数量错误 | 圆/圆弧草图分段、孔特征顺序 |
| 拓扑计数相同但图不同 | 面邻接、边界方向、错误局部连接 |
| UV 原点或范围错误 | 特征创建顺序、工具起始平面 |
### 6.2 先匹配低维输入,再匹配高维结果
工程特征一般遵循:
```text
点/尺寸 -> 草图边 -> 闭合轮廓 -> 面 -> 拉伸/旋转 -> 布尔 -> 圆角
```
当最终圆柱面分段不对时,优先检查草图圆弧数量,而不是在最终实体上补切割面。低维输入通常才是拓扑来源。
### 6.3 用解析面统计做中间目标
完整拓扑还没对齐时,解析面统计是非常有效的阶段目标。例如:
```text
目标:Plane 16, Cylinder 22, Sphere 4, Torus 4
```
候选若为:
```text
Plane 16, Cylinder 18, Sphere 4, Torus 4
```
说明主体、台阶和圆角网络很可能已正确,剩余问题集中在圆柱类特征,不应重做整个主体。
### 6.4 保存候选,但不要让试验污染最终入口
建议保留:
- 每轮 STEP;
- 每轮报告;
- 每轮截图;
- 差异日志;
- 被否定的假设及原因。
最终应收敛为一个独立、可读、可回放的入口,不能依赖多个临时试验脚本互相导入。
## 7. 本次实践中走过的弯路
### 7.1 只根据截图猜主特征
问题:截图遮挡内部结构,容易把缺口、孔和圆角顺序解释错。
改进:截图只用于提出假设;立即用解析曲面、面边界和截面验证。
### 7.2 对所有边做圆角
问题:`fillet(all_edges)` 产生了过多球面,且破坏目标的平面和圆环统计。
改进:根据设计语义分组选边。先识别“左侧开口轮廓”这一语义组,再验证它生成的解析面组合。
### 7.3 用两个半圆实体切出分段孔
问题:虽然圆柱面数量增加到目标值,却额外产生了两个径向平面。
改进:用两条半圆弧组成一个闭合草图,再做一次拉伸切除。
### 7.4 把体积和面积一致当作完成
问题:完整圆柱孔和两个半圆柱孔具有相同几何、体积和面积,但拓扑不同。
改进:加入唯一拓扑数量、解析面统计和邻接图同构。
### 7.5 直接比较平面原点和轴向
问题:同一个平面可选择任意平面内原点,同一根轴可反向,导致大量假差异。
改进:比较规范化平面方程和规范化轴线,不比较任意坐标架选择。
### 7.6 截面采样过密
问题:逐点 BREP 分类成本高,六张高分辨率截面会超时。
改进:截面图保持适中分辨率用于直观诊断;精确几何一致由双向布尔差集负责。
### 7.7 先做沉孔再从肩部做小孔
问题:最终几何和拓扑正确,但小孔圆柱参数原点与目标不同。
改进:先从入口平面做贯穿孔,再做沉孔。这个顺序既符合常见加工意图,也复现目标 UV 历史。
## 8. 推荐的自动化产物
每个逆向任务建议固定生成:
```text
reference_report.json
candidate_report.json
candidate.step
candidate_replayed.step
candidate.model.json
candidate_views.png
candidate_slice_overlay.png
candidate_brep_compare.json
```
最终验收至少包括:
```text
BREP valid
双向差集为零
唯一 Face/Edge/Vertex 数量一致
解析面和边类型统计一致
几何标记邻接图同构
关键解析参数一致
模型图可重放
回放结果再次通过全部检查
```
## 9. 核心方法总结
最有效的逆向顺序不是“看图后直接建模”,而是:
```text
建立精确基准
-> 恢复坐标语义
-> 清点解析几何
-> 从平面、轴线和边界恢复尺寸关系
-> 用多视图与截面建立直觉
-> 提出最小工程特征树
-> 逐特征构建和输出
-> 用差异类型指导下一轮
-> 双向差集验证几何
-> 几何标记图验证拓扑
-> UV/曲线参数验证特征历史
-> 模型图回放并重复全部验收
```
最重要的判断原则是:
> 几何和拓扑是硬约束;工程师先验只用于在满足硬约束的多种过程之间做选择。解析几何告诉我们“是什么”,拓扑分段告诉我们“草图如何画”,参数范围和坐标架经常告诉我们“特征按什么顺序做”。