ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

电梯状态检测实战:VOC转YOLO格式与YOLOv8训练全攻略

2026/10/2 9:31:29 拓冰建站 浏览量
电梯状态检测实战:VOC转YOLO格式与YOLOv8训练全攻略 简介这是一份面向电梯场景的目标检测数据集同时提供Pascal VOC与YOLO格式标注可用于电梯开关状态识别、人员进出检测等任务的模型训练与算法验证适合计算机视觉研究者、算法工程师及深度学习入门者使用。压缩包为7z格式共2000个文件以XML标注文件为主体1999个另含1份使用说明TXT整体大小约77.26MB数据覆盖电梯关闭、电梯内有人、电梯空置、电梯开启四个类别总计3330个标注框各类别框数分布均衡并由labelImg工具完成逐框校准标注质量可靠可直接导入主流目标检测框架进行训练与评估。目前已有548人学习/下载是一份结构清晰、可直接上手的电梯监控场景数据集。1. 电梯开关状态人员进出检测数据集不只是“有图有标注”这么简单拿到“电梯开关状态人员进出检测数据集VOCYOLO格式2220张4类别.7z”这个压缩包第一反应别急着解压就跑训练。电梯场景和通用目标检测最大的区别在于门开关状态是细粒度状态变化人员进出是带方向语义的行为判断两者叠在一起单纯用COCO预训练模型直接迁移效果往往很一般。这套数据集的价值在于把“门开/门关/人员进入/人员离开”四个类别固定成监督信号让模型学会的是电梯轿厢内的状态机而不是泛泛的人脸或人体框。适合做电梯困人监测、楼层停靠行为分析、智慧社区安防的从业者对只想拿来跑通YOLO流程的新手它同样是一份干净的入门数据。2. 数据集拆解4个类别定义与VOC/YOLO两种标注的真实差异2.1 四个类别到底怎么划分的类别定义直接决定模型能不能收敛。常见做法是四个类别对应四类独立语义door_open电梯门处于完全开启或开启超过一半的状态框选门洞区域。door_closed两扇门完全闭合或缝隙小于一个阈值框选门扇整体。person_enter人员从候梯厅走入轿厢身体重心越过门线且朝向轿厢内部。person_exit人员从轿厢走向候梯厅身体重心越过门线且朝向外部。这里最容易踩的坑是person_enter和person_exit的边界帧怎么切。常见标注规则是以人体脚部位置越过电梯门中线为分界点脚部在中线内侧标person_enter外侧标person_exit。如果按胸部或头部位置切人员转身时会出现一个框同时带两个标签的情况。2220张图对四类目标来说不算宽裕尤其是person_enter和person_exit这类带方向语义的类别单类可能只有三四百个实例。训练时类别不平衡会直接表现在recall上后面第五章会讲怎么处理。2.2 VOC的XML和YOLO的TXT在说什么VOC格式的标注文件是XML每个目标一个object节点坐标用左上角和右下角绝对值annotation folderimages/folder filenameelevator_001_0001.jpg/filename size width1920/width height1080/height depth3/depth /size object namedoor_open/name bndbox xmin512/xmin ymin300/ymin xmax1400/xmax ymax980/ymax /bndbox /object /annotationYOLO格式是TXT每行一个目标内容为类别id center_x center_y width height坐标全部归一化到0到10 0.497917 0.592593 0.462500 0.629630两种格式看着只是写法不同实际影响训练的两个关键点第一VOC的xmin/ymin是像素坐标YOLO的center_x/center_y和width/height是归一化后的相对坐标转换时除的是图片宽高不是标注框宽高。第二VOC的类别名是字符串YOLO的类别id是整数id顺序必须和data.yaml里的names列表一一对应顺序反了模型会把door_open当person_enter训。提示拿到数据集第一件事打开classes.txt或data.yaml确认类别顺序再抽三张图手工对照XML/TXT和原图这一步能省后面三个小时的排查时间。2.3 目录结构长什么样解压后常见目录结构如下elevator_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── annotations/ │ ├── voc/ │ │ ├── train/ │ │ └── val/ │ └── yolo/ │ ├── train/ │ └── val/ ├── classes.txt └── data.yamltrain和val的划分一般是8:2即约1776张训练、444张验证。如果压缩包里已经是这个结构直接改data.yaml的路径就能训。如果只有图片和统一标注目录没有按train/val分好需要自己做划分第三章会给出脚本。3. 把VOC转成YOLO格式转换脚本与数据集划分实操3.1 转换脚本的完整写法如果压缩包里同时给了两种格式转换这步可以跳过但多数从业者拿到手的是VOC需要自己转YOLO。下面是我常用的转换脚本import xml.etree.ElementTree as ET import os from pathlib import Path CLASS_MAP { door_open: 0, door_closed: 1, person_enter: 2, person_exit: 3 } def convert_voc_to_yolo(xml_path, out_txt_path, img_width, img_height): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: continue bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 坐标裁剪防止标注超出图片边界导致训练报错 xmin max(0, min(xmin, img_width)) xmax max(0, min(xmax, img_width)) ymin max(0, min(ymin, img_height)) ymax max(0, min(ymax, img_height)) # 跳过退化框宽或高为0 if xmax xmin or ymax ymin: continue cx (xmin xmax) / 2 / img_width cy (ymin ymax) / 2 / img_height w (xmax - xmin) / img_width h (ymax - ymin) / img_height lines.append(f{CLASS_MAP[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) voc_root elevator_dataset/annotations/voc yolo_root elevator_dataset/annotations/yolo image_root elevator_dataset/images for split in [train, val]: voc_dir os.path.join(voc_root, split) yolo_dir os.path.join(yolo_root, split) os.makedirs(yolo_dir, exist_okTrue) for xml_name in os.listdir(voc_dir): if not xml_name.endswith(.xml): continue stem xml_name[:-4] img_path os.path.join(image_root, split, stem .jpg) # 读图片尺寸不能写死1920x1080 from PIL import Image with Image.open(img_path) as im: w, h im.size convert_voc_to_yolo( os.path.join(voc_dir, xml_name), os.path.join(yolo_dir, stem .txt), w, h )这段脚本的逻辑分三层先解析XML拿到每个目标的类别名和像素坐标再把像素坐标归一化成YOLO所需的中心点加宽高最后按类别映射表写入TXT。中间做了两件容易漏的事坐标裁剪防止标注出界、跳过宽高为0的退化框。这两步不做训练时YOLO会在数据加载阶段直接报AssertionError: bbox with no area之类的错。3.2 图片尺寸为什么必须动态读取有的转换脚本图省事写死img_width1920, img_height1080。这在本数据集的图片尺寸不统一时会出大问题归一化坐标失真模型训练时mAP会莫名低几个点而且很难排查。用PIL.Image.open动态读宽高成本几乎为零但能保证坐标换算始终正确。3.3 数据集划分脚本如果压缩包没有预先划分train/valimport random import shutil from pathlib import Path random.seed(42) image_root Path(elevator_dataset/images_all) ans_root Path(elevator_dataset/annotations_yolo) train_dir Path(elevator_dataset/images/train) val_dir Path(elevator_dataset/images/val) train_ans_dir Path(elevator_dataset/annotations/yolo/train) val_ans_dir Path(elevator_dataset/annotations/yolo/val) split_ratio 0.8 image_paths list(image_root.glob(*.jpg)) random.shuffle(image_paths) train_size int(len(image_paths) * split_ratio) train_paths image_paths[:train_size] val_paths image_paths[train_size:] for sub_dir in [train_dir, val_dir, train_ans_dir, val_ans_dir]: sub_dir.mkdir(parentsTrue, exist_okTrue) for img_path in train_paths: stem img_path.stem txt_path ans_root / f{stem}.txt if txt_path.exists(): shutil.copy(img_path, train_dir / img_path.name) shutil.copy(txt_path, train_ans_dir / f{stem}.txt) else: print(f警告: {stem} 缺少标注文件已跳过)这里random.seed(42)是关键固定随机种子让划分结果可复现。每次运行都换随机种子的话同样的数据训练结果对比就没有意义。划分时只拷贝有对应TXT的图片这一步能筛出标注文件缺失的脏数据。4. 用YOLOv8在本地训练电梯检测模型配置与命令4.1 数据配置文件data.yamlYOLOv8训练前先写好数据集描述文件path: /home/user/elevator_dataset train: images/train val: images/val nc: 4 names: 0: door_open 1: door_closed 2: person_enter 3: person_exit注意path建议写绝对路径写相对路径在切换工作目录时容易找不到数据。train和val配的是相对于path的子路径。nc必须和names数量一致少了会报类别数不匹配多了训练时最后一个类别没有样本会导致loss异常。注意names的顺序必须和上一章CLASS_MAP里的 id 顺序一致。如果压缩包里自带的data.yaml的names顺序和标注TXT里的id不一致优先改data.yaml不要改TXT因为改大量TXT容易手滑改错。4.2 训练命令与关键参数yolo detect train \ modelyolov8n.pt \ dataelevator_dataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ workers4 \ patience20 \ projectelevator_yolo \ namerun1参数含义逐个说modelyolov8n.pt用nano版本做迁移学习2220张图的规模带不动大模型直接上yolov8x会严重过拟合epochs100对这个数据量够用前30轮loss下降最明显后面是缓慢优化imgsz640是速度和精度的平衡点电梯监控画面一般本身分辨率不低但训练时统一缩到640能加快迭代batch16在8G显存的显卡上大概占5G左右显存不够就降到8patience20是早停连续20轮验证集mAP没提升就自动停防止无效训练浪费时间。训练过程中如果看到loss在几十轮后突然跳到NaN常见原因是batch太小加学习率太大把lr0从默认0.01降到0.001重跑。如果只看到训练集loss正常但验证集mAP一直不动优先怀疑标注类别不平衡不是模型问题。4.3 训练输出怎么读训练结束后在elevator_yolo/run1/下可以看到results.csv、weights/best.pt、weights/last.pt。best.pt是验证集mAP最高的权重last.pt是最后一轮的权重部署时用best.pt。打开results.csv重点看四列metrics/mAP50(B)、metrics/mAP50-95(B)、train/box_loss和val/box_loss。mAP50在0.85以上对这个场景算正常水平mAP50-95一般比mAP50低0.2到0.3不用慌。混淆矩阵保存在confusion_matrix.png这个图能直接看出四个类之间互相认错的情况。如果person_enter和person_exit大量互认说明标注时的边界帧定义不一致需要回看训练集里这两个类的照片统一判断标准。5. 避坑7z解压、标注丢失与训练崩溃的常见问题5.1 7z压缩文件密码正确但一直报错现象解压时输入压缩包密码提示正确但解压中途弹出“数据错误”或“CRC校验失败”。原因7z在WinRAR里解压时对高压缩比的文件容易出错WinRAR处理7z格式本身就不是强项。解决改用7-Zip官方工具或命令行7z x elevator_dataset.7z -o./elevator_dataset如果是Linux服务器提示command not found先装p7zip和p7zip-fullsudo apt install p7zip-full解压后检查文件数量是否和压缩包内一致不要直接开始训练。5.2 图片和标注文件名对不上现象训练时提示No labels found in ...或某张图片找不到对应TXT。原因压缩包内部分图片是PNG但标注文件按JPG命名或者文件名带空格。解决先统计数量对账find images -name *.jpg | wc -l find annotations -name *.txt | wc -l数量不一致时写脚本找出没有标注的图片删除或重新标注别带着脏数据硬训。5.3 YOLO训练中BN崩溃导致loss全是NaN现象训练进行到某轮开始box_loss和cls_loss直接变成nan之后所有指标全部NaN。原因电梯轿厢内反光导致部分图片有过曝区域RGB值异常加上batch太小BN层统计量在个别batch上翻车。解决先调小学习率lr00.001再不行换imgsz416降低输入分辨率最后检查训练图片里是否有纯白或纯黑图删除后用labelimg重新补标。5.4 混淆矩阵总和不为1现象验证完看混淆矩阵每行数字加起来不是1。原因YOLO的混淆矩阵带背景类别归一化时背景列也参与计算看起来总和多于1。这是正常的不是模型坏了。解决不用管总和直接看对角线数值占比四个类都能到0.85以上就算达标。person_enter和person_exit互认严重时把两个类的标注边界从“脚部过线”改成“整体重心过线”重新转标注训练一轮对比。5.5 训练到一半显存溢出现象CUDA out of memory中断训练。原因batch设太大或开了太多workers导致内存吃了显存。解决batch降到8workers降到2还溢出就换modelyolov8n.pt加imgsz416这套组合在4G显存上也能跑完整个训练流程。6. 模型没白干验证集表现分析与部署时的三个注意点训练完别急着收工先用验证集跑一轮预测挑出最差的几张图看看模型到底错在哪yolo detect val \ modelelevator_yolo/run1/weights/best.pt \ dataelevator_dataset/data.yaml \ conf0.25重点看两类失败样本一是夜间或地下车库光线极暗的图door_open和door_closed容易误检这时候在部署端加一个亮度预处理灰度化后做直方图均衡化对出图质量有明显改善二是ظة多人在电梯口互相遮挡时person_enter和person_exit的框会跳变部署推理时对同一目标做跟踪只有连续三帧都是同一个类别才输出状态变化事件能有效减少误报。部署时还有一个高频坑训练用imgsz640部署时为了速度改成imgsz320精度会掉一点但电梯门开关这种大目标其实不受影响。真正重要的是把conf阈值调高到0.5左右电梯场景漏检一次可以靠下一帧补上误报一次就要出工单宁严勿松。我自己的习惯是每次训练完都把best.pt和对应的data.yaml拷到一起存档标注格式转换脚本也一并存好。过了三个月回头调模型时发现纯靠记忆根本找不回当时的类别顺序和数据划分有这些文件在重新训练一轮只需要十分钟。希望这个数据集的整理和训练流程能帮你少踩几个我当年翻过的坑祝顺利。本文还有配套的精品资源点击获取