YOLOv8+DeepSORT车辆检测与计数实战:从原理到毕设落地

发布时间:2026/10/1 23:45:54
YOLOv8+DeepSORT车辆检测与计数实战:从原理到毕设落地 简介这是一份基于YOLOv8与DeepSORT的智能车辆目标检测、跟踪与计数项目源于高分毕业设计适合计算机、人工智能、自动化等专业学生用于课程设计、大作业或毕业设计参考。项目代码已调试通过可直接运行覆盖目标检测、多目标跟踪、跨线计数等完整流程并包含可视化界面便于二次开发与功能扩展。资源包共42个文件以Python源码为主附带YOLOv8模型权重、DeepSORT配置参数、YAML配置文件及测试视频整体约50.26MB结构清晰从模型加载到跟踪计数均有对应模块支撑。目前已有348人学习使用尤其适合需要快速搭建车辆检测计数系统、理解检测与跟踪融合思路的初学者及进阶者可作为毕业设计选题的直接基础。项目还提供了演示效果图与说明文档方便对照验证算法效果具有较高的实践参考价值。1. YOLOv8-deepsort 车辆检测与计数毕设选它到底图什么如果你在毕业设计题目里看到“智能车辆目标检测车辆跟踪车辆计数”大概率已经做好了掉一层皮的准备。这套组合拳的常规做法就是 YOLOv8 负责“看见车”DeepSORT 负责“盯住同一辆车”最后用计数逻辑统计通过某个区域的车辆数量。它能解决的实际问题很具体一段监控视频里到底过了多少辆车、每辆车从哪边进哪边出、能不能实时框出来跟住。相比只做检测加跟踪和计数的完整链路更能体现“系统”思维这也是为什么每年都有人拿它做毕设、拿它凑比赛。适合那种手里有视频数据、想做出可演示效果、又不想只交一份训练报告的人——但前提是你要清楚环境怎么搭、模型怎么训、计数怎么落以及最容易在哪里翻车。2. 先搞懂 YOLOv8 和 DeepSORT 是怎么配合的检测给“眼睛”跟踪给“记忆”2.1 YOLOv8 负责的活从锚框到无锚框为什么它更适合车辆场景YOLOv8 是 Ultralytics 推出的目标检测模型它把 YOLOv5 的 Anchor-Based 换成了 Anchor-Free也就是不再预设一堆固定形状的候选框而是让模型直接预测目标中心点位置和宽高。对于车辆这种形状相对规整、长宽比变化不算极端的目标Anchor-Free 的好处是省掉了聚类 Anchor 的步骤训练代码更干净精度也没有明显损失。网络结构整体分为 Backbone、Neck、Head 三段Backbone 用 CSPDarknet 提取多尺度特征Neck 用 PAN-FPN 把深浅层特征融合Head 输出分类和回归结果。相比 YOLOv5v8 的 Head 做了解耦把分类和回归分开到两个分支收敛更快。用 YOLOv8 做车辆检测还有一个现实原因预训练权重好找、数据集好凑。COCO 类别里包含 car、bus、truck起步阶段直接用 yolov8n.pt 或 yolov8s.pt 就能看到车辆框。真要训练自己的数据Ultralytics 的 YOLO 格式只需要一个 txt 文件写类别和归一化坐标半天能改完。这就是它适合毕设的核心原因——不是它比所有模型都强而是你在有限时间里“够得着”。2.2 DeepSORT 负责的活卡尔曼滤波和级联匹配怎么把“同一辆车”记住DeepSORT 是 SORT 的改进版。SORT 只靠卡尔曼滤波预测目标位置然后用匈牙利算法做 IoU 匹配缺点是一旦目标被遮挡或者短暂丢失ID 很容易跳变。DeepSORT 加入了两样东西外观特征和级联匹配。所谓外观特征就是用一个小型 ReID 网络把每个检测框里的目标提成一个特征向量如果两帧的两个框特征距离足够近就认为概率上是同一辆车。所谓级联匹配就是优先给那些连续丢失帧数少的目标做匹配丢了很久的目标反而放到后面再处理这样能减少 ID Switch。实际配合时YOLOv8 输出的每帧检测框会送给 DeepSORT 的 update 接口。DeepSORT 内部先对上一帧确认过的轨迹做卡尔曼预测拿预测框和当前帧检测框做 IoU 计算同时算外观特征余弦距离两个矩阵加权后作为代价矩阵交给匈牙利算法求最优匹配。匹配上的轨迹更新状态没匹配上的检测框尝试新建轨迹连续多帧匹配不上就删除轨迹。这套逻辑对车辆这种刚体目标很友好因为车辆很少出现剧烈形变外观特征也比较稳定。2.3 选了 DeepSORT 而不是 ByteTrack你的毕设说明该怎么写现在很多新项目用 ByteTrack、BoT-SORT效果确实比 DeepSORT 好尤其在小目标场景。但毕设选题叫“YOLOv8-deepsort”意味着你已经定了技术路线。常见做法是思路部分写“先用 YOLOv8 获取高置信度检测框再用 DeepSORT 做跨帧数据关联”创新点写“针对车辆遮挡问题改进了级联匹配策略”或者“融合了车辆颜色特征”。这不是让你硬吹而是说实话——DeepSORT 的框架足够开放替换 ReID 模型、修改代价矩阵权重都很容易。你可以在论文里对比 DeepSORT 和 ByteTrack 的 ID Switch 数量如果实验结果 DeepSORT 稍差就写“本方案在保证精度的同时更关注外观特征的可解释性”答辩老师没法反驳。需要记住一点DeepSORT 只是跟踪模块质量上限由检测决定。YOLOv8 漏检一辆车DeepSORT 再怎么算也补不回来。所以调试优先级永远是“先抠检测再调跟踪”。3. 搭建环境并跑通最小推理Ubuntu 20.04 下从零到看到车辆框3.1 CPU 和 GPU 环境怎么选GTX 1660 Ti 这类卡够不够用环境配置是第一个劝退点。常见做法是直接用 Ultralytics 提供的 pip 包省去编译 Darknet 的痛苦。如果你手头是 GTX 1660 Ti 6G 显存跑 yolov8n 和 yolov8s 完全够视频推理能到 20 FPS 以上显存只有 4G 的笔记本就老老实实用 yolov8n批量尺寸调小。CPU 版也能跑但推理速度会掉到每帧几百毫秒适合验证流程不适合演示。操作系统推荐 Ubuntu 20.04配合 Python 3.8 或 3.9坑最少。深度学习框架就是 PyTorchCUDA 装 11.8 或 12.1 都行。先确认 NVIDIA 驱动版本再装对应版本的 PyTorch。之前遇到过有人先在 Ubuntu 里装了驱动 535又装 CUDA 11.7最后 torch.cuda.is_available() 永远是 False原因就是 PyTorch 自带的 CUDA runtime 和系统 CUDA 对不上。所以装 PyTorch 时选带了 cu118 或 cu121 后缀的版本不用管系统里有没有 CUDA toolkit。3.2 最小命令三步走建虚拟环境、装包、下载预训练权重# 第一步创建并激活 Python 虚拟环境避免和系统 Python 打架 conda create -n yolo python3.9 -y conda activate yolo # 第二步安装 PyTorch这里用 CUDA 11.8 版本注意不要装成 CPU 版 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 第三步安装 Ultralytics 全家桶deep-sort-realtime 是 DeepSORT 的封装实现 pip install ultralytics deep-sort-realtime opencv-python第一个坑在第二步。有些人图省事直接pip install torch装出来的通常是 CPU 版。验证方法很简单进 Python 执行import torch; print(torch.cuda.is_available())输出 False 就说明白装了。第二个坑是 deep-sort-realtime 这个库它在 PyPI 上的名字带短横线import 时用的是deep_sort_realtime下划线别搞混。# 下载 YOLOv8 预训练权重n 是 nano 版本文件约 6MB最快 yolo predict modelyolov8n.pt sourcetest.mp4 showTrue这条命令会下载权重到当前目录然后对 test.mp4 做检测并弹出窗口显示。第一次跑通这个你就完成了环境验证。source 可以换成图片、摄像头设备号或视频目录showTrue 是弹窗在无桌面服务器上去掉就行。如果想保存结果而不是弹窗改一行# 保存到 runs/detect/predict输出带标注框的视频文件 yolo predict modelyolov8n.pt sourcetest.mp4 saveTrue逻辑说明Ultralytics 的 yolo 命令是一个统一入口predict 子命令负责推理。model 参数指定权重文件source 是输入源saveTrue 表示把结果写到 runs 目录。这套命令做快速验证非常合适但进入跟踪和计数阶段要用 Python API 替代因为需要拿到每一帧的检测框坐标喂给 DeepSORT而不是只看一个视频成品。3.3 把 YOLOv8 检测结果接到 DeepSORT核心代码与参数含义import cv2 from ultralytics import YOLO from deep_sort_realtime.deepsort_tracker import DeepSort # 加载模型参数含义conf_thres 置信度阈值低于这个的检测框会被丢掉 model YOLO(yolov8n.pt) tracker DeepSort(max_age30, embeddermobilenet) # 车辆类别COCO 里 car2, bus5, truck7 VEHICLE_CLASSES [2, 5, 7] cap cv2.VideoCapture(test.mp4) while True: ret, frame cap.read() if not ret: break # 只做检测不做跟踪results[0].boxes 里是当前帧全部检测框 results model(frame, verboseFalse)[0] detections [] for box in results.boxes: cls int(box.cls[0]) if cls not in VEHICLE_CLASSES: continue # DeepSORT 要求输入格式为 [left, top, width, height]注意是 xywh x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) detections.append(([x1, y1, x2 - x1, y2 - y1], conf, cls)) # update 返回跟踪对象列表每个对象有 track_id 和 bbox tracks tracker.update_tracks(detections, frameframe) for track in tracks: if not track.is_confirmed(): continue track_id track.track_id ltrb track.to_ltrb() # [left, top, right, bottom] cv2.rectangle(frame, (int(ltrb[0]), int(ltrb[1])), (int(ltrb[2]), int(ltrb[3])), (0, 255, 0), 2) cv2.putText(frame, fID:{track_id}, (int(ltrb[0]), int(ltrb[1]) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(frame, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段代码的关键点有三个。第一detections 列表里的坐标必须是左上的 x、y 加宽高DeepSORT 内部要算 IoU 和卡尔曼预测格式不对会报错或用错位置。第二update_tracks(detections, frameframe)里的 frame 参数给的是原始帧ReID 模型会从框里裁剪区域提取特征如果你传了缩放后的帧特征质量会下降。第三is_confirmed()过滤掉前几帧还没稳定下来的轨迹避免画面里出现一堆闪一下就没的 ID。max_age30的含义是轨迹在连续 30 帧内没匹配到检测框也不会立即删除车辆被大卡车短暂遮挡时能保住 ID。embeddermobilenet是 ReID 特征提取器精度一般但速度快毕设够用如果换embedderosnet特征更准但推理速度会掉一截。embedder未指定时会默认下载一个权重文件第一次运行需要联网。4. 训练你自己的数据集标注、格式转换和 YOLOv8 训练参数调优4.1 数据从哪来视频抽帧、Labelme 标注和 YOLO 格式的转换脚本训练自己的车辆检测器无非几条路用现成公开数据集UA-DETRAC、BDD100K或者自己采集视频抽帧标注。毕设场景里多数人的需求是“识别我这个路口/这条路上的车”公开数据集的场景和你不一样现场采集更稳。抽帧代码很简单OpenCV 按固定间隔取帧即可建议每秒抽 1 帧到 2 帧不要每帧都存否则大量相似图片会让训练集冗余。标注工具常见做法是 Labelme画矩形框后保存为 JSON。Labelme 的 JSON 是多边形格式和 YOLO 的 txt 不一样需要写个小脚本转换。转换逻辑从 JSON 的 shapes 里拿每个框的 points 坐标计算出左上角和右下角再除以图片宽高做归一化写入 txt。注意 YOLO 格式一行一个目标顺序是class_id center_x center_y width height四个值都是 0 到 1 的小数。以下脚本可以直接改路径用import json import os # 把 Labelme JSON 转成 YOLO txt图片名和 JSON 名保持一致 def convert_labelme_to_yolo(json_path, out_dir, class_map): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] lines [] for shape in data[shapes]: label shape[label] if label not in class_map: continue pts shape[points] # Labelme 存的是多边形顶点矩形框取 x 和 y 的极值 xs [p[0] for p in pts] ys [p[1] for p in pts] x1, y1, x2, y2 min(xs), min(ys), max(xs), max(ys) cx ((x1 x2) / 2) / img_w cy ((y1 y2) / 2) / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{class_map[label]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) out_path os.path.join(out_dir, os.path.basename(json_path).replace(.json, .txt)) with open(out_path, w) as f: f.write(\n.join(lines))参数说明class_map是你自己的类别字典比如{car: 0, bus: 1, truck: 2}顺序一旦定下来就不要改改了你得重训。这段逻辑里最关键的是取min/max算矩形边界因为 Labelme 允许你画多边形但 YOLO 只接受轴对齐的矩形框。如果标注时把车标成了斜方向的多边形这里会被强行拉成矩形虽然有一点误差但可接受。4.2 数据集目录结构和训练命令以及必须理解的 6 个参数含义Ultralytics 要求数据集按固定目录放图片和标注文件同名。结构如下dataset/ images/ train/ val/ labels/ train/ val/images 和 labels 是同级目录train 和 val 的比例一般 8:2 或 9:1。数量上毕设级别每类目标建议不少于 500 张少于 300 张训练效果会很飘。划分训练集时注意同一段视频抽出来的帧不要全放 train应该按视频片段混洗否则模型学的是“这段路的路面纹理”放到新视频上立刻失效。训练前还要写一个 data.yamlpath: /home/user/dataset train: images/train val: images/val nc: 3 names: [car, bus, truck]然后执行训练命令yolo train datadata.yaml modelyolov8n.pt epochs50 imgsz640 batch16 device0这里 6 个参数是必调的。modelyolov8n.pt表示从预训练权重接着训叫迁移学习比从零训收敛快得多。epochs50对毕设够用一般 30 轮以后 loss 就平了。imgsz640是训练时缩放尺寸车辆这种大目标 640 够用要检测很远的小车可以试 960但显存占用会翻一倍。batch16根据显存调6G 显存跑 yolov8n 批量 16 没问题跑 yolov8s 就降到 8。device0是 GPU 编号CPU 训练写devicecpu但你会等到崩溃。还有一个隐藏参数是workers控制数据加载线程数默认 8在 Windows 上改成 0 或 2否则容易 DataLoader 卡死。训练结束后看 runs/detect/train 目录里的结果。results.png里能看到 loss 曲线和 PR 曲线weights/best.pt是验证集上最好的权重部署时用它而不是last.pt。如果发生过拟合也就是训练 loss 还在降但验证 loss 反弹就减少 epochs、增加数据增强或加早停。4.3 画损失函数曲线训练完别只看 mAP曲线图才是答辩素材很多人在答辩 PPT 里放一张训练 log 截图老师根本看不清。更好的做法是用训练过程中生成的 results.csv 画损失曲线展示 val/box_loss 的下降趋势。Ultralytics 每次训练都会在 weights 同目录生成 results.csv里面有 metrics/precision、metrics/recall、val/box_loss 等列。直接用 pandas 读出来画图import pandas as pd import matplotlib.pyplot as plt # 读取训练结果路径按实际训练目录改 df pd.read_csv(runs/detect/train/results.csv) # 画验证集 box loss平滑掉前期的剧烈抖动 plt.plot(df[epoch], df[val/box_loss], labelval box loss) plt.xlabel(epoch) plt.ylabel(loss) plt.legend() plt.title(Validation Box Loss) plt.savefig(loss_curve.png, dpi150)常见坑是 results.csv 的列名会根据 Ultralytics 版本变化老版本叫val/box_loss新版本可能叫val/box_loss或val/loss/box。稳妥做法是先用print(df.columns.tolist())看一遍列名再画。损失曲线不是为了自我感动而是用来看“模型有没有在学”如果 20 轮后 loss 还在高位震荡说明标注质量有问题或学习率不合适。5. 车辆计数与高频踩坑记录跨线逻辑和 5 条最具价值的排查经验5.1 车辆计数怎么实现虚拟线圈法而不是数轨迹点常见做法是在画面中画一条虚拟线或一个虚拟区域车过线就计数。最简单的实现是“线圈法”配合“跨线状态机”。每条轨迹有一个上次所在位置当上一帧在线的 A 侧、当前帧在线的 B 侧就判定完成一次穿越。比直接比较中心点坐标更稳的做法是记下每次跨线前一帧和后一帧的 y 坐标只有当 y 坐标跨过设定线时才触发同时给每条轨迹加一个counted标志位防止同一辆车重复计数。如果同时要统计双向车流就在线上方方向和线下方方向各计一次。关键参数是线的位置最好避开停车区域和遮挡严重的区域。如果你把线画在画面中央的红绿灯前面车停在线附近来回挪动状态机会反复触发数字就会爆炸。所以线的位置要落在车辆正常行驶、不停留的路段。5.2 避坑记录ID Switch 和漏检让计数失真怎么办第一条踩坑是 ID Switch 导致同一条轨迹被计两次。现象是画面里一辆车过了线计数器加了 2。原因是车辆被遮挡后 DeepSORT 重新分配了新 ID原轨迹被删除新轨迹没有 counted 标记。解决方法是把计入条件从“当条轨迹第一次跨线”改为“每个新 ID 连续两帧都在线另一侧再计入”并且对同一物理位置的车设一个“最近计数时间戳”过滤比如 3 秒内不允许在同一个位置附近重复计数。这个过滤条件看起来有点土但在视频流里非常好用。第二条踩坑是检测框抖动导致跨线误判。现象是车辆还没到线计数已经跳了。原因是 YOLOv8 框的上下边缘帧间抖动中心点位置在临界线附近反复横跳。解决方法是不要直接比较中心点而要用检测框的底部中点作为参考点。车辆作为地面目标底部中点比中心点更贴近实际位置抖动幅度也更小。第三条踩坑是车辆被漏检造成计数丢失。现象是深色车在黑天或者树荫下直接消失计数器少统计。原因是低光照或遮挡场景下检测置信度低于阈值被过滤。解决方法是把 conf_thres 从默认 0.25 降到 0.15代价是误检框增加再用 DeepSORT 的 max_age 把短暂消失的车保持住。核心调参思路是“检测阈值要让召回率优先跟踪算法负责处理误检”而不是企图用一个阈值同时解决两侧问题。第四条踩坑是 tracking 里 embedder 首次下载权重失败导致程序崩溃。现象是运行到update_tracks时报错提示下载超时。原因是 deep-sort-realtime 默认的 ReID 权重需要从网上下载网络不稳定就挂在半路。解决方法是手动下载权重放到缓存目录或者在创建 DeepSort 时指定embeddernone先不启用外观特征只靠运动模型跑通流程后续再补权重。第五条踩坑是 CPU 机器上视频推理越来越慢。现象是前几帧还挺快后面越来越卡最后画面像幻灯片。原因是 DeepSORT 的轨迹列表不断膨胀每帧都要对大量轨迹算代价矩阵加上 ReID 特征提取吃 CPU。解决方法是调低 max_age 到 15同时每 100 帧清理一次过期轨迹把nms_max_overlap调到 0.5 减少重叠框。CPU 跑演示验证时输入视频分辨率缩到 960 宽比全高清推理快接近一倍。5.3 改进 DeepSORT 的两个有效小改论文里的“机制创新”就靠这个纯调参撑不起毕业设计的创新点但改结构又怕难度超标。有两个折中的小改法非常实用。第一个是更换 ReID 权重把默认的 mobilenet 特征换成 OSNet车辆的外观区分度会明显提高尤其对黑白两色的车。改法就一行DeepSort(embedderosnet)代价是特征提取慢一些但对 640×640 的车辆截图影响不大。第二个是修改代价矩阵的权重让运动信息权重降低、外观信息权重升高。在 deep_sort_realtime 的 tracker 源码里匹配时同时对 IoU 距离和余弦距离做加权默认权重一般是两者五五开车辆场景下可以改成外观权重 0.7、运动权重 0.3。答辩时这么写车辆在视频中移动轨迹平滑但外观特征更稳定因此提高外观特征在关联代价中的权重缓解遮挡下的 ID Switch。这个说法站得住脚代码改动又只有几行是性价比最高的“机制改进”。注意改完要跑同一段视频对比 DeepSORT 默认参数下的 ID Switch 次数把数字写进论文。6. 让毕设真正跑起来模型导出、效果验证和一点老实话模型训好后离“能演示”还差一步导出。如果你的演示机器没有 GPU把 best.pt 导出成 ONNXCPU 推理能快不少。执行yolo export modelbest.pt formatonnx imgsz640导出后用 onnxruntime 替换 YOLO 的 PyTorch 推理检测阶段速度能提升 30% 到 50%。ONNX 模型配合 deep_sort_realtime 跑 CPU 视频640p 分辨率基本能到实时。导出后务必验证一次精度ONNX 在某些算子上的输出和 PyTorch 不完全一致常见做法是跑同一帧图对比输出框偏差超过几个像素就得查算子兼容性。验证效果别只看“框得准”。你要准备三段不同场景的视频白天路口、傍晚树荫、车辆进出遮挡严重的路段。记录三组指标检测 mAP、跟踪 ID Switch 次数、计数误差率。ID Switch 次数可以从输出结果里统计同一辆车 ID 变化的次数计数误差率用人工数车数作为基准。答辩时老师最常问“你的系统怎么证明比原来好”有这三组数就能答。最后一句话我每次跟做毕设的人都说这个方向的工作量上限很高但下限也确实不高把环境、训练、跟踪、计数四条链路完整跑通你就已经超过大多数只交了模型准确率表格的人。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询