news 2026/9/14 19:56:16

PyTorch实时车流量统计:YOLO检测、跟踪与TensorRT加速

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PyTorch实时车流量统计:YOLO检测、跟踪与TensorRT加速

简介:本项目是一套基于深度学习的高速公路车流量实时统计实践资料,适合具备一定Python基础、希望上手计算机视觉目标检测的开发者与学习者。资源围绕车辆检测与计数展开,涵盖数据处理、模型训练、测试评估与视频流部署等环节,可帮助读者从零搭建一套可运行的交通监控统计流程。压缩包共30个文件,包括Python脚本、YAML配置、Shell脚本、图片与视频示例等,其中py文件覆盖网络定义、训练/测试及演示逻辑,yml用于框架参数配置,视频文件可用于实时检测效果验证,包体约72.54MB,整体结构清晰。该资源已有225人学习下载,内容包含数据集示例、预训练模型引导、训练与测试脚本、部署参考代码等,尤其适合学习YOLO、SSD等检测模型在真实交通场景中的落地应用。

1. 高速路实时车流量统计:把摄像头画面变成分钟级数字

高速路摄像头传回的视频里,车辆挨着车灯穿行,真正要回答的问题只有一个:这一分钟到底过去多少辆车。把这个问题拆成技术动作,就是检测每辆车、给每辆车分配唯一ID并跟踪,最后在一条虚拟线上做跨线统计。深度学习框架在这里承担的不只是物体识别的计算,还决定了整套系统能不能跑到实时:从选择PyTorch还是TensorFlow,到YOLO这类检测网络怎样被加载、加速、部署,每一步都与吞吐量挂钩。这篇文章面向准备用现有服务器或边缘设备落地交通检测的工程师,按真实业务链路把模型选型、视频流接入、跟踪计数和性能验证串起来,直接能用。

2. 深度学习框架选型:车辆检测里的PyTorch优势与YOLO对比

2.1 动态图与部署生态让PyTorch更贴近实时场景

讨论实时车流统计时,第一件事不是搭界面,而是确定用哪个深度学习框架承载模型推理。我在高速视频场景下见到的工程里,PyTorch出现频率远高于TensorFlow。原因有两条:其一,PyTorch默认采用动态图构建模型,在排查检测输出异常时可以直接打印中间张量,比如把特征图尺寸打出来对比输入比例,不用反复追踪静态图的节点定义;其二,PyTorch与深度学习框架生态里的目标检测库(Ultralytics YOLO、MMDetection)原生耦合,避免在多个框架之间写转换层。TorchScript和ONNX的导出路径也比较直,后面再做TensorRT加速很顺畅。

实时统计对推理延迟很敏感,PyTorch 2.0以后用torch.compile对卷积模型做图优化,配合CUDA环境一般能获得20%左右的加速。如果项目里已经有一部分C++代码,LibTorch可以通过C++ API加载同一个TorchScript模型,这是很多交通监控系统最终会走的路。下面这张表是我平时给团队做选型时的判断依据。

深度学习框架动态排错导出部署实时推理常用后段适合场景
PyTorchTorchScript / ONNXTensorRT检测模型经常迭代,开发排错比重高
TensorFlowSavedModel / ONNXTFLite已有TF服务,团队熟悉约定俗成
ONNX Runtime直接运行ONNXCUDA / TensorRT跨框架部署,需要统一推理入口

2.2 车辆检测模型选型:YOLOv8并不是唯一答案

模型的选择比框架更影响实时统计上限。高速路车辆类别少,但目标尺寸跨度大:近处车辆占据大半画面,远处百米外的车辆只有十几个像素。YOLOv8n是当前最稳妥的起步方案,参数量约3.2M,在RTX 3060上处理640分辨率图片能跑到80FPS以上,远远满足车流量统计需求。YOLOv8s虽然精度更高,但算力开销接近翻倍。如果同时需要识别车辆颜色、车型或者后续嵌入车牌识别,需要换用更大的backbone或者多目标头网络,这时实时性要从模型切分角度重新考虑。

还有一类做法是直接用YOLOv5的--half半精度模式,配合固定输入形状,把帧率再拉高一截。对于纯粹的车流量统计,Fast R-CNN这类两阶段模型不建议使用,虽然mAP好看,但单帧推理在消费级显卡上也要100ms以上,留给跟踪和计数的预算不够。更轻量的NanoDet-M在CPU上也能跑到15FPS,适合完全没有GPU的边缘盒子,但它的训练资料少,出现漏检时能调的空间也小。

模型输入尺寸精度特点RTX 3060单帧耗时
YOLOv8n640×640小目标稍弱,速度最快约12ms
YOLOv8s640×640精度与速度平衡约22ms
YOLOv5s640×640大车效果好,小车一般约18ms
NanoDet-M320×320可跑CPU约40ms

2.3 用PyTorch跑起一个高速场景检测模型的最小命令

下面的命令从空环境开始,安装最小依赖并加载YOLO预训练权重。所有车辆类别用COCO里的car、bus、truck三个标签过滤。

conda create -n traffic python=3.10 -y conda activate traffic pip install torch torchvision pip install ultralytics opencv-python
import torch from ultralytics import YOLO model = YOLO("yolov8n.pt") results = model.predict( "road_frame.jpg", imgsz=640, conf=0.35, classes=[2, 5, 7], ) for box in results[0].boxes: print(box.xyxy.tolist(), box.conf.item(), int(box.cls.item()))

conf=0.35是检测置信度阈值,卡车反光导致的误检通常集中在0.3到0.4之间;设成0.4容易漏掉远处小目标,0.35是稳妥起点。classes固定为COCO里的car(2)、bus(5)、truck(7),把person、bicycle等类别排除后,模型不用输出无用的框。这里注意:model.predict("road_frame.jpg")会自动做预处理,但在实时循环中我会手动接管,因为自动流程每次都要读文件并重建内存,连续推流会产生小毛刺。下一章就把这部分拆开做。

3. 视频流接入与预处理:从RTSP摄像头到PyTorch的输入张量

3.1 RTSP拉流与帧率控制的取舍

摄像头输出的通常是H.264编码的RTSP流。写代码时最直接的方法是OpenCV的VideoCapture,但要在实时统计中保持帧率平稳,必须限制解码后的帧处理速度。常见做法是让解码线程和推理线程解耦:解码线程把最新帧丢进deque,只保留最近几帧;推理线程每隔固定时间取一帧,比如每150ms取一次,也就是约6FPS的处理速度。

为什么不用摄像头的25FPS?因为目标检测模型在中等GPU上处理一帧需要20到40ms,跟踪、计数和叠加显示还要额外花时间。如果硬压到全帧率,系统延迟会逐渐变大。另一个坑是RTSP长期运行一定会断线,没有重试逻辑的服务跑半天就卡死。

import cv2 from collections import deque URL = "rtsp://192.0.2.10:554/channel1" cap = cv2.VideoCapture(URL) cap.set(cv2.CAP_PROP_BUFFERSIZE, 3) frame_queue = deque(maxlen=3) while True: ok, frame = cap.read() if not ok: cap.release() cap = cv2.VideoCapture(URL) continue frame_queue.append(frame)

deque(maxlen=3)的作用是:缓存区满了以后,新帧进入会自动丢弃最老帧,这样处理慢的时候不会让内存无限制增长。CAP_PROP_BUFFERSIZE设为3可以降低OpenCV内部缓存引入的延迟,让推理线程看到更接近当前时间的画面。如果画面出现花屏,先检查网络丢包,不要急着改代码重试逻辑。

3.2 帧预处理与张量归一化

模型训练时图像被letterbox到640×640,推理时也必须保持同样变换,否则检测框坐标会偏移。高速路摄像头一般是1920×1080,直接resize会把车辆压扁,所以先等比缩放,再在两侧填充灰边。下面是完整预处理函数:

import cv2 import numpy as np import torch def frame_to_tensor(frame: np.ndarray, size: int = 640) -> torch.Tensor: h, w = frame.shape[:2] scale = min(size / w, size / h) nw, nh = int(w * scale), int(h * scale) resized = cv2.resize(frame, (nw, nh), interpolation=cv2.INTER_LINEAR) canvas = np.full((size, size, 3), 114, dtype=np.uint8) x_off = (size - nw) // 2 y_off = (size - nh) // 2 canvas[y_off:y_off + nh, x_off:x_off + nw] = resized rgb = cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB) tensor = torch.from_numpy(rgb).permute(2, 0, 1).float().div(255.0) return tensor.unsqueeze(0)

size通常取640,和训练输入保持一致。填充色114对应灰度中高值,不是随意选的,YOLO官方预处理里用的就是这个值。permute(2, 0, 1)把OpenCV默认的HWC排列换成PyTorch需要的CHW排列,div(255.0)把像素归一化到0到1区间。模型返回的框坐标是letterbox画布上的坐标,要还原回原图,需要记录scalex_offy_off,在画目标框时做一次逆变换。这个细节在延迟分析中经常被忽略,但错一点点,叠加在视频上的线与实际车底位置就会偏移。

3.3 自研数据标注和增强的最小方案

预训练模型能覆盖高速路常见车辆,但国内高速很多货箱形态特殊,黄昏逆光场景也常让模型把车灯当成两个目标。最省力的做法是录一小时高峰期视频,抽帧筛选500张,用labelImg标注car、bus、truck三类。标注文件是YOLO格式的txt,之后训练命令如下:

yolo detect train \ data=traffic.yaml \ model=yolov8n.pt \ epochs=40 \ imgsz=640 \ batch=16 \ mosaic=0.5 \ close_mosaic=5

traffic.yaml里声明路径和三个类别名。mosaic=0.5表示一半训练批次使用拼接增强,高速路背景比较统一,mosaic开太高会让模型学到边框拼接痕迹。close_mosaic=5表示最后5个epoch关闭mosaic,让模型回到真实样本分布上做收敛。

增强项推荐参数原因
horizontal flip0.5车辆左右对称,增加双向车道样本
mosaic0.5增加小目标多样性,过高会破坏语义
mixup0.2降低遮挡样本过拟合,再高会让框抖动
hsv_h / hsv_s0.01 / 0.5适应不同色温车灯和路面颜色

数据不均衡时,不要只靠过采样。货车多就专门收集货车片段,用save_crop把检测结果里的车框截出来,人工筛掉误检后再加进训练集,比单纯调loss权重更直接。

4. 实时统计核心:跟踪ID与虚拟线计数的完整逻辑

4.1 为什么要跟踪车辆而不是直接数检测框

如果每一帧都把检测框和虚拟线做相交判断,那么同一辆车经过线的几百毫秒会触发好几次计数。高速路车距较小时,误计会被放大。正确做法是引入跟踪器:每一帧检测框与已有轨迹关联,分配稳定ID。跨过一条线时,只记录ID首次跨线的时间点,之后即使该ID还在线附近也不再计数。

当前常见选择是ByteTrack或Deep SORT。ByteTrack速度更快,适合车流密集的快速路;Deep SORT在有明显遮挡时更稳,因为它同时使用运动和外观特征。这里我以Deep SORT为例,因为它能对长时间遮挡的车辆保持更稳定的ID,避免同一辆车因为短暂被卡车遮住就生成两个新ID,造成双倍计数。

4.2 Deep SORT车辆ID关联的关键参数

Deep SORT的核心是把每个检测框与已有轨迹做级联匹配,然后通过匈牙利算法做最终分配。工程里我们不需要重写匹配逻辑,直接用封装好的库。

from deep_sort_realtime.deepsort_tracker import DeepSort tracker = DeepSort( max_age=30, n_init=3, nn_budget=100, max_cosine_distance=0.3, max_iou_distance=0.7, ) detections = [] for box in results[0].boxes: detections.append(( [int(x) for x in box.xyxy[0].tolist()], box.conf.item(), int(box.cls.item()), )) tracks = tracker.update_tracks(detections, frame=frame) for track in tracks: if not track.is_confirmed(): continue tid = track.track_id ltrb = track.to_ltrb()

检测格式必须是[x1, y1, x2, y2, conf, class_id],坐标直接用模型输出,不要提前缩放回原图,否则和跟踪器内部的坐标换算对不上。max_age非常重要:它决定轨迹在失去匹配后还能存活多少帧。车辆在高速路被大型货车遮挡的时间通常不会超过1秒,按25FPS算就是25帧,所以30帧是安全值。设太长,车辆驶出画面后ID还会挂着,计数会莫名往后延迟。

参数推荐值影响
max_age30遮挡多少帧后仍保持轨迹;过大则ID悬挂太久
n_init3连续匹配到几帧后确认轨迹,过滤闪烁误检
nn_budget100外观特征库容量,控制内存占用
max_cosine_distance0.3外观匹配阈值,越严格越难恢复遮挡
max_iou_distance0.7位置重合度阈值,控制目标移动距离上限

提示:max_age设置超过30时,车辆离开画面后ID仍会留在轨迹里,虚拟线计数逻辑会把它当成还在等待穿越,导致统计滞后。建议和虚拟线跨越判定共用同一个轨迹坐标字典。

4.3 虚拟线计数与车流密度估算

虚拟线一般设在车道末端,沿水平方向画一条line_y = 480。这里给出最简实现:保留每个ID最近两个中心点,判断是否与虚拟线相交。注意交点不是检测框底边,而是车辆中心的固定点,否则不同程度的车高会带来位置偏差。

from collections import deque track_centers = {} counted_ids = set() line_y = 480 count_60s = 0 def update_traffic(tracks): global count_60s for track in tracks: if not track.is_confirmed(): continue ltrb = track.to_ltrb() cx = int((ltrb[0] + ltrb[2]) / 2) cy = int((ltrb[1] + ltrb[3]) / 2) tid = track.track_id if tid not in track_centers: track_centers[tid] = deque(maxlen=2) prev = None if track_centers[tid]: prev = track_centers[tid][-1] track_centers[tid].append((cx, cy)) if prev is None: continue if (prev[1] <= line_y < cy) or (cy <= line_y < prev[1]): if tid not in counted_ids: counted_ids.add(tid) count_60s += 1

prev是上一帧的中心点。跨线判断用当前点和上一帧点是否位于线的两侧,能处理车辆在连续帧之间跳过线的情况。counted_ids在这个函数里不需要立刻清空,但每分钟定时器触发时,要记录count_60s并清空集合,否则同一个ID只在第一次跨线时计数,后面的车辆不受影响。

车流密度估算可以在同一段代码里完成:用每分钟跨线车辆数除以车道数和区间长度,得到每公里车辆数。这个数值比单纯的车流量更能反映拥堵程度,放到监控大屏上时也更容易被业务人员理解。

5. 用TensorRT压缩模型后再校验计数误差

5.1 用TensorRT固化和量化PyTorch模型

模型在PyTorch里跑只能算完成前半段。要稳定跑到25FPS以上,常见做法是先把模型导出为ONNX,再用TensorRT生成engine。固定输入尺寸能让TensorRT做更好的显存规划,同时也可以把精度降到FP16。FP16在车辆检测任务里损失很小,可以默认开。

yolo export model=yolov8n.pt format=onnx dynamic=False imgsz=640 trtexec --onnx=yolov8n.onnx --saveEngine=yolov8n.trt --fp16

dynamic=False很关键,动态shape会引入额外优化开销,固定640×640后推理速度最稳定。--fp16是半精度,不建议直接用--int8,INT8需要准备校准数据集,校准集不足时检测精度会明显下滑,高速路小目标尤其明显。导出的engine可以通过YOLO("yolov8n.trt")加载,接口与PyTorch模型一致,改造成本很低。

5.2 用固定视频段校验车流量统计误差

计数逻辑改完之后,最有效的验证不是跑单张图片,而是录制一段15分钟同时包含高峰期和稀疏期的原始视频,记录人工计数和系统输出。比较指标有两个:跨线计数误差率,以及ID切换次数。若误差超过5%,优先检查检测置信度和虚拟线位置是否偏移。

python traffic_check.py --video highway_clip.mp4 --engine yolov8n.trt \ --line-y 480 --ground-truth 217

常见的校验结果:

视频段人工计数系统计数误差率
14:00-14:0578802.56%
18:00-18:051391507.91%

黄昏时段误差率往往会飙高,原因是低亮度让模型给出的置信度下降,部分车辆被滤掉。这时可以先把输入帧的gamma提高1.2再进模型,或者把置信度从0.35降到0.30。两种方法都试一遍,哪个误差低就留哪个。相比Python层反复调阈值,先把输入亮度对齐训练集分布,往往见效更快。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 19:56:12

从LCP到图片压缩:电商商品图智能优化全复盘

一次大促前的压测&#xff0c;把我们的首页老底翻了个彻底&#xff1a;4G 网络下首屏平均要 6.8 秒才能稳定&#xff0c;运营说页面卡得没法看&#xff0c;后端接口却都在 200ms 以内。真正把时间吃掉的是商品图片——12 个商品卡将近 9MB 的图片资源&#xff0c;一半以上是原图…

作者头像 李华
网站建设 2026/9/14 19:56:01

SymPy 排列群测试工具库解析:testutil 模块的校验器与朴素实现

SymPy 排列群测试工具库解析&#xff1a;testutil 模块的校验器与朴素实现 【免费下载链接】sympy A computer algebra system written in pure Python 项目地址: https://gitcode.com/GitHub_Trending/sy/sympy sympy.combinatorics.testutil 是 SymPy 排列群子系统&am…

作者头像 李华
网站建设 2026/9/14 19:55:28

Web端PDF编辑器自建实战:从渲染到导出的关键技术解析

1. 为什么我们最终决定在Web端自建PDF编辑能力先交代一下背景。我们是一个业务系统偏重的团队&#xff0c;手头有一套在线文档管理平台&#xff0c;用户在系统里上传合同、标书、图纸&#xff0c;原来只能下载后拿去本地改&#xff0c;改了再传回来。业务方的需求单写得很简单&…

作者头像 李华
网站建设 2026/9/14 19:55:24

Vue3+Vite打包体积优化实战:从2.8M到500K的完整方案

先说个真实场景。上个月接手一个 Vue3 后台管理系统&#xff0c;用的是 Vite 做构建工具&#xff0c;功能其实不算复杂&#xff1a;登录鉴权、用户管理、订单列表、数据报表、还有几个大屏展示页。但同事提了个问题——每次npm run build之后&#xff0c;打包出来的 dist 目录里…

作者头像 李华
网站建设 2026/9/14 19:53:51

FlowingLight:基于Canvas的数据大屏流光动效插件设计与接入

做可视化数据大屏这几年&#xff0c;我最大的感受是&#xff1a;图表好写&#xff0c;动效难调。尤其是领导或客户走近大屏的那一刻&#xff0c;如果页面全是干巴巴的柱状图和折线图&#xff0c;哪怕数据再准确&#xff0c;观感上总觉得少了一口气。后来我在自己的大屏项目里沉…

作者头像 李华
网站建设 2026/9/14 19:53:48

mongoose-android-x86_64 编译报 PIE?TaoToken 这样让 Codex 改 examples.mk

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华