ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

垃圾分类检测系统实战:YOLO与大模型双引擎落地解析

垃圾分类检测系统实战:YOLO与大模型双引擎落地解析

垃圾分类检测系统听起来像是一个在公园里就能做的演示项目,真正落地到智慧城市的垃圾房、中转站甚至分拣流水线上,才会发现坑比想象中多得多。过去两个多月,我们团队一直在调一套“视觉识别+大模型推理”的双引擎方案:摄像头负责看,YOLO负责把垃圾框出来,DeepSeek负责补上“这东西到底该往哪个桶里扔”的脑力。这篇文章不是产品宣传稿,而是把这套系统从选型、训练、接入大模型,到融合决策、边缘部署、对接分拣设备的完整过程,连同那些翻车现场,一起摊开来讲。

如果你正在做智慧城市相关的视觉项目,或者想给自家的垃圾分类设备加上识别能力,又或者只是好奇YOLO和目标检测这类算法怎么和大模型配合落地,这篇文章都值得往下看。我会把每一层做了什么、为什么这么做、换成你会踩什么坑,全部交代清楚。

1. 双引擎架构:为什么YOLO和大模型非得一起用

很多团队一开始的思路都是“上一个更强的目标检测算法就完事了”。我们最初也试过只靠YOLO做全部分类,结果在真实场景里碰了一鼻子灰。原因很简单:目标检测输出的是“瓶子里装的是塑料瓶”“纸箱”“一次性纸杯”这种物体类别,但垃圾分类要的答案是“这个物体现在属于哪一类”,这两件事之间隔着大量常识推理。

1.1 纯视觉方案究竟输在哪

一个沾着油污的外卖纸盒,视觉模型能认出它是纸盒,却判断不出它“还能不能算可回收物”。一个被压扁的易拉罐,从侧面看几乎变成了一块铝片,类别框怎么打都别扭。更麻烦的是破碎陶瓷碗,训练集里如果没专门准备这类样本,模型给出的置信度经常低到不敢用。这些场景在干净的数据集里根本不会出现,但每天在垃圾投放点都在发生。

也就是说,垃圾分类本质上不是“这是什么物体”,而是“这个物体的当前状态适合归到哪一类”。前者是视觉模型的强项,后者需要材质知识、污染判断、常识规则,甚至需要一点“现场沟通”能力。单靠视觉模型硬扛,要么把识别规则写成几百条if-else,要么在真实场景里反复误判。

1.2 系统分层与三份输出

所以我们把系统拆成了两层。第一层是视觉识别层,用YOLO做目标检测,输出每个垃圾的类别候选、置信度和它在图像中的位置框;第二层是语义推理层,把YOLO给出的候选信息以及拍摄场景、用户补充信息转成文本提示,交给DeepSeek去推理最终分类。

这套系统最终会产出三份结果:视觉结果、大模型结果、规则裁决结果。视觉结果保留检测框和原始坐标,主要给后续分拣机械臂或闸机联动用;大模型结果包含最终类别、可回收性判断和一句给用户看的投放指引;规则层则是一个硬规则引擎,负责在大模型结果“看起来不可信”时修正,或者在断网时接管决策。三份结果同时留档,等人工复核时还能查到当时的判断依据,这一条在正式运营时非常有用。

2. 视觉识别层的选型与训练:从YOLOv5到YOLOv8的取舍

视觉层是整个系统的“眼睛”,选什么版本的YOLO直接决定了后续所有工作量的上限。我们团队内部做了两轮对比才最终定下来,这里把取舍过程原原本本写出来。

2.1 版本对比,我们最终选了谁

当时手上主要对比了YOLOv5s、YOLOv8s和更新一代的轻量模型。YOLOv5s最成熟,网上资料多,部署踩坑少,但小目标和低光场景的表现已经有些吃力;YOLOv8s在精度上比v5s有明显提升,而且导出ONNX再到边缘设备加速的链路更顺;新一代轻量模型体积更小,但在我们自己的数据集上精度方差反而更大,有一些类别会出现时好时坏的情况。

对比项YOLOv5sYOLOv8s新一代轻量模型
模型体积约14MB约21MB约11MB
小目标识别一般较好中等
低光鲁棒性较弱较强中上
边缘部署便利性成熟稳定导出链路顺生态较新
我们的结论备选最终选用暂不采用

最后我们选了YOLOv8s,理由很实际:它的精度提升能直接在垃圾分类场景里带来可感知的误判减少,而体积增大这点代价在边缘设备上通过量化完全能接受。如果你的相机距离垃圾更近,目标更大,YOLOv5s也足够,还能省一点显存,但就我们的投放点环境来说,v8s更稳。

2.2 数据集和标注规范

模型跑的准不准,七成看数据集。我们自己整理了一套覆盖18类的垃圾分类图像集,包括纸箱、塑料瓶、易拉罐、玻璃瓶、旧衣物、金属制品、剩菜、果皮、茶渣、蛋壳、干电池、药品、灯泡、陶瓷碎片、卫生纸、一次性餐盒、塑料袋、快递包装等。每一类都尽量收集了不同光照、不同角度、不同遮挡程度下的真实拍摄图,总共约2500张原始图,再用数据增强扩到3800多张。

标注规范比很多人想象中更讲究。凡是遮挡面积超过60%的目标我们统一不标,因为这类样本会把模型练得“犹豫”;小于全图面积5%的目标也一律不标,让模型专注在投递口可操作范围内的物体。标注工具选的是常见的图形标注工具,边框尽量贴合物体边缘,避免把大量背景带进训练样本。重点难点是易混淆类别,比如矿泉水瓶和塑料杯、纸箱和牛皮纸袋,这些类别之间的区分我们专门开会定了标注细则,保证不同标注人对同一物体打出的类别一致。

2.3 训练策略与实测精度

训练上我们没有从零开始,而是用预训练权重做迁移学习。先冻结主干网络只训练检测头,跑60个epoch让边界框快速收敛,再解冻全部层做全量微调。优化器用SGD,初始学习率设0.01,配合余弦退火衰减,batch size取32。整个训练过程在单张显卡上大约跑了8个小时。

最终模型在验证集上的mAP@0.5约为92.3%,mAP@0.5:0.95约为81.7%。单看数字好像不错,但分到具体类别上就能发现问题:矿泉水瓶的识别精度很高,塑料杯和一次性纸杯的混淆率却偏高,理由是它们在外形和颜色上太接近,光靠视觉很难分清楚。这个模糊点我们后面没有硬让视觉解决,而是交给了大模型那一层去兜底,这也印证了双引擎架构的必要性。

3. DeepSeek接入:提示词怎么写决定了系统的“智商”

视觉层出了问题,大模型层就要接住。DeepSeek这一类大模型在这里承担的不是“聊天”功能,而是作为垃圾分类规则推理器。提示词怎么写、输出怎么解析,直接决定系统是真智能还是“看起来智能”。

3.1 为什么选API调用而不是本地部署

我们一开始也认真评估过在边缘设备上直接部署大模型,结果算了一笔账就放弃了。边缘设备的内存和算力要留给YOLO推理,再让它跑一个完整的大模型,延迟会飙到不可接受。再加上试点点位本身具备稳定网络环境和机房网关,API调用的延迟完全可以被异步处理掩盖,所以我们最终选择了API接入方式。

接入时需要注意的第一个点是流式输出和超时控制。分类场景下的返回内容很短,没必要开启流式输出,我们直接把超时时间设成15秒,超过就自动降级到规则引擎。API连接做了两级优化:一是复用连接而不是每次新建,二是把并发数控制在5路以内,避免触发限流,实测下来平均单次推理延迟能稳定在1.5秒以内,这个速度对投放点的使用节奏完全够。

3.2 提示词工程与结构化输出

大模型系统的智商,八成体现在提示词里。我们没有简单地把“这是什么垃圾”丢给模型,而是设计了一套非常具体的角色扮演提示词,要求它必须输出一个JSON对象。

SYSTEM_PROMPT = """你是智慧城市垃圾分类点的投放判定助手。 输入信息包含目标检测模型给出的候选类别、置信度以及投放场景。 你的任务是根据这些信息推理出该垃圾的最终分类。 必须遵守的规则: 1. 输出格式只能是JSON,不要输出任何额外解释。 2. JSON字段为{"category": "四分类之一", "class_name": "具体类别", "recyclable": true/false, "reason": "一句话理由", "guide": "给用户的投放提示"}。 3. 如果候选类别之间存在明显冲突,优先选择与“废纸/废塑料/废金属/废玻璃”相关的可回收类别。 4. 不确定时输出category为"other",不要猜测。 5. 沾有油污或食物残渣的纸盒、塑料盒一律按"other"处理。 6. 黑色塑料袋反光不等于金属,优先按不可回收垃圾处理。 7. 陶瓷、砖瓦等坚硬易碎物统一归入"other"。 """

还有一点容易被忽略:temperature参数要调到很低。我们最终设为0.1,让模型的每次输出尽量稳定,而不是像聊天机器人那样每次给出不同措辞。max_tokens控制在256以内,防止模型为了凑字数输出大量无关内容。

3.3 解析输出时最容易翻车的地方

大模型在压力测试下偶尔会输出非标准JSON,比如在JSON前面加一段“好的,我来为您分析”,或者在JSON后面多一个逗号。我们写了一个专门的后处理函数,先通过正则匹配提取从第一个左花括号到最后一个右花括号的片段,再做JSON解析,解析失败自动重试一次。重试依然失败就降级到规则引擎,同时把这次异常记录下来,作为后续迭代提示词的样本。

另一个翻车点是跟踪过程中的目标漂移。同一件垃圾在视频流里连续出现几帧,YOLO每帧给出的候选类别可能有微小差异,如果我们盲目地每帧都调一次大模型,结果会左右横跳。我们的做法是先做轻量级目标跟踪,累积一个目标的多次检测结果,取置信度最高的候选列表后再统一交给大模型。这样既减少了API调用次数,也避免了分类结果在屏幕上反复变来变去。

4. 双引擎融合规则:置信度阈值、硬规则表和离线兜底

YOLO和大模型各干各的还不行,关键是它们之间的“接头”规则。我们把融合决策做成了一套带优先级的流程,不能简单说“谁置信度高听谁的”。

4.1 决策流程:谁先说话,谁最后拍板

整体决策逻辑用一段伪代码可以讲清楚:

def decide(det_result): if det_result.top1.conf >= 0.85: return apply_hard_rule(det_result.top1.cls) elif det_result.top1.conf >= 0.50: candidates = det_result.top3 llm_answer = llm_reasoning( candidates=candidates, scene=scene_info, user_hint=user_hint, ) return apply_hard_rule(llm_answer["category"]) else: return NEED_HUMAN_REVIEW

阈值为什么取0.85而不是0.9或0.8?我们拿验证集算过精确率-召回率曲线,在0.85这个点上,系统直接采纳视觉结果时精确率能达到97%以上,误判率足够低。一旦低于0.5,视觉模型和大模型的联合判断都不可靠,这时候与其硬猜,不如让用户重拍或者走人工复核。

还有一个容易被忽略的细节:大模型返回的结果必须再过一遍硬规则表。比如无论大模型怎么想,干电池和药品这类有害垃圾必须直接进有害箱,不允许因为沾了食物残渣就归到其他类。硬规则表的存在不是为了替代大模型,而是保证最关键的几条安全底线不被模型偶然带偏。

4.2 离线规则引擎

真实部署不像演示环境那样永远网络顺畅。我们特意留了一套离线规则引擎,它本质上是把YOLO输出的类别直接映射到四分类的静态表,比如纸箱映射到可回收、剩菜映射到厨余、药品映射到有害、陶瓷碎片映射到其他。当API调用超时或者连续失败时,系统自动切到离线模式,识别速度完全没有影响,只是少了推理理由和投放指引的生成。

这套兜底设计在实际试点里救过我们一次。有一周投放点所在区域的网络出现了间歇性抖动,靠离线规则引擎撑住了三个小时,后台记录显示那段时间用户基本没有感知到系统异常。别小看这个细节,智慧城市项目里“稳定不出错”比“偶尔秀一把智能”更能赢得现场管理员信任。

5. 现场跑了两周:准确率数据与三类高频翻车场景

实验室指标再漂亮,进了真实投放点就是另一回事。我们在某个试点社区的垃圾房和投放亭同时部署了两套设备,连续跑了两周,采集到4160次有效投放记录,并按四分类口径做了人工复核。

5.1 测试环境与量化结果

两套点位环境完全不同:垃圾房灯光充足但经常有反光地面,投放亭光照变化大、傍晚逆光严重。整体准确率数据如下:

类别样本数识别准确率
可回收垃圾142093.1%
厨余垃圾118089.7%
有害垃圾49095.2%
其他垃圾107087.6%

看数据好像还行,但把错误样本翻出来逐条看,会发现问题的分布非常集中。

5.2 翻车场景复盘

第一个高频翻车是黑色垃圾袋反光。垃圾袋被揉捏后形成很多褶皱,强光一打,YOLO经常在反光区域误检出一个高置信度的“金属/铝箔”候选框,随后大模型也根据视觉输入给出了“金属可回收”的错误结论。这个问题我们之前只在数据增强里加了随机HSV扰动,没想到真实反光的强度和方向性远不是随机染色能模拟的。后来专门采集了一批不同材质垃圾袋的实拍反光图,并在提示词里明确写入“黑色塑料袋反光不等于金属”,误判率立刻降了一大半。

第二个高频翻车是外卖盒的食物残渣。干净的纸质外卖盒是可回收物,但只要里面有油渍或剩饭残渣,按分类规则必须归入其他垃圾。YOLO只能识别出这是纸盒,根本无法感知残渣状态。我们尝试在提示词里要求大模型“根据上下文推断污染状态”,但文本输入里本来就不带图像细节,这属于信息缺失,再聪明的模型也猜不出来。最终解法是在投放交互层面加了一道用户确认:当系统识别到“纸盒/塑料盒”且这类物体属于可回收物时,屏幕会弹出一个选项问用户“盒内是否已有食物残渣”,用户确认后再决定最终投放口。虽然多了一步交互,但准确率到了98%以上,现场居民也普遍觉得这个提问合理。

第三个翻车是破碎陶瓷碗。YOLO训练集里正常完整陶瓷碗有不少,但破碎后的形态差异太大,模型给出的置信度往往是0.3附近,直接进入了人工复核流程。我们补了300多张破碎陶瓷片的实拍样本,并在硬规则表里加了一条“陶瓷类易碎品一律归入其他垃圾”,这个问题才算彻底解决。

5.3 改完之后的提升

把这三类问题处理掉之后,第二轮一周的测试里,整体准确率从88.7%提升到了94.2%。其中其他垃圾这个类别的提升最明显,从87.6%涨到了92.8%。这组数据也印证了一个判断:垃圾分类识别系统的瓶颈往往不在模型能力,而在场景理解是否完整,而场景理解恰恰需要视觉、大模型、规则和交互四层一起配合。

6. 边缘设备部署与调优:从帧率焦虑到稳定运行

部署环节最大的挑战是:投放点现场的环境远不如机房干净,设备功耗有上限,温度时高时低,网络还可能抖动。这一章讲我们在边缘设备上做的具体优化。

6.1 硬件和安装位置

我们用了一款嵌入式GPU开发板,型号是带Tensor加速能力的Jetson Orin Nano系列。这个级别的算力跑YOLOv8s量化版完全没问题,而且整机功耗控制在一个普通电源适配器能承受的范围。刚开始也试过在树莓派上跑,但帧率只有5帧左右,识别一帧要等很久,体验非常糟糕。

安装位置比想象中更重要。第一版我们把摄像头正上方朝下装,结果发现垃圾投放时经常出现强反光,而且正上方视角会把被拍的垃圾压成扁平的俯视图,很多类别特征丢失。后来改成45度斜装,加了一个遮光罩,工作距离控制在0.3到0.8米,视野范围大概30厘米乘40厘米。切换后的识别准确率整体高了3个百分点,这类土办法不见得写进论文,但对落地的帮助非常大。

6.2 量化、预热与并发控制

模型优化方面,我们对比了FP16和INT8两种量化方案。FP16部署时单帧推理耗时约38毫秒,精度只损失0.8%;INT8部署时单帧推理耗时约22毫秒,精度损失2.4%。因为投放点不需要高速连拍,对单次投放我们只会截取关键帧,所以两种方案都能满足需求,但综合边缘设备的功耗和热量,最终选了INT8,长期运行的稳定性更好。

方案单帧耗时mAP损失结论
FP1638ms-0.8%精度优先可选
INT822ms-2.4%最终选用

除了模型本身,系统层面的调优还有三件小事。第一件是模型预热,设备刚启动时显卡驱动和ONNX Runtime都会经历一个较慢的初始化阶段,我们在初始化脚本里强制跑了几次空推理,让运行库把相关算子加载完。第二件是API连接池预热,同样是在启动阶段就建立好到大模型API的连接,避免第一个用户来投放时经历漫长的建连等待。第三件是对识别线程做了互斥锁保护,原因是Jetson上的显存和CPU内存共享,YOLO推理和大模型API调用如果同时在高负荷运转,偶尔会触发进程被杀,用互斥锁把关键路径串行化之后就没再出现过。

7. 给分拣设备留的联动接口与状态机设计

识别系统最终要给分拣设备发指令,让对应分类的闸机或机械臂动作。这块看似简单,但协议设计不仔细,现场设备就会乱套。

7.1 指令协议设计

我们设计了一套极简的串口指令协议,帧格式固定为:帧头AA 55、指令码、目标箱号、置信度档位、请求ID、CRC16校验。指令码只有四条,对应投放至可回收箱、厨余箱、有害箱、其他箱,外加一条人工复核指令。

指令码含义目标箱号
0x01投放可回收0x01
0x02投放厨余0x02
0x03投放有害0x03
0x04投放其他0x04
0x0F人工复核0x00

为什么要把置信度档位也放进帧里?因为分拣设备需要决定动作力度和执行速度。置信度高于0.9时,设备可以快速动作;置信度低于0.7时,设备应该放慢一点,留出时间让管理员目检。这类细节如果不提前设计,等到硬件联调时就会被反复拉通,非常浪费时间。

7.2 状态机与人工复核

设备联动部分我们维护了一个简单的状态机:待识别、已决策、指令已发送、等待设备应答、执行成功、执行失败。每一个状态都带有唯一请求ID,所有日志都围绕这个ID组织,排查问题的时候只要一键搜索就能看到从图像采集到设备动作的完整链路。

人工复核的流程也接进了这个状态机。当识别置信度过低,或者用户主动按了“不确定”按钮,系统会把当前图像和候选类别推送到管理后台。管理员在网页端确认后,这个样本会被自动标记为有效训练数据,定期回流到下一次的数据集迭代里。这样系统不需要额外的人工标注工具就能持续积累现场真实样本,两周试点下来,我们靠这个机制白捡了一千多张高价值难例。要拿这些数据去跑下一版模型,准确率还能继续往上走。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进