做视觉识别项目这些年,我越来越确认一个判断:真正决定一个模型能不能用的,往往不是算法本体的新奇程度,而是数据、环境和工程细节。最近复盘了一个基于深度学习YOLOv8的可回收塑料识别分类检测系统,从数据集构建、模型训练到UI界面封装,完整走了一遍工程化流程。整个过程踩了不少坑,但也沉淀出一套可以复用的方法。这篇文章就把这个项目的完整思路、核心代码逻辑和个人经验一次说清楚,给准备做垃圾分类、目标检测相关项目的朋友一个参考。
这个项目解决的实际问题很直接:在回收环节,人工分拣可回收塑料的效率低、主观性强,不同塑料材质(瓶、盒、袋、杯)外观相似度高,单靠人眼很难快速准确归类。用深度学习模型对摄像头画面或现场照片做实时识别,把塑料按常见材质类别区分出来,同时给出置信度和数量统计,能大幅减轻分拣和质检环节的负担。它适合两类人参考:一类是正在做毕业设计或课程项目的学生,另一类是想把目标检测技术落地到环保场景的开发者。
1. 项目全貌梳理:这个系统到底要解决什么问题
1.1 从需求谈起:为什么需要一套独立的塑料识别系统
很多人刚接触垃圾分类项目时,第一个反应是“直接用通用的目标检测模型不就行了”。确实,通用模型能检测出瓶子、盒子、袋子,但回收场景的需求远比“认出物体是什么”更细。塑料回收强调的是材质类别的区分:PET水瓶、HDPE洗洁精瓶、PP外卖盒、PS泡沫箱,外观上可能看起来差不多,但回收处理路径完全不同。如果模型只能告诉你“这里有一个瓶子”,那它在实际分拣产线上一点用都没有。
另一个痛点在于检测环境的复杂性。回收站的图片里往往存在大量重叠、遮挡、反光,塑料本身的形变又很强(压扁的瓶子、揉皱的袋子),这些都对模型的鲁棒性提出了很高要求。因此这个项目从一开始就不是“套个YOLOv8跑通demo”就结束的事,而是要构建一个能区分材质、能在真实环境里稳定推理、同时具备基础操作界面的完整小系统。
1.2 方案选型:为什么锁定YOLOv8而不是其他目标检测算法
选YOLOv8不是因为它是最新,而是因为它足够“顺手”。关于YOLOv5、YOLOv7、YOLOv8这几个主流版本,我做了对比:
| 版本 | 训练流程复杂度 | 部署灵活性 | 社区活跃度 | 我的推荐场景 |
|---|---|---|---|---|
| YOLOv5 | 低 | 中 | 高但官方维护放缓 | 老项目维护、已有历史代码 |
| YOLOv7 | 中 | 中 | 中 | 追求极致精度且有调优经验 |
| YOLOv8 | 极低 | 高(ONNX/TFLite等) | 目前最高 | 新项目首选,特别是带UI工程 |
YOLOv8的优势集中在两点。第一,官方在Ultralytics这个框架里把训练、验证、推理、导出整个流程都收敛成了几行命令,对数据集的格式要求也比之前更宽松,省去了大量暗坑。第二,它的Anchor-Free检测头和C2f结构在中小数据集上表现稳定,不像某些复杂模型需要大量算力才能跑出效果。对于可回收塑料这种类别多、样本量不大且依赖细节区分的场景,YOLOv8的n/s/m系列模型完全够用。
当然,如果你后续要部署到移动端或者嵌入式设备,YOLOv8的导出链路会更顺滑。这一点在项目规划时就值得提前考虑,避免模型训到一半才发现没法转换。
2. 数据集准备:决定模型效果的第一道关卡
2.1 塑料类别体系:先定标准再动手采集
很多新手做目标检测项目,上来就标注,结果训练完才发现类别定义混乱:同一个物体在不同图片里被标成不同的类,或者不同物体被标成同一类。这个问题在塑料识别上尤其严重。我在这个项目里先做了一件事:明确类别体系。
可回收塑料通常按树脂种类来分,我采用的是回收行业最常见的七类划分:PET(聚酯饮料瓶)、HDPE(高密度聚乙烯,常见于洗护用品瓶)、PVC(聚氯乙烯,管道和部分包装)、LDPE(低密度聚乙烯,保鲜膜和塑料袋)、PP(聚丙烯,外卖盒和部分瓶盖)、PS(聚苯乙烯,泡沫箱和一次性餐盒),以及一个“其他塑料”兜底类别。
这样划分的好处是,它直接对应回收处理环节的实际需求,模型输出的每一类都有明确的去向。实际操作中,还需要注意类间的相似性,比如透明PET水瓶和透明HDPE瓶在外观上很难区分,差别主要在瓶底标志和材质质感。针对这类难点,我给每一类都单独建了一个子文件夹,标注前先人工过一遍,把明显错误的样本剔除掉,避免把噪声带进模型。
2.2 采集与标注:格式转换与数据清洗
数据采集要尽量还原真实场景。如果只是网上找些干净背景的商品图,训练出来的模型一到现场就基本失效。我用手机分别在室内灯光、自然光、逆光、夜间闪光灯等条件下拍摄塑料单体与堆叠场景,同时收集了一批回收站实拍照片。整个过程采集了大约4200张有效图片,确保每类样本量不低于300张,这算是一个小而稳的起步规模。
标注阶段使用LabelImg,导出的是Pascal VOC格式的XML文件。但YOLOv8需要的是YOLO格式的TXT标注文件,并且类别要从0开始编号。所以这里有一个必做的转换环节。坐标归一化逻辑是:将边界框的x_center、y_center、width、height全部除以图片的宽高,文件里每一行对应一个目标:类别索引 + 归一化后的中心点坐标和宽高。
import os, xml.etree.ElementTree as ET from PIL import Image def convert_xml_to_yolo(xml_path, out_dir, class_names): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find("size").find("width").text) img_h = int(root.find("size").find("height").text) lines = [] for obj in root.findall("object"): name = obj.find("name").text if name not in class_names: continue cls_id = class_names.index(name) box = obj.find("bndbox") x_min = float(box.find("xmin").text) y_min = float(box.find("ymin").text) x_max = float(box.find("xmax").text) y_max = float(box.find("ymax").text) x_center = (x_min + x_max) / 2.0 / img_w y_center = (y_min + y_max) / 2.0 / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") txt_name = os.path.splitext(os.path.basename(xml_path))[0] + ".txt" with open(os.path.join(out_dir, txt_name), "w") as f: f.write("\n".join(lines)) class_names = ["PET", "HDPE", "PVC", "LDPE", "PP", "PS", "Other"]这段转换脚本里面有两个容易被忽略的细节。第一,类别顺序一旦写入类别文件后就不要随意改动,否则之前转换好的标注全部错位。第二,标注框的边界不能超出图像范围,转换前最好检查一遍x_max是否大于图片宽度、y_max是否大于图片高度,出现越界要整体外扩或者舍弃该框,否则训练时报错找半天都找不到原因。
2.3 数据增强与样本划分:训练稳定的基础
数据集规模在深度学习项目里从来都是越大越好,但对大部分个人开发者来说,能凑到几千张图已经是极限。数据增强就是在这个规模下提升模型泛化能力的关键手段。YOLOv8在训练时会自动应用Mosaic增强、随机仿射变换、HSV色彩扰动等策略,这大大省去了手动做增强的工作。但我仍建议额外做一些针对塑料特点的增强:轻度高斯模糊模拟运动场景中的脱焦,随机亮度和对比度调整模拟不同光照,以及多次按小角度旋转模拟相机角度变化。
训练集与验证集的划分也有讲究。如果图片按原始文件夹顺序简单切分,很容易出现同一场景的连续帧同时出现在训练集和验证集里,模型在验证集上看起来准确率很高,一到真实场景立刻打回原形。我按拍摄批次来切分,保证训练集和验证集来自不同批次的光照条件,这样评估结果才有参考性。最终划分比例约8:2,训练集3360张,验证集840张。
3. 模型训练与调优:让精度曲线一步步收敛
3.1 环境与配置版本匹配
训练环境是整个项目里最容易劝退新手的一环。我的建议是先用CPU把全流程跑通,哪怕训练慢一点,至少能确认代码链路没问题,再去折腾GPU加速。这个项目需要的基础组件包括:Python 3.9或3.10、合适版本的PyTorch、Ultralytics框架包、OpenCV等。CUDA版本和PyTorch必须匹配,我自己实测过几种组合,最省心的路径是先安装对应CUDA版本的PyTorch官方包,再安装Ultralytics,让框架自动检测当前设备是否可用。
装好环境后,训练数据还需要一个data.yaml配置文件。这里面的内容很关键,直接决定模型去哪里找训练数据、数据里有哪些类别。一个典型的配置如下:
path: D:/plastic_project/dataset train: images/train val: images/val nc: 7 names: ['PET', 'HDPE', 'PVC', 'LDPE', 'PP', 'PS', 'Other']注意path路径如果写错,训练时模型会一直报错找不到图片。为了避免这种尴尬,我在运行前先用Python脚本检查一遍标注文件和图片文件是否一一对应,确认没有孤立的TXT文件或者缺失的TXT文件再开始训练。
3.2 训练参数调整与优化过程
YOLOv8提供了从n到x的多种规格模型,我最初用了YOLOv8n,迭代较快,训练一次只需要约40分钟。但跑了50轮之后发现,对于塑料这种材质纹理本身就偏弱的类别,n模型的特征提取能力不足,对HDPE和LDPE这种薄壁塑料的区分度不好。于是换成YOLOv8s,在精度上有了明显提升。
训练指令本身不复杂:
yolo train data=data.yaml model=yolov8s.pt epochs=120 imgsz=640 batch=16 device=0 patience=20几个参数的取舍逻辑如下。epochs我设到120,配合patience=20早停机制,如果验证集指标连续20轮不提升就自动停止,既省时间又防止过拟合。imgsz=640是精度与显存开销的平衡点,在1080p源图上缩放到640像素,对塑料瓶这类大目标完全够用,对小物体可以在部署阶段用更高分辨率测试。batch=16在个人电脑上属于相对安全的值,如果显存不够就降到8,不要硬扛,否则训练中途OOM断掉更浪费时间。
训练过程中我重点关注几个曲线的变化。正常情况下train/loss和val/loss都应该稳步下降,如果train_loss下降但val_loss回升,基本可以判定过拟合。我这次训练在第80轮左右遇到过val_loss轻微回升的情况,由于早停机制介入,在第100轮时模型自动停止,最终得到一组相对稳定的权重。
3.3 评估指标与模型导出
训练结束后,直接用框架自带的结果就可以检验效果。重点关注三个数字:mAP50、mAP50-95和运行帧率。mAP50衡量的是IOU阈值0.5下的平均精度,适合快速判断模型整体是否可用。mAP50-95更严格,反映模型在不同定位精度下的综合表现,对实际部署更有参考意义。我训练的s模型最终mAP50约达到0.89,mAP50-95约0.67,在验证集上的表现符合预期。
模型导出同样是一行命令的事:
yolo export model=best.pt format=onnx imgsz=640导出ONNX格式的意义在于,UI界面端可以用ONNX Runtime来做推理,部署时无需完整安装深度学习训练框架,体积和依赖都大幅减小。如果后续要部署到手机端,还可以继续导出TFLite格式。实测下来,同样的s模型在CPU推理单张640像素图片耗时约110毫秒,GPU约10到20毫秒,实时性可以接受。
4. UI界面开发:从命令行到可视化落地
4.1 界面方案选择:快速开发与深度定制的取舍
训练好模型只是第一步,对一个完整项目来说,使用者不可能打开命令行去敲推理指令。这里需要一套界面,提供图形化的操作入口。我在方案选型时对比了两种路线:Gradio和PyQt5。Gradio是纯Python的Web界面库,几十行代码就能做一个浏览器交互页面,适合快速演示和产品原型。PyQt5则是桌面应用方案,界面可控性更强,适合做带摄像头的桌面工具,缺点是需要处理界面线程与推理线程的同步问题。
考虑到这个系统需要支持摄像头实时检测,且后续可能接入分拣产线,我最终选择PyQt5,把按钮、图像显示区域、检测信息列表放在同一个窗口里。界面虽不华丽,但胜在逻辑清晰、可扩展性强。如果你只是想在短时间内展示成果,Gradio方案同样推荐,我已经把两种代码都保留在项目里,方便切换。
4.2 核心功能模块:图片、视频与摄像头检测
UI界面围绕三种输入模式来设计:本地图片检测、本地视频检测、摄像头实时检测。三种模式共用同一个推理核心,只是数据来源不同。推理核心的逻辑很直接:
from ultralytics import YOLO model = YOLO("best.pt") def run_inference(source, conf_thres=0.5): results = model.predict(source, conf=conf_thres, verbose=False) return results摄像头实时检测是其中最容易出问题的环节。如果直接在UI主线程里做推理,画面会卡得让人崩溃。正确做法是把摄像头采集和推理放进单独的工作线程,通过信号把结果传回界面线程刷新显示。我这里用一个QThread作为摄像头工作线程,每读取一帧就调用一次predict,然后通过自定义信号把绘制好检测框的画面发送到界面显示。
还需要注意摄像头的设备号选择。在同一台机器上接入多个摄像头时,设备号可能是0、1、2,我在界面上加了设备号选择控件,让用户尝试不同设备号。实测中,部分笔记本电脑自带的摄像头设备号是1而不是0,如果没有这个选项,用户连画面都看不到。
4.3 结果统计与生产接入
检测结果不能只是画个框就结束。在实际使用中,操作人员最关心的往往是“当前画面里有多少个PET瓶、多少个PP盒”,以及这些目标的总数。因此界面右侧我加了一个统计面板,实时罗列各类别的检测数量,并显示每个目标框的类别、置信度和坐标。检测完成后,结果可以一键导出为CSV文件,或者把标注好检测框的图片保存到本地,作为现场巡检的留档数据。
如果需要接入自动分拣设备,这个界面的结果统计稍作改动就能输出为串口指令或TCP消息,根据类别ID触发不同的分拣阀动作。我在设计时已经预留了这样一个接口函数,后续接硬件时不需要改动模型和检测逻辑,只需要在接口函数里加上控制指令即可。这一点对于希望把demo变成真实产线应用的朋友来说,能省掉不少返工成本。
5. 常见问题与排查技巧实录
5.1 环境类问题与处理
在这个项目里遇到最多的环境类问题是设备不可用和依赖冲突。比如环境装好后,PyTorch始终识别不到GPU,跑训练代码时设备显示CPU。排查思路很简单,先用一段基础命令确认CUDA环境是否正常:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"如果输出里cuda.is_available()为False,问题基本出现在PyTorch版本与CUDA驱动不匹配。此时不要急着重装CUDA,先检查驱动是否满足要求,再对照PyTorch官方安装命令重装对应版本。这类问题在整个项目周期里出现过两次,一次是驱动版本过低,一次是环境里同时存在CPU版和GPU版PyTorch导致加载错乱。
另一个高频问题出现在labelme或LabelImg标注生成的JSON/XML文件与YOLO格式转换之间。我遇到过标注框坐标为负值的情况,这在图片边缘目标被截断时经常发生。转换脚本如果没有做坐标裁剪,训练时会直接报错或者出现异常的loss波动。建议在转换脚本里加一句坐标截断逻辑,把小于0的坐标归零,大于图片宽高的坐标裁剪到边界。
5.2 识别效果类问题与处理
模型在验证集上表现不错,但拿到现场拍一张照片就误检漏检,这类问题我遇得不少。首当其冲的原因是现场光照条件与训练集差异过大。比如室内黄光下塑料瓶壁反光严重,模型就可能把高光区域误判为其他类别。我的处理方法是给训练集补充一批黄光、逆光条件的图片,并适度提高HSV色彩增强的强度。如果你不方便补拍,更快的临时方案是调高置信度阈值到0.6以上,虽然会漏掉少量目标,但能显著降低误报率。
类别混淆问题也比较典型,尤其是HDPE和LDPE这类质地接近的薄塑料,表象都是半透明的软质片材。这种混淆不能只靠模型调参解决,需要回到数据层面,找到两种材质比较有区分度的细节部位单独放大标注,比如塑料瓶的封口纹路和底部结构,让模型有更明确的分类依据。
5.3 常见问题速查表
| 现象 | 可能原因 | 处理建议 |
|---|---|---|
| 训练时数据加载失败 | data.yaml路径错误 | 使用绝对路径并检查目录结构 |
| loss持续不下降 | 学习率过大或数据噪声高 | 调低初始学习率,清洗标注数据 |
| mAP较低且类间混淆 | 类别样本不平衡 | 增加少量类别样本或做针对性增强 |
| 摄像头画面卡顿 | 在主线程进行推理 | 用QThread把推理移出UI主线程 |
| 检测框位置偏移 | 标注坐标归一化错误 | 检查XML转TXT的宽高计算方式 |
| 模型单个目标重复框选 | NMS参数不理想 | 调整非极大值抑制的IOU阈值 |
5.4 一点通用工程建议
如果你正打算照着这个项目做一遍,我最后给你几个非常实用的建议。第一,别在数据集上省时间,模型精度不够时先回头检查数据,再考虑换更大的预训练权重。第二,训练参数不要盲目加大epoch和batch,显存不够时显卡会直接报错,先把batch调小,确认能稳定运行再逐步提升。第三,UI界面一定独立成模块,哪怕当前只是命令行工具,也把推理函数封装好,后续加界面时能少改很多代码。第四,所有的模型权重、训练日志、标注脚本都归档在固定目录结构中,训练到一半时文件找不到了,那种体验真的会让人崩溃。
做完整个流程,我最大的体会是:目标检测项目最花时间的部分其实不是在调模型,而是在整理数据、处理环境、完善界面这些工程细节上。YOLOv8的性能下限已经很高,真正让项目有区分度的,是你对数据质量的把控和对业务场景的理解。这个系统后续还有很多可以扩展的方向,比如把检测结果直接联动到分拣机构的控制信号,或者用知识蒸馏把模型压缩到移动端做口袋巡检工具。只要基础的识别链路是稳定的,这些扩展都只是时间问题。