基于YOLOv8的道路车流量检测与计数系统实践
发布时间:2026/9/11 13:47:34来源:尧图网络
简介这套道路车流量检测系统基于Python与YOLOv8算法构建面向交通管理、城市规划及毕业设计等场景提供从目标检测到车辆计数的完整落地工具。整个资源包共307个文件包含126个Python脚本、125个编译后的pyc文件、37个YAML配置文件以及训练好的pt/onnx模型、数据集标签和演示视频整体约155.1MB代码结构清晰、注释详细可大幅降低二次开发门槛。预处理、检测、计数、可视化等模块分工明确用户无需从零搭建框架运行训练好的模型即可完成实时车流统计。当前已有87人学习下载对于需要快速复现车辆检测项目的学生或开发者而言这份资料既可直接部署也能作为系统学习YOLOv8工程实现的样例参考。1. 道路车流量检测系统为什么值得自己搭如果你接到的任务是统计某条路双向各有多少辆车最直接的做法是在路口埋地磁线圈或请人蹲点数。但现在的需求往往是临时性的某个路口要评估是否该加红绿灯某段高速要分析拥堵时间窗口或者你只是想给毕业设计找一个既能拿得出手又能真实运行的视觉项目。这时候基于摄像头视频的车辆检测与计数系统就是成本最低、部署最快的方案。一个 Python 脚本加一块普通 GPU把视频流喂进去几秒钟就能给出车流量统计。而 YOLOv8 作为当前目标检测领域成熟度最高的框架之一正好把模型训练难和部署难这两件事都压低了配合完整的代码和训练好的权重基本就是开箱即用。这篇博客就按照我实际搭这套系统时走过的路把从环境配置、代码结构到计数逻辑和常见排错讲清楚让你拿到类似工程后能直接改出能用的版本。2. YOLOv8 做车流量检测的原理与选型2.1 从检测到计数的完整链路车流量检测不是单纯地把车框出来就算结束它背后是一条检测 → 跟踪 → 计数的流水线。YOLOv8 负责的第一件事是在每一帧图像里找出所有车辆目标输出每个目标的边界框坐标、类别和置信度。但视频是连续的如果在每一帧里独立检测同一辆车在连续帧里会产生多个框我们没法知道哪个框是同一辆车计数就会爆炸。所以需要跟踪算法给每一辆车分配一个唯一 ID比如 DeepSORT 或者 ByteTrack然后在视频帧之间关联同一个 ID 的车辆。最后一步是计数逻辑在画面里人为指定一条虚拟线或一个区域当某辆车中心点穿过虚拟线或者进入、离开区域就记一次数。这样跑完一段视频就能得到南向北 120 辆北向南 98 辆这样的结果。YOLOv8 在这个链路里承担的是最重的感知部分。它的网络结构延续了 CSPDarknet 的思想主干网络提取特征颈部用 PAN-FPN 融合多尺度特征头部则是解耦的分类头和回归头。和 YOLOv5 相比YOLOv8 把 anchor-based 换成了 anchor-free回归分支直接预测目标中心点到四条边的距离这让它在小目标比如远处驶来的车辆上的表现更稳。而且 YOLOv8 的 head 里用了 Distribution Focal Loss对边界框的定位精度也有提升在车辆这种形状规整、尺度变化大的类别上往往能比 YOLOv5 高出两三个点的 mAP。2.2 为什么选 YOLOv8 而不是 Faster R-CNN 或 YOLOv5在选型时我其实纠结过一阵子。Faster R-CNN 是两阶段检测器精度上限理论上更高但推理速度通常在 5~10 FPS 左右对视频流来说太勉强了。YOLOv5 虽然也成熟但社区里改版太多部署时容易遇到权重文件版本不匹配的问题。YOLOv8 由 Ultralytics 官方维护接口统一训练和推理的封装做得非常干净一个YOLO类就能完成加载、预测和可视化。对于车流量这种对实时性有要求的任务YOLOv8s 在 GTX 1660 Ti 上能做到接近 60 FPS 的推理速度足够处理 30 FPS 的常规监控视频。还有一点很关键YOLOv8 支持直接输出到视频和摄像头也支持导出成 ONNX、TensorRT 等格式方便后续挪到 RK3588 这类边缘设备上跑。如果你的最终目标是低成本部署而不是在 PC 上演示那 YOLOv8 的生态会更顺。不过选型也要看你的硬件条件如果只有 CPU 且需要实时那 YOLOv8n 这个 nano 版本是唯一选择它的模型权重只有 6MB 左右在 i5 处理器上也能跑到 20 FPS 上下。2.3 数据集与预训练模型的构成一个可以直接使用的车流量检测系统数据通常包含两部分用于训练或微调的车辆检测数据集以及训练好的 YOLOv8 权重文件。数据集常见的类别是 car、bus、truck、motorcycle 这四类但很多简化项目的训练标签只保留一个类别 car把所有轮式车辆都记成车这样计数准确率更容易做高因为类间混淆没了。数据集的目录组织要保持 YOLOv8 读取的习惯datasets/ vehicles/ images/ train/ img_001.jpg val/ img_100.jpg labels/ train/ img_001.txt val/ img_100.txt每张图片对应的 txt 文件里每一行表示一个目标格式是class x_center y_center width height前四个值都是相对于图片宽高归一化后的坐标。你需要确认手里数据的标注是否严格满足这个格式。最常见的翻车情况是数据集来自某个开源平台标注用的长宽是无归一化的整数值直接拿去训练损失函数会降不下去模型预测结果也完全不对。模型部分一般会拿到两个文件一个是best.pt是从你的数据集或者通用车辆数据集上训练出来的最终权重另一个是last.pt是训练过程中最后一个 epoch 的权重通常比best.pt大一点但效果不一定更好。我在实际项目里只会用best.pt并且会把训练时的.yaml配置文件保留下来因为best.pt里存储的类别数、类别名称要和你的推理脚本完全对齐否则会出现类别序号错位这种很隐蔽的问题。3. 环境准备与代码结构拿到完整代码后先跑通3.1 Python 环境与依赖安装拿到一套完整的 Python 项目最忌讳的就是立刻去跑main.py。先确认 Python 版本和依赖环境。YOLOv8 官方要求 Python 3.8 以上我用的是 3.10PyTorch 2.0 以上。如果你的显卡是 NVIDIA 系的先装 CUDA 版本的 PyTorch再装 Ultralytics 包顺序反了容易出现torch.cuda.is_available()返回 False 的诡异问题。推荐用虚拟环境隔离避免把系统 Python 搞乱。我这里给出 Linux/Mac 下的操作Windows 做法一致只是把source换成venv\Scripts\activate# 1. 创建虚拟环境 python -m venv venv source venv/bin/activate # 2. 安装 PyTorch注意先从官网确认 CUDA 版本 # 例如 CUDA 11.8 执行下面这行 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 3. 安装 YOLOv8 及其依赖 pip install ultralytics opencv-python numpy # 4. 如果做跟踪需要额外安装 ByteTrack 相关依赖 pip install lap这里的逻辑是有顺序的ultralytics包会同时安装torch的 CPU 版本如果你不提前装但 CPU 版在推理视频时会慢到无法接受。另外opencv-python负责视频读写numpy负责数组运算lap是求解线性分配的库DeepSORT 和 ByteTrack 都会用到。如果你的环境是 Python 3.12要注意部分依赖可能没有预编译的 wheel到时候直接用 Python 3.10 最省心。装完后验证一下from ultralytics import YOLO print(YOLO.__name__)如果能正常打印说明环境通了。3.2 代码目录结构的作用拿到别人的完整代码第一件事是读懂目录结构。一套标准的 YOLOv8 车流量检测系统目录一般长这样traffic_counter/ main.py detect_count.py config.py utils/ __init__.py tracker.py counter.py models/ best.pt videos/ test_road.mp4 datasets/ ... requirements.txtmain.py是入口通常负责读取视频路径、调用模型和输出结果。detect_count.py封装了检测和计数的核心类。config.py存放所有可调参数比如置信度阈值、IOU 阈值、虚拟线坐标。utils/tracker.py是跟踪器的封装utils/counter.py是计数逻辑。模型的权重文件放在models目录视频样本放在videos目录。你要改的东西很有可能是config.py里的虚拟线位置而不是detect_count.py里的算法。因为不同摄像头画面角度不一样在画面中间横一条线线的坐标y值是要按你的实际画面比例调整的。我见过有人把这个坐标写死在代码里换了个视频就计数失败最后查了半天才找到问题。3.3 加载训练好的模型做最小推理跑通模型是最低要求但不等于跑通整个系统。先写一个最小脚本确认权重文件能加载、能对单张图片做预测from ultralytics import YOLO # 加载训练好的权重 model YOLO(models/best.pt) # 对一张测试图片进行推理 results model.predict( sourcevideos/test_frame.jpg, conf0.25, iou0.45, saveTrue ) # 查看检测结果 for r in results: boxes r.boxes for box in boxes: print(box.cls, box.conf, box.xyxy)这里conf0.25是置信度阈值低于 0.25 的检测框会被丢弃iou0.45是 NMS 的 IoU 阈值用来去掉重叠的框。boxes.xyxy是边界框的像素坐标格式是[x_min, y_min, x_max, y_max]。如果你打印出来的cls是一个数字比如 0、1、2不要慌这很正常model.names可以查到数字对应的类别名。如果这一步输出为空大概率是你的权重文件类别名和预训练类别不匹配或者测试图片本身就没有车。先原图打开确认一下别急着调置信度。4. 道路车流量检测的核心实现检测、跟踪与计数4.1 基于虚拟线的车辆计数逻辑车辆计数的算法看起来不难但坑很多。最常见的做法是虚拟线检测法在视频画面里指定一条线段通常是从左到右横穿车道的水平线然后对每一帧检测到的每个车辆框计算其中心点(cx, cy)判断cy是否与虚拟线的y坐标相交并且记录这个中心点是否是第一次穿过。更稳妥的做法是结合跟踪 ID 来做一辆车从出现到消失其中心点轨迹会经历y line_y到y line_y或相反的过程只有跟踪 ID 对应的轨迹跨过虚拟线时才计一次数。如果不加跟踪直接用每一帧中心点在线附近就加一同一辆车在虚拟线附近抖动两三次就会重复计数。所以跟踪是必须的。代码里可以这样组织def is_crossing_line(prev_center, curr_center, line_y): # prev_center 是上一帧的中心点curr_center 是当前帧中心点 # 返回 True 表示这一帧内该车跨过了虚拟线 return (prev_center[1] - line_y) * (curr_center[1] - line_y) 0这个函数利用叉积判断符号变化比直接比较绝对值更鲁棒能容忍跟踪框的小幅抖动。实际在视频帧率较低比如 10 FPS时车辆在相邻帧间可能跳过了line_y的位置但中心点符号依然会变化所以这种方法仍然有效。4.2 从视频帧到实时计数主循环怎么写把检测、跟踪、计数串起来主循环通常长这样import cv2 from ultralytics import YOLO from utils.counter import VehicleCounter from utils.tracker import ByteTrack model YOLO(models/best.pt) tracker ByteTrack() counter VehicleCounter(line_y500) # 虚拟线 y 坐标 cap cv2.VideoCapture(videos/test_road.mp4) frame_id 0 while cap.isOpened(): ret, frame cap.read() if not ret: break # 1. YOLOv8 检测 results model.predict(frame, conf0.3, iou0.5, verboseFalse) boxes results[0].boxes.xyxy.cpu().numpy() scores results[0].boxes.conf.cpu().numpy() classes results[0].boxes.cls.cpu().numpy() # 2. 更新跟踪器得到每个框的 ID tracks tracker.update(boxes, scores, classes, frame) # 3. 更新计数器 counter.update(tracks, frame_id) # 4. 可视化 annotated results[0].plot() cv2.line(annotated, (0, counter.line_y), (annotated.shape[1], counter.line_y), (0, 0, 255), 2) cv2.imshow(Traffic Counter, annotated) if cv2.waitKey(1) ord(q): break frame_id 1 cap.release() cv2.destroyAllWindows() score counter.get_count() print(f通过虚拟线的车辆总数: {score})这段逻辑很清晰每次从视频中读一帧先用model.predict得到所有车辆框再把框交给ByteTrack更新轨迹和 ID最后把带 ID 的轨迹交给计数器判断是否跨线。值得注意的点是model.predict每次调用都会重新做一次前向推理如果中间有预处理、后处理的开销帧率会比直接调用model.track低一些。ultralytics其实内置了model.track()方法它可以同时做检测和跟踪但内置跟踪器是botsort默认参数不一定适合所有场景。我的经验是如果只是做车流量model.track(persistTrue)更快但一旦车辆重识别效果不好还是得用外挂的 ByteTrack 来调参。ByteTrack的核心参数是track_thresh和match_thresh。前者是检测框保留的置信度阈值通常设 0.25~0.4太高容易漏掉远处小车太低会引入误检后者是匹配距离阈值控制两帧之间的框关联跨度设 0.9~0.99。如果视频里车辆密集且互相遮挡match_thresh可以适当调低避免跟踪 ID 频繁切换。4.3 关键参数表控制精度的旋钮为了让这个系统能够在实际场景中调整到可用我把最常调的参数整理成下表每一个参数修改后都要重新跑一遍视频验证参数所在位置推荐范围调高时的效果调低时的效果confYOLO 检测0.25~0.5减少误检但可能漏掉远处车辆增加召回但会出现大量假框iouNMS0.4~0.7同一辆车保留更多重叠框抑制较多重叠框可能漏掉近距离车辆line_y计数器画面高度 1/3 ~ 1/2计数线更靠近摄像头车辆越大越好检测计数线更远车辆小容易漏检track_threshByteTrack0.2~0.5只跟踪高置信度目标允许跟踪低置信度的模糊车辆match_threshByteTrack0.8~0.99跟踪更保守ID 不易切换跟踪更激进可能跳 ID另外视频帧率也会影响计数。如果输入视频是 30 FPS但检测速度只有 15 FPS你会跳帧这样车辆轨迹中心点会间隔更大跨线检测依然有效但跟踪的匹配会变难。我的做法是先检查cap.get(cv2.CAP_PROP_FPS)确认真实帧率然后在tracker.update里传frame_time让 ByteTrack 的卡尔曼滤波器知道时间步长这样预测位置才准确。5. 优化与排错把车流量准确率从能跑提到能用5.1 用 ROI 屏蔽干扰区域很多时候摄像头画面里不仅有道路还有人行道、商铺招牌、甚至树叶在晃动。YOLOv8 的检测器会把行人也框出来如果你的训练数据里没有行人类别它仍然可能把行人误认为车辆尤其是在遮挡情况下。直接限制检测区域是最有效的办法在config.py里定义多边形 ROI只保留中心点在 ROI 内的检测框。多边形可以用cv2.fillPoly配合掩膜实现也可以简化为只在虚拟线附近一个带状区域检测。我习惯的做法是统计虚拟线上下各 100 像素范围的检测框因为车辆跨线时必然在这个区域内。你可以把counter.py里判断中心点坐标的代码增加一个is_in_roi(center)函数让只有中心点落在道路车道范围内的车才参与计数。这样还能顺带解决双向车道互相干扰的问题——如果不区分方向你只需要一条计数线如果分方向就要用两条线分别在画面的左侧和右侧或者用 ROI 隔离。5.2 处理重复计数的最后一个技巧计数数偏高最常见的两个原因一是同一辆车在虚拟线附近来回轨迹抖动导致跨线检测了两次二是 tracking 丢失后重新分配了新的 ID而新 ID 也跨了线。解决第一个问题不难在计数器里维护一个字典last_id_record当某 ID 已经计过数后至少等待其中心点移动到距虚拟线 50 像素以外才允许该 ID 再次计数。解决第二个问题就要提高检测的质量而不是跟踪参数。我的验证方法是把计数过程可视化不仅绘制边界框和跟踪 ID还把每次计数事件在画面上方打印一行字回放视频时逐帧观察。一旦发现某个 ID 被计了两次就暂停检查当前帧的检测框是否丢失然后回看前面的帧确认是遮挡还是检测阈值问题。这个操作比你盲调参数高效得多。比如你发现这是夜间车灯反光导致的车身断裂那就应该考虑把置信度调低而不是改match_thresh。5.3 输出一份可验证的统计结果系统最终要交付的是一份数据。除了打印总车流量我还会把每一辆车的轨迹记录导出为 CSV包含时间戳、车辆类别、方向、车道号。这样用户可以人工抽几段视频去核对数出的车辆数而不是只看一个总数。这个 CSV 的地理意义也很直接比如可以按小时聚合就能得到高峰期车流量曲线。具体实现只需要在计数器里加一个queue每次跨线时把记录写入results.csvimport csv from datetime import datetime def log_count(vehicle_id, cls, direction, timestamp): with open(output.csv, a, newline) as f: writer csv.writer(f) writer.writerow([timestamp, vehicle_id, cls, direction])到这里整个道路车流量检测系统就已经能稳定运行了。最后的调试建议是你最好准备两段不同场景的视频一段光线好、车流稀疏用来验证系统的底线精度一段光线差、车流密集用来检验你的调参能力。把两段视频的检测结果打印出来作对比你就能知道当前系统在什么条件下可以真正直接用。本文还有配套的精品资源点击获取
网站建设高端定制企业官网