ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLO实战:轨道矿车场景人员检测数据处理与训练调优

YOLO实战:轨道矿车场景人员检测数据处理与训练调优 简介轨道上矿车和人员检测数据集MineCarWithPeople-yolo-data是一份面向矿业安全生产的YOLO格式目标检测资源主要面向需要训练矿车识别与人员闯入检测模型的算法工程师、科研人员和矿业智能化项目团队。压缩包共2000个文件包含1045张真实矿井轨道环境图像、954个文本标签标注文件和1个模型训练配置文件整体大小约206.69MB目录结构可直接对接YOLO训练流程有效节省数据整理时间。数据已明确划分为训练集与验证集标注类别包括Normal正常矿车与With People有人员覆盖轨道运输常见安全监测场景为模型调优与效果评估提供了可靠样本。目前已有118人学习参考借助该数据集可省去现场采集和人工标注的大量工作快速投入矿井人员风险预警、矿车运行状态监测等方向的算法验证与项目落地。1. 一份轨道矿车与人员检测的 YOLO 数据治的是“人在车旁”这个真实场景矿业现场的轨道监控画面里最让人绷着神经的画面就两种矿车在跑矿车旁边有人。MineCarWithPeople 这几个字写在这个 YOLO 数据集的包名上其实就把问题压缩成了一件事——近千张真实矿业轨道环境的图片按 Normal 和 With People 两个类别分好train、valid 目录都铺好了。它解决的是轨道区域“有没有人”的判定问题服务矿业安全监测这类需要实时判断的工业场景。适合谁用做矿业安全监控的视觉工程师想拿真实场景练 YOLOv8 训练流程的人以及被要求尽快交一个“有人靠近自动预警”demo 的现场实施。这份资源的名字已经写得够直白MineCarWithPeople_yolo_data按这个名字拿到压缩包之后剩下的就是解压、核对、训练、调门限这几件事。2. 解压、核对、补标签把 zip 变成一份能直接开训的干净数据集压缩包和训练之间隔着一层检查。很多人拿到数据集就是解压、改路径、开训YOLO 训练时会悄悄跳过对不上号的图片等几十轮跑完回看日志才发现训练集少了小一半。数据解压本身不难难的是解压之后的状态管理。这一章按我自己的操作习惯来先看包、再解压、然后做三样核对全程十分钟以内。2.1 先用 zipinfo 看包结构再决定怎么解压我拿到 zip 的第一件事是在 Linux 下用 zipinfo 看包内容而不是直接 unzip。这样能看到包内目录层级知道解压出来是单层目录还是散落一堆 jpg避免原地解压把图片全撒在当前文件夹里。zipinfo MineCarWithPeople_yolo_data_轨道上矿车和人员检测数据集.zip | head -20zipinfo 输出里能看到文件总数和目录结构head -20 截取前面二十行就够了主要看包内有没有 images、labels 这种顶层目录。确认完结构再解压用 -d 指定目标目录unzip MineCarWithPeople_yolo_data_轨道上矿车和人员检测数据集.zip -d mine_car_data/这里的 -d 参数指定解压目标目录让所有素材收拢到 mine_car_data 下面后面改 yaml 和写脚本都清爽很多。外层中文目录名是个隐患有的 Linux 环境解压后中文目录不规范后面读取路径时会报“Cant open image file”我会在避坑章节单独说。稳妥的做法是解压完立刻重命名成纯英文mv 轨道上矿车和人员检测数据集 mine_car_data如果解压后发现文件名乱码常见原因是压缩包在 Windows 下用中文编码压的此时可以尝试用 GBK 字符集解压unzip 新版支持 -O 参数指定编码unzip -O GBK MineCarWithPeople_yolo_data_轨道上矿车和人员检测数据集.zip -d mine_car_data/-O GBK 是让 unzip 用 GBK 解码文件名而不是默认的 UTF-8能解决大部分 Windows 打包带来的中文乱码问题。如果 unzip 版本不支持 -O就用 Python zipfile 解压时手动做 cp437 到 gbk 的转换这是另一套方案实际中我会先用 unzip 试试不通再上脚本。2.2 目录结构核对确认 train/valid 与 data.yaml 对得上解压完成后先看目录树。一个标准的 YOLO 检测数据集长下面这样images 和 labels 分 train/valid 两个子集文件名一一对应mine_car_data/ ├── data.yaml ├── images/ │ ├── train/ │ └── valid/ └── labels/ ├── train/ └── valid/data.yaml 是训练入口压缩包里的摘要已经给出了它的核心内容正常应该是这样的两分类配置train: ../train/images val: ../valid/images nc: 2 names: - Normal - With People这份 yaml 的 train 和 val 用的是相对路径依赖于执行训练时的工作目录一旦你换机器、换目录路径就失效。我一般的做法是把这个配置改成绝对路径看起来不优雅但能省掉后续“File not found”的折腾后面的训练章节里我会给修改后的完整版本。现在重点是核对目录有没有缺层如果解压出来是嵌套两层目录先 mv 合并一层。2.3 图和标签数量对齐缺失的标注不会报错只会悄悄丢样本YOLO 训练不像文本任务那样严格报错图片缺了对应 txt它只会跳过这张图训练日志里混过去。所以我会先写一个小脚本把每个子集的图片数和标签数统计出来任何一个子集对不上号都要先处理再训练。import os base mine_car_data for split in [train, valid]: img_dir os.path.join(base, images, split) lab_dir os.path.join(base, labels, split) imgs [f for f in os.listdir(img_dir) if f.endswith((.jpg, .png))] labels [f for f in os.listdir(lab_dir) if f.endswith(.txt)] img_ids {os.path.splitext(f)[0] for f in imgs} lab_ids {os.path.splitext(f)[0] for f in labels} print(f{split}: images{len(img_ids)}, labels{len(lab_ids)}) print(missing labels:, img_ids - lab_ids) print(missing images:, lab_ids - img_ids)这段脚本的逻辑是分别取图片和标签的文件主名做差集两边各缺什么一目了然。正常状态下 images 和 labels 数量一致missing 为空。如果 missing labels 有值先确认是不是真的没标注而不是脚本路径写错。再看标签内容是否合法。YOLO 格式每个 txt 对应一张图每一行是“class x_center y_center width height”class 编号从 0 开始。抽几张看看head -3 mine_car_data/labels/valid/new_04_11.txt awk {if ($1 ! 0 $1 ! 1) print FILENAME, $0} mine_car_data/labels/*/*.txtawk 这行命令是检查所有标签里有没有类别号超出 0 和 1 的如果有说明标注工具导出时出了问题。再检查零字节的标签文件find mine_car_data/labels -name *.txt -size 0c零字节标签意味着这张图里什么都没标训练时同样会被跳过。这类文件我会直接删掉对应图或者手工补一个标签而不是留着让训练集悄悄缩水。顺带说一句如果你解压后只看到 jpg 没有 labels 目录说明这份数据是图像级分类标注不是检测框标注那就得用 labelimg 之类工具按 YOLO 格式把人框出来再训练那一步耗时主要看你对场景熟不熟。3. 写 yaml、配增强、跑训练YOLOv8 吃掉这份两分类数据目录核对完数据就是能进模型的状态了。这一章走完整条训练链路改 yaml、确认环境、选参数、跑训练、看曲线。我按 YOLOv8 来写当前这个版本生态最稳命令通用性也最高。3.1 先改 data.yaml路径绝对化names 顺序一个字符都不能动训练前我会把 data.yaml 改成下面这个形态使用绝对路径指向数据根目录path: /data/mine_car_data train: images/train val: images/valid nc: 2 names: 0: Normal 1: With Peoplepath 指向数据集根目录train 和 val 基于 path 写相对路径。这样比原来那种写成../train/images的写法稳因为后者依赖你启动训练时所在的目录位置一旦在别的路径下执行相对位置就对不上了。改成绝对路径后不管从哪个目录启动训练都能找到数据。names 的两行有一个特别容易翻车的点names 的 0 和 1 必须跟 labels 里的 class 编号一致。如果你觉得 With People 重要想把它放成类别 0不能只改 names而是要先把所有 txt 里的 class 做成一次 0/1 互换否则模型学到的是反的。我吃过这个亏后面避坑章详细写。3.2 环境依赖一个干净的虚拟环境比新版本更重要训练前确认环境。常见做法是 Anaconda 起一个干净环境装 ultralytics 和配套的 torchPython 3.10 这个组合比较省心pip install ultralytics python -c import torch; print(torch.__version__)ultralytics 包会把 yolo 命令行和 Python 接口一起装上第二条命令只是确认 torch 能正常导入。如果你有 N 卡先确认 NVIDIA 驱动和 CUDA 工具的版本能对上PyTorch 的 CUDA 版和驱动版本不匹配是最常见的起步坑。3.3 训练命令与关键参数小数据集别急着上大变体数据集不足千张两个类别标注的是轨道场景我先用 yolov8n 起步。n 是 nano最轻量的变体对样本量不大的场景够用训练快而且不容易一上来就过拟合。yolo detect train \ modelyolov8n.pt \ datamine_car_data.yaml \ epochs120 \ imgsz640 \ batch16 \ patience20 \ device0 \ projectruns/train_car参数说明如下这几个是影响最直接的参数取值作用与选型理由modelyolov8n.pt轻量模型千张图级别用大模型容易过拟合n 是起步首选imgsz640YOLO 系列默认输入尺寸显存够且人员目标偏小时再考虑 960batch168G 显存建议 16没显卡用 CPU 就降到 48epochs120小数据集训练收敛快120 轮足够配 patience 更省时间patience2020 轮验证集指标无提升就停不必跑满全过程训练时我对增强参数会额外加几句因为这份数据接近千张不算多全靠模型硬学容易把小类目学歪。我一般会在命令行追加这些yolo detect train \ modelyolov8n.pt \ datamine_car_data.yaml \ epochs120 \ imgsz640 \ batch16 \ patience20 \ scale0.3 \ fliplr0.5 \ hsv_h0.015 \ mosaic0.5scale0.3 意思是训练时图像缩放幅度允许 30%模拟矿车离摄像头远近不同的情况fliplr0.5 是 50% 概率水平翻转轨道场景左右对称这个增强性价比很高mosaic0.5 是把 mosaic 增强概率设为一半而不是默认的满开因为小数据集里如果场景重复度高mosaic 拼出来的图经常夹带大块无意义碎片。3.4 训练中看什么曲线比日志重要别急着动损失函数训练跑起来后项目目录 runs/train_car 下会按时生成 results.png这张图比终端日志有用得多。上面有 train 和 val 的 box_loss、cls_loss、dfl_loss 三条曲线的变化还有 precision、recall、mAP50、mAP50-95 的趋势。判断标准很直接训练 loss 持续下降验证集 loss 也降说明模型在正常学训练 loss 还在降但验证 loss 开始反弹就是过拟合信号此时 best.pt 往往在验证曲线最低点那一轮附近epochs 跑满也没关系patience 会自动停。这条链路里最不用动的就是损失函数本身YOLOv8 自带的分类损失和 DFL 对两分类小数据是够用的真正坑人的是标签噪声和类别不平衡后者的处理放避坑章。4. 验证与门限调优mAP 之外漏检率才是现场命门训练完先别急着挂摄像头。模型文件 best.pt 是验证集指标最高的那一个但验证集指标和现场表现之间还隔着置信度门限这道坎。这一章只做两件事跑标准验证、把门限调到现场能接受的程度。4.1 跑通 val把混淆矩阵和 PR 曲线导出来一个命令就能跑验证结果会输出到 runs/val 目录yolo detect val \ modelruns/train_car/weights/best.pt \ datamine_car_data.yaml跑完会在 runs/val 下生成 confusion_matrix.png、PR_curve.png、results.csv 这些文件。工业场景里我会重点看两个东西混淆矩阵里把 With People 判成 Normal 的那一格数字以及 PR 曲线在小目标区间的走势。mAP50 和 mAP50-95 的区别要分清mAP50 是 IoU 阈值取 0.5对框位置宽容工业场景用它衡量“有没有检出来”mAP50-95 是多个 IoU 阈值的平均对框的贴合度更敏感。矿业轨道场景里人员检出比框稍微偏一点更重要所以与现场预期直接相关的是 mAP50 和召回率而不是那个看起来更漂亮的综合分。4.2 置信度门限怎么调扫描一遍再定别拍脑袋默认推理时 conf 是 0.25意思是有人的类别置信度超过 0.25 才输出框。这个值对现场不一定合适门限越低漏检越少但误报多门限越高误报少但可能漏人。工业安全场景里漏检比误报严重得多调整门限需要数据支撑而不是手感。我一般会对验证集跑一个门限扫描把不同 conf 下检出的“有人帧”数量打印出来import torch from glob import glob model torch.hub.load(ultralytics/yolov8, custom, pathruns/train_car/weights/best.pt, force_reloadTrue) imgs sorted(glob(mine_car_data/images/valid/*.jpg)) for conf in (0.15, 0.25, 0.35, 0.45): alerts 0 for p in imgs: r model(p, confconf) if any(float(d.conf) conf and int(d.cls) 1 for d in r.boxes): alerts 1 print(fconf{conf}, frames_with_person{alerts}/{len(imgs)})逻辑很简单同一批验证图分别用 0.15 到 0.45 的门限跑一遍统计有多少张图判定为“有人”。如果 conf 从 0.35 降到 0.25 时告警帧数突然大幅跳升说明这段区间是误报高发带门限要避开如果跳升平缓说明检出结果稳定选低一点更安全。门限这东西看着是数学调起来有一半是玄学但至少先让数据告诉你跳到哪了再结合现场拍脑袋。5. 避坑记录标签反了、目标太小、中文路径五个真实教训这一章写的是这类轨道场景数据集最常见的五个坑全是用训练轮数换来的血泪经验。每一条按现象、原因、解决的方式记录希望你能在踩到之前看到。5.1 标签含义反转Normal 不是“矿车正常”现象训练完测试模型把有人的图判成 Normal把空场景判成 With People准确率接近随机猜测。原因Normal 在这份数据里的语义是“画面里没有人员/无人员干扰”不是“矿车运行正常”。只看 names 列表很容易把 0 和 1 的意义理解反尤其是当 names 写成 Normal 和 With People 这种带误导性的短语时。解决训练前随机挑 20 张图把 jpg 和对应 txt 内容一起看一遍确认类别号与画面内容匹配。等训练完再拿 10 张没进训练集的图人工复核一次预测结果。这个习惯花不了几分钟能省一整轮返工。5.2 人员目标太小imgsz640 导致远端漏检现象验证集 mAP50 有 0.8 以上但拉回现场视频远处的人员框一闪而过甚至完全没有框。原因监控画面里轨道远端人员只占二三十像素输入 640 后缩得更小特征基本被压没了。mAP 是被近处大目标撑起来的远端人员是被平均掉的少数派。解决把 imgsz 调到 960 重训小目标召回明显提升代价是显存占用接近翻倍。如果显卡显存有限把画面按区域切块分别推理或者只对远端区域单独跑一次检测。动手之前先确认摄像头安装位置和人员活动范围人大概率只出现在固定几块区域的话ROI 切块是性价比最高的方案。5.3 中文路径和中文目录导致的图片读取失败现象训练报错“Cant open image file”但文件确实存在ls 也能看到。原因Windows 下打包的中文目录在 Linux 解压后可能出现编码错乱或者 OpenCV 读取路径时遭遇中文解析问题。外层目录名“轨道上矿车和人员检测数据集”这种长中文名最容易踩雷。解决解压后立刻把数据集根目录重命名为纯英文比如 mine_car_data图片文件名保持英文数字混合也没问题。训练启动前先完整遍历一遍图片路径别等报了错再翻日志。5.4 类别不平衡时别第一刀就砍向损失函数现象With People 类别样本很少训练几十轮后 cls_loss 始终降不下来验证集上这个类别全漏。原因样本不均衡时优化器被 Normal 类主导模型倾向于把一切都预测成多数类。而新手最容易在这种时候去网上搜“yolo 损失函数 改进”然后盲改损失折腾两天指标没变。解决这份数据两分类相对简单先做两件事。第一把 With People 的小样本离线复制增强几份做翻转、亮度变化补充数量第二检查是不是标注本身就少漏标导致模型没东西可学。这两步做完还有问题再考虑类别权重或替换损失函数数据层面永远是第一优先。5.5 标注框太草率模型学到的是“半个人”现象训练时 loss 降得很快但验证时把矿车上的安全帽都当成人员或者只检出人的上半身。原因部分标签框得不准只框了半身或者把远处的人整个漏标模型学到的目标特征变成了“画面中部的某个区域块”。解决用 fat 文件把最难样本拼成网格图人工逐张看标注框是否贴合目标尤其是小目标。如果标注质量确实差我的做法是先用一个现成的人员检测模型做预标注再人工修正这比从零重新标快得多。6. 把单帧检测变成时序判断两行逻辑压住误报再谈精度单帧目标检测的结果直接拿去报警在工业现场一定会被误报折磨到怀疑人生。摄像头轻微抖动、矿灯光斑、安全帽反光都可能导致某一帧出现假阳性。工程上最常用也最有效的做法是加一个时序窗口不是看到一帧就报而是连续若干帧里检测到有人的次数达到阈值才触发。6.1 用队列做滑动窗口10 帧里 6 帧判有人才报警代码非常简单一个队列就能实现from collections import deque class PersonAlarm: def __init__(self, window10, need6, conf0.35): self.buffer deque(maxlenwindow) self.need need self.conf conf def update(self, results): has_person any( int(d.cls) 1 and float(d.conf) self.conf for d in results.boxes ) self.buffer.append(has_person) return sum(self.buffer) self.needbuffer 保存最近 window 帧的布尔结果每来一帧就往右推一格旧帧自动丢弃。sum(self.buffer) 统计最近 10 帧里检出有人的帧数达到 need 才返回触发。为什么用“10 帧中 6 帧”而不是“连续 6 帧”因为视频流里单帧偶发抖动很常见连续 6 帧的要求容易被一次瞬间干扰打断而 10 中 6 等于给出了一秒左右的容忍度天然平滑毛刺。参数按现场需求调摄像头 10 帧每秒时window10 就是 1 秒窗口need6 意味着 1 秒内 6 次判定有人才报警。如果现场对漏报零容忍把 need 降到 4如果误报太多就升到 8。6.2 一道教训我第一次把这类检测模型挂到现场监控时只看了 mAP 就以为收工了结果现场当天误报三次值班长一句“这系统不太行”让我记到现在。后来我拿到任何数据集都会先搞清楚三件事摄像头离轨道多远人在画面里大概占多大规则里说的“有人”到底是人在车旁边还是人在车上。带着这三个答案去调门限和窗口再回来谈模型精度顺序不能反。从那以后我每做完一批数据都要先拿 30 分钟去理解现场约束再动手写参数。希望帮到你。本文还有配套的精品资源点击获取
返回列表