自动泊车和代客泊车(AVP)这两年越来越卷,真正下过地库、跑过实车的人应该都有感触:视觉感知才是整个系统里最容易翻车的环节。车位能不能稳定检出、地锁和锥桶会不会漏掉、地面箭头和减速带能不能看清,这些细碎问题做不好,停车场再智能也白搭。
这篇博文就以“基于目标检测模型的停车位检测、低矮障碍物、地面标识检测”为线索,完整聊聊我从方案选型、数据标注到模型训练、部署实测的整套落地经验,重点讲清楚这类任务和常规通用检测到底差在哪,以及为什么现在大家都在追macs仅5MB的轻量化目标检测模型。内容适合正在做自动泊车感知、智慧停车场改造,或者想入手边缘端检测项目的朋友,直接照着参考就行。
1. 场景拆解:三个任务为什么必须放在一个模型里做
很多第一次接触这个需求的人都会问:停车位检测、低矮障碍物、地面标识本来是三件事,各自拿一个专用模型不好吗?答案是:理论上可以,工程上不行。
1.1 三种目标的本质差异与检测难度
停车位检测的目标是车位线、车位入口和可用空位,本质上是“线状”和“区域”的组合,很多方案还会引入分割模型先提取车位线再拟合角点。低矮障碍物则截然不同,它们是地锁、锥桶、石墩、消防栓、减速带这类立体小物体,高度通常低于40厘米,在图像里占的像素很少。地面标识又是另一类,箭头、文字、停止线、网格线都是“平面符号”,本身没有物理高度,全靠颜色和边缘对比度区分。
放到同一个模型里,意味着同一个特征提取网络要同时适应三类差异极大的目标形态。经过实测,多任务共享主干在车位这类结构特征明显、地面标识这类纹理特征明显、低矮障碍物这类深度轮廓特征明显的目标上,特征冲突并不大,反而由于共享了底层的边缘、纹理、颜色特征,整体泛化能力比三个独立模型更稳。
1.2 感知任务在自动泊车链路中的位置
在真实的自动泊车链路里,感知通常分两路:环视鱼眼摄像头负责近距离、低速场景,前视或侧视摄像头负责远距离预判。停车位检测、低矮障碍物、地面标识检测恰恰横跨这两路输入。
环视图像中,停车位和低矮障碍物是泊车入位的关键输入,车能不能安全停进车位,就看这两类检得准不准;前视图像中,地面标识和远距离障碍物是路径规划的关键输入,车辆需要提前判断前方是否有减速带、禁停区域或障碍物。把三类任务统一到一个检测模型里,节省了计算资源和内存占用,还避免了多模型串联带来的延迟叠加,这对强调实时性的车载平台尤为重要。
注意:如果你只做停车位检测,可以不考虑低矮障碍物;但如果你做的是完整泊车系统,这两者必须同时存在,否则“检测到空车位,但车里藏着地锁”这种致命场景就拦不住。
2. 模型选型与轻量化路线:5MB模型是怎么炼成的
目标检测模型这几年迭代非常快,YOLO系列从v5一路卷到v11,还有RT-DETR这类Transformer结构,外加各种轻量主干。但落到车载嵌入式平台,约束条件远比刷榜要苛刻。
2.1 从YOLO到轻量化:模型选型对比
我最早在这个项目里用的是YOLOv5s,在3080显卡上训练,测下来mAP有0.87左右,看起来很漂亮。但一部署到车规级平台,问题就来了:单帧推理时间接近120毫秒,内存占用超过400MB,前端摄像头和环视摄像头同时跑两个模型,直接压垮了调度器。
后来我系统对比了几条路线:
| 模型方案 | 参数量 | 输入尺寸 | 推理耗时(边缘平台) | 车位mAP | 障碍物mAP | 地面标识mAP |
|---|---|---|---|---|---|---|
| YOLOv5s | 7.2M | 640x640 | 120ms | 0.89 | 0.83 | 0.91 |
| YOLOv8n | 3.2M | 640x640 | 85ms | 0.86 | 0.78 | 0.89 |
| RT-DETR-tiny | 4.0M | 640x640 | 140ms | 0.84 | 0.76 | 0.87 |
| 轻量化剪枝+蒸馏 | 0.8M | 512x512 | 32ms | 0.82 | 0.75 | 0.85 |
最终我选择的路线是:以YOLOv8n为基线,做结构化剪枝结合蒸馏,把模型压到0.8M参数量、约5MB的权重文件,换来边缘平台上32毫秒的推理速度。
2.2 通道剪枝、量化与蒸馏的组合拳
5MB目标检测模型绝不是凭空冒出来的,核心手段有三步:
第一步,通道剪枝。对YOLOv8n的每个卷积层计算BN层缩放因子的L1范数,把贡献小的通道直接剪掉。这一步要控制剪枝比例,建议从30%开始,逐步增加到50%,剪得太猛会导致mAP断崖式下跌。我在三类目标上实测,剪枝50%后mAP平均掉了0.04,但推理速度提升了近一倍。
第二步,知识蒸馏。用原版YOLOv8n作为教师模型,让轻量化学生模型去模仿教师的特征图输出和预测分布。这一步对恢复剪枝掉精度非常有效,尤其对低矮障碍物这类小目标,蒸馏比单纯重新训练能高出3-5个百分点的mAP。
第三步,INT8量化。模型训练完成后,用校准集做INT8量化,这一步几乎不损失精度,但能进一步压缩模型体积并加速推理。实际操作中,5MB的模型文件就是剪枝后FP16权重再做INT8量化得到的结果。
2.3 输入分辨率与算力权衡
模型轻量化只是把“同样的事做得更快”,真正影响检测效果的上限是输入分辨率。地面标识中的文字和箭头,在远距离下只占十几个像素,分辨率太低基本无解。
我做过一组对照测试:输入640x640时,5米外的地面箭头识别率约85%;降到416x416后,直接跌到65%。对停车位线和低矮障碍物也有影响,但远没有地面标识那么敏感。
最终的工程取舍是:前视摄像头采用576x576输入,环视摄像头采用512x512输入,既保证小目标的召回率,又控制整体计算量。你如果算力特别紧张,可以保留640x640给地面标识专用分支,其余任务共享轻量特征,但对大多数项目来说,576已经够用。
3. 数据标注与训练细节:三类任务的脏活累活
模型结构只是骨架,数据才是血肉。这个项目的难点不在于网络结构多新颖,而在于数据标注标准和训练细节上,稍不注意就会踩坑。
3.1 停车位标注:垂直、平行、斜列的标准不统一
停车位标注看起来简单,把车位线框出来就行?实际上远不是这样。垂直车位和斜列车位的消失点不同,平行车位的车位线往往只有前后两条短线,侧向没有线。如果标注框只是简单框住两条线的区域,模型学到的其实是“一片空地的边缘”,而不是“一个可以停车的位置”。
我的标注规范是:对垂直和斜列车位,标注框覆盖整个车位区域,同时单独增加一个角点类别,标记入口线的两个端点;对平行车位,标注框以前后两条短线的中心连线为基准,框体略向内收,避免把相邻车位的空间也框进来。训练时还需要额外监督角点与框的匹配关系,这样模型才能学到“车位不仅是一块区域,还必须存在可进入的开口”。
3.2 低矮障碍物的边界定义与标注细节
低矮障碍物是最难统一标注标准的类别。地锁、锥桶、石墩、消防栓、减速带,形态差异极大,遮挡情况也各不相同。最开始我把所有低于车身高度的物体都归为“障碍物”,训练出来的模型经常把地面上的窨井盖、树影也当成障碍物。
后来我把类别拆分得更细:地锁单独一类,带反光条的锥桶单独一类,固定石墩单独一类,减速带单独一类,其他低矮物体归为通用障碍物。这个调整非常有效,地锁的召回率从70%左右提升到90%以上,误检率也直线下降。
标注框还有一个细节:低矮障碍物要贴着地面标注,不要包进整个物体的三维空间。模型根据检测框底边位置估算距离,如果框的上沿包含了太多天空背景,底边位置会被抬高,导致测距偏大。
3.3 地面标识的易混淆问题:箭头、文字、停止线
地面标识类别比较少,就画箭头、文字、停止线、禁停网格、减速带标识这几种,但误检率反而是三个任务里最高的。原因是地库里光线复杂,不同材质地面反射不同,同样一个左转箭头,在环氧地坪和水泥地上呈现的特征差异极大。
为了防止模型学偏,我把训练集中的每一张地面标识图都做了精细清洗。比如左转箭头和直行箭头的区别只在箭头的折角,标注框要精确贴合箭头边缘;文字类标识只保留完整文字块,半个字或磨损严重的样本直接剔除。因为地面标识在图像中往往是透视畸变的,我还专门对标注掩膜做了透视矫正,让每个箭头都恢复成近似的正视角形状,这个操作大幅减少了类别混淆。
3.4 数据增强与样本平衡的实战策略
三类目标天然存在严重的不平衡:一个地库里,可用的标准车位可能上千个,但地锁只有几十个,地面箭头更是几场才出现一个。如果不做平衡,模型训练会被车位样本带偏。
我的处理方法是:先按类别计算数量,对低矮障碍物和地面标识做马赛克增强和复制粘贴增强,每个训练epoch随机复制这些稀有类别的实例到其他图像中。复制时要注意透视变换和光照匹配,否则模型学到的是“贴图感”,到了真实场景反而检测不到。
此外,由于地库普遍光线暗、逆光场景多,我将训练集的曝光度、色温、噪声模拟做了大幅随机化,还加入了模拟雨天反光的贴纸增强。测试下来,这一套增强让模型在下雨天和早晚高峰逆光场景的鲁棒性明显提高。
4. 模型训练与部署实测:从数据集到边缘平台
这一节重点说训练配置和部署环节的实操细节,包括损失函数设置、训练超参选择,以及最终在边缘平台上跑出来的真实数据。
4.1 损失函数与训练超参配置
多类别检测任务,损失函数主要由三部分组成:分类损失、框回归损失和置信度损失。YOLOv8默认使用BCEWithLogitsLoss处理分类,CIoU处理框回归。我在实际训练时做了一处关键调整:针对低矮障碍物这类小目标,把框回归损失换成了面向旋转框的变体,同时将小目标的权重乘了1.5倍,这相当于告诉模型“宁可错杀、不可漏检”,对后续避障非常关键。
训练超参方面,我最常用的是一套通用配置:
| 超参数 | 数值 | 说明 |
|---|---|---|
| 输入尺寸 | 576x576 | 兼顾小目标和计算量 |
| batch size | 64 | 显存不够就用梯度累积 |
| epoch | 300 | 配合早停,一般180轮收敛 |
| 初始学习率 | 0.01 | 用warmup+余弦退火 |
| 数据增强 | mosaic 1.0 + mixup 0.2 | 小目标增强开启 |
| 类别权重 | 车位1.0 / 障碍物1.5 / 标识1.2 | 平衡不平衡样本 |
4.2 训练过程中的精度监控
训练过程不能只盯着全局mAP看,三类任务要分开盯。全局mAP由车位拉高,掩盖了地面标识或障碍物的短板,最后部署时才暴露问题,代价就大了。
我在训练脚本里加了一个自定义评估函数,每5个epoch在验证集上分别计算三类目标的mAP和Recall。当车位mAP已经到0.9而低矮障碍物还在0.6徘徊时,我会先查是不是样本量太少,再查是不是增强策略不对。有一次障碍物mAP迟迟上不去,排查后发现是标注框不规范,大量地锁标注框把连接臂也算进去了,模型一直没分清地锁和地面投影,重新清理数据后一轮就涨了5个点。
4.3 边缘平台上的部署与推理优化
模型训练好并不代表能用。真正上车或上边缘盒子后,还有几个关键优化要做。
首先是把ONNX导出后做算子融合和nms优化。NMS(非极大值抑制)在CPU上经常成为瓶颈,我用的策略是引入一个轻量级的解耦NMS模块,将不同类别的目标分开做抑制,再把置信度阈值调高到0.45,单帧NMS耗时从8ms降到2ms。
其次是动态shape静态化。车载平台普遍对动态shape支持不好,推理时频繁重分配内存。我把模型输入固定为576x576,使用静态shape推理,实测FPS比动态shape提升了10%以上。
最终在边缘平台上得到的实测数据如下:INT8量化后的模型文件约5MB,单帧推理时间32ms,内存占用约180MB,稳定跑30FPS。三类目标综合mAP为0.82,其中停车位mAP 0.85、低矮障碍物mAP 0.75、地面标识mAP 0.83,符合整个项目“人可接受、车可执行”的基本要求。
5. 常见问题与排查技巧实录
这个项目踩过的坑和总结出的排查方法比训练本身的容量还大,整理几个最有代表性的。
5.1 停车位误检:把地锁、车轮也当成了车位
最常见的问题是车位检测把地锁、车轮、甚至地面的阴影误判为可停区域。原因有两个:一是训练样本中“空车位+地锁”的负样本太少,模型只看到了车位线和整洁地面,没学会“车位里有东西就不算空位”;二是停车位标注框太宽,包含了相邻车位的边缘。
解决方案有三个方向:一是增加大量“有人停车位和破损车位”的负样本,专门标注车位的不可用状态;二是在车位检测分支上增加一个语义分割辅助头,让模型学会区分车位区域内的“地面”和“非地面”;三是后处理规则,如果检测框内同时出现低矮障碍物或车辆目标,则判定该车位不可用。第三种方案最简单直接,也是我最终采用的。
5.2 低矮障碍物漏检:暗光、反光和低对比度是元凶
低矮障碍物的漏检高发场景集中在地下车库的暗光区域和环氧地坪的反光区。地锁本身颜色暗,又紧贴地面,在夜间几乎和背景融为一体。YOLO这类anchor-based模型对10x10像素以下的目标非常不敏感,这是结构决定的,靠换模型很难根治。
我的排查步骤是先统计漏检目标的尺寸分布。如果大量漏检目标在20x20像素以下,优先做两件事:一是提升输入分辨率,从512改成576或640;二是加“小目标增强”策略,在数据增强阶段把目标随机等比缩小后再粘贴到大图中,强制模型学习小尺寸特征。如果漏检目标尺寸分布正常,那就是对比度问题,增加CLAHE自适应直方图均衡化的预处理,或者引入融合红外通道的双目输入,也能明显改善。
5.3 地面标识误检:强烈建议加区域约束
地面标识类的误检基本都发生在非泊车区域,比如车道中央的杂物、地面积水反光等。单纯加数据效果有限,因为背景变化太丰富,永远收集不完。更好的办法是从部署层面加“区域约束”:结合车道线检测或语义分割,先划分出“可行驶区域”和“疑似标识区域”,然后只在这些区域内做地面标识的检测后处理。
这个方法听起来简单,实际效果却比任何数据增强都显著。原因是地面标识在物理世界里本身就是有明确位置边界的,箭头和停止线只会出现在特定区域,模型如果出现“在墙上检出一个左转箭头”这种离谱输出,区域约束直接就能把它过滤掉。我在项目中引入了一个极简的地面分割分支,输出32x32的低分辨率地面掩码,再做目标过滤,误检率从7%降到了2%以内。
6. 算力搭台与后续扩展方向
项目的核心已经跑通,但距离一个成熟产品还有不少路。基于目前这套基线和摸索出的方法论,后续还有几个值得投入的方向。
6.1 传感器融合与多视角拼接
单摄像头检测在遮挡场景下很难突破,车位被旁边车挡住、地锁被车尾挡住都是常事。下一步可以引入环视系统的多摄像头拼接,将四个鱼眼镜头的检测结果投影到统一的世界坐标系,做跨视角融合。具体做法是把每个摄像头检测到的车位角点和障碍物底边通过外参投影到鸟瞰图,用匈牙利匹配做跨镜跟踪,这样即便某个视角被遮挡,其他视角仍能给出有效目标。
6.2 时序信息与运动预测
当前模型是单帧检测,对静态图像处理没问题,但在车辆缓行过程中容易出现抖动和跳变。引入时序信息后,可以用一个轻量级的跟踪器,对车位和障碍物做短时跟踪,保持检测结果的连续性。还可以结合IMU进行运动补偿,降低车辆颠簸带来的检测框抖動。这个扩展对AVP场景尤其重要,车辆在倒车入库过程中需要稳定跟踪后方的地锁和限位器,单帧检测做不到这种连续稳定。
6.3 自监督与增量学习
停车场场景千差万别,新商场的车位线和标识风格可能完全不同于训练集。手动打标成本太高,可以用自监督方式先做预训练:把大量未标注的地库图像丢给模型做对比学习,让模型先学会区分“这是地库、这是室外停车场、这是水泥地面”,再在少量标注数据上微调,能显著减少新场景的适配成本。增量学习则让模型在部署后持续学习新发现的车位样式,但要注意防止灾难性遗忘,我建议用经验回放结合正则约束的方式。
如果有人问我这个项目做过最值的事是什么,我会说是把停车位、低矮障碍物和地面标识这三个任务真正揉进了同一个轻量模型里,并且验证了它在真车环境下的可行性。过程中的坑很多,但每解决一个,对整个泊车感知链路的理解就更深一层。最后分享一个小技巧:无论模型结构怎么换,一定要在项目最开始就把验证集按场景分层建好,否则后面调模型会一直在原地打转,因为你是凭感觉判断好坏,而不是用数据。