尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

YOLOv5+DeepSORT驾驶员分心驾驶检测:检测追踪到行为判定实战

YOLOv5+DeepSORT驾驶员分心驾驶检测:检测追踪到行为判定实战 简介一份面向毕业设计、课程设计与期末大作业的驾驶员分心驾驶行为预警系统源码包基于YOLOv5目标检测与DeepSORT多目标跟踪技术覆盖疲劳驾驶、危险行为识别等典型场景适合有一定编程基础、希望快速搭建完整深度学习项目的学生参考。压缩包共59个文件包含20个Python脚本、18个YAML配置以及预训练权重、人脸关键点模型、演示视频、操作手册和界面文件等整体约118.04MB目录结构清晰便于按模块阅读调试。其中源码覆盖检测、疲劳判断与界面交互配置文件对应模型结构与训练参数手册则梳理环境搭建和运行说明。源码带有详细注释文档和演示录屏可辅助理解目标检测、目标跟踪到疲劳状态判断的完整流程从视频输入到预警结果输出均有对应实现。目前已有106人学习下载适合作为毕业设计、期末大作业或课程设计的高分参考项目。1. 驾驶员分心驾驶检测项目YOLOv5 检测加 DeepSORT 追踪的毕设源码包复现前先想清楚这条链路驾驶员分心驾驶行为检测这个题目YOLOv5 做目标检测、DeepSORT 做多目标追踪听起来是成熟得不能再成熟的组合但真正把项目跑起来的人都知道模型能出框只是第一步把「检测框」和「追踪 ID」对齐再靠连续帧判断疲劳和分心行为才是这套源码的难点所在。这套毕业设计源码包同时包含 YOLOv5 检测、DeepSORT 追踪和文档说明适合要做课程设计、毕业设计或者想快速上手一个完整工程链路的人。它能帮你解决的是从录制的驾驶视频里识别打电话、吸烟、打哈欠、闭眼等行为并输出带 ID 的统计结果而不只是贴一张标注图。2. 系统架构与检测、追踪两条主线为什么是 YOLOv5 加 DeepSORT而不是端到端方案2.1 检测器负责「是什么」追踪器负责「是谁」分心驾驶检测和普通目标检测最大的区别在于行为是一个时间概念。单帧里出现一个手机可能是乘客拿着连续三十帧里同一个驾驶员 ID 一直在贴近耳边才值得判定为「接打电话」。所以这套系统的数据流是这样走的摄像头采集一帧画面YOLOv5 先做目标检测输出若干个边界框和类别接着把带有置信度的检测框交给 DeepSORTDeepSORT 根据框的位置、大小、外观特征做帧间关联给每个目标分配一个稳定的 ID最后在上层用一个行为判定模块把「帧级别」的检测结果做成「行为级别」的统计输出。这种检测加追踪的架构好处是把两个问题拆开解决。YOLOv5 只需要回答「这一帧里有哪些危险物和行为姿态」DeepSORT 只负责回答「这些目标如何跨帧关联」行为判定再基于 ID 做时间窗口统计。相比端到端的视频行为识别模型比如 SlowFast、3D CNN这条链路的工程实现更简单、标注成本更低、可解释性更强这也是绝大多数毕业设计选这条路的原因。2.2 检测输出与追踪输入要对齐哪些字段YOLOv5 的 inference 输出经过 non_max_suppression 之后每条检测结果有六个字段x1, y1, x2, y2, conf, cls分别是左上角坐标、右下角坐标、置信度和类别 ID。DeepSORT 的 update 方法需要的是目标的边界框坐标、置信度、类别和一个用于外观匹配的特征向量。这里的对齐点是坐标必须从像素坐标直接传入类别必须映射到追踪器的类别表而特征向量一般由追踪器内部的一个 ReID 小网络从检测框裁剪出来的图片区域提取。我一般会在接入层写一个process_frame函数把 YOLOv5 的 tensor 检测结果转成 numpy 数组再按 DeepSORT 的接口格式传递。这里最容易踩坑的是坐标系不一致YOLOv5 默认输出的是 xyxy 格式而部分 DeepSORT 版本接收的是x1, y1, x2, y2但更新输出的是tlwh左上角 x、y、宽、高如果后面画框用的是输出值就得自己再转一次。另一个对齐点是类别映射假设你的类别表是[phone, smoke, yawn, closed_eye, drink, normal]那么检测器输出的 cls0 代表 phone传给追踪器时也要带同一个映射否则行为判定模块读到的类别是错位的。2.3 行为判定层把帧级检测变成行为级统计有了追踪 ID行为判定就能落到一个很朴素的思路上对每个 ID 维护一个固定长度的滑动窗口统计窗口内各类别出现的频次占比超过阈值就判定为发生了该行为。下面是一个常见的判定实现我把窗口长度设为 30 帧对应每秒 30 帧视频里约 1 秒的观察窗口。# behavior_judge.py —— 按追踪ID做滑动窗口行为统计 from collections import Counter, deque BEHAVIOR_WINDOW 30 # 窗口长度30帧约1秒 BEHAVIOR_RATIO 0.7 # 占比阈值超过70%才判定 state_queue {} # track_id - deque(cls_id) def judge_behavior(track_id, cls_id): if track_id not in state_queue: state_queue[track_id] deque(maxlenBEHAVIOR_WINDOW) queue state_queue[track_id] queue.append(cls_id) if len(queue) BEHAVIOR_WINDOW: return None counter Counter(queue) top_cls, top_cnt counter.most_common(1)[0] if top_cnt / BEHAVIOR_WINDOW BEHAVIOR_RATIO: return top_cls return None这个逻辑的关键参数是两个窗口长度和占比阈值。窗口太短碰巧检测到一帧手机放在耳边就会被误判窗口太长真实短促的低头看手机行为又容易被吞掉。我建议行为触发类打电话、喝水、吸烟用 0.7 的占比和 30 帧左右窗口而疲劳类闭眼用更短窗口比如 10 帧因为闭眼的单次时长通常不足一秒。这里要记住这个判定模块属于规则层它不参与模型训练但最终输出给答辩看的行为统计图都是在这层生成的。3. 环境配置与数据准备从拿到源码包到第一次能跑通训练3.1 环境安装Python、CUDA 和依赖版本一次性锁死这套源码的第一道门槛是环境。YOLOv5 的版本迭代会连带 torch、torchvision 版本变化DeepSORT 部分又引入了 scipy、sklearn 之类的依赖。我一般建议先建一个干净的 conda 环境再按 requirements.txt 安装不要直接在 base 环境里操作不然很容易出现包冲突又不敢动。# 创建专用环境Python 版本按仓库 requirements 要求来 conda create -n drive_act python3.8 conda activate drive_act # 先装 PyTorch 和 torchvision注意 CUDA 版本 # CUDA 11.x 环境用 -c pytorchCPU 机器去掉后面的 cu 后缀 conda install pytorch torchvision torchaudio cudatoolkit11.3 -c pytorch # 从源码包 requirements.txt 安装剩余依赖 pip install -r requirements.txt这套安装顺序里最容易翻车的是 torch 与 CUDA 不匹配。很多同学的机器装的是 CUDA 12却拿着旧版仓库里的代码去装 torch 1.10跑起来直接提示CUDA initialization error。我的做法是先nvidia-smi看驱动支持的 CUDA 版本再回头选 torch 版本装完后用python -c import torch; print(torch.cuda.is_available())验证输出 True 再往下走。3.2 数据集目录与标签格式VOC 标注转 YOLO 训练格式这份资源里的文档说明一般会带数据集目录的组织方式。YOLOv5 训练需要 images 和 labels 两个镜像目录每张图对应一个同名 txt 文件txt 里每行是一个目标的标注类别ID x_center y_center width height坐标全部归一化到 01。如果你拿到的是 VOC 格式的 XML 标注或者视频标注平台导出的 XML就需要先做一次格式转换。# voc2yolo.py —— 把VOC XML标注转成YOLO训练txt import os import xml.etree.ElementTree as ET CLASSES [phone, smoke, yawn, closed_eye, drink, normal] def convert(xml_file, out_dir): tree ET.parse(xml_file) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): cls obj.find(name).text if cls not in CLASSES: continue box obj.find(bndbox) x1, y1 float(box.find(xmin).text), float(box.find(ymin).text) x2, y2 float(box.find(xmax).text), float(box.find(ymax).text) x_center ((x1 x2) / 2) / w y_center ((y1 y2) / 2) / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{CLASSES.index(cls)} {x_center:.6f} {y_center:.6f} {bw:.6f} {bh:.6f}) out_name os.path.basename(xml_file).replace(.xml, .txt) with open(os.path.join(out_dir, out_name), w, encodingutf-8) as f: f.write(\n.join(lines))这段脚本的注意点在CLASSES列表的顺序上。YOLO 的类别 ID 完全由列表顺序决定后面训练配置 yaml 文件中的 classes 列表必须跟这里的顺序完全一致否则训练的模型在推理阶段类别全乱。另外转换后最好抽查两个 txt 文件用 OpenCV 把归一化坐标还原回像素坐标画一遍框确认没有出现坐标越界或者宽高为 0 的情况。3.3 数据配置文件与预训练权重数据准备好之后需要改data/drive.yaml。这个文件告诉 YOLOv5 训练集、验证集路径和类别信息结构固定但路径千万别写绝对路径到别人的电脑上否则换机器就没法复现。# data/drive.yaml train: data/drive/images/train val: data/drive/images/val nc: 6 names: [phone, smoke, yawn, closed_eye, drink, normal]预训练权重这一步仓库里通常会说明用 YOLOv5 官方在 COCO 上预训练好的yolov5s.pt作为起点。工程上传权重文件比较常见如果没有上传就从你本地已有的 YOLOv5 项目目录里复制一份yolov5s.pt到weights/下。注意权重文件版本要和代码仓库版本匹配严格来说 v5.0 和 v6.0 的权重结构有差异混用会在加载时报缺少 key 的错。4. 训练与推理落库让模型从「跑通」到真正能用于行为识别4.1 训练命令与日志里该盯哪些指标环境装好、数据格式对了就可以开始训练。YOLOv5 训练命令的参数不算复杂但几个关键值直接决定训练成败。我给出的命令适用于 v5.0 的目录结构如果你手里的仓库是 v6.0 以上--cfg参数可以省略模型结构由 weights 对应的 yaml 决定。python train.py \ --data data/drive.yaml \ --weights weights/yolov5s.pt \ --cfg models/yolov5s.yaml \ --epochs 150 \ --batch-size 16 \ --img 640 \ --device 0 \ --hyp data/hyps/hyp.scratch-low.yaml这条命令里的--batch-size 16是以 8GB 显存为基准估算的如果是 6GB 显存就降到 8显存不够时千万别硬撑OOM 会中断整个训练。--img 640是输入分辨率检测小目标比如手中的手机时可以提到 800 或 960代价是训练时间翻倍。训练过程中我一般只看三类日志每轮的 mAP0.5、mAP0.5:0.95 和 loss 曲线。分心驾驶这类小目标检测场景mAP0.5:0.95 能到 0.6 以上就够用了mAP0.5 至少要看 0.85这个值上不去说明数据集本身有问题先别调参回去查标签。4.2 视频推理与行为判定的完整集成训练完得到runs/train/exp/weights/best.pt接下来的任务是把检测、追踪、行为判定三段串起来。这里我给一个最小可运行的推理脚本它把 YOLOv5 的检测结果转成 numpy 数组后送入 DeepSORT 更新再调用上一章的judge_behavior做行为统计。# track_demo.py —— 检测 追踪 行为判定三段集成 import cv2 import torch from yolov5.models.experimental import attempt_load from yolov5.utils.general import non_max_suppression from deep_sort.deep_sort import DeepSort device torch.device(cuda:0) model attempt_load(weights/best.pt, devicedevice) model.eval() # max_dist控制外观匹配容忍度max_age控制ID丢失后的存活帧数n_init是确认ID所需最少帧数 tracker DeepSort(deep_sort/deep/checkpoint/ckpt.t7, max_dist0.2, max_iou_distance0.7, max_age70, n_init3) def process_frame(frame): # YOLOv5推理 pred model(frame, augmentFalse)[0] det non_max_suppression(pred, conf_thres0.4, iou_thres0.5)[0] if det is None or len(det) 0: return [] bboxes det[:, :4].cpu().numpy() # xyxy格式 confs det[:, 4].cpu().numpy() cls_ids det[:, 5].cpu().numpy().astype(int) # DeepSORT更新返回 (x, y, w, h, track_id, cls, conf) outputs tracker.update(bboxes, confs, cls_ids, frame) return outputs这段代码里两个参数值得单独说。max_dist0.2是特征向量余弦距离的阈值驾驶员经常转头、低头外观变化大这个值可以放宽到 0.3max_age70是目标丢失后最多存活 70 帧调太大会出现两个不同的人被连成同一个 ID调太小则同一个驾驶员转身后 ID 就断了。non_max_suppression的conf_thres0.4也值得斟酌检测置信度低于这个值的框会被丢掉如果发现漏检多就调低到 0.25。4.3 DeepSORT 参数与场景的调优方向DeepSORT 在驾驶场景里有几个特定问题驾驶员和乘客距离近、头部转向频繁、车窗反光造成短暂检测丢失。针对这些情况我通常这样调max_age提高到 70100因为驾驶员弯腰捡东西可能造成两三秒的检测中断n_init保持 3 帧快速确认新出现的 ID避免短暂出现的误检框被当成新目标max_iou_distance保持 0.7 以下防止检测框抖动时把同一目标拆成两个。如果你拿到的源码版本里 ReID 特征提取是基于行人重识别的放在驾驶舱内效果不一定好因为驾驶员上半身和行人的外观分布差距大这时可以把max_dist放宽到 0.35 左右更多依赖位置和 IoU 做匹配。5. 避坑记录驾驶员状态检测最常见的五个翻车现场5.1 训练中途显存溢出或直接被杀死现象训练跑到十几个 epoch终端报CUDA out of memory或者进程被系统直接 kill有时重启后再次训练又在同样位置崩。原因显存溢出最常见的原因是 batch-size 设得太大或者--workers开太多导致 CPU 和 GPU 之间数据传输排队显存峰值瞬间拉高。系统 kill 则多半是内存不够Windows 和 Linux 的表现不一样Linux 下通常会有 OOM Killer 日志。解决先把这个 batch-size 配--batch-size 8跑通 10 个 epoch确认稳定后再往上加。同时把--workers 4 --pin_memory显式写出来不要用默认值自动探测。如果你的卡是 4GB 这种小显存直接在训练命令里加--amp混合精度能把显存占用降一半。5.2 把「手里拿手机」误判成「打电话」行为统计虚高现象视频里驾驶员只是拿着手机看导航系统却一直报打电话或者低头看了两秒手机判定结果是低头行为正常。原因类别定义和判定阈值太松。数据集里如果用「手持手机」代替「接近耳边」模型在两种姿态上的特征几乎一样行为判定层的占比阈值又设得低滑动窗口里出现几帧手机类别就被触发。解决把类别表拆细phone_hold和phone_call分开建标签行为判定阈值从 0.5 提到 0.8再加一个约束判定打电话必须满足「手机框的中心点位于头部框的上半部分」这个几何条件用代码过滤掉明显放在方向盘上的手机检测框。5.3 DeepSORT 的 ID 频繁跳变同一个驾驶员被当成多个人现象视频回放时驾驶员头部附近的 ID 数字在 3、7、12 之间来回跳行为统计里出现同一时段多个 ID 都有「低头」记录。原因检测框不稳定是根源。分心驾驶视频里手、方向盘、手机和面部经常短时遮挡YOLOv5 的输出框一抖DeepSORT 的级联匹配就对不上。另一个原因是max_age太小检测丢了两帧就删掉轨迹目标重新出现时被看成新目标。解决先在检测侧给框做平滑对连续帧的检测框做指数移动平均再把max_age调到 90 以上让轨迹在短暂丢失后能重新关联。如果代码里用的是官方 DeepSORT 原版可以把级联匹配里min_height的限制放宽避免因为框变矮就拒绝匹配。5.4 训练集 mAP 很高换到实拍视频里漏检严重现象训练结束看到验证集 mAP0.5 到 0.9拿自己手机录了一段车内视频去测打电话的框基本出不来。原因训练集和测试集的场景差异大。很多公开驾驶行为数据集是仿真驾驶舱拍的光线均匀、角度固定而实拍视频存在逆光、夜间、车窗反光、驾驶员低头角度大等问题模型的泛化能力扛不住。解决两类手段一起上。数据集侧加入实拍的夜间和逆光图片数量占到总量 20% 以上训练超参数侧把hyp.scratch-low.yaml里的hsv_h、hsv_s适当调大增加颜色增强强度让模型对光照变化更不敏感。我做这类项目时习惯留出 10% 的自录视频做最后的验证集而不是只看公开数据集的指标。5.5 保存视频干净但实时预览卡顿和掉帧现象推理脚本跑视频文件一切正常换成实时摄像头输入后界面卡成 PPT行为判定也明显滞后。原因检测加追踪本身耗时如果在显示器上再叠加画框和文字开销更大。另外 OpenCV 读取摄像头用的是默认缓冲如果推理速度跟不上采帧速度缓冲堆满后延迟持续增大。解决摄像头读取单独用一个线程推理线程每处理完一帧再从队列里取最新帧落后的旧帧直接丢弃。画框时的文字渲染改成只更新每 10 帧一次的统计标签不要在每一帧都重绘完整的统计面板。还有一个细节DeepSORT 的特征提取模型ckpt.t7用的是 CPU 推理还是 GPU 推理在DeepSort初始化时显式传入use_cudaTrue否则整个追踪器会变成 CPU 推理速度掉一半以上。6. 验证效果与调优技巧自己录一段 30 秒视频把检测、追踪、行为三段全部测出问题拿到这套源码后别急着改代码先做一次端到端验证。我的习惯是自己拿手机对着副驾驶拍一段 30 秒左右的视频包含三个动作拿起手机看屏幕、把手机放耳边说话、然后靠在座椅上闭眼两秒。这段视频不超过 50MB转成 640 宽的 mp4作为整个系统的验收基准。接下来写一段验证脚本把每一帧的检测框、追踪 ID、类别、置信度和行为判定结果全部落进 CSV。这一步极其重要因为可视化回放只能看到模糊的「有没有框」落成表格才能精准知道是检测丢了还是追踪 ID 断了还是行为判定窗口写了 bug。# eval_pipeline.py —— 输出逐帧分析日志 import cv2 import csv from track_demo import process_frame from behavior_judge import judge_behavior cap cv2.VideoCapture(test_car.mp4) writer csv.writer(open(result.csv, w, newline, encodingutf-8)) writer.writerow([frame, time_ms, track_id, cls, conf, behavior]) frame_id 0 while True: ok, frame cap.read() if not ok: break outputs process_frame(frame) for tlwh, tid, cls, conf in outputs: behavior judge_behavior(tid, cls) writer.writerow([frame_id, cap.get(cv2.CAP_PROP_POS_MSEC), tid, cls, f{conf:.2f}, behavior]) frame_id 1跑完后打开 CSV重点看三个位置打电话动作开始的那几帧追踪 ID 是否稳定闭眼动作持续两秒的时间段里行为列是否连续输出同一类别以及深色衣服或逆光时检测置信度是否掉到 0.4 以下。我在这类调试里最深刻的教训是所有调参都要以这种逐帧日志为准而不是凭肉眼看视频猜测问题出在哪个环节。每次改完max_age或conf_thres我会强制自己重新跑一遍这段验收视频对比 CSV 里的 ID 切换次数和误判帧数。这样做两三次之后你对自己手里这份源码的边界会非常清楚——知道哪个参数治哪种病也知道哪些场景它天生扛不住。希望帮到你。本文还有配套的精品资源点击获取
返回列表