feat: 算法升级、整车配准集成与性能分析文档
算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3 适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新 后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动 前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复 文档: 新增5份算法性能分析报告
This commit is contained in:
140
docs/点云测距算法性能分析报告.md
Normal file
140
docs/点云测距算法性能分析报告.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# 点云测距算法性能分析报告
|
||||
|
||||
> 分析时间:2026-06-25
|
||||
> 算法版本:`train_measurement_system_v2.2`
|
||||
> 入口类:`TaskManager`(`src/task_manager.py`)
|
||||
> 测试环境:INTEL SSDSC2KB01(SATA SSD,~394 MB/s);CPU 128 核
|
||||
> 配置文件:`config/measurements_0618.yaml`(20 个测量任务)
|
||||
|
||||
---
|
||||
|
||||
## 执行流程总览
|
||||
|
||||
```
|
||||
train_measurement_run.py (adapter)
|
||||
├─ tm.pointcloud_path_override = pc_path
|
||||
├─ tm.load_config() # 读 YAML,解析 20 个任务
|
||||
├─ tm.load_point_cloud() # 一次性加载整车点云到内存 ← 瓶颈1
|
||||
└─ tm.run_all_tasks()
|
||||
├─ 任务1: plane_to_plane
|
||||
│ ├─ ROIExtractor.extract_bbox_points() # numpy 布尔索引,O(N)
|
||||
│ ├─ PlaneFitter.fit() × 2 # RANSAC,O(k×M)
|
||||
│ └─ distance calculation
|
||||
├─ 任务2: plane_to_plane ... (× 10 个)
|
||||
├─ 任务n: point_to_point ... (× 3 个)
|
||||
├─ 任务n: point_to_plane ... (× 3 个)
|
||||
├─ 任务n: plane_to_plane + 4_hole_center ... (× 2 个)
|
||||
└─ 任务n: point_to_point + hole_center ... (× 2 个)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各步骤详细说明与时间估算
|
||||
|
||||
### 第一阶段:I/O — 加载点云
|
||||
|
||||
| # | 步骤 | 代码位置 | 说明 | 估算时间 |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `PointCloudLoader.load_ply` | `pointcloud_loader.py:11` | 逐点 `struct.unpack` for loop,**O(N) 次 Python 调用** ← 瓶颈 | **~3 min(10亿点)** |
|
||||
|
||||
**关键问题**:当前 `_read_binary_ply` 使用 `struct.unpack` 逐点循环,实测 ~5 M 点/s。对比 `np.frombuffer`(~30 M 点/s)慢 6×,10 亿点需要约 3 分钟仅用于读取。
|
||||
|
||||
```python
|
||||
# 当前实现:每个点一次 Python 函数调用
|
||||
for _ in range(vertex_count): # ← N次循环
|
||||
data = struct.unpack(fmt, f.read(struct_size)) # ← N次 struct.unpack
|
||||
points.append(data[:3])
|
||||
|
||||
# 理想实现:一次性内存操作
|
||||
data = f.read(vertex_count * struct_size)
|
||||
arr = np.frombuffer(data, dtype=record_dtype) # ← 1次调用,~6× 更快
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 第二阶段:每个测量任务的执行(共 20 个)
|
||||
|
||||
每个任务共享同一份全量点云 `self.point_cloud`(已加载到内存),按序独立执行。
|
||||
|
||||
#### 子步骤 A:ROI 裁剪
|
||||
|
||||
| # | 步骤 | 代码位置 | 说明 | 估算时间(单任务) |
|
||||
|---|---|---|---|---|
|
||||
| 2 | `ROIExtractor.extract_bbox_points` | `roi_extractor.py:10` | numpy 全量布尔掩码,O(N) | **~2–5 s(10亿点)** |
|
||||
|
||||
> **重要**:每个任务都调用一次 `extract_bbox_points`,对 10 亿点全量 numpy 数组做 3 列比较。20 个任务 × 2–5 s = **~40–100 s 只用于 ROI 掩码**,而 ROI 内实际只有 100–10 万个点。
|
||||
|
||||
各任务 ROI 尺寸与预期点数(10 亿点密度下):
|
||||
|
||||
| 任务 | 类型 | source ROI 尺寸 (mm) | source 预期点数 |
|
||||
|---|---|---|---|
|
||||
| 任务21_侧门高度 | plane_to_plane | `[1000, 4, 20]` | ~221 pts |
|
||||
| 任务2_车体长度 | plane_to_plane | `[4, 400, 1600]` | ~7,091 pts |
|
||||
| 任务43_窗口下沿高度 | plane_to_plane | `[1210, 2, 5]` | ~33 pts |
|
||||
| 任务44_车顶高度 | plane_to_plane | `[10449, 2, 474]` | ~27,439 pts |
|
||||
|
||||
#### 子步骤 B:平面拟合(plane_to_plane / point_to_plane 任务)
|
||||
|
||||
| # | 步骤 | 代码位置 | 默认参数 | 说明 | 估算时间(单任务) |
|
||||
|---|---|---|---|---|---|
|
||||
| 3 | `PlaneFitter.fit`(RANSAC) | `plane_fitter.py:44` | `max_iter=1000, threshold=0.1, sample_pts=100` | RANSAC:1000 次随机抽 3 点 + 全 ROI 距离计算 | **< 0.1 s(ROI内千点级)** |
|
||||
| 4 | SVD 精确平面拟合 | `plane_fitter.py:137` | `max_svd_pts=10000` | 收敛后 SVD 精确拟合;>1 万点自动降采样 | **< 0.1 s** |
|
||||
| 5 | 计算平面间距离 | `plane_distance.py` | — | 点到平面距离 + 统计 | **< 0.01 s** |
|
||||
|
||||
#### 子步骤 C:孔心检测(含 `_hole_center` 的任务)
|
||||
|
||||
| # | 步骤 | 代码位置 | 说明 | 估算时间(单任务) |
|
||||
|---|---|---|---|---|
|
||||
| 6 | `HoleDetector.detect` | `hole_detect/hole_detector.py` | 圆心检测 + RANSAC 圆拟合 | **~2–5 s** |
|
||||
|
||||
---
|
||||
|
||||
### 第三阶段:结果输出
|
||||
|
||||
| # | 步骤 | 代码位置 | 说明 | 估算时间 |
|
||||
|---|---|---|---|---|
|
||||
| 7 | `ExcelExporter.export` | `excel_exporter.py` | openpyxl 生成 xlsx | **~5 s** |
|
||||
| 8 | `MeasurementLogger.save` | `measurement_logger.py` | JSON 结果落盘 | **< 1 s** |
|
||||
|
||||
---
|
||||
|
||||
## 总时间汇总(10 亿点输入)
|
||||
|
||||
| 阶段 | 估算耗时 | 说明 |
|
||||
|---|---|---|
|
||||
| **struct loop 读取 12 GB 点云** | **~3 min** | 10 亿次 Python 调用 |
|
||||
| **20 次全量 ROI 布尔索引** | **~40–100 s** | 每次 O(N),共 20 次 |
|
||||
| 20 个任务实际测量计算 | **~20 s** | ROI 内点数少,RANSAC 极快 |
|
||||
| 孔心检测(共 6 次) | **~20 s** | — |
|
||||
| 输出(Excel + JSON) | **~5 s** | — |
|
||||
| **估算总计** | **~5–6 min** | 远超算法方测试的 ROI 后场景 |
|
||||
|
||||
> 算法方基准(~2 min)是直接在 ROI 后的小点云上跑的结果,没有计入全量加载和 20 次全量 ROI 索引的开销。
|
||||
|
||||
---
|
||||
|
||||
## 主要瓶颈与优化建议
|
||||
|
||||
### 瓶颈 1:`struct.unpack` 逐点读取(改算法方可根治)
|
||||
|
||||
```
|
||||
当前:struct.unpack × 10亿次 → ~3 min
|
||||
优化:np.frombuffer 一次性读 → ~35 s
|
||||
速度提升:~5×,改动:pointcloud_loader.py _read_binary_ply 约 3 行
|
||||
```
|
||||
|
||||
### 瓶颈 2:20 次全量 numpy 布尔索引(适配器层可缓解)
|
||||
|
||||
```
|
||||
当前:每个任务都对 10亿点数组做 3 列 boolean 比较,共 20 次
|
||||
优化方案 A:adapter 预处理——用 Open3D 先按整车大 bbox 粗裁剪到 ~1亿点,
|
||||
再传给 TaskManager,ROI 索引代价降 10×
|
||||
优化方案 B:修改 TaskManager,第一次裁剪后缓存点云,
|
||||
下一个任务的 ROI 在上次结果上继续裁(层次裁剪)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GPU 使用情况
|
||||
|
||||
**不使用 GPU。** 所有计算为 CPU + numpy + scipy,无 torch/CUDA 依赖。
|
||||
Reference in New Issue
Block a user