简介YOLOv5与DeepSORT结合的车辆检测与多目标跟踪项目资源面向具备一定深度学习基础的开发者、研究人员及智能交通方向学习者有助于解决车辆识别、跟踪及数据集准备等实际问题。压缩包共2000个文件大小约29.76MB核心为1588个XML标注文件配合409个TXT标签/数据列表、MD说明文档和PY数据划分脚本覆盖从标注数据到训练验证集划分的完整环节已有150人学习下载。借助清晰的目录结构、README说明及划分脚本使用者可快速理解数据组织方式在YOLOv5框架中直接开展车辆检测训练并结合DeepSORT算法实践多目标跟踪基于现成数据集还可进行迁移学习、算法对比或面向智能交通监控、停车场管理等场景的二次开发显著节省数据整理与预处理时间适合在真实项目或课程设计中快速复用。1. 车辆检测项目YOLOv5 与 DeepSORT 的组合适合直接拿去二次开发的完整资源先说结论这份资源不是那种只有权重文件的半成品而是把目标检测、多目标跟踪、数据集三件事一次性凑齐了。最核心的价值在于它自带一套已经拆分好的车辆标注数据TXT 格式的 YOLO 标签外加一个 split_train_val.py 划分脚本拿到手之后不用再从零标注数据能把精力直接放到模型训练和跟踪调参上。项目主体是基于 YOLOv5 做单帧检测DeepSORT 负责跨帧关联同一辆车实测能稳定输出车辆的 ID 轨迹适用于车流统计、卡口监控、停车场分析这类场景。适合正在做车辆识别相关课程设计、毕设或者在智能交通方向做原型验证的开发者。2. 检测与跟踪的咬合方式为什么 YOLOv5 和 DeepSORT 必须分开跑2.1 单帧检测与跨帧关联的分工逻辑YOLOv5 的本质是单帧目标检测器输入一张图片输出这一帧里所有车辆的边界框、类别和置信度。它没有任何记忆能力上一帧检测到一辆车这一帧它不认识。也就是说如果直接用 YOLOv5 对视频逐帧检测你会得到一个不断跳动的框但不知道哪个框是上一帧的哪辆车更数不出「有几辆车先后通过了这条车道」这种问题。DeepSORT 解决的就是「跨帧关联」这件事。它会把每一帧检测器输出的框当作观测值通过卡尔曼滤波预测每个目标在当前帧的位置再结合外观特征一个 ReID 网络提取的特征向量和 IOU 距离把新检测框和历史轨迹关联起来。合并之后每一辆车就有了唯一的 ID从出现到消失全程保持同一个编号。这两个模型一个管「看到什么东西」一个管「记住谁是谁」串联起来的结构如下YOLOv5 对视频帧做检测输出x1, y1, x2, y2, class_id, conf的列表把检测结果从原图坐标系转换成 DeepSORT 需要的格式通常是ltrb或cxcywhDeepSORT 使用这些检测框更新轨迹决定哪些框属于已有目标、哪些是新目标、哪些轨迹已失效返回带 ID 的追踪框画到帧上输出这样分工的好处是检测和跟踪可以独立调参。检测不准就调 YOLOv5 的置信度阈值、训练数据跟踪不稳定就调 DeepSORT 的级联匹配参数互不干扰。坏处是两端都要维护但这也是目前工程上最稳的组合。2.2 DeepSORT 的四步状态循环级联匹配、IOU、特征距离与轨迹生命周期DeepSORT 对每个目标维护一条轨迹内部用卡尔曼滤波器做状态预测再用匈牙利算法做匹配。实际跑起来一共有四个步骤每帧都会走一遍预测用每个轨迹的历史运动状态预测它在当前帧的位置级联匹配优先给最近更新过的轨迹分配检测框解决遮挡后 ID 频繁跳变的问题IOU 匹配对未匹配上的轨迹和检测框用边界框重叠程度做最后一轮关联更新匹配上的轨迹用检测框修正预测值未匹配的轨迹进入待删除队列这个流程里最影响效果的是级联匹配的前置条件。代码里通常用max_cosine_distance默认为 0.2控制外观特征的容忍度特征距离超过这个值就拒绝匹配。取值越大匹配越宽松误关联的概率上升取值越小匹配越严格容易出现漏匹配导致 ID 频繁重新分配。还有一个max_age参数表示轨迹在失去匹配后最多保留多少帧。取 30 表示一辆车被遮挡 30 帧以内不会丢 ID超过就被判定轨迹终止。做车辆跟踪时这个值建议调大一些因为车流里大型车辆相互遮挡非常常见把max_age从默认 30 调到 5080车辆被遮挡后重新出现的 ID 保持率会有明显改善。2.3 YOLOv5 与 DeepSORT 的工程连接从模型输出到追踪框工程上连接两个模型的方式有很多常见做法是改 YOLOv5 的detect.py在推理循环里把检测结果送入 DeepSORT 的 tracker 对象。伪代码如下# tracker 初始化部分 from deep_sort_pytorch.deep_sort import DeepSort # 传入检测模型权重路径和 ReID 权重路径 # max_cosine_distance 控制外观匹配阈值 # max_age 控制轨迹在丢失匹配后的存活帧数 deepsort DeepSort( deepsort_pytorch/checkpoints/osnet_x0_25_msmt17.pt, max_cosine_distance0.3, max_age60, nn_budget100, use_cudaTrue ) # 每帧推理的主循环 for frame in video_frames: # YOLOv5 检测得到 xyxy左上右下坐标的检测框 results model(frame, size640) detections results.xyxy[0] # [x1, y1, x2, y2, conf, class_id] # 过滤低置信度目标保留车辆类别 conf_mask detections[:, 4] 0.4 class_mask detections[:, 5] 2 # 类别 2 按实际类别编号调整 vehicles detections[conf_mask class_mask] # 取前四列作为 bbox第五列滤波后的置信度作为检测器得分 bbox_xyxy vehicles[:, :4] confidences vehicles[:, 4] # 送入 DeepSORT 更新轨迹 outputs deepsort.update(bbox_xyxy, confidences, frame) # outputs 的结构为 [x1, y1, x2, y2, id]逐框绘制即可 for x1, y1, x2, y2, obj_id in outputs: # 绘制边界框和 ID 号 draw_box_with_id(frame, x1, y1, x2, y2, obj_id)代码里需要重点说明的是DeepSort的初始化参数。max_cosine_distance我一般设 0.3比默认的 0.2 稍宽松因为车辆这类目标的颜色差异相对小特征过于严格会导致前后帧匹配不上的概率增加。nn_budget控制外观特征库的大小设 100 即可太大增加计算量太小导致特征unavailable。2.4 检测与跟踪的性能平衡YOLOv5 和 DeepSORT 串行跑整体速度由慢的一端决定。实际测下来在 1080P 视频上用 YOLOv5s 加 DeepSORT 跑 CPU大约 3~8 FPS用 GPU如某入门级显卡能到 25~40 FPS。如果要把速度拉上去有两个思路一是把输入分辨率从 640 降到 416 甚至 320检测精度略有回落但跟踪不易中断。二是只对检测结果中置信度最高的前 N 个框做特征提取减少 DeepSORT 内部 ReID 网络的推理次数。很多实际部署项目就是走这条路效果好且改动小。3. 数据集与脚本TXT 标注怎么用、怎么划分训练集和验证集3.1 数据集目录的组织方式这份资源里的数据集部分不是那种一张图配一个 XML 的传统 VOC 结构而是 YOLO 系原生的 TXT 标注格式。每个标注文件对应一张图片文件名和图片名一致后缀是.txt。文件里每一行代表一个目标class_id x_center y_center width height注意这里坐标全部是归一化坐标除以了图片宽度和高度值域在 0 到 1 之间。以car_net_141.txt为例内容大致如下0 0.482981 0.614741 0.216466 0.135039 0 0.244710 0.602345 0.147392 0.117419 0 0.374651 0.667713 0.186699 0.093452第一列的0是类别编号如果只有汽车这一类classes.txt里就只写一行car。后面四个数字分别是中心点 X、中心点 Y、宽度、高度全部是归一化后的比例值。在实际使用时注意不要把这些坐标直接当成像素坐标传给 OpenCV 画框。需要乘回图片宽高才得到像素级的xyxy或cxcywh。这是最容易被新手踩的坑。3.2 split_train_val.py 的数据划分逻辑split_train_val.py的作用是把图片和标注按比例划分为训练集和验证集并生成两个文本文件——train.txt和val.txt项目里已经有一个val.txt存在。这个脚本内部逻辑通常包含几个步骤扫描指定目录下所有.txt标注文件按设定比例如 0.85 训练、0.15 验证随机划分把图片的绝对路径写入train.txt和val.txt要求图片目录和标注目录结构匹配否则报错import os import random # 读取某个目录下所有标注文件不含非 txt 文件 label_dir labels/train labels [f for f in os.listdir(label_dir) if f.endswith(.txt)] # 可复现划分固定随机种子保证每次训练的划分一致 random.seed(42) random.shuffle(labels) val_ratio 0.15 val_count int(len(labels) * val_ratio) train_list labels[val_count:] val_list labels[:val_count] # 注意写入的是图片路径不是标注路径 # YOLOv5 训练时通过 txt 里的路径定位图片再自动查找对应标注文件 with open(train.txt, w) as f: for item in train_list: img_path os.path.join(images/train, item.replace(.txt, .jpg)) f.write(img_path \n) with open(val.txt, w) as f: for item in val_list: img_path os.path.join(images/train, item.replace(.txt, .jpg)) f.write(img_path \n)这段脚本最关键的是它把「划分依据」和「读取路径」分开了随机打乱的是标注文件的文件名但写入文本的是图片路径。如果不理解这一点很容易在改脚本时把后缀名写错导致训练时 YOLOv5 报AssertionError: train: No labels in ...。3.3 手动核对 val.txt 的有效性拿到项目自带的val.txt之后推荐先检查一下里面路径是否正确。常见做法是打开文件任意取三行逐个ls或test -f确认图片真实存在while read p; do if [ ! -f $p ]; then echo 缺失文件: $p fi done val.txt为什么要强调整这个我接过不少开源数据集有一种情况是标注、划分脚本都齐全唯独图片被省略掉或者图片目录名和脚本里不一致导致val.txt里全是死路径。先跑一遍这个检查可以帮你避开最浪费时间的数据对齐问题。另外TXT 标注里比较容易混入空文件也就是 0 字节的.txt代表这张图没有车辆。YOLOv5 训练时会把这种样本当作背景图处理正常情况下没问题。但如果你拿到数据后自己批量处理过标签必须确保空文件确实存在而不是缺失缺失的话 YOLOv5 会直接跳过这张图导致采样数量不对。4. 训练与推理全流程从数据到带 ID 的追踪输出4.1 三种使用方式直接推理、微调训练、全量重训拿到这套资源后按目标不同有三级玩法直接推理把项目自带的 YOLOv5 权重和 DeepSORT 串起来对视频做实时检测加追踪。适合快速验证效果、做 Demo微调训练基于 COCO 预训练的 YOLOv5s 权重用项目提供的数据集训练 50~100 个 epoch适应具体场景。适合有一批自己的同类车辆图片全量重训改模型结构或类别数从零训练。需要的数据量大如果不是搞科研不太建议多数人适合第二种。微调训练还有一个隐形的收益COCO 预训练权重对交通车辆的类别比较熟几十个 epoch 就能收敛到不错的精度比从零训省好几个小时。4.2 YOLOv5 训练命令与关键参数训练阶段建议用 YOLOv5 官方仓库的train.py。命令大致如下python train.py \ --data vehicle.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --data-dir ./dataset \ --project ./runs/trainvehicle.yaml的内容决定了数据从哪里加载必须和你的目录结构完全对应train: ./dataset/train.txt val: ./dataset/val.txt nc: 1 names: [car]这里的nc是类别数因为有且只有车辆这一类所以为 1。names列表里的名称按class_id的顺序排列只影响可视化标签不影响训练。切不可把nc写成你标注文件里的目标总数那是另一个概念。参数方面--img 640是训练分辨率--batch 16是批大小--epochs 100是训练轮数。如果你的显存只有 4GB 左右batch 调成 8--img降到 512不然很容易 OOM。训练时盯着val/box_loss和mAP0.5这两项看前者只降不升说明模型在收敛后者达到 0.85 以上就够用在大多数车流量场景了。4.3 推理阶段如何输出带追踪 ID 的视频推理时用到的是前面 2.3 节那套结构。核心命令部分可以直接用脚本跑也可以用 YOLOv5 自带的detect.py降低门槛python detect.py \ --source traffic_video.mp4 \ --weights runs/train/exp/weights/best.pt \ --conf-thres 0.4 \ --iou-thres 0.45 \ --img 640 \ --save-vid--conf-thres 0.4把置信度阈值提到 0.4比默认 0.25 高一些可以在车辆密集区减少误检。代价是远处的弱小目标可能被过滤掉。我的习惯是先跑一遍默认参数统计输出的漏检和误检情况再决定要不要动阈值。如果你想在检测完的输出上继续做跟踪推荐把 detect 的输出接一个视频处理管道。工程上最简洁的做法是先检测存成带置信度的检测结果文件再单独跑跟踪脚本这样如果跟踪效果不对不用重新跑检测。4.4 输出的可视化数据怎么解读追踪输出的每一帧目标框上方会显示 ID 号例如ID 17表示当前帧里这辆车是第 17 个被系统识别并持续跟踪的目标。检查跟踪效果时重点关注三件事同一辆车在整个过场过程中 ID 是否始终一致两辆车交汇后 ID 有没有互换车辆完全遮挡再出现时ID 是保持原编号还是新分配这三个现象分别对应检测框稳定性、外观特征区分度、遮挡处理能力。出现哪一个问题优先排查的模块不同下面避坑章节会细说。5. 避坑标注、训练、跟踪三层的血泪经验5.1 标注坐标归一化翻车框画偏了半个图现象用 OpenCV 或标注工具可视化时框的位置和车辆实际位置偏差很大有的甚至画到图外。原因TXT 标注用的是归一化坐标不是像素坐标。直接用x_center * img_width还原时如果图片尺寸读取方式和标注工具不一致比如标注时用 1920x1080读取时 OpenCV 给错了尺寸整个框就错位。解决检查可视化脚本里的宽高读取顺序。OpenCV 读图后h, w image.shape[:2]宽是w高是h。归一化还原公式是x_pixel x_center * wy_pixel y_center * h不要写反。5.2 DeepSORT 的 ReID 权重缺失外观匹配完全失效现象代码能跑但跟踪 ID 疯狂乱跳一辆车开过去出现五六个新 ID却没有任何报错。原因不少开源整合包里把 ReID 权重的下载地址放在 README 里没有跟随代码一起打包。DeepSort()初始化时找不到权重文件不会崩溃但此时外观特征全为空只靠 IOU 匹配稍微遮挡几下 ID 就断裂。解决先确认deepsort/checkpoints/下存在有效的.pt权重再检查max_cosine_distance是否在 0.20.5 之间。另外用torch.load手动验证一下加载权重后看输出的特征向量是否因torch.empty初始化如果是说明权重没真正加载成功。5.3 车辆大面积遮挡导致 ID 断裂现象公交车或卡车前后交叠车辆从大型障碍物后重新出现时被分配了新 ID车流统计数字明显虚高。原因DeepSORT 的max_age太小轨迹在遮挡期内被判为死亡车辆重新出现时自然开新轨迹。解决把max_age从默认 30 提高到 6090。同时把max_cosine_distance从 0.2 放宽到 0.3让外观相似度匹配有更多回旋余地。这两个参数一改ID 稳定率通常能提升 20% 以上。5.4 训练时路径和结构的坑No labels 报错现象训练启动后立刻报AssertionError: train: No labels in ...。原因train.txt或val.txt里的图片路径和标注目录结构不匹配。最常见的是脚本里写死了images/train前缀但你的图片实际放在images/raw。解决把train.txt中的某个路径拿出去ls验证。路径必须写完整绝对路径或用相对于yolov5目录的相对路径。用脚本批量生成的路径一定要先抽查三行再跑训练不要盲信脚本一次成功。5.5 batch 参数和显存不匹配导致训练中断现象训练跑到一半GPU 显存占满进程被杀损失几千步进度。原因batch 设得过大又没有开启梯度累积。4GB 显存硬上 batch 32 大概率爆显存。解决先按batch 16 / img 640起跑OOM 就减半 batch。这样损失一半训练吞吐但至少不会废掉整个训练。条件允许时用--batch 8 --img 640加--accumulate 2等效 batch 16 但显存占用减半。6. 进阶技巧置信度阈值与跟踪参数联调让 ID 保持率再上一个台阶很多人把「检测参数」和「跟踪参数」分得清清楚楚实际工程里它们必须联动。YOLOv5 的conf-thres降低时检测框数量变多DeepSORT 的匹配压力变大误关联概率随之上升conf-thres提高时框变少漏检增多轨迹得不到观测更新ID 又容易断裂。所以调试的核心是找到两者配合的最优点。我最常用的一条验证路径是固定conf-thres为 0.3max_cosine_distance为 0.3max_age为 60统计连续 500 帧内 ID 的切换次数和轨迹断裂点调低conf-thres到 0.2观察 ID 切换次数是否上升回归一步调高max_age到 90再统计 ID 断裂率实际操作中记录一张表conf-thres、max_age、跟踪中断次数、ID 总数。这样 4 组实验下来基本能锁定适合你视频场景的参数组合。不要凭感觉去改参数之间是联动的每次只动一个变量是基本原则。另外如果你的场景是高速卡口车速快、遮挡极少可以把max_age缩小到 20 左右此时轨迹生命期短ID 释放快系统反应敏捷反而适合。停车场那种多遮挡慢速场景max_age就得 80 起步。越快的车速越短的遮挡时间max_age不需要太大越慢的拥堵路段遮挡时间长max_age小了必丢 ID。理解了这一点换场景调参就不会无据可依。我自己跑车流统计时翻车最狠的一次是全程默认参数结果一条直路上三辆车互相超车最后 ID 总数到了 37 个实际只有 3 辆车。从那以后我每次拿到新的视频数据都强制走一遍上面这套参数联调流程先固定检测阈值然后逐个动跟踪参数再记录 ID 稳定率。调一组参数花不了几分钟但能省掉事后数错车的整个麻烦。这套资源正好可以拿来做这件事希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?