简介:基于YOLOv8的无人机航拍红外人车识别目标检测项目代码,面向目标检测学习者和无人机视觉应用开发者,用于解决复杂背景下的红外行人、车辆检测问题。包体共468个文件,涵盖Python训练/推理脚本、YAML模型配置、环境依赖说明、Dockerfile容器化部署配置、预训练权重pt以及Jupyter示例等,压缩包大小约51.27MB,按文件和目录组织清晰,可支撑环境搭建、数据加载、模型训练与导出全流程。项目中包含丰富的Markdown文档和注释,配合作者博客中的数据集与项目说明,便于理解数据标注格式、训练参数调优及推理实现细节。目前已有127人学习下载,适合需要参考完整工程结构、复现检测效果并进行二次开发的读者。
1. 无人机航拍红外人车识别为什么非 YOLOv8 不可
先给一个反直觉的结论:在无人机航拍的红外视频里,行人目标的尺寸常常只有 8×8 到 16×16 像素,而车辆热辐射强烈、对比度极高,一个画面里你可能同时碰到“比噪声还小的人”和“边缘锐利得像标靶的车”。这种动态范围撕裂,恰好是 YOLOv8 这类 Anchor-Free 目标检测模型相对好应付的场景——它不需要像传统两阶段检测器那样依赖候选框与锚框匹配,而是直接在特征图上回归中心点与宽高,少了锚框饥饿问题。但真跑到无人机上,模型反而容易在下采样过程里把人丢光:红外单通道信息的纹理极弱,YOLOv8 的 C2f 结构一旦堆得太深、保留通道太窄,小目标特征就被噪声淹没了。这篇文章要解决的,是把 YOLOv8 从通用目标检测迁移到“无人机航拍 + 红外 + 人车二分类”这个具体组合的落地路径:从结构取舍到数据集标注、从训练参数到损失曲线判读、从边缘部署到小目标增强验证。适合正在做毕业设计、红外探测课题,或者要给行业无人机加装算法面的研发工程师。
2. YOLOv8 目标检测的 C2f 结构与 Anchor-Free 检测头在红外场景的取舍
2.1 红外图像相比可见光少了一半信息量
红外单通道图像本质上记录的是目标与背景的温差辐射分布,不依赖光照、不受阴影干扰,但也没有颜色和纹理细节。以 640×512 分辨率的非制冷长波红外机芯为例,一个在 100 米高度拍摄的行人热签名大约覆盖 10 到 20 个像素,车辆热签名略好,也只是 40×40 左右。这种目标尺寸分布决定了模型设计的第一条约束:骨干网络不能丢掉 4 倍或 8 倍下采样级别的特征。YOLOv8 的 P3 特征层(输入尺寸的 1/8)是后续小目标检测的主战场,C2f 模块里 split 出来的特征会与梯度流再次拼接,信息密度比 YOLOv5 的 C3 更高,但也更考验显存。实际选择时,除非目标已经在画面里占到 100 像素以上,否则不建议为了推理速度把输入尺寸压到 416 以下。
2.2 C2f 模块、SPPF 与检测头在 YOLOv8 配置里的直接体现
YOLOv8 的模型结构由 Ultralytics 用 yaml 描述,下面是一份针对红外单通道输入的 YOLOv8s 简化配置,保留了可训练层与模块的完整映射:
# yolov8s-ir.yaml(输入为单通道红外图) nc: 2 # person, vehicle scales: s: [0.33, 0.50, 1024] # depth, width, max_channels backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, Conv, [128, 3, 2]] - [-1, 3, C2f, [128, True]] - [-1, 1, Conv, [256, 3, 2]] - [-1, 6, C2f, [256, True]] - [-1, 1, Conv, [512, 3, 2]] - [-1, 6, C2f, [512, True]] - [-1, 1, Conv, [1024, 3, 2]] - [-1, 3, C2f, [1024, True]] - [-1, 1, SPPF, [1024, 5]] head: - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 6], 1, Concat, [1]] - [-1, 3, C2f, [512]] - [-1, 1, nn.Upsample, [None, 2, "nearest"]] - [[-1, 4], 1, Concat, [1]] - [-1, 3, C2f, [256]] - [-1, 1, Detect, [nc]]这份配置的核心是让 P5 深层的强语义特征逐级上采样,与 P4、P3 的浅层高分辨率特征拼接。C2f 中的True表示启用残差连接,这会让梯度回传更顺,收敛更快。SPPF用多个 5×5 最大池化级联替代前代的 SPP,减少了 20% 左右的计算量,同时在最后一层池化前保留了 1024 通道,对红外大目标(车辆)依然有足够的感受野。
提示:把第一层卷积的输入通道从 3 改成 1 是关键改动。很多红外项目直接加载 coco 预训练权重,然后用三通道重复灰度图去训练,效果远不如单通道随机初始化或红外域自监督预训练。
2.3 Anchor-Free 检测头为什么更适合红外人车混检
YOLOv8 检测头不再使用 YOLOv5 的 Anchor-Based 方式,而是把目标检测转化为两个分支:分类分支用 sigmoid 输出类别概率,回归分支用 Distribution Focal Loss 直接估计边界框。它不需要聚类得到锚框尺寸,这对于红外图像非常友好,因为红外人车目标的宽高比不像可见光那样稳定:人可能因姿态变成 0.2 的扁长框,车则会因航向角不同从 1.0 的正方形变成 3.0 的长条形。锚框聚类在混合曲线形态下会得到一个尴尬的折中,Anchor-Free 则避免了这种先验误差。另一个实际收益是在后处理阶段少了锚框匹配导致的重复检测,对无人机红外这种目标数量少、但每帧都抖动的视频流,False Positive 更容易被压住。
2.4 针对红外的 head 改进可以等训练跑通再做
很多人一上来就改检测头,把裸模型直接换成 BiFPN、加注意力。这个思路在红外小目标场景没错,但会引入两个前期难以排查的问题:一是数据集本身质量不够,改结构只会放大标注噪声;二是 C2f 带来的梯度流变化意味着学习率、损失权重全部要重调。我的建议是第一阶段先把原版 YOLOv8 的 head 配上改进的损失权重,比如把分类损失中 person 类别的正负样本权重调到 1.2 到 1.5,优先解决小目标的漏检而不是精确到个位数的框精度。等 mAP50 稳定在 0.6 以上,再考虑在 P2 层加一个超小目标检测头,或者把 Neck 部分换成加权双向特征融合,这个时机点通常在第 80 轮训练之后。
| 对比维度 | YOLOv5 C3 结构 | YOLOv8 C2f 结构 |
|---|---|---|
| 特征拼接方式 | 单分支输出 | split 后多分支拼接 |
| 梯度流 | 单一回传 | 残差并联回传 |
| 小目标特征保留 | 一般 | 较好 |
| 显存占用 | 低 | 同宽度下略高约 10% |
| 红外小目标适配 | 需要额外加注意力 | 原生即可起步 |
表里的显存差异在 640×512 输入下实际只有几百 MB,对大多数显卡可以忽略,真正影响选型的是输入分辨率和 batch size。
3. 无人机红外数据集的标注与 YOLOv8 训练全流程
3.1 红外小目标切图:不做切图,训练就是白费
无人机航拍红外图的分辨率普遍在 640×512 到 1280×1024 之间,直接整图输入会让小目标在 8 倍下采样后只剩一两个像素,训练出来的模型几乎全是漏检。常见做法是先把原始红外图做切图预处理,再在切块上标注、训练。这会带来一个问题:目标被切在边缘,尤其是行人目标,特征被切断后模型学习不到完整的热辐射轮廓。
我一般会把切图做成带重叠率的滑动窗口,重叠率取 15% 到 20%。以 640×512 原图、训练输入 640 为例,切图窗口取 512,步长取 416,每个目标至少完整出现在一张切块中。切图的同时要把标注框坐标同步换算成新图内的相对坐标,超出边界的目标直接丢弃。下面是最小可用切图脚本:
import cv2 import numpy as np def slice_image_with_labels(image, labels, window_size=512, step=416, save_prefix="patch"): h, w = image.shape[:2] patches, out_labels = [], [] for y in range(0, h - window_size + 1, step): for x in range(0, w - window_size + 1, step): patch = image[y:y + window_size, x:x + window_size] # 过滤与当前窗口相交的标注框 keep = [] for cls, xc, yc, bw, bh in labels: # YOLO 格式: class, centers, w, h x1, y1 = (xc - bw / 2) * w, (yc - bh / 2) * h x2, y2 = (xc + bw / 2) * w, (yc + bh / 2) * h # 仅保留中心点落在窗口内的目标 if x <= x1 * (x2 - x1) / w * 0 + x1 and x1 < x + window_size and \ y <= y1 and y1 < y + window_size: if x1 >= x and y1 >= y and x2 <= x + window_size and y2 <= y + window_size: keep.append([cls, (x1 - x) / window_size, (y1 - y) / window_size, (x2 - x1) / window_size, (y2 - y1) / window_size]) if len(keep) > 0: patches.append(patch) out_labels.append(keep) for i, (patch, labels) in enumerate(zip(patches, out_labels)): cv2.imwrite(f"{save_prefix}_{i}.png", patch) with open(f"{save_prefix}_{i}.txt", "w") as f: for line in labels: f.write(" ".join(str(v) for v in line) + "\n") return patches, out_labels切图窗口越大保留的上下文越多,但红外小目标会被压得更小;窗口越小目标像素占比越大,可训练的有效样本越多,但容易丢失车辆的整体轮廓,导致车的类别置信度下降。实际中 512 与 640 是两个常用值,配合 Mosaic 增强,红外人车这个组合基本能覆盖。
3.2 红外单通道数据增强的取舍
红外图像不能像可见光那样做随机 HSV 扰动,颜色空间没有意义。但可以做灰度线性拉伸、直方图均衡化和高斯噪声注入。热成像的对比度取决于环境温差,比如白天车顶升温后和路面接近,夜间人和建筑物背景区别明显,同一个模型要适应这种温差变化,最好的方式是在线做对比度增强。YOLOv8 的hsv_h、hsv_s参数对单通道图几乎无效,但hsv_v相当于调增益,可以保留。我在训练红外模型时会把hsv_v设为 0.3,把translate设为 0.2,用小幅度平移模拟无人机悬停震动带来的目标位置抖动,这比可见光项目里常用的 0.1 要略大一些。
3.3 环境配置与最小训练命令
环境搭建方面,YOLOv8 官方基于 PyTorch,PyTorch 版本推荐 2.0 以上,CUDA 11.8 或 12.1 在这个任务上都有大量验证,如果显卡是 GTX 1660 Ti 这种 6GB 显存的,建议把batch压到 8 以下,同时开启amp混合精度,训练时间会从不可接受降到 8 到 10 小时可得一个可用权重。
# 安装 ultralytics 与依赖 pip install ultralytics torch torchvision # 直接开始训练红外数据集 yolo detect train \ model=yolov8s.pt \ data=infrared_person_vehicle.yaml \ imgsz=640 \ batch=8 \ epochs=150 \ patience=30 \ amp=True \ project=run_ir_drone训练时会自动下载 COCO 预训练权重,下面是一份数据配置文件的内容示例:
path: ./datasets/infrared_drone # 数据集根目录 train: images/train val: images/val nc: 2 names: 0: person 1: vehicle第一次训练建议先拿yolov8s.pt预训练权重跑 30 轮,确认 loss 正常下降后再完整训练。若发现 loss 收敛慢,优先检查数据增强里的scale参数,默认 0.5 的缩放范围对红外小目标过强,建议改成 0.3。
4. YOLOv8 训练参数、损失函数与红外评价指标怎么调
4.1 从 6GB 到 24GB 显存的关键超参设置
红外航拍数据集通常不会太大,几百到几千张切图是常态。在这个体量下,训练超参有明显区别于 COCO 的地方:学习率不宜照抄默认的lr0=0.01,我一般用 0.005 起步,避免前期过拟合到少量高亮车辆目标上。weight_decay保持 0.0005,但会把cos_lr=True打开,因为红外人车目标尺度差异大,余弦退火能帮助后期精细拟合小目标。
| 超参数 | 推荐值 | 说明 |
|---|---|---|
| imgsz | 640 | 保持红外人形至少 10px;低于 512 小目标必丢 |
| batch | 8 / 16 / 32 | 6GB 卡用 8,12GB 用 16,24GB 用 32 |
| optimizer | SGD 或 AdamW | 数据量小建议 SGD,收敛稳;AdamW 收敛快但易过拟合 |
| lr0 / lrf | 0.005 / 0.01 | 红外域与 COCO 差异大,降低初始学习率 |
| hsv_v | 0.3 | 模拟热成像增益变化 |
| scale | 0.3 | 目标本身是小目标,缩放过大等于抹掉目标 |
| fl_gamma | 1.5 | 焦点损失参数,提高 hard example 权重 |
参数之间有关联。比如把imgsz从 640 提到 768,小目标像素变大,但 batch 要相应减半,训练时间增加约 80%。只有当目标普遍小于 10 像素时才值得这么做。
4.2 用 YOLOv8 的损失函数曲线判断训练健康度
YOLOv8 的损失由三部分组成:box_loss 用 CIoU 计算边界框回归损失,cls_loss 用 BCEWithLogits 算分类损失,还有 DFL 损失负责学习边界框四条边与锚点的距离分布。训练过程中,Ultralytics 会把每一步的损失写到runs/detect/train/results.csv里,直接用 Pandas 读出来画图,这是判断过拟合最直观的方法。下面是一个画 YOLOv8 损失函数曲线的脚本:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/train/results.csv") df.columns = [c.strip() for c in df.columns] fig, axes = plt.subplots(1, 3, figsize=(15, 4)) axes[0].plot(df["epoch"], df["train/box_loss"], label="box") axes[0].plot(df["epoch"], df["train/cls_loss"], label="cls") axes[0].plot(df["epoch"], df["train/dfl_loss"], label="dfl") axes[0].set_title("train losses") axes[0].legend() axes[1].plot(df["epoch"], df["val/box_loss"], label="val box") axes[1].plot(df["epoch"], df["val/cls_loss"], label="val cls") axes[1].set_title("val losses") axes[1].legend() axes[2].plot(df["epoch"], df["metrics/precision(B)"], label="precision") axes[2].plot(df["epoch"], df["metrics/recall(B)"], label="recall") axes[2].plot(df["epoch"], df["metrics/mAP50(B)"], label="mAP50") axes[2].set_title("metrics") axes[2].legend() plt.tight_layout() plt.savefig("loss_curves.png", dpi=150)该脚本中三个子图要配合看,比如训练损失下降、验证损失不降,则进入过拟合,应在 patience 触发前手动停止;训练损失和验证损失同时不降,说明学习率过低或特征提取能力不足,此时应调整 C2f 的 depth 倍数或换更大的模型尺寸。如果train/box_loss下降到 1.0 以下但val/cls_loss居高不下,通常是因为红外图像中人与车热特征在某些场景下高度重叠,需要增加负样本或调整 fl_gamma。
4.3 红外小目标检测的评价参数不能只看 mAP
YOLOv8 默认输出mAP50和mAP50-95,但红外小目标检测领域对“检测到”的定义更苛刻,目标只要偏出中心点 3 个像素,就算人工标注也常有不一致。除了常规的精确率、召回率、F1,红外场景还会用置信度阈值扫描后的检测概率、虚警率,以及信杂比增益和背景抑制因子来评价算法对弱小目标的增强效果。实际工程里我至少会看三组数据:mAP50 衡量整体可用性;目标尺寸在 10 像素以下的召回率,这部分直接决定无人机高度上限;以及每帧平均虚警数——如果一帧出现超过 3 个假目标,地面站人员很快会疲劳误判。
5. 切块推理增强与 TensorRT 部署热红外模型的验证套路
5.1 训练时切图,推理时也要切图
训练做过切图,推理时直接用整图会面临尺度漂移问题。常见做法是让推理逻辑复现训练时的切图策略:把无人机下传的红外帧按 512 窗口、128 步长切成多块,分别推理后合并结果,重叠区域用 NMS 去重。这块逻辑可以封装成predict_patches函数,核心是记录每个 patch 相对原图的偏移量。无人机载板算力有限时,优先只对检测目标方差较大的区域做强切图,背景均匀的天空区域直接跳过。
5.2 导出 ONNX/TensorRT 并做精度对比
模型训练完成后导出为 TensorRT engine,在 Jetson 或香橙派这类边缘设备上,推理速度比 PyTorch 快 3 倍以上。导出前先转 ONNX,验证推理逻辑与 PyTorch 输出一致,再转 engine。导出命令如下:
# 导出 ONNX,opset=12 兼容更多部署框架 yolo export model=runs/detect/train/weights/best.pt format=onnx opset=12 imgsz=640 # 转 TensorRT FP16,注意显存足够 trtexec --onnx=best.onnx --saveEngine=best_fp16.engine --fp16提示:如果 ONNX 推理结果与 PyTorch 差异超过 2%,优先检查模型输入是否做了同样的归一化,YOLOv8 默认除以 255,切图函数里不要再额外做标准化。
5.3 用尺寸分桶 AP 验证小目标增强收益
一个值得做的小技巧:把验证集目标按像素尺寸分为 <16px、16-32px、32px 以上三桶,分别计算 AP,这样能精确定位增强手段是救了小目标还是只提了大目标。实际项目中,经过切块推理,<16px 目标的召回率通常能从 0.3 提升到 0.7 以上,这个收益来自两个层面:一是目标在切块中的相对尺寸变大,二是推理时重叠窗口提供了重复观测,等效于多帧确认,降低了单帧噪声导致的漏检。建议每次改动推理策略后,都把这三个分桶数值固化到实验记录里,红外人车识别的算法迭代就变成数据驱动的数字游戏,而不是感觉游戏。
本文还有配套的精品资源,点击获取