ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

飞鸟检测与跟踪实战:基于YOLOv5+DeepSORT的完整方案

飞鸟检测与跟踪实战:基于YOLOv5+DeepSORT的完整方案 简介YOLOv5-DeepSORT飞鸟视觉检测与跟踪项目包整合了YOLOv5目标检测与DeepSORT多目标跟踪技术覆盖模型训练、推理与追踪全流程面向计算机视觉初学者、生态研究者和AI应用开发者适用于野外鸟类监测、行为分析及实时视频监控等场景。压缩包共244个文件以Python脚本、YAML配置、预训练权重和标注图片为主并包含训练日志、说明文档、Dockerfile与演示视频整体约212MB。已有819人学习/下载。解压后可直接获得完整项目代码、权重及配置文件便于复现飞鸟检测跟踪流程通过阅读源码可理解YOLOv5与DeepSORT的协作机制也能迁移到行人、车辆等目标追踪任务是一份实操性很强的计算机视觉参考资料。 做飞鸟视觉检测和跟踪这个项目最初的想法很简单想统计一下住处附近湿地公园里有哪些鸟、大概多少只、活动规律怎么样。但真正上手之后才发现飞鸟检测和通用目标检测完全是两码事——目标小、运动快、姿态多变、经常被树枝遮挡常规YOLO模型直接跑漏检率会高到让你怀疑人生。后来用YOLOv5做检测、Deepsort做多目标跟踪把整个链路调通之后不管是鸟类监测、机场驱鸟、农业鸟害防治还是风力发电机的鸟撞预警这套方案都能直接迁移复用。这篇文章把我从环境搭建、数据集制作、模型训练到部署落地的完整经验整理出来踩过的坑和排查思路都会写清楚给正在做类似项目的朋友一个可参考的路径。1. 先搞清楚一件事为什么飞鸟检测跟踪这么难1.1 飞鸟目标的核心难点在哪里飞鸟检测的难点不在算法复杂而在目标的物理特性。鸟类在画面里通常只有几十个像素属于典型的小目标检测。以1080P画面为例一只在50米外飞行的麻雀在图像里大概只有20x20像素左右而YOLOv5默认下采样的特征图最小的检测头对应的是8倍下采样也就是原图160x160的区域浓缩成20x20的格子小目标在深层特征图里几乎丢失了语义信息。另一个问题是运动模糊和姿态变化。鸟类扇翅膀的频率很高如果用1/100秒的快门拍摄翅膀边缘一定是糊的鸟在飞行中还会不断转向、俯冲、爬升同一个个体在不同帧里的外观差异很大。这对检测器本身还能容忍但对后续的跟踪算法是致命的——ReID特征不稳定很容易导致ID切换。还有一个常被忽略的问题是遮挡。鸟飞过树枝、电线杆、其他鸟的瞬间检测框会闪烁。Deepsort解决的是跨帧关联问题不负责“找回”被遮挡的目标所以检测阶段一旦连续丢帧跟踪效果就会断崖式下降。这也是很多Demo跑起来很酷、一到真实场景就崩的根源。1.2 为什么选YOLOv5加Deepsort这套组合目标检测和跟踪其实有两种技术路线。一种是联合方案比如FairMOT、ByteTrack这类检测和ReID特征在同一个网络里输出速度更快但训练难度高超参数敏感另一种是分离方案检测用YOLOv5、跟踪用Deepsort两个模块各自独立优化风险隔离出了问题也好排查。我最终选了分离方案。原因是鸟类场景里检测和跟踪的瓶颈不一样检测要解决小目标和遮挡跟踪要解决ID切换和短暂丢失后的重识别。用两个独立模块可以分别调优检测模型换一个版本跟踪逻辑不用动反过来Deepsort的关联参数调坏了检测结果依然可以单独保存不会全盘崩溃。YOLOv5本身在工程化方面做得非常成熟导出ONNX、TensorRT、NCNN都有现成脚本训练流程封装得很好换数据集不需要改代码这在后续部署到嵌入式设备时省了大力气。Deepsort代码很轻量核心就是一个卡尔曼滤波加匈牙利匹配依赖少单CPU都能跑适合嵌套进各种项目里。2. 环境搭建与模型选型从零跑通第一版2.1 训练环境和推理环境的配置记录先说训练环境。YOLOv5对硬件要求不算苛刻但有几个版本匹配的坑必须提前避掉。我当时的机器是RTX 3060 12G装的是CUDA 11.8加cuDNN 8.6PyTorch用1.13.1。这个组合实测非常稳。如果你用RTX 40系显卡建议直接上PyTorch 2.x加CUDA 12.x因为40系的Ada架构对老版本CUDA的兼容性有问题。再提醒一个之前踩过的坑显卡驱动、CUDA Toolkit、PyTorch三者的版本必须互相匹配。你可以用nvidia-smi查看驱动支持的CUDA版本号这个版本号是你驱动能支持的最高版本不代表你要装这么高。PyTorch官方安装命令里会写明配套的CUDA版本比如pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118这里cu118就表示CUDA 11.8。如果直接pip install torch装的是CPU版本训练速度会慢几十倍。克隆YOLOv5仓库后运行pip install -r requirements.txt会安装所有依赖。我当时的测试命令是python detect.py --weights yolov5s.pt --source data/images/bus.jpg能正常输出检测结果说明环境已经通了。Deepsort部分我用的是mikel-brostrom/yolov5_deepsort这个仓库的旧版本它直接集成了检测和跟踪适合快速跑通Demo。但也因为它耦合度高后续我要换自己的检测权重时需要把权重路径和类别数都改一遍不够灵活。后来我干脆只用它做参考自己写了一个调用Deepsort的脚本只依赖deep_sort_pytorch的核心代码。2.2 推理阶段的硬件资源消耗先给一个直观的数据YOLOv5s模型在RTX 3060上推理一张1080P图片耗时大概8到12毫秒Deepsort跟踪一个目标框大概耗时1到2毫秒。也就是说整套算法跑下来单帧处理时间在15毫秒以内能做到实时。但如果图片里目标数量很多比如一群鸟同时飞过Deepsort的匈牙利匹配矩阵会变大耗时可能翻倍。这里需要说明一个常见的误区Deepsort的“实时性”和检测器的FPS不能简单相加。YOLOv5是GPU并行计算Deepsort的卡尔曼滤波和特征提取部分大量依赖CPU和矩阵运算两者是串行执行的。整个管线的吞吐量取决于“检测耗时跟踪耗时”而不是只看检测器的FPS。所以如果你要部署到嵌入式设备比如树莓派5建议把YOLOv5换成NCNN格式的int8量化模型或者直接用TensorRT的FP16能压到30毫秒以内Deepsort那边的特征提取网络也可以用轻量级的MobileNet系列替换精度损失不大。3. 数据才是真正的护城河飞鸟数据集的构建与训练3.1 飞鸟数据从哪里来、怎么标如果直接拿COCO数据集里预训练的YOLOv5s来检测飞鸟效果会很差。COCO的80类里虽然有bird这一类但训练样本以常见的鸽子、麻雀、鹦鹉为主对湿地里的白鹭、苍鹭、翠鸟等外观差异很大的鸟种识别效果不稳定。所以老老实实准备自己的数据集这是整个项目中最花时间的一环。数据来源有两个途径。第一是网上公开的鸟类数据集比如CUB-200-2011有约6000张图片、200种鸟类但它是细粒度分类数据集标注不包含检测框需要自己转第二是自己拍摄架个长焦相机对准鸟巢、投食点或池塘边录视频然后抽帧。我用的策略是两者结合公开数据集补充常见鸟种自己拍摄的视频补充实际场景里的姿态和光线条件。抽帧工具有很多我用的是OpenCV写脚本每5帧存一张。这里有个小技巧不要均匀抽帧因为鸟类视频里大量帧是空的或目标极小均匀抽帧的标注效率很低。我改为先跑一遍YOLOv5预训练模型把置信度超过0.3的帧导出来再用标注工具筛选。这样需要人工标注的帧数大概能少一半。标注工具用的LabelImg输出YOLO格式的txt文件。每一行是class_id x_center y_center width height坐标归一化到0到1之间。需要注意类别编号从0开始如果只有飞鸟一个类那类ID就是0。标注边界框时我尽量贴紧鸟的身体轮廓不要包含太多背景因为背景像素太多会干扰特征提取尤其是鸟的翅膀边缘宁可框小一点不要把别的鸟或树枝框进来。3.2 训练参数怎么调标注完成后整理成YOLOv5要求的目录结构images和labels文件夹一一对应。接下来是关键的一步写数据集配置文件。# bird.yaml train: /data/bird/train/images val: /data/bird/val/images nc: 1 names: [bird]训练命令我建议这样写python train.py --data bird.yaml --weights yolov5s.pt --img 640 --batch-size 16 --epochs 100 --device 0这里有几个参数值得展开说。--img 640是训练分辨率如果你发现鸟目标特别小可以尝试--img 1280用更大的输入尺寸让模型看到更多细节但显存占用和推理耗时也会成倍增加。--batch-size受显存限制3060 12G跑yolov5s、640分辨率、batch 16是上限。--epochs 100是我的经验值飞鸟数据集如果只有几千张100轮足够收敛。超参数文件hyp.scratch-low.yaml里的lr0初始学习率默认0.01一般不用改。但mosaic数据增强建议保留它对小目标检测的帮助很大hsv_h、hsv_s、hsv_v这些颜色增强参数我稍微调高了一点因为鸟类羽毛颜色在不同光照条件下变化很大。训练完成后看results.png里的val/box_loss和mAP_0.5曲线。如果mAP_0.5能到90%以上检测效果基本可用。如果mAP很低先别急着调超参数检查一下数据集是不是有问题比如标注框不贴、类别不平衡、训练集验证集分布不一致这些都可能导致指标上不去。3.3 训练之后必须做的一个检查训练完成后不要只看mAP指标一定要跑一段真实视频逐帧观察检测框的稳定性。我遇到过一种情况mAP指标很好看可一跑视频框在鸟身上来回抖一帧框头一帧框尾而且置信度忽高忽低。这通常说明模型对鸟的姿态变化拟合不够训练数据里飞行姿态太少。解决办法是补充飞行中的样本特别是逆光、逆风悬停这类边缘情况。另外还有一个容易被忽视的问题——类别间的混淆。如果数据集中不仅有鸟还有无人机、飞机模型这些目标外观相似分类器容易混淆。在飞鸟检测场景里如果误检率高可以单独加一类“not_bird”作为负样本把容易误检的物体标进去让模型学会区分。负样本不需要很多几百张就够。4. 检测与跟踪联动从框到ID的完整链路4.1 YOLOv5输出怎么喂给DeepsortYOLOv5输出的检测结果格式是[x1, y1, x2, y2, conf, class]而Deepsort期望的输入格式是[x1, y1, w, h, conf, class]其中x1, y1是边界框左上角坐标w, h是宽高。这个转换看着简单但很多人第一次用的时候会漏掉宽高的计算导致跟踪框错位。转换代码如下import numpy as np from deep_sort_pytorch.deep_sort import DeepSort deepsort DeepSort(ckpt.t7, max_dist0.2, max_iou_distance0.7, max_age70, n_init3, nn_budget100) def yolo_to_deepsort(detections): tracks [] for det in detections: x1, y1, x2, y2, conf, cls det w x2 - x1 h y2 - y1 tracks.append([x1, y1, w, h, conf, cls]) return np.array(tracks)喂给deepsort.update之后返回的跟踪结果里包含每个目标的ID和更新后的边界框。如果某个目标被遮挡或者丢失Deepsort会通过卡尔曼滤波预测它的位置继续输出一个预测框直到max_age帧后仍未匹配才会删除ID。这里的max_age我建议设置在50到70之间太短则遮挡后ID容易丢失太长则目标已经飞走但ID还占着会导致轨迹漂移。4.2 核心参数的经验调整Deepsort有几个核心参数直接影响跟踪质量。max_dist是级联匹配时使用的最大余弦距离阈值默认0.2。它的含义是当前检测框的外观特征和历史轨迹的外观特征之间的距离超过这个值就认为不匹配。如果你发现ID切换频繁可以适当调小到0.1如果你发现不少检测框没能跟任何轨迹关联可以适当调大到0.3。max_iou_distance是IOU距离阈值默认0.7用于处理外观特征不明显的情况。但飞鸟目标小、运动快相邻帧之间IOU往往很小甚至完全没有重叠这时IOU匹配就失灵了。我的经验是飞鸟场景下与其依赖IOU不如把max_dist调小一点、让外观特征匹配承担更多责任同时把max_age调大一点容忍检测短时间丢失。还有一个容易踩坑的地方是ReID特征提取网络。Deepsort默认用的是在Market1501数据集上训练的ckpt.t7这个数据集主要是行人对鸟类完全不适用。如果你发现跟踪时ID频繁漂移问题大概率出在这里。解决方案是下载一个鸟类ReID数据集或者干脆用YOLOv5倒数第二层的特征向量代替ReID特征——虽然精度不如专门训练的ReID模型但至少特征是从鸟类数据里学出来的实际效果比行人ReID好很多。4.3 轨迹输出和数据统计跟踪链路打通之后我还会把轨迹数据存下来。每帧记录每个目标的ID、边界框中心点坐标、时间戳存成CSV。这些数据后续做统计分析很有用根据轨迹点可以计算鸟的飞行速度、活动范围、停留时间。计算速度时要注意像素距离需要乘以一个比例系数才能换算成实际米数这个系数可以通过在画面中测量一个已知长度的参照物得到比如一个1.5米高的标牌测量它在画面中的像素高度比例系数就是1.5除以像素值。5. 部署落地模型转换、嵌入式部署与性能调优5.1 模型导出与格式选择训练好的YOLOv5权重是PyTorch格式迁移到不同平台要选择合适的部署格式。如果你在树莓派5这类ARM设备上部署推荐导出NCNN格式适合CPU推理如果你是英伟达Jetson设备推荐TensorRT FP16速度最快如果只是普通Linux服务器ONNX Runtime最简单。导出命令很简单python export.py --weights runs/train/exp/weights/best.pt --include onnx --opset 12导出ONNX后建议用onnx-simplifier简化一下模型结构可以减少一些冗余算子提升推理速度。然后再用onnx2ncnn工具转换成NCNN格式。整个转换过程基本不需要改代码但要注意YOLOv5版本不同输出的后处理逻辑可能不一样建议导出时打开--simplify参数。在树莓派5上部署时还有一个重要的后处理问题。YOLOv5的输出有三个尺度的特征图分别对应大、中、小目标输出格式是[batch, anchors, 5num_classes]。转换到NCNN后需要自己实现非极大值抑制。这里有个细节如果在端侧推理框架里做NMS要注意输入的置信度阈值不要设太高建议0.25左右否则小目标会被提前过滤掉等到NMS之后再根据业务需求把置信度阈值提高到0.5以上。5.2 性能瓶颈分析和优化路径在实际部署中性能瓶颈往往不在检测器而在跟踪模块的ReID特征提取。我实测过在树莓派5上YOLOv5s转NCNN后单帧推理约25毫秒而ReID特征提取每帧需要约15毫秒加上卡尔曼滤波和匈牙利匹配总耗时接近50毫秒也就是20FPS勉强算流畅。如果目标数量多匹配矩阵变大帧率还会下降。优化路径有三条。第一降低输入分辨率从640降到416或320推理时间几乎减半但对小目标的检出率会有影响适合飞鸟目标较大的近景场景。第二把ReID模型换成更轻量的结构比如去掉最后一层全连接、只在中间层取128维特征精度略降但速度提升明显。第三开启多线程和流水线检测和ReID特征提取放在不同的线程里用队列缓冲可以把整体延迟压下去。我实际测试下来树莓派5上这样做可以稳定跑到25FPS左右基本满足实时性要求。5.3 边缘场景下的部署注意事项如果你部署的场景是户外还有两个容易被忽视的问题一是相机画面里的背景会随季节变化春天树叶嫩绿、秋天枯黄模型在不同季节的表现可能有差异建议部署后持续收集新数据定期做增量训练二是户外光线变化剧烈逆光时鸟的轮廓会融入背景检测置信度骤降可以在相机端开启HDR模式或者在后处理中动态调整曝光。另外监控视频中的鸟从远处飞近时目标框从小到大变化如果训练数据里近景大目标不够多模型对大鸟的回归可能不稳定。我在训练时用Mosaic和Copy-Paste增强把不同尺度的鸟贴到同一张图里明显改善了大目标的检测稳定性。6. 实操踩坑记录常见问题与排查思路6.1 训练和运行期的报错排查这部分整理了我在整个过程中碰到的高频问题包含了问题现象、可能原因和解决办法方便你直接对照。问题现象可能原因解决办法训练时CUDA out of memorybatch_size过大或分辨率过高减小batch_size、改用yolov5s、关掉Mosaic导出ONNX失败提示Unsupported operatorPyTorch版本过旧、opset版本不匹配升级PyTorch--opset 12或13检测框全部偏在画面边缘训练时未设置--rect或数据分布不均匀检查数据集图片尺寸分布统一resize策略Deepsort出现大量ID SwitchReID特征与鸟类不匹配或者max_dist过大换成鸟类ReID模型调整max_dist在0.1到0.2之间转换NCNN后输出全为0后处理代码里的anchor顺序与官方不一致确认导出时是否使用相同anchor检查输入归一化方式树莓派推理每帧耗时超过200ms未开启多线程或者用了FP32模型开启4线程用int8量化模型开启NEON优化排查时我有一个固定的思路问题出在检测还是跟踪用日志区分。先把检测结果单独输出到一张图上看框准不准再把跟踪结果输出到另一张图上看ID跳不跳。两步分开排查问题定位会快很多。如果检测框本身就在抖动调跟踪参数是没用的反过来也一样。6.2 一些针对飞鸟场景的独家建议最后分享几个针对飞鸟场景的独家经验是纯文档里找不到的。第一飞鸟检测不要只用单帧做判断。鸟类飞行轨迹有惯性用Deepsort的卡尔曼滤波预测位置即使某帧检测不到也能依靠历史轨迹推测一个合理位置输出依然平滑。所以在视频分析场景里Deepsort的预测能力比检测器的置信度更有价值你要学会“信任”轨迹。第二不要在夜间或强逆光环境下部署。飞鸟检测的输入质量决定了整个链路的性能上限夜间红外相机的图像噪声大、目标纹理少YOLOv5和Deepsort都会严重退化。如果一定需要夜间监测建议改用热成像相机然后重新训练一个针对热成像图像的模型而不是直接把可见光模型迁移过去。第三关于模型迭代的数据回流机制。我在项目里加了一个“难例回传”流程运行Deepsort时如果某个目标连续几帧的置信度都低于阈值但轨迹一直存在就把这些帧截取下来每周整理一次补充标注后再增量训练。这个流程跑起来之后模型的误检率和漏检率下降非常明显。你做的项目如果也有长期运行的场景强烈建议加上这个机制它比任何调参都管用。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进