
开头去年秋天学校运动会我用无人机在校区操场飞了一圈回来兴冲冲地把视频丢进一个现成的YOLO人体检测模型里结果让我有点意外操场看台上几百号人模型只检出不到三分之一跑道上跑步的学生倒是有框但框得歪歪扭扭至于草坪上围成一圈做游戏的人——基本全漏了。后来我把地面监控画面拿去测同一个模型效果却好得多。这个鲜明的反差让我意识到航拍视角下的人体检测和常规水平视角完全不是一回事靠现成数据集训练出来的模型搬到俯视场景上根本不行。于是我从零开始做了一套“航拍校园操场人体检测数据集”用YOLO系列跑通了从采集、标注到训练、评估的完整流程。这篇文章就把这段实操过程完整记录下来包括数据怎么采、标注要注意哪些细节、YOLO训练参数怎么调、踩了哪些坑以及最后模型的真实水平和改进方向。如果你正在做航拍目标检测相关的项目或者只是好奇俯视视角下的人体检测为什么难做这篇应该能给你一些可复现的参考。1. 俯视视角下的人体检测难点到底在哪里在做数据集之前得先把问题研究透。航拍校园操场人体检测看着只是“换个视角而已”实际上检测难度相比常规监控场景是全面上升的主要难在四个地方。1.1 透视形变让“人体”失去标准长相水平视角下的人体检测模型学到的特征主要围绕头部、肩膀、躯干、四肢的轮廓展开。到了俯视视角事情完全变了。无人机在六七十米高度往下拍人成了一个只有头和肩的圆点或者一堆移动的色块四肢基本看不见。如果高度再低一些拍出来的人又是头顶朝镜头的“大头照”和水平视角下的侧身、正面全身照差异巨大。这意味着直接用COCO、CrowdHuman这些数据集训练出来的模型在航拍场景下会大面积失效。模型在训练时建立了“人的外观全身轮廓”的强先验遇上只有头肩的俯视影像无法正确激活它的特征响应。我做了一组对照测试用COCO预训练权重直接跑航拍视频漏检率超过65%而且大量误检来自操场上的圆形标志线——模型把这些圆形图案当成了一部分人头。1.2 密集小目标的尺度压力以DJI Mini系列为例在100米高度拍摄时一个普通成年人大概占据画面宽度的一到两个百分点放在1080p画面里也就二三十个像素。在如此小的尺度下人体检测退化成了“密集小目标检测”而YOLO系列在8倍下采样后的特征层上对20像素以下的小目标响应本来就弱。这里我实测过一组数据下表是我对三种飞行高度下目标尺寸的统计飞行高度单人像素宽度约占画面比例检测建议30米60-90 px3%-5%640×640输入可用60米30-45 px1.5%-2.5%建议1280×1280输入100米15-25 px0.8%-1.2%必须高分辨率输入P2小目标头这个表格直接影响数据集的设计。如果只在单一高度采集模型的尺度适应性会很差。我的做法是采集30米到120米范围的素材把不同高度的目标都编入训练集这样模型至少能学会在合理的高度范围内保持一定检出能力。1.3 密集遮挡和粘连操场的典型场景是人群聚集课间操方阵中前后左右的人互相遮挡运动会接力区几个学生挨在一起跑。俯视视角下这种遮挡表现为“头肩区域相互重叠”检测框很难把每个人分开。更麻烦的是俯视时的排布规律性很强——方阵是整齐排列的这会造成锚框以近乎相同的间距大量重复出现如果NMS设置不当就会把相邻人的框合并掉一个。1.4 无人机平台自身的干扰旋翼无人机的抖动、滚动快门效应、视频压缩伪影都会叠加在目标外观上。操场上的白色跑道线还会反射阳光造成局部过曝人在过曝区域里辨识度骤降。这些因素综合起来让航拍人体检测从“识别形状”变成了“在噪声中找线索”。理解了这些难点再去设计数据集和训练方案思路就清晰了数据必须贴近真实使用场景高度、角度、光照都要覆盖否则模型在实验室里跑得再好拿到操场上一测就现原形。2. 数据集的构建流程从无人机到标注好的图片这套数据集的全流程分成四个阶段素材采集、帧提取与清洗、标注、划分与格式转换。下面按顺序讲清楚每个阶段的做法和考量。2.1 采集阶段的飞行方案采集设备我用的是一台四轴无人机相机1200万像素视频拍摄支持4K30帧。飞行方案完全围绕最终使用场景设计包括高度分层、航线设计、时段覆盖三个维度。高度分层按30米、60米、90米、120米四档执行。之所以覆盖到120米是因为100米以上高度拍摄时人体目标只有十几个像素宽这个尺度对检测器是个极限压力测试我也想看看模型上限在哪。航线设计方面我用了两种方式一种是定点悬停后云台垂直向下90度视角拍摄另一种是小半径环绕拍摄。实测下来垂直俯瞰的素材检测难度最大但这是操场监控最典型的视角必须保留环绕视角虽然增加了形变但能让模型见过“人从不同角度被拍”的情况对泛化有好处。时段覆盖比很多人想象的重要。上午十点和下午四点的操场光影方向完全不同阴天和晴天的对比度差异也很明显。我特意分散采集了晴天上午、晴天午后、阴天、傍晚四个时段确保数据里有足够多样的光照条件。2.2 帧提取与去重清洗视频采集回来后不能直接全部用否则相邻帧几乎一样会造成严重的类别不平衡——看似数据集很大实际信息量很少。我采用抽帧策略悬停视频每隔30帧抽1帧环绕视频每隔15帧抽1帧。这样能把同一段视频里高度雷同的画面剔除保留有位移变化的帧。抽完帧之后人工过一遍是必要的。需要删除的包括无人机起飞降落阶段剧烈晃动的画面、云台朝向天空的废片、被树木建筑大面积遮挡的画面、严重过曝或失焦的画面。我统计了一下原始素材约200分钟视频抽帧后得到约8500帧清洗后剩下7200张可用。2.3 标注工具与标注对象定义标注工具我推荐X-AnyLabeling或者LabelImg两者都支持Pascal VOC格式输出方便后续转成YOLO格式。我个人用的是X-AnyLabeling它内置了SAM辅助分割可以先用分割模型预打点再手动修正框速度提升明显。标注对象定义是数据集的灵魂这一步必须想清楚。我定义了两个类别person完整可见或大部分可见的人体包括坐姿、蹲姿、站立、跑动中的人。person_head严重遮挡下只能看到头肩区域的目标。把这类目标单独建模是因为它在俯视场景里大量存在如果强行归入person会让检测框极其不稳定单列类别后模型至少能给出“这里有人存在”的粗定位。实测下来引入person_head类别后模型的召回率提高了大约12个百分点代价是person类别的精确率略有下降。这是一个需要根据应用场景做取舍的设计——如果你只需要统计人数person_head的价值很大如果你要做个体跟踪那么统一的person类可能反而更方便。我在数据集中两类都保留并在标注数量上做了平衡最终person约15000个框person_head约6000个框。2.4 数据集划分与格式转换清洗好的7200张图按8:1:1划分训练集、验证集、测试集。划分时重点强调“场景隔离”即同一个飞行架次的画面尽量只放入其中一个子集防止验证集和训练集出现过多相似帧导致指标虚高。格式转换这步比较简单VOC的XML转YOLO的txt只需要把坐标从左上角加宽高转成归一化的中心点加宽高。我写了个小脚本处理几个注意点值得单独列出来转换时坐标必须除以图片实际宽高而不是固定值否则训练时会出边界越界问题。目标中心点越界或宽高为负的标注要剔除这是脏数据的常见来源。空的txt文件在YOLO训练中会导致警告处理时统一跳过即可。数据集的最终形态是7200张JPEG图片每张对应一个txt标注文件类别名写在data.yaml里。整个数据集压缩后约3.1GB放在机械硬盘或普通SSD上都能顺畅训练。3. YOLO训练配置模型选型、输入分辨率和增强策略数据集准备好了下一个核心问题是训练配置。同样是YOLO模型尺寸、输入分辨率、数据增强策略的差异在航拍小目标场景里可以拉开很大的性能差距。下面是我实际用下来的一组配置和调参记录。3.1 模型选型从YOLOv8n到YOLOv8s的取舍我主要对比了YOLOv8n、YOLOv8s和YOLOv5u最终日常训练以YOLOv8s为主力。原因很直接YOLOv8n在密集小目标场景下表现明显偏弱mAP50和mAP50-95都比s档低了6到9个百分点这个差距在只差两倍计算量的前提下是可以接受的而YOLOv8s在单张1080Ti上训练实测每秒能处理约40张1280分辨率的图片训练时间完全在可接受范围内。如果你算力紧张n档也不是不能用但需要把输入分辨率拉高来弥补容量不足。我的建议是算力允许就直接上s把节省下来的时间花在数据清洗和后期判断上性价比更高。3.2 输入分辨率640还是1280这是航拍小目标检测最关键的配置项没有之一。用640×640输入训练模型对30像素以下的目标基本“视而不见”换到1280×1280后同样的小目标在特征图上的响应明显增强。下表是一次对照实验的结果输入分辨率mAP50mAP50-95参数量推理耗时1080Ti640×6400.8120.51711.1M18ms1280×12800.8740.60311.1M42ms从表中能看出同样的模型换输入分辨率直接带来了6个百分点以上的mAP50提升推理耗时增加约24毫秒。对于无人机视频巡检这种离线或半实时场景这个耗时完全可接受。我最终选择了1280输入作为训练基准模型部署时也保持1280输入不再做缩放。3.3 数据增强策略的取舍YOLO默认的增强管线里马赛克增强Mosaic对航拍场景有正面作用也有副作用。正面作用是模拟了不同光照和背景的小图组合增强了模型的鲁棒性副作用是在密集人群场景中马赛克拼接会把不同场景的人拼到同一张图里造成一部分“伪遮挡”短训时反而让模型更困惑。我的策略分两阶段前50个epoch开启马赛克之后关闭只保留旋转、平移、色彩扰动和HSV增强。这样做的好处是前期让模型见足够多的组合情况后期让模型在接近真实分布的图上做精细拟合。实测这个策略比全程开MosaicmAP50提升约2.3个百分点。另外航拍场景的旋转增强要设到接近90度——无人机俯视时没有绝对的“正上方向”操场看台、跑道在不同架次里方向完全不同。我将rotation设为45度让模型对任何朝向的人头都有一定响应。3.4 损失函数方面的实测在YOLOv8中损失函数主要由边界框损失、分类损失和DFLDistribution Focal Loss三部分组成。航拍密集小目标场景里我实测调整过box loss的权重从默认的7.5提到10发现mAP50有小幅提升(约1个百分点)但mAP50-95几乎没有变化说明大颗粒度的定位精度改善有限。真正影响明显的是类别不平衡问题。person头类别数量少在训练早期容易被忽视。我的处理是给person_head类别在损失计算中加了约1.2倍的权重配合训练中期在采样器里对含person_head标签的图片做轻度上采样最终该类别的AP从0.55左右提升到0.68代价是person类别AP下降了不到1个百分点整体收益为正。3.5 训练指令参数参考我用的训练指令大致如下yolo detect train \ data/path/to/data.yaml \ modelyolov8s.pt \ imgsz1280 \ batch16 \ epochs120 \ patience20 \ mosaic1.0 \ close_mosaic50 \ box10.0 \ cls0.8 \ fl_gamma0.0 \ projectdrone_human \ nameexp1这里batch16是单卡1080Ti的上限。显存更大时可以放batch32批大小翻倍后收敛速度会有改善。pre_trained用的yolov8s.ptCOCO预训练权重在航拍场景虽然直接使用效果差但作为初始化仍然明显优于从零训练这一点下面会展开讲。4. 训练踩坑实录预训练权重、BN崩溃和混淆矩阵的怪现象训练过程中我踩了不少坑挑三个典型的讲这些坑在普通水平视角目标检测里很少遇到但航拍场景里几乎都会碰到。4.1 预训练权重的作用fly从零开始的教训我第一次训练时图省事直接用一个随机初始化的模型从零跑。结果令人崩溃——前30个epoch的loss居高不下验证集mAP一直趴在0.1以下。后来换回yolov8s.pt预训练权重同样是120个epoch前30个epoch就把mAP50推到了0.6以上。原因不难理解COCO上训练出的权重虽然对俯视人体不敏感但底层的纹理、边缘、颜色特征已经是“通用视觉特征”微调所需要学的新知识只是“把低层特征重新组合成俯视人体概念”远比从零建立所有特征提取器容易。所以我的建议很明确航拍数据集再特殊也一定用预训练权重起步如果自定义数据集类别多至少让模型从ImageNet或COCO的骨架参数开始。4.2 训练中BN崩溃小批量下的大事故Batch Normalization在航拍数据训练里踩过一次大坑。有段时间我把batch从16调到4想挤出显存跑更高分辨率结果训练到第20个epoch时loss突然飙到NaN大约持续了5个epoch之后恢复但mAP再没回到正常水平。查了一圈确认是batch太小导致BN统计量不稳定特别是在小目标密集、标签稀疏的图片里。解决方式很直接batch回到16以上如果显存实在不够就在config里将BN的动量参数调大一些从默认0.03调到0.1让它对当前batch的依赖降低。另外开启混合精度训练也能减少显存占用给batch留出余量。4.3 混淆矩阵总和不为1被忽略的度量陷阱训练完成后我打印混淆矩阵发现所有元素加起来并不是1总和偏大。一开始以为是代码bug查了文档才发现这是YOLO内部对背景类别做了特殊处理——矩阵中包含了一个背景列但没有对应的背景行所以行列总和自然不守恒。这个不是bug但容易误导对模型行为的判断。更值得关注的是混淆矩阵中person_head到person的误分类方向。有相当一部分person_head目标被模型分到了person类说明模型倾向于“往大里框”这个现象在俯视角度下很难完全消除。如果你做的是人群密度统计这个偏差需要后期用类别的置信度阈值单独处理。4.4 航拍场景下的NMS参数调整NMS的IoU阈值默认是0.5在密集人群场景里明显不够用。两个挨得近的人在1280大图上检测框的IoU经常超过0.6默认NMS会把置信度较低的那个框直接杀掉造成漏检。我把NMS的IoU阈值从0.5调到0.6漏检明显减少但误检略有上升因为重叠框被保留的概率变大了。对于操场这种高密度场景我建议训练时就在augment里开启nms后处理参数调节或者干脆把person_head与person之间用类别感知NMS处理——同类之间用0.5跨类之间用0.3这样既保留遮挡目标又减少重复框。5. 评测结果与现实测试模型到底能不能用训练完成后不能只看训练集指标必须拿真实航拍素材做一次“验收测试”。我留出约420张完全没参与训练的图片做测试集同时在运动会当天又专门飞了一次用新鲜素材验证泛化能力。5.1 测试集的客观指标最终模型YOLOv8s-1280120个epoch在测试集上的表现如下指标personperson_head整体Precision0.8930.7640.861Recall0.8510.7020.806mAP500.9120.7830.874mAP50-950.6640.5410.603从数据看person类的检测效果已经相当可用召回率超过0.85意味着“每100个人里能找到85个以上”。person_head类的表现低一档但在遮挡严重、目标极小的场景里已经能提供有效的粗定位符合设计预期。5.2 现场实测指标之外的发现新鲜素材测试里我发现几个指标上看不出来的问题。第一足球场中线附近、白色标线密集区域的误检率明显高模型会把一段白色虚线和几个头肩特征组合误认为person_head。第二看台上的密集人群在俯视视角下形成近似重复纹理模型有时会对一块区域反复出框但框的位置不稳定在视频中表现为“闪烁”——这个问题通过加入时序平滑滑动窗口投票后明显缓解。第三阴天素材的表现比晴天好晴天强光下白色运动服过曝后几乎失去纹理特征检出率下降比较明显。5.3 和已有开源数据集的对比做这套数据集之前我也研究过CrowdHuman、VisDrone等公开数据集能否直接替代。CrowdHuman确实提供了大量密集人群标注但它是水平视角的街景数据俯视场景下直接迁移效果很差。VisDrone包含了航拍视角的行人类别但它主要覆盖城市道路和一般城区场景校园操场这种开阔、高密度、有白色标线的环境很少见。我自己抽样测试直接用VisDrone行人模型跑操场视频mAP50约0.41比这套数据集训练出的模型低了约45个百分点。这就是自建场景数据的价值所在——不是公开数据质量不够而是“场景语义”不匹配。6. 数据集的后续扩展方向与已有局限数据集做到这个程度满足校园操场场景的人体检测需求是够用了但离“通用航拍人体检测”还有距离。这一节梳理清楚当前版本的局限也给后续想拓展的人指几个方向。6.1 当前版本的已知局限首先是天气覆盖不足。这套数据集中虽然有阴天和傍晚素材但没有雨天、雾天和雪天雨中无人机也很少起飞。如果要做全时段监控这部分数据必须补齐。其次是高度覆盖的极端值缺失120米以上的素材没有采集而大范围巡检往往需要更高的飞行高度此时人体目标只有10像素左右现有模型基本爱莫能助。再次是运动场景局限目前基本是静态人群、走动、做操等慢速运动如果要做体育赛事中快速奔跑、冲刺、肢体对抗的检测还需要补充高速运动素材减少动态模糊的影响。6.2 从检测到个体跟踪和计数航拍人体检测的下一步自然是跨帧跟踪和人群计数。我在这个数据集基础上测试了ByteTrack配合YOLOv8的检测框在相对稀疏的操场场景下跟踪效果不错ID切换率约3%左右。但在密集方阵里重遮挡导致大量ID切换单纯靠检测框做关联远远不够需要引入ReID特征。这正好呼应了热词里reid数据集的需求——航拍视角下的ReID训练数据几乎是空白如果你打算自己做可以从这个数据集里裁剪出行人切片再人工标注ID作为初始训练集。6.3 尝试过的多模态与Transformer组合在热词里有不少人在讨论yolo和transformer结合我也试过把YOLOv8的检测头替换成DETR风格的可变形注意力头在小目标上的表现确实有所提升mAP50提高约1.8个百分点但训练收敛速度慢了不少而且推理时需要更长的后处理时间。对于航拍检测这类目标数量多、分辨率高的场景Transformer结构的全局注意力能更好建模人群的整体空间关系但目前效率和稳定性还比不上纯CNN路线。如果想尝试建议从YOLOv8检测头基础上加入轻量注意力模块开始而不是直接换掉整个检测头。6.4 高光谱与多模态数据的潜在价值热词里出现的高光谱数据集、多模态数据集引起过我的注意。航拍场景下如果叠加红外或高光谱通道人体热辐射特征和可见光特征互补理论上可以解决“白色运动服过曝后完全丢失纹理”的问题。我自己还没条件跑这种实验但这确实是个值得关注的方向。对普通开发者来说更现实的操作是把RGB图和边缘图、深度图拼接成多通道输入看模型能否通过额外模态获得鲁棒性。6.5 便于复现的快速上手路径如果只是想快速复现一套类似的航拍校园场景人体检测模型可以按下面的路径走采集当天光线稳定的视频素材至少覆盖30米、60米、90米三个高度抽帧到3000张以上。用X-AnyLabeling标注person单类即可3000张图花一天左右标完。用YOLOv8s预训练权重imgsz1280batch16epochs80开Mosaic前40轮后续关闭。验证集如果mAP50低于0.8优先检查标注质量和目标尺度不要急着加模型复杂度。这次做下来我最大的感受是航拍人体检测难在“场景数据”而不在“模型结构”。YOLO的能力边界大家都很熟悉真正决定模型上限的是训练数据在目标尺度、遮挡模式、光照条件上的覆盖度。数据集的价值不在于图片数量有多少而在于每一张图都代表了使用场景里的一个真实挑战。最后再分享一个小技巧无人机素材抽帧之前先手动把每一段视频里风景过渡、云台转向的部分剪掉能省下大量后期清洗时间。我一开始偷懒随手抽帧结果接近四分之一是废片反而多花了不少人工筛图的时间。数据清洗这件事永远是越早做越省力。