
简介本资源为毕业设计《基于yolov5deepsort实现车辆目标跟踪与应用》的完整Python项目源码与文档说明面向计算机、人工智能、通信工程、自动化等专业的在校学生、教师及企业员工适合作为毕设、课程设计、作业或项目初期立项演示也适合具备一定基础的学习者进阶参考。项目围绕车辆目标跟踪展开涵盖摄像头视频播放、目标检测与多目标跟踪、车流量统计、车辆违停检测、车辆逆行检测以及信息栏控制等功能模块代码均经过测试运行成功。压缩包共134个文件约43.71MB以64个py源码文件为核心辅以30个yaml配置、9个png与2个jpg示意图、7个xml标注、6个sh脚本、3个md说明文档以及Dockerfile、license、ipynb教程等目录结构清晰便于按模块检索与二次开发。目前已有108人学习下载下载后建议先阅读README.md仅供学习参考切勿用于商业用途。1. 从一份能跑通的 YOLOv5DeepSORT 毕设包说起车辆跟踪到底难在哪如果你正在做车辆目标跟踪相关的毕业设计大概率已经翻过不少 YOLOv5 加 DeepSORT 的教程但真正动手时还是会卡在几个地方环境装不上、权重加载报错、跟踪 ID 频繁跳变、统计逻辑对不上实际车流。这份《基于 YOLOv5DeepSORT 实现车辆目标跟踪与应用》的 Python 项目源码包解决的就是从检测到跟踪再到业务应用这条完整链路的落地问题。它不是单纯给你一个检测脚本而是把摄像头视频播放、多目标跟踪、车流量统计、违停检测、逆行检测、信息栏控制这几个功能串成了一个可运行的系统。适合计算机、人工智能、通信、自动化等专业的在校学生做毕设或课程设计也适合刚接触多目标跟踪、想找一个能跑通的基线项目来改的开发者。下面我按实际拆包和复现的顺序把这份资源的技术选型、运行步骤、参数含义和容易翻车的地方讲清楚。2. 拆开这个包YOLOv5 检测与 DeepSORT 跟踪是怎么串起来的2.1 为什么是 YOLOv5 DeepSORT 这个组合车辆目标跟踪这个任务拆开看就两件事每一帧里车在哪以及同一辆车在连续帧里怎么对应上。YOLOv5 负责前者DeepSORT 负责后者。YOLOv5 在速度和精度之间平衡得比较好模型体积小用 PyTorch 加载方便对毕设这种算力有限、又要实时出效果的场景很合适。DeepSORT 的核心是在 SORT 的基础上加了外观特征提取网络用余弦距离做级联匹配能在车辆被短暂遮挡后重新找回同一个 ID这对车流量统计和违停判断很关键——ID 一跳变统计就全乱了。这个包里检测和跟踪是解耦的YOLOv5 输出每帧的检测框DeepSORT 拿这些框去做预测和匹配。这种结构的好处是你想换检测器或者换跟踪器都相对独立不用把整个系统推倒重来。常见做法是把检测置信度阈值设在 0.4 到 0.5 之间太低会引入大量误检DeepSORT 的匹配压力陡增太高又会漏掉远处的小目标车辆导致跟踪断链。2.2 目录结构和关键文件说明拿到包之后先别急着跑花两分钟把结构看清楚后面排错会快很多。从文件清单来看核心内容大致是这样分布的文件/目录作用Yolov5-Deepsort-main.iml项目配置文件标记项目根目录和模块类型tutorial.ipynb交互式教程通常包含分步运行和可视化演示Dockerfile容器化构建脚本用于统一环境YOLOv5模型.jpg/train.jpg模型结构或训练过程示意图辅助理解LICENSE开源协议注意商用限制.gitignore/.gitkeep版本控制忽略规则和占位文件tutorial.ipynb是这个包里最值得先打开的文件它一般会把视频读取、模型加载、跟踪循环、结果绘制这几步拆成独立的单元格你可以一格一格跑看每一步的输出对不对。Dockerfile的存在说明作者考虑过环境一致性问题如果你本地环境怎么都配不好用容器跑是一个兜底方案。2.3 从视频流到跟踪结果的核心代码逻辑下面这段是这类项目里最典型的跟踪主循环结构我按实际项目里常见的写法整理出来你可以对照包里的代码看import cv2 import torch from models.experimental import attempt_load from utils.datasets import LoadStreams, LoadImages from deep_sort.utils.parser import get_config from deep_sort.deep_sort import DeepSort # 加载 YOLOv5 模型weights 替换成包里的权重文件路径 model attempt_load(weights/yolov5s.pt, map_locationcpu) model.eval() # 初始化 DeepSORT配置文件里指定特征提取网络和匹配阈值 cfg get_config() cfg.merge_from_file(deep_sort/configs/deep_sort.yaml) deepsort DeepSort( cfg.DEEPSORT.REID_CKPT, max_distcfg.DEEPSORT.MAX_DIST, # 外观特征最大余弦距离 min_confidencecfg.DEEPSORT.MIN_CONFIDENCE, # 检测框最低置信度 nms_max_overlapcfg.DEEPSORT.NMS_MAX_OVERLAP, # NMS 重叠阈值 max_iou_distancecfg.DEEPSORT.MAX_IOU_DISTANCE, max_agecfg.DEEPSORT.MAX_AGE, # 轨迹丢失后保留帧数 n_initcfg.DEEPSORT.N_INIT, # 连续命中多少次才确认轨迹 nn_budgetcfg.DEEPSORT.NN_BUDGET, # 外观特征库容量 use_cudaTrue ) cap cv2.VideoCapture(test_video.mp4) while cap.isOpened(): ret, frame cap.read() if not ret: break # YOLOv5 推理输出检测框 img letterbox(frame, new_shape640)[0] img img[:, :, ::-1].transpose(2, 0, 1) img torch.from_numpy(img).float() / 255.0 pred model(img.unsqueeze(0))[0] detections non_max_suppression(pred, conf_thres0.5, iou_thres0.45)[0] # 把检测框送入 DeepSORT 做匹配 if detections is not None: bbox_xywh detections[:, :4].cpu().numpy() confs detections[:, 4].cpu().numpy() outputs deepsort.update(bbox_xywh, confs, frame) for track in outputs: x1, y1, x2, y2, track_id track cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, fID:{int(track_id)}, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(Tracking, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码里几个参数直接决定跟踪效果。max_dist控制外观匹配的宽松程度调大能容忍更多外观变化但容易串 ID调小则容易丢目标一般从 0.2 开始试。max_age是轨迹丢失后保留多少帧再删除车辆被遮挡时这个值太小会导致 ID 切换太大又会让已经离开画面的车残留轨迹30 帧左右是比较常见的起点。n_init是连续命中多少次才确认一条新轨迹设成 3 可以过滤掉大部分一闪而过的误检。nn_budget是每个轨迹保留的外观特征数量越大越准但越吃内存毕设场景 100 够用。2.4 车流量统计与违停、逆行的判定逻辑跟踪跑通之后业务功能才有意义。车流量统计的常见做法是在画面里画一条虚拟检测线当某个 track_id 的检测框中心点从线的一侧移动到另一侧时计数加一。这里有个坑如果车辆在检测线附近来回抖动中心点反复穿越计数会重复累加。解决办法是给每个 track_id 记录一个已计数标记或者要求连续两帧都在另一侧才确认。违停检测的逻辑是给每个 track_id 维护一个停留计时器如果检测框中心点在某个区域内停留超过设定时长比如 10 秒就标记为违停。逆行检测则需要预先定义车道的方向向量计算车辆运动方向与车道方向的夹角超过阈值就判定逆行。这两个功能都依赖跟踪 ID 的稳定性ID 一跳变计时器和方向计算全部重置所以前面调 DeepSORT 参数时不要只盯着检测框好不好看要盯着 ID 稳不稳。3. 把项目跑起来环境配置、权重加载与视频推理的完整步骤3.1 Python 环境与依赖安装这个项目基于 PyTorch对 Python 版本有要求。常见做法是用 Python 3.8 或 3.9太新的版本有些依赖包还没跟上。先确认你的 Python 版本python --version # 如果低于 3.8建议用 conda 建一个独立环境 conda create -n yolov5_deepsort python3.8 conda activate yolov5_deepsort然后安装核心依赖。这个包大概率会带一个requirements.txt如果有就直接装pip install -r requirements.txt如果没有按这个项目类型手动装这几个是跑不掉的pip install torch torchvision opencv-python numpy scipy pip install easydict pyyaml tqdm matplotlibtorch的安装要注意和 CUDA 版本匹配如果你有 NVIDIA 显卡并且装了 CUDA去 PyTorch 官网选对应版本没有显卡就用 CPU 版推理会慢但功能不受影响。opencv-python是视频读取和绘制的核心easydict和pyyaml是 DeepSORT 配置文件解析用的缺一个都会在导入阶段报错。3.2 权重文件与配置文件检查YOLOv5 的权重文件通常是.pt格式和 DeepSORT 的 ReID 模型权重.t7或.pt需要确认路径正确。打开deep_sort/configs/deep_sort.yaml检查REID_CKPT指向的文件是否存在。常见报错是FileNotFoundError或者RuntimeError: Error(s) in loading state_dict前者是路径不对后者多半是权重文件和网络结构不匹配。YOLOv5 这边如果你用的是包内自带的权重确认attempt_load里的路径和实际文件名一致。如果打算用自己训练的模型注意类别数要和代码里的nc参数对上车辆检测一般是 1 类car或者多类car, bus, truck类别数不对会在后处理时索引越界。3.3 用 tutorial.ipynb 分步验证环境配好之后先打开tutorial.ipynb。这种教程文件的价值在于它把整个流程拆成了可独立运行的单元格你可以逐格执行定位问题出在哪一步。一般顺序是导入依赖 → 加载模型 → 读取视频 → 单帧检测测试 → 完整跟踪循环 → 结果保存。如果某个单元格报错先看错误类型。ModuleNotFoundError是依赖没装全CUDA out of memory是显存不够把use_cuda改成False或者换小一号的模型比如从yolov5m换到yolov5s。AssertionError多半是输入尺寸或通道数不对检查letterbox的new_shape参数和模型期望的输入是否一致。3.4 命令行推理与结果输出如果不想用 notebook也可以直接跑推理脚本。这类项目一般会提供一个detect.py或者track.pypython track.py --source test_video.mp4 --weights weights/yolov5s.pt --conf 0.5 --save-txt --save-vid--source可以是视频文件路径也可以是0表示调用摄像头。--conf是检测置信度阈值--save-txt会把每帧的跟踪结果存成文本方便后续做统计。--save-vid输出带标注的视频。如果你要做车流量统计先跑一遍保存结果再写一个后处理脚本读文本文件做计数比在推理循环里直接统计更容易调试。提示第一次跑先用一段短一点的视频比如 10 到 20 秒确认整个流程通了再换长视频。长视频跑一半报错前面的时间就白费了。4. 避坑与排查ID 跳变、显存溢出、统计重复这些坑怎么填4.1 跟踪 ID 频繁跳变现象同一辆车在连续几帧里 ID 从 1 变成 5 又变成 12统计结果完全不可用。原因DeepSORT 的外观特征匹配不够稳定常见诱因是检测框抖动太大、max_dist设得过大导致错误匹配、或者max_age太小导致轨迹被提前删除。解决先把max_dist从默认值往下调试 0.15 到 0.2把max_age提到 50 到 70给遮挡留更多恢复时间检查 YOLOv5 的conf_thres如果设得太低比如 0.2大量低质量检测框会干扰匹配提到 0.45 到 0.5 通常能明显改善。4.2 显存溢出导致推理中断现象跑了几十帧之后报CUDA out of memory程序崩溃。原因DeepSORT 的特征库nn_budget设得太大或者视频分辨率太高导致 YOLOv5 输入尺寸过大。解决把nn_budget从默认的 100 降到 50在 YOLOv5 预处理阶段把new_shape从 1280 降到 640如果还不行在推理循环里加torch.cuda.empty_cache()或者直接切到 CPU 模式跑。CPU 模式慢但不会崩适合调试阶段用。4.3 车流量统计重复计数现象一辆车过去计数器加了 2 甚至 3。原因车辆在检测线附近速度慢或者有轻微倒车中心点反复穿越检测线。解决给每个 track_id 加一个状态标记只有从未计数状态变为已计数状态时才加一或者要求中心点连续 3 帧都在检测线另一侧才确认穿越。后一种更稳但会引入几帧的延迟对实时性要求不高的毕设场景完全可接受。4.4 违停检测误报现象正常行驶的车辆被标记为违停。原因停留计时器没有排除车辆在路口等红灯的情况或者检测区域画得太大把正常车道也包进去了。解决把违停检测区域缩小到路边或禁停区不要覆盖整个画面给计时器加一个速度判断如果车辆中心点位移超过阈值就重置计时器只有低速或静止才累加停留时间。4.5 视频读取失败或帧率为零现象cv2.VideoCapture返回False或者读出来的帧是空的。原因视频编码格式不被 OpenCV 支持或者文件路径里有中文。解决先用ffmpeg把视频转成 H.264 编码的 mp4路径全部用英文如果是在 Linux 服务器上跑确认装了libopencv-dev和对应的视频解码库。这个坑很隐蔽因为文件明明存在但 OpenCV 就是读不了。5. 进阶用法换模型、调参和把跟踪结果接进自己的业务逻辑5.1 替换成自己训练的 YOLOv5 模型如果你想检测特定车型或者加一些自定义类别可以自己标注数据训练 YOLOv5然后把导出的.pt替换掉原来的权重。关键点是类别数和类别名称要同步改。在detect.py或track.py里找到解析检测结果的地方把names字典改成你自己的类别映射。如果只检测车辆nc1类别名写[vehicle]就行。训练自己的数据集时注意标注格式用 YOLO 的 txt 格式每行是class_id x_center y_center width height坐标要归一化到 0 到 1。5.2 DeepSORT 参数调优的实操顺序调参不要一次改一堆按影响从大到小来。先调conf_thres这个决定哪些检测框进入跟踪器影响最大。然后调max_dist和max_iou_distance这两个控制匹配的宽松度。再调max_age和n_init这两个影响轨迹的生命周期。最后调nn_budget这个主要影响内存和长时跟踪的稳定性。每次只改一个参数跑同一段测试视频对比 ID 切换次数和统计结果。我一般会准备一段包含遮挡、并线、停车等场景的测试视频专门用来验证参数改动有没有引入新的问题。5.3 把跟踪结果接入业务逻辑的通用模式不管你是做车流量统计、违停检测还是逆行判断通用的模式是维护一个track_id到状态字典的映射每帧更新状态然后根据状态做业务判断。下面是一个简化的状态管理框架from collections import defaultdict class TrackState: def __init__(self): self.first_seen None # 首次出现时间 self.last_seen None # 最后出现时间 self.counted False # 是否已计数 self.positions [] # 历史位置用于方向判断 self.stationary_frames 0 # 静止帧计数 track_states defaultdict(TrackState) def update_business_logic(track_id, center_x, center_y, timestamp): state track_states[track_id] if state.first_seen is None: state.first_seen timestamp state.last_seen timestamp state.positions.append((center_x, center_y)) # 只保留最近 30 帧的位置避免内存无限增长 if len(state.positions) 30: state.positions.pop(0) # 判断是否静止最近 10 帧位移小于阈值 if len(state.positions) 10: recent state.positions[-10:] dx max(p[0] for p in recent) - min(p[0] for p in recent) dy max(p[1] for p in recent) - min(p[1] for p in recent) if dx 5 and dy 5: state.stationary_frames 1 else: state.stationary_frames 0 # 违停判断静止超过 300 帧约 10 秒按 30fps 算 if state.stationary_frames 300: return illegal_parking return None这个框架的好处是把跟踪和业务判断解耦了你换跟踪器或者改业务规则都不用动核心逻辑。positions列表只保留最近 30 帧防止长时间运行内存涨上去。stationary_frames用连续帧计数而不是时间戳是因为视频帧率可能变化用帧数更稳定。5.4 验证跟踪效果的一个笨办法但很管用调完参数之后怎么知道效果变好了我的习惯是选一段 30 秒左右的测试视频手动数一下画面里出现了多少辆车然后跑跟踪看输出的 track_id 总数和手动数的差多少。如果 track_id 总数远大于实际车辆数说明 ID 跳变严重如果远小于说明漏检或者轨迹合并了。这个办法不精确但比盯着画面看直观得多。另外把--save-txt打开用脚本统计每个 track_id 的持续帧数正常跟踪的车辆持续帧数应该和它在画面里出现的时长成正比如果大量 track_id 只持续几帧就消失说明n_init或者max_age需要调。从那以后我每次拿到一个新的跟踪项目都会先跑一遍这个笨办法把 ID 稳定性摸清楚再动业务逻辑。希望这份拆解能帮你少走点弯路顺利把毕设跑通。本文还有配套的精品资源点击获取