
简介本资源是一个基于YOLOv8与DeepSORT算法融合的智能车辆分析系统面向计算机、人工智能、自动化等专业本科生及研究生适用于毕业设计、课程大作业与期末项目实践。系统完整实现车辆目标检测、多目标跟踪与区域车辆计数三大核心功能代码经实测可直接运行答辩获评98分兼具工程可行性与教学示范性。压缩包共42个文件含31个Python源码涵盖主程序app.py、模型推理main.py、DeepSORT跟踪模块、工具函数utils等、2个配置文件.yaml与.txt、2份说明文档README.md与demo.png、1个预训练模型yolov8n.pt、1个测试视频test.mp4及界面截图等整体大小50.26MB结构清晰、模块解耦便于理解算法流程与二次开发。目前已有347人学习下载配套资源包含可运行环境配置requirements.txt、可视化界面参考webui.png及典型场景测试录像为初学者提供从环境搭建到结果展示的闭环学习路径也为进阶者预留了参数调优与功能扩展接口。 做毕业设计选“智能车辆检测跟踪计数”这个方向的人很多原因不外乎三点场景直观监控视频、道路卡口素材遍地都是技术栈成熟目标检测和跟踪的公开方案多到挑花眼汇报效果好跑通之后能直接看到画面里一辆辆车的框、编号和计数结果答辩演示完全不虚。但真上手之后问题就来了检测用 YOLOv8跟踪用 DeepSORT两个东西单独跑都有现成教程合在一起却总在细节上翻车——检测框坐标怎么喂给跟踪器、ID 为什么老是跳变、车辆跨线后计数重复还是漏计、跑视频卡得一帧一帧跳。这篇就顺着完整思路把从环境搭建、数据准备、模型训练到跟踪计数联调的全过程捋一遍重点说清楚每一步背后的原理和我在实际调试中踩过的坑。内容面向准备做相关毕设、课设或者单纯想在自己的项目里落地车辆检测跟踪的读者按这篇的思路走至少能少走两周弯路。1. 技术方案选型为什么偏偏是 YOLOv8 DeepSORT1.1 YOLOv8 在车辆检测任务上的天然优势做车辆检测可选方案其实不少。传统方法如 HOG SVM、背景建模之类早就不适合复杂交通场景两阶段检测器 Faster R-CNN 精度确实可以但速度实在感人处理视频流时很难达到实时。YOLO 系列作为一阶段检测器的代表在速度和精度之间取得了很好的平衡而 YOLOv8 相比之前的 v5、v7 版本有几个点特别适合“车辆”这个目标。先说最重要的YOLOv8 从 anchor-based 换成了 anchor-free。在 YOLOv5 里anchor 尺寸是聚类出来的跟训练数据分布强相关换一批数据集可能就要重新聚类。而 YOLOv8 直接在特征图的每个位置上预测目标中心点和宽高少了一套 anchor 的调参逻辑。车辆这个目标有个特点——尺寸跨度大远处的车可能只有几十个像素近处的车能占满半个屏幕。anchor-free 设计相比固定 anchor 集对不同尺寸目标的适应性天然更好省了不少预处理的功夫。其次是 YOLOv8 的模型结构自带了很多工程化的改进。比如 C2f 模块替换了原来的 C3 模块梯度流动更丰富对小目标的特征提取更充分Head 部分从耦合改成了解耦头分类和回归两个分支各管各的收敛更快。还有一个容易被忽略的点YOLOv8 的 Ultralytics 框架集成度非常高训练、验证、导出 ONNX/TensorRT全都是命令行直接搞定对毕设这种“时间紧任务重”的场景来说这种开箱即用的体验能救了老命。不过 YOLOv8 也不是没缺点。它默认在 COCO 数据集上预训练COCO 里小物体多模型对车辆这种中等大小目标的先验其实不太够所以迁移到自己的车辆数据集上重新训练是必须的。很多人直接拿预训练权重跑视频效果看起来还行但一旦遇到自己的监控场景角度刁钻、车型小众误检漏检就都出来了。1.2 DeepSORT 为什么能解决“跟踪”问题目标检测解决的是一个帧内的问题这一帧画面里哪些位置有车。但连续的视频流里同一个目标在前后帧中哪里出现了、走了多远、现在到哪个位置了检测算法是不知道的。这就轮到跟踪算法登场。DeepSORT 本质上是 SORT 的改进版核心思路是“检测 轨迹关联”。SORT 用卡尔曼滤波预测每个目标下一帧的位置再用匈牙利算法在“预测位置”和“当前帧检测框”之间做匹配。这种方式在目标运动轨迹比较线性、遮挡不多的时候效果很好但一旦目标被短暂遮挡编号很容易丢失或切换。DeepSORT 的改进在于引入了外观特征——用一个简单的 ReID 模型对每个检测框提取特征向量匹配时不仅看位置上的 IoU 距离还看外观特征的余弦相似度。这样即使目标被遮挡几帧再出现时依然能通过外观特征找回原来的 ID。放到毕设这个场景里DeepSORT 其实还有个隐性的好处它的原理讲起来非常清晰论文好写。卡尔曼滤波、匈牙利算法、级联匹配每个名词都是经典知识点答辩时评委问“你跟踪模块是怎么实现的”你可以从状态预测讲到数据关联体系非常完整。如果换成 ByteTrack 这种“简单粗暴但效果好”的跟踪器虽然精度可能更高但论文里的理论支撑反而显得单薄。DeepSORT 的短板也很明确它的 ReID 模型通常在行人数据集上训练用在车辆上特征判别力会弱一些而且它严重依赖检测质量检测框一抖跟踪就容易出问题。这个后面在调试环节细说。1.3 车辆计数的三种常见方案对比计数是整个项目里最容易被低估的一环。很多人觉得检测跟踪都跑通了计数还不简单实际上计数的方案选择直接决定了后面逻辑的复杂度。第一种是区域计数。在画面里画一个检测区域比如某个路口、某段车道统计区域内当前有多少辆车。这个方法实现最简单核心代码就是判断检测框中心点是否在包围区域内适合做“当前路口的停车数量/拥堵程度”统计。缺点是区域边界的处理很麻烦车停在边界上时数还是不数很容易和预期不一致。第二种是虚拟检测线计数。在画面某个位置画一条线比如一条横向的虚拟线代表“停止线”车辆越过这条线就计数一次。这是交通统计里最常用的方式因为物理意义明确——通过某个断面的车流量是多少。实现上需要跟踪轨迹的辅助要判断车辆是从线的一侧运动到另一侧还得避免同一个目标来回穿越导致重复计数。第三种是基于轨迹的计数。不仅记录目标越过的线还持续追踪它的完整运动路径可以统计转弯方向、平均速度等更丰富的信息。作为毕设来说这种方案展示效果最好但代码量和调试成本也最高。我最后采用的是“虚拟检测线 跨线方向判断”的方案也就是第二种。它在毕设演示时最直观画面里画一条红线车经过时计数器 1左上角显示总数和进出方向评委一眼就能看懂。而且这种方案对车辆跟踪质量的要求刚好卡在 DeepSORT 的能力范围内如果跟踪偶尔丢帧导致轨迹不连续只要跨线那几帧的关联做对计数依然准确。2. 环境搭建与数据集准备最容易劝退人的一步2.1 版本组合与安装避坑YOLOv8 的环境配置在网上教程一大堆但版本之间互相打架的情况非常普遍。我踩过比较典型的坑是 PyTorch 版本和 CUDA 版本对不上装完 ultralytics 之后一跑推理直接报错找不到 CUDA driver。我最终稳定使用的组合如下Python 3.93.10 和 3.11 也能用但有些老代码对 deep_sort 库兼容性不稳PyTorch 2.1.0 CUDA 11.8ultralytics 8.0.xopencv-python 4.8.xscikit-learn、scipy、filterpy安装的时候注意PyTorch 不要用 pip install torch 默认装 CPU 版一定要指定 CUDA 版本源。命令是pip install torch2.1.0 torchvision0.16.0 torchaudio2.1.0 --index-url https://download.pytorch.org/whl/cu118 pip install ultralyticsDeepSORT 的部分不要用 pip 直接装现成包有很多第三方包的接口实现不一致。建议克隆开源项目mikel-brostrom/yolov8_tracking或者ZQPei/deep_sort_pytorch然后单独把 deep_sort 目录拷出来这样接口完全可控后面做计数逻辑也方便。我最后用的就是 ZQPei 版本的 deep_sort 模块把里面的model_data路径改成自己的 ReID 权重。装完之后先跑一个最简单的验证确认环境没问题再往下走yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg如果输出一个带着检测框的 bus.jpg说明基础环境没问题。2.2 车辆数据集选择公开数据集还是自己标数据集这块我见过不少同学一上来就打算自己拍视频、自己标几千张图结果两周时间全搭进去效果还不行。说实话作为毕业设计没必要完全自建数据集公开的车辆数据集足够覆盖大部分需求。常用的几个UA-DETRAC纯车辆检测和跟踪数据集场景是北京和天津的十字路口包含多类车辆标注有 8000 多帧的标注图像。缺点是分辨率偏低部分场景光照条件一般。BDD100K伯克利发布的自动驾驶数据集10 万张图像包含汽车、公交车、卡车、自行车、行人等多个类别天气和时段覆盖非常全适合做“多路况适应”的模型训练。COCO 里的车辆子类car、bus、truck 这三个类别加起来样本量非常大如果只是做 demo直接用 YOLOv8 官方在 COCO 上训练的权重都能凑合。我的建议是用 BDD100K 里 car、bus、truck 三个类别作为主要训练集再叠加 UA-DETRAC 中你自己监控场景对应视角的帧来微调。因为 BDD100K 场景覆盖广模型泛化性更好UA-DETRAC 是固定点位监控视角和毕设演示场景的视角更接近。两者混合后兼顾泛化和场景适配。如果确实想用自己的数据比如学校门口、自己拍的停车场那就要走“抽帧 标注”的流程。从视频里抽帧时一定不要只抽连续帧相邻帧画面基本一样标了也是浪费。建议每秒抽 1 帧再打乱顺序挑选保证数据多样性。标注工具推荐用 X-AnyLabeling界面友好支持自动标注后用人工修正效率能提升不少。2.3 标注格式与数据清洗的细节YOLO 格式的标注是class_id x_center y_center width height注意 center 和 width/height 都是相对于图像宽高的归一化值。比如一张 1920x1080 的图里一辆车检测框左上角 (100, 200)右下角 (300, 500)那么标注为0 0.1042 0.3241 0.1042 0.2778其中 x_center (100 300) / 2 / 1920 0.1042y_center (200 500) / 2 / 1080 0.3241width (300-100)/1920 0.1042height (500-200)/1080 0.2778。这里有个特别容易犯的错有些标注工具导出的是“左上角 宽高”格式转 YOLO 格式时宽高忘了除以图片宽高训练时 loss 直接起飞。建议用脚本统一转换不要手动算。数据清洗这块说一下标注完成后一定要逐类检查实例数量分布。很多时候自己标的车类占了 80%公交车类只占 2%模型训练出来公交车基本学不会。要么补标公交车数据要么干脆合并类别。对我来说车、公交车、卡车三类在交通流量统计场景里其实没有必要分得太细分类别一是为了显示工作量和论文丰富度二是因为 ReID 特征对宽体车和小轿车的外观差异本来也敏感分开后跟踪效果会更好一点。但如果时间紧合并成“vehicle”单类完全不影响计数效果。3. YOLOv8 训练实操跑通训练并调出有效果的模型3.1 模型规格选择与配置文件YOLOv8 的模型规格从 n、s、m、l 到 x参数量和精度递增。这里给个实际参考GTX 1660 Ti 这种 6G 显存的卡用 yolo8m 在 640 分辨率下训练 batch size 也就开到 8 左右yolov8l 基本别想如果只有 4G 显存老老实实从 yolov8s 起步。选择建议是只要效果展示、机器一般yolov8s性价比最高要冲一冲精度、机器还行yolov8m精度和速度平衡点纯折腾、有云 GPUyolov8l 起但训练时间成倍增加训练前需要准备 data.yaml内容大概是train: ./datasets/train/ val: ./datasets/val/ nc: 3 names: [car, bus, truck]路径用相对路径容易报错建议改成绝对路径这个坑我踩过——从一台机器复制到另一台训练时相对路径解析出错搞了好久才发现是路径问题。3.2 训练参数与损失曲线解读训练命令是最简单的一行yolo detect train datadata.yaml modelyolov8s.pt epochs100 imgsz640 batch8 device0几个参数的底层逻辑要清楚epochs 不是越多越好。50 轮开始看验证集 mAP 是否还在涨100 轮一般足够再多就有过拟合风险。imgsz 是精度和速度的杠杆。640 是基准如果监控视频里的车普遍很小试试 960小目标检测效果会有明显提升但显存占用和推理时间都会涨。我做毕设用的 640因为在监控画面里车辆不算太小640 够用。batch 大小受显存限制不必硬调。8 在 6G 显存上是 yolo8m 640 的极限再大就 OOM。训练过程中看 loss 曲线是有路数的不要只看终点要看趋势。train/box_loss和train/cls_loss应该在训练过程中平滑下降val/box_loss和val/cls_loss跟跌但略高于训练值这是正常现象。如果 val loss 开始反弹而 train loss 还在降说明过拟合了这时候要么加数据增强要么减少 epochs。YOLOv8 的 loss 函数比较平滑一般不会像老版本那样剧烈震荡。3.3 模型评估与导出的实操记录训练完后第一件事是跑验证集看指标yolo detect val modelruns/detect/train/weights/best.pt datadata.yaml主要看 mAP50 和 mAP50-95 两个指标。毕设答辩时mAP50 在 85% 以上就属于相当能打的结果mAP50-95 能到 60% 就算不错。比如我用 yolo8m 在混合数据集上训练完mAP50 大约 89%mAP50-95 大约 63%展示效果已经不错了。导出模型时要注意直接用 .pt 文件做推理没问题但后续接 DeepSORT 和实时视频处理时建议导出成 ONNX 格式顺便可以尝试 TensorRT 引擎。TensorRT 在 1660 Ti 上跑 yolo8m 640 分辨率推理时间能从约 25ms 降到约 12ms视频实时处理的余量会大很多。导出命令yolo export modelbest.pt formatonnx dynamicTrue opset11 yolo export modelbest.pt formatengine device0这里提醒一句导出 engine 格式需要先装好 TensorRT版本必须和 CUDA 匹配很容易踩坑。毕设阶段用 ONNX 就够了engine 属于锦上添花。4. DeepSORT 跟踪集成与车辆计数逻辑实现4.1 检测结果如何接入跟踪器这一步是整个项目里坑最密集的地方。YOLOv8 输出的检测结果格式是xyxy左上角 x1, y1右下角 x2, y2加置信度加类别。DeepSORT 的输入格式也是xyxy看似直接兼容但细看有几个细节要注意。把检测结果组装成 detections 的代码大概长这样import numpy as np from deep_sort import DeepSort deepsort DeepSort(/path/to/deep_sort/deep/checkpoint/ckpt.t7, max_dist0.2, max_iou_distance0.7, max_age70, n_init3, nn_budget100) def run_tracking(frame, results): tracks [] if results[0] is not None: for det in results[0]: x1, y1, x2, y2, conf, cls det # 置信度过滤低于阈值的不要减少误检对跟踪的干扰 if conf 0.4: continue tracks.append([x1, y1, x2, y2, conf]) if len(tracks) 0: outputs deepsort.update(np.array(tracks), frame) else: outputs deepsort.update([], frame) return outputsdeepsort.update返回的每个 track 包含x1, y1, x2, y2, track_id等信息。拿到 track_id 后就可以给每辆车画框、编号、画轨迹线了。几个关键参数的含义要搞清楚max_dist外观特征的最大余弦距离阈值超过这个值就认为不是同一个目标。对于车辆因为外观可能相似度很高同款黑色轿车太多这个值不宜设得太大0.2 左右比较合适。max_age轨迹丢失后最多保留多少帧。设太大会导致目标已经离开画面但轨迹还占着资源计数逻辑如果跟轨迹绑着就出问题设太小则目标被遮挡几帧后 ID 直接切。车辆场景下 50~70 帧比较合适。n_init新轨迹需要连续匹配成功多少帧才开始确认。默认 3 或者 5 都行设小了容易把误检当成新目标设大了计数器响应慢。4.2 计数逻辑虚拟检测线与防重复计数虚拟检测线实现的核心就两件事判断车辆是否跨线以及跨线后如何不重复计数。先说跨线判断。我定义了一条横向检测线比如COUNT_LINE_Y int(frame_height * 0.7)然后对每个被跟踪的车辆保存它的中心点坐标。当某一帧车辆的center_y大于线位置下方而下一帧或接下来几帧的center_y小于线位置上方说明车辆从下往上穿过了线反向同理。这个逻辑看起来简单但实际做的时候要处理两个问题检测框抖动导致中心点在线附近反复横跳车辆速度太快导致两帧之间中心点直接跳过了线位置而没有触发判断。解决抖动问题我用了轨迹移动平均不直接用当前帧的检测框中心点而是取最近 5 帧的平均中心点来判断跨线。这样单帧抖动就被抹平了。解决跳帧问题单独判断“上一帧在线内、当前帧在线外”是不够的。更稳的做法是记录每个目标的上一帧位置和当前帧位置判断这两帧的连线是否与检测线相交。这个用线段相交判断做很简单代码也不复杂def is_crossing(prev_y, curr_y, line_y): # 返回 -1: 从上往下穿过, 1: 从下往上穿过, 0: 未穿过 if prev_y line_y and curr_y line_y: return 1 if prev_y line_y and curr_y line_y: return -1 return 0避免重复计数的核心是维护一个counted_ids集合。每辆车跨线计数后立刻把 track_id 放进集合里后面不再处理。要注意的是标记时机要选在跨线动作完成之后判断完成后立刻加入集合不要等几帧后再加防止同一目标在线的另一侧再次满足跨线条件被重复计数。完整的计数逻辑伪码counted_ids set() count_in 0 count_out 0 for track in tracks: track_id track.track_id center_y (track.ymin track.ymax) / 2 if track_id in counted_ids: continue prev_center_y track_history.get(track_id, center_y) cross_dir is_crossing(prev_center_y, center_y, COUNT_LINE_Y) if cross_dir 0: count_in 1 counted_ids.add(track_id) elif cross_dir 0: count_out 1 counted_ids.add(track_id) track_history[track_id] center_y注意track_history要定时清理删掉那些已经不在画面里max_age超时被删的 track_id否则内存越积越多。我最初没做清理跑一个小时的视频内存涨了四五个 G就是这里泄漏的。4.3 完整流程串起来从视频帧到统计结果整个主循环的长相用伪代码描述大概是这样的cap cv2.VideoCapture(test.mp4) while True: ret, frame cap.read() if not ret: break results model(frame) # YOLOv8 检测 tracks deepsort.update_with_detections(results, frame) # DeepSORT 跟踪 frame draw_tracks(frame, tracks) frame draw_count_line(frame, COUNT_LINE_Y, count_in, count_out) frame draw_statistics(frame, count_in, count_out, fps) cv2.imshow(Vehicle Counting System, frame)前期调试时建议打开窗口实时看画面确认框、ID、计数三个要素都正常后再接视频保存输出。保存结果用cv2.VideoWriter编码器用mp4v就行帧率设置成和源视频一致否则导出的视频会长得怪怪的。5. 常见问题与排查技巧实录5.1 问题速查表现象根因排查/解决方法车还没过线就计数了检测框在线上方边缘抖动用轨迹平滑最近 N 帧平均替代单帧中心点判断调低置信度阈值防止半透明误检同一辆车被数两次车辆跨线后 tracking ID 切换被当成新目标跨线后短期内不要删除计数标记提高 DeepSORT max_age减少 ID 切换频率左边开过来的车没被计数检测线位置碰到画面边缘检测框漏检多把检测线往画面中间挪提高线附近的检测置信度阈值少而准比多而乱好视频很卡fps 只有个位数模型太大或者没用 TensorRT换 yolo8s/yolov8n导出 ONNX/TensorRT跳帧检测ID 频繁切换ReID 模型特征判别力不足调小 max_dist 到 0.15 左右检查训练数据中同款车辆样本是否过多夜间车辆漏检严重训练数据缺少暗光场景训练集加入夜间/雨天样本对视频做预处理亮度均衡、直方图均衡化后推理深色车和阴影混在一起检测框包含大面积阴影调高置信度阈值训练时加入带阴影的负样本纯演示可忽略5.2 调参心得与独家避坑这里有几个搜索结果里不会明确写的点是实际操作中反复试出来的。第一个是关于检测阈值的设置。检测模块输出的置信度建议分为两层过滤。第一层在 YOLOv8 检测端conf0.25给一个宽松的初筛第二层在接入 DeepSORT 前用conf0.4或0.5做一次严格过滤。为什么这么做因为 DeepSORT 对误检的容忍度很低一个误检框一旦被当成新目标初始化后续几帧都会用卡尔曼滤波持续跟踪它在画面上表现为一个“幽灵框”乱飘还会干扰计数。所以宁可漏掉一些低置信度的真车也别让假目标进入跟踪器。这个经验在处理远处的小车时特别重要——远处车辆本身像素少置信度低但要是被误检成公交亭、树影之类的东西计数结果就很难看了。第二个是关于计数线的位置。别把检测线放在画面边缘或目标刚出现的位置放在画面中后段 60%~75% 的位置最稳。原因很简单目标刚进入画面时DeepSORT 需要几帧来确认轨迹n_init3 的效果如果检测线太靠边车从画面边缘进来直接跨线可能轨迹还没确认计数就漏了。放到中后段车辆已经跟踪了好几十帧ID 状态稳定计数判断才可靠。第三个是关于输出帧率的处理。处理实际视频时如果源视频 30fps模型推理 跟踪整体只能跑到 15fps生成的视频即使保存为 30fps画面也会一顿一顿的看起来像“跳帧播放”。这直接会影响答辩演示的印象分。我的处理方式是记录处理每帧的实际耗时按真实 fps 写入输出视频。虽然帧率数字上可能只有 15fps但播放起来是匀速的、流畅的观感比强制 30fps 好太多。5.3 扩展改进方向从毕设到能打的项目如果毕设答辩想多展示一点或者后续想把项目做成完整系统有两条很自然的改进路线。一条是跟踪器替换。DeepSORT 在车辆密集、遮挡严重的场景下ID 切换问题仍然明显。把 DeepSORT 换成 ByteTrack 或 StrongSORT精度会明显提升尤其是 ByteTrack 不依赖 ReID 特征速度还快在车辆场景下效果比 DeepSORT 好不少。不过话又说回来毕设做 DeepSORT 依然是最稳的路线因为换个跟踪器意味着整篇论文的实验对比、原理阐述都要重写工作量翻倍。另一条是增加统计维度。当前只做了断面车流量统计如果再加一个 ROI 区域统计当前路口的车辆排队长度或者结合跨线时刻做平均车速估算整个项目的技术层次和展示价值会高一个台阶。车速估算的原理并不复杂利用跨线前后两帧的中心点位移和时间间隔得到像素速度再结合车长标定做粗略换算即可不用引入新的算法体系。最后说点实战体会整个项目做下来我最大的心得是这类视觉毕设代码跑通只是开始把逻辑理清楚、把参数调稳、把“为什么这么设”讲明白才是拿高分的关键。YOLOv8 和 DeepSORT 各自的教程多如牛毛但能把两者无缝对接起来并实现准确计数的细节——检测置信度的两级过滤、跨线判断的轨迹平滑、计数集合的生命周期管理——这些才是实际调试中真正花时间的地方。如果时间允许建议搭建一套自己的测试视频库白天、傍晚、夜间各几条车流量从小到大各选几段。每跑一轮改动都用同样的测试视频对比帧率和计数准确率记录一个简单的对比表格。这个过程本身就很像工程实践中的评估流程答辩时拿出来讲说服力很强。本文还有配套的精品资源点击获取