人行道看起来不起眼,但在智慧城市项目里,它几乎是所有“最后一百米”问题的交汇点——盲道是否被占用、路面是否有破损、违规摆摊是否侵占了通行空间、外卖电动车是不是骑上了人行道。以前这类需求全靠人工巡检或者固定摄像头抽帧看,效率低且容易漏。我去年接到一个城市精细化管理项目,核心需求就是做一套人行道场景检测系统,然而翻遍公开数据集,要么类别太粗(只有笼统的“路面”),要么场景严重偏向欧美城市。最终我们选定了这批10,300张的YOLO格式人行道场景检测数据集,配合半自动标注管线补全场景缺口,前后两个月把模型从baseline mAP 0.52拉到了0.74。这篇就围绕这个数据集,把选型思路、标注清洗、YOLO训练调参、以及智慧城市落地时那些坑,完整写一遍。
1. 数据集整体设计与选型思路
1.1 为什么需要专门的人行道场景数据集
通用目标检测数据集(COCO、VOC)里当然也有人、车、交通标志这些类别,但它们并不理解“人行道”这个空间概念。城市管理关心的不是“这里有一辆车”,而是“这辆车停在了人行道上”,也就是说,目标类别必须和空间约束绑在一起。智慧城市里最典型的几个需求——盲道占用检测、违规停车识别、市容乱象巡查、共享单车违停——全部依赖“人行道”作为前置语义。
这批10,300张的数据集正是冲着这个场景维度去的。它覆盖了早中晚不同光照、晴天雨天、城市主干道和背街小巷等环境变化,比通用数据集更能还原真实执法场景。而且它的标注格式直接给了YOLO的txt格式,省去了从COCO或VOC转换的时间成本。对于做智慧城市算法团队来说,这个“开箱即用”的价值比想象中大——省下的不只是转换脚本那两天,而是整个数据pipeline的调试时间。
1.2 YOLO格式标注的核心结构
YOLO格式的每张图片对应一个同名txt文件,每一行代表一个目标实例,格式是:
class_id x_center y_center width height注意,这里的四个坐标值是相对图片宽高的归一化数值,不是像素坐标。比如图片是1920x1080,一个目标中心点在(960, 540)处,宽400像素、高200像素,那标注就成了:
0 0.5 0.5 0.2083 0.1852很多新手直接拿像素坐标喂进去,训练时loss爆表或者完全不收敛,基本都是这个原因。我在检查数据集时习惯先写个小脚本统计所有txt的坐标范围,如果出现x_center + width / 2 大于1的情况,就说明存在越界标注,需要回炉。
这个数据集的类别体系通常是围绕人行道场景组织的。核心几类大概包括:person(行人)、bicycle(自行车)、motorcycle(摩托车)、car(违规进入人行道的车辆)、traffic light、traffic sign,有些版本还会细分bus、truck。实际做智慧城市项目时,我会根据需求再补充盲道、井盖、积水区域这类市政要素——原数据集没有这些类别,但可以作为后续增量训练的基础。
1.3 10,300张数据的规模评估与增广策略
10,300张在目标检测数据集里属于中等偏上规模。COCO的train2017有11.8万张,但那是80个类别;如果单看少样本场景,比如铁路病害检测、水下目标,很多数据集连5000张都不到。对行人、车辆这种类别差异明显、纹理清晰的目标来说,一万张以上的数据足以支撑一个可上线的模型。
我的做法是在原始10,300张基础上做三类增广扩充到约3万张。第一是几何增广,包括水平翻转、随机旋转±15度、随机缩放0.8到1.2倍;第二是色彩增广,包括HSV抖动、亮度随机变化,重点模拟黄昏和夜间路灯下的色温偏移;第三是混合增广,用Mosaic和Copy-Paste把多个场景的目标拼到一张图里,增强模型对小目标的敏感度。
增广不是越多越好。我踩过的坑是加了随机旋转后,检测框的贴合度明显下降——行人框歪了5度,实际IoU掉得厉害。所以旋转角度限制在±15度以内,并且只在训练后期(warmup之后)开启,避免模型前期被增广分布带偏。
2. 核心细节解析与数据质量把控
2.1 标注类别的层次设计与边界定义
人行道场景检测最大的标注难点不是“画框”,而是“什么情况算违规”。以机动车上人行道为例,车头进入人行道超过1/3就算占用,还是只要轮胎压线就算?这直接决定了正负样本的判定边界。数据集的标注者如果标准不统一,模型学到的特征就是一团浆糊。
在智慧城市项目里,我建议把检测目标分成三层逻辑:静态要素(人行道本身、盲道、井盖)、动态主体(行人、非机动车、机动车)、违规状态(车辆侵入、摊贩占用)。这个数据集基本覆盖了前两类,第三类需要结合业务规则在后处理里加判断逻辑。简单说,检测模型负责检测目标在哪里、是什么,合规判断交给规则引擎——比如检测到“车辆”类别位于人行道区域且停留时间超过阈值,就触发违规事件。
2.2 数据清洗:比想象中更关键的步骤
拿到10,300张数据后,第一步绝不是直接训练。我先写了几个过滤脚本做清洗:
第一层过滤:损坏文件。检查jpg是否可以正常解码、txt与jpg是否一一对应、txt内每行是否恰好5个数值。这批数据在打包传输过程中经常出现丢行或者图片截断的问题,不清理的话训练中途直接报错。
第二层过滤:小目标框。标注框如果小于20x20像素,对检测任务基本没有学习价值,而且会干扰loss计算。我统计了框的分布,把极小框和超大框(占比超过图幅70%)单独拿出来人工复核。
第三层过滤:类别不平衡。行人框再多也不嫌多,但“bus”和“truck”如果只有几百个框,训练时会因为样本太少导致这类AP极低。对这种类别,我选择先不纳入训练,等后续补充数据再开类。
清洗完我删掉了约5%的问题数据,剩下的9,800张左右用于训练。数据集的“干净程度”直接影响最终模型效果,这一步不能省。
2.3 训练集/验证集划分的注意事项
划分数据时我坚持按“场景”而不是按“图片”划分。比如同一路段不同时间点拍的图片,如果一部分进了训练集、一部分进了验证集,那验证指标会虚高——模型可能已经“记住”了这条路的地砖纹理。正确做法是按路段或场景文件夹粗粒度划分,保证训练集和验证集的场景分布近似但不重叠。
我的划分比例是8:1:1,即约7,800张训练、1,000张验证、1,000张测试。测试集必须保证在训练过程中完全不可见,只用来看最终指标的。很多团队图省事只分train和val,结果上线后发现泛化能力不足,回头查代码才发现val被反复拿去调参,早就不“干净”了。
3. 基于YOLO的训练实操与关键参数
3.1 环境选择:YOLOv8还是YOLOv5
我在这批数据上同时试过YOLOv5和YOLOv8,最终上线用的是YOLOv8m。理由很简单:YOLOv8的C2f结构和Anchor-Free头在处理人行道场景中密集小目标(远处行人、共享单车)时更稳定,而且官方预训练权重在COCO上的特征迁移对人、自行车、汽车这些类别非常友好,收敛速度明显快于从零训练。
如果你的部署平台是边缘盒子(比如Jetson Orin或瑞芯微RK3588),我建议用YOLOv8s,精度损失不大但推理帧率能高一截。我实测过在Orin NX上,YOLOv8s用TensorRT FP16推理,640x640输入可以跑到35FPS,v8m就只有20FPS左右。智慧城市项目通常不需要实时视频流全量检测,5秒一帧都够用,所以更看重精度。
3.2 数据组织与训练启动
按YOLO的标准目录结构组织数据:
sidewalk_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── sidewalk.yamlsidewalk.yaml内容如下:
train: /data/sidewalk/images/train val: /data/sidewalk/images/val nc: 6 names: ['person', 'bicycle', 'motorcycle', 'car', 'traffic_light', 'traffic_sign']启动训练我用的是这个命令:
yolo detect train \ --model yolov8m.pt \ --data sidewalk.yaml \ --epochs 100 \ --imgsz 640 \ --batch 32 \ --workers 8 \ --optimizer SGD \ --lr0 0.01 \ --lrf 0.01 \ --mosaic 1.0 \ --cache ram几个参数我特别解释一下。batch从默认的16提到32,是因为RTX 4090的显存足够,大batch能让SGD的梯度更稳定。cache ram把图片全部加载到内存,训练时省去磁盘IO等待,实测一轮从45秒降到20秒。mosaic保持1.0,对小目标检测增益明显。如果显存不够,建议优先降batch而不是降imgsz,因为分辨率直接影响小目标的检测效果。
3.3 损失函数与训练监控要点
YOLOv8的损失box_loss主要用CIoU,类别和置信度用的是BCE。如果发现训练几天后分类收敛良好但box位置不准(典型表现是loss降不下去但mAP卡在0.6附近),可以考虑换成SIoU或EIoU。改动方式是在ultralytics源码的loss.py里调整,不复杂但效果直接。
训练过程中我重点盯四个曲线:train/box_loss、val/box_loss、metrics/mAP50、metrics/mAP50-95。如果train loss持续下降而val loss在第60个epoch开始反弹,说明过拟合。解决办法是加大--augment的随机性,或者用--dropout 0.1做正则,也可以直接提前停掉用best.pt。
3.4 实测结果:从baseline到可用模型
直接用官配参数训练这个数据集,100个epoch后mAP50能到0.62左右,但mAP50-95只有0.41,这个水平做智慧城市演示足够,做正式执法依据还差口气。后来我把imgsz从640提到960,mAP50直接涨了4个点。代价是训练速度慢了将近一半,推理时也对应需要提升输入分辨率,对显存小的盒子就不太友好。
最终上线模型是YOLOv8m,输入960x960,mAP50 0.74,mAP50-95 0.53。单帧推理在4090上12ms,在Orin NX上约60ms。这个精度对“检测到人行道上有什么”已经够用,关键是把误检率压到5%以下,否则后台审核人员一天到晚在处理假警报。
4. 从数据集到智慧城市应用落地
4.1 智慧城管场景的业务映射
好数据集的最终价值要落在业务闭环里。在我参与的城市精细化管理平台里,人行道检测模型主要对接四类场景:
| 场景 | 检测目标 | 业务处置 |
|---|---|---|
| 盲道占用 | 车辆、摊贩 | 生成工单,通知属地街道整改 |
| 共享单车违停 | 自行车、电动车 | 统计淤积量,调度清运车辆 |
| 路面破损 | 坑洼、积水 | 上传市政养护平台 |
| 违规经营 | 摊位、遮阳棚 | 联动城管执法系统 |
每个场景需要不同的检测后处理逻辑。比如共享单车违停,检测到目标还不够,需要额外做密度聚类——当某个格网点密度超过阈值时才算“淤积事件”。这一步通常用简单的DBSCAN处理坐标信息就能搞定,不需要重新训练模型。
4.2 部署架构与数据回流
部署上我们采用“端边云”三层。边缘侧IPC或摄像头通过RTSP拉流,边缘盒子以5秒间隔抽取关键帧做检测,只上报置信度超过0.6的结果,云端负责存储、工单流转和数据分析。之所以不在云端做全量视频分析,是因为带宽和算力成本太高,边缘预处理能把数据量压缩掉90%以上。
数据回流是很多人忽略的环节。模型在A城区训练得再好,到B城区可能因为地砖颜色不同、树影遮挡方式不同而掉点。所以我保留了“难例回流”通道:边缘检测器在低置信度区间(0.4-0.6)的样本定期回传,由人工打标后增量训练。每个月迭代一版模型,上线6个月后mAP50逐步稳定在0.78左右。数据集在这个闭环中扮演的是“冷启动底座”的角色,后续的护城河来自持续回流数据的活水。
4.3 推理性能优化与裁剪
在边缘设备上跑YOLO,有几个必须做的优化。第一是TensorRT导出,FP16几乎无损,INT8需要校准集,这个校准集可以直接从验证集里抽500张,用TensorRT自带的calibrator跑一遍,实测INT8相比FP16只掉1-2个点mAP,但帧率提升近一倍。第二是减少预处理开销,把BGR2RGB和letterbox操作合并到GPU上做,避免CPU-GPU拷贝延迟。第三是检测后NMS的阈值调整,人行道场景目标相对稀疏,可以适当降低NMS的IoU阈值到0.5,减少重复框,提升有效检测率。
另外提一个容易被忽略的点:如果部署现场光照差异极大(逆光、夜间路灯、树影),建议在预处理环节加入自适应直方图均衡化(CLAHE)。仅在推理图像进入网络前做一次,不会影响训练分布,但能显著提升低光照场景的召回率,尤其是夜间行人检测。
5. 常见问题与排查技巧实录
5.1 训练Loss常见异常与应对
loss刚开局就是NaN。通常是学习率过大或者数据里有异常标注(比如归一化坐标出现负数、inf)。先检查数据清洗脚本,再降低lr0到0.001重新跑。如果是YOLOv8用了AMP混合精度导致,可以加--amp False排除。
loss收敛但mAP很低。检查是不是类别不平衡。比如car类别有3万框,traffic_light类别只有800框,mAP被少数类别拖死。可以加--class_weights给低样本类别加权,或者干脆把样本过少的类别从训练中剔除。
mAP50还行但mAP50-95极差。这是边框定位精度不足的信号。优先提高imgsz,其次改box_loss为SIoU,再不行就加训练轮数并配套更强的学习率衰减策略。
5.2 部署后检测效果不达预期的排查路径
模型在测试集上mAP很高,但上线后效果差,这是最常见的反馈。我给个排查清单:
- 先看分辨率差异:训练时imgsz=640,摄像头拉流分辨率是2560x1440,直接缩放会导致小目标糊掉。要么拉升训练分辨率,要么在推理时做分块检测。
- 再看帧率与曝光:运动模糊会让行人拖影,尤其是晚间快门速度不够。可以在摄像头端开启运动补偿或提高帧率。
- 最后看视角差异:数据集里如果是枪机平视视角,部署现场是球机俯视视角,目标外观差异很大。此时最有效的办法不是调参,而是补充部署场景的实拍数据做微调。
我接手的一个项目被这个问题卡了三周,后来发现是因为现场摄像头有偏色,整个画面偏黄,导致模型把大量地砖纹理误检为“路面病害”。加了一层白平衡校准后误检率直接降了60%。这种环境适配问题永远是实际部署比训练中出现得多得多。
5.3 数据迭代过程中的长期维护建议
数据集是会过期的。同一批10,300张数据不可能支撑一个项目三年的效果。我在项目维护阶段建立了一个月迭代节奏:
- 每周从生产环境回收500张难例图,简单清洗后进入候选池;
- 每月挑出800-1200张有效难例,和原始训练集混合,重训一版模型;
- 每季度做一次全量评测,对比新旧模型在固定测试集上的指标,确保只升不降。
这样做最大的作用是防止模型“遗忘”。只拿新数据增量训练而不混合旧数据,模型会对新场景过拟合,旧场景的指标开始掉。混合训练是老生常谈,但很多人嫌麻烦不做,最后模型越调越偏,只能回滚。
另外说一点自己的体会:做智慧城市项目,不要对模型精度抱有不切实际的幻想。很多场景本身存在大量标注歧义——一个斜跨人行道的电动车,到底算正停还是行驶中?标注员自己都分不清,强行建模只会让模型学到噪声。与其在模型结构上死磕,不如把业务规则前置,用检测器输出候选目标,用规则引擎做最终判定。这条路线不仅工程上更可靠,业务部门也更容易接受你的解释。
这批10,300张的人行道数据集,是我做过同类项目里“性价比”比较高的一个起点。它不是完美的——类别覆盖有限、极端天气样本不足、部分标注框偏大,但它把“人行道场景”从通用目标检测里单独拎了出来,给智慧城市项目提供了一个扎实的底座。剩下的,就看各位怎么在这个底座上做业务适配和增量迭代了。