恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
YOLOv11煤流检测与皮带识别:数据集制作与模型训练实战
首页
资讯中心
/
YOLOv11煤流检测与皮带识别:数据集制作与模型训练实战
YOLOv11煤流检测与皮带识别:数据集制作与模型训练实战
发布时间:2026/10/12 0:58:42
简介数据集涵盖煤矿井下及地面运输场景中煤与传送带的标注图像采用YOLOv11格式组织每个目标由txt文件记录类别与归一化坐标适合目标检测模型训练与验证面向矿山自动化、工业视觉等领域算法工程师和研究人员。压缩包共625个文件包含312张jpg原图、312个txt标注文件以及1个yaml类别配置文件图像与标注一一对应整体大小约39.63MB下载后可直接接入YOLO系列训练流程。数据集中标注皮带和煤两类目标平均识别率高达99.5%对皮带边缘、煤堆轮廓等特征刻画细致可支撑皮带异物检测、煤流状态识别、运输设备安全监控等场景的算法调优与效果评估。目前已有518人学习下载说明该数据集在同类资源中具备较高的实用性与认可度。文件名保留原始拍摄时间和采样点编号便于按时间回溯现场工况也能帮助研究者划分训练集与验证集时保持场景多样性。1. 煤和传送带识别数据集为什么 99.5% 这个数字要先打个问号煤矿皮带运输场景里煤流检测和皮带跑偏识别一直是两个被反复提起的需求。这块场景有个天然难点煤是黑色皮带也是黑色暗色场景下目标和背景的对比度极低很多通用检测模型在这类数据上直接翻车。所谓“煤和传送带识别数据集使用 yolov11 格式标注”本质上是把皮带和煤分别作为独立类别用 YOLO 系标签格式统一标注供目标检测模型直接训练使用。适合谁两类人一是现场做皮带保护系统、需要本地化训练检测模型的工程师二是想用现成标注数据快速迭代验证 yolov11 效果的算法开发。但 99.5% 这个数字我的态度是当作宣传值看待真实的精度取决于数据集的场景覆盖度和验证集构成读完这篇你能自己跑出真实数值。2. 数据集构成与标注规范先弄懂 YOLOv11 格式到底长什么样2.1 一个可用的煤与皮带数据集目录结构应该是这样的用 YOLOv11Ultralytics 系训练目标检测数据集的目录组织直接决定你能不能一次跑通。常见做法是分成 images 和 labels 两个根目录各自再按 train / val / test 划分。你也可以用单目录加 txt 清单的方式但工程上我一般会保持 images/labels 分离的结构因为后续做数据清洗、增量训练时更好操作。一个典型结构如下coal_belt_dataset/ ├── images/ │ ├── train/ │ │ ├── mine_0123.jpg │ │ ├── mine_0124.jpg │ │ └── ... │ ├── val/ │ │ ├── mine_1001.jpg │ │ └── ... │ └── test/ │ └── ... ├── labels/ │ ├── train/ │ │ ├── mine_0123.txt │ │ ├── mine_0124.txt │ │ └── ... │ ├── val/ │ │ ├── mine_1001.txt │ │ └── ... │ └── test/ │ └── ... ├── data.yaml └── dataset_stats.pydata.yaml 的内容是训练入口告诉 yolov11 去哪里读图、读标注以及类别名是什么。下面是最小可用的配置path: ./coal_belt_dataset train: images/train val: images/val test: images/test nc: 2 names: 0: belt 1: coal注意 path 的写法。这里如果填相对路径就一定要确保你运行训练命令的终端工作目录在 coal_belt_dataset 的上一级否则 yolov11 会报路径找不到。踩过这个坑的人不在少数问题不是因为模型不行而是路径解析失败。2.2 标注文本里的 5 个数字归一化坐标是唯一标准YOLOv11 格式标注没那么多玄学核心就是每个图片对应一个同名的 txt 文件每一行代表一个目标框。每行固定是 5 个数值用空格分隔第一个是类别 id后面四个是中心点 x、中心点 y、框宽 w、框高 h全部用归一化坐标表示范围在 0 到 1 之间。0 0.450012 0.532108 0.812344 0.124567 1 0.320001 0.410235 0.023411 0.018334第一行表示一个皮带实例类别 id 0框中心落在图片 45% 宽度、53% 高度处框宽占整图宽 81%高占整图高 12%。皮带通常是横向贯穿画面的大目标所以这个 w 值会很大。第二行是一个煤堆或煤流区域通常是小目标w 和 h 都很小。这里有个很多人容易搞混的点如果你的标注工具导出的是 VOC 格式的 xml 或者 COCO 格式的 json必须先做格式转换而不是手动改文件名硬塞进去。转换逻辑其实很简单VOC 给的是左上角 xmin、ymin 右下角 xmax、ymax 像素坐标你要先算出框宽高和中心点再除以图片宽高做归一化。转换脚本的核心部分长这样import cv2 import xml.etree.ElementTree as ET import os def voc_to_yolo(xml_path, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() yolo_lines [] for obj in root.findall(object): name obj.find(name).text class_id class_map.get(name, -1) if class_id -1: continue 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_width y_center (ymin ymax) / 2.0 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height # 边界保护避免出现 0 或 1 的极端值 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) yolo_lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return \n.join(yolo_lines)转换脚本里有三个细节值得注意第一个x_center 的计算用的是 (xmin xmax) / 2不是 xmin w/2这两者理论上等价但前者能避免浮点累积误差第二个边界保护在煤矿这类大目标场景尤其重要因为标注框超出画面边界是常态不限制会出现中心点在 0 到 1 之外的情况模型训练时直接报错第三个保留至少 6 位小数归一化后的小目标框可能只有 0.02 的宽高精度不够会导致小目标框位置抖动。2.3 类别定义只有 2 个但别把“煤”和“皮带”标混这个数据集的类别只有 belt 和 coal 两类看起来简单实际标注的时候才是真正容易出问题的地方。皮带在画面上通常是暗色背景区域煤是叠加在皮带上的暗色前景物。当煤流覆盖住皮带表面时边界是模糊的标注人员很容易把煤的框框到皮带外面去或者把皮带裸露区域标成煤。我见过一份煤与皮带数据集的标注质量报告mAP 始终在 80% 上下不去后来查出原因大约 12% 的煤框把皮带边缘也包进去了导致模型学到的是“皮带边缘特征”而不是煤本身的纹理特征。这种现象在深度学习圈子里叫标注噪声解决的办法不是重新标而是做框的 IoU 过滤——把标注框与类别语义明显冲突的样本挑出来人工复检。标注时的另一个常见误区是皮带是长条目标一张图中可能贯穿整个画面有些人喜欢拆成多段标注有些人喜欢一框到底。YOLOv11 对大目标的检测能力不差建议一框到底拆段会让模型认为皮带有断裂推理时的置信度会波动。煤的标注则相反分散的小煤块各自独立标注煤流连成片时用一个稍大的框覆盖整体密度高的区域即可不要试图把每个煤块都精确扣边那样既费人力又会让模型过拟合标注风格而不是煤本身。3. 训练前的数据准备划分数据集与质量校验脚本3.1 按场景划分而不是按文件名随机切很多人拿到数据集第一步就是 random split把所有图片打乱按 8:1:1 分成训练验证测试。这在煤与皮带场景是错的。矿上的图像数据通常来自不同摄像头、不同时段、不同光照条件随机切分会导致同一个场景片段同时出现在训练集和验证集里模型记住的是场景背景而非皮带和煤本身验证精度虚高一到新增点位就翻车。我一般按现场采集的文件夹或时间段来划分。假设原始图片按拍摄点位和日期组织划分脚本这样写import os import random import shutil from collections import defaultdict image_root raw_images train_ratio 0.8 val_ratio 0.1 # 按点位分组 scene_groups defaultdict(list) for img_name in os.listdir(image_root): scene_id img_name.split(_)[0] # 假设文件名前缀是场景id scene_groups[scene_id].append(img_name) # 按组划分整组进入训练或验证不拆开 all_scene_ids list(scene_groups.keys()) random.shuffle(all_scene_ids) train_scenes all_scene_ids[:int(len(all_scene_ids) * train_ratio)] val_scenes all_scene_ids[int(len(all_scene_ids) * train_ratio): int(len(all_scene_ids) * (train_ratio val_ratio))] test_scenes all_scene_ids[int(len(all_scene_ids) * (train_ratio val_ratio)):] # 复制图片和同名标注到目标目录 for split, scenes in [(train, train_scenes), (val, val_scenes), (test, test_scenes)]: for scene_id in scenes: for img_name in scene_groups[scene_id]: src_img os.path.join(image_root, img_name) src_lbl src_img.replace(.jpg, .txt).replace(.png, .txt) dst_img_dir fcoal_belt_dataset/images/{split} dst_lbl_dir fcoal_belt_dataset/labels/{split} os.makedirs(dst_img_dir, exist_okTrue) os.makedirs(dst_lbl_dir, exist_okTrue) shutil.copy(src_img, os.path.join(dst_img_dir, img_name)) shutil.copy(src_lbl, os.path.join(dst_lbl_dir, os.path.basename(src_lbl)))这种按组划分的方式短期看验证集上的 mAP 可能不如随机切分高但能真实反映模型在新场景下的表现。做煤流识别这种项目目标是要在不同矿井、不同角度、不同光照下都能工作场景独立是必须的。这里注意文件名前缀的假定如果原始文件名没有这个规律可以用子目录名作为分组依据。核心思路是“来自同一段视频或同一时刻连续帧的图片不能跨组”。3.2 图片尺寸统一与标注框合法性检查YOLOv11 支持自适应图片缩放但这不代表你可以把 4K 和 640x480 的图混在一起直接训练。混用会导致两个问题一是 PyTorch 的 DataLoader 会按批次内最大尺寸补齐内存消耗飙升二是小图上的标注框在归一化后语义发生变化——640 宽上的 0.5 宽和 4K 图上的 0.5 宽实际物理尺寸差远了。训练前做一次全量检查脚本很有必要。检查项包括三类图片是否能正常读取、标注文件与图片是否同名对应、标注文件里的坐标是否都在 [0,1] 区间内。import cv2 import os import numpy as np img_dir coal_belt_dataset/images/train lbl_dir coal_belt_dataset/labels/train for img_name in os.listdir(img_dir): img_path os.path.join(img_dir, img_name) lbl_path os.path.join(lbl_dir, img_name.replace(.jpg, .txt)) if not os.path.exists(lbl_path): print(f[缺失标注] {img_name}) continue img cv2.imread(img_path) if img is None: print(f[图片损坏] {img_name}) continue h, w img.shape[:2] with open(lbl_path, r) as f: lines f.readlines() for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: print(f[格式错误] {img_name} 第{i1}行: {line.strip()}) continue cls, cx, cy, bw, bh int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) if cls not in [0, 1]: print(f[类别越界] {img_name} 第{i1}行: cls{cls}) if not (0 cx 1 and 0 cy 1 and 0 bw 1 and 0 bh 1): print(f[坐标越界] {img_name} 第{i1}行: {line.strip()}) print(检查完成)图片损坏这个问题在煤矿现场数据里非常常见。井下摄像头的存储设备经常异常断电最后几帧图片文件头不完整OpenCV 读出来是 None。如果不清理训练到一半 DataLoader 崩掉前面十几个小时白跑。这个脚本很难一次性覆盖所有异常但能把最常见的几种过滤掉。3.3 data.yaml 之外的超参初值从 yolov11n 还是 yolov11s 开始煤和皮带这个任务属于目标尺度差异极大的场景皮带是大目标煤是小目标。起步模型选哪个直接影响你的时间成本和显存占用。我一般先跑 yolov11n 做 baseline原因很简单n 模型参数最少单卡可以跑较大 batch几分钟就能看到 loss 是否收敛用来验证数据质量最合适。如果 baseline 的 mAP50 能到 0.95 左右说明数据没问题如果只有 0.8先别急着换大模型回头查数据的问题。等到 baseline 确认数据无误再上 yolov11s 或 yolov11m 拉精度。训练命令的最小形式yolo detect train \ modelyolo11n.pt \ datacoal_belt_dataset/data.yaml \ imgsz640 \ epochs100 \ batch16 \ device0 \ projectruns/train \ namecoal_belt_yolo11n几个参数的含义拆开说imgsz640 是输入分辨率煤流场景建议不低于 640太小了煤块在缩略图上只有几个像素模型根本学不到纹理epochs 第一次跑 100 就够重点看前 50 轮的 loss 曲线batch 大小根据显存调整遇到 OOM 就先降到 8不要硬撑name 是实验别名同一模型跑多组对比实验时用来区分输出目录。4. 用 YOLOv11 训练煤与皮带检测训练配置与精度验证流程4.1 训练过程中的 5 个关键日志指标怎么读训练跑起来后终端会持续输出 loss、精度、召回率等指标。新手最容易犯的错是盯着 loss 数值本身看收敛没有但 YOLO 系列的 loss 是多个 loss 加权的复合值绝对值大小没有绝对意义。更应该关注的是 val 集上的 mAP50 和 mAP50-95 随 epoch 的变化。正常情况是前 10 轮 mAP50 快速爬升到 0.9 左右后面 30 轮在 0.95 上下震荡最终稳定。如果你看到 mAP50 一直在 0.5 到 0.7 之间上不去先停一下做三类排查数据标注是否大面积出错、类别定义是否重叠、场景划分是否泄露。其中标注问题最常见用上一章的检查脚本重新扫一遍特别关注框过大或过小的异常样本。训练完成后的输出目录里会有 weights/best.pt 和 weights/last.pt。best.pt 是验证集最优的权重last.pt 是最后一轮的权重。煤与皮带这类工业场景我通常直接拿 best.pt 用因为 last.pt 在训练后期容易过拟合训练集泛化能力反而更差。4.2 验证脚本计算验证集上的精确率、召回率与漏检率很多人以为跑完训练就算完事了验证集指标只是打印出来看看。不对。煤流检测的落地评价不只是 mAP关键看漏检率和误报率。井下环境中皮带撕裂或煤流异常是安全事故漏检的代价远远高于误报。用训练好的 best.pt 对验证集做批推理并输出指标from ultralytics import YOLO model YOLO(runs/train/coal_belt_yolo11n/weights/best.pt) # 在验证集上评估 metrics model.val( datacoal_belt_dataset/data.yaml, splitval, imgsz640, conf0.25, iou0.5, projectruns/eval, namecoal_belt_val ) # 打印各类别 AP print(Class AP50:) for cls_name, ap50 in zip(metrics.names.values(), metrics.box.ap50): cls_idx list(metrics.names.keys())[list(metrics.names.values()).index(cls_name)] print(f {cls_name}: {metrics.box.ap50[cls_idx]:.4f})这段代码里最关键的是 conf 参数。验证集评估时把它设成和实际部署时一致的值这样评估结果才有参考意义。训练时模型内部的置信度阈值和推理时的 conf 是两回事很多人在训练时不管在部署时把 conf 设到 0.5mAP 掉了 10 个点以为是模型不行其实只是阈值变了。验证输出里要重点看每个类别的 AP50。皮带这类大目标AP50 一般能到 0.99 以上煤的 AP50 如果低于 0.9说明模型对小目标的特征学习不到位。这时候先测试一下推理时的输入分辨率——把 imgsz 从 640 调到 960煤块的像素面积变大AP 会有明显提升。代价是推理速度变慢工业场景如果对实时性有要求这就需要在速度和精度之间取平衡。4.3 实测视频推理置信度阈值与 NMS 参数怎么联动往视频流上部署是煤流识别项目的最终归宿。推理脚本本身不复杂from ultralytics import YOLO import cv2 model YOLO(runs/train/coal_belt_yolo11n/weights/best.pt) cap cv2.VideoCapture(test_mine_video.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break results model(frame, imgsz640, conf0.35, iou0.45, device0) for r in results: boxes r.boxes for box in boxes: cls_id int(box.cls[0].item()) conf float(box.conf[0].item()) xyxy box.xyxy[0].tolist() if cls_id 0: # belt color (0, 255, 0) label fbelt {conf:.2f} else: # coal color (0, 0, 255) label fcoal {conf:.2f} cv2.rectangle(frame, (int(xyxy[0]), int(xyxy[1])), (int(xyxy[2]), int(xyxy[3])), color, 2) cv2.putText(frame, label, (int(xyxy[0]), int(xyxy[1]) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imshow(Coal Belt Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()conf 和 iou 两个参数在现场要根据视频观察调整。conf 越高漏检越多conf 越低误报越多。煤流场景我一般从 0.35 起调如果误报频繁就逐步升到 0.4如果漏检严重就降到 0.25。iou 是 NMS 的去重阈值目标重叠度高时需要调低否则多个框叠加输出影响后续逻辑判断。5. 煤与皮带识别训练里的 5 个高频坑现象、原因与解决办法5.1 训练正常但推理时皮带框乱跳现象模型在验证集上 mAP 很高但部署到视频流时皮带框忽大忽小甚至一帧能检测到、下一帧消失。原因皮带场景中训练数据的皮带框如果部分标注成“只框了可见区域”部分标注成“整条皮带”模型学到的框尺寸分布是双峰的。推理时同一帧不同位置可能同时触发两种尺度的预测NMS 压不掉产生抖动。解决重新检查标注统一皮带的框范围策略。推荐统一框完整皮带区域。如果历史标注已经无法追溯可以在 post-process 里对检测结果做时间维度的平滑滤波对连续帧的框坐标做指数平均牺牲少量实时性换取稳定性。5.2 煤和皮带同为黑色模型把皮带误检成煤现象conf0.25 时模型频繁在皮带裸露区域输出煤的检测框误报率高达 30%。原因两个类别在灰度特征上高度相似模型更多依赖上下文位置信息做区分。当验证集中的煤样本大多出现在皮带上方或表面时模型学到“皮带区域 有煤”的强先验背景稍微有一点纹理变化就触发煤框。解决这也是这类二分类的典型困境。第一步提高 conf 到 0.5 观察误报变化如果显著下降说明是置信度校准问题部署时用更高的阈值即可第二步如果 conf 提到 0.6 还压不住就要从数据层面入手专门裁剪一批“皮带裸露无煤”的负样本加入训练集告诉模型皮带本身长什么样。这是最有效但最费人工的做法。5.3 加了大量负样本反而不收敛现象为抑制误报加入一批无煤皮带图片作为负样本后训练 loss 震荡剧烈mAP 反而下降。原因数据集中原本所有图片都是正样本包含至少一个目标。加入纯负样本后类别分布从 1:1 变成了正样本与负样本比例失衡尤其是煤类目标在负样本里完全没有对应标注模型在反向传播时梯度方向冲突。解决负样本不要无脑堆。控制负样本占比在总数据量的 20% 以内并且不能整批加入。同时验证集和测试集也要保留一部分负样本否则评估时无法反映真实误报率。这类数据的正确评估指标是 F1 或误报率/漏检率联合观察而不是只看 mAP。5.4 小煤块漏检煤流分散时大面积检测不到现象煤流密集时检测正常煤量少、分布分散时几乎全部漏检。原因煤块在 640x640 输入下只有十几个像素经过 YOLOv11 的多层下采样后小目标的特征图响应极其微弱。这是 YOLO 系模型的固有问题不是参数能解决的。解决分两路走。第一路推理时用更高分辨率imgsz960 或 1280小目标在输入端的像素面积变大检测率会明显改善第二路如果硬件限制不能提分辨率就针对小目标单独训练一个检测分支把原图切片成 4 个子图分别推理再合并结果。后者工程量大但工业场景中只要人员安全或设备保护依赖这个检测就值得做。5.5 99.5% 的精度为什么换了个摄像头就变成 85%现象用某个井下摄像头采集的数据训练验证集精度 99.5%拿到另一个矿井部署后精度掉到 85% 甚至更低。原因不同摄像头的安装角度、高度、焦距不同导致皮带和煤在画面中的尺度和长宽比变化。训练数据的场景多样性不足模型过拟合了原摄像头的视角特征。解决采集数据时尽可能覆盖多个机位、多种光照条件、不同煤种。数据集文档里标注采集环境训练时按机位分组做场景划分参考 3.1 的做法。如果现场已经部署了多路摄像头可以先用原模型做自动标注再由人工抽检修正快速扩充新场景的训练数据这是业界常见的半自动迭代做法。6. 进阶用法用训练好的权重做半自动标注把新场景数据滚进训练集模型训好后最有价值的用法不是直接部署结束而是利用它加速新场景数据的迭代。煤矿现场有大量未标注的摄像头数据靠人工标注一帧要十几秒一个场景几千帧就是好几个工作日。用训练好的模型做预标注人工只需筛选修正能把周期压缩到一个小时内。核心思路是用 best.pt 对新图片做批推理把高置信度的检测结果转换成 YOLO 格式标注文本低置信度的框单独提出来让人工复核。脚本实现from ultralytics import YOLO import os model YOLO(runs/train/coal_belt_yolo11n/weights/best.pt) new_images new_scene_raw/ output_labels new_scene_labels/ os.makedirs(output_labels, exist_okTrue) for img_name in os.listdir(new_images): img_path os.path.join(new_images, img_name) results model(img_path, imgsz640, conf0.7, iou0.5, verboseFalse) lines [] for r in results: for box in r.boxes: cls_id int(box.cls[0].item()) conf float(box.conf[0].item()) if conf 0.7: # 低置信度框不写入标注文件留给人去判断 continue # 归一化坐标输出 x_center, y_center, w, h box.xywhn[0].tolist() lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(os.path.join(output_labels, img_name.replace(.jpg, .txt)), w) as f: f.write(\n.join(lines)) print(预标注完成请人工复核低置信度区域)这个流程的关键在 conf0.7。预标注阶段的置信度阈值要设置得比部署时高保险策略是“只标注确定的把不确定的留给人”。低置信度框不是没有价值它们往往是新场景里模型没见过的新形态覆盖这些才能让模型快速适应新环境。精度上预标注生成的训练数据不能直接拿来训。至少要抽样 10% 的标注结果由人复核。复核时重点看框是否贴合目标边缘、类别是否判错、有没有漏检的大目标。复核完成后再合并进原始训练集跑一轮增量训练。做煤矿现场的项目模型精度永远只是一方面真正值钱的是持续迭代数据的流程。我见过太多团队拿着一个高精度模型到一个新场景就躺平然后被现场数据打回原形。数据是这类场景的命脉训练出来的模型反过来加速数据生产形成闭环这件事做成了识别率 99.5% 就不再是宣传数字而是你实测的日常结果。希望这篇笔记帮你在自己的场景里把这条闭环跑起来。本文还有配套的精品资源点击获取