M-detector 动态物体聚类与跟踪:体素聚类、地面估计与 OOBB 包围盒原理一篇讲透
【免费下载链接】M-detector项目地址: https://gitcode.com/gh_mirrors/md/M-detector
M-detector 是一款基于 LiDAR 点云流的动态物体检测算法,它能逐个点对判断运动状态,延迟仅几微秒。本文带你从原理层面看懂它的三大核心模块:体素聚类、地面估计与OOBB 有向包围盒如何实现动态物体的聚类与跟踪。
一、M-detector 是做什么的 🚗
M-detector 由香港大学 MARS 实验室开发,核心思路基于遮挡原理:当激光雷达观测到某处的深度在短时间窗口内发生变化,说明有物体正在遮挡或被遮挡,从而判定该点属于运动物体(如车辆、行人)。
它的典型工作流是:
- 动态点筛选:基于多帧深度图(DepthMap)对每个点做运动判定,源码见 DynObjFilter.h;
- 体素聚类:把动态点合并成一个个物体级簇;
- 地面估计与去除:剔除被误判为动态的地面点;
- OOBB 包围盒估计:为每个物体给出带朝向的最小包围盒,支撑跟踪与预测。
后三步集中在 DynObjCluster.cpp 的DynObjCluster类中实现,下面逐一拆解。
二、体素聚类:把散点变成物体 📦
1. 为什么用体素而不是 KD-Tree DBSCAN
传统做法是 KD-Tree + DBSCAN(代码中保留了GetClusterResult分支,DynObjCluster.cpp),但逐点建索引开销大。M-detector 改用体素网格聚类:将空间划分为Voxel_revolusion(默认 0.3 m)的立方体格子,动态点直接落格,再按 26 邻域连通生长出簇,复杂度接近线性,适合嵌入式实时部署。
核心入口是GetClusterResult_voxel(DynObjCluster.cpp),内部调用VOXEL_CLUSTER(voxel_cluster.h)构建全局体素地图umap并提取voxel_clusters。
2. 点分配与簇过滤
PubClusterResult_voxel(DynObjCluster.cpp)完成了两件事:
- 包围盒初值:统计每个簇占据的最小/最大体素坐标,生成初始 AABB(轴对齐包围盒),仅保留在至少两个维度上尺寸超过体素大小的簇,过滤噪声碎片;
- 原始点回填:遍历本帧原始点,按体素归属分配到"簇内点、簇外地面扩展点、框内填充点"三类体素地图(
umap/umap_ground/umap_insidebox),并更新每个点的动态标签dyn_tag。
此外还有两道质量关:
isolate_remove(DynObjCluster.cpp):对簇内部再做一次小范围体素聚类,只保留最大子簇,剔除离群孤立点;- 可信度检查:若簇的"原始点占比"低于
thrustable_thresold(默认 0.3),说明大部分是填充点而非真实动态点,整个簇会被丢弃。
三、地面估计:把误报的地面点踢出去 🗺️
运动车辆底部会压到雷达的"地面回波",若不去除,聚类结果会拖出一条长长的尾巴。M-detector 在簇外扩展的体素(umap_ground)中做地面平面拟合:
- 分块拟合:
ground_estimate(DynObjCluster.cpp)把候选地面点分成若干小块,对每块用esti_plane(基于列主元 QR 分解的最小二乘平面拟合,DynObjCluster.cpp)求平面,要求平面法向与车体系 Z 轴夹角小于 30°(保证是"接近水平"的平面); - 择优投票:统计每块平面在全点集中的贴合点数,取覆盖最多的平面,要求支持点数超过 20% 或 500 点才认定地面成立;
- 区域生长:
event_extend(DynObjCluster.cpp)从地面体素出发做 6 邻域生长,把邻近且"法向不平行于地面"的体素并入物体——这正好能找回被车身遮挡、投影到地面的车底点; - 地面去除:
ground_remove(DynObjCluster.cpp)把到平面距离小于 0.1 m 的点标记为静态,剔除出簇。
四、OOBB 包围盒:给物体一个带朝向的"盒子" 📐
轴对齐框(AABB)无法表达车辆朝向。M-detector 的oobb_estimate(DynObjCluster.cpp)用法向直方图投票求主方向:
- 法向采样:对簇内每个点数足够(≥5)的体素拟合平面法向;
- 等距面积圆盘分箱:
EA_disk(EA_disk.h)把法向从球面坐标投影到圆盘并分环分扇区,避免球面采样密度不均; - 加权投票:每个扇区的得分为
sqrt(点数) / |法向 z 分量|累加,sqrt抑制大簇体素的过度影响,1/z放大接近水平面的体素权重(更贴合"长条状车辆侧面"假设),取得分最高的两个扇区作为主方向与辅助方向; - 构建旋转矩阵 R:若地面估计成功,则第一列直接取地面法向(保证盒子"站"在地面上),否则用主方向并对齐世界 Z 轴;
- 投影求极值:把簇内所有点变换到 R 坐标系下取 min/max,得到带朝向的 OOBB 最小/最大点,写入
bbox_t结构体(DynObjCluster.h)。
输出的bbox_t同时携带中心位姿、点索引、地面点与 OOBB 参数,可直接用于 RViz 可视化、轨迹预测和下游 IoU 评估(见 cal_recall_multi.cpp)。
五、如何配置与上手运行 🚀
- 参数配置:不同数据集的雷达参数(深度图分辨率、遮挡阈值、
cluster_Voxel_revolusion、cluster_thrustable_thresold等)位于 config/kitti/kitti0.yaml、config/nuscenes/nuscenes0.yaml、config/waymo/waymo0.yaml、config/avia/avia0.yaml; - 启动文件:
launch/detector_kitti.launch等数据集专属 launch,配合里程计节点回放 bag 即可运行; - 依赖:Ubuntu ≥ 18.04、ROS ≥ Melodic、PCL ≥ 1.8、Eigen ≥ 3.3.4 与 TBB(CMakeLists.txt 中需修改 TBB 路径)。
六、总结
M-detector 的动态物体聚类与跟踪链路可以概括为一句话:体素聚类负责"聚",地面估计负责"净",OOBB 负责"框"。三者均以体素网格为公共数据结构,充分利用 GPU/CPU 并行(std::execution::par),在嵌入式平台上实现了毫秒级的物体级检测与跟踪——这正是它能做到"逐点、微秒级延迟"动态检测的关键工程基础。
【免费下载链接】M-detector项目地址: https://gitcode.com/gh_mirrors/md/M-detector
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考