
简介面向毫米波雷达数据处理应用场景提供聚类算法系列博文配套的代码和数据集适合正在学习机器学习、雷达目标检测的开发者或研究人员。资源压缩包共40个文件以8个脚本和32个文本数据集构成总计848KB。脚本覆盖K均值、基于密度聚类、谱聚类等算法实现并附有轮廓系数、紧密度、分离度等评估函数文本数据集包含多种公开标准聚类数据与仿真点云数据。已有875人学习下载。通过这套资料读者可免去自行构造数据的繁琐直接运行代码观察不同聚类算法在多样数据上的表现并结合真实标签计算多项聚类有效性指标便于对比不同算法在凸形、密度不均及流形分布数据上的适用性与参数调整。同时数据集与脚本结构对应清晰可帮助快速迁移到车载毫米波雷达的目标检测与聚类分析任务中提升动手实践能力。 很多人第一次接触毫米波雷达点云都会觉得“这就是一堆点嘛直接聚类就好了”可真把数据喂进去才发现效果和想象中完全不是一回事。雷达点云和激光雷达点云长得像脾气完全不同激光雷达的点密、分布均匀聚起来很顺畅毫米波雷达点云稀疏、抖动大、不同距离下密度差异悬殊同一个目标可能分裂成好几块噪声点还特别多。这篇博文就是我在写毫米波雷达数据处理聚类算法系列时沉淀下来的配套代码和数据集导航里面包含代码的模块设计、数据集怎么选、DBSCAN这类算法在真实雷达数据上怎么调参以及我踩过的一些坑。不管你是刚开始接触雷达感知还是已经在做相关开发应该都能从里面找到可以直接上手的部分。1. 为什么毫米波雷达点云离不开聚类这一步先聊聊为什么要做聚类。毫米波雷达每次扫描会输出几十到几百个点每个点携带距离、方位角、径向速度、回波强度等信息。一个真正的目标——比如前方三四十米的一辆车——在雷达看来并不是一个点而是车身上多个反射点叠在一起保险杠、车尾边缘、后视镜区域可能各自产生了多个点。行人也是一样腿、躯干、手臂都会产生反射。如果不对这些点做归组后续的跟踪模块就不知道该拿哪个点去建航迹分类模块也没法判断“这一簇点到底是一辆车还是一个人”。聚类的目标就是在一个扫描周期内把物理上属于同一个目标的点归到同一个集合里顺便把孤立的噪声点识别出来丢掉。这一步做得好不好直接影响后面跟踪和识别的准确率。毫米波雷达点云有几个特点是聚类时必须考虑的稀疏性一个目标上可能只有几个到十几个点远没有激光雷达那么密集。密度不均匀近距离点密集远距离点稀疏固定聚类半径很容易顾此失彼。多普勒速度不一致车上不同反射点的径向速度可能存在差异比如车辆转弯时两端速度明显不同只看空间距离容易把一个目标拆成两半。多径与噪声雷达波经过地面、护栏反射后再回到接收机会产生虚假点这类点聚不进去也没关系但算法要能把它当作噪声容忍掉。所以在毫米波雷达数据处理链路中聚类算法既要做目标点云的划分又要承担噪声过滤的任务。它有别于图像处理里“前背景分割”的概念更接近“在噪声环境中进行密度分布的重新组织”。这也是为什么基于密度的聚类算法在雷达领域特别受欢迎。这个系列涉及的算法以DBSCAN为主同时也整理了KMeans、层次聚类等常见方法在雷达场景下的适用性对比。平时看论文的时候我也会顺手把一些针对雷达点云设计的改进聚类方法记录下来后续会逐步补充到代码仓库里。2. 配套代码结构拿到就能跑的四个模块这个系列配套的代码仓库分成几个独立的模块目的就是让读者能很快把整个流程跑通再针对自己的数据做修改。目录结构大概是这样的radar_cluster/ ├── config.py # 全局参数配置文件 ├── data_loader.py # 数据加载和格式转换 ├── cluster/ │ ├── __init__.py │ ├── dbscan.py # DBSCAN聚类实现与封装 │ ├── kmeans.py # KMeans聚类封装 │ └── utils.py # 坐标转换、距离计算等工具函数 ├── visualize.py # 聚类结果可视化 ├── evaluate.py # 聚类效果评估 └── main.py # 主运行入口依赖环境非常轻量Python 3.8以上numpy、scikit-learn、matplotlib、pandas都是做数据处理常用的库。不建议一上来就引入深度学习框架雷达点云聚类用传统的机器学习算法效率更高、结果可解释性更强。dbscan.py核心只做一件事把scikit-learn的DBSCAN封装成适合雷达点云处理的形式。因为实际用的时候我们往往需要自定义距离度量或者按照距离自适应调整邻域半径直接调原生接口反而麻烦。# cluster/dbscan.py 核心封装示例 import numpy as np from sklearn.cluster import DBSCAN as SkDBSCAN def adaptive_eps(range_values, base_eps1.5, slope0.05, min_eps1.0): 根据距离自适应获取每个点的聚类半径。 雷达远距离点的空间间隔更大固定半径容易把远距离目标漏检 所以让半径随距离线性增加。 return np.maximum(min_eps, base_eps slope * range_values) def cluster_points(points, eps1.5, min_samples3, distance_metriceuclidean): 执行DBSCAN聚类。 points: Nx3或Nx4数组列为[x, y, z]或[x, y, z, range_rate]。 model SkDBSCAN(epseps, min_samplesmin_samples, metricdistance_metric) labels model.fit_predict(points) return labelsdata_loader.py负责把不同来源的数据统一成numpy数组。不同公开数据集的字段命名差异很大我一般会在这一层把所有数据转换成统一的格式frame_id, x, y, z, range_rate, rcs, label其中label是有标注数据集里的真值类别没有的话填-1。统一格式之后cluster模块不用关心数据是从RadarScenes来的还是自己采集的只管处理。主流程图很简单load_data()读入一帧点云preprocess()做坐标转换、滤除无效点cluster()执行聚类visualize()画出聚类前后对比图。如果你只是想来跑个demo在项目根目录下准备一份点云CSV然后执行python main.py --data_path ./data/demo_frame.csv --show_plot代码会输出一个2D散点图不同聚类簇用不同颜色标记噪声点统一显示为灰色。3. 数据集应该怎么选公开数据源与自采方案雷达点云聚类的公开数据集比图像和激光雷达数据集少得多但也不是没有。我实测下来有三个数据集最适合拿来学习和验证聚类算法RadarScenes、Astyx和nuScenes。它们的标注质量、传感器类型和点云密度差异比较大适合不同目的的实验。数据集传感器类型点云格式标注类型适用场景RadarScenes多部毫米波雷达ndarray含距离、角度、多普勒、RCS逐点目标类别真实道路场景聚类验证Astyx高分辨率毫米波雷达pcd文件3D目标框小目标聚类、高精度验证nuScenes5部毫米波雷达激光雷达相机json格式3D目标框多模态联合研究自采数据TI/大陆等雷达套件csv/npy按需标注特定场景算法验证先说RadarScenes这个数据集来自德国乌尔姆大学的真实道路采集包含大量城市路况。它最大的优点是每个点都有逐点语义标签标记出这个点是行人、车辆、自行车还是其他目标。对于聚类课题来说这简直是天然的评估集你可以把聚类结果和点级标签对比直接算出聚类的纯度、召回率不用自己人工标注。Astyx的高分辨率雷达数据质量非常高点云相对更密集适合用来测试聚类算法在各种姿态目标上的表现。但目前它的公开数据量不大场景覆盖不如RadarScenes广。nuScenes是自动驾驶多模态研究的常用数据集包含激光雷达、相机和毫米波雷达如果你后续想把聚类和激光雷达检测结果做对比或者做多传感器融合这个数据集会非常合适。但它的毫米波雷达点云密度相对较低单独做雷达点云聚类实验时效果不如RadarScenes直观。还有一个容易踩的坑不要拿KITTI来做毫米波雷达点云聚类的实验。KITTI的光彩在于激光雷达和视觉它没有提供毫米波雷达的数据网上虽然有人转过所谓的KITTI雷达数据但缺乏权威标注拿来做聚类验证容易栽跟头。数据集拿回来之后不能直接用必须预处理。我踩过的坑是RadarScenes原数据提供的是极坐标下的距离和角度你需要自己转成笛卡尔坐标才能做欧氏距离聚类。转换公式很简单但坐标方向定义引用了不少时间。# 极坐标转笛卡尔坐标 def polar_to_cartesian(range_m, azimuth_rad, elevation_rad0.0): 毫米波雷达极坐标转笛卡尔坐标。 约定x为车前方正方向y为左侧正方向z为垂直向上。 x range_m * np.cos(elevation_rad) * np.cos(azimuth_rad) y range_m * np.cos(elevation_rad) * np.sin(azimuth_rad) z range_m * np.sin(elevation_rad) return x, y, z雷达安装方式不同坐标系定义会不一样代码里需要根据传感器的安装偏移量做一次外参修正。如果你用的是自己做的小型雷达套件这个步骤务必用标定数据验证一次不然聚类结果会莫名奇妙地偏向一侧。4. DBSCAN调参实战从固定值到距离自适应的演进雷达点云聚类最常用的算法是DBSCAN它的优势很明显不需要预先设定聚类数量能自动识别噪声点适合稀疏分布数据。但DBSCAN的两个参数——邻域半径eps和最小样本数min_samples——在雷达点云上调起来有大学问。我早期的实现是给全场景设同一个eps值结果在近距离效果还行到了中远距离目标漏检率一下子飙升。原因其实很直白雷达的角度分辨率是固定的距离越大相邻角度之间的横向间隔越大。一辆20米外的车点与点之间的间距可能只有0.3米同样的车放到50米外同一个目标反射点之间的间距就可能拉大到了1米。固定eps1.5时近处目标勉强能聚到一起远处目标直接散架。解决办法是让eps跟随距离自适应变化。我常用的公式是eps_i max(min_eps, base_eps slope * range_i)其中base_eps根据近距离的点云密度来定slope控制半径随距离增长的幅度min_eps防止过近处半径太小导致过分割。对这个参数组合我在多组测试数据上的经验是base_eps取1.2到1.8slope取0.03到0.08min_eps取0.8到1.0起始这几个值都不会太离谱。除了空间距离多普勒速度是雷达点云独有的一个非常有区分的维度。两个空间位置相近、但径向速度差异很大的点大概率不是同一个目标反过来一辆车上的点即便空间上稍有分散只要速度一致就很可能是一个整体。我后来把距离度量升级成综合距离效果明显改善def enhanced_distance(p1, p2, w_speed0.8): 空间距离 加权速度差异构成综合距离。 d_spatial np.linalg.norm(p1[:3] - p2[:3]) d_speed abs(p1[3] - p2[3]) if len(p1) 3 else 0.0 return d_spatial w_speed * d_speed给速度差异加上权重后车辆转弯时不容易把车头和车尾拆成两个簇行人那种速度变化大的目标也不会和旁边的静止护栏混在一起。速度维度的权重不是越大越好太大会把同一目标上速度稍有差异的点拆开我这个值是根据几组数据标定的实际使用时建议自己在自己的数据集上扫一遍。另一个容易被忽略的问题是在笛卡尔坐标下聚类还是在极坐标下聚类。笛卡尔坐标直观、好可视化但雷达原始测量是在距离-角度-速度域。同一辆车在极坐标下的距离维度很紧凑在笛卡尔坐标下“距离轴”上的点会被映射到“x/y轴”上坐标分布不再均匀。我实验下来短距离场景用笛卡尔坐标问题不大中远距离下极坐标聚类更稳定。不过极坐标聚类需要自定义距离函数代码复杂度高一些后续我写针对性的帖子细说。min_samples这个参数雷达点云一般设3到5就够了。设1、2噪声点很容易聚成小簇虚警特别多设太高行人和自行车这种点少的弱小目标直接丢了。建议先用默认的3跑一遍看可视化结果再决定要不要调。评估聚类效果时不要只盯着轮廓系数看。雷达点云的轮廓系数容易虚高因为噪声点被当作独立簇时轮廓系数也很漂亮但这是没有意义的结果。我做评估时更看重两件事第一已知真值的目标有没有被完整地聚成单簇第二两个目标有没有被错误地并到一簇。有了逐点标注的数据集比如RadarScenes可以直接用调整互信息和簇纯度这类指标做定量对比。5. 代码落地的隐藏细节从demo到工程化的距离把聚类代码从demo状态搬到实际工程里会碰到很多意想不到的问题。这里列几个我实际踩过的坑希望能帮你少绕弯路。多帧点云积累问题。单帧雷达点云太稀疏尤其在交通流量小的路段一帧里一辆车可能就三五个点聚类后稳定性很差。我的做法是做多帧积累把连续几帧的点云拼到一起再聚类。但这带来一个副作用车辆在运动点云会拖影。必须在积累前用雷达本身测量的径向速度做运动补偿不然一个静止目标会被拉成一条线聚类结果自然就乱了。运动补偿的基本思路是按本车速度和目标径向速度推算位移把不同帧的点补偿到同一时刻坐标系下。做得精细一点还要考虑本车横摆角速度。地面杂波和静态点处理。道路上的地面反射、雨水杂波、护栏引起的多径反射会在聚类结果里形成不必要的簇。雷达点云一般包含RCS雷达散射截面积信息地面杂波的RCS特征通常比较弱且不稳定可以设一个RCS阈值先把明显杂波滤掉。如果雷达支持垂直视场还可以利用高度差把地面点去掉。但这个方法要谨慎路沿和减速带这类低矮目标也有些有用点滤得太狠会直接把它们从点云里抹掉。目标分裂与合并的内在矛盾。长车卡车、巴士在雷达里的反射点分布范围很大聚成单簇容易导致簇内点间距过大拆开又会导致一个车变成几个簇。我的处理方式是“先聚类、后合并”聚类后对相邻簇检测质心距离和速度一致性如果满足条件就合并。这个策略在目标较多的场景下比盲目调大eps更可控。合并逻辑可以参考下面的伪代码def merge_clusters(labels, points, max_distance3.0, max_speed_diff1.5): 基于质心距离和速度一致性合并可能属于同一目标的簇。 # 计算每簇质心和平均速度 # 遍历簇对满足条件则合并标签嵌入式部署的性能问题。高级辅助驾驶Controller的算力有限DBSCAN的原始实现是O(n^2)的复杂度在点云规模大的时候会吃不消。scikit-learn的DBSCAN底层用了kd-tree或者ball-tree加速效果不错但嵌入式环境下不一定有完整的Python生态。工程化部署时一般会把聚类算法用C重写或者用雷达供应商提供的库来做。如果要在Python里先验证性能建议用好numpy的向量化计算避免在Python层写三重循环这个优化往往能把单帧处理时间从几百毫秒降到几十毫秒。与后续跟踪模块的衔接。聚类输出的簇只是“这一帧的目标点云”还没有时间维度。要让目标在一段时间内连续存在需要把聚类结果接入跟踪模块。雷达点云聚类后通常输出的是每个簇的质心、点数量、平均RCS和速度信息这些特征值个数不多用来关联不同帧的同一目标是足够的。做特征选择时想长远一点后续跟踪可能需要哪些量聚类代码最好在一开始就把这些量算好存好别等到接跟踪模块时再回头改数据结构。最后再说一个经验宁可花时间多试几种距离度量也不要急着改eps。雷达点云聚类的很多问题是“距离定义不对”造成的而不是“半径参数没调好”。把速度维度加进距离度量把坐标系从笛卡尔换成极坐标往往比调一天参数更管用。如果你在跑这套代码时发现聚类结果很怪优先检查坐标转换是否存在方向偏差其次检查速度维度有没有参与聚类最后再动参数。这套排查顺序帮我解决过绝大多数看起来“参数怎么调都没用”的问题。本文还有配套的精品资源点击获取