资讯动态

大块煤识别数据集构建与YOLOv5训练全流程实战

发布时间:2026/8/26 11:26:43 来源:尧图企业网站定制
简介在工业视觉领域针对特定物料的目标识别往往面临数据稀缺与场景单一的双重挑战。从目标检测的基础概念出发如何利用有限的高质量样本训练出稳定可用的模型是工程落地的关键。文章聚焦矿山场景下的煤块粒度检测需求系统讲解了大块煤识别数据集的完整构建流程包括现场图像采集、质量筛选、标注规范制定、PascalVOC格式转YOLO格式的核心坐标变换以及基于PyTorch训练YOLOv5模型的参数配置与调试技巧。同时结合实际部署中的问题分析了数据增强策略、类别映射、显存优化等常见坑点。内容覆盖从数据准备到模型验证的全链路为从事煤矿智能化、皮带异物检测及边缘计算视觉项目的开发者提供了具备工程参考价值的实践路径。 大块煤识别听上去是个比较细分的场景但真正到过选煤厂、洗煤厂或者矿井地面生产系统的人都知道这是现场特别刚需的一个需求。皮带上的大块煤如果没被及时发现轻则造成溜煤嘴堵塞重则直接砸坏破碎机齿辊、划伤皮带处理一次 downtime 的成本远不是几千块钱能打住的。我这段时间正好集中精力整理了一批煤矿现场采集的图片数据一共1767张全部按 YOLOv5 PyTorch 的标准格式做了标注专门用来训练大块煤识别模型。下面把整个数据集的构建过程、标注规范、格式转换以及训练验证的完整链路拆开讲一遍希望能给同样在做矿山视觉项目的朋友省掉一些踩坑的时间。1. 项目缘起与整体设计思路1.1 为什么要单独做大块煤识别在矿山生产环节煤炭经过采掘、运输、洗选等多道工序粒度分布非常不均匀。从井下提升上来的原煤里偶尔会混入粒度超过800mm甚至1米以上的特大块煤。这些大块煤在进入破碎站之前若不能被有效识别并处理会给后端设备带来不小的压力。我在做这个项目之前调研过现场已有的方案——有的煤矿用人工盯监控有的厂用了简单的红外对射或雷达料位计但这些方法要么实时性差、要么误报率高。真正能对“块度”进行视觉判断的还是得靠目标检测模型。而 YOLOv5 在工业场景的部署成熟度非常高从训练到 TensorRT 加速都有现成方案所以数据集按它的格式来做是当下最稳妥的选择。1.2 数据集规模的合理性分析很多人一听到只有1767张图第一反应是“是不是太少了”。这里需要解释一下工业场景数据集的特殊性。现场采集的图像与公开数据集如 COCO、VOC有一个根本差异场景高度单一背景基本锁定在皮带、煤堆、溜槽口这几个固定区域目标形态虽然多变形但类别极少不需要学习几百个类别的语义差异。1767张图如果全部用于训练配上合理的数据增强策略对于单类目标的检测任务来说是完全够用的。我的实际经验是单类别工业检测场景下1500~2500张经过精标注的图像就能训练出可用的模型。与通用场景数据集动辄几万张相比工业场景更依赖标注质量而非标注数量。另外1767这个数字也是经过筛选后的结果。最初从现场采集到的原始图片其实有2600多张但很多存在明显的运动模糊、曝光过度或镜头被煤尘遮挡的情况。人工筛掉低质量图像后剩余有效样本就是这1767张。1.3 模型选型的递进逻辑在确定 YOLOv5 之前我也对比过 Faster R-CNN、SSD 以及 YOLOv8、YOLOv9 等更新版本的模型。Faster R-CNN 虽然在小目标检测上有优势但推理速度在嵌入式设备上不太理想——选煤厂现场如果要用边缘计算盒子跑模型帧率至少要保证在 25FPS 以上Faster R-CNN 很难达到。而 YOLOv8 虽然 AP 指标普遍更高但工程化生态相对 YOLOv5 存在一些断层很多部署工具链还是优先适配 v5。提示对于矿山现场的工业项目“稳定复现”比“指标好看”重要得多。YOLOv5 的权重文件转换、自研插件支持、量化部署流程都已经经过大量项目验证这也是我最终选择它的原因。2. 现场图像采集与数据预处理细节2.1 采集场景的多样性与覆盖数据集构建的第一步不是标注而是采集。如果采集端就没有覆盖到足够的工况后面标注得再精细模型的天花板也很低。这次采集涉及的场景主要有四个主井口皮带卸料口煤流从高处落下存在大量扬尘光线忽明忽暗筛分车间震动筛前段大块煤通常在此时被分离出来背景相对干净破碎站前端给料机煤块静态堆积多于动态流动适合捕捉完整的轮廓露天储煤场输送带巡检段环境光受天气影响大存在逆光、阴影等情况。采集设备统一采用了固定在皮带上方约 3米处 的海康威视工业相机分辨率设置为 1920x1080帧率 30FPS。这里有一个关键参数——相机与皮带面的角度最好控制在 60~75度 的俯角。如果角度过直煤块的俯视图会严重压缩高度信息导致标注框的长宽比失真角度过斜又会产生严重的遮挡问题。2.2 图像去重与清晰度筛选从 2600 多张原始图像筛选到 1767 张不是简单用人眼一张张过的那样效率太低。我写了一个简单的清晰度评估脚本通过 Laplacian 算子的方差值来衡量图像的模糊程度阈值设定为 100。低于该阈值的图像自动加入淘汰列表然后再由人工进行二次确认。实际筛选过程中有一个容易被忽略的细节连续帧图像的相似度非常高。从 30FPS 的监控视频中抽帧相邻几帧的内容几乎是相同的。如果数据集里存在大量高度相似的图片训练时模型会过拟合到某些特定位置的背景上。因此我用感知哈希算法pHash计算了图像的相似度去掉了与已有图像相似度高于 97% 的冗余帧。import cv2 import numpy as np def is_blurry(image_path, threshold100.0): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) laplacian_var cv2.Laplacian(img, cv2.CV_64F).var() return laplacian_var threshold筛选之后还要对保留图像做光照归一化的预处理吗我的建议是如果训练时计划加入马赛克Mosaic增强就不需要做过于激进的归一化保留现场的明暗差异反而有助于模型在不同光照条件下的泛化能力。2.3 数据增强策略的提前规划在标注之前就规划数据增强策略是因为这会影响标注框的精读要求。比如我计划训练时使用 Mosaic、MixUp、HSV 扰动以及随机平移缩放四种增强方式其中 Mosaic 增强会将四张图拼接成一张某种程度会对目标的边缘信息产生干扰。这要求标注框必须紧贴煤块边缘不能留出过多的背景余量否则增强之后模型会学到错误的目标轮廓。3. 标注标准与实操全流程3.1 标注规范是怎么定的做数据标注最重要的事情不是打开工具开始画框而是先定标准。没有标准三个人标注出来的框可能风格完全不一样模型学出来也会很“精神分裂”。我定义的大块煤识别标准是粒度超过 300mm 的煤块视为正样本一个完整煤块对应一个边界框。这个阈值不是凭空定的而是根据破碎机入口的最大允许粒度反推出来的。小于 300mm 的煤块在现有工艺下不会对设备造成威胁如果强行标注反而会让模型把大量精力花在学习“小目标”上稀释对真正危险目标的响应权重。边界框的贴合度要求是框的上下左右边缘要尽量贴合煤块的可见轮廓允许的误差不超过 3~5 个像素。对于被遮挡或被煤粉覆盖的煤块如果可见部分低于整块的 60%直接在标注时标记为“忽略”ignore不参与最终标签文件生成。对于堆叠在一起的多块大煤处理原则是每个可见的独立煤块都单独标注若是粘接成一个整体的煤簇则按一个目标标注。判断“独立”还是“整体”的视觉依据是煤块之间是否有明显的深色缝隙——如果缝隙清晰可见说明是两个目标如果边界完全融合无法辨别就按一个目标处理。3.2 标注工具选型对比数据标注工具的选择直接影响标注效率。我对比了 LabelImg、X-AnyLabeling、CVAT 和 Roboflow 四个工具最终选择了 LabelImg 作为主力工具。工具优点缺点适用场景LabelImg轻量、离线可用、格式兼容性最好功能单一、无自动辅助单机小批量标注X-AnyLabeling支持 SAM 自动分割辅助模型加载慢、依赖较重大目标精细标注CVAT支持多人协作、功能全面需要服务器部署团队并行标注Roboflow在线操作、内置增强数据需上传、有隐私风险非敏感场景由于本次数据集涉及煤矿现场图像存在数据安全方面的考虑不适合上传到公开的在线标注平台。最终采用的方案是用 LabelImg 做预标注再用 X-AnyLabeling 对困难样本进行精细调整。3.3 LabelImg 标注实操流程LabelImg 的安装和启动很简单但有几个操作细节需要注意pip install labelImg labelImg打开软件后要注意设置三个地方默认标注保存格式选择 PascalVOCXML格式因为后续转换到 YOLO 格式需要借助转换工具PascalVOC 的兼容性最好勾选“使用默认类别”预先在predefined_classes.txt文件中写入large_coal避免每次都要手动输入类别名开启自动保存模式防止标注过程中因软件闪退丢失进度。标注的时候按W键创建边界框框好一个目标后按CtrlS保存再按D键切换到下一张图。这样的键鼠配合熟练以后一张图平均花 40~60 秒可以完成标注。这里要特别提一个视觉错觉问题。煤块在皮带上常见的状态是表面反光、边缘与背景深浅相近人眼在盯了几十分钟后很容易出现疲劳误判。我的做法是每标注 45 分钟休息 5 分钟同时桌面背景调成灰色——白色背景会让高光区域的煤块边缘识别难度变大灰度背景的对比度更接近煤块的真实轮廓。3.4 双人复核机制的必要性整个数据集标注完成后我组织了一个三人小组进行交叉复核。流程是标注者A完成自己的图片后不允许马上进入下一批而是先随机抽取10%的图片交由标注者B进行逐框检查重点检查四类问题漏标有明显煤块未框选、框偏边界框明显偏离轮廓、尺寸错误把小块煤误标为大块、类别错误把煤矸石框成了煤块。复核阶段发现的主要问题是漏标数量占比约为 6% 左右这比想象中要高。尤其出现在煤块堆叠区域模型很容易漏掉被遮挡的煤块。复核后这一比例降到 0.5% 以下。4. 数据集格式转换与目录结构整理4.1 从 PascalVOC 到 YOLOv5 格式的坐标转换LabelImg 默认保存的 PascalVOC 格式中边界框坐标记录的是左上角和右下角的像素坐标。YOLOv5 训练时使用的是未归一化的中心点坐标与宽高并且必须归一化到 0~1 之间。这两者之间的转换是数据集构建的核心环节不容有失。转换公式如下x_center (x_min x_max) / 2 / image_widthy_center (y_min y_max) / 2 / image_heightwidth (x_max - x_min) / image_widthheight (y_max - y_min) / image_height对应到实际代码用 Python 脚本批量处理所有 XML 文件生成对应的 txt 标签文件。文本标签的格式是class_id x_center y_center width height每个目标占一行所有数值保留六位小数。import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_file, out_file, class_list): tree ET.parse(xml_file) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_list: continue cls_id class_list.index(name) bbox obj.find(bndbox) x_min int(bbox.find(xmin).text) y_min int(bbox.find(ymin).text) x_max int(bbox.find(xmax).text) y_max int(bbox.find(ymax).text) x_center (x_min x_max) / 2 / img_w y_center (y_min y_max) / 2 / 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}) with open(out_file, w) as f: f.write(\n.join(lines))转换之后一定要抽检。我随机抽取了 20 张图片把转换后的 txt 标签加载回图像上检查边界框是否跟原标注一致。加载的方式有两种一是用 OpenCV 直接画框叠加看效果二是用 YOLOv5 仓库自带的detect.py的绘图逻辑验证。如果两者有任一位置对不上说明转换脚本的坐标系变换存在问题必须排查后再继续。4.2 目录结构与数据集划分YOLOv5 训练时对数据集的目录结构有一定约定虽然不是强制但遵循它的目录规范会让你在配置 yaml 文件时省不少事。整理后的目录结构如下coal_dataset/ ├── images/ │ ├── train/ │ │ ├── img_0001.jpg │ │ ├── img_0002.jpg │ │ └── ... │ ├── val/ │ │ └── ... │ └── test/ │ └── ... ├── labels/ │ ├── train/ │ │ ├── img_0001.txt │ │ ├── img_0002.txt │ │ └── ... │ ├── val/ │ │ └── ... │ └── test/ │ └── ... └── coal_data.yaml划分比例用的是 8:1:1即 1414张 作为训练集、176张 作为验证集、177张 作为测试集。这里我要强调一点数据划分绝对不能有随机性重复即同一张图像的增强变体不能同时出现在训练集和验证集中。否则验证指标会被严重虚高模型上线后性能打回原形。对矿山场景的数据划分我建议采用按时间段切割的方式。例如前 80% 的时间段采的数据用于训练后 20% 用于验证。这样能真实检验模型在新场景下的泛化能力而不是只对“见过的图”做记忆。4.3 data.yaml 配置的坑data.yaml 是 YOLOv5 读取数据集信息的入口里面指定的路径可以是绝对路径也可以是相对于项目根的相对路径。最容易踩的坑是 Windows 和 Linux 下路径分隔符的差异。train: /data/coal_dataset/images/train val: /data/coal_dataset/images/val test: /data/coal_dataset/images/test nc: 1 names: [large_coal]其中nc是类别数量这里只有一个大块煤类别所以设为 1。如果你后续想区分“大块煤”和“超大块煤”两个类别就要将 nc 改为 2并调整 names 列表。但这需要在数据标注时就按两个类别来标否则需要在后期做标签合并转换白白多花一遍功夫。5. YOLOv5训练环境搭建与验证实验5.1 PyTorch环境安装版本对照训练之前先把环境搭好。YOLOv5 对 PyTorch 的版本适配比较敏感过于新的 PyTorch 版本如 2.5 以上在某些旧版 YOLOv5 代码上会触发算子兼容性问题。我自己测试的稳定组合是Python 3.8 或 3.10PyTorch 1.13.1 或 2.0.1CPU/GPU均可训练建议用GPUtorchvision 0.14.1 或 0.15.2CUDA 11.7如果使用GPUGPU 环境下安装 PyTorch 的示例命令如下。pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu117注意尽量不要用最新版 PyTorch 直接跑 YOLOv5 的旧分支容易出现torch.cuda.ampAPI 变动导致的训练中断。如果项目没有历史遗留依赖问题也可以直接拉取 YOLOv5 当前更新的分支它对新版 PyTorch 做了适配。5.2 训练参数的选择与超参数调整数据量只有 1700 多张属于典型的小数据集训练场景。训练参数不能照搬 COCO 的默认配置需要针对性调整。我的训练命令如下python train.py --img 640 --batch 16 --epochs 100 --data coal_data.yaml --weights yolov5s.pt --device 0这里关键参数的选择逻辑--img 640输入分辨率。现场相机原始是 1920x1080缩放到 640 分辨率训练既能保留大块煤的轮廓特征又不至于让计算量过大。如果是识别粒度小于 50mm 的小煤块则建议使用 1280 分辨率--batch 16显存 12GB 的情况下yolov5s 模型用 batch 16不会爆显存。如果 GPU 显存只有 8GB改成 8--epochs 100100 轮对小数据集已经足够。我在训练到第 79 轮时 mAP 就趋于收敛早停机制patience20会自动截断--weights yolov5s.pt使用 COCO 预训练权重做迁移学习。注意这个参数会从官方仓库下载权重文件如果网络不稳可以手动下载后放到weights/目录。超参数文件hyp.scratch-low.yaml中有几个值我做了调整hsv_h、hsv_s、hsv_v适度增强了饱和度扰动因为煤矿现场的颜色变化比较大fliplr: 0.5水平翻转增强但垂直翻转必须关掉flipud: 0.0因为煤块在皮带上的方向是受重力约束的垂直翻转会生成现实中不可能出现的样本mosaic: 1.0保持默认开启对提升小目标检测有很大帮助。5.3 训练结果指标怎么看训练结束后的指标输出中重点关注三个值mAP0.5、mAP0.5:0.95、Precision和Recall。我在这个数据集上的训练结果如下指标数值mAP0.50.963mAP0.5:0.950.871Precision0.958Recall0.944对于单类别检测mAP0.5 超过 0.95 属于比较理想的状态。这说明模型在验证集上对“大块煤”这个目标的识别能力已经相当稳定。不过指标好看不代表一定能上线。模型最终检出的效果还需要在测试集上做逐帧可视化确认重点关注有没有把煤堆边缘的阴影误检为大块煤或者把破碎机衬板误检为目标。6. 常见问题排查与经验分享6.1 标注阶段的问题与对策标注阶段最容易犯的错误是“背景连片”的问题。煤堆上如果同时堆积了粒度较大的煤块人眼很容易把它们看成连成一片的整体。此时如果直接画一个大框把整片区域框起来模型学习到的是“大片煤”而不是“单个大块煤”。可行的解决技巧是在标注时先画出每个独立煤块的边界框框的边缘稍微内收 2~3 个像素人为制造间距让检测器更好地区分邻近目标。另外一种常见的错误是手滑产生的“零面积框”即打开软件后没有框选就直接保存生成的 XML 文件中坐标全是 0。这种标签在训练时会导致 loss 直接变成 NaN。写一个简单的数据清洗脚本可以避免这个问题def check_zero_bbox(txt_file): with open(txt_file, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: return True x, y, w, h map(float, parts[1:]) if w 0 or h 0: return True return False6.2 训练阶段常见报错YOLOv5 训练中遇到的报错90% 来自标签文件和配置文件。这里列出三个我实际踩过的坑第一个是AssertionError: Label class xx exceeds nc1。这个报错的意思是标签文件里出现了类别编号大于或等于 1 的类别。原因是在 VOC 转 YOLO 时类别映射表没有排除ignore类把ignore也写成了编号 1。检查predefined_classes.txt或者转换脚本里的 class_list 即可修复。第二个是CUDA out of memory。如果 12GB 显存跑不了可以在训练命令里加上--cache-images把图像缓存到内存中降低显存峰值或直接减小 batch 和--workers线程数。不要一开始就换更大显存的显卡——大多数情况是 batch 设置过大或数据加载通道分配不合理。第三个是训练 loss 在初期就出现nan。我排除了学习率设置的问题后发现原因是数据集中有一张图片的标签行数异常多超过了 500 行。后来定位到是标注过程中误将重复标注保存了多次。清洗脚本里需要加上“同一个 txt 文件中重复坐标过滤”的逻辑。6.3 模型部署时对数据集的重新审视模型训练完毕后在部署环节会遇到一个新的问题现场相机视角、安装高度与数据集采集时不一致。我在实际部署时遇到过“模型在测试集上 mAP 达到 0.95但部署到现场后对堆积层的识别效果很差”的情况。原因是训练数据中约 70% 的样本是皮带运输状态下的动态煤流而现场特定位置的相机是斜视堆煤区的静态煤堆——目标姿态分布发生了变化。解决这个问题的方式是二次数据补充从现场部署位置的实时视频流中再抽取 200~300 帧不同角度、不同堆料高度的图像做标注后追加到原有数据集中重新训练。这个过程是不可避免的也说明了“一次标注、终身使用”在工业场景中并不现实数据集的持续迭代和补充是模型可靠性的核心保障。6.4 大块煤数据集后续的扩展方向项目做到这里数据集本身已经可以支撑大块煤识别的基础需求。但后续可以做几个方向的扩展。第一是增加类别维度。除了“大块煤”还可以将“煤矸石”“锚杆”“托盘”等异物纳入标注范围因为选煤厂最关注的不仅是粒度异常还有混入原煤中的金属异物。第二是标注形式升级。从 2D 边界框升级为实例分割掩码用 YOLOv8-seg 或 Mask R-CNN可以更精确地计算煤块的实际面积和粒度分布。当前 2D 检测只能告诉现场“这个位置有个大块”但无法回答“这块煤到底有多大”后者对破碎机自动控制系统的指导价值更高。第三是与边缘计算设备联动。我测试过将当前模型转换到 TensorRT FP16 精度在 Jetson Orin Nano 上推理速度可以达到 30ms 左右完全满足现场实时检测的要求。数据集格式从一开始就采用 YOLOv5 PyTorch使得后续接入边缘部署平台的路径非常平滑。写在最后的一个建议做了几条生产线的大块煤识别数据集和模型之后我最大的感受是工业场景的数据集核心不在数量多而在“准”。标注的精细程度、边界框的贴合度、类别的明确界定这些看起来琐碎的细节最终都会直接反映在模型的稳定性上。如果让我重新做一次这个项目我会在标注环节投入更多时间制定验证用例——比如专门画一批只有边缘轮廓的煤块图用它们来检验标注人员的判断标准是否一致。这个动作花费的时间不多但对数据集质量的提升是立竿见影的。希望这次的实操细节能帮到正在做矿山视觉方向的朋友少走几步弯路。本文还有配套的精品资源点击获取

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

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

免费获取报价