
简介YOLOv7无人机实时探测人体是一篇学术论文PDF面向计算机视觉、深度学习与无人机应用领域解决无人机热红外图像和视频中的人员检测难题。这类场景常存在目标尺度小、背景复杂、分辨率低、标注数据稀缺等挑战。作者基于CNN架构提出完整的无人机TIR目标检测框架使用前视红外相机采集数据并训练YOLOv7模型。验证中交并比阈值0.5时人体检测精度达72.5%速度约161帧/秒同时评估了不同观察角度下的交叉检测性能为公共安全与智能巡检提供可行方案。资源包仅含1个PDF文档约2.01MB论文结构完整涵盖摘要、引言、方法、实验与结论性能指标详实可作为目标检测方向学术写作参考。目前已有269人学习读者可复现实验、借鉴FLIR数据采集与评估思路适合算法工程师、研究人员及高年级学生。1. 当无人机在天上找人的时候YOLOv7凭什么敢做实时主力YOLOv7无人机实时探测人体说白了就是把目标检测模型塞进机载或地面站在无人机俯拍画面里把行人实时框出来。这事难点不在模型本身而在三个叠加的约束目标小、视角俯、机身晃。地面监控里行人占画面几百像素到了无人机上可能只有二三十像素姿态又是从头顶往下看日常训练集里那种正面全身照基本失效再加上桨叶震动和空中风扰画面时不时糊一帧。YOLOv7在这个场景里是性价比很高的选择——它的E-ELAN骨干在不大幅涨参数的前提下把mAP推高了一截辅助训练头对小目标的增益肉眼可见而且部署链路从PyTorch到TensorRT都被社区趟平了。这篇就按我实际部署的路子从数据、训练、导出到机载推理把参数和坑一次说清适合手里有无人机、准备做搜救或巡检人体检测的工程师。2. 让模型先认得无人机视角下的人自制数据集与训练参数2.1 从公开数据集到自采数据VOC转YOLO格式脚本与四个边界坑训练无人机视角的人体检测数据集只有两个来源公开航拍数据集和自己飞出来的数据。公开的VisDrone、UA-DETRAC都是俯拍场景行人密集、尺度小和你的任务高度接近适合做预训练。但公开集里类别杂直接拿来训会带偏检测头我一般先把类别过滤成只留person再用自己的飞行数据做微调。无人机自采数据建议别一上来就录视频抽帧那样一个行人在连续帧里出现几十次训练集里同一目标反复出现模型容易过拟合到具体姿态。正确做法是手动挑帧保证同一目标在一段轨迹里最多出现3到5帧姿态覆盖走路、站立、弯腰、奔跑光照覆盖顺光、逆光、树荫。标注这块无人机视角下的行人经常只有几十像素标注时要把模糊不清的目标标成“忽略”区域或者干脆删掉让loss不背这个锅。如果你拿到的公开集是VOC格式需要先转成YOLO的txt格式。转换脚本我贴一个能直接用的版本import os import xml.etree.ElementTree as ET from pathlib import Path CLASSES [person] # 按实际类别顺序替换person必须排在你yaml里的相同位置 def clamp(value, lo, hi): return max(lo, min(value, hi)) def convert_voc_to_yolo(xml_path, out_txt, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue # 只保留person box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 转yolo要拿中心点和宽高且归一化 x_center ((x1 x2) / 2.0) / img_w y_center ((y1 y2) / 2.0) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 越界坐标必须夹紧否则训练时anchor匹配算出来的loss会变成nan x_center clamp(x_center, 0.0, 1.0) y_center clamp(y_center, 0.0, 1.0) w clamp(w, 0.0, 1.0) h clamp(h, 0.0, 1.0) lines.append(f{CLASSES.index(name)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt, w, encodingutf-8) as f: f.write(\n.join(lines)) # 用法遍历JPEGImages里的每张图找到同名xml生成同名txt放到labels目录 # 注意目标和标签的目录结构要和yaml里的path对齐这段脚本有三个值得留意的点。第一CLASSES列表的顺序必须和训练yaml里的names完全一致否则类别错位会影响整个检测结果。第二VOC的bbox是闭区间宽高直接相减会差1像素对归一化影响极小但对小目标来说这1像素可能就差一个IoU阈值建议用x2 - x1 1。第三越界坐标在裁剪标注时很常见无人机俯拍画面边缘的行人往往被截断不夹紧的话模型训练时边框回归会输出负数。YOLO格式还要注意一张图的txt文件名必须和图片名一致目录分开images和labels两个文件夹平级。写完脚本后用check_labels.py可视化一次把所有标注画回原图看一眼能省后续排查标注错位的半天时间。2.2 训练参数一次说全img size、batch、epochs、mosaic的取舍训练YOLOv7做无人机人体检测最影响结果的是输入分辨率。地面监控习惯用640×640无人机俯拍目标小我建议直接上1280×1280训练推理时再用640或768跑实时性。不知道你发现没有很多论文里YOLOv7在VisDrone上刷分靠的就是把输入分辨率拉高代价是显存翻倍、推理变慢。这是精度和帧率的第一个天平。batch size在单卡上能塞多大就塞多大但有个前提显存不够时优先降batch而不是降分辨率。Batch size从16掉到4mAP可能跌3到5个点分辨率从1280掉到640mAP可能跌8个点以上。所以我的优先级是先保分辨率再保batch。epochs在无人机小目标数据上不能太省120轮起步200轮也不夸张配合早停机制看验证集变化。YOLOv7的超参数文件里mosaic和mixup这两个增强对俯拍小目标要慎开。Mosaic把四张图拼一张训练时小目标会被缩放得更小边缘截断也更多开局阶段模型容易学崩。我的做法是前20轮把mosaic关掉让模型先在大目标上稳住后期再开mosaic提升泛化。fliplr水平翻转可以开着无人机俯拍没有左右语义翻转是安全的增强flipud上下翻转慎用如果任务里要区分行人是站着还是躺着上下翻会把语义搞乱。下面是我在NVIDIA RTX 4090上跑VisDrone微调的一套参数参考参数设置值说明img-size1280 1280训练分辨率推理另行调低batch-size1624G显存可到24梯度累积也行epochs150看best.pt是否还在涨mosaic0.0前20轮之后1.0两个阶段切换fliplr0.5水平翻转scale0.5随机缩放幅度别太大hsv_h / hsv_s0.015 / 0.7光照变化大的场景可再调高workers4过高容易卡在读图还有个很多人忽略的参数是label_smoothing在无人机数据集标注质量一般时开到0.05到0.1能防过拟合代价是置信度整体会偏低一些后面部署时要把conf-thres相应调低。2.3 训练命令与断点续训不让人工重新排队YOLOv7的训练入口是train.py命令参数和YOLOv5高度相似但有几个不同的地方--cfg要指向cfg/training/yolov7.yaml--weights支持预训练权重--hyp指定超参文件。下面是跑通的最小命令python train.py \ --workers 4 \ --device 0 \ --batch-size 16 \ --data visdrone-person.yaml \ --img 1280 1280 \ --cfg cfg/training/yolov7.yaml \ --weights yolov7.pt \ --epochs 150 \ --name drone-person \ --label-smoothing 0.05 \ --patience 30--weights yolov7.pt是官方在COCO上的预训练权重前面提过画一个不加任何路径前缀的文件名train.py会从官方仓库下载到本地你联网不方便时也可以自己手动下载后放到仓库根目录。--patience 30表示验证集30轮不涨就早停。训练中断后不想从头跑用--resume 1配合--name指定上次的run目录python train.py --resume 1 --name drone-person它会自动找runs/train/drone-person/last.pt续上。这里有个血泪经验换机器续训时WC和--cache-images的缓存目录对不上会报错直接把runs/train/drone-person/下的缓存文件删掉再resume一分钟能省一晚上。训练开始后第一个要看的是loss曲线能不能在50轮内降到0.05以下。YOLOv7的训练loss包含box_loss、obj_loss和cls_loss如果obj_loss一直徘徊不降多半是匹配正样本的anchor和你的目标尺度差异太大后面第4章会详细讲。训练完看results.png里的mAP0.5:0.95曲线无人机小目标数据集能做到0.35以上就值得上板子试了别强求COCO那种0.5以上的数字。3. 从PyTorch到机载推理ONNX导出、TensorRT部署与硬件选型3.1 导出ONNX的正确打开方式dynamic输入与end2end模式训练完拿到best.pt离上无人机还有很长一段路。PyTorch模型直接跑推理不是不行但对机载算力来说太奢侈——Python的GIL、动态图和CUDA Graph之间的调度开销足以让帧率掉三分之一。第一步是导出成ONNX再用TensorRT或OpenVINO进一步编译。YOLOv7官方仓库自带export.py我一般这样用python export.py \ --weights runs/train/drone-person/weights/best.pt \ --grid \ --end2end \ --simplify \ --dynamic \ --topk-all 100 \ --iou-thres 0.45 \ --conf-thres 0.25 \ --img-size 640 640 \ --device 0参数逐个说。--grid表示ONNX里保留grid生成逻辑不展开成每个尺度的预测层--end2end把NMS也塞进模型里输出直接是检测结果而不是原始预测张量。这两个参数配合使用好处是后续部署不用自己写NMS解码坏处是NMS的阈值被固定进模型里想调就得重新导出。--dynamic让输入尺寸可变但TensorRT的dynamic shape在部分板上性能不如静态shape后面会讲。--simplify用onnx-simplifier做图优化去掉一些冗余算子。导出的best.onnx先用onnxruntime在x86上验证一遍和PyTorch的推理结果比对。常见坑是导出时--img-size设了1280但部署时想用640推理shape对不上报错我这边的原则是部署用哪个分辨率导出就用哪个分辨率不要为了“灵活”而把dynamic开着上板子。另外YOLOv7的export.py在不同PyTorch版本下导出的ONNX算子集有差异如果遇到“Unsupported operator”报错把torch.onnx.export里的opset_version改成11或12再试比去翻算子实现快得多。3.2 TensorRT静态batch与FP16把模型压进Jetson的最后一脚ONNX只是中间产物机载推理的终点一般是TensorRT。用trtexec做编译最省事它不写一行代码就能把ONNX编译成TensorRT引擎/usr/src/tensorrt/bin/trtexec \ --onnxbest.onnx \ --fp16 \ --workspace2048 \ --minShapesimages:1x3x640x640 \ --optShapesimages:1x3x640x640 \ --maxShapesimages:4x3x640x640 \ --saveEnginebest.trt--fp16是机载部署的常态操作YOLOv7的权重和激活值在FP16下精度损失很小mAP大概掉0.5到1个点换来的是推理速度翻倍。--minShapes到--maxShapes定义了dynamic batch的范围这里把batch限定在1到4避免显存被过度预留。如果你确定推理永远单batch干脆把min/opt/max都写成1x3x640x640TensorRT会做静态shape优化性能还能再涨一截。编译完的.trt引擎文件是跟具体GPU型号绑定的Jetson Orin上编译的引擎换到AGX Xavier上跑不了得重新编译。这个坑记得写进交付文档里。用TensorRT C API做推理时核心是enqueueV2输入的buffer要提前用cudaMalloc分配好否则第一次推理会因为host和device内存没对齐而崩掉。Python端可以继续用pycuda包一个推理循环无人机项目对延迟敏感能提前用C写推理线程就别贪图Python的便利。3.3 三个平台的取舍Jetson Orin、Nano和x86工控机选机载推理硬件先算一笔账。YOLOv7在640×640输入下TensorRT FP16的推理延迟大致是Jetson Orin NX在12到18msJetson Nano在90到120msx86工控机配RTX 4060在8到10ms。这个差距决定了你的飞行平台功耗和载重预算。平台功耗640×640 FP16延迟适合场景Jetson Orin NX 16G10-25W12-18ms大疆M300这类载重大的机型Jetson Nano 4G5-10W90-120ms轻量化验证帧率10fps以内能接受x86 RTX 4060100W8-10ms地面站处理图传回传画面Jetson Nano跑YOLOv7其实很勉强4G内存跑FP16推理勉强够但前处理和后处理一叠加就吃紧帧率在7到10fps徘徊。新项目可以直接跳过Nano上Orin Nano或Orin NX差价在两三千元内换来的是把检测帧率从10fps拉到40fps对于需要实时跟踪行人的任务来说是决定性的差距。x86工控机的优势是生态成熟OpenCV和TensorRT版本随便折腾调试一次过劣势是功耗和重量对中小型无人机不友好一般只适合放地面站回传视频流做二次检测。还有一种做法是前端无人机只跑轻量化的人体检测把可疑目标的图像裁切后传回地面站由x86上的大模型做二次确认这样机载功耗和地面精度两头都占。硬件选型这块常见做法就按任务带宽、载重和延迟需求来权衡没有通吃的答案。4. 避坑无人机实时检测里最容易翻车的5个细节4.1 训练到一半卡在“Transferring parameter”——数据线程死锁现象训练开始后没跑几步控制台停在Transferring parameter...不动GPU利用率降为0CPU也看不见忙。原因YOLOv7的datasets.py里num_workers开太高配合Mosaic增强的多进程采样在自定义数据集尺寸不统一时子进程之间互相等锁死。解决先把--workers 4降到--workers 0跑通一轮确认代码没问题再逐步往上加。另外cache-images缓存会生成一个大文件多卡训练时每张卡都要读一遍容易出现文件句柄耗尽把缓存文件放在SSD上能大幅改善。4.2 多卡训练batch大于1直接报错除不尽的最后一个batch现象单卡训练一切正常双卡一开--batch-size 24报错“Split size mismatch”或者直接RuntimeError。原因多卡DataParallel会把batch平分到每张卡24个样本分到两张卡各12个没问题但当数据集长度不能被整除时最后一个batch只有19个样本一张卡分9个、另一张卡分10个TensorFlow的batch split逻辑就崩了。解决训练命令里加--drop-last参数让最后一个不完整的batch直接丢弃代价是每个epoch少几个样本对收敛几乎无影响。这个坑在我第一次跑YOLOv7双卡时踩了整整一下午最后翻了datasets.py源码才发现是drop_last没开。4.3 置信度0.3的检测框被NMS滤掉机载抖动下阈值不能照搬地面现象地面站上调试得好好的模型上飞机一飞漏检率突然从5%涨到15%看日志发现很多框的置信度只有0.25到0.35低于后处理的conf-thres0.4所以被滤掉了。原因无人机桨叶的微震动让画面产生亚像素抖动行人的纹理在检测头里被打散置信度整体被压低。解决把导出的--conf-thres降到0.15到0.25同时把--iou-thres提到0.5以上让低置信度框之间别互相覆盖。这样改完虚警会增加但航拍场景里人体目标本来就是稀疏的多几十个虚警框总比漏掉一个真实行人有价值。常见做法是在后处理里再加一帧的置信度平滑让检测结果和飞控的姿态数据做一次卡尔曼滤波把抖出来的野值吃掉。4.4 IMU采样率不足200Hz时云台补偿滞后检测框跟着画面“漂”现象无人机悬停时检测框稳定一旦打杆机动检测框开始在原位置和实际位置之间来回跳跟踪轨迹明显滞后。原因飞控的IMU采样率低于200Hz时云台姿态补偿的更新频率跟不上相机画面的帧率导致图像和IMU数据的时间戳对不齐检测框在画面上的投影位置抖动。解决硬件层面飞控上的IMU采样率至少开到200Hz以上在调参软件里把采样率拉满软件层面把检测结果里加一个时间戳对齐模块用最近一帧的IMU姿态去校正预测框坐标。这个问题容易被归到“模型不准”其实是机载传感器链路的问题排查时先看飞控日志里IMU时间戳和视频帧的时间差超过一个帧间隔就要处理。4.5 anchor和strides与数据集不匹配小目标检测率低到怀疑人生现象训练时看mAP曲线在0.3附近卡死不涨验证集上小目标低于32像素的召回率只有30%多。原因YOLOv7默认的anchor是基于COCO目标设计的以中大型目标为主无人机俯拍数据集里目标尺度的分布和COCO差异很大默认anchor覆盖不到小目标。解决训练前用YOLOv7自带的tools/脚本统计你数据集的标注框分布按结果修改cfg/training/yolov7.yaml里的anchor尺寸或者直接用k-means重新聚类生成锚框。另一个办法是把输入分辨率从640提到1280训练等于把目标在特征图上的尺寸放大等效于让小目标跨过anchor匹配的阈值。我推荐两个都做anchor聚类一次再加上高分辨率训练小目标的mAP能拉回来10个点以上。5. 机载推理的帧率与精度平衡Tiled推理、模型轻量化与降级策略5.1 推理时的即用优化half精度与固定shape的取舍TensorRT编译完成、能跑通之后先别急着上飞机把推理这块的杂音全部清掉。YOLOv7的export.py导出的engine默认做了NMS合并但输入输出仍要符合TensorRT的ICudaEngine接口。Python推理循环里最关键的是把图像的预处理从CPU挪到GPUdef preprocess(img, size(640, 640)): 按YOLOv7的letterbox方式缩放不破坏宽高比 import cv2 import numpy as np # 等比缩放然后填充到size h, w img.shape[:2] ratio min(size[0] / w, size[1] / h) new_w, new_h int(w * ratio), int(h * ratio) resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_LINEAR) canvas np.full((size[1], size[0], 3), 114, dtypenp.uint8) canvas[:new_h, :new_w] resized # BGR转RGBHWC转CHW归一化到0-1 blob cv2.dnn.blobFromImage(canvas, 1.0/255.0, size, swapRBTrue) return blob, ratio, (new_w, new_h)这段代码用cv2.dnn.blobFromImage一步做完缩放、通道转换和归一化比分开写几行快。ratio和填充尺寸要记下来后处理时要把预测框坐标映射回原始图如果忘了这一步框的位置就会偏得离谱。TensorRT引擎输入用cuda.mem_alloc分配固定大小反复用同一块buffer避免每帧都cudaMalloc这在Jetson上是性能杀手。5.2 大图切成tile推理找回无人机小目标的最后一根稻草无人机挂载的可见光相机分辨率往往是3840×2160直接resize到640×640推理一个行人可能只剩4×4像素检测头根本认不出来。这时候有个在航拍检测里特别好用的技巧——Tiled推理把大图切成若干有重叠的块逐块推理再把结果映射回原图坐标。切块策略很关键块大小取640或768重叠区至少留50像素否则目标正好跨在切割线上时会被劈成两半。def infer_tiled(engine, img, tile_size640, overlap50, conf_thres0.25): 大图分块推理返回映射回原图的xyxy框列表 import numpy as np h, w img.shape[:2] boxes [] step tile_size - overlap for y in range(0, h, step): for x in range(0, w, step): x2 min(x tile_size, w) y2 min(y tile_size, h) tile img[y:y2, x:x2] # 块尺寸不足tile_size时用灰色填充 if tile.shape[0] tile_size or tile.shape[1] tile_size: canvas np.full((tile_size, tile_size, 3), 114, dtypenp.uint8) canvas[:tile.shape[0], :tile.shape[1]] tile tile canvas # 单块推理代码略去engine.run的细节 preds run_inference(engine, tile) for box in preds: # 把块内坐标加上块的偏移映射回原图 box.x1 x box.y1 y box.x2 x box.y2 y boxes.append(box) # 全图最后做一次NMS去掉重叠框 return nms(boxes, conf_thres, iou_thres0.5)Tiled推理的代价是推理次数变多3840×2160切成640的块要跑20多次帧率从40fps掉到8fps左右。但它的precision收益非常大小目标检测率能翻倍。我的做法是把Tiled推理做成两级流水线低分辨率全图先跑一遍快速检测发现有可疑目标的小区域再对该区域做tile级二次检测。这样既保住实时性又不丢小目标。5.3 帧率与精度的降级策略三档可调的运行模式机载场景里光照、飞行高度、电量都会变化推理策略不能只有一档。我习惯把部署代码做成三档可切换模式输入分辨率Tiled开关FP16帧率适用场景高速巡检640×640关开30-40fps白天、飞行高度低于50米标准巡检1280×1280关开15-20fps黄昏、目标偏小、高度超过80米高精度搜索640×640960 Tiled开开5-8fps搜救、目标极小、允许悬停搜索切换逻辑放在飞控的下行链路里根据图传帧率和任务模式自动切换人工也可以在地面站手动强制。要注意三档模式对应的是三个不同的TensorRT引擎分别编译好放内存里切换时重新绑定ICudaEngine的上下文别边推理边加载引擎会在Jetson上卡出几十毫秒的空白。还有个更狠的降级方案模型蒸馏。把YOLOv7当teacher蒸馏一个YOLOv5n或YOLOv7-tiny当studentstudent模型参数量只有原来的三分之一帧率能再翻一倍。但蒸馏训练要花时间重新跑一遍数据不是所有项目都有这个预算建议先把Tiled和降级策略用起来不够再动蒸馏。6. 真机验证与日志回放检测模型敢不敢上天的最后一道关模型在桌面验证集上mAP再漂亮也不代表在无人机上能用。真机验证我一般分两步走第一步是地面跑机测试把相机接上推理板对着道路行人拍10分钟记录检测结果的置信度、框位置和帧率。第二步才是挂载飞行但飞行测试要设计好数据采集方案让检测日志和视频帧严格同步不然回来根本没法定位问题。日志记录用结构化JSON写每帧记下时间戳、检测框的xyxy坐标、置信度和当前飞行高度。回来排查时写一个回放脚本把日志里的框叠加到对应视频帧上逐帧确认漏检和误检。这个回放脚本是排查定位的钥匙我们有次在森林场景里漏检率暴增回放发现是树影晃动导致置信度大面积低于阈值把conf_thres从0.3降到0.2就救回来了。真机验证时不光要统计TP、FP、FN还要计算每个行人在画面里从出现到消失的连续检测帧数连续5帧以下说明检测不稳定需要回去调NMS阈值或者降低输入分辨率。这块我在交付时会给客户两套东西一套离线评测脚本能自动算PR曲线一套在线回放工具把真机日志叠到视频上。顺手记录的习惯是每次飞行前把trtexec的引擎参数打印到日志头部包括FP16是否开启、输入shape、conf阈值这样回看数据能快速知道这套结果是在什么配置下跑的。无人机实时人体检测这个方向模型本身只是三分之一数据的采集节奏、部署的降级策略和真机验证的严谨度决定它能不能在野外真正扛住。希望帮到你。本文还有配套的精品资源点击获取