news 2026/9/28 21:05:08

YOLOv5+DeepSORT高速车流人流量统计:跟踪稳定才是计数关键

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5+DeepSORT高速车流人流量统计:跟踪稳定才是计数关键

简介:基于YOLOv5与DeepSORT算法实现的高速移动场景下车流人流量统计实战项目,适合计算机相关专业毕业设计、课程设计或项目练习。项目由大四学生完成并经导师评审认可,代码完整、可直接运行,对初学者较为友好。压缩包共117个文件,约80.21MB,包含55个Python源码、20个YAML配置、1个预训练权重文件(pt)、4个MP4演示视频及DOCX使用手册等,覆盖模型训练、目标检测、追踪计数与部署配置等环节。另有Dockerfile、Shell脚本及GIF效果图,便于环境搭建与结果预览。目前已有65人学习使用,尤其适合需要快速搭建完整检测计数方案、理清多目标追踪流程的开发者,可直接基于源码二次开发或作为答辩展示材料。

1. yolov5+deepsort 做高速移动车流人流量统计:为什么说难点在“跟踪”而不是“检测”

高速监控画面里,一辆车从入画到出画可能只有几秒钟,人眼盯着屏幕数完一个小时的录像,误差且不说,眼睛先受不了。用 yolov5+deepsort 实现高速移动车流人流量统计,本质上不是“把车框出来”这么简单:yolov5 负责每帧目标检测,deepsort 负责把同一辆车在连续帧里的身份串成一条稳定轨迹,只有轨迹稳定了,“通过检测线的车辆数”才有意义。这个毕业设计最典型的分水岭也在这里——检测模型一跑就出框,但一接跟踪就开始丢 ID、重复计数、遮挡断轨。本文按“原理 → 环境 → 核心代码 → 踩坑 → 加分项”的顺序把这个项目完整落地,适合有 Python 和 PyTorch 基础、想拿一个能演示能答辩的完整系统当毕设入口的同学。

2. 项目原理拆解:先看清检测与跟踪的分工,再理解计数逻辑

2.1 yolov5 单帧检测到底输出了什么

yolov5 是单阶段目标检测器,输入一帧图像,输出的是 N 个检测框,每个框带 6 个值:x1、y1、x2、y2、confidence、class_id。注意,这个输出是“无状态”的——它只告诉你这一帧里哪里有车、哪里有人,不告诉你这个框和上一帧的哪个框是同一个人或同一辆车。

很多刚接触这个项目的同学会在这里产生一个误解:视频第一帧检测出 10 辆车,第二帧检测出 12 辆,是不是我画面里的车流就是 12 减 10?完全不是。第二帧的 12 个框里,可能有 9 个是上一帧那 10 辆车里的,另外 3 个是新进入画面的,还有 1 辆上一帧的车被遮挡漏检了。没有身份关联,你连“哪辆是新的”都判断不了。

yolov5 内部还有一个关键后处理步骤叫 NMS(非极大值抑制)。模型原始输出会有大量重叠框,NMS 按置信度排序,把与最高分框重叠度超过阈值的框抑制掉,保留最具代表性的那个。这个阈值就是 iou-thres,后面调参会用到。

我建议你在动手写主循环前,先做一个最小验证:拿一段高速路口的短视频,跑通 yolov5 检测,把每帧检测到的框数、每个框的置信度打印出来,保存成日志。这个日志是你后面排查跟踪问题时的“基准答案”。很多看起来像是跟踪的问题,根因其实在检测阶段就埋下了。

2.2 deepsort 如何把跨帧目标串成同一条轨迹

deepsort 的输入是每帧的检测框序列,输出是带有全局唯一 track_id 的目标框。它内部由三块组成:

卡尔曼滤波负责运动预测。每一帧跟踪器会基于上一帧的位置和速度,预测当前帧目标应该出现在哪里。预测结果和真实检测框之间计算一个“门控距离”,超出合理运动范围的匹配直接被排除。高速场景下,车辆帧间位移大,卡尔曼滤波的预测协方差如果不匹配实际车速,就会出现“本来该往右走,预测框却停在原地”的情况,导致匹配失败。

匈牙利算法负责最优匹配。得到所有预测轨迹和当前帧检测框的代价矩阵后,用匈牙利算法求全局最优匹配,尽量让每个检测框都找到最合理的轨迹。这一步是 deepsort 的“调度核心”,它保证匹配不是贪心的一对一,而是全局代价最小化。

外观特征负责身份校验。仅靠运动预测,两辆车交错时很容易互相抢身份。deepsort 在初始化每条轨迹时,会用一个 ReID 小网络提取目标的外观特征向量,存进特征库,然后与当前检测框的外观特征计算余弦距离。运动相似但外观不一致的匹配会被拉低权重,这就是 deepsort 比原始 sort 算法抗 ID Switch 强很多的原因。

这三个模块的配合决定了跟踪质量,也对应三个核心超参数:max_dist 控制外观距离上限,max_age 控制轨迹丢失后还能保留多少帧,n_init 控制新轨迹需要连续命中多少帧才转正。后面第 4 章会展开调参逻辑。

2.3 车流人流计数的落地形式:虚拟检测线加一点状态管理

跟踪只是手段,统计才是目的。最常见的计数实现是画一条虚拟检测线(也可以是一个区域),然后判断目标中心点是否跨过这条线。

这里有一个关键设计决策:什么时候算“通过”?我一般用前后帧位置结合检测线判断。比如检测线是一条水平线,上一帧目标中心在线上方,这一帧在下方,就算一次穿越。这样比单纯判断“当前帧中心在下方”更稳,能避免目标在线上来回抖动造成的重复计数。

计数的状态管理也很重要。每条轨迹有一个 track_id,你需要在内存里维护一个 dict,记录哪些 track_id 已经计数过了。否则车辆在检测线附近因为遮挡丢失 ID、重新生成新 ID 后会被再计一次。这个 dict 就是“已统计名单”,每帧更新,项目结束时清空。

2.4 一个需要提前确认的边界:统计类别和方向问题

车流人流量统计不是“把一切框都数一遍”。COCO 类别里,car、truck、bus、motorbike、bicycle 都属于车辆,person 属于行人。你的项目如果要求区分“车流量”和“人流量”,就需要在检测结果里按 class_id 做分流,并且决定是否把自行车归到车辆还是行人侧。

方向统计则依赖检测线的方向性。高速出入口一般需要分“入城”和“出城”两个方向,车流量的统计要按穿越方向分别计数。实现上需要记录目标上一帧中心点相对检测线的位置,再判断本次穿越是正向还是反向。

我建议你在最开始就确定:统计几个方向、分几类目标、检测线画在什么位置。不要等代码跑通再改,因为检测线的坐标和方向逻辑会蔓延到主循环、状态管理、结果保存多个地方,后期改动容易引入隐蔽 bug。

3. conda 环境搭建与源码目录梳理:用最快的路径跑通第一帧

3.1 为什么要用 conda 单独建环境,Python 版本怎么选

yolov5 的依赖经历过多次迭代,PyTorch 版本、torchvision 版本、numpy 版本之间互相有兼容性要求。如果你直接在系统 Python 里装,大概率会因为某个包版本冲突浪费一整天。用 conda 单独建一个环境是最稳妥的做法,隔离干净,删了重建也方便。

Python 版本我建议选 3.8。yolov5 官方在较长一段时间内对 3.8 支持最稳定,3.10 之后某些依赖需要重新编译,对毕设项目没必要冒险。创建命令如下:

conda create -n yolo_deepsort python=3.8 -y conda activate yolo_deepsort

创建这一步完成后,先确认环境激活是否成功,再装 PyTorch。不要急着把 CUDA 相关包一次全装完,因为你的显卡驱动版本和 CUDA 版本未必匹配。稳妥的做法是先用 CPU 版本跑通项目,再换 CUDA 版提速。

3.2 依赖安装与权重文件准备

安装 PyTorch 时,我一般先用如下命令装 CPU 版,验证代码流程:

pip install torch==1.12.1 torchvision==0.13.1 --index-url https://download.pytorch.org/whl/cpu

CPU 版跑通后,再到 PyTorch 官网按显卡驱动选对应的 CUDA 版本安装。这一步非常重要:不要直接pip install torch装最新版,最新版对老显卡支持差,而且某些 torchvision 接口和 yolov5 源码不兼容。

随后安装其他依赖:

pip install opencv-python numpy pillow pyyaml scipy tqdm seaborn matplotlib

这个项目还需要两个权重文件:yolov5s.pt(检测权重)和 ckpt.t7(deepsort 的 ReID 权重)。yolov5s.pt 从 yolov5 仓库的 release 页面下载,ckpt.t7 从 deep sort pytorch 相关仓库的 release 或网盘下载。两个文件体积都不大,但下载后要检查文件能否被 torch.load 正常加载,这一步常被忽略。

3.3 跑通项目模板的命令与验证方法

项目源码目录通常长这样,yolov5 检测模块和 deep_sort 跟踪模块并列,主脚本在根目录:

project_root/ ├── yolov5/ # 检测模块 │ ├── models/ │ ├── utils/ │ └── weights/yolov5s.pt ├── deep_sort/ # 跟踪模块 │ ├── deep_sort/ │ └── deep/checkpoint/ckpt.t7 ├── track.py # 主运行脚本 ├── configs/ └── runs/ # 输出结果目录

拿到代码后,不要直接跑大视频,先用一个小视频做冒烟测试。命令类似:

python track.py --source assets/test_clip.mp4 --yolo-weights yolov5/weights/yolov5s.pt --conf-thres 0.25 --iou-thres 0.45

跑通的标准是:视频窗口正常弹出,检测框跟着画面里的车动,每辆车左上角有稳定的 ID 数字。如果视频窗口一闪而过、没有显示计数,先检查视频源路径和输出路径,再检查控制台有没有报错信息。

这一步最常见的失败点是cv2.VideoCapture打不开视频文件,原因往往是 OpenCV 没装全或视频编码器缺失。先换 mp4 试试,最后才考虑重装 opencv-python。

4. 核心代码实现:检测循环、跟踪循环与跨线计数三块拼图

4.1 加载 yolov5 模型,conf-thres 和 iou-thres 该怎么设

主循环开始前,先加载检测模型并解析类别名。项目使用 COCO 类别,所以要确认你的代码里读的是 COCO 标签还是自定义标签。

import torch from pathlib import Path from yolov5.models.experimental import attempt_load from yolov5.utils.torch_utils import select_device # 加载权重,这一步会把模型结构定义和权重一起加载进来 device = select_device('0' if torch.cuda.is_available() else 'cpu') model = attempt_load('yolov5/weights/yolov5s.pt', map_location=device) model.eval() # 类别名列表,COCO 数据集共 80 类 names = model.module.names if hasattr(model, 'module') else model.names print('可用的检测类别:', names)

逻辑说明:attempt_load是 yolov5 提供的一个兼容加载函数,能处理不同来源的权重文件。map_location=device特别关键,它决定权重加载到 GPU 还是 CPU。本机没有 CUDA 时,不写这个参数会直接报错。

names的作用是后续把 class_id 转成可读的类别名。你能看到names[2]是 car,names[0]是 person。注意有些自定义训练模型里这个列表会被替换,逻辑要跟着变。

参数说明:这里出现的两个阈值conf-thres和iou-thres不是模型本身的参数,而是在推理后处理阶段生效的后处理参数。conf_thres=0.25表示置信度低于 0.25 的框直接丢弃,适合远处目标多的高速场景;iou_thres=0.45是 NMS 的重叠阈值,两颗车距近、互相遮挡时,这个值不宜设太高,否则会把两辆车框成一个。

4.2 初始化 deepsort,读懂 max_age、max_dist、n_init 三个超参数

跟踪器的初始化参数直接决定整条计数链路的质量,每个参数都要理解后才好调。

from deep_sort.deep_sort import DeepSort # 初始化 deepsort 跟踪器 tracker = DeepSort( model_path='deep_sort/deep/checkpoint/ckpt.t7', max_dist=0.3, # 外观特征余弦距离阈值,默认0.2,高速场景放宽到0.3 max_iou_distance=0.7, # 级联匹配中 IoU 阈值,默认0.7 max_age=90, # 轨迹最大保留帧数,默认70,高速遮挡多时调大 n_init=3, # 连续命中3帧才确认轨迹 nn_budget=100, # ReID 特征缓存数量 use_cuda=torch.cuda.is_available() )

参数说明逐条拆:

  • max_dist=0.3:ReID 特征向量和轨迹特征库之间的最大余弦距离。阈值越大,匹配越宽松,越不容易丢 ID,但也越容易出现错误匹配。默认 0.2 在市区场景够用,高速场景车速快、帧间外观变化大,我经常放宽到 0.3。
  • max_iou_distance=0.7:用于级联匹配的最小 IoU 阈值,0.7 表示预测框和检测框重合度高于 0.7 才考虑匹配。这个值和 yolov5 的 iou-thres 是两回事,后者管 NMS,前者管跟踪匹配,别混。
  • max_age=90:一条轨迹连续多少帧没有被任何检测框匹配上,就从跟踪器中删除。高速场景下车辆可能被路牌、天桥遮挡 10 到 20 帧,默认 70 够用,但遇到长隧道就直接断。调大到 90 以上能减少轨迹断裂,代价是目标离开画面后还会残留几秒,延迟计数时需要注意。
  • n_init=3:新轨迹要连续命中 3 帧才会被确认,低于这个帧数的都算“未确认轨迹”。n_init 设太大会漏掉快速穿越画面的目标,设太小会出现大量一次性 ID。

4.3 车流人流量统计的跨线计数实现与去重

主循环是这个项目的心脏。检测、跟踪、计数、可视化全在视频帧的循环里完成。这里直接给出一个经过整理的实现骨架:

import cv2 import numpy as np import torch from yolov5.utils.general import non_max_suppression, scale_coords # 虚拟横线,假设帧高720 LINE_Y = 450 # 存储已经计数的 track_id,防止重复计数 counted_ids = set() # 分类统计 vehicle_count = 0 person_count = 0 cap = cv2.VideoCapture('assets/highway.mp4') while cap.isOpened(): ret, frame = cap.read() if not ret: break # -------- 检测部分 -------- img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) img_tensor = torch.from_numpy(img_rgb).permute(2, 0, 1).unsqueeze(0).float() / 255.0 img_tensor = img_tensor.to(device) with torch.no_grad(): pred = model(img_tensor)[0] det = non_max_suppression(pred, conf_thres=0.25, iou_thres=0.45)[0] boxes_xywh, confs, clss = [], [], [] if det is not None and len(det): # 坐标已经相对于预处理尺寸,缩放回原始帧 det[:, :4] = scale_coords(img_tensor.shape[2:], det[:, :4], frame.shape).round() for *xyxy, conf, cls_id in det: x1, y1, x2, y2 = map(int, xyxy) # 只保留车辆和行人 if int(cls_id) in [0, 2, 5, 7]: # person, car, bus, truck boxes_xywh.append([x1, y1, x2 - x1, y2 - y1]) confs.append(float(conf)) clss.append(int(cls_id)) # -------- 跟踪部分 -------- if len(boxes_xywh) > 0: tracks = tracker.update(np.array(boxes_xywh), confs, frame) else: tracks = tracker.update(np.empty((0, 4)), np.empty(0), frame) # -------- 跨线计数 -------- for trk in tracks: x1, y1, x2, y2, track_id = trk center_y = (y1 + y2) / 2.0 # 中心跨过检测线即计数,每个 track_id 只计一次 if center_y > LINE_Y and track_id not in counted_ids: counted_ids.add(track_id) # 通过中心点反查类别,这里用跟踪框和检测框中心点最近匹配 # 简化处理:从当前帧检测结果中找到中心点最近的类 if nearest_cls is not None: if nearest_cls == 0: person_count += 1 else: vehicle_count += 1 # -------- 可视化 -------- cv2.line(frame, (50, LINE_Y), (frame.shape[1] - 50, LINE_Y), (0, 255, 0), 2) for trk in tracks: x1, y1, x2, y2, track_id = trk cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, f'ID:{track_id}', (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.putText(frame, f'vehicles: {vehicle_count} persons: {person_count}', (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) cv2.imshow('traffic count', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()

这段代码的逻辑链是:对每一帧先让 yolov5 产出检测框,再让 deepsort 把这些框和上一帧的轨迹做匹配,得到带 track_id 的稳定跟踪框,最后用中心点穿越检测线的方法进行计数。其中scale_coords这行很关键,yolov5 内部会把输入图片缩放到 640x640,输出的坐标也是基于 640 尺寸的,不缩放回原始帧尺寸,画框和计数坐标全都会偏移。

counted_ids用 set 存储已计数的 track_id,这是去重的核心。如果目标被遮挡导致 ID 丢失,deepsort 分配了一个从未见过的新 ID,set 里没有记录,就会再计一次,这就是重复计数的直接原因之一。想缓解,只能把跟踪调稳,让 ID 尽量不跳变。

tracks数组来自deep_sort库的 update 方法,每一行是[x1, y1, x2, y2, track_id]。注意它不带类别,所以计数阶段需要从检测结果里把类别“补回来”。做法是:对每个跟踪框,在当前帧所有检测框里找中心点欧氏距离最近的那个框,取其类别。这算是工程上最朴素且有效的类别关联方法,实际项目中我见过有人直接把cls_id塞进 tracking 接口一并输出,这样代码更干净但耦合度高。

4.4 把计数结果写到文件,而不是只显示在窗口里

演示时视频窗口实时显示计数就够了,但毕设答辩需要结果数据来支撑。我建议在代码末尾把统计结果导出为文本文件或 CSV:

with open('runs/count_result.csv', 'w') as f: f.write('vehicle_count,person_count\n') f.write(f'{vehicle_count},{person_count}\n') print('统计完成,车辆数:', vehicle_count, '行人数:', person_count)

另外还可以保存一版“带叠加框的标注视频”。实现上只需要在每次cv2.imshow之前把帧写入cv2.VideoWriter,这里不再重复贴代码。要提醒的是,VideoWriter 的编码器选择要用 mp4v,帧率和原视频一致,否则输出视频会花屏或长度不对。

最后调试时,在控制台打印每一帧的检测数量、跟踪数量、计数累计数量,会大幅加速定位问题。尤其是计数数字在某一帧突然跳了很大的数,基本可以直接定位是去重逻辑失效,而不是模型出了问题。

5. 高速场景避坑排查:计数翻车的五个典型问题和修法

5.1 同一辆车被计数两次,ID 跳变的真凶

现象:监控视频里一辆白色轿车驶过检测线,画面中的 ID 从 37 突然跳成 51,最终计数统计里显示两辆车。回放时发现实际上只有一辆车。

原因:车辆在高速场景帧间位移大,卡尔曼滤波预测的位置和实际位置偏差太大,或外观特征因为反光、图像模糊等原因变化剧烈,deepsort 没能把当前检测框和原轨迹匹配上,于是分配了新 ID。

解决:先看是不是检测漏帧造成的。连续丢帧超过 max_age,轨迹就会从删除队列里消失,之后新目标重新建轨迹。将max_age从 70 调到 120,再观察 ID 跳变是否减少。另一个方向是放宽max_dist,0.2 太紧就改 0.3,让外观匹配更容易命中。注意代价是误匹配变多,所以调参后要仔细观察不同车辆交错时是否互相换 ID。

5.2 目标一帧漏检就丢跟踪,轨迹被切断

现象:一辆车被路牌挡住了一瞬间,跟踪框直接消失,车从路牌另一边出来时变成了新 ID;人流量统计里一个行人被数了两次。

原因:检测器在遮挡瞬间没有输出对应的框,deepsort 找不到匹配的检测框,只能让轨迹进入“未命中”状态。如果这种情况持续超过 max_age,轨迹就被移除了。

解决:max_age 是首选,调大到 150 以上,让轨迹容忍更长时间的遮挡。另一个角度是降低 conf-thres,从 0.25 降到 0.15,让遮挡瞬间被漏检的车辆还有可能被检测出来。不过要小心低阈值带来了大量误检框,跟踪器会把噪声也当成目标,ID 数量反而膨胀。所以更推荐结合场景:高速遮挡多,max_age 优先调大,再用一个低置信度阈值做补充。

5.3 远处的车和行人完全检测不到,conf-thres 设太高

现象:视频里远处驶来的车辆,肉眼能看到,但画面里没有检测框,直到距离很近时才有框。计数结果比实际流量少一截。

原因:远处目标在图像中的像素面积小,模型对它的响应置信度本来就低。conf-thres 默认 0.25 时,这些低置信度框会被全部过滤,等于从源头上切断了跟踪的可能性。

解决:把conf-thres下调到 0.1 或 0.15 试一下,你会发现远处目标开始出框,但噪声也会变多。另一个有效措施是提高输入分辨率,检测时把帧缩放到 640 不够,就用 960 甚至 1280,小目标占的像素面积变大,模型更容易输出高置信度结果。代价是每帧推理时间变长,GPU 占用升高。高速场景如果视频本身是 1080p,我建议缩放到 960 即可,1280 对速度影响明显但精度提升有限,性价比不高。

5.4 车流密集时两个目标黏成一个框

现象:两辆并排的轿车在画面里被画成了同一个红色框,计数直接少算一辆;或者两辆车交错时 ID 互相交换。

原因:NMS 的 IoU 阈值设得太高,导致重叠程度较大的相邻车辆被抑制掉其中一个框。跟踪器匹配时又因为错误的检测框位置,把两个目标合并成了一条轨迹。

解决:iou_thres从 0.45 调到 0.3,让 NMS 对重叠框更敏感,两车距离近时也能各自保留出独立的框。注意 NMS 阈值太低的副作用是同一辆车可能被输出多个框,这时 deepsort 匹配会困惑。我的经验是高速并排车流场景 0.35 左右比较稳,做实验时把几个值都试一遍,看哪个值下跟踪 ID 不跳变、不合并。

5.5 deepsort 权重加载失败与依赖版本乱账

现象:运行track.py时报FileNotFoundError找不到ckpt.t7,或者报KeyError、RuntimeError,提示模型结构不匹配。

原因:权重文件没有实际下载成功,文件只有几十 KB 但实际应为几百 MB;另一个原因是 deepsort 源码版本和权重文件训练时用的 torchreid 版本不一致,导致torch.load反序列化时字典键对不上。

解决:下载权重后先看文件大小,ckpt.t7通常 300MB 以上,大小不对就重新下载。加载时不要直接torch.load(path),先加一行打印权重文件的内部键名,确认它来自哪个版本的 ReID 模型,再和源码里的加载逻辑比对。如果是版本问题,去 deep_sort_pytorch 仓库找和权重文件对应的源码版本,不要混搭最新版和旧权重。环境方面,scikit-learn和scipy版本也要盯住,某些老代码依赖sklearn.preprocessing里的旧函数,新版本换位置会直接ImportError。

6. 给这个项目加分:超参数调优、自定义数据集训练与 ONNX 部署

6.1 用计数误差和 ID Switch 验证你的系统不是“看起来能用”

毕设答辩最怕被问“你统计的准确率是多少”,如果你只说“感觉挺准”就会被追问到翻车。我会把验证拆成两个指标:计数准确率和 ID Switch 次数。手动准备一段 30 秒左右的测试视频,先人工数出真实车辆数,再跑项目得到统计数,算误差。ID Switch 次数需要你观察视频回放里同一辆车的跟踪 ID 变化了几次,变化次数越多,说明跟踪质量越差,重复计数风险越高。

参数调优时每次都跑同一段测试视频,对比几个版本的数字。这是最朴素的消融实验,但比任何花哨的指标都更有说服力。最后把对比表放进论文里,整个系统的可信度立刻不同。

6.2 想提高检测精度,就训练你自己的数据集

市面上预训练 yolov5s 权重在通用场景下表现不错,但你的毕设场景如果是特定路口、特定角度,通用模型未必有最优效果。训练自己的数据集是值得做的进阶:标注工具用 labelimg,导出 YOLO 格式的 txt 标注文件,每个文件夹放图片和同名 txt,然后改data/custom.yaml里的类别数量和路径,调用train.py --data custom.yaml --weights yolov5s.pt --img 640 --epochs 100。训练完成后,把best.pt换到第 4 章的attempt_load里即可。

这里要注意,训练集的标注质量直接决定跟踪阶段的体验。如果标注框比实际车辆大不少,检测框位置不稳定,deepsort 匹配会频繁出错。

6.3 把模型导出 ONNX,部署到边缘设备

答辩加分项之一是证明你的方案不只是跑在实验室 GPU 上,还能部署到边缘设备。yolov5 官方提供export.py脚本,一条命令导出 ONNX 权重;deepsort 的 ReID 网络比较轻量,直接部署也没问题:

python yolov5/export.py --weights yolov5/weights/yolov5s.pt --include onnx --opset 12

导出后拿到一个 onnx 文件,市面上大部分推理框架(ONNX Runtime、TensorRT)都能直接加载。边缘设备部署的常见做法是用 TensorRT 引擎加速推理,把 onnx 转成 trt 模型后运行,帧率能比 PyTorch 原生推理快数倍。不过 TensorRT 的优化过程极度依赖 GPU 型号,换一张显卡就要重新转一次模型,这个坑提前知道就不慌。

这段路走完,你的毕设就有了完整闭环:检测准确率可解释、跟踪稳定有验证、计数误差可量化、部署方案可演示。我自己做这类项目时,习惯把每次调参后的视频和原始视频放一起对比播放,用肉眼看 ID 跳变是最快的问题定位办法。真希望有人早点告诉我这些超参数不是越多越好,把 max_age 和 conf-thres 这两个值调明白,项目基本就成功了大半;调不明白的时候,先停手检查检测框质量,而不是在跟踪参数里空转。希望这篇能帮你把这个方案稳稳落地。

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

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

《一文吃透红黑树:性质、插入、旋转与代码实现》

一、为什么需要红黑树 1.1 一切的起点:二叉搜索树(BST)二叉搜索树(Binary Search Tree)的思想非常朴素:左子树的所有值都比根小,右子树的所有值都比根大。借助这个性质,查找一个元素…

作者头像 李华
网站建设 2026/9/28 21:04:20

Innovus数字后端入门:Floorplan与Powerplan实战指南

1. 数字后端入门第一课:Floorplan与Powerplan到底在做什么刚接触数字后端的人,十有八九会在Innovus里被Floorplan和Powerplan这两个环节卡住。工具报错一大堆,DRC违规满屏飘红,电源网络压降超标,绕线绕不通&#xff0c…

作者头像 李华
网站建设 2026/9/28 21:03:54

AI智能体重构旅行规划:Prompt工程与FastAPI实时票务接口实战

1. 旅行规划工作流为什么需要AI智能体重构做过旅行规划的人都有一个共同感受:这件事看起来简单,实际上是一个典型的多约束优化问题。你要同时考虑时间窗口、预算上限、交通衔接、景点开放时间、个人偏好、同行人意见,甚至还要留出应对突发状况…

作者头像 李华