YOLOv8训练托盘检测数据集:从解压到部署全流程
发布时间:2026/9/16 6:16:03来源:尧图网络
简介面向仓库管理、物流自动化和目标检测学习者的托盘检测专用数据集同时提供VOC与YOLO两种主流标注格式可直接用于YOLO、Faster R-CNN、SSD等模型训练、验证与性能对比解决仓库托盘识别场景的数据集准备问题。压缩包约192.54MB共2000个文件图片、xml与txt分别存放在JPEGImages、Annotations、labels三个文件夹目录结构清晰标签类别仅含托盘tuopan1182张高分辨率仓库场景图像的标注框总数达38971个平均每张约33个框标注密度高且未做数据增强。已有129人浏览学习。读者可获得完整规范的矩形框标注文件便于直接配置目标检测训练流程高清原图保留了真实仓库环境下的光照、遮挡与堆叠细节可为库存盘点、自动化叉车引导、物流分拣等应用提供可靠的样本支撑。1. 托盘检测为什么值得单独做一个数据集仓库自动化改造里托盘的识别属于那种“看起来不难、一落地就翻车”的场景。AMR、叉车、机械臂抓取第一步都是找到托盘的确切位置和朝向而仓库光照差、托盘堆叠、破损和胶膜反光让通用目标检测模型在 COCO 上表现再惊艳落到现场也经常出现漏检和框偏移。这 1182 张的托盘检测数据集正是在这类场景下收集的只聚焦托盘这一类目标标注覆盖了空托盘、带货物托盘、不同颜色和不同堆叠状态的样本配套 YOLO 和 VOC 两套标注省掉最耗时的人工转格式环节。它的价值不在于把 mAP 刷到多高而在于给仓库场景的感知提供一个可复用的基线。对做 AMR 导航、仓储盘点或物流自动化的工程师来说拿这个数据集做预训练、做算法选型、做标注规范的验证都比从零开始标数据快得多。对刚入行目标检测的人来说它也是一个能完整走通“数据 → 训练 → 评估”流程的小规模数据集——1182 张图不大单卡几分钟一个 epoch足够频繁调参试错也足够暴露出数据质量、格式转换、类别不平衡这些真实项目里必然遇到的问题。下面从解压开始把这个数据集从到手到训出模型的全过程拆开讲。2. 解压后先别急着训练把 YOLO 和 VOC 两套标注核对清楚2.1 压缩包的目录结构长什么样解压后第一件事不是找训练脚本而是把目录结构完整看一遍。一个规范的物体检测数据集压缩包通常包含 images 和 annotations 两大块VOC 格式用 XML 存放标注YOLO 格式用 TXT 存放标注文件名一一对应。unzip 仓库内托盘检测数据集yolovoc格式1182张.zip -d pallet_dataset cd pallet_dataset find . -type f | head -50如果压缩包里的组织结构不那么规整比如图片和标注文件混在同一个目录也不用急着手动排序先看文件后缀分布find . -type f \( -name *.jpg -o -name *.jpeg -o -name *.png \) | wc -l find . -type f \( -name *.xml \) | wc -l find . -type f \( -name *.txt \) | wc -l图片数量、XML 数量、TXT 数量应该基本一致都是 1182 左右。如果 XML 和 TXT 数量明显少于图片说明标注文件缺失后续训练时要么先清洗数据要么得重新标注缺失的样本。这一步也要确认图片和标注文件的文件名是否一一对应——比如图片叫pallet_0001.jpg标注应该叫pallet_0001.xml和pallet_0001.txt文件名不匹配会导致训练时报assertion failed或者直接跳过该样本。2.2 用 Python 脚本核对 VOC 标注结构VOC 格式的 XML 标注里关键节点是size和object。size记录图片的宽、高、通道数YOLO 格式做归一化时要用它做分母object节点下的bndbox是目标框的左上角和右下角坐标。直接读 XML 的方式import os import xml.etree.ElementTree as ET def parse_voc_xml(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.find(width).text) height int(size.find(height).text) objects [] for obj in root.iter(object): name obj.find(name).text bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) objects.append({name: name, bbox: [xmin, ymin, xmax, ymax]}) return width, height, objects xml_dir pallet_dataset/annotations/xmls for xml_file in os.listdir(xml_dir)[:5]: w, h, objs parse_voc_xml(os.path.join(xml_dir, xml_file)) print(f{xml_file}: {w}x{h}, {len(objs)} objects) for obj in objs: print(f {obj[name]}: {obj[bbox]})这段代码做两件事一是确认 XML 能正常解析不存在字段缺失或类型错误二是看一眼每个 XML 里的目标数量和类别名能直观判断数据集的类别分布。如果输出显示某些 XML 里bndbox的值为空或者坐标值明显超出图片尺寸那这批标注需要清洗后再进入训练流程。2.3 YOLO 格式的归一化坐标验证YOLO 格式的 TXT 每行代表一个目标格式是class x_center y_center width height四个坐标值都归一化到 01 之间。不要凭肉眼判断用脚本做越界检查import numpy as np def parse_yolo_txt(txt_path): boxes [] with open(txt_path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, xc, yc, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) boxes.append([cls, xc, yc, w, h]) return boxes def check_normalized(box): xc, yc, w, h box[1], box[2], box[3], box[4] if not (0 xc 1 and 0 yc 1 and 0 w 1 and 0 h 1): return False if xc - w/2 0 or xc w/2 1 or yc - h/2 0 or yc h/2 1: return False return True txt_dir pallet_dataset/annotations/txts issue_count 0 for txt_file in os.listdir(txt_dir): boxes parse_yolo_txt(os.path.join(txt_dir, txt_file)) for box in boxes: if not check_normalized(box): issue_count 1 print(fIssue in {txt_file}: {box}) print(fTotal issues: {issue_count})归一化坐标越界是 YOLO 系列模型训练时报 NaN loss 的最常见原因之一。中心点坐标落在 01 之外还好理解容易忽略的是x_center - w/2 0这种框超出图像边界的情况。这类问题在 VOC 转 YOLO 时很常见因为 XML 里的 xmin、ymin 是从 0 开始计数而某些转换脚本会少减 1 或多减 1导致边界框整体偏移了几个像素。对 1182 张图的数据集这一步检查花不了两分钟但能省掉后面排查 NaN loss 的半天时间。3. 用 YOLOv8 在托盘数据集上跑通训练闭环3.1 准备 labels 和 images 的标准 YOLO 目录无论是原始压缩包已经分好train/val/test还是只有全量图片和标注都建议先整理成 YOLO 系列模型标准的数据目录结构再写 data.yaml。Ultralytics YOLOv8 的目录规范是根目录下分images和labels下面各自按train/val/test划分cd pallet_dataset mkdir -p yolodata/images/train yolodata/images/val yolodata/images/test mkdir -p yolodata/labels/train yolodata/labels/val yolodata/labels/test划分比例常见做法是 8:1:1 或 8:2:0。1182 张的数据量不大val至少留 100 张以上才有统计意义test如果没划可以先用val替代做预测验证。划分时注意按文件名 hash 或随机种子切分不要直接按目录切——如果原始图片是按拍摄批次组织的按目录切会导致 train 和 val 的光照、背景高度相似评估结果虚高。写 data.yaml 时只需要指定路径和类别名# pallet_dataset/yolodata/pallet.yaml path: /home/user/pallet_dataset/yolodata train: images/train val: images/val test: images/test nc: 1 names: [pallet]托盘检测是单类别任务nc设成 1names只需要一个pallet。如果原始数据里另外标了forklift、goods等类别要按压缩包里的说明确认类别名顺序VOC 转 YOLO 时类别索引是按下标排的顺序错了训练不会报错但预测结果会错位。3.2 训练的最小命令和参数含义用 Ultralytics YOLOv8 训练命令行直接跑cd pallet_dataset yolo detect train \ datayolodata/pallet.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ batch16 \ device0 \ projectruns \ namepallet_yolov8nmodelyolov8n.pt是 COCO 预训练权重YOLOv8 提供 n/s/m/l/x 五档n 最轻量、速度最快x 精度最高但耗时翻倍。1182 张的数据集属于小规模用 n 或 s 是合理的起始选择——用 x 容易在训练中期就过拟合val 指标反而下降。imgsz640是训练时的输入分辨率托盘这种目标在画面里占比较大640 已经够用如果现场图片中托盘离得远、在画面里偏小可以提高到 960但显存占用和训练耗时都会上升。device0指使用第一块 CUDA GPU没有 GPU 的机器上改成devicecpu但训练速度会慢一个数量级。训练过程里 Loss 曲线稳步下降、val 指标随 epoch 增加缓慢爬升属于正常现象。如果发现训练 loss 降下去了但 val loss 到某个 epoch 后开始回升说明模型开始过拟合应该降低 epochs 或增加数据增强。3.3 训练中必看的三个输出信号训练日志里三列输出值得关注box_loss、cls_loss、dfl_loss。对托盘检测这类单类别任务cls_loss通常会降得很快因为二分类问题本身简单box_loss和dfl_loss决定最终框的位置精度这两项下降缓慢是正常的但如果在 20 个 epoch 后还没开始下降要考虑学习率设置或数据标注质量。另外一个必看的输出是训练完成后自动生成的confusion_matrix.png和results.png。单类别任务所有目标都是正样本混淆矩阵重点看背景被误检成托盘的比例——如果背景误检率高于 5%说明训练数据里托盘之外的负样本不够需要在现场采集更多不含托盘的图片加入训练。4. 影响托盘检测精度的关键参数调优清单4.1 batch size、imgsz 和显存的关系batch size 是显存和梯度稳定性的折中。YOLOv8 默认的batch16在 12GB 显存的显卡上搭配 yolov8n 和 imgsz640 是可以跑得动的如果显存不够优先降 batch 而不是降 imgsz。托盘检测的框通常比较大imgsz 从 640 降到 480 对标注框的影响不大但 batch 从 16 降到 8 会让梯度估计的噪声变大可能影响收敛稳定性。如果显存特别紧张用batch-1让 Ultralytics 自动探测当前 GPU 能承受的最大 batchyolo detect train \ datayolodata/pallet.yaml \ modelyolov8s.pt \ epochs80 \ imgsz640 \ batch-1 \ device0batch-1会自动做一次前向推导来估计可用的 batch size这对刚开始用不熟悉的数据集时很有帮助。训练完成后查看runs/detect/pallet_yolov8s/args.yaml里面记录着实际使用的 batch 值下次可以手动指定。4.2 托盘数据集的锚框和分辨率偏好托盘的一个显著特点是宽高比极端——标准托盘的长宽比约为 1.2:1但实际拍摄时由于透视角度画面里托盘的宽高比可能从 0.5 到 2.0 都有。YOLOv8 是 anchor-free 设计不需要像 YOLOv5 那样手动调 anchor但你仍然可以通过观察标注框的分布来确认模型没有在异常尺度上浪费参数。用一段脚本统计标注框的宽高分布import os import numpy as np def collect_box_shapes(labels_dir): shapes [] for txt_file in os.listdir(labels_dir): with open(os.path.join(labels_dir, txt_file), r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue w float(parts[3]) h float(parts[4]) if 0 w 1 and 0 h 1: shapes.append([w, h]) return np.array(shapes) shapes collect_box_shapes(yolodata/labels/train) print(box width mean/median:, shapes[:, 0].mean(), np.median(shapes[:, 0])) print(box height mean/median:, shapes[:, 1].mean(), np.median(shapes[:, 1]))如果框宽的中位数明显小于 0.3说明数据集中很多托盘在画面中占比较小这时应该考虑把imgsz提高到 960 甚至 1280让小目标在特征图上占据更多像素。如果框宽中位数在 0.5 以上说明托盘近景居多640 分辨率足够。4.3 类别不平衡和样本筛选策略虽然托盘数据集是单类别但样本内部可能存在不平衡例如空托盘样本 800 张、带货物托盘 382 张。模型默认会把两者当成同一个类但如果需要区分“空托盘”和“载货托盘”两个状态就要把类别改成 2 类并重新标注。从实际业务出发如果现场只需要知道“这里有一个托盘”没有细分状态单类别训练更稳。如果确实需要细分先统计原始数据集里每个类别的图片数对样本少的类别做离线增强。1182 张的规模下翻转、旋转、亮度扰动就够用过重的增强反而会让模型学到非真实特征。5. VOC 标注转 YOLO 格式的坑和批量转换脚本5.1 最容易出错的坐标转换逻辑VOC 的坐标是绝对像素值YOLO 需要的是相对归一化坐标转换公式并不复杂x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height但实际转换时三个细节容易出错。第一xmin 和 xmax 是浮点数还是整数——如果原始标注用整数转换前先确认 xmax 是否需要加 1Pascal VOC 标注的像素范围是 0width-1某些工具生成的标注会多 1加错一个像素放在归一化坐标里差别极微小但批量转完后所有框都会偏移同样的大小。第二类别索引从 0 开始数据集里的类别名要先排序再分配索引pallet是第一个类别索引为 0。第三转换完的坐标精度保留到 6 位小数就足够不需要保留更多因为 YOLO 格式的浮点精度要求不高。5.2 批量转换脚本模板一个能直接改用的批量转换脚本import os import glob import xml.etree.ElementTree as ET def voc_to_yolo(xml_file, class_names): tree ET.parse(xml_file) root tree.getroot() size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_names: continue cls_id class_names.index(name) bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) return lines class_names [pallet] xml_dir pallet_dataset/annotations/xmls txt_dir pallet_dataset/annotations/txts os.makedirs(txt_dir, exist_okTrue) for xml_file in glob.glob(os.path.join(xml_dir, *.xml)): base os.path.splitext(os.path.basename(xml_file))[0] lines voc_to_yolo(xml_file, class_names) if lines: with open(os.path.join(txt_dir, f{base}.txt), w) as f: f.write(\n.join(lines))转换后打开一个 txt 对比原图确认框的位置和大小与图中托盘贴合。注意脚本里class_names是硬编码的如果数据集里还有别的类别要按类别名字典顺序排列否则索引会错。5.3 转完格式后用训练脚本自检Ultralytics YOLOv8 在做数据校验时会检查 labels 和 images 的对应关系、类别索引是否越界这些错误会在训练启动时直接抛出yolo detect train \ datayolodata/pallet.yaml \ modelyolov8n.pt \ epochs1 \ imgsz640 \ batch4 \ device0只跑 1 个 epoch 就能完成数据加载和校验。如果在这个阶段报了“found X bad labels”之类的警告说明部分 txt 标注有问题按警告里的文件路径定位修复。数据量只有 1182 张这一步最多几分钟但能把格式问题全部暴露出来。6. 训练完成后如何验证托盘检测的实际效果6.1 用 val 集做一次定量评估训练完成后 Ultralytics 会自动在 val 集上进行评估输出结果在runs/detect/pallet_yolov8n/目录下的val_batch0_pred.jpg和results.csv里。results.csv 里每行对应一个 epoch 的验证结果重点看mAP50和mAP50-95两列。对于托盘这种单一类别、尺度变化不小的目标mAP50能到 0.95 以上算优秀0.85 以上算可用。mAP50-95低于 0.7 一般意味着框的回归精度不够此时先往上调 imgsz 或者换更大模型。6.2 PR 曲线和置信度阈值的实际意义模型权重可在runs/detect/pallet_yolov8n/weights/best.pt找到用它跑一次性预测yolo detect predict \ modelruns/detect/pallet_yolov8n/weights/best.pt \ sourcetest_images/ \ conf0.5 \ saveTrue \ projectruns \ namepallet_test参数conf控制置信度阈值。在仓库现场部署时托盘漏检的代价比误检高——漏检意味着机器人撞上或绕行误检只是多一次确认——所以阈值通常调到 0.30.4 而不是默认的 0.25。用conf0.3跑一批现场图片观察误报数量如果背景误报多对目标区域不做筛选就把阈值往 0.6 回调这个取舍没有标准答案取决于业务上更难以容忍哪一种错误。6.3 快慢两种验证方式定量验证结束后用下面这段脚本做一次慢速检验把预测结果和标注框画在同一张图上直接观察偏差from ultralytics import YOLO import cv2 model YOLO(runs/detect/pallet_yolov8n/weights/best.pt) results model.predict(test_images/, conf0.4) for i, r in enumerate(results): img r.plot() cv2.imwrite(fvisual_check_{i}.jpg, img)生成的可视化结果里标注框为细线、预测框为粗线叠在一起能直观看出边界贴合度。预测框始终比标注框大一圈说明训练数据里标注偏紧实际部署时在预测结果上加大约 5% 的边距即可预测框小一圈同理反方向扩展即可。这一步不是为了调参而是为了确认模型输出的后处理是否需要按照现场传感器安装位置做偏移修正托盘检测最终落地上也常用这种方式把模型输出和实际机械臂抓取点对齐。本文还有配套的精品资源点击获取
网站建设高端定制企业官网