简介:本资源为基于YOLOv8的无人机航拍牧羊目标检测项目代码,面向深度学习与计算机视觉方向的学习者、研究者及需要落地航拍牲畜识别方案的开发者。项目围绕无人机视角下的羊群检测任务,提供从数据配置、模型训练到推理部署的完整工程结构,适合具备一定Python与PyTorch基础的中高级读者参考复现。压缩包共468个文件,约26.23MB,以227个md说明文档、130个py源码、43个yaml与12个yml配置为主,另含pt权重、cpp推理示例、sh脚本、ipynb笔记及少量图片与csv结果文件,覆盖训练、验证、导出与多平台部署等环节。按requirements.txt配置环境即可运行。目前已有85人学习关注。读者可据此掌握航拍小目标检测的数据组织方式、YOLOv8训练调参流程、模型推理与C++部署思路,并借助文档与配置快速搭建可复现的牧羊识别实验环境。
1. 无人机航拍牧羊识别:这套 YOLOv8 代码到底能解决什么
牧区找羊这件事,靠人骑马巡山效率极低,一片草场转下来大半天,羊群散进灌木丛或山坳里,肉眼根本数不清。换成无人机航拍,单架次覆盖几平方公里,但新的问题来了:拍回来的视频几百上千帧,靠人一帧帧看,眼睛看花了也容易漏。这套 YOLOv8 无人机航拍牧羊识别项目代码,解决的就是从「航拍画面」到「羊只位置」这一步自动化。它基于 YOLOv8 目标检测框架,针对航拍视角下的羊只目标做训练和推理,输出每只羊的边界框和置信度。适合两类人:一是做智慧牧场、草原巡检的工程团队,需要快速搭一个可用的检测基线;二是正在学 YOLOv8 落地流程的开发者,想拿一个真实场景的数据集和训练脚本跑通全链路。代码包里通常包含数据集配置文件、训练脚本、推理脚本和权重文件,拿到手改改路径就能跑。
2. 航拍牧羊检测的技术选型:为什么是 YOLOv8 而不是别的
2.1 航拍场景对检测器的三个硬要求
无人机航拍牧羊识别和普通地面拍摄的目标检测有本质区别。第一,目标尺度小。无人机飞行高度通常在 30 到 120 米,一只羊在 4K 画面里可能只占 40 到 80 像素,YOLOv8 的 P3 特征层(stride 8)刚好能覆盖这个尺度范围,再小的目标就得靠 P2 层,但 P2 会显著增加计算量。第二,背景干扰强。草原纹理、灌木阴影、石头都容易和羊只混淆,尤其白羊在枯草背景下的对比度很低。第三,实时性要求。无人机图传链路带宽有限,检测模型不能太重,YOLOv8n 或 YOLOv8s 是比较平衡的选择,参数量分别在 3.2M 和 11.2M 左右。
YOLOv8 相比 YOLOv5 的核心改动在于:C2f 模块替换了 C3 模块,梯度分流更充分;Anchor-Free 解耦头省去了聚类先验框的步骤,对小目标密集场景更友好;损失函数用 TaskAlignedAssigner 做正负样本分配,训练收敛更稳。这些改动在航拍小目标场景下带来的实际收益是:同等参数量下 mAP 通常有 1 到 3 个点的提升,而且不需要针对数据集重新聚类 anchor。
2.2 模型规格选择与参数对照
项目代码一般会提供多个规格的配置,选哪个取决于你的部署平台和精度要求。下面这张表是我在实际项目里总结的选型参考:
| 模型规格 | 参数量 | 输入尺寸 | 适合场景 | 推理设备参考 |
|---|---|---|---|---|
| YOLOv8n | 3.2M | 640 | 机载实时推理 | Jetson Orin Nano |
| YOLOv8s | 11.2M | 640 | 地面站后处理 | GTX 1660 Ti |
| YOLOv8m | 25.9M | 640/1280 | 高精度离线分析 | RTX 3060 及以上 |
| YOLOv8l | 43.7M | 1280 | 科研级精度 | RTX 3090 |
如果只是跑通流程验证效果,建议从 YOLOv8n 加 640 输入尺寸起步,训练一轮大概几分钟就能看到 loss 下降。如果追求召回率,把输入尺寸提到 1280 再配合 YOLOv8s,小目标召回会有明显改善,但显存占用大概翻倍。
2.3 环境搭建:从零到能跑训练
项目代码通常要求 Python 3.8 以上、PyTorch 1.8 以上。下面是我在 Ubuntu 20.04 上验证过的环境搭建步骤,CPU 版本也能跑推理,只是训练会慢很多:
# 创建虚拟环境,避免和系统 Python 冲突 conda create -n yolov8_sheep python=3.10 -y conda activate yolov8_sheep # 安装 PyTorch,有 GPU 的按 CUDA 版本选,没有就用 CPU 版 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装 ultralytics 和项目依赖 pip install ultralytics opencv-python numpy pyyaml tqdm这里有几个参数值得注意。cu118对应 CUDA 11.8,如果你的驱动版本较低,换成cu117或cu116。ultralytics包自带 YOLOv8 的完整实现,不需要单独克隆仓库。安装完成后用yolo checks命令可以验证环境是否正常,它会输出 PyTorch 版本、CUDA 可用性和设备信息。
提示:如果
yolo checks报错找不到命令,说明 ultralytics 没装进当前环境,检查 conda 环境是否激活。
3. 数据集准备与标注:航拍羊只数据的处理流程
3.1 数据采集与标注工具选择
航拍牧羊数据集一般来自无人机挂载的可见光相机,常见分辨率是 3840×2160 或 1920×1080。采集时建议覆盖不同时间段和光照条件:清晨、正午、傍晚各拍一些,顺光和逆光都要有,这样训练出来的模型泛化能力更强。飞行高度也尽量多样化,30 米、60 米、100 米各飞几个架次,让模型适应不同尺度的羊只。
标注工具用 LabelImg 或 Labelme 都行,YOLOv8 需要的是 YOLO 格式的 txt 标注文件,每行格式是类别编号 中心x 中心y 宽度 高度,坐标全部归一化到 0 到 1 之间。如果手头是 Labelme 的 JSON 格式,需要转一道:
import json import os def labelme_to_yolo(json_path, output_dir, class_names): """将 Labelme JSON 转为 YOLO txt 格式""" with open(json_path, 'r', encoding='utf-8') as f: data = json.load(f) img_w = data['imageWidth'] img_h = data['imageHeight'] lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_names: continue cls_id = class_names.index(label) points = shape['points'] xs = [p[0] for p in points] ys = [p[1] for p in points] # 计算归一化中心点和宽高 cx = (min(xs) + max(xs)) / 2.0 / img_w cy = (min(ys) + max(ys)) / 2.0 / img_h w = (max(xs) - min(xs)) / img_w h = (max(ys) - min(ys)) / img_h lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") base = os.path.splitext(os.path.basename(json_path))[0] with open(os.path.join(output_dir, base + '.txt'), 'w') as f: f.write('\n'.join(lines))这段代码的逻辑是:读取 Labelme 的 JSON 文件,提取每个标注框的四个顶点坐标,取最小外接矩形,再归一化。class_names是类别列表,牧羊场景通常只有「sheep」一个类,如果还要区分羊羔和成年羊,就加两个类别。转换完成后,每张图片对应一个同名 txt 文件,放在labels目录下。
3.2 数据集目录结构与 YAML 配置
YOLOv8 要求的数据集目录结构是固定的,不能随意改:
sheep_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片(可选) ├── labels/ │ ├── train/ # 训练集标注 │ ├── val/ # 验证集标注 │ └── test/ # 测试集标注 └── sheep.yaml # 数据集配置文件sheep.yaml的内容如下:
path: /home/user/sheep_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 test: images/test # 测试集相对路径 nc: 1 # 类别数量 names: # 类别名称 0: sheep这里path用绝对路径最稳妥,相对路径容易因为工作目录变化而找不到文件。nc和names必须对应,如果只有羊一个类,nc: 1,names里只写0: sheep。如果数据集里还有牧羊犬或其他动物,按需增加类别编号。
3.3 数据增强策略与参数设置
航拍数据天然存在样本不均衡问题:晴天多、阴天少,顺光多、逆光少。YOLOv8 内置了多种数据增强,训练时通过参数控制:
from ultralytics import YOLO model = YOLO('yolov8n.pt') model.train( data='sheep.yaml', epochs=100, imgsz=640, batch=16, hsv_h=0.015, # 色调抖动,模拟不同光照 hsv_s=0.7, # 饱和度抖动 hsv_v=0.4, # 亮度抖动 degrees=10.0, # 随机旋转角度 translate=0.1, # 随机平移比例 scale=0.5, # 随机缩放比例 fliplr=0.5, # 水平翻转概率 mosaic=1.0, # Mosaic 增强概率 mixup=0.1, # Mixup 增强概率 patience=20, # 早停耐心值 device=0 # GPU 编号,CPU 填 'cpu' )hsv_h、hsv_s、hsv_v这三个参数对航拍场景特别重要,因为无人机在不同时间段拍摄的画面色温差异很大。mosaic=1.0表示每张图都做 Mosaic 拼接,这对小目标检测有好处,但训练后期可以降到 0.5 让模型更关注单图特征。patience=20表示 20 轮验证集指标不提升就停止训练,避免过拟合。
4. 训练与推理:从权重文件到实际检测效果
4.1 训练过程监控与损失曲线解读
训练启动后,终端会实时输出每轮的 box_loss、cls_loss、dfl_loss 和 mAP 指标。box_loss 是边界框回归损失,cls_loss 是分类损失,dfl_loss 是分布焦点损失。正常情况下,三个 loss 都应该稳步下降,如果 cls_loss 震荡剧烈,可能是学习率偏大或 batch size 太小。
训练完成后,runs/detect/train/目录下会生成results.csv和weights/best.pt。用下面的代码画损失曲线:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('runs/detect/train/results.csv') df.columns = df.columns.str.strip() # 去除列名空格 fig, axes = plt.subplots(1, 2, figsize=(12, 4)) axes[0].plot(df['epoch'], df['train/box_loss'], label='box_loss') axes[0].plot(df['epoch'], df['train/cls_loss'], label='cls_loss') axes[0].set_xlabel('Epoch') axes[0].set_ylabel('Loss') axes[0].legend() axes[0].set_title('Training Loss') axes[1].plot(df['epoch'], df['metrics/mAP50(B)'], label='mAP50') axes[1].plot(df['epoch'], df['metrics/mAP50-95(B)'], label='mAP50-95') axes[1].set_xlabel('Epoch') axes[1].set_ylabel('mAP') axes[1].legend() axes[1].set_title('Validation mAP') plt.tight_layout() plt.savefig('training_curves.png', dpi=150)这段代码读取训练日志 CSV,分别画损失曲线和 mAP 曲线。重点看 mAP50 是否在后期趋于平稳,如果还在上升就说明训练轮数不够,可以加大 epochs 继续训。如果 mAP50 很高但 mAP50-95 很低,说明框的位置不够准,可以适当调高 box_loss 的权重。
4.2 推理脚本与批量检测
训练好的权重用best.pt,推理代码很简洁:
from ultralytics import YOLO import cv2 model = YOLO('runs/detect/train/weights/best.pt') # 单张图片推理 results = model('test_image.jpg', conf=0.25, iou=0.45, imgsz=640) for r in results: boxes = r.boxes for box in boxes: x1, y1, x2, y2 = box.xyxy[0].tolist() conf = box.conf[0].item() cls = int(box.cls[0].item()) print(f"类别: {cls}, 置信度: {conf:.2f}, 位置: ({x1:.0f},{y1:.0f})-({x2:.0f},{y2:.0f})") # 保存带标注的结果图 annotated = r.plot() cv2.imwrite('result.jpg', annotated)conf=0.25是置信度阈值,低于这个值的检测框会被过滤。航拍场景下如果羊只密集,可以适当降低到 0.15 提高召回,但误检也会增加。iou=0.45是 NMS 的 IoU 阈值,控制重叠框的合并程度。imgsz=640要和训练时保持一致,否则精度会下降。
批量处理视频或图片文件夹时,把路径换成文件夹或视频文件即可,YOLOv8 会自动逐帧处理:
# 处理整个文件夹 results = model('path/to/images/', conf=0.25, save=True) # 处理视频 results = model('drone_video.mp4', conf=0.25, save=True, stream=True) for r in results: pass # 每帧结果自动保存4.3 模型导出与部署格式选择
如果要把模型部署到边缘设备,需要导出成对应格式。常见的有 ONNX、TensorRT、OpenVINO:
# 导出 ONNX,通用性最好 yolo export model=best.pt format=onnx opset=12 simplify=True # 导出 TensorRT,NVIDIA 设备上推理最快 yolo export model=best.pt format=engine half=True device=0 # 导出 OpenVINO,Intel CPU 上加速 yolo export model=best.pt format=openvino half=Trueopset=12是 ONNX 算子集版本,太低会缺算子,太高某些推理引擎不支持。half=True表示 FP16 半精度,速度大概提升 30% 到 50%,精度损失通常在 1 个点以内。导出后的模型文件在best.pt同目录下,文件名后缀对应格式。
5. 避坑与排查:航拍牧羊检测的五个血泪教训
5.1 现象:训练 loss 正常下降但 mAP 始终为 0
原因:标注文件的坐标没有归一化,或者类别编号从 1 开始而不是从 0 开始。YOLOv8 要求类别编号从 0 起,坐标必须在 0 到 1 之间。
解决:用脚本检查所有 txt 文件,确认每行第一个数字是 0,后面四个数字都小于等于 1。如果坐标是像素值,除以图片宽高重新生成。
5.2 现象:推理时检测框大量重叠,同一只羊被框了三四次
原因:NMS 的 IoU 阈值设得太高,或者模型训练时正样本分配过于宽松。
解决:推理时把iou从默认 0.7 降到 0.45 到 0.5 之间。如果仍然重叠严重,检查训练时overlap_mask和mask_ratio参数,分割任务才需要调这两个,检测任务保持默认即可。
5.3 现象:模型在验证集上表现很好,但换一片草场就大量漏检
原因:数据集多样性不足,训练集的草场纹理、光照条件和实际部署场景差异太大。航拍数据尤其容易过拟合到某一种背景。
解决:补充不同草场、不同季节、不同飞行高度的数据。如果没法重新采集,用更强的数据增强:把hsv_h提到 0.03,hsv_v提到 0.6,degrees提到 15,模拟更极端的光照和角度变化。
5.4 现象:训练到一半突然报 CUDA out of memory
原因:batch size 太大,或者输入尺寸从 640 提到 1280 后显存翻倍。GTX 1660 Ti 只有 6GB 显存,跑 YOLOv8s 加 1280 输入很容易爆。
解决:把 batch 降到 8 或 4,或者用梯度累积模拟大 batch。YOLOv8 支持batch=-1自动选择,但自动值往往偏大,建议手动设。如果显存实在不够,用imgsz=640训练,推理时再放大输入。
5.5 现象:导出的 ONNX 模型推理结果和 PyTorch 不一致
原因:导出时没有加simplify=True,或者 opset 版本和推理引擎不匹配。另外,YOLOv8 的输出格式在导出前后可能有维度顺序变化。
解决:导出时固定用opset=12加simplify=True。导出后用 ONNX Runtime 跑一张测试图,和 PyTorch 结果对比,如果框的位置有偏移,检查预处理是否一致——PyTorch 推理时 YOLOv8 内部做了 letterbox 缩放,ONNX 推理需要手动实现同样的预处理。
6. 进阶技巧:用 TTA 和切片推理把航拍小目标召回拉满
航拍牧羊检测最头疼的就是小目标漏检。一只 60 米高度拍摄的羊可能只有 50 像素宽,YOLOv8n 在 640 输入下 P3 层感受野勉强覆盖,再小就力不从心了。除了换更大的模型,还有两个不增加训练成本但能显著提升召回的手段:TTA(测试时增强)和切片推理(SAHI)。
TTA 的思路是推理时对同一张图做多种变换——水平翻转、多尺度缩放——分别推理后再把结果融合。YOLOv8 内置了 TTA 支持:
from ultralytics import YOLO model = YOLO('best.pt') # 开启 TTA 推理 results = model('test_image.jpg', augment=True, conf=0.2, iou=0.5)augment=True就是开启 TTA,它会对每张图做水平翻转和三个尺度的缩放,推理时间大概变成原来的 3 到 4 倍,但 mAP 通常能提升 1 到 2 个点。如果部署平台算力充裕,这个开销值得。
切片推理更适合超大分辨率航拍图。把 4K 图切成 640×640 的小块分别检测,再把结果拼回去,小目标在切片里就变成了「大目标」。SAHI 库封装了这个流程:
from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction detection_model = AutoDetectionModel.from_pretrained( model_type='yolov8', model_path='best.pt', confidence_threshold=0.2, device='cuda:0' ) result = get_sliced_prediction( 'large_drone_image.jpg', detection_model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2 ) result.export_visuals(export_dir='sahi_output/')slice_height和slice_width是切片尺寸,一般设成和训练输入一致。overlap_height_ratio=0.2表示相邻切片有 20% 重叠,防止目标被切在边界上漏检。这个比例不能太小,否则边界目标会丢;也不能太大,否则重复检测增多、后处理变慢。我一般用 0.2 到 0.3 之间。
这两个方法可以叠加:先切片,每个切片推理时开 TTA,召回率能再上一个台阶,但推理时间会到原来的 10 倍以上,适合离线分析场景,不适合机载实时。
还有一个容易忽略的细节:航拍图像的 EXIF 里带有 GPS 坐标和云台角度,检测到羊只后可以把像素坐标反算成经纬度,直接输出每只羊的地理位置。反算需要相机内参和无人机姿态数据,常见做法是用pyproj做坐标转换,再用pix4d或OpenDroneMap生成的正射影像做底图匹配。这一步做完,检测结果就不只是屏幕上的框,而是可以直接导入牧场管理系统的地理数据。
从那以后我每次拿到新的航拍数据集,都强制先跑一遍 50 张图的切片推理对比,确认小目标召回没有明显下降,再决定要不要上 TTA。这个习惯帮我省了好几次返工。希望帮到你。
本文还有配套的精品资源,点击获取