先说点实在的。看到“基于YOLOv8的危险驾驶行为监控与语音提醒系统”这个标题,我第一反应是这活儿不像表面看起来那么简单。很多人以为装个检测模型、框出个“闭眼”“打电话”就算完事,但真正要落地一个能用的监控系统,核心难点从来不在单帧识别,而在“时序判定”“误报抑制”和“工程整合”这三件事上。这篇文章就把这个项目从头到尾拆开讲:模型怎么选、数据怎么整、行为怎么判、语音怎么提醒、三种检测模式怎么打通,顺便把那些只有亲手踩过坑才知道的细节一并交代清楚。
适合看这篇文章的人,基本有三类:一是拿这个题目做毕业设计或课程项目的学生,需要一个能跑通、能讲解、还能抗住答辩提问的完整方案;二是做车载安全、网约车管理、驾驶行为分析这类业务的技术人员,想快速搭建一个可扩展的监控原型;三是刚学完目标检测、想通过一个综合项目把PyTorch、YOLOv8、OpenCV这些技能串起来的CV爱好者。无论哪一类,这篇文章都能给你一套可以照抄的作业,而不是零散的代码片段。
1. 项目整体拆解:需求、场景与选型逻辑
1.1 需求本质:这系统到底在检测什么
标题里“危险驾驶行为”这六个字,乍一看很宽泛,但落到具体工程上其实可以拆成三类:疲劳状态、分心行为和违规动作。疲劳状态就是闭眼、打哈欠、低头;分心行为就是看手机、操作中控、回头跟乘客聊天;违规动作则包括抽烟、喝水、双手离开方向盘等。这个项目里,我们通常聚焦前两类,也就是疲劳和分心,因为这两类在视觉上最容易用目标检测+状态判定来解决,而且事故相关性最高。
但这里有个关键认知要提前建立:目标检测模型输出的只是一帧画面里的物体类别和位置框,它本身并不理解“连续闭眼3秒意味着疲劳”。所以整个系统必须是一个分层结构:感知层负责识别眼睛状态、嘴巴状态、手部行为;推理层负责把连续十几帧、几十帧的检测结果放进一个滑动窗口里做统计;决策层才负责判断“当前是否构成危险驾驶”、该报哪一级的警;最后交互层用语音把提醒播报出来。这个分层思路是整个项目的骨架,后文所有代码和参数都围绕它展开。
1.2 为什么选YOLOv8而不是其他模型
选YOLOv8,核心原因是它在“精度、速度、生态”这个三角里取得了最舒服的平衡点。YOLOv5虽然成熟稳定,但结构相对旧了;YOLOv7精度不错,但代码分支复杂,部署稍显费劲;RT-DETR精度高但推理速度偏慢,不适合要做实时摄像头的场景。YOLOv8用的是C2f结构(比C3更丰富的梯度流),把检测头改成了解耦头(分类和回归分支分离),又全面转向anchor-free,训练收敛更快,对目标形状变化的适应力也更强。它的API设计非常友好,用Ultralytics官方库,几百行代码就能完成训练、验证、导出、推理闭环。
从工程角度看,选PyTorch框架几乎是必然的。YOLOv8的官方实现就是PyTorch,社区生态、预训练模型、调试工具都围绕PyTorch展开。而且后续如果要导出ONNX、部署到边缘设备,PyTorch的转换链路最成熟。对做毕设或原型系统的人来说,PyTorch加YOLOv8意味着项目从第一天起就站在一个成熟的基座上,不需要自己写训练管线、数据加载器这些轮子。
什么情况下会考虑其他方案?如果最终要部署到Jetson Nano这类资源极紧张的设备上,或者对延迟要求极高,那YOLOv8n加TensorRT导出会更有优势;如果要对极小目标(比如远处汽车里的手机)有更高召回,RT-DETR和带Transformer的模型可能更合适。但就通用项目而言,YOLOv8s是首选的“万金油”配置。
1.3 行为类别设计与报警判定思路
行为类别的设计直接决定模型训练难度和系统误报率。我给这个项目的建议类别是七类:睁眼(open_eye)、闭眼(closed_eye)、打哈欠(yawning)、看手机(phone_use)、抽烟(smoking)、正常驾驶(normal_driving),外加一个背景类。有人会疑惑“正常驾驶”也算一类?算,而且很重要。在训练数据里保留它,相当于给了模型一个明确的“负样本出口”,能显著减少把方向盘、饰品、路牌误判成危险行为的概率。
有了单帧类别,还要解决“什么时候报警”的问题。疲劳的经典判断依据是PERCLOS指标(单位时间内眼睛闭合时间所占比例),工程上更简化的做法是“滑动窗口统计”:连续N帧中,如果闭眼帧数超过M就判定为疲劳。例如在30FPS的视频里,统计最近60帧(即2秒),闭眼比例超过50%就触发提醒。这种判定的好处是天然具备“去抖”功能:偶尔一帧闭眼(比如正常眨眼)不会触发误报,只有持续闭眼才会报警。
分心行为的判定则不同:看手机、抽烟这类动作具有“瞬时性”,通常只需要连续2到5帧确认,就能触发提醒。因为误报的代价不一样:疲劳报警要求宁缺毋滥,频繁乱报会让司机免疫;分心报警则要求反应及时,拖几秒就可能出事。
2. 环境搭建与工程结构规划
2.1 PyTorch与Ultralytics环境配置
环境配置看起来简单,但版本匹配是新手第一道坎。我建议用conda或venv创建独立环境,Python版本不要选太激进,3.9到3.11之间比较稳。
conda create -n driver python=3.10 -y conda activate driverPyTorch的安装要跟CUDA版本对齐。先用nvidia-smi看显卡驱动支持的CUDA版本,然后去PyTorch官网选对应命令。以CUDA 12.1为例:
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121装完验证一下:
import torch print(torch.__version__) print(torch.cuda.is_available())torch.cuda.is_available()必须返回True,否则后面训练速度会慢到怀疑人生。如果返回False,大概率是PyTorch的CUDA版本和驱动不匹配,或者装成了CPU版。
然后是Ultralytics库。这一个包就能搞定模型加载、训练、推理:
pip install ultralytics opencv-python顺手把后续要用的库一起装了:
pip install pyttsx3 pyyaml tqdm这里有个小建议:pyttsx3是跨平台的离线语音库,Windows、Linux都能用,且不需要联网。如果你用的是新版macOS,pyttsx3偶尔会抽风,那就改用系统自带的say命令或者播放预录好的wav文件。后文语音模块部分会展开说。
2.2 工程目录与模块划分
拿到这个标题,最忌讳的就是把所有代码堆在一个main.py里。项目做到后面必然会反复调参、加功能、修bug,模块化设计能救你一命。我常用的目录结构如下:
driver_monitor/ ├── config.py # 所有可调参数集中管理 ├── dataset.yaml # 训练数据配置 ├── train.py # 模型训练脚本 ├── detect.py # 三种模式的主检测逻辑 ├── behavior_judge.py # 时序判定、EAR计算、报警状态机 ├── alert.py # 语音提醒模块 ├── utils.py # 绘图、文件处理等公共函数 ├── weights/ # 存放训练好的模型权重 └── runs/ # 训练日志与验证结果为什么一定要有config.py?因为阈值、报警间隔、置信度这些参数,在实验阶段你会改几十遍。把它们全集中到一个文件里,用字典或类管理,比在代码里到处搜索魔法数字舒服太多了。我习惯把关键参数写成这样:
class Config: # 模型相关 MODEL_PATH = "weights/best.pt" CONF_THRESHOLD = 0.4 # 检测置信度阈值 IOU_THRESHOLD = 0.45 # NMS的IoU阈值 IMG_SIZE = 640 # 推理输入尺寸 # 疲劳判定相关 EAR_EYE_RATIO = 0.22 # 眼睛宽高比低于该值视为闭眼 CLOSE_EYE_FRAMES = 45 # 连续闭眼超过45帧则报警 YAWN_FRAMES = 20 # 连续打哈欠超过20帧则报警 # 分心判定相关 PHONE_CONFIRM_FRAMES = 5 # 连续检测到手机帧数 SMOKING_CONFIRM_FRAMES = 5 # 语音提醒相关 ALERT_COOLDOWN = 10 # 两次同类报警的最小间隔(秒) ALERT_VOICE_RATE = 180 # 语音播报语速 # 输入源相关 CAMERA_ID = 0 # 默认摄像头ID VIDEO_PATH = "demo.mp4" # 视频文件路径 IMAGE_PATH = "test.jpg" # 图片路径2.3 模型选型:YOLOv8n到底够不够用
Ultralytics官方提供n、s、m、l、x五种尺寸,参数规模从几百万到几千万不等。我实际测试下来,在普通的消费级显卡(比如中端N卡)上,YOLOv8s在640分辨率下推理延迟约15到25毫秒,完全满足实时摄像头需求;YOLOv8n延迟只有几个毫秒,但小目标检出的稳定性会差一些。做危险驾驶监控,眼睛、嘴巴、手机都属于小目标,所以我不是特别建议一上来就用n,除非你的目标设备是CPU或者嵌入式板子。
| 模型 | 参数量 | 输入尺寸 | GPU推理耗时(约) | mAP50经验值 | 适用场景 |
|---|---|---|---|---|---|
| YOLOv8n | 3.2M | 640 | 5-8ms | 较低 | 嵌入式、ARM、CPU |
| YOLOv8s | 11.2M | 640 | 15-25ms | 中等 | 本项目首选、通用GPU |
| YOLOv8m | 25.9M | 640 | 30-40ms | 较高 | 精度优先、GPU较强 |
| YOLOv8l | 43.7M | 640 | 50-70ms | 很高 | 离线分析、服务器 |
| YOLOv8x | 68.2M | 640 | 80-120ms | 最高 | 追求极限精度 |
注意:上面的耗时在不同显卡上差异很大,数据仅供参考。关键是项目起步时就在
config.py里把MODEL_PATH和IMG_SIZE抽出来,这样中途想从s换成n,只需改一行配置,不用动其他代码。
3. 核心算法与行为判定细节
3.1 目标检测输出怎么用
YOLOv8在PyTorch里推理非常简单:
from ultralytics import YOLO model = YOLO("weights/best.pt") results = model.predict(source="frame.jpg", conf=0.4, iou=0.45, imgsz=640, verbose=False) for r in results: boxes = r.boxes.xyxy.cpu().numpy() # 检测框坐标 confs = r.boxes.conf.cpu().numpy() # 置信度 clses = r.boxes.cls.cpu().numpy().astype(int) # 类别索引拿到这些数组后,你的项目才刚开始。results[0].boxes里的xyxy是左上角和右下角坐标,单位是像素。做行为判定时,不仅要看类别,还要看检测框的位置、宽高比、置信度。比如闭眼类别,如果检测框的宽高比明显异常(比如接近正方形),很可能是误检,因为正常闭眼的框应该是扁长形的。这些启发式规则虽然老土,但在宁缺毋滥的疲劳报警场景里非常有效。
3.2 EAR眼部纵横比与疲劳判定
纯靠YOLOv8检测“闭眼”类别做时序统计,已经能work,但有一个隐患:模型对“闭眼”和“微眯眼”的边界把握不稳,训练数据里这类样本稍少,单帧识别就会抖动。更稳的做法是引入EAR(Eye Aspect Ratio,眼部纵横比)。
EAR的原理特别简单:人的眼睛有六个关键点,眼角两个点、上下眼睑各两个点。当眼睛完全睁开时,上下眼睑的距离比较大,跟眼角距离的比值约为0.3左右;当眼睛闭合时,这个比值会骤降到0.1以下。用公式表示:
EAR = (||P2 - P6|| + ||P3 - P5||) / (2 * ||P1 - P4||)P1和P4是左右眼角,P2、P3是上眼睑,P5、P6是下眼睑。EAR值跟人脸距离摄像头的远近无关,因为它是一个比例值,这是它最棒的特性。
但这里有个工程问题:YOLOv8标准的检测模型不输出人脸关键点,要算EAR需要额外接一个关键点模型,比如YOLOv8-seg或者专门的人脸关键点模型。如果不想增加系统复杂度,有一个折中方案:用检测模型输出的“闭眼”类别框,用框的宽高比作为EAR的替代指标。
def is_eye_closed(bbox, ear_threshold=0.22): x1, y1, x2, y2 = bbox width = x2 - x1 height = y2 - y1 if width == 0: return False ratio = height / width return ratio < ear_threshold为什么宽高比阈值取0.22?从经验来看,正常睁眼时眼睛框的宽高比通常在0.25到0.4之间,闭眼时在0.05到0.15之间,取0.22给了一个不错的边界。用这套逻辑,即便模型偶尔在开闭眼之间抖动,数据波动也会被平滑掉,配合滑动窗口效果很好。
疲劳判定的完整逻辑可以这样写:
class FatigueJudge: def __init__(self, window_size=60, close_ratio=0.5, alert_frames=45): self.window = deque(maxlen=window_size) self.close_count = 0 self.alert_frames = alert_frames def update(self, is_closed): self.window.append(1 if is_closed else 0) close_count = sum(self.window) if close_count >= self.alert_frames: return True # 触发疲劳报警 return False这个滑动窗口天然解决了眨眼误报的问题。人正常眨眼大概0.1到0.2秒,折算成30FPS就是3到6帧。60帧的窗口里,一两次眨眼根本掀不起浪花,只有持续闭眼十几秒才会让闭眼帧数触及报警线。
3.3 打哈欠、用手机、抽烟等行为规则
打哈欠的判定逻辑和闭眼类似。标准的做法是检测嘴巴区域,计算嘴巴纵横比MAR(Mouth Aspect Ratio),连续多帧张嘴幅度大且稳定就认为是哈欠。如果你不想额外做关键点,直接利用“yawning”类别加上宽高比过滤也行,但效果会有点糙。我的建议是:训练数据里把“打哈欠”这一类样本收集充分一点,模型单独输出一个类别,然后用持续帧数判定。
用手机和抽烟的判定则有细化空间。纯靠模型输出“phone_use”类别当然可以,但我发现一个细节:很多时候司机手里拿着手机,手的位置会和方向盘区域重叠,或者手机处于屏幕边缘,导致检测框不稳。更稳的策略是双通道判断:
- 检测到“phone_use”类别,置信度超过设定值;
- 检测到手部区域,且手部框与手机框的交并比超过0.3。
两个条件同时满足才报警。这套方案看似多了一步,实际能过滤掉大量“司机手里拿着类似手机的物体”“手机放在支架上但没碰”“屏幕反光造成误检”等干扰情况。
报警一定要分级。我的经验是分成“轻度提醒”和“危险警告”两个档位。轻度提醒的语音内容可以柔和一些,比如“检测到您有闭眼趋势,请注意休息”;危险警告则要在连续触发多次后升级,用更醒目的语音和界面提示。同时,每次同类报警之间加一个冷却时间,防止语音播报变成噪音,司机直接忽略系统。
4. 数据准备与训练调优全流程
4.1 数据集的获取与增广
做这个项目,最现实的问题是没有现成的大规模数据集。公开的驾驶行为数据集能找到一些,但通常存在类别不均衡、场景单一、图像质量参差不齐的问题。我的做法是“公开数据打底、自采数据补缺、增强数据兜底”。
自采数据不用太多,关键是针对性。拿手机对着自己录一段打哈欠、眯眼、看手机、抽烟的视频,按帧抽出来,也就五六百张图,但能把模型在真实驾驶光线下的表现提升一个档次。采集的时候注意戴上帽子、不戴帽子、戴眼镜、不戴眼镜多来几组,让模型看到尽可能多的视觉特征。
增广环节,Ultralytics内置的增强参数就够用。训练时重点开Mosaic和MixUp,它们能模拟物体之间的遮挡和混叠,对提升模型在复杂场景下的鲁棒性非常有帮助。HSV扰动也建议开,这样模型不会对特定色温的光线过拟合。标注工具就用labelImg,框出目标后导出YOLO格式(类别ID加上归一化的中心点坐标和宽高)。
数据量上,每一类至少准备800到1000张标注图,如果某些类别(比如抽烟)只有五六百张,靠增广也能撑起来。要注意类别均衡,睁眼和正常驾驶作为大类可以多一点,闭眼和打哈欠不能太少,否则模型会被大类带偏。
4.2 训练配置与参数说明
训练前要写一个dataset.yaml,告诉模型数据在哪里、类别是什么:
path: ./datasets/driver train: images/train val: images/val names: 0: open_eye 1: closed_eye 2: yawning 3: phone_use 4: smoking 5: normal_driving然后直接跑训练:
yolo detect train data=dataset.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0几个重要参数说明一下。model=yolov8s.pt表示加载COCO预训练权重继续训练,这是迁移学习的标准操作,能大幅加速收敛、提升精度。epochs=100对这个小规模数据集来说足够,配合早停机制(patience=15),一般在五六十轮就稳定了。batch要看显存大小,16G显存跑s模型配batch=16比较舒服。
训练过程中重点关注四个指标:box_loss(定位损失)、cls_loss(分类损失)、mAP50(IoU为0.5时的平均精度)、mAP50-95(更严格的平均精度)。mAP50达到0.9以上并不稀奇,更值得关注的是mAP50-95,它反映了模型在不同IoU阈值下的稳健性。
4.3 模型评估与导出
训练完成后,runs/detect/train/weights/目录下会有best.pt和last.pt。best.pt是验证集表现最好的权重,测试、部署都用它。验证一下:
yolo detect val model=weights/best.pt data=dataset.yaml会输出详细的分类别P、R、mAP50、mAP50-95报告。拿到报告后,重点看闭眼和打哈欠这两个类别的召回率。如果召回率偏低,说明模型把不少正样本漏掉了,要么加数据,要么把置信度阈值调低。如果某一类精度偏低,说明误检太多,检查一下增广参数是否过于激进。
部署到摄像头实时检测时,还可以做两个加速处理。一是推理时设置half=True开启FP16半精度推理,显存占用降一半,速度还能快一截;二是把模型导出成ONNX或TensorRT。对这个项目而言,PyTorch原生就够用,ONNX导出主要用于边缘设备迁移。
5. 三种检测模式与语音提醒的完整落地
5.1 图像模式:最简单也最容易出效果
图像模式是三种模式里最直白的,适合快速验证模型效果和调参。逻辑就是读图、推理、画框、保存。
import cv2 from ultralytics import YOLO model = YOLO("weights/best.pt") frame = cv2.imread("test.jpg") results = model(frame, conf=0.4, iou=0.45, imgsz=640, verbose=False) annotated = results[0].plot() cv2.imwrite("output.jpg", annotated)results[0].plot()是Ultralytics自带的可视化方法,会把检测框、类别名、置信度都画好。但注意,这个绘制函数画的是带有英文类别名的框,如果想在工程里显示中文标签,需要自己用OpenCV重写绘制逻辑,把类别ID映射成中文再调用cv2.putText。
5.2 视频文件模式:处理时序的关键战场
视频模式是整套系统的核心,因为有了时间轴,疲劳判定和分心判定才能发挥作用。基础流程:
cap = cv2.VideoCapture("demo.mp4") fps = cap.get(cv2.CAP_PROP_FPS) while cap.isOpened(): ret, frame = cap.read() if not ret: break results = model(frame, conf=0.4, iou=0.45, imgsz=640, verbose=False)[0] # 提取各类别的检测框和置信度 # 更新FatigueJudge和分心判定器 # 按需触发语音提醒 # 画面叠加状态和报警信息 cap.release()视频模式里最关键的参数是跳帧策略。如果视频是30FPS,而每帧都要做完整的检测和行为判定,GPU占用高且CPU也会波动。实际操作中,我习惯每隔一帧检测一次(即15FPS的检测率)。疲劳判定依赖的是连续闭眼的时间,不是帧率,隔帧检测完全够用,还能把速度提一倍。
另一个容易踩的坑是视频坐标问题。很多视频文件的路径里有中文或空格,OpenCV在Windows上可能读不出来,建议统一用英文路径,或者在代码里做好转码处理。
5.3 摄像头实时模式:性能和延迟的博弈
摄像头模式在代码上只是把输入源从文件路径换成设备ID:
cap = cv2.VideoCapture(0) # 0代表默认摄像头但工程上要处理的问题完全不同。首先,摄像头的原始分辨率往往很高(比如1920x1080),直接喂给模型会非常吃力。我的做法是先拿到原始帧,用cv2.resize压缩到1280x720或960x540,再进行检测和绘制。UI显示可以放大回原分辨率,检测分辨率保持小尺寸,这样实时性会好很多。
其次,摄像头下的行为判定要更加谨慎,因为画面里的背景、光线会持续变化。我记得有次测试,摄像头对着窗户,午后阳光西斜,窗帘的阴影投到了驾驶员脸上,模型把阴影误判成了闭眼,疲劳报警频发。后来加了EAR宽高比过滤,顺带在代码里做了ROI限定:只对画面中心区域的人脸框做闭眼判定,远处背景里路过的人脸直接忽略。这两个改动把误报率降了一个数量级。
摄像头模式还要做多线程。检测推理运行在GPU线程,语音提醒运行在独立的线程,UI画面更新在第三个线程。否则语音播报一旦卡住,检测循环会全线阻塞。
5.4 语音提醒模块的三种可靠实现
语音提醒是这个项目区别于普通检测Demo的灵魂。我最推荐pyttsx3,它离线、轻量、跨平台:
import pyttsx3 engine = pyttsx3.init() engine.setProperty("rate", 180) engine.setProperty("volume", 0.9) def speak(text): engine.say(text) engine.runAndWait()但这里有个大坑:runAndWait是阻塞的,如果直接在检测循环里调用,卡顿会非常明显。正确做法是把报警文本扔进一个队列,由语音线程消费:
import threading import queue alert_queue = queue.Queue() def alert_worker(): while True: text = alert_queue.get() engine.say(text) engine.runAndWait() threading.Thread(target=alert_worker, daemon=True).start() # 检测到危险行为时: alert_queue.put("检测到疲劳驾驶,请立即休息")如果对语音音色有要求,还可以使用预录音频文件,用pygame.mixer或playsound播放下载好的MP3提醒音。这种方式最稳定,且可以选择任何想要的声音。语音播报的内容也建议分级:疲劳提醒、分心提醒、危险升级警告各用不同的文案和音量。
6. 常见问题与排查技巧实录
6.1 慢、卡、掉帧问题实战排查
摄像头实时检测时觉得卡,先别急着换模型,按这个顺序排查。第一,看GPU占用率;nvidia-smi显示占用率低且CPU爆满,说明推理根本没用上GPU,多半是PyTorch安装成了CPU版。第二,检查输入尺寸;代码里imgsz如果还保持在1280,模型计算量是640的四倍,卡是正常的。第三,检查有没有在循环里做重复加载;有人把YOLO("weights/best.pt")写进了while循环,每帧加载一次模型,这种代码神仙来了也卡。
如果显卡实在不行,还有两个降级方案:换YOLOv8n模型、隔帧检测从隔1帧改成隔3帧。效果会有一点损失,但至少能保持流畅。
6.2 模型漏检误检问题
漏检和误检的问题要分开看。漏检主要是小目标检测不稳定,比如远处的人脸只有几十像素大。解决办法:提高输入分辨率到768或896,或者专门对小目标区域做二次放大裁剪后再检测。误检的原因更杂,最典型的是类别混淆,比如把握方向盘的手误认为拿手机的手。这种问题光调阈值没用,真正有效的办法是检查训练集的标注质量,看是不是“手持手机”和“正常握方向盘”的视觉区分度不够。如果实在分不清,可以考虑把“hand_on_wheel”单独拉出来做负样本,模型才知道这两者需要区分。
6.3 运行环境与集成问题速查
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| torch.cuda.is_available()返回False | PyTorch版本和CUDA驱动不匹配 | 按nvidia-smi显示的版本重新安装对应PyTorch |
| 摄像头画面是黑的 | 摄像头被其他应用占用或没权限 | 关闭占用程序,检查设备管理器权限 |
| 语音提醒没声音 | pyttsx3初始化失败或声道占用 | 换用预录音频播放方案 |
| 中文路径读不了视频 | OpenCV在Windows下对中文路径支持差 | 文件改英文名,或用np.fromfile读入再解码 |
| 训练到一半显存溢出 | batch太大或分辨率过高 | 减小batch、开启梯度累积,或换更小的模型 |
| 视频播放时快时慢 | 检测耗时波动导致帧率不稳定 | 用固定间隔读取帧,检测和显示分离线程 |
实际上,你会发现这个项目最难的部分不是YOLOv8训练,也不是PyTorch编程,而是“把视觉检测结果转化为可靠的驾驶状态判断”这个过程。阈值设松了误报骚扰司机,设紧了漏报失去意义;检测频率设快了硬件扛不住,设慢了错过关键时间窗口。我个人的体会是,先拿一段自己录的十几分钟驾驶视频作为“金标准”,每改一次参数就跑一遍,对比报警时间和误报次数,来回几轮之后就能摸到一套适合自己场景的参数组合。
这个系统的扩展性也很好,后续想加疲劳分级的脑电波信号也好、想接入车载CAN总线获取方向盘转角数据也好,现有架构里加一个传感器模块、再并一个判定器就行。项目的骨架稳了,后续的功能都像往上垒积木,不会推翻重来。