
多传感融合学习笔记写到第五篇终于轮到图像目标识别这个关键环节。前面的笔记大多在聊传感器模型、坐标变换、点云数据结构摄像头这块一直没正式展开。原因很简单图像原始数据如果不先转换成“哪里有什么目标”的结构化信息摄像头在多传感融合系统里基本等于摆设。这篇笔记的核心就这么一件事利用YOLO完成图像目标识别把每一帧画面里的车、人、自行车、红绿灯都框出来。做完这一步后面才能拿检测结果去和激光雷达点云、毫米波雷达测距数据对齐做真正的多传感器融合。这篇笔记适合两类人。一类是刚开始接触多传感融合、想知道YOLO在整条感知链路里到底站在什么位置的另一类是已经能用YOLO跑出检测框但不知道2D框怎么和3D点云结合起来的。我会把原理、实操、融合接口和排坑经验一起串起来讲尽量让不同基础的读者都能跟得上。1. 融合框架里为什么必须先搞定图像识别1.1 把图像变成目标列表摄像头在融合里负责什么先说清楚一个前提多传感融合不是把传感器数据随意拼在一起而是让不同传感器互补短板。典型配置是三件套摄像头、激光雷达、毫米波雷达。摄像头信息稠密、有颜色和纹理但缺少深度受光照影响大激光雷达能给出精确的3D坐标点云但点云本质上是稀疏的坐标集合没有语义信息机器不知道这一堆点是一个人还是一棵树毫米波雷达有速度和距离信息但分辨率低很难单独区分相邻目标。在这种配置下融合的第一级任务往往是让摄像头先“看明白”。只有图像能把物体类别讲清楚。所以YOLO要解决的核心问题就是把一帧图像变成一条结构化的目标列表列表里每一项包含目标类别、2D边界框位置和置信度。有了这个列表激光雷达点云才有对象去对应融合后端才知道哪些点云属于同一个目标进一步估计目标相对自车的距离和速度。这也是整个感知链路的第一步。摄像头视频流进来后先做目标识别然后YOLO输出的2D框作为“先验区域”引导后续点云处理。如果这一步漏检或误检后面所有融合环节都会被带偏。1.2 实时性考量为什么课程选YOLO而不是两阶段检测器入门目标检测时最先接触的往往是两阶段检测器比如RCNN系列。这类模型的思路是先借助区域提议网络找出可能包含目标的候选框再对每个候选框做分类和位置回归。这样做精度确实高但速度慢得离谱一帧图像在普通GPU上也要几百毫秒起步完全没法满足车载场景对实时性的要求。YOLO走的是另一条路。它把目标检测当成一个回归问题来处理一次前向推理直接把边界框坐标、目标类别、置信度全部输出。少了区域提议这个大环节速度优势非常明显。在GPU上对一段视频做推理YOLO通常能把每帧处理时间压到几十毫秒级别这才让“摄像头每秒处理30帧、实时形成目标列表”成为可能。课程选YOLO还有一个很现实的原因它的开源生态成熟、预训练权重丰富。无论是官方提供的COCO预训练模型还是社区里一堆现成的推理脚本拿过来就能用。做多传感融合项目的人模型只是一个组件不值得花大量时间从底层训练一个检测器。把成熟轮子用起来把精力放在融合链路本身才是正确的投入方向。1.3 YOLO在感知架构中的定位我在这个模块里跑通的感知流程大概是这样的摄像头视频流按帧送入YOLO得到2D目标框列表。激光雷达点云通过联合标定的外参矩阵投影到图像平面。投影后落在某个检测框区域内的点云被裁剪出来作为该目标的ROI点云。对ROI点云做聚类和测距拿到这个目标相对自车的距离、尺寸、运动状态。把目标级数据交给后续的跟踪和决策模块。从这个流程能看出YOLO输出的2D检测框在这里不完全是一个终点结果它更像是一个“空间索引”。更直白地说融合系统利用2D框把全量点云切成若干有语义标签的局部点云块从而把“识别目标”和“测量目标”两件事绑定在一起。这是YOLO在多传感融合里最核心的用途。2. YOLO检测原理与输出张量的正确打开方式2.1 一次前向推理直接给框单阶段思想YOLO的核心思想可以用一句话概括把检测任务当成回归问题整张图像只看一次直接得到所有目标的位置和类别。具体做法是把输入图像划分成S×S的网格。每个网格负责预测那些中心点落在该网格内的目标。每个网格会产生B个候选边界框每个候选框包含五个量中心坐标x、中心坐标y、宽度w、高度h以及置信度confidence。同时每个网格还会预测C个类别的概率分布表示如果这个网格内有目标它属于各类别的可能性。置信度这个量初学者容易混淆。它由两部分组成一是这个网格里有没有目标二是预测框和真实框的重合程度。数学上写作Pr(Object) × IoU(pred, truth)。也就是说置信度既要表达“这里有没有东西”又要表达“框得准不准”。最终判断一个候选框是不是真目标时还要把类别概率和置信度乘起来得到一个综合得分再拿这个得分去和阈值比较。速度快的根源就在这里。两阶段检测器要把“找出候选区域”和“识别区域”拆成两步YOLO把这两件事压缩进同一个网络并且一次性输出所有目标的坐标和类别。代价也有在极密集、极小目标的场景下YOLO的精度会略逊于精心调优的两阶段模型。但在实时融合系统里这个取舍是合理的。2.2 网格、锚框和多尺度输出张量形状怎么读以YOLOv3的模型结构为例它是最容易帮助理解多尺度检测的版本。输入一张416×416的图像网络输出不是单一的结果而是三个不同尺度的特征图分别对应大目标、中目标和小目标。检测尺度特征图尺寸感受野特点主要负责的目标大目标尺度13×13感受野大全局信息充分车、公交车等大面积目标中目标尺度26×26感受野中等行人、骑行者等常规目标小目标尺度52×52边缘细节保留多远处的行人、小物体多尺度输出的意义在于浅层特征图保留了更多空间细节适合锁定小目标深层特征图经过更多卷积和池化语义信息更丰富适合判断大目标。让模型同时在多个尺度上做检测能显著缓解小目标漏检的问题。锚框anchor box是另一个初学者容易懵的概念。简单理解锚框是预先设定的一组边界框尺寸先验。YOLO并不直接预测目标的绝对宽高而是预测“相对某个锚框的偏移量”。这么做相当于给回归任务一个初始参考点训练更容易收敛。通常锚框尺寸由聚类算法在训练集上统计得到不同数据集、不同目标比例下锚框设置会不一样。如果你看到输出张量形状是(batch, 255, 13, 13)其中255代表3×5类别数这里3就是每个网格位置预测的锚框数量5是x、y、w、h和置信度。解码时要把这个张量拆解成若干个候选框再经过筛选才能得到最终检测框。理解这个解码过程比背一个模型结构图要有用得多。2.3 置信度阈值和NMS怎么设置模型输出的候选框数量非常多大部分都是低置信度或者重叠的框。后处理阶段通常做两步操作。第一步按综合得分过滤。所有得分低于置信度阈值的候选框直接丢弃。阈值调得高误检少但漏检多阈值调得低漏检少但画面会很乱。以我的实测经验在通用场景下置信度阈值设在0.4左右比较合适。跑官方COCO权重、画面比较干净时0.35到0.5之间都合理具体要看应用对漏检和误检的容忍度。第二步对剩余候选框做非极大值抑制NMS。思路是如果两个框都框住了同一个目标且交并比IoU超过阈值就保留得分更高的那个抑制另一个。NMS的IoU阈值如果设得偏大比如0.7两个相近的框容易被合并成一个相邻的不同目标可能被误认为同一个设得偏小同一个目标周围可能出现多个重复框。我用下来比较稳定的区间是0.4到0.5密集行人场景可以进一步下调到0.3左右。边界情况也要注意如果画面里目标本来就非常密集比如行人成群走过NMS会特别纠结。这时候与其反复调阈值不如从训练数据层面增加密集场景样本让模型自己学会区分相邻目标。3. 从2D框到3D点云融合的关键接口3.1 坐标标定像素坐标是怎么算出来的图像目标检测得到的是像素平面上的2D框点云是3D空间中的坐标点把两者对应起来靠的是传感器标定。整个标定链路的公式大致可以写成z_c × [u, v, 1]^T K × [R | t] × [x_lidar, y_lidar, z_lidar, 1]^T这里[u, v]是像素坐标K是相机内参矩阵它描述焦距、主点等相机自身属性。[R | t]是外参矩阵描述雷达坐标系相对于相机坐标系的旋转和平移。标定的本质就是求出这一组旋转和平移参数。实际操作中一般会使用标定板在相机画面里提取角点在雷达点云里找到对应的3D特征构造多组对应点对后求解PnP问题。整个过程说复杂也复杂说简单也简单最怕的是“标完一次就扔着不管”。融合系统里设备支架稍微动过几毫米、相机换过镜头、车体重新装配过外参都可能偏移投影结果就会歪掉。这个公式也解释了为什么融合实验里“投影错位”是高频问题。很多时候不是YOLO检测的问题而是K矩阵和[R | t]矩阵本身没对齐好。所以做融合项目一开始就把标定流程规范化远比临时抱佛脚调检测参数要重要。3.2 检测框变成点云ROI算力和噪声一起降YOLO检测出2D框之后下一步通常是把2D框扩展到三维空间形成一个视锥区域也就是从相机光心出发经过2D框四个角点向外延伸的四棱锥体。激光雷达点云中只有落在这个视锥范围内的点才被保留下来作为这个目标的候选点云。这个操作的价值是双重的。第一计算量大幅下降。全量点云可能有几十万个点对每个点做聚类分析成本很高裁剪成若干ROI后只需要在局部点云上做处理实时性压力小多了。第二噪声减少。远处杂散的反射点、地面点往往因为不在检测框投影范围内而被过滤掉点云质量更干净。实际实现时并不一定要严格构造三维锥体。更常见的做法是这么几步拿到检测框四个角的像素坐标。把雷达点云通过标定矩阵投影到图像平面得到每个点对应的像素位置。判断这个像素位置是否落在检测框内是则保留否则丢弃。对保留的ROI点云做聚类用聚类结果计算目标距离、尺寸等物理量。这个流程在课程里被我简称为“先框后聚”。它有个天然优势因为2D框提供了目标的语义身份ROI点云天然携带“这是行人还是车辆”的标签后续不用再做物体分类省掉一个分类模型。如果在某帧里发现某个检测框内点云特别少先别急着怪天气或传感器故障。有可能是检测框太紧把目标的真实边缘切成两半。也有可能是时间对齐出问题导致点云和图像不是同一时刻的数据。这两个因素在融合实验里都很常见。3.3 时间同步30帧相机和10Hz雷达怎么对齐传感器融合还有一个工程上绕不开的问题时间同步。摄像头通常工作在30FPS激光雷达常见工作频率是10Hz两路数据传输到处理器的时间也不一样。如果直接把“最近一帧图像”和“最近一帧点云”强行配对运动中的目标会出现框和点错位。解决思路是给每帧数据打上时间戳各自维护一个缓存队列。图像帧到达时去点云队列里找时间戳最接近的那一帧点云如果两者时间差超过预设阈值比如50毫秒就宁可丢弃这次配对等下一帧也不要拿陈旧数据硬算。对于高速运动目标这种方式能明显减少框和点云之间的错位。这个细节看起来不起眼但我在实验里吃过亏。最初没做时间戳匹配只是简单用“最新接收到的雷达数据”结果自车前方目标稍有移动ROI就偏出去一两米。后来改成时间戳对齐效果立刻正常了。工程问题有时候就是这么朴实解决方案不玄学但确实影响结果。4. YOLO实操过程记录环境、命令、参数与现场效果4.1 环境准备与模型选型我是按YOLOv5的代码仓库来做实验的原因很简单文档全、依赖安装简单、训练推理一条命令搞定。环境搭建步骤如下conda create -n yolo python3.8 conda activate yolo pip install torch torchvision pip install opencv-python git clone 官方YOLOv5仓库 cd yolov5 pip install -r requirements.txt如果机器有NVIDIA显卡建议装对应版本的CUDA版PyTorchCPU跑推理也能出结果但视频实时性很难保证。我最初在纯CPU环境跑一帧要几百毫秒基本没法用。换到GPU环境后速度提升一个量级。所以环境准备的首个建议是确认你的机器能调用GPU再开始玩目标检测。模型选型方面YOLOv5系列给了s、m、l、x等尺寸选项对应不同的模型深度和宽度。我在课程实验里默认用yolov5s优先保证跑得动、跑得快。几个型号对比大致是这样模型精度推理速度显存占用适用场景yolov5s适中最快最低实车实验、入门复现yolov5m较好较快中等一般实验项目yolov5l好慢较高离线分析、高精度需求yolov5x最好最慢很高离线处理、服务器选型原则很朴素能用小模型解决问题就不上大模型。检测精度不足时先尝试提高输入分辨率、增加训练数据最后再考虑增大模型体积。盲目上大模型显存先撑不住帧率也掉得厉害。4.2 推理命令与关键参数用官方预训练权重做推理一条命令就够了python detect.py \ --source road.mp4 \ --weights yolov5s.pt \ --conf-thres 0.4 \ --iou-thres 0.45 \ --img 640这里每个参数都有讲究。--source可以是视频文件、图片目录、单张图片或者摄像头ID--weights指定模型权重文件--conf-thres是置信度阈值--iou-thres是NMS的IoU阈值--img是输入图像尺寸。运行完之后检测结果视频会输出到runs/detect/xxx目录下。第一次跑的时候我直接用了默认参数图上全是低置信度的框远处自行车、墙上的海报、路牌都被勾了出来画面乱成一团。把--conf-thres从0.25调到0.4之后结果干净许多。这个调整不是玄学它就是在漏检率和误检率之间做权衡。想要暴力一点就设低阈值保证所有疑似目标都被框出来想要画面干净、直接拿去做融合任务就适当调高。还有一个容易忽视的点--img分辨率。同样一个目标在640分辨率下可能只有十几个像素检测器根本没法识别把它喂到1280分辨率下小目标就会清晰很多。代价是推理时间变长、显存占用变大。实用建议是先按视频内容尝试两档分辨率观察小目标漏检有没有明显改善再决定要不要长期使用高分辨率。4.3 自定义数据集微调流程课程实验的最后阶段需要在自己的数据集上做微调。整个流程可以概括为采集或整理图像、标注、配置数据集描述文件、启动训练。标注格式用的是YOLO格式每个标注文件是一行一行的文本每行包含类别编号、归一化后的中心点x坐标、中心点y坐标、宽度w、高度h。比如一张图像对应一个txt文件内容是“0 0.5 0.4 0.2 0.3”这种格式。目录结构通常这样组织data/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml里写明路径、类别数量和类别名称。配置正确之后一句命令就能开始训练python train.py \ --data data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100这里必须说一个容易犯的错误不要从零开始训练一定要用官方预训练权重作为起点。目标检测模型的骨干网络部分用于提取特征在大规模数据集上已经学得很好了微调阶段只需要让它适应你的数据分布。从零训练既慢又容易不收敛完全没必要。训练时要关注的指标不仅有最终的mAP还有训练曲线是否平稳。如果loss曲线震荡得厉害优先检查学习率是否过大、batch size是否太小、数据标注是否一致。我见过不少新手数据漏标了一堆目标训练过程看起来在跑其实模型学的全是噪声。4.4 实测效果观察我把一段白天城市道路视频喂给模型做实测。车辆检测很稳定普通轿车、SUV、大客车都能框上行人检测整体不错但远处小目标和被部分遮挡的行人会偶尔漏检骑行者在某些帧会被误判成行人因为轮廓确实有点接近。推理速度方面GPU环境下每帧大约40毫秒左右做融合实验够用。换到夜间视频后问题立刻就暴露了行人漏检明显增加低照度环境下图像对比度不足很多目标在图像里灰蒙蒙一片检测器识别不出来。这个结果不是YOLO的缺陷而是摄像头传感器的物理限制。这也顺带证明了融合系统的必要性当摄像头在夜间“看不见”时激光雷达和毫米波雷达依然能提供有效数据补上检测链路的空缺。多传感融合不是做不做的问题而是必须做的问题。5. 排坑实录我从这个模块踩过的坑5.1 小目标漏检的连锁反应小目标漏检是目标检测的老大难。在融合场景里这个问题会被放大YOLO没框出来后面的点云ROI裁剪根本不会发生目标直接就从整个感知系统里消失了。排查思路从几个方向入手。先看输入分辨率如果检测时用的--img是640甚至更低远处的车辆可能只有十几个像素换到1280分辨率会有明显改善。再看置信度阈值有些目标其实被检测出来了只是得分略低于阈值而被丢弃适当调低阈值就能捞回来。最后看训练数据模型如果没见过大量小目标样本推理时自然对它们不敏感这种就得靠数据增强和收集小目标样本解决。在融合层面也有一个补救办法对没有检测框、但点云中有明显聚类特征的空间区域做“无标签检测”再用雷达数据反推目标的存在。但这只能作为辅助手段不能替代检测器。目标级融合最终需要语义标签能靠检测器解决的问题最好在检测器层面解决。5.2 重叠目标的NMS问题两个行人并肩走时模型经常只输出一个框或者两个框来回跳。这个问题的根源通常在NMS的IoU阈值上。阈值设得偏大时两个重叠度高的目标框会被当成同一个目标互相抑制只剩一个阈值偏小相同目标周围又会有多个重复框。针对行人这类细长目标我习惯把NMS的IoU阈值调低一些比如0.3到0.4。另一个思路是在训练时增加重叠目标的样本占比让模型自己学会区分相近目标。运行时的后处理参数能解决一部分问题但真正靠谱的方案还是让模型在大量密集场景上见过足够多样的情况。5.3 显存不足与推理延迟课程实验用的机器显存有限最初想直接跑yolov5x模型结果前向推理刚跑起来就显存溢出程序直接崩掉。换成yolov5s之后问题立刻消失显存占用大概在2G到3G之间推理速度也回到了实时范围。如果你的机器显存紧张几个立竿见影的调整方式模型尺寸从x或l降到s或m显存成倍下降。推理时增加--half参数开启半精度浮点计算显存占用进一步降低。batch size在推理场景下保持为1即可没必要追求更大。关闭可视化窗口和视频流实时渲染减少额外的GPU开销。显存告急时优先跑通功能后续再考虑用模型压缩或推理加速框架来提升性能。不要一开始就在最重的模型上卡住先把整个融合链路跑顺比单模块堆精度重要得多。5.4 融合投影整体错位这个坑在我实验里耗的时间最长。单看YOLO检测结果没有任何问题边界框框得很准单看点云也没有问题激光雷达数据正常。可一旦把点云投影到检测框里框是正的点却整体偏了半米。这就是融合链路里典型的标定或同步问题。排查顺序有讲究。先做静态检查单独投影几帧点云到图像上观察点云轮廓和实际物体的边缘是否对齐。如果整体偏了优先怀疑外参矩阵检查旋转矩阵和平移向量是否有符号或方向错误。再查时间戳图像和点云是不是同一时刻的数据。最后检查相机内参镜头畸变校正参数如果缺失远端目标投影会明显偏移。我最后定位到的问题是外参矩阵中的平移向量符号写反了。排查过程很磨人但教训很清晰融合链路里一旦出现“单模块都好、合起来不对”的情况八成在接口层不是算法层。先把输入数据的坐标系、时间戳、矩阵维度这些基础项核对干净再回头怀疑算法。5.5 检测置信度始终偏低有时候模型能框出正确目标但得分始终只在0.3到0.4徘徊上不去。这个问题的根子通常在训练数据而非网络结构。目标类别在数据集中出现频次太低模型对它的特征学习不充分或者目标经常处于遮挡状态完整特征没机会暴露或者训练集和推理场景之间的光照、视角差异太大。解决办法有几个方向。数据层面扩充困难样本对低置信度目标做过采样用图像增强模拟更多遮挡和光照变化。训练层面可以适当调整类别损失权重让模型在训练时更关注弱势类别。运行层面把置信度阈值调低把这类目标先捞出来再说代价是误检率上升。如果模型在验证集上表现正常一到实际场景就掉点那不是参数问题是数据分布问题。重新采集场景数据、重新标注远比在推理脚本里折腾阈值更有效。这个模块学完之后我最直观的感受是YOLO本身已经是很成熟的工具真正难的是把它和整个传感器系统缝合起来。我在实验里花时间最多的不是改网络结构而是调置信度阈值、对时间戳、检查外参矩阵。做出一个能跑的检测脚本很容易做出一个能和点云稳定配对的检测模块需要的是对传感器接口细节的耐心。下一步我打算把YOLO检测结果接到目标跟踪模块里处理连续帧之间目标ID保持的问题等跑通了再来更新这个系列的笔记。