
简介Python违规驾驶行为识别系统源码是一套面向毕业设计场景的完整代码工程。项目基于Python开发围绕驾驶行为识别任务提供从数据读取、特征处理、模型训练到推理检测的完整闭环适合有Python基础的高校学生作为课设或毕设框架。压缩包共128个文件约64.93MB其中81个py源文件为核心代码涵盖算法与程序入口权重文件保存了训练好的模型可直接加载使用脚本用于自动化执行训练或检测流程文档提供环境配置说明有助于快速上手。已有1061人学习/下载说明其在毕设群体中获得一定认可。透过这份源码读者能获得一套可运行的驾驶行为识别示例理解模型构建与调用方式还可在现有基础上修改数据集、调整参数、替换网络结构进行二次开发清晰的目录结构对毕业论文的系统设计章节也有直接参考价值。1. 一个摄像头盯几十路视频违规驾驶行为识别系统到底在解决什么你手上有一个类似的需求车队要监控司机是否打电话、抽烟、打瞌睡考试中心要自动为科目三评判抓取违规动作。人工盯几十路视频根本不划算于是这套“Python违规驾驶行为识别系统源码”就成了毕设或产品原型的常见切入点。它的价值不是某一个模型多聪明而是把目标检测、人脸关键点和规则判定串成一条可解释的流水线——视频进事件和截图出。适合想用PyTorch或YOLO做计算机视觉方向毕设、又不希望结果只是撞大运的人。这篇笔记按我实际搭过的路径把选型、代码、训练和坑一次讲透。2. 选型先于编码检测关键点规则判定为什么是毕设的最优解2.1 三条路线怎么选从一张图分类到一帧视频定位违规驾驶行为识别最常被拿来做毕设的路线有3条我先说结论图级分类最容易出分但最容易在答辩时被问住目标检测可解释性最好关键点检测补充了“人在做什么”的语义三者按“检测关键点规则”组合才是适合做成系统源码的形态。第一条路线是“图级分类”。典型做法是拿公开的司机状态分类数据集比如合规驾驶、右手发短信、右手打电话、左手发短信、左手打电话、操作收音机、喝水、拿身后物品、整理头发、对话乘客这10类。把图片喂给ResNet或MobileNet训练快、指标漂亮但它是图级标签模型输出只是“这一帧是什么行为”拿不出“手机在哪只手里、手和耳朵距离多少”这种可解释证据。答辩时追问一句“误报怎么定位”整个系统就变成黑匣子。第二条路线是“目标检测”。用YOLO类模型输出司机、手机、烟、手等目标的bbox。好处是每个违规都能给出位置证据调试时画框一看就知道模型在学什么而且检测结果是单帧的可以挂在后续规则引擎上做事件判定。坏处是烟这类小目标本身难标难检纯靠检测容易漏和误报。第三条路线是“关键点检测”。用MediaPipe或OpenPose这类库直接提取眼、手、嘴的关键点坐标不算冷门。它最大优势是疲劳检测可以直接用EAR眼睛纵横比计算无需训练专门的疲劳分类器手和嘴的距离也能辅助判断打电话和抽烟。但关键点在暗光、遮挡下会抖得厉害单独用扛不住实际场景。所以我的方案是用目标检测负责“场景里有什么东西”用关键点提供“人的姿态和疲劳状态”再用一层规则判定把它们融合成违规事件。这套代码结构清楚每一层的边界都画得出来做毕设演示时也能一帧一帧拿出来讲。2.2 打电话、抽烟、疲劳三条核心规则怎么设计规则引擎是整个系统的灵魂。把两类模型的输出变成可触发的违规事件每个规则都要回答三个问题用什么作为证据、证据要连续多久、阈值定多少。打电话的常见判据是“手机与人手或耳朵的位置关系”。一种做法是计算手机bbox与人手bbox的IoUIoU大于0.3且持续3帧以上判为使用手机另一种做法是检测到手部关键点后计算手腕坐标到手机bbox中心的距离小于某个像素阈值。我一般把两种做法都保留因为不同摄像头角度下两个判据的可靠性会互换。抽烟的判据比较棘手香烟目标又小又是非刚性纯YOLO检烟误报率很高。常见做法是退一步检测手部关键点食指指尖附近与嘴部关键点的距离当“手在嘴边”且手部附近存在一个疑似香烟的小区域目标时才累积抽烟事件。这个设计牺牲了部分检测召回但换来的误报率大幅下降。疲劳用EAR公式EAR (||p2-p6|| ||p3-p5||) / (2 * ||p1-p4||)分别取上下眼睑6个点中的两组垂直距离和一个水平距离正常睁眼在0.25-0.35闭眼会掉到0.1以下。设定阈值EAR0.25且连续20帧按30fps约0.67秒记一次闭眼再统计连续3秒内的闭眼占比超过40%就触发疲劳事件。下面这张表是规则引擎的初始参数后续训练完模型再按实际视频调行为证据来源判定阈值连续帧数打电话手机bbox与手bbox的IoU大于0.3IoU 0.33帧打电话备用手腕关键点到手机bbox中心距离距离小于30像素5帧抽烟手部关键点接近嘴部附近烟雾区域距离小于40像素5帧疲劳EAR低于0.25闭眼占比超过40%0.25 / 40%20帧加3秒规则层统一穿过一个“防抖”函数任何事件必须在M帧内连续出现才输出。这是做行为识别最容易忽略、也最影响体验的细节。2.3 系统流程与代码分层让黑匣子变白盒子这个系统的数据流是一条单向流水线读视频帧、送入目标检测模型得到目标列表、送入关键点模型得到人脸和手部坐标、规则引擎按帧累积证据、事件成立后写JSON日志并保存截图。每一级只依赖上一级的输出不互相耦合。代码结构上我习惯分成四个模块detector.py负责YOLO推理并输出归一化的目标列表landmarker.py负责关键点提取和EAR计算rules.py放所有行为规则main.py只做视频读取、事件持久化和可视化。模型权重放在weights/目录日志输出到runs/events/截图归档到runs/snapshots/。这样分层带来的直接好处是换模型的时候不用碰规则改规则的时候不用动模型出问题时顺着日志往回查目标没检出还是规则没触发一目了然。后面的复现步骤就按这个结构来。3. 跑通最小系统从空环境到第一行推理代码3.1 环境准备先别急着写代码把Python环境钉住做这套系统最容易在环境上翻车。我的建议是先用conda建一个独立环境Python版本钉在3.10避免torch、mediapipe在3.11或3.12上的轮子滞后问题。如果你还没装Python先把vscode的Python环境配置好再回来我见过不少人在这一步花掉半天。conda create -n driver_behavior python3.10 -y conda activate driver_behavior pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python mediapipe numpy pandas matplotlib第一段命令装的是深度学习框架。--index-url指定了CUDA 11.8的轮子源如果你的显卡驱动支持CUDA 12也可以去掉这个参数直接装默认版本。没有独立显卡也能跑只是后续训练很痛苦。mediapipe的安装经常在依赖上冲突所以单独一行安装报错时也容易定位。注意conda环境解不出包时优先检查是否装了vscode的Python插件并把解释器切到conda环境里不要急着重装Python。3.2 第一行推理代码用YOLOv8检测手机目标先用YOLO的COCO预训练权重把检测链路跑通。COCO里有person类别id 0和cell phone类别id 67没有cigarette所以抽烟的检测要等第4章自己训练后替换权重。下面的代码读取一帧图片只保留人和手机两类目标from ultralytics import YOLO import cv2 model YOLO(yolov8n.pt) # 第一次运行会自动下载预训练权重 frame cv2.imread(sample.jpg) results model.predict( sourceframe, conf0.35, # 置信度阈值低于0.35的目标丢弃 iou0.45, # NMS交并比阈值越大越容易合并重叠框 classes[0, 67], # 只保留person(0)和cell phone(67) imgsz640, # 输入尺寸越大越准但越慢 devicecpu, # 有显卡改成 0 verboseFalse, ) boxes results[0].boxes for box in boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) label model.names[int(box.cls)] conf float(box.conf) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(frame, f{label} {conf:.2f}, (x1, y1 - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)conf和iou这两个参数直接影响误报和漏报。手机在车内视角下通常小于40x40像素conf定在0.35是平衡值如果误报多先提到0.5而不是换模型。imgsz640是速度和精度的默认平衡在CPU上跑到640已经接近极限想再快就降到480代价是小目标召回率下降。普通侧上方摄像头视角下这个配置足够验证全链路。3.3 关键点与EAR不训练也能算出疲劳状态疲劳检测通常用MediaPipe Face Mesh。它返回人脸468个关键点左眼取索引[33,160,158,133,153,144]右眼取[362,385,387,263,373,380]每组6个点计算EAR。这段代码把EAR计算封装成函数供规则引擎调用import mediapipe as mp import numpy as np LEFT_EYE [33, 160, 158, 133, 153, 144] RIGHT_EYE [362, 385, 387, 263, 373, 380] def eye_aspect_ratio(landmarks, indices): pts [(landmarks[i].x, landmarks[i].y) for i in indices] pts np.array(pts) # EAR (竖直距离A 竖直距离B) / (2 * 水平距离C) a np.linalg.norm(pts[1] - pts[5]) b np.linalg.norm(pts[2] - pts[4]) c np.linalg.norm(pts[0] - pts[3]) return (a b) / (2.0 * c)EAR的取值和人脸大小、摄像头距离无关因为它做的是归一化计算。正常睁眼普遍在0.28-0.35闭眼会降到0.12以下。阈值0.25是平均水平的经验值但它对单个人会有偏差这就是后面避坑章节说的“玄学问题”正式系统需要启动时标定。3.4 最小判定主循环把检测和EAR合并成事件把前面两块拼到一起主循环里同时跑目标检测和EAR用连续帧窗口判断疲劳事件。这个循环就是整个系统的骨架cap cv2.VideoCapture(demo.mp4) fps cap.get(cv2.CAP_PROP_FPS) EAR_THRESHOLD 0.25 FATIGUE_FRAMES int(fps * 0.8) # 连续闭眼0.8秒触发 ear_closed_count 0 while True: ret, frame cap.read() if not ret: break # 检测与关键点提取这里使用前面封装好的函数 detections detect_objects(frame) # YOLO推理见3.2 face_landmarks get_face_landmarks(frame) # MediaPipe推理 ear eye_aspect_ratio(face_landmarks, LEFT_EYE) if ear EAR_THRESHOLD: ear_closed_count 1 else: ear_closed_count 0 if ear_closed_count FATIGUE_FRAMES: write_event(fatigue, frame, {ear: ear}) ear_closed_count 0 # 触发后重置避免连续刷屏这里两个关键参数是EAR_THRESHOLD和FATIGUE_FRAMES。0.8秒的窗口是为了过滤正常眨眼——单次眨眼只有0.2-0.4秒不会误触发连续0.8秒闭眼基本可以确认是疲劳。detections在完整系统里会传给规则引擎做电话和抽烟判断这个最小循环里先只看疲劳一路。事件触发后写日志和截图的操作放到write_event里不要阻塞主循环。4. 把“能跑”升级成“能用”训练自己的行为检测模型4.1 数据从哪来公开数据集与自采数据的取舍预训练权重只能证明链路正确毕设里要拿出自己的成果就必须训练一套适应实际视角的数据。数据来源通常两条路。第一条是公开数据集。常见的“分心驾驶”10类数据集在Kaggle上有完整版本包含司机在驾驶位被前置摄像头拍摄的图片适合训练图级行为分类如果你想要带bbox标注的目标检测数据就需要找更专门的驾驶场景检测数据集或者直接在公开数据集上重新标注。公开数据的优势是量大劣势是拍摄机位和你自己的部署视角很可能不一致直接拿去训练容易在换场景时失效。第二条是自采数据。找一台模拟驾驶舱、一个人、一部手机用不同机位各录30-60分钟视频抽帧后标注。自采规模不需要很大每类行为1000-2000帧就够微调一个YOLO模型关键是覆盖多个角度方向盘上方、右侧A柱、挡风玻璃后方。如果做疲劳检测还要注意采集不同光照——夜间只靠IR补光的摄像头和白天画面差异极大需要单独标一批。我的习惯是两手都做公开数据集用来预训练和对比基线自采数据用来微调最终模型效果以自采数据上的表现为准。答辩时这个数据制作过程比调参更有说服力。4.2 把标注整理成YOLO格式转换脚本与四个边界坑YOLO训练需要txt格式标注每行class_id x_center y_center 坐标都是相对图片宽高的归一化值。很多公开数据集给的是VOC XML需要转换。下面这段脚本把单个XML转成YOLO txtimport xml.etree.ElementTree as ET CLASS_NAMES [driver, cellphone, cigarette, hand] def voc_to_yolo(xml_path, out_path, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 转换为中心点加宽高的归一化坐标 cx (x1 x2) / 2.0 / img_w cy (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h # 越界处理超出图片边界的框会被YOLO直接丢样本 cx max(0, min(cx, 1.0)) cy max(0, min(cy, 1.0)) w min(w, 1.0) h min(h, 1.0) lines.append(f{CLASS_NAMES.index(name)} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))转换脚本本身不复杂但四个坑很典型。第一图片尺寸必须和标注尺寸同源脚本里的img_w和img_h如果是从EXIF读的而训练时用了cv2读图RGB/BGR和旋转信息可能不一致导致坐标错位统一用cv2.imread后拿shape做宽高最稳。第二class的字符串要和训练配置严格一致数据集里叫“cell phone”配置里写“cellphone”模型不会报错但会丢掉全部分类。第三越界框不要删除而要截断否则训练时某些小目标因为边缘被裁直接变成背景。第四别忘记生成对应的train.txt和val.txt索引文件YOLO按这两个文件找图片而不是扫描目录。4.3 训练命令与常用参数GPU不多也能跑训练配置文件data.yaml放在项目根目录内容如下train: data/train/images val: data/val/images nc: 4 names: [driver, cellphone, cigarette, hand]然后用ultralytics命令行训练。下面的命令是起步参数训练时间在单张消费级显卡上约2小时yolo detect train dataconfig.yaml modelyolov8n.yaml epochs50 imgsz640 batch16 lr00.01 patience10 augmentTrue几个参数按需求调整batch16是显存8GB左右的常用值显存小就降batch但同步降lrepochs50对微调够用从头训练建议100以上lr00.01是微调预训练权重的推荐值如果是从头训练改成0.1。patience10表示10个epoch验证集没提升就早停。augmentTrue会开Mosaic、HSV扰动等增强对车内视角这种场景很关键。注意没有GPU时CPU跑50个epoch可能十几个小时起步。毕设时间紧就直接用云端GPU资源或者把类别缩小到两类先验证流程。4.4 评估与导出先看混淆矩阵再导出ONNX训练结束后先跑验证看看每个类别的单独指标而不是只盯平均mAPyolo val modelruns/detect/train/weights/best.pt dataconfig.yaml跑完会在runs/detect/val目录下生成混淆矩阵和PR曲线图。对违规驾驶这个场景最该关注的是cigarette类的召回率因为它目标小、样本少mAP容易被其它类拉平。各类别AP差距太大时回到4.2补对应类别数据。导出ONNX是为了部署时不再依赖PyTorch用OpenCV的DNN模块也能推理。一条命令搞定yolo export modelruns/detect/train/weights/best.pt formatonnx imgsz640导出后可以用onnxruntime验证输出shape。COCO预训练模型输出是[1, 84, 8400]其中844个坐标加80个类别分数自定义4类时输出是[1, 8, 8400]。8400是640输入下三个特征图网格的总和。如果shape对不上优先检查导出时imgsz和训练时是否一致。5. 避坑集锦违规驾驶行为识别最常见的5个翻车现场5.1 手机检测频繁误报把方向盘、饮料瓶当成了手机现象在车内视角下测试方向盘上的金属Logo、饮料罐、甚至插在杯架里的钥匙串都被标成了cell phone误报率一度超过30%。原因COCO预训练的cell phone类别主要在手持设备场景下学习对室内小物体泛化尚可但车内强反光、小目标、遮挡频繁模型只学到“一个小矩形物体”就可以打分。另外8400个候选框里置信度0.35以下虽然被滤掉但在方向盘这种纹理丰富区域局部特征和手机太接近。解决先别急着换更大的模型做三件事——第一把检测输入imgsz从640升到800同时conf提到0.45误报率通常能降一半第二自采数据时特意采集方向盘、饮料瓶、卡片等干扰物要么留作未标注背景帧要么单独标成“distractor”让模型在训练中见过这些干扰项第三在规则层加先验手机bbox必须与人手bbox的重叠面积大于某一阈值才进入“使用手机”事件单纯出现在驾驶室不等于违规。5.2 换一个摄像头角度准确率骤降数据分布漂移现象在驾驶位侧上方拍摄的视频上验证打电话检测的mAP达到0.78换成挡风玻璃顶部的下沉视角后直接掉到0.55误报翻倍。原因目标检测模型对拍摄角度极其敏感。侧上方看到的是手机正脸顶部视角看到的是反光玻璃上的手机轮廓纹理特征完全不同。数据集的拍摄机位单一模型学会的是“该角度下的手机长什么样”而不是“手机是什么”。解决收集数据时就规划至少3个机位每个机位的样本量均衡训练时在augment里增加随机透视变换参数比如perspective0.0005并开启随机旋转逼迫模型学到尺度不变特征验证时按机位分别统计AP而不是混在一起看哪个机位掉分就单独补那个角度的数据。这套做法能显著提升换场景后的泛化能力。5.3 视频流处理帧率跟不上程序“假死”还漏报现象跑本地视频一切正常接实时摄像头后画面卡顿事件触发严重滞后甚至整个程序在几分钟后无响应。原因经典的单线程代码里cap.read()被YOLO推理阻塞——摄像头缓冲区积压读帧越来越慢同时检测模型推理速度低于视频帧率帧越积越多内存飙升。程序“假死”的表象背后是生产消费模型没有设计好。解决把读帧和推理拆成两个线程中间放一个有限队列maxsize8队列满了就丢弃最旧的帧而不是丢弃所有后续帧。推理端做ROI裁剪先缩小全图判一次手部和手机候选区再对候选区做第二次高分辨率检测整体吞吐能提升1.5-2倍。事件判定不要基于帧号而要基于时间戳否则丢帧后事件序列会错位。这是整个系统从“能跑”到“能用”的分水岭。5.4 EAR疲劳阈值在不同司机身上失效阈值是玄学现象同一套阈值眼睛大的测试者刚睁开眼就被报疲劳眼睛小的测试者闭着眼开车却不触发。原因EAR虽然做了几何归一化但不同人的眼裂宽度、上下眼睑距离差异很大。0.25这个阈值来自平均人脸统计对个例根本不准。这个问题的本质是阈值不是模型参数而是个体参数。解决系统启动时增加标定流程让司机正视摄像头10秒计算出此人睁眼状态下的EAR基线然后把阈值设为基线的0.75。同时增加PERCLOS指标单位时间闭眼帧数比例作为兜底即使某个人EAR整体偏低只要闭眼比例异常上升仍然能触发疲劳。我实际用下来个体标定比调一个固定阈值有效得多。5.5 Loss降到0.04但mAP不涨过拟合比想象中来得早现象训练loss一路降到0.04val mAP50-95却停在0.30不动继续训练也不提升。翻看结果发现train loss还在降val loss已经往上走。原因这是典型的过拟合特征工程不到位时模型开始“背题目”。违规驾驶数据集里同一段视频抽出的帧高度相似模型很容易记住背景而不是行为类别样本不均衡时比如cigarette只有几百张而cellphone有几千张少样本类别的AP被长期拖垮。解决第一按“视频片段”划分数据集而不是按帧随机划分杜绝同源帧同时出现在训练和验证中第二对少数类做重采样复制粘贴增强把烟的小目标复制到别的背景上能有效提升小目标召回第三分开看每个类别的AP曲线哪个类不涨去补哪个类的数据而不是调全局学习率赌运气。最后用固定随机种子跑一个baseline后续所有实验都跟它对比才能判断改动是否真有效。6. 从论文演示到可交付成果部署、验证与复盘6.1 ONNX部署用OpenCV替代PyTorch推理模型训练完成后导出ONNX再部署是低成本路径。PyTorch运行时依赖重、启动慢换到OpenCV的DNN模块后普通小主机也能跑起来。加载核心只有三行net cv2.dnn.readNetFromONNX(best.onnx) blob cv2.dnn.blobFromImage(frame, 1/255.0, (640, 640), swapRBTrue) out net.forward()out是[1, 8, 8400]的原始张量自定义为4类时需要把通道维度置换到[1, 8400, 8]前4列是坐标后4列是类别置信度再做阈值过滤和NMS。这些后处理逻辑不再依赖torch整个部署项从几个GB的框架依赖降到几十MB。6.2 事件日志与截图归档交付物的最后一步事件触发后不能只打屏幕。我习惯把每次违规写进一条JSON记录并把原帧和画框帧各存一张图。字段最少四列时间戳、行为类型、关联置信度、视频帧号。交出去之后用户能快速翻查不用重看整段视频。字段示例用途timestamp2025-03-15 14:23:11事件发生时间event_typephone / smoke / fatigue行为类别confidence0.82关联目标置信度frame_id34712回放定位帧号6.3 用混淆矩阵和数据可视化给答辩加分评估只报一个mAP答辩时很容易被追问细节。建议把YOLO验证结果做成三张图混淆矩阵、按类的PR曲线、事件时间分布热力图。前两张展示模型边界第三张证明规则引擎在真实视频里持续输出有效事件而不是随机触发。这套可视化用python数据分析与可视化工具就可以完成。我自己最后悔的是前期把大量时间花在调规则上规则越堆越长误报却压不住后来狠心删到只剩“检测关键点连续帧”三个维度代码短了效果反而更好。做这类系统先把工程边界钉死再谈模型调参。希望帮到你。本文还有配套的精品资源点击获取