
简介这份资源面向具备一定Python基础的研究人员与环保监测行业技术人员提供一套基于YOLOv11的水面垃圾检测系统完整实现方案用于自动识别与定位水面漂浮污染物可服务于水域污染治理与环保监测等场景。压缩包内共1个docx文档约41KB内容涵盖项目介绍、环境准备、数据集配置、模型训练、ONNX模型导出、性能评估与指标可视化、GUI界面搭建及完整代码整合等模块并附有参考资料、注意事项与未来改进方向。文档以流程记录与关键源码样例为主读者可据此复现从数据准备到模型部署的全过程理解多类别垃圾识别与评估指标可视化的实现思路同时获得超参数调整、过拟合规避等排错经验。目前已有441人学习下载适合希望快速上手水面垃圾检测项目、对照实操并查漏补缺的中级开发者参考。1. 水面垃圾检测为什么值得用 YOLOv11 重做一遍去年夏天帮一个景区做水面巡检对方原本靠人工划船拍照、上岸后肉眼数漂浮物一天下来两个人只能覆盖两公里河道漏检率还高得离谱。换成基于 YOLOv11 的水面垃圾检测系统之后同一段河道用无人船跑一圈二十分钟出结果塑料瓶、泡沫块、枯枝这些目标都能框出来。这不是炫技是刚需——水面垃圾检测系统要解决的核心问题就三个小目标多、反光干扰强、部署环境算力有限。YOLOv11 恰好在这三点上都有针对性改进C3k2 模块和 C2PSA 注意力机制让它在小目标上的召回比前代更稳同时模型体积还能压到边缘设备能跑的程度。这篇文章面向两类人一类是手里有水面图像数据、想训一个能用的检测模型但不知道从哪下手的工程师另一类是已经跑过 YOLO 系列、想搞清楚水面场景下参数该怎么调、坑在哪的老手。我会把数据准备、环境配置、训练调参、推理部署整条链路拆开讲每个环节都给可复现的命令和代码不跳步。整套方案包含完整的程序和数据组织方式你照着走一遍就能得到自己的水面垃圾检测模型。2. 水面垃圾数据集怎么整从原始照片到 YOLO 格式的完整链路2.1 水面垃圾的类别定义与标注边界水面垃圾检测和通用目标检测最大的区别在于类别边界模糊。一个泡了一半的塑料袋你说它是塑料还是其他垃圾一根挂着水草的树枝算枯枝还是算水草我的做法是先把类别收敛到 4 到 6 类覆盖实际巡检中最需要上报的目标塑料瓶、泡沫块、塑料袋、枯枝、水草团、其他漂浮物。类别太多会导致标注一致性下降模型学到的特征反而混乱。标注工具用 LabelImg 或 X-AnyLabeling 都行导出 YOLO 格式的 txt。这里有个血泪经验标注框不要贴太紧。水面目标边缘往往被水花或反光模糊框贴太紧会让模型学到噪声。我一般让框比目标实际边缘外扩 3 到 5 个像素小目标可以外扩 2 个像素。另外水面倒影不要标只标实际物体。倒影标注是新手最容易翻车的地方模型会把倒影当目标导致水面平静时误检率飙升。标注完成后每个图像对应一个同名 txt 文件每行格式是类别索引 中心x 中心y 宽 高坐标全部归一化到 0 到 1。检查一遍有没有越界值有的话说明标注时拖出了图像边界需要修正。2.2 数据增强策略水面场景该加什么、不该加什么YOLOv11 自带的数据增强里HSV 色调抖动、随机翻转、马赛克增强都是默认开的。但水面场景有它的特殊性不能无脑用默认配置。翻转增强要小心。水平翻转对水面垃圾是安全的因为垃圾在水面上没有固定朝向。但垂直翻转要关掉水面倒影和天空在上方垂直翻转会产生物理上不合理的图像模型会学到错误的空间先验。在data.yaml同级目录的hyp.yaml里把flipud设为 0.0。马赛克增强对水面小目标有帮助但mosaic概率不要拉到 1.0。我一般设 0.5 到 0.7太高会让小目标在拼接图里被裁切得太碎训练后期 loss 震荡。另外水面反光是高频干扰可以加一点随机亮度调整但对比度调整幅度要小否则会把反光区域压成和垃圾一样的暗斑。如果你手头数据量少于 2000 张建议额外加 Cutout 增强随机遮挡图像中一小块区域强迫模型不依赖局部特征。这个在 ultralytics 框架里可以通过自定义 augment 实现也可以离线用 albumentations 做一遍再喂进去。2.3 数据集目录结构与 data.yaml 配置YOLOv11 要求的数据集目录结构很固定我直接给一个我常用的模板water_garbage/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml的内容如下path: /home/user/water_garbage train: images/train val: images/val test: images/test names: 0: plastic_bottle 1: foam 2: plastic_bag 3: branch 4: waterweed 5: other这里有个容易忽略的点path用绝对路径不要用相对路径。ultralytics 在不同版本里对相对路径的解析基准不一样用绝对路径能避免训练时找不到数据的玄学问题。另外names的索引必须从 0 开始连续中间不能跳号否则训练时类别映射会错位。划分比例我一般按 7:2:1 分 train/val/test。如果数据量少于 1500 张test 集可以省掉用 val 集同时做验证和最终评估但要在报告里注明。划分时注意同一段视频截出来的帧不要跨集分布否则验证集精度会虚高。正确做法是按视频源或拍摄时段划分保证验证集和训练集来自不同场景。3. YOLOv11 环境配置与训练从零跑通第一个水面垃圾模型3.1 环境配置的版本锁定与常见依赖冲突ultralytics 框架更新很快不同版本之间 API 有差异。我建议锁定一个稳定版本不要盲目追新。截至我写这篇时的稳定组合是 Python 3.10 PyTorch 2.1 ultralytics 8.3.x。CUDA 版本根据你的显卡驱动来30 系卡用 CUDA 11.8 或 12.1 都行。安装命令conda create -n yolo11 python3.10 -y conda activate yolo11 pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics8.3.0 pip install opencv-python albumentations这里有个坑如果你之前装过其他版本的 ultralytics先pip uninstall ultralytics再装不要直接覆盖安装。残留的旧版本文件会导致from ultralytics import YOLO时报一些莫名其妙的 AttributeError。另一个坑是 numpy 版本ultralytics 8.3 要求 numpy2.0如果你环境里是 numpy 2.xopencv 会报错先降级。验证安装是否成功from ultralytics import YOLO model YOLO(yolo11n.pt) print(model.info())如果能看到模型结构信息输出说明环境没问题。yolo11n.pt是 nano 版本的预训练权重第一次运行会自动下载。如果下载慢可以手动从 ultralytics 的 release 页面下载后放到当前目录代码里直接写本地路径。3.2 用预训练权重做迁移训练的最小命令水面垃圾检测数据量通常不大从零训练不现实必须用 COCO 预训练权重做迁移学习。最小训练命令yolo detect train \ modelyolo11s.pt \ data/home/user/water_garbage/data.yaml \ epochs150 \ imgsz640 \ batch16 \ device0 \ projectwater_garbage_runs \ nameexp1 \ pretrainedTrue \ optimizerAdamW \ lr00.001 \ lrf0.01 \ warmup_epochs3 \ cos_lrTrue \ patience30逐项说明modelyolo11s.pt选 small 版本nano 太小欠拟合medium 以上在边缘设备跑不动s 是精度和速度的平衡点。imgsz640是默认输入尺寸水面小目标多的话可以提到 800 或 960但显存占用会明显上升8G 显存建议不超过 800。batch16根据显存调显存不够就降到 8 或 4同时把lr0按比例降一点。optimizerAdamW比 SGD 收敛快适合小数据集。cos_lrTrue开启余弦退火后期 loss 更稳。patience30是早停耐心值30 轮验证集指标不提升就停省时间。训练过程中重点看三个指标metrics/mAP50、metrics/mAP50-95和train/box_loss。mAP50 到 0.85 以上基本可用mAP50-95 到 0.55 以上算不错。如果 box_loss 一直不降检查标注有没有问题如果 mAP 震荡大把batch调大或lr0调小。3.3 小目标优化的三个关键参数水面垃圾里小目标占比很高一个远处的塑料瓶在 640 分辨率下可能只有 10 几个像素。YOLOv11 本身对小目标做了改进但参数不调还是白搭。第一个参数是imgsz。前面说了可以提到 800 或 960但更聪明的做法是用rectTrue做矩形推理保持长边不变、短边补齐减少无效填充。不过训练时rect默认就是开的推理时可以在predict里显式设rectTrue。第二个参数是anchor相关的。YOLOv11 是 anchor-free 的没有传统 anchor 调参但boxloss 的权重会影响小目标回归。在hyp.yaml里把box从默认 7.5 提到 8.5 到 9.0让小目标定位更准。同时把cls从 0.5 降到 0.3 到 0.4因为水面垃圾类别边界模糊分类 loss 权重太高会压制定位学习。第三个参数是overlap_mask和mask_ratio如果你用的是分割模型。检测模型不用管这两个。但检测模型里有个max_det参数默认 300水面垃圾密集场景可能不够推理时设到 500。3.4 训练日志怎么看loss 曲线与指标解读训练启动后runs/detect/water_garbage_runs/exp1/目录下会有results.csv和weights/文件夹。results.csv每行是一个 epoch 的指标重点看这几列train/box_loss、train/cls_loss、metrics/mAP50、metrics/mAP50-95。正常训练曲线应该是box_loss 和 cls_loss 在前 10 个 epoch 快速下降之后缓慢下降并趋于平稳mAP50 在前 20 个 epoch 快速上升之后缓慢上升。如果 box_loss 下降但 mAP 不涨说明模型在过拟合训练集检查验证集标注质量。如果 cls_loss 震荡剧烈把cls权重再降一点或者增大batch。weights/下会保存best.pt和last.pt。best.pt是验证集 mAP50 最高的权重推理用这个。last.pt是最后一个 epoch 的权重用来恢复训练。不要用last.pt做推理除非你确定最后一个 epoch 是最好的。4. 推理、部署与效果验证让模型真正跑在水面巡检设备上4.1 单张图像和视频流的推理命令训练完拿到best.pt先做单张推理验证yolo detect predict \ modelruns/detect/water_garbage_runs/exp1/weights/best.pt \ source/home/user/test_images/ \ imgsz800 \ conf0.35 \ iou0.5 \ saveTrue \ save_txtTrue \ projectinference_results \ nametest1conf0.35是置信度阈值水面场景建议设 0.3 到 0.4太低误检多太高漏检多。iou0.5是 NMS 的 IoU 阈值密集目标可以降到 0.45。save_txtTrue会把检测框坐标存成 txt方便后续统计。imgsz800和训练时保持一致不要训练用 640 推理用 1280尺度不匹配会导致精度下降。视频流推理把source换成视频文件路径或摄像头索引0。如果是 RTSP 流ultralytics 也支持但延迟会高一些建议用streamTrue做逐帧处理避免内存堆积。4.2 用 Python 脚本做批量推理与结果保存实际巡检场景需要批量处理一个文件夹里的所有图像并输出结构化结果。下面这个脚本我用了很多次直接改路径就能跑from ultralytics import YOLO import os import json from pathlib import Path model YOLO(runs/detect/water_garbage_runs/exp1/weights/best.pt) image_dir Path(/home/user/test_images) output_dir Path(/home/user/inference_output) output_dir.mkdir(parentsTrue, exist_okTrue) all_results [] for img_path in sorted(image_dir.glob(*.jpg)): results model.predict( sourcestr(img_path), imgsz800, conf0.35, iou0.5, verboseFalse ) for r in results: boxes r.boxes if boxes is None: continue for i in range(len(boxes)): cls_id int(boxes.cls[i].item()) conf float(boxes.conf[i].item()) xyxy boxes.xyxy[i].tolist() all_results.append({ image: img_path.name, class: model.names[cls_id], confidence: round(conf, 4), bbox: [round(v, 2) for v in xyxy] }) # 保存带标注的图像 r.save(filenamestr(output_dir / fannotated_{img_path.name})) with open(output_dir / detections.json, w, encodingutf-8) as f: json.dump(all_results, f, ensure_asciiFalse, indent2) print(f共处理 {len(list(image_dir.glob(*.jpg)))} 张图像检测到 {len(all_results)} 个目标)逻辑说明遍历图像目录逐张推理把每个检测框的类别、置信度、坐标存进列表最后统一写 JSON。r.save()保存带框图像方便人工复核。verboseFalse关掉每张图的详细日志批量处理时输出更干净。参数方面conf和iou根据实际场景微调如果发现漏检多就降conf误检多就升conf。4.3 边缘设备部署ONNX 导出与推理速度优化水面巡检设备通常是 Jetson 或树莓派级别的算力直接跑 PyTorch 权重太慢。导出 ONNX 再推理是常见做法yolo export modelruns/detect/water_garbage_runs/exp1/weights/best.pt formatonnx imgsz640 opset12 simplifyTrue导出后在 Jetson 上用 ONNX Runtime 或 TensorRT 推理。TensorRT 速度最快但转换麻烦ONNX Runtime 兼容性好速度也够用。实测在 Jetson Xavier NX 上yolo11s 导出 ONNX 后单帧推理约 45ms换算下来 22 FPS满足实时巡检需求。如果还要更快可以把imgsz降到 480 或 512精度会掉 2 到 3 个点但速度能翻倍。另一个优化点是量化用 FP16 推理速度提升约 30%精度几乎不掉。INT8 量化要小心水面反光区域容易产生量化噪声导致误检增加建议先用 FP16。4.4 效果验证mAP 之外还要看什么mAP 是标准指标但水面巡检场景不能只看 mAP。我一般还会看三个业务指标漏检率、误检率和单帧耗时。漏检率用测试集里漏掉的目标数除以总目标数误检率用误检框数除以总检测框数。单帧耗时在目标设备上实测不要用桌面显卡的数据糊弄自己。另外按目标尺寸分层统计 mAP。把测试集目标按面积分成小32²、中32²到96²、大96²三档分别算 mAP。水面垃圾检测的瓶颈几乎都在小目标上如果小目标 mAP 低于 0.5说明模型还没真正可用需要回去调imgsz或加小目标增强。5. 水面垃圾检测避坑清单5 个我踩过的真实坑5.1 坑一验证集精度很高实际部署一塌糊涂现象训练完 mAP50 到 0.92信心满满拿到河边跑结果漏检一大片。原因训练集和验证集来自同一段视频的相邻帧画面几乎一样模型只是记住了背景。这是数据划分的锅不是模型的锅。解决按视频源或拍摄时段划分数据集确保验证集和训练集来自不同场景。如果数据来源单一至少按时间间隔划分比如前 70% 时间段的帧做训练后 30% 做验证。5.2 坑二水面反光被当成垃圾误检率居高不下现象模型在阳光强烈的正午误检率飙升把水面反光框成泡沫块。原因训练集里正午样本太少模型没见过强反光场景把反光的高亮区域学成了泡沫特征。解决补充不同光照条件的训练样本尤其是正午强反光和傍晚弱光。如果补不了在推理时加一个后处理检测框内像素方差过低说明是均匀反光就过滤掉。这个后处理用 OpenCV 几行代码就能实现。5.3 坑三训练到一半 loss 突然变 NaN现象训练到第 60 多个 epochbox_loss 突然变成 NaN训练中断。原因学习率太高或者数据里有脏标注。脏标注包括坐标越界、宽高为 0、类别索引超范围。解决先检查标注文件用脚本扫一遍所有 txt把越界和零宽高的行删掉。然后把lr0从 0.001 降到 0.0005加warmup_epochs5。如果还 NaN把amp关掉ampFalse混合精度训练在某些显卡上会出这个问题。5.4 坑四ONNX 导出成功但推理结果和 PyTorch 不一致现象PyTorch 推理正常导出 ONNX 后用 ONNX Runtime 跑框的位置偏移明显。原因导出时imgsz和推理时不一致或者opset版本太低导致某些算子行为不同。解决导出和推理用同一个imgszopset用 12 或以上。导出后先用 ONNX Runtime 跑一张和 PyTorch 相同的图对比输出确认一致再部署。如果不一致检查预处理是否一致尤其是归一化和通道顺序。5.5 坑五模型在 Jetson 上跑不动帧率只有个位数现象桌面显卡上 60 FPS放到 Jetson 上只有 5 FPS。原因PyTorch 权重没导出 ONNX或者导出后没用 TensorRT 加速。Jetson 的 CPU 很弱纯 PyTorch 推理跑不动。解决导出 ONNX 后用 TensorRT 或 ONNX Runtime GPU 推理。另外把imgsz降到 640 或 512batch设为 1。如果还慢换 yolo11n 模型精度掉一点但速度能上来。6. 把模型训到能用的最后一公里三个进阶技巧第一个技巧是伪标签半监督训练。水面垃圾数据标注成本高可以先用手头少量标注数据训一个基础模型用它推理未标注图像把高置信度conf0.7的检测框作为伪标签加入训练集再训一轮。我试过用 500 张标注数据加 2000 张伪标签数据mAP50 比纯 500 张标注数据提升了 6 个点。注意伪标签要过滤低置信度和重叠框否则会引入噪声。第二个技巧是测试时增强TTA。推理时把图像水平翻转、多尺度缩放各跑一遍把结果做 NMS 融合。ultralytics 的predict里设augmentTrue就能开 TTA。实测 mAP50 能提升 1 到 2 个点代价是推理时间翻 3 倍。如果设备算力够、对精度要求高值得开。第三个技巧是类别权重平衡。水面垃圾数据里塑料瓶可能占 60%水草只占 5%模型会偏向多数类。在hyp.yaml里设cls_pw或者用focal_loss替代默认的 BCE loss让少数类获得更高权重。我一般先统计各类别实例数把权重设为实例数反比的平方根效果比直接反比更稳。最后说一个我自己的习惯每次训完模型不管指标多好我都会拿 20 张完全没参与训练的新图跑一遍人工数漏检和误检。指标是给论文看的这 20 张图才是给实际巡检用的。如果这 20 张里漏检超过 3 个不管 mAP 多高我都回去继续调。这个习惯帮我避免了好几次“指标漂亮、落地翻车”的尴尬。希望帮到你。本文还有配套的精品资源点击获取