算法更新: delivery v1.2.1、平整度v1.5、测距v2.2、整车配准v1.3 适配器: vehicle-registration 新增、各算法 manifest 和 adapter 更新 后端: taskCadFiles/source 字段迁移、derivedPointClouds 注册、取消任务联动 前端: ElapsedTime 时长显示、两步算法流程、canRun 逻辑修复 文档: 新增5份算法性能分析报告
5.9 KiB
5.9 KiB
点云测距算法性能分析报告
分析时间: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 函数调用
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 依赖。