简介一份基于YOLO的课堂行为检测系统毕业设计/课程设计资源包面向计算机视觉与深度学习学习者以及需要完成课设、毕设的高校学生。系统采用YOLO目标检测算法对课堂视频中的举手、听讲、阅读、写字等学生行为进行实时识别训练好的模型可转换至ONNX格式便于跨框架部署。资源包共27个文件压缩后约26.85MB涵盖6个Python源码文件主程序、视频测试、UI界面、图像测试等、训练好的模型权重、效果展示与训练结果图、UI设计文件、模型训练说明和README文档从数据准备到训练评估均有对应脚本与笔记便于逐模块对照实践。目前已有72人学习下载适合作为目标检测项目的完整参考能帮助读者快速掌握YOLOv8的使用方法、自定义数据集训练流程以及实际部署的常见思路并在此基础上进一步扩展学生行为分析等功能。1. 课堂行为检测到底在检测什么YOLO选型、类别定义与这套系统的边界做毕业设计或课程设计时「基于YOLO的课堂行为检测」是这几年的热门方向。所谓课堂行为检测就是拿教室摄像头拍到的画面实时识别画面里每个学生在做什么——认真听课、举手发言、趴在桌上、低头玩手机、站起来回答问题这些行为本质上都是目标检测任务先定位人在画面中的位置再给每个人打一个行为标签。这套系统选YOLO而不是Faster R-CNN或SSD核心原因是课堂场景对实时性要求高教室里有几十个学生每帧都要处理YOLO的端到端检测速度和精度平衡得最好。适合的人群也很明确计算机视觉方向的本科生、研究生或者想快速搭一套课堂分析Demo的教育信息化从业者。下面我从数据、训练、避坑到部署把这套系统的完整落地路径拆开讲。2. 把课堂视频变成训练数据标注规范、数据增强与目录组织2.1 行为类别定义与标注框的取舍课堂行为检测的第一步不是急着写代码而是先把「检测什么」定死。我见过很多同学一上来就标了十几类行为结果模型训练出来一团糟。课堂场景下行为类别一般控制在五到八类比较合理。常见做法是这样划分类别ID行为名称标注要点0认真听课正常坐姿面朝前方框住整个上半身1举手手臂抬起超过肩膀框选坐姿范围不要单独框手臂2低头写字头部明显低于正常坐姿框住躯干和桌面区域3趴桌睡觉头部贴近桌面或完全趴在桌上4站立身体完全离开座位框选站立后的上半身5玩手机头部下垂、手部在桌面以下有手机遮挡标注时框住人这里有个关键取舍同一个学生画面里只能有一个框框内行为取主要行为。比如一个学生一边低头写字一边偷瞄手机框里只能标「低头写字」不能又标写字又标玩手机。YOLO是单标签分类一个锚框只能对应一个类别这个规则不定清楚后边训练时loss会非常不稳定。标注工具推荐直接用LabelImg它有图形界面画框之后可以直接导出成YOLO格式的txt文件。LabelImg的安装很简单pip装一下就能用它的输出是每个图片对应一个同名txt文件每行格式是class_id x_center y_center width height注意这四个数都是归一化到0到1之间的也就是相对于图片宽高的比例。2.2 数据增强策略与类别均衡课堂场景有个很典型的难题数据严重不均衡。「认真听课」和「低头写字」占了百分之六七十「玩手机」和「趴桌睡觉」可能只有百分之几。如果直接用原始数据训练模型会对少数类完全无视推理时把玩手机的学生全判成认真听课。常见的解法有两类。第一类是过采样把少数类的图片复制几份但直接复制容易过拟合更好的做法是复制的同时做增强。第二类是给损失函数加类别权重YOLOv8里可以在训练配置中用class weight参数让少数类在计算loss时权重更高。数据增强方面YOLOv8自带的增强策略已经很强包括mosaic、mixup、随机翻转、随机色域变换。课堂场景里我一般建议开启mosaic和hsv增强因为教室光线会随时间变化上午和下午的色温不同hsv增强能模拟这种变化。但要关掉一个东西随机翻转。课堂场景里学生朝前坐的方向是固定的左右翻转虽然不改变行为语义但会干扰模型对「举手」这类方向敏感行为的判断实际效果往往是掉点。训练参数里fliplr0.0要显式设一下。2.3 数据集目录结构与YOLO格式转换整个数据集目录推荐按YOLO官方约定的结构整理classroom_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ └── val/ # 验证集图片 ├── labels/ │ ├── train/ # 训练集标签txt │ └── val/ # 验证集标签txt └── classroom.yamlclassroom.yaml 是训练的入口配置文件内容长这样path: /home/user/classroom_dataset train: images/train val: images/val nc: 6 names: [focus, hand_up, writing, sleeping, standing, phone]# 如果原来用的是VOC格式的XML标注可以写个脚本转成YOLO格式 python xml2yolo.py --xml_dir VOCdevkit/Annotations --out_dir classroom_dataset/labelsyaml里那个path建议写成绝对路径Ultralytics框架在相对路径下偶尔会出现找不到数据的诡异问题。nc是类别数names的顺序必须和之前标注时的类别ID完全一致这个顺序一旦搞错模型训练出来所有标签都会错位——我见过有人把names顺序调了一下然后整个模型报废重训的血泪经验。如果是从公开数据集找课堂行为数据要留意有些数据集的类别定义跟上面这个清单对不上比如有的把「举手」和「站起回答」合并成一类。合并类别能降低训练难度但也牺牲了细粒度信息。我自己的习惯是毕业设计追求能跑通、指标好看合并类别是聪明选择如果论文需要细化分析再拆开单独训。3. 训练YOLOv8课堂行为检测模型环境配置、参数调优与结果评估3.1 环境搭建与预训练权重下载这套系统的训练环境常用组合是Python 3.9 PyTorch 2.x CUDA 11.8 Ultralytics YOLOv8。Ultralytics是YOLOv8的官方训练框架一条命令就能完成训练、验证和导出。# 创建虚拟环境并安装依赖建议用conda或venv别直接装进系统Python conda create -n yolo_classroom python3.9 -y conda activate yolo_classroom pip install ultralytics torch torchvision --index-url https://download.pytorch.org/whl/cu118这里有个常见坑PyTorch的CPU版本和GPU版本安装指令不一样用上面这个index-url指定CUDA版本装出来的torch才能调用显卡。装完之后跑一下python -c import torch; print(torch.cuda.is_available())输出True才说明GPU环境OK否则训练速度会慢到怀疑人生。预训练权重的下载也是很多人卡住的地方。YOLOv8的权重文件托管在GitHub Release上国内直连经常失败。常见做法是用ultralytics框架自动下载from ultralytics import YOLO model YOLO(yolov8s.pt) # 首次运行会自动下载到当前目录如果自动下载失败或太慢就手动从官方仓库的Release页面把yolov8s.pt下载下来放进项目根目录再执行同样的代码。课堂行为检测属于中等复杂度任务用yolov8ssmall版本性价比最高。yolov8n速度最快但精度低遇到后排小目标会漏得厉害yolov8m参数多训练时间长对毕业设计来说s足够了。3.2 训练参数配置epoch、batch_size与学习率训练这一步是整个系统的核心参数配置直接影响最终效果。下面这条命令是我调过多次之后比较稳的配置yolo train dataclassroom.yaml modelyolov8s.pt epochs100 imgsz640 batch16 lr00.01 patience15 device0每个参数的作用我拆开说一下参数值为什么这么设epochs100课堂行为类间差异不算大100轮足够收敛配合patience早停imgsz640YOLOv8内置分辨率640x640输入。教室画面里后排学生确实小用640是速度和精度的折中batch16取决于显存。16G显存跑yolov8s没问题显存不够降到8lr00.01Ultralytics的默认初始学习率就是0.01一般不动它patience15连续15轮验证集mAP不提升就自动停止防止过拟合瞎烧时间训练过程中终端会实时打印每一轮的loss值、精确率precision、召回率recall和mAP50。这里有个判断训练状态的小技巧前10轮loss下降快是正常现象如果前5轮loss完全不动基本是数据有问题比如标签顺序错乱或者图片解码失败训练到30轮之后loss曲线变成缓慢下降的抖动状态也是正常的不用慌。训练结束后结果会存在runs/detect/train目录下。那个best.pt就是验证集指标最好的权重后续推理部署都用它。last.pt是最后一轮的权重一般不用。3.3 训练日志解读与评估指标分析训练完不要只看mAP50那只是及格线。课堂行为检测这个任务真正要盯的是两个东西混淆矩阵和每类的PR曲线。yolo val modelruns/detect/train/weights/best.pt dataclassroom.yaml跑完验证后runs/detect/val目录里会生成混淆矩阵图和PR曲线图。混淆矩阵能直接告诉你哪两类行为容易混淆。我训过的一套数据里「低头写字」和「趴桌睡觉」的混淆率最高原因也很简单两个行为的训练样本里头部位置和姿态太接近模型分不清。如果发现混淆矩阵里某两类的互认率超过15%就要回头检查这两类的标注样本把边界情况重新标一遍。关于mAP的波动很多新手会被迷惑。训练过程中mAP50-95和mAP50是两套指标mAP50-95对框的定位精度要求更严格数值低一些是正常的不用看到mAP50-95只有0.4就觉得模型废了。课堂行为这种场景mAP50达到0.85以上、mAP50-95过0.6这套模型基本就能拿去用了。另外有个看结果的细节用yolo predict跑一张测试图片把conf0.5阈值设上。如果很多框显示不完整学生被框得七零八落说明imgsz和实际输入分辨率不匹配如果很多目标完全没框出来就要考虑是不是漏检而不是误检。漏检和误检的排查方向完全不同一个是数据问题一个是阈值和后处理问题别搞混。4. 避坑指南课堂行为检测从训练到部署的五个翻车现场4.1 loss在降但mAP不涨现象训练日志里loss每轮都在降看起来很正常但验证集的mAP50一直在0.3左右上不去。原因这是过拟合的前兆。loss下降代表模型在训练集上越来越拟合mAP不涨说明它在新数据上没学会真正的泛化特征。课堂行为数据集往往来源于固定的几个教室摄像头视角场景单一模型学的是背景特征而不是行为特征。解决第一把数据增强强度提上去尤其是hsv和mosaic的强度第二检查训练集和验证集是否来自同一个视频片段——如果是从同一段视频里按时间顺序切分的前8成做训练后2成做验证模型记忆了时间邻近性正确做法是把不同教室、不同时间段的视频打乱后随机划分。我一般按视频文件为单位划分而不是按帧划分。4.2 后排小目标学生完全检测不到现象前排学生检测得很准但教室后排的学生要么漏检要么检测框特别飘置信度很低。原因课堂摄像头大多是教室前方或侧上方后排学生在画面里只占几十x几十像素。YOLOv8在640x640输入下小目标的特征图分辨率不足以承载足够信息属于尺度问题。解决第一个办法粗暴但有效把imgsz提升到960甚至1280重训一轮。第二个办法是推理阶段用TTA或SAHI切片推理。我实际项目里更推荐后者——训练保持640x640推理时用SAHI把原图切成多个重叠切片分别检测再合并结果。SAHI库集成方式很简单from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model AutoDetectionModel.from_pretrained( model_typeultralytics, model_pathbest.pt, confidence_threshold0.4, image_size640 ) get_sliced_prediction(classroom_frame.jpg, model, slice_height320, slice_width320)小目标漏检的本质是输入分辨率不够切片推理相当于把原图放大后再局部检测对后排学生这种目标特别有效。4.3 推理帧率只有个位数现象用训练好的模型跑视频流每秒只能处理七八帧画面卡成PPT根本达不到实时。原因多半是推理时用了原始的PyTorch模型没有做任何优化。float32的PyTorch推理在CPU或普通GPU上都跑不快很多人直接拿best.pt跑视频掉进了这个坑。解决先把模型导出成FP16精度的ONNX格式再转成TensorRT的engine格式基本能让速度翻三倍以上。导出命令和引擎加载方式如下yolo export modelbest.pt formatonnx opset12 simplifyTruetrtexec --onnxbest.onnx --saveEnginebest.engine --fp16from ultralytics import YOLO model YOLO(best.engine) results model.predict(frame, device0)如果还嫌慢就把推理分辨率往下调。用1280训练的模型推理时imgsz960或800速度能上去不少精度损失可以接受。课堂行为检测不是医疗影像不需要做像素级判断速度优先。4.4 把举手误判成站立现象有些学生只是手举得比较高模型却直接判定为站立。这两个行为在逻辑上一个坐着、一个站着误判之后统计数据完全没法用。原因两种行为的共同特征是头部和躯干在画面中明显高于周围的同学如果标注时把举手的框拉得过大把椅子和桌面都包进去模型学到的是「高框站立」这个错误规律。解决重新检查训练集里这两类的标注框高度分布。举手的框应该紧贴身体站立时的框必须在纵向上比坐着时长出一截。如果不确定某个边界样本该归哪类宁可直接删掉这个样本也不要在模棱两可的框上硬标。模糊样本对模型的伤害比少量干净样本的缺失更大。另外可以在后处理阶段加一条逻辑规则同一人在连续N帧中如果检测类别频繁在「举手」和「站立」之间跳变取出现次数更多的那类做平滑输出。4.5 导出ONNX后精度明显下降现象PyTorch模型跑着mAP0.85导出ONNX之后再跑同一批测试图mAP掉了五六个点。原因这个问题多半出在模型导出时的输入尺寸和训练尺寸不一致。训练用了640的imgsz导出时如果没有显式指定尺寸ONNX会默认用模型配置里的尺寸而推理时如果又用别的分辨率去跑检测精度就会掉。另一个常见原因是导出的模型没有带NMS后处理导致推理输出需要额外的后处理逻辑处理不善会引入定位偏差。解决导出时固定输入尺寸并确认推理端的预处理逻辑和训练时一致包括归一化方式、BGR/RGB通道顺序、resize方式。YOLOv8官方导出的ONNX本身不带NMS需要外部接一个非极大值抑制。用onnxruntime推理的代码框架大致如下import onnxruntime as ort import numpy as np session ort.InferenceSession(best.onnx) input_name session.get_inputs()[0].name # 预处理resize到640x640BGR转RGB归一化到0-1 img cv2.resize(frame, (640, 640)) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 img img.transpose(2, 0, 1)[None, :] outputs session.run(None, {input_name: img}) # 输出shape为 [1, 84, 8400]8400是anchor数量844个坐标80个COCO类别 # 需要自己解析坐标、做NMS和后处理ONNX导出的精度损失很多时候不是模型问题而是前后处理不一致的问题。检查顺序先确认输入预处理一致再确认输出后处理逻辑正确最后才怀疑模型本身。5. 把模型部署到课堂视频流接入、帧率优化与行为统计报表5.1 用OpenCV接入教室摄像头视频流训练完的模型最终要落到实际场景里跑。教室里的摄像头大多是RTSP协议的网络摄像头OpenCV可以直接拉流。部署时把模型加载一次然后循环读帧、推理、显示结果是基本框架import cv2 from ultralytics import YOLO model YOLO(best.engine) cap cv2.VideoCapture(rtsp://192.168.1.100:554/stream1) frame_count 0 while True: ret, frame cap.read() if not ret: break # 每隔一帧做一次推理减轻CPU/GPU压力 if frame_count % 2 0: results model.predict(frame, conf0.5, iou0.45, verboseFalse) annotated results[0].plot() cv2.imshow(classroom_monitor, annotated) frame_count 1 if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()这里的conf0.5和iou0.45两个参数值得说一下。conf阈值控制的是「框达到多可信才输出」课堂场景建议设0.4到0.5之间设太高会漏检后排小目标设太低会出现大量假阳性框。iou阈值是NMS的抑制阈值0.45是默认值如果画面里学生比较密集、相互遮挡严重可以把iou调到0.3试试能减少重叠框干扰。5.2 检测结果落库与行为统计实时检测只是第一步课堂行为检测系统的价值在统计报表。把每一帧检测到的人数和行为类别写入数据库按时间段做聚合统计才能支撑起「课堂活跃度分析」「学生专注度曲线」这类上层应用。这里用SQLite就够了毕业设计不需要上MySQL。import sqlite3 import time conn sqlite3.connect(classroom_stats.db) conn.execute(CREATE TABLE IF NOT EXISTS behavior_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT, behavior_class TEXT, confidence REAL, student_pos TEXT )) # 每处理完一帧把检测结果批量写入 def log_results(results, timestamp): for box in results[0].boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() conn.execute( INSERT INTO behavior_log (timestamp, behavior_class, confidence, student_pos) VALUES (?, ?, ?, ?), (timestamp, class_names[cls_id], conf, str(xyxy)) ) conn.commit()统计逻辑上有个细节单帧统计会受偶然性影响比如一个学生低头捡个笔这一帧就被记成「低头写字」。正确做法是做时间窗口聚合统计每20秒或每1分钟内各类行为的主导类别。常见做法是维护一个滑动窗口窗口内取出现次数最多的类别作为该时段的状态。5.3 二次开发路线从检测到教育场景落地这套系统的进阶方向我列几条实际可走的路径都基于已有检测结果做拓展不需要重新训练模型第一把DeepSORT或ByteTrack追踪接进来给每个学生分配一个稳定的ID。有了ID就能画出一条学生个体在整节课中的行为时间线专注度、举手次数、站立次数都能精确到人而不是只做群体统计。这个功能对教育场景的价值远大于单纯画框。第二在检测结果上加一个课堂活跃度评分。比如定义权重听课1分、举手2分、站立1分、低头0分、趴桌-1分、玩手机-2分对窗口内全班所有学生的检测框做加权平均生成一条整节课的活跃度曲线。直接用模型输出做计算不需要额外训练别的网络。第三把统计接口化做成Web页面。Flask或FastAPI起一个轻量服务前端定时拉取接口数据渲染曲线图。技术栈简单但呈现效果好答辩时直接展示了系统的完整闭环。最后说明一下我在这类项目里踩过最深的坑就是急着训模型、忽略数据质量。从那以后我每次拿到课堂行为检测的数据集都会强制自己先花半天时间把标注逐张检查一遍、把类别分布统计出来再启动训练流程。数据干净了后面所有步骤都能顺理成章地跑通。希望帮到你。本文还有配套的精品资源点击获取