简介:面向无人机夜间车辆检测任务,这份资源整理了1000张夜间真实场景图片及对应标签,可解决航拍视角下夜间车辆样本稀缺、场景单一等问题,适合计算机视觉初学者、算法工程师及无人机项目开发者用于模型训练、算法验证以及作为通用车辆检测数据集的有效补充。资源为单个PDF文档形式交付,大小约4.13MB,文档内包含数据集基本情况介绍、缩略图预览、labelimg标注截图及百度网盘获取方式;实际图片和VOC/COCO/YOLO三种格式标签需按文档指引下载。目前已有372人学习/浏览。数据集覆盖夜间城市道路行驶、道边停车、停车场、小区等多类无人机视角场景,并包含车辆遮挡与严重遮挡图像,整体统一标注为"car"类别,采用labelimg工具完成高质量标注;不区分轿车、SUV或货车,便于直接用于YOLO等目标检测算法。附赠的YOLO11一键训练脚本支持GPU(GPUs)、CPU和Mac(M芯片)三平台,提供博主训练结果日志,读者可按脚本快速搭建训练流程并进行多平台效果对比。
1. 夜间无人机拍到的车辆,为什么比白天数据难训十倍
晚上用无人机拍车,光看画面我觉得“没那么难”,但真把目标检测模型放到夜间航拍上,漏检率和误检率会同时飙升。白天训练好的权重直接拿来跑夜晚,车灯会被当作圆形目标,路灯和地面反光也能骗过网络,而车体本身因为暗,反而被漏掉。要做到夜间车辆检测落地,最实在的做法就是给模型喂专门的夜间数据,并配合更小、更强的训练控制。这个标题给的方案很直接:一个无人机场景的夜间车辆检测数据集,1000张图,同时带VOC、COCO、YOLO三种格式标签,外加一套能在GPU、CPU、Mac三平台跑YOLO11的一键训练脚本。它适合无人机巡检、夜间安防、智慧交通这类白天晚上都要工作的从业者。
2. 先拆这份夜间车辆检测数据集:1000张图与三种标签格式的真实用途
拿到数据集,先别急着训练。脚本能一次跑通的前提,是目录结构和标签格式匹配YOLO11的读取规则。三种格式同时给,并不只是“多一份存档”,而是让同一个数据集能喂给不同的训练链:VOC适合老标注工具和传统检测;COCO适合做预训练迁移和统一管理;YOLO的txt格式是YOLO11训练时真正会读取的东西。下面我把三种格式差异、目录布局和一个常用的格式转换脚本都展开。
2.1 VOC/COCO/YOLO三种格式的差异:你手里拿到的到底是什么
先看最常见也最容易被忽视的差异:坐标定义和类别映射方式。用一张表说清楚。
| 格式 | 典型文件形态 | 坐标定义 | 类别表示 |
|---|---|---|---|
| VOC | 每张图一个XML文件 | xmin、ymin、xmax、ymax,绝对像素值 | <object><name>car</name>,类别名在XML里 |
| COCO | 整个数据集一个JSON文件 | bbox为[x, y, width, height],绝对像素值 | categories数组里维护id到name的映射 |
| YOLO | 每张图一个txt文件 | class_id x_center y_center width height,全部归一化到0~1 | 只有类别id,必须配合names列表 |
关键点在于YOLO的txt是“无头”文件,它自己不带类别名。很多人把标注导成YOLO格式后,拷贝到新机器发现只有一堆数字,不知道0是car还是van,这就是典型的黑匣子问题。VOC和COCO自带类别名,反而安全。三种格式同时给,真正价值不是格式本身,而是把类别映射关系、坐标基准都留住了。我拿到任何数据集,会先看一眼labels里的数字范围和data.yaml的names是否一致,这一步能避免后面训练彻底跑偏。如果有机会往遥感图像目标检测或三维目标检测延伸,COCO JSON也更容易扩展深度标注。
2.2 标签文件目录怎么摆,才能让YOLO11训练脚本直接读
YOLO11的训练入口是yolo detect train,它不关心你的数据集叫什么名字,只关心data.yaml里给的路劲。常规目录安排如下:
dataset_root/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ ├── annotations/ │ ├── train.xml │ └── val.xml ├── coco.json └── data.yaml这个结构的好处是:训练时只用到images和labels,annotations和coco.json留作可视化、格式转换和后续做迁移学习的备份。很多团队把VOC的XML和YOLO的txt混在一起放,训练前还要现做索引,很耽误事。
data.yaml是训练脚本的第一依赖,内容通常是:
path: /absolute/path/to/dataset_root train: images/train val: images/val names: 0: carpath最好写绝对路径,不要写..这种相对路径,Mac和Windows对斜杠的处理不一样,容易踩坑。names必须和labels里的id顺序严格一致。如果这个数据集只有一个类别car,那0: car就够了。检查完这两项,训练脚本就能直接索引到图片和标签。
2.3 只有VOC标注?用Python把XML转成YOLO txt和COCO JSON
虽然标题说三种格式都带,但实际工作中经常只能拿到其中一种。我一般会在目录里准备一个转换脚本,方便从VOC XML生成另外两种格式。下面这段代码是常用的转换核心。
# voc2yolo_coco.py import xml.etree.ElementTree as ET import json def voc2yolo(xml_file, txt_file, img_width, img_height, class_names): tree = ET.parse(xml_file) root = tree.getroot() lines = [] for obj in root.iter("object"): cls = obj.find("name").text if cls not in class_names: continue x1 = int(obj.find("bndbox/xmin").text) y1 = int(obj.find("bndbox/ymin").text) x2 = int(obj.find("bndbox/xmax").text) y2 = int(obj.find("bndbox/ymax").text) # 转成归一化的中心点坐标和宽高,注意宽高要除以图片尺寸 x_center = (x1 + x2) / 2.0 / img_width y_center = (y1 + y2) / 2.0 / img_height w = (x2 - x1) / img_width h = (y2 - y1) / img_height lines.append(f"{class_names.index(cls)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(txt_file, "w") as f: f.write("\n".join(lines))这段代码里最容易错的是x_center和w:x1和x2是绝对像素,相减得到宽度,再除以图片宽度才是归一化数值。很多人只归一化中心点,忘了除宽高,导致训练loss不降。class_names.index(cls)能保证类别id和data.yaml一致,前提是你传入的class_names列表顺序固定。
转COCO json则要维护全局的image_id和annotation_id:
def voc2coco(xml_files, out_json, class_names): images, annotations = [], [] ann_id = 1 for img_id, xml_file in enumerate(xml_files): root = ET.parse(xml_file).getroot() size = root.find("size") w = int(size.find("width").text) h = int(size.find("height").text) images.append({ "id": img_id, "file_name": root.find("filename").text, "width": w, "height": h }) for obj in root.iter("object"): cls = obj.find("name").text if cls not in class_names: continue x1 = int(obj.find("bndbox/xmin").text) y1 = int(obj.find("bndbox/ymin").text) x2 = int(obj.find("bndbox/xmax").text) y2 = int(obj.find("bndbox/ymax").text) annotations.append({ "id": ann_id, "image_id": img_id, "category_id": class_names.index(cls), "bbox": [x1, y1, x2 - x1, y2 - y1], "area": (x2 - x1) * (y2 - y1), "iscrowd": 0 }) ann_id += 1 coco = { "images": images, "annotations": annotations, "categories": [{"id": i, "name": n} for i, n in enumerate(class_names)], } with open(out_json, "w") as f: json.dump(coco, f)这段代码里COCO的bbox用的是x, y, width, height,和VOC的xmin, ymin, xmax, ymax不同。转完后验证一条:读取json里任意annotation,再用OpenCV画框,框体应该完全贴合车辆轮廓。只要这一步过了,三种格式才算真正对齐。
3. 一键训练脚本怎么落地:GPU/CPU/Mac三平台的环境判断与最小命令
一键脚本不是炫技,是防止每次训练前被环境问题逼疯。YOLO11使用的ultralytics训练器,靠device参数控制设备:NVIDIA显卡写cuda,纯CPU写cpu,Apple Silicon的GPU写mps。三者的计算后端完全不同,所以脚本第一步必须是平台检测,而不是让用户手动填device。
3.1 脚本先做平台检测:为什么GPU(CUDA)、CPU、Mac(MPS)不能共用一套启动参数
GPU训练依赖CUDA,需要NVIDIA驱动、CUDA运行时和GPU版PyTorch三样对齐。CPU训练最稳,但夜间航拍图像尺寸大,一个epoch跑起来可能比GPU慢十倍。Mac的MPS走Metal接口,速度介于两者之间,但PyTorch对MPS的算子支持不如CUDA完整,某些层会在跑到一半才报错。
平台检测脚本通常长这样:
# platform_check.py import torch if torch.cuda.is_available(): device = "cuda" # NVIDIA GPU,走CUDA后端 elif torch.backends.mps.is_available(): device = "mps" # Apple Silicon GPU,走Metal后端 else: device = "cpu" # 兜底 print(f"detected device: {device}") print(f"pytorch: {torch.__version__}")这段代码里有两个坑:第一,torch.cuda.is_available()为False,很可能是PyTorch装成了CPU版,而不是硬件不支持;第二,在Intel Mac上mps.is_available()会是False,所以条件顺序不能反,否则所有Mac都会落到mps然后报错。脚本最好再提供一个环境变量开关,比如FORCE_CPU=1,当MPS训练中途被杀时能快速切回CPU兜底。
3.2 一键脚本的核心逻辑:从数据集目录到YOLO11训练的最小bash
一键脚本的本质是拼命令,同时把参数默认值写进去,减少每次重复输入。下面这个脚本是常见做法:
#!/bin/bash # train_yolo11.sh DATA=$1 # data.yaml的绝对路径 EPOCHS=${2:-100} # 默认100轮 BATCH=${3:-16} # 默认batch 16 IMGSZ=${4:-1280} # 输入尺寸,夜间小目标建议1280 DEVICE=$(python platform_check.py) echo "Using device: $DEVICE" yolo detect train \ model=yolo11n.pt \ data=$DATA \ epochs=$EPOCHS \ batch=$BATCH \ imgsz=$IMGSZ \ device=$DEVICE \ project=runs/detect \ name=train_nightmodel=yolo11n.pt是让脚本第一次运行时自动下载预训练权重;如果没有联网,YOLO11会把权重下载失败,但脚本仍然能用随机初始化的权重训练。imgsz=1280不是随便写的,无人机俯视视角下车辆像素少,640的输入会把目标压成几个像素,后面第4章会详细讲。name=train_night用来区分多次实验,所有输出会放到runs/detect/train_night目录。如果你在Windows原生环境没有bash,可以用Git Bash或WSL跑,也可以把同样的参数改成python -m形式。
3.3 手动分解这条训练命令:batch/epoch/workers在三种平台的推荐值
一键脚本给出了默认值,但真到调参时要按平台能力改。我一般按这个表起步:
| 平台 | device | 推荐batch | 推荐workers | 备注 |
|---|---|---|---|---|
| NVIDIA GPU,8G显存 | cuda | 8~16 | 4~8 | imgsz=1280时建议降到8 |
| Apple Silicon Mac | mps | 8~16 | 2~4 | 共享内存,太大容易被系统杀 |
| 纯CPU | cpu | 4~8 | 0~2 | workers过高反而卡在数据加载 |
imgsz翻倍显存占用约翻4倍,所以batch和imgsz必须一起看。我在8G显存的笔记本上会先用batch=8 imgsz=1280,如果OOM就降到batch=4。Windows上DataLoader的worker经常抽风,一旦报错就先workers=0,虽然慢一点但不会卡死。epochs多少要看数据集规模:1000张图的夜间任务,100轮够起步,200轮看收敛曲线。
3.4 用GPU训练时,驱动和PyTorch的CUDA版本怎么对齐
这个坑出现频率极高:torch.cuda.is_available()返回False,或者训练到一半报CUDA error。先看两张牌:
nvidia-smi python -c "import torch; print(torch.version.cuda)"nvidia-smi右上角显示驱动支持的最高CUDA版本,torch.version.cuda显示PyTorch编译用的CUDA版本。PyTorch的CUDA版本不必等于驱动最高版本,只要PyTorch要求的CUDA不高于驱动支持的上限就能跑。例如驱动支持CUDA 12.2,PyTorch用CUDA 12.1没问题。但很多新手装的是CPU版PyTorch,这时候torch.cuda.is_available()永远为False,PyTorch安装教程里选GPU版才能解决。这个检查放在训练脚本开头,省得跑一小时后才发现用了CPU。
4. 针对夜间车辆检测的四个参数必调项:小目标、车灯过曝与误检
数据、脚本都齐了,直接训练可能能跑,但效果不一定好。夜间车辆检测最常见的两个失败模式是:小目标漏检,以及把路灯或地面反射当车。下面这四个参数是调优时的优先对象。
4.1 输入分辨率大小:在俯视小车面前别用默认640
YOLO11的网络结构本身有不错的多尺度融合能力,但输入分辨率直接决定网络能“看到”的目标大小。无人机在60米高度看一辆轿车,目标宽度可能只有30像素;如果缩放到640,目标就变成不到10像素,特征被严重压缩。
训练命令里把imgsz提上去:
yolo detect train ... imgsz=1280imgsz同时影响训练集和验证集的resize尺寸。8G显存跑1280有压力,就配batch=4,或者把图像裁剪成分块再训练。我见过不少团队在640下模型一直漏检,改成1280后同样的数据mAP50直接提升十多个点,这就是输入分辨率的力量。
4.2 数据增强别照搬白天:HSV、Mosaic、MixUp在夜间的取舍
YOLO11默认开启一系列增强:HSV颜色扰动、Mosaic拼图、MixUp混图。这些在白天数据集上很好用,但夜间图像整体偏暗、色彩分布窄,过强的颜色扰动反而破坏暗部轮廓。
常用做法是把增强参数调保守:
yolo detect train ... hsv_v=0.2 mosaic=0.5 mixup=0.0hsv_v控制亮度变化幅度,白天我常设0.4,夜间建议0.2以下,否则车体和柏油路的对比会被随机打没。mosaic=0.5保留一部分拼接增强,让模型看到不同场景的拼接车辆,但概率降低,避免小目标在拼接时被切掉。mixup=0.0是我在夜间任务里的默认值,因为两张暗图叠加后,车灯区域会过曝成一大片光斑,反而制造假样本。
4.3 类别不均衡和难例挖掘:夜间车灯像路灯怎么压误检
很多夜间车辆数据集只有一个类别car,但难例不在类别数上,而在背景:路灯是圆形亮斑,地面反光是长条形亮斑,远处车灯更像车灯。模型很自然会把所有亮斑都预测成车辆。
压误检最直接的办法是加负样本。在数据集里放一批完全没有车的夜间道路图像,并给它们空标签txt,让网络知道“背景区域不该输出目标”。训练命令里无需特殊参数,YOLO11会正常读取空txt。如果负样本不够,先跑一次val,看混淆矩阵里background被预测成vehicle的比例,再针对高频误检场景补图。上线时我也会调高置信度阈值:
yolo detect val ... conf_thres=0.35conf_thres默认0.25,夜间误检多,提到0.35或0.5能压掉很多路灯,但代价是暗处真车也可能被滤掉。阈值不是一次定死,要看具体告警场景允许的误报率。
4.4 早停与模型选择:看哪个指标决定最终权重
训练脚本默认会根据验证集mAP50-95挑选best.pt。但对夜间航拍,mAP50-95容易被大量低置信度框拉低,真正落地时更关心mAP50和召回率。我一般会把早停提前:
yolo detect train ... patience=30patience=30表示连续30轮验证指标没提升就停。夜间数据集小,训练到后期很容易过拟合,太长的patience只会浪费时间。选权重时不要只盯着best.pt,也看一眼last.pt:如果两者mAP差太多,说明训练过程不稳定,先检查数据划分和增强是否太激进。
5. 常见问题与避坑:从数据标注到三平台训练最容易翻车的五个点
在YOLO11落地时,绝大多数问题不是模型不行,而是坐标系、平台和数据集割裂造成的。下面五条按发生频率排序,基本每一条都能让人浪费半天。
5.1 标注坐标系搞混:VOC的xmin/ymin与YOLO的归一化中心点
现象:训练loss一直不降,或者训练能收敛但框全偏到图片角落。
原因:把VOC的绝对坐标直接当成分母,没有除以图片宽高;或者把xmax当成了框宽度。
解决:检查labels里所有txt的数值,正常必须全部在0~1之间。转换公式固定为x_center=(xmin+xmax)/2/width,w=(xmax-xmin)/width,y方向同理。我每次转换后会随机抽三张图,用OpenCV画框验证,确认框贴合车辆再训练,避免闷头跑。
5.2 夜间图像标注容易漏标:暗处的车辆肉眼看不见
现象:验证集mAP不低,但单独统计暗部车辆时召回率奇差。
原因:标注员在黑暗画面上看不清车体轮廓,只标了车灯亮的目标,车尾和车身被漏掉。
解决:给标注用的图片先做亮度增强或直方图均衡,让标注软件能显示暗部细节,训练时仍用原始暗图。如果数据集有多个相机位,优先把俯视暗部的样本多标几遍。夜间数据集的标注质量,直接决定模型对“没亮灯的车”敏不敏感。
5.3 Mac上MPS训练不稳定:不是报错就是中途被杀
现象:在Apple Silicon上跑MPS,训练几分钟后系统直接把进程杀掉,或者报算子不支持。
原因:PyTorch的MPS后端还不够完整,某些卷积或归一化算子没有对应实现;batch太大也会让内存压力飙升。
解决:先强制device=cpu跑一个epoch做冒烟测试,确认数据和脚本没问题;再切回MPS,把batch减半,workers设成2。如果MPS还是挂,就用CPU把声明周期跑完,夜间数据量不大,1000张图CPU训练一天也能接受。
5.4 Windows GPU上训练报CUDA out of memory,或DataLoader worker崩溃
现象:batch=16一开工就OOM;workers=8时训练界面一直不动。
原因:imgsz=1280加batch=16远超显卡显存;Windows平台多进程数据加载有更高安全限制。
解决:先workers=0、batch=8跑通,再逐步往上加。同时用nvidia-smi确认没有别的进程占显存。如果电脑有两个GPU,但训练只想用一张,明确传device=0,不要让它自动选到第二张卡。
5.5 训练结果飘:重复训练两次mAP差5个点以上
现象:同一数据集、同一脚本,两次训练结果差异很大,best.pt和last.pt差距也很大。
原因:数据集只有1000张,验证集随机划分带来的波动被放大;mosaic增强带来更多随机性。
解决:在训练命令里固定随机种子,YOLO11可以传seed=42;把验证集单独留出来,不要每次重新划分。多跑两轮取稳定权重,是我最常做的急救动作。这属于典型的“玄学”问题,背后其实是小数据集的方差。
6. 训练完怎么验证:夜间检测的mAP、混淆矩阵和误检排查技巧
训练完拿到best.pt,不等于能上线。夜间车辆检测最怕误检把路灯光斑当车,真到接入告警时会被迫关掉整个识别。我的习惯是先用val脚本复现验证指标,再看混淆矩阵,最后导出模型。三步做完才敢交付。
先跑一次标准验证:
yolo detect val model=runs/detect/train_night/weights/best.pt data=your_data.yaml imgsz=1280输出里有两个关键指标:mAP50和mAP50-95。夜间航拍我更看重mAP50,因为实际业务不会把IoU卡到0.75。如果mAP50高但线上误检多,重点去调置信度阈值。
然后打开验证输出目录里的混淆矩阵图。单类别时,最该看的是background被预测成vehicle的那一格;数值高,说明路灯和反光被当成车。此时优先加负样本或提阈值。如果是vehicle被漏到background,则要先回分辨率,再考虑增强是否过度。
最后导出部署格式:
yolo export model=best.pt format=onnx imgsz=1280导出ONNX可以用onnxruntime在Jetson、RK3588这类边缘设备上跑,也有利于后续做TensorRT优化。无人机机载算力有限,导出时模型用yolo11n这类小结构,速度才跟得上。
我这些年吃过最大的亏,就是拿训练指标直接当交付指标。夜间场景骗人最狠,验证集上mAP再漂亮,到了路灯密集路段还是会漏。所以现在无论多急,我都会先跑val、再看混淆矩阵,最后导出一个真实测试视频来验收。希望帮到你。
本文还有配套的精品资源,点击获取