YOLOv5红外车辆检测实战:从数据集训练到部署避坑指南
发布时间:2026/10/1 17:25:07来源:尧图网络
简介基于YOLOV5的红外车辆检测与识别方案整合了源码、模型和数据集面向希望落地夜间或低照度车辆监控的计算机视觉工程师与智能交通开发者。方案充分利用红外热成像不受光照影响的特性结合YOLOV5的锚框机制与改进损失函数可实时输出车辆目标边界框与类别在夜间或恶劣天气下依然保持稳定识别。压缩包共128个文件整体约263.77MB涵盖Python训练与推理脚本、预训练权重、YAML配置、红外图像样本、XML标注文件以及MP4演示视频等从数据准备到模型部署的关键环节均有覆盖目录结构清晰便于按模块对照学习。当前已有1061人学习适合在算法基础上快速跑通完整流程再使用自有红外数据微调模型以适配特定场景。通过这套资源既能理解目标检测模型在特殊成像条件下的应用也能获得可直接改写的工程代码与实验素材用于安全预警、交通流量统计等实时分析任务。1. 红外车辆识别与检测为什么我不选传统视觉而是 YOLOv5红外车辆检测和红外车辆识别听起来像同一个需求落地时却经常被热源、背景和天气同时干扰。红外热成像不受可见光限制夜间、雨雾天都能拍到车但车辆轮廓在热成像里有时和路灯、空调外机混在一起传统边缘检测在这种画面上极易翻车。YOLOv5 是单阶段检测里最成熟的那一档速度和精度平衡训练、推理、导出代码都是现成的不用自己搭网络。这个资源包把源码、预训练权重、红外车辆数据集打包在一起适合做智能交通、安防夜视、辅助驾驶的开发者直接复现也可以拿来自有数据集微调。2. 拆包看货YOLOv5 源码、训练日志和测试图在项目里分别干什么拿到一个压缩包我第一件事不是跑 demo而是先把文件列表过一遍搞清楚哪些是训练产物、哪些是输入素材、哪些可以直接删。这个资源包里混了 TensorBoard 日志、IDEA 工程文件、若干 JPG 测试图标准 YOLOv5 源码目录结构也和它们待在一起。先把家底摸清后面训练和推理才不容易被日志文件带偏。2.1 资源包骨干文件一览哪些能删哪些必须留我先把压缩包里有代表性的文件整理成一张表方便你对照自己手里的目录看。文件路径类型在 YOLOv5 项目里的作用events.out.tfevents.1615438896.*TensorBoard 事件日志记录某次训练过程的 loss、mAP、学习率变化用来判断训练是否收敛.gitignoreGit 配置把runs/、datasets/、*.pt等大文件排除在版本控制外car_ddd.imlIDEA 模块文件纯 IDE 工程配置不影响训练和推理删掉也没事1.jpg / 2.jpg / 3.jpg测试图快速验证模型输出用的普通红外或可见光图000033.jpg / kd3.jpg数据集样本红外车辆图像实例可作为训练或推理样例about.jpg附加素材常见于文档或 README 配图非必须runs/exp*/weights/best.pt模型权重训练完成后保存的最佳权重是推理和后续部署的核心文件看到events.out.tfevents.*这类文件出现在压缩包根目录说明这份资源是经历过完整训练流程的不是只放了网络骨架。我习惯的做法是把runs/目录单独拎出来看如果里面有exp/weights/last.pt和best.pt那这个包就可以直接进入推理验证阶段如果只有日志没有权重就得先按第 3 章的命令自己训练。至于car_ddd.iml它来自 IntelliJ IDEA说明原作者可能用 PyCharm 打开过整个项目。这类文件在你自己的仓库里应该加进.gitignore否则团队协作时每个人本地路径不一样容易产生无意义的 diff。.gitignore本身倒是能看出原作者的工程习惯里面一般会忽略runs/、datasets/、__pycache__/这些都是 YOLOv5 的标准排除项。提示训练日志和权重不要混在根目录。我见过有人把events.out.tfevents.*直接提交到 Git结果每次训练都产生大量不可读的二进制变更最后被迫清理历史。建议一进项目先建runs/作为统一输出目录。2.2 红外图像进网络前的单通道转三通道处理YOLOv5 的预处理默认接收三通道图像而红外热成像相机输出的往往是单通道灰度图有些还是 16bit 的原始数据。直接把单通道图交给模型会在这行判断时卡住import cv2 # 红外相机输出的原始图通常是单通道且可能是 16bit img cv2.imread(000033.jpg, cv2.IMREAD_UNCHANGED) if img.ndim 2: # 16bit 像素范围通常是 0~65535先归一化到 8bit img cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX).astype(uint8) # 复制成三通道让 YOLOv5 的网络输入分支正常走 img cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)这段代码里最容易忽略的是cv2.normalize。红外图像如果不做归一化就强制转uint8高亮度的热源区域会直接饱和成一片白车辆轮廓细节全丢。用NORM_MINMAX把像素拉伸到 0 到 255比固定阈值更稳。复制成三通道是因为 YOLOv5 的 Conv 层在第一层就按三通道初始化如果你非要用单通道图硬塞还得改model.yaml里的ch参数完全没必要。还有一个细节训练和推理必须用同一套预处理。如果你训练时做了灰度转三通道推理时直接用原始红外 16bit 图检测框坐标会整体偏移。实际工程里我会把这段预处理封装成函数训练脚本和部署脚本共用避免两边各写各的。2.3 环境配置先让 YOLOv5 跑通一张图再谈训练YOLOv5 的环境配置坑主要在依赖版本上。资源包里一般自带requirements.txt我每次都会先执行这一句pip install -r requirements.txt如果你在国内网络环境可以加镜像源加速但不建议全部依赖都从镜像装PyTorch 这类大包最好还是按官方源走避免 CUDA 版本被镜像包覆盖掉。装完后先不急着训练用一张测试图跑一次推理python detect.py --source 1.jpg --weights yolov5s.pt --img 640 --conf-thres 0.4--conf-thres 0.4是置信度阈值低于 0.4 的框会被过滤掉。红外场景下车辆的热特征明显阈值可以适当放低到 0.3但要是调太低路边的暖水井盖也会被框出来。第一次跑通后再去看runs/detect/exp/里输出的标注图确认环境没问题再进入数据集训练阶段。环境配置这块最容易翻车的是 PyTorch 和 CUDA 版本不匹配。YOLOv5 官方在requirements.txt里写的是torch1.7.0但如果你显卡驱动太老装最新版 PyTorch 反而起不来。我一般先跑python -c import torch; print(torch.cuda.is_available())输出True再继续省得后面训练到一半才报 CUDA error。3. 用 YOLOv5 训练自己的红外车辆数据集目录、参数和断点续训把资源包里的源码和权重拆清楚之后真正的重头戏是用你自己的红外车辆数据训练一个可用模型。很多人在这一步卡住不是因为网络结构不懂而是数据目录、标注格式和训练参数没对齐。YOLOv5 的代码很成熟只要目录结构符合规范训练命令就那么一行。3.1 YOLO 标注格式与数据集目录结构YOLOv5 的数据集目录结构是固定的新老版本有细微差别但核心逻辑一致。资源包里的红外车辆数据如果没有整理成这个结构我建议先手动归拢datasets/ ├── infrared-vehicle/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── infrared.yamlimages/train/放训练图像labels/train/放对应的标注 txt 文件一张图对应一个同名 txt。每行格式是class_id x_center y_center width height坐标全部归一化到 0 到 1。红外车辆数据通常只有一个类别类别 ID 写 0 即可。拿到的标注可能是 PASCAL VOC 的 XML 格式或者 LabelMe 的 JSON 格式都需要转成 YOLO 格式。我每次都会写一个转换脚本避免手工改import os from xml.etree import ElementTree as ET def voc_to_yolo(xml_path, out_path, class_list): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_list: continue cls_id class_list.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 归一化边界框坐标 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(out_path, w) as f: f.write(\n.join(lines)) if __name__ __main__: class_list [vehicle] for xml in os.listdir(xml_labels): voc_to_yolo(os.path.join(xml_labels, xml), os.path.join(labels, xml.replace(.xml, .txt)), class_list)脚本里class_list定义了类别 ID 与名称的映射顺序一旦确定就不要改否则训练中断后重新标注会很麻烦。坐标归一化是 YOLO 格式的硬性要求因为模型在训练时会按照输入尺寸等比缩放图像绝对像素坐标在不同分辨率下无法对齐。数据集配置文件infrared.yaml内容如下train: datasets/infrared-vehicle/images/train val: datasets/infrared-vehicle/images/val nc: 1 names: [vehicle]nc是类别数红外车辆检测一般只有vehicle一类但如果你的场景要区分轿车和卡车nc就改成 2names改成[car, truck]。路径建议用相对路径避免换机器后还要改配置。3.2 训练命令与关键参数batch、epoch、学习率、自动锚框目录结构就绪后训练命令并不复杂python train.py --data data/infrared.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --device 0--weights yolov5s.pt是预训练权重。你可能会有个疑问红外图像和可见光图像差异这么大预训练权重还有用吗有用但要辩证地看。YOLOv5s 在 COCO 上学到的边缘、纹理、形状特征仍然能迁移到红外图像上尤其是车辆这种有明确几何轮廓的目标。但红外图像的热特征分布和可见光差异较大所以预训练权重只能作为起点不能指望它直接给出理想结果。--img 640是训练输入尺寸。红外图像里车辆如果占比较小可以提升到 768 甚至 960但显存占用会明显上升。低显存环境里我一般先用--img 512 --batch 8跑通流程再根据显存余量逐步加大。关于超参数YOLOv5 默认的data/hyps/hyp.scratch-low.yaml合理初学者不要动太多。真正值得调的是学习率lr0: 0.01 # 初始学习率 lrf: 0.2 # 最终学习率 lr0 * lrf momentum: 0.937 # SGD 动量 warmup_epochs: 3.0红外数据集如果只有几百张图lr0可以降到 0.005防止大步长把预训练权重冲垮。YOLOv5 还有一个被很多人忽略的机制是自动锚框。训练启动时它会重新计算数据集的锚框尺寸并在日志里打印AutoAnchor相关输出。只要你没手动关闭它就会根据真实边界框尺寸优化锚框这个环节不用干预但值得知道它的存在因为后续排查小目标漏检时锚框尺寸是一个排查点。3.3 断点续训与损失曲线判断训练到一半断电、显存溢出、手动中断都是常态。YOLOv5 每次保存两个权重last.pt和best.pt。last.pt是断点续训的后悔药best.pt是验证集上 mAP 最高的权重。继续训练用这一句python train.py --resume runs/exp/weights/last.pt光会续训还不够得会看训练是否正常。资源包里的events.out.tfevents.*日志就是干这个的。启动 TensorBoardtensorboard --logdir runs/exp然后看三个曲线train/loss应该持续下降val/loss应该先降后稳如果val/loss在某轮开始反弹而train/loss还在降那就是过拟合。红外数据量通常不大过拟合很常见解决办法不是盲目加 epoch而是加数据增强或提前终止。best.pt的保存逻辑是只要验证集 mAP 比历史最好高就覆盖所以即使训练中断你最后也能拿到一个可用权重。这也是为什么我强烈建议训练脚本跑起来后每隔一段时间就去runs/exp/看一眼best.pt而不是死等 100 个 epoch 全部跑完。4. 实时检测跑通从单张图到 RTSP 视频流的部署路径训练得到best.pt之后接下来的问题是怎么把它用起来。实时检测这个说法听起来简单实际上从单张图到视频流之间还隔着置信度调参、后处理逻辑、硬件资源分配这三道坎。我先从最简单的detect.py说起再一步步改造。4.1 用内置 detect.py 跑图片和视频先验证权重拿到best.pt第一件事是在那几张测试图上先跑一遍:python detect.py --weights runs/exp/weights/best.pt --source 1.jpg --img 640 --conf-thres 0.4 --iou-thres 0.45--source支持图片路径、视频路径、文件夹路径甚至摄像头序号。输出结果默认放在runs/detect/exp/旁边会生成一个带标注框的可视化图。--iou-thres 0.45是 NMS 的 IoU 阈值两个框重叠度超过这个值就只保留置信度更高的那个。红外场景下车身温度接近容易出现多个候选框叠在一起iou-thres调低到 0.4 可以减少重叠框残留但也可能把紧挨着的两辆车合并成一个框。如果你想同时看模型对多张图片的结果把source指到图片文件夹即可python detect.py --weights runs/exp/weights/best.pt --source test_imgs/ --img 640 --conf-thres 0.4这一步是验证模型最直接的方式如果框的位置和车辆轮廓吻合说明训练流程没白走。如果发现大量错检先别急着调参数回到第 3 章的val/loss曲线和训练集构成去排查。4.2 接入摄像头和 RTSP 流自定义推理脚本detect.py虽然能接摄像头但实际项目里我们通常需要把检测结果嵌入到自己的业务逻辑里比如统计车流量、保存报警截图。这时候我会用自定义推理脚本import cv2 import torch from pathlib import Path # 用本地源码加载模型避免每次运行都走下载逻辑 model torch.hub.load(yolov5, custom, pathruns/exp/weights/best.pt, sourcelocal) # 推理参数直接挂在模型对象上 model.conf 0.4 model.iou 0.45 model.classes [0] # 只需要 vehicle 这一个类别 # 接入 RTSP 网络摄像头也可以用本机摄像头编号 0 cap cv2.VideoCapture(rtsp://user:password192.168.1.64:554/stream1) while True: ret, frame cap.read() if not ret: break # size640 是输入分辨率和训练保持一致 result model(frame, size640) # xyxy 返回 x1, y1, x2, y2, confidence, class dets result.xyxy[0].cpu().numpy() for x1, y1, x2, y2, conf, cls in dets: cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, fvehicle {conf:.2f}, (int(x1), int(y1) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) cv2.imshow(infrared-vehicle, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码有几个关键点。sourcelocal是必须要加的否则torch.hub.load会尝试从 GitHub 拉取 YOLOv5 源码在内网环境直接爬死。model.classes [0]做类别过滤只保留vehicle类红外场景下如果有其他类别误检这一步能有效压制。后处理上result.xyxy[0]已经把推理结果从 tensor 转成方便循环的格式cpu().numpy()是为了脱离 GPU 张量做绘图操作。RTSP 流的延迟通常来自解码头而检测本身在 GPU 上跑的话单帧耗时大约在 10 到 30 毫秒。如果发现画面明显卡顿优先检查网络带宽而不是模型大小。4.3 低显存部署半精度推理与 TensorRT 导出低显存运行模型是热搜词里被问得最多的问题。YOLOv5 自带两个抓手半精度推理和模型导出。半精度推理在detect.py里加--half即可显存占用基本减半速度还有提升python detect.py --weights best.pt --source rtsp://xxx --img 640 --half --conf-thres 0.4如果想进一步压榨性能用export.py导出 TensorRT enginepython export.py --weights best.pt --include engine --device 0 --img 640 --half导出后推理代码不变torch.hub.load加载的依然是best.pt但如果你想让加载速度更快可以走 C 部署或者用 TensorRT Python API。这个环节最容易忽视的是导出后的 engine 文件绑定了当前 GPU 架构换到另一张显卡上可能失效需要重新导出。我通常保留best.pt作为可交互格式engine只用于固定设备上的正式部署。5. 红外车辆识别避坑五个我在调试中反复踩的具体问题红外车辆检测的坑跟普通可见光检测还真不一样。颜色信息缺失、热源干扰、日夜差异大这些问题不是调一个阈值能解决的。我挑五个反复踩过的具体问题每条按照现象、原因、解决来写看能不能帮你少走一段弯路。5.1 数据侧的坑伪彩色、时段分布和小目标第一个问题用伪彩色红外图训练mAP 反而下降。现象把红外相机输出的伪彩色图铁红色或彩虹色直接喂给 YOLOv5训练时 loss 能降但验证时误检率很高经常把高温区域当成车辆。原因伪彩色本质上是一种可视化映射同一辆车在不同环境温度下颜色差异巨大模型学到的是“颜色像车”而不是“形状像车”。而且伪彩色编码会放大热噪声干扰特征提取。解决改回原始灰度图按 2.2 节的方式归一化并转三通道。灰度图保留了完整的亮度梯度车辆轮廓和背景热源之间的温差边界比伪彩色更线性模型泛化也更稳。第二个问题只有白天红外数据夜间部署时漏检一堆。现象白天测试 mAP 到了 0.9晚上一到现场车屁股后面的热尾气、路灯光晕全被框出来真车反而漏掉。原因红外图像在不同时段的热特征分布差异大。白天车辆受阳光照射车身温度高且均匀夜间车前脸温度低只有发动机舱和排气管位置亮整体轮廓不完整。解决训练集必须覆盖夜间、凌晨、雨雾等低照度时段至少保证夜间样本占比 30% 以上。如果数据集不够先用资源包里的 infrared 样本做预训练再用夜间红外图微调。Mosaic 数据增强对拼接夜间样本也有帮助。5.2 训练与部署侧的坑OOM、中断和权重加载第三个问题训练时 OOM显存直接爆掉。现象--batch 16 --img 640启动后几秒报CUDA out of memory进程被杀。原因显卡显存不足常见于 6G 或 8G 显存的卡。YOLOv5 的显存占用和batch、img、workers三个参数正相关。解决先--batch 8 --img 512把流程跑通确认数据集没问题后再往上加。如果还想保持大 batch可以在train.py里加--amp开启混合精度训练显存能省 30% 到 40%。NVIDIA 显卡建议优先用 AMP速度影响很小。第四个问题训练中断后从头再来之前的时间全白费。现象训练到第 80 轮时手动 CtrlC或者断电再启动train.py --weights yolov5s.pt发现 loss 从高位重新开始。原因训练启动命令里没有加--resume模型重新从预训练权重开始不是从上一次last.pt继续。解决训练中断后执行python train.py --resume runs/exp/weights/last.pt。同时养成习惯训练脚本里设置好--project runs/exp --name infrared_v1确保每次训练的权重输出到独立目录不会被后续实验覆盖。第五个问题加载best.pt时报NotImplementedError或 CUDA 版本错误。现象detect.py启动后立刻报错堆栈信息指向torch.load提示权重文件无法反序列化或 CUDA 驱动不匹配。原因YOLOv5 的权重文件内部包含模型定义和训练超参数PyTorch 版本差异会导致torch.load在反序列化时找不到对应的类或方法。另外如果训练时用的 Python 环境和部署环境不一致也会出现类似的兼容性问题。解决不要跨 Python 版本迁移运行环境。训练完后的best.pt在部署机上加载时尽量新建一个完全相同的 conda 环境torch主版本号保持一致。如果实在无法保证环境一致用torch.load(path, map_locationcpu)离线加载一遍再重新保存成干净权重可以减少部分兼容性干扰。6. 最后一步把 best.pt 放到独立测试集上强制算 mAP训练和部署流程跑通后最容易被忽略的是对模型做一个独立测试集验证。很多人只看训练日志里的val/loss就认为模型差不多了实际上这个评价并不全面。YOLOv5 自带val.py但它的验证集是训练时切出来的分布一致参考价值有限。我更习惯把数据集按 7:2:1 切成训练、验证、测试三份测试集从训练开始就彻底隔离最后用它来算一次 mAP作为模型是否达标的唯一依据。强制验证命令如下python val.py --data data/infrared.yaml --weights runs/exp/weights/best.pt --img 640 --half --save-json --save-conf--save-json会输出 COCO 格式的 JSON 结果方便你用官方工具复算 mAP--save-conf会把每个框的置信度一并保存。红外车辆这类单类目标mAP0.5 至少要过 0.85 才算能出门mAP0.5:0.95 则要看你的允许范围一般在 0.55 到 0.7 之间。除了 mAP我还会强迫自己去看runs/val/exp/下的混淆矩阵图。红外场景最容易出现的问题是把背景热源暖气管道、路灯杆识别成车混淆矩阵里看非车辆类别是否被误判为vehicle比单纯看 mAP 更直观。这个验证环节我真的吃过亏。之前有一次训练了 120 轮训练 loss 降到很低带着best.pt直接上现场结果路边的石墩全被框成了车。后来发现是因为验证时只看了整体精度没有看混淆矩阵背景误检被平均指标掩盖了。从那以后我每次换数据集训练收尾都强制走一遍独立测试集验证和混淆矩阵检查不然心里就没底。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网