ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

汽车充电插口识别数据集制作与YOLO v5训练实战指南

汽车充电插口识别数据集制作与YOLO v5训练实战指南 简介一套面向汽车充电插口识别的YOLOv5标注数据集围绕快充与慢充插口检测任务构建适合算法工程师、智能充电系统开发者使用也可用于充电桩运维、自动巡检和新能源汽车视觉识别的项目实践。数据集共481个文件主体包含240张jpg原始图片、240个txt标注文件以及1个yaml配置文件压缩包整体只有9.54MB体量轻、结构清晰。其中txt文件记录每个目标的类别与边界框坐标yaml定义类别名称和路径可直接导入YOLOv5训练环境免去格式转换与手工标注环节。目前已有244人学习下载说明该细分场景的数据需求真实存在具有参考价值。借助这套数据用户可以快速完成数据集划分、模型训练与验证在此基础上继续扩充样本即可提升识别稳定度对快充、慢充插口自动区分很有帮助。1. 汽车充电插口识别数据集到底在解决什么问题先看清快充、慢充的标注边界停车场里的充电机器人要插枪第一件事往往不是识别车牌而是判断目标车左后方的接口是快充还是慢充。汽车充电插口识别数据集的核心使命就是为YOLO v5这类目标检测模型提供一份带快充、慢充类别标签的标注数据每个充电口一个边界框类别二选一标签整理成YOLO v5标准格式后放进训练脚本就能开跑。它直接解决自动化充电、充电口盖板联动、巡检机器人对准等场景的落地问题。适合正在做充电桩视觉识别的算法工程师、智能硬件集成商也适合想拿真实业务数据练手YOLO v5的新手。不过在下载或制作数据集之前有一个决定成败的问题要回答你的类别到底按什么语义定。2. 站在标注角度看快充与慢充类别怎么定、框怎么画才不会白标2.1 直流口与交流口的视觉差异先批一眼数据再定类别方案“快充”和“慢充”在物理世界里对应直流充电口和交流充电口。直流快充的插口孔位普遍更多、孔径更粗整体结构更复杂枪头也更重交流慢充的插口孔位少、孔径细常见于家充桩和部分公共慢充桩。这些差异在图像上足够明显同一车型的快慢充口摆在画面里人眼一眼就能分辨。真正容易出问题的不是快慢充本身而是你的拍摄场景里还有没有第三类东西——比如插在口子里的充电枪。我一般会先抽样浏览一遍数据集里所有图片统计三个比例空口快充、插枪快充、空口慢充、插枪慢充各占多少。如果插枪样本占比很高就要决定类别边界是指“充电口的物理类型”还是指“当前这个口是否空闲”。这个决定直接改写标签语义前者是快慢充二分类后者是空闲/占用二分类。数据集标题写明“可识别快充、慢充插口”语义按前者走但如果你的业务是想让机器人知道能不能插枪常见做法是保留快慢充两个类别再额外训练一个分类器判断口内是否有枪头遮挡而不是把类别拆成三个。目标检测的类别体系设计有一条边界类别只能由视觉形态决定。充电中、空闲、故障这类状态不改变物理插口的形态时不要为了状态信息硬拆类别否则某几个状态样本量不足模型会把它们当噪声处理。这也是图像识别项目里被问得最多的一个分类误区——语义识别和视觉识别的目标不一样别混在一起定标签。2.2 边界框到底框哪里不框盖板、不框枪头、不框线缆拿到一份标注数据时新手最容易忽略的是边界框的定义粒度。同一个充电口有人框整个塑料外壳有人框内部金属端子有人把充电口盖板也框进来结果就是模型在同一批数据里被迫拟合两种“正确”答案。常见做法是边界框锁定充电口外壳的内沿也就是枪头插进去前你视线能看到的那一圈物理边界盖板、铰链、车身腰线、充电线缆都不入框。插枪状态下枪头会遮挡插口中央此时框要能躲开枪头的轮廓。如果枪头遮挡超过一半这张样本的类别置信度已经不可靠建议直接删除而不是硬留。数据量不足时也可以保留但要在txt标注里只画可见的那部分插口轮廓而不是把整个枪头顶着的那块区域当作插口框。这个细节在训练后期直接影响精度模型学到的是“插口孔位和外壳边缘”而不是“枪头的黑色手柄”。下表是我在标注充电口数据时常用的框选对照区域该不该框原因插口外壳内沿该框物理插口的稳定视觉边界充电口盖板不该框盖板外形随车型差异大充电枪手柄与线缆不该框枪头形态会污染类别特征插口内部金属端子不单独框小目标标注一致性差LED导光带按需框夜间反光会引起误检2.3 两个类别够用吗为什么“占用”状态不单开一个类不少人拿到这份快慢充数据集后第一反应是“我要识别充电中/空闲/故障三种状态”于是把标注从两个类改成三个类。这个改动在YOLO v5里技术上没问题但实际效果往往翻车三种状态的图片数量极不平衡故障样本可能只有几十张模型会把故障状态学成背景噪声最终三个类全部被拉低。常见的可靠分工是主检测模型只负责快充/慢充的二分类定位状态判断交给另一层规则或一个轻量分类器用检测框裁剪出来的区域做二次识别而不是指望一个检测头同时输出物理类型和运行状态。数据集标注越纯粹模型在真实场景里越不容易被无关信息干扰。3. 把标注整理成YOLO v5格式转换脚本与目录结构的可抄版本3.1 先搭目录树images与labels同名后缀配对是硬性要求YOLO v5对目录结构有固定约定图片放在images目录标签放在同级的labels目录标签文件名必须和图片名同名扩展名是.txt。比如fast_charge_001.jpg对应labels/fast_charge_001.txt。训练集、验证集各自维护一套images/labels子目录这个结构在训练脚本里直接引用。mkdir -p datasets/car_charge/train/images datasets/car_charge/train/labels mkdir -p datasets/car_charge/val/images datasets/car_charge/val/labels这里val的labels目录可以留空但如果后续要做评估必须放真实标注。YOLO v5在训练时如果检测到val的labels目录不存在会自动跳过验证这会让你的训练过程少掉混淆矩阵输出不便于判断快充和慢充分别学得怎么样。我一般会把val的标注一并准备好哪怕val图片只有几十张。如果数据集作者已经直接提供了txt格式你可以跳过转换步骤但如果你拿到的是VOC XML或COCO JSON标注下一步的坐标换算脚本是少不了的。3.2 从VOC XML到YOLO txt中心点归一化坐标的转换脚本YOLO v5的txt每一行表示一个目标格式固定为class_id x_center y_center width height其中后四个值都是归一化到0到1的相对坐标。VOC XML里存的是像素坐标需要除以图片宽高。下面这个脚本是我平时在用的最小转换版本import xml.etree.ElementTree as ET def voc_xml_to_yolo(xml_file, class_to_id, img_w, img_h): 单个XML转YOLO txt格式的行列表 tree ET.parse(xml_file) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in class_to_id: continue 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 lines.append(f{class_to_id[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines这段代码里最关键的是class_to_id字典。它决定了“fast_charge”是0还是“slow_charge”是0这个顺序一旦定下来整个数据集的所有txt都必须遵守。后面训练配置文件里的names列表顺序也必须和它完全一致不然模型会学出一套“张冠李戴”的映射。img_w和img_h如果XML里的size节点缺失可以用OpenCV读取原图尺寸。这是目标对象识别里最常见的兜底手段也算是opencv识别物体的一个典型用法import cv2 img cv2.imread(img_path) h, w img.shape[:2]3.3 转换后立刻做自检空文件、越界坐标、类别ID连续性转换脚本跑完不是终点只要有一张图片尺寸读错整个数据集的坐标就全错了。我一般在转换完成后跑一个自检脚本只做三件事检查txt文件是否为空、检查坐标是否在0到1之间、检查类别ID有没有超过nc - 1。import os label_dir datasets/car_charge/train/labels max_id 0 for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(格式错误, fname, line) continue class_id int(parts[0]) max_id max(max_id, class_id) coords [float(v) for v in parts[1:]] if any(v 0 or v 1 for v in coords): print(坐标越界, fname, line) print(max class id:, max_id)这段自检脚本里的max_id很关键。如果它大于1说明txt里出现了类别2但你的配置文件只声明了2个类训练时就会报错或者把类别2静默忽略。出现这种情况的原因通常是不同批次标注时class_to_id字典不一致比如第一批把fast_charge记为0第二批误记为2。遇到这种问题不要手动改txt要回到转换脚本统一重新生成。坐标越界多半是XML里的bndbox宽高为负需要回到原始标注检查是否有框重叠或反框的情况。4. 用YOLO v5训练充电插口识别模型data.yaml与训练参数的落地方案4.1 data.yaml的最小六行配置路径指向images目录YOLO v5的训练入口从data/*.yaml开始。这个yaml文件的作用是把训练集、验证集路径和类别清单告诉程序。你不需要在yaml里写labels路径YOLO v5会自动根据images的路径推导出同级labels目录。# datasets/car_charge.yaml train: /data/car_charge/train/images val: /data/car_charge/val/images nc: 2 names: [fast_charge, slow_charge]这里的names顺序必须和转换脚本里的class_to_id顺序一致。如果转换脚本里class_to_id {fast_charge: 0, slow_charge: 1}那names列表的第一个元素就是0号类别第二个是1号类别。顺序写反不会报错但训练出来的模型会把快充当慢充、慢充当快充而且这类错误很难在loss曲线上看出来只有到了部署实测才会暴露。train和val的路径既支持绝对路径也支持相对路径但如果你的图片在移动硬盘或云盘挂载点建议统一用绝对路径避免换机器后路径失效。这也是“识别不到”问题里最常见的低级原因。4.2 训练最小命令与必调参数img尺寸、batch、epochs训练命令从YOLO v5仓库根目录执行以下是我在充电口识别任务上最常用的一条起步命令python train.py \ --img 640 \ --batch 16 \ --epochs 100 \ --data datasets/car_charge.yaml \ --weights weights/yolov5s.pt \ --project runs/car_charge参数说明--img 640是输入分辨率。充电口在车位监控画面里通常只占1%到5%如果你手头的图片正好是这种场景640的起步值偏小建议直接上960。分辨率和显存占用成正比显存不够就把batch减半。--batch 16是批量大小。单卡16G显存跑yolov5simg640时一般能撑住如果OOM报错优先减batch而不是减img。--epochs 100是训练轮数。充电口识别不算是特别难的任务100轮足够模型收敛到可用状态早停参数建议打开。--weights weights/yolov5s.pt是预训练权重。小模型s版本在迁移学习场景下效果好、训练快数据量不足时也比从零训练可靠。训练时不要一上来就改数据增强参数。YOLO v5默认的hyp.scratch-low.yaml对大多数检测任务已经足够。快慢充识别翻车往往不是因为增强不够而是因为前面的标注问题没解决。等基线跑通了再考虑针对小目标做mosic增强或加一层随机裁剪。4.3 训练时看哪些指标Fast_charge和Slow_charge各自的AP才是关键训练启动后不要只盯着整体mAP。充电口识别是两个类别的二分类检测问题类别数少整体mAP会被一个类的表现平均掉掩盖掉“慢充很好、快充很差”的问题。正确做法是每隔几十个epoch看验证集上每个类别的AP值。python val.py \ --weights runs/car_charge/exp/weights/best.pt \ --data datasets/car_charge.yaml \ --task valval.py运行结束后YOLO v5会在runs/car_charge/exp/下输出一个混淆矩阵图confusion_matrix.png这是判断快慢充互相误检的最直接工具。正常情况应该是fast_charge和slow_charge所在的主对角线值接近0.9以上如果出现较大副对角线值说明两个类别之间存在系统性误检请回到第2章检查标注框是否混入了枪头或盖板。训练结束看results.png里的val/box_loss和val/cls_loss曲线。box_loss下降说明定位稳定cls_loss下降说明分类正确。如果cls_loss在训练后期反而上升多半是学习率没降到底可以调低--lr0或者延长--warmup_epochs。还有一个习惯值得养成每次训练结束随机抽20张验证集图片做推理可视化用肉眼看一遍框的位置。数据集标错的地方指标不一定反映得出来但肉眼能直接看出来。5. 充电口识别数据集的5个避坑现场现象、原因、排查路径5.1 快充、慢充互相误检枪头入框是头号嫌疑现象模型把插着枪的快充口识别成慢充或者把枪头单独框出来当成另一个目标。白天无遮挡场景一切正常枪头一出现就乱。原因标注时把充电枪手柄甚至线缆一并框进了边界框。枪头的视觉特征比插口孔位更突出黑色手柄、粗线缆模型经过几十轮学习后会自动把“枪头存在”当成快充或慢充的主要判据。一旦换了品牌、换了枪头颜色识别立刻崩。解决重新回到标注阶段把包含枪头的框全部修正为只框插口外壳内沿遮挡超过一半的样本直接删除。如果枪头样本量大可以用这些图片单独做一次困难样本挖掘看模型在未被枪头覆盖的插口区域能否稳定出框。修正后重训误检率通常会明显回落。5.2 小目标整片漏检充电口在640分辨率下只有几十个像素现象训练时mAP很高但部署到真实车位上完全检不到。车辆在画面里占据大面积充电口在画面右下角模型输出全为空。原因充电口相对整张图比例太小。YOLO v5下采样到20×20的特征图后一个原本30×30像素的充电口只剩不到两个像素特征已经消失。这个问题和它是不是充电口无关——车牌识别、农业病虫害识别里的叶片病斑、小零件缺陷检测都栽在同一件事上。解决第一优先级是提高输入分辨率把--img从640提到960甚至1280显存不够就换一个更轻的模型权重比如yolov5n或yolov5s的较小输入。第二优先级是改标注粒度确认一个充电口只有一个框不要把孔位拆成多个小框。如果这两招还不够参考第6章的切图推理方案把原图切成多块再分别检测。5.3 夜间导光带反光模型把LED光环当成插口现象白天识别正常晚上充电口周围的LED导光带一亮预测框大面积偏移甚至稳定框到光带所在的那片区域。原因导光带与插口外沿在暗环境下形成高对比的闭合轮廓模型学到的是“发光矩形”而不是插口孔位。这是数据集制作阶段没有覆盖夜间样本导致的。充电口识别场景里夜间恰恰是自动化充电的高频时段忽略这一批样本等于放弃一半业务场景。解决把夜间数据单独挑出来做亮度增强复制一份放进训练集并保证train/val分布一致。同时可以在hyp.yaml里加大hsv_h和hsv_s的扰动范围让模型对颜色偏移更鲁棒。如果条件允许采集一批导光带不同亮度阶段的图片按亮、暗、熄灭三个层级分桶逐个确认模型在哪一档掉链子。5.4 训练报错Dataset not found文件名配对问题现象启动训练后几秒内报错提示Dataset not found或AssertionError: train: No labels in .../train/labels。检查路径发现路径确实存在把train路径直接复制到浏览器地址栏也能打开。原因YOLO v5要求labels目录名必须固定为labelstxt文件名必须与图片文件名完全一致。常见的坑是Windows系统复制到Linux后文件名大小写变化或者是图片是fast_charge_001.JPG标签是fast_charge_001.txt——字符串完全对不上。解决训练前先跑一次配对自检文件名不匹配的图片单独列出来import os img_dir datasets/car_charge/train/images label_dir datasets/car_charge/train/labels for img_file in os.listdir(img_dir): base os.path.splitext(img_file)[0] if not os.path.exists(os.path.join(label_dir, base .txt)): print(缺少标签:, img_file)运行后如果发现大量缺失优先检查是不是大小写问题如果是个别图片缺失就把它们从训练集挪到val集不要留空标签在训练目录里因为空txt会被YOLO v5当成背景样本参与训练本来没有目标的图被强制学习为背景会干扰正样本的梯度。5.5 换机器后类别ID对不上模型输出“张冠李戴”现象同一份数据集在本机训练效果正常换到另一台机器上训练后模型把慢充全部识别成快充loss曲线却显示正常收敛。原因另一台机器上的class_to_id映射和本机不一致。比如本机fast_charge是0另一台机器的转换脚本里把它设成了1但data.yaml里的names顺序还是[fast_charge, slow_charge]导致模型学到的0号特征对应慢充而推理时0号输出被解释成快充。解决每次拿到新机器的数据集先打印一张txt的第一列看看最大值是多少awk {print $1} datasets/car_charge/train/labels/*.txt | sort -n | uniq -c如果输出的类别ID只有0和1说明二分类没问题如果出现了大于等于2的值说明数据里混入了别的类别或者转换脚本反复执行导致标签叠加。这个命令几秒钟就能跑完建议写进你的数据集验收清单每次换设备、换同事、换数据副本都执行一遍。我也在这个坑里栽过两次后来养成习惯数据集交接时连class_to_id字典和转换脚本一起交接光交接txt文件等于埋雷。6. 两个进阶技巧用混淆矩阵定位误检视角、用切图推理救小目标6.1 用混淆矩阵定位“哪些视角的快充被人为看成慢充”训练完跑python val.py输出目录里的confusion_matrix.png只能告诉你“快充和慢充有互相误检”但不知道具体是哪些视角。我的做法是把val集按角度信息做一个粗略分桶正对、侧对、仰拍、插枪遮挡这几类各抽20张图分别跑推理统计每个桶里fast_charge被误判成slow_charge的图片数。哪个桶误判率高就回到那个桶的标注原图看框的边界是不是画歪了。视角导致的误检通常不是模型能力问题而是某一类视角下的标注一致性本身就差。比如侧对角度下插口外壳有透视变形标注员容易把外壳外沿多框出两三像素恰好让慢充口看起来像快充口。修完标注重训一次比加大数据增强更有效。6.2 一个可抄的切图推理脚本tile_infer的思路与合并注意点小目标漏检在充电口识别里太常见了。我常用的兜底方案是切图推理把原图切成几个固定尺寸的块每块独立送入模型再把结果映射回原图坐标。下面是一个最小实现的思路import numpy as np def tile_infer(model, img, tile_size640, overlap32): h, w img.shape[:2] detections [] for top in range(0, h - tile_size 1, tile_size - overlap): for left in range(0, w - tile_size 1, tile_size - overlap): crop img[top:top tile_size, left:left tile_size] # 单块推理坐标偏移回原图 results model(crop, sizetile_size) for det in results.xyxy[0].cpu().numpy(): x1, y1, x2, y2 det[:4] detections.append([ x1 left, y1 top, x2 left, y2 top, det[4], int(det[5]) ]) return detections切图推理有两个注意点。第一是重叠区不能省相邻tile之间至少留32像素重叠否则跨界的目标会被一刀切掉大半第二是合并检测结果时要做一次NMS去重同一个充电口如果落在两个tile的重叠区里会产生两个框很多实现里漏了这一步导致输出重复框。我一般会直接调YOLO v5的non_max_suppression函数对detections再做一次后处理。切图推理的训练阶段不用改只在推理阶段做训练时仍然在整图尺度学习。在我的项目里切图推理加夜间样本增强这两个习惯通常能把充电口识别的召回率拉高8到12个百分点。做这类贴近业务的数据集判断标准从来不是测试集指标多好看而是机器人晚上开进车库、灯光昏暗、充电口左歪右斜时它能不能一次就把快慢充认对。希望这些经验帮到你少踩几个来回。本文还有配套的精品资源点击获取
返回列表