YOLOv5+SORT车辆行人追踪:稳定ID的工程实践

发布时间:2026/9/1 12:43:45
YOLOv5+SORT车辆行人追踪:稳定ID的工程实践 简介这是一套面向计算机视觉初学者与课程设计者的实战型目标检测与多目标追踪系统源码基于PyTorch框架整合YOLOv5目标检测模型与SORT算法实现对车辆、行人等动态目标的实时识别与ID连续追踪。资源适用于本科毕业设计、人工智能课程大作业及智能交通方向实践项目无需额外调参或代码修改即可直接运行。压缩包共101个文件含42个核心Python脚本涵盖数据加载、模型推理、轨迹关联与可视化、51个编译后pyc文件、2个配置yaml模型结构与超参、1个预训练权重pt文件、1张示例测试图及README.md说明文档整体体积79.92MB结构清晰、模块解耦明确。已有1549人学习下载配套完整工程目录与通用接口封装便于理解YOLOv5后处理逻辑、卡尔曼滤波预测机制及匈牙利算法匹配流程是掌握端到端多目标追踪Pipeline的优质入门范例。 上周有个读者发私信说他做交通监控相关的小项目时用YOLOv5检测已经跑通了每一帧都能把车辆和行人框出来但视频一拖进播放器就露馅——同一个行人在连续帧里被当成好几个新目标ID一直跳统计数目的结果完全没法看。这个问题我太熟了刚接触多目标追踪时同样被卡在这里。你需要的不是更好的检测器而是一个能把这些检测框串成轨迹的追踪器。这篇要拆解的就是基于PyTorch实现的YOLOv5SORT车辆行人目标识别及追踪系统源码一套把检测和追踪打通、让每个目标从进画面到出画面都保持稳定ID的完整方案。这套源码解决的是一个非常具体的现实问题视频里的目标识别不只是在单帧里画框而是要回答“这个框是谁”“它从哪来、到哪去”。放在交通场景里就是能够跟踪某辆车从画面右侧驶入、穿过路口、再从左侧离开全程ID不变轨迹连续。对需要车流量统计、行人路径分析、区域入侵检测的人来说这是最基础的工程底座。如果你正在做目标检测想往追踪延伸或者做完课程设计想进一步形成完整项目这篇可以让你对整套系统的模块划分、参数调优和踩坑点有一个全景认识。1. 为什么是YOLOv5SORT这套组合到底解决了什么问题1.1 目标检测选型成熟度与部署成本的权衡先聊检测器。为什么项目里选的是YOLOv5而不是更早的Faster R-CNN或者更新的YOLOv8、YOLOXYOLOv5是基于PyTorch实现的单阶段目标检测框架和PyTorch生态的亲和度非常高。做车辆行人识别时常见目标的尺寸相对明确——车是中等偏大的刚性目标行人是中等偏小但外形稳定的目标单阶段检测器在小目标上的表现虽然不如两阶段理想但胜在速度极快视频流场景下需要的就是这个速度。Faster R-CNN精度高但两阶段的推理耗时会让你在视频流里跑不动变相增加部署成本。那为什么不用更新的YOLOv8我的看法是YOLOv5的社区资料、权重文件、训练脚本、问答数量都是最多的。对一个要落地、要调试、要复现的项目来说“遇到问题能搜到答案”本身就是巨大的工程价值。YOLOv8更适合精力充裕、想追新的场景而不是作为一套系统的默认底座。换一个更实际的角度你下载到的源码、配套权重、训练方案大都基于YOLOv5这套源码选它是为了让你第一次跑通时少踩框架层面的坑。1.2 追踪方案选型SORT为何能在轻量场景站住脚多目标追踪的主流做法叫Tracking-by-Detection先检测后关联。SORTSimple Online and Realtime Tracking就是这种范式的代表全称里的Simple和Realtime已经概括了它的性格简单、在线、实时。SORT的核心只有两部分卡尔曼滤波做状态预测匈牙利算法做检测框与轨迹的匹配。它不需要训练额外的Re-ID模型不需要提取外观特征。这个特性对车辆和行人的轻量追踪场景特别重要因为Re-ID模型本身又是一套跨镜追踪的复杂体系需要专门的数据集训练推理时还要额外占用算力。SORT把这些都省了只靠位置和速度信息就把轨迹串起来实现了“检测器输出什么追踪器就关联什么”。从目标特性看车辆尤其适合SORT。车辆运动接近匀速刚体卡尔曼滤波里恒速模型的假设对它来说基本成立。行人虽然遮挡更频繁、运动模式更灵活但在监控摄像头固定的场景下单帧之间的位移很小SORT依然能保持不错的连贯性。说白了这套组合不是万能药但它在“固定摄像头、中低密度、白天光照”这类典型交通场景下是性价比最高的组合。1.3 检测与追踪解耦替换任意一环的成本有多低这套源码最容易被忽略的设计优点是检测和追踪完全解耦。YOLOv5只负责产生检测框SORT只负责接收检测框并维护轨迹两者之间通过一个统一的数据结构交互没有模块内部互相侵入。解耦带来的直接收益是你随时可以把检测器从YOLOv5换成YOLOv8、YOLOX只要把检测结果转换成统一的框坐标格式喂给SORT就行反过来你也可以把SORT换成DeepSORT、ByteTrack而不需要动检测部分。我曾经在另一个项目里把YOLOv5的权重直接换成YOLOv8导出的ONNX模型只改了几行坐标转换代码追踪链路完全没动。这个设计思路比任何单个模块的选型都重要它让系统有了持续迭代的空间。下载源码后建议先关注detector和tracker之间的接口层读懂了这一层整个项目也就懂了一半。2. 拿到源码之后核心模块与追踪链路拆解2.1 目录结构与各模块职责拿到源码解压之后先别急着跑花五分钟看一遍目录结构。一套合格的检测追踪项目模块划分通常类似这样project/ ├── weights/ # 模型权重文件 │ └── yolov5s.pt ├── detector/ # 检测器封装层 │ └── yolo_detector.py ├── tracker/ # 追踪器实现层 │ ├── sort.py │ └── kalman_tracker.py ├── utils/ # 工具函数 │ ├── 坐标转换.py │ └── 可视化.py ├── demo.py # 主入口 └── requirements.txtdetector目录里的yolo_detector.py负责加载YOLOv5模型、对输入帧做letterbox预处理、执行推理、执行NMS、把结果从模型输出空间还原到原图坐标。tracker目录里的sort.py是核心管理所有轨迹的增删改查和匹配关系kalman_tracker.py封装了单个目标的卡尔曼滤波状态。utils目录负责画框、画轨迹线、处理视频读写这类杂活。这里有个容易被忽略的点weights目录里放的权重版本必须和detector里的模型定义匹配。YOLOv5官方发布的yolov5s.pt对应的是配套的yaml结构和类别映射如果你随便找了个自训练的权重放进去容易出现类别数量不一致、推理结果错乱的问题。第一次跑通时建议直接用官方预训练权重。2.2 检测结果到追踪器的数据流从box到track的转变主循环的逻辑可以用一条数据流串起来VideoCapture读一帧图片送入detector得到若干检测框每个框带着类别和置信度经过类别过滤后把检测框从xyxy格式转换为SORT内部的中心点宽高格式喂给sort.update()再拿到更新后的轨迹列表绘制到画面上。这条链路里最容易出错的就是坐标转换。YOLOv5推理出来的原始坐标是相对于输入图片尺寸比如640×640像素的而原始视频帧可能是1920×1080letterbox预处理时做了等比缩放和灰边填充后处理必须把检测框坐标还原到原图坐标系。SORT里卡尔曼滤波器的状态向量是[u, v, s, r]——u和v是中心点x和ys是框面积r是宽高比。而YOLOv5默认输出的是xyxy格式的左上角和右下角坐标。如果你的代码在这两个坐标系之间转来转去时漏了某一步追踪结果就会出现“框和轨迹偏移”的诡异现象。我排查过很多类似问题几乎有一半的“追踪漂移”bug不是追踪器的问题而是检测框坐标在前处理/后处理环节没对齐。所以拆解这套源码时建议把坐标转换这一行单独标记出来逐段对比原图坐标和模型输入坐标确认无误再进行下一步。2.3 SORT内部三大核心状态预测、数据关联与ID管理SORT追踪器内部维护着一个轨迹集合每条轨迹包含一个卡尔曼滤波器、一个持续命中计数器、一个丢失帧计数器和唯一ID。每一帧都完成三个阶段第一是状态预测。上一帧的每条轨迹用卡尔曼滤波器预测当前帧的位置和速度。用大白话说就是你通过目标上一刻的位置和速度估算它此刻大概移动到哪。卡尔曼滤波的厉害之处在于它会把预测和观测做一个带权重的融合噪声大的观测自动降低信任度预测稳定的轨迹则保持平滑。第二是数据关联。把当前帧检测器输出的所有框和预测出的轨迹位置做IoU计算得到代价矩阵然后用匈牙利算法linear_sum_assignment求最优匹配。这里用IoU而不是欧氏距离是因为同一目标在相邻帧内位置变化很小检测框和预测框的交叠比例能够直观反映“是不是同一个目标”。匹配的目标是让所有匹配的总代价最小——就好比多个骑手和多个订单之间做分配让整体配送距离最短。第三是ID管理。成功匹配的轨迹用当前检测结果更新状态命中数加一没有匹配到检测框的轨迹丢失帧数加一超过max_age就删掉没有匹配到任何已有轨迹的检测框则作为新目标建立新轨迹。这个逻辑看似简单但所有调优经验都集中在这三个数字上max_age、min_hits和IoU阈值。一会儿到第4部分会详细展开。3. 从零跑通的完整路径环境配置、依赖安装与常见报错3.1 环境准备Python虚拟环境、PyTorch与CUDA版本怎么配环境配置是新手最容易卡住的一关。我建议用Anaconda创建独立虚拟环境不要直接装在base环境里否则不同项目之间的依赖很容易打架。conda create -n vehicle_tracking python3.8 conda activate vehicle_trackingPython版本建议3.8或3.9虽然YOLOv5后续版本对3.10/3.11也兼容但很多第三方依赖尤其filterpy这类老库在3.8下最稳。接着安装PyTorch。这里最容易翻车PyTorch的CPU版和GPU版命令不同GPU版还需要匹配本机的CUDA版本。先通过nvidia-smi查看驱动支持的最高CUDA版本比如显示CUDA Version: 12.1那就安装对应的CUDA 12.1版PyTorchpip install torch torchvision --index-url https://download.pytorch.org/whl/cu121如果本机没有NVIDIA显卡或者只是想在CPU上验证流程就装CPU版pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu注意一个常见误区nvidia-smi显示的是驱动支持的最高CUDA版本不代表当前环境中已经安装了CUDA Toolkit。PyTorch的CUDA安装包是自带的不需要额外安装完整Toolkit。装完之后验证一下python -c import torch; print(torch.__version__, torch.cuda.is_available())如果输出里cuda.is_available()为True说明GPU可用。如果为False先检查PyTorch版本是否真的装了GPU版再检查显卡驱动版本是否过旧。3.2 依赖安装与权重下载最容易翻车的几个细节装完PyTorch开始装YOLOv5和SORT的依赖。YOLOv5官方的requirements里包含opencv-python、numpy、matplotlib、pyyaml、tqdm等直接按官方要求安装即可。SORT那边主要依赖filterpy、scipy、numpy。filterpy这个库很关键而且有个经典的坑老版本SORT代码里写的是from filterpy.kalman import KalmanFilter如果filterpy版本装的是比较新的版本某些API签名有变化或者直接ImportError整个程序起不来。如果遇到filterpy导入报错可以尝试pip install filterpy1.4.5这个版本相对稳定和常规SORT代码兼容性最好。另外scipy一定要装匈牙利算法的linear_sum_assignment在scipy.optimize里很多精简版SORT实现会漏掉这个依赖。权重文件方面官方yolov5s.pt大约14MB适合快速验证yolov5m.pt大约42MB精度更高。第一次跑建议直接用yolov5s.pt把整个链路跑通后再换m对比效果。下载时如果直接从GitHub release下载慢可以用镜像站或者科学方式下载后手动放入weights目录。这里有个小建议不要使用来源不明的“增强版”或“魔改版”权重这类权重很多人训练时改了类别映射放进标准YOLOv5代码里会出现类别完全对不上、检测结果乱成一团的问题。3.3 运行源码命令行参数与第一手验证依赖装好后准备一段测试视频。如果没有现成的交通视频可以用摄像头抓一段或者从公开数据集里截取一个片段。运行命令大概是python demo.py --source test.mp4 --weights weights/yolov5s.pt --conf 0.4 --iou 0.45第一次跑的时候建议不要把参数调得太激进先把默认参数跑通。程序会在输出视频里画出每个目标的框框上标注类别和ID号并在视频流下方或日志中打印当前帧的目标数。验证是否跑通的标准很简单选一段车辆从远处驶入再驶出的视频观察同一辆车从进画面到出画面ID是不是一路保持不变。如果中途没有明显遮挡ID却从5跳成了17说明追踪参数需要调整或者是检测框抖动太厉害。实际操作时我一般会连续盯住一个目标看30帧以上确认它的ID没有变化才认为系统是健康状态。不要只看画面里“有框有ID”就完事那只能说明程序没崩不代表追踪生效了。4. 针对车辆与行人的调优实践阈值、参数与效果验证4.1 检测参数调优车辆与行人的置信度阈值怎么分开设很多人拿到这套系统第一反应是调SORT其实先要调的是检测器的置信度阈值。检测器置信度阈值直接决定了哪些框能进入追踪器如果阈值太高大量低置信度的真实目标会被过滤掉追踪器再多轨道也白搭如果阈值太低大量误检框会生成大量假轨迹。车辆和行人的阈值应该分开考虑。车辆是刚性目标特征明显置信度普遍偏高阈值设0.45到0.5问题不大。行人相对难检测一些尤其在远处或部分遮挡时置信度会降到0.3左右如果统一用0.5的阈值远处行人几乎全部丢失追踪轨迹自然断断续续。经验上行人置信度阈值可以放到0.3到0.35。在YOLO类别过滤上还有一个实用技巧只保留你关心的类别。COCO数据集中和交通场景直接相关的是person0、bicycle1、car2、motorbike3、bus5、truck7。在检测后处理时把其他类别过滤掉可以大幅减少SORT收到的无用检测框降低误关联概率。这里给出一个常用的参数速查表参数建议值说明conf_thres车辆0.45~0.5车辆置信度高可以稍严格conf_thres行人0.3~0.35小目标/遮挡时置信度低需要放宽NMS iou_thres0.45~0.5过滤同一个目标的重复框classes[0,1,2,3,5,7]只保留交通相关目标如果你使用的源码没有直接支持按类别设置不同置信度阈值可以在检测输出之后手动加一段逻辑根据检测框的类别label分别用不同的置信度阈值去过滤。这是我对这套源码做过的第一个改造效果立竿见影。4.2 追踪参数调优max_age、min_hits与IoU匹配窗口聊完检测参数回到SORT本身的三个核心参数。这三个参数直接影响轨迹的连续性和ID稳定性。max_age表示一条轨迹在没有匹配到检测框的情况下最多保留多少帧。SORT默认值一般是1到2这意味着目标一旦被遮挡一两帧轨迹就被删除之后目标重新出现时会被当成新目标分配新ID。对于车辆场景我一般调到3到5因为车辆在十字路口被其他车辆短暂遮挡很常见几帧之后重新出现时ID不应该变。但注意max_age调大也有副作用轨迹保留期间如果目标已经离开画面但算法还在“等它回来”这个死轨迹可能会抢走原本属于其他目标的检测框造成ID串扰。所以调大max_age的同时IoU匹配阈值也需要配合调整。min_hits表示一条轨迹至少要连续匹配成功多少帧才会对外输出。默认3到5比较稳。它的作用是把检测器偶发误检产生的单帧假框过滤掉。如果你要的是实时响应比如车流计数系统希望目标一出现就立刻计数min_hits可以降到1或2换取响应速度但代价是误检也会被计入。IoU阈值用于决定检测框和预测轨迹的匹配程度。默认0.3。车辆场景下如果车辆间距很近、画面中车辆密集0.3容易导致相邻车辆的检测框被误匹配此时可以降到0.25让匹配条件变苛刻。如果摄像头架设较高、目标在画面里偏小相邻帧间框的重叠率天然偏高可以提到0.4让匹配更宽松。这个参数没有绝对正确答案需要根据画面比例实测微调。4.3 实测中常见的问题现象与处置方法调参过程中有几类现象几乎每个人都会遇到。我直接列出处理思路方便对照排查。现象一同一辆车的ID在画面里反复跳变。这是最常见的。首先检查检测器输出的框是否抖动严重如果同目标在相邻帧里框的大小和位置忽大忽小说明置信度阈值偏低或者NMS阈值偏高产生了不稳定检测框。先把conf_thres往上提0.05到0.1看看框是否稳定下来。如果框稳定了ID还是跳再调大max_age。现象二行人站在画面边缘不动却每隔几帧被分配一个新ID。这是因为SORT只靠位置和速度关联行人静止时卡尔曼滤波预测的位置和实际位置高度重合理论上应该很稳但如果检测器偶尔漏检并且max_age太小轨迹被删除后行人再被检测到时就成了新目标。处理方法把max_age调大同时调低行人置信度阈值减少漏检。现象三两个行人擦肩而过之后ID互换了。这是SORT的经典短板。两个目标重叠时检测框高度重合匈牙利算法只能靠IoU判断极容易把A的轨迹关联到B的检测框上。单纯调参很难根治需要引入外观特征DeepSORT方案这个在后面的扩展部分细说。现象四车辆和行人效果无法兼顾。如果检测器和追踪器用同一套阈值经常会发现车辆效果很好但行人丢失严重或者行人稳定但车辆误检太多。解决办法就是前面提到的按类别分开设置置信度阈值这是成本最低的改进。4.4 性能瓶颈分析帧率上不去的几个原因如果跑起来发现帧率很低先别急着换设备。整个系统的瓶颈几乎都在检测器SORT本身的卡尔曼滤波和匈牙利匹配对算力的消耗可以忽略不计。所以帧率上不去的优化方向非常明确压缩检测时间。第一个可调参数是输入尺寸。YOLOv5默认输入640×640如果改成480×480或者更小推理速度会明显提升代价是小目标检测能力下降。车辆和行人的目标尺寸足够大时适当降低输入尺寸对精度影响不显著。第二个是启用半精度推理YOLOv5的halfTrue选项可以让支持FP16的GPU推理速度大幅提升。第三个是检查显存占用如果batch size设置过大导致显存溢出反而会因为显存交换拖慢速度这种情况下减小batch反而更快。还有一个实用技巧是跳帧追踪。对固定摄像头场景每一帧的目标位移其实很小可以每隔一帧才调用检测器中间那帧用SORT的预测结果顶替。这样检测耗时直接减半追踪效果在一两帧的尺度内几乎无损。不过这个技巧需要修改主循环逻辑建议在基础链路跑通、确认追踪效果正常之后再尝试否则排查问题时很难分清是检测的问题还是跳帧策略的问题。5. 这套系统的适用边界与后续扩展思路5.1 它适合哪些场景不适合哪些场景任何技术方案都有它的适用范围。YOLOv5SORT组合最适合的是摄像头固定不动、目标密度中等、目标大部分时间完整可见、光照条件正常的交通监控和园区安防场景。在这些条件下这套系统能够提供稳定的ID输出和连续轨迹足以支撑车流量统计、行人路径分析等基础业务。但有几个场景我建议直接考虑换方案。第一是密集人群场景大量行人相互遮挡、频繁交错SORT仅靠位置信息做关联会大量ID切换效果会很差这种情况更适合ByteTrack或OC-SORT这类对低置信度框更友好的算法。第二是移动摄像头或视角快速变化的场景卡尔曼滤波的恒速模型假设在相机自身运动时会崩坏。第三是夜间或强逆光场景检测器本身召回率大幅下降追踪效果自然无从谈起。第四是无人机俯拍视角目标在画面中极小且运动模式复杂SORT也会力不从心。这套系统的另一个边界是它不做重识别。SORT只能知道“这个目标在连续帧里是同一个”无法回答“这个目标5分钟前是不是出现过”。如果你需要跨时间段、跨摄像头识别同一个行人或车辆需要升级到Re-ID体系。5.2 从SORT到DeepSORT外观特征如何补足运动模型的短板很多跑完这套源码的人下一个自然的疑问是怎么减少ID切换最直接的升级路径就是把它变成DeepSORT。DeepSORT在SORT的基础上增加了一个分支每个检测框裁剪出目标区域送入一个Re-ID特征提取网络得到一个外观特征向量已有的每条轨迹也维护一个外观特征历史库。匹配时运动匹配马氏距离和外观匹配余弦相似度加权综合再用级联匹配策略优先匹配最近被频繁观测的轨迹。这样即使两个目标位置重叠只要外观特征差异明显就不会轻易互换ID。代价也是很明确的需要额外加载一个Re-ID模型推理时间增加同时需要足够好的特征提取能力。实际项目中如果你不是做跨镜追踪目标又主要是车辆我的建议是先把外观特征用轻量的方式补充进匹配代价矩阵不一定上完整的DeepSORT。比如给每个轨迹缓存最近N帧的检测框直方图特征匹配时在IoU基础上加一个颜色直方图相似度惩罚项ID切换就能明显减少。这个轻量改造实现简单也不需要额外训练模型适用于大多数交通场景。5.3 从检测器到业务逻辑往真实项目落地的下一步当检测和追踪链路稳定之后系统才有资格承载业务逻辑。这里列举几个典型的落地方向。车辆计数与流量统计在一个固定断面设置虚拟检测线当某条轨迹的中心点穿过检测线时记录该轨迹的ID若ID未重复则计数值加一。这个方案的关键是轨迹方向和过线判定的鲁棒性。区域入侵与越界检测在画面中标注禁止区域检测目标中心点是否进入该区域配合轨迹预测还可以在目标即将进入时发出预警。车辆逆行检测利用轨迹的位移方向与预设车道方向做比对方向逆反即触发告警。跑模型训练自己的数据如果目标场景是园区内部特定车辆或特定类别的行人比如区分工作人员和访客可以在标注数据集上微调YOLOv5。训练数据需要整理成YOLO格式的txt标注类别ID和检测阶段的类别过滤逻辑保持一致在官方训练脚本里指定数据配置文件和预训练权重即可。部署到边缘设备如果想把这套系统部署到Jetson、RK3568这类嵌入式平台需要把YOLOv5导出为ONNX再通过TensorRT或RKNN工具转换并做INT8量化。YOLOv5s量化后单帧推理时间可以压到几十毫秒量级SORT部分在CPU上跑也绰绰有余。这套源码的检测追踪分离设计让这种部署迁移的适配成本大幅降低。最后说一点我自己的感受。这套源码的价值在于它把“检测”和“追踪”之间的缝隙补齐了而这恰恰是很多教程不会教的部分。单帧检测是入门多目标追踪是工程从前者跨到后者你会开始认真思考坐标转换、匹配策略、参数边界这些教科书里讲得少但实战中躲不开的问题。建议你先拿这套源码跑通不要一上来就想着换算法。把整个链路跑顺之后再去替换检测器或者升级追踪器你才会真正理解每一步设计的意义。如果调试过程中遇到奇怪的现象优先怀疑坐标转换——多目标追踪里至少一半的bug出在框坐标格式不统一上。本文还有配套的精品资源点击获取