基于YOLOV8与ByteTrack的道路车流量检测系统实战 简介这份资源面向计算机、人工智能方向的毕业设计学生与交通检测开发者提供一套可直接运行的道路车流量检测系统。系统基于YOLOv8实时目标检测算法用Python实现车辆识别与流量统计适用于交通管理、城市规划等场景无需从零搭建框架。压缩包共307个文件约155.1MB包含126个py源码、125个pyc编译文件、37个yaml配置、2个pt权重与2个onnx模型以及mp4测试视频、jpg示例图和xml标注数据覆盖训练、推理与部署全流程。已有88人学习下载。资源附带训练好的模型与数据集代码结构清晰、注释详尽读者可直接复现检测效果也能借助配置文件和权重快速迁移到自有场景对毕业设计而言既是实用工具也是理解YOLOv8工程落地的学习范例。1. 道路车流量检测系统从一段路口视频到可用的车流统计手头有一段路口监控视频想统计每个方向过了多少辆车、车型分布如何、有没有拥堵苗头这件事用 Python 加 YOLOV8 完全能自己搭起来。标题里说的「系统完整代码以及训练好的模型数据都有可以直接使用」落到工程上其实是一套固定套路用 YOLOV8 做车辆检测用跟踪算法给每辆车分配 ID再用一条虚拟线或一个区域做越线计数最后把结果写进 CSV 或画到画面上。它解决的是「不依赖厂商盒子、不花授权费自己就能跑出车流量数据」的问题适合做交通调研的学生、做智慧路口原型的嵌入式工程师以及需要快速验证算法可行性的算法同学。下面我按自己实际搭过几套的经验把选型、代码、参数和踩过的坑一次讲清楚你照着能复现出一套能出数的系统。2. 车流量检测的技术选型为什么是 YOLOV8 加跟踪而不是纯检测2.1 检测、跟踪、计数三段式拆解车流量统计的本质不是「认出车」而是「数清楚车」。只做检测的话同一辆车在连续 30 帧里会被检出 30 次直接计数会得到 30 倍虚高的结果。所以完整链路一定是三段检测负责每帧找出车在哪跟踪负责把跨帧的同一个目标串成一条轨迹计数逻辑负责判断这条轨迹有没有穿过你关心的那条线或那个区域。YOLOV8 在这套链路里承担第一段。它相比 YOLOv5 的主要变化是 anchor-free 检测头和解耦头设计对小目标和密集车流更友好路口场景里车辆互相遮挡是常态这点很关键。跟踪这一段工程上最省事的是 ByteTrack它不依赖外观特征只靠检测框的 IoU 和运动预测做关联速度快、显存占用低GTX1660Ti 这种级别的卡也能跑到实时。计数这一段没有现成库得自己写核心就是判断轨迹点序列是否跨越了预设线段。选型上还有一个现实理由YOLOV8 的生态足够成熟ultralytics一个包就把训练、推理、导出全包了预训练权重直接能检出 car、bus、truck、motorcycle 这几类路口场景基本够用。如果你要区分具体车型或者识别车牌那才需要自己标注数据再微调否则拿官方权重就能起步。2.2 环境搭建CPU 版和 GPU 版怎么选先装环境。Python 建议 3.8 到 3.10太新的版本偶尔会和 torch 的轮子对不上。下面这套命令在 Ubuntu 20.04 和 Windows 上都能用CPU 版和 GPU 版差别只在 torch 的安装源。# 建虚拟环境避免污染系统 Python python -m venv traffic_env source traffic_env/bin/activate # Windows 用 traffic_env\Scripts\activate # 先装 torch。CPU 版用下面这行 pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 有 NVIDIA 显卡的用这行cu118 对应 CUDA 11.8按自己驱动版本改 # pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 再装 ultralytics 和跟踪、可视化依赖 pip install ultralytics opencv-python numpy lapultralytics会自动带上 YOLOV8 的推理和训练接口lap是 ByteTrack 做匈牙利匹配要用的线性分配库漏装它跟踪会直接报错。CPU 版推理一帧 640 尺寸大概 200 到 400 毫秒只适合验证逻辑真要跑视频流一张 6G 显存的卡就能实时。装完用yolo checks确认环境它会打印出 torch 版本、CUDA 是否可用、以及能识别的设备。提示如果pip install lap在 Windows 上编译失败直接装预编译轮子pip install lap --only-binary :all:别去折腾 Visual C 构建工具。2.3 预训练模型直接可用但要知道它的边界官方yolov8n.pt是最小的 nano 版本6MB 左右检出 COCO 的 80 类其中和交通相关的是 car2、motorcycle3、bus5、truck7。路口场景直接拿它推理就能用但有两个边界要清楚一是夜间或逆光下小目标召回会掉二是它不区分轿车和 SUV也不认车牌。如果你的需求只是「数车流量」这些边界不影响如果要分车型统计就得自己标注数据微调yolov8训练自己的数据集那套流程走一遍即可。模型选择上n 版最快但精度最低s 版是精度和速度的平衡点m 版以上在路口这种密集场景召回更好但更吃显存。我一般先用 n 版把整条链路跑通确认计数逻辑没问题再换 s 版提升检出率。换模型只改一行权重路径不用动其他代码。3. 用 YOLOV8 加 ByteTrack 跑通车流量计数的最小代码3.1 检测加跟踪的推理主循环下面这段是核心把检测、跟踪、计数三件事串起来。计数逻辑用「越线检测」在画面里画一条虚拟线车辆轨迹从线的一侧到另一侧就算一次。import cv2 from ultralytics import YOLO from collections import defaultdict # 加载预训练模型换成 yolov8s.pt 可提升精度 model YOLO(yolov8n.pt) # 只保留交通相关类别COCO 索引2 car, 3 motorcycle, 5 bus, 7 truck VEHICLE_CLASSES [2, 3, 5, 7] # 虚拟计数线起点和终点坐标按自己视频分辨率改 LINE [(100, 400), (1180, 400)] # 记录每条轨迹的历史中心点用于判断越线方向 track_history defaultdict(list) # 每个方向已计数的轨迹 ID 集合防止同一辆车重复计数 counted_ids set() count_up, count_down 0, 0 cap cv2.VideoCapture(road.mp4) fps cap.get(cv2.CAP_PROP_FPS) while cap.isOpened(): ret, frame cap.read() if not ret: break # persistTrue 让跟踪器在帧间保持状态这是跨帧跟踪的关键 results model.track(frame, persistTrue, classesVEHICLE_CLASSES, trackerbytetrack.yaml, verboseFalse) if results[0].boxes.id is not None: boxes results[0].boxes.xywh.cpu().numpy() ids results[0].boxes.id.int().cpu().numpy() for box, tid in zip(boxes, ids): cx, cy int(box[0]), int(box[1]) track_history[tid].append((cx, cy)) # 只保留最近 30 个点避免内存无限增长 if len(track_history[tid]) 30: track_history[tid].pop(0) # 越线判断用轨迹前后两点相对线的位置变化 if len(track_history[tid]) 2 and tid not in counted_ids: prev track_history[tid][-2] curr track_history[tid][-1] if crossed_line(prev, curr, LINE): if curr[1] prev[1]: count_down 1 else: count_up 1 counted_ids.add(tid) # 画线和计数结果 cv2.line(frame, LINE[0], LINE[1], (0, 255, 255), 2) cv2.putText(frame, fUP: {count_up} DOWN: {count_down}, (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3) cv2.imshow(traffic, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()model.track是 ultralytics 封装好的接口persistTrue让跟踪器记住上一帧的状态这是跨帧关联的前提漏了这个参数每帧都会重新分配 ID计数直接失效。classes参数把非车辆类别过滤掉减少跟踪器负担。track_history存每个 ID 的中心点轨迹越线判断就靠它。counted_ids是防重复计数的后悔药同一辆车压线抖动时不会数两次。3.2 越线判断函数与参数含义上面调用的crossed_line得自己实现核心是判断线段 AB 是否和计数线 CD 相交。def ccw(a, b, c): # 判断点 c 在有向线段 ab 的哪一侧 return (c[1] - a[1]) * (b[0] - a[0]) (b[1] - a[1]) * (c[0] - a[0]) def crossed_line(p1, p2, line): # p1-p2 是轨迹前后两点line 是计数线两端 a, b line # 两次 ccw 异号说明两线段相交 return ccw(p1, a, b) ! ccw(p2, a, b) and ccw(p1, p2, a) ! ccw(p1, p2, b)这是标准的线段相交判定ccw判断点的相对方位两次判断异号就说明轨迹穿过了计数线。参数上唯一要调的是计数线坐标LINE它必须和你的视频分辨率匹配。1080P 视频里线画在 y400 可能偏上720P 里就合适跑之前先用cap.get打印宽高确认。3.3 参数调优置信度、IoU 和跟踪阈值默认参数在车流稀疏时够用密集路口必须调。三个关键参数参数默认值路口场景建议作用conf0.250.3~0.4检测置信度阈值调高减少误检iou0.70.5~0.6NMS 的 IoU 阈值调低减少重叠框trackerbytetrack.yaml可换 botsort.yaml跟踪器配置遮挡多用 botsort置信度调到 0.3 以上能滤掉大量远处小目标的误检但调太高会漏掉被遮挡的车。IoU 调到 0.5 能缓解密集车流里同一辆车出多个框的问题。如果遮挡特别严重把跟踪器换成botsort.yaml它带外观特征重识别能力更强代价是慢一些。这些参数在model.track里直接传比如model.track(frame, conf0.35, iou0.5, ...)。4. 车流量检测的避坑与排查那些让计数翻车的细节4.1 同一辆车被数了十几次现象是计数结果远高于实际车流一辆车过去数字跳好几下。原因是轨迹 ID 在帧间不稳定跟踪器把同一辆车当成多个目标每个新 ID 都触发一次越线。解决办法是提高检测稳定性把 conf 调到 0.35 以上换 s 版模型提升召回跟踪器从 bytetrack 换成 botsort。另外counted_ids这个集合必须保留它是最后一道防线即使 ID 抖动只要轨迹中心点没真正越线就不会重复计数。4.2 车辆停在线上导致反复计数现象是路口红灯时数字乱跳。原因是车停在计数线附近中心点在线的两侧来回抖动每次都判定为越线。解决办法是加一个「越线确认」机制轨迹点必须连续 N 帧都在线的另一侧才确认计数或者用轨迹的位移方向而不是单帧位置判断。我一般要求轨迹前后两点距离大于 5 像素才做越线判断静止抖动直接被过滤。4.3 CPU 推理慢到没法看现象是 CPU 版跑 1080P 视频一帧要一秒画面卡成幻灯片。原因是 YOLOV8 默认输入尺寸 640CPU 上矩阵运算慢。解决办法有三个把输入尺寸降到 416 或 320用model.track(frame, imgsz416)换 n 版模型或者抽帧处理每 3 帧推理一次中间帧用跟踪器预测位置。抽帧对车流量统计影响很小因为车不会在一帧内跨过整条线。4.4 夜间和逆光下漏检严重现象是白天计数正常晚上数字腰斩。原因是预训练模型在低照度下特征提取能力下降。解决办法是先做图像增强再推理用 CLAHE 做自适应直方图均衡或者 gamma 校正提亮暗部。代码上在model.track之前加一行frame cv2.createCLAHE(2.0, (8,8)).apply(cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY))再转回三通道。如果夜间是刚需最彻底的办法是标注一批夜间数据微调模型。4.5 计数线和行驶方向对不上现象是上行和下行数字反了或者只统计到一个方向。原因是计数线的坐标方向和你预期的行驶方向不一致crossed_line只判断相交不判断方向方向靠curr[1] prev[1]这种 y 坐标比较来定。如果摄像头是斜装的y 坐标比较就不准得改成沿行驶方向的投影判断。跑之前先用一帧画面把线画出来看一眼确认线是横跨车道而不是顺着车道。5. 从能跑到好用把车流量数据落成可用报表链路跑通只是起点真正交付时还得把逐帧结果变成能看的报表。我的习惯是在主循环里把每次计数事件写进一个列表记录时间戳、方向、车型循环结束后用 pandas 汇总成每分钟车流量。import pandas as pd from datetime import timedelta # 计数事件列表主循环里每计数一次 append 一条 events [] # 元素形如 {frame: n, time: t, dir: up, cls: car} # 循环结束后转成 DataFrame df pd.DataFrame(events) df[minute] df[time].apply(lambda s: str(timedelta(secondsint(s)))[:-4]) report df.groupby([minute, dir, cls]).size().unstack(fill_value0) report.to_csv(traffic_report.csv, encodingutf-8-sig) print(report.head())encodingutf-8-sig是为了 Excel 打开不乱码这个坑我踩过不止一次。groupby按分钟、方向、车型三个维度聚合出来的就是一张能直接给调研用的表。如果想看趋势再画个折线图yolov8画损失函数曲线图那套 matplotlib 用法直接套过来就行。验证计数准不准最土也最可靠的办法是人工数一段。挑 5 分钟视频自己盯着数一遍和系统结果对比误差在 5% 以内就算可用。误差大就回到第 4 章逐条排查。我一般还会把带计数标注的视频导出一份方便回看哪一帧数错了。进阶一点可以把计数结果接到一个简单的 Web 看板上用 Flask 起个服务前端定时拉 CSV 刷新。这套东西部署到 RK3588 这类边缘设备上就是一个小型路口终端rk3588部署yolov8的流程是把模型导出成 ONNX 再用 RKNN 工具链转换推理部分换成 RKNN 的接口计数逻辑原封不动。低显存设备上跑不动大模型n 版加抽帧是唯一现实的选择。最后说个我自己的习惯每次换视频源第一件事不是跑计数而是先只跑检测把每一类的检出框画出来看一遍确认模型在这个场景下认得出车。检测都不准后面跟踪和计数全是空中楼阁。这套系统没有玄学出问题一定是某个环节的参数或逻辑没对上逐段验证就能定位。希望帮到你。本文还有配套的精品资源点击获取