资讯动态

YOLOv10空货架检测实战:从数据集构建到UI界面部署全流程

发布时间:2026/10/21 16:28:44 来源:尧图企业网站定制
一提到“YOLOv10”和“零售场景”很多人第一反应是货架商品识别、客流统计这类常规玩法。但“空货架检测”这个切入点其实更贴近超市运营的真实痛点——缺货导致营业额损失补货不及时又拉低顾客体验。这个项目标题里最关键的两个词一个是“YOLOv10”说明用的是2024年发布的无NMS新架构另一个是“UI界面”意味着它不只是一段训练脚本而是一个完整能跑起来、甚至能交给运营人员直接使用的系统。这篇文章就围绕这个项目把从数据集构建、模型训练到UI界面封装、部署上线的完整链路拆开讲透。我拿到这个项目标题的时候脑子里第一反应是“这确实是个适合动手复现的实战项目”。因为它把计算机视觉里最经典的目标检测任务落在了零售业一个具体到不能再具体的业务场景上。对于刚接触YOLO系列的开发者来说它是“调包→训练→推理”的完整闭环对于做毕设或者工程项目的同学来说它又自带“数据集→模型→界面”三层齐全的交付形态。整篇文章下来我会结合自己在类似项目里的实操经验把每个环节容易踩的坑都指出来。1. 项目整体拆解空货架识别到底在解决什么1.1 业务痛点与需求解析超市货架出现大面积空洞意味着两种情况一种是商品确实卖完了但没来得及补另一种是商品在仓库但店员不知道货架已经空了。这两种情况都会直接影响销售额。有研究统计过零售门店的平均缺货率大约在8%到10%之间促销期间甚至能到15%以上。顾客发现想要的商品不在货架上大概率转头就走这损失的可不是这一单而是顾客对这家店的信任。传统做法是店员定时巡场肉眼扫货架。但一个中大型超市有几千个SKU巡场一遍至少半小时高峰期根本来不及补。用固定摄像头实时盯货架又存在大量视觉遮挡、灯光变化、货架层板反光这类干扰因素。所以这个系统要解决的不是“静态图片里有没有商品”而是“在复杂环境下持续、稳定地判断某个货架区域是不是缺货了”。YOLOv10的优势在于它能做实时帧检测配合稍后提到的UI界面可以轮询多个摄像头画面一旦发现空货架区域立刻弹出告警记录。1.2 为什么选YOLOv10而不是其他模型标题把这个项目的技术栈写得很明确就是YOLOv10但这背后其实有一个选型逻辑。YOLOv8是过去两年工程落地最常见的选择YOLOv9则更侧重于可逆编程和梯度信息保留模型结构更复杂。而YOLOv10最大的变化是去掉了NMS非极大值抑制后处理提出了一致的双标签分配策略。这意味着推理时候少了一个后处理阶段速度提升的同时检测头的设计也大幅简化。实操层面有一个非常直观的对比在一张1080P的超市货架图上用YOLOv8s做推理NMS阶段可能耗时5到8毫秒GPU利用率经常出现“计算空闲、等待后处理”的尴尬局面。YOLOv10s去掉NMS之后整体端到端延迟可以压缩20%到30%。对于超市这种通常需要同时并发处理6到8路摄像头的场景这个速度优势直接影响硬件选型——不用上昂贵的高端显卡一块中端显卡就能扛住多路实时推理。另外YOLOv10在COCO数据集上精度也不落后s模型mAP50能到56%以上足够识别空货架这种明显视觉特征。1.3 系统整体技术栈与交付形态一套完整的系统交付的不只是模型权重而是围绕模型构建的可用产品。我按照标题里的关键词把整个项目拆成四个模块模型核心YOLOv10目标检测模型加载训练好的权重进行空货架识别。数据支撑YOLO格式标注数据集涵盖多种货架、多种光照条件。UI交互层基于Python桌面GUI的展示与告警平台支持选择图片、视频、摄像头输入。工程化代码模型加载、检测推理、结果绘制、数据记录等全套Python源码。这四个模块环环相扣缺了哪一个项目就只能在命令行里跑个demo无法真正“用”起来。尤其是UI界面很多初学者会低估它的工程复杂度总以为训练完模型就大功告成了结果到了汇报演示阶段才发现拿命令行窗口给业务人员看效果根本说服不了人。后面我会详细说明怎样把模型封装进一个看完就能上手的界面。2. 数据集构建决定模型上限的关键一步2.1 YOLO数据格式与类别设计YOLO标签格式很好理解每一张图片都对应一个同名txt文件txt里每一行代表一个目标框格式是class_id x_center y_center width height其中中心点坐标和宽高都是相对于图片宽高的归一化值。空货架检测这个任务里类别怎么设计会直接影响模型的学习难度。最稳妥的方案是只设一个类别比如就叫“empty_shelf”也就是“空货架”。不要试图把“满货架”“半满货架”“空货架”都做成不同类别因为半满和满之间的边界非常主观标注员自己都可能意见不统一模型学起来会更困惑。真正的检测目标只有空货架区域模型只需要学会区分“空”和“不空”两种视觉状态。标注空货架时有一个技巧不是标注整个货架层板而是只标注“本来应该摆商品的那个空白区域”。比如一个五层货架每一层可能只有一个空位那就单独标一个框如果一整层都空了就标注整个层板区域。这样标注的好处是模型学到的是“空洞区域”的形状和上下文特征而不是泛泛地学习“货架”本身因为货架永远在那里并不需要检测。2.2 数据采集策略与场景覆盖超市环境里的干扰因素非常多冷柜灯管的反光、促销立牌、顾客的手推车、甚至货架最底层贴近地面的暗角都会影响模型表现。所以数据采集阶段就要有意识地增加负样本的多样性。首先固定机位拍摄的货架图远不够要尽量模拟实际部署角度。如果摄像头装在天花板斜向下45度那训练数据也最好以这个视角为主。不同楼层的货架高度、间距都不同有条件的话至少在三个不同位置的货架各拍一段视频再抽帧成图片。我参考过的某零售项目在一个样本量只有1200张图片的数据集上模型在自家测试集上mAP50有92%但换了一个采光完全不同的门店直接掉到71%。原因就是训练数据里白天自然光的图片太多缺少暖黄灯光、日光灯频闪、傍晚暗光这三种场景。后来补了200张低照度图片和150张强反光图片mAP才回到85%以上。数据覆盖的优先级永远高于模型调参。2.3 数据增强与样本平衡YOLOv10的ultralytics库内置了丰富的在线数据增强策略比如马赛克增强、随机透视、色彩空间扰动、水平翻转。但用在货架检测上有几个增强操作必须谨慎。水平翻转可以用因为货架左右对称翻转后语义不变。但旋转增强要限制角度不要随便旋转90度或45度因为货架通常是水平方向的矩形检测框旋转过大会把层板的上下文信息切碎。另外马赛克增强默认开启它会把四张图拼在一起训练对于小目标检测很有帮助但如果发现训练早期loss下降慢可以尝试把mosaic概率从1.0降为0.5让模型先适应单图内容。样本平衡方面如果“空货架”目标框数量明显少于“普通货架”或背景样本容易导致模型倾向于把一切目标都判为背景。可以在标注完成后统计一张图平均有多少个空货架的框理想值在1到3个之间。如果大量图片都是0个空货架框那训练时模型会严重偏向负样本导致误检增多。宁可删除一些完全没有空货架的训练图片也不要让正负样本比例过于悬殊。3. 环境搭建与模型训练从零跑通YOLOv103.1 GPU环境与依赖版本YOLOv10基于ultralytics框架安装相当简单打开终端执行pip install ultralytics就能装好全部核心依赖。但这只是表面顺利真正容易出问题的是依赖版本之间的兼容性。我的建议是Python 3.9或3.10版本PyTorch 2.1以上。如果你的显卡是N卡CUDA一定要装对版本否则训练时你会发现代码一直在CPU上跑那种“GPU利用率0%”的体验非常让人头疼。装完ultralytics之后先用一句yolo detect predict modelyolov10s.pt sourcehttps://ultralytics.com/images/bus.jpg做验证如果这句能正常出结果说明环境基本通了。从YOLOv10官方发布的模型里选预训练权重最常用的是yolov10s.pt和yolov10m.pt。s模型速度快适合做实时检测m模型精度稍高对硬件要求也高一点。对于超市空货架这种目标不算太小的场景s模型足够。如果你的训练数据里货架比较远、目标框偏小可以换成m模型精度会有一个明显的提升。注意第一次跑YOLOv10训练之前建议先跑一个最小规模的epoch比如3轮确认数据路径、标签格式、训练流程都通了再跑完整训练。否则等你跑到第50轮才发现标签路径配错了浪费几个小时。3.2 数据配置YAML文件ultralytics框架通过YAML文件描述数据集信息我贴一个实战可用的配置路径根据自己的目录调整path: ./datasets/supermarket_shelf train: images/train val: images/val test: images/test names: 0: empty_shelf注意names这里的类别编号必须和标注文件里的class_id对应上。有些标注工具比如LabelImg或X-AnyLabeling导出的类别顺序是自定义的如果txt里的id是0那么YAML里的第一个类别就是它。千万不能搞乱顺序否则模型会把“empty_shelf”标签对应到别的位置loss值会异常高。数据集目录结构建议如下datasets/supermarket_shelf/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml训练集、验证集、测试集按8:1:1划分。不要在同一个文件夹里边训练边验证防止数据泄漏导致验证指标虚高真正部署时却表现不佳。3.3 训练参数设置与调优过程训练命令很简单核心是以下几个参数yolo detect train modelyolov10s.pt datadata.yaml epochs100 batch16 imgsz640 device0epochs100对空货架检测这种单类别任务来说100轮完全够用。如果数据集特别小低于800张甚至50轮就能收敛。batch16根据显卡显存调整。12GB显存跑yolov10sbatch16没问题8GB显存要降到8。imgsz640YOLO默认的训练分辨率。如果你的货架图片是1080P直接用640输入即可不推荐刻意提高到1280训练速度和显存占用都会成倍增长。训练过程中我在实际项目里主要是盯两个指标val/box_loss和mAP50。box_loss在训练初期会快速下降到30轮左右趋于平缓这是正常现象。真正需要警惕的是训练loss一直下降、验证loss突然反弹这基本可以断定过拟合了需要加数据增强或提前停止训练。ultralytics框架会自动保存best.pt和last.pt训练结束后直接用best.pt做推理即可。3.4 模型导出与优化训练完成后如果要集成到UI界面或部署到生产环境最好把PyTorch权重导出为ONNX格式或TensorRT格式。yolo export modelbest.pt formatonnx imgsz640导出ONNX有实际好处一是推理速度比PyTorch原生更快二是可以脱离PyTorch环境运行方便后续在服务端或边缘设备上部署。如果用的是NVIDIA显卡还可以用formattensorrt做进一步加速实测在部分显卡上能把推理耗时再压缩30%到50%。4. UI界面设计让模型真正“可交付”4.1 界面功能规划UI界面是整个项目的“门面”。很多技术人容易忽略一点业务人员判断一个系统好不好用不是看你的mAP指标有多高而是看界面是否直观、操作是否顺畅。所以我规划这个系统界面时最少要包含五个功能区域输入选择区支持选择单张图片、本地视频文件或打开摄像头实时画面。检测结果显示区展示检测框、置信度分数以及“该区域空货架数量”等汇总信息。实时画面预览区显示原始视频流和检测结果叠加画面。告警记录区每次检测到空货架时自动记录时间、置信度、货架位置截图。模型状态区显示当前加载的模型名称、推理帧率。如果你想让界面再专业一点可以加一个“批量检测”功能用户选择一批图片后自动对每张图检测最后导出结果到CSV。这个功能在做货架巡检报告时非常实用。4.2 核心代码实现选型上PyQt5是目前搭配YOLO做桌面端UI最成熟的方案也可以选PySide6两者API基本一致。主窗口的框架代码比较简单我摘一段核心逻辑说明关键的线程设计问题。import sys import cv2 import torch from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QPushButton, QVBoxLayout, QWidget from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import QTimer from ultralytics import YOLO class ShelfDetectorApp(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(超市空货架识别检测系统) self.model YOLO(best.pt) self.video_label QLabel(self) self.button QPushButton(打开摄像头, self) layout QVBoxLayout() layout.addWidget(self.video_label) layout.addWidget(self.button) container QWidget() container.setLayout(layout) self.setCentralWidget(container) self.timer QTimer() self.timer.timeout.connect(self.update_frame) self.cap None self.button.clicked.connect(self.toggle_camera) def toggle_camera(self): if self.cap is None: self.cap cv2.VideoCapture(0) self.timer.start(30) else: self.timer.stop() self.cap.release() self.cap None def update_frame(self): ret, frame self.cap.read() if not ret: return results self.model(frame)[0] annotated results.plot() # 转换为QImage并显示 rgb_image cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qt_image QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) self.video_label.setPixmap(QPixmap.fromImage(qt_image))所有消耗性能的推理操作都不应该直接放在UI线程里否则界面会卡成幻灯片。正确的做法是用QThread把推理放到子线程主线程只负责接收结果并刷新画面。如果项目刚开始做可以用上面的简化版先跑通但正式交付版本一定要用线程池或独立推理线程。4.3 推理性能优化与帧率控制USB摄像头的原始帧率通常是30fps但YOLOv10推理速度不一定能跟到这个帧率。实际项目中我一般把检测频率限制为每2到3帧检测一次中间帧直接复用上一次的检测结果绘制这样视觉上依然是流畅的实时画面GPU负载却下降了一倍多。如果要同时处理多个画面可以对每路视频流启动一个独立检测线程每个线程持有自己的模型实例。注意PyTorch模型在多线程环境下的推理是线程安全的但同一时刻多个线程调用同一个模型实例仍可能出现CUDA上下文冲突。最稳妥的做法是每路摄像头单独加载一个模型副本或者用进程池来隔离。4.4 告警与记录功能关于告警和记录可以约定一个逻辑检测到空货架时把带标注的结果图保存到./alerts/目录文件名用时间戳生成同时在界面状态栏显示当前告警数量。如果需要更复杂的告警通知比如推送消息或邮件可以在检测函数里增加一个阈值判断只有当置信度高于0.6或空货架数量超过设定值时才触发告警防止误报造成“狼来了”效应。5. 常见问题与排查技巧实录5.1 标注与训练阶段常见问题我先列一个排查表把实操中碰到最多的几个问题整理出来问题现象根本原因解决方案训练时loss始终不降数据路径正常但loss在3到5之间徘徊标签和图像不匹配用脚本随机抽几张图把标注框画出来检查所有目标都检测不到mAP始终为0类别名或class_id配置错误检查data.yaml的names顺序是否和txt文件中的id一致模型只识别货架不识别空区大量误检货架层板标注颗粒度太粗调整标注方式只标空白区域不标层板训练过拟合验证loss反弹数据量不足或增强过强减少增强强度增加真实数据5.2 推理与部署阶段常见问题推理阶段最让人头疼的问题是“模型在验证集上表现良好一到实际摄像头画面上就频繁误检”。造成这个现象的原因很可能是训练数据里没有覆盖摄像头画面中的眩光、隔三差五的促销标签或者货架底层的阴影。解决思路是采集一小段实际部署环境的视频抽帧后补充到训练集里再微调几轮效果通常立竿见影。另一个高频问题是“检测框颤抖得厉害”。相邻两帧同一目标框位置跳来跳去视觉体验很差。根本原因是单帧检测缺少时间维度的平滑尤其当货架上有半透明包装时模型置信度不稳定。解决办法很简单加一个轻量级的EMA指数移动平均滤波让检测框的坐标在时间序列上平滑过渡界面观感会立刻改善。5.3 一个我用过的调试小技巧训练过程中想要快速判断模型学得好不好除了看mAP曲线还有一个非常直观的技巧把best.pt在测试集上跑一遍再把检测结果可视化的图片批量导出挑几张典型的“多目标”场景图用眼睛观察标注框是否贴合真实空货架轮廓。如果发现部分框把相邻商品也框进去了说明模型的边界回归还需要更精细的训练此时可以适当增加iou阈值或对标注框做微调重新训练几轮。这比单纯死磕训练指标要有效得多。6. 项目完整落地与扩展方向6.1 从demo到可用的交付清单一个能拿去演示或者交付的项目不应该只包含一个模型文件和一段训练代码。我按自己的习惯整理了一份最终交付检查清单你可以直接对照模型文件best.pt或导出的best.onnx。训练配置data.yaml、训练参数脚本。数据集说明标注格式说明、类别字典、数据增强策略。UI界面源码完整可运行的项目目录包含依赖requirements.txt。使用文档环境安装、模型替换、界面操作三部分。示例数据2到3段测试视频或一批测试图片方便快速验收。这六项里最容易漏的是“使用文档”。哪怕读者是你导师或组内同事也不要省略文档因为你无法保证对方的运行环境和你一模一样。requirements.txt里锁定好版本号避免对方装依赖时因为版本冲突浪费时间。6.2 部署方案的进一步优化如果是在一个中型超市部署摄像头的数量可能在8路以上此时单机多线程方案就显得吃力。更划算的思路是服务器或工作站上集中跑YOLOv10推理通过RTSP拉流统一读取摄像头视频检测结果通过HTTP接口或消息队列推给前端大屏。这样摄像头侧不需要额外算力运维升级也只动服务器端。另外考虑到超市货架是固定不动的可以先对画面做静态区域标定人工划定好每一个货架区域然后检测只在这些区域内运行。这样可以过滤掉顾客、购物车等大量无关移动目标误报率会成倍下降。这个做法在真实项目里非常实用是很多商用零售分析系统的核心思路。6.3 后续可以做的功能扩展项目做完以后想更进一步的话有几个方向值得尝试把“空货架”升级为“缺货程度识别”比如按百分比划分0到100的缺货等级这样运营系统可以按紧急程度生成补货工单。增加货架区域自动标定功能利用货架横梁、层板边缘的特征做线段检测自动划分区域省掉人工标定步骤。接入门店销售系统检测到空货架后自动查询该商品库存如果库存充足则生成补货提示否则自动生成采购申请。把模型导出为TensorRT部署到超低功耗的边缘设备比如Jetson系列进一步压低硬件成本。我在实际接触的零售项目中发现“空货架检测”只是前端感知层真正产生业务价值的是它的后续动作——补货任务能不能自动流转到对应员工的手持设备上缺货商品库存数据能不能同步更新。所以如果你打算把这个项目做成一套完整产品不妨往“感知→决策→执行”这条链路上多思考几步。最后说一点自己做这个项目的体会。YOLOv10这套工具链已经相当成熟真正的难点从来不是模型本身而是你愿不愿意花时间在数据质量、场景覆盖、界面体验这些“杂活”上。数据集干净训练参数合理UI界面封装到位这个项目就能从“能跑”变成“好用”。反过来如果没有数据上的积累哪怕换更强的模型上线后照样被现实场景锤得体无完肤。动手之前先问自己我手上的数据能不能代表真实部署时的环境如果答案不太确定那就先去拍视频、抽帧、标注。这是做所有视觉检测项目最值得投入的一步。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑