简介:本资源为基于YOLOv5与DeepSORT的跟踪及卡尔曼滤波预测Python项目源码包,面向计算机、人工智能、通信工程、自动化等专业的在校学生、教师及企业员工,可用于毕业设计、课程设计、作业或项目初期立项演示。项目在BDD100K自动驾驶数据集上完成训练与测试,涵盖目标检测、多目标跟踪与轨迹预测的完整流程,代码均经运行验证,答辩评审平均分达96分。压缩包共180个文件,约44.47MB,以78个py源码、47个yaml配置、16个txt说明、11个yml及10个md文档为主,另含Dockerfile、权重文件与Jupyter教程,结构清晰便于按模块学习。目前已有195人学习下载。读者可获得可复现的检测跟踪方案、数据集训练配置、卡尔曼滤波预测实现及排错思路,基础较好者还可在此基础上修改扩展,实现更多功能。
1. 从一段路口监控说起:这套 YOLOv5+DeepSORT+卡尔曼滤波源码到底能干什么
如果你手头有一段固定机位的视频,想让它自动认出画面里的人或车,并且给每个目标分配一个稳定 ID、画出运动轨迹,那这套基于 YOLOv5 + DeepSORT + 卡尔曼滤波的 Python 源码包就是冲这个场景来的。它把三件事串成了一条流水线:YOLOv5 负责逐帧检测目标框,DeepSORT 负责把当前帧的框和上一帧的轨迹做关联,卡尔曼滤波夹在中间做状态预测和平滑。很多人第一次跑多目标跟踪,最直观的翻车就是 ID 频繁跳变——同一个人走着走着编号从 3 变成 17,这套源码的价值就在于把检测、关联、预测三层拆开,让你能逐层调参,而不是面对一个黑匣子干瞪眼。
它适合谁?做课程设计、毕设、安防 demo、客流统计原型的人,以及想搞懂 tracking-by-detection 这条经典路线到底怎么落地的人。不适合谁?想直接上产线级高并发、要跨镜追踪、要 ReID 大规模检索的,这套是单机原型思路,别硬套。下面我按“先跑通、再拆解、再避坑、最后进阶”的顺序,把这份资源拆开讲透。
2. 环境配置与首次跑通:从 conda 建环境到视频出框
2.1 依赖选型:为什么是 PyTorch + OpenCV 这套组合
这套源码的骨架是 Python,检测端用 YOLOv5(PyTorch 实现),跟踪端 DeepSORT 依赖 NumPy、SciPy 做卡尔曼滤波和匈牙利匹配,视频读写靠 OpenCV。选型理由很直接:YOLOv5 的工程化程度高,权重文件小、推理快,CPU 也能勉强跑;DeepSORT 是 tracking-by-detection 里最成熟的基线之一,代码可读性好,改起来不费劲。卡尔曼滤波不是单独一个库,而是嵌在 DeepSORT 的KalmanFilter类里,用 NumPy 手写的 8 维状态向量(中心点 x、y、宽高比、高度 + 各自的速度),这也是后面调参要盯的核心。
常见做法是 conda 建独立环境,避免和你机器上已有的 torch 版本打架。Python 版本建议 3.8~3.10,太新了有些老依赖轮子不全。
# 创建并激活环境,python 版本按你机器上能装到的来 conda create -n yolo_track python=3.9 -y conda activate yolo_track # 装 PyTorch,CPU 版够跑通,有卡就换成对应 CUDA 版本 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 跟踪和视频处理相关依赖 pip install opencv-python numpy scipy filterpy pip install -r requirements.txt # 源码包根目录一般会带这个文件逻辑说明:先隔离环境是血泪经验,torch 和 numpy 的版本冲突能让你排查一下午。filterpy有些 DeepSORT 实现会用到,源码里如果自带卡尔曼实现就不强依赖,装了也不冲突。参数上,--index-url指向 CPU 轮子源,有 NVIDIA 卡的换成 cu118 之类的对应源,速度差好几倍。
2.2 权重与目录结构:把模型文件放对位置
YOLOv5 的检测权重(.pt文件)和 DeepSORT 的 ReID 权重(.t7或.pb)是两套东西,别混。检测权重决定“框得准不准”,ReID 权重决定“外观特征像不像”,后者直接影响 ID 切换频率。源码包里通常有个weights/目录,把yolov5s.pt放进去;DeepSORT 的mars-small128.pb或ckpt.t7放到deep_sort/deep/checkpoint/下。
# 典型目录结构(以源码包实际为准) # project/ # ├── yolov5/ # 检测模块 # ├── deep_sort/ # 跟踪模块 # │ └── deep/checkpoint/ # ReID 权重 # ├── weights/yolov5s.pt # 检测权重 # ├── configs/deep_sort.yaml # 跟踪参数 # └── demo.py # 入口脚本 # 跑通一条视频 python demo.py --source test.mp4 --weights weights/yolov5s.pt --config configs/deep_sort.yaml逻辑说明:--source可以是视频文件、摄像头编号(0)或图片目录;--weights指向检测权重;--config是 DeepSORT 的参数文件,里面管着最大丢失帧数、匹配阈值这些关键项。第一次跑建议先用官方yolov5s.pt,别急着换自己训练的权重,先把整条链路跑通再替换变量,这是排查问题的基本纪律。
2.3 输出解读:框、ID、轨迹线分别代表什么
跑起来后画面里会出现三类东西:彩色检测框、框角上的数字 ID、以及目标身后拖的轨迹线。框来自 YOLOv5 的置信度过滤,ID 来自 DeepSORT 的轨迹管理,轨迹线是卡尔曼滤波预测位置的历史累积。如果框在但 ID 一直变,问题在关联层;如果框本身就在闪,问题在检测层。分清楚这两层,调参才有方向。
提示:首次运行如果报
No module named 'deep_sort',多半是工作目录不对,在项目根目录下执行,或者把根目录加进PYTHONPATH。
3. 拆开 DeepSORT:卡尔曼滤波预测和匈牙利匹配到底怎么配合
3.1 卡尔曼滤波的 8 维状态:预测什么、更新什么
DeepSORT 里的卡尔曼滤波用的是匀速模型,状态向量 8 维:[x, y, a, h, vx, vy, va, vh],前四个是中心点坐标、宽高比、高度,后四个是对应速度。预测阶段用状态转移矩阵把上一帧的状态推到当前帧,更新阶段用当前帧的检测框去修正预测值。它的作用不是“让框更准”,而是“在检测漏帧时给出一个合理的位置估计”,让轨迹不至于断掉。
# 简化示意,实际在 deep_sort/kalman_filter.py 里 import numpy as np class KalmanFilter: def __init__(self): # 8 维状态,4 维观测 self.ndim, self.dt = 8, 1.0 self._motion_mat = np.eye(2 * self.ndim // 2, 2 * self.ndim // 2) for i in range(self.ndim // 2): self._motion_mat[i, self.ndim // 2 + i] = self.dt # 位置 += 速度 * dt def predict(self, mean, covariance): # 状态外推:x' = F x mean = self._motion_mat @ mean # 协方差外推:P' = F P F^T + Q,Q 是过程噪声 covariance = self._motion_mat @ covariance @ self._motion_mat.T + self._std_weight_position ** 2 return mean, covariance逻辑说明:_motion_mat就是匀速模型的核心,位置项加上速度项乘以时间步。过程噪声Q越大,滤波器越“信检测”;越小,越“信预测”。源码里_std_weight_position和_std_weight_velocity这两个系数是调参入口,检测抖动大就适当调大位置噪声。
3.2 级联匹配与 IoU 匹配:为什么 ID 会跳
DeepSORT 的关联分两步:先做级联匹配(按轨迹被遮挡的时长分层,越新的轨迹优先级越高),再做 IoU 匹配兜底。级联匹配用的是外观特征余弦距离 + 马氏距离的加权和,超过max_dist阈值就判为不匹配。ID 跳变的根因通常有三个:外观特征区分度不够(ReID 权重弱)、max_dist设太松导致错配、max_age太小导致轨迹过早删除。
# configs/deep_sort.yaml 关键参数 DEEPSORT: MAX_DIST: 0.2 # 外观匹配阈值,越小越严格 MAX_IOU_DISTANCE: 0.7 # IoU 匹配阈值 MAX_AGE: 70 # 轨迹丢失多少帧后删除 N_INIT: 3 # 连续命中多少帧才确认轨迹 NN_BUDGET: 100 # 每个轨迹保留多少历史特征逻辑说明:MAX_DIST从 0.2 调到 0.3,匹配变宽松,遮挡后更容易接上,但错配风险上升;MAX_AGE调大能让被遮挡的目标“等回来”,代价是内存和误跟增加。这几个参数没有万能值,得拿你自己的视频试。
3.3 用一段视频验证跟踪稳定性
验证方法很朴素:找一段有遮挡、有交叉行走的视频,跑完看 ID 切换次数。可以加一行日志统计每个 ID 的存活帧数,存活帧数异常短的就是碎片化轨迹。
# 在跟踪循环里统计轨迹寿命 track_life = {} for frame in video: tracks = tracker.update(dets, frame) for t in tracks: tid = t.track_id track_life[tid] = track_life.get(tid, 0) + 1 # 跑完打印,寿命 < 10 帧的基本是误跟或碎片 print(sorted(track_life.items(), key=lambda x: x[1]))逻辑说明:这个统计能帮你量化“ID 跳变”到底多严重,比肉眼看靠谱。如果大量轨迹寿命只有几帧,先查检测置信度是不是太低,再查N_INIT是不是设太小。
4. 避坑与排查:这套源码最容易翻车的五个地方
4.1 现象:所有目标共用同一个 ID
原因:ReID 特征提取没生效,通常是权重路径写错或deep_sort.yaml里MODEL_PATH指向了不存在的文件,特征全返回零向量,余弦距离失效。 解决:打印特征提取的输出维度,确认不是全零;核对权重路径,.pb和.t7别搞混,TensorFlow 版和 PyTorch 版权重不通用。
4.2 现象:CPU 上跑一帧要好几秒
原因:YOLOv5 默认输入尺寸 640,CPU 推理本身就慢,再加上 DeepSORT 每帧对每个检测框都跑一次 ReID 特征提取,开销叠加。 解决:把检测输入降到 416 或 320,--img-size调小;ReID 特征可以隔帧提取或只对确认轨迹提取,牺牲一点精度换速度。
4.3 现象:视频输出没有轨迹线或画面全黑
原因:OpenCV 的VideoWriter编码器不匹配,或者宽高和输入视频不一致,写出来的文件打不开。 解决:VideoWriter的fourcc换成mp4v或XVID,宽高严格用cap.get(cv2.CAP_PROP_FRAME_WIDTH/HEIGHT)读出来的值,别手写。
4.4 现象:换自己的数据集后检测框乱飞
原因:YOLOv5 权重和你的类别不匹配,或者没改data.yaml里的nc和类别名,模型把新类别硬套到旧类别上。 解决:用yolov5/train.py在自己的数据集上微调,改data.yaml的nc、names和路径,训练完把best.pt替换进weights/。
4.5 现象:卡尔曼预测的框明显滞后于目标
原因:过程噪声Q设太小,滤波器过度信任匀速假设,目标加速或转向时预测跟不上。 解决:调大_std_weight_velocity,让滤波器更信任观测;或者对高机动目标换成交互式多模型(IMM),不过那就不在这套源码范围内了。
5. 进阶:把跟踪结果接进业务,以及我踩过的一个坑
跑通只是起点,真正要用起来得把跟踪输出接进业务逻辑。比如做越线计数,就在画面里画一条虚拟线,判断轨迹中心点前后两帧是否跨线;做停留时长统计,就记录每个 ID 首次出现和最后出现的帧号差。这些都不需要改跟踪核心,在demo.py的循环里加判断即可。
# 越线计数示意 line_y = 300 crossed = set() for t in tracks: tid = t.track_id cx, cy = t.to_tlbr()[:2] # 取中心点 if tid not in crossed and abs(cy - line_y) < 5: crossed.add(tid) print(f"ID {tid} 越线")逻辑说明:to_tlbr()返回左上右下坐标,取中心点判断和虚拟线的距离。阈值 5 像素是经验值,目标快就调大,慢就调小。这个逻辑简单但够用,客流统计原型基本靠它。
进阶调优上,如果 ID 切换还是多,可以换更强的 ReID 模型(比如 OSNet),或者把检测和跟踪的帧率解耦——检测每两帧跑一次,跟踪每帧都跑,用卡尔曼预测补中间帧。这个思路在算力紧张时特别管用。
说个我自己的教训。有次赶一个演示,我图省事直接用了默认MAX_AGE=30,结果现场视频里目标被柱子挡了两秒,ID 直接断成两个,演示当场翻车。从那以后我每次拿到新场景视频,都强制先跑一遍参数扫描:MAX_AGE从 30 到 90 各跑一次,看 ID 切换次数曲线,选拐点。这个习惯帮我省了无数次返工。希望这套源码和上面的拆解,能帮你少走点弯路,把跟踪这条链路真正跑顺。
本文还有配套的精品资源,点击获取