☰
YOLOv8实战:飞机、鸟类、无人机三分类检测与RK3588部署
2026/10/4 2:59:36 网站建设 项目流程

简介:本资源面向计算机视觉算法工程师、无人机反制与低空安防研究者及目标检测学习者,提供一套可直接训练的YOLOv8细分类型飞机、鸟类、无人机检测模型与配套数据集,解决细粒度目标识别中类别混淆、样本不足的问题。压缩包共约2000个文件,以1984个txt标签文件为主,另含13个md说明文档、2个pdf资料与1个yaml配置文件,整体约867.2MB。数据集包含1万多张图像,采用YOLO格式标注,已划分train、val、test三个子集,并附data.yaml,yolov5、yolov7、yolov8等主流框架均可直接读取训练。目录结构清晰,标签与图像一一对应,可区分具体飞机型号,便于快速复现与二次开发。目前已有443人学习下载,适合需要细粒度检测方案、想省去数据清洗与划分环节的读者参考使用。

1. 从一张混淆矩阵说起:飞机、鸟类、无人机为什么要放在同一个检测头里

去年帮一个做机场净空监测的团队看模型,他们拿 COCO 预训练的 YOLOv8m 直接跑场区监控,结果一张图里同时出现客机、海鸥和四旋翼,模型把远处巡航的无人机标成了鸟,把贴地飞的海鸥标成了无人机,飞机倒是没漏,但置信度忽高忽低。问题不在 YOLOv8 本身,而在于 COCO 里bird、airplane是两个独立类,drone压根没有对应类别,模型只能把无人机往kite或bird上靠。这就是「细分类型飞机-鸟类-无人机检测」这个方向真正要解决的事:把三类空中目标收进一个检测头,让模型学会区分它们的轮廓、纹理和运动上下文,而不是靠单帧颜色硬猜。

这套方案适合三类人:做低空安防、机场净空、电力巡检的工程团队;手里有无人机航拍或塔台监控视频、想自己标数据训模型的人;以及已经跑通 yolov8训练自己的数据集、想换一套更贴场景类别的开发者。它不解决「从零发明检测器」,而是把 YOLOv8 这套成熟框架落到一个具体的三分类任务上,顺带把数据集构建、训练参数、部署到 RK3588 这类边缘板的路径讲清楚。下面按「数据怎么来 → 模型怎么训 → 坑在哪 → 怎么验证」的顺序展开,能抄的地方直接给命令和配置。

2. 数据集构建:飞机、鸟类、无人机三类的标注边界怎么划

2.1 三类目标的视觉差异与标注粒度

飞机、鸟类、无人机在单帧图像里的可分性其实比想象中弱。大型客机有固定翼和发动机短舱,轮廓规整、长宽比稳定;鸟类翅膀形态随扑翼相位剧烈变化,同一只鸟在不同帧里可能是「一」字也可能是「V」字;多旋翼无人机在远距离下就是一个带旋翼模糊的小十字,和悬停的猛禽轮廓高度相似。标注时如果只框一个外接矩形,模型学到的更多是背景和尺度先验,而不是目标本身。

我一般建议按「可见主体」标注:飞机框住机身加机翼,不把尾迹算进去;鸟类框住躯干和展开的翼展,翅膀收拢时按实际像素范围框;无人机框住机体加旋翼盘面,不把地面投影算进去。三类都只用一个类框,不做部件级标注,因为细分类型检测的落地场景要的是「是什么」,不是「哪个部位」。如果后续要做机型细分,再在飞机类里加子类,那是另一个任务。

标注工具用 LabelImg 或 CVAT 都行,导出 YOLO 格式的 txt。每张图对应一个同名 txt,每行class_id x_center y_center width height,坐标归一化到 0-1。类别映射固定为0: airplane, 1: bird, 2: drone,这个顺序一旦定下就不要在训练中途改,否则标签和权重对不上,损失曲线会直接起飞。

2.2 数据来源与增强策略

公开数据里,飞机可以用 HRSC2016 或 DOTA 里的机场子集,鸟类可以用 CUB-200 里带飞行姿态的图,无人机可以找 UAVDT 或自己用消费级无人机拍。但直接拼凑会有严重的域偏移:HRSC2016 是遥感俯视,CUB 是近距离侧拍,UAVDT 是车载视角,三者混在一起训,模型会先学「视角」而不是「类别」。我的做法是分域采样,每个域至少占 20%,再用强增强把视角差异抹平。

增强用 YOLOv8 内置的mosaic、mixup、hsv_h/s/v、degrees、translate、scale、flipud/fliplr。针对这个任务,degrees可以开到 15,因为空中目标姿态多变;flipud要慎用,飞机倒飞和鸟倒飞在真实场景里极少,开了反而引入噪声。mosaic概率保持默认 1.0,但要注意它会把四张图拼成一张,小目标被缩得更小,如果数据里无人机本身就只有几十像素,建议把mosaic降到 0.5 并配合copy_paste做小目标复制。

数据划分按 8:1:1 走,但不要随机分。同一段视频的连续帧必须落在同一个集合里,否则训练集和验证集共享背景,mAP 会虚高。我一般按视频源或拍摄批次划分,保证验证集里的场景在训练集里没见过。划分脚本用 Python 写,读 images 目录,按文件名前缀分组,再按组分配。

import os, random, shutil from pathlib import Path random.seed(42) root = Path("datasets/air_bird_drone") img_dir = root / "images" lbl_dir = root / "labels" # 按文件名前缀分组,同组不拆散 groups = {} for img in img_dir.glob("*.jpg"): key = img.stem.split("_")[0] # 假设命名如 airport_0001.jpg groups.setdefault(key, []).append(img.stem) keys = list(groups.keys()) random.shuffle(keys) n = len(keys) train_k = keys[:int(n*0.8)] val_k = keys[int(n*0.8):int(n*0.9)] test_k = keys[int(n*0.9):] for split, ks in [("train", train_k), ("val", val_k), ("test", test_k)]: (root / split / "images").mkdir(parents=True, exist_ok=True) (root / split / "labels").mkdir(parents=True, exist_ok=True) for k in ks: for stem in groups[k]: shutil.copy(img_dir / f"{stem}.jpg", root / split / "images" / f"{stem}.jpg") shutil.copy(lbl_dir / f"{stem}.txt", root / split / "labels" / f"{stem}.txt") print("split done:", len(train_k), len(val_k), len(test_k))

这段脚本的关键在key = img.stem.split("_")[0],它把同一批次或同一视频的帧绑在一起。如果你的命名没有统一前缀,就换成按目录名分组。random.seed(42)保证每次划分一致,方便复现。跑完检查三个 split 的类别分布,如果某一类在验证集里少于 5%,说明采样不均,要回去补数据。

2.3 data.yaml 的写法与路径陷阱

YOLOv8 靠一个 yaml 文件找数据和类别名。常见写法:

path: /home/user/datasets/air_bird_drone train: train/images val: val/images test: test/images nc: 3 names: 0: airplane 1: bird 2: drone

path用绝对路径最稳,相对路径在yolo train从不同工作目录启动时会翻车。names的键必须是整数,写成字符串"0"在某些版本里会报KeyError。如果训练时提示No labels found,先确认 labels 目录和 images 目录同级且文件名一一对应,再确认 txt 里没有空行和多余空格。YOLOv8 对空 txt 是容忍的,但一行里多一个空格就会解析失败,这个坑我踩过不止一次。

3. 训练配置:从 yolov8n 到 yolov8m 的选型与参数落点

3.1 模型规模与输入分辨率怎么定

YOLOv8 提供 n/s/m/l/x 五档。飞机-鸟类-无人机这个任务,如果部署在 RK3588 或 GTX1660Ti 这类算力有限的设备上,yolov8n和yolov8s是主力。n 的参数量约 3.2M,s 约 11.2M,m 约 25.9M。实测在 640 输入下,n 在 1660Ti 上能跑到 100+ FPS,s 大概 60 FPS,m 掉到 30 FPS 左右。如果场景里无人机目标普遍小于 32 像素,优先提分辨率而不是换大模型:imgsz=960带来的小目标召回提升,通常比 n 换 m 更明显。

输入分辨率的选择要看目标像素分布。先统计训练集里三类目标的框面积,算 sqrt(w*h) 的中位数。如果中位数在 20-40 像素,imgsz=640够用;低于 20,上 960 或 1280;高于 80,640 就够,再大只是浪费算力。改分辨率时记得同步改batch,显存不够就降 batch 或开amp。

3.2 训练命令与关键参数

最小可跑命令:

yolo detect train \ data=/home/user/datasets/air_bird_drone/data.yaml \ model=yolov8s.pt \ imgsz=640 \ epochs=150 \ batch=16 \ device=0 \ workers=8 \ project=runs/abd \ name=exp_s_640 \ pretrained=True \ optimizer=SGD \ lr0=0.01 \ lrf=0.01 \ momentum=0.937 \ weight_decay=0.0005 \ warmup_epochs=3.0 \ cos_lr=True \ close_mosaic=10 \ patience=30 \ seed=0

逐项说:pretrained=True加载 COCO 权重,三类任务微调必须开,否则收敛慢且 mAP 低一截。optimizer=SGD是 YOLOv8 官方默认,AdamW在小数据集上容易过拟合,除非数据量低于 2000 张再考虑。lr0=0.01配合cos_lr=True做余弦退火,lrf=0.01是最终学习率系数,即最低降到 0.0001。warmup_epochs=3.0让前 3 个 epoch 学习率从低到高爬,避免一开始就把预训练权重冲烂。close_mosaic=10在最后 10 个 epoch 关掉 mosaic,让模型在真实分布上收尾,这个对 mAP 提升通常有 1-2 个点。patience=30是早停,30 轮验证 mAP 不涨就停,省时间。

如果显存不够,把batch降到 8,同时把lr0按比例降到 0.005,因为 batch 变小梯度噪声变大,学习率不降容易震荡。workers设成 CPU 核数的 0.75 左右,设太大反而因为 IO 争抢变慢。

3.3 损失曲线与指标怎么看

训练启动后,runs/abd/exp_s_640/下会生成results.csv和weights/。results.csv里关注train/box_loss、train/cls_loss、val/box_loss、val/cls_loss、metrics/mAP50、metrics/mAP50-95。正常情况 box_loss 和 cls_loss 都单调下降,val 曲线略高于 train 但趋势一致。如果 train 降 val 不降甚至上升,就是过拟合,加数据或加dropout(YOLOv8 里通过dropout参数,默认 0,可设 0.1)。如果两条都平着不降,检查学习率是不是太小或标签有没有问题。

画损失曲线用 pandas + matplotlib,读results.csv直接 plot。注意results.csv的列名带空格,读的时候用skipinitialspace=True。这个脚本很短,但能帮你快速判断训练是不是在正轨上,比盯着终端刷屏有用。

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/abd/exp_s_640/results.csv", skipinitialspace=True) df.columns = [c.strip() for c in df.columns] fig, ax = plt.subplots(1, 2, figsize=(12, 4)) ax[0].plot(df["epoch"], df["train/box_loss"], label="train_box") ax[0].plot(df["epoch"], df["val/box_loss"], label="val_box") ax[0].set_xlabel("epoch"); ax[0].set_ylabel("box_loss"); ax[0].legend() ax[1].plot(df["epoch"], df["metrics/mAP50"], label="mAP50") ax[1].plot(df["epoch"], df["metrics/mAP50-95"], label="mAP50-95") ax[1].set_xlabel("epoch"); ax[1].set_ylabel("mAP"); ax[1].legend() plt.tight_layout(); plt.savefig("loss_curve.png", dpi=150)

跑完看loss_curve.png,如果 mAP50 在 0.85 以上、mAP50-95 在 0.55 以上,三类任务基本可用。低于这个数先别急着换模型,回去查标注质量和验证集分布。

4. 避坑与排查:三类混检最容易翻车的五个地方

4.1 鸟类和无人机互相误检,mAP 卡在 0.6 上不去

现象:验证集里鸟被标成 drone、无人机被标成 bird,两类混淆矩阵对角线外数值很高。原因通常是训练集里这两类的背景高度相似,都是天空,且目标尺度接近,模型只能靠纹理区分,而远距离下纹理被压缩没了。解决:在数据里加入「同背景不同类」的硬负样本,比如同一片天空下既有鸟又有无人机的帧;同时把imgsz提到 960,让纹理信息保留更多;如果还不行,在names里把两类合并成aerial先训一版,再在检测头上做二阶段细分。

4.2 飞机类召回高但误检地面车辆

现象:机场场景里,跑道上的摆渡车、加油车被标成 airplane。原因是飞机和车辆在俯视或侧视下都是长条形,且训练集里飞机样本多、车辆样本少,模型把「长条+灰色」当成了飞机特征。解决:在数据里显式加入车辆负样本,标成背景(不写进 txt 或单独加一个vehicle类再在推理时过滤);同时开rect=True做矩形推理,减少 letterbox 带来的形变,让长宽比特征更可靠。

4.3 训练到一半 loss 变 NaN

现象:train/box_loss突然变成 nan,训练中断。原因多半是学习率太大或某张图的标签坐标越界(比如 x_center 写成 1.2)。解决:先检查所有 txt,用脚本扫一遍坐标范围,把越界的行修掉或删掉对应图;然后把lr0降到 0.005,开amp=True(默认开)但把grad_clip设成 10.0。YOLOv8 默认有梯度裁剪,但极端标签仍会炸。

4.4 验证集 mAP 很高,实际部署漏检严重

现象:results.csv里 mAP50 0.9,但拿现场视频跑,无人机漏检一半。原因是验证集和训练集同源,背景、光照、拍摄角度都相似,模型没学到泛化。解决:重新按拍摄批次划分验证集,确保验证集里的场景训练时没见过;再补一版跨域测试,比如用训练时没出现过的机型或天气。如果跨域掉点超过 20 个 mAP,说明数据多样性不够,回去补数据比调参有用。

4.5 导出 RK3588 后精度掉一截

现象:PyTorch 权重 mAP50 0.88,转 ONNX 再转 RKNN 后掉到 0.75。原因通常是量化校准集选得不好,或者导出时opset和dynamic设置不对。解决:导出 ONNX 用opset=12、dynamic=False、simplify=True;RKNN 量化时校准集要从验证集里抽 200-500 张,覆盖三类目标和不同光照,不要只用一类。如果还掉点,把mean_values和std_values对齐训练时的归一化参数,YOLOv8 默认是 0-1 归一化,RKNN 里不要重复除 255。

5. 部署到 RK3588 与推理验证:从 pt 到 rknn 的完整链路

5.1 导出 ONNX 与 RKNN 转换

RK3588 的 NPU 只吃 RKNN 格式,链路是pt → onnx → rknn。先导出 ONNX:

yolo export \ model=runs/abd/exp_s_640/weights/best.pt \ format=onnx \ imgsz=640 \ opset=12 \ simplify=True \ dynamic=False

simplify=True会调 onnx-simplifier 去掉冗余节点,RKNN 转换时更稳。dynamic=False固定输入尺寸,NPU 对动态 shape 支持差。导出后用onnxruntime跑一张图,确认输出和 PyTorch 一致,再进 RKNN 工具链。

RKNN 转换脚本:

from rknn.api import RKNN rknn = RKNN(verbose=True) rknn.config( mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform="rk3588", quantized_dtype="asymmetric_quantized-8", ) rknn.load_onnx(model="best.onnx") rknn.build(do_quantization=True, dataset="calib.txt") rknn.export_rknn("best.rknn") rknn.release()

mean_values和std_values这里写0和255,是因为 YOLOv8 训练时输入是 0-1,RKNN 内部会做(x - mean) / std,等价于除 255。如果你的训练用了别的归一化,这里要对应改。calib.txt每行一张校准图路径,抽 300 张左右,三类目标都要有。quantized_dtype用默认的 8bit 非对称量化,精度和速度平衡最好。

5.2 板端推理与后处理对齐

RK3588 上跑 RKNN 模型,输出是三个尺度的特征图,需要自己做 decode 和 NMS。常见做法是用 rknn_model_zoo 里的 YOLOv8 demo 改类别数和阈值。关键参数:conf_thres=0.25、iou_thres=0.45、max_det=300。如果板端漏检多,先把conf_thres降到 0.15 看召回能不能上来,再决定是调阈值还是回去补数据。

验证方法:在板端跑一段现场视频,把检测结果和 PyTorch 同帧结果并排看。如果板端框偏移明显,检查 letterbox 的 padding 是否在 decode 时正确还原;如果类别错乱,检查names顺序是否和训练时一致。我一般会在板端存 100 帧带框结果,和 PC 端逐帧比对,确认 mAP 掉点不超过 3 个点才算过关。

5.3 一个容易被忽略的验证技巧

部署完别只看 mAP,拿一段「三类同框」的视频跑,统计每类的漏检和误检。具体做法:人工标 200 帧作为测试基准,写脚本算每类的 precision/recall,再画 PR 曲线。如果某一类 recall 低于 0.7,优先查这一类在训练集里的样本量和标注质量,而不是调模型。我自己的习惯是每次训完模型,先跑一遍「三类同框」测试集,再决定要不要上线。这个习惯帮我省过好几次返工——有一次飞机类 recall 只有 0.65,查下来是标注时把尾迹也算进框里,模型学偏了,重新标了 300 张就好了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询