Files
lipan 29f1d45d66 feat: 算法升级、整车配准集成与性能分析文档
算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3
适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新
后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动
前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复
文档: 新增5份算法性能分析报告
2026-06-25 08:22:20 +00:00

257 lines
11 KiB
Markdown
Raw Permalink 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.
# coupler_panel — 集成开发者使用指南
> 高铁车钩面板偏转角测量算法
>
> **主输入:ROI JSON + 点云 PLY + CAD STP(三件套)**
> **主输出:含两个方向偏转角的 Excel 报表**
---
## 1. 一句话集成
```python
from coupler_panel.pipeline import run_coupler_measurement_pipeline
from coupler_panel.roi_select import load_roi_json
result = run_coupler_measurement_pipeline(
input_cloud = "train_2_1T.ply", # 待测点云(可为整车,内部按 ROI 流式裁剪)
out_dir = "runs/today", # 中间产物 + report.json 落盘
profile = "train2_coupler", # 该零件 profile
roi_spec = load_roi_json("roi.json"), # ROI = 裁剪超参数 ./examples/roi_train2_coupler.json
)
print(result.theta_right_deg, result.theta_ref_deg) # 两个偏转角
print(result.dx_y_max_mm, result.dx_z_max_mm) # 最大 x 偏差
```
要直接产出 Excel,用集成入口脚本(§3)。
---
## 2. 安装
```bash
python -m pip install -r requirements.txt # 锁定版本(Python 3.10 验证)
pip install -e ".[all]" # core + cloud + cad + viz + report
```
按需子集见 `pyproject.toml` 的 extras:`[cloud]` `[cad]` `[viz]` `[report]`。
---
## 3. 集成入口(三文件 → Excel)
`examples/run_integration.py` 是给集成方的标准入口:
```bash
python examples/run_integration.py \
--roi examples/roi_train2_coupler.json \ # ROI JSON
--cloud /path/to/train_2_1T.ply \ # 整车点云(可多 GB)
--stp /path/to/asm.stp \ # CAD STP(审计)
--out runs/train2 \ # 输出目录
--profile train2_coupler
```
产出 `runs/train2/eval_report.xlsx`(30 列,见 §7)+ 全部中间产物。
可选参数:`--gt-theta-right` / `--gt-theta-ref`(已知理论偏转,用于误差列,默认 0)、`--sample-id`、`--config`。
---
## 4. 三件输入说明
### 4.1 ROI JSON(裁剪超参数)
6-key 轴对齐包围盒,**坐标系必须与点云一致**,单位 mm:
```json
{
"x_min": 11526.0, "x_max": 11733.0,
"y_min": 588.0, "y_max": 1192.0,
"z_min": -950.0, "z_max": 950.0
}
```
- 点 `(x,y,z)` 保留条件:`x_min<=x<=x_max ∧ y_min<=y<=y_max ∧ z_min<=z<=z_max`
- 必须 `min<max`,否则报 `ValueError`;多余 key(如 `comment`)忽略
- **算法直接拿这六个数裁剪**。当输入是二进制 PLY 且 ROI 是有限 bbox 时,走**流式裁剪**(memmap 分块,不整体加载),可处理 GB 级整车点云
- 想全保留:传 `roi_json=None` 或 `roi_spec=default_roi_spec()`(±∞)
- 裁剪诊断写到 `out_dir/roi.json`(`mode`、`n_in`、`n_out`、`bbox`)
> **坐标系坑(必读)**:ROI 的六个边界写在**点云自己的坐标系**里。若点云是 x=车长 / y=车高 / z=车宽(部分整车数据集如此),ROI 也按此填。ROI 裁出 0 点时算法抛 `InsufficientPointsError` 并提示检查坐标系。
### 4.2 点云 PLY
- 支持 `.ply`(二进制/ASCII)/ `.pcd` / `.xyz` / `.txt` / `.csv`
- 单位 **mm**;算法只读 x/y/z 前三列(颜色/法向等额外列容忍)
- **可为整车扫描**(多 GB)——ROI 流式裁剪后才进入算法
- **算法不做配准**:假设上游扫描端外参已标定、点云已在 CAD/车体帧(原因见 §8)
### 4.3 CAD STP
当前 STP 的角色取决于规模:
- **单零件 STP**:可被 `parse_cad` 解析,用于提供面板法向 seed + 自动整定检测参数(见 §6)。传 `cad_stp_path=...`。
- **整车装配 STP**(数百 MB、数千实例):项目的 `parse_cad`(单零件设计)无法处理,此时 STP 仅作**审计/可追溯**输入(路径记入报表),不参与计算——检测参数由 profile 提供、法向用 PCA。
> 若将来需要从整车 STP 提取目标几何,需另写装配级 XCAF 解析(不能复用单零件 `parse_cad`)。
---
## 5. 主入口 API
```python
def run_coupler_measurement_pipeline(
input_cloud : str | Path, # 必填:点云路径(可整车)
out_dir : str | Path, # 必填:输出目录
*,
config_path : str | Path | None = None, # 可选:自定义 YAML / dict
profile : str = "auto", # 可选:preset profile
cad_stp_path: str | Path | None = None, # 可选:单零件 STP(法向 + autotune)
roi_spec : ROISpec | None = None, # 二选一:内存 ROI 对象
roi_json : str | Path | None = None, # 二选一:ROI JSON 路径
progress : Callable[[str, float], None] | None = None,
) -> CouplerMeasurement
```
**算法链路**:
```
crop_ply_bbox(流式 ROI 裁剪)
→ [layer_select] 沿法向隔离前层(profile 开关,去多层重叠)
→ [autotune] 从 CAD 孔径 + 点距自整定(仅默认 profile + 单零件 CAD 时)
→ denoise_cloud
→ compute_coupler_measurement
validate → detect 4 holes → build frame
→ extract ROI → fit plane(RANSAC+SVD) → metrics
```
**profile 选择**:
- 有匹配 profile(如 `train2_coupler` / `lu1_af4`)→ 直接指定,最稳。
- 无 profile 但传了单零件 `cad_stp_path` → `profile="auto"`,走自动整定(§6)。
**配置覆盖**:`config_path` 可传 dict 覆盖任意 `configs/default.yaml` 字段。
### 5.1 返回值 `CouplerMeasurement`
```python
theta_right_deg, theta_ref_deg # 两个偏转角(度)
D_y_max_mm, D_z_max_mm # 两方向最大距中心偏差
dx_y_max_mm, dx_z_max_mm # 最大 x 向偏差
center_C, normal # 面板中心、拟合法向(n_x>0)
holes, plane, frame # 4 孔 / 平面 / 参考系子结构
quality_flags, timings, report # 诊断;report == out_dir/report.json
result.to_dict() # 完全 JSON 化
```
### 5.2 异常
```
CouplerError
├── InvalidPointCloudError / UnsupportedFormatError 输入非法 / 格式不支持
├── InsufficientPointsError 点数过少 / ROI 裁出 0 点
├── CADParseError STP 解析失败
├── HoleDetectionError 4 孔检测失败
└── PlaneFitError 平面拟合失败
```
非致命问题在 `result.quality_flags`(如 `plane_inlier_ratio_low`)。
---
## 6. 检测参数:profile vs 自动整定
不同零件的定位孔半径、是否有中心孔、点云密度都不同,检测参数必须匹配,否则报 `HoleDetectionError: only N hole-shaped cavities found`。两种适配方式:
1. **per-part profile(推荐用于已知零件)**:`configs/profiles/<name>.yaml` 写死该零件的孔径带、面积窗、是否前层隔离等。已有 `train2_coupler`(整车车钩面板)、`lu1_af4`(单零件车钩面板)。
2. **autotune(单零件 + 默认 profile 时)**:传单零件 `cad_stp_path` 且 `profile=auto`、无 `config`,算法从 CAD 定位孔半径 + 去噪后点距自动推导检测参数(`coupler_panel/detection/autotune.py`)。指定具体 profile 或 config 时**完全跳过**。
> 整车场景用 profile(`train2_coupler`),因为整车 STP 不能 parse、autotune 不可用。
---
## 7. 输出
### 7.1 Excel 报表(`out_dir/eval_report.xlsx`,30 列)
样本标识、输入 PLY、ROI kind、**θ_right / θ_ref 的 GT/预测/误差**、D_y/D_z_max、dx_y/dx_z_max、中心 C(x/y/z)、法向 n(x/y/z)、平面内点率/残差、配准 RMSE/最大残差(主路径恒空)、ROI 前后点数、quality_flags、**算法运行时间 `Algorithm runtime (s)`**(输入→返回整段 wall-clock)、内核阶段累计、算法/schema 版本。
多组结果用 `coupler_panel.reporting.write_excel_report(rows, path)` 合并成一张表。
### 7.2 落盘中间产物(`out_dir/`)
| 文件 | 内容 |
|---|---|
| `config.resolved.yaml` | 实际生效的合并配置 |
| `roi.json` | ROI spec + 裁剪诊断 |
| `01_gt.json` | (单零件 CAD parse 时)审计 GT |
| `02_sampled.ply` | ROI 裁剪后的点云 |
| `transformed.ply` | (前层隔离开启时)隔离后的层 |
| `05_denoised.ply` | 去噪后点云(喂给测量算法的最终点云) |
| `report.json` | 完整结果,等于 `result.to_dict()` |
---
## 8. 为什么不做配准
车钩面板的 4 个定位孔与面板一起刚体旋转。用「扫到的 4 孔 → CAD 原始 4 孔」做 Kabsch,会精确解出「扫描器位姿 · 面板偏转」的复合刚体变换并反向作用,把偏转随位姿误差**一起消掉**,下游再读不到偏转——这是几何性质,不是 bug。
故主算法假设上游扫描端外参标定、点云已在车体帧。研究路径 `run_from_cad_pipeline`(含 Kabsch + 注入 oracle)完整保留作上限对照。
---
## 9. 集成模板
```python
import logging, time, json
from pathlib import Path
from coupler_panel.pipeline import run_coupler_measurement_pipeline
from coupler_panel.roi_select import load_roi_json
from coupler_panel.reporting import build_row_from_summary, write_excel_report
from coupler_panel.exceptions import CouplerError
log = logging.getLogger(__name__)
def measure(cloud, roi_json, stp, out_dir, profile="train2_coupler"):
t0 = time.perf_counter()
try:
r = run_coupler_measurement_pipeline(
input_cloud=cloud, out_dir=out_dir,
profile=profile, roi_spec=load_roi_json(roi_json))
except CouplerError as e:
log.error("measurement failed: %s", e)
return None
dt = time.perf_counter() - t0
diag = json.loads((Path(out_dir)/"roi.json").read_text(encoding="utf-8"))["diagnostics"]
row = build_row_from_summary(
sample_id=Path(cloud).stem, ply_path=str(cloud), roi_kind="aabb",
measured=r.to_dict(), gt_angles=(0.0, 0.0), registration=None,
n_points_in=diag.get("n_in", 0), n_points_after_roi=diag.get("n_out", 0),
pipeline_seconds=dt)
write_excel_report([row], Path(out_dir)/"eval_report.xlsx")
return r
```
---
## 10. 坐标 & 单位速查
| 量 | 单位 | 说明 |
|---|---|---|
| 点云坐标 | mm | 轴含义由数据集决定(见 README / §4.1) |
| 偏转角 | 度 | `θ_right=atan2(n_y,n_x)`、`θ_ref=atan2(n_z,n_x)` |
| 法向 | 单位向量 | 强制 `n_x>0` |
| `right_axis_sign` | ±1 | 右方向符号(profile `metrics` 配置) |
---
## 11. 版本兼容
| 标识 | 含义 |
|---|---|
| `coupler_panel.__version__` | 包版本(语义化) |
| `coupler_panel.ALGORITHM_VERSION` | 算法版本,升级时**数值可能变化** |
| `coupler_panel.SCHEMA_VERSION` | 结果结构版本,仅破坏性变更递增 |
建议:落库时记录 `algorithm_version`;反序列化 `report.json` 时校验 `schema_version` 主版本。