☰
YOLO驾驶员行为检测实战:从数据集构建到训练调参
2026/9/30 16:27:19 网站建设 项目流程

前阵子刚把一个驾驶舱内的驾驶员行为检测项目跑完,用到的就是一套22600张的YOLO智能驾驶数据集。从最开始的采集、清洗、标注,到后来用YOLO训练和落地部署,整个链路走下来踩了不少坑,也整理出了一些常规文档里不太会写的实操经验。这篇文章就沉下来把这些东西讲清楚:这个数据集怎么设计、标注规范怎么定、训练参数怎么调、哪些环节最容易翻车。这套经验不仅适用于DMS驾驶员监控,凡是手里拿到一批YOLO标注数据、想自己训练检测模型的读者,基本都能用得上。

先说一点背景。所谓驾驶员行为检测,简单说就是在车载摄像头画面里,实时识别驾驶员是不是在打电话、看手机、喝水、抽烟、打哈欠、闭眼或者视线偏离。它并不是一个单纯的目标分类问题,除了要判断"有没有这个行为",还得知道"行为发生在图像里的哪个区域"。所以工程上前期把数据整理成YOLO格式的检测数据集是最稳妥的路径,后面训练、评估、导出部署都有现成链路。这套22600张的数据集,类别覆盖了正常驾驶和七类危险行为,配上YOLO的标注格式,基本可以支撑一套完整的DMS感知模型从零训练到装车验证。

1. 项目整体思路:为什么攒22600张检测数据集

1.1 驾驶员行为检测到底检测什么

这个项目的核心目标不是做一个通用目标检测器,而是做一个面向座舱场景的专用检测器。所以第一件要做的事情,就是把"驾驶员行为"拆成模型能学习、能标注、能评估的具体类别。我最终定下来的是下面这8类:

  • normal:正常驾驶、目视前方、双手或单手在方向盘上
  • calling:手持电话放在耳边通话
  • phoning:低头/斜视看手机屏幕
  • drinking:拿起水杯或水瓶喝水
  • smoking:吸烟、手部持烟靠近嘴部
  • yawning:打哈欠,通常伴随大张嘴
  • eyes_closed:闭眼,可能是疲劳驾驶的强信号
  • looking_away:视线明显偏离前方,转头看侧窗、后视镜或副驾

这里要特别提醒,类别定义不是拍脑袋定8个就完事,而是要在采集数据之前写成一页规范文档。比如"calling"和"phoning"看起来都是玩手机,但一个是接打电话、一个是在手机上滑动/浏览,动作姿势差异很大。如果标注人员和算法人员对这两个类的判断标准不一致,后面训练出来的模型会非常飘。我在项目里就把这两类做了很明确的分割:手机贴近耳朵算calling,手机在胸前或者大腿上方、视线向下,就算phoning。

另外,这个检测目标的边界也要设计清楚。DMS摄像头一般装在方向盘上方或A柱附近,视角覆盖驾驶员上半身、方向盘区域和部分车窗。模型输出的是行为主体对应的边界框,也就是人形框、手部框、手机框、水杯框、烟支框这类目标框。这类检测框的存在,本质上是在给后面的行为判定逻辑提供空间信息。有了框,才好判断"手和手机是否重叠""手是否靠近嘴""眼睛是否处于闭合状态"。

1.2 为什么选YOLO而不是分类网络或两阶段检测器

做驾驶员行为检测,其实存在三种技术路线:纯分类网络、两阶段检测器、YOLO这类单阶段检测器。

先看纯分类,比如用ResNet训练一个图像分类模型,输入一整张截图,输出"正常/打电话/喝水"这样的标签。这种方案在自动驾驶行业早期见过不少,但问题在于它完全丢失了位置信息。驾驶员在画面中可能只占比较小的区域,手部动作更小,分类模型很难告诉系统"这个动作发生在哪里"。而且座舱内不同的驾驶员坐姿、体格差异,会让分类模型把注意力放在背景或身体整体上,而不是真正的手部和设备交互细节上。

再看两阶段检测器,Faster R-CNN这类结构精度是够的,但推理速度在嵌入式平台上不太理想。DMS系统通常跑在车规级芯片上,资源非常有限,实时性要求起码是视觉部分推理能在30 FPS左右。两阶段检测器在这个约束下有些吃力。

所以最后选了YOLO。实际上YOLO单阶段检测的速度天然占优,尤其YOLOv8之后,检测头、损失函数、训练策略都很成熟,有COCO预训练权重可以直接微调。更关键的是,它的生态太完整了:训练要用到的数据增强、评估指标、模型导出,整个工具链都是现成的,对于数据工程型团队来说,省掉很多自研成本。现在社区里还有YOLOv9、YOLO11以及一些结合Transformer或Mamba的变体在研究讨论,作为工程落地,YOLOv8目前仍然是稳的选择。

1.3 22600张数据集的规模与划分逻辑

很多刚接触检测项目的人会有一个疑问:22600张图到底够不够训练?答案要看场景复杂度和类别数。对于驾驶员行为检测这种固定机位、场景相对受限的任务来说,2万张级别是完全够跑出可用模型的,前提是采集的光照覆盖度足够。如果全是白天RGB图像、全是同一个驾驶员,那一万张和五万张差别不会太大;但如果白天、夜间、逆光、隧道、戴墨镜戴帽子等情况都要覆盖,样本量就需要堆上去。

我当时的划分方案是训练、验证、测试按大约8:1:1切。精确计算的话:

  • train = 22600 约 80% = 18080张
  • val = 22600 约 10% = 2260张
  • test = 22600 约 10% = 2260张

实际执行时我留了大概300张作为极端困难样本的测试集,所以验证集和测试集加了个余量,总数仍然对应22600张总量。这样划分的好处是,验证集足够稳定地反映训练过程中的模型状态,测试集则完全从调参循环里隔离出去,用来做最终指标评估。

这里有一个经验:按类别比例进行分层切分,不要直接对整个文件夹随机shuffle。如果random shuffle,某些类别在整个数据池里本身就少,验证集里可能出现一类只有十几张的情况。我的做法是先把所有图片按最小类别统计,保证train/val/test每个类别都保持接近8:1:1的比例,再在每一类内部做随机抽取。这样后续看每个类别的AP曲线时,才不会被验证集样本过少带偏。

2. 采集与标注:决定模型上限的前置工作

2.1 舱内采集场景怎么搭

数据质量的第一步是采集设备的安装位置和成像条件。DMS摄像头一般装在两个位置:内后视镜后方和驾驶员侧A柱,俯视角度大概10到15度,视野里应该看到方向盘、驾驶员的上半身和面部侧前方。我踩过的一个坑是摄像头装太高,俯角太大,结果手部动作被方向盘遮挡严重,喝水动作基本看不到。后来把镜头角度微调之后,标注的困难程度一下降低了很多。

光照条件是座舱场景最大的变量。白天从车窗进来的自然光会在驾驶员面部形成很强的明暗对比,夜晚车内几乎没有可见光。所以采集方案必须同时覆盖可见光和近红外。实际做法是使用带红外截止片切换功能的摄像头,白天输出RGB图,夜间开启红外补光后输出近红外图。注意,近红外图本质上是单通道灰度信息,但很多相机封装出来仍然是三通道,只是三个通道数值接近。这一类图在训练阶段如果直接和白天彩色图混在一起,会让网络产生域漂移。我自己的方案是训练前把RGB图也全部转成灰度三通道,让模型不再依赖颜色信息。

除了光照,还要考虑装饰物和人体姿态差异。采集样本时尽量找不同体型的驾驶员、让驾驶员穿不同颜色的衣服、戴帽子、戴墨镜、戴口罩,甚至复杂阴影下都要有覆盖。建议把采集时间段分布在早中晚和夜间,并记录每张图的元信息,比如光照环境、驾驶员ID、行为类型。有了这些元信息,后面做域分析时才不用靠肉眼猜数据构成。

2.2 行为类别定义与边界规范

标注规范写得好不好,直接决定标注一致性和模型精度上限。前面已经列了8个类别,这里重点讲边界情况的处理。

首先是"同时发生多个行为怎么办"。例如驾驶员一边打电话一边喝咖啡。我的标注规则是:只要同一帧里多个行为在图像上表现明显,就分别标注对应的目标框。也就是说一个类别实例是一个检测框,人和手可以有多个框同时出现,并不强制把整个帧打成一个标签。这样做的好处是模型学到的是独立的行为框,而不是学习"整张图的综合状态",后处理阶段再根据框和框之间的位置关系做行为判定。

然后是"动作刚开始和即将结束怎么界定"。这属于时序切片问题。例如拿起水杯往嘴边送的过程中,在哪个时间点算drinking?我在规范里写的是,水杯与嘴部的横向距离小于一个拳头的距离,或者杯口有倾斜进入嘴部区域的动作,才算drinking。如果没有这层约定,标注人员会在动作临界帧上产生巨大分歧。项目里的做法是先在每个视频序列中采样关键帧,由一个人统一标注,然后另一个标注人员做交叉验证。

关于目标框的大小限制也有规则。驾驶员实际在图像里的尺度不会太大,但也不至于小到不可辨。我们要求标注框的最小边不小于20像素,遮挡超过80%的目标不标,模棱两可时宁可漏标也不要错标。原因很简单:漏标只会让模型在这类样本上召回率低一点,错标却会让模型学到错误特征,后面在真实场景中出现"把正常驾驶识别成打电话"这种不可接受的误报。

2.3 标注流程与YOLO格式细节

标注工具上,我推荐支持YOLO格式的开源工具。之前的项目里用过LabelImg和X-AnyLabeling,后者对大数据量更友好一些,支持自动保存、快捷键、预标注模型导入。用YOLO格式存储时,每个txt文件对应一张图片,文件中的每一行是:

class_id x_center y_center width height

其中坐标全部是归一化到0-1之间的值,分别对应边界框中心点的xy和框的宽高。比如一行是2 0.3213 0.5487 0.1234 0.2678,表示第2类(phoning),框中心在图像坐标的(0.3213, 0.5487),宽高分别是0.1234和0.2678。归一化的好处是不同分辨率图片可以共用同一套标签文件。如果摄像头分辨率从1080P换成720P,标注文件完全不用动。

标注流程我分三步走:第一步用弱模型预标注,生成初版伪标签;第二步由人工在工具里修正边界框和类别;第三步做交叉复检和抽检。预标注不一定看模型本身的精度,而是帮标注员节省画框的时间,人工只需要拖一拖边界,效率能提升很多。抽检比例我设在5%左右,如果发现某类标签的修正率超过一定阈值,就退回重标该批次。

这里还要提醒一个很容易翻车的问题:标注过程中如果调整了图片尺寸,标签坐标一定不能直接沿用,要重新换算归一化坐标。我见过有同事把一批1080P的标签直接套到720P图片上,结果所有框的位置都偏了约三分之一,整个批次的训练直接跑偏。

3. 用YOLO训练驾驶员行为检测:从配置到评估全流程

3.1 数据集目录组织与YAML配置

拿到标注好的数据后,第一步是按YOLO的标准目录结构整理数据。虽然YOLO也支持通过脚本动态读取,但我还是建议直接落成目录文件,方便后面交给任何一台训练机器复现。标准结构如下:

driver_behavior/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── driver_behavior.yaml

images和labels下面的文件名是一一对应的,图片叫frame_000123.jpg,标签就叫frame_000123.txt。一张图片如果没有任何行为目标,对应txt文件是空的,但文件本身要存在,否则训练时YOLO日志里会报"label not found"一类的问题。这个问题虽然不影响最终结果,却会拖慢训练速度、增加排查困扰。

YAML配置文件是关键入口,我写的配置如下:

path: /data/driver_behavior train: images/train val: images/val test: images/test names: 0: normal 1: calling 2: phoning 3: drinking 4: smoking 5: yawning 6: eyes_closed 7: looking_away

需要注意names的索引必须和标注txt里的class_id完全对齐。这些看起来是小问题,但实际项目里我就犯过把正常驾驶放在索引7而不是索引0的错误,结果训练出来模型对"normal"这个类完全没反应,日志里那个类别的AP直接是0。遇到这种情况,不要怀疑网络结构,先检查标签和names对不对齐。

3.2 训练参数、预训练权重与损失函数怎么选

训练使用的工具是Ultralytics YOLO,框架已经封装好了数据读取、增强、损失计算和评估逻辑,核心工作在于把参数选择解释清楚。以一个中等规模的DMS模型为例,我的常用参数如下表:

参数推荐值说明
modelyolov8s.pt预训练权重,规模适中
imgsz640输入分辨率,兼顾精度与速度
batch16单卡或双卡时常用值
epochs120结合早停使用,一般80-120内收敛
lr00.01初始学习率,YOLO默认值附近
patience20连续20轮验证集不提升则停止
seed2024固定随机种子,方便复现

预训练权重是第一个要强调的点。直接下载YOLOv8在COCO上预训练好的权重文件,而不是从随机初始化开始训练,非常关键。COCO虽然和驾驶员行为场景差异很大,但它已经让网络学到了通用的边缘、纹理和形状特征,微调阶段能快速收敛。我实测下来,同样100个epoch,用预训练权重从零头跑到mAP50约94%左右,从零训练大概率只能到88%左右,而且更加容易过拟合。热词里也常有人问"yolo预训练模型下载",实际上就是官方发布仓库里的yolov8n.pt、yolov8s.pt这些文件,注意权重版本和要导出的模型架构保持一致就行。

损失函数这一块,YOLOv8的检测头同时包含三个分支:边界框回归的CIoU损失、分类的BCE损失和DFL分布损失。DFL它把框的坐标预测拆成离散分布,对边缘精度和小目标更友好。这些损失默认权重已经调得很均衡,一般不需要改动。如果非要在DMS场景里调,我会把分类损失里"normal"类的权重降一点,因为正常驾驶类别前景框数量大、背景简单,过强的梯度反而会让模型在危险行为类别上欠拟合。

训练命令非常简单,但要注意命令里的path路径要能在运行机器上访问到完整数据。命令写出来是这个样子:

yolo detect train \ data=driver_behavior.yaml \ model=yolov8s.pt \ epochs=120 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ device=0 \ seed=2024

3.3 数据增强在座舱场景的取舍

YOLO自带的增强策略默认是很激进的,包括Mosaic、MixUp、HSV扰动、随机翻转、缩放等。Mosaic四图拼接和MixUp叠加都能显著提升模型对复杂环境的鲁棒性,但在驾驶员行为检测场景里不能无脑全开。

我踩过的坑是mosaic导致手部小目标被切得支离破碎,反而拉低了"喝水""吸烟"这种强依赖手部细节的类别。原因是Mosaic把四张图缩小拼到一张图里,手上的手机、水杯本来就小,缩小后彻底看不清。解决办法是把mosaic超参从默认1.0降到0.3左右,让更多训练样本保持原始尺度。MixUp我是直接关闭的,因为驾驶员行为检测里"行为边界"本来就比较精细,叠图会让边界更模糊。

HSV增强对RGB图有效,但我前面说了会把所有图像转成灰度图训练,所以颜色变化增强基本用不上。真正有用的是亮度对比度扰动,适合模拟车内不同时间的反光。水平翻转我保留了,但它存在一个驾驶位方向的潜在混淆问题:如果采集车是左舵驾驶,翻转后变成右舵视角,打电话的手从右手变为左手,这在实际部署时通常不是大问题,因为模型学习的更多是"手贴近脸"的语义关系,而不是绝对手性。但如果你的落地市场要求严格区分左右手,就要考虑关闭水平翻转。

缩放增强我建议重点开,尤其scale在0.5到1.0的区间。这个操作能模拟不同身高的驾驶员在画面里呈现的尺度差异,让模型对目标尺寸更鲁棒。translate和degrees的增强可以给一点但不宜大,因为DMS摄像头是固定机位,画面里目标不会大幅平移或旋转,过度增强反而会让模型学习到不真实的位置分布。

3.4 评估指标与模型导出

训练结束后,正式评估要在独立test集上执行。验证命令如下,它会输出每一类以及整体的mAP50、mAP50-95、precision和recall:

yolo detect val \ model=runs/detect/train/weights/best.pt \ data=driver_behavior.yaml \ split=test

DMS场景里,我最看重的是两类指标:一个是calling、phoning、drinking这些危险行为的recall,也就是"真正危险行为有没有被漏掉";另一个是normal类的precision,也就是"正常驾驶有没有被误报成危险行为"。误报这件事在产品里非常影响体验,如果正常开个车突然报警说你在打电话,用户很快就对该系统失去信任。所以我在训练时,会把normal类的置信度阈值单独调高一点,危险行为类的阈值调低一点,做一个偏置。

模型导出到部署平台一般走ONNX或TensorRT。导出命令是:

yolo export model=best.pt format=onnx dynamic=True opset=12

如果目标是Jetson这类嵌入式设备,我会进一步转成TensorRT的engine文件,用FP16精度。工程实践里YOLOv8s在Jetson Orin上跑640x640输入,TensorRT优化后单帧推理大概在10到20毫秒,完全满足实时要求。部署后还要接一个平滑后处理,因为单帧检测可能偶尔抖动,用简单的滑窗投票或者卡尔曼滤波对行为标签做时序修正,误报率能再降一截。

4. 训练实战中的常见问题与排查技巧

4.1 训练不收敛或loss异常怎么查

训练过程中遇到loss不收敛或者出现NaN,是最让人头大的问题。我把它整理成一个速查表,很多场景下可以直接对照处理。

现象可能原因排查与处理办法
train loss和val loss都降不下去标签坐标归一化错误或类别索引错位随机抽几张图,把标签画出来可视化检查
loss出现NaN学习率过大、batch过小、BN统计量崩溃降低lr、增大batch;不行就换成预训练权重重新开始
train loss很低但val loss高过拟合减少训练轮数、增加数据增强、降低模型规模或加dropout
某个类别AP一直为0该类别在验证集里太少,或标注类别名和names不对齐统计类别框数分布,检查YAML names顺序
训练日志里有大量"label not found"图片和标签文件名不匹配检查images和labels目录下文件是否一一对应

最有效的排查手段不是盯着loss曲线猜,而是把标签可视化。我一般在训练前写几行脚本,把训练集里任意图的标注框画出来,人眼扫一遍就知道类别和位置有没有问题。这一步成本很低,但对后续训练效率的影响非常大。

4.2 类别不均衡和BN崩溃的处理

驾驶员行为数据集天然存在类别不均衡的问题。正常驾驶样本可能占了40%以上,而吸烟、闭眼这类行为可能只有5%到8%。YOLO默认训练会把每个类别同等对待,小类别会被大类别淹没。

我的处理方式是两步走:第一步在数据层面做采样调整,把样本量最大的几个类别做欠采样,让每类框数控制在一个比较接近的范围内;第二步在训练时开启cls_loss的加权参数,对小类别给略高的损失权重。这里要注意,不要只依赖损失权重来处理严重不均衡,数据层面的调整仍然是最直接的。

再单独说说BN崩溃。所谓"yolo训练中bn崩溃",表现是训练刚开始loss还正常,跑到几十个iteration后loss突然变成NaN,或者验证集的指标突然从90%掉到0。常见诱发原因是batch size开太小。BN层需要在batch内部统计均值和方差,batch为1或2的时候统计量抖动非常剧烈,尤其是在数据呈现明显光照分布差异时。如果显存不够,不要硬撑大batch,可以降低输入分辨率或换更轻量的模型,优先保证batch不低于8,最好到16。另外,如果发现loss在训练前几个epoch就出现震荡加剧,也可以临时把lr0调到0.005试一下,先让BN统计稳定下来再恢复正常学习率。

4.3 混淆矩阵总和不唯一是怎么回事

很多人在YOLO的训练结果里看混淆矩阵,发现横着加、竖着加、总体加起来对不上,就开始怀疑是不是代码bug。这里把逻辑讲透:YOLO输出的混淆矩阵,纵轴一般是真实类别,横轴是预测类别,单元格写的是样本数量或者归一化比例。每一行的总和,等于该类别在数据集中的真实样本数;每一列的总和,则等于模型预测为该类别的样本数。由于存在漏检和误检,行和与列和天然不相等,矩阵本身也不是方阵或者数值不对称。所谓"总和不唯一",不是bug,而是混淆矩阵同时表达了分类错误和检测遗漏两件事。

我在项目里看到这个矩阵,会重点看两个区域:一个是"predicted as normal"这一列,如果危险行为类别在正常驾驶列里有较多数量,说明模型会把危险行为当成正常,这是最危险的漏报;另一个是"predicted as calling"这一类,看是否存在把喝水误判成打电话的问题。只有把矩阵结合具体类别的语义来看,它才有真正的调优价值。

4.4 红外夜视数据与白天数据的域差异

前面提到夜间红外图和白天RGB图在像素分布上完全不同。如果你把它们直接混在一起训练,模型容易出现选择性遗忘:某段时间偏向白天,某段时间偏向夜间。我这边有两条解决路径,可以按项目阶段选。

路径一是统一灰度。在数据进网络前把所有RGB图转成单通道灰度,再复制成三通道喂给网络。这样白天和夜间的图像在通道数值上处于同一个域,模型的输入分布一致性大幅提升。缺点是损失了颜色信息,但DMS任务本身对颜色依赖不大,我们判断的是"有没有打电话、有没有喝水",而不是"手机是什么颜色",所以这个方案在我项目里效果很好。

路径二是做光照扰动增强。训练时对灰度图随机改变对比度和亮度,模拟夜色、隧道、逆光等不同亮度环境。YOLO的HSV增强里也有value扰动,但那是针对彩色图的,我在改用灰度图后会写一个自定义预处理,把亮度扰动范围加大一点,比如0.6到1.4倍,这样网络对红外补光下偏暗的画面会更鲁棒。实测下来,夜间测试集上的mAP50能提升3到5个百分点。

4.5 手部小目标与遮挡检测优化

手部在座舱图像里往往只有30到50个像素的宽度,属于典型的小目标。小目标检测在YOLO里如果只靠提升输入分辨率,虽然有效但推理成本会上去。我建议按三个层次去优化。

先尝试提高imgsz。把640提升到800或960,手部区域的分辨率会明显增加,小目标召回率能涨一截。但这对嵌入式平台的算力要求也更高,需要实测推理时间。再试试开启SAHI这类切片推理方案,大图切块后分别检测再合并结果,可以在不动训练模型的情况下提升小目标精度,但推理耗时增加明显,一般只用于离线分析或者后装高算力平台。最后是模型层面的配合,训练时给手部相关类别更充分的标注框,不要只标手,连手机、水杯一并标出来,让模型学习到"手和物体互动"的组合特征,这样即使手部本身被遮挡,物体框也能给出足够线索。

遮挡是驾驶员行为检测里躲不开的话题。喝水时水杯会挡住嘴部,接打电话时手会挡住耳朵。我的经验是,遮挡严重的样本不要直接删掉,它们反而是关键困难样本。可以在标注规范里单独建立"遮挡但可辨识"和"遮挡不可辨识"的区分标准,前者保留标注,后者删除。这样模型才能学会在真实遮挡场景下依然给出可靠检测,而不是只见过"完美无遮挡"的样本,一到路上遇到遮挡就失灵。

最后分享一条个人体会比较深的经验:整个项目跑下来,我最大的教训是标注一致性远大于标注数量。如果你手里的数据来源复杂、多个标注人员批次的风格差异大,模型AP再高也会在真实场景里翻车。所以哪怕时间紧张,也要先抽一部分数据做一致性测试,让两个人标同一批图,统计框和类别的吻合度。这个步骤做好了,后面的训练、调参、部署才会顺。另外一个小技巧:训练时把白天图全部转灰度,夜间红外图保持灰度输入,能大幅降低夜间掉点的风险,这个改动几乎零成本而且实测很稳,值得直接抄进自己的流程里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询