ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

飞鸟检测跟踪实战:YOLOv5+DeepSORT全流程与边缘部署指南

飞鸟检测跟踪实战:YOLOv5+DeepSORT全流程与边缘部署指南 简介YOLOv5-DeepSORT飞鸟视觉检测与跟踪是一套融合目标检测与多目标跟踪的完整项目面向计算机视觉学习者、鸟类生态研究人员及野生动物保护开发者可解决视频与图像序列中鸟类实时定位与连续追踪问题。项目以YOLOv5作为检测器利用U-Net式结构与数据增强、多尺度训练提升检测精度同时借助DeepSORT中的Siamese网络与卡尔曼滤波实现跨帧身份关联即使遇到遮挡或短暂消失也能稳定恢复轨迹。压缩包共244个文件、约212MB主要包含60个Python脚本、45个YAML配置、34个pyc缓存、25张jpg与12张png图片另有pt权重、mp4演示视频、md说明文档等从训练配置到推理部署均有覆盖目录结构清晰便于检索。目前已有819人学习。读者可获得可直接运行的完整代码、预训练权重、效果演示及相关说明既能快速复现飞鸟检测与跟踪流程也能将这套“检测追踪”方案迁移到交通监控、赛事分析等场景。 做飞鸟视觉检测跟踪这个项目起因是有个做生态监测的朋友找我想从监控视频里自动统计一片湿地上的鸟类数量和活动轨迹。一开始我天真地以为YOLOv5检测加DeepSORT跟踪这条经典链路直接套上去就行毕竟行人、车辆目标早就被跑烂了。真把模型拿到飞鸟画面上跑了一遍才发现问题远没有想象中简单——目标小、姿态多变、遮挡严重光是让检测框稳定不抖就折腾了很久更别提跟踪时ID频繁跳变。这套方案最终落地到边缘设备上经历了数据标注、训练调参、后处理优化和部署移植的全过程我把完整路线和关键坑位都写在这里给正准备做同类项目的人一个参考。1. 飞鸟目标为什么这么难伺候小目标、高姿态、场景多变1.1 小目标在神经网络里的天然劣势飞鸟在监控画面里往往只占据几十个甚至十几个像素。YOLOv5默认把输入图片缩放到640x640经过5次下采样后特征图最小分辨率只有20x20。一个原本28x28像素的小鸟在最深层特征图里可能只剩下不到1个像素点的响应检测头根本拿不到有效信息。这就是为什么直接拿COCO预训练权重去检测飞鸟漏检率会高得离谱——不是模型退化了是训练数据里的小目标分布和你的场景完全不匹配。我一开始没有意识到尺度匹配的重要性拿到一批监控视频就抽帧标注、直接训练结果mAP0.5只有0.4左右远距离的鸟基本全丢。后来做了两个改动一是把训练和推理分辨率从640提到960二是给模型计算了适合本数据集的anchor涨点非常明显。这给我的第一个教训是飞鸟项目不要迷信预训练权重更不要默认640x640够用模型输入尺寸和anchor都要跟着目标尺度走。1.2 检测和跟踪是两个完全不同的层次很多人对这套方案的理解是YOLOv5给出每帧的检测框DeepSORT把相邻帧的框连起来就完事了。实际不是这样。检测只回答这一帧里鸟在哪跟踪回答的是这一帧里的鸟和上一帧里的哪只是同一只。后者需要额外的运动预测和外观匹配机制一旦检测框发生抖动或者目标短暂消失跟踪就容易断链、换ID。飞鸟这个目标尤其不友好。它的运动不像车辆那样有明确的道路约束方向突变、急停急转都是常态卡尔曼滤波的恒定速度假设经常失准。而且飞鸟的外观特征高度相似同一种鸟在画面里几乎就是相同的灰度小色块ReID外观分支给不了太大区分度。这导致Tracking的难度核心不在算法本身而在于检测质量和ID匹配策略的配合。1.3 典型的真实应用场景这套检测跟踪能力可以落地的场景很多机场鸟情监测需要实时跟踪鸟类路径提前判断是否有撞机风险农业果园的智能驱鸟系统需要先检测到鸟群再触发声光驱赶同时跟踪判断驱赶是否有效生态观测需要统计固定时间段内的鸟类迁徙数量和活动范围。这些场景的共同特点是实时性要求高、算力资源有限通常不会给你一台带高端显卡的服务器而是要求在嵌入式盒子、Jetson或者树莓派这种设备上跑起来。2. 飞鸟数据集的采集与标注模型性能的天花板在这一步就焊死了2.1 数据采集策略比想象中重要飞鸟数据集的第一个问题是来源。如果你只有几十段监控视频抽帧后画面高度相似模型很容易过拟合到特定场景的纹理上换个环境就废。我建议至少从三个渠道组合数据自己的监控视频抽帧、公开数据集CUB-200、COCO的bird类别、部分无人机航拍鸟类数据集、以及网上抓取不同背景下的鸟类图片。类别设计上除非有区分鸟种的硬需求否则第一版项目强烈建议只设一个bird类让模型专注于解决有没有鸟、在哪这个核心问题分类工作交给后续专门的模型处理否则类别多了小目标更难分。数据多样性要特别注意三点背景多样性水面、天空、草丛、树枝、姿态多样性飞行展开、收翅滑翔、站立、尺度多样性特写、中景、远景。采集后建议按场景分层抽取训练集和验证集不要简单随机切分否则验证集和训练集高度同源mAP会虚高一到现场就现原形。2.2 标注规范和容易翻车的细节标注工具我用过labelImg和X-AnyLabeling。飞鸟这种小目标标注框不要贴得太紧——检测框稍微包含一点背景反而有利于训练稳定因为模型学到的特征会有上下文信息。另一个容易翻车的地方是遮挡问题多只鸟重叠时如果被遮挡的目标分辨不清宁可不标也不要画一个包含两只鸟的大框这种噪声框会把模型带偏。比较特殊的一点是飞鸟的宽高比变化极大。滑翔时翅膀展开宽高比可能是1:3收翅俯冲时又接近1:1。标注的时候不用刻意统一但YOLO格式的归一化坐标必须算准确我见过不少人把中心点坐标和宽高算错一位小数导致训练时loss不收敛甚至一开始就NaN。标注完一定要做一次可视化检查脚本可以把标注框画到图片上人工抽查几百张这个步骤不能省。2.3 数据增强和样本平衡小目标检测对数据增强非常敏感。YOLOv5内置了mosaic、copy_paste、mixup、随机HSV扰动等增强策略mosaic对提升小目标效果很好因为它把四张图拼在一起强制模型学习不同尺度下的目标。但如果你的数据集里飞鸟全是小目标mosaic之后目标变得更小反而可能超出模型能力范围。我自己的做法是mosaic开启但把scale控制在0.5-1.5之间同时依赖copy_paste把小目标复制到不同背景上让模型见更多组合方式。还有一个很多人忽略的点近处大目标会主导训练loss远处小目标被淹没。可以在dataloader里做一点目标区域重采样的调整或者直接增加小目标样本的占比。不过不要过度如果小目标占比超过80%模型又可能对大目标不敏感。目标是让不同尺度的样本都保持可见的比例。3. YOLOv5训练实战环境搭好只算开始超参数决定最终精度3.1 显卡驱动、CUDA和PyTorch的版本匹配环境搭建是很多新手卡住的第一关。飞鸟项目用YOLOv5官方仓库PyTorch版本和显卡驱动必须匹配。第一步是敲nvidia-smi看显卡驱动支持的最高CUDA版本比如驱动显示CUDA Version: 12.4那就不能用需要CUDA 13的版本。然后装对应CUDA Toolkit和cuDNN或者更省心的方式直接用conda创建虚拟环境pip安装对应torch版本时让它把CUDA依赖一起带下来conda create -n bird python3.10 -y conda activate bird pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121 pip install -r requirements.txt这里有一个常见的判断错误很多人以为装完NVIDIA驱动就能用GPU实际必须确认PyTorch装的是CUDA编译版本。装完跑一句python -c import torch; print(torch.cuda.is_available())输出True再继续。如果输出False问题基本都出在驱动版本太老或者torch装了CPU版。3.2 用自己的数据集跑通训练流程环境没问题之后把数据集按YOLO目录结构整理好images/train、images/val、labels/train、labels/val每张图片对应的txt标签文件名要和图片名一致。然后写一个data.yamltrain: datasets/bird/images/train val: datasets/bird/images/val nc: 1 names: 0: bird启动训练的命令我一般这么写python train.py --data data.yaml --weights yolov5s.pt --img 960 --batch 16 --epochs 200 --device 0 --cos-lr --multi-scale对于飞鸟项目imgsz我最终固定在960batch大小由显存决定16比较稳妥。首次训练建议用yolov5s权重做预训练比从零开始收敛快得多。训练过程中用val mAP0.5作为主要观测指标不要只看训练集loss那是骗人的。如果发现val mAP开始下降而train loss还在降说明过拟合了早停或者加大数据增强。3.3 几个关键超参数的实战调整YOLOv5默认的超参数文件是hyp.scratch-low.yamlbase lr是0.01warmup_epochs是3。飞鸟小目标训练时我发现两个参数值得改一是mosaic开启时训练早期容易出现loss震荡可以适当调大warmup_epochs到5二是copy_paste对小目标有正向帮助可以在超参里把copy_paste设为0.5。anchor策略值得单独说。YOLOv5训练时默认开启autoanchor它会根据你数据集中所有标注框的实际宽高比用k-means重新计算初始anchor。如果你看到日志里有Autoanchor: 5 anchors recomputed说明它已经在适应你的数据了。我建议观察一下生成的anchor分布如果发现所有anchor宽度高度都集中在极小值附近说明你的数据集里小目标占比太高这时候需要考虑降低imgsz或者增加大目标样本否则anchor差异太小检测头学不出多尺度能力。4. 后处理才是部署的隐形门槛从NMS参数到i.MX8MP算子切分4.1 后处理参数决定了最终输出质量训练完成后模型输出的是raw预测张量要经过解码、阈值过滤、NMS才能变成坐标框。很多人只关心训练mAP忽略后处理参数对实际效果的影响。在飞鸟这种小目标场景下置信度阈值的设置非常敏感YOLOv5默认conf_thres0.25但小目标因为特征弱置信度普遍偏低默认阈值会把大量真鸟滤掉。我实际调下来0.15-0.2效果更合适代价是误检框会多一些需要后面用跟踪逻辑去过滤。NMS的IoU阈值同样关键。默认iou_thres0.45对飞鸟偏宽松两只鸟离得近时容易合并成一个框。飞鸟群密集时我建议调到0.3-0.35宁可多保留两个重叠框让跟踪去解决也不要让检测阶段就把目标吞并掉。另外要检查NMS是否限制了最大输出框数比如max_det300飞鸟群可能几百只同时入画上限不够会丢目标。4.2 在NXP i.MX8MP这类边缘盒子上问题集中在算子切分i.MX8MP是我实际部署过的一个边缘平台它内置NPU算力在2.3TOPS左右听着还行但YOLOv5结构里的很多算子NPU并不全支持。通过NXP的eIQ工具链转换模型时通常只能把主干网络放到NPU上加速后处理部分sigmoid、box decode、NMS得在CPU上跑。这就出现一个很典型的瓶颈NPU推理只花10msCPU后处理花掉30ms整体帧率被拖垮。解决思路有两个方向一是把后处理逻辑用C重写避免Python的额外开销二是采用算子切分策略让特征解码和NMS尽量并行优化。我自己在imx8mp上实测YOLOv5s转INT8精度后主干网络NPU能跑到15-20fps但完整流程一跑帧率掉到8fps左右问题就在后处理。后来我把检测输出的低置信度框先粗过滤一遍把候选框数量压到几百个再进NMS速度有明显提升。4.3 模型量化的精度风险INT8量化对飞鸟小目标的影响不能轻视。小目标在量化后特征值的量化误差会被放大本来置信度就只有0.2-0.3量化后可能跌到0.1以下直接被阈值滤掉。所以部署量化模型时校准集的选择比模型转换本身更重要一定要从实际场景里抽取覆盖各种尺度的图片做校准而不是随便拿几十张训练图片应付。我在项目里吃过这个亏量化后漏检率上升了15%重新校准后才恢复。5. DeepSORT接入的完整逻辑检测框如何变成一条稳定的飞鸟轨迹5.1 为什么单帧检测不够用视频里只有一串离散的检测框看起来能用实际上根本无法回答来了几只鸟它们的运动轨迹是什么。DeepSORT的核心价值在两个地方卡尔曼滤波负责运动预测能补上目标短暂丢失的检测ReID外观特征负责个体区分防止相邻鸟混淆。但对飞鸟来说后者的帮助没有想象中大因为飞鸟的外观特征太相似同种鸟之间的ReID区分度很低。所以我不建议直接照搬DeepSORT默认配置。默认参数是在行人数据集上调出来的目标大、外观特征丰富和飞鸟场景完全不同。我自己在飞鸟项目里的经验是大量依赖运动匹配卡尔曼预测和马氏距离外观余弦距离仅作为辅助判断阈值放宽一些。5.2 DeepSORT工作流程拆解DeepSORT的每一帧处理大致分几步检测框输入后先转换成统一的[x,y,w,h,conf]格式卡尔曼滤波根据上一帧的轨迹状态预测当前帧位置预测框和检测框计算两个距离度量——运动上的马氏距离和外观上的余弦距离用匈牙利算法求最优匹配匹配结果再分三类确定的轨迹更新状态未匹配的检测框启动新轨迹或补匹配未匹配的轨迹先保留一段时间max_age等待回归。这里有个细节很关键级联匹配。算法倾向于优先匹配那些刚被连续确认的轨迹防止目标长时间被遮挡后旧轨迹抢占了新检测框。对于飞鸟如果检测不稳定n_init建议从默认的3加大到5让轨迹连续确认足够帧数后才算有效max_age可以保留默认70帧但飞鸟飞出画面很久再回来时基本会变成新ID这只能靠优化检测稳定性来解决跟踪器本身救不了。5.3 飞鸟场景的ReID策略我原本用DeepSORT仓里自带的ReID模型后来发现它是在行人数据上训的对飞鸟几乎没有区分能力几何所有的鸟算出来的外观特征距离都在0.1以内。硬用它反而会让匈牙利匹配混乱因为任何轨迹和任何检测的外观距离都很近。两个替代方案一是完全去掉外观分支只用运动匹配在目标稀疏、运动方向差异大的场景里完全够用而且速度更快二是用自己数据集的飞鸟crop图训练一个轻量ReID模型输出的特征再参与距离计算。我最终采用的是第二种但ReID模型不大MobileNet结构的特征提取网络就够了。如果你的场景里鸟的数量少、轨迹简单直接用第一种更省事。5.4 跟踪层性能优化的常规手段DeepSORT的CPU开销主要来自ReID特征提取。优化方案包括把特征提取改为每两帧做一次中间帧只用运动预测限制同时跟踪的最大轨迹数对检测框做裁剪后resize到特征网络输入尺寸减小计算量。在树莓派5那种设备上特征提取如果每帧都跑瓶颈会立刻从检测转移到跟踪整体帧率断崖式下降。6. 在树莓派5这种低算力设备上把它跑起来6.1 推理引擎选择别指望Python速度树莓派5有四个Cortex-A76核没有专门的大算力NPU。把训练好的YOLOv5模型转成ONNX再用ONNX Runtime部署是最容易跑通的路子但float32推理速度很难看。想提速主流路线是NCNN或LiteRTTensorFlow Lite转INT8量化后YOLOv5n在树莓派5上能到大约10fpsYOLOv5s只有3-5fps。这个帧率单独跑检测勉强能看但要再加上DeepSORT几乎没法实时。我实测下来的建议是树莓派5这种设备上目标不是跑大模型而是把计算量压到系统能负担的范围。模型先用yolov5n输入分辨率降到416开启INT8量化检测帧率稳定在10fps以上跟踪层不做每帧ReID只靠运动匹配让整体流程在8-10fps运行。对于飞鸟这种快速移动目标8帧以上其实已经可以形成可用的轨迹只是对极端快速俯冲的轨迹不会太平滑。6.2 跳帧检测和线程化是组合拳在低算力设备上提升感知帧率有一个很成熟的思路每两到三帧才做一次检测中间帧完全靠卡尔曼预测顶上去。DeepSORT本身就带运动预测所以这个思路几乎是专为它设计的。实际测试下来检测频率降低到原来的三分之一轨迹断链率增加得并不明显但CPU占用降了一大截。摄像头采集、推理、跟踪、预览如果都堆在一个进程里建议拆成线程或者用队列做生产消费模型。固定摄像头场景还有一个好用的技巧用背景差分为跟踪提供先验。飞鸟检测的误报很多来自树枝晃动、光影变化背景模型可以先把这些区域过滤掉再送进YOLOv5能显著降低误检进而减少跟踪器的ID错配。6.3 我在这个项目里最想说的一句话整套流程走完之后我最深的体会是检测不稳定时别急着调跟踪器。DeepSORT只是把检测结果按时间顺序组合起来检测框一旦频繁抖动再好的匹配策略也救不回来。飞鸟检测跟踪这个项目60%的工作量都在数据标注和检测效果打磨上跟踪部分反而是最后水到渠成的事。如果你的项目也卡在跟踪跳ID先回去看检测框是不是有大量的漏检和误检90%的问题都在那里。小目标检测是一场持久战但方向和顺序对了结果早晚会出来。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

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