☰
基于YOLOv9的空中飞鸟识别检测系统:从数据集标注到工程部署全流程
2026/10/11 20:37:55 网站建设 项目流程

简介:本资源是一套基于YOLOv9搭建的空中飞鸟识别检测系统,面向计算机、人工智能、自动化等专业学生及机场驱鸟预警场景开发者,可用于毕业设计、课程项目或实际部署参考。压缩包共181个文件,约61.67MB,包含83个Python源码、30个YAML配置文件、22张JPG示例图、3个PT权重文件及PNG评估曲线、CSV训练日志等,覆盖训练、推理与评估全流程。资源提供详细运行教程,从Anaconda与PyCharm环境配置、依赖安装,到数据集准备、YAML类别修改、train_dual.py训练参数调整,再到detect_dual.py测试与结果输出,均有说明,并附带训练好的模型与评估指标曲线。目前已有442人学习下载,适合希望快速复现YOLOv9目标检测流程、掌握飞鸟识别与预警系统开发的读者参考使用。

1. 机场围界外那 300 米,为什么值得单独训一个飞鸟检测模型

机场驱鸟这件事,真正难的不是"看见鸟",而是"在鸟进入跑道净空区之前就看见它"。雷达能扫到大群候鸟,可对单只低空掠过的家鸽、夜鹭经常漏报;人眼盯监控更不现实,一个塔台席位同时看十几路画面,鸟在画面里可能只占 20 来个像素。这就是基于 YOLOv9 实现的空中飞鸟识别检测系统要解决的问题:把通用目标检测里最吃小目标和遮挡的一类场景单独拎出来,用一套可复现的 Python 源码、预训练模型和评估指标,把"飞鸟识别检测"从论文指标落到能跑在围界摄像头上的工程链路。

这套东西适合谁?一是做智慧机场、电力巡检、生态监测的算法工程师,手里有摄像头但缺一个能直接改的飞鸟检测基线;二是想拿 YOLOv9 练手目标检测全流程的学生和转行者,飞鸟数据集类别少、标注清晰,比 COCO 更适合入门。它不解决"驱赶"本身——驱赶是声波、激光、猎鹰的事,模型只负责在正确的时间给出正确的框和置信度。把边界划清楚,后面每一步才不跑偏。

2. 拆开这个 zip:源码、模型、评估指标各自管什么

拿到一个"源码+教程+模型+评估指标"的压缩包,第一反应不该是双击运行,而是先搞清楚四类文件在整条链路里的位置。很多人翻车就翻在这里:把权重当源码跑,或者拿训练脚本去推理,报错看不懂就怪环境。

2.1 目录结构与四类产物的职责边界

一个典型的飞鸟检测工程目录,常见做法是长这样:

bird_detection/ ├── datasets/ │ ├── images/ # 原始图片,按 train/val 分 │ ├── labels/ # YOLO 格式 txt 标注 │ └── birds.yaml # 数据集配置文件 ├── models/ │ └── yolov9-bird.pt # 训练好的权重 ├── cfg/ │ └── yolov9-c.yaml # 网络结构配置 ├── train.py # 训练入口 ├── detect.py # 推理入口 ├── val.py # 评估入口 └── requirements.txt # 依赖清单

四类产物职责要分清:源码是 train/detect/val 三个入口加配置,决定"怎么算";模型是.pt权重,决定"算出来是什么",它依赖源码里的网络结构定义,结构对不上权重就加载失败;评估指标是 val.py 跑出来的 mAP、Precision、Recall,决定"算得准不准";教程是把这四者串起来的顺序说明。新手最容易犯的错,是只改 detect.py 里的图片路径,却忘了birds.yaml里的nc(类别数)和权重不匹配。

提示:先确认birds.yaml里nc: 1(只有"bird"一类)还是多类。飞鸟检测绝大多数场景是单类,多类往往是"鸟/无人机/风筝"混标,类别数错了训练直接崩。

2.2 环境依赖与版本对齐:先跑通再谈调优

YOLOv9 对 PyTorch 和 CUDA 版本比较敏感,血泪经验是:不要用最新版 torch 硬上,先按 requirements 锁版本。

# 建议先建独立环境,避免污染系统 python conda create -n bird python=3.9 -y conda activate bird # 安装与 CUDA 匹配的 torch,这里以 cu118 为例 pip install torch==2.0.1 torchvision==0.15.2 --index-url https://download.pytorch.org/whl/cu118 # 再装其余依赖 pip install -r requirements.txt # 验证 GPU 是否可用 python -c "import torch; print(torch.cuda.is_available(), torch.__version__)"

逻辑说明:先隔离环境,再装 torch,最后装其余依赖,顺序反了容易出现 numpy 版本冲突。参数上,python=3.9是兼容性最好的选择,3.11 以上部分依赖轮子还没跟上。torch.cuda.is_available()返回False时,先别怀疑代码,八成是 CUDA 驱动版本低于 torch 编译版本,用nvidia-smi看驱动支持的 CUDA 上限,再回退 torch 版本。

如果显存只有 6G 左右,训练时把batch调到 8 甚至 4,配合--img 640,别一上来就 1280。飞鸟目标小,很多人想当然上大分辨率,结果 OOM 直接劝退,其实 640 配合合适的数据增强已经够用。

3. 从标注到训练:飞鸟数据集怎么准备才不返工

飞鸟检测的精度上限,七成由数据决定,三成才是模型和调参。这一章讲清楚数据从哪来、怎么标、怎么转格式、怎么配增强,每一步都对应可抄的命令。

3.1 数据来源与标注规范:小目标标注的三个硬约束

数据来源常见三类:公开数据集(如部分鸟类识别数据集)、自采围界监控截图、以及从视频抽帧。自采数据最贴合场景,但要注意:抽帧不能太密,相邻帧几乎一样,会造成训练集和验证集泄漏,指标虚高。一般按每秒 1 帧抽,再人工去重。

标注用 LabelImg 或 CVAT,导出 YOLO 格式。飞鸟标注有三个硬约束:

  • 框要贴紧鸟的可见轮廓,不要把翅膀外的空白也框进去,否则模型学到的是"天空背景"。
  • 小于 8×8 像素的目标建议丢弃,标了也是噪声,模型学不会还拉低 Recall。
  • 遮挡超过 70% 的鸟要么标可见部分,要么不标,别硬标整只,框和实际像素对不上。

标注完成后,目录要整理成 YOLO 要求的 images/labels 平行结构,文件名一一对应。

3.2 格式转换与 birds.yaml 配置:一次配对,终身受益

如果标注是从 VOC 的 xml 来的,需要转成 YOLO 的 txt。转换脚本如下:

import os import xml.etree.ElementTree as ET # VOC 的类别名,飞鸟场景通常只有 bird classes = ["bird"] def convert(size, box): # size: (w, h),box: (xmin, xmax, ymin, ymax) dw, dh = 1.0 / size[0], 1.0 / size[1] x = (box[0] + box[1]) / 2.0 y = (box[2] + box[3]) / 2.0 w = box[1] - box[0] h = box[3] - box[2] # 归一化到 0~1,YOLO 要求中心点+宽高 return x * dw, y * dh, w * dw, h * dh def convert_annotation(xml_path, out_dir): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") w, h = int(size.find("width").text), int(size.find("height").text) name = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, name + ".txt"), "w") as f: for obj in root.iter("object"): cls = obj.find("name").text if cls not in classes: continue cls_id = classes.index(cls) bnd = obj.find("bndbox") box = (float(bnd.find("xmin").text), float(bnd.find("xmax").text), float(bnd.find("ymin").text), float(bnd.find("ymax").text)) bb = convert((w, h), box) # 类别 id + 归一化坐标,空格分隔 f.write(f"{cls_id} " + " ".join([f"{v:.6f}" for v in bb]) + "\n")

逻辑说明:YOLO 的标注是"类别 id + 中心点 x/y + 宽 + 高",全部归一化到 0~1,和 VOC 的绝对像素坐标完全不同,这是转换最容易错的地方。参数上classes必须和birds.yaml里的names顺序一致,否则类别 id 全错。f"{v:.6f}"保留 6 位小数,避免精度丢失导致框偏移。

对应的birds.yaml:

path: ./datasets train: images/train val: images/val nc: 1 names: ["bird"]

path是数据集根目录,train/val是相对路径。nc必须等于len(names),这是新手最高频的报错来源。

3.3 训练命令与关键参数:把 batch 和 img 调对

训练入口一般长这样:

python train.py \ --weights yolov9-c.pt \ --cfg cfg/yolov9-c.yaml \ --data datasets/birds.yaml \ --epochs 100 \ --batch 16 \ --img 640 \ --device 0 \ --workers 8 \ --name bird_exp

逐参数说明:--weights用官方预训练权重做迁移学习,比从头训收敛快得多;--cfg指定网络结构,必须和权重匹配;--epochs 100对单类小数据集通常够,看验证集 mAP 不再涨就可以早停;--batch 16是显存和稳定性的平衡点,显存不够就降;--img 640是输入分辨率,飞鸟小目标可以试 960,但显存翻倍;--workers 8是数据加载线程,CPU 核少就降到 4,否则会卡在 dataloader。

训练过程中重点看三个信号:box_loss是否稳定下降、验证集mAP@0.5是否上升、cls_loss是否异常。如果 box_loss 降但 mAP 不涨,多半是过拟合或验证集分布和训练集差太多。

4. 推理与评估:mAP 好看不等于现场能用

训练完拿到权重,很多人直接看 mAP 就下结论,这是典型的翻车点。mAP 是在固定验证集上算的,现场是动态视频流,光照、运动模糊、背景干扰全不一样。这一章讲推理怎么跑、指标怎么读、两者怎么对齐。

4.1 推理脚本与置信度阈值:0.25 不是万能值

推理命令:

python detect.py \ --weights models/yolov9-bird.pt \ --source datasets/images/val \ --img 640 \ --conf 0.25 \ --iou 0.45 \ --device 0 \ --save-txt

--conf 0.25是置信度阈值,低于它的框直接丢;--iou 0.45是 NMS 的 IoU 阈值,控制重叠框合并。这两个值直接决定漏报和误报的平衡。飞鸟场景里,宁可误报也别漏报,因为漏一只鸟可能意味着一次鸟击风险,所以 conf 可以降到 0.15~0.2,代价是天空中的云、远处建筑边缘可能被误检。--save-txt把检测结果存成 txt,方便后续做轨迹跟踪。

注意:conf 调低后误报增多,别急着骂模型,先看误报的都是什么。如果全是云和反光,说明训练集里缺少这类负样本,补一批"无鸟"背景图进去重训,比调阈值有效。

4.2 评估指标怎么读:Precision、Recall 与 mAP 的取舍

评估命令:

python val.py \ --weights models/yolov9-bird.pt \ --data datasets/birds.yaml \ --img 640 \ --batch 16 \ --task val

输出里几个关键指标:

指标含义飞鸟场景关注点
Precision检出的框里有多少是真的鸟低则误报多,驱鸟设备空放
Recall真实鸟里有多少被检出低则漏报,直接关系安全
mAP@0.5IoU 0.5 下的平均精度综合参考,别只看它
mAP@0.5:0.95更严格的 IoU 区间反映框的贴合度

飞鸟检测里 Recall 比 Precision 更重要。一个常见现象是 mAP@0.5 到 0.85 了,但小目标 Recall 只有 0.5,说明模型对大鸟准、对远处小鸟漏。这时候要么提高输入分辨率,要么在数据里补小目标样本,光调阈值救不回来。

4.3 把验证指标和现场表现对齐:抽帧回放验证

最靠谱的验证方法,是把现场视频抽帧,用推理脚本跑一遍,人工核对。做法:

# 用 ffmpeg 按每秒 1 帧抽帧 ffmpeg -i airport_fence.mp4 -vf fps=1 frames/%04d.jpg # 对抽出的帧批量推理 python detect.py --weights models/yolov9-bird.pt --source frames/ --conf 0.2 --save-txt --project runs/field

然后统计:真实鸟出现的帧里,有多少帧被检出(现场 Recall);没有鸟的帧里,有多少帧误报(现场误报率)。这两个数才是决定这套系统能不能上线的依据。验证集 mAP 只是入场券,现场抽帧回放才是终考。

5. 避坑与排查:飞鸟检测最常见的 5 个翻车现场

这一章全是踩过的坑,按"现象 → 原因 → 解决"写,照着排查能省大量时间。

坑一:训练 loss 正常但 mAP 一直是 0。现象:box_loss 在降,验证集 mAP 始终 0。原因:birds.yaml里nc和标注里的类别 id 对不上,或者 labels 目录路径写错,模型根本没读到标注。解决:先跑一个只有 5 张图的小数据集,确认能过拟合到 mAP 接近 1,再上全量数据。

坑二:推理时框满天飞,全是误报。现象:一张纯天空图检出十几个鸟框。原因:conf 阈值设太低,或者训练集里几乎没有负样本,模型没见过"没有鸟"的画面。解决:补 10%~20% 的无鸟背景图进训练集,conf 回调到 0.25 以上。

坑三:小目标全漏,大鸟能检出。现象:近处鸟能框住,远处几十像素的鸟一个不报。原因:输入分辨率 640 下,小目标经过下采样后特征几乎消失。解决:--img提到 960 或 1280,同时在数据增强里开启 mosaic,让模型多见小目标拼接场景。

坑四:换了台机器权重加载失败。现象:RuntimeError: Unexpected key(s) in state_dict。原因:权重是用不同cfg结构训的,或者 torch 版本差异导致键名变化。解决:确认--cfg和权重配套,跨版本加载用torch.load(..., map_location='cpu')先看键名。

坑五:视频推理帧率极低,实时性不够。现象:单帧推理几百毫秒,达不到 25fps。原因:没开半精度,或者 batch 推理没做,逐帧调用。解决:推理加--half开 FP16,视频流用批处理或 TensorRT 加速,别用原始 PyTorch 逐帧跑。

6. 让飞鸟检测真正跑在围界上:从单帧到预警链路

模型训好、指标达标,离"能用于机场飞鸟驱赶预警"还差最后一公里。这一公里不是算法问题,是工程链路问题。我一般会做三件事,把单帧检测变成可用的预警信号。

第一件是加跟踪。单帧检测会抖动,同一只鸟在相邻帧可能时有时无。用 ByteTrack 或简单的 IoU 匹配把帧间框关联起来,得到轨迹。有了轨迹才能算速度和方向,才能判断这只鸟是"路过"还是"朝跑道逼近"。代码上就是在 detect.py 后面接一个跟踪器,把每帧的框喂进去,输出带 id 的轨迹。

第二件是设预警规则。不是检出鸟就报警,那样塔台会被吵死。规则通常是:轨迹进入预设的净空区多边形 + 持续 N 帧 + 置信度均值超过阈值,才触发预警。净空区用多边形坐标定义,和摄像头标定对应。这一步用 shapely 做点在多边形内判断即可。

第三件是做误报抑制。现场最大的干扰是云、飞虫、镜头脏点。云移动慢、面积大,可以用框的宽高比和面积过滤;飞虫轨迹杂乱无规律,可以用轨迹平滑度过滤。这些规则不优雅,但管用。

验证这套链路是否值得投入,我的习惯是:拿一段真实围界视频,跑完整链路,统计"真实鸟击风险事件"里有多少被提前预警、有多少误报。如果预警提前量能到 10 秒以上、误报每小时少于 3 次,这套方案就值得往生产推。达不到,就回去补数据或调规则,别急着上更多模型。

最后说个我自己的教训:我早期总想一步到位上最复杂的模型和最多的增强,结果调了两周指标还不如一个干净数据集加基础 YOLOv9。飞鸟检测这活儿,数据质量永远排在模型前面。先把标注和负样本做扎实,再谈结构和调参,这条路我走过,希望你少走。希望帮到你。

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

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

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

立即咨询