☰
智慧矿山井下安监数据集构建:四类目标检测与YOLO训练实战
2026/10/21 14:49:50 网站建设 项目流程

矿下作业的安全管理,一直是智慧矿山建设的硬骨头。我最早接触这个方向,是给某矿业集团的智能安监系统做视觉识别模块。当时甲方提了一个非常具体的需求:不只要识别工人有没有戴安全帽,还要看清现场人员的实时位置、巷道里应急指示灯的状态,以及工人随身自救器的佩戴情况。这几个目标混在一起,行业内能直接用的数据集几乎没有。后来我们花了大半年自采自标,攒出了一套4369张图片、涵盖人员、安全帽、指示灯、自救器4个类别的数据集,并整理成了VOC和YOLO两种通用格式。

这篇文章就把这套数据集的构建思路、标注细节、格式差异和训练实测一次性讲透。内容覆盖数据采集的井下环境约束、四类目标的标注决策、VOC与YOLO格式的转换坑点,以及用YOLO系列训练后的真实精度表现。目标读者是正在做智慧矿山、工业安监、或者准备自建数据集做目标检测的算法工程师和学生,哪怕你完全没碰过矿井数据,里面的踩坑经验也能用在其他工业视觉项目上。

1. 智慧矿井为什么需要同时检测这4类目标

很多做安监算法的人一上来就盯着安全帽识别,觉得"戴没戴帽子"是最关键的。但实际去过矿上你就知道,井下的安全管理是多维度的。单独检测安全帽,只能回答"工人是否违规"这一个问题,而无法回答"井下现在有多少人""这些人是否具备应急自救条件""逃生通道指示灯是否正常工作"这些更核心的问题。

1.1 四类目标背后的安全管理逻辑

我按真实业务价值把这4类目标重新排了个序,方便你理解数据集设计的逻辑:

  1. 人员:这是所有安监系统的"底账"。不管是确认作业区域有没有人滞留、统计交接班人数,还是事故发生时判断被困位置,都依赖对人员的精准检测。井下人员检测的难点在于工人着装高度相似、矿灯造成局部过曝、以及人员之间经常互相遮挡。

  2. 安全帽:这是合规检查的核心项。矿下安全帽和普通工地安全帽外观差异其实很大,它有专门的下颏带、顶部加强筋和矿灯卡座,侧面看轮廓更宽。检测模型的难点在于工人的帽子经常和煤壁、设备颜色融为一体,尤其在低照度环境下。

  3. 指示灯:巷道里的应急指示灯、方向指示牌、设备状态灯,属于典型的"小目标检测"。指示灯在画面里可能只有十几个像素,却承担着逃生指引的关键功能。这类目标对模型输入分辨率的要求非常高,低分辨率下基本必漏。

  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两种格式的本质区别

标题里写了"VOC+YOLO格式",这其实是目标检测数据集最常见的一种双格式交付方式。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格式用于数据可视化检查、跨框架迁移(很多老框架配置只接受VOC),YOLO格式则用于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.jpg,YOLO标注就是frame_000123.txt,VOC标注就是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 \ data=config/data.yaml \ model=yolov8n.pt \ epochs=200 \ imgsz=1280 \ batch=16 \ lr0=0.005 \ patience=30

这里有两个参数值得单独讲。第一是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)是否TensorRT
YOLOv8n128028否
YOLOv8n128011是
YOLOv8s128047否
YOLOv8s128019是

井下视频通常接的是25帧/秒的摄像头,采用YOLOv8n+TensorRT后能在25fps以上稳定运行。如果你部署的单路摄像头较多,CPU资源紧张,更推荐把视频流抽帧到每2秒1帧再送检,对于人员安全行为分析的场景,2秒的响应延迟完全在可接受范围内。

5. 用这套数据最容易踩的四个坑及应对

这是全文最有实战价值的部分,我把在矿井类数据集上反复翻车的四个坑单独拎出来讲。

5.1 标注框长宽比极度悬殊导致NMS表现异常

人员站姿框通常是高是宽的好几倍,而指示灯框接近正方形。当这两类目标在画面中靠近时,NMS会依据IoU抑制低分框,高瘦的人员框与正方形指示灯框面积重叠时,由于IoU计算只关注面积交并比,指示小框容易整个落在人员框内从而被误删。

应对策略有两种:一是泛化性最好的方案——在后处理阶段按类别分组做NMS,person一组,其余小目标一组,分开抑制。二是在训练阶段引入更严格的损失权重,抑制长宽比极端的误框生成。实际操作里我建议直接用类别分组的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张现场照片标注好测一下,看哪一类最弱,再针对性地补数据或调后处理。神经网络在这种长尾分布极强的工业场景里,不会自己变聪明,得靠人替它把短板补上。

另外我最近在做的事是给这套数据集补充视频帧间的时序信息,把单帧检测升级成短时序列检测,用帧间运动特征辅助判断自救器佩戴和人员跌倒状态。工业安监的需求永远在往前跑,数据集的维护也得跟上。希望这篇拆解对你有用。

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

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

立即咨询