# 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 |