尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

激光雷达车辆跟踪仿真:从点云分割到JPDA关联的完整链路

激光雷达车辆跟踪仿真:从点云分割到JPDA关联的完整链路 简介这份压缩包是基于 MATLAB 的激光雷达车辆检测、分类与跟踪仿真程序面向自动驾驶感知、ADAS 及多目标跟踪方向的工程师和研究人员。程序以车载激光雷达点云数据为输入完整演示了接地层分割、语义分割、定向边界框拟合和面向跟踪的边界框输出并结合 JPDA 跟踪器与交互式多模型滤波器实现对高速场景中车辆的稳健跟踪适合用于算法验证、教学实验以及课程设计参考。压缩包大小仅 125KB共 12 个文件其中包含 8 个 .m 脚本/函数、3 个 .p 加密辅助函数和 1 个 .mlx 实时脚本便于直接运行和二次修改。目前已有 345 人学习下载具备一定参考价值。通过源码与实时脚本读者可深入理解点云分割、语义特征提取、三维框拟合与多目标跟踪的完整衔接掌握激光雷达感知模块从算法设计到工程实现的关键思路为后续扩展摄像头融合或多传感器方案打下基础。1. 激光雷达车辆跟踪仿真先别急着调网络把链路串起来在高速公路场景里用激光雷达做车辆跟踪真正让人头疼的不是“检测不出来”而是“上一帧和这一帧到底是不是同一辆车”。点云稀疏、车距多变、车身被遮挡检测框在帧间跳来跳去分类置信度忽高忽低此时再复杂的神经网络也救不了跟踪环节。这套 MATLAB 仿真把「地面分割 → 语义分割 → 定向边界框 → JPDA 关联 IMM 滤波」整条链放在一个可运行的示例里对于正在做感知算法验证、课程设计或者刚开始接触自动驾驶目标跟踪的同学来说它提供了教科书里很少讲到的工程化实现。尤其是那些 helper 函数里封装的细节比如重加权层、Sigmoid 层、多类初始化 IMM 滤波器值得逐个拆开看。2. 从点云到目标框流程设计与关键文件2.1 模块划分每个 helper 函数都在干一件事打开DetectClassifyAndTrackOrientedBoundingBoxInLidarExample.mlx后会看到目录里有一堆 helper 文件。最初很容易被这些文件名劝退其实把它们按职责分组脉络就清楚了。下表是常见分组方式具体细节以你打开的版本为准。分组文件职责数据与模型helperLidarModel.m维护激光雷达模型参数模拟传感器帧或读取记录帧检测前端helperLidarObjecDetector.m调用语义分割网络输出检测框、类别和置信度层封装helperReweightLayer.p、helperSigmoidLayer.p、helperResizeLayer.p网络中的自定义层执行特征尺寸调整和概率映射边界框建模helperCvmeasCuboid.m、helperCtmeasCuboid.m恒速/恒转弯模型的测量函数用于面向跟踪的边界框运动模型helperConstvelCuboid.m、helperConstturnCuboid.m目标状态预测维持 cuboid 状态量跟踪初始化helperMultiClassInitIMMFilter.m按检测类别初始化 IMM 滤波器可视化helperLidarObjectDetectionDisplay.m在鸟瞰图或三维视图中叠加检测框与轨迹.p文件并不是损坏文件而是为了保护内部实现而打包的 P 代码。运行时 MATLAB 会直接调用不需要打开。换句话说这些层在推理阶段是黑盒但通过名字和输入输出尺寸能推断作用helperResizeLayer把任意尺寸的点云特征统一到网络输入尺寸helperSigmoidLayer把网络输出压缩到 0-1helperReweightLayer则对分类特征做加权让网络更关注车辆相关区域。这种用自定义层做后处理的思路值得在自己训练的网络里借鉴。提示.p文件可以正常被调用但不建议手动改名或移动P 代码与具体 MATLAB 版本存在绑定关系。2.2 主流程一条完整的检测分类跟踪链路整个示例的流程可以概括为对每一帧点云先用接地层分割去掉地面剩下的点送进语义分割网络得到每个点的类别再对类别为车辆的点做聚类和定向边界框拟合最后把连续帧的检测框送入 JPDA 跟踪器通过多模型滤波平滑轨迹。我一般会把这条链路画成四段预处理、检测、拟合、跟踪。调试时逐段检查而不是直接从端到端结果猜问题否则很容易把地面残留的误检和轨迹断点混为一谈。运行时直接打开上面的.mlx文件运行即可。首次运行需要保证 MATLAB 已经安装了 Lidar Toolbox 和 Deep Learning Toolbox否则程序会在加载网络时报错。下面的代码是读取第一帧并调用检测器的示意% 读取示例数据中的第一帧点云 % 返回值是 pointCloud 对象包含 XYZ 坐标和强度信息 frameData load(highwayLidarData.mat); % 示例数据按帧存储 ptCloud frameData.lidarData{1}; % 调用检测器得到检测结果 % 输出 detections 是结构体数组字段包括位置/尺寸/偏航角/类别/置信度 detector helperLidarObjectDetector(); % 先构造检测器需先加载网络 detections detector.detect(ptCloud, Threshold, 0.5);上面这段代码里Threshold参数用来过滤低置信度检测框。高速公路上车辆目标较大置信度阈值可以放宽到 0.3-0.5如果场景里行人较多建议把阈值调高到 0.6 以上否则误检会干扰后续的 JPDA 关联。detect方法的具体名称可能随示例版本变化如果直接运行报错就参考.mlx实时编辑器里的自动补全提示改用step方法。2.3 为什么选了 JPDA 加 IMM多目标场景的工程折中多目标跟踪有多种关联方案从最简单的最近邻到全局最优的 MHT。这个示例选择 JPDA 并不是因为它最复杂而是因为在高速公路场景下目标数量有限、杂波相对可控。JPDA 用概率加权的方式处理“一个检测可能同时属于多个轨迹”的模糊性比硬关联更稳定计算代价也远低于 MHT。配合 IMM 后同一时刻允许滤波器在恒速和恒转弯模型之间切换能处理车辆换道和转弯时的机动变化。后面第 4 章会详细讲这两个模块的参数如何调这里先建立整体印象检测器负责“看到”跟踪器负责“认住”。3. 接地层分割与语义分割把点云切成可理解的类别3.1 先分割地面能省掉一大半误检在激光雷达点云里路面点数量往往占到一半以上。如果不先把地面点去掉语义分割网络会把大量计算花在区分地面和低矮障碍物上而且地面点拟合出来的“车辆框”几乎没有意义。常见的做法是用segmentLidarGround这类函数根据点到拟合平面的距离和角度把地面点挑出来。这个示例里把接地层分割放在最前面也正是为了降低后续检测器的压力。% 使用 MATLAB Lidar Toolbox 的地面分割函数 % MaxGroundDistance点到拟合平面的最大距离单位米 % MaxAngularDistance法向量与竖直方向的最大夹角单位度 groundParams struct(MaxGroundDistance, 0.3, ... MaxAngularDistance, 6); [groundIdx, nonGroundIdx] segmentLidarGround(ptCloud, groundParams); % 取出非地面点单独处理 nonGroundCloud select(ptCloud, nonGroundIdx);MaxGroundDistance设得太大会把小土坡和路沿也划成地面目标点会被误删设得太小则地面割不干净残留下大量噪点。高速场景一般取 0.2-0.4 米城市颠簸路段可以放大到 0.5同时把MaxAngularDistance放到 8 度左右。这个函数返回的是逻辑索引select用于生成只含非地面点的新点云。如果你使用的示例版本没有调用segmentLidarGround那说明地面分割被合并进了网络分类这部分参数就用不上了可以直接看下一个小节。3.2 语义分割网络自定义层和类别输出经过地面分割后的点云仍然包含车辆、护栏、绿化带、路牌等多种物体。为了确定每个点属于哪一类示例用了一个语义分割网络对整帧点云做逐点分类。从目录中的 helper 函数可以看出网络在推理时经过了尺寸调整和 Sigmoid 激活两个额外步骤。Sigmoid 层可以把 logits 压到 0-1 之间适合多标签输出如果网络输出的是每个类别的概率也可以用 softmax。这里的重加权层则是为了让网络更关注车辆相关点因为场景里大部分点都是背景不重采样或加权的话训练出来的模型会倾向于把所有点都预测为背景。网络输出的结果是一个与点云点数相同的标签向量。常见的数据类型有两种一种是 categorical 数组类别名直接可读另一种是 uint8 数字编码需要查表映射。我一般会先用一段代码快速确认类别列表% 检查分割标签的类别和数量避免后面索引越界 segLabels semanticseg(nonGroundCloud, net); categories(segLabels) % 显示所有类别名 % 只保留车辆类别 vehicleIdx segLabels vehicle; vehicleCloud select(nonGroundCloud, vehicleIdx);semanticseg是 MATLAB 深度学习推理的标准函数输入可以是图片也可以是预处理后的点云张量。第一个输出segLabels是 categorical 数组第二个返回值是 score 矩阵通常取最大得分作为最终标签。需要说明的是不同训练数据集的类别命名不一样有的版本会把“vehicle”细分为 car、truck、bus有的是统一成一类。在调后续跟踪参数前最好先打印categories(segLabels)确认具体类名否则类别名写错会导致空点云跟踪器完全收不到检测。3.3 从语义分割到候选框的中间步骤拿到车辆点云之后还不能直接跟踪因为同一辆车的点往往被聚成好几块不同车的点也可能因为距离近而连在一起。常见的处理顺序是先对车辆点做欧式距离聚类再用每个簇的点云拟合三维定向包围盒。聚类阈值的选取直接影响后续拟合质量阈值太小会把一辆车劈成两段阈值太大会把相邻车辆粘连。高速公路车距较大用 1.0-1.5 米的距离阈值通常够用。% 对车辆点按欧式距离聚类最小簇点数为 20 clusterIdx pcsegdist(vehicleCloud, 1.2, NumClusterPoints, 20); % 对每个簇单独拟合有向包围盒得到中心点/尺寸/偏航角 % 在示例中helperCvmeasCuboid 负责把状态向量转成测量框 % 拟合后的检测结果会传给 trackerJPDApcsegdist的第二个输入是阈值第三个参数NumClusterPoints是可选参数表示小于指定点数的簇直接丢弃这样能滤掉距离很远的孤立点。拟合包围盒时需要同时返回长宽高和偏航角而偏航角的估计稳定性最容易出问题——点云一旦稀疏角度可能会跳变。比如一辆车离 ego 车 80 米远时点云可能只有十几二十个点角度估计结果每帧差 10 度以上。这时候先不要急着调检测网络而是应该靠第 4 章的滤波模型来平滑角度变化。4. 定向边界框拟合与 IMM 滤波把检测变成稳定轨迹4.1 定向边界框的状态表示激光雷达检测输出的是三维框而不是二维矩形因此状态向量至少包含中心点位置(x,y,z)、尺寸(l,w,h)和偏航角theta。这个示例里的helperConstvelCuboid和helperConstturnCuboid正是为了在状态预测中维持这种 cuboid 形状。相比普通的点目标运动模型cuboid 模型额外把尺寸和朝向放进状态里好处是跟踪结果可以直接用于下游的决策模块缺点是状态维度更高模型切换更容易发散。% 以恒速模型为例状态向量定义如下 % state [x; y; z; l; w; h; theta; vx; vy; vz] state [35.2; 1.8; 0.1; 4.5; 1.9; 1.5; 0.35; 12.0; 0.2; 0]; % 调用 helper 函数做一步预测 predState helperConstvelCuboid.predict(state, dt);dt是两帧之间的时间间隔可以由传感器时间戳计算得出通常激光雷达是 10 Hz 或 20 Hz。上面状态中尺寸被当作常量参与预测这样滤波器不会因为检测框大小抖动而过快改变尺寸估计偏航角和速度则正常传播。如果你发现某辆车在直线道路上方向角仍频繁翻转多半是状态初始化的偏航角方差设得过大可以把初始协方差矩阵中 theta 对应的元素从 0.1 降到 0.02。4.2 IMM 滤波何时该相信恒速模型高速公路上的车辆大部分时间保持近似匀速但并线或进出匝道时会出现明显的横向机动。单一恒速模型在机动阶段会产生很大偏差单一恒转弯模型在直线段又容易过拟合噪声。IMM 的思路是同时维护两个模型每个时刻根据量测残差重新计算两个模型的概率最终输出是加权融合结果。这个示例里的helperMultiClassInitIMMFilter.m还做了一件事根据检测到的目标类别选择不同的初始噪声参数。卡车惯性大过程噪声可以比轿车小一些轿车变道灵活角速度噪声则需要给得更足。% 构造一个双模型 IMM 滤波器 % 模型1恒速模型2恒转弯 immFilter helperMultiClassInitIMMFilter( ... NumModels, 2, ... ModelTypes, {constvel, constturn}, ... ModelProbabilities, [0.9, 0.1]); % 初始概率偏向恒速 % 设置过程噪声 immFilter.ProcessNoise(1) 1.0; % 恒速模型过程噪声 immFilter.ProcessNoise(2) 2.5; % 恒转弯模型过程噪声ModelProbabilities表示初始时认为目标处于恒速状态的概率为 0.9。高速直行场景这样设置很合适如果是城市十字路口建议把恒转弯的初始概率提高到 0.3 以上。ProcessNoise两个维度的含义不同第一个模型对应加速度噪声第二个模型对应角加速度噪声调参时最好分开观察不要笼统地一路加到很大否则轨迹会严重抖动。另一个经验是如果跟踪结果在变道时滞后明显优先增加恒转弯模型的过程噪声而不是降低恒速模型的噪声。4.3 JPDA 关联处理检测框之间的“你争我抢”在一个目标密集的场景里单个检测框可能落在多个预测门内。JPDA 的做法是计算每个检测与每个轨迹的关联概率再用概率加权后的测量更新轨迹。与全局最近邻相比JPDA 不需要强制把一个检测分配给唯一轨迹所以即使两辆车短暂交错也不会因为一次误分配就丢失轨迹。实现时重点看两个参数关联门限GateThreshold和检测概率DetectionProbability。门限设得过大远处噪声会被拉进轨迹设得过小真实检测会被拒之门外。% JPDA 跟踪器参数示意 tracker trackerJPDA(FilterInitializationFcn, initIMMFilter, ... GateThreshold, 25, ... DetectionProbability, 0.85, ... AssignmentThreshold, [30 200], ... ConfirmationThreshold, [2 3], ... DeletionThreshold, [5 5]);GateThreshold是马氏距离平方阈值25 对应约 5 个标准差比较宽松。如果场景中虚假检测多可以缩到 16-20。ConfirmationThreshold表示一个轨迹需要被连续确认多少次才正式输出避免单帧误检造成虚警。AssignmentThreshold里第一个值是分配的代价值上限第二个值是漏检代价上限两者相差通常保持 5-10 倍。实际调试中我一般先固定GateThreshold再调整DeletionThreshold让短暂遮挡的轨迹不被立刻删除具体数值取决于目标被大车遮挡的频繁程度。5. 调试验证让检测框和轨迹对得上5.1 可视化验证的小技巧示例里helperLidarObjectDetectionDisplay.m会把检测框和跟踪轨迹同时画在三维场景中。我建议在调试初期把点云显示改成单一颜色只保留检测框和轨迹线否则点云密度太大轨迹断点根本看不清。如果发现检测框已经正确但轨迹没跟上优先检查跟踪器的确认阈值而不是怀疑检测网络。% 从正上方观察检查检测框是否压住车道以及角度是否合理 ax helperLidarObjectDetectionDisplay(ptCloud, detections, tracker); view(ax, [0 0 1]) % 俯视视角view在 MATLAB 三维场景里很常用。俯视视角能直观看到框的偏航角是否与车道方向一致。另一个技巧是把连续几帧的轨迹点连成线如果轨迹出现锯齿说明过程噪声偏大如果轨迹在目标换道时跟不上说明过程噪声偏小或恒转弯模型概率太低。每次修改参数后只用这一帧来观察不够至少要跑几十帧看稳定后的轨迹。5.2 参数调试的先后顺序不要一上来同时调十个参数。我一般按这个顺序先调地面分割阈值直到非地面点中看不到大块路面再调检测置信度阈值确保车辆完整召回接着调聚类阈值和包围盒拟合的最小点数最后才动跟踪器参数。每调一步就看对应的中间输出比如分割后点云、检测框、关联结果而不是只看最终 RMSE。因为跟踪器的关联错误往往会掩盖前面模块的问题调完跟踪再回头改检测成本更高。5.3 一套能快速复现的验证脚本把下面这段脚本放到示例目录下可以自动统计连续 100 帧的轨迹数。如果轨迹数接近真实车辆数且中途断档少说明参数基本可用如果轨迹数远大于检测数通常是确认阈值太低把单帧误检当成了轨迹。numFrames 100; trackCount zeros(numFrames, 1); for i 1:numFrames ptCloud frameData.lidarData{i}; detections detector.detect(ptCloud); trackCount(i) numel(tracker.tracks()); end plot(trackCount)这段代码只是验证思路实际运行前要根据 helper 函数接口把 detector 和 tracker 对象构建好。重点是观察轨迹数曲线是否存在周期性掉坑如果掉坑位置总是出现在三车道右侧大概率是那个区域点云稀疏导致检测漏检如果掉坑时间固定间隔两秒则需要检查跟踪器的删除阈值是否把短暂遮挡的目标提前删掉了。最后再回到DeletionThreshold或检测置信度上做局部调整。本文还有配套的精品资源点击获取
返回列表