ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于YOLOv11的水面垃圾检测系统实战:从数据集构建到部署优化

基于YOLOv11的水面垃圾检测系统实战:从数据集构建到部署优化 简介这份资源是一套基于YOLOv11的水面垃圾检测系统完整项目文档面向具备一定Python基础的研究人员与环保监测领域技术人员帮助其快速搭建可自动识别水面漂浮物并输出类别与位置信息的检测方案。文档围绕环境配置、数据集准备与配置文件、模型训练、ONNX模型导出、性能评估及指标可视化、GUI界面创建与完整代码整合等环节展开并附有项目特点、参考资料、未来改进方向与注意事项等模块便于读者理解整体流程与关键实现细节。资源包共1个文件为docx格式大小约41KB内容涵盖详细流程记录、关键源码样例与重要提醒。目前已有441人学习下载适合希望将计算机视觉技术落地于水面污染治理与环保监测场景的读者参考借鉴。1. 水面垃圾检测为什么值得用 YOLOv11 重做一遍去年夏天我帮一个景区做水面漂浮物巡检最初想用传统图像处理凑合结果反光、波纹、水草阴影轮番上阵阈值调一天换一个样误报率高到巡检员直接拒用。后来换成 YOLOv11 重训同样的摄像头、同样的光照漏检和误报同时压下来才意识到水面垃圾检测这件事模型选型比调参重要得多。这篇笔记就围绕「基于 YOLOv11 的水面垃圾检测系统」展开把数据怎么标、环境怎么配、小目标怎么救、推理结果怎么保存讲透。适合两类人一类是刚接触 YOLOv11、想拿一个完整场景练手的新手另一类是做河道、湖面、港口巡检需要一套能落地检测方案的工程师。整套流程我会按能复现的标准写参数和坑都标清楚。2. 水面垃圾检测的数据集怎么建才不返工水面垃圾检测的成败七成在数据三成在模型。很多人一上来就找开源数据集跑通了 demo 就以为成了结果换到自己水域直接翻车。原因很简单水面场景的域差异极大静水湖面和流动河道的反光模式完全不同南方水葫芦和北方塑料瓶的形态也差得远。所以我的习惯是先明确检测类别再决定标注策略最后才考虑要不要混入公开数据做预训练。2.1 类别定义与标注粒度水面垃圾检测常见的类别划分有两种思路。一种是按材质分塑料瓶、塑料袋、泡沫、纸屑、水草。另一种是按巡检需求分可回收漂浮物、不可回收漂浮物、疑似水草。我一般推荐第二种因为巡检员关心的是「要不要派人去打捞」而不是这玩意到底是 PET 还是 PP。类别数控制在 3 到 5 类太多会导致小类别样本不足训练时被大类别压制。标注粒度上水面垃圾有个特殊问题远处的小目标在图像里可能只有十几个像素标注框稍微松一点模型就学不到有效特征。我的做法是对小于 32×32 像素的目标标注框紧贴目标边缘不留背景对大于 64×64 的目标框可以稍微宽松保留一点上下文。这个策略在 YOLOv11 的小目标检测上效果明显后面第 4 章会展开讲。标注工具用 LabelImg 或 CVAT 都行导出 YOLO 格式的 txt。每张图的标注文件里一行代表一个目标格式是类别索引 中心x 中心y 宽 高坐标都归一化到 0 到 1。这里有个容易忽略的点如果一张图里没有目标也要保留一个空的 txt 文件否则训练时数据加载器会报错。2.2 数据采集与增强的边界采集设备建议用 1080P 以上分辨率的摄像头帧率不用太高15 到 25 帧足够。关键是采集时段要覆盖清晨逆光、正午强反光、傍晚弱光、阴天散射光。每个时段至少采 200 张总数据集控制在 2000 到 5000 张。太少模型学不动太多标注成本扛不住。数据增强方面YOLOv11 自带 mosaic、mixup、随机翻转。但水面场景有两个增强要慎用一是垂直翻转因为水面垃圾的倒影和本体有固定上下关系翻转后语义就乱了二是大角度旋转超过 15 度的旋转会让水面纹理失真。我一般只开水平翻转和 ±10 度以内的旋转mosaic 概率设 0.5 左右。# 数据集目录结构示例 # datasets/ # images/ # train/ val/ # labels/ # train/ val/ # data.yaml 内容如下 path: ./datasets train: images/train val: images/val names: 0: recyclable 1: non_recyclable 2: suspected_plant上面这个data.yaml是 YOLOv11 训练时的数据配置文件。path是数据集根目录train和val是相对路径。names里的类别顺序必须和标注时的类别索引一致否则训练出来的模型会把塑料瓶认成水草。改类别时只改names和标注文件里的索引不要动目录结构。提示标注完成后用脚本统计一下每个类别的框数量。如果某个类别少于总框数的 5%要么补采要么合并到相近类别否则训练时这个类别基本学不出来。3. YOLOv11 环境配置与训练参数怎么定环境配置是新手最容易卡住的地方。网上教程一堆但很多是复制粘贴的版本对不上就报错。我按自己装过十几台机器的经验给一套能跑通的组合。先说结论Python 3.10 加 PyTorch 2.1 以上CUDA 版本跟显卡驱动匹配ultralytics 用 pip 装最新稳定版。不要用 conda 混装容易出玄学问题。3.1 从零配一套能跑的训练环境第一步确认显卡驱动和 CUDA 版本。命令行执行nvidia-smi右上角显示的是驱动支持的最高 CUDA 版本。比如显示 12.1那你可以装 CUDA 11.8 或 12.1 的 PyTorch。我一般选比最高版本低一档的稳定性更好。第二步建虚拟环境。用 venv 就行轻量。python -m venv yolo11_env source yolo11_env/bin/activate # Windows 用 yolo11_env\Scripts\activate pip install --upgrade pip pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics这段命令先建了一个干净的 Python 虚拟环境然后装 PyTorch 和 ultralytics。--index-url指定 CUDA 11.8 的 PyTorch 源如果你显卡驱动只支持 CUDA 11.7就把 cu118 改成 cu117。装完后执行python -c import torch; print(torch.cuda.is_available())输出 True 才算成功。如果输出 False八成是 PyTorch 版本和驱动不匹配回退一档重装。第三步验证 ultralytics 能不能加载模型。执行yolo predict modelyolo11n.pt sourcehttps://ultralytics.com/images/bus.jpg会自动下载 yolo11n 权重并推理一张示例图。能跑通说明环境没问题。权重文件默认下载到当前目录后面训练自己的模型时可以用这个预训练权重做迁移学习。3.2 训练参数怎么设才不浪费显卡YOLOv11 的训练入口是yolo train关键参数有十几个但真正影响水面垃圾检测效果的我总结为五个imgsz、batch、epochs、lr0、patience。imgsz是输入图像尺寸。水面小目标多我一般设 640 起步如果显存够设 960 或 1280 对小目标更友好。但要注意imgsz必须是 32 的倍数否则 YOLOv11 的 backbone 会报维度错误。batch是批大小。8G 显存跑 640 尺寸batch 设 16 比较稳12G 显存可以设 32。如果训练时出现 CUDA out of memory先把 batch 减半不要急着换显卡。epochs是训练轮数。水面垃圾数据集一般 2000 到 5000 张我设 150 到 300 轮。配合patience参数如果 50 轮内验证集指标没提升就自动停止省时间。lr0是初始学习率。用预训练权重时设 0.01 比较合适从零训练才需要设 0.1。学习率太大会导致 loss 震荡太小则收敛慢。yolo train modelyolo11s.pt datadata.yaml epochs200 imgsz960 batch16 lr00.01 patience50 device0这条命令用 yolo11s 预训练权重在自定义数据集上训练 200 轮输入尺寸 960批大小 16初始学习率 0.0150 轮无提升则早停使用 0 号显卡。训练日志会输出每轮的 box_loss、cls_loss 和 mAP。重点看 mAP50-95这个指标比 mAP50 更严格能反映小目标的检测质量。训练完成后权重保存在runs/detect/train/weights/best.pt。这个文件就是后面推理和部署要用的。如果训练中断了可以用resumeTrue从上次的 last.pt 继续但要注意resume 会沿用之前的参数改参数得重新开一轮。注意训练前把data.yaml里的路径改成绝对路径相对路径在部分环境下会找不到文件。另外Windows 下路径用反斜杠要转义建议统一用正斜杠。4. 小目标检测翻车怎么救三个可落地的优化手段水面垃圾检测最头疼的就是小目标。远处一个塑料瓶在 1080P 图像里可能只占 20×30 像素YOLOv11 默认的 P3 特征层感受野偏大小目标特征容易被淹没。我试过几种方案有的有效有的纯属折腾下面按性价比排序。4.1 输入分辨率与特征层调整最直接的办法是提高输入分辨率。imgsz从 640 提到 960 或 1280小目标的像素面积翻倍检测率能提升 10 到 20 个百分点。代价是显存和推理时间增加。如果部署端是边缘设备算力有限就得权衡。第二个办法是调整特征层。YOLOv11 默认用 P3、P4、P5 三层做检测P3 对应 80×80 的特征图适合小目标。但如果你的小目标特别小可以尝试加一层 P2对应 160×160。不过 ultralytics 官方没有直接开放 P2 的配置需要改模型 yaml。我一般不建议新手动这个容易把预训练权重对不上。# 推理时提高分辨率并保存结果 from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest_images/, imgsz1280, conf0.25, iou0.45, saveTrue, save_txtTrue, projectinference_output, namewater_garbage )这段代码加载训练好的权重对test_images目录下的图片做推理。imgsz1280提高分辨率救小目标conf0.25是置信度阈值低于这个值的框不输出iou0.45是 NMS 的 IoU 阈值控制重叠框的合并。saveTrue保存带框的图片save_txtTrue保存检测结果的 txt 文件格式和标注文件一致方便后续做统计或二次处理。输出目录在inference_output/water_garbage。4.2 数据层面的小目标增强模型层面动不了太多就从数据下手。我常用的两个策略一是 copy-paste 增强把小目标从一张图复制到另一张图的空白水域增加小目标样本密度二是随机缩放把图像缩小后再放大模拟远距离成像。这两个策略在 ultralytics 里没有现成开关得自己写脚本做离线增强。import cv2 import random import os def random_scale_aug(img, label, scale_range(0.5, 0.8)): h, w img.shape[:2] scale random.uniform(*scale_range) new_w, new_h int(w * scale), int(h * scale) img_resized cv2.resize(img, (new_w, new_h)) # 将缩放后的图像贴回原尺寸画布模拟远距离小目标 canvas cv2.resize(img_resized, (w, h)) return canvas, label # label 需同步缩放此处省略具体坐标变换这个函数把图像先缩小再放大目标在最终图像里变得更小同时保留水面纹理。scale_range控制缩放比例0.5 到 0.8 之间比较自然。注意标注框的坐标也要同步变换否则标签和图像对不上。这个增强适合在训练前离线做把增强后的图加入训练集不要在线做否则每个 epoch 的增强结果不稳定影响收敛判断。4.3 后处理里的置信度与 NMS 调参推理阶段还有一招调低置信度阈值配合更宽松的 NMS。默认conf0.25对小目标可以降到 0.15让更多候选框进入 NMS。但这样会引入更多误报所以iou要相应调整从 0.45 降到 0.35让重叠框更容易被合并。这个策略的本质是用误报换漏检。水面巡检场景里漏检一个塑料瓶的代价比误报一个水草高所以值得。但如果是自动打捞设备误报会导致机械臂空抓那就得反过来调。参数没有绝对最优看业务容忍度。提示调完 conf 和 iou 后用验证集跑一遍画 PR 曲线看 F1 分数最高的点对应的阈值。不要凭感觉设。5. 推理结果保存与部署上线的避坑清单模型训好了推理也跑通了但离上线还有一段路。我见过太多项目卡在最后一步结果保存格式不对、部署环境缺库、批量推理内存泄漏。这一章把常见坑列出来每条按现象、原因、解决写。5.1 结果保存的格式与目录管理YOLOv11 的save_txtTrue会为每张图生成一个 txt内容格式是类别 x y w h conf坐标是归一化的。但如果你要做统计报表比如「今天检测到多少个塑料瓶」得自己解析这些 txt。我一般写个汇总脚本遍历输出目录按类别计数。另一个坑是目录覆盖。project和name参数如果不变第二次推理会覆盖第一次的结果。建议在name里加时间戳或者每次推理前手动清空输出目录。5.2 部署环境的依赖锁定训练环境能跑不代表部署环境能跑。ultralytics 依赖 opencv、numpy、pillow 等库版本不锁定换台机器就报错。我的习惯是训练完执行pip freeze requirements.txt部署时按这个文件装。如果部署端是 ARM 架构PyTorch 要换对应的 wheel不能直接用 x86 的。5.3 批量推理的内存与显存管理批量推理时如果一次性把几千张图传给predict显存会爆。正确做法是分批每批 16 到 32 张或者用streamTrue让 ultralytics 逐张处理。另外推理完的results对象如果不释放内存会持续增长。在循环里处理完一批就del results让 GC 回收。5.4 常见报错与排查现象训练时 loss 变成 NaN。原因学习率太大或数据里有脏标注。解决把lr0降到 0.001检查标注文件有没有坐标超出 0 到 1 范围的。现象推理时框的位置偏移。原因训练和推理的imgsz不一致或者图像预处理方式不同。解决确保推理的imgsz和训练时一致不要混用 letterbox 和 resize。现象mAP 很高但实际漏检严重。原因验证集和训练集分布太像过拟合了。解决重新划分验证集确保验证集包含不同时段、不同水域的图。现象保存的 txt 里类别索引对不上。原因data.yaml的names顺序和标注时不一致。解决统一用一份names标注和训练都从这里读。现象GPU 利用率低训练慢。原因batch太小或数据加载器线程数不够。解决增大batch设置workers8但注意 Windows 下workers设太大反而会卡。6. 把检测结果用起来从单帧推理到视频流统计单张图推理只是验证真正上线要处理视频流。我一般用 OpenCV 读摄像头或视频文件逐帧送给 YOLOv11再把检测结果画到帧上同时累计每类的数量。这里有个技巧不要每帧都做 NMS 和统计可以隔帧检测中间帧用跟踪算法补降低算力消耗。import cv2 from ultralytics import YOLO from collections import defaultdict model YOLO(runs/detect/train/weights/best.pt) cap cv2.VideoCapture(river.mp4) counter defaultdict(int) frame_id 0 while cap.isOpened(): ret, frame cap.read() if not ret: break frame_id 1 if frame_id % 3 ! 0: # 每 3 帧检测一次 continue results model.predict(frame, imgsz960, conf0.25, verboseFalse) for box in results[0].boxes: cls_id int(box.cls[0]) counter[cls_id] 1 x1, y1, x2, y2 map(int, box.xyxy[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow(Water Garbage Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() print(dict(counter))这段代码每 3 帧检测一次减少算力占用。counter字典累计每个类别的检测次数最后打印出来。实际部署时可以把计数结果写到数据库或推送到监控大屏。注意verboseFalse关掉 ultralytics 的日志输出否则控制台会刷屏。验证检测效果时我习惯用两个指标一是每类目标的召回率用验证集跑二是视频流里的误报率人工看 10 分钟录像数一下误报次数。召回率低于 80% 就回去补数据误报率高于 10% 就调conf和iou。最后说个血泪教训别在训练集上评估模型。我早期偷懒直接拿训练集跑 mAP结果 0.95上线后惨不忍睹。后来老老实实划 20% 做验证集指标掉到 0.78但上线后稳定。模型评估这件事没有后悔药只有老老实实分数据。希望帮到你。本文还有配套的精品资源点击获取
返回列表