YOLOv8+ByteTrack实现实时车辆检测追踪与交通流量统计

发布时间:2026/9/27 1:32:30
YOLOv8+ByteTrack实现实时车辆检测追踪与交通流量统计 简介这是一份基于YOLOv8与ByteTrack的车辆多目标检测追踪与交通流量统计系统实现面向智能交通、计算机视觉方向的开发者与学生可用于实时车辆识别、跨帧追踪、计数统计及道路监控优化等场景。资源包共12个文件整体大小约70.72MB包含Python源码脚本、演示视频、效果图片、说明文档及备份文件目录结构简明便于直接运行和二次开发。已有69人浏览/学习。通过源码可掌握YOLOv8目标检测、ByteTrack轨迹关联、车辆计数与类型区分等核心逻辑演示视频和截图能直观展示检测追踪效果帮助快速验证算法流程、理解交通流量统计的实现思路。系统还涉及违章停车、违规变道等行为的自动识别扩展方向适合希望将目标检测与多目标跟踪落地到智能交通项目的入门及进阶学习者参考。资源来源于网络分享仅限学习交流使用。1. 实时车辆检测追踪与流量统计一套系统解决的路口排队问题当路口摄像头对着车流你想回答的其实不是一个“有车没车”的问题而是一连串问题此刻画面里有多少辆车每辆车从哪来、往哪去这个方向一分钟过了多少辆如果把问题拆给技术系统它就是标题里那套组合——YOLOv8把每一帧里的车辆框出来ByteTrack给每个框一个稳定ID再把同一辆车的框串成轨迹最后用一条虚拟检测线或“车进车出”区域完成交通流量统计。这套思路适用于城市路口、高速匝道、园区出入卡口能直接给信号灯配时、拥堵预警和调度系统喂数据。适合准备做毕业设计、课程项目或刚接手一个实时监控类业务的工程师。全文不绕弯子按「为什么这样选、怎么搭起来跑通、参数怎么调、坑在哪」的顺序写你能照着复现的最小命令和判断逻辑都在下面。2. 为什么这一套组合能覆盖“检测追踪统计”全链路2.1 YOLOv8在“只检测车辆”这件事上的三个理由车辆目标本身的尺寸分布很宽近处大巴、远处轿车尺度变化大。YOLOv8相比前代最大的变化是换成了Anchor-Free检测头直接回归中心点和宽高对尺度差异的容忍度更好。同时它默认C2f结构比C3更擅长融合多尺度特征配合SPPF小目标的召回率比YOLOv5同参数量下有可见提升。对车辆数据来说你不需要像分割任务那样精细一个框加置信度就够YOLOv8的模型体量从n到x可选给“实时”留了很大余量。我一般会在GPU上用s或m在CPU盒子上用n或s在RK3588这类边缘设备上会再压缩成INT8。第二个理由是部署生态。YOLOv8官方提供ultralytics库训练、验证、导出ONNX/TensorRT都一条命令这对不打算长期维护检测代码的流量统计项目太关键了。第三个理由是踩坑成本低网上关于YOLOv8环境搭建、YOLOv8训练参数含义、数据集处理的资料密度很高你遇到一个怪问题搜一下就有解决方案这在工程排期里是实打实的时间红利。2.2 ByteTrack为什么被选中低阈值关联加上“不分家”的轨迹检测模型只负责当前帧的框而“这辆车到底是刚进画面还是上一帧那辆”必须靠追踪器回答。ByteTrack的核心思路是“不做太复杂的外观匹配靠IoU和低置信度框把轨迹续上”。它放弃了DeepSORT里那套ReID特征先用高置信度框做关联再用低置信度框补关联从而把遮挡和漏检导致的ID Switch压下去。对车辆这种外型相似、速度方向规律的目标这个策略很有效而且它没有任何额外训练负担直接吃检测器输出。ByteTrack的默认状态也是为行人/车辆场景调的它的卡尔曼滤波器用的是匀速模型适合机动性不强的交通流。对比SORT优势在于低置信度框的使用对比DeepSORT少了ReID模型的推理开销。所以它的推荐适用场景正是这里的“路况车流”密集、外形相似、变化平缓。2.3 从一帧原始画面到一组流量数字数据流是怎么走的整套系统的数据流可以拆成四段。第一段是视频输入通过OpenCV的VideoCapture从摄像头或MP4文件读帧第二段是检测YOLOv8把每帧变成一组坐标框和置信度第三段是追踪ByteTrack把框序列关联成带ID的轨迹输出当前帧每个人目标的ID和框第四段是统计在画面里预先画好一条虚拟检测线或一个虚拟出口区域每次某个ID的框中心穿越这条线或进入离开这个区域计数器加一。这里有个工程上的关键选择统计放在追踪之后而不是检测之后因为只有同一辆车的轨迹形如“在t帧经过线”你才能准确去重不会因为一帧漏检导致同一辆车被统计两次。我见过不少把检测框直接拿去做交点判断的系统一旦目标短暂消失又出现计数就翻倍。所以建议你无论如何先从检测和追踪两条链路跑通再把统计逻辑接在追踪结果上。3. 环境搭建与车辆数据集准备先让YOLOv8能为你所用3.1 Ubuntu 20.04上的YOLOv8环境搭建CPU版三条命令跑通常见做法是先搭好Python环境再装ultralyticsCPU版本安装不依赖CUDA反而能提前把数据链路跑通方便后面换GPU。以下命令在Ubuntu 20.04上可以直接执行# 创建独立环境避免污染系统Python python3 -m venv yolo_venv source yolo_venv/bin/activate # 安装CPU版PyTorch坑少且不强行拉CUDA pip install torch2.2.2cpu torchvision0.17.2cpu -f https://download.pytorch.org/whl/torch_stable.html # 安装ultralytics自带YOLOv8命令工具 pip install ultralytics第一步创建venv环境是必须做的因为ultralytics会拉大量依赖。第二步CPU版PyTorch如果不指定indexpip会默认装CUDA版后面在无GPU机器上运行反而会报找不到驱动。第三步ultralytics的whl包较大如果网络慢可以加-i指定国内镜像。装完后验证一下安装是否可用代码是yolo predict sourcehttps://ultralytics.com/images/bus.jpg看到输出结果中有人和车的框说明环境OK。3.2 用Labelme标注车辆数据集后处理成YOLOv8能用的格式YOLOv8训练的标签是txt文件每行内容是“类别id 中心x 中心y 宽度 高度”坐标全部归一化到0到1文件名必须和图片同名且后缀是.txt。Labelme标出来的格式是JSON需要转换。网上很多人拿到一批Labelme标注后不知道怎么转这里给出去掉透明背景的转换脚本最核心的部分import json import os from PIL import Image def labelme2yolo(json_path, img_path, save_dir, categories): with open(json_path, r, encodingutf-8) as f: data json.load(f) img Image.open(img_path) w, h img.size yolo_lines [] for shape in data[shapes]: label shape[label] points shape[points] # 取矩形框左上角与右下角 x1, y1 points[0] x2, y2 points[1] cx (x1 x2) / 2 / w cy (y1 y2) / 2 / h bw abs(x2 - x1) / w bh abs(y2 - y1) / h yolo_lines.append(f{categories[label]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) save_name os.path.basename(json_path).replace(.json, .txt) with open(os.path.join(save_dir, save_name), w) as f: f.write(\n.join(yolo_lines))这个脚本的核心逻辑是把Labelme的矩形框坐标从绝对像素值转成相对宽高比例。categories是一个字典比如{car:0,bus:1,truck:2}。你需要注意的点是YOLOv8要求中心点坐标所以这里要除以图片宽高很多人错在这里直接存了左上角坐标。还有就是Labelme允许多边形标注如果你的标注是描边车辆形状而不是矩形要先把多边形换算成外接矩形否则训练出来的框会偏。处理完标签后还要按比例切分训练集和验证集通常8:2或9:1路径组织成images/train、labels/train这样两个根目录YOLOv8直接认这个结构。3.3 训练车辆模型时必调的参数与损失函数曲线怎么看训练命令本身不复杂复杂的是参数。一个最基本的训练命令是yolo detect train datavehicle.yaml modelyolov8n.pt epochs100 imgsz640 batch16 lr00.01vehicle.yaml的写法大概是train: /home/user/vehicle/images/train、val: /home/user/vehicle/images/val、nc: 3、names: [car,bus,truck]。modelyolov8n.pt表示用COCO预训练权重做迁移学习这比从头训练收敛快得多。epochs对于车辆这类类别差异不算大的任务100轮一般足够如果你数据量很大到上万张可以增加到200轮。imgsz除非你想看在更低分辨率下的表现否则固定640。batch大小由显存决定GTX1660Ti这种6GB显存跑640分辨率batch设8到16比较稳。训练完成后在你设定的runs/detect/train目录里能看到results.png它包含损失函数曲线图。如果你要单独画损失曲线可以解析训练日志里的loss值train/box_loss是边界框损失train/cls_loss是分类损失train/dfl_loss是分布损失。一个判断标准是如果val/box_loss在前30轮还在下降说明没收敛完要加epoch如果训练损失下降但验证损失在第50轮回升说明过拟合该加数据增强或者减小模型规模。很多人只看总损失不拆分量但box_loss对车辆框的位置精度更敏感。3.4 用训练好的模型跑单帧推理确认检测输出格式训练完之后你可以先拿一张测试图跑推理验证结果yolo predict modelruns/detect/train/weights/best.pt sourcetest.jpg saveTrue如果要拿到可编程调用的结果不只是存图需要在Python里手动推理from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict(test.jpg, verboseFalse) boxes results[0].boxes for box in boxes: xyxy box.xyxy.cpu().numpy()[0] # 左上角右下角坐标 conf box.conf.cpu().numpy()[0] cls box.cls.cpu().numpy()[0] print(车辆框坐标, xyxy, 置信度, conf, 类别, cls)这里的xyxy是像素坐标后面做ByteTrack关联时要直接喂这种格式。很多新手在这里顺手把框缩放了导致ByteTrack匹配错乱。所以建议你把xyxy、conf、cls三个变量直接组装成一批检测结果再调用ByteTrack。4. 用ByteTrack把检测结果串成轨迹再让流量数字从屏幕里走出来4.1 YOLOv8检测结果如何喂给ByteTrack最少依赖的对接方式ByteTrack在GitHub上有官方版本提供ByteTracker类但官方代码为了集成不同检测器写得比较重。在流量统计项目里我建议直接拿官方核心文件里最必要的部分只保留STrack、BaseTrack、KalmanFilter和主类避免多余的缓存逻辑。喂给追踪器的数据是一系列检测结果每个检测结果包含框坐标、置信度、类别。这些信息要组装成ByteTrack需要的输入格式import numpy as np from pathlib import Path import sys sys.path.append(bytetrack_path) from mstracker import ByteTrack tracker ByteTrack(min_box_area300, track_thresh0.45, match_thresh0.8) def feed_to_tracker(results): dets [] for box in results[0].boxes: xyxy box.xyxy.cpu().numpy().squeeze().tolist() conf float(box.conf.cpu().numpy().squeeze()) cls int(box.cls.cpu().numpy().squeeze()) dets.append([*xyxy, conf, cls]) dets np.array(dets, dtypefloat).reshape(-1, 6) online_targets tracker.update(dets, None) return online_targetsstrack对象的tlwh属性是目标的左上角坐标加宽高track_id是它在追踪器里的唯一编号。这里第二个参数传None表示不使用额外特征ByteTrack默认用IoU关联已足够。det里的类别字段对ByteTrack没用但如果你的统计逻辑需要区分大巴和小车可以在后面保存ID时把类别字段一起记录。4.2 交通流量计数的两种常见方案虚拟检测线与区域进出判定流量统计的核心是确定一个目标“经过指定位置”。最常见的做法是画一条虚拟检测线当车辆框中心点跨越这条线时计数一次。在OpenCV里假设检测线是y fix_y判断逻辑只需要检测目标ID是第一次从y fix_y变到y fix_y时触发计数。这有一个坑如果车辆停在检测线上中心点在两帧之间来回抖动会重复计数。解决方法是给每个ID维护一个Boolean状态只有状态从0变1或从1变0才计数且设置冷却帧。第二种方法是画一个矩形区域作为“入口”当某个ID的中心点第一次进入区域时计数离开再进入要重新计数。这种方案对拥堵、走停状态更稳。两种方案的比例在真实路口场景中虚拟线适合通畅流区域判定适合信号灯口的排队流。我的习惯是优先区域判定因为车道宽度往往超过一个像素线的鲁棒性差。4.3 基于ID和轨迹的统计去重避免一辆车被数两次这里的关键是理解ByteTrack在断帧后的行为。一个目标被遮挡两秒再出现ByteTrack可能给新ID也可能恢复旧ID取决于关联匹配阈值。如果你只统计“中心点穿过线”这一个条件断帧后恢复的旧ID可能再次穿过线导致重复计数。所以正确办法是只统计第一次穿越后续同一ID不再数。代码实现里给统计模块一个字典passed_id set()只有track_id不在这个集合中且穿越方向符合条件时计数然后加入集合。注意ByteTrack的ID号在追踪器内部是循环利用的长时间运行后旧ID可能被回收重新分配给新目标这时如果只靠ID去重某个新目标会因为“ID存在于集合中”被漏统计。解决方法是给ID加上时间戳只在30秒内生效或者统计时不依赖ID而依赖卡尔曼滤波预测的轨迹连续性。但后者实现复杂度高一般我会先加时间戳去重用遇到极端情况再考虑轨迹连续性。4.4 实时视频流接入与显示输出延迟从哪里产生cv2.VideoCapture可以从本地视频、RTSP或USB摄像头取流。实时性瓶颈主要在三处视频解码、YOLOv8推理、ByteTrack计算。ByteTrack本身非常轻相比YOLOv8推理消耗几乎可以忽略。所以重点是把YOLOv8推理降下来。对于1080p视频如果GPU是GTX1660TiYOLOv8s推理约20ms但加上视频解码和绘制单帧总耗时约40ms刚好接近25FPS。如果要上30FPS要么缩小推理分辨率到416或512要么换成YOLOv8n。以下是用OpenCV读取视频并对每一帧做追踪统计的循环import cv2 from ultralytics import YOLO model YOLO(best.pt) cap cv2.VideoCapture(road.mp4) count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break results model.predict(frame, imgsz640, verboseFalse) online_targets feed_to_tracker(results) for t in online_targets: tlwh t.tlwh tid t.track_id # 判断车辆中心点是否穿过统计线 cv2.imshow(stream, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这段循环最需要注意的是model.predict(frame, ...)每次调用都有序列化开销如果追求性能要改用model(frame, ...)或者使用streamTrue的生成器模式。在连续视频流推理时建议不要重复创建YOLO对象而是复用模型实例。对于RTSP流断线重连的问题我通常会在ret为False时做三次重试再等待5秒重新VideoCapture否则长期运行会因偶然网络抖动退出整个程序。5. 实时车辆多目标追踪的常见踩坑与排查5.1 车辆ID频繁跳变明明是同一辆车却换来换去现象画面中一辆车匀速直行右上角的ID序号一下是7一下是23过两秒变成15。原因ByteTrack的match_thresh设置过低导致匹配条件过于严格一个遮挡或几帧漏检后轨迹匹配不上被当作新目标。解决把match_thresh往大写比如从0.8改成0.9或0.98让关联更宽松。同时检查YOLOv8的conf_thres设置如果检测置信度阈值设太高导致低置信度车辆框被滤掉ByteTrack就没有低置信度框做补充关联了。我自己的经验是YOLOv8推理时把conf设为0.3iou设为0.5再配合track_thresh0.45对路口车辆场景最稳定。5.2 帧率上不去GPU却才跑到一半现象GPU利用率只有40%左右但视频帧率只有12FPS。原因瓶颈不在推理而在图像预处理和结果后处理。model.predict内部包含letterbox、颜色格式转换、NMS等模块这些在CPU上执行如果输入分辨率大、batch为1预处理会占掉10-15ms。解决将model.predict改成model(frame, devicecuda)并设置pre_transform缩短或者把imgsz从640缩到512并关闭verbose。另外检查是否开启了amp除非在特殊设备上建议保持默认True它在推理时能减少等待。还有一个常见坑是视频解码线程和推理线程串行IMX335摄像头输出H.265时CPU解码会吃满一核帧率被解码拖累解决方案是改用硬解码或降低码流。5.3 车辆重合时计数重复把大卡车数成两辆现象一辆卡车被YOLOv8检测成一个框但ByteTrack却把它当成两个ID计数翻倍。原因目标被上部车体的镂空部分或阴影切开YOLOv8在同一辆车附近输出两个重叠框ByteTrack对重叠框判定成两个不同目标。这种情况更多发生在模型训练数据里没有这类卡车样本另一个原因是置信度阈值过低双框被保留。解决在追踪器输入端做非极大值抑制NMS把针对同一物理目标的多个框合并可设置iou_threshold在0.3左右肉眼观察重叠框是否被清除。还有一个偏门但有用的办法是降低min_box_area到500滤掉那些来自远方的过小框因为远处车辆重叠导致的误框比例最高。5.4 夜间或逆光场景漏检率飙升现象路灯暗、车灯亮画面里车灯亮成一团YOLOv8对远光灯车头的检测框完全丢失。原因训练数据主要都是白天样本模型没学过强光切割下的目标形状。解决收集夜间逆光的真实数据加入训练集如果数据量少可以用图像增强模拟把亮度随机下降、加高斯噪声但效果有限。另一个工程方案是数据不变但调整预处理参数model.predict(frame, augmentTrue)开启测试时增强能小幅度提升漏检但会让推理时间翻倍。实测中最好用的还是给模型喂带自动白平衡的摄像头流很多摄像头在夜间会切换红外模式画面从彩色变黑白你需要在训练集中也加入灰度车辆框样本否则一样翻车。5.5 在RK3588上部署YOLOv8时帧率只有个位数现象GPU服务器上能跑50FPS的YOLOv8s用rknn-toolkit2转成RKNN模型后在RK3588上只有5FPS。原因没有做量化或选错量化类型以及模型算子与NPU不兼容。解决先用rknn-toolkit2导出INT8量化模型校准数据集选500张车辆图片避免用COCO全类别校准。转模型时把target_platform设为rk3588开启optimize_level1。如果某些算子不兼容比如YOLOv8的DFL层考虑使用RKNN官方提供的yolov8.rknn示例它内部做了算子替换。如果跑不到目标帧率还要考虑把输入分辨率从640降到512或者换成YOLOv8n。部署时加载RKNN模型用rknnlite.api和用ultralytics时完全不同要重写推理代码。6. 进阶优化让交通流量系统更准更快一个可加的改进都在这里6.1 改进YOLOv8的head或加入注意力机制对小目标的作用点你的交通流量系统如果对远处小目标经常漏检可以尝试把YOLOv8的检测头换成BiFPN或加入协调注意力机制。常见做法是修改ultralytics下的模块定义但这要动源码维护成本高。更轻量的做法是提升imgsz到960让小目标拥有更多特征像素但推理时间增加适合RTSP录播或夜间慢速车道不适合实时30FPS路口。如果坚持要模型层面改进建议从增加小目标检测层开始给YOLOv8模型加一层P2输出头比单纯加注意力更直接对流量统计有效。做这个改进前先统计你的数据集里车辆框的宽高分布如果小目标占比超过30%才有必要动模型。6.2 从GPU服务器到RK3588的完整部署路径以及流量统计代码怎么写在RK3588上部署的关键不是把前端代码跑起来而是把每帧的检测结果封装成统一接口让追踪计数逻辑完全共享。你可以把RKNN推理写成一个返回[x1,y1,x2,y2,conf,cls]的函数然后喂给同一个ByteTrack代码。注意RKNN模型的输入格式是NHWC的uint8或float16YOLOv8源码前处理里的letterbox要自己实现。这里有一个我自己常用的经验不要直接用rknn.inference的原始输出再解析先跑通一个最小例子把输出形状打出来对照YOLOv8的decoded格式再做后处理否则很容易在输出张量的排列顺序上浪费一天。部署完成后量化评估流量统计误差统计真实手动数30分钟视频对比系统输出误差应小于5%。6.3 用一段连续视频做回归验证而不是用静态图片集系统整体调试到接近可用时最后做一次回归验证。我从第一台路灯下车辆计数项目学到的习惯是录一段10分钟的白天、夜晚混合视频带有不同类型车辆和遮挡场景。然后跑完整的检测—追踪—计数流。用这段视频验证三个指标一、计数误差是否在5%以内二、目标追踪ID保持率也就是同一辆车从进入画面到离开画面ID保持一致的帧数比例是否大于90%三、系统是否在长时间运行后内存溢出或线程卡死。三个指标都通过才把系统接到真实摄像头。我见过有的系统在实验室数据上很棒一接现场断电后RTSP重连就崩溃最后只好加看门狗定时器重启进程这种血泪经验希望你能提前避开。整套方案做下来我最深的感觉是“检测模型重要追踪参数更吃经验”。同样一套YOLOv8和ByteTrack在不同路口亮度和密度的画面下match_thresh和conf_thres至少要调两个轮次才能稳定。所以建议你先把开源模型和追踪器跑出一个可用的baseline再逐步替换成自己训练的车种模型并对准统计线做现场校正。本文里每一段参数落差和每一步代码逻辑都是值得花时间去验证的希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询