简介本资源为毕业设计《基于yolov5deepsort实现车辆目标跟踪与应用》的完整Python项目源码与文档说明面向计算机、人工智能、通信工程、自动化等专业的在校学生、教师及企业员工适合作为毕设、课程设计、作业或项目初期立项演示也适合具备一定基础的学习者进阶参考。项目围绕车辆目标跟踪展开涵盖摄像头视频播放、目标检测与多目标跟踪、车流量统计、车辆违停检测、车辆逆行检测以及信息栏控制等功能模块代码均经过测试运行成功。压缩包共134个文件约43.71MB以64个py源码文件、30个yaml配置、9个png与2个jpg图像、7个xml标注、6个sh脚本及3个md文档为主另含Dockerfile、ipynb教程与UI文件结构完整便于按模块学习。目前已有108人学习下载。下载后可先阅读README.md在理解检测与跟踪流程的基础上修改代码实现自定义功能仅供学习参考切勿用于商业用途。1. 从一段路口视频说起yolov5deepsort 车辆跟踪到底在做什么假设你手里有一段城市路口的高清视频画面里几十辆车来回穿梭有的被前车挡住有的在画面边缘进进出出。现在要你统计每辆车在画面里停留了多久、走了哪条轨迹、有没有逆行——靠人眼盯十分钟就崩溃。基于 yolov5deepsort 实现车辆目标跟踪要解决的正是这件事yolov5 负责在每一帧里把车框出来deepsort 负责把这一帧的框和上一帧的框对应起来给每辆车分配一个稳定的 ID让它在整个视频里“认得出是同一辆”。这套组合是车辆目标跟踪方向里最经典、最容易复现的入门方案也是很多毕业设计选它的原因检测有成熟权重、跟踪有开源实现、Python 生态齐全一台带独显的笔记本就能跑通。适合谁适合刚接触计算机视觉、需要一套能跑通、能改、能写进论文的完整链路的人。但“能跑通”和“跑得稳”之间隔着不少坑下面按落地顺序拆开讲。2. 检测与跟踪的分工yolov5 出框deepsort 认车2.1 为什么不是“一个模型全干完”很多人第一反应是既然 yolov5 已经能检测车辆为什么还要 deepsort因为检测模型是“无记忆”的——它只看当前这一帧输出一堆框和类别帧与帧之间没有任何关联。第 1 帧里那辆白色轿车是框 A第 2 帧里它移动了 20 像素变成框 B检测模型不知道 A 和 B 是同一辆车。跟踪要补的就是这个“跨帧身份”。deepsort 的思路是对每个检测框提取一个外观特征向量ReID 特征同时用卡尔曼滤波预测目标下一帧可能出现的位置再把“外观相似度”和“位置匹配度”结合起来做匈牙利匹配。匹配上的框继承旧 ID没匹配上的框新建 ID连续多帧没匹配上的 ID 就删掉。所以它的核心价值是在遮挡、短暂丢失后还能把 ID 接回来这正是车辆跟踪里最需要的。常见做法是yolov5 用官方或自训练的车辆检测权重deepsort 用开源实现里的默认 ReID 模型。两者通过一个“检测结果 → 跟踪器 update”的接口串起来中间的数据格式是[x1, y1, x2, y2, conf, cls]。2.2 最小可跑通的目录与依赖先别急着改代码把环境跑通是第一关。我一般会建一个干净的虚拟环境避免和系统里的其他包打架。# 创建并激活虚拟环境Python 3.8 兼容性最好 python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate # 安装核心依赖torch 按自己 CUDA 版本去官网选对应命令 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install opencv-python numpy scipy lap这里lap是做匈牙利匹配的线性分配库deepsort 依赖它scipy用于卡尔曼滤波里的矩阵运算。torch 的安装命令一定要按自己显卡的 CUDA 版本选装错了会出现“能 import 但一跑就报 CUDA error”的玄学问题。目录上我习惯这样分project/ ├── yolov5/ # 检测模型与推理代码 ├── deep_sort/ # 跟踪器 ├── weights/ # 检测权重 ├── configs/ # 跟踪参数 ├── videos/ # 输入视频 └── main.py # 串联入口2.3 把检测结果喂给跟踪器的最小代码下面这段是串联的核心逻辑去掉花哨封装只保留“读帧 → 检测 → 跟踪 → 画框”四步。import cv2 import torch from deep_sort import DeepSort # 加载 yolov5 模型weights 换成自己的车辆检测权重 model torch.hub.load(ultralytics/yolov5, custom, pathweights/vehicle.pt, device0) model.conf 0.4 # 置信度阈值低于此值的框直接丢 model.iou 0.45 # NMS 的 IoU 阈值控制重叠框合并 # 初始化 deepsortmax_dist 控制外观匹配的宽松程度 tracker DeepSort(model_pathdeep_sort/deep/checkpoint/ckpt.t7, max_dist0.2, max_iou_distance0.7, max_age70) cap cv2.VideoCapture(videos/road.mp4) while True: ret, frame cap.read() if not ret: break # yolov5 推理结果里 xyxy 是框conf 是置信度cls 是类别 results model(frame) dets results.xyxy[0].cpu().numpy() # 只保留车辆类别假设 2 是 car5 是 bus7 是 truck dets dets[[int(d[5]) in (2, 5, 7) for d in dets]] if len(dets) 0: bbox_xywh dets[:, :4] # deepsort 要的是 [cx, cy, w, h] 格式这里做转换 bbox_xywh[:, 2] - bbox_xywh[:, 0] bbox_xywh[:, 3] - bbox_xywh[:, 1] bbox_xywh[:, 0] bbox_xywh[:, 2] / 2 bbox_xywh[:, 1] bbox_xywh[:, 3] / 2 confs dets[:, 4] # 更新跟踪器返回带 track_id 的框 outputs tracker.update(bbox_xywh, confs, frame) for out in outputs: x1, y1, x2, y2, tid out cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, fID {int(tid)}, (int(x1), int(y1) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(track, frame) if cv2.waitKey(1) 0xFF 27: break cap.release() cv2.destroyAllWindows()逻辑说明yolov5 的results.xyxy[0]输出的是[x1, y1, x2, y2, conf, cls]而 deepsort 的update需要[cx, cy, w, h]和对应的置信度所以中间要做一次坐标转换这一步漏了会导致框整体偏移。参数上model.conf调低会引入更多误检deepsort 就会频繁新建 ID调高会漏检跟踪容易断。max_age是“一个 ID 最多允许丢失多少帧”车辆被遮挡时这个值太小会直接换 ID太大又会让已经离开画面的车“阴魂不散”一般 30 到 70 之间试。3. 参数调优让 ID 不跳、不丢、不重复3.1 三个最影响跟踪效果的参数deepsort 里真正需要动手调的不多但下面三个几乎决定了成败参数作用调大后果调小后果建议起点max_dist外观特征匹配的最大余弦距离容易把不同车匹配成同一 ID同一辆车频繁换 ID0.2max_iou_distance位置匹配的 IoU 阈值遮挡后容易错配正常移动也匹配不上0.7max_ageID 允许丢失的帧数离开画面的车残留遮挡即换 ID50这三个参数没有万能值和视频帧率、车辆速度、遮挡频率都有关。我的习惯是先用默认值跑一遍把出现问题的片段截出来再针对性调一个参数不要一次改三个。3.2 检测质量才是跟踪的上限血泪经验deepsort 跟踪效果差八成不是跟踪器的问题而是检测框在抖。yolov5 如果每帧框的位置跳来跳去卡尔曼滤波的预测就会失准外观特征也会因为框内内容变化而漂移。所以调跟踪之前先把检测稳住# 推理时开启 TTA 和更高分辨率能明显减少框抖动 results model(frame, size1280, augmentTrue) # 对同一目标连续帧的框做简单平滑抑制抖动size1280比默认的 640 能检出更远的小车但速度会下降实时场景要权衡。augmentTrue是测试时增强精度略升、速度略降。如果做的是离线分析这两个都开如果是实时建议只提分辨率。3.3 用配置文件管理参数而不是硬编码参数一多散在代码里就是灾难。我一般抽一个 yaml# configs/track.yaml detect: conf: 0.4 iou: 0.45 size: 1280 track: max_dist: 0.2 max_iou_distance: 0.7 max_age: 50 n_init: 3 # 连续命中多少帧才确认一个新 IDn_init这个参数容易被忽略它要求新目标连续被检测到 3 帧才正式分配 ID能有效过滤掉一闪而过的误检。代价是车辆刚进入画面时会有两三帧没有 ID属于正常现象。4. 避坑与排查那些让 ID 乱跳的真实原因4.1 现象同一辆车 ID 频繁变化原因通常是外观特征匹配失败。车辆颜色相近、光照突变、或者检测框抖动导致框内像素变化大都会让 ReID 特征对不上。解决先把max_dist从 0.2 放宽到 0.3 试试同时检查检测框是否稳定如果画面里同色车多可以考虑换一个更强的 ReID 模型或者提高检测分辨率让框更贴合车身。4.2 现象车辆被遮挡后 ID 直接换新这是max_age太小或卡尔曼预测不准。遮挡期间检测不到目标跟踪器靠预测维持如果max_age只有 10遮挡 15 帧后 ID 就被删了。解决把max_age提到 50 以上同时确认max_iou_distance不要太小否则遮挡结束后位置偏移稍大就匹配不上。4.3 现象画面边缘出现“幽灵 ID”车辆已经驶出画面但 ID 还挂在边缘不动。原因是max_age太大跟踪器一直在等它回来。解决适当降低max_age或者在代码里加一个判断——如果某个 ID 的框连续多帧贴在画面边界且没有检测支持主动删除。4.4 现象一跑就报 lap 相关错误lap库在部分 Python 版本上编译失败报Microsoft Visual C 14.0 is required之类。解决Windows 上装 Visual C Build Tools或者直接用pip install lap --only-binary :all:装预编译版本实在不行换scipy.optimize.linear_sum_assignment自己替换匹配部分。4.5 现象GPU 显存够但速度还是很慢常见原因是每帧都在做 ReID 特征提取而 ReID 模型默认跑在 CPU 上。解决确认 deepsort 里的特征提取网络也.to(cuda)并且把检测和跟踪的 batch 处理打开。另一个隐藏坑是 OpenCV 的cv2.imshow在高分辨率下会拖慢循环离线分析时直接写视频文件别开窗口。5. 从能跑到好用轨迹导出与效果验证的一个技巧跑通之后真正让这套方案“有价值”的一步是把轨迹落成结构化数据而不是只停留在画面上画框。我一般会在跟踪循环里顺手把每个 ID 的轨迹点存下来import json tracks {} # {track_id: [[frame_idx, cx, cy], ...]} # 在画框循环里追加 for out in outputs: x1, y1, x2, y2, tid out cx, cy (x1 x2) / 2, (y1 y2) / 2 tracks.setdefault(int(tid), []).append([frame_idx, cx, cy]) # 循环结束后导出 with open(tracks.json, w) as f: json.dump(tracks, f)有了这份 JSON你就能做很多“论文里能写、答辩时能演示”的事统计每辆车的平均速度相邻帧位移除以帧间隔、判断是否逆行轨迹方向与车道方向夹角、画热力图看车流集中在哪。验证跟踪效果时我常用的一个笨办法但很有效挑一段有遮挡的视频人工数出实际车辆数再对比程序输出的 ID 总数。如果 ID 总数明显多于实际车辆数说明 ID 切换频繁回去调max_dist和max_age如果明显少说明漏检或过早合并检查检测阈值。还有一个容易被忽略的点帧率。如果视频是 30fps但你的处理速度只有 5fps卡尔曼滤波的预测步长和实际时间就对不上跟踪会明显发飘。离线处理时问题不大实时场景一定要么降分辨率提速度要么在跟踪器里按实际时间间隔更新。我自己踩得最深的一次坑是拿一个训练集里没见过的车型去跑检测框时有时无deepsort 的 ID 像抽奖一样乱跳调了两天参数都没用最后发现是检测权重根本不认识那类车。从那以后我养成了一个习惯跟踪出问题先单独把检测结果可视化一遍确认框本身是对的再去动跟踪参数。这个顺序能省掉大量无效调试。希望帮到你。本文还有配套的精品资源点击获取