简介:这是一套面向计算机视觉初学者与课程设计者的实战型目标检测与多目标追踪系统源码,基于PyTorch框架整合YOLOv5目标检测模型与SORT算法,实现对车辆、行人等动态目标的实时识别与ID连续追踪。资源适用于本科毕业设计、人工智能课程大作业及智能交通方向实践项目,无需额外调参或代码修改即可直接运行。压缩包共101个文件,含42个核心Python脚本(涵盖数据加载、模型推理、轨迹关联与可视化)、51个编译后pyc文件、2个配置yaml(模型结构与超参)、1个预训练权重pt文件、1张示例测试图及README.md说明文档,整体体积79.92MB,结构清晰、模块解耦明确。已有1549人学习下载,配套完整工程目录与通用接口封装,便于理解YOLOv5后处理逻辑、卡尔曼滤波预测机制及匈牙利算法匹配流程,是掌握端到端多目标追踪Pipeline的优质入门范例。 上周有个读者发私信说,他做交通监控相关的小项目时,用YOLOv5检测已经跑通了,每一帧都能把车辆和行人框出来,但视频一拖进播放器就露馅——同一个行人,在连续帧里被当成好几个新目标,ID一直跳,统计数目的结果完全没法看。这个问题我太熟了,刚接触多目标追踪时同样被卡在这里。你需要的不是更好的检测器,而是一个能把这些检测框串成轨迹的追踪器。这篇要拆解的,就是基于PyTorch实现的YOLOv5+SORT车辆行人目标识别及追踪系统源码,一套把检测和追踪打通、让每个目标从进画面到出画面都保持稳定ID的完整方案。
这套源码解决的是一个非常具体的现实问题:视频里的目标识别不只是在单帧里画框,而是要回答“这个框是谁”“它从哪来、到哪去”。放在交通场景里,就是能够跟踪某辆车从画面右侧驶入、穿过路口、再从左侧离开,全程ID不变,轨迹连续。对需要车流量统计、行人路径分析、区域入侵检测的人来说,这是最基础的工程底座。如果你正在做目标检测想往追踪延伸,或者做完课程设计想进一步形成完整项目,这篇可以让你对整套系统的模块划分、参数调优和踩坑点有一个全景认识。
1. 为什么是YOLOv5+SORT:这套组合到底解决了什么问题
1.1 目标检测选型:成熟度与部署成本的权衡
先聊检测器。为什么项目里选的是YOLOv5,而不是更早的Faster R-CNN,或者更新的YOLOv8、YOLOX?
YOLOv5是基于PyTorch实现的单阶段目标检测框架,和PyTorch生态的亲和度非常高。做车辆行人识别时,常见目标的尺寸相对明确——车是中等偏大的刚性目标,行人是中等偏小但外形稳定的目标,单阶段检测器在小目标上的表现虽然不如两阶段理想,但胜在速度极快,视频流场景下需要的就是这个速度。Faster R-CNN精度高,但两阶段的推理耗时会让你在视频流里跑不动,变相增加部署成本。
那为什么不用更新的YOLOv8?我的看法是,YOLOv5的社区资料、权重文件、训练脚本、问答数量都是最多的。对一个要落地、要调试、要复现的项目来说,“遇到问题能搜到答案”本身就是巨大的工程价值。YOLOv8更适合精力充裕、想追新的场景,而不是作为一套系统的默认底座。换一个更实际的角度:你下载到的源码、配套权重、训练方案大都基于YOLOv5,这套源码选它,是为了让你第一次跑通时少踩框架层面的坑。
1.2 追踪方案选型:SORT为何能在轻量场景站住脚
多目标追踪的主流做法叫Tracking-by-Detection,先检测后关联。SORT(Simple 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×1080,letterbox预处理时做了等比缩放和灰边填充,后处理必须把检测框坐标还原到原图坐标系。SORT里卡尔曼滤波器的状态向量是[u, v, s, r]——u和v是中心点x和y,s是框面积,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 python=3.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版PyTorch:
pip 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 filterpy==1.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数据集中和交通场景直接相关的是person(0)、bicycle(1)、car(2)、motorbike(3)、bus(5)、truck(7)。在检测后处理时把其他类别过滤掉,可以大幅减少SORT收到的无用检测框,降低误关联概率。这里给出一个常用的参数速查表:
| 参数 | 建议值 | 说明 |
|---|---|---|
| conf_thres(车辆) | 0.45~0.5 | 车辆置信度高,可以稍严格 |
| conf_thres(行人) | 0.3~0.35 | 小目标/遮挡时置信度低,需要放宽 |
| NMS iou_thres | 0.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的half=True选项可以让支持FP16的GPU推理速度大幅提升。第三个是检查显存占用,如果batch size设置过大导致显存溢出,反而会因为显存交换拖慢速度,这种情况下减小batch反而更快。
还有一个实用技巧是跳帧追踪。对固定摄像头场景,每一帧的目标位移其实很小,可以每隔一帧才调用检测器,中间那帧用SORT的预测结果顶替。这样检测耗时直接减半,追踪效果在一两帧的尺度内几乎无损。不过这个技巧需要修改主循环逻辑,建议在基础链路跑通、确认追踪效果正常之后再尝试,否则排查问题时很难分清是检测的问题还是跳帧策略的问题。
5. 这套系统的适用边界与后续扩展思路
5.1 它适合哪些场景,不适合哪些场景
任何技术方案都有它的适用范围。YOLOv5+SORT组合最适合的是:摄像头固定不动、目标密度中等、目标大部分时间完整可见、光照条件正常的交通监控和园区安防场景。在这些条件下,这套系统能够提供稳定的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出在框坐标格式不统一上。
本文还有配套的精品资源,点击获取