☰
智慧城市人行道检测实战:自建YOLO数据集与边缘部署全流程
2026/10/14 14:23:23 网站建设 项目流程

四月份接了个智慧城市项目,要在几十个路口和重点路段部署人行道场景检测系统。我一开始的思路很直接:拿现成的YOLO模型跑一遍,看效果再说。结果一到真实场景就露馅了——行人和电动自行车框得歪歪扭扭,井盖、路锥、路面裂缝这类市政设施目标基本认不出来,树荫底下稍微遮挡一点就漏检。后来我下了个决心,不再指望通用模型,花了三周多时间自己整理了一套人行道场景检测数据集,一共10300张图,全部转成YOLO格式,从采集、清洗、标注到训练、部署完整走了一遍。

这篇不卖课也不卖数据,纯粹是把中间踩过的坑和实际能复用的路径记录下来,给同样在做智慧城市、道路巡检、违停识别方向的同行一个参考。通篇围绕"为什么必须自建场景数据集""数据怎么攒怎么清洗""标注规范怎么定""用什么版本的YOLO训练""部署到边缘设备会遇到什么"这几个问题来写,提到的参数、策略都是我实测过的,虽然不是唯一解,但至少是能落地的解。

1. 通用数据集在人行道场景下的失灵点

很多人会问:COCO、BDD100K这些公开数据集规模不是挺大的吗?直接用它们训练不就行了?我一开始也这么想,但真正把人行道场景的需求拆开之后,发现通用数据集和这个场景之间有明显的错位,而且不是靠调参能弥合的。

1.1 类别错位:通用模型认不全人行道的目标

COCO有80类,看着不少,但落到人行道场景里,真正用得上的类别就那几样:person、bicycle、car、traffic light、stop sign,剩下的基本都在数星星。问题在于,人行道场景里数量占绝对大头的是电动自行车、外卖车、共享单车和三轮车,这些在COCO里根本没有对应类别。BDD100K的类别相对接近自动驾驶场景,有rider这类标签,但同样缺井盖、路锥、垃圾桶、施工围挡这些市政设施细类。

做智慧城市项目的人应该都知道,业务需求通常是两条线并行的:一条是交管视角,要看行人和非机动车有没有违规、有没有汽车闯上人行道;另一条是市政视角,要看井盖有没有破损、路面有没有裂缝、有没有占道堆物。想用一套通用模型同时满足这两条线,只能拆成好几个模型去处理,数据之间还存在重叠和冲突。自己定义一个贴合业务的标签体系,才是最省事的做法。

1.2 视角差异:立杆俯拍与行车视角的错配

这是我在实际测试里感触最深的一个点。公开数据集里的大部分图像来自行车记录仪、车内摄像头或者手持设备,拍摄视角基本是平视或者轻微俯视,目标在画面里比较完整、比例正常。但智慧城市项目里摄像头一般装在6到8米的立杆上,往下倾斜30到45度俯拍,行人、车辆在画面里是"头顶小、肩膀宽"的形态,比例很怪。

同一个行人,在COCO训练出来的模型认知里可能是100x200像素左右的直立框,到了立杆俯拍画面里往往缩成40x60像素,而且还经常被街边大树、广告牌、公交站台遮挡。直接把通用模型拿来推理,漏检率一下子飙上去,处理起来非常难受。这个视角差异是客观存在的,不是简单的数据增强能覆盖的问题——本质上,模型需要在"俯视+广角+高密度遮挡"的数据分布里重新学习目标特征。

1.3 场景复杂度:树荫、遮挡与高密度行人的干扰

人行道是我做过的最"脏"的视觉场景之一。背景里什么都有:树荫光斑、广告牌、公交站台、施工围挡、外卖柜、共享单车停放区,再加上行人和车辆互相遮挡,一个目标经常只露出半边甚至三分之一。通用数据集里的图像通常背景相对干净,就算有遮挡,目标之间也不会像人行道早晚高峰那样重叠到离谱。

这种高密度遮挡场景下,通用模型的precision和recall都不太稳定,要么把消防栓当成人,要么把树影里的电瓶车漏掉。直接部署到算力有限的边缘盒子上,误检率根本压不住,因为后处理再怎么调阈值也是顾此失彼。说到底,场景和数据的匹配程度,比模型结构本身更决定上限。

注意:不是说公开数据集没有用,它们非常适合做预训练基座。我强烈建议的思路是——先在公开数据集上训一版通用模型,用它做预标注和负样本挖掘,再用自建的人行道数据去微调。这样效率比完全从零开始快得多。

2. 10300张图的来源构成:公开筛图、自采与清洗

数据集的规模不是拍脑袋定的。项目要求覆盖至少三个目标场景,且每个大类别目标数量不能太少,否则模型学不深。我当时初步攒了大概12500张原始图,经过清洗、去重、筛劣之后剩下10300张,这个量级对于人行道场景来说是足够启动一轮训练的。

2.1 数据来源的三条渠道

第一条渠道是从公开数据集里筛图。我主要翻了Cityscapes和BDD100K,重点挑选人行道占比高、摄像机安装位置较高的帧。公开数据里的图像质量和标注规范都不错,能省下不少功夫。不过需要提醒一句:很多公开数据集的License只允许学术用途,商业项目要用的话需要逐条确认,别等上线了才发现授权有问题。

第二条渠道是自行采集。这个是数据质量的下限保障。我安排团队在不同城市、不同时段(早高峰、晚高峰、夜间、雨天)拍摄了路口、商业街、学校门口、医院门口这几类典型位置,大概产出4600多张有效图。拍摄时尽量用接近立杆高度的机位和俯拍角度,同时留意不要拍到人脸特写和车辆车牌信息,后续商用合规风险会小很多。

第三条渠道是合作伙伴提供的存量素材。市政养护单位的日常巡检记录里,有大量工程视角的行人道照片和视频片段,我挑了约1900张用于补充路面病害和施工围挡这类细类。这类素材的特点是真实、稀缺、外界很难自行采集,缺点是构图和光照条件不稳定,清洗时要格外留心。

2.2 清洗流程:清掉哪些图、留下哪些图

清洗不是看一眼不好看就删,得有可复现的标准。我给自己定了几条硬性指标:

  • 清晰度:用拉普拉斯方差评估,数值小于某个阈值的视为模糊帧,直接删掉。具体阈值要根据数据集整体分布去定,我当时先用50张样本试跑,确定在100左右比较合适。
  • 曝光与光照:夜间过曝到纯白、强逆光导致目标完全淹没在高光里的图,少量保留可以增强鲁棒性,但不能占比超过5%,否则模型会被带偏。
  • 重复帧:视频抽帧时会产生大量相似度极高的相邻帧,用SSIM和上一帧做对比,相似度超过0.9的剔除,否则训练集里全是高度自相关的数据,验证集指标会虚高。
  • 分辨率:统一不低于1920x1080,低于这个分辨率的图对于密集小目标检测基本没什么训练价值。

清洗后的10300张图,类别分布上行人、电动自行车、汽车、井盖、路锥这几类偏多,路面病害和施工围挡相对少一些。说实话类别不平衡在这个场景是常态,后面可以通过采样策略和损失函数去缓解,不用一开始追求完美均衡。

2.3 train/val/test划分不能被忽略的细节

划分数据集的时候,大多数人直接随机split,这是个隐蔽的坑。我的做法是按摄像头点位和目录来划分,保证同一个摄像机视角下的连续帧不会同时出现在训练集和验证集里。原因很简单:如果同一路视频的相邻帧被拆到两个集合里,模型相当于提前见过答案,验证精度会虚高好几个点,等到部署到新点位立马现出原形。

我的划分比例是7:2:1,训练集约7210张,验证集约2060张,测试集约1030张。测试集单独锁起来,整个训练调参期间不允许看它的效果,只在最后评估一轮。这样才能真实反映模型在新点位上的表现。

提示:数据集的划分记录一定要留档。后面迭代时经常要回答"为什么这个版本在某个点位掉点"的问题,没有划分记录几乎是无法定位的。

3. 标注规则才是这类数据集的隐藏主线

做过数据项目的朋友应该都有体会:标注规范写得不细,后患无穷。尤其是人行道这种目标类别多、遮挡频繁、对象边界模糊的场景,如果两个标注员对同一张图的框法不一致,模型学到的就是一团矛盾。我在做这套数据集时,前前后后改了四版标注规范,下面把最终定下来的一套关键规则列出来。

3.1 标签体系的业务化设计

标签不是越多越好,而是要为业务判定服务。我最终定了11个类别,设计逻辑遵循"交管+市政"双线需求:

类别ID标签名覆盖对象业务用途
0pedestrian行人(含走路、站立、推婴儿车、推行轮椅的人)行人流量统计、闯红灯判定
1cyclist骑自行车的人,人和车整体框入非机动车违章
2e-bike电动自行车、摩托车、三轮车,人和车整体框入非机动车违章、外卖车管理
3vehicle汽车,包括违规驶入或停入园路的车违停检测
4traffic_light红绿灯灯体,只标灯头不标杆件信号灯识别
5sign交通标志、禁令标志,含杆件区域一起框标志可见性巡检
6roadblock路锥、水马、雪糕筒、隔离桩施工占道判断
7manhole井盖,只标可见的圆形区域市政设施巡检
8dumpster垃圾桶、垃圾箱、垃圾袋市容环境问题
9pavement_damage路面坑槽、明显裂缝、破损地砖市政病害巡检
10construction施工围挡、护栏、堆物占道施工监测

这个设计有几个讲究。一是cyclist和e-bike都是"人车整体框",而不是分别标人和车。好处是业务上直接就能判断是哪类非机动车,坏处是当人离开车的时候(比如快递员上楼送件),模型会把人标成pedestrian,把车标成e-bike,逻辑上需要后处理去配对。二是manhole和pavement_damage这两个类别业务很"重",标注时稍不留神就会跑偏,所以边界规则必须写死。

3.2 边界规则怎么定才不会撕

标注规则不写清楚,最终一定会出现同一目标被标成不同类别的扯皮。我这里列几条亲自踩过坑后定下的规矩:

  • 遮挡超过50%的目标不标注。刚被大树挡住一半以上的行人,宁可不标,也不要给出一个歪歪扭扭的框,否则模型学着学着就迷茫了。
  • 目标最小可见尺寸不低于15像素(短边)。太小的目标不是不能识别,而是标注精确度没有保障,在智慧城市场景里15像素以下的远处目标对业务判定意义也不大。
  • 骑行者一律把人和车整体框入,不拆开。如果只有车没人,标vehicle类(共享单车停放在路边);如果只有人推着车走,标pedestrian。
  • 红绿灯只标灯头发光面,不标黑杆。杆件太长会把画面里其他小目标全部挤掉,给边框回归造成误差。
  • 井盖只标注圆形的可见区域,被落叶、积水盖住超过一半就不标。
  • 路面病害只标"看得到的边缘",不脑补裂缝走向,裂缝断开就标多个独立框,不要硬连成一条。

每一条规则我都能说出一条具体的失败案例。比如"红绿灯不标黑杆"这条,就是因为在第一版标注里把杆子一起框进去了,导致模型学出来的框很长,和行人框重叠严重,后处理NMS直接把行人干掉了。

3.3 标注工具与半自动加速

工具选型上,小团队单机作业我推荐X-AnyLabeling,它是开源的,支持加载YOLO模型做预标注,然后人工在预标注结果上修正。整套流程下来,一张复杂人行道图的标注时间能从纯手工的3分钟以上压到40秒左右,效率提升非常明显。

多人协作的话,用CVAT会更顺手,尤其是在线分配任务、多人同时标注、冲突管理这些功能很成熟。不过CVAT的部署和运维有学习成本,一个人干小项目的话稍显重。我最后是蛇形走位:先本地批量预标注,再在CVAT上做交叉校验和修正,兼顾效率和一致性。

到这里插一句心里话:10300张图、合计两万多个目标框,听上去是个大工程,但流程理顺之后比想象中快得多。真正耗时间的不是画框,而是反复统一标准、review争议样本、给标注规则打补丁。

3.4 抽检和交叉一致性校验

标注完不等于可以训练了,必须做质量抽检。我的做法是按批次抽5%的图,交给两名标注员完全独立地重新标一遍,然后用Kappa系数评估一致性。阈值定在0.7以上才算通过;低于这个数就说明标注标准还没有统一,需要回头重看规范、对齐典型争议图。

这个环节非常值得花时间。我第一次抽检时Kappa只有0.62,问题集中出现在"推婴儿车的人算不算pedestrian""施工围挡和roadblock怎么区分"这两个点上。开会逐张对齐后,第二轮Kappa升到0.81,后面模型训练的效果也明显稳了。

4. 用不同YOLO版本的实测对比和损失函数观察

数据准备好了,接下来就是模型训练。我先后用YOLOv8、YOLO11的几个尺寸做了对比实验。这一节把配置、结果和对损失函数的一些观察整理出来,方便你们复现和对照。

4.1 训练环境与超参配置

硬件是两台RTX 4090,显存24GB,操作系统Ubuntu 22.04。训练脚本基于Ultralytics的CLI,核心参数如下:

  • batch size:16
  • 输入分辨率:1280x1280(小目标密集场景下比640提升明显,代价是训练和推理都变慢)
  • 优化器:SGD,momentum=0.937
  • 初始学习率:0.01,配合cosine decay
  • epochs:200,开了早停
  • mosaic:前180个epoch开启,最后20个epoch关闭
  • 类别权重:因为存在类别不平衡,给manhole和pavement_damage各乘了1.2的权重系数

这里提一下mosaic的调整逻辑。Ultralytics默认全程开mosaic,但在人行道这种高密度遮挡场景里,mosaic会把大量行人裁切成半个身子、一个头或者一条腿,模型学到的一堆残缺目标特征,测试时遇到完整目标反而框不准。后来我改成"前期开mosaic增强多样性和稳定收敛,后期关掉mosaic精修",小目标的recall有明显回升。

4.2 四个模型版本的实测成绩

我用同一份划分好的数据,分别跑了YOLOv8s、YOLOv8m、YOLO11s、YOLO11m,指标对比如下(自测参考值,具体会因为硬件和数据划分略有浮动):

模型输入分辨率mAP50mAP50-95参数量单帧推理耗时(640输入, RTX 4090)
YOLOv8s12800.7410.51211.2M约3.1ms
YOLOv8m12800.7930.55825.9M约5.5ms
YOLO11s12800.7580.5299.4M约2.8ms
YOLO11m12800.8140.58220.0M约4.7ms

结论很明确:在同等输入分辨率下,YOLO11m是本数据集上精度和速度平衡最好的一个,参数量不算大,mAP50接近0.82,mAP50-95拉到0.58以上。YOLOv8s适合算力特别紧张、只求实时跑通现场验证的场景;YOLOv8m则是一个"够用但不够精"的中间选项,如果要部署新点位对精度要求高,我不会选它。

还有一点需要说明:上面表格里的"单帧推理耗时"是640输入在RTX 4090上跑出来的,真实部署到Jetson这类边缘设备上完全不是这个数,后面第5节会单独聊。

4.3 损失函数不是调参的全部

YOLO的损失函数组成是:分类损失(BCE)+ 定位损失(IoU系列)+ 置信度损失(BCE)。默认情况下,YOLOv8和YOLO11用的是CIoU作为回归损失,它综合了重叠面积、中心点距离和宽高比。

我在密集人行道场景里观察到一个现象:CIoU在目标互相重叠的情况下,边界框回归容易"被挤偏"。举个例子,一个人站在电动自行车旁边,车把和行人手臂在画面里重叠,CIoU会倾向于让框尽量贴合中心点,结果两个框相互妥协,哪个都不准。

后来我实验了一组SIoU(考虑角度惩罚),密集行人场景的box稳定了一些,框和目标的贴合度肉眼可见地变好,但换来的是部分小目标分类漂移增加,整体mAP只提升了0.3左右,训练时间却拉长了将近20%。这个投入产出比不算划算,所以最终我还是回归了默认的CIoU,把精力更多放在数据增强和输入分辨率上。

说实话,损失函数在这个项目里不是决定性因素。真正拉高mAP的,是1280输入分辨率、关闭后期mosaic、针对性补充失败案例数据这三件事。损失函数属于锦上添花,不是雪中送炭。对外讲技术方案时我一般先讲数据,再讲损失函数,顺序不能反。

4.4 几个让精度大幅变化的失败案例

失败的案例往往比成功指标更有参考价值。这里挑三个印象最深的:

第一个是蓝色雨棚问题。一开始模型频繁把遮雨棚下的电动自行车误检成vehicle,把卖菜摊位的蓝色棚子误检成manhole。原因很直接:训练集里带雨棚、遮阳棚的样本太少了,模型只能靠颜色和形状去猜。补了大概300张带棚子场景的图之后,这两类误检直接降了70%。

第二个是放学场景的小目标。小学门口晚高峰,远处行人在1280分辨率下也只有20x40像素左右,640输入基本全漏。把输入升到1280后小目标recall提了一大截,但边缘设备的推理压力也随之而来。这个属于典型的精度-速度trade-off,业务上要自己权衡。

第三个是树荫误检。树影摇曳导致地面光斑不断变化,模型一度把地面上的树影轮廓认成路面裂缝。这问题不是加数据能彻底解决的,最后是通过时序滤波,把连续帧中目标停留时间不足0.5秒的检测结果过滤掉,才把误报压下去。

5. 从训练服务器到AGX Orin边缘部署的落地观察

模型在RTX 4090上跑得快不算本事,部署到边缘盒子还能维持可用的精度和帧率,才是智慧城市项目的真正挑战。这一节记录我在Jetson AGX Orin上部署实测的一手数据和一些工程细节。

5.1 导出ONNX与TensorRT加速的关键点

训练完的模型不能直接拿去部署,格式需要转一圈。我的导出流程是:

yolo export model=yolo11m.pt format=onnx dynamic=True opset=16

导出后的ONNX模型在Jetson上用trtexec转成TensorRT引擎:

trtexec --onnx=yolo11m.onnx --saveEngine=yolo11m_fp16.engine --fp16

这个步骤里有一个值得提醒的坑:YOLOv8和YOLO11导出的ONNX默认不包含NMS(非极大值抑制)层。NMS需要放在模型外部,也就是说推理流程是"模型出框 -> 自己做NMS"。如果你习惯直接套用某个框架的端到端部署包,很可能在NMS这一步被卡住,等于是把产出结果托管在应用层代码里处理。

另外,FP16量化在精度上几乎没有感知损失,mAP50大约只掉0.5个百分点,换来的是推理速度提升接近一倍,在人行道场景完全值得开。

5.2 AGX Orin上的推理速度与取舍

在Jetson AGX Orin(32GB版本)上,TensorRT FP16引擎的实测速度如下:

模型输入分辨率单帧推理耗时约合帧率
YOLOv8s960约25ms40 FPS
YOLOv8m1280约65ms15 FPS
YOLO11m1280约55ms18 FPS
YOLO11m960约35ms28 FPS

如果是单路视频流,YOLO11m在1280输入下18FPS基本够用;如果一路摄像头要覆盖多车道和人行道两个方向,建议降到960输入,帧率直接升到28FPS,精度损失在密集小目标上也能接受。

多路摄像头同时推理是另一个故事。十几路视频同时上AGX Orin,YOLO11m想都跑满肯定不现实。我的工程策略是给每路视频分配独立的推理队列,非关键帧按1/2或1/3抽帧处理,关键帧(比如检测到目标后的确认帧)走全分辨率。这样在保证不漏大事的前提下,总算力勉强够撑住。

5.3 检测框之外:业务规则和多模态分析的衔接

部署阶段最容易犯的错误是:以为模型输出几个框,业务就闭环了。实际上目标检测只是智慧城市感知链路的前半段。比如"违规占用人行道"这个判断,不能单凭一帧里有个vehicle框就报警——车可能只是路过,或者正短暂停靠有人上下车。我习惯在模型外面再包一层规则引擎:

  • 目标框在连续多帧中都存在,且中心点位移小于阈值,判定为"停留"。
  • 停留的vehicle框与禁停区域(预先用多边形标注)计算IoU,超过0.5才触发违停告警。
  • 相邻帧的同一个目标用跟踪算法(如ByteTrack)关联,避免重复计数和误报。

另外,只靠矩形框分类确实无法回答"这个人是骑行还是推行""这辆送货车是正在卸货还是违章停放"这类细粒度问题。这两年我注意到基于目标检测+多模态AI分析的趋势越来越明显,拿CLIP或者多模态VLM去读检测框里的内容做语义理解,能让业务判定从"是什么"升级到"在做什么"。这个方向在智慧交通和智慧市政场景里很有落地空间,但这套数据集本身仍然是最底层的地基。

5.4 模型持续迭代:hard example回流

数据集做到10300张不是终点,真正的长期工作是把部署后采集到的hard example持续回流到训练集里。我现在的做法是:每个点位保留最近一个月的误检和漏检截图,每月做一次筛选,挑出对模型提升最有价值的几百张图补充标注,重新微调一轮。

这个循环机制一开始看不见明显效果,但跑三个多月后,新点位首测的误报率基本能降一半。比我一开始花大把时间追求完美标注划算得多。模型需要的是"在错误中不断长出适应现场数据的能力",这个思路放在哪里都不会错。

最后分享一个体会。我做完这个项目最大的收获,不是模型精度最终刷到了多少,而是意识到智慧城市算法这条链路里,真正决定成败的往往不是模型结构多复杂、算力堆得多高,而是数据和业务标签体系设计得是否贴合场景。训练一个能用的YOLO模型,一周时间足够了;但把数据、标注、清洗、部署、回流这条链路跑顺,才是一个项目真正耗费心力的地方。如果你们也在做人行道检测或者类似的道路场景项目,我的建议是先从最容易获得数据的几个摄像机点位开始,跑通一遍采集-清洗-标注-训练-部署闭环,再谈扩大规模和优化精度。就我的经验来看,十个点位的高质量增量数据,带来收益远比十次复杂的模型改动大。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询