简介:这套基于YOLOv8的驾驶行为检测项目代码,面向目标检测开发者和智能驾驶安全领域技术人员,用于解决车内人员闭眼、抽烟、打电话等危险行为的实时识别问题,可应用于疲劳驾驶预警、分心驾驶监管、营运车辆安全监测等多种实际场景。压缩包共34个文件,以Python脚本、YAML配置、文本标注及Markdown说明为主,覆盖环境配置、数据准备与自定义数据集结构设计(含5215张训练图、1303张验证图)、YOLO格式标注规范、训练参数(学习率、批大小、损失函数)设置、模型推理与性能评估等完整流程。项目实现了图片、视频、摄像头三种推理方式,并通过PyQt5构建图形界面,支持实时显示检测结果,方便快速验证和二次开发。整个资源包仅34KB,工程模块划分清晰,从依赖库安装、数据下载、模型训练到检测评估均有对应代码,还附带测试用例和开发文档,便于理解项目整体架构与排错。目前已有125人学习下载,适合希望掌握YOLOv8项目完整落地实施的中高级开发者,也可作为车载智能化安全系统的参考实现。
1. YOLOv8驾驶行为检测:这份代码包能让你少踩两周坑
驾驶行为检测是辅助驾驶和车队管理里最容易被轻视的一环,很多人拿到YOLOv8代码包就直接训练,结果卡在数据标注、类别不均衡和模型震荡上。这份资源不是只给一个训练脚本,而是把数据准备、自动标注、训练调参、边缘部署串成完整流程的代码包。我拆完这套代码之后,最大的感受是:它帮你把从摄像头画面到行为判定结果之间所有容易翻车的细节都处理好了,适合已经跑通YOLOv8基础检测、但想快速落地驾驶行为识别项目的开发者。
2. 环境搭建与项目结构:把依赖锁死才能跑得顺
2.1 依赖清单与安装命令:版本错了就是白干
这套代码基于YOLOv8官方实现,依赖集中在requirements.txt里。常见做法是先创建独立的conda环境,避免把系统Python搞乱。我一般用Python 3.9,PyTorch 2.0以上的CPU/GPU版本都能兼容。如果你的机器是旧显卡,装PyTorch之前先确认CUDA版本,比如CUDA 11.8对应torch 2.1.0,装错版本会在import时报libcudart.so not found之类的错。
conda create -n drive_detect python=3.9 conda activate drive_detect pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 pip install yolo-tools opencv-python numpy pandas pyyaml tqdm逻辑很简单:先装深度学习框架,再装YOLO封装库和图像处理库。torch和torchvision的版本必须严格对应,否则后面加载模型权重时会直接报错。opencv-python用来做视频抽帧和画框,pandas和pyyaml用来处理标注文件和配置文件。
参数说明:conda环境名随意,但建议用英文,避免后续路径处理时遇到编码问题。torch版本不必追新,2.0~2.2之间都行,重点是和你的显卡驱动CUDA版本匹配。--index-url指定的是PyTorch官方预编译包地址,如果网络不稳,可以改成pip install torch torchvision走默认源,但要注意默认源的torch版本可能不带CUDA支持。
2.2 项目目录结构:每个脚本在流程中扮演的角色
解压代码包后,目录比普通训练脚本多了数据预处理和部署两大部分。我用tree命令看了下结构:
drive_detect/ ├── cfg/ │ ├── data.yaml │ └── hyperparams.yaml ├── data/ │ ├── raw_videos/ │ ├── frames/ │ ├── labels/ │ └── dataset/ ├── models/ │ └── yolov8s.pt ├── scripts/ │ ├── extract_frames.py │ ├── auto_label.py │ ├── convert_yolo.py │ ├── train.py │ └── detect_video.py └── runs/ └── exp/cfg目录存数据集配置和超参数,data目录是标准YOLO格式的数据集,runs是训练输出。scripts里的脚本顺序执行就能从原始视频走到训练完成,这是我判断一套代码包是否完整的关键依据——光有train.py的代码包基本都要自己补一堆工具。
特别说一下hyperparams.yaml。它不是YOLOv8默认的那套参数,而是根据驾驶行为场景调过的:学习率设了0.01,warmup_epochs设了5,mosaic增强只开了前半程。这个文件比训练命令里的参数优先级更高,改训练配置优先改这里,而不是改命令行。
2.3 预训练权重与配置文件:先定数据规格
代码包默认使用yolov8s.pt预训练权重。我建议新手先用s版本,m和l版本在驾驶行为这类目标较小、遮挡较多的场景里精度提升有限,但推理速度会明显变慢。先用小模型跑通全流程,最后再换大模型。
data.yaml是数据集配置,内容大致如下:
path: /home/yourname/drive_detect/data/dataset train: images/train val: images/val names: 0: normal 1: phone 2: smoking 3: drinking 4: turn 5: reach这里的path我建议写成绝对路径,特别是你在VSCode或者远程服务器上跑训练时,相对路径会因为工作目录不一致而找不到数据集,排错很浪费时间。names顺序要和标注文件里的类别ID完全一致,类别ID从0开始。如果改动类别,记得同步改hyperparams.yaml里的类别权重,否则不平衡问题会更严重。
3. 数据准备与标注格式:从视频到训练样本的三个关键步骤
3.1 行为类别定义:把"打电话"和"摸手机"分开
这套代码定义了六类行为:normal、phone、smoking、drinking、turn、reach。"phone"特指打电话时手机贴近耳朵,单手或双手握方向盘的角度可能不同;"reach"指伸手去够手机但没有放到耳边。类别的边界必须提前定死,否则标注的人会凭感觉标,模型学出来就是一团浆糊。
我在实际项目中遇到过公开数据集里没有的类别,比如"喝水",需要自己定义并统一操作标准。A同学当时把"喝水"和"伸手拿水杯"混在一起,导致训练出来的模型在司机手碰到杯子的瞬间误报。后来我们规定:只有杯子离开台面且接触嘴唇才算drinking,手在杯子上但没拿起来不算。
所以拿到这套代码,先别急着跑训练,把cfg里的names定义和标注规范打印出来,分发给标注人员,明确相似动作的边界。这一步做得越细,后面模型收敛越快。
3.2 视频抽帧脚本:按场景密度抽,不要每帧都存
原始视频往往是25fps或30fps,如果全部抽出来,数据量会爆炸且相邻帧几乎一样,模型学不到多样性。常见做法是按比例抽帧,比如每隔5帧取1帧,再结合场景变化判断是否保留。代码包里的extract_frames.py支持两个参数:--sample_interval控制抽帧间隔,--min_interval控制两帧之间的最小时间间隔。
python scripts/extract_frames.py --video data/raw_videos/001.mp4 --output data/frames/001 --sample_interval 5脚本内部用OpenCV逐帧读取,然后按间隔保存:
import cv2 import os cap = cv2.VideoCapture(video_path) frame_idx = 0 saved_idx = 0 while True: ret, frame = cap.read() if not ret: break if frame_idx % sample_interval == 0: cv2.imwrite(os.path.join(output_dir, f'{saved_idx:06d}.jpg'), frame) saved_idx += 1 frame_idx += 1 cap.release()逻辑是根据帧号取模,每5帧保留1帧。参数说明:sample_interval取5左右比较合理,场景变化快的路段建议取3,高速巡航路段可以取10,关键原则是让不同姿态的样本尽可能都出现。抽完帧后一定要随机抽看几十张,确认没有重复度过高的帧。我曾经在隧道场景抽出一半是黑屏或过曝帧,直接导致后面标注白干。
3.3 半自动标注:预标注加人工修正
完全手标六类行为太痛苦,代码包里给了一个基于YOLO的预标注脚本,先用通用权重识别驾驶员和手部,再自动生成候选框。虽然不能直接用,但能把标注工作量减少一半以上。
from yolodetect import YOLOModel import cv2 model = YOLOModel('yolov8n.pt') # 通用预训练模型 frame = cv2.imread('data/frames/001/000001.jpg') results = model.detect(frame, classes=[0]) # 只保留person类别 for box in results.boxes: x1, y1, x2, y2 = box.xyxy print(f'候选框: {x1:.0f}, {y1:.0f}, {x2:.0f}, {y2:.0f}')classes参数限定类别ID,避免把车、路标等无关物体也框出来。预标注只负责给出目标位置,最终类别需要人工确认。A同学试过完全信任预标注结果,结果模型把方向盘上的手机支架误认为phone,后期返工更费时间。半自动的正确姿势是:预标注后按帧抽检,发现明显错误立即修正,而不是全部人工重标。
这里有个小技巧:预标注脚本会把置信度低于0.3的框直接丢掉,但驾驶行为场景里很多手部动作是遮挡的,低置信度框不一定错。我把阈值调到0.2,多保留一些候选框,宁多勿缺。
3.4 YOLO格式转换与数据集划分
标注工具导出的格式可能是VOC的XML或者COCO的JSON,需要统一转成YOLO的txt格式,即每行"类别 x_center y_center width height",坐标是归一化的。
import os import random def convert_annotations(annotation_list, output_path): with open(output_path, 'w') as f: for cls_id, cx, cy, w, h in annotation_list: # 确保坐标在0-1范围内,越界直接跳过 if not (0 <= cx <= 1 and 0 <= cy <= 1 and 0 <= w <= 1 and 0 <= h <= 1): continue f.write(f'{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}\n') random.seed(42) files = [f for f in os.listdir('data/labels') if f.endswith('.txt')] random.shuffle(files) val_count = int(len(files) * 0.2) val_files = files[:val_count] train_files = files[val_count:]随机种子固定为42,保证每次划分结果一致。val比例取20%,驾驶行为数据集中各类别分布差异很大,后面可以在训练时调整权重。转换脚本里一定要检查归一化坐标是否在0~1之间,有越界的框直接删除,否则训练时会报错或产生nan。
划分完数据后,我会统计每个类别的样本数,做个柱状图。如果phone是5000张,drinking只有500张,训练时就要考虑类别权重或者数据增强,否则小类别基本会被淹没。
4. 训练调参与避坑:模型不收敛先查这五个地方
4.1 训练命令与超参数推荐:默认参数不是万能药
代码包的train.py封装好了训练逻辑,可以直接用命令行启动:
cd drive_detect python scripts/train.py --data cfg/data.yaml --weights models/yolov8s.pt \ --epochs 100 --batch 16 --imgsz 640 \ --device 0 --project runs --name exp_driveepochs取100,batch size根据显存调整,8G显存跑yolov8s时batch 16已经接近上限。imgsz保持640,不要调大,因为驾驶行为检测的目标区域是驾驶室,分辨率太大只会增加计算量。
学习率建议用代码包里的hyperparams.yaml,里面写了learning_rate=0.01和momentum=0.937。这两个值在YOLO系列里是经验值,但不同数据集的最优学习率可能相差一个数量级。我的习惯是先用默认值跑20个epoch,如果loss下降慢就把初始学习率调到0.02,如果loss震荡就调回0.005。
训练时还有一个容易忽略的参数是close_mosaic。代码包默认在最后10个epoch关闭mosaic增强,因为mosaic合成的图片和真实场景有差异,最后阶段用真实图片微调能让模型更稳定。如果你全流程都开着mosaic,很容易出现验证集mAP高但实际视频检测掉帧的情况。
4.2 损失曲线与mAP判读:训练loss下降不代表模型能用
训练结束看runs/exp_drive里的结果,主要看val_loss和mAP50曲线。很多人只盯着train_loss,发现它降到0.1就以为成功了,结果在实际视频上一测试全是漏检。原因在于驾驶行为类别之间存在强相似性,比如smoking和drinking都有手到嘴边的动作,训练loss低只能说明模型记住了训练集,泛化性要看val_loss是否同步下降。
mAP50在驾驶行为数据集上能达到0.75以上就算不错。如果mAP50一直在0.5徘徊,优先看混淆矩阵,大概率是phone和reach互相混淆。这个时候再加大epochs意义不大,应该回头检查标注质量,看是否有大量类别边界模糊的样本。
另外要盯住每个类别的recall值。有时候整体mAP还行,但其中一个类别(比如drinking)recall只有0.3。这种情况一般是样本数量太少,不是训练参数问题,需要回头补数据。
4.3 避坑记录:五个常见坑,每个都有人翻车
坑一:训练到中途loss变成nan。现象:第20个epoch开始loss输出nan,之后模型输出全为空。原因:学习率过大导致梯度爆炸,或是标注框坐标超出图像边界。解决:先检查标注txt里的坐标值,过滤掉大于1或小于0的框;再把初始学习率调低到0.005,并把warmup_epochs从3提高到5。那次我排查到凌晨,最后发现是有一个标注框宽度写成负数,模型直接在反向传播时算出了nan。
坑二:训练loss很低,验证mAP却很低。现象:train_loss到0.2,val_loss不降,mAP只有0.3。原因:训练集和验证集分布不一致,比如训练集都来自白天视频,验证集包含夜间帧。解决:重新划分数据集,保证白天/夜晚/雨天在每个集合里都有占比;同时开启数据增强中的HSV增强来提升泛化性。这样做之后mAP从0.3直接跳到0.6,效果立竿见影。
坑三:检测框在连续帧里抖动。现象:同一部手机,前一帧框大小200,后一帧变成150,视觉上跳变。原因:逐帧独立推理,没有时序平滑。解决:在推理脚本里对相邻帧的检测框做IoU匹配,如果重叠度大于0.7,就用加权平均替代当前框。这个方案简单,不用引入跟踪模型,对检测框稳定性提升非常明显。
坑四:驾驶员戴墨镜时抽烟检测失效。现象:墨镜从视觉上遮挡了部分面部,模型关键点缺失,手在嘴边的动作无法关联。原因:训练集没有戴墨镜的样本,模型没学过这个特征组合。解决:收集墨镜场景的数据,把这部分样本单独拿出来做数据增强,比如加曝光变化和旋转,让模型把手部动作作为主要判断依据,而不是依赖面部细节。后来我还试过把戴墨镜的样本单独设成一个类别的变体,效果没有前者好。
坑五:推理速度只有15fps,达不到实时。现象:部署在边缘设备上,模型推理占满CPU。原因:直接用了yolov8l模型且输入尺寸为960。解决:换成yolov8s模型,imgsz降到416,导出ONNX后用推理加速库,速度能提高到30fps以上。如果还嫌不够,就把输入帧裁剪到驾驶室区域,因为方向盘外的区域对行为判定没有贡献。
5. 推理与部署:从单帧检测到连续行为提醒的落地技巧
5.1 视频推理脚本:加上行为判定与计数
训练完成后,检测脚本不能只是画框,还要有行为判定逻辑。这段代码实现了一个简单的行为计数器:
from yolodetect import YOLOModel import cv2 model = YOLOModel('runs/exp_drive/weights/best.pt') cap = cv2.VideoCapture('test.mp4') counter = {'phone': 0, 'smoking': 0} while True: ret, frame = cap.read() if not ret: break results = model.detect(frame, conf=0.5) for box in results.boxes: cls_name = box.class_name if cls_name in counter: counter[cls_name] += 1 cap.release() print(counter)conf阈值取0.5,针对驾驶行为场景建议调高到0.6~0.7,因为这类任务的误报比漏报更危险。counter统计的是行为出现次数,实际使用时还需要加时间窗口判断,比如连续5秒内累计超过20次phone才触发报警,这样能滤掉瞬时误检。
5.2 模型导出与加速:ONNX是必经之路
部署时不能直接加载PyTorch权重,要先导出为ONNX格式,再转成推理引擎格式。导出命令如下:
yolo export model=runs/exp_drive/weights/best.pt format=onnx imgsz=640 device=0导出后用onnxruntime验证输出形状是否正常,然后做FP16量化。量化后模型体积减半,推理速度提升明显,精度损失在驾驶行为检测场景下通常可以接受。我一般会对比原始模型和量化模型在同一段测试视频上的检测结果,重点关注"drinking"类别的召回率有没有下降,如果下降了就退回FP32。
5.3 时序投票:降低误报的土办法
单帧检测模型解决不了遮挡和快速动作的问题。我接入这套代码时,在输出层后面加了一个简单的时序平均模块:连续三帧的类别结果做投票,只有两帧以上判定为同一行为才输出。这个改动让误报率降低了大概一半。注意投票窗口不能太大,否则动作已经结束了才报警,反而失去实时性。
从那以后,我每次用这套驾驶行为检测代码,都会强制先跑一遍数据验证脚本,确认每类样本数量、检查标注框质量,再开始训练。数据是这种项目最大的变数,模型和参数反而是最可控的部分。希望帮到你。
本文还有配套的精品资源,点击获取