
简介这是一份基于YoloV8的学生行为检测系统完整项目面向计算机视觉方向学习者以及需要完成毕业设计、课程设计的在校生。项目覆盖了数据配置、模型训练、推理部署与效果评估全流程提供可直接运行的Python源码、训练好的YOLOv8模型权重、评估指标曲线与部署教程能够应用于课堂行为识别、学生专注度分析等真实场景。压缩包共20个文件其中以Python脚本、pyc编译文件、YAML配置文件、Jupyter Notebook与TensorRT engine模型为主还包含CSV指标文件、演示图片等整体大小20.5MB目录结构清晰便于按模块查阅和二次开发。目前已有964人学习下载该资源。项目代码经过实际运行测试功能稳定适合在此基础上修改扩展直接作为毕业设计、课程设计或项目演示使用对希望从实战中掌握YOLOv8训练与部署流程的初学者也很有帮助。1. 为什么说这套YoloV8学生行为检测源码是一份完整的工程而非玩具拿到压缩包的第一反应是看 model 目录里有什么。里面躺着 yolov8n.engine 而不是 .pt这说明作者没停在“训练完、出几条评估指标曲线”就交差而是把权重转成了 TensorRT 引擎并配了一条 GPU 视频处理管线。很多 YOLOv8 毕业设计项目到 mAP0.5 折线图就结束了这份源码多走了部署这一步答辩时自然拿得出手。整套源码由 app.py、VideoProcessorGPU.py、VideoProcessorGPUCV.py、test.ipynb、test.py、config 下的 demo.yaml 与 data.yaml 组成覆盖数据组织、训练、评估、TensorRT 推理到行为统计 CSV 输出的完整闭环。对课程设计和毕业设计来说它的价值不只是“调好了能跑”而是你能顺着链路看懂数据集怎么组织、评估曲线怎么读、为什么最终选 yolov8n 而不是 yolov8m。适合做计算机视觉课设或毕设的本科生以及想把 YOLOv8 快速落到课堂监控盒子上的工程师。第一次接触 YOLOv8 的话建议先把“训练—验证—导出—推理”这条线完整跑通再换自己的数据。下文按这条流水线顺序拆开。2. YOLOv8网络结构与学生行为数据集从C2f到data.yaml2.1 为什么选YOLOv8n而不是更大尺寸的模型YOLOv8 系列按深度和宽度分成 n / s / m / l / x 五档。nano 版本参数量约 3.2M计算量在 8.7 GFLOPs 上下。学生行为检测是固定摄像头下的监控场景目标尺度集中在几十到两百像素大部分时间里画面变化不大用 s 或 m 模型获得的精度增益有限延迟却会翻倍。资源包附带 yolov8n.engine说明作者在精度与帧率之间明确选了后者优先。网络结构上YOLOv8 相对 v5 有两个关键变化。第一是 C2f 模块替代了 C3输入先经过一个 1×1 卷积分成两条分支其中一条依次堆叠 n 个 Bottleneck每经过一个 Bottleneck 就把中间特征 concat 到输出列表最后全部拼接后过 1×1 卷积。梯度从多个路径回传深层特征表达能力比 C3 更好。第二是检测头从 anchor-based 换成 anchor-free每个特征图位置直接预测到目标框四条边的距离与类别概率不再预先聚类锚框。对行为检测来说举手、阅读、睡觉的姿态差异极大anchor-free 头不依赖预设框分布更匹配这种多形态目标。有人会问为什么不用 YOLOv8-pose 提取关键点再做时序分类。因为姿态估计的标注成本要高一到两个数量级且教室遮挡严重后排学生互相叠压关键点标注基本不可用。这份项目的核心是目标检测用检测框框住学生再结合框坐标、类别与置信度做行为状态判断工程上更容易复现论文里也更好解释。2.2 data.yaml与YOLOv8训练自己的数据集标注规范config 目录下的 data.yaml 是训练入口。常见写法如下类别按课堂行为定义学生行为检测系统里我一般保持在 6 类左右类别太多会稀释小样本类的召回path: ./datasets/student train: images/train val: images/val nc: 6 names: 0: hand_raising 1: reading 2: writing 3: sleeping 4: phone 5: talking这里 nc 必须和 names 一一对应类别顺序定下来就不能再动否则模型与标签对不上。两个关键点需要留意。第一train / val 建议按 9:1 划分且要按视频而不是按帧随机分同一段视频里时间相邻的帧出现在两个集合中会让评估指标虚高答辩时容易被打穿。第二每类样本量尽量均衡到 800 张以上phone 和 talking 这类出现率低的行为要主动补充否则模型很容易漏检或误判成手部动作。标注工具用 LabelImg 或 labelme 都行导出格式是 YOLO 的 txt每一行对应一个目标class_id cx cy w h坐标全部归一化到 [0,1]。class_id 必须从 0 开始且与 data.yaml 的 names 顺序严格一致这是 YOLO 系训练自己的数据集时最容易翻车的一环。2.3 训练流程与评估指标曲线的正确读法训练时建议直接加载 COCO 预训练权重做迁移学习这是课程设计里提升收敛速度最划算的手段yolo detect train \ dataconfig/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ device0 \ patience30每个参数都有取舍。data 指向 2.2 的配置文件model 写 yolov8n.pt 表示加载官方在 COCO 上预训练的权重epochs150 对课设数据集足够早停会帮你提前退出batch16 在 8GB 显存上安全显存小就降到 8imgsz640 是速度与精度的平衡点patience30 表示 30 个 epoch 内验证指标没有提升就自动停止。训练结束runs/detect/train 下会生成 results.csv 和各项评估指标曲线图。这三组指标是答辩时评委必看的含义如下指标横轴关注点train/box_loss 与 val/box_lossepoch两条线距离太大说明过拟合都在高位说明欠拟合metrics/mAP50(B)epoch0.85 以上属于可交付水准0.90 以上属于优质metrics/mAP50-95(B)epoch综合框位置精度数值低于 mAP50 是正常现象两个常见误读值得提醒。第一拿最后一个 epoch 的指标当最终结果正确做法是取 best.pt 对应的行因为过拟合会让尾段曲线抖动第二只看 mAP 不看 PR 曲线PR 曲线横轴召回率、纵轴精确率曲线越贴近右上角代表各类别判别越稳定课程设计报告里放 PR 曲线比放 loss 曲线更有说服力。画损失函数曲线图时直接把 results.csv 里对应的列抽出来用 matplotlib 绘图即可比截图训练日志规范得多。3. 从PyTorch权重到TensorRT engineVideoProcessorGPU的推理管线3.1 依赖矩阵与CUDA、TensorRT版本坑跑这套源码需要 GPU 吗答案是分阶段。训练阶段用 CPU 也能跑只是慢推理阶段想吃到 VideoProcessorGPU 的红利就得有 NVIDIA 显卡和 TensorRT。源码要跑起来requirements.txt 列的是 Python 侧依赖真正的坑在 CUDA、cuDNN 和 TensorRT 的版本匹配上。TensorRT 生成的 engine 文件与显卡架构强绑定model/yolov8n.engine 在 A 机器上导出的换到另一张卡上大概率报 Engine could not be deserialized。拿到压缩包后的第二步是确认本机环境python -c import torch, tensorrt as trt; print(torch.__version__, trt.__version__) nvcc --version nvidia-smi我这边常用的稳定组合如下实际以各版本官方支持矩阵为准CUDATensorRTPython适用场景11.88.6.x3.8 / 3.10GTX 16 系、RTX 20/30 系老驱动12.110.x3.10RTX 40 系新卡FP16 支持更完整torch 与 trt 版本相差太大会在 ONNX 转 engine 时遇到算子兼容问题最常见报错是 Unsupported ONNX opset把 opset 降到 1214 通常能解决。如果本机没有 TensorRT 环境也可以先走 ONNX Runtime 的 CUDA 执行提供方但帧率比 engine 低不少这是给部署教程留的退路。3.2 导出TensorRT engine的完整过程基于训练得到的最佳权重导出 engine我一般分两步走而不是一条命令直接出 engine。第一步导出 ONNX检查算子完整性第二步由 ONNX 转 engineyolo export modelruns/detect/train/weights/best.pt formatonnx opset12 imgsz640 yolo export modelruns/detect/train/weights/best.onnx formatengine device0 halfTrue workspace4formatonnx 先生成中间表示便于检查是否有不支持的算子opset12 是兼容性最好的算子集版本。第二步 formatengine 是 TensorRT 的目标格式halfTrue 启用 FP16行为检测场景下类别少、目标大FP16 导致的 mAP 下降通常在 0.5 个百分点以内帧率提升却能有 40% 以上workspace4 表示分配 4GB 显存给 kernel 自动调优显存小的机器要改小这个值否则导出过程直接 OOM。顺便提一句导出完的 best.engine 建议立刻加一行日志记录它的输入尺寸和 binding 数量因为在 TensorRT 10.x 上binding 的查询接口和 8.x 不一样动态 batch 的 set 方式也变了。源码里 model 目录下的 yolov8n.engine 就是这么来的。提示engine 文件绑定 GPU 架构换卡必须重新导出。先用 nvidia-smi 确认显卡型号再决定 TensorRT 版本否则大概率白跑一趟。3.3 VideoProcessorGPU.py 的推理管线拆解VideoProcessorGPU 是这份源码的核心。名字里的 GPU 说明整条链路都在显存里流转预处理、推理、后处理三段式结构下面这个简化骨架与源码逻辑保持一致class VideoProcessorGPU: def __init__(self, engine_path, conf_thres0.35, iou_thres0.45): runtime trt.Runtime(trt.Logger(trt.Logger.WARNING)) with open(engine_path, rb) as f: self.engine runtime.deserialize_cuda_engine(f.read()) self.context self.engine.create_execution_context() self.conf_thres conf_thres self.iou_thres iou_thres def preprocess(self, frame): # letterbox 保持宽高比缩放至 640x640 img, ratio, (dw, dh) letterbox(frame, (640, 640)) # BGR - RGB, HWC - CHW, 归一化到 [0,1] img img[:, :, ::-1].transpose(2, 0, 1) img np.ascontiguousarray(img, dtypenp.float32) / 255.0 return img, ratio, (dw, dh) def infer(self, tensor): d_input cuda.mem_alloc(tensor.nbytes) d_output cuda.mem_alloc(self.output_size) cuda.memcpy_htod_async(d_input, tensor.ravel(), self.stream) # 异步推理不阻塞 CPU self.context.execute_async_v2( bindings[int(d_input), int(d_output)], stream_handleself.stream.handle ) cuda.memcpy_dtoh_async(self.output, d_output, self.stream) self.stream.synchronize() return self.output三个参数值得根据现场改conf_thres 默认 0.35课堂场景误检多就调到 0.45漏检多就降到 0.25iou_thres 默认 0.45行为类别之间框重叠少放宽到 0.5 影响不大letterbox 填充会让坐标偏移所以后处理必须用 ratio 和 dw、dh 把检测框映射回原图否则画框位置肉眼可见地偏。模型输出形状是 1×84×8400其中 8400 个锚点由 80×80、40×40、20×20 三个尺度相加得到84 维前 4 维是框坐标第 5 维是目标置信度后面是类别概率。后处理先按 conf_thres 过滤再做 NMS 去重最后用 ratio 还原框坐标一帧的检测列表就出来了。整条链路跑下来25 FPS 的输入视频能稳定在 1530 FPS 处理瓶颈通常在视频解码而不是模型推理。4. 端到端复现从视频流到output.csv行为统计4.1 用test.py验证engine与预处理链路拿到源码后第一个动作不是改参数而是用 test.py 或 test.ipynb 确认当前环境能加载 engine。报错就回到 3.2 重新导出正常就取视频第一帧打印检测结果cap cv2.VideoCapture(demo.mp4) ret, frame cap.read() if not ret: raise SystemExit(cannot read demo video) dets processor.process_frame(frame) for det in dets: x1, y1, x2, y2, cls_id, conf det label f{names[cls_id]} {conf:.2f} cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2) cv2.putText(frame, label, (int(x1), int(y1) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)这一步能同时验证三件事engine 是否可反序列化、预处理是否与训练时一致、后处理坐标是否映射正确。如果输出为空先把 conf 降到 0.25 重跑仍然为空就从目标尺度和画面亮度上找原因。多数行为检测漏检来自训练数据与实际场景不匹配比如训练集里学生都坐着测试画面却出现躺姿这类问题调阈值是解决不了的得回数据侧。4.2 行为时间线统计与output.csv的生成单帧检测结果只回答了“这一帧里谁在什么位置干什么”课程设计需要的是“某学生在 14:03:12 到 14:04:02 持续睡觉”。VideoProcessorGPU 之外的聚合逻辑实现了这层转换核心是一个小型状态机对每个目标维护类别、起始时间和连续帧数当同一目标连续多帧保持同一类别才判定行为成立。def update_stats(stats, dets, ts): for det in dets: x1, y1, x2, y2, cls_id, conf det # 按检测框中心所在的 50x50 网格作为轻量级目标 ID cell f{int((x1 x2) / 2 // 50)}_{int((y1 y2) / 2 // 50)} if cell not in stats: stats[cell] {cls: cls_id, count: 1, start: ts} continue cur stats[cell] if cur[cls] cls_id: cur[count] 1 else: if cur[count] 3: write_csv(cur, ts) # 上一行为结束落盘 stats[cell] {cls: cls_id, count: 1, start: ts}这里用检测框中心所在的网格做跨帧关联比 DeepSORT 轻量得多固定摄像头下足够稳定。后排两人挨得近时把 50 调成 40 像素代价是同一人轻微晃动更容易换 key需要靠 countN 的持续帧数来滤波。原始输出直接写 CSV列结构我建议这么定列名含义示例start_time行为起始时间戳秒14:03:12end_time行为结束时间戳14:04:02duration_sec持续时长50.0class_name行为类别sleepingconfidence平均置信度0.87答辩时评委常问“怎么证明统计是准的”与其贴十张曲线不如拿出 3 段人工标注过的视频片段对比行为开始与结束时间误差在 1 秒以内这才是闭环验证。4.3 抽帧策略与批量处理监控视频一秒 25 帧行为变化却很慢教师举手从开始到结束持续两三秒每帧推理只会把算力浪费在几乎相同的画面上。合理的抽帧间隔是 100 毫秒左右for video_path in Path(videos).glob(*.mp4): cap cv2.VideoCapture(str(video_path)) fps cap.get(cv2.CAP_PROP_FPS) step max(1, round(fps / 10)) idx 0 while True: ret, frame cap.read() if not ret: break if idx % step 0: dets processor.process_frame(frame) ts cap.get(cv2.CAP_PROP_POS_MSEC) / 1000.0 update_stats(stats, dets, ts) idx 1stepround(fps/10) 把推理负载降到原来的 10%40%对行为统计的精度损失很小。特别注意时间戳用 CAP_PROP_POS_MSEC它比手动累加帧数更抗丢帧摄像头 USB 接入时帧率不稳定依赖帧计数会累积时间误差CSV 里的 duration_sec 就不可信了。4.4 app.py整合成可复用模块把 4.1 到 4.3 串起来就是 app.py 的骨架启动时加载 engine遍历视频、读帧、预处理、推理、更新统计最后统一写 CSV。调试通过后把这套逻辑封装成一个 process_video 函数对外只暴露视频路径、engine 路径和输出路径三个参数后续不管接 FastAPI 还是命令行都方便。这也是部署教程里最该保留干净接口的部分。5. 落地课堂场景的调优点与排错清单5.1 相机角度与目标尺度的现实问题第 4 章跑通以后实际部署会遇到三个训练时想不到的问题。第一是相机角度训练数据大多是正视角教室实际监控往往是斜向俯拍后排学生目标可能只有 30×60 像素yolov8n 在 640 分辨率下对这种小目标召回偏弱。这里有两个可选方案一个是把推理分辨率提到 960延迟上涨约 60%另一个是训练时打开 mosaic 与 scale 增强让模型见过更多小尺寸正样本后者的收益更持久。第二是光照突变教室窗帘拉上的一瞬间画面亮度骤降会导致误检和漏检同时增加。常见做法是在预处理前做自适应直方图均衡或者在 app.py 里检测到帧平均亮度低于阈值时切换视频流参数这属于策略层优化不动模型。第三是同一画面的目标密度后排七八个学生挤在一起时低置信度框会被 NMS 直接压掉。此时优先调 iou_thres 而不是 conf_thres因为大量框是真实存在只是相互重叠IOU 阈值放宽到 0.55 能保留更多边缘目标。5.2 高频报错与排查顺序症状可能原因处理engine 加载失败TensorRT 版本不一致或 GPU 架构不匹配nvidia-smi 确认型号重新导出 engine输出全 0帧未转 RGB 或未除以 255比对 preprocess 与训练时预处理框位置偏移letterbox 的 dw/dh 未还原检查后处理是否乘回 ratioGPU 利用率低瓶颈在 cv2 视频解码换成 VideoProcessorGPUCV 或 ffmpeg 硬解漏检躺姿睡觉训练数据缺躺姿补充该类图像重训 30 轮以上排查顺序固定为先确认 engine 能加载再验预处理一致性然后用静态图验证后处理坐标最后才怀疑模型权重和数据集。按这个顺序能省掉大量猜忌时间。5.3 把行为统计接成摘要输出output.csv 生成之后下一步是把行为统计直接聚合成课堂摘要方便没有视觉背景的老师直接阅读。import pandas as pd df pd.read_csv(output.csv) grouped df.groupby(class_name)[duration_sec].sum().sort_values(ascendingFalse) print(本节课行为时长分钟) print(grouped.round(2).to_string()) print(f检测到行为片段数: {len(df)})这段代码直接消费第 4 章产出的 CSVgroupby 按类别聚合时长sort_values 让占比最高的行为排前面一分钟内能生成一节课的报表摘要。如果行为类别里有 phone 和 sleeping还可以再按教室区域切分把坐标映射到座位网格输出“后排第 3 组手机使用次数偏高”这类结论这已经是行为检测项目从“能识别”走向“能决策”的分界线了。顺着这份源码再往下走把 VideoProcessorGPU 的输入改成 CUDA 上的 NV12 直接搬运省掉 H2D 拷贝性能还能再上一档那是 Jetson 部署时最常见的优化手法也是这份源码留给你的下一个接口。本文还有配套的精品资源点击获取