ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YOLOv11红外与可见光双传感器融合目标检测与追踪实战

YOLOv11红外与可见光双传感器融合目标检测与追踪实战 简介面向计算机视觉与目标追踪开发者YOLOv11红外与可见光双传感器目标追踪实践文档系统梳理了跨模态融合在复杂环境下的目标检测与跟踪方法重点解决单传感器在夜间、低光照或遮挡场景中性能受限的问题。资源为单个PDF电子文档共38页压缩包约2.18MB支持章节跳转阅读器左侧可显示大纲并快速定位目录。已有468人学习使用。文档内容完整涵盖YOLO系列发展历程、YOLOv11骨干网络/颈部/检测头结构及其工作原理与训练方法红外与可见光传感器原理及数据特性对比数据级、特征级、决策级三类跨模态融合策略的具体实现还包含基于多模态数据融合的YOLOv11网络结构改进、目标特征增强与匹配优化、运动模型预测优化以及安防监控、智能交通、工业检测、农业监测等应用案例分析兼具理论讲解与工程参考价值适合算法研究者、AI工程师及相关专业学生拓展技术视野。1. 跨模态融合实践在解决什么夜间检测的“睁眼瞎”问题夜间、雾天、强背光这三个场景随便遇到一个纯可见光的目标检测模型基本就成睁眼瞎红外图能看清温度轮廓可纹理细节太弱单独丢给 YOLO 又容易误检漏检。标题里这套“跨模态融合实践-YOLOv11红外与可见光双传感器目标追踪”做的就是让这两种互补图像在一套网络里协作用 YOLOv11 同时吃进红外和可见光两路输入在光照不足、目标被遮挡或背景杂乱时依然能稳定检测并进一步输出连续轨迹。它面向的是正在做双传感器感知落地的工程师解决的是“单模态模型白天能用、晚上就翻车”的痛点也适合刚入门想找一条可靠融合路线的同学。下文从选型、数据对齐到训练参数和踩坑记录给出一条能直接照着做的路径。2. 为什么用 YOLOv11 做双传感器融合从网络结构到通道拼接2.1 YOLOv11 相比前代改了什么融合任务更吃哪些改动YOLOv11 发布后很快被拿来替换 YOLOv8核心变化集中在主干和颈部。主干里 C3k2 模块替代了部分 C2f 结构计算量更低梯度传递更直接颈部 SPPF 依然保留但整体特征复用方式更精简。检测头继续走 anchor-free 路线分类和回归分支解耦对尺度和位置预测更干净。这些改动对红外与可见光融合来说并不只是“跑分更好看”而是直接关系到两路图像的特征能不能在一个网络里有效汇合。红外图的特点是高对比度但低纹理可见光图则纹理丰富却容易受光照干扰。融合的前提是网络在浅层能同时保留“形状边缘”和“热辐射轮廓”两类信息。YOLOv11 的主干第一层就是普通卷积加 BN 加 SiLU输入通道可以自由扩展这给通道级拼接留下了很直接的入口。颈部的高分辨率特征层对 32×32 以下的小目标敏感而红外传感器常见场景恰恰是小目标比如远处的行人或车辆这两点匹配度很高。实际做融合时我一般只在意三个结构点第一层卷积的输入通道能改成 6颈部 P3 层有足够分辨率去感知小尺寸热源检测头的分类分支能够区分“红外高亮但可见光不可见”的虚警。YOLOv11 这三处都具备改造成本比换用更重的双流网络低得多。2.2 双输入通道改造把红外图和可见光图“拼”进网络入口最常见的通道级融合做法是把红外图归一化后与可见光图在通道维直接拼接得到一个 6 通道输入然后修改主干第一层卷积的输入通道数量。这个做法被许多开源项目采用工程上最省事也最容易调试。实现时可以读取官方预训练权重把第一个卷积的权重复制过去原可见光部分保留原始核新增的红外通道用原权重在通道维求平均初始化避免激活值量级突变。import torch import torch.nn as nn # 假设 model 是加载好的 YOLO 模型第一层卷积记为 stem_conv # 这里创建一个输入通道为 6 的新卷积输出通道与原来一致 in_ch, out_ch stem_conv.weight.shape[1], stem_conv.weight.shape[0] new_stem nn.Conv2d(in_ch * 2, out_ch, kernel_sizestem_conv.kernel_size, stridestem_conv.stride, paddingstem_conv.padding) with torch.no_grad(): # 保留原来的可见光权重 new_stem.weight[:, :in_ch] stem_conv.weight.data # 红外通道用原权重所有通道的均值初始化 new_stem.weight[:, in_ch:] stem_conv.weight.data.mean(dim1, keepdimTrue) if stem_conv.bias is not None: new_stem.bias stem_conv.bias.data这段代码的逻辑是先读取原模型第一层卷积的输入输出通道数再建立一个输入通道翻倍的新卷积。新卷积的可见光部分沿用原权重红外部分用原权重的通道均值初始化。这样拼接后的 6 通道输入不会因为权重初始化不当导致训练初期震荡。需要注意这里说的是“常见做法”并非 YOLOv11 源码内置功能具体层名和结构以你实际加载的模型文件为准。2.3 权重下载与预训练选择做融合训练时不要从零初始化红外数据通常几百到几千张网络很难自己学到有效的底层特征。直接用官方预训练权重微调收敛速度和最终精度都更好。在 ultralytics 命令行或者 Python 里执行YOLO(yolo11n.pt)首次运行会自动把对应权重文件下载到当前工作目录无需手工找链接。预训练权重建议先用yolo11n.pt起步因为输入通道改造后早期训练稳定性更重要模型体积小、迭代快适合验证融合管道有没有问题。等流程跑通再换yolo11s.pt或yolo11m.pt按精度需求递增。这里有个很实际的经验yolo11n 在红外小目标上精度上限有限如果夜间行人检测要求较高直接上 yolo11s 能少走一轮弯路。3. 数据准备红外与可见光的配对、对齐与标注3.1 双传感器采集的时间同步与空间配准没有配对良好的数据融合训练从一开始就是错的。双传感器采集时最优先解决的是时间同步红外相机帧率通常低于可见光常见做法是让可见光等待红外帧或采用硬件触发信号同时采集保证两路的物体位置不会因为时差偏移。软件同步的替代方案是记录每帧时间戳再用插值选取最近邻匹配但运动目标一快就容易出现残影错位精度要求高的场合不建议省硬件成本。空间配准要解决的是视角差异。两路相机安装在相近位置但不可能完全重合需要通过标定得到可见光到红外坐标的变换关系。最稳妥的方式是使用棋盘格或者高亮热源靶标拍摄一组标定图然后估计单应矩阵或相似变换。这里给出一个用 ECC 自动配准的可用脚本。import cv2 import numpy as np # rgb_gray 和 ir_gray 是同一时刻、同一场景下缩放为同尺寸的两路灰度图 # 初始化使用单位矩阵迭代优化单应性变换 warp_matrix np.eye(3, dtypenp.float32) criteria (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 200, 1e-5) # ECC 迭代估计可见光到红外的 warp 矩阵 cc, warp cv2.findTransformECC( cv2.cvtColor(ir_gray, cv2.COLOR_GRAY2BGR), cv2.cvtColor(rgb_gray, cv2.COLOR_GRAY2BGR), warp_matrix, cv2.MOTION_HOMOGRAPHY, criteria, None, 1 ) # 用估计出的矩阵把可见光图变换到红外视角 aligned_rgb cv2.warpPerspective(rgb_gray, warp, (ir_gray.shape[1], ir_gray.shape[0]))这个脚本先把两路图转成三通道以满足 ECC 接口要求再用单应性迭代配准。MOTION_HOMOGRAPHY是对视角差异较大的场景最可靠的选择如果两路相机装配足够靠近用MOTION_AFFINE更稳定。配准结果要人工抽帧检查轮廓重叠误差在 1 到 2 个像素以内才能用于后续标注和训练。3.2 红外 16 位 RAW 到 8 位AGC、直方图均衡与亮度归一化红外传感器输出通常是 14 位或 16 位 RAW 数据直接当成普通图片读进去动态范围远超网络期望。训练前必须做位深压缩。简单的线性映射虽然可以保留原始对比度但夜间场景下目标温度与环境相近时会变得非常灰平。更通用做法是分两步先裁掉高低 1% 的极端值做线性拉伸再做 CLAHE 局部对比度增强。import cv2 import numpy as np raw cv2.imread(ir_16bit.png, cv2.IMREAD_UNCHANGED) # 去掉极值线性映射到 0-255 min_val, max_val np.percentile(raw, [1, 99]) norm np.clip((raw.astype(np.float32) - min_val) / (max_val - min_val), 0, 1) norm_8bit (norm * 255).astype(np.uint8) # CLAHE 增强局部纹理避免红外图过度平滑 clahe cv2.createCLAHE(clipLimit2.5, tileGridSize(8, 8)) enhanced clahe.apply(norm_8bit) cv2.imwrite(ir_8bit.png, enhanced)代码逻辑很直接percentile裁剪掉极端噪声线性映射把大部分有效像素铺满整个灰度范围最后 CLAHE 让热目标的边缘更锐利。clipLimit取值在 2.0 到 3.0 之间比较合适太低没有增强效果太高会出现明显的块状光晕。处理后的红外图再与可见光图拼接整体的像素均值和方差都接近自然图像后面的训练才不容易出 BN 抖动问题。3.3 标注格式与 YOLO 标签生成标注策略有两种一种只标可见光图通过配准矩阵反投到红外图另一种是在融合图上手工标。前者省人力但误差会随配准精度传导后者精确代价是工作量翻倍。我实际采用的方法是先在可见光图上标一次生成 YOLO 格式的 txt 标签然后用已验证的配准矩阵把框的中心点和宽高映射到红外视角生成第二份标签训练时对两路输入分别加载只在融合分支统一处理坐标。这里有一个容易忽略的细节YOLO 标签的坐标是相对于单张图片宽高的归一化浮点值。如果红外和可见光分辨率不一样配准后再做标签映射时不要忘记同步缩放。建议先统一缩放为相同尺寸再标注避免缩放链路过长。标签生成后抽 30 到 50 张图用可视化脚本叠加画框检查红外图上框中心必须落在热源最亮区域的中心附近否则不能进入训练集。4. 融合策略与训练实战通道拼接、特征级融合与超参配置4.1 输入级、特征级、决策级三种融合怎么选跨模态融合在目标检测里通常分三个层次。输入级融合就是第 2 章讲的 6 通道拼接最简单网络在底层自己去学两路特征的关系适合起步和验证管道。特征级融合是把红外和可见光分别跑主干在 FPN 层做特征相加、拼接或注意力加权效果更好但显存和工程量翻倍。决策级融合则是两路分别推理再对检测框做 NMS 合并实现最直觉但单模型效率低实际项目中除非已有两个独立模型否则不推荐。对大多数场景我建议先用输入级融合跑通同时保留一个做法训练后期把红外特征在主干中段单独拉出来做一层极轻量的 attention 融合这个思路介于输入级和特征级之间。下面这张表可以直接用于方案选型。融合方式工程成本精度收益推理速度适用情况输入级通道拼接低中高起步首选数据量 5k 以下特征级 FPN 拼接较高高中小目标密集、光线极端变化决策级 NMS 合并低低最低已有两套独立模型临时拼接4.2 从零配置环境并跑通最小训练脚本先确认环境里有没有可用的 GPU然后安装 ultralytics 包。这里给出一套适合 0 基础读者的环境配置命令PyTorch 版本以官方安装命令为准不要混装不同源的包。# 创建虚拟环境避免和系统 Python 冲突 conda create -n dualfusion python3.10 -y conda activate dualfusion # 安装带 CUDA 支持的 PyTorch按你的 CUDA 版本调整命令 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 ultralyticsYOLOv11 就包含在这个包里 pip install ultralytics环境装好后把训练数据按下面的目录结构放好datasets/ dualset/ images/ train/ seq01_frame001_ir.jpg seq01_frame001_rgb.jpg val/ labels/ train/ seq01_frame001_ir.txt seq01_frame001_rgb.txt val/注意名字里要保留_ir和_rgb后缀这样后续写数据加载器时可以按文件名自动配对。训练入口脚本如下from ultralytics import YOLO model YOLO(yolo11n.pt) results model.train( datadual_rgb_ir.yaml, epochs120, imgsz640, batch16, lr00.005, lrf0.01, mosaic0.0, mixup0.0, cos_lrTrue, workers8, )这段脚本关闭了 mosaic 和 mixup后面避坑章会详细解释为什么。lr0设置为 0.005 比默认低因为红外域和可见光域的像素分布差异大学习率太高容易让 BN 统计量来回震荡。cos_lr打开可以让后期收敛更平滑。4.3 关键训练参数怎么调epochs、batch size 与 imgszepochs 起步设 120 到 150因为 6 通道输入比 3 通道需要更多轮次把红外通道的权重激活。imgsz用 640 即可如果小目标占比高并且显存允许调到 800 对红外小目标有肉眼可见的提升但训练时间会涨约 50%。batch在 16 以上比较稳。batch 太小会导致 BN 统计量受单张红外极端亮度图影响出现 loss 曲线锯齿状抖动。显存不够时优先减小imgsz不要轻易把 batch 降到 8 以下。还有一个容易被忽略的参数是cacheTrue如果数据集小于一万张打开缓存能显著缩短每个 epoch 的加载时间尤其是 16 位红外图读取很慢的场景。这里补一句如果训练时显存不够常见做法是开启 gradient checkpointing但 ultralytics 没有直接暴露这个开关我一般会手动降 batch 或升级到 24G 显存的卡。4.4 给 YOLOv11 加一个轻量注意力组件做小目标优化纯输入级融合在近距离大目标上表现不错但夜间远处行人只有几个像素时仍然会漏。常见做法是在主干末端加一个极轻量的通道注意力让网络更关注红外和可见光差异最大的通道。实现时不需要改动检测头只需在主干输出的张量后插入一个 SE 模块。import torch import torch.nn as nn class ChannelAttention(nn.Module): def __init__(self, channels, reduction16): super().__init__() self.fc nn.Sequential( nn.AdaptiveAvgPool2d(1), nn.Conv2d(channels, channels // reduction, 1), nn.ReLU(inplaceTrue), nn.Conv2d(channels // reduction, channels, 1), nn.Sigmoid(), ) def forward(self, x): w self.fc(x) return x * w这个模块把特征图压缩到 1×1再用两个卷积层还原到原先通道数输出 0 到 1 之间的注意力权重逐通道乘回原特征。reduction16表示通道压缩比调小到 8 能更强地突出关键通道但会增加一点参数量。插入位置建议放在主干最后一个输出层的后面而不是颈部每个层都加这样既提升小目标响应又不至于让训练变得玄学。加完注意力后把第 4.2 节的训练脚本直接跑一遍红外小目标的召回率通常能提高 3 到 5 个点。5. 双传感器融合避坑检测翻车的五个常见原因与排查5.1 红外单通道直接复制三通道训练不收敛现象红外图用 OpenCV 读出来是单通道有人直接cv2.cvtColor转成三通道喂进去训练时 loss 一开始下降但到第 30 轮左右就卡住精度提不上去。原因把同一份灰度复制三份相当于给网络喂了三份完全相关的信息通道注意力学到的东西是退化的而且红外图对比度低复制操作并没有增加任何有效特征。解决红外图只做一次灰度增强然后在拼接阶段与可见光的三个通道直接拼成 6 通道。也就是说输入数据里保留红外为单通道不要提前复制。拼接代码在数据加载器里完成不在硬盘上生成三通道的冗余文件。5.2 mosaic 增强把两路图的空间关系切碎了现象训练时开了默认的 mosaic 增强loss 降得很慢验证集 mAP 徘徊在 0.5 左右上不去。原因ultralytics 的 mosaic 是把多张图随机裁剪拼接而红外和可见光是按文件名配对的两个独立文件数据加载器会分别独立做随机裁剪导致同一个训练批次里红外图和它的可见光配对图来自完全不同的场景网络学到的是两路不相干信息的叠加。解决训练时直接关闭mosaic0.0和mixup0.0。如果想保留一部分数据增强可以用mosaic0.2这种低比例靠人工确定配对图在同一张画布上做相同的几何变换否则不要开。5.3 BN 统计在红外和可见光之间来回抖动现象训练曲线出现规律的锯齿状每个 epoch 的前三分之一 loss 偏高后三分之一又掉下来验证集波动大。原因输入级拼接后红外通道像素值通常集中在 50 到 150 之间而可见光通道在 0 到 255 分布更均匀。BN 层是按 batch 统计均值和方差的如果 batch 里红外图与可见光图比例不均匀统计量就会被某一类图带偏。解决在预处理阶段把两路图都做同样标准的归一化让它们的均值和方差接近。具体地红外图做完 CLAHE 后再按可见光的均值方差做一次全局线性归一化。另外保证一个 batch 内两路图数量相等读图时按文件名配对采样。5.4 配准基准错了标注全废现象训练出来的模型白天检测很准傍晚或夜间在同一场景下框偏移明显红外图上的目标热源和可见光目标位置差出几十个像素。原因标注时只用了可见光图生成的标签没有把坐标映射到红外视角训练时红外输入对应的是错误位置的目标网络只好学一个模糊的折中位置。解决标签必须建立在配准后的共同坐标系上。先做一次严格的单应矩阵标定然后把可见光标签反投到红外图上抽帧检查全部偏移小于 2 像素再进训练集。这个步骤不要省它是整个融合项目里最容易被忽略却影响最大的后悔药。5.5 模型过拟合到可见光域红外信息完全没学到现象训练完成后做消融实验把红外通道全置零mAP 几乎没有下降说明模型完全没利用红外信息。原因可见光图纹理丰富loss 下降更容易依赖可见光分支红外分支梯度被淹没。这在数据量少、红外对比度不高时尤其常见。解决两个手段配合使用。一是训练时随机丢弃可见光通道即每次迭代有 20% 概率只输入红外图逼迫网络学习热源特征二是在预处理里加强红外图的 CLAHE 强度让热源边缘更锐利。做完这两步把红外通道置零再测一次mAP 应当显著下降这才说明融合真正生效了。6. 融合后的目标追踪ByteTrack 联动、置信度校准与推理结果保存6.1 用 ByteTrack 跑双传感器推理检测模型训好后追踪可以直接复用 ultralytics 内置的 ByteTrack。它按检测框的置信度分为高、低两档先用高置信度框确立轨迹再用低置信度框修复遮挡造成的断裂这个机制对红外目标偶尔闪烁、置信度忽高忽低的情况非常友好。from ultralytics import YOLO model YOLO(best_fused.pt) results model.track( sourcedual_sensor_video.mp4, saveTrue, conf0.35, iou0.5, trackerbytetrack.yaml, )这段代码把视频里的每一帧做双通道拼接输入并输出带 ID 的追踪结果。conf建议设在 0.3 到 0.4 之间红外目标在远距离时置信度天然偏低设太高会把真实目标当成噪声滤掉。iou保持默认 0.5双传感器拼接后的检测框通常比较稳定不需要单独调。6.2 保存推理结果与置信度校准saveTrue会自动把追踪视频存到runs/track目录但生产环境往往需要每帧的框和 ID 落到文件里。常见做法是遍历results取出每个目标的xyxy坐标、置信度和 ID按帧写入 CSV。同时建议统计一下模型在白天、夜间、雨雾三个时段输出的平均置信度如果夜间置信度明显低于白天 0.1 以上就在追踪层面对夜间场景单独降阈值而不是全局调参。我自己习惯的做法是固定模型后先跑一版测试序列手动检查三种场景下置信度分布曲线。置信度校准这件事做一次就行但它决定了后续追踪 ID 切换的次数。实践里我发现融合模型的夜间漏检大多不是网络能力不够而是追踪阶段阈值设得太高把低置信度真目标过滤了。这是最值得先检查的参数。希望这篇实践笔记能帮你少踩几个坑把红外与可见光融合这条路走得顺一点。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进