简介:本资源是面向计算机视觉初学者与智能交通应用开发者的停车位目标检测专用数据集,基于YOLOv8框架构建,适用于停车场自动化管理、车位状态识别等实际场景的模型训练与验证。数据集共1520张真实停车场图像(含延时摄影、多角度监控画面),经标准化预处理:自动EXIF定向校正、统一缩放至640×640分辨率,并通过水平/垂直翻转、±15°随机旋转、亮度扰动、高斯模糊及椒盐噪声增强,为每张原图生成3个增强样本,显著提升模型鲁棒性。压缩包含2000个文件,主体为1520个YOLOv8格式txt标注文件、479张jpg图像(含原始与增强样本)及1个结构清晰的data.yaml配置文件,总大小164.36MB,目录组织规范,开箱即用于YOLOv8训练流程。目前已有162人学习下载,可直接支撑从数据加载、模型训练到推理部署的完整实践闭环。 我刚把YOLOv8项目从通用检测切到停车位检测时,一个很强烈的感受是:网上现成的"停车位检测数据集"不少,但真能拿来训练出可靠模型的并不多。很多所谓公开数据集要么是在同一场景里连拍的照片,样本多样性严重不足,要么标注风格五花八门——有人标车位线框,有人标空位/占用位,还有人把整辆车的矩形框也算一份。这篇内容就围绕我实际跑通的一条路线来写:从数据集的选型、自建标注,到YOLOv8训练、踩坑排查,再到最后导出部署。目标读者是手里已经跑过基础目标检测、但对停车位这种细分场景还不太有把握的人。
1. 停车位检测到底难在哪:不是"框一个车位"这么简单
1.1 两个容易搞混的子任务:车位线检测和空位检测
先说一个非常容易踩的坑。停车位检测在真实项目里通常会分化成两个任务:一个是"车位线检测",模型要去框出画面里所有有标线的车位区域;另一个是"空位检测",模型需要在框出车位的同时判断这个车位当前是空着还是被占用。
这两个任务对标注的要求完全不同。如果只做车位线检测,一张图里标一个类别就行;但如果你最终要输出的是"哪个车位能停",那就必须把每个车位的状态标成两个类别。我见过有人在同一个类里既标空位又标占用,训练出来的模型在实拍画面里频繁把空位后面露出的一截车轮误判成占用,原因就是标注阶段没做类别区分。所以在做数据集之前,先想清楚你的业务到底需要哪个结果。
1.2 通用目标检测模型为什么在停车场里容易失灵
很多人拿YOLOv8默认的COCO预训练权重去停车场实拍画面直接测试,结果往往是一堆漏检和误检。这不是YOLOv8不行,而是停车位场景有几个通用检测不常遇到的硬伤:
- 标线退化:国内很多停车场的白色标线磨损非常严重,尤其露天停车场,日晒雨淋之后标线边缘模糊到人眼都要仔细辨认,模型自然更容易漏检。这是停车位检测最大的现实敌人。
- 视角问题:通用检测模型训练数据大多是平视或稍微俯视的画面,而停车位监控摄像头通常是高杆俯拍,或者干脆是无人机视角。视角一变,特征分布就变了,泛化能力立刻下降。
- 小目标占比高:从高处俯拍时,单个车位在1080p画面里的尺寸往往只有几十个像素,接近小目标检测的范畴。YOLOv8的默认检测头对小目标本来就偏弱,更别提很多开源数据集用的是720p甚至更低分辨率。
- 光照跨度大:地下车库的荧光灯、地面停车场的正午强光、傍晚逆光,光照条件变化范围极大。只靠数据增强里的HSV抖动,很难模拟出真实的极端光照。
这些问题单靠调参解决不了,源头还是在数据上。
2. 数据集从哪来:开源、合成、自建三条路怎么选
2.1 开源数据集横向对比:PKLot、CNRPark-EXT、ACM
我在做这个项目时,把几个用得比较多的公开数据集挨个试了一遍,简单列个表方便你选型。
| 数据集 | 场景 | 规模/特点 | 适合做什么 |
|---|---|---|---|
| PKLot | 巴西两个露天停车场 | 12000+张图像,包含晴天/阴天/雨天标注 | 露天场景的空位分类,光照变化丰富 |
| CNRPark-EXT | 欧洲某园区停车场 | 超过24小时连续采集,带空/占用标注 | 做时序推理、模拟真实监控流 |
| ACM | 航拍视角停车场 | 无人机俯拍,分辨率较高 | 做航拍场景的车位检测训练 |
| 自建 | 你的实际部署场地 | 数量可控,质量可控 | 最终落地效果通常最好 |
以上设计表要加点实际使用结论。PKLot的样本确实多,但是两个停车场的固定机位,场景其实比较单调,而且图像分辨率不高,训练出来的模型换个停车场就掉点。CNRPark-EXT更适合做验证,不适合做主力训练数据。ACM的航拍视角很好,但每张图的车位太多,标注密集程度高,如果只是几百个样本起步,训练成本也不低。
2.2 自建数据集的采集要点:角度、时段、反复拍摄
如果你希望最终部署在某个具体停车场,我强烈建议花一到两周时间去采集自建数据集。这样做的好处是模型见到的就是部署时要见到的画面,从源头上消除域差异。采集时注意三点:
- 机位固定后不要来回挪。停车场监控角度通常固定,采集时也尽量固定机位,让模型学习这个固定视野下"车位长什么样",而不是学习各种透视变形。
- 覆盖不同时段和天气。至少要拍一组大晴天正午、一组阴天、一组晚上灯光开启、一组清晨低照度的样本。如果条件允许,雨天也拍一组,雨天地面反光对特征干扰非常大。
- 每一处标线磨损的车位都要拍。这是很多人漏掉的——只拍清晰标线,导致模型在真实场景里遇到模糊标线直接歇菜。我在标注时会把"标线模糊但仍可辨认"的车位单独分组,训练时对这些样本做额外增强,效果提升很显著。
2.3 标注规范:用什么工具、怎么设计类别
标注工具我推荐用LabelImg或者LabelStudio。LabelImg轻量、和YOLO格式无缝衔接,适合快速框标注;LabelStudio适合团队协作,但上手稍微复杂一点。两者都支持输出YOLO txt格式,后面省去转换步骤。
类别设计上,如果你的目标是最小可行产品,建议直接用两个类:
- 0: empty(空车位)
- 1: occupied(占用车位)
这样模型在推理时天然输出每个车位的状态,不用再额外写判断逻辑。你可能会问:那要不要单独设一个"background"或者"line"类?我的经验是不要。YOLOv8本身会对无目标的区域做背景判定,额外加一个"line"类反而会干扰目标框的回归,尤其当车位线与相邻车位有重叠时。
另外有一个很关键的原则:不要框进车位的全部物理区域。停车位的矩形框在画面里经常被停着的车辆遮挡一部分,如果框把车也包进去,模型会学到"车位区域包括了车身侧面",在空位检测时反而容易把后车的一角算进来。我的做法是:框只圈标线内侧的可停车区域,即使这样会导致某些标注框与车体重叠,也比包住整车要稳。
3. YOLOv8训练前的数据组织与关键配置
3.1 数据集目录结构和格式转换
YOLOv8对数据集的目录要求比较固定,但也足够灵活。我习惯的目录结构是:
dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── parking.yamlimages里放图片,labels里放同名的txt标注文件。每个txt文件按YOLO格式逐行写:class x_center y_center width height,其中坐标值是相对图片宽高的归一化小数。如果你用LabelImg导出的是VOC XML格式,需要先做一次转换,脚本网上很多,但注意一定不要用绝对路径拼接图片路径,尽量处理成相对路径,后面换机器训练才不会出问题。
train和val的划分要注意:按停车场场景切分,不要按文件随机切分。如果你把同一个机位拍到的连续帧既放train又放val,模型的mAP会虚高到接近1.0,但换到新场景立刻打回原形。我一般按"不同天拍摄的批次"来切,保证验证集里的画面是模型没见过的。
3.2 data.yaml 正确写法与几个易错点
配置文件内容不复杂,容易错的是路径和类别名:
path: ./dataset train: images/train val: images/val nc: 2 names: 0: empty 1: occupied易错点有这么几个:
path如果写成相对路径,注意它相对于你执行trian命令的工作目录,而不是yaml文件所在目录。放错位置会报找不到图片,排查起来很闹心。nc必须与names的个数一致,否则训练直接报错。这个很粗暴,但报错信息也很清楚。- 如果有两个类,
names是0和1,不要写成1和2。训练时会自动从0开始。
3.3 模型选型:为什么最终选了yolov8s而不是n或m
很多教程上来就让人用yolov8s,但没有解释为什么。这里说一下我的横向测试结果。显卡是GTX 1660 Ti,6GB显存,这个约束条件直接决定了选择范围。
| 模型 | 参数量 | 显存占用(batch=16, imgsz=640) | 训练耗时/epoch(约1000张图) | 实测mAP50 |
|---|---|---|---|---|
| yolov8n | 3.2M | 约2.5GB | 约30s | 0.86 |
| yolov8s | 11.2M | 约4.8GB | 约55s | 0.92 |
| yolov8m | 25.9M | 溢出或需要降batch | - | - |
我最终选yolov8s,因为n在标线模糊的车位上漏检率明显要高,而m在6GB卡上得把batch降到4左右才能跑,训练时间翻倍,收益却不明显。如果你想在CPU上跑,那只能用n,但说实话CPU训练这种数据集太折磨了。
有一点要说清楚:预训练权重非常有用,不要随机初始化从零训练。用YOLOv8在COCO上的官方权重作为起点,即便COCO里根本没有停车位这个类别,模型也已经学好了通用的边缘、纹理、形状特征,迁移到停车位任务能少花大量迭代。命令里直接指定model=yolov8s.pt即可,ultralytics会自动下载。
4. GTX 1660 Ti实测:训练参数、损失曲线与标线退化增强
4.1 我跑通的完整训练脚本与参数解释
下面是我的训练命令,直接放出来给有同样显卡的朋友做参考:
yolo detect train \ data=parking.yaml \ model=yolov8s.pt \ epochs=120 \ imgsz=640 \ batch=16 \ cache=True \ device=0 \ workers=4 \ lr0=0.01 \ lrf=0.01 \ warmup_epochs=3 \ mosaic=1.0 \ patience=20逐项解释几个关键的:
imgsz=640:不是越高越好。停车位是小目标,理论上图片越大越好,但是640在GTX 1660 Ti上已经是batch=16的稳定上限。如果你把imgsz提到960,batch得降到8,整体训练速度反而更慢,且收益有限。如果后期要用更高分辨率推理,可以训练时用640,推理时用960,YOLOv8对输入尺寸的鲁棒性还不错。mosaic=1.0:马赛克增强对停车位场景非常有效,尤其能模拟车位线被截断的画面。但注意最后10个epoch建议手动关掉,或者让ultralytics自动在最后epoch关闭,否则模型可能学不到精准的框回归。patience=20:早停,连续20个epoch验证集mAP没有提升就停。我这次跑到第80轮就停了,后面40轮都在波动,早停能省不少时间。cache=True:把图片加载到内存里,省去每轮重复IO的时间。如果你内存小于16GB,谨慎开启。
4.2 损失曲线怎么看:正常训练的图像长什么样
训练结束后,在runs/detect/trainX目录下会生成results.png,包含box_loss、cls_loss、dfl_loss的train曲线和val曲线,以及mAP50、mAP50-95的指标曲线。
正常的曲线长这样:训练损失和验证损失都在下降,mAP曲线在前期快速上升,随后渐趋平缓,验证损失偶尔有小波动是正常的。如果出现"训练损失降得很低、验证损失却不降反升",这是过拟合的典型信号,优先检查训练集和验证集是不是有相同场景的帧泄漏,其次再看是不是数据量太少,最后才考虑加大增强。
另一个值得关注的指标是mAP50 和 mAP50-95 的差距。停车位检测的框通常比较大,mAP50很容易刷到0.9以上,但mAP50-95往往只有0.6左右。这个差距大说明你的框定位不够精确——对停车位业务来说,框偏几个像素通常无所谓,但如果要做后续的车位级联判断,框的稳定性还是重要的。
4.3 标线淡化场景的针对性增强
我把这一类放在训练之后单独说,是因为它被我验证过是最有效的提点手段。停车场标线褪色是普遍现象,靠真实采集只能覆盖一部分磨损状态,更高效的办法是让模型在训练时"见过"更多退化样式。
在ultralytics的配置里,数据增强主要靠hsv_h、hsv_s、hsv_v以及翻转、缩放、平移这些参数。针对标线淡化,我额外做了两件事:
- 在训练集里手动对50%的图片做高斯模糊增强,模拟远景失焦和标线边缘不清的情况。
- 把一部分图片转为灰度图,让模型去掉颜色依赖,更关注标线与地面的亮度对比关系。这一步对夜间场景效果特别明显,因为夜间很多标线其实是靠反光亮度来区分的,而不是靠颜色。
实测下来,加了这两项增强后,mAP50从0.88左右提升到0.92,而且漏检率明显下降,代价只是训练时间增加了10%左右。代价很小,收益很大。
5. 训练中跑出来的几个坑:从报错排查到指标陷阱
5.1 ultralytics和NumPy 2.x的兼容性问题
这里顺手记录一个很常见但对新手来说很头疼的报错。如果你使用较新版本的ultralytics,训练刚开始时会报类似"AttributeError: 'numpy._core.multiarray._reconstruct'..."的错误,本质是NumPy 2.x引入了不向后兼容的改动,而ultralytics内部某些模块还依赖旧版NumPy的pickle序列化接口。
解决方案有两个:一个是把NumPy降级到1.24.x,这个最省事;另一个是把ultralytics升级到最新版。但注意,升级ultralytics也有风险,有些新版本训练参数变了,旧脚本可能报unexpected keyword argument。我个人是固定版本链,形成一套稳定的虚拟环境,不做频繁升级。
5.2 验证mAP很高,实拍却识别不出来:三个检查项
这个坑很多人会踩一次。模型在验证集上mAP50接近0.95,但拿到真实停车场画面一测,经常是漏检一堆。原因通常出在三个方面:
- 验证集泄漏:就像前面说的,train和val没有按场景切分,模型其实是"背下"了这些画面。
- 推理尺寸和训练尺寸不一致:训练用640,实拍输入用1280,模型很难适应这么大幅度的尺度变化。建议推理时用和训练时接近的尺寸,或者把推理尺寸控制在960以内。
- 置信度阈值设置不合理:默认conf=0.25对停车位场景可能偏高,尤其是标线褪色的车位,模型输出的置信度普遍偏低。可以在测试时把conf降到0.1,看召回率是否大幅提升,以此来诊断模型是在"漏检"还是"不自信"。如果降到0.1后召回率明显上来了,说明模型其实学到了特征,只是车位本身的视觉特征就弱,需要靠调阈值取舍。
5.3 空位和占用类别不均衡的处理思路
自建数据集时,采集到的"占用"样本通常远多于"空位"样本(因为白天停车场大部分车位都有车)。这种不均衡会导致模型倾向于把所有车位都判定为occupied,因为这样在训练集上准确率更高。
我的处理办法比较直接:在采集阶段就刻意去拍空位多的时段,比如清晨和夜间,尽量让训练集里空位和占用的比例接近1:1。如果实在凑不齐,才考虑用复制粘贴的方式扩充空位样本,或者调整loss权重。但坦白说,调loss权重属于治标不治本,数据域本身的分布不平衡是根因。
5.4 容易忽略的标注边界问题
标注时一个常见的争议是:车头已经越过车位线但车身还在车道里,这时候框应该怎么画?我的经验是以车位线为准,不按车辆投影为准。也就是说,只要车头进入车位区域,这个车位就算是occupied,但矩形框仍然框住整个车位线内部区域。这样标注出来的数据集,模型学习到的是"车位于某一时刻是否保持可停状态",而不是"车A和车位B的重叠面积有多大"。前者更贴近停车位检测的真实业务语义。
6. 训练完之后:导出、部署与下一步优化
6.1 ONNX导出和高分辨率推理的注意点
训练收敛后,第一步是导出成ONNX格式,方便后续各种部署:
yolo export model=best.pt format=onnx opset=12导出时注意一点:如果目标平台支持FP16,建议导出时顺手做半精度转换,模型体积和推理速度都会有明显改善。如果后期要用TensorRT进行推理,ONNX的opset版本尽量和TensorRT版本匹配,否则会遇到不支持的算子。
在Jetson Nano这类边缘设备上实测,FP16的yolov8s模型,640输入下推理一张图大约在20-30毫秒,基本能满足停车场道闸这种实时性不高的场景。如果追求更快,可以裁剪模型到yolov8n,速度能再翻一倍,但准确率有一定损失,需要做权衡。
6.2 从yolov8s到嵌入式设备:模型量化和轻量化的方向
如果你要把模型装到嵌入式设备上,比如RK3588或者Jetson Nano,有几个方向是可以直接去做的:
- ONNX转NCNN:对国产嵌入式平台非常友好,支持int8量化。int8量化需要准备一小批校准图片,用验证集里的几百张图就够了,不需要重新训练。量化后模型体积大约是FP32的四分之一,在部分芯片上的推理速度可以提升2-3倍。
- TensorRT FP16:在Jetson平台上性能表现优秀,稳定性也好。缺点是需要编译引擎,对硬件绑定较强,换一台设备就要重新编译。
- 模型蒸馏:如果你希望保留yolov8s的精度,但部署的时候用yolov8n的速度,可以用蒸馏方式让小型模型学习大模型的软标签输出。这个方案需要一定的代码改动,但收益是实打实的。
我个人的建议是:如果只是验证概念,直接用ONNX在PC上跑就足够了;如果真的要落地到道闸或巡检设备上,优先花时间做TensorRT或者NCNN的int8量化。
6.3 后续可以做的方向:车位状态追踪与多摄像头融合
训练完一个静态检测模型只是第一步。实际业务里,停车场管理系统通常还需要知道"某个车位被占了多久"、"哪些车位长时间空置"这类推断信息。这就需要在检测框的基础上,加上一个简单的跟踪器,比如ByteTrack或者DeepSort,对同一个车位ID做时序管理。基于YOLOv8的检测框输出做跟踪,不复杂,但能显著提升系统的实用价值。
更进一步的方向是多摄像头融合。单摄像头总有盲区,两个摄像头拼起来覆盖整个停车场,需要处理视野重叠区域的输入输出去重。这一块会和车位编号关联,涉及更多业务逻辑,但从检测模型的角度看,上游做好检测、下游做关联,是整个系统里最经典的架构。
6.4 关于我自己实验环境的一些补充
最后补充一点硬件相关。我用的训练机是GTX 1660 Ti 6GB,跑yolov8s训练大约19分钟一个epoch,整体项目跑了大概5个小时训练加上数据整理。如果你手上的显卡是RTX 3060或更高,训练时间和显存限制都宽裕很多,可以直接尝试yolov8m。反过来说,如果只有CPU,建议优先考虑用Google Colab的免费GPU来跑,训练效率会好非常多。
另外提一句,我用的是Linux环境,Windows下大多数命令是兼容的,但路径分隔符和文件权限偶尔会出些小问题。如果在Windows上遇到奇怪的文件找不到报错,优先检查一下路径是不是被反斜杠转义了。
做停车位检测这个细分方向,其实没有特别复杂的高深理论,真正拉开差距的往往就是数据集的构造方式和对场景特殊性的理解。把标线退化、视角差异、类别定义这些问题在数据准备阶段想清楚,训练环节反而是最顺的。
本文还有配套的精品资源,点击获取