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

13 KiB
Raw Blame History

coupler_panel — 集成开发者使用指南

高铁车钩面板偏转角与最大偏差测量算法

主输入:CAD STP + 点云 PLY + ROI JSON

主输出:两个方向偏转角 θ_right / θ_ref、最大 x 向偏差 dx_y_max / dx_z_max,以及一份完整可序列化的 report.json。


1. 一句话集成

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. 安装

# 全量安装(推荐)— 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)**格式:

{
  "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

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/)。

配置覆盖:

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

@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 一致
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 进度回调

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 帧):

{
  "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(不读文件):

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 + 应用诊断:

{
  "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. 集成模板

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. 命令行(备用,非主集成路径)

# 等价于 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