feat: 算法升级、整车配准集成与性能分析文档

算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3
适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新
后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动
前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复
文档: 新增5份算法性能分析报告
This commit is contained in:
2026-06-25 08:22:20 +00:00
parent cec0766e67
commit 29f1d45d66
266 changed files with 1311475 additions and 2846 deletions

View File

@@ -0,0 +1,256 @@
# 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` 主版本。