ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

无人机航拍人体检测实战:从自建数据集到YOLO调优部署

无人机航拍人体检测实战:从自建数据集到YOLO调优部署 第一次把无人机飞过学校操场上空时我盯着图传画面里的那些小黑点愣了一下——四百米的跑道围起来几十上百号学生正在做课间操但从120米高度往下看每个人只有十几个像素几乎和跑道纹理、草坪阴影混在一起。当时我手里有一套在常规行人数据集上训练好的YOLO模型拉过去直接测了一下漏检率高得没法看误检也不少。后来我干脆自己动手把航拍校园操场人体检测数据集这件事从头做了一遍采集、抽帧、清洗、标注、训练、调优、部署整条链路走通。这篇文章就是那次项目的完整复盘会讲清楚航拍视角下人体检测的难点、数据集到底该怎么建、YOLO怎么选型和调参以及那些不踩一遍根本发现不了的坑。不管你是要做智慧校园安防、体育课自动分析还是单纯想练手自建数据集这篇都值得看完再动手。1. 为什么航拍操场上的人体检测这么反直觉1.1 俯视视角带来的尺度难题做目标检测的人都知道一句话小目标是一切检测器的天敌。这句话在航拍操场上体现得淋漓尽致。同样一个人在普通街道场景里行人通常占据画面几百到上千像素模型很容易提取到清晰的轮廓、衣物纹理、肢体结构但从120米高度俯拍一个成年人投影到4K画面里大约只有20×40像素有时候整个头肩区域连10个像素都不到。我抽了两千帧素材做过统计60%以上的人体实例都属于COCO定义里的小目标边长小于32像素甚至极小目标。小目标为什么难这里有个很直观的类比在一张A4纸上找一只蚂蚁和在一面墙上找一只蚂蚁难度完全不在一个量级。目标越小能供给特征提取器的信息就越少而YOLO这类一阶段检测器为了速度特征图会经过多次下采样输入640×640的图经过32倍下采样后最小特征图只有20×20一个16像素的小人映射上去不到一个像素格锚框匹配和分类都容易出问题。这直接导致两个现象一是漏检模型根本没看见这个目标二是框不准检测框要么偏大要么偏小IoU很低。我在第一次用普通YOLO模型测试时漏检率接近一半这还只是静态帧加上运动模糊之后基本没法用。1.2 密集人群、运动模糊与光照扰动操场场景第二个特点是密。课间操时间全校几百号人按班级方阵排列人与人之间几乎没有间隙从航拍视角看过去就是一片挨着一片的人头肩膀。这种密集粘连会让NMS非极大值抑制环节非常痛苦大量检测框彼此重叠、抑制来抑制去最后保留的框数量严重低于实际人数。运动模糊也是个大问题。航拍状态下有两个运动源一是人在跑动、跳跃、做操肢体动作幅度大二是无人机本身在悬停或飞行中会抖动加上常见的卷帘快门效应画面中的人物很容易出现拖影。拖影状态下模型看到的不再是一个清晰的人而是一坨边缘模糊的色块特征质量断崖式下降。光照更是操场场景独有的坑。塑胶跑道通常是红色或蓝色阳光直射下反光很强足球场草坪的绿色纹理在画面里和深色衣服非常接近上午的斜射光会让整个操场出现大面积长阴影——阴影里的人和阴影边缘的误差几乎是航拍检测误检的第一大来源。我后来统计过误检样本三分之一以上都是把影子、跑道线、场边器材当成了人。1.3 现成行人数据集帮不上忙的三个原因很多人第一反应是拿现成数据集练我一开始也这么干过。实测下来迁移效果非常差原因有三个第一是视角不匹配。COCO的person类别主要是平视或轻微俯视的行人模型学到的是有头、有肩、有腿的正立人体特征航拍俯视下人体主要特征是头肩圆形轮廓手臂展开完全不同的特征分布。第二是尺度分布不匹配。CrowdHuman这类密集行人数据集里行人实例通常占画面很大比例平均高度在几百像素级别航拍画面里人是几百像素的反向极端——极小目标为主。用平视数据预训练的模型对极小目标的anchor匹配和特征敏感性都很差。第三是背景语义完全不同。街景的背景是道路、建筑、车辆操场背景是跑道、草坪、球场线、看台。背景差异带来的误检模式也完全不同。这三个原因叠加起来结论只有一个这个场景必须自建数据集靠公开数据集硬迁移是走不通的。2. 数据集构建的全流程从航拍采集到可训练的标注规范2.1 数据采集与抽帧策略我总共飞了6个架次采集了4个多小时的素材最终清理出约1.2万张有效训练图。这里有个很多人容易忽略的点做数据集不是把视频里每一帧都拿来标注那样既浪费人力又会因为相邻帧过于相似导致模型严重过拟合。我的抽帧策略是视频按2秒间隔抽1帧这样能覆盖动作变化又不会产生大量近乎重复的画面。全部抽完约1.5万帧再经过一轮清洗把以下三类画面剔除掉严重过曝或欠曝的帧、无人机转弯时画面剧烈运动的帧、目标被大面积遮挡的帧最终保留约1.2万张。采集条件要有意识地拉开多样性。飞行高度我做了80米、100米、120米、150米四档角度覆盖正俯视90°和倾斜俯视45°~60°时间段分了上午、中午、下午天气选了晴天和阴天各半。校园场景尽量覆盖课间操、升旗仪式、体育课、课间自由活动、运动会等不同活动状态。如果只拍课间操模型对人躺着人快速奔跑这类姿态几乎零容忍——这一点我在训练后深有体会。这里插一句隐私合规的提醒校园操场虽然属于公共区域但航拍会无意识采集到人脸、校服标识等信息。我的做法是标注完成后对所有公开版本图片做人脸区域模糊化数据仅用于科研和教学用途发布时附上使用协议。这个环节不能省省了后面麻烦更大。2.2 标注规范与小目标定义标注工具我用的X-anylabeling它支持YOLO格式直接导出比先标VOC再转换省一步。标注类别就一类person类别ID为0。但人的标注边界如果定不细后面训练一定会出怪问题。我最终定下来的规范是这样的单个人体框紧贴身体包含完整躯干四肢摆开时框入四肢不做最小包围盒缩水。遮挡面积小于30%的正常标注遮挡超过70%的不标注。很密集的多人场景里每个人分别拉框允许框之间重叠但不要用一个大框去套一群人。对于极小目标边长小于8像素只要连续多帧都能稳定看见就标只闪一帧的忽略避免给模型注入大量噪声。逆光下人脸完全不可见但只要身体轮廓可辨正常标注。这套规范的思路是宁缺毋滥每个标注入库的框都必须是人这一概念的高置信样本。标注里最忌讳模糊地带太多模型学到模棱两可的特征最后检测结果就模棱两可。全部标完后统计出来的人体实例数约6.8万个平均每张图5.7人。最密的一张画面是课间操方阵单图手动数出来有183人那是我标得最崩溃的一张图。2.3 数据集结构与训练/验证/测试划分目录结构我直接按YOLO惯例组织后续训练脚本零修改就能跑dataset/ ├── images/ │ ├── train/ # 9600张 │ ├── val/ # 1200张 │ └── test/ # 1200张 ├── labels/ │ ├── train/ # 对应的yolo格式txt │ ├── val/ │ └── test/ └── data.yaml # 类别配置文件划分比例是8:1:1。但这里有个关键细节划分时以架次为单位做分组而不是简单随机洗牌。也就是说同一个飞行架次里的帧要么全进训练集要么全进验证集——否则无人机在某一架次的固定航线会让大量场景相关画面同时出现在训练和验证里导致验证指标虚高。我第一次没注意验证集mAP刷到0.94换成分组划分后掉到0.91这才是真实水平。data.yaml内容也很简单path: /data/human_detection train: images/train val: images/val test: images/test nc: 1 names: 0: person另外我在标注完成后做了数据增强扩展把总样本量翻到了约2.4万张。增强手段不是无脑堆而是针对航拍俯视场景选的水平翻转、随机旋转±30度、随机裁剪、hsv色域扰动、马赛克增强。特别注意别用垂直翻转——航拍俯视图垂直翻转后语义会变怪训练出的模型容易产生上下颠倒的误检。3. YOLO选型与训练配置为什么最终选了YOLOv8s3.1 模型选型对比用惯了YOLO的同学都知道现在版本多到眼花缭乱YOLOv5、YOLOv6、YOLOv7、YOLOv8、YOLOv9、YOLOv10、RT-DETR。选型不能只看排行要看场景匹配度。我在这个项目上对比过四类方案模型参数量航拍小目标表现部署生态结论YOLOv5s7.2M中规中矩anchor设计需要手动调最成熟保底选择YOLOv7-tiny6.2M小目标一般训练技巧敏感较成熟不推荐YOLOv8s11.2M小目标较好DFL损失更稳导出部署很顺最终选择RT-DETR32M以上强但吃数据收敛慢边缘设备吃力数据少时不划算最终选YOLOv8s的原因很简单第一它的检测头集成了DFLDistribution Focal Loss对边界框回归的质量建模更细在小目标上能带来更精准的定位第二训练稳定性比YOLOv5好开箱即用的默认超参数更省心第三导出部署生态完善后面转ONNX、TensorRT都很顺。参数量大一点没关系我的主要部署目标是PC端服务和带GPU的边缘盒子推理速度完全够用。3.2 关键训练超参数与数据增强配置训练分两个阶段。第一阶段用640分辨率快速验证方案跑100个epoch确认数据没有标注错误、loss能正常收敛第二阶段切换到1280分辨率精调跑200个epoch。为什么最后用1280而不是一直640前面说过航拍人体尺度普遍偏小输入分辨率越高小目标映射到特征图上的信息越多。实测下来从640提到1280验证集mAP50-95提升了约6个点代价是训练时间几乎翻倍。阶段二的训练配置如下# hyp.yaml lr0: 0.01 lrf: 0.01 momentum: 0.937 weight_decay: 0.0005 warmup_epochs: 3 box: 7.5 cls: 0.5 dfl: 1.5 mosaic: 1.0 mixup: 0.2 copy_paste: 0.3 flipud: 0.1 close_mosaic: 10 scale: 0.5这里几个超参数的用意我得展开讲不然照着抄也不知道为什么。mosaic开1.0是因为它把4张图拼成1张训练等于变相增大了batch对密集小目标场景非常有效。但mosaic也有副作用生成图中包含大量被裁剪的半截人体模型容易学会对半个身体响应。所以我开了close_mosaic10意思是最后10个epoch关闭mosaic用真实比例图片做精修让模型把框位拉准。mixup和copy_paste都是正则化性质的数据增强我把系数调到0.2和0.3属于温和干预。航拍场景目标太密集copy_paste把一个人体粘贴到另一张图的空白草坪区域等于给模型制造负样本中的正样本能有效降低密集区域的漏检。但系数不能开太大否则目标之间互相污染训练反而变差。flipud设成0.1——前面说过垂直翻转对俯视场景语义有影响但0.1的小概率又相当于给模型加了一点扰动实测这个值利大于弊。还有一个很有用的trick数据集里的小目标尺度分布和COCO预训练模型默认的anchor分布差异很大我直接用k-means在自有数据集上重新聚类了一组anchor尺寸替换默认值。这一步不需要写复杂代码ultralytics提供了自动计算anchor的命令跑一遍拿到结果填进模型配置即可。聚类出来的anchor比默认的小一截训练收敛速度和最终mAP都有明显改善。训练设备是一张RTX 3090 24G1280分辨率下batch设8。整个阶段二跑了约一天半loss曲线和验证指标都达到了预期。如果你手里的卡显存只有12G建议batch降到4同时把scale增强对应调小训练时间长一点但也能跑。3.3 训练监控与过拟合控制训练过程中我最关注的指标不是总loss而是以下四个Precision、Recall、mAP50、mAP50-95。很多人只看loss曲线这是不够的——loss降了不代表检测质量好尤其是密集场景loss曲线漂亮但NMS后框数量不对的情况很常见。另一个我养成的习惯是每个epoch保存验证集上的混淆矩阵。YOLO训练日志会在权重文件夹里生成confusion_matrix.png它能直观告诉我漏检和误检分别在哪。航拍操场场景里我观察到的典型问题是predicted person对真值person的命中率很高但是阴影和跑道线被当成person的false positive也不少。后期专项处理就是围绕混淆矩阵的误检类别去补样本。过拟合的预兆要早识别。我第一版模型训到150轮时训练集loss还在降验证集mAP已经横盘甚至微降这就是典型过拟合信号。处理办法有三板斧加大数据增强强度、增加验证集样本多样性、降低最后一个阶段的epoch数。实际操作中我用的是FirstLaw以验证集mAP开始下跌的那个epoch为基准把后面所有epoch的权重都移动到mAP最高点那个checkpoint配合早停。4. 实测结果与高频翻车点复盘4.1 漏检与误检的主要来源训练完成后我拿独立采集的3段没参与训练的视频做了测试。测试分辨率是4K3840×2160策略是先把视频帧切成1280的块分别检测再合并。最终整段视频统计mAP50约0.91mAP50-95约0.62。这个数字比验证集略低属于正常的域差。但数字不能说明一切。逐帧看检测结果漏检和误检的来源非常集中我整理成了表格问题原因解决办法奔跑中的人漏检运动模糊严重特征被拖影破坏训练集中加大运动模糊模拟增强密集方阵中心漏检目标过密anchor匹配冲突提高输入分辨率更小anchor阴影被误检为人树影、看台影子形状接近人形补充大量阴影负样本体育器材误检球门、跨栏架结构类似人体加入背景类别或hard negative mining逆光低对比漏检目标与背景亮度接近训练集加入gamma扰动阴影误检是最典型的航拍操场问题。操场周围的树在下午会把长长的树影投到跑道上随着时间推移影子还会移动变化这些影子的形状、方向都和人很像。模型没法理解影子是影子它只看到一片深色区域特征分布近似人形。我后来把误检帧收集回来单独加了800张阴影样本训练误检率直接降了45%。这就是负样本的威力——一个目标检测模型的好坏一半取决于正样本标得好不好另一半取决于负样本有没有教会它不要乱认。4.2 训练崩溃BN层参数爆炸的排查训练过程中遇到过一次很典型的崩溃值得单独拿出来讲。现象是第一阶段跑到第40轮左右loss突然变成NaN之后所有指标归零日志里还能看到权重数值出现inf。网上搜一圈你会看到很多人说这是BN崩溃。排查链路是这样的先看是不是学习率太大——把lr0从0.01降到0.001重跑依然在第40轮附近炸说明不是lr的问题。再看数据结构——检查labels文件夹没有空文件、没有超出图片范围的框排除标注问题。然后看增强——把mosaic关闭单独跑训练立刻恢复正常问题锁定在mosaic上。mosaic为什么会崩因为mosaic拼接后4张不同光照条件的图片出现在同一张训练图里局部区域的像素统计差异极大而BN层是在每个batch上计算均值和方差。如果batch比较小我第一阶段用batch4跑的BN统计量严重不稳定加上1.0的mosaic带来的极端分布某个卷积层的激活值溢出loss直接NaN。这是小batch强增强场景下的经典翻车组合。最终修复方案是batch提到16在640分辨率下显存够用mosaic从1.0降到0.8给BN层设置更合理的momentum默认0.1不动同时在yaml里开启warmup_epochs3让学习率在前几个epoch从0线性爬升不让梯度一开始就走太狠。改完后再跑loss曲线一路平稳再没崩过。这个坑我建议每个用YOLO做自定义数据集训练的人都提前知道因为它一旦发生不是重训一个epoch的事而是可能排查好几天。4.3 面向部署的推理优化思路训练完的模型不能直接上生产还有个部署优化要讲。我用ultralytics导出ONNX再转TensorRT FP16在PC端测试时640输入YOLOv8s单帧推理约2.5ms但4K原图直接放进去会非常慢而且大量小目标在缩放到640的过程中被直接抹掉。最终采用的方案是切片推理把4K原图切成1280×1280的子图子图之间保持50%重叠逐块检测后再把结果映射回原图坐标用NMS合并重叠区域的重复框。这个思路跟SAHISlicing Aided Hyper Inference一脉相承只是我用Python脚本手写了一遍也顺便理解了它的精髓。实测切片推理下4K视频帧在PC端约40ms一帧比直接整图检测快且准得多。如果是边缘设备部署我在Jetson Orin Nano上也跑通了640输入下YOLOv8s约18ms一帧1280切片会慢到不可接受所以在嵌入式场景我的策略是牺牲分辨率、适度提高置信度阈值把漏检控制在可接受范围内。部署后还有一个性能杀手容易被忽略NMS的IoU阈值。密集人群场景下IoU阈值默认0.45会让大量靠近的目标互相抑制我实测把阈值调到0.3密集方阵的召回率明显回升——代价是相邻目标之间偶尔出现重复框需要配合置信度阈值0.5一起来用。5. 这份数据集能怎么用从操场到更多场景5.1 可以直接落地的应用方向数据集的直接价值是给开阔场景俯视人体检测这件事打地基。基于这套数据训出来的模型在以下方向都有机会直接落地校园安防是离得最近的一个。操场人员聚集预警、大规模活动人数统计、课间异常奔跑检测本质上都是先检出人再分析行为。我在项目结束后拿同型号模型做了个简单的人数统计demo对一段5分钟课间操视频进行检测计数输出曲线能正确反映集合-做操-解散的人数变化趋势误差在8%以内。体育教学分析是另一个高价值场景从航拍画面对课间操做标准化评分需要知道每个方阵的人头分布和动作节奏这个第一步也是人体检测。没有精准的检测框后面所有姿态估计和骨骼点提取都是空谈。泛化到更开阔的公共场景也很有潜力城市广场、体育场、公园草坪、大型园区这些场景的俯视人体分布规律和操场非常接近模型可以直接做迁移微调通常只要几百张新场景数据就能快速适配而不是从零开始攒数据。5.2 后续扩展多类别、多视角、时序跟踪现在已经跑通的项目还只是第一版我认为有三条扩展路径值得继续走第一从单类别扩展成多类别。操场场景里摔倒的人“奔跑的人”“聚集的人群都是很有业务价值的类别。操作上不需要重标全部数据只需在现有框上追加行为属性标签比如person_runningperson_falling然后转成多类别YOLO再训一版。摔倒检测这类功能在校园安防里非常受欢迎。第二从单一俯视角扩展到多视角。目前的训练数据集中在45度以上俯视实际部署中摄像头可能安装在15度、30度等更矮的角度。补充这些视角的数据能让模型在低视角杆状监控场景也保持稳定——这就是典型的domain expansion。第三从单帧检测升级为时序跟踪。接一个Bytetrack或DeepSORT追踪器把视频帧的检测结果关联成连续轨迹就能做人员轨迹回放区域停留时长统计异常徘徊预警。实测Bytetrack在密集人群场景比DeepSORT稳定得多几乎不需要调参成本极低收益明显。这几条路径不需要重新推倒重来都是基于现有数据集和模型做增量迭代。这也是我当时坚持做好数据的动力——数据资产是越积累越值钱的东西。写到这我最大的体会是做这种垂直场景检测项目练模型的时间其实只占三成七成都在数据上。从采集策略到标注规范再到负样本设计每一环都在悄悄决定最终模型的精度上限。你花的每一分钟在数据清洗上的时间最后都会变成mAP上的真实回报。如果你正在做类似的项目最实用的建议就是多飞几个架次、多收集点阴影和反光样本、标注规范写细一点、训练前先验证集分组划分正确——把这些基本功做扎实模型翻车的概率会小非常多。
返回列表