feat: initial commit - massive-cloud point cloud processing system

This commit is contained in:
2026-05-27 09:51:42 +00:00
commit 16811100d1
391 changed files with 78338 additions and 0 deletions

View File

@@ -0,0 +1,363 @@
# underframe_deflection 集成调用指南 (v1.1)
> 高铁底架纵梁挠度测量算法 —— 基于点云的计算。
> 本文档面向把本算法集成进上层软件的开发者:怎么准备 **CAD 基准 + 扫描点云 + ROI JSON** 三件套输入、怎么调用、怎么读结果。
---
## 0. TL;DR
```python
from underframe_deflection import run_measurement_pipeline
report = run_measurement_pipeline(
input_cloud="scan.ply",
cad_path="reference.ply",
roi_path="roi.json",
out_dir="out/",
)
# 关心的两个核心数值
print(report["deflection"]["left"]["selected_measurement"]["signed_deflection_mm"])
print(report["deflection"]["right"]["selected_measurement"]["signed_deflection_mm"])
```
落盘后会得到:
```
out/
├── report.json # 完整结果
├── report.xlsx # 误差报表(Summary + 4 张中间表)
├── deflection_curves.csv # 沿 X 的挠度曲线
├── beam_tracks.csv # 候选梁切片诊断
├── images/ # 5 张诊断 PNG
└── ply/ # 4 个着色 PLY 叠加图
```
---
## 1. 算法的三个输入
| 输入 | 类型 | 必需 | 说明 |
| --- | --- | --- | --- |
| **扫描点云** | `.ply / .xyz / .txt / .csv` | ✅ 必须 | 现场采集得到的 (N, 3) XYZ 点云,单位 mm |
| **CAD 基准** | `.ply / .xyz / .stl / .obj` | 可选但强烈建议 | 预先 tessellate / 采样过的 CAD 几何,作为配准基准坐标系;不直接吃 STEP/IGES |
| **ROI JSON** | `.json` | 可选但强烈建议 | 描述车底测量区域的轴对齐 bbox,坐标基于 CAD 坐标系 |
> 三者**全给**时走 v1.1 完整流程:去噪 → 配准到 CAD 系 → 按 ROI JSON 截区 → 算挠度 → 出 Excel。
> 只给扫描点云时走 legacy 流程,与本算法 v1.0 行为完全一致(仅多出 bilateral 这一步可用 `--no-bilateral` 关掉)。
### 1.1 ROI JSON 文件格式
固定 6 个 float 字段,单位 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
}
```
约束:每个轴 `min < max`,且都是有限实数;任一字段缺失或非法 → `InvalidPointCloudError`。
`test_data/sample_roi.json` 是一个可直接使用的样例。
### 1.2 CAD 基准
- **不解析 STEP / IGES**。如果你只有 `.stp`,请用 CAD 软件或离线脚本先把它转成 `.stl` / `.ply` / `.obj`,再喂给本算法。
- 三角网格的话只读顶点位置,face 信息忽略 —— 算法内部把 CAD 当作"参考点集"用 ICP 对齐。
- `test_data/m000004802409t02_asm_100000pts.ply` 是从原始 STEP 采样得到的 10 万点 PLY,可直接当 CAD 基准。
---
## 2. 三种调用方式
### 2.1 命令行(最简单的集成方式)
```bash
underframe-measure \
--input-cloud scan.ply \
--cad reference.ply \
--roi roi.json \
--out-dir out/
```
可选开关:
| 开关 | 含义 |
| --- | --- |
| `--config CONFIG.json` | 覆盖部分内部超参数(结构同 `DEFAULT_CONFIG`) |
| `--profile {auto,M,TC,TP,custom}` | 调参档位 |
| `--skip-registration` | 给了 `--cad` 但不跑 ICP(适合调试或扫描已经预对齐) |
| `--no-bilateral` | 关闭双边滤波(v1.0 行为) |
| `--skip-excel` | 不写 `.xlsx` |
未安装本包时,等价命令:
```bash
python -m underframe_deflection.cli --input-cloud scan.ply ...
```
### 2.2 一次性 Python 函数(推荐)
```python
from underframe_deflection import run_measurement_pipeline
report = run_measurement_pipeline(
input_cloud="scan.ply",
cad_path="reference.ply", # 可选
roi_path="roi.json", # 可选
out_dir="out/",
config_path=None, # 可选 JSON 配置覆盖
profile="auto", # 可选档位
skip_registration=False,
enable_bilateral=True,
skip_excel=False,
)
```
**返回**:`report` 是一个 `dict`,结构与 `out/report.json` 完全一致。
### 2.3 纯内存计算(不读写文件)
如果你已经从其它来源拿到了点云和 ROI(例如直接从相机驱动 / 数据库取数据),可以避开文件 I/O:
```python
import numpy as np
from underframe_deflection import compute_deflection, load_point_cloud
points = load_point_cloud("scan.ply") # 或自己构造 (N, 3) ndarray
roi_aabb = { # 与 ROI JSON 同结构的 dict
"x_min": -12500, "x_max": 12500,
"y_min": 150, "y_max": 4100,
"z_min": -1680, "z_max": 200,
}
result = compute_deflection(points, roi_aabb=roi_aabb)
print(result.left.selected_signed_mm, result.right.selected_signed_mm)
```
> 注意:`compute_deflection` 本身**不做 CAD 配准**。如果你想自己控制配准过程,先调用 `register_to_cad`,把变换应用到点云后再喂给 `compute_deflection`,配合 `config["frame"]["use_input_axes"]=True`(这本来就是默认值)即可:
```python
from underframe_deflection import (
read_cad_reference, register_to_cad, compute_deflection,
)
from underframe_deflection.registration import apply_transform
scan = load_point_cloud("scan.ply")
cad = read_cad_reference("reference.ply")
# 你需要一份预解算好的 config(spacing 之类)。最简单的办法:
from underframe_deflection.config import load_config, resolve_numeric_parameters, cloud_geometry
from underframe_deflection.preprocess import estimate_spacing
cfg = load_config(None, "auto")
cfg = resolve_numeric_parameters(cfg, cloud_geometry(scan), estimate_spacing(scan))
reg = register_to_cad(scan, cad, cfg)
scan_aligned = apply_transform(scan, reg.R, reg.t)
result = compute_deflection(scan_aligned, roi_aabb=roi_aabb)
```
---
## 3. 读取输出
### 3.1 核心数值(最常用)
```python
left = report["deflection"]["left"]["selected_measurement"]
right = report["deflection"]["right"]["selected_measurement"]
if left["available"]:
x_mm = left["x_mm"]
signed = left["signed_deflection_mm"] # 带符号,向下为负
absolute = left["absolute_deflection_mm"]
type_ = left["selected_type"] # "downward" / "upward"
beam_pt = left["beam_point"] # [x, y, z]
ref_pt = left["reference_point"] # [x, y, z]
```
如果你用的是 `compute_deflection`,公开数据类更直接:
```python
result.left.selected_signed_mm
result.left.max_downward_mm
result.left.max_downward_x_mm
result.left.max_upward_mm
result.left.reference_confidence
result.curves.x_mm # ndarray
result.curves.left_deflection_mm # ndarray, NaN 表示该 X 处无效
```
### 3.2 质量警告 `quality_flags`
**非致命**问题列在这里,调用方应判断是否要重测:
| Flag | 含义 |
| --- | --- |
| `left_track_missing` / `right_track_missing` | 单侧梁追踪失败 |
| `left_representative_line_missing` / `right_...` | 单侧代表线拟合失败 |
| `left_reference_unavailable` / `right_...` | 单侧参考线不可用 |
| `left_reference_low_confidence` / `right_...` | 参考线置信度低 |
| `left_track_low_coverage` / `right_...` | 单侧追踪覆盖率 < 45% |
| `left_right_symmetry_warning` | 左右对称性偏差 > 18% |
| **`registration_not_converged`** | ICP 未收敛到容差内 |
| **`registration_poor_quality`** | ICP 最终 RMS > max(10 mm, 2×点间距) |
| **`registration_low_inlier_ratio`** | ICP 内点比例 < 50% |
| **`passthrough_empty`** | ROI passthrough 后点过少,已自动回退 |
| **`bilateral_degraded`** | bilateral k 邻域被裁剪 |
加粗的几项是 v1.1 新增。
### 3.3 配准信息
```python
reg = report["registration"]
if reg["performed"]:
reg["rms_mm"] # 最终对齐残差
reg["initial_rms_mm"] # 粗对齐后的初始残差
reg["iterations"]
reg["inlier_ratio"]
reg["converged"] # bool
reg["R"] # 3x3 list
reg["t"] # length-3 list
```
### 3.4 Excel 误差报表
`out/report.xlsx` 5 个 sheet:
| Sheet | 内容 |
| --- | --- |
| **Summary** | key/value 形式:版本、输入路径、配准统计、预处理各阶段计数、左右挠度核心数值、质量警告 |
| **Deflection_Curves** | 沿 X 的逐点表:x / 左右 beam_y, beam_z / 左右 ref_y, ref_z / 左右 deflection / 左右 confidence |
| **Beam_Tracks** | 每片候选条带:slice_index、y_center、z_low、width、score、selected_rank 等 |
| **Reference_Lines** | 左右参考线 front/back 端点、direction、residual、confidence |
| **Representative_Points** | 左右每个代表点的 x/y/z/width/residual/confidence |
> 用 `openpyxl`、`pandas.read_excel`、Excel/LibreOffice 都可以直接读。
### 3.5 异常
| 异常类 | 触发条件 |
| --- | --- |
| `InvalidPointCloudError` | 点云 / CAD / ROI JSON 文件格式非法、字段缺失、AABB 反转 |
| `UnsupportedFormatError` | 文件后缀不在支持列表 |
| `InsufficientPointsError` | 点数过少(点云 < 256)或 ROI 过滤后无重叠 |
| `FrameInferenceError` | 自动坐标系推断失败(仅 legacy 路径) |
集成方应在外层 `try / except DeflectionError` 兜底,并把 `quality_flags` 落入日志或上层告警。
---
## 4. 配置覆盖(高级)
绝大多数场景不需要碰配置。需要微调时,通过 `--config my.json` 或 `config=` 传入嵌套 dict,与 `DEFAULT_CONFIG` 深度合并:
```python
config = {
"preprocess": {
"bilateral": {"enabled": True, "k": 32, "sigma_n_deg": 10.0},
# 也可以从外部传 passthrough,但通常用 ROI JSON 即可
},
"registration": {
"icp_max_iter": 80,
"trim_ratio": 0.15,
},
"reference": {
"low_confidence_threshold": 0.5,
},
"deflection": {
"evaluation_grid_factor": 0.5, # 更密的挠度采样
},
}
```
完整字段定义见 `underframe_deflection/config.py: DEFAULT_CONFIG`。
---
## 5. 进度回调
`compute_deflection` 支持 `progress` 回调:
```python
def on_progress(stage: str, fraction: float) -> None:
print(f"[{fraction:5.1%}] {stage}")
result = compute_deflection(points, roi_aabb=roi_aabb, progress=on_progress)
```
阶段顺序:`preprocess → frame → beam_tracking → line_fitting → reference → deflection`。
`run_measurement_pipeline` 当前未透传 progress 回调,需要时请直接调 `compute_deflection`。
---
## 6. 坐标系约定
输入点云**必须**满足以下约定,否则结果无意义:
- **X 轴**:纵向(车长方向)
- **Y 轴**:横向(车宽方向)
- **Z 轴**:高度方向,**向上为正**
- 单位:毫米
CAD 基准也必须遵循同样的轴向;ROI JSON 中的 bbox 字段在 CAD 坐标系下。
扫描点云轴向不同 → 调用前自行做置换 / 旋转。
---
## 7. 版本兼容性
- `__version__` —— 包版本
- `ALGORITHM_VERSION` —— 算法版本(升级会改变数值结果)
- `SCHEMA_VERSION` —— `report.json` 结构版本(仅在破坏性变更时递增;新字段不递增)
v1.1 新增了非破坏性的可选字段(`cad_reference / roi_source / roi_aabb / registration / preprocess.passthrough / preprocess.bilateral / roi.source / quality_flags 新值`),`SCHEMA_VERSION` 保持 `"1.0"` 不变。
---
## 8. 集成模板
```python
import logging
from underframe_deflection import (
run_measurement_pipeline,
DeflectionError,
)
log = logging.getLogger(__name__)
def measure(scan_path: str, cad_path: str, roi_path: str, out_dir: str) -> dict | None:
try:
report = run_measurement_pipeline(
input_cloud=scan_path,
cad_path=cad_path,
roi_path=roi_path,
out_dir=out_dir,
)
except DeflectionError as exc:
log.error("deflection failed: %s", exc)
return None
flags = report.get("quality_flags") or []
if flags:
log.warning("quality flags: %s", flags)
return {
"schema_version": report["schema_version"],
"algorithm_version": report["algorithm_version"],
"left_signed_mm": report["deflection"]["left"].get("selected_measurement", {}).get("signed_deflection_mm"),
"right_signed_mm": report["deflection"]["right"].get("selected_measurement", {}).get("signed_deflection_mm"),
"registration_rms_mm": report["registration"].get("rms_mm"),
"flags": flags,
}
```