
之前在做一个驾驶行为分析相关的课程项目时需要在车载摄像头画面里实时判断司机是否“单手开车”。一开始以为直接用现成的 YOLO 模型检测“手”就够了真正做起来才发现手检测出来了怎么判断“正在驾驶”、怎么排除玩手机、摸方向盘、伸手拿东西这些干扰情况才是真正的难点。这篇文章就把这套“基于 YOLO 的单手危险驾驶行为识别系统”从零到落地完整拆解一遍。内容覆盖数据集准备、YOLO 模型训练、行为判断算法、实时推理、误报抑制和常见坑点。不管是做人工智能毕设还是想在公司项目里做驾驶行为分析都可以照着搭一套。1. 什么是单手危险驾驶行为识别系统1.1 系统的核心任务先说清楚到底要识别什么。“单手驾驶”在交管和驾驶安全领域属于危险驾驶行为的一种。司机在驾驶过程中如果右手离开方向盘去操作中控、拿水杯、接电话或者左手长时间搭在车窗上都会显著降低对车辆的操控能力。尤其在高速、弯道或紧急避障场景下单手操控的风险会被放大。这套系统的任务是从一段视频流车载摄像头画面中实时检测出“司机的手在哪里”“方向盘在哪里”然后根据两者的位置关系判断当前是否处于单手驾驶状态。更严谨一点说系统要区分三种状态正常驾驶双手都在方向盘上或者至少双手都在方向盘区域附近。单手驾驶只有一只手在方向盘上另一只手脱离方向盘。双手脱离方向盘两只手都不在方向盘上例如看手机、拿东西、双手离开方向盘。为什么还要区分“双手脱离”因为从安全角度来说双手脱离方向盘比单手驾驶更严重。如果系统能把这两类都识别出来输出给上层业务做报警价值会大很多。1.2 为什么选择 YOLO目前做目标检测的算法很多Faster R-CNN、SSD、YOLO 系列、CenterNet 等。但在真实驾驶场景里车载终端的算力有限摄像头帧率一般要保持在 10~30 FPS检测实时性要求很高。YOLO 的核心优势是“单阶段检测”一次前向推理直接输出目标类别和边框位置速度和精度比较均衡。相比两阶段检测器先提取候选框再分类YOLO 更适合部署在边缘设备和实时视频流场景中。从毕设或工程落地的角度看YOLO 生态也非常友好公开预训练权重丰富迁移学习成本低。标注数据格式简单txt 归一化坐标处理方便。训练、验证、导出部署全链路工具齐全。社区资料多踩坑容易找到解决方案。1.3 系统整体架构整套系统可以拆成四层数据层车载摄像头视频流 / 离线视频文件 检测层YOLO 模型检测方向盘、左手、右手 逻辑层根据检测框位置关系判断驾驶状态 输出层可视化标注、告警日志、截图保存逻辑层是本文的重点之一。模型只负责“找出目标”不负责“理解行为”。行为判断需要我们自己设计规则这也是很多初学者容易忽略的地方。2. 环境准备与项目结构2.1 开发环境本文示例以 Windows 10/11 Python 3.9 为例LinuxUbuntu环境同理。操作系统Windows 10/11 或 Ubuntu 20.04Python3.8 或 3.9 或 3.10CUDA建议 11.7 以上如果使用 GPU 训练PyTorch根据 CUDA 版本安装例如 1.13 或 2.0目标检测框架YOLOv5 或 YOLOv8 都可以本文示例以 YOLOv8 为主标注工具LabelImg 或 X-AnyLabeling版本需要根据你的实际环境调整。不要盲目照抄网上某篇文章的安装命令先确认自己的显卡驱动版本和 CUDA 版本再选择对应的 PyTorch 版本。查看 GPU 和 CUDA 环境的命令nvidia-smi python -c import torch; print(torch.__version__); print(torch.cuda.is_available())如果 GPU 不可用也可以先用 CPU 训练一个小数据集验证整个流程只是训练速度会慢很多。2.2 安装依赖以 YOLOv8 为例使用 ultralytics 库pip install ultralytics如果使用的是 YOLOv5官方仓库安装方式如下git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt不管用哪个版本建议都创建独立虚拟环境避免污染系统中的其他 Python 包。python -m venv venv venv\Scripts\activate # Windows source venv/bin/activate # Linux/macOS2.3 项目目录结构推荐使用下面这样的目录结构dangerous-driving-detection/ ├── dataset/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── data.yaml ├── models/ ├── runs/ │ └── detect/ ├── detect.py ├── behavior_judge.py ├── utils.py └── requirements.txt3. 数据集准备与标注3.1 数据集方案做驾驶行为识别数据集有两种来源。第一种是直接使用公开数据集例如 State Farm Distracted Driver Detection凯歌竞赛数据集。这个数据集包含十类驾驶行为比如正常驾驶、发短信、打电话、调中控等。优点是数据量大、类别标准。缺点是图片不是严格的“方向盘 手”目标检测标注格式需要转换成 YOLO 格式才能用。第二种是自采数据。用一个摄像头从驾驶位侧方或者斜前方拍摄司机上半身和方向盘区域录制不同场景下正常驾驶、单手驾驶、双手脱离方向盘的视频然后抽帧标注。对毕设来说建议采用“公开数据集预训练 自采数据微调”的组合方案。先用公开数据把模型训练到能稳定检测手和方向盘再用自采数据针对自身摄像头角度做微调效果会好很多。3.2 类别设计这里先想清楚检测类别。一种做法是检测“手”和“方向盘”两个类别0: steering_wheel 1: hand这种方案优点是类别简单、标注快但只能判断“有手在方向盘附近”无法区分左手和右手。另一种做法是直接检测三类0: steering_wheel 1: left_hand 2: right_hand左手右手的标注成本更高但行为判断可以做到更细比如只靠右手驾驶时到底是左手脱离还是右手脱离可以准确判断。本文推荐使用三类方案理由是行为判断逻辑会更严格、更有说服力答辩/项目汇报时也更有亮点。3.3 使用 LabelImg 标注LabelImg 是常用的 YOLO 格式标注工具安装方式pip install labelimg启动labelimg在软件中设置标注格式选择 YOLO打开图片目录创建类别steering_wheel、left_hand、right_hand对每一张图片画框并选择类别标注完成后每张图片对应一个 txt 文件内容格式如下0 0.5234375 0.5763889 0.2187500 0.2083333 1 0.6250000 0.4861111 0.0937500 0.1250000 2 0.4320312 0.5208333 0.1093750 0.1527778每行代表一个目标第一个数字是类别 id。后四个数字分别是归一化中心点 x、y、宽度 w、高度 h。取值范围都在 0~1 之间用像素坐标除以图片宽高得到。3.4 数据集配置文件在数据集根目录创建data.yamlpath: dataset train: images/train val: images/val nc: 3 names: 0: steering_wheel 1: left_hand 2: right_hand注意path建议使用绝对路径或相对于配置文件所在目录的路径。如果训练时报错找不到图片优先检查这里。4. 基于 YOLO 的目标检测模型训练4.1 模型选型目前毕设和工业项目中使用最多的还是 YOLOv5 和 YOLOv8。YOLOv5成熟稳定文档多部署资料丰富适合求稳。YOLOv8内置 API 更简洁支持实例分割、姿态估计等扩展训练代码写起来更少。本文示例以 YOLOv8 为例因为 API 调用更直观。代码中涉及版本差异的地方会特别说明。4.2 训练命令训练前先下载预训练权重。yolo detect train modelyolov8n.pt datadataset/data.yaml epochs100 imgsz640 batch8参数含义modelyolov8n.pt使用 YOLOv8n 预训练权重。n 是 nano 版本模型最小最快适合毕设快速验证如果精度不够可以换成yolov8s.pt或yolov8m.pt。datadataset/data.yaml数据集配置。epochs100训练轮数。imgsz640输入图片分辨率。640 是 YOLO 最常用的分辨率训练和推理保持一致效果最好。batch8批大小根据显存调整。显存不够就调小到 4 或 2。如果使用 YOLOv5命令类似python train.py --data dataset/data.yaml --weights yolov5s.pt --epochs 100 --batch-size 8 --img 6404.3 训练输出与评估指标训练完成后runs/detect/train目录下会生成多个文件weights/best.pt验证集效果最好的权重。weights/last.pt最后一轮权重。results.png训练过程的 loss 曲线和指标曲线。confusion_matrix.png混淆矩阵。重点关注的指标Precision查准率预测为某类别的框中真正属于该类别的比例。Recall查全率真实存在的该类目标中被正确检测出来的比例。mAP50IoU 阈值为 0.5 时的平均精度最常用的评估指标。mAP50-95从 0.5 到 0.95 不同 IoU 阈值的平均精度更严格。对于“手”这种小目标mAP50 达到 0.85 以上、mAP50-95 达到 0.6 以上基本够用了。但如果实际场景中漏检严重还要进一步优化。4.4 模型调优方向在驾驶室场景中最常见的两个问题是手部目标小、方向盘与手的遮挡重叠严重。调优可以从这几个方向入手数据层面增加不同光照、不同坐姿、不同体型的样本对图片做随机裁剪、旋转、马赛克增强。模型层面选择更大的输入分辨率如 640 - 768但推理速度会下降。训练层面调整 anchor 或使用更高版本的 YOLO 内置 anchor-free 结构对小目标更友好。后处理层面调低置信度阈值允许更多候选框交给逻辑层过滤。5. 行为识别逻辑与告警机制模型训练完成后系统已经可以检测出方向盘、左手、右手三类目标。接下来要做的是行为判断根据检测框的位置关系判断当前驾驶状态。5.1 行为判断的整体思路每一帧推理完成后得到类似下面的结果目标1类别 left_hand置信度 0.92坐标 (x1, y1, x2, y2) 目标2类别 right_hand置信度 0.87坐标 (x1, y1, x2, y2) 目标3类别 steering_wheel置信度 0.95坐标 (x1, y1, x2, y2)判断逻辑简单说就是计算每只手和方向盘检测框的位置重叠关系。在手部目标可靠的情况下可以采用“中心点是否落在方向盘框内”作为判断依据左手中心点落在方向盘框内记为left_on_wheel True。右手中心点落在方向盘框内记为right_on_wheel True。双手都在方向盘上正常驾驶。只有一只手在方向盘上单手驾驶。双手都不在方向盘上双手脱离方向盘。这种方案简单直观也方便在 PPT 中画判断流程图。5.2 行为判断代码实现创建一个behavior_judge.py代码如下# 文件路径behavior_judge.py from dataclasses import dataclass from typing import List, Tuple dataclass class DetectedObject: 单个检测结果的简单封装 cls_id: int conf: float box: Tuple[float, float, float, float] # x1, y1, x2, y2 def point_in_box(px: float, py: float, box: Tuple[float, float, float, float]) - bool: 判断点 (px, py) 是否在检测框内 box: x1, y1, x2, y2 x1, y1, x2, y2 box return x1 px x2 and y1 py y2 def compute_iou(box_a: Tuple[float, float, float, float], box_b: Tuple[float, float, float, float]) - float: 计算两个检测框的 IoU交并比用于判断手部与方向盘的重叠程度 ax1, ay1, ax2, ay2 box_a bx1, by1, bx2, by2 box_b inter_x1 max(ax1, bx1) inter_y1 max(ay1, by1) inter_x2 min(ax2, bx2) inter_y2 min(ay2, by2) inter_w max(0, inter_x2 - inter_x1) inter_h max(0, inter_y2 - inter_y1) inter_area inter_w * inter_h area_a (ax2 - ax1) * (ay2 - ay1) area_b (bx2 - bx1) * (by2 - by1) union_area area_a area_b - inter_area if union_area 0: return 0.0 return inter_area / union_area def judge_driving_state(detections: List[DetectedObject], cls_names: dict, center_threshold: float 0.5, iou_threshold: float 0.10) - str: 根据检测结果判断驾驶状态。 返回 normal - 双手都在方向盘上 single_hand - 单手驾驶 hands_off - 双手脱离方向盘 unknown - 关键目标缺失无法判断 # 从检测结果中提取方向盘和左右手 steering_wheel_box None left_hand_box None right_hand_box None for det in detections: name cls_names.get(det.cls_id, ) if name steering_wheel: # 取置信度最高的方向盘作为主方向盘 if steering_wheel_box is None or det.conf 0: steering_wheel_box det.box elif name left_hand: left_hand_box det.box elif name right_hand: right_hand_box det.box # 缺失关键目标时返回 unknown if steering_wheel_box is None: return unknown left_on_wheel False right_on_wheel False # 方式一利用手中心点是否落在方向盘框内 if left_hand_box is not None: lx (left_hand_box[0] left_hand_box[2]) / 2.0 ly (left_hand_box[1] left_hand_box[3]) / 2.0 left_on_wheel point_in_box(lx, ly, steering_wheel_box) if right_hand_box is not None: rx (right_hand_box[0] right_hand_box[2]) / 2.0 ry (right_hand_box[1] right_hand_box[3]) / 2.0 right_on_wheel point_in_box(rx, ry, steering_wheel_box) # 方式二利用手框与方向盘框的 IoU 做二次确认 if left_hand_box is not None and not left_on_wheel: iou compute_iou(left_hand_box, steering_wheel_box) if iou iou_threshold: left_on_wheel True if right_hand_box is not None and not right_on_wheel: iou compute_iou(right_hand_box, steering_wheel_box) if iou iou_threshold: right_on_wheel True if left_on_wheel and right_on_wheel: return normal if left_on_wheel or right_on_wheel: return single_hand return hands_off代码说明DetectedObject是检测结果的简单封装方便后面接入 YOLO 输出。point_in_box判断手中心点是否在方向盘框内。compute_iou计算手框和方向盘框的重叠比例。由于手部通常部分被方向盘遮挡IoU 阈值不要设置太高这里取 0.10 左右即可。judge_driving_state先提取目标再判断左右手是否“在方向盘上”最后返回状态。5.3 状态平滑与误报抑制单帧判断直接使用会有明显的抖动问题。比如手刚好在方向盘边缘来回移动时状态会在normal和single_hand之间快速跳变。解决思路是用“连续帧投票”或“延时触发”机制。这里实现一个简单的StateTracker# 文件路径utils.py from collections import deque class StateTracker: 基于滑动窗口的状态平滑器。 仅当最近 N 帧中超过 M 帧判定为同一状态时才确认该状态。 def __init__(self, window_size: int 10, confirm_threshold: float 0.6): self.window_size window_size self.confirm_threshold confirm_threshold self.history deque(maxlenwindow_size) def update(self, state: str) - str: self.history.append(state) if len(self.history) self.window_size: return normal # 统计最近窗口内各个状态的占比 counts {} for s in self.history: counts[s] counts.get(s, 0) 1 # 如果unknown占比过高忽略该窗口 unknown_ratio counts.get(unknown, 0) / len(self.history) if unknown_ratio 0.5: return normal # 找出占比最高的状态 best_state max(counts, keycounts.get) best_ratio counts[best_state] / len(self.history) if best_ratio self.confirm_threshold and best_state ! unknown: return best_state return normal在实时推理循环中每次得到单帧状态后调用tracker.update(state)拿到的才是最终状态。这样可以避免单帧误判导致误报。5.4 告警输出与日志记录检测到single_hand或hands_off之后系统需要输出告警。这里提供两种方式控制台打印 截图保存。可选调用语音提示Windows 下可以使用win32com.client或者使用pygame播放提示音。截图保存代码# 文件路径utils.py import cv2 import os from datetime import datetime def save_alert_frame(frame, alert_label: str, save_dir: str alerts) - str: 保存告警帧到本地返回保存的文件路径。 os.makedirs(save_dir, exist_okTrue) ts datetime.now().strftime(%Y%m%d_%H%M%S_%f) filename f{alert_label}_{ts}.jpg filepath os.path.join(save_dir, filename) cv2.imwrite(filepath, frame) return filepath告警逻辑可以这样写# 文件路径utils.py def handle_alert(frame, final_state: str, save_dir: str alerts): if final_state normal: return label { single_hand: Single-Hand Driving, hands_off: Hands Off Wheel, }.get(final_state, final_state) save_alert_frame(frame, label, save_dir) print(f[ALERT] {label} detected at {datetime.now().strftime(%H:%M:%S)})这里有一个工程上的小建议不要连续对同一状态重复保存大量截图。可以在告警触发后加“冷却时间”例如 3 秒内不再重复告警避免日志爆炸。6. 实时推理与可视化演示6.1 视频流读取与推理将以上模块串联起来写一个完整的实时推理脚本detect.py。# 文件路径detect.py import cv2 from ultralytics import YOLO from behavior_judge import DetectedObject, judge_driving_state from utils import StateTracker, handle_alert # 类别名称与训练时的配置保持一致 CLS_NAMES { 0: steering_wheel, 1: left_hand, 2: right_hand, } # 颜色映射用于可视化 COLOR_MAP { steering_wheel: (0, 255, 0), left_hand: (255, 0, 0), right_hand: (0, 0, 255), } def draw_detections(frame, detections): 在图像上绘制检测框和类别标签 for det in detections: name CLS_NAMES.get(det.cls_id, unknown) x1, y1, x2, y2 [int(v) for v in det.box] color COLOR_MAP.get(name, (255, 255, 255)) cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2) label f{name} {det.conf:.2f} cv2.putText(frame, label, (x1, max(0, y1 - 8)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) return frame def build_detections(result, conf_threshold: float 0.5): 将 YOLOv8 的检测结果转换为 DetectedObject 列表。 detections [] boxes result.boxes if boxes is None: return detections for i in range(len(boxes)): cls_id int(boxes.cls[i].item()) conf float(boxes.conf[i].item()) if conf conf_threshold: continue x1, y1, x2, y2 boxes.xyxy[i].tolist() detections.append(DetectedObject(cls_idcls_id, confconf, box(x1, y1, x2, y2))) return detections def main(video_path: str test.mp4): model YOLO(runs/detect/train/weights/best.pt) tracker StateTracker(window_size10, confirm_threshold0.6) cap cv2.VideoCapture(video_path) if not cap.isOpened(): print(无法打开视频文件:, video_path) return while True: ret, frame cap.read() if not ret: break # YOLO 推理 result model.predict(frame, imgsz640, verboseFalse)[0] detections build_detections(result, conf_threshold0.45) # 行为判断 single_state judge_driving_state(detections, CLS_NAMES) final_state tracker.update(single_state) # 绘制检测框 frame draw_detections(frame, detections) # 状态显示 state_text { normal: Normal, single_hand: Single-Hand Driving, hands_off: Hands Off Wheel, }.get(final_state, Unknown) state_color (0, 200, 0) if final_state normal else (0, 0, 255) cv2.putText(frame, state_text, (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1.2, state_color, 3) # 告警处理 handle_alert(frame, final_state) # 显示画面 cv2.imshow(Driving Behavior Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows() if __name__ __main__: main(test.mp4)运行方式python detect.py也可以把video_path换成摄像头设备号读取摄像头cap cv2.VideoCapture(0) # 0 表示第一个摄像头6.2 可视化效果说明运行后画面中会出现三类检测框绿色框方向盘。蓝色框左手。红色框右手。画面左上角显示当前驾驶状态红色表示告警状态绿色表示正常状态。告警时会保存当前画面到alerts目录。如果你希望输出更完整的 UI可以把这段推理逻辑嵌入 PyQt/PySide 桌面程序把画面显示在 QLabel 上把状态输出到 QTableWidget 中。不过作为毕设演示OpenCV 自带窗口已经够用。6.3 离线视频测试建议先用录制的离线视频测试不要直接上车测试。录制测试视频时最好覆盖这些场景正常双手驾驶不少于 2 分钟。右手单手驾驶 1 分钟。左手单手驾驶 1 分钟。双手脱离方向盘操作中控 30 秒。模拟夜间暗光环境 30 秒。这样可以快速暴露模型在角度、亮度变化下的漏检问题。7. 常见问题与排查思路在实际训练和部署过程中最容易遇到以下问题。问题现象常见原因解决思路训练 loss 不下降学习率过大/过小、数据集太小、标签错乱检查 data.yaml 中 nc 和 names 是否匹配使用预训练权重适当调整学习率手部目标漏检严重手部目标较小、样本不足增加手部样本提高输入分辨率 imgsz降低置信度阈值做更多数据增强方向盘和手互相遮挡目标重叠导致 NMS 抑制提高标注质量训练时开启更大 mosaic 增强逻辑层使用中心点和 IoU 双重判断状态频繁跳变没有做帧间平滑增加 StateTracker使用连续帧投票机制推理速度很慢模型太大、输入分辨率太高、无 GPU换成 YOLOv8n/YOLOv5s降低 imgsz用 TensorRT 或 ONNX 加速告警截图刷屏没有冷却时间增加冷却机制例如 3 秒内只告警一次视频中某帧方向盘被身体遮挡遮挡严重导致检测失败返回 unknown 状态多帧平滑考虑多摄像头方案这里特别提醒一个问题很多初学者把“置信度阈值”调得很高认为阈值越高越准确。实际上在驾驶室场景中阈值过高会大量漏检手部小目标导致行为判断变成unknown。建议置信度阈值设置在 0.35~0.5 之间先保证召回再交给逻辑层做过滤。8. 模型导出与部署扩展8.1 导出 ONNX毕设答辩时经常被问到“能不能部署到嵌入式设备”。如果能把模型导出为 ONNX 格式再转 TensorRT 或 OpenVINO说服力会强很多。YOLOv8 导出 ONNX 很简单yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640导出后可以使用 onnxruntime 推理pip install onnxruntime-gpu8.2 使用 ONNX Runtime 推理# 文件路径infer_onnx.py import cv2 import numpy as np import onnxruntime as ort def infer(model_path: str, image_path: str): session ort.InferenceSession(model_path, providers[CUDAExecutionProvider, CPUExecutionProvider]) img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_resized cv2.resize(img, (640, 640)) img_array img_resized.astype(np.float32) / 255.0 img_array np.transpose(img_array, (2, 0, 1)) img_array np.expand_dims(img_array, axis0) input_name session.get_inputs()[0].name outputs session.run(None, {input_name: img_array}) # outputs[0] 的形状为 (1, 84, 8400)需要做后处理解析 # 不同 YOLO 版本的输出格式略有差异这里只演示推理入口 print(ONNX inference output shape:, outputs[0].shape)注意ONNX 输出的后处理解析比 PyTorch 推理稍微复杂一点。如果是做工程部署建议使用ultralytics自带的导出和推理封装或者参考官方文档解析输出。这里不再展开因为毕设阶段通常用 PyTorch 推理演示就足够了。8.3 进一步加速如果目标是部署到 Jetson 或工控机主流方案是转 TensorRT 或 OpenVINO。推理时可以把分辨率控制在 640并且关闭后处理中的冗余逻辑帧率可以显著提升。需要注意TensorRT 引擎与 GPU 型号、TensorRT 版本强相关换机器需要重新导出。不要相信“一个引擎文件到处用”的说法。9. 最佳实践与工程建议9.1 数据采集的合规与安全这一点必须重视。采集驾驶室视频时需要获得驾驶员本人的知情同意。测试系统时尽量使用模拟驾驶环境、录制的视频或封闭测试场地不要在实际道路上一边开车一边测试模型。系统只能作为驾驶辅助预警不能替代驾驶员的安全判断。这类项目在毕设答辩时老师很可能会问“你的系统有没有考虑隐私和伦理问题”提前准备好合规方案是加分项。9.2 模型训练的数据质量优先级高于模型结构很多同学一开始纠结“用 YOLOv5 还是 YOLOv8”“用 s 还是 m”实际在驾驶室场景中数据质量对效果的影响远大于模型结构。重点关注方向盘是否被驾驶员身体大面积遮挡。手的形态是否多样握方向盘、搭在方向盘、靠近方向盘。光照是否覆盖白天、夜间、逆光。摄像头角度是否与最终部署角度一致。如果摄像头安装角度不同即使训练集再大迁移效果也会打折扣。9.3 行为判断规则要可解释毕设答辩时老师会特别关注行为判断逻辑。建议准备一张“目标检测结果 - 状态判定规则”的表格左手在方向盘右手在方向盘判定结果是是normal是否single_hand否是single_hand否否hands_off这套规则简单清晰不需要复杂的模型也方便向非技术背景的人解释。9.4 日志与告警的可维护性在真实项目中告警数据要落到数据库或者结构化日志里。毕设阶段可以先用文本日志但建议设计好字段timestamp, state, left_hand_conf, right_hand_conf, steering_wheel_conf, image_path后续做统计分析时这些字段可以直接用来画驾驶行为分布图也可以作为“基于 YOLO 危险驾驶行为分析”的亮点材料。9.5 关于实时性与部署设备如果是在普通笔记本上运行 YOLOv8n640 分辨率速度通常可以达到 20~30 FPS 以上。实际帧率取决于 CPU/GPU 性能。如果在纯 CPU 环境运行可以尝试使用更小的模型yolov8n。推理帧率控制在 10 FPS 就够用不需要每帧都推理。使用隔帧检测每 2 帧检测一次中间帧直接沿用上一帧结果。但需要注意隔帧检测可能会导致漏掉快速变化的动作建议只在性能不足时使用。10. 总结与下一步学习方向这篇文章从项目背景、数据集标注、YOLO 模型训练、行为判断逻辑、实时推理到告警机制完整实现了一个“基于 YOLO 单手危险驾驶行为识别系统”。接下来你可以按照下面几个方向继续扩展把行为状态接入微信/钉钉机器人告警。将模型导出成 ONNX/TensorRT 部署到 NVIDIA Jetson 设备。使用 ByteTrack 或 DeepSORT 对司机手部做长时间轨迹跟踪识别“长时间单手驾驶”这种更细粒度的行为。引入人脸关键点或姿态估计模型增加“闭眼”“打哈欠”“低头看手机”等多维度疲劳驾驶识别。如果你正在做相关毕设或工程实践建议先从“录制 5 分钟测试视频 - 训练一个最小可用的检测模型 - 跑通实时推理”开始先把链路跑通再逐步调优。拿不准的地方优先用数据说话多标注、多测试、多记录比盲目调整模型参数有效得多。