资讯动态

智慧矿山井下安监数据集构建:四类目标检测与YOLO训练实战

发布时间:2026/10/21 14:50:07 来源:尧图企业网站定制
矿下作业的安全管理一直是智慧矿山建设的硬骨头。我最早接触这个方向是给某矿业集团的智能安监系统做视觉识别模块。当时甲方提了一个非常具体的需求不只要识别工人有没有戴安全帽还要看清现场人员的实时位置、巷道里应急指示灯的状态以及工人随身自救器的佩戴情况。这几个目标混在一起行业内能直接用的数据集几乎没有。后来我们花了大半年自采自标攒出了一套4369张图片、涵盖人员、安全帽、指示灯、自救器4个类别的数据集并整理成了VOC和YOLO两种通用格式。这篇文章就把这套数据集的构建思路、标注细节、格式差异和训练实测一次性讲透。内容覆盖数据采集的井下环境约束、四类目标的标注决策、VOC与YOLO格式的转换坑点以及用YOLO系列训练后的真实精度表现。目标读者是正在做智慧矿山、工业安监、或者准备自建数据集做目标检测的算法工程师和学生哪怕你完全没碰过矿井数据里面的踩坑经验也能用在其他工业视觉项目上。1. 智慧矿井为什么需要同时检测这4类目标很多做安监算法的人一上来就盯着安全帽识别觉得戴没戴帽子是最关键的。但实际去过矿上你就知道井下的安全管理是多维度的。单独检测安全帽只能回答工人是否违规这一个问题而无法回答井下现在有多少人这些人是否具备应急自救条件逃生通道指示灯是否正常工作这些更核心的问题。1.1 四类目标背后的安全管理逻辑我按真实业务价值把这4类目标重新排了个序方便你理解数据集设计的逻辑人员这是所有安监系统的底账。不管是确认作业区域有没有人滞留、统计交接班人数还是事故发生时判断被困位置都依赖对人员的精准检测。井下人员检测的难点在于工人着装高度相似、矿灯造成局部过曝、以及人员之间经常互相遮挡。安全帽这是合规检查的核心项。矿下安全帽和普通工地安全帽外观差异其实很大它有专门的下颏带、顶部加强筋和矿灯卡座侧面看轮廓更宽。检测模型的难点在于工人的帽子经常和煤壁、设备颜色融为一体尤其在低照度环境下。指示灯巷道里的应急指示灯、方向指示牌、设备状态灯属于典型的小目标检测。指示灯在画面里可能只有十几个像素却承担着逃生指引的关键功能。这类目标对模型输入分辨率的要求非常高低分辨率下基本必漏。自救器这是最容易被人忽略、但实际上是救命目标的类别。自救器是矿工随身佩戴的应急供氧装置通常挎在腰间或斜背在肩上体积不大形状和灭火器有点像但更扁平。检查自救器是否佩戴本质上是检查工人的应急自救能力是否落实到了个人。1.2 多任务检测比单任务检测的工程优势之前在另一个项目里我们单独部署了一套安全帽检测模型精度做到95%以上看起来很不错。但客户真正拿到现场用的时候发现系统只能提示有人没戴帽却无法联动定位到具体人员、无法判断此人是否佩戴自救器、无法确认附近指示灯是否正常。结果就是安保人员接到报警后还要自己对着监控画面找半天人在哪。把人员、安全帽、指示灯、自救器放进同一个模型里检测工程上最大的好处是特征共享。井下低照度、多粉尘、强反光的场景特征是通用的一个模型提取这些底层特征后分叉给4个检测头使用整体推理速度远快于跑4个独立模型。另外多类别一起训练还能互相纠错比如人员框应该包含安全帽如果模型的检测头在人员框内部找不到帽子说明可能存在漏检这种空间关系约束在逻辑上更容易做后处理。所以这4类目标不是随手凑的而是矿山安监系统做闭环管理的最小必要集合。2. 数据集构成拆解4369张、4类别、两种格式到底意味着什么刚拿到这套数据集的朋友第一反应往往是4369张够不够训VOC和YOLO格式到底有什么区别。我把数据集的整体情况拆开讲讲。2.1 样本分布与标注框数量这套数据集一共4369张图片共标注了4个类别的目标框我按类别统计了大致分布类别中文标签标注框数量约出现的场景特征person人员7500行走、站立、弯腰作业、遮挡helmet安全帽6800佩戴在头部、拿在手中、放在设备上indicator指示灯3500巷道壁、设备面板、应急出口上方self-rescuer自救器2200斜挎腰间、佩戴状态、放在架子上人员和安全帽的标注框数量最多因为它们是一张图里几乎必然出现的主体目标指示灯框数中等但单框面积极小属于数量够但难度大的类别自救器框数最少因为很多作业场景里工人并没有随身携带属于典型的长尾类别。真实矿山场景就是这样的分布没必要人为把四个类别平衡到完全一样反而容易失真。2.2 VOC和YOLO两种格式的本质区别标题里写了VOCYOLO格式这其实是目标检测数据集最常见的一种双格式交付方式。VOC格式是基于XML文件的每张图片对应一个同名的XML文件里面记录了图片尺寸、目标类别以及目标的bounding box坐标xmin、ymin、xmax、ymax即左上角和右下角坐标。这种格式是人类可读的用标注工具打开XML直接能看懂坐标适合数据检查、可视化、以及跑SSD、Faster R-CNN这类传统检测模型。YOLO格式则完全不同。它把标注信息写到TXT文件里每行代表一个目标格式是类别ID 中心点x 中心点y 宽度w 高度h而x、y、w、h全部是相对于图片宽度和高度的归一化比值取值范围0到1。这里有一个非常常见的坑YOLO的坐标中心点不是像素坐标而是比例坐标。很多新手转换格式时忘了归一化直接把像素值写进去训练时模型损失直接异常。这两类格式各有适用场景。VOC格式用于数据可视化检查、跨框架迁移很多老框架配置只接受VOCYOLO格式则用于Ultralytics YOLO、YOLOv5、YOLOv8等现代训练管线读取效率更高且无需运行时解析XML。我做数据集时习惯双格式同时交付就是为了让使用者不用再做一次格式转换这个零价值但极易出错的环节。2.3 数据集目录结构参考如果你是自己造数据建议按下面的结构存放这套目录结构是复现和训练最省心的一种dataset/ ├── images/ │ ├── train/ # 约3000张 │ ├── val/ # 约900张 │ └── test/ # 约469张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ ├── annotations/ │ ├── train/ # VOC格式xml │ ├── val/ │ └── test/ ├── classes.txt # 类别列表一行一个类别 └── data.yaml # YOLO训练需要的配置文件需要注意图片和标注文件命名要保持完全一致只是扩展名不同。图片是frame_000123.jpgYOLO标注就是frame_000123.txtVOC标注就是frame_000123.xml。一旦文件名对不上训练时很多目标会被当成背景漏检率飙升。3. 从0构建这类矿井数据集采集、清洗、标注的完整实操链路很多文章只给现成的数据集下载完直接训练但我认为还是有必要讲一遍构建过程。因为了解原始数据怎么来的你才知道模型在什么边界条件下会失效。3.1 井下采集环境的特殊约束井下采集不是拿着相机随便拍。首先井下属于防爆区域普通数码相机和手机是禁止带入的必须使用矿用本安型防爆相机或固定安装的防爆摄像机。这个约束直接决定了采集设备的参数分辨率一般在200万到500万像素之间帧率不高且多数不带闪光灯只能依赖矿灯照明。所以你看到的数据集里很多图片有明显的暗角、煤尘反光点和局部过曝这不是采集质量问题而是真实井下成像的真实样子。我们在数据采集时跑了好几个采区覆盖了主运输巷道、采煤工作面、掘进头、变电所、水泵房这几类典型的井下场景。场景多样性比数量更重要。如果只在一条干净整洁的巷道里拍4000张模型学到的矿井特征就只是那一条巷道的纹理换个工作面立马失效。3.2 清洗与负样本设计采集完原始视频后按每秒取帧或者按运动变化取帧得到数万张候选帧。清洗的目标是去掉三类废图完全模糊的、纯黑无内容的、内容重复度过高的。纯黑图片在井下不罕见因为视频可能在设备休眠或遮挡时仍在录制。我一般用两个指标做初筛图像方差低于阈值判为过暗或纯色和相邻帧PSNR相似度太高就跳过。这里有一个我们反复强调的点不要只筛掉坏图还要有意识地保留一部分难图。比如矿灯直射导致的中心过曝图、煤尘浓重导致的目标轮廓模糊图、以及工人背对镜头只露出半个安全帽的图。这类图是模型泛化能力的试金石全删掉的话模型在评测集上好看到了现场遇到同样情况直接就瞎了。3.3 四类目标的标注决策细节标注是数据集质量的天花板。下面这些决策是我们踩过坑之后总结出来的非常值得参考人员person只要画面里出现一个完整或大部分可见的人体就按人体外接矩形标注。如果一个人被设备遮挡了70%以上只露出一只手或一只脚不标。因为标注这种极端局部目标边界框占的面积太小且类别语义不明确容易干扰模型学习。如果人坐在驾驶室里透过车窗看到完整上半身正常标。安全帽helmet只标注符合矿用安全帽外观特征的帽子。工人在井下可能戴棉帽、布帽子或者只戴矿灯头箍这些都不标安全帽类别。特别需要注意的是帽子拿在手里或者放在桌上时只要完整性可见也标注——这能帮助模型学到安全帽是一种物体而不是长在人头上的附属品提升检测的泛化能力。指示灯indicator这是一个极其考验标注耐心的类别。指示灯的像素面积通常只有20x20到40x40而且受光照影响经常过曝成一片白斑。我们的规则是只要人眼能分辨出这是灯并且大致轮廓可判断就标注如果完全过曝到只剩一片白无法判断是灯还是反光不标。标注时务必把图像放大到200%否则框的位置会偏半个身位导致训练时回归不准。自救器self-rescuer只标注已佩戴或可明确辨识的自救器。自救器通常挂在腰侧和人体框大量重叠在YOLO训练时容易被当成背景或互相抑制。我们处理的原则是如果自救器和人体重叠程度太高依然照常标注不因为重叠而删除。因为真实场景中自救器就是贴在人身侧的如果训练数据里全是分离摆放的自救器模型根本学不会实际佩戴状态下的检测。3.4 双格式输出的标注流程标注工具我长期用的是LabelImg用于VOC格式输出和Labelme用于多边形标注再转矩形框。实际上更高效的做法是用LabelImg直接标保存为VOC XML格式然后写一个脚本转成YOLO的TXT格式。下面是我常用的转换脚本核心片段Python实现可以直接跑import os import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, out_dir, classes): tree ET.parse(xml_path) root tree.getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 边界框超出图片时裁剪到[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) lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) out_path os.path.join(out_dir, Path(xml_path).stem .txt) with open(out_path, w) as f: f.write(\n.join(lines)) # 用法 classes [person, helmet, indicator, self-rescuer] voc_to_yolo(data/annotations/0001.xml, data/labels/train/, classes)有一个细节转换时务必做坐标归一化和越界裁剪。因为标注过程中手滑导致框的右边界超出图片宽度的现象很常见如果不裁剪YOLO读入后会出现坐标大于1的情况轻则损失曲线震荡重则训练直接报错。4. 实际训练中的关键参数与表现从YOLOv8跑一组真实对照数据集到手最终要落到训练上。我用Ultralytics YOLOv8n和YOLOv8s分别做了对比实验。之所以不用更大规模的模型是因为井下安监部署端往往是防爆边缘盒子或老旧工控机推理资源有限模型再强跑不动等于零。4.1 数据划分与训练配置划分数据时遵循一个原则按场景划分而不是按文件随机划分。什么意思呢如果同一个视频连续帧画面几乎一样随机分到train和val里相当于拿答案考试评估指标虚高。正确做法是先把视频场景分组让同一个场景的视频帧只进入一个集合保证验证集场景是模型没见过的。训练配置我给出可以直接参考的一组参数# data.yaml path: D:/mine_dataset train: images/train val: images/val test: images/test nc: 4 names: [person, helmet, indicator, self-rescuer]训练命令yolo detect train \ dataconfig/data.yaml \ modelyolov8n.pt \ epochs200 \ imgsz1280 \ batch16 \ lr00.005 \ patience30这里有两个参数值得单独讲。第一是imgsz很多人习惯用640或320但对于指示灯这种小目标640分辨率下很多灯的尺寸才不到10个像素特征几乎淹没在特征图的最深层。我们把输入分辨率提到1280指示灯的AP直接提升了接近15个百分点。第二是lr0初始学习率从默认的0.01降为0.005并配合warmup是因为这个数据集的背景复杂度极高煤壁纹理和矿灯反光会给浅层特征带来强干扰学习率过大容易在早期就震荡发散。4.2 四个类别的AP表现与原因分析200个epochs跑下来验证集上的结果如下mAP50-95的整体值在0.72左右类别mAP50mAP50-95主要扣分原因person0.9510.672遮挡严重时漏检helmet0.9380.653与煤壁颜色相近时误检indicator0.8420.501小目标定位偏差大self-rescuer0.7810.436样本少且与人体重叠可以看到person和helmet这种中大尺寸、样本丰富的类别AP很漂亮indicator因为目标太小AP0.50掉到0.50区间自救器则是又小又少又重叠AP最低。这组数据其实还原了一个客观事实数据集的平均mAP是假的看类别分项才能找到真实瓶颈。如果你的场景里自救器检测是刚需那就得靠针对性数据增强比如对自救器区域做过采样复制粘贴增强和后处理比如在人体框内设置ROI再检测自救器来补救不能指望通用模型自己学会。4.3 推理速度对照在NVIDIA Jetson Orin Nano这样的边缘设备上关闭TensorRT和开启TensorRT的两组推理表现模型输入分辨率推理耗时ms是否TensorRTYOLOv8n128028否YOLOv8n128011是YOLOv8s128047否YOLOv8s128019是井下视频通常接的是25帧/秒的摄像头采用YOLOv8nTensorRT后能在25fps以上稳定运行。如果你部署的单路摄像头较多CPU资源紧张更推荐把视频流抽帧到每2秒1帧再送检对于人员安全行为分析的场景2秒的响应延迟完全在可接受范围内。5. 用这套数据最容易踩的四个坑及应对这是全文最有实战价值的部分我把在矿井类数据集上反复翻车的四个坑单独拎出来讲。5.1 标注框长宽比极度悬殊导致NMS表现异常人员站姿框通常是高是宽的好几倍而指示灯框接近正方形。当这两类目标在画面中靠近时NMS会依据IoU抑制低分框高瘦的人员框与正方形指示灯框面积重叠时由于IoU计算只关注面积交并比指示小框容易整个落在人员框内从而被误删。应对策略有两种一是泛化性最好的方案——在后处理阶段按类别分组做NMSperson一组其余小目标一组分开抑制。二是在训练阶段引入更严格的损失权重抑制长宽比极端的误框生成。实际操作里我建议直接用类别分组的NMS改动最小且效果立竿见影。5.2 过度增强对比导致安全帽过拟合煤尘反光为了提升低照度下的小目标检测效果很多同学会加Mosaic和HSV增强。矿井图像本身有大量煤尘反光点安全帽顶部是高光的ABS材料增强过头后模型会把高亮小圆斑当成安全帽特征频繁把煤壁上的水痕、反光标记识别成帽子误检率飙升。我在复测中发现把hsv_h从默认的0.015降到0.005把hsv_s从0.7降到0.3之后安全帽误检明显减少。增强不是越猛越好针对高噪声工业场景适度的色彩扰动反而更稳。5.3 指示灯类别被误检成人员或自救器这个坑比较隐蔽。井下红灯、绿灯在低分辨率下轮廓模糊且很多指示灯的安装高度在1.8米左右和人的头部位置接近。模型提取到的边缘特征如果不充分会把圆形的红灯当成戴红色安全帽的人头把长方形的方向指示灯当成自救器外壳。我们后来在标注时专门增加了一类背景负样本的hard-negative mining把那些和指示灯长得极像的矿灯反光、设备状态灯、红色压力表表盘全部截出来作为背景图片加入训练集。增加约600张负样本后指示灯的误检率从8%降到了2%以内效果非常显著。5.4 自救器类别样本不足导致的模型压根不学如果训练日志里自救器类别的loss长期不下降多半不是超参问题而是样本量太少模型认为这个类别不值得为它分配特征容量。自救器框只有几千个而人员框有上万个默认训练时梯度贡献完全被大类别淹没。这里的做法是给自救器类别的损失单独加权。在Ultralytics的配置里可以通过修改损失权重实现。一个更直接的做法是把训练集中包含自救器的图片复制一份并对自救器所在区域做随机裁剪、旋转、亮度扰动后再拼回原图即复制粘贴增强人为扩充样本到接近指示灯的规模。这属于最朴素但有效的数据均衡手段。写在最后矿下数据集比普通公开数据集麻烦的地方主要在于成像条件不稳定和类别语义高度依赖场景。自己造数据也好用现成数据集微调也好我都建议你围绕自己的真实部署场景做一轮小批量验证——拍100张现场照片标注好测一下看哪一类最弱再针对性地补数据或调后处理。神经网络在这种长尾分布极强的工业场景里不会自己变聪明得靠人替它把短板补上。另外我最近在做的事是给这套数据集补充视频帧间的时序信息把单帧检测升级成短时序列检测用帧间运动特征辅助判断自救器佩戴和人员跌倒状态。工业安监的需求永远在往前跑数据集的维护也得跟上。希望这篇拆解对你有用。

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

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

免费获取报价 →
↑