Files
massive-cloud/alg/delivery_v1.1/coupler_panel_release/USAGE.md

377 lines
13 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.
# coupler_panel — 集成开发者使用指南
> 高铁车钩面板偏转角与最大偏差测量算法
>
> 主输入:**CAD STP + 点云 PLY + ROI JSON**
>
> 主输出:两个方向偏转角 `θ_right` / `θ_ref`、最大 x 向偏差 `dx_y_max` / `dx_z_max`,以及一份完整可序列化的 `report.json`。
---
## 1. 一句话集成
```python
from coupler_panel.pipeline import run_coupler_measurement_pipeline
result = run_coupler_measurement_pipeline(
input_cloud = "scan.ply", # 你的测量点云(已在 CAD 帧)
cad_stp_path = "panel.stp", # 同款面板的 CAD STP
roi_json = "roi.json", # ROI 描述(不裁剪就传 mode=all)
out_dir = "runs/today", # 中间产物 + report.json 落盘目录
)
print(result.theta_right_deg, result.theta_ref_deg) # 两个偏转角
print(result.dx_y_max_mm, result.dx_z_max_mm) # 最大 x 偏差
```
返回 `CouplerMeasurement`,全字段语义见 §4。`out_dir` 下额外得到 8 个文件(含 `report.json`,便于排查 / 后处理)。
---
## 2. 安装
```bash
# 全量安装(推荐)— core + 点云 IO + STP 解析 + 可视化 + Excel
python -m pip install -r requirements.txt
pip install -e ".[all]"
# 也可以按需取子集(见 pyproject.toml 的 extras):
pip install -e ".[cloud]" # 只要点云 + 去噪
pip install -e ".[cad]" # 需要解析 STP
pip install -e ".[report]" # 需要 Excel 报表
```
`requirements.txt` 锁定了端到端跑通过的版本(Python 3.10 验证)。
---
## 3. 三件输入的准备
### 3.1 CAD STP
- 提供面板的 STEP 文件路径。
- 算法用它做两件事:
1. 从拓扑直接提 4 个定位孔 + 中心大孔 + 面板法向(写入 `01_gt.json`,作审计用)
2. 把 CAD 法向作为 `panel_normal_seed` 喂给 hole-detection 的投影几何,加速并稳定 2D 网格化
- **可以不传**(`cad_stp_path=None`)—— 此时 hole detection 走 PCA 自行估法向;适用于你已经预先把法向算好/扫描坐标系本来就规范的场景。建议生产里**始终传**,便于审计。
### 3.2 点云 PLY
- 支持 `.ply` / `.pcd` / `.xyz` / `.txt` / `.csv`(PCD 与二进制 PLY 需要 open3d)。
- 数组形状 `(N, 3)`,单位 **mm**。
- **必须在 CAD 坐标系**:
- x = 车体纵向,y = 车体宽度,z = 车体高度
- 算法默认面板法向 `n_x > 0`
- **不再做配准**(参见 §6 "为什么不配准")。这意味着上游扫描端外参必须已经标定 —— 把点云对齐到 CAD 帧是扫描软件的职责,不是本算法的职责。
- 可以带颜色 / 法向等额外列;算法只读前 3 列。
### 3.3 ROI JSON
定义"PLY 里哪些点要纳入后续算法",**轴对齐 bounding box(AABB)**格式:
```json
{
"x_min": -12500.0,
"x_max": 12500.0,
"y_min": 150.0,
"y_max": 4100.0,
"z_min": -1680.0,
"z_max": 200.0
}
```
六个 key 全部必填,单位 mm,坐标系 CAD 帧。点 `(x, y, z)` 被保留的条件是:
```
x_min <= x <= x_max ∧ y_min <= y <= y_max ∧ z_min <= z <= z_max
```
- **想保留全部点云**:把 6 个边界写得比点云自身 bbox 还宽即可(不需要任何 mode 切换)。
- **想不写文件**:传 `roi_json=None`,算法走"±∞" 默认(pass-through)。
- **想程序内构造**:传 `roi_spec=ROISpec(x_min=..., ...)`。
---
## 4. 公共 API 详解
### 4.1 主入口 `run_coupler_measurement_pipeline`
```python
def run_coupler_measurement_pipeline(
input_cloud : str | Path, # 必填:点云路径
out_dir : str | Path, # 必填:输出目录(自动创建)
*,
config_path : str | Path | None = None, # 可选:自定义 YAML
profile : str = "auto", # 可选:preset profile
cad_stp_path: str | Path | None = None, # 推荐:CAD STP 路径
roi_spec : ROISpec | None = None, # 二选一:内存 ROI 对象
roi_json : str | Path | None = None, # 二选一:ROI JSON 路径
progress : Callable[[str, float], None] | None = None,
) -> CouplerMeasurement
```
**算法链路(固定不可绕过)**:
```
load_point_cloud → apply_roi → [optional CAD parse → panel_normal_seed]
→ denoise_cloud → compute_coupler_measurement
├─ validate
├─ detect 4 holes
├─ build reference frame
├─ extract panel ROI
├─ fit panel plane (RANSAC + SVD)
└─ compute metrics
```
**Profile**:传 `"auto"` 即可;若你的面板型号对应到一个特定 profile,可指定 `profile="lu1_af4"` 等(详见 `configs/profiles/`)。
**配置覆盖**:
```python
result = run_coupler_measurement_pipeline(
input_cloud="scan.ply",
out_dir="runs/x",
config_path={
# 任何在 configs/default.yaml 里的字段都可以这样覆盖
"denoise": {"steps": [{"type": "voxel", "voxel_mm": 0.3}]},
"plane_fit": {"ransac_inlier_thresh_mm": 1.0},
"metrics": {"right_axis_sign": -1},
},
)
```
### 4.2 返回值 `CouplerMeasurement`
```python
@dataclass
class CouplerMeasurement:
# 版本号
schema_version: str # 字段语义稳定性 → SCHEMA_VERSION
algorithm_version: str # 数值结果稳定性 → ALGORITHM_VERSION
# === 6 个主测量量 ===
theta_right_deg: float # 右偏转角 atan2(n_y, n_x),applied right_axis_sign
theta_ref_deg: float # 上偏转角 atan2(n_z, n_x)
D_y_max_mm: float # max |y_i - C_y|
D_z_max_mm: float # max |z_i - C_z|
dx_y_max_mm: float # |A| · D_y_max, A = -n_y/n_x
dx_z_max_mm: float # |B| · D_z_max, B = -n_z/n_x
# === 几何 ===
center_C: tuple[float, float, float] # 面板中心 (4 孔均值)
normal: tuple[float, float, float] # 拟合面板法向,n_x > 0
# === 详细子结构 ===
holes: HoleSet # 检测到的 4 个定位孔 + 可选中心孔
plane: PanelPlane # normal + offset + inlier_ratio + RMS
frame: ReferenceFrame # C + 参考面 z=C_z + 纵向中面 y=C_y
# === 诊断 ===
quality_flags: list[str] # 例如 'plane_inlier_ratio_low'
timings: list[StageTiming] # 每阶段秒数 + 峰值内存
report: dict # 全量可序列化,与 report.json 一致
```
```python
result.to_dict() # 完全 JSON 化的 dict(落 DB / 网络传输用)
```
### 4.3 异常
```
CouplerError # 所有可预见异常的基类
├── InvalidPointCloudError # 输入形状/dtype/NaN 非法
│ └── UnsupportedFormatError # 文件后缀不支持
├── InsufficientPointsError # 点数 < 200 或 ROI 后 < 100
├── CADParseError # STP 解析失败 / 找不到 4 孔
├── HoleDetectionError # 点云上 4 孔检测失败
└── PlaneFitError # RANSAC 找不到平面
```
非致命问题通过 `result.quality_flags` 暴露:
| Flag | 含义 |
|---|---|
| `plane_inlier_ratio_low` | 平面拟合内点率低于 `plane_fit.min_inlier_ratio` |
### 4.4 进度回调
```python
def on_progress(stage: str, fraction: float) -> None:
print(f"[{fraction:5.1%}] {stage}")
result = run_coupler_measurement_pipeline(..., progress=on_progress)
```
阶段顺序:`validate → detect_holes → build_frame → extract_roi → fit_plane → metrics`。
---
## 5. ROI JSON schema 详细
**完整字段**(6 个 key,全部必填,单位 mm,坐标系 CAD 帧):
```json
{
"x_min": -12500.0,
"x_max": 12500.0,
"y_min": 150.0,
"y_max": 4100.0,
"z_min": -1680.0,
"z_max": 200.0
}
```
**规则**:
- 必须满足 `x_min < x_max` 且 `y_min < y_max` 且 `z_min < z_max`,否则 `load_roi_json` 抛 `ValueError`
- 不在 [min, max] 闭区间内的点被丢弃(用 `>=` 与 `<=`,包含边界)
- 多余的 key(例如 `comment`、`source`)会被忽略,方便挂元数据
**手动构造 ROI(不读文件)**:
```python
from coupler_panel.roi_select import ROISpec, default_roi_spec
# 全保留默认(六个轴都是 ±∞)
spec = default_roi_spec()
# 自定义 bbox
spec = ROISpec(
x_min=-12000, x_max= 100,
y_min= -300, y_max= 1200,
z_min= -1000, z_max= 800,
)
result = run_coupler_measurement_pipeline(..., roi_spec=spec)
```
**诊断输出**:
每次运行后 `out_dir/roi.json` 写出实际生效的 spec + 应用诊断:
```jsonc
{
"spec": { "x_min": -12000.0, ... },
"diagnostics": {
"kind": "aabb",
"n_in": 96662,
"n_out": 96662,
"kept_ratio": 1.0,
"bounds": { "x_min": -12000.0, ... }
}
}
```
---
## 6. 为什么主路径**不做配准**
车钩面板上的 4 个定位孔与面板一起刚体旋转。如果用"扫到的 4 孔 → CAD 原始 4 孔"做 Kabsch 配准,数学上会**精确解出**"扫描器位姿误差 · 面板偏转"的复合刚体变换并反向作用 —— 把面板偏转随同位姿误差**一起消掉**,下游平面拟合再读不到偏转信号。
这是几何性质,不是实现 bug。
工程上,主算法假设扫描端外参标定让点云已经在 CAD 帧。如果你的设备做不到位姿标定,要么:
1. 在面板**外**装基准(不动)做 Kabsch
2. 用 ICP 或其他不依赖 4 孔的方法
研究模式(`coupler_panel.pipeline.run_from_cad_pipeline`)保留了 4 孔 Kabsch + 注入偏转 oracle 的能力,用于上限对照实验;生产集成请用 `run_coupler_measurement_pipeline`。
---
## 7. 落盘产物(`out_dir/`)
| 文件 | 内容 |
|---|---|
| `config.resolved.yaml` | 解析合并后实际生效的完整配置 |
| `roi.json` | ROI spec + 应用诊断(n_in / n_out / kept_ratio) |
| `01_gt.json` | (传了 cad_stp_path 时)从 STP 提的审计用 GT |
| `02_sampled.ply` | ROI 裁剪后的点云 |
| `05_denoised.ply` | 去噪后点云(喂给测量算法的最终点云) |
| `10_metrics.json` | 等价于 `report.json` 的内部 report 部分 |
| `report.json` | **完整结果**,等于 `result.to_dict()`;schema 见 §4.2 |
| `images/` | 占位空目录(仅 `viz` 模块写图时会用) |
---
## 8. 集成模板
```python
import logging
from pathlib import Path
from coupler_panel.pipeline import run_coupler_measurement_pipeline
from coupler_panel.exceptions import CouplerError
log = logging.getLogger(__name__)
def measure_coupler(
cloud_path: Path,
cad_stp: Path,
roi_json: Path,
out_dir: Path,
) -> dict | None:
"""主集成入口。失败时返回 None;成功时返回 6 个测量量 + flags。"""
try:
result = run_coupler_measurement_pipeline(
input_cloud = cloud_path,
cad_stp_path = cad_stp,
roi_json = roi_json,
out_dir = out_dir,
progress = lambda s, f: log.debug("%s %.0f%%", s, f * 100),
)
except CouplerError as exc:
log.error("coupler measurement failed: %s", exc)
return None
if result.quality_flags:
log.warning("quality flags: %s", result.quality_flags)
return {
"schema_version": result.schema_version,
"algorithm_version": result.algorithm_version,
"theta_right_deg": result.theta_right_deg,
"theta_ref_deg": result.theta_ref_deg,
"dx_y_max_mm": result.dx_y_max_mm,
"dx_z_max_mm": result.dx_z_max_mm,
"center_C_mm": list(result.center_C),
"normal": list(result.normal),
"flags": result.quality_flags,
}
```
---
## 9. 命令行(备用,非主集成路径)
```bash
# 等价于 run_coupler_measurement_pipeline(input_cloud=..., out_dir=...)
coupler-measure --input-cloud scan.ply --out-dir runs/today
```
> 当前 CLI 暂未把 `--cad` 与 `--roi` 暴露出来(设计上 CLI 主要给研究流程用);集成方请直接用 Python API。
---
## 10. 版本兼容性
| 标识 | 在哪 | 含义 |
|---|---|---|
| `coupler_panel.__version__` | `coupler_panel._version` | 包版本,遵循语义化版本 |
| `coupler_panel.ALGORITHM_VERSION` | 同上 | 算法版本;升级算法时递增,**数值结果可能变化** |
| `coupler_panel.SCHEMA_VERSION` | 同上 | `CouplerMeasurement` / `report.json` 字段结构;只在破坏性变更时递增;**新增可选字段不递增** |
集成建议:
1. 落库时把 `algorithm_version` 写进每条记录,便于历史复现
2. 反序列化 `report.json` 时校验 `schema_version` 主版本号
---
## 11. 坐标 & 单位速查
| 量 | 单位 | 约定 |
|---|---|---|
| 点云坐标 | mm | x=纵向、y=宽度、z=高度 |
| 角度 | 度 (deg) | `θ_right` = atan2(n_y, n_x)、`θ_ref` = atan2(n_z, n_x) |
| 法向 | 单位向量 | 强制 `n_x > 0` |
| `right_axis_sign` | ±1 | 如果车体"右"是 +y 留 1;如果是 -y 则配置 -1 |