ARTICLE DETAIL

建站实战干货

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

基于YOLOv8的智能垃圾桶满溢检测实战:从数据集训练到部署避坑

2026/10/1 3:56:20 拓冰建站 浏览量
基于YOLOv8的智能垃圾桶满溢检测实战:从数据集训练到部署避坑 简介基于YOLOv8的智能垃圾桶满溢检测项目面向计算机视觉与深度学习方向的毕业设计及课程设计场景提供从模型训练到可视化界面展示的完整闭环体验。资源包共8个文件压缩包仅15.91MB包含3个Python脚本、3个PyTorch模型权重和2个说明文档分别承担可视化页面设计、训练/推理调用、模型加载与README指引等功能文件体量小、结构精炼便于快速定位与部署。目前已有44人学习下载适合需要直接跑通完整项目的初学者也适合想在YOLOv8基础上做迁移改进的学生开发者。项目打包了源码、数据集、可视化界面与部署说明运行后可输出混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果与标签分布图可直接用于答辩评审或课程效果展示。1. 基于YOLOv8的智能垃圾桶满溢检测它到底解决谁的什么痛点楼道垃圾桶堆到冒尖、园区分类点满了没人清、食堂后厨的桶盖上堆出一个小山包——这种场景靠人定时巡检漏掉一次就溢到地上。装超声波传感器要改每个桶、供电走线全是活而视觉方案只需要一个摄像头对着桶区让YOLOv8在画面里框出“满溢”的位置把框接上提示音或监控页面闪烁标记就行。标题里这套打包方案就是干这件事数据集负责教会模型认垃圾特征训练脚本负责产出权重可视化界面让非技术人员看得见结果部署教程负责把环境从零搭起来。它适合要做毕设、课程设计、又想短时间拿出可演示成果的人。实际最容易劝退的不是训练本身而是从数据到界面这条链路上的细节后面几章会把这些坑拆开讲。2. 为什么是YOLOv8网络结构、anchor-free 与算力选型YOLOv8不是唯一能做满溢检测的模型但它是当前“效果、上手难度、生态”三者里最平衡的选择。这一章先说清楚它凭什么适合这个任务再讲网络结构里哪几层真正影响垃圾桶这种目标的检测结果最后落到硬件选型——毕竟很多人的毕设机器只有一块 GTX1660Ti 甚至纯 CPU。2.1 满溢检测为什么不能退化成图像分类不少初版方案把满溢检测当成二分类问题输入一张图输出“满了”或“没满”。真实场景里这基本不能用。摄像头视角固定时画面里可能并排摆着三四个桶第一个空着、第三个已经冒尖分类网络只能给整张图打一个标签既不知道是哪一只桶满了也没法画框。目标检测输出的边界框可以直接对应到“第几个桶”这才是现场需要的粒度。YOLOv8 对满溢这种中大型目标有两个结构上的优势。一是 anchor-free 回归不再像 YOLOv5 那样依赖预设锚框尺寸垃圾桶在画面里忽远忽近、可大可小它都能直接回归出框二是解耦检测头decoupled head分类和回归分成两个分支避免这两个任务在训练时相互干扰。直观感受就是相同数据集下YOLOv8 的边界框比前代模型更贴垃圾袋轮廓漏检也少一点。2.2 YOLOv8网络结构里值得看的那三层YOLOv8网络结构图一开始看会眼花真正影响满溢检测的只有三块。Backbone 用 CSPDarknet 变体逐层提取特征垃圾桶的袋装垃圾、纸箱、堆叠物在这种特征空间里差异很大纹理乱不乱、轮廓规则不规则都是在 Backbone 阶段分出来的。Neck 层用类似 PAN-FPN 的结构把深层语义信息传回浅层好处是“袋口探出来一截”这种局部凸起也能被感知到而不是只认整体轮廓。输出端在 P3、P4、P5 三个尺度上分别预测P3 偏向小目标P5 偏向大目标。满溢垃圾袋在监控画面里通常属于中大型目标真正出问题的反而是堆得太高、袋口褶皱严重、轮廓严重变形的极端形态。这种要靠训练数据覆盖不是调结构能解决的。所以选模型型号时最重要的结论是别一上来就用 YOLOv8x参数翻倍、推理变慢对满溢检测这种目标尺度简单的任务收益很小。型号参数量约CPU 单帧 640 推理GTX1660Ti 推理建议场景YOLOv8n3.2M80~120ms5~10msCPU 部署、树莓派、跳帧监控YOLOv8s11.2M300~400ms10~20ms可视化界面主流选择YOLOv8m25.9M600ms 以上20~30ms刷 mAP 用不建议实时2.3 算力评估CPU 能不能跑、GTX1660Ti 跑什么“Ubuntu20.04 搭建 YOLOv8 环境 CPU 版本”是很多人搜过的词说明 CPU 跑 YOLOv8 是刚需。我的结论是CPU 能跑但别跑 s 以上的模型。用 YOLOv8n 在 1280x720 的监控画面上做 640x640 推理i5 十二代大概 80~120ms 一帧如果只做 1 秒推 2~3 帧的巡检CPU 完全扛得住。如果一定要在 CPU 上跑 s 模型先把模型导出成 OpenVINO 格式推理速度能快一倍左右别直接拿 PyTorch 权重硬跑。有 GTX1660Ti 的同学完全可以跑 s 模型显存 6G 训练 640 分辨率够用。推理端 10~20ms 一帧做实时视频流毫无压力。考虑过 RK3588 这类板子的话流程会是“训练 PyTorch 权重 → 导出 ONNX → 转 RKNN → 板端推理”n 模型量化后跑得很轻松s 模型也能跑但延迟会高一些。满溢检测有个天然优势垃圾桶不是几秒内突然满的哪怕 2 秒才推一帧也不会漏掉状态变化所以算力焦虑在这个任务上被大幅缓解了。3. 从采集到训练把场景做成 YOLOv8 能吃的数据集YOLOv8训练自己的数据集听起来是启动命令就行但实际上决定模型能不能用的是标注和划分这两步。这一章从采集讲起把 Labelme 标注、转 YOLO txt、划分数据集、增强策略四件事串起来每一步给可复制的脚本和参数说明。3.1 用 Labelme 标注并转成 YOLO 格式先拍素材。拿手机或监控截图覆盖白天、傍晚、夜间补光三种光线每只桶至少拍“空桶、半满、堆满”三个状态总数 400~600 张够用。不要只拍满溢的桶否则模型会学会“见桶就报警”。标注用 Labelme画矩形框或多边形都行推荐矩形框速度快且满溢目标轮廓本身不规则多边形标注后期转回来也是近似框。类别建议只设两个overflow满溢和normal未满。有人会把桶身、桶盖、垃圾袋分开标对纯满溢检测是多余的反而增加标注量。Labelme 存的是 JSON 文件坐标是像素值YOLO 需要的是归一化的中心点坐标和宽高。转格式脚本如下# convert_labelme_to_yolo.py import json import glob import os from PIL import Image CLASS_MAP {overflow: 0, normal: 1} def convert(json_path): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_path os.path.join(os.path.dirname(json_path), data[imagePath]) img Image.open(img_path) w, h img.size lines [] for shape in data[shapes]: label shape[label] if label not in CLASS_MAP: continue pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] xmin, xmax min(xs), max(xs) ymin, ymax min(ys), max(ys) x_center (xmin xmax) / 2.0 / w y_center (ymin ymax) / 2.0 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h lines.append(f{CLASS_MAP[label]} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) txt_path json_path.replace(.json, .txt) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) for json_path in glob.glob(label_json/*.json): convert(json_path) print(fconverted: {json_path})这段脚本的核心逻辑是读取 Labelme 导出的 JSON → 从shapes里拿到每个标注框的多边形点集 → 取所有点的最小外接矩形 → 转换成 YOLO 要求的类别 x_center y_center width height格式 → 写入同名 txt。三个参数值得注意。CLASS_MAP控制类别名到数字 ID 的映射YOLO 训练时只认数字classes.txt 里的顺序必须和这里一致。Image.open().size取的是原图宽高如果训练前做过缩放或裁剪txt 里的坐标会全部错位。bbox的宽高做了归一化YOLO 格式里所有值都在 0~1 之间检查 txt 里出现大于 1 的值说明原图尺寸取错了。转换后检查一下打开一张图和它的 txt把x_center * w - box_w * w / 2还原成左上角坐标画出来看框的位置对不对这一步能省很多训练后才发现标注错位的后悔药。3.2 数据划分train、val、test 别放在同一个文件夹里划分脚本看似简单最大的坑是漏掉同名 txt。严格按 7:2:1 划分并且图片和标注文件必须一起移动# split_dataset.py import random import shutil from pathlib import Path random.seed(42) images list(Path(images).glob(*.jpg)) random.shuffle(images) n len(images) train_cut int(n * 0.7) val_cut int(n * 0.9) splits { train: images[:train_cut], val: images[train_cut:val_cut], test: images[val_cut:], } for split_name, items in splits.items(): img_out Path(datasets/trash_detect) / split_name / images label_out Path(datasets/trash_detect) / split_name / labels img_out.mkdir(parentsTrue, exist_okTrue) label_out.mkdir(parentsTrue, exist_okTrue) for img in items: shutil.copy(img, img_out / img.name) txt img.with_suffix(.txt) if txt.exists(): shutil.copy(txt, label_out / txt.name) else: print(fmissing label: {img.name})random.seed(42)保证每次运行划分结果一致你换机器重新跑也不会导致训练集和验证集发生变化。img.with_suffix(.txt)是找同名标注文件的关键如果当时转格式时改了后缀这里就要同步改。missing label会打印出漏标的图这一步检查完再进训练否则模型会在缺标注的图上学到错误特征。划分完还要写一个 data.yaml 给 YOLOv8 用path: datasets/trash_detect train: train/images val: val/images test: test/images names: 0: overflow 1: normal注意path建议写相对路径不要写/home/xxx这种绝对路径换机器部署训练时不用改配置。names顺序必须和 3.1 里的CLASS_MAP一致顺序反了模型训练和推理时的类别就全反了。3.3 数据增强参数哪些对满溢检测真的有用YOLOv8 自带的增强参数都在训练命令里常见配置是这样跑的yolo detect train \ datadatasets/trash_detect/data.yaml \ modelyolov8s.pt \ epochs80 \ imgsz640 \ batch16 \ mosaic0.5 \ hsv_h0.015 \ hsv_s0.4 \ hsv_v0.4 \ fliplr0.5 \ patience20这些参数对满溢检测的影响要分开看。mosaic0.5是四张图拼一张训练对中大型目标帮助有限垃圾桶本身够大不需要靠 mosaic 把小目标拼接出来反而会让模型学到“画面角落切一半的桶”这种假样本我习惯压到 0.5 以下。hsv_h/s/v微调能模拟不同光线条件垃圾桶场景白天黄昏差异大这些值得保留但幅度别拉太满否则垃圾袋颜色会被扭曲成奇怪的色调。fliplr0.5水平翻转对大多数场景有效但如果你的垃圾桶上有明显文字标识或方向性图案翻转后模型会混淆。另一个容易忽略的是类别不均衡。满溢样本如果只占 20%模型天然偏向预测 normalval 的 mAP 看着还行实际现场的满溢桶全漏掉。解决方法是训练前先数一下两个类别的占比满溢明显少时把满溢样本在数据里复制两份即可不用去改损失函数权重。还有一种情况是标注框太大把整个桶连同旁边的地面都框进去了模型学到的特征是“一片区域里有桶就有满溢”推理时把正常桶也框出来标注时满溢框尽量贴垃圾袋边缘这一点比任何增强参数都重要。4. 满溢检测避坑记录训练翻车与部署黑匣子排查跑通一个 demo 容易跑出一个在真实场景不改判的模型难。下面几条都是做这个方向会真实踩到的坑按现象、原因、解决的顺序写遇到可以直接对照。4.1 训练期第一个坑loss 一直降val 就是不涨现象train 的 loss 一路向下训练曲线很漂亮但 val 的 mAP0.5 卡在 0.6 左右上不去甚至还在某个 epoch 开始掉头。原因数据集量太少模型容量过大典型的过拟合。三四百张图训练 YOLOv8s 跑 150 个 epoch模型有能力把训练集背下来验证集上泛化就会停滞。解决先看训练完后生成的results.png确认 loss 曲线在哪个 epoch 开始分叉。再把 epoch 降到 80 以内加上patience20让早停机制起作用当 val loss 连续 20 个 epoch 不下降就自动停止。最后检查一下是不是 augment 参数开太猛mosaic 太高会导致一半训练样本是拼接图验证时永远见不到这种分布。注意patience不是越大越好设 20 左右足够。设太大会在过拟合之后还继续跑白白浪费时间。4.2 训练期第二个坑行人、纸箱被识别成满溢现象验证集效果不错但拿到现场一跑人从桶旁边走过被框成 overflow背景里堆的快递纸箱也被框出来。原因训练集里缺少“没有满溢但是有杂物干扰”的负样本。模型学到的是“杂乱的袋装轮廓”人的衣服堆叠、纸箱的纹理同样符合这个特征于是误判。解决专门去采集没有满溢但有人、杂物、树影的帧标成 normal 加入训练集。这种“难负样本”有时候比多拍几十张满溢图更管用。另外满溢的标注框要紧贴垃圾袋边缘不要把桶身和周围地面都框进去框内背景越少模型越容易聚焦在真正的“满溢状态”上。4.3 部署期第一个坑界面开着没框命令行跑却有结果现象可视化界面打开摄像头后一直没框程序也没报错但同样的权重在终端跑一张测试图有正常结果。原因界面代码和推理代码用的是两个模型文件或者界面推理时把 conf 设成了 0.9 这种过高的阈值。满溢目标不像行人那么明显垃圾袋轮廓在低光下置信度通常在 0.4~0.7 之间阈值设 0.9 等于过滤掉一切。解决在界面里把conf、iou、imgsz三个值暴露成可调节的参数先调到conf0.3看能不能出框。这类黑匣子问题我用排除法先用一张固定的满溢图测模型单独跑再测界面里同一个模型跑最后测界面里跑同一张图基本能定位是权重路径问题还是阈值问题。别一上来就怀疑代码逻辑路径写死和阈值设死是最常见的两种翻车。4.4 部署期第二个坑中文路径把所有东西坑死现象数据集放在“桌面\垃圾桶满溢”目录训练时报错找不到图片训练完了 UI 加载权重直接崩溃。原因Windows 下中文路径和相对路径混用OpenCV 和 PyTorch 对这些字符的处理不一致训练时可能侥幸通过部署时换了一台机器就炸。解决整个项目放到纯英文路径比如D:/trash_project/数据集、权重、界面代码都在这个目录下。模型权重本身命名也保持英文不用“最佳模型.pt”这种文件名。这个坑在打包给毕设导师演示时最容易出现换一台电脑就炸提前规避掉。4.5 一个关于 CPU 推理的真相很多人跑通训练后发现自己笔记本 CPU 推理 s 模型一帧要 400~600ms以为是代码问题。这个延迟对满溢检测其实是能接受的垃圾不会在 1 秒内满出来2 秒推一帧完全不会漏状态。如果非要更快两个办法换 n 模型重新训练或者导出 OpenVINO 格式。别在代码层面瞎优化换模型和换推理后端比调线程快得多。GTX1660Ti 跑 s 模型大概 15ms 一帧视频监控场景下完全够用不用再往上加硬件。5. 交付前做一遍用跳帧推理脚本验证报警时效训练完的模型不能只在测试集上自嗨把它接上视频流跑一轮看的是“满溢→报警”这条链路能不能闭环。满溢检测的画面变化慢不需要每帧推理跳帧能省下一大半算力。# video_detect_demo.py import cv2 import time from ultralytics import YOLO model YOLO(best.pt) # 第3章训练出来的权重 cap cv2.VideoCapture(0) # 换成视频文件路径也行 frame_id 0 alert_count 0 last_alert_time 0.0 ALERT_FRAMES 3 # 连续 N 帧判定满溢才报警 COOLDOWN 5.0 # 报警冷却时间单位秒 FRAME_SKIP 25 # 每 25 帧推理一次 while True: ret, frame cap.read() if not ret: break frame_id 1 if frame_id % FRAME_SKIP ! 0: continue results model(frame, conf0.35, imgsz640, verboseFalse) has_overflow False for r in results: for box in r.boxes: if int(box.cls) 0: # 0 对应 overflow has_overflow True x1, y1, x2, y2 map(int, box.xyxy[0]) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, foverflow {box.conf:.2f}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) if has_overflow: alert_count 1 else: alert_count 0 now time.time() if alert_count ALERT_FRAMES and now - last_alert_time COOLDOWN: print(ALERT: trash bin overflow detected) last_alert_time now cv2.imshow(trash_detect, frame) if cv2.waitKey(1) 0xFF ord(q): breakFRAME_SKIP25的意思是每 25 帧取一帧推理约等于每秒抽 1~2 帧满溢变化慢这个频率足够。ALERT_FRAMES3连续 3 帧都判定 overflow 才报警避免单帧误检触发。COOLDOWN5是报警冷却防止同一状态重复报警刷屏。conf0.35比训练时的默认 0.25 略高兼顾召回和误报。验证时如果发现模型在某个垃圾桶上反复误报把那一帧的画面存下来补标成 normal 再训练一轮如果发现漏报调低 conf 到 0.3。这个“采集坏例、补标、重训”的循环就是满溢检测模型往后越来越准的唯一路径。我自己的习惯是最后在界面里把当前检测的类别、置信度、帧号都打印出来就像脚本里overflow 0.87这种输出现场调阈值时能直接看到数值变化比只看框好用得多。有一次就是因为训练时开了全量 mosaic 忘了关验证和训练分布不一致现场几乎不出框后来统一成mosaic0.5才恢复正常。希望这篇写下来能让你少走几段弯路。本文还有配套的精品资源点击获取