简介:一套面向工业安全场景的目标检测数据集,聚焦车间工人、安全帽与安全背心的识别,适用于YOLO系列、Faster Rcnn、SSD等主流深度学习模型训练。数据集包含3465张图片,标注person、helmet、vest三个类别,图片与txt标签已按训练集、验证集、测试集划分,并附带指定类别信息的yaml文件及xml标签;txt文件采用YOLO格式归一化坐标,yaml内含类别名称与路径配置,可直接用于YOLOv5至YOLOv10等算法框架。压缩包共2000个文件,其中1999个为txt标注文件,1个为yaml配置文件,整体大小约357.09MB。数据集面向工业安全生产监控、人员规范着装检测等场景,图片来自真实车间监控视角,涵盖不同光照与复杂背景,目录结构清晰,免去自行划分数据集的繁琐步骤,有助于快速开展模型训练与效果验证。已有438人学习下载,适合正在研发车间安全智能预警系统的深度学习开发者使用。
1. 车间安全穿戴检测:一类数据集撑起整个工业视觉项目
做工业视觉这几年,我接到的需求里出现频率最高的不是“检测产品缺陷”,而是“识别工人有没有戴安全帽、穿安全背心”。这个需求听起来简单,真正落地时却让不少人翻车:不是模型效果差到没法用,而是根本没有一套合用的数据集,最后只能从头攒数据、标数据,把八成时间耗在数据上。安全生产法规里对劳保穿戴有硬性要求,车间现场靠人盯根本盯不过来,目标检测技术就是为了解决这个“人管不过来”的场景——用摄像头替代巡检员,自动发现未佩戴安全帽或反光背心的行为并告警。这篇笔记面向的是手里有摄像头、想用目标检测搭一套车间安全穿戴识别的工程师或项目经理,我会按数据准备、模型选型、参数调优、踩坑复盘、上线验证这条路径,把这套方案的落地细节讲清楚。
2. 目标检测选型:从 YOLO 家族里挑一个能扛住车间场景的模型
2.1 YOLOv8 为什么是比 YOLOv5 更稳妥的起点
车间工人、安全帽、安全背心识别本质上是一个类别少、目标尺寸分布极不均匀的检测任务。安全帽在画面里通常是小目标,安全背心是中大目标,工人身体则是大目标。这种场景对模型的要求不是“什么都能检测”,而是对中后期层级的特征表达能力要求均衡——既要保住小目标的细节纹理,又不能牺牲大目标的语义感受野。
YOLOv8 相比 YOLOv5 最大的变化是 head 结构改成了 Anchor-Free,并由 Decoupled Head 分别输出分类和回归分支。锚框机制在处理安全背心这类宽高比变化较大的目标时需要设计先验框,调不好会直接压低 recall;Anchor-Free 的方式则老实得多,每个位置直接预测“这里有没有目标中心点”和“到四条边的距离”,对安全帽、背心这类形状相对固定的物件反而更友好。C2f 结构替换了原先的 C3,梯度流动更充分,在用自己标注的中小数据集训练时不容易出现深层次特征断裂导致的不收敛问题。
我的习惯是新项目第一版模型用 YOLOv8n 或 YOLOv8s,先跑通一条完整链路再做精度提升。
# 安装 ultralytics 库,注意锁定版本,避免后续接口变动影响训练脚本 pip install ultralytics==8.2.0 # 下载预训练权重,基于 COCO 的预训练模型能让迁移学习起点更合理 yolo predict model=yolov8n.pt source=./test_image.jpg这里的“锁定版本”是经验之谈。ultralytics 的接口迭代非常快,8.2.0 和 8.3.x 在回调函数、验证集评估指标名称上都有变动,不锁版本的话,训练脚本在几周后重跑时可能会突然报错。YOLOv8n 参数量仅 3.2M 左右,在 GTX 1660 这类老显卡上也能轻松跑训练;但如果你最后要部署到 Jetson 系列边缘设备,就更应该从 n 和 s 起步,直接上 m 或 l 会让帧率掉到不可用的地步。
2.2 YOLOv5 和 YOLOv11:什么时候该回头看老模型、什么时候该追新
并非所有项目都该无脑选新版本。如果产线上已有的推理框架是基于 YOLOv5 的 ONNX 导出来写的,更换模型意味着整个预处理后处理代码都要跟着改,风险不小。YOLOv5 的 v6.0 版本在 720P 输入下、GTX 1080Ti 推理单张只要 5ms 上下,老代码稳定,工程团队对它熟悉,此时继续用 YOLOv5 维护也是合理选择。
YOLOv11 则在 Backbone 里引入了 C3k2 结构并用 R flops 做了更细粒度的计算约束。但就安全帽、背心这类小类别检测而言,模型结构的收益远不如数据质量来得直接。我的判断是:如果你的团队已经跑通 YOLOv8,没必要为追新而迁移到 v11;如果是从零起步且未来要接复杂场景(如同时识别多种违规行为),直接上 YOLOv11 也说得过去。评估时不要只盯着 mAP,要把 GPU 占用、推理耗时和误报率一起加进对比表。
| 模型 | 输入尺寸 | mAP50-95(公开安全帽数据参考) | 推理耗时(T4, FP16) | 显存占用(bs=16, 640px) |
|---|---|---|---|---|
| YOLOv5s | 640 | 中 | 约 3.8ms | 约 3.5G |
| YOLOv8s | 640 | 较高 | 约 5.2ms | 约 4.2G |
| YOLOv11s | 640 | 较高 | 约 5.5ms | 约 4.4G |
数字只是横向参考,不要把它当成绝对标准。关键结论是:这个场景里 s 级模型已经能满足绝大多数厂区需求,追求更大的 m 和 l 会显著降低边缘设备的并发路数。
3. 车间工人、安全帽、安全背心数据集:从公开资源到自建数据一条路走通
3.1 coco2017 数据集里能直接“白嫖”到哪些类别
coco2017 数据集包含 80 个类别,每张图像都有 instance 标注,格式是 JSON polygen。让工人、安全帽、背心识别项目少走弯路的第一步,是确认公共数据集的类别映射——如果只是做小规模验证,完全可以直接抽取 COCO 中的 person 类作为工人识别基准,甚至把“tie(领带)”类误标造成的干扰删掉后,再叠加自采的帽子、背心数据。
# 抽取 COCO 中 person 类别的图片列表,用于验证检测 backbone 是否正常工作 from pycocotools.coco import COCO coco = COCO('./annotations/instances_train2017.json') person_ids = coco.getCatIds(catNms=['person']) img_ids = coco.getImgIds(catIds=person_ids) print(f"包含 person 的图像总数: {len(img_ids)}")这段代码背后的逻辑是:先验证现有工具链能否正确读取 COCO JSON,再决定后续标注格式要不要统一转成 YOLO 的 txt。很多人一上来就想训练自己数据集,却在数据加载这一步才发现 label 文件路径写错、类别索引从 0 还是从 1 开始等低错,非常耗时。先把 person 跑通,相当于给整条管线做了一次“体检”。
不过 COCO 里的 person 大量是日常场景,和车间环境差异很大——穿着反光背心、戴安全帽、站在机床旁的工人形态并不常见。因此 COCO 只能当 baseline,不能作为真正的训练主力。
3.2 自建车间场景数据集:摄像头抽帧、数据清洗与类别定义
真正的核心数据集必须来自目标车间或同类车间。用监控视频抽帧是最经济的做法,海康或大华的 RTSP 流通过 FFmpeg 抽帧,每秒取 1 帧即可,避免相邻帧高度重复造成的数据冗余。抽帧后的图片还要做人工筛选,把画面模糊、目标占比过小、遮挡超过 50% 的图片剔除。
# 从 RTSP 流每 2 秒抽一帧,保存为 jpg,用于构建车间原始数据集 ffmpeg -rtsp_transport tcp -i "rtsp://your_camera_ip:554/stream1" \ -vf "fps=1/2,scale=1280:-1" -q:v 2 -f image2 worker_%04d.jpg这里强制走 TCP 而不是默认的 UDP,是因为 UDP 在弱网环境下容易出现花屏帧,花屏帧进入数据集就是脏数据。scale=1280:-1把宽度统一到 1280,保持原始宽高比不变,防止挤压变形影响标注时对边界框的判断。如果后期要训练小目标检测,这个尺寸通常不会让安全帽缩小到无法标注的程度;若需要看清更远距离的帽子和背心,则要改用 1920 宽或按 ROI 区域裁剪后再存帧,否则安全帽可能只有十几个像素,标注出来的 bbox 又有大量噪声。
类别定义上,我坚持只设三个类。第一类是“工人(person)”,无论是否穿戴合规都归为 person;第二类是“安全帽(helmet)”,第三类是“安全背心(vest)”。不要多做“未戴安全帽”这个类别——它和“戴了但被遮挡”的边界极难划清,硬分会导致类别间样本极度不均衡。逻辑判断放到后处理去写:检测到 person 但没匹配到 helmet,就告警“未佩戴安全帽”。
3.3 标注工具选型与标注边界规则:把 labelImg 的坑提前堵住
标注工具我用 labelImg 的时间最长,也推荐新手上路先用它。虽然界面朴素,但它稳定、安装简单、支持 PascalVOC 和 YOLO 两种导出格式,对几百张图片的小项目足够。
标注时要提前定几条硬规则,否则返工成本极高:
- 安全帽的 bbox 只框帽子本体,不要包含额头和头发;帽檐略微出界没关系,但最多只能超出 5% 像素,否则模型会把帽檐当特征学偏。
- 安全背心的 bbox 框到反光条边缘即可,不要把肩膀以外的背景包进来;正反两面都要标注,侧面人像如果背心被手臂遮挡超过 40%,直接跳过该帧,不要硬标。
- 每个类别至少保证 3000 个实例,且不同光线、不同工位、不同体型的样本尽量均衡。
# labelImg 通过 pip 安装,启动后从“打开目录”选择图片目录,“更改保存目录”指定输出 pip install labelImg labelImg标注输出格式建议直接选 YOLO。因为 YOLO 格式每个目标一行class x_center y_center width height,坐标是归一化后的相对值,训练时不需要再做坐标换算,少一层出错概率。等所有图标注完,用脚本统计每个类别的标注框数量,如果发现某个类别的数量偏少,就要针对性补充采集图片再标注。
3.4 数据集切分与验证集构成的“玄学”:不是随机 split 就完事
数据切分看似是小事,但这个环节里藏着项目成败的伏笔。按 8:1:1 随机切分 train / val / test 是常规操作,但车间监控数据是按时间序列抽帧的,同一个工人连续几帧会出现在不同集合中,导致评估指标虚高——模型看到了“见过的脸”在另一个场景里出现,自然容易检测正确。正确做法是按视频片段切分:每个摄像头或每段连续录像为粒度,整体划分到不同集合,保证同一时间片段只出现在一个集合中。
另外,验证集图片务必包含至少 20% 的“难例”,即工人远离摄像头、姿态非直立、车间光源造成逆光的样本。很多模型在验证集上 mAP 很高,一到现场就抓瞎,正是因为验证集里全是清晰、正面的理想图片,完全没体现出真实场景的分布。
4. 用 YOLOv8 训练自己的车间穿戴检测数据集:从命令到三个必调参数
4.1 训练命令与目录结构:先让数据加载零报错
先建立标准目录结构,这一步看着基础,却能省下大量排查路径的时间:
dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml 是训练入口,内容如下:
# 类别定义:顺序必须与标注文件中的 class_id 严格一致 path: ./dataset train: images/train val: images/val test: images/test names: 0: person 1: helmet 2: vest注意path用相对路径的坑——ultralytics 在训练时会基于当前工作目录解析path,如果训练脚本是从项目根目录运行的还好;若是通过 Docker 挂载目录运行,路径容易指错,报 FileNotFoundError。我建议把path写成绝对路径,虽然不优雅,但能避免绝大多数新手期路径问题。
# 训练安全帽背心检测模型,选 s 级模型,训练 120 轮 yolo detect train model=yolov8s.pt data=./dataset/data.yaml \ epochs=120 batch=16 imgsz=640 device=0训练流程启动后,要盯着终端的 mAP50 和 mAP50-95 变化曲线。如果 loss 从第二轮开始还迟迟不降,多半是数据集有问题,先终止训练,检查标注框是否出现坐标越界(如 width 或 height 为 0)。如果 mAP 在最后 20 轮还在缓慢上升,说明数据量不够,加大 epochs 到 200 通常还能缓解;但不要指望 mAP 会大幅冲到 0.95——车间场景中远距离小目标的安全帽本身就极难完美框定。
4.2 参数调优:学习率、mosaic 增强与 anchor 自动调优
第一个必调参数是学习率。ultralytics 默认 lr0=0.01,但这是针对 COCO 这种百万级数据集的设定;小数据集下学习率过高会导致早期 loss 爆降后迅速震荡。我的经验是:训练样本少于 5000 张时,把 lr0 降到 0.003 起跑;样本在 5000 到 20000 张之间,0.005 比较稳妥。
# 调低学习率防止小数据集过拟合震荡 yolo detect train model=yolov8s.pt data=./dataset/data.yaml \ epochs=150 batch=16 imgsz=640 device=0 lr0=0.005第二个必调参数是 mosaic 增强。YOLOv8 默认开启 mosaic=1.0,即每轮训练有 100% 概率把 4 张图拼接成一张马赛克图。这在数据量大时能显著提升模型的泛化能力;但小数据集上,如果 mosaic 概率过高,模型会学到大量拼接痕迹,安全帽的边界还可能被拼接缝隙切断。训练 5000 张以下的数据集时,我一般把 mosaic 调到 0.5。此外从第 80 轮开始自动关闭 mosaic 是 YOLOv8 默认行为,不用手工干预。
第三个关键参数是close_mosaic和cache的组合。cache=True可以预加载所有图片到显存/内存中,大幅提升训练速度;但当数据量超过 2 万张时会占用过多内存,而且对随机增强的多样性也是一种限制。
# 关闭部分 mosaic,开启缓存加速小数据集训练 yolo detect train model=yolov8s.pt data=./dataset/data.yaml \ epochs=150 batch=16 imgsz=640 device=0 \ mosaic=0.5 close_mosaic=10 cache=Trueclose_mosaic=10表示在最后 10 轮关闭马赛克增强,让模型在接近真实分布的数据上做最后的精调,这对小目标的定位精度影响很明显。数据量小于 3000 张时,cache=True 后显存占用会升高,遇到 OOM 就把 batch 从 16 降到 8,不要盲目加显存。
4.3 验证集指标解读:mAP50 与 mAP50-95 到底哪个决定能否上线
训练结束后,ultralytics 会在runs/detect/train/目录下生成results.csv。我通常先看mAP50是否达到 0.9,再看mAP50-95是否超过 0.7。这两个指标的含义差别很大:mAP50 只要求预测框和真实框的 IoU 超过 0.5 就算检测正确,对安全帽这种小目标来说,IoU=0.5 已经相当宽容;mAP50-95 则从 0.5 到 0.95 以 0.05 为步长取平均,对框的精确位置要求极高。
车间告警系统能容忍 bbox 稍微偏一点,只要“帽子/背心是否佩戴”判断正确即可,所以 mAP50 才是上线决策的核心指标。如果你的 mAP50 高于 0.92 而 mAP50-95 只有 0.6,仍然可以先上线试用,再通过难例挖掘不断迭代。
# 读取 results.csv,定位验证集指标 import pandas as pd df = pd.read_csv('runs/detect/train/results.csv') cols = df.columns print([c for c in cols if 'mAP50' in c or 'map50' in c])5. 车间安全穿戴检测避坑手册:五个高频翻车现场与解决方案
5.1 反光背心的“反光”把模型学成了噪点提取器
现象:模型在白天识别背心正常,到了晚上或逆光环境,误检率暴涨,把车间的金属反光、墙壁踢脚线反光条都识别成了“安全背心”。
原因:安全背心的反光条在强光下形成高亮区域,与周围环境对比度过大。模型学到的是“高亮=背心”,而不是“穿着在人身上的高亮条带”这个完整语义。
解决:训练集中必须包含至少 30% 的背光、低照度、强反光样本。我在实际项目中会故意引入不同角度、不同强度的光源变化的图片。如果现场能采集到不同班次、不同天气的光线变化,尽量采集全。实在缺数据时,用图像增强库对已有的背心图片做亮度抖动和强高斯噪声,虽然不如真实样本自然,但能在一定程度上拉平误检率。部署时还可以加入一条后处理规则:检测到的“背心”如果不在任何 person 框内,直接丢弃这个检测结果。
5.2 安全帽检测的漏检率集中在远距离和低头动作
现象:近处工人戴帽子识别得挺准,画面远端戴红色安全帽的工人经常漏检。
原因:头盔在画面中仅占约 20x20 像素甚至更小,模型在深层特征图中已经将该区域的特征信息压缩得所剩无几;同时红色安全帽在偏色监控画面中容易和背景环境融为一体,旧式模拟摄像头下尤其明显。
解决:一是提高输入分辨率。imgsz=640改成 800 或 960,小目标的 AP 通常能涨 3 到 5 个百分点,但推理耗时也会同步上升——我的做法是先评估现场摄像头的安装高度和覆盖范围,再选择输入尺寸。二是增加“小目标复制粘贴”策略,将有安全帽的小目标裁剪下来,随机粘贴到背景图中,构成新的训练样本。这是简单的数据合成方式,不要和 mosaic 混淆,它更直接、更可控。
5.3 标注文件坐标越界引发整批训练样本被丢弃
现象:训练日志中WARNING ⚠️ skipping image (invalid data)反复出现,训练结束后 mAP 一直上不去。
原因:标注工具导出时,手滑把 bbox 的 x 坐标写超出图像宽度,或者用自动标注脚本时出现了归一化坐标大于 1 的情况。YOLO 格式要求0 <= x_center <= 1、0 <= w <= 1,越界的行会被训练管线静默跳过。
解决:写一个校验脚本,遍历所有 label 文件,检查每个数值是否在[0, 1]区间内、width 和 height 是否大于 0。发现异常就定位到对应图片,重新修正标注或删除整张图。这个脚本应该在每次新标注完数据后立即执行,而不是等到训练时报错了再回头查。
import os label_dir = './dataset/labels/train' for f in os.listdir(label_dir): path = os.path.join(label_dir, f) for line in open(path): vals = list(map(float, line.strip().split())) if len(vals) != 5: print(f'{f} 格式错误: {line}') if not (0 <= vals[1] <= 1 and 0 <= vals[2] <= 1 and 0 <= vals[3] <= 1 and 0 <= vals[4] <= 1): print(f'{f} 坐标越界: {line}')5.4 数据集只有晴天数据,雨天现场误检率直接翻倍
现象:模型在验收测试时表现良好,结果上线后遇到下雨天,车间门口区域的“工人”检测数量暴增,画面里雨水打在地面形成的水花都被识别出人形。
原因:监控摄像头在雨天会有雨滴附着在镜头保护罩上,形成局部模糊和光斑。模型没见过这种视觉退化,于是把高响应区域误判为目标。
解决:部署层面,在摄像头端启用自动雨刷功能或定期清理镜头;算法层面,把训练集扩充到包含雨天、雾天画面——不是要收集“坏天气”到很多,而是要覆盖摄像头位于室外进出通道时的真实退化模式。更简单的方式是训练完成后用真实雨天画面做一次“二次验证”,如果误检过多但不想重新训练,可以先在告警逻辑中限定检测区域(ROI),把容易出误报的背景区域从检测范围中挖掉,缓解上线压力。
5.5 训练中断后恢复训练,mAP 反而掉得更厉害
现象:训练到 60 轮时显卡过热导致进程崩溃,使用resume=True从 last.pt 继续训练,又跑了 40 轮后 mAP 不升反降。
原因:断点恢复时把学习率调度器也一起恢复了,而默认的 cosine schedule 在第 60 轮时学习率已经降得很低,重新恢复后学习率被重置到较高值,导致 loss 在原有最优区间附近震荡。
解决:resume 后把 lr0 重新设置为一个较小值,比如 0.001,再跑 30 轮,让权重在低学习率下慢慢地重新收敛。这个做法更像“微调”而不是“继续训练”。如果断点发生在学习率已经很低的后半程,干脆不要 resume,直接从头再训练一遍,有时反而更快更稳。
6. 模型导出与车间实时验证:一次剪枝优化带来的推理速度翻倍
训练完成不等于项目完成。部署时的模型往往要做一次轻量化优化,尤其是在边缘设备上。我常用的方式是先转 ONNX,再基于 ONNX Runtime 做 FP16 量化——这个操作在 Tesla T4 或 Jetson Orin 上效果明显,安全帽检测这种小模型通常能获得接近双倍的推理速度提升,而 mAP 损失几乎可以忽略不计。
# 导出 FP16 精度的 ONNX 模型供 Edge 端部署 yolo export model=best.pt format=onnx half=True simplify=Truehalf=True是关键参数,它会把权重从 FP32 压缩成 FP16。simplify=True会做一次计算图简化,去掉一些冗余的 reshape 和 transpose 操作。导出后务必用 ONNX Runtime 跑一次推理验证,避免因为算子兼容性问题导致终端推理失败。
import onnxruntime as ort import numpy as np session = ort.InferenceSession('best.onnx', providers=['CUDAExecutionProvider']) input_name = session.get_inputs()[0].name # 模拟一张 3x640x640 的输入 dummy = np.random.rand(1, 3, 640, 640).astype(np.float16) output = session.run(None, {input_name: dummy}) print('输出张量数量:', len(output))验证阶段我会用一短段线上视频做真实场景回放,把模型输出的 bbox 叠加到画面上,统计“每 100 帧的漏检次数”和“误报次数”两个指标。漏检比误报更致命——漏检意味着工人确实没戴帽子但系统没报警,安全考核时这就是事故隐患;误报最多让中控室多弹几次提示,让人厌烦但不会导致安全事故。所以我调参时的优先级永远是追 recall,而不是追 precision。
调试时还有一个容易被忽略的点:摄像头画面上检测到的人脸、手臂等身体部位不要参与“未戴帽”判定。比如一个工人只露出了手臂,系统检测不到头盔,如果此时告警,就会天天误报。我给后人留的一个习惯是:在 person 框的下半部分挖一个 ROI 区域,只有当 worker 的上半身区域(框的顶部 30% 区域)被检测到时,才执行帽子和背心的匹配逻辑。这个朴素的几何判断能砍掉一半以上的低质量误报。
这套方案做下来,模型本身的 mAP 不是最值得骄傲的东西,反而是“数据构建 + 部署边界条件”这两件事定下了项目的成败。希望这篇笔记能把你在车间安全穿戴识别这个方向上需要绕开的坑提前标出来,帮到你。
本文还有配套的精品资源,点击获取