ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

YOLO目标检测实战:人行道与盲道障碍物检测系统搭建全流程

YOLO目标检测实战:人行道与盲道障碍物检测系统搭建全流程 1. 这个检测系统到底在解决什么问题1.1 为什么把目标锁在人行道和盲道障碍物先说一个我实际跑项目时遇到的场景。某个城市做市政设施巡检人工巡查盲道占用、破损、违规施工这些问题效率低不说漏检率还特别高。一条路几公里走下来光拍照记录就得大半天回去还得人工整理照片、填表格、派工单整个流程又慢又容易出错。人行道和盲道障碍物检测本质上就是把“城市道路是否通畅、无障碍设施是否完好”这件事从人工肉眼巡查变成算法自动识别。核心目标物包括几类占道停放的车辆、施工挡板、堆放的建材杂物、破损缺失的盲道砖、违规设置的立柱或路障以及井盖缺失这类安全隐患。每类目标对算法来说就是一个类别检测系统要做的是在图像、视频、摄像头画面里实时圈出这些障碍物的位置并标出类别置信度。这个系统的价值在于替代重复性人力劳动。传统人工巡检的痛点是三个一是效率低二是标准不统一三是问题发现之后难以形成可追溯的记录。算法检测天然适合这类重复、规则相对固定的视觉任务。只要标注数据足够规范模型就能在一张照片里同时定位多个障碍物还能直接输出结构化结果后续对接上报系统、生成工单就很方便。这个项目适合谁看如果你是做智慧城市、市政巡检、无障碍设施管理相关工作的算法工程师或学生或者你正在找一个人工智能目标检测的完整实战项目来练手这篇内容会从数据准备、模型训练到部署推理带你完整走一遍。如果你是初学者想弄明白YOLO系列模型怎么落地到真实场景也可以跟着思路复现。1.2 系统整体能力拆解图像、视频、摄像头三种入口这个检测系统在功能设计上分了三个入口分别是单张图像检测、视频文件检测和摄像头实时检测。三种入口的数据流不一样处理逻辑也有差异。图像检测最简单输入一张图片经过模型推理输出标注了检测框和类别的图片同时可以保存检测结果。视频检测是把视频逐帧拆开按照设定的抽帧间隔逐帧推理再把结果写回视频或者输出成带标注的视频文件。摄像头实时检测对性能要求最高需要边采集画面边推理还要保证画面流畅、输出延迟尽量低。我最初做这个系统时先把图像检测跑通再把图像检测函数改造成视频检测最后才做摄像头实时检测。这个顺序非常重要因为每一步的报错和调试范围都是可控的。如果你一上来就写摄像头程序很难分清是图像读取问题、模型推理问题还是显示问题。三种入口的共用部分是同一个模型推理核心只不过换不同的数据来源和输出方式。这个设计思路在工程上叫做“数据源解耦”也就是把“从哪读数据”和“怎么分析数据”分开后面不管接入新的RTSP流、无人机图传还是手机推流只需要新增一个数据接入模块检测核心完全不用动。1.3 技术底座为什么选择PyTorch与YOLO系列模型标题里写的是“yolov13”这里我按YOLO系列框架来理解。YOLO系列模型是目前目标检测领域里落地最广泛的一类算法核心思路是把整张图片划分成网格每个格子直接预测目标框和类别一次前向推理就能输出所有目标的位置信息。相比传统两阶段检测器YOLO的优势是速度快、结构简单、部署方便。选择PyTorch框架主要是因为三个原因。第一PyTorch的生态最完整YOLO系列几乎所有开源实现都是基于它的遇到问题能搜到大量现成经验。第二PyTorch的调试体验好模型在前向传播过程中可以随时打印中间张量的形状和值这在排查检测框输出异常时特别有用。第三PyTorch转换到部署端比较方便既可以通过ONNX导出到其他推理框架也可以直接用原生TorchScript做加速推理。技术选型的本质是降低复现成本。我在项目里坚持用开源模型做预训练初始化而不是从头训练。从头训练一个目标检测模型需要上百万张标注图片和大量算力普通场景下根本跑不动。用预训练权重做迁移学习相当于模型已经学会了“怎么识别物体”我们只需要微调它学会“这是人行道上的障碍物”这个特定任务数据量需求会降到几千张级别普通显卡也能训练。2. 数据集构建障碍物检测的命根子2.1 先定类别哪些东西必须被识别训练一个检测模型前第一件事不是写代码而是明确要检测哪些类别。类别定得太粗模型分不清具体问题定得太细标注工作量和模型难度都会暴涨。我在这个项目里选了六个核心类别基本覆盖了人行道和盲道最常见的障碍物场景占道车辆包括自行车、电动车、三轮车、机动车停在人行道或盲道上的情况施工围挡市政施工临时设置的围挡、挡板、水马堆物堆料砖块、沙石、杂物等堆放占用盲道障碍桩道路入口设置的固定立柱、石球等阻挡设施盲道破损盲道砖缺失、断裂、隆起造成的不连续井盖异常井盖缺失、破损、移位形成的路面隐患这里有一个关键判断类别不是越全越好。比如你看到一辆共享单车倒在盲道上这算“占道车辆”还是“倒伏单车”如果两类都设标注和模型都会很痛苦。实际项目中通常按处置方式分类因为系统下游对接的是市政工单不同处置方式对应不同负责部门分类越贴近业务流程模型产出的结果越好用。2.2 数据采集场景多样性的坑数据集构建往往占用整个项目60%以上的时间而采集端最容易翻车。我踩过的一个典型坑是只在一个时间段、一种天气下采集图片导致模型在阴天或者夜晚场景下直接失效。为了提升泛化能力数据采集阶段必须刻意覆盖多种条件。我在模拟项目X里做了一个采集矩阵按时间段白天、傍晚、夜间、天气晴天、阴天、雨后、区域商业区、居民区、学校周边三个维度组合采集。如果条件受限至少保证有两类光照环境差异明显的样本。还有一个很容易被忽视的问题有些障碍物本身就是“稀缺样本”。比如井盖缺失日常巡检中不常见但不代表不重要。这种类别样本量不够时模型很容易漏检只能通过补采或用数据增强策略来缓解。你可以去市政养护单位沟通或者关注相关投诉平台的图片收集这类少见样本。另外采集设备不用太讲究手机拍摄就可以。但要注意拍摄角度尽量模拟巡检人员的视角——手机平行或者略微俯视拍摄人行道避免全部都是高空俯拍的视角否则部署时摄像头安装角度不对模型识别效果会明显下降。2.3 标注规范与数据增强策略数据标注是决定模型上限的环节。我用的标注工具是LabelImg导出为YOLO格式的txt文件每行一个目标格式为类别序号 中心点x归一化坐标 中心点y归一化坐标 目标宽度归一化 目标高度归一化。标注规范上我总结了几条实用原则遮挡目标也要标全边界框框出目标完整轮廓而不是只标可见部分小目标不能漏标比如远处的小型障碍物漏标会导致模型后期对小目标不敏感同一个目标在多张图片里重复出现不要紧关键是目标形态要多样边界框紧贴目标主体不要为了“省事”把多个相邻目标框成一个标注完成后我的建议是抽查至少20%的标注样本重点看有没有漏标和边界框过松过紧的问题。标注质量差模型训练出来精度一定差这个锅甩不到模型头上。数据增强方面YOLO系列自带Mosaic增强、随机透视变换、HSV色域变化等功能。实际训练时我开了Mosaic增强这个策略把四张图片拼接成一张输入相当于变相扩大了batch size能明显提升小目标检测效果。色域变化也开着模拟不同光照条件让模型对亮度变化更鲁棒。3. 模型训练从环境配置到参数调优3.1 环境安装与硬件选型先说环境。PyTorch的安装比较直接我建议用conda建一个独立环境避免跟系统其他库冲突。深度学习框架的版本要跟CUDA版本匹配如果你的显卡驱动是较新的版本直接装对应版本的PyTorch即可。硬件方面模型训练一张入门级显卡是可以跑起来的。我实测过在显存6GB的卡上用640分辨率的输入batch大小设为8可以正常训练。如果你的显存只有4GB可以把输入分辨率降到416或者把batch降到4。显存不够时会出现CUDA out of memory报错这时候优先调小batch而不是换更复杂的模型。训练之前的目录结构也值得提前规划好。我的项目目录大概是这样的project/ ├── data/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/ ├── models/ ├── runs/ ├── train.py ├── detect.py └── requirements.txt目录清晰度会直接影响后期调试效率。我之前犯过错误把所有图片放在一个大目录里没有分训练集和验证集导致训练时验证集的loss完全不正常排查了半天才发现是验证集被混进了训练集。3.2 模型配置文件与超参数选择YOLO系列的训练入口通常由一个yaml配置文件控制里面写明了数据路径、类别数量、模型结构等关键参数。我在模拟项目X里用的数据配置示例大概长这样path: ./data train: images/train val: images/val nc: 6 names: [vehicle, construction_fence, stacked_materials, barrier_pole, tactile_paving_damage, manhole_issue]这里nc表示类别数量names列表的顺序必须和标注txt文件里的类别序号一一对应。我自己因为names顺序错位吃过亏模型输出的标签跟实际目标完全对不上查了很久才发现是配置文件里类别列表跟标注时的类别表顺序不一致。超参数方面初始学习率我设成0.01使用SGD优化器配合余弦退火学习率调度。batch大小根据显存调整输出分辨率统一用640。训练轮数设置150轮左右但我不是死等150轮结束而是每训练一段时间就保存一次权重选验证集上mAP最高的那一个作为最终模型。3.3 训练过程的实测记录与判断标准训练时最直接的判断标准是两类loss曲线box_loss边界框损失和cls_loss分类损失。我一般盯两个指标训练集loss和验证集loss。训练集loss持续下降验证集loss也跟着下降说明模型在学习如果训练集loss还在降验证集loss开始反弹说明过拟合了应该提前停止或者增加数据增强。还有一个经验值得分享训练初期loss下降慢很正常不用焦虑。第一轮loss可能在0.1以上几十轮之后才降到0.03左右。真正需要警惕的是loss突然变成NaN这种情况通常是学习率过大或者某张异常数据触发了梯度爆炸把学习率调小一个数量级就能解决。我训练了一个基于YOLO系列的模型最终在验证集上mAP达到0.82左右几个主要类别的平均精度差异不大只有“盲道破损”这一类稍微低一点原因是破损形态差异特别大有的断一块有的裂一条缝。改进方向是增加这类样本数量而不是调整模型结构。4. 推理部署图像、视频、摄像头三种检测模式实现4.1 图像检测单张图片的推理管线图像检测是整个系统的地基代码逻辑清晰方便后续扩展。核心流程是读取图片、预处理、模型推理、后处理、结果绘制。首先加载模型权重。这里有一个容易踩坑的点加载时要指定类别数和配置文件否则模型结构头部的输出维度跟你的数据不匹配直接报错。加载完成之后把模型切换成eval模式关闭梯度计算否则推理速度会慢上好几倍。预处理环节模型期望的输入是RGB图像像素值归一化到0到1之间并且resize成训练时的尺寸。这里特别提醒OpenCV默认读入的图片是BGR通道顺序不转换的话检测结果往往不对——最典型的现象是目标框的位置基本正确但类别判断完全混乱因为模型看到的颜色通道跟你标注时的训练数据不一致。推理完成后模型输出的是原始的检测张量还不能直接画框需要经过非极大值抑制处理把重叠的检测框合并去掉置信度低于阈值的框。直觉上可以这么理解同一个目标附近可能预测出好几个框NMS的作用是保留最可信的那个把其他多余框清掉。画框和写类别名时字体和线条粗细要根据图片分辨率调整。大图上用固定粗细线可能不明显小图上又可能把目标全遮住。我一般用相对值线宽取图片宽度的千分之二左右。4.2 视频检测按帧抽检与结果输出视频检测本质上就是“循环读帧 图像检测”。但这里有一个重要性能考量视频一秒钟有25到30帧每一帧都推理一遍算力消耗大、速度慢而且相邻帧的内容高度重复逐帧检测属于浪费。我通常设置一个抽帧间隔参数比如每3帧检测一次或者每5帧检测一次。这样做的好处是推理次数减少视频处理速度提升明显。如果项目对实时性要求高甚至可以每10帧检测一次然后把检测结果直接复用到中间帧实际显示效果不会有太大差别。对市政巡检来说目标移动速度慢抽帧策略完全够用。视频读取方面使用OpenCV的VideoCapture读取视频文件循环里read()返回值判断是否读到了最后一帧。输出方面可以使用VideoWriter写视频文件注意设置编码格式和fps。另外一个容易忽略的点是保存检测结果视频时要保证输出视频的尺寸跟原始视频一致。如果模型内部做了resize画框坐标必须映射回原图尺寸否则检测框会偏离目标位置。这个映射逻辑在图像检测里也得注意但视频场景下因为连续帧播放偏移问题会特别显眼。4.3 摄像头实时检测延迟控制与界面显示摄像头实时检测是三种模式里最考验工程能力的部分。核心难点不是模型推理而是“读取帧的速度”和“处理帧的速度”不匹配导致画面卡顿、延迟越来越高。先讲一个基本框架。摄像头画面通过OpenCV的VideoCapture(0)打开如果摄像头编号不唯一可以参数里指定具体设备。实时循环大概是这样的逻辑读取一帧画面如果检测线程空闲就交给检测线程处理如果检测还没完成就先显示上一帧的检测结果不阻塞等待。import cv2 import torch import time # 假设 model 是训练好的模型 model.eval() cap cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 跳帧计数 frame_skip 2 frame_count 0 last_results None while True: ret, frame cap.read() if not ret: break # 如果到达跳帧间隔执行检测 if frame_count % frame_skip 0: results detect_frame(model, frame) # 返回绘制后的图像 last_results results # 优先显示最新检测结果 display last_results if last_results is not None else frame cv2.imshow(Detection, display) if cv2.waitKey(1) 0xFF ord(q): break frame_count 1 cap.release() cv2.destroyAllWindows()延迟控制的另一个关键点是把图像读取和模型推理放到不同的线程或者至少不要在读取帧的同一线程里跑模型推理。因为在推理期间摄像头缓冲区会持续积压新帧读到的帧越来越陈旧画面延迟越来越大。把两个操作解耦后检测线程专注推理展示线程专注显示延迟体验会好很多。摄像头画质也会影响检测效果。我建议把摄像头分辨率设成1280x720不需要4K因为过高的分辨率对模型精度提升有限反而增加预处理耗时。光线较暗的环境里可以适当开启摄像头的低照度模式或者加补光因为模型在低光照下误检率会明显上升。5. 常见问题与排查技巧实录5.1 训练不收敛或loss波动训练过程中最让人头疼的问题就是loss不收敛。我遇到过几种情况一种是loss一直维持在0.1上下不下降另一种是loss剧烈震荡还有一种是loss直接变成NaN。loss不下降先检查学习率是不是太小。YOLO系列默认初始学习率是0.01如果基础环境变了比如数据集规模特别小学习率可以降到0.001左右重新跑。学习率太大则表现为loss震荡特别在训练后期需要配合余弦退火把学习率降下来。loss变NaN最常见的原因是梯度爆炸。我排查的第一件事是看数据集中有没有异常标注比如某个目标框坐标超过图片边界或者类别序号超出类别总数这类脏数据会直接破坏训练。第二个排查点是学习率把学习率调低一个数量级再试。还有一个比较少但真实存在的坑显卡温度的波动导致硬件不稳定也会偶发出现NaN这种情况下换个时间段跑同样的训练如果不再出现说明不是代码问题。5.2 摄像头推流延迟、卡顿摄像头检测最常见的问题是画面越来越卡、延迟越来越大。原因前面提过图像读取线程和推理线程没有解耦导致显示的是过期帧。遇到延迟问题时我的排查顺序是先看视频读取速度再看推理速度。先单独测试摄像头读帧的速度如果本身就慢那可能是摄像头驱动或USB带宽问题换一个摄像头试试。如果读帧正常推理每帧耗时超过200毫秒那就需要优化推理速度。推理提速有几个方向一是直接把输入分辨率从640降到416速度提升明显精度稍微下降二是用半精度推理只要显卡支持就能在保持精度基本不变的情况下大幅提速三是做TensorRT等专用加速这个在嵌入式设备上效果最明显但部署复杂度会高一些。5.3 小目标漏检和误检人行道障碍物检测里小目标的问题特别突出——比如远处的电动车、很小的盲道砖破损。YOLO系列模型对越大越清晰的目标效果越好小目标天然吃亏。解决小目标漏检我的经验是两条路。第一条是数据层面保证训练集中小目标样本占比合理并且对所有小目标框都严格标注不能因为目标小就顺手忽略。第二条是推理层面使用多尺度推理对同一张图片用原始分辨率、放大的分辨率各推理一次合并结果能提升小目标召回率但推理耗时加倍这种策略适合离线处理不适合实时场景。误检的高发场景也有规律夜间路灯下的树影、斑马线旁的石墩子都可能被误识别为障碍物。经验上看误检往往集中在特定场景下统计错误样本中出现频率最高的图片形态再有针对性补充负样本和图谱往往能快速收敛问题。不要一上来就加数据量很多情况下误检是训练数据里背景环境太单一导致的。5.4 模型加速与压缩经验之前在一个演示环境里我只用CPU跑推理。模型前向传播一次就要几百毫秒根本没法做实时检测。后面我用了两个技巧一是把模型导出成TorchScript格式去掉动态图开销二是使用半精度计算在支持的硬件上推理速度几乎翻倍。还有一个实战技巧是减少推理中的无用计算。比如对视频检测场景背景基本不变可以只在画面变化明显的区域做检测但这个方法实现复杂容易引入新问题我在实际项目里用得不多。更稳妥的方案是固定输入尺寸、减少NMS的候选框数量上限比如把每张图片最多保留300个检测框改成100个推理速度会有可感知的提升对典型场景影响不大。6. 这套系统的延伸方向与个人体会6.1 从检测到上报对接工单系统模型检测出障碍物只是第一步真正产生业务价值的是检测结果能自动触发处置流程。我在模拟的项目里把检测结果导出成带时间戳、位置坐标、现场照片的结构化记录再通过接口拉到市政巡查平台上自动生成待办任务。这个方向对系统的要求就不只是检测精度了还有定位能力。单靠摄像头画面无法精确知道障碍物在哪条路的哪个具体位置所以部署时很多项目会接入经纬度信息或者在巡检车上加GPS模块检测到目标的同时记录当时的定位。对接工单系统后发现问题的确认效率提升了现场复核时间也减少了。如果只做检测不做闭环系统的价值会大打折扣。6.2 一些个人实操体会系统性来说在这类目标检测项目里80%的时间都在和数据打交道。模型结构、训练参数这些社区里的开源方案已经很成熟了真正决定项目成败的是你有没有一套干净规范的数据集。标注质量差、场景覆盖不全再好的模型也救不回来。另外一点体会是算法模型要在真实场景里边用边改。在实验室验证集上mAP高不代表现场效果就好。摄像头安装高度、光线角度、画面抖动都会影响检测效果。我建议项目上线后保留一段时间的现场数据持续补充训练集模型效果会越用越好而不是一锤子买卖。最后想分享的一个细节技巧是给模型写推理脚本时尽量把预处理和后处理封装成独立函数参数用配置文件管理这样后期换数据、换场景、调阈值时不需要改动核心代码只改配置就能完成能省掉很多重复调试的时间。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表