简介:针对无人机视角下的船只检测与海上搜救任务,压缩包内整合了基于YOLO11的完整目标识别方案,覆盖从数据标注、模型训练到推理部署的主要环节,适合有一定深度学习基础的开发者或研究人员直接参考实践。包体共2000个文件,压缩后约676.5MB,文件类型以1059个txt标签、284张jpg样本图像、169个Python脚本、88个yaml配置和377个md文档为主,分别对应数据集标签、训练图片、训练与推理脚本、环境配置及使用说明,目录组织便于快速检索。数据集类别聚焦于船(boat)这一水上目标,可在无人机巡逻、水域监管等场景下降低背景干扰;目前已有88人学习/下载。数据集包含YOLO格式与VOC格式标注,已按train/val/test划分并附有data.yaml,可直接用于YOLOv5/v8/v9/v10/v11/v12等主流算法训练;同时提供训练好的模型、评估指标曲线图,以及C++/Python推理示例和网页可视化样例,便于在无人机水域巡逻、落水人员搜救等场景中快速部署和效果验证。
1. 先说你最关心的:这套 yolo11 无人机船只检测方案,能直接用于海上搜救吗
你手里这个名为 ultralytics-yolo11 无人机视角船只检测的压缩包,打开之后应该是三样东西:一套基于 ultralytics 框架的 yolo11 工程代码、一份标注好的水上目标数据集、若干训练好的模型权重。我先给结论:这套东西拿来跑通流程完全没问题,但如果你直接把它部署到海上搜救任务里,大概率会翻车——不是因为代码有 bug,而是因为数据集里的“船”和你现场要搜的“船”,在视角、光照、海况上差得太远。这篇文章我就按一条完整的落地路径来讲:yolo11 网络结构为什么适合无人机视角,数据集该怎么盘点和使用,训练参数怎么调,以及部署到视频流时的注意事项和排错方法。
2. 无人机视角为什么难:小目标、运动模糊与复杂水面,yolo11 凭什么能打
2.1 从 yolo11 网络结构看针对小目标的三个变化
从 yolo11 网络结构说起,不是要背论文,而是要弄明白:为什么同样是目标检测,yolo11 比上一代更适合无人机视角下的水上目标识别。无人机飞在 100 米到 500 米高度时,一艘 12 米长的渔船在画面里往往只有几十个像素。模型对这类小目标的敏感度,取决于三个结构点。
第一是 C3k2 模块替换了之前的 C3/C2f。这个改动直接降低了推理时的计算量,同时保持了足够的梯度流。对小目标检测来说,它的价值不在单层特征,而在于“同样的算力预算下可以把网络做得更深”。我用同样的显存跑 yolo11m 和上一代同量级模型,训练速度大概快了 15%,这意味着你有更多空间去调高输入分辨率,而分辨率恰恰是小目标召回率的第一影响因素。
第二是 SPPF 结构保留了多尺度池化能力,它把不同感受野的特征拼接后送入检测头。在无人机的俯视画面里,一艘大货轮和一艘小皮划艇的尺度差异可能超过 50 倍,SPPF 输出的多尺度特征图是同时召回大小目标的基础。关键是后面接的 PAN-FPN 路径,把高分辨率浅层特征和语义丰富的深层特征做了融合,小目标主要靠高分辨率特征图兜底。
第三是 C2PSA 自注意力模块。海面场景的背景极其单调,大面积的水面纹理没有有效信息,目标就散落在这个大背景里。注意力机制的作用是让模型学会“忽略水、关注异常点”,在深层特征图上建模全局关系。这一点的实际收益在你的数据集上验证最直观:同样训练 100 个 epoch,带注意力的权重在“远距离小目标”上的召回率通常比不带注意力高 3 到 5 个点。
另外 yolo11 延续了 anchor-free 检测头。船只的长宽比变化很大,货轮可能 1:6,救生筏接近 1:1,anchor-free 的设计直接回归中心点和宽高,省去了为不同船型手工调锚框的麻烦。这对数据集的类别杂、比例杂的情况非常友好,也减少了一个容易出错的人工干预点。
2.2 数据集是这套方案的硬通货:类别分布与标注格式
这个压缩包里最值钱的资源其实是数据集,不是权重。权重是数据集训练出来的副产品,数据质量决定了模型上限。拿到数据之后第一件事不是训练,而是盘点。我一般会先跑一段统计脚本,看看标注类别分布和目标尺寸分布,这一步能避免你后面在错误的数据上浪费十几个小时。
from pathlib import Path from collections import Counter labels_dir = Path("datasets/ship/labels/train") cls_counter = Counter() size_counter = Counter() for label_file in labels_dir.glob("*.txt"): for line in label_file.read_text().strip().splitlines(): parts = line.split() if len(parts) != 5: continue cls, cx, cy, w, h = parts cls_counter[cls] += 1 area = float(w) * float(h) if area < 0.01: size_counter["small"] += 1 elif area < 0.04: size_counter["medium"] += 1 else: size_counter["large"] += 1 print("类别分布:", cls_counter) print("目标尺寸分布:", size_counter)这段代码遍历训练集的所有 txt 标注,统计每个类别的实例数量,并按照归一化面积把目标分成小、中、大三档。注意 YOLO 格式的每一行是“类别 cx cy w h”,其中 w 和 h 是相对于图片宽高的比例,值域在 0 到 1 之间。如果 small 档占比超过 70%,说明这个数据集以小目标为主,训练时就要把 imgsz 拉高;如果某个类别的实例数不足另一个类别的十分之一,那就是典型的类别不平衡,后面训练策略必须特殊处理。
还要检查标注框有没有问题:坐标是否越界、宽度高度是否为负、类别 ID 是否超出 categories 数量。常见做法是写一个校验循环,把 w 或 h 小于等于 0 的标注行直接报错,这些脏数据会在训练时产生 NaN 损失。这种组织方式与 coco2017 数据集结构的逻辑一致,但 YOLO 的 txt 更精简,没有 json 层级,所以自检脚本必须自己写。
2.3 模型版本选择的判断标准:n/s/m/l/x 怎么选
yolo11 有 n、s、m、l、x 五个版本,压缩包里通常也只训练了其中的某几个。很多人一上来就选 x,觉得参数越多越好,结果在无人机机载设备上跑不动,白白浪费了训练时间。我一般按部署目标和数据量来定,而不是按“最强模型”来定。
| 版本 | 参数量 | 推理速度 | 适用设备 | 典型场景 |
|---|---|---|---|---|
| yolo11n | 约 2.6M | 最快 | Jetson Nano、树莓派 | 机载实时检测 |
| yolo11s | 约 9.4M | 快 | Jetson Orin、笔记本 GPU | 移动地面站 |
| yolo11m | 约 20M | 中等 | 单张 2080/3060 级别 | 高精度地面处理 |
| yolo11l | 约 25M | 较慢 | 服务器 GPU | 事后分析 |
| yolo11x | 约 57M | 最慢 | 多卡服务器 | 追求极限精度 |
选择逻辑有三条。第一,数据量少于 5000 张时,上 m 以上的版本容易过拟合,s 反而是稳定选择。第二,海上搜救如果是无人机机载实时检测,n 或 s 是唯一能保证帧率的选项;地面站处理离线视频,m 性价比最高。第三,先跑通再升级,不要一上来就训练 l 或 x,先用 s 把数据 pipeline 验证干净,再换大模型看收益。你会发现很多时候 s 到 m 的 mAP 提升只有 1 到 2 个点,但推理时间翻了一倍,这时候值不值得就要看任务本身了。
3. 环境搭建与数据集准备:用 ultralytics 跑通 yolo11 训练的最小配置
3.1 安装 ultralytics 与确认 GPU 环境的三个命令
yolo11 环境配置最常翻车的点不是 ultralytics 库本身,而是 PyTorch 和 CUDA 的版本匹配问题。这套方案用的是 ultralytics 框架,它把训练、验证、推理、导出都封装成了命令行和 Python API。安装本身很简单,但装完之后先别急着训练,按下面的顺序把环境确认一遍。
# 创建虚拟环境,避免污染系统 Python conda create -n yolo11 python=3.10 -y conda activate yolo11 # 安装 ultralytics,会自动拉取依赖的 torch 和 torchvision pip install ultralytics # 验证 GPU 是否可用,输出 True 才继续 python -c "import torch; print(torch.cuda.is_available())"第一行创建了一个独立的 Python 3.10 虚拟环境。为什么强调虚拟环境?因为 ultralytics 依赖的 numpy、opencv-python 版本比较敏感,和系统里其他项目的依赖经常打架。第二行安装 ultralytics,它会自动安装 torch、torchvision、opencv 等核心依赖。第三行是关键验证命令,如果输出 False,说明 PyTorch 装的 CPU 版本或者 CUDA 驱动版本不匹配。常见处理办法是先运行 nvidia-smi 查看驱动支持的最高 CUDA 版本,再去 PyTorch 官网选对应的安装命令,不要用 pip 默认安装的 torch,那个大概率是 CPU 版。
提示:如果 conda 创建环境很慢,可以用 python -m venv yolo11 代替,效果一样。关键是不要直接用系统 Python 装。
3.2 数据集目录结构与 YAML 配置
数据集目录需要按照 ultralytics 约定的结构组织。标准格式是 images 和 labels 两个大目录,各自下面再按 train、val 分一次。如果你拿到的压缩包里的组织方式不一样,先写成下面这个结构再做训练,否则 data.yaml 里的路径会指错。
datasets/ship/ ├── images/ │ ├── train/ │ │ ├── 001.jpg │ │ └── ... │ └── val/ │ ├── 002.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── 001.txt │ │ └── ... │ └── val/ │ └── 002.txt └── data.yaml对应的 data.yaml 配置是这样的:
path: datasets/ship train: images/train val: images/val nc: 2 names: 0: ship 1: person_in_water这里的 path 是相对于你执行训练命令的工作目录,train 和 val 是相对 path 的路径。nc 是类别总数,names 里每个 ID 必须和标签文件里的第一个数字对应。这个文件的坑在于路径一旦写成绝对路径,换到另一台机器就必须改,所以尽量用相对路径。另外,train 和 val 的图片数量比例不用严格遵循 9:1,但 val 集合至少要保证每个类别有 50 个以上实例,否则验证集的 mAP 波动会非常大,你很难判断模型到底是变好了还是运气变好了。
3.3 验证预训练模型:下载即用的模型检查
训练之前,先确认 ultralytics 自带权重能不能正常跑通推理。这一步的目的是把你的环境、权重文件、推理函数这一整条链路打通,不要等到训练完了才发现权重文件损坏或者推理接口调用方式不对。
# 用自带的预训练权重对测试图片做检测 yolo detect predict \ model=yolo11s.pt \ source=test.jpg \ imgsz=1280 \ conf=0.25 \ iou=0.45 \ save=True这条命令会把 test.jpg 里检测到的目标框画出来并保存到 runs/detect/predict 目录。model 参数指定权重文件,首次运行会自动下载 yolo11s.pt。imgsz 是推理时的输入尺寸,这里设置了 1280 而不是默认的 640,原因是无人机视角的小目标在低分辨率下会被压缩成几个像素,检测头根本来不及响应。conf 是置信度阈值,低于这个值的框会被丢弃;iou 是 NMS 时的交并比阈值,控制两个重叠框是否合并。跑完之后打开保存的图片,如果框的位置和类别对得上,说明环境没问题;如果测试图里什么都没有,先降低 conf 到 0.1 试试,可能是目标太小导致置信度上不去。
提示:如果你的测试图片是从视频里截出来的,注意检查 JPEG 压缩痕迹,压缩率过高的小目标区域会糊成一团,推理结果差是正常的,不代表模型有问题。
4. 训练自己的水上目标模型:关键参数与训练策略
4.1 训练命令与参数说明
用 ultralytics 训练自己的数据集,核心命令只有一条,但参数要按任务特点调整。下面是我在训练无人机视角船只检测模型时的常用配置,它兼顾了显存占用和检测精度。
yolo detect train \ model=yolo11s.pt \ data=datasets/ship/data.yaml \ epochs=100 \ imgsz=1280 \ batch=16 \ device=0 \ patience=20 \ save_period=10 \ project=runs \ name=ship_v1model 参数指定预训练权重的路径,这里用 yolo11s.pt 作为起点,而不是 from scratch。用预训练权重做迁移学习可以大幅缩短收敛时间,yolo11s.pt 是在 COCO 上预训练的,对通用目标的特征提取能力可以直接迁移到船只检测上。imgsz=1280 是这一场景下最重要的参数,它直接决定小目标在输入张量里占多少个像素。batch=16 是显存和训练速度的折中,如果你的显卡只有 8GB 显存,imgsz=1280 的情况下 batch 可能需要降到 8 或 4,否则会报 CUDA out of memory。device=0 指定第一块 GPU,省得它跑到 CPU 上去。save_period=10 表示每 10 个 epoch 存一次权重,配合下面的早停策略使用。
训练启动后,可以用 yolo detect train 的实时输出监控 loss 变化。正常情况下前 10 个 epoch 的 box_loss 和 cls_loss 应该稳步下降,如果 loss 曲线几乎是平的,别急着加训练轮数,先停下来检查数据集和上一章提到的问题。
提示:第一次训练建议先用 10 个 epoch 跑通全流程,确认数据加载、caching、验证集评测都没问题,再正式跑 100 个 epoch,省得最后发现配置错误白等半天。
4.2 数据增强参数:无人机视角最该动哪几个
ultralytics 的数据增强默认参数在大多数场景下表现不错,但无人机视角有它的特殊性,需要重点调整四个参数。增强参数可以写在 data.yaml 里,也可以作为命令行参数传入。下面这段是经过实际验证的配置:
# data.yaml 中追加的增强配置 mosaic: 1.0 scale: 0.5 degrees: 45 hsv_h: 0.015 fliplr: 0.5mosaic 是四张图拼接成一张进行训练,它对小目标检测的帮助明显,因为拼接后每张子图的缩放比例不同,变相增加了目标尺度的多样性,无人机视角的船只尺度差异极大,这个增强建议保持开启。scale 控制目标缩放范围,0.5 意味着训练时图片会在 0.5 到 1.5 倍之间随机缩放,这个值不要调太大,否则小目标会被缩放得几乎不可见,模型以为是噪声。
degrees 控制随机旋转角度。无人机巡航时航向会改变,目标在画面里的朝向不固定,所以旋转增强是必要的。但我建议不要设到 180 或 360,因为水上目标有明确的“上下”概念,比如船的船头和船尾纹理不同,过度旋转会产生大量不合理样本,让模型学歪。45 度是个平衡值。hsv_h 是色调抖动,海上场景不同时间段的光照色温差异大,稍微加一点可以提升泛化性。fliplr 水平翻转是几乎无成本的增强,船只的左右对称性比较强,0.5 的概率是安全的。
4.3 早停、断点续训与结果评估
训练过程中断了怎么办?这是很多人忽略的一个问题,特别是训练在服务器上跑了好几个小时,突然断电或者显存被别的进程抢占导致中断。ultralytics 支持断点续训,前提是训练时没关掉 cache 和保存中间权重。
# 从 last.pt 恢复训练 yolo detect train \ model=runs/ship_v1/weights/last.pt \ resume=Trueresume=True 会自动读取上次训练的参数和优化器状态,继续从断点处往下跑。这里有个注意点:resume 模式下不需要再传 data 和 epochs,它会从上一次的配置里读取。所以如果你中途改了数据增强参数,resume 不会生效,需要重新用完整命令开一轮新训练。
早停参数 patience=20 的效果是验证集 mAP 连续 20 个 epoch 没有提升就自动停止。这个机制非常实用,因为深度学习训练有一个“收益递减”的规律,前 30 个 epoch 提升明显,后面可能只在小数点后三位波动。早停帮你省下的时间可以用来调参或准备更多数据。训练结束后,重点看 runs/ship_v1 目录下生成的 results.csv 和 confusion_matrix.png。results.csv 里记录了每个 epoch 的 box_loss、cls_loss、mAP50、mAP50-95,观察 mAP50-95 的波动幅度能判断模型是否稳定;混淆矩阵则能直观看到“船”和“落水人员”这两个类别之间有没有互相误判。
5. 避坑清单:无人机视角船只检测的五个常见问题与排查
5.1 小目标漏检严重
现象:验证集上的 mAP50 有 0.85,看起来很漂亮,但把模型放到无人机实拍视频里,稍远一点的船就完全检不到,画面里出现一个又一个漏检框。
原因:这是无人机视角的老大难。验证集的标注分布和实际飞行场景不一致,你在数据里看到的“小目标”在 1280 分辨率下可能还有 20 个像素,但实际飞高到 300 米以后,目标直接缩到 8 个像素以下。另一个原因是训练时 mosaic 增强虽然增加了尺度多样性,但小目标在缩放过程中会被严重扭曲,特征丢失后模型根本没有机会学习。
解决:第一步把 imgsz 从 1280 提升到 1536 或 1600,看显存能不能撑住;第二步检查你的数据集中小目标实例占比,如果 small 档占比低于 30%,说明不是模型问题,是数据本身缺少小目标样本,需要补充高空视角的图片或者对现有图片做切片(把一张大图切成四张训练)。第三步是部署时用 TTA(测试时增强)的 scale 选项,虽然推理慢一些,但能明显提升小目标召回率。
5.2 雾天逆光误检
现象:模型在晴朗天气下表现正常,一到有雾或者逆光的时段,把浪花、泡沫、甚至阴影识别成“船”或“落水人员”。
原因:海上搜救多半是在恶劣天气下进行的,但训练数据里大多是晴天。低照度和雾气导致的目标轮廓模糊,让模型只能捕捉到局部纹理特征,而浪花和落水人员在灰度分布、纹理复杂度上其实很像。这是典型的分辨力不足问题,不是模型“变笨了”。
解决:收集硬负样本加入训练集。具体做法是把误检图片裁出来,单独存成一个“background”目录,标注为空图片(txt 文件里没有任何标注行),加入训练数据。模型看了这些负样本之后,会学会把“看起来像但是目标特征不完整”的区域压到低置信度。另外,部署时把置信度阈值从 0.25 提高到 0.4,并且加一个尺寸过滤器,船只的长宽比通常大于 2,落水人员接近 1,可以用几何约束过滤掉明显不合理的检测框。
5.3 loss 不降或者直接变 NaN
现象:训练跑了好几个 epoch,box_loss 和 cls_loss 纹丝不动,甚至掉到 NaN。
原因:loss 不降多半是数据读取的问题,最常见的是数据集的标签和图片不对应,或者 labels 文件夹里混入了空的 txt 文件;NaN 通常是因为标注里有坐标越界的框、宽高为负的脏数据,导致计算损失时出现除零或根号下负数。
解决:先看训练日志里每一轮的图片数量,确认数据集没有读空;再写脚本检查标注文件里的所有坐标是否在 0 到 1 之间,宽高是否大于 0;最后看一下训练时保存的 train_batch0.jpg,如果标注框画的位置和物体明显对不上,那就是标签和图片的对应关系错位了,重建目录映射是最快的解决办法。如果前面都正常,检查学习率是否过高,把 lr0 从 0.01 降到 0.001 重试。
5.4 推理卡顿,帧率上不去
现象:模型在电脑上跑视频检测有 30 FPS,上了无人机的机载设备只有 3 FPS,完全没法实时用。
原因:机载设备的算力本来就弱,如果直接用 PyTorch 的 FP32 精度跑推理,单帧推理时间就会达到几百毫秒。另一个常见原因是推理时没有指定 GPU,代码默认跑在 CPU 上,帧率自然惨不忍睹。
解决:先把模型导出成 TensorRT 的 engine 格式,FP16 精度,推理速度能提升 3 到 5 倍;再检查推理代码里的 device 参数,明确指定为 0 或 1,而不是靠自动检测。如果还是达不到实时要求,考虑换 yolo11n 而不是 yolo11s,精度损失换来帧率翻倍,在搜救这种需要快速响应但不需要像素级精度的场景下是划算的。
5.5 类别不平衡,核心类别被淹没
现象:数据集的“船”有 5000 个实例,“落水人员”只有 80 个实例,训练完模型几乎检不出落水人员,输出全是船。
原因:这是搜救任务里最致命的问题。模型在训练时会倾向优化大多数类别的损失,少数类别学不到足够特征。验证集 mAP 看起来不差,那是因为船的检测太强,把整体指标拉高了。
解决:对少数类别做过采样复制,把落水人员图片重复 3 到 5 次加入训练集,让两个类别的实例数接近 1:3 以内;如果数据实锤无法增多,考虑在 loss 上做文章,给少数类别更高的 cls_loss 权重;再不济就用“二次检测”的思路——先用这个模型筛掉大部分水面区域,针对剩下的可疑区域单独跑一个人体检测模型,相当于用两个专注的模型替代一个什么都要干的模型。
6. 部署到视频推理链路:检测脚本与漏检率验证
6.1 对视频流做检测并落盘的完整脚本
把训练好的权重接到视频检测上,代码量其实很少,但有几个参数值得认真调。下面是我常用的一段推理脚本,处理无人机拍摄的视频文件:
from ultralytics import YOLO # 加载训练好的权重 model = YOLO("runs/ship_v1/weights/best.pt") # 视频推理,保存结果 results = model.predict( source="search.mp4", imgsz=1280, conf=0.25, iou=0.45, vid_stride=5, save=True, save_txt=True, project="runs/detect", name="ship_infer", ) # 打印每一帧的检测信息 for frame_idx, r in enumerate(results): if r.boxes is not None: for box in r.boxes: cls_id = int(box.cls[0]) conf = float(box.conf[0]) print(f"帧 {frame_idx * 5}: 类别 {cls_id}, 置信度 {conf:.2f}")vid_stride=5 表示每 5 帧检测一次,这是一个很重要的参数。无人机在巡航时移动速度相对慢,目标不会因为跳几帧就消失,这个设置可以把推理负载降低到原来的五分之一。save_txt=True 会把每帧的检测结果保存为 txt 文件,便于后续做数据统计。如果 detection 的框在视频里抖得很厉害,可以在 predict 调用里加上 agnostic_nms=True,强制做类别无关的 NMS,让重叠的同类框只保留一个,减少误报。
6.2 用验证集量化漏检率,评估这个方向值不值得继续投入
模型跑通之后,最重要的一步是验证它到底能不能用。我习惯的做法是:从无人机实拍视频里随机抽 30 帧,人工标注出所有目标,再用脚本跑一遍模型,对比人工标注和模型输出的差异,算漏检率和误检率。漏检率 = 模型没检出的目标数 / 人工标注目标总数,这个指标比 mAP 更直观地反映搜救任务的实际效能。
漏检率在 10% 以下,可以进入试点;10% 到 30%,需要补充小目标和恶劣天气样本继续训练;30% 以上,说明数据集和任务的差距太大,这个方向暂时不值得投入,应该先多采集目标水域的实拍数据,而不是继续调参数。我自己的习惯是每次都把漏检样本截图保存下来,定期复盘这些样本里有没有规律——比如全都是逆光、全都是船体遮挡,找到规律等于找到了下一次数据采集的优先方向。希望这套流程能帮你少走一些弯路,把精力花在真正影响搜救效率的问题上。
本文还有配套的精品资源,点击获取