☰
基于YOLOv8的仓库货物盘点系统:从训练到部署全流程实战
2026/9/28 13:09:37 网站建设 项目流程

简介:本资源为基于YOLOv8的仓库货物盘点系统完整项目包,面向计算机、人工智能、通信工程等专业的在校学生与教师,适合作为毕业设计、课程设计或大作业的参考方案,也便于初学者进阶学习目标检测的落地流程。包内共97个文件,以70个Python源码为核心,辅以4个pt权重文件、5个xml配置、12个pyc缓存及mp4演示视频等,压缩包约24.21MB,涵盖模型训练、检测推理、可视化界面与部署说明等模块。项目已通过运行测试,可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果与标签分布图,并配有可视化页面,部署简单、操作直观。目前已有48人学习下载,读者可据此快速复现完整盘点流程,理解YOLOv8训练与推理细节,并在此基础上修改扩展功能,满足答辩与项目演示需求。

1. 仓库货物盘点系统为什么值得用 YOLOv8 重做一遍

做过仓库盘点的人都清楚,传统方式无非两种:人工拿着 PDA 逐个扫码,或者靠电子标签、地磅、称重来间接推算。前者慢,一个人一天盘不了几个货架;后者贵,改造一套 RFID 或称重货架的成本,中小仓库根本扛不住。而基于 YOLOv8 的仓库货物盘点系统,本质上是把「看一眼就知道有多少箱、什么品类、有没有缺货」这件事交给摄像头和检测模型来做——固定机位拍货架,模型识别货箱类别与数量,再叠加一个可视化界面把结果呈现出来,配合完整数据集和部署教程,简单部署即可运行。

这套方案真正吸引人的地方在于门槛被拉低了。YOLOv8 本身是 Ultralytics 维护的单阶段检测框架,训练脚本封装得相当干净,yolov8n.pt这种小模型在普通显卡甚至 CPU 上都能跑推理。仓库货物盘点这个场景又天然适合检测任务:货箱边界清晰、类别有限、光照相对可控,不像自动驾驶那样有极端的长尾情况。所以它特别适合两类人——一类是拿它做毕设或课程设计的学生,需要一套功能完善、操作简单、能演示、能写进论文的完整项目;另一类是仓库一线想先做个原型验证的工程师,想用最低成本看看视觉盘点到底靠不靠谱。

我先把结论放这儿:这套东西能跑通,但「能跑通」和「盘得准」之间隔着数据集的坑、类别定义的坑和部署环境的坑。下面几章我会把从环境搭建、数据集处理、训练调参到界面部署的完整路径拆开讲,中间专门留一章讲我踩过的坑,最后一章讲怎么验证它到底值不值得投入。

2. 从零把 YOLOv8 仓库盘点环境跑起来

2.1 环境选型:CPU 版还是 GPU 版,先想清楚

很多人一上来就纠结装哪个版本,其实判断标准很简单:你是要训练还是要推理。如果只是拿现成权重跑推理、做界面演示,CPU 版完全够用,yolov8n在 CPU 上单张图推理大概几百毫秒,盘点场景不需要实时 30 帧,够看。但如果你要拿自己的数据集重新训练,那必须上 GPU,CPU 训练一个 epoch 能让你等到怀疑人生。

我一般推荐 Ubuntu 20.04 作为基础系统,Python 用 3.9 或 3.10,这两个版本和 PyTorch、Ultralytics 的兼容性最稳。Windows 也能跑,但后面部署到边缘设备或者做服务化的时候,Linux 会省很多事。下面是一套 CPU 版的最小安装流程,GPU 版把 torch 的安装命令换成对应 CUDA 版本即可。

# 创建独立环境,别污染系统 Python conda create -n warehouse python=3.10 -y conda activate warehouse # CPU 版 PyTorch,体积小、装得快 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics,它会自动带上 opencv、numpy 等依赖 pip install ultralytics # 验证安装,能打印版本号就说明通了 yolo version

这段命令的逻辑是:先隔离环境,再装 PyTorch 作为推理后端,最后装 ultralytics 这个高层封装。参数上唯一要注意的是--index-url,它决定了你装的是 CPU 版还是 CUDA 版,装错了不会报错,但训练时会发现用不上显卡。验证那一步别省,yolo version能跑通,说明命令行入口已经注册好了,后面所有yolo开头的命令才有意义。

提示:如果你用的是 GPU,把第二条命令换成pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118,具体 cu 版本要和你显卡驱动支持的 CUDA 版本对齐,装完用torch.cuda.is_available()确认返回 True。

2.2 目录结构:先规划好,别等训练完再搬家

仓库盘点项目文件不少——数据集、权重、配置文件、界面代码、部署脚本。我见过太多人把所有东西堆在一个文件夹里,最后自己都找不到哪个权重是哪个数据集训出来的。建议一开始就按下面的结构组织:

warehouse_inventory/ ├── datasets/ │ └── warehouse/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── data.yaml ├── runs/ # 训练输出,ultralytics 自动生成 ├── weights/ │ └── best.pt ├── ui/ # 可视化界面代码 └── deploy/ # 部署脚本

这个结构的核心是datasets和runs分离。datasets放原始数据和标注,runs是训练过程自动产出的日志、权重、曲线图,两者混在一起会让你在清理实验时误删数据。data.yaml是 YOLOv8 读取数据集的入口文件,它的路径写法直接决定训练能不能启动,下一节会专门讲。

2.3 data.yaml 怎么写:三个路径字段一个都不能错

data.yaml是训练配置里最容易翻车的地方,因为它对路径的解析规则有点反直觉。一个能用的仓库盘点配置长这样:

# 数据集根目录,建议写绝对路径,避免相对路径歧义 path: /home/user/warehouse_inventory/datasets/warehouse train: images/train val: images/val # 类别数和类别名,顺序必须和标注文件里的 class id 对应 nc: 5 names: 0: carton_large 1: carton_small 2: pallet 3: bag 4: empty_slot

这里的关键是path和train/val的关系:train和val是相对于path的路径,不是相对于 yaml 文件本身。很多人把train写成绝对路径,结果和path拼接后变成双重路径,训练直接报找不到图片。nc和names必须严格对应,names的 key 就是标注文件里每行开头的数字,写错一个,模型就会把货箱学成托盘。

注意:empty_slot这个类别是我强烈建议加的。仓库盘点不只是数货,还要发现空位,把「空货位」当成一个类别去检测,比事后用规则判断更稳。

2.4 用预训练权重先跑一次推理,确认链路通

在动训练之前,先用官方预训练权重跑一次推理,确认环境、模型加载、结果保存这条链路是通的。这一步能帮你排除掉 80% 的环境问题。

# 用最小的 yolov8n 权重,对一张货架图做推理 yolo predict model=yolov8n.pt source=./test_shelf.jpg save=True conf=0.25 # 输出会保存在 runs/detect/predict/ 下

model指定权重,第一次运行会自动下载yolov8n.pt;source可以是单张图、文件夹,甚至摄像头编号;save=True把带框的结果图存下来;conf=0.25是置信度阈值,低于这个值的框不显示。跑完去看输出图,如果框的位置离谱或者一个框都没有,先别怀疑模型,大概率是图片路径或者图片本身有问题。这一步跑通,说明你的 YOLOv8 环境是健康的,可以进入数据集环节了。

3. 数据集处理:仓库货物盘点标注与格式转换

3.1 仓库场景的数据集该怎么采、怎么标

数据集是这套系统里最花时间、也最决定上限的部分。仓库场景的采集有几个要点:机位要覆盖你实际部署时会用的角度,一般是斜上方俯拍货架;光照要包含白天和夜间开灯两种,因为仓库晚上灯光和白天差别很大;货箱的堆叠状态要多样,单箱、满架、半空都要有。数量上,每个类别至少 200 到 300 个实例,5 个类别的话,1000 到 1500 张图是个比较现实的起点。

标注工具用 labelme 或 labelImg 都行,但要注意:YOLOv8 要的是 YOLO 格式的 txt,不是 labelme 默认的 json。如果你用 labelme 标,需要转换;用 labelImg 直接选 YOLO 格式导出更省事。标注时有个血泪经验——货箱边界框要贴着箱体边缘,不要把整个货架层都框进去,否则模型学到的「货箱」会包含大量背景,推理时框会偏大。

3.2 从 labelme 的 json 转成 YOLO 的 txt

如果你手上是 labelme 标注的 json,下面这个脚本能批量转换。它的逻辑是读每个 json 里的多边形点,算出外接矩形,再按图像宽高归一化成 YOLO 需要的class cx cy w h格式。

import json import os from pathlib import Path # 类别名到 id 的映射,必须和 data.yaml 里的 names 一致 class_map = {"carton_large": 0, "carton_small": 1, "pallet": 2, "bag": 3, "empty_slot": 4} def convert(json_dir, out_dir, img_w, img_h): os.makedirs(out_dir, exist_ok=True) for jf in Path(json_dir).glob("*.json"): data = json.loads(jf.read_text(encoding="utf-8")) 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] # 计算外接矩形并归一化 cx = (min(xs) + max(xs)) / 2 / img_w cy = (min(ys) + max(ys)) / 2 / img_h w = (max(xs) - min(xs)) / img_w h = (max(ys) - min(ys)) / img_h lines.append(f"{class_map[label]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") # 同名 txt 输出 (Path(out_dir) / (jf.stem + ".txt")).write_text("\n".join(lines), encoding="utf-8") convert("./labels_json", "./labels_txt", 1920, 1080)

参数说明:img_w和img_h必须是你标注时那张图的真实尺寸,写错了归一化坐标就全错,模型会学到完全错误的框。class_map要和data.yaml的names严格一致,顺序错了类别就串了。转换完建议随机抽几张,用可视化脚本把框画回图上确认一遍,别直接扔进训练。

3.3 数据集划分与目录落位

转换完的 txt 和原图要按 train/val 分开放。常见做法是 8:2 划分,但仓库场景我建议 7:3,因为不同货架的差异比较大,验证集太小会看不出过拟合。划分脚本很简单,核心就是随机打乱后按比例复制:

import random, shutil from pathlib import Path imgs = list(Path("./images_all").glob("*.jpg")) random.seed(42) # 固定种子,保证可复现 random.shuffle(imgs) split = int(len(imgs) * 0.7) for i, img in enumerate(imgs): subset = "train" if i < split else "val" shutil.copy(img, f"./datasets/warehouse/images/{subset}/{img.name}") lbl = Path("./labels_txt") / (img.stem + ".txt") if lbl.exists(): shutil.copy(lbl, f"./datasets/warehouse/labels/{subset}/{lbl.name}")

random.seed(42)是为了让每次划分结果一致,方便复现实验。图片和标签必须同名同批移动,漏掉标签的图片在训练时会被当成负样本,反而干扰模型。落位完成后,目录结构要和第 2 章规划的一致,data.yaml里的train/val指向images/train和images/val即可。

4. 训练、调参与模型导出:让盘点模型真正可用

4.1 启动训练:一条命令背后的参数含义

数据集准备好后,训练本身是一条命令的事,但参数决定了你得到的是能用的模型还是废权重。

yolo detect train \ data=datasets/warehouse/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/warehouse \ name=exp1

逐个说:data指向你的 yaml;model=yolov8n.pt表示从预训练权重开始微调,这比从零训练收敛快得多,仓库这种小数据集千万别从零训;epochs=100是上限,实际会被patience提前终止;imgsz=640是输入分辨率,货箱目标如果很小,可以提到 960 甚至 1280,但显存和速度会成倍变化;batch=16按显存调,爆显存就减半;lr0=0.01是初始学习率,微调场景这个值比较稳;patience=20表示 20 个 epoch 验证指标不提升就停,这是省时间的后悔药。

4.2 关键参数怎么调:三个最影响盘点效果的旋钮

训练参数几十个,但真正影响仓库盘点效果的,我认为就三个。

第一个是imgsz。仓库货架图往往是高分辨率,货箱在整图里占比不大,640 可能让远处的小箱变成几个像素。我的经验是:如果验证时发现漏检集中在远货架,就把imgsz提到 960,同时batch减半。

第二个是数据增强里的mosaic和mixup。YOLOv8 默认开 mosaic,它把四张图拼成一张,对小目标友好,但仓库场景货箱堆叠有固定结构,过度拼接会让模型学到不真实的组合。我一般把mosaic=0.5调低,mixup保持默认或关掉。

第三个是类别不平衡。如果empty_slot样本远少于货箱,模型会倾向不预测它。可以在 yaml 里给类别加权,或者干脆多补采空货位的图。这个没有一键参数,只能靠数据层面解决。

4.3 看训练曲线:怎么判断该停还是该救

训练跑起来后,runs/warehouse/exp1/下会有results.csv和一堆曲线图。重点看三条线:train/box_loss和val/box_loss的差距,如果验证损失很早就开始上升而训练损失还在降,就是过拟合,该早停或者加数据;metrics/mAP50是主指标,仓库盘点一般能到 0.85 以上算可用;metrics/mAP50-95更严格,能反映框的精度,如果它明显偏低,说明框的位置不够准,回去检查标注质量。

如果 mAP 卡在 0.5 左右上不去,先别调参,回去看验证集的预测图。十有八九是标注框偏大、类别标错,或者验证集里有训练集没见过的货架类型。调参救不了数据问题,这是我踩过最深的坑。

4.4 导出模型:给界面和部署用

训练完的best.pt是 PyTorch 权重,界面调用可以直接用。但如果要部署到边缘设备或者追求推理速度,可以导出成 ONNX:

# 导出 ONNX,动态轴适配不同输入尺寸 yolo export model=runs/warehouse/exp1/weights/best.pt format=onnx dynamic=True simplify=True

format=onnx指定导出格式,dynamic=True让输入尺寸可变,simplify=True会做图优化,减小体积、提升推理速度。导出后的 onnx 文件可以脱离 ultralytics 环境,用 onnxruntime 加载,这对部署到没有 Python 环境的设备很关键。如果你的目标是 RK3588 这类板端,还需要进一步转成 RKNN,那是另一套流程,核心是把 ONNX 作为中间格式。

5. 可视化界面与部署:把模型变成能演示的系统

5.1 界面选型:Gradio 最快,PyQt 最像「系统」

可视化界面这块,取决于你的用途。如果是毕设演示、快速验证,Gradio 是首选,几十行代码就能做出上传图片、显示检测结果、统计数量的网页界面。如果要求是一个能打包成 exe 的桌面「系统」,那 PyQt 或 PySide 更合适,看起来更正式,但开发量大。

Gradio 的最小实现长这样:

import gradio as gr from ultralytics import YOLO model = YOLO("runs/warehouse/exp1/weights/best.pt") def detect(img): results = model(img, conf=0.3) # 统计每个类别的数量,用于盘点 counts = {} for box in results[0].boxes: cls = model.names[int(box.cls)] counts[cls] = counts.get(cls, 0) + 1 # 返回带框的图和统计文本 annotated = results[0].plot() summary = "\n".join(f"{k}: {v}" for k, v in counts.items()) return annotated, summary gr.Interface( fn=detect, inputs=gr.Image(type="numpy"), outputs=[gr.Image(type="numpy"), gr.Textbox(label="盘点结果")], title="仓库货物盘点系统" ).launch(server_name="0.0.0.0", server_port=7860)

conf=0.3是界面里的置信度阈值,比训练时略高,是为了减少误检,盘点场景宁可漏检也别把空位误报成货箱。results[0].plot()直接返回画好框的 numpy 图,省去自己画框。counts字典就是盘点结果,按类别汇总数量。server_name="0.0.0.0"让局域网内其他设备也能访问,方便演示。

5.2 部署到服务器:用 systemd 保活

界面跑起来只是第一步,要让它长期稳定运行,得用进程守护。Linux 下用 systemd 是最省心的:

# /etc/systemd/system/warehouse.service [Unit] Description=Warehouse Inventory UI After=network.target [Service] User=ubuntu WorkingDirectory=/home/ubuntu/warehouse_inventory ExecStart=/home/ubuntu/miniconda3/envs/warehouse/bin/python ui/app.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target

ExecStart要用你 conda 环境里的 python 绝对路径,否则 systemd 找不到依赖。Restart=always保证崩溃后自动拉起,RestartSec=5是重启间隔。写完systemctl daemon-reload && systemctl enable --now warehouse就能开机自启。这套配置我在多个项目里用过,比 nohup 靠谱得多。

5.3 部署到边缘设备:模型转换的注意点

如果要把盘点系统放到仓库现场的边缘盒子上,比如 RK3588 这类带 NPU 的板子,流程是 PyTorch → ONNX → RKNN。关键注意点有两个:一是导出 ONNX 时输入尺寸最好固定,动态轴在板端转换时容易出问题;二是量化,INT8 量化能大幅提速,但仓库场景里货箱颜色接近,量化后精度可能掉几个点,建议先用 FP16 验证精度,再决定要不要 INT8。板端部署的坑主要在算子支持上,转换时如果报某个算子不支持,通常需要换更简单的模型结构或者手动替换算子。

6. 避坑与排查:仓库盘点项目里最容易翻车的五件事

6.1 训练 loss 正常但 mAP 一直是 0

现象:训练日志里 box_loss 在降,但 mAP50 始终是 0 或者极低。原因:九成是data.yaml的names顺序和标注文件的 class id 对不上,或者nc写错。模型在学,但学到的类别和验证时对不上,指标自然为 0。解决:随便打开一个标注 txt,看每行第一个数字,和 yaml 里的 names 逐一对;再确认nc等于 names 的条目数。这个坑我见过太多次,改完立刻正常。

6.2 推理时框全堆在货架顶部

现象:检测结果里所有框都集中在图像上方,位置明显不对。原因:标注时用了错误的图像尺寸做归一化,比如原图是 1920x1080,转换脚本里却传了 640x640。归一化坐标是按错误尺寸算的,模型学到的框位置整体偏移。解决:重新用正确尺寸转换一遍标注,或者写个脚本批量校验归一化坐标是否都在 0 到 1 之间,超出范围的直接暴露问题。

6.3 界面能跑但一上传大图就卡死

现象:Gradio 界面上传手机拍的高分辨率图,页面转圈很久甚至超时。原因:模型推理前会把图缩放到imgsz,但读图和预处理本身在高分辨率下很耗时,加上 CPU 推理,单张图可能要好几秒。解决:在detect函数里先把输入图缩到长边 1280 再送模型,或者把imgsz设小一点。如果还是慢,考虑导出 ONNX 用 onnxruntime 推理,CPU 上通常比 PyTorch 快一截。

6.4 换了个仓库就完全盘不准

现象:在 A 仓库训的模型,拿到 B 仓库推理,漏检误检一大堆。原因:模型过拟合到了 A 仓库的货架结构、光照和货箱外观,泛化性不足。解决:这不是调参能解决的,要么在 B 仓库补采数据做微调,要么在训练时就加入多仓库、多光照的数据增强。我的习惯是训练集里至少混入两个不同货架布局的数据,哪怕每个少一点,泛化性会好很多。

6.5 部署后服务莫名其妙挂掉

现象:systemd 服务跑一段时间就重启,日志里看不出明显错误。原因:多半是内存泄漏或者显存没释放,尤其是反复加载模型或者处理大图时。解决:把模型加载放在全局只做一次,不要在每次请求里重新YOLO(...);处理完的中间变量及时释放;在 systemd 里加MemoryMax限制,超了让它重启而不是拖垮整机。这个坑比较隐蔽,加个内存监控日志会好排查很多。

7. 怎么验证这套盘点系统值不值得投入

到这一步,系统能跑、能演示了,但真正决定要不要投入生产,得看几个硬指标。我一般会做一轮小规模实测:选一个真实货架,人工数一遍作为 ground truth,然后用系统盘三遍,看三次结果的一致性和与人工的偏差。如果同一张图三次结果都不一样,说明模型不稳定,可能是置信度阈值卡在了边界上;如果稳定但和人工差得多,那是模型精度问题。

验证时重点看两个数:盘点准确率(识别正确的货箱数 / 实际货箱数)和误报率(把空位或杂物误判成货箱的比例)。仓库场景里,误报比漏检更烦人,因为漏检你还能靠人工补,误报会让你以为有货实际没有。所以阈值宁可调高一点,conf从 0.3 起,根据实测往上加。

还有一个容易被忽略的验证维度:不同光照下的稳定性。白天和夜间开灯各测一轮,如果夜间掉得厉害,要么补夜间数据,要么在摄像头端加补光。这个不是模型的问题,是采集端的问题,但会直接决定系统能不能 24 小时用。

最后说个我自己的习惯。每次训完一个版本,我都会把best.pt、data.yaml、results.csv和当时的验证图打包存一份,命名带上日期和数据集版本。因为仓库盘点这种项目,你永远不知道哪天老板会说「上个月那个版本好像更准」。留好后悔药,比事后重新训一遍省太多事。这套基于 YOLOv8 的仓库货物盘点系统,从技术路径上完全可行,成本也压得住,值不值得做,取决于你的场景里货箱类别是否稳定、光照是否可控——这两点满足,就可以动手了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询