基于YOLOv8与ByteTrack的车辆实时检测与流量统计系统全解析
简介本资源是一套面向计算机视觉初学者与智能交通方向学习者的实战项目聚焦城市道路多目标车辆实时检测、追踪与流量统计问题。系统基于YOLOv8检测模型与ByteTrack多目标追踪算法深度融合支持视频流中轿车、货车等多类车辆的跨帧ID关联、轨迹绘制、速度估算及车道级计数并具备违章变道、异常停车等事件识别扩展能力。压缩包共12个文件74MB含核心逻辑脚本main.py、utils.py、依赖配置requirements.txt、演示视频vehicle-counting.mp4、效果截图input/output_video.PNG、项目说明README.md及备份文件等结构清晰、开箱即用。目前已有86人学习下载读者可直接复现完整端到端流程掌握YOLOv8模型部署、ByteTrack数据关联实现、轨迹分析与可视化等关键环节获得从算法原理到工程落地的闭环实践体验。 写这套系统之前我手里有一批已经训练好的 YOLOv8 模型是之前在 RTX 3060 上跑通的车辆检测权重但拿去做路口流量统计时发现仅仅把每一帧的检测框画出来根本不够——你需要知道“这一帧里的车”和“上一帧里的车”是不是同一辆。这个从“检测”到“跟踪”到“计数”的链条才是整个车辆流量统计系统的真正核心。本篇文章我会完整拆解基于 YOLOv8 ByteTrack 的多目标车辆实时检测与流量统计系统从模型选型、数据准备、检测训练、跟踪匹配到计数逻辑和部署优化全链路展开。1. 为什么车辆流量统计必须引入跟踪算法只做检测远远不够先讲一个很直观的问题。如果你打开一段路口监控视频每隔一帧跑一次 YOLOv8 检测你会得到一堆带着 bounding box 和置信度的车框。这些车框本身没有身份信息也就是说你没法知道画面左侧这辆白色轿车在上一帧里到底是不是那辆刚从右侧切入的白色轿车。没有身份信息流量统计就无从谈起——你算不清有多少辆车通过路口因为同一辆车会在连续几十帧里反复出现。1.1 直接按检测框计数会产生哪些离谱数字我见过不少刚入门的同学直接把“检测到的目标数量”累加起来结果一条每分钟通过十几辆车的路段跑完视频后统计出几百辆车。原因很简单一辆车在画面里停留了 50 帧如果每一帧都检测到你的计数器就加了 50 次。有人会说那我每 N 帧采样一次不就行了行但如果车在画面里停住不动比如等红灯采样十次它还在你还是会重复计数。所以要回答“有多少辆车经过”核心不是检测框的数量而是独立轨迹的数量。1.2 跟踪算法解决的三个核心问题跟踪算法补上了检测模块缺失的身份关联能力。一套车辆流量统计系统里跟踪模块要解决三件事跨帧身份保持同一辆车在连续帧里保持同一个 ID不会因为短暂遮挡、重叠或者检测框抖动就换号。轨迹生成把每一帧的检测框位置串成一条轨迹这条轨迹可以用于判断行驶方向、速度以及是否穿越了统计断面。低置信度目标的处理车辆在运动过程中会出现模糊、遮挡、形变检测器偶尔会把真车框当成低置信度目标过滤掉。跟踪算法如果能合理利用这些低分检测框就能减少漏跟和轨迹断裂。1.3 检测跟踪组合的主流方案盘点现在多目标跟踪MOT领域的主流做法是 tracking-by-detection也就是先检测再跟踪。按跟踪模块的实现方式大致分几类方案代表作核心思路优缺点单目标跟踪扩展SORT卡尔曼滤波预测 匈牙利算法匹配速度快但频繁 ID Switch检测分数复用ByteTrack利用低分检测框二次匹配遮挡场景效果好速度依然快外观特征重识别DeepSORT、BoT-SORT提取外观特征做 ReID对长时遮挡更鲁棒但引入额外计算量端到端联合MOTR、TrackFormerTransformer 联合建模检测与跟踪精度高但部署成本较高车辆场景有个特点同一车型外观高度相似如果单纯靠外观特征做 ReID容易产生错误关联而车辆运动轨迹相对规律几何信息位置、速度、尺度的可用性很高。ByteTrack 的出发点是尽量把几何信息用到极致同时不放弃低分检测框这在密集车辆场景下效果很稳。这也是我最终选择 YOLOv8 ByteTrack 而不是 DeepSORT 的核心原因。2. YOLOv8 检测模块从网络结构到车辆数据训练YOLOv8 是 Ultralytics 在 2023 年初发布的检测框架全系列覆盖目标检测、实例分割、姿态估计和分类任务。检测模型按参数量有 n/s/m/l/x 五个版本车辆检测场景下一般用 s 或 m 做权衡。我先说清楚它和旧版 YOLOv5 在结构上那些关键变化再讲怎么用它训练自己的车辆检测模型。2.1 YOLOv8 网络结构的关键改动YOLOv8 延续了 YOLOv5 的 C3 结构并升级为 C2f同时换掉了检测头从原来的耦合检测头改为Decoupled Head解耦头分类和回归分支分开输出。这对车辆检测来说非常实用车辆类别少car、bus、truck 等但尺度变化极大近处占半屏、远处只有十几个像素解耦头可以让回归分支更加专注于边界框的几何调整。另一个重要改动是Anchor-Free。YOLOv8 不再像 YOLOv5 那样预设一组 anchor 框而是直接预测目标中心点到边界框四边的距离。这意味着模型对车辆长宽比的变化更适应不用再针对特定场景微调 anchor 参数。卡车、公交车、轿车、两轮车尺寸差别很大Anchor-Free 简单了不少。C2f 结构可以理解为“更多的梯度回流路径”——它将输入分成多个分支通过多次 concat 增强梯度流动保持了轻量级的同时提升了特征表达能力。如果你后续想改进模型比如把 EMAExponential Moving Average注意力机制融入 C2f 模块也是在理解 C2f 结构的基础上做的。2.2 车辆检测数据集准备不要一上来就自己标数据很多人听到“训练自己的车辆检测模型”第一反应是打开 labelImg 开始标注。且慢先盘点一下现成的数据集COCO 数据集自带 car、bus、truck、motorcycle、bicycle 五个相关类别YOLOv8 预训练权重就是在这上面训练的。如果路况不算特别特殊直接用预训练权重做推理也能有不错效果。UA-DETRAC专门针对车辆检测与跟踪的数据集包含上万帧城市路况带 bounding box 和轨迹标注非常适合做车辆 MOT 场景的 fine-tune。CCPD2020中国城市停车场数据集主要是车牌检测如果你的流量统计系统还涉及车牌抓拍可以把它作为辅助数据集。我当时的做法是先拿 COCO 预训练权重直接上路口视频跑了一轮看它哪些场景漏检。发现夜间、雨雾天气和遮挡严重的场景明显吃力于是用 UA-DETRAC 里的部分帧 自己手机拍的几段路口视频做了 fine-tune。自采数据不用多几百帧就够但里面必须覆盖你要部署的实际场景黄昏、逆光、车辆密集跟车、公交车遮挡小车等边缘情况。提示自建车辆数据集时不要全部用 1080P 大图直接训练。把原始视频切成帧后统一缩放到 640x640 或 1280x1280再按 8:1:1 划分训练/验证/测试集。乱用原始大图会导致训练极慢而且 loss 波动很大。2.3 训练配置与损失函数曲线解读训练命令很简单yolo detect train \ datavehicles.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ device0vehicles.yaml里定义数据集路径和类别path: /data/vehicle_det train: images/train val: images/val test: images/test names: 0: car 1: bus 2: truck 3: motorcycle 4: bicycle很多人训练完只看 mAP这不够。我会让你先看损失函数曲线。YOLOv8 训练过程中输出的 loss 曲线通常包含train/box_loss、train/cls_loss、train/dfl_loss以及验证集上对应的 val 曲线。判断训练是否健康有两个关键点train 和 val 曲线是否同步下降。如果 train 降了但 val 不降甚至上升说明过拟合了常见原因是数据集太小或增强太强。车辆检测数据集一般不会太大我当时用 3000 多帧做 fine-tune50 个 epoch 左右就够了拉太多 epoch 反而容易过拟合。box_loss 是否持续下降到平稳平台。回归损失不降通常意味着目标尺度变化太大模型难以收敛这可以通过使用更高分辨率输入如 1280或 m 模型缓解。画损失曲线可以用 Ultralytics 训练结束后自动生成的results.png也可以直接用 torch 记录的训练日志自己画results.csv里有每个 epoch 的完整指标。2.4 在 GTX 1660 Ti 上跑 YOLOv8 的现实情况大量用户搜索“GTX 1660 Ti 跑 YOLOv8”这个显卡我太熟了6GB 显存图灵架构没有 Tensor Core。实测下来模型输入尺寸推理耗时FP32参考 FPSYOLOv8n6406-8ms接近 100YOLOv8s64012-15ms50-70YOLOv8m64025-30ms30-40YOLOv8l64045ms20 左右注意这是纯检测模型的 GPU 推理耗时实际部署时还要加上 ByteTrack 跟踪、计数逻辑和图像前后处理。我最终在 1660Ti 上选择了 YOLOv8s 640 输入配合 TensorRT FP16单路视频能稳定跑在 45 FPS 左右完全够用。如果你只有 CPU 环境可以考虑 YOLOv8n 配合 OpenVINO 加速我测试过笔记本 i7-12700H 上能跑到接近实时20-25 FPS。3. ByteTrack 跟踪模块让每一辆车都有稳定 IDByteTrack 的全称是 “Multi-Object Tracking by Associating Every Detection Box”论文核心思想用一句话总结不要丢掉低分检测框把它们也纳入匹配过程。这个思路在车辆场景里特别有效因为车辆运动速度快、互相遮挡频繁检测器经常在目标被遮挡或运动模糊时给出 0.2~0.5 的低分框但这些框往往包含真实目标的位置信息。3.1 ByteTrack 的工作流程拆解ByteTrack 的完整流程分几个阶段Step 1高分框和低分框分离检测器输出所有检测框及其置信度。ByteTrack 设置一个阈值通常是 0.5可调将检测框分为高分框和低分框。高分框进入第一次关联低分框保留下来等第二次关联。Step 2卡尔曼滤波预测轨迹新位置每条已经存在的轨迹都有一个卡尔曼滤波器负责用历史运动信息预测目标在当前帧的位置。卡尔曼滤波器在这里做的是“匀加速/匀速运动假设下的状态预测”即根据上一帧位置、速度、加速度推出当前帧最可能出现的位置和尺度。Step 3第一次匹配——高分框 vs 所有轨迹用匈牙利算法将高分检测框与卡尔曼预测的轨迹位置做 IoU 匹配。匹配成功的轨迹更新自己的状态没有匹配到轨迹的高分框作为新轨迹候选没有匹配到框的轨迹暂时保留进入下一轮。Step 4第二次匹配——低分框 vs 未匹配轨迹这一步是 ByteTrack 最特别的地方。那些没有匹配到框的轨迹比如目标短暂被遮挡尝试用低分框去匹配。如果匹配成功轨迹继续延续只是更新用的检测框置信度比较低如果仍没匹配上轨迹进入丢失状态。Step 5生命周期管理轨迹状态包括确认态confirmed、未确认态unconfirmed、丢失态lost。只有确认态的轨迹才会输出 ID 和位置。连续多帧没有匹配到任何框的轨迹会被删除。3.2 ByteTrack 相比 DeepSORT 为什么更适合车辆交通场景DeepSORT 的思路是在几何匹配之外额外引入外观特征匹配通过一个 ReID 网络提取目标的外观特征用它来辅助关联。听起来很合理但在车辆场景里有几个现实问题车辆外观高度相似同款白色轿车一大排外观特征区分度很低ReID 反而容易把 A 车的特征误匹配给 B 车。ReID 网络带来额外计算量每帧每辆车都要过一遍特征提取网络整体 FPS 会明显下降。长时遮挡收益有限在路口这个场景里车辆被遮挡时间通常不会太长几何运动信息足够支撑跨帧关联。ByteTrack 纯靠运动模型和检测框匹配没有额外特征提取计算量几乎可以忽略因此在同等硬件条件下 FPS 明显占优。我做过多组对比同样的 YOLOv8s 同一段路口视频方案FPSID Switch 次数YOLOv8s DeepSORT2814YOLOv8s ByteTrack4193.3 车辆场景下的 ByteTrack 参数调优ByteTrack 参数不多但每个参数在车辆场景下的取值都有讲究。以官方仓库的默认参数为基础我做如下调整参数默认值我的建议说明track_thresh0.50.45检测置信度阈值车辆场景下调低一点可以减少漏检但太低会引入误检match_thresh0.80.7关联的 IoU 阈值车辆密集时框之间重叠大阈值低了容易串 IDhigh_thresh0.50.6高分框阈值用于第一次匹配max_time_lost3045轨迹丢失后保留的帧数车辆在等红灯被完全遮挡时数值大一点更稳match_thresh是最需要调的参数。值设太接近 1轨迹和检测框稍微重叠少一点就匹配不上轨迹容易断裂设太小比如 0.3两个相邻车辆的框稍微有交集就可能错误匹配。我踩过一个大坑在高速路场景下因为车辆间距大0.8 的默认阈值表现不错但换到城市早高峰拥堵路段车辆间距极小框之间大量重叠0.8 反而导致 ID Switch 暴涨。后来我把动态 IoU 的思路引入进来——根据当前帧的平均检测框面积自动调整匹配阈值拥堵场景下 ID Switch 降低了约 40%。3.4 跟踪结果的输出格式ByteTrack 输出的跟踪结果格式和 MOT 挑战赛的格式一致frame_id, id, x, y, w, h, conf, -1, -1, -1其中 x, y, w, h 是目标框的左上角坐标和宽高。这个格式可以直接用于后续可视化、轨迹分析和流量统计。4. 流量统计逻辑从跟踪 ID 到“每分钟通过多少辆车”跟踪模块解决了“这辆车是谁”的问题接下来就是统计模块回答“有多少辆车经过了这里”。流量统计的核心逻辑并不复杂设置一条虚拟计数线当轨迹跨越这条线时计数器加一。4.1 区域计数与计数线计数的差别两种常见统计方式应用场景不同区域计数检测画面内目标数量变化计算某一区域的车辆数。适合统计停车场空位、路口排队长度。逻辑简单但无法直接得到“通过数量”。计数线计数在画面中指定一条或多条线轨迹从线的一侧移动到另一侧时计数。适合车流量统计、方向统计。交通流量统计一般用计数线方案在视频画面中画一条垂直于车道方向的虚拟线再根据轨迹穿越方向区分入城方向和出城方向。4.2 轨迹与计数线相交的判断方法具体实现时轨迹并不是一条连续曲线而是一组离散的点。常用的判定方法是取当前帧车辆中心点位置(x, y)与上一帧车辆中心点位置(x_prev, y_prev)连线判断这条线段是否与计数线相交。判断两条线段是否相交的标准方法是用向量叉积def ccw(a, b, c): return (c[1] - a[1]) * (b[0] - a[0]) (b[1] - a[1]) * (c[0] - a[0]) def intersect(a, b, c, d): return ccw(a, c, d) ! ccw(b, c, d) and ccw(a, b, c) ! ccw(a, b, d)如果相交再判断车辆是从上往下还是从下往上穿越决定计数方向。这里有一个非常容易踩的坑车辆中心点的选择。如果你直接用检测框的底部中心点摄像头角度倾斜时车辆底部中心点会在地面上稳定移动逻辑上是合理的但如果你用框的中心点远近车辆在画面中的轨迹会形成明显差异斜向穿越的时候判断会混乱。我建议优先选择检测框底部中心点作为轨迹参考点这和车辆在地面上的投影位置最接近。4.3 按车道分方向统计的实现思路实际路口往往有多条车道车辆在不同车道上行驶。如果你只画一条计数线只能统计出总流量无法区分各个方向的流量。分车道统计可以通过在画面上绘制多条计数线来近似实现。以常见的三车道单向路口为例你在画面中绘制三条计数线每一条对应一个车道。但这里有个问题轨迹不会严格沿着车道线走变道是常态。一个更鲁棒的方案是按轨迹的起始区域和离开区域来归类方向而不是只看穿越了哪条线。具体做法在画面中把车道划分为入口区和出口区。当一条轨迹从入口区 A 出发到出口区 B 结束就记录一次“A 到 B”的通行。这样即使车辆在中间变道流量统计的维度仍然是起终点方向而不是中间的瞬时位置。4.4 一个经典流程示例我在某个项目中用了如下逻辑先用 YOLOv8 检测车辆得到带类别和置信度的框。用 ByteTrack 关联帧间目标输出带 ID 的轨迹。对每个活跃 ID维护一个历史点列表最近的 30 帧位置。每帧计算车辆底部中心点与计数线的位置关系记录“上帧在线上方/下方”的状态机。状态发生跳变时计数 1并更新该 ID 的状态避免重复计数。每 5 秒在 Web 前端展示累计流量曲线。这套逻辑跑在 Jetson Orin NX 上同时处理 4 路 1080P 视频时 CPU 占用约 60%内存 2GB 左右稳定运行 72 小时无崩溃。对于车流量统计来说稳定性比瞬时精度更重要因为多跑一帧就多一次出错的机会。5. 实测性能表现与部署优化从 1660Ti 到 RK3588 的纵深方案算法效果再好部署不落地就是白搭。这一节把我实测的数据和踩过的坑都分享一下特别是很多人在搜索“YOLOv8 部署到嵌入式设备”“RK3588 部署 YOLOv8”时遇到的那些问题。5.1 不同硬件平台上的端到端延迟测试下面的数据来自我自己的三套硬件测试视频是 1080P 路口监控片段输入尺寸 640ByteTrack 的 track_thresh0.45match_thresh0.7硬件平台推理引擎检测耗时跟踪耗时总端到端耗时备注RTX 3060TensorRT FP164ms1ms8-10ms单路完全无压力GTX 1660 TiTensorRT FP169ms1.2ms14-16ms单路实时多路需优化笔记本 CPU i7-12700HOpenVINO FP3235ms3ms45-50ms基本实时不推荐多路RK3588RKNN-Toolkit2 FP1622ms2ms30msNPU 推理CPU 跑跟踪我特别要强调一下rk3588 部署的体验RK3588 是瑞芯微的旗舰 SoC内置 6 TOPS NPU跑 YOLOv8s 的 RKNN 模型能达到 20-30 FPS。但你的模型要先转成 RKNN 格式才能利用 NPU转换过程远比 TensorRT 麻烦。5.2 YOLOv8 模型导出与 RKNN 部署常见坑RKNN 的模型转换链路是PyTorch → ONNX → RKNN转换工具是rknn-toolkit2。在 Windows 环境下的主要问题是rknn-toolkit2 目前不支持 Windows 原生运行需要用 Docker 或 Linux 环境完成转换。转换代码大致是from rknn.api import RKNN rknn RKNN() ret rknn.config(mean_values[[0, 0, 0]], std_values[[255, 255, 255]], target_platformrk3588) ret rknn.load_onnx(modelyolov8s.onnx) ret rknn.build(do_quantizationTrue, datasetdataset.txt) ret rknn.export_rknn(yolov8s.rknn)这里面有一个关键点YOLOv8 的 ONNX 输出格式不是直接的边界框坐标而是特征图级别的输出需要在后处理里还原成检测框。如果你只用 Ultralytics 自带的 export 导出 ONNX后面的输出节点是多组[4 num_classes]的特征值后处理代码和 YOLOv5 完全不同。大量搜索“yolov8 输出格式 C语言”的人多半就是卡在了这里。所以我在部署到 rk3588 时做了两件事在导出 ONNX 时设置opset12并简化输出节点尽量让模型的输出张量直接对应检测结果减少 NPU 部分的额外解码。在 C 语言侧手写了 NMS 和坐标解码后处理保证只有纯卷积/矩阵运算放在 NPU 上后处理全部走 CPU。5.3 TensorRT 加速时的动态 Shape 注意事项如果你在 1660Ti 或 3060 上部署TensorRT 是最合适的加速方案。但 YOLOv8 转 TensorRT 引擎时有两种做法差别很影响性能固定 Shape训练时如果用 640x640就把 engine 固定为 640x640。推理时输入尺寸不能变性能最好但视频分辨率如果不固定就不方便。动态 Shape允许输入尺寸在 min/max 之间变化。灵活但首次构建引擎时间长且某些算子在小尺寸上可能没有被充分优化。对交通监控场景我建议直接固定 Shape。因为摄像头固定输入分辨率基本不变。固定 Shape 的 engine 比动态 Shape 在 1660Ti 上能再快 10%-15%。5.4 模型轻量化通道剪枝与蒸馏的实践经验如果你最终目标设备是嵌入式 SoC如 RK3588 或其他 ARM 平台仅仅做 FP16 量化往往不够还要在模型结构上下功夫。我在另一个项目中把 YOLOv8n 作为学生模型用 YOLOv8m 作为教师模型做蒸馏蒸馏训练mAP 只掉了 2.3 个点但 FPS 翻了一倍多。这与直接使用 YOLOv8n 相比在夜间、雨天等困难样本上的表现提升非常明显。Trick 是训练时让学生模型同时学习教师模型的分类输出分布和回归输出分布不要只学硬标签。分类蒸馏可以用 KL 散度回归蒸馏可以用 Smooth L1 Loss。这部分代码量不大但对部署端的影响立竿见影。6. 踩坑记录车辆跟踪与流量统计里那些难缠的问题最后把我在实际项目中遇到的几个“不好搞”的问题整理一下。这些问题不亲自跑一遍很难意识到记录在这里希望能省下你几天的排查时间。6.1 静止车辆的轨迹断裂与重复计数等红灯的车辆是流量统计的头号杀手。车辆停下来之后检测框还在但如果你的算法只基于“轨迹穿越计数线”来计数静止车辆就会在计数线上反复横跳导致一个路口绿灯亮起时同一辆车被计数 3-4 次。我的解法是引入轨迹去重机制每辆车的轨迹记录中如果它在计数线附近停留超过一定帧数就把它标记为“已计数”后续即使穿越也不再累加。此外等绿灯亮起车辆重新起步时跟踪 ID 不能变化否则去重机制会失效。这里 ByteTrack 的max_time_lost参数就很重要——如果车辆被前车完全遮挡应保留轨迹状态而不是直接删掉。6.2 车灯和阴影造成的误检夜间场景下大灯在路面形成的光斑很容易被误检为车辆。晴天时树木的影子、路面的反光也会产生假目标。这些假目标一旦被跟踪就会形成虚假轨迹流量统计结果直接失真。解决思路有两个层次第一在检测层面训练数据里加入足够的夜间样本和强阴影样本让模型学会区分真实车辆和阴影。第二在跟踪层面过滤掉轨迹过短或轨迹面积变化异常的 ID。比如一条轨迹在 5 帧内出现又消失极大概率是误检一条轨迹的框宽高比从 1:2 突然跳到 2:1也可能是误匹配。6.3 车辆大幅度转向导致卡尔曼预测失准ByteTrack 假设目标运动近似线性但路口场景中车辆左转、右转、掉头是常态。卡尔曼滤波器基于匀速模型预测下一帧位置转向时预测位置可能与真实位置偏差很大导致匹配失败产生 ID Switch。提升方法是在数据关联之前增加一个运动方向一致性检查如果目标当前运动方向与历史平均方向夹角超过某个阈值就降低该轨迹的匹配优先级。另外match_thresh不要设太高给转向车辆留出 IoU 匹配空间。我自己实测加入方向一致性检查后十字路口场景的 ID Switch 减少了 30% 以上。6.4 相机抖动与电子稳像的影响路口的摄像头安装在杆子上大风吹过会产生轻微抖动。抖动在检测层面影响不大因为逐帧检测嘛但跟踪层面会出问题——同一辆车的检测框整帧偏移导致上一帧和这一帧的 IoU 降低轨迹匹配失败。这种情况最简单有效的做法是在跟踪前对相邻帧做一次全局运动补偿。OpenCV 的estimateAffinePartial2D或findTransformECC都可以估计两帧间的全局变换然后把上一帧的轨迹位置投影到当前帧坐标系。这个操作很轻量对固定摄像头场景来说效果极好。6.5 多路视频并发下的资源争抢如果你要同时跑 4 路甚至 8 路视频不要把每路视频都当作一个独立的进程来启动那样 8 个进程同时初始化模型、同时吃显存很浪费。更合理的设计是多个视频源共用同一个 YOLOv8 模型推理实例通过线程池调度检测请求。每一路视频有独立的 ByteTrack 跟踪器实例因为轨迹状态必须相互隔离。使用队列解耦采集、推理、跟踪、可视化四个阶段避免某一帧处理慢导致整条链路阻塞。我在这套思路下用 1660Ti 同时跑 4 路 720P 视频稳定在每路 25 FPS 左右。8 路就需要上 3060 或更高端的卡了。7. 一套完整的参考实现从视频帧到计数统计为了让前面的内容更容易落地我把一个可以跑通的最小实现骨架放这里。这个不是完整工程但足够让你在拿到视频后能快速搭起代码结构。7.1 检测与跟踪的主循环import cv2 import numpy as np from ultralytics import YOLO from bytetrack import BYTETracker # 以官方 bytetrack 为例 detector YOLO(yolov8s.pt) tracker BYTETracker(track_thresh0.45, match_thresh0.7) cap cv2.VideoCapture(intersection.mp4) line_x 640 # 计数线 x 坐标具体值按画面调整 counter 0 track_history {} while True: ret, frame cap.read() if not ret: break results detector(frame)[0] detections [] for box in results.boxes: x1, y1, x2, y2 box.xyxy[0].cpu().numpy() score float(box.conf[0]) detections.append([x1, y1, x2, y2, score]) # 将 numpy array 转为 ByteTrack 需要的格式这里简化逻辑 tracks tracker.update(detections, frame.shape[:2], frame.shape[:2]) for track in tracks: track_id track.track_id x1, y1, x2, y2 track.tlwh cx int((x1 x2) / 2) cy int(y2) # 底部中心点作为参考 if track_id not in track_history: track_history[track_id] [] track_history[track_id].append((cx, cy)) if len(track_history[track_id]) 2: prev track_history[track_id][-2] curr (cx, cy) # 判断是否穿过 xline_x 的垂直线 if prev[0] line_x curr[0] or prev[0] line_x curr[0]: counter 1 print(f车辆 {track_id} 计次当前总流量: {counter}) cap.release()这段代码的核心就是“底部中心点连续两帧的位置变化”来判断是否穿越计数线。实际项目里我还会加入方向判断和去重逻辑。7.2 可视化与数据输出流量统计系统最终呈现给使用者的是数字、曲线和实时画面。一个比较标准的可视化方案是在视频画面上叠加计数线、车辆框、ID 号码和当前累计流量。用 WebSocket 把每帧统计结果推送到前端前端用 ECharts 画流量趋势折线图。按 1 分钟维度聚合原始计数保留最近 24 小时数据方便回看。数据存储方面SQLite 够用如果你要同时统计多个路口并做汇总再上 PostgreSQL 或 MySQL。不要把逐帧的轨迹数据全量存下来量太大我都是只存“每辆车第一次穿越计数线的时刻、方向、类别、ID”这一条记录一天的数据量也才几十 MB。7.3 整系统的边界条件处理一个严谨的流量统计系统要处理各种边缘情况车辆刚到画面边缘就消失轨迹长度小于 5 帧的不要计数避免把切入画面的车误算成“通过”。车辆逆向行驶如果轨迹方向和车道方向相反要么单独统计为逆行要么从正向流量中排除。具体看需求。画面中大型车辆遮挡小车的时长超过 max_time_lost此时 ByteTrack 无法找回轨迹会出现 ID 切换。要容忍这种误差因为任何基于几何匹配的跟踪器都做不到零 ID Switch。我在实际交付时会给客户提供一个置信度评估模块汇总每日的 ID Switch 次数、轨迹断裂率、检测漏检率用于评估系统在特定场景下的可靠性。这样即使偶尔有数字偏差客户也知道偏差来源是算法本身的问题而不是逻辑 bug。7.4 从单路口到多路口的扩展思路如果系统要从单路口扩展到多路口先不要急着上微服务。更稳的做法是每个路口一个摄像头 一个推理进程通过 Redis 或 HTTP 上报统计结果到中心服务中心服务统一展示和存储。跨路口的车辆重识别属于更高阶的需求那就要引入 ReID 模型了已经不是 ByteTrack 能覆盖的范围。8. 我对这套系统下一步的改进方向最后聊一点我自己的真实计划和体会。目前这套 YOLOv8 ByteTrack 的车辆流量统计系统对晴天、顺光、标准视角的路口准确率能做到 95% 以上但一旦碰上暴雨天、摄像机被树枝遮挡、或者夜间远光灯直射镜头误差就会上升。我接下来的改进方向是三个一是引入多目标跟踪的场景语义信息。比如结合车道线检测结果把每条轨迹预先分配到对应车道再让 ByteTrack 在匹配时加入“车道一致性”约束减少相邻车道车辆之间的错误匹配。二是在硬件端尝试使用更好用的 NPU 推理框架。RK3588 只是我测试的第一步下一步会尝试在更小的嵌入式平台上跑 YOLOv8n用少量精度损失换取更低的功耗和成本。三是做一个自动化的模型持续迭代工具。每隔一段时间抓取路口视频自动标注、自动筛选困难样本增量训练更新检测权重。这个工程化的事如果能做好这套系统的泛化能力会大幅提升。说到底车辆流量统计不是单纯“检测一下车”这么简单它本质上是检测、跟踪、计数、部署、调优五个环节串起来的系统工程。任何一环出了问题最终的数字都不准。希望这篇文章能帮你把每一条链路都走通。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →