feat: initial commit - massive-cloud point cloud processing system
This commit is contained in:
376
alg/delivery_v1.1/coupler_panel_release/USAGE.md
Normal file
376
alg/delivery_v1.1/coupler_panel_release/USAGE.md
Normal file
@@ -0,0 +1,376 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user