
简介面向人工智能、电子信息、物联网等专业的毕业设计、课程设计与项目初期演示这份基于YOLOv8的多端车流检测系统完整源码与资料包适合有Python与深度学习基础的学生、教师或开发者使用。项目已通过导师指导答辩评审分达95分且代码均经运行验证可直接部署或在其上扩展自定义功能。压缩包共397个文件以Python源码150个py、pyc编译文件、YOLOv8模型配置34个yaml与训练权重pt为核心另含UI界面文件、SQL数据库、shell脚本、环境配置、二维码资料及mp4演示视频整包约16.94MB目录层次覆盖训练、配置、界面与运行素材。内部图片素材涵盖告警、默认与测试等场景可帮助对照模型检测和告警输出效果。目前已有165人学习下载适合需要完整项目参考、完成毕设课设或进一步研究多端车流检测的开发者。1. 车流检测毕设为什么都绕不开 YOLOv8先把多端这件事说清楚做车流检测的毕业设计十个人里有八个最后都落在 YOLOv8 上剩下两个在 YOLOv5 和 YOLOv9 之间反复横跳。这个选择本身没什么悬念YOLOv8 有官方预训练权重、有完善的文档、有 Ultralytics 封装好的训练验证接口对一个需要在几个月内拿出完整系统的学生来说这是性价比最高的起点。真正让人翻车的往往不是模型选型而是车流检测系统这六个字背后的完整链路——模型训练只是其中一环你还要处理数据集、调参、推理加速、界面展示甚至多端这个词到底怎么落地。把标题拆开看基于 YOLOv8意味着检测核心是现成的你要做的是数据、训练和集成车流检测是场景限定核心指标不是 mAP 而是能不能数清一辆辆过去的车多端在毕设语境里通常指桌面端检测程序、Web 管理后台、移动端查看这三件套偶尔加一个 RK3588 之类的板端部署作为加分项毕设开源则决定了你需要的不只是代码能跑还要有文档、答辩材料、能讲清楚的设计思路。这篇文章按这个链路展开把它做成一套能照做的实战方案。2. 训练数据集怎么准备不给模型喂垃圾模型才不会给你吐垃圾2.1 公开数据集选型从 UA-DETRAC 到 CarDD先想清楚你要检测什么车流检测的数据集选择取决于你定义的车是什么范围。如果只做车辆计数一个vehicle类别就够了如果要做车型统计就要把 car、bus、truck 分开标。公开数据集里最常用的是 UA-DETRAC它有 10 小时 24 帧/秒的路口视频标注了 8000 多辆车场景覆盖白天、夜晚、晴天、雨天缺点是标注框是车辆整体框没有区分车型而且解压后目录结构比较乱需要自己写脚本整理。另一个常见选择是 CarDD这个数据集主要面向车辆损伤检测类别里包含 dent、scratch、crack 之类的损伤类型不适合做常规车流计数。如果你只想快速验证流程直接用 COCO 预训练权重里的 car、bus、truck 三个类别跑一遍看清了效果再回头决定要不要重新标注。我的建议是毕设场景下先用公开车辆数据集把整套流程跑通再用自己录制的路口视频补充标注 200~500 张这样既保证训练量又能体现你做的是自己的系统。2.2 用 Labelme 标注自己的路口视频帧JSON 转 YOLO 格式的一个脚本如果你决定自己标注常见流程是用 Labelme 画矩形框导出 JSON然后转成 YOLO 需要的 txt 格式。这里有一个坑值得先说Labelme 输出的 JSON 里标注坐标是绝对像素坐标而 YOLO 需要的是归一化后的中心点坐标和宽高而且类别 id 从 0 开始。转换脚本我一般这么写import json import os def labelme_to_yolo(json_path, output_dir, class_names): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] txt_name os.path.splitext(os.path.basename(json_path))[0] .txt out_lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue cls_id class_names.index(label) points shape[points] x1, y1 points[0] x2, y2 points[1] # 确保 x1 x2, y1 y2 x1, x2 min(x1, x2), max(x1, x2) y1, y2 min(y1, y2), max(y1, y2) # 转归一化中心点坐标 cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h bw (x2 - x1) / img_w bh (y2 - y1) / img_h out_lines.append(f{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(out_lines)) # class_names 顺序要和训练配置里的类别保持一致 class_names [car, bus, truck, motorcycle] labelme_to_yolo(frame_001.json, ./labels, class_names)这个脚本有两点容易写错一是 Labelme 的矩形框 points 是左上和右下两个点但手抖画的时候可能画成从右下往左上所以必须做 min/max 处理二是类别名称和训练时 YOLOv8 的 data.yaml 里定义的顺序必须完全一致否则类别 id 错位训练出来的模型会十条命不够用。如果你用 CVAT 标注导出时可以直接选 YOLO 格式省掉这一步。2.3 数据划分与目录结构train/val 的比例陷阱YOLOv8 训练时通过 data.yaml 里的 path、train、val 来定位数据。常见目录结构是这样的dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 与训练图片同名的 txt │ └── val/ └── data.yaml划分比例上毕设场景我建议训练集占 85%~90%验证集 10%~15%。这不是拍脑袋车流检测是大目标场景车辆在画面里占的面积大检测难度相对低不需要像小目标检测那样留 20% 给验证集。但有一个容易被忽略的点是——按视频帧切分数据时千万不要随机打乱后再切否则同一辆车连续 10 帧的图像会同时出现在训练集和验证集里验证 mAP 虚高答辩时被老师问一句就露馅。正确做法是按视频片段划分比如录了 5 段视频用 4 段做训练1 段做验证然后在每个片段内部再做随机采样。data.yaml 的写法也有讲究用绝对路径最省心但交代码时要把 path 改成相对路径不然评审老师换个目录跑就报错。我一般会加一句注释提醒自己这是相对路径写法# data.yaml path: ./dataset # 相对当前工作目录 train: images/train val: images/val names: 0: car 1: bus 2: truck 3: motorcycle3. 用 YOLOv8 训练车流检测模型从环境搭建到参数调优一稿过3.1 Ubuntu 20.04 搭建 YOLOv8 CPU/GPU 环境一次装对的命令序列环境搭建是新手劝退重灾区尤其是 Ubuntu 20.04 上装 YOLOv8。先说结论GPU 版用pip install ultralytics就能装完但 PyTorch 的 CUDA 版本必须和显卡驱动匹配CPU 版反而简单Ubuntu 20.04 自带的 Python 3.8 直接装也能跑就是训练慢得让人怀疑人生。装环境的命令按这个顺序来# 1. 创建虚拟环境避免污染系统 Python conda create -n yolov8 python3.10 -y conda activate yolov8 # 2. 安装 PyTorchCPU 版直接走默认源 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 3. 安装 ultralytics 包自带 YOLOv8 全部命令行能力 pip install ultralytics如果你是 NVIDIA 显卡先跑nvidia-smi看驱动支持的 CUDA 版本再装对应版本的 PyTorch。比如驱动支持 CUDA 11.8就装pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118。装完之后用python -c import torch; print(torch.cuda.is_available())验证输出 True 才算数。很多人的翻车点是装了 GPU 版 PyTorch但实际调用的还是 CPU训练一个 epoch 要半小时然后跑去论坛问为什么这么慢——先跑这一行验证省的后面全是无效努力。另一种常见做法是直接克隆 ultralytics 的 GitHub 仓库来用好处是方便改源码、看训练细节坏处是升级麻烦。我一般直接用 pip 安装的包毕设场景不需要改 C 底层Python 层面对ultralytics的调用足够完成所有功能。3.2 训练命令与关键参数跑一次车流检测训练到底该调什么训练命令本身很简单难的是参数理解。下面这个命令是我在车流检测场景下的常用配置每条参数都被我踩过坑yolo train \ modelyolov8s.pt \ data./dataset/data.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ workers4 \ patience15 \ optimizerAdamW \ lr00.001 \ lrf0.01 \ project./runs \ nametraffic_flow逐个说参数的含义和选择理由。modelyolov8s.pt是预训练权重s 是 small 版本车流检测这种大目标场景不需要 extra-large 模型反而推理速度更关键s 是性价比最高的起点。imgsz640是输入分辨率车流检测的 ROI 区域一般是路口全景车辆目标大小在 30~100 像素之间640 够用如果摄像头离得远导致车辆很小可以试 960但显存占用会涨一大截。patience15是早停参数连续 15 个 epoch 验证集 mAP 不提升就停止。这个参数被很多人忽略但它是省时间的神器——车流检测数据集如果质量不错往往 60 个 epoch 就收敛了后面的训练纯属浪费电。optimizerAdamW的选择有讲究如果你用默认的 SGD学习率要调得更保守AdamW 对新手更友好收敛稳定代价是最终精度可能比调好的 SGD 低零点几个点但毕设完全够用。lr00.001是初始学习率lrf0.01是最终学习率缩放系数这个组合走的是 cosine 衰减后面 30 个 epoch 基本是在做微调。训练完之后runs/traffic_flow/weights/best.pt就是你要的最终模型。跟它一起生成的还有results.png训练曲线图和confusion_matrix.png这两个图是答辩时展示训练过程最直观的素材别删。3.3 迁移学习的正确打开方式冻结前 10 层还是全部微调基于预训练权重做迁移学习是车流检测的默认操作但迁移到什么程度是个值得讨论的问题。预训练权重是在 COCO 上训的COCO 里有 car、bus、truck所以权重已经学会了车辆的基本特征。最常见的做法是整模型微调也就是直接yolo train modelyolov8s.pt不冻结任何层这在小数据集上效果最好因为车辆特征跟 COCO 接近微调全部层能快速适配你数据集的场景。另一种做法是冻结骨干网络前 10 层只训练 Neck 和 Head适合数据集很小的场景比如只有几百张。冻结的坏处是训练速度提升有限、精度反而可能下降因为车流场景的视角、光照和 COCO 差异不小。我测过两组对比同样 2000 张路口数据全量微调的 mAP50 比冻结前 10 层高 4~5 个百分点。结论很直接车流检测场景下不要冻结全量微调就完了。4. 多端系统怎么搭Web 端展示、桌面端检测、移动端查看的分工4.1 多端在毕设里的真实含义一个后端 API 撑起三个界面标题里的多端如果不拆解清楚很容易做成一个四不像。毕设语境下的多端通常指同一个检测服务通过 API 承接三类客户端Web 端管理后台看车流量统计和实时视频流、桌面端本地视频文件检测工具上传一段录像标出每一帧的车和数量、移动端小程序或 App只做监控查看不做重计算。这里的关键设计是检测只做一次结果广播到所有端。为此后端要拆成两个部分——检测服务和 API 服务。检测服务用 YOLOv8 对视频帧做推理把每帧的检测结果序列化API 服务负责把结果推给前端展示。一个后端撑起三个界面分工清晰答辩也好讲。常见做法是 flask 或 fastapi 做 API把权重加载到内存里接口收到图像路径就返回检测结果。下面这个 Flask 接口是我在毕设里常用的骨架from flask import Flask, request, jsonify from ultralytics import YOLO app Flask(__name__) model YOLO(runs/traffic_flow/weights/best.pt) app.route(/detect, methods[POST]) def detect(): # 接收前端传来的图片路径或文件 img_file request.files.get(image) if img_file is None: return jsonify({error: no image}), 400 # 保存到临时目录后推理 img_path f./tmp/{img_file.filename} img_file.save(img_path) results model.predict(img_path, conf0.5) # 提取检测框、类别、置信度组装成 JSON 返回 detections [] for r in results: for box in r.boxes: x1, y1, x2, y2 box.xyxy[0].tolist() conf float(box.conf[0]) cls int(box.cls[0]) detections.append({ bbox: [x1, y1, x2, y2], conf: conf, class: model.names[cls] }) return jsonify({detections: detections, count: len(detections)}) if __name__ __main__: app.run(host0.0.0.0, port5000)这个接口的妙处在于模型只加载一次每次请求做推理然后把结果转成 JSON。前端不管是 Web 还是移动端拿到这个 JSON 都能自己画框、数数量、做图表。注意conf0.5这个阈值车流检测场景建议在 0.4~0.5 之间调阈值太高会漏检远处的小车太低会出现大量误检框后面的避坑章节我会细说。4.2 Web 端接入实时检测结果WebSocket 推送代替轮询如果 Web 端要展示实时车流画面用 HTTP 轮询是能跑但不专业的做法视频流每秒 25 帧HTTP 轮询每秒请求 25 次后端和前端都受不了。正确的做法是用 WebSocket 推流后端检测完一帧直接把结果 push 给前端前端画框。这个设计在答辩时是加分项因为评委喜欢看到有工程意识的方案。WebSocket 的实现不复杂用flask-sock或websockets库都行。但这里有一个现实考量车流检测系统的实时性要求并不高监控场景统计车流量按秒计就够了不需要每帧都推。我的做法是检测线程以 5 FPS 的频率处理视频流每 0.2 秒推一帧结果给前端这样既真实展示实时检测效果又把计算压力降低到可接受的水平。前端拿到同一条 WebSocket 通道的推送再做车辆的轨迹跟踪或计数这就从检测系统升维成了车流量统计系统。4.3 移动端只用来看结果不做本地推理省掉 80% 的复杂度移动端在毕设里的定位是远程查看。很多同学一上来就想把 YOLOv8 跑到手机上然后卡在模型转换和算力不足上项目拖到中期还没跑通。正确的做法是App 通过 HTTP 请求调用后端的/detect接口把图片传上去拿到检测结果和车流量数据用图表展示历史趋势。这个方案对移动端零压力一台普通的 Android 模拟器就能跑iOS 也能用同一套 HTTP 方案。如果你想体现一点技术深度可以加一个抓拍上报功能用户在移动端拍照上传后端把照片保存下来并追加到数据集里作为增量训练素材形成数据闭环。这个功能让开源数据集 自标注的链路变得自然也是答辩时一个很好的亮点。5. 车流检测的避坑指南5 个能把人逼疯的问题和解决办法5.1 漏检严重远处的车全没检测出来现象模型在验证集上 mAP 有 0.85但拿到实际路口视频上一跑远处的车辆频繁漏检尤其是画面里只占 20×20 像素的小车。原因训练数据的标注框大多集中在近景大车上模型对小目标的特征学习不足。另外conf阈值设置过高也会导致低置信度的远车被过滤掉。解决先在推理时把conf降到 0.35 看效果如果远车能检测出来但误检变多说明是阈值问题如果降到 0.3 还是漏检说明是训练数据问题需要补充远处车辆较多的标注样本。另一个思路是把imgsz从 640 提到 960输入分辨率越高小目标保留的特征越多代价是推理速度变慢。5.2 同一个车被框了两次NMS 失效的真相现象一辆车同时被两个重叠的框和一个单独的框检测到最终输出里出现了重复框。原因YOLOv8 的 NMS 后处理机制对同一位置的高置信度框有过滤但当目标出现遮挡或视角变化导致置信度接近时NMS 的 IoU 阈值可能没把它们视为同一个目标。解决在model.predict里显式传入iou0.45这个参数控制 NMS 判定重叠框是否属于同一目标的阈值。IoU 阈值设置得越低重复框越少但设太低会把相邻的两辆车合并成一个框。车流检测场景我推荐 0.4~0.5 之间拥堵路口可以试 0.35。5.3 CPU 推理帧率只有 2 FPS还没开始就卡死现象代码跑通了但用 CPU 对视频流做推理帧率低得没法看直播画面全是幻灯片。原因在 CPU 上做推理确实慢尤其你用的是 yolov8s 甚至 yolov8m单帧推理时间可能到 300~500 毫秒。另一个隐藏因素是没有用 FP16 或 TensorRT纯 FP32 推理在 CPU 上只会更慢。解决CPU 场景换成 yolov8nnano 版本推理速度能提升 2~3 倍精度下降约 5% mAP对车流计数足够。如果想做板端部署RK3588、Jetson Orin 之类的平台有现成的加速方案rk3588 部署 yolov8 常见做法是导出 ONNX 再用 RKNN 工具转成 RKNN 格式跑 NPU这块工作量不小建议至少留出两周时间。注意 yolov8n 是 n 不是 s很多新手没看文档就把模型名写错跑起来才发现用的是 nano 的权重。5.4 训练到一半 loss 变 NaN梯度爆炸的经典病因现象训练在某个 epoch 后 loss 突然变成 nan然后 mAP 一路归零前面几十个 epoch 的成果全部作废。原因最常见的是学习率设置太高或 batch size 太大导致显存溢出后状态异常。还有一个隐蔽原因是数据里有 0 像素的纯黑图模型在这种图上输出 NaN。解决先把lr0降到 0.0005 重跑这个问题多半能解决。同时检查数据里有没有全黑或全白的损坏图片写一个脚本扫一遍把像素方差为 0 的图删掉。有一个后悔药值得知道训练每 5 个 epoch 自动保存 checkpoint如果 loss 变 NaN可以用上一次正常保存的权重runs/traffic_flow/weights/last.pt恢复。5.5 转换 ONNX 后推理结果全错归一化方式的坑现象导出 ONNX 后推理检测框全部偏移到画面边缘跟原始模型的结果完全对不上。原因YOLOv8 的预处理包含了图像归一化但 ONNX 导出时默认输入是[1,3,640,640]的 RGB 浮点张量需要调用方自己做好归一化。很多推理代码直接读图转数组就塞进模型颜色通道和归一化尺度全错。解决用官方yolo export modelbest.pt formatonnx导出的 ONNX 已经包含了预处理逻辑的元数据但用 onnxruntime 手动加载时需要自己对齐letterbox和归一化。一个省事的方案是直接用ultralytics库自带的推理接口它会自动处理预处理不要手动写 inference 代码。车流检测的落地场景下能用现成接口就别重复造轮子这属于典型的看起来简单做起来全是坑的操作。6. 性能验证与答辩素材准备一份拿得出手的评估报告怎么产出来模型训练完不是终点你还需要一套能证明这个系统确实有效的验证流程。毕设答辩时老师不关心你的代码风格关心的是三件事检测精度多少、跟同类方案比强在哪、系统能不能实际运行。对应到素材上你需要三类产出精度指标图、推理速度测试表、一段边检测边计数的演示视频。第一类产出很好拿训练结束后runs/traffic_flow/目录下已经生成了results.png包含 loss 曲线和 mAP 曲线把它放进论文当配图再配合一张测试集的结果预览图就够了。第二类产出需要你自己动手测用一个单独的测试视频记录不同conf阈值下的检测帧率和漏检数整理成表格用来证明你调过参、验证过性能。第三类产出是加分项截取一段路口视频用训练好的模型跑推理把检测框和计数画面导出成 MP4放进答辩 PPT 里演示。这个视频建议录一段夜间场景的因为夜间车流检测通常效果会差一些如果你能在夜间场景也有不错的表现说明你的系统有实用价值老师的印象分会差别很大。如果你的选题偏向硬件可以考虑加上 RK3588 板端部署的环节训练好的模型导出 ONNX再转 RKNN 上板推理。但这一步务必放在毕设中期之前启动模型转换和板端调试的时间评估保守一点两周起步。它的价值不只是多一个端而是让你的课题从基于 YOLOv8 的车流检测升级成从数据训练到边缘部署的完整链路在答辩时的技术上限会明显高出一档。最后说一个我自己踩过的坑也算是一个习惯不管题目多忙一定要从第一天就在一个独立目录里维护自己的训练日志包括数据集来源、每轮实验的参数、跑出来的 mAP 变化。做车流检测最怕的不是模型效果差而是答辩前两周想不起来自己当时为什么用imgsz640而不是 960或者为什么把验证集选了那两段视频。把每个当时觉得无所谓的决定都记下来等写论文的时候你会感谢自己。这也是我做了几个项目之后才养成的习惯希望帮到你。本文还有配套的精品资源点击获取