ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

大疆无人飞行感知竞赛包复现指南:从环境配置到参数调优

2026/10/6 2:59:29 拓冰建站 浏览量
大疆无人飞行感知竞赛包复现指南:从环境配置到参数调优 简介大疆2022无人飞行智能感知技术竞赛香港理工大学参赛项目包集成通过全部目标并斩获全国第28名的完整机器人感知与导航方案定位为可供复现的竞赛级工程样例。面向无人机竞赛选手、SLAM/视觉感知研究者及ROS开发者覆盖视觉目标检测、雷达/红外/超声波融合感知、自主定位建图、路径规划与避障控制等核心环节。压缩包共81个文件容量40.2MB以msg/srv定义ROS通信接口Python/C源码实现检测与决策算法launch/yaml/xml负责节点启动与参数配置PDF工程说明和MD笔记记录调试思路GIF动图展示实机运行效果。其中RoboMaster-main涵盖airsim_ros_pkgs仿真环境、detect系列感知脚本、smooth_control平滑控制以及unitree_nav_ros导航子项目便于快速搭建基于AirSim的无人飞行感知测试平台从数据流到控制指令形成闭环。已有100人学习适合希望系统掌握竞赛级无人机智能感知、自主导航与工程组织方法的开发者。1. 大疆2022无人飞行智能感知技术竞赛这份“全国第28名.zip”是什么值不值得打开第一次看到“大疆2022无人飞行智能感知技术竞赛代表香港理工大学参赛Pass所有目标。全国第28名。zip”这个文件名的人多半以为里面只有排名截图。真正做感知的人会把它当成一个调试现场藏在 zip 里的是模型权重、推理脚本、评测配置以及一套当时能跑完全部测试目标但分数没进前列的完整方案。2022 年大疆举办的无人飞行智能感知竞赛核心任务是从无人机拍到的视频帧里检测目标并把结果按格式提交评分按检测的完整度和误报情况综合给出。这样一份压缩包恰好是很好的坐标系能看清竞赛交付物长什么样、推理怎么复现、参数怎么调也能理解为什么“全部通过”只拿到第 28 名。下面按这条线展开看完你拿到同类 zip 也能原地复现和继续优化。2. 解开大疆智能感知压缩包先校验交付结构和环境再跑推理拿到 zip 后的前 30 分钟决定后面是顺利复现还是反复翻车。不要直接双击解压然后双击运行第一步是确认包内结构、校验压缩包完整性再把推理环境固定下来。这三件事做完后面所有报错都能缩小到很小的范围。2.1 交付包里通常有哪几类文件先按清单查缺竞赛交付 zip 的结构和普通项目仓库不太一样它既有源码也有产物常见做法是包含模型权重、推理脚本、配置文件、输出结果和说明文档。一个典型的目录长这样DJI_2022_perception/ ├── README.md ├── requirements.txt ├── config/ │ ├── class_names.txt │ └── dataset.yaml ├── weights/ │ ├── model_best.pt │ └── last.pt ├── src/ │ ├── detect.py │ ├── eval.py │ └── utils/ │ ├── preprocess.py │ ├── nms.py │ └── visualize.py └── outputs/ ├── result.json └── vis/这个结构基本覆盖了竞赛包所有必要单元weights给推理提供权重src放检测和评测逻辑outputs放当时生成的提交结果README记录运行方式。我拿到包后会先对照 README 里的文件清单检查每个目录是否存在而不是直接去跑 detect.py。最常犯的错误是只关心权重文件忽略了class_names.txt和dataset.yaml后续类别索引错位、评测字段对不上排查半天才发现是配置文件没同步。需要特别留意的是源码里有没有硬编码的绝对路径。很多队伍在本地开发时写成/home/user/...或D:/contest/...换机器后这些路径全部失效。快速排查方式是解压后用 grep 搜索绝对路径特征grep -rn /home\|/root\|D: DJI_2022_perception/src若有输出就把相应位置改成相对路径或通过命令行参数传入。这一步看起来琐碎但能省下至少两个小时的排查时间。模型权重如果体积大通常只放一个下载链接而不是直接放在 zip 里这也是需要先确认的。2.2 校验 zip 完整性和 EOCD解压报错别急着换软件竞赛包在传输过程中出现损坏的概率不低尤其是超过 1GB 的文件经过网盘、微信转发或 U 盘拷贝经常出现中心目录损坏。判断压缩包是否完整标准做法是先做哈希比对再做压缩包自检sha256sum 大疆2022无人飞行智能感知技术竞赛.zip unzip -t 大疆2022无人飞行智能感知技术竞赛.zip | tail -n 5sha256sum需要一个可信的原始哈希值做比较如果 README 或下载页提供了哈希就以它为准。unzip -t会逐个文件测试 CRC 完整性末尾输出No errors detected in compressed data才算通过。实际中最常见的报错有两类一类是压缩包解到一半提示文件名乱码或 CRC 失败另一类是直接提示could not find end-of-central-directory也就是 EOCD 记录丢失或损坏。遇到 EOCD 问题时不要反复更换解压软件去撞概率。EOCD 位于 zip 文件末尾作用是告诉解压器中心目录在哪里它坏了之后WinRAR、7-Zip、系统的资源管理器行为各不相同有的能蒙对一部分有的直接放弃。可以先试 7-Zip 的自检再尝试用 zip 自带工具重建中心目录7z t 大疆2022无人飞行智能感知技术竞赛.zip zip -FF 大疆2022无人飞行智能感知技术竞赛.zip --out repaired.zipzip -FF会扫描文件里的本地文件头来重建中心目录工作目录下会生成一个repaired.zip。这是一颗后悔药但只对中心目录损坏有效如果文件本身被截断修出来的包能解压但部分文件内容已经残缺。所以解压完一定要回到模型权重上做一次加载验证否则后面跑推理时模型能 load 但结果全是一片背景那时候才真的血泪。2.3 固定推理环境torch、OpenCV 与 CUDA 的版本对齐无人飞行智能感知竞赛的推理代码基本都跑 PyTorch而 PyTorch 版本和 CUDA 版本是坑最多的地方。不夸张地说环境不对时程序报错可能完全不在 torch 上而是 OpenCV 打不开视频、模型权重反向不兼容或者 cuDNN 算子报错。我一般会在项目目录里建独立虚拟环境绝不直接装在系统 Python 上python -m venv venv_dji source venv_dji/bin/activate pip install -r requirements.txt python -c import torch, cv2; print(torch.__version__, cv2.__version__) nvidia-smivenv 的好处是这个项目的依赖不会污染其他项目反过来也一样。requirements.txt里如果锁定了版本就严格按它装如果没锁就先从权重文件的元数据去判断训练时的 torch 版本而不是装最新版。拿.pt文件里的torch.__version__信息做参考是可行的但更稳妥的判断方式是直接试跑一次前向推理能出结果再继续。CUDA 版本这里单独提醒一件事nvidia-smi显示的 Driver 版本支持范围很宽它不等于 PyTorch 实际使用的 CUDA runtime 版本。PyTorch 通过 pip 安装时自带了 CUDA 运行库只要驱动版本大于等于 PyTorch 要求的版本就行不需要自己单独安装整套 CUDA Toolkit。常见翻车点是拿 torch 报的 CUDA error 去重装驱动最后把系统环境弄坏。环境固定后做一次最小验证用一张测试图或者一个短视频跑一遍推理确认整个链路能通。只有这一步过了后面调参才有意义否则你改了半天阈值最终都分不清是环境问题还是算法问题。3. 跑通推理管线把“Pass 所有目标”的提交结果复现出来“Pass 所有目标”听起来像满分其实更接近竞赛里的一个门槛所有评测样本都通过了判定条件。要复现这个结果必须把推理入口弄明白然后按评测方要求的字段写出结果文件。整个过程有三件具体事读懂入口参数、生成规范 JSON、找到 PASS 的判定边界。3.1 先读配置和权重模型入口文件到底接收哪些参数竞赛检测代码多沿用 YOLO 系检测器的 CLI 风格入口为detect.py参数数量和命名各家略有差异核心不外乎权重路径、输入来源、置信度阈值、IOU 阈值、推理尺寸和输出目录。典型启动命令如下python src/detect.py \ --weights weights/model_best.pt \ --source data/val_videos \ --img-size 1280 \ --conf 0.25 \ --iou 0.45 \ --device 0 \ --save-json这条命令把val_videos目录下每个视频逐帧送入模型检测结果按 JSON 保存到默认输出目录。各参数含义如下参数典型初值作用为什么先这么设--weightsmodel_best.pt指定模型权重选择验证集上表现最好的权重不要用last.pt--sourcedata/val_videos输入视频或图片目录评测数据通常按视频组织目录传入比单文件省事--img-size1280推理缩放尺寸对多数无人机画面是召回与速度的折中点--conf0.25置信度阈值低于该值的检测框直接丢弃--iou0.45NMS 的 IOU 阈值去掉重叠框的默认参数--device0推理设备0 表示第一张 GPUCPU 推理通常来不及--save-json无输出评测用结果不开启这个参数结果就只看得到可视化图改参数前先跑一次默认配置把结果存下来当作 baseline。之后每次调参都与这份 baseline 对比而不是凭感觉看两三张效果图下结论。尤其是--conf和--iou它们直接影响“全部通过”的判定后面单独讲。3.2 运行推理并把结果写成评测要求的 JSON评测系统读取的一般是检测结果列表每个条目包含帧号、类别、置信度和目标框。下面这段代码是典型写法的骨架import json results [] for frame_id, frame in enumerate(video_reader): preds model(frame) # 每个元素: [x, y, w, h, score, cls_id] for *xywh, score, cls_id in preds: results.append({ frame_id: frame_id, category_id: int(cls_id), score: float(score), bbox: [round(float(v), 2) for v in xywh], }) with open(outputs/result.json, w) as f: json.dump(results, f)这里三个字段最容易出错。第一是frame_id不同评测方对帧号的约定不一样有从 0 开始也有从 1 开始统一按 README 或样例结果文件的起始值来。第二是category_id它对应class_names.txt里的索引注意模型内部类别顺序可能和评测方给的标准顺序不一致必要时做一次映射。第三是坐标bbox的坐标系必须和评测方的要求一致常见做法是[x, y, w, h]或[x1, y1, x2, y2]而且有的要求归一化坐标有的要求原始像素坐标。写完结果后先抽样检查几个框的位置是否和原图吻合。直接把 JSON 提交前用可视化脚本把检测框画在对应帧上肉眼确认偏移量。这一步能发现很多隐蔽问题比如横纵坐标反了、宽高写成了中心点坐标等。坐标写错时评测分数几乎为零但本地却不报错属于最典型的“黑匣子翻车”。3.3 “全部通过”不等于满分PASS 判定到底在哪定义很多队伍把评测脚本一起打进交付包所以eval.py或dataset.yaml里往往藏着判定规则。PASS 条件一般不是在每个目标上都要求精确坐标重合而是设定了一个阈值组合检测框与真值框的 IOU 超过某个值就算命中某个类别的命中率超过某个比例就通过。能全部通过只说明每个评测样本都越过了这根线不代表检测质量已经到顶级。复现成绩时我会把 eval.py 里的阈值参数找出来读一遍。重点看三个变量IOU 匹配阈值、单帧最低命中率、是否对误检有惩罚。把默认阈值记下来然后自己把置信度阈值往严了调一档看 PASS 结果会不会崩。如果评测脚本默认 IOU 阈值是 0.5本地能全过那我再把置信度从 0.25 提回 0.3仍能过说明这个成绩有冗余如果一调就崩说明当时是在阈值边缘擦过去的后续任何预处理变化都可能让它掉下去。这里有个容易被忽略的点评测脚本本身可能也有 bug 或者边界情况。如果它能复现当时的 PASS 结果就先用它做 baseline不要急着改评测逻辑等成绩稳定复现后再去优化自己的检测结果。4. 分差藏在参数和预处理里大疆无人机目标检测的三个调优点到了这个阶段推理已经跑通结果也能输出接下来核心问题就变成为什么第 28 名而不是更高。无人飞行场景和普通路面检测最大的区别在于目标小、视场大、目标密集因此置信度阈值、IOU 阈值和推理尺寸这三个参数影响非常直接。绝大多数队伍差距就是这里拉开的。4.1 置信度阈值不是越小越好PASS 率与误检数的平衡直觉上降低置信度阈值能捞回更多目标从而提高通过率。但要小心评测脚本对误检的处理很多赛事不是只看召回而是对每个视频的误检数量有硬限制或者直接把误检折算成罚分。阈值放太低时模型会把树影、屋顶、水面反光当成目标结果是漏检少了误检多了PASS 率反而下降。我处理这类问题时会在小验证集上扫一遍阈值而不是只试一两个值for conf in [0.05, 0.10, 0.15, 0.20, 0.25, 0.30]: pass_rate quick_eval(confconf, iou0.45) false_alarm count_false(confconf) print(fconf{conf:.2f} pass{pass_rate:.3f} false{false_alarm})quick_eval可以复用交付包里的评测脚本不必每次都完整跑count_false统计误检框数量。输出结果通常呈现一个规律阈值降到某个点后通过率不再上升误检却开始陡增。取那个拐点附近的值作为最终配置。如果评测脚本没有误检惩罚那确实可以把阈值压得很低但比赛通常不会这么做否则人人把阈值设成 0.001 就能刷高召回。4.2 NMS IOU 与重复框小目标在高空视角下容易被压没无人机视角下目标往往成团出现停车场里的车紧密排列广场上的人三五成群。默认的 NMS IOU 阈值 0.45 在普通场景够用但遇到密集小目标时两个相邻目标的检测框 IOU 很容易超过 0.45结果其中一个目标被当成另一个的重复框直接抑制掉。解决办法是把 NMS 的 IOU 阈值适当放宽比如 0.6 到 0.7让重叠度更高的框也能共存。但放宽 IOU 会带来重复框增多的问题同一个目标可能输出两三个框。评测时如果允许多个框匹配同一个真值但只记一次命中重复框影响不大如果对多检有惩罚就要再补一个按置信度合并的逻辑。更精细的做法是按类别设置不同的 IOU 阈值因为人和车在画面里的密集程度差异很大。# 自定义 NMS 时按类别使用不同 iou 阈值 iou_map { 0: 0.45, # 车辆相对稀疏沿用默认 1: 0.65, # 行人容易聚集放宽保留 }实现时把预测框按cls_id分组每组调对应阈值做 NMS再把结果合并。这个改动不涉及重新训练只改推理代码属于性价比比较高的调参项。需要注意的是放宽 IOU 后要重新跑第 3 章的完整评测不能只看两帧可视化就下结论。4.3 推理尺寸和切图从 1280 到 1920 之前先算显存大疆消费级无人机拍摄的画面通常是 4K 甚至更高原始帧里一个行人可能只有 10 到 15 个像素。统一缩放到 1280 再进网络后小目标可能只剩几个像素检测器几乎不可能把它们找出来。把推理尺寸提升到 1920 往往能明显提高小目标召回但计算量按面积增长显存占用和推理时间都会翻倍。动手之前先看显存余量别再推理时被系统杀掉进程。nvidia-smi --query-gpumemory.total,memory.used,memory.free --formatcsv合计算力不够的情况下整体放大不如切图把大图切成有重叠的小块分别推理再将结果映射回原图坐标。这样做既能保留小目标的像素尺寸又不会让单帧显存开销暴涨。切图逻辑并不复杂def sliding_crop(img, crop_size960, overlap64): h, w img.shape[:2] step crop_size - overlap for y in range(0, h, step): for x in range(0, w, step): crop img[y:y crop_size, x:x crop_size] yield crop, (x, y) for crop, (offset_x, offset_y) in sliding_crop(frame): preds model(crop) for *xywh, score, cls_id in preds: xywh[0] offset_x xywh[1] offset_y all_preds.append([*xywh, score, cls_id])overlap参数很关键一般取目标尺寸的 1.5 到 2 倍。切图最怕目标恰好落在切缝上被截成两半重叠区就是用来缓解这个问题的。切图后所有预测回到原图坐标可能产生大量重叠框所以最后要再做一次全图 NMS。第一次切图时建议先只跑几十帧人工检查切缝处的检测情况确认没问题再全量跑。这里最容易出的玄学问题是切图后小目标反而变多了其实是重叠区重复框没清理干净不是模型变强了。5. 大疆感知竞赛常见问题与排查三个真实坑这一章说几个我自己在复现同类竞赛包时踩过的问题。每条都按“现象、原因、解决”来写方便你遇到同款问题时直接对号入座。5.1 压缩包能解压、权重能加载但推理结果全是背景现象unzip和7z t都通过模型用torch.load也能加载跑完一整轮推理后评测结果只有个位数通过可视化输出里几乎看不到检测框。原因zip 检查通过只能说明压缩包内部文件头完整不代表权重文件内容没被截断。PyTorch 加载.pt文件时采用 pickle 序列化当文件被截断但尾部恰好保留了关键结构时加载过程可能不报错只是部分张量的值变成默认初始化或缺失数据。模型能跑但输出已经没有实际感知能力。解决不要信任“能 load 就是没问题”要在加载后检查模型状态字典和配置结构的匹配度。如果配置里定义的是 80 个类别state_dict里检测头输出通道数也按 80 对应那么重点检查epoch和best_score之类的元数据字段。更稳妥的做法是直接拿验证集上一段肉眼可辨的片段推理如果该出的框没出基本就是权重损坏重传原文件比对哈希后替换。5.2 视频帧与 EXIF 时间戳错位导致后半段全部漏检现象前半段视频检测正常后半段的目标框整体偏移或者同一目标反复闪烁消失甚至从某个时间点开始全部漏检。原因竞赛视频经过剪辑或压缩后帧率和时间戳并不总是连续。很多推理脚本默认按文件名的序号逐帧处理但文件名序号是物理帧顺序而评测系统按原始时间轴取帧两者的偏移积累到后半段就完全错位。还有一种情况是视频采集端保留了 EXIF 信息但拿来做帧对齐时没有考虑原视频的起始时间。解决先用元数据工具查看每帧的拍摄时间再决定是否按时间重新建立帧索引。从单帧 JPEG 提取拍摄时间的代码很简单from PIL import Image img Image.open(frame_00123.jpg) exif img.getexif() dt_original exif.get(0x0132) # DateTimeOriginal print(dt_original)如果每一帧都带时间戳就按时间戳排序生成新的帧序号如果不带就按视频容器的 fps 和第一帧时间外推。改完帧序号后重新生成 result.json 并检查后半段检测分布是否恢复正常。这个问题最坑的地方是现象看起来像模型退化实际是数据喂错了属于典型的定位半天找不到根因。5.3 推理进程显存 OOM视频流别一次性读进内存现象推理跑到几百帧后进程被系统杀掉日志没有任何 Python 报错dmesg里能看到 OOM 记录。重新用更小的 batch 跑有时能跑完有时仍然崩溃。原因检测脚本把整个视频的帧一次性np.stack成一个大数组或者 DataLoader 的num_workers设置过高导致内存峰值超过机器负载。无人机视频分辨率高一帧 4K 图像在内存里就是约 12MB一个三分钟视频按 30fps 有 5400 帧整体加载轻松超过 64GB 内存。GPU 显存也会被推理中间结果占满如果不做流式处理迟早崩溃。解决把逐帧循环改成生成器并用带batch_size的 DataLoader 控制同时送入 GPU 的帧数。from torch.utils.data import DataLoader, TensorDataset def frame_generator(video_path): cap cv2.VideoCapture(video_path) while True: ok, frame cap.read() if not ok: break yield frame cap.release() # 分批送入模型避免一次性 stack for batch in DataLoader(list(frame_generator(video_path)), batch_size8, num_workers2): with torch.no_grad(): preds model(batch)注意每处理完一个 batch模型输出要及时转移到 CPU 并释放 GPU tensor。不要每轮都调用torch.cuda.empty_cache()那样反复清空缓存反而拖慢速度让 PyTorch 自己管理显存通常更高效。真遇到显存紧张优先降低batch_size而不是把推理尺寸一降到底。6. 拿到 28 名后还想往上走两个改动成本低、收益靠验证的技巧6.1 多尺度 TTA把漏检目标捞回来一次排名靠前的队伍通常不只用单一尺度推理。竞赛评测不像实时直播它只要求最终提交结果所以可以把多尺度推理当作离线优化手段。做法是对同一帧做 0.8 倍、1.0 倍、1.5 倍缩放分别推理后将结果映射回原图再做一次 NMS 合并import cv2 scales [0.8, 1.0, 1.5] all_dets [] for s in scales: resized cv2.resize(frame, (int(w * s), int(h * s))) dets model(resized, conf0.05) dets[:, :4] / s # 坐标映射回原图 all_dets.append(dets) final_dets nms(torch.cat(all_dets), iou0.5)多尺度带来的提升主要集中在中大尺度目标上小目标受益有限如果时间预算允许再结合 4.3 的切图。TTA 会把推理时间放大三倍左右非常适合只跑一次评测的场景不适合要实时出结果的机上推理。一个经验是每次只加一个尺度对比评测分数不要一次性把五个尺度全开否则最后很难说清楚是哪个尺度起作用。6.2 输出结果再后处理用连续几帧的置信度做平滑检测器对单帧的判断是独立的但视频里目标在连续几帧之间是有关联的。如果某帧因为运动模糊导致置信度骤降直接按阈值过滤就会丢掉这个目标。常见做法是用简单 IOU 匹配把连续帧里同一目标关联起来用近几帧的平均置信度替代当前帧的置信度smoothed_score 0.75 * avg(last3_scores) 0.25 * current_score if smoothed_score conf_thres: keep(frame_id, bbox, smoothed_score)这个技巧能提升评测中“目标闪烁”场景的稳定性但它也会让响应变迟钝不适合在线决策。做这一步时后处理逻辑要写清楚合并规则、平滑窗口都要留成可配置参数方便后续切换。我就吃过一次亏平滑窗口设成 5 帧后目标在中间两帧被一个场景级误检带偏反而制造出新的虚警。所以每次改动后都要完整重跑评测脚本用数值而不是个案判断。现在拿到一份竞赛交付 zip我的习惯是先跑哈希和自检再固定环境最后才是调参数顺序反了后面每一步都在给前面欠的债买单。这个流程救过我很多次也希望帮到你。本文还有配套的精品资源点击获取