ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLOv5与PyQt实战:构建驾驶员监控系统(DMS)全流程解析

YOLOv5与PyQt实战:构建驾驶员监控系统(DMS)全流程解析 简介本资源面向智能交通与车载安全领域的算法工程师、高校研究者及计算机视觉初学者提供一套完整的驾驶员分神行为检测解决方案聚焦抽烟、打电话、喝水、吃东西四类高风险驾驶行为识别。压缩包含2000个文件主体为1994个YOLO格式txt标签文件及配套的3个Python主程序含PyQt5可视化界面、3份PDF环境配置与使用指南整体大小356.88MB。已有628人学习下载说明其在DMSDriver Monitoring System实际落地场景中具备较强参考价值。资源附带已划分好的5000张高质量图像数据集train/val/test三级目录完备并提供标准data.yaml配置文件支持YOLOv5至YOLOv9系列模型直接训练PyQt界面可一键加载模型、实时视频流检测与结果可视化大幅降低部署门槛两份环境配置PDF覆盖主流YOLO版本适配要点显著减少环境搭建踩坑成本。 前一阵把YOLOv5和DMSDriver Monitoring System驾驶员监控系统凑到了一起做了个能识别抽烟、打电话、喝水、吃东西四类分神行为的完整工程外带一套PyQt界面。说实话这类需求最近咨询量很大——两客一危、网约车平台、物流车队、驾校考试都在推但网上教程大多只教你怎么跑通官方demo距离“能用的工程”还差着数据集、训练调优、界面联动、模型部署一大截。这篇我打算把整个链路从头到尾捋一遍讲讲踩过的坑、看过的指标、以及一些常规文档里不会写的小门道适合手里有基础Python/PyTorch概念、想把这个项目做成毕设或真实产品的朋友参考。DMS本身不是一个新概念但YOLOv5的成熟度让它的实现门槛低了一个量级。以前做驾驶行为识别要么靠传统图像处理做手势/特征判断要么上姿态估计复杂度高、鲁棒性差现在直接用目标检测框住手部区域和物体区域再从逻辑层面判断行为状态工程上简单不少。不过简单归简单真正要交付一个能稳定识别抽烟、打电话、喝水、吃东西的DMS还是会碰到一系列现实问题下面逐个拆。1. 项目拆解DMS检测到底在解决什么问题1.1 四类行为的检测逻辑本质上是在找“物”和“手的交互”先说一个关键的概念抽烟、打电话、喝水、吃东西这四类行为如果单纯靠姿态估计或者行为识别模型来做训练数据量要求高、类别容易混淆而且司机座椅角度不同、体型不同泛化会很差。但如果你把问题降维成“目标检测关系判断”难度马上不一样了——你需要检测的目标是什么是手、嘴部区域以及香烟、手机、水杯、食物这类物件。检测模型输出的就是若干个边界框然后根据框与框的位置关系做行为判定。这也是为什么选YOLOv5而不是直接上时空卷积网络或Transformer的重要原因。DMS场景是实时视频流需要低延迟、高帧率推理YOLOv5在边缘设备Jetson、RK3588、RV1106这类平台上都有成熟的部署方案量化、裁剪、TensorRT转换的坑基本被踩平了社区资料丰富任何一个环节出了问题都能搜到对应解法。不过在真正标注数据前我建议先把行为判定规则定义清楚不然标注员会疯掉。比如“打电话”和“手持手机”怎么区分“喝水”和“吃东西”在瓶口/食物靠近嘴部时怎么界定我的做法是定义一套基于检测框的判定规则只有当“目标物体框”与“嘴部区域框”发生重叠且持续若干帧时才判定为对应行为。“手持手机但没靠近耳边”算手持状态不算打电话。这样做的好处是模型只需要输出物体、手、人脸的稳定检测框行为逻辑交给后处理系统的可解释性和调试性都强很多。1.2 功能范围与痛点单模型多任务还是多模型协作刚开始不少人会想直接把抽烟、喝水、吃东西、打电话当成四个类别训练一个四分类的目标检测模型行不行理论上可以实际操作中你会发现一个很典型的问题——类别之间的数据极度不平衡。抽烟的人比喝水的人多得多因为喝水这个动作本身就很快几帧就结束了图像样本难采集。另外“吃东西”和“喝水”的手部动作高度相似单纯靠物体类别区分容易把食物和水杯搞混。我做的时候采用了“基础检测模型 后处理状态机”的方案训练一个模型检测类别包括person或face、hand、phone、cigarette、cup/bottle、food然后由后处理逻辑判断当前行为。虽然任务还是多类别检测但每个类别都很独立、边界清晰比直接把“smoking”、“calling”、“drinking”、“eating”作为类别训练更容易收敛。特别说明如果你的标注数据量不大这种方式对单类别的检测准确率提升非常明显因为每个类别的特征更纯粹。1.3 界面层的作用从“算法demo”到“可用系统”PyQt界面在这个项目里不能简单看成是一个“显示框”。真正落地DMS界面至少要有三块功能实时视频显示与检测结果叠加、报警事件记录与图片抓拍、参数配置摄像头源、检测阈值、报警延迟等。如果做车队管理系统还需要对接数据库或云端API把报警事件推送到管理后台。界面是算法与用户之间的唯一交互层做得不好哪怕模型99%准确率客户也会觉得“这系统不行”。2. 数据集构建决定DMS模型上限的关键环节2.1 数据来源与数量规划别指望一次性找齐DMS数据集最尴尬的地方在于公开的驾驶行为数据集比如State Farm Distracted Driver Detection、3MDAD不少但大多是分心驾驶分类数据不是目标检测格式而且场景偏向欧美人种和国外车型。直接拿来做国内DMS泛化存在明显问题。我的建议是组合方案先用公开数据集做预训练再用自采数据做微调。具体来说第一阶段跑通流程时可以用公开数据集里的图像转成YOLO格式后训练一版基线模型看看类别是否可分、loss能不能正常下降。第二阶段再针对实际场景自采数据重点采集不同光照白天、逆光、夜间红外、不同姿态司机高矮胖瘦、座椅前后、不同设备安装角度仪表台、后视镜附近的图像。如果项目预算有限自采数据建议至少保证每个类别1000~2000张有效图像注意这里的“有效”指标注框清晰、包含典型动作姿态、有背景多样性不是同一段视频连续抽帧。题外话别忽视合成数据。用3D模型渲染或图像合成把手机、香烟、水杯等物体贴到真实驾驶图像上可以快速扩充边缘案例比如手机在方向盘下方、香烟在嘴角的瞬间。合成数据在DMS这种场景中效果不错因为物体本身纹理清晰、遮挡关系比较简单。2.2 数据标注规范这几条规则定下来再动手标注质量直接决定模型训练效果比调参重要十倍。围绕DMS场景我认为有以下几条核心标注规则边界框要贴紧目标轮廓尤其是香烟、水杯这类小目标框得越准小目标检测效果越好。严重遮挡的物体要标注吗我的经验是可见面积小于30%的物体不标避免给模型传递错误信息。嘴部区域建议单独标注为“face”或“mouth”后处理判断行为时要用到。如果模型能稳定输出嘴部框后续“喝水/抽烟/吃东西”的行为判定就简单很多。打电话和手持手机不要混标类别就是“phone”是否打电话由后处理判定否则标注员的主观判断会引入大量噪声。夜间红外图像和白天可见光图像建议分目录存放或者统一做灰度增强后再训练不然模型会在颜色特征上过拟合。2.3 数据增强策略DMS场景最需要的几种YOLOv5自带的数据增强mosaic、mixup、随机透视、HSV变换等对通用目标检测很有效但DMS场景有它的特殊性。我建议在默认增强基础上重点强化以下策略亮度/对比度扰动模拟不同时间段的光照变化尤其是逆光和隧道出入口的明暗突变。高斯噪声模拟低照度下的传感器噪声对夜间红外场景有正向帮助。随机遮挡模拟方向盘、A柱对物体和手部的遮挡增强模型在部分遮挡情况下的鲁棒性。小目标复制粘贴把尺寸较小的手机、香烟样本随机复制到图像的合理区域比如中控台、方向盘附近解决小目标样本不足问题。注意不要对整个训练集无脑使用过强的mosaic增强。DMS场景中手部、手机这类目标尺度变化不大mosaic过度反而会让模型学到奇怪的上下文关联我在实验中就遇到过mosaic比例过高导致夜间样本检测率下降的情况。3. YOLOv5训练全流程从环境配置到模型收敛3.1 环境搭建版本匹配才是最大的坑YOLOv5的安装步骤并不复杂常遇到的问题几乎都集中在版本匹配上。以我常用的组合为例Python 3.8 PyTorch 1.10.0或1.12.0 CUDA 11.3clone官方仓库后直接跑pip install -r requirements.txt基本能一次性通过。这里需要重点提醒很多初学者在Windows上搭环境会直接conda install pytorch装了CPU版本训练速度慢还不报错跑了一个epoch才发现不对。建议装完先执行python -c import torch; print(torch.cuda.is_available())如果是True再继续。另外如果目标设备是RV1106、RK3568这类边缘芯片训练阶段最好还在PC上做训练环境生成.pt权重后再转成对应平台的推理格式。边缘平台一般不支持大batch训练强行在板子上训练既慢又容易内存溢出。这类板子通常需要你针对特定芯片做模型转换比如RV1106的RKNN模型转换对算子的支持有限最好提前查一下YOLOv5使用的算子是否都被支持不然部署阶段会被卡住。3.2 超参数配置一组能直接用的起步参数YOLOv5提供了很多超参数默认配置在COCO上表现不错但DMS场景相对简单类别少、目标类型固定需要调整的地方不多。我的初始参数如下imgsz: 640DMS场景下目标不算太小640足够如果用TensorRT量化可以尝试480或416精度损失不大速度更快batch: 16或32取决于GPU显存先看训练过程GPU利用率是否大于90%再用nvidia-smi观察显存余量不要一上来就选最大batchepochs: 100~200数据量不大的话100轮足够关键是观察val loss是否在后期回升hyp: 保留默认hyp.scratch-low只调两个fl_gamma设置为0.0即不使用focal loss因为类别相对均衡mosaic设置为1.0但配合我前面说的自定义增强使用具体训练命令通常是python train.py --img 640 --batch 16 --epochs 150 --data dms.yaml --weights yolov5s.pt --cache--cache参数可以一次性把图像缓存到内存中小数据集下能大幅缩短训练时间。个人经验是先跑50个epoch快速验证数据标注和配置有没有问题再跑完整150个epoch避免拿着有问题的数据傻等几个小时。3.3 训练过程监控与收敛判断训练时不要只盯着那个花花绿绿的loss曲线你真正要关心的是三个东西train loss和val loss的距离如果两者差距越拉越大说明过拟合开始需要增加数据增强或提前停。P/R曲线DMS场景更看重召回率漏报比误报更可怕所以在模型调优阶段可以适当牺牲一点precision把conf_thres从默认的0.25降到0.15甚至0.1。每个类别的AP单独看不能只看mAP因为“喝水”这类少见类别的AP往往远低于“抽烟”如果喝水AP明显偏低优先去补数据而不是盲目调参。贴一组我的实际训练记录数据集共8000张左右四类行为相关目标共20000个框训练150轮后mAP0.5达到0.92mAP0.5:0.95在0.68左右其中phone和cigarette的AP较高food和bottle略低。在Jetson Orin Nano上实测推理速度约25ms/帧TensorRT FP16完全满足实时性要求。3.4 小目标优化手机、香烟这类小物体怎么提升召回DMS场景中手机和香烟常常只占图像面积的很小一部分尤其在1080P视频流中可能只有几十个像素宽。针对这个问题我认为优先做两件事一是把输入分辨率从640提到960。虽然推理速度会下降约30%但小目标召回率提升明显。如果你用的是RTX 3060以上显卡做推理这个开销可以接受如果部署在边缘盒子建议配合TensorRT做FP16或INT8量化来弥补速度损失。二是调整anchor。YOLOv5会自动计算anchor但如果你用的是自定义数据集训练前建议先跑一下检查anchor是否合理。可以在训练日志中看到kmeans_anchors的数值如果绝大多数anchors都在10~30像素区间说明默认anchors对数据集来说偏大需要调整anchor数量或尺寸。4. PyQt界面开发把模型能力变成用户可用的产品4.1 界面框架设计三个页面打底PyQt界面的功能规划我建议按以下三个页面来组织实时监控页显示摄像头/视频流画面叠加检测框和类别标签下方放状态栏当前行为、报警等级、连续报警帧数。事件记录页展示历史报警事件列表包含时间、类型、抓拍图片缩略图支持点击查看大图。事件记录可以本地存SQLite轻量、无需额外部署数据库服务。参数设置页选择视频源本地摄像头/RTSP流、设置检测阈值、报警延迟帧数、是否开启声音报警、模型文件路径。这套结构基本能覆盖DMS从单机演示到后台管理对接的绝大多数需求。如果只是毕设展示第一页和第三页就够了如果是商用demo事件记录页一定要有这是给客户演示时最能加分的功能。4.2 多线程处理Qt界面卡死的根源与解法PyQt YOLOv5最容易犯的错误就是直接在UI线程里跑模型推理。YOLOv5前向推理虽然只要几十毫秒但视频解码、图像预处理、NMS这些操作累加起来加上Python GIL的限制会让界面明显掉帧甚至完全卡死。标准解法是采用QThread 队列/信号槽机制视频采集线程负责从摄像头或视频文件读取帧放入队列。推理线程从队列取出帧执行预处理、模型推理、后处理通过信号将带检测结果的帧发回主线程。主线程UI线程只负责接收结果帧并更新画面同时响应用户操作。我在代码里用了一个带最大长度的queue.Queue比如maxsize5如果队列满了就先丢最旧的帧保证推理线程总是处理最新画面。这样界面始终流畅也不会出现延迟越积越严重的问题。另外要提醒PyQt中信号槽的跨线程连接要指定Qt.QueuedConnection否则结果帧的传递可能出问题。4.3 模型加载与推理封装别把YOLOv5的detect.py直接塞进界面YOLOv5官方detect.py写得很好但它是一个完整的命令行工具塞进PyQt里会很别扭。我建议把推理过程封装成一个独立的Detector类类初始化时加载模型类方法中实现frame级别的检测class Detector: def __init__(self, weights_path, devicecuda:0, conf_thres0.25, iou_thres0.45): self.model torch.hub.load(ultralytics/yolov5, custom, pathweights_path, force_reloadFalse) self.model.conf conf_thres self.model.iou iou_thres self.device device def detect(self, frame): results self.model(frame, size640) boxes results.xyxy[0].cpu().numpy() # 返回boxes、classes、scores列表 return boxes使用torch.hub.load加载模型的优点是你不用手动处理权重文件和yaml的匹配类内部已经搞定。后续如果切换到ONNX或TensorRT模型只需修改Detector类内部的推理实现界面部分完全不动。4.4 行为判定逻辑把检测框变成报警事件模型输出的是“有哪些目标”而DMS产品需要的是“当前是什么行为”中间这道逻辑需要自己写。我实现的判定流程是当前帧检测结果中phone/cigarette/cup/food等物体框检测到至少一个。对每个物体框计算与嘴部区域框face框的一个子区域取下半部分的IoU或中心点距离。如果物体框与嘴部区域重叠面积超过一定阈值比如物体框面积的20%标记为“疑似行为”。连续N帧根据经验设为5~10帧即约0.2~0.4秒都判为“疑似”才触发报警。这个延迟设置很关键能有效过滤掉拿水杯但没喝、拿手机但没打电话的瞬间。这套后处理逻辑写起来很简单但调试时你会发现很多边界case。比如司机把手机放在耳边打电话但是头转向窗外face框和phone框还是能正常检出并判定但司机用手捂着嘴打电话phone可能被手完全遮挡检测不到phone框导致漏报。这时就需要结合hand框——如果hand框在face框附近且持续了较长时间也可以触发“疑似电话”报警。灵活运用多类别检测结果是DMS系统鲁棒性提升的关键。4.5 打包成exePyInstaller的坑点复盘用PyInstaller把PyQt程序打包成exe网上的教程很多但DMS项目有几个特殊的地方要注意YOLOv5模型是.pt文件在打包时容易遗漏。建议在spec文件里显式加入模型路径或者把模型放在一个单独的models目录中程序运行时通过相对路径加载。PyQt本身很大打包出来动不动就几百MB。可以用UPX压缩或者用Nuitka替代PyInstaller体积更小、启动更快但配置复杂度高一些。摄像头驱动和相关DLL经常被忽略。一个典型的坑是打包后的exe在自己的电脑上能正常打开摄像头换个电脑就打不开因为缺少MSVCP140.dll或类似运行库。建议打包时把VC运行库一起带上或者在目标机器上先安装对应运行库。如果是按“窗口程序”方式打包没有控制台注意日志信息会丢失排查问题会很难受。我的做法是加一个简单的日志文件把关键阶段信息写入日志。我用PyInstaller打包一个包含YOLOv5s模型和PyQt的项目最终exe体积在400MB左右因为torch和CUDA的Python库体积太大。如果系统对安装包体积敏感建议把模型推理改成ONNX Runtime版本依赖库会小非常多。5. 部署与性能优化从开发机到边缘设备的距离5.1 模型导出ONNX/TensorRT的选择DMS项目最终若是在车载设备上运行直接用.pt权重跑肯定是效率最低的一种方式。我的建议是如果设备是Jetson系列首选TensorRT。用YOLOv5官方export.py导出为engine格式推理速度能比PyTorch原生快3~5倍。导出命令python export.py --weights yolov5s.pt --include engine --device 0 --half如果是瑞芯微RK3588/RV1106这类芯片需要先导出ONNX再通过RKNN-Toolkit转成rknn模型。注意YOLOv5新版本可能用了某些运算符在RKNN转换时不一定支持建议用v6.0或v7.0的官方版本转换工具链相对成熟。如果目标机器只有CPU建议导出ONNX后用ONNX Runtime推理并开启INT8量化需要校准数据集速度提升非常明显。5.2 夜间红外场景的特殊处理DMS必须支持夜间开车场景而红外摄像头和可见光摄像头拍摄的画面差异很大。红外图像通常是黑白/灰度图如果训练集里混入了大量彩色图像模型在红外画面上容易出现误检或漏检。我踩过的坑是模型在白天测试效果非常好一到晚上各种乱报。解决办法是在训练集里按一定比例我用了约20%加入红外灰度图像同时对红外图像做直方图均衡化作为增强让模型同时适应两种域。如果条件允许用同一批行为场景分别录制白天和夜间版本配对扩充数据效果最理想。5.3 性能瓶颈分析与优化方向DMS系统的性能瓶颈通常是“视频解码预处理”和“模型推理”两个环节而非后处理。视频解码在Jetson上可以用GStreamer硬解码推理用TensorRT预处理如果用PyTorch的letterbox实现在CPU上会占不少时间可以考虑改用opencv或numpy实现。一个典型的优化是把图像预处理从Python循环改成批量操作避免在for循环里逐张做resize和归一化。另外如果DMS同时接多个摄像头比如主驾和副驾各一个建议使用线程池并发推理而不是线性逐路处理实测在Jetson Orin上两路并发推理的帧率比串行高出40%以上。6. 常见问题与排查技巧实录以一个速查表的形式把我在这个项目里遇到的典型问题和解决方案整理出来方便你照着排查现象可能原因排查思路与解决训练loss不降学习率过大、数据标注错误先把学习率降到1e-4检查几个标注框是否错位训练正常但val mAP很低过拟合、类别不平衡增加数据增强重点补充AP低的类别样本白天准、夜间不稳红外/可见光域差异训练集加入红外灰度图做直方图均衡界面卡顿、视频延迟大推理放UI线程或队列积压改用QThread队列最大长度限制丢弃旧帧小目标检测不到输入分辨率低anchor不合适imgsz调到960检查anchor分布并调整打包后exe打不开摄像头缺少运行库或摄像头DLL安装VC运行库把推理逻辑做成日志可查报警太多误报判定阈值太低、状态机缺失提高重叠面积阈值增加连续帧确认逻辑手机被手挡住漏报警模型检测不到遮挡目标结合hand检测框辅助判断不单纯依赖phone框TensorRT推理结果不对预处理不一致、动态shape问题保证letterbox方式一致尝试固定batch size导出多个司机体型差异大检测框通用性不足数据集中增加不同体型、不同座椅位置的样本在实际操作中我还有一个关于“调试状态机”的小技巧不要每次修改行为判定逻辑都去跑真实视频那样效率太低了。可以写一个模拟器把视频逐帧喂给模型模型输出检测框后通过配置化的行为规则进行判定同时把每一帧的判定中间变量物体框坐标、嘴部区域、重叠面积全部打印到日志。这样在调试报警阈值时你能清楚地看到在哪一帧、因为什么条件被触发了报警逻辑排查效率提升非常多。7. 最后再分享一点工程心得项目做到后期你会发现最难的不是把YOLOv5跑起来也不是把界面画出来而是“如何让系统在杂乱的真实场景中稳定工作”。DMS会被装在各种各样奇怪的位置后视镜后面、A柱旁边、中控台上方每辆车的光线条件和遮挡物都不同。如果只用一个固定模型和一套固定阈值很难做到万无一失。我个人比较推荐的路线是先快速跑通一个端到端demo验证客户诉求然后再花大量时间在数据和场景适配层面包括针对性采集、标注规范和阈值调优。模型结构反而是最不需要频繁折腾的部分YOLOv5s足够应付大多数DMS场景。对了还有件事值得提一下——如果你打算把这个项目作为毕设建议在答辩时重点讲清楚“数据标注规范”和“行为判定状态机”的设计这两块是最能体现工程思维的比单纯讲“我跑通了YOLOv5”要有说服力得多。本文还有配套的精品资源点击获取
返回列表