无人机多目标识别:基于深度残差网络与FPN的工程实践

发布时间:2026/9/15 12:28:57
无人机多目标识别:基于深度残差网络与FPN的工程实践 1. 先从需求说起无人机视角下的“多目标”到底指什么做无人机视觉感知这段时间最大的体会是多目标识别真正难的不是模型跑通而是让模型在无人机这个不断运动的载体上保持稳定。我手上这套系统既要识别无人机向下俯拍的车辆、人员、施工设施也要识别平视或斜视视野里突然闯入的低慢小无人机。深度残差网络从一开始就是骨干网络的备选后来从精度、推理速度和部署成熟度几方面比较下来它成了最省心的一个。本文记录的是这套方案从标注、训练到实测告警的完整链路适合正在做机载视觉目标检测、低空安防、巡检视觉识别的朋友参考。1.1 两类任务不能混在一个模型里“无人机多目标识别”容易被人理解成一个大模型解决所有问题实际不是。我的项目里其实存在两类差异很大的识别任务。第一类是无人机向下看的地面目标识别。这个场景里目标尺度相对可观一辆车在1080P画面中通常能占到上百像素车辆、行人、施工堆料分布在不同区域互相遮挡少但排列密集背景是道路、植被、屋顶纹理非常杂乱。第二类是无人机平视或斜视视野里出现的空中目标也就是闯入当前空域的另一架小型无人机。这种目标通常是“低慢小”飞行速度不高但在远距离时画面里可能只有十几个像素颜色和云、楼体、飞鸟接近对比度很低。两类任务的难点几乎不重叠地面目标考验模型在复杂背景下的鲁棒性空中目标考验模型对极小目标的敏感性。如果硬塞进一个检测器里训练时很容易出现类别样本不平衡地面目标类别把模型“带偏”小目标成了牺牲品。所以项目的实际做法是共享同一个ResNet骨干但检测头和训练策略分开优化。1.2 为什么深度残差网络是合理的骨干选择选择深度残差网络最直接的原因是它解决了深层网络训练时的退化问题。网络不是越深越好普通深层卷积网络在反向传播时梯度经过多层连乘之后容易消失训练误差反而比浅层网络更高。ResNet通过残差连接让某一层的输出可以等于输入加上一个残差项网络至少能学习到恒等映射。这样一来模型加深不再必然带来性能下降。用生活里的事打比方往背包里塞东西塞得多了最早放进去的东西可能被压变形甚至弄丢。残差连接相当于在每个隔层上加了根备份拉链塞新东西时旧信息仍然能被随时找回来。对无人机视觉来说底层特征包含目标的边缘、纹理、亮度信息高层特征包含语义类别信息没有残差连接的话小目标边缘信息在深层几乎找不回来。我在项目里对比过ResNet34、ResNet50和ResNet101。ResNet34在嵌入式设备上推理速度最快但对极小目标的表达力不够ResNet101精度有提升可机载算力撑不住三者在mAP上的差距只有1到2个点每秒帧数却差出一大截。最后选了ResNet50一方面它有足够深度的语义特征另一方面预训练权重丰富工程上踩坑少。1.3 识别系统不是单独跑算法的要和飞控云台联动刚开始我犯过一个错误以为只要把检测代码部署到机载电脑上就完事了。后来实测发现无人机在飞行时云台角度、飞机姿态、曝光参数都会直接影响图像质量。识别模块输出目标框后如果希望云台持续跟踪目标需要把目标中心坐标换算成云台角度增量再交给飞控的串级PID环执行。整个链路里识别延迟每增加100毫秒云台算出的角度就可能明显滞后导致目标跑出画面。所以做识别系统时不能只看模型精度还要考虑感知和控制的时延。我在识别模块和飞控之间用共享内存通信把检测结果以结构化消息发送云台根据消息里的目标位置做平滑跟随。这样处理之后地面目标可以被锁定空中目标一旦确认也会触发告警整个系统才真正具备实用价值。这部分的经验也告诉团队算法在无人机场景里是感知闭环的一块拼图而不是独立存在的模型。2. 骨干网络替换与检测头的拼装细节确定了ResNet50作为骨干之后后面的路也不是现成的。目标检测框架里有随机初始化的权重有预训练权重有不带特征融合的普通检测头也有多尺度特征金字塔。到底怎么拼直接影响小目标能不能被召回。2.1 ResNet在检测器中不只是“提特征”在目标检测模型里ResNet通常作为Backbone负责从输入图像提取特征但这只是第一步。不同层输出的特征图分辨率不同浅层分辨率高包含更多空间细节深层分辨率低语义更抽象。如果直接用最后一层特征图做检测小目标早就被下采样得面目全非了。我的做法是让ResNet输出多个尺度的特征层一般取stride为8、16、32的三层记为C3、C4、C5再通过FPN融合生成对不同尺度都有较好响应的特征金字塔。融合时高层的语义信息通过上采样逐层往下传每一层都能同时获得空间细节和语义信息。这样模型在识别地面车辆时能用大感受野的特征在识别低慢小无人机时又能用高分辨率的小感受野特征。核心代码框架大概是这样import torch import torch.nn as nn class ResNetFPN(nn.Module): def __init__(self, backboneresnet50, fpn_channels256): super().__init__() self.backbone torch.hub.load(pytorch/vision, backbone, pretrainedTrue) self.fpn_channels fpn_channels # 取resnet中间层作为多尺度特征 self.C3 nn.Conv2d(512, fpn_channels, 1) self.C4 nn.Conv2d(1024, fpn_channels, 1) self.C5 nn.Conv2d(2048, fpn_channels, 1) # FPN上采样融合 self.top_down_lateral4 nn.Conv2d(fpn_channels, fpn_channels, 3, padding1) self.top_down_lateral3 nn.Conv2d(fpn_channels, fpn_channels, 3, padding1) def forward(self, x): # 得到C3, C4, C5 c3 self.backbone.layer3(self.backbone.layer2(self.backbone.layer1(x))) c4 self.backbone.layer4(c3) c5 self.backbone.layer4(c4) # 实际按输出阶段调整 # 1x1卷积统一通道 p5 self.C5(c5) p4 self.C4(c4) p3 self.C3(c3) # 上采样融合 p4 p4 nn.functional.interpolate(p5, sizep4.shape[-2:], modenearest) p3 p3 nn.functional.interpolate(p4, sizep3.shape[-2:], modenearest) # 再经过3x3卷积消除上采样噪声 p4 self.top_down_lateral4(p4) p3 self.top_down_lateral3(p3) return p3, p4, p5实际代码里我会用官方预训练模型输出C2到C5四层并单独为小目标增加一个C2层C2特征的stride是4分辨率更高专门服务于低慢小无人机这类极小的目标。2.2 小目标处理从C5回到C2的FPN通道低慢小无人机在远距离时有多小1280×720画面里可能只有15×15像素stride为16的特征图上连2个像素都不到常规检测头几乎没有可能稳定识别。项目里试过很多办法最后有显著效果的是两件事提高输入分辨率增加C2特征融合。输入分辨率方面地面目标识别用640×640就可以因为车辆行人占的像素足够多用更高分辨率反而拖慢推理。空中目标识别则直接上到1280×1280虽然帧率会下降一截但小目标的召回率提升非常明显。C2特征融合方面把ResNet第二个Stage输出的stride为4的特征也接进FPN让检测头在高分辨率特征图上工作。小目标检测的另一个问题在Anchor设置上。默认的检测框架里Anchor面积通常从32×32起步这对15像素的目标明显太大。我在配置里把最小Anchor面积改到4×4同时增加多个细粒度的宽高比。这样调整后无人机目标的正样本数量翻了一倍训练时模型终于有机会学到“小亮斑也是目标”的映射关系。2.3 机载算力卡脖子ResNet变体的取舍测试地面服务器上怎么跑都行上了无人机就是另一回事。机载电脑算力有限散热有限还要同时跑飞控、图传、识别每一帧必须在一定时间算完。我当时在Jetson Orin NX上做了详细的骨干网络对比测试统一使用FP16推理输入分辨率640×640。表格里记录了我比较关心的几个指标骨干网络输入尺寸帧率FPSmAP0.5GPU占用ResNet18640×640580.71568%ResNet34640×640460.74275%ResNet50640×640350.76884%ResNet101640×640170.78196%ResNet18和ResNet34的帧率确实很诱人但在自建数据集上它们对低慢小无人机的召回率比ResNet50低了大约5到8个百分点。ResNet101接近19FPS对25Hz云台闭环来说已经不够用所以没有考虑。最终选择ResNet5035FPS配合TensorRT加速还有余量跑后处理和跟踪器。团队里也有同事劝我直接上MobileNetV3但从稳定性和可调试性来说ResNet家族在工程化环境里仍然更稳妥。3. 数据集和训练策略同样一张图喂给网络的方式差很远模型结构只决定上限数据质量和训练策略才决定最终效果。无人机视角的数据集不像普通COCO那样容易获得公开数据集的场景和自建场景又不完全一致训练过程中踩了不少坑。3.1 公开数据集和自建小目标数据集怎么配合我用的公开数据以VisDrone和DTLD为主这两个数据集都是典型无人机俯拍视角包含车辆、行人、自行车等常见地面目标训练出来的模型对地面场景的鲁棒性很好。但空中目标和低慢小无人机在公开数据集里非常稀缺只能用自建数据补。自建数据部分我用第二架无人机搭载不同的云台相机在不同光照、不同季节、不同飞行高度下拍摄了大量同视角素材。标注时不只是框出无人机还要标注它的型号状态多旋翼、固定翼和遮挡程度。整个过程很耗人工一个资深标注员一天有效标注量大概200到300个目标低慢小目标太小放大几倍才能看清边界。类别不均衡问题也是在这里暴露的。地面车辆可能一次采集就出现上千个标注框空中无人机可能一整天只有几十个框。我的解决方案是对少样本类别做过采样训练时如果一批图像里无人机目标少于阈值就从无人机样本池里额外补充。数据合成也帮了不少忙把无人机扣图贴到不同天空背景中再随机缩放旋转、调亮度低成本扩充了大量样本。3.2 数据增强顺序对无人机场景很关键数据增强不是把常用手段全堆上去就行了顺序和幅度都会影响小目标。我的训练流程顺序是Mosaic、MixUp、随机水平翻转、随机旋转、色调增强、随机运动模糊。Mosaic把四张图拼成一张能显著增加图像中的目标数量这是模型能快速收敛的启动器。但对小目标来说如果Mosaic缩放因子太大15像素的目标很容易变成5像素彻底退化。旋转的幅度也要控制无人机会倾斜但不会倒着飞所以我只做±10°的小角度随机旋转避免大量标注框变形。运动模糊增强非常重要因为无人机飞行时相机曝光往往产生轻微拖影不加模糊增强的模型在实拍中很难适应。核心参数是模糊核大小我控制在0到3像素太大了会把目标直接抹平。在线增强阶段会动态生成大量的变体离线增强则适合把特定小目标通过贴图方式合成到大幅背景里。离线增强一定要同步修改标注文件遇到多边形框还要重新计算外接矩形这块逻辑容易漏我因为漏同步标注导致过一轮训练数据白跑后来专门写了校验脚本检查每张图的标注框是否超出图像范围、是否与目标区域重叠。3.3 Loss与正负样本分配的经验训练目标检测器核心是分类损失、回归损失和目标性损失三者的平衡。小目标数量少难易程度不一样我用Focal Loss做分类损失让模型把注意力集中在那些难以区分的样本上而不是被大量简单负样本淹没。回归损失使用CIoU Loss它同时考虑框的重叠率、中心点距离和宽高比对无人机这种形状变化不大的目标效果很稳定。正负样本分配是另一个容易出错的环节。Anchor-based方法里Anchor和真实框的IoU超过阈值才被认为是正样本。低慢小无人机目标太小默认IoU阈值0.5会让绝大多数Anchor都低于阈值正样本数量极少。我把小目标的匹配阈值降到了0.35虽然会引入一部分质量不高的正样本但整体效果比完全没有正样本好得多。训练时先用COCO预训练权重初始化前20个epoch冻结Backbone只训练检测头和FPN等损失稳定下降后再解冻全部层以更小学习率精调。完整训练大约跑了80轮批量大小16输入尺寸分两档先用640×640预训练再用1280×1280微调低慢小无人机的召回率因此提升了约6个百分点。4. 实测链路从模型输出到弹窗告警模型训练完只是一个起点真正交付时要有完整链路视频采集、推理检测、目标关联、告警判断、界面反馈。我的演示版本跑通之后能对画面里的低慢小无人机自动框选并弹出告警信息这背后是一整条后处理和判定逻辑。4.1 现场测试结果帧率、召回率、误报率测试环境是一套自组六旋翼无人机机载Jetson Orin NX 16GB摄像头为4K云台相机实际取流分辨率设为1920×1080并推导到1280×720进行推理。地面目标场景飞行高度30到60米车辆目标平均像素尺寸约60×50行人约20×30。空中目标场景测试距离50到200米目标像素尺寸大约10到40像素。对比几个版本模型后的关键指标如下测试项ResNet50FPN无C2ResNet50FPN含C2最终上线版本车辆召回率91.2%91.8%94.3%行人召回率88.6%89.2%91.0%低慢小无人机召回率76.3%84.5%89.7%误报率每小时8次6次3次端到端延迟45ms68ms75ms可以看到加入C2特征和丰富Anchor后低慢小无人机的召回率提升显著但同时延迟也从45毫秒涨到68毫秒。为了补偿延迟后处理采用了多线程异步推理视频采集线程、推理线程、告警线程完全分离画面显示仍然流畅。演示系统中一旦模型连续确认目标是无人机立即在界面上用红色框锁定并弹出告警信息同时记录时间戳和当前机载GPS位置。4.2 为什么低慢小无人机目标容易漏警怎么补救远距离低慢小无人机是这套系统里最难啃的骨头漏警主要来自三个原因目标面积太小、目标对比度低、目标被环境遮挡。为了减少漏警我在推理阶段增加了一个低置信度保护机制。普通地面目标置信度阈值设到0.45低慢小无人机目标只设到0.25宁可多产生候选项也不希望真正目标被过滤掉。候选目标多了以后误报数量必然上升这时需要用后处理来过滤。天空背景下的云朵、飞鸟、远处信号塔都容易在低阈值时被当成无人机。补救办法是增加背景上下文分支将图像分成天空区域和非天空区域对落在天空区域外的目标提高置信度要求。另一个有效方案是做图像金字塔输入当目标尺寸小于某个像素阈值时只在原图二倍放大区域做二次检测让网络重新判断一次这是最简单有效的“小目标补检”方式。4.3 后处理里的多帧确认逻辑单帧检测结果不能直接触发告警这几乎是一条铁律。无人机飞行过程中轻微的云台抖动、编码器压缩噪声、单帧噪声都有可能让检测框闪烁或跳到背景上。如果每帧都弹告警操作员很快就会被大量误报淹没。我的多帧确认逻辑是这样设计的目标检测结果先和目标跟踪器关联每个跟踪目标维护一个历史检测计数。只有连续3帧中至少2帧检测到同一目标且目标框面积变化不超过50%、中心点移动符合运动模型才会从“候选”状态进入“确认”状态此时才弹窗告警。确认状态下的目标如果连续5帧丢失则自动解触发防止目标飞走或误入其他目标后告警不消失。这个逻辑看着简单但大大提升了演示效果。我之前只做单帧阈值判断时演示现场出现了一只飞鸟导致系统连续告警三次的尴尬情况。加入多帧确认后误告警完全被压下来真正的无人机进入视场也能在0.3秒内稳定弹出告警框。5. 工程化过程中踩过的坑这一部分是我最想分享的。模型结构、训练策略在GitHub上都能抄到但实机调试中积累的细节往往要踩坑之后才明白。5.1 运动模糊和快门参数的双向影响无人机高速飞行或转弯时如果曝光时间太长整个画面会产生运动模糊小目标会糊成一条短线。第一次实飞测试时我用的是自动曝光模式下午光线合适但效果很差地面车辆轮廓发虚更别提10像素的无人机。解决方法是把云台相机切换到快门优先模式曝光时间压到1/1000秒以上换来的代价是感光度升高、暗部噪声明显增加。这里需要双向权衡快门太快传感器增益大噪声多小目标虽然轮廓清晰但和背景的对比度可能被噪声干扰快门太慢目标模糊同样识别不了。项目里最后把快门时间固定在1/800到1/1200秒之间根据光照自适应调整同时保留少量运动模糊数据增强让模型对轻微拖影有容忍度。5.2 飞鸟、云层和虚警之间的博弈凡是做过空中目标识别的人一定遇到过飞鸟虚警问题。飞鸟和无人机混合在远处时外观就是一团边界不平滑的小暗块人眼都容易认错。我的做法是把手头所有含飞鸟的失效样本单独抽出来重新标注成“飞鸟”类别让模型先学会区分它。即使暂时不要求检测飞鸟把飞鸟作为额外类别训练也比直接当成背景要好因为背景类别包含大量相似物体会干扰分类边界。云层的虚警则更有意思大片云在运动时边缘特征很像悬停无人机。后来我在后处理里增加了天空概率判断对图像中的高亮度区域和低纹理区域降低目标置信度同时要求无人机候选框内部纹理方差不低于一定阈值。云层的纹理方差通常很低这个特征能过滤掉一大半云层虚警。5.3 训练闭环差评样本回灌部署初期的模型会有很多漏警和误报最怕的是现场出了问题却不知道怎么反馈到训练集。我在系统里增加了样本回灌机制每次识别结果都会在本地保存原始帧、检测框、置信度、人工标记结果。如果现场操作员在界面上手动纠正了某个框系统会把对应的图像和标签写入“待回灌”数据池。每周对回灌池做一次清洗去掉重复率高的帧修正错误标签然后合并进下一次训练集。这个过程看似繁琐却是模型在真实场景里持续变准的核心动力。三轮回灌训练之后低慢小无人机数据集的mAP又提升了将近3个点很多之前不能解决的特定角度、特定光照下的问题都被新增样本吸收掉了。模型不是训一次就结束的东西它是一个需要持续喂养和修正的在线系统。6. 再往深走一点到这里一套能跑的深度残差网络多目标识别系统已经完整了。但如果项目有余量还有两个方向值得深入改进。6.1 加一个跟踪器来稳定识别框单帧检测结果即使很准确目标框也难免有抖动。我在最终版本里接入了ByteTrack跟踪器对每个识别目标分配唯一ID让同一个目标在连续帧中保持框位置平滑。跟踪器带来的另一个好处是当目标短暂被树梢、楼体遮挡时检测器暂时漏检跟踪器可以根据运动趋势继续预测位置等目标重新出现后又能快速衔接上。多帧确认逻辑其实也依赖跟踪器只有稳定关联的目标才能计算连续检测次数。接入跟踪器后无人机编队飞行场景也能有效区分同一型号的多个目标帧间不会出现ID乱跳。对低慢小无人机的跟踪测试里位置平滑效果尤其明显弹窗告警框不再在目标周围来回跳。6.2 从ResNet到蒸馏出的轻量化模型如果你也要在更小的机载设备上部署可以考虑知识蒸馏。我用ResNet50作为教师模型把预测的soft label和中间层特征同时蒸馏给一个ResNet18学生模型。学生模型的参数量只有原来的四分之一左右在Jetson Orin NX上的推理帧率从35FPS提升到52FPS而低慢小无人机召回率只下降了不到2个百分点。蒸馏训练时要注意温度参数我设成4温度太高会让不同类别的概率差异过小学生学习不到清晰的边界。中间层蒸馏只取C2、C3、C4三层太深层特征对小目标没有增量帮助。量化方面FP16是安全的INT8损失明显尤其是小目标类别默认量化校准后的召回率暴跌了15个百分点所以我坚持在机载端使用FP16推理不追求极端的量化率。最后想提醒一句无论模型改动多大每次换结构、换训练策略都要先在老版本数据集上跑一遍回归测试否则很容易出现“低慢小检测涨了、地面车辆跌了”这种跷跷板问题。深度残差网络给了我一个足够稳定的基准后面的优化才有地方落脚。