YOLOv8校园宿舍安全监测系统:从数据集到部署全流程 简介《基于YOLOv8的校园宿舍安全监测系统》是一份面向计算机视觉方向学生与开发者的完整项目包适用于毕业设计、课程设计或项目初期演示。系统以YOLOv8目标检测为核心包含完整数据集、可视化界面、模型训练与部署说明代码已测试通过简单部署即可运行功能完善且易于操作。包内共8个文件涵盖Python源码负责可视化页面、视频检测与模型训练、预训练权重文件以及部署说明文档整体压缩包仅15.91MB轻量便于下载使用。目前已有62人学习项目支持生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图覆盖模型评估关键维度便于答辩展示。对于需要快速上手或完善毕设成果的用户资源附带清晰的部署教程与运行指引保证拿来就能用也适合中等基础的学习者在此基础上扩展实现其他检测功能。1. 基于YOLOv8的校园宿舍安全监测系统先搞清楚它替你解决了什么每年毕设季都能看到一批关于《基于YOLOv8的校园宿舍安全监测系统》的资源帖源码、可视化界面、完整数据集和部署教程打包在一起评论区问得最多的就是能不能跑起来。宿舍安全监测这个题目本身不复杂摄像头画面进来模型把玩手机、睡觉、吸烟、打闹这类行为自动框出来界面实时显示并留存记录恰好把目标检测、GUI 开发和模型部署三段技能串成一条完整的线。这份资源适合两类人一类是选定了检测类毕设、但不想从标注数据开始折腾两周的学生另一类是想快速过一遍 YOLOv8 从训练到落地的完整闭环、再基于它改自己方案的从业者。它给的不是黑科技是一个能直接上台演示的完整基线。2. 数据集与训练参数把宿舍场景图片喂进 YOLOv8 前要做对的几件事2.1 数据集结构与标注格式VOC 转 YOLO 的转换脚本拿到的资源里如果带完整数据集先不要急着训练第一件事是把目录结构看一遍。YOLOv8 训练时认的是 YOLO txt 格式的标注不是 labelme 的 JSON也不是 VOC 的 XML。一份能直接跑起来的数据集长这样dataset/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 ├── labels/ │ ├── train/ # 与图片同名的 .txt 标注 │ └── val/ └── dataset.yaml每一张图片对应一个同名 txttxt 里每一行是类别id cx cy w h四个坐标值都做了归一化范围在 0 到 1 之间。宿舍场景标注的类别一般就四五类常见的是吸烟、玩手机、睡觉、打闹这几类数据集已经按 train/val 分好的话基本可以直接开训。如果拿到的是 labelme 标注的 JSON常见做法是写一段转换脚本统一处理我一般会这样做import json import os from glob import glob class_map {smoking: 0, phone: 1, sleep: 2, fighting: 3} for json_path in glob(labelme_jsons/*.json): with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] txt_path os.path.join( labels, os.path.basename(json_path).replace(.json, .txt) ) lines [] for shape in data[shapes]: label shape[label] if label not in class_map: continue # labelme 的 points 是 [[x1,y1],[x2,y2]]不保证顺序 (x1, y1), (x2, y2) shape[points] cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w abs(x2 - x1) / img_w h abs(y2 - y1) / img_h lines.append(f{class_map[label]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines))这段脚本做的事情很直接把 labelme 的矩形框坐标换算成 YOLO 的归一化中心点坐标。有两个细节值得注意。第一class_map 里的 id 必须和 dataset.yaml 里 names 的顺序严格一致顺序错了模型训练出来类别全乱这属于最容易翻车又最难发现的问题。第二points 只取前两个点当矩形框如果标注时画的是多边形我一般直接用最小包围盒宿舍这类场景对象形状规整包围盒损失可以接受。数据划分也要看一眼。如果 images 下只有一个文件夹没有 train/val 之分说明资源给的是原始图集需要自己跑一次划分脚本。常见做法是 8:2 或 9:1 随机划分注意同一场景的连续帧尽量放进同一个集合避免验证集里出现与训练集几乎相同的画面那样 mAP 虚高答辩时经不起追问。2.2 训练参数怎么配img、batch、epochs、device 各自的含义处理完数据集就开始训练自己的数据集命令行里最常用的一条是yolo detect train \ modelyolov8n.pt \ datadataset.yaml \ imgsz640 \ batch16 \ epochs100 \ device0这段命令看着简单但参数含义值得一条条盯一遍。model 指定的是预训练权重入口yolov8n.pt 是最轻的 nano 版本宿舍场景只有四五个类别n 的精度完全够用跑起来也快data 指向刚才说的 dataset.yamlimgsz 是模型输入分辨率640 是速度和精度的平衡点也是大多数公开权重默认训练的分辨率。参数作用宿舍场景建议值model预训练权重/网络结构入口yolov8n.ptimgsz模型输入分辨率640batch每轮喂给显卡的图片数8~16epochs总训练轮数100patience验证指标不再提升时早停的等待轮数20device0 表示第一张显卡cpu 表示无显卡0workers数据加载进程数4~8batch 和显存直接相关6G 显存的 GTX 1660 Ti 这类卡batch 16 加 imgsz 640 基本到顶再往上就会爆显存。epochs 不是越大越好100 轮对这个小数据集足够配合 patience 早停能避免无意义训练。device 填 0 表示用第一张显卡没显卡就填 cpu但 cpu 训练会慢到怀疑人生建议要么租一张云 GPU要么先用资源里训练好的权重跑通推理训练留到有显卡的环境再补。workers 默认 8 有时候反而会让 Windows 机器报错改成 4 更稳。如果训练时发现 loss 完全不降先检查 dataset.yaml 里的路径是不是绝对路径。相对路径在换机器后经常出问题我一般统一改成绝对路径path 字段指向数据集所在目录train/val 填相对 path 的子目录这样换机器只改一行。2.3 训练产物与权重选型best.pt、last.pt 和损失曲线训练跑完后runs/detect/ 下会生成一个带编号的目录里面 weights/ 里有两个权重best.pt 和 last.pt。选择逻辑很简单best.pt 是验证集上指标最好的一版界面推理和最终交付都用它last.pt 是最后一轮的状态一般只在你想继续训练时用。两者体积一样差别在模型参数状态。同时目录里还有 results.csv 和 results.png这里面逐行记录了每一轮的 train/box_loss、val/box_loss、mAP50 等指标。拿到项目先打开这个 csv 看一眼如果 val loss 一路下降且没有在尾部反弹说明训练是干净的如果 val loss 掉到某个点后持续回升基本就是过拟合这时候要让 epochs 配合早停而不是硬拉满。这也是判断一份资源模型能不能信最直接的证据比任何 README 描述都可靠。3. 可视化界面与检测推理从权重到监测台的完整链路3.1 界面技术栈与模块划分检测区、告警区与日志区拿到带界面的资源先看主窗口的模块划分。这类毕设最常见的界面方案是 PyQt5 加 OpenCVPyQt5 管窗口和控件OpenCV 管图像读取显示。主窗口一般分三个区域中间是视频显示区用 QLabel 承载实时画面右侧是告警信息区列出当前检测到的行为类别和数量底部是按钮和日志区负责打开摄像头、打开视频文件和记录检测历史。整体操作流是打开摄像头或视频文件→启动检测线程→定时刷新画面→触发告警时在界面上亮警告并落盘日志。一个最小可运行的界面骨架长这样from PyQt5.QtWidgets import ( QMainWindow, QLabel, QPushButton, QVBoxLayout, QHBoxLayout, QWidget ) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(校园宿舍安全监测系统) self.video_label QLabel(等待视频输入) self.video_label.setMinimumSize(960, 540) self.btn_camera QPushButton(打开摄像头) self.btn_file QPushButton(打开视频文件) layout QVBoxLayout() layout.addWidget(self.video_label) layout.addWidget(self.btn_camera) layout.addWidget(self.btn_file) container QWidget() container.setLayout(layout) self.setCentralWidget(container)这段代码先不涉及任何检测逻辑先把控件搭起来。原因是我习惯先把界面框架跑通再接模型否则一旦推理代码有问题界面和检测会纠缠在一起排查起来要翻一倍时间。完整源码里通常会在这个骨架基础上加表格控件、告警状态栏和统计图表但核心交互逻辑不变读懂了骨架剩下的控件都是平铺直叙往布局里加。3.2 视频流推理线程为什么不能在主线程里跑 YOLO这是整个系统最容易让人卡住的地方。新手拿到手图省事把 model(frame) 直接写在按钮的槽函数里结果一点打开摄像头窗口立刻转圈拖都拖不动。原因很直接YOLO 推理一次要几十到上百毫秒而这个时间窗口内 GUI 主线程被占住事件循环停了界面自然假死。解决思路是线程分离推理放进独立线程界面只负责刷新显示。常见做法是import threading import queue import cv2 from ultralytics import YOLO class DetectThread(threading.Thread): def __init__(self, model_pathbest.pt, src0): super().__init__() self.model YOLO(model_path) self.cap cv2.VideoCapture(src) if not self.cap.isOpened(): raise IOError(无法打开视频源: %s % src) self.frame_queue queue.Queue(maxsize2) self.running True def run(self): while self.running: ret, frame self.cap.read() if not ret: break results self.model.predict(frame, conf0.45, verboseFalse) if self.frame_queue.full(): try: self.frame_queue.get_nowait() except queue.Empty: pass self.frame_queue.put((frame, results[0]))几个参数说明。conf0.45 是置信度阈值低于这个值的目标会被过滤宿舍场景如果误报多可以往上调到 0.5如果漏检多就往下调。frame_queue 的 maxsize 设为 2 是刻意的界面消费速度赶不上推理速度时队列能自动丢旧帧避免内存无节制增长。这里没有用 Qt 的 QThread 而是用 Python threading因为线程里不碰任何界面控件只往队列里塞数据跨线程操作控件才是崩溃的根源。界面线程那边用一个 QTimer 定时从队列取帧取到就转成 QImage 显示取不到就跳过这一帧。定时器间隔我一般设 30ms 左右对应大约 33fps 的刷新率足够流畅也不会把 CPU 空转打满。注意 QTimer 的槽函数里不要做任何模型推理这正是它和推理线程的边界。3.3 结果叠加与告警触发画框、计数与日志落盘推理线程拿到结果后界面线程从队列取出一帧做三件事在原图画框、统计各类别数量、触发告警写日志。def draw_and_alert(frame, result, alert_cnt3): names result.names stats {} for box in result.boxes: cls int(box.cls[0]) conf float(box.conf[0]) x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) label f{names[cls]} {conf:.2f} cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(frame, label, (x1, y1 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) stats[names[cls]] stats.get(names[cls], 0) 1 for name, cnt in stats.items(): if cnt alert_cnt: cv2.putText(frame, fALERT: {name} x{cnt}, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 3) return frame, stats这里有个细节result.boxes.xyxy 返回的是 Tensortolist() 之后才是 Python 列表直接拿来给 cv2.rectangle 用没问题但如果你自己写坐标运算记得先把 Tensor 转出来否则小数点精度会莫名其妙多出一串。告警阈值 alert_cnt 设为 3表示同一类目标出现三个以上才触发实际使用中可以根据宿舍人数调整否则走廊里路过几个人就乱报警。日志落盘一般追加写入 CSV格式是时间戳、类别、数量、置信度均值。做毕设答辩时这份记录可以直接当运行数据展示比如统计一天内哪种行为出现频次最高比单纯喊检测准确有说服力得多。界面端的告警列表可以用 QTableWidget 实时刷新每触发一次插一行数据量大了之后记得限制最大行数否则界面会越跑越慢。4. 部署与运行常见问题排查五个让新手翻车的坑这五个坑是我看过的项目里出现频率最高的每一跳都是血泪经验换来的按现象、原因、解决的顺序过一遍省得你自己从头踩。4.1 pip 装完 ultralytics 后 import 报错现象按照 README 执行 pip install ultralytics 后import ultralytics 报 AttributeError或者提示 No module named torchvision。原因绝大多数是 torch、torchvision 和 ultralytics 三者的版本错位。ultralytics 新版本会对 torch 版本有要求直接裸装容易装到不匹配的组合另外 Python 3.8 以下的旧解释器也不支持新版本。解决不要往系统 Python 里装先建独立环境。conda create -n yolo python3.10然后 pip install ultralytics让它自动拉取匹配的依赖如果安装后仍报 torchvision 缺失单独 pip install torchvision 并检查 torch.version是否与 torchvision 配套。环境隔离能挡掉八成此类问题那三小时的环境配置玄学一大半都是混装造成的。4.2 训练时 CUDA out of memory现象训练命令刚跑起来第一个 epoch 都没结束就报 CUDA out of memory有时连显卡驱动一起崩掉。原因显存被 batch 和 imgsz 联手吃满。宿舍数据集图片分辨率不高但 640x640 的输入加 16 的 batch在 6G 显存卡上已经很紧张。解决先减 batch从 16 降到 8 再降到 4还不行就把 imgsz 从 640 降到 480。经验值是 GTX 1660 Ti 这类 6G 卡batch 8 加 imgsz 640 可以稳定跑完如果非要大 batch可以缩小 imgsz 或者换个显存更大的卡。有一点要注意减免 batch 后学习率最好同步下调否则收敛会变慢loss 曲线会明显比之前飘。4.3 打开摄像头界面卡死现象点击打开摄像头后窗口无响应拖不动操作系统弹未响应。原因把推理写进了按钮槽函数YOLO 推理阻塞了 GUI 事件循环。这在上一章已经说了根因但实际项目里最容易犯的是另外一个线程写了一半处理完的帧又直接在子线程里 setPixmap导致 Qt 崩溃。解决严格遵循线程分工推理线程只往 queue 里放帧界面线程用 QTimer 定时从 queue 取帧并刷新。如果界面线程消费不过来丢帧即可不要扩大队列容量画面卡顿比丢帧更影响演示效果两者权衡选丢帧。4.4 检测框位置错乱现象框能画出来但框的位置和物体对不上有的框偏到了画面角落。原因推理时 YOLO 会把输入图 letterbox 缩放到 640x640检测出的坐标是缩放后图像上的坐标直接在原图上画坐标必然错位。这个问题在接触 YOLO 不到一个月的人身上几乎必现。解决不要自己手动转坐标直接用 result.plot() 拿画好框的图像或者用 result.boxes.xyxy 结合 letterbox 参数还原。最省事的做法是让 ultralytics 自己处理把结果对象的 plot() 结果转成 QImage 显示一张图最多多花几毫秒换来的是坐标永远正确这个成本完全值得。4.5 加载权重报 size mismatch现象model YOLO(best.pt) 这一行报 RuntimeError提示 state_dict 里大量 key 的尺寸不匹配。原因改过网络结构。比如为了提精度在 yaml 里插了注意力模块之后直接加载原版预训练权重层名和尺寸匹配不上。解决加载预训练时用 strictFalse只加载能匹配的部分其余层随机初始化或者干脆不加载预训练权重从头训练。对宿舍这类样本量不大的场景更推荐的做法是先用原版结构训一个 baseline确认指标以后再改结构这样对比才有意义否则你没法解释精度提升到底来自结构改进还是数据运气。5. 把模型精度再往上提注意力模块、数据增强与验收验证技巧5.1 在 YOLOv8 结构里加一个注意力模块如果想在 baseline 上提几个点 mAP最轻量的改动是在主干后面加一个 CBAM 注意力模块。Ultralytics 支持在 yaml 里直接引用自定义模块做法是在 ultralytics/nn/modules.py 里注册 CBAM 类然后在模型 yaml 的 backbone 尾部插入一行backbone: # ... 前面的层省略 - [-1, 1, Conv, [256, 3, 2]] - [-1, 1, CBAM, []] - [-1, 1, SPPF, [256, 5]]这样改完加载预训练权重必须 strictFalse因为新增层的参数在预训练里不存在。宿舍这类小目标占比不高的场景CBAM 能小幅提升 mAP50但推理耗时也会增加值不值取决于你答辩想突出什么。5.2 数据增强与训练策略微调不换结构也能提指标直接用命令行覆盖增强参数。imgsz 提到 960 能改善小目标召回配合 hsv 颜色扰动缓解不同光源下的泛化问题yolo detect train modelyolov8n.pt datadataset.yaml imgsz960 batch8 epochs120 hsv_h0.015 hsv_s0.7注意 imgsz 提到 960 后 batch 要相应减半否则显存必爆翻转、旋转这类几何增强对玩手机这种朝向敏感的目标反而有害别乱加。5.3 用验证集结果判断模型能不能交差答辩前跑一次干净验证yolo detect val modelruns/detect/trainX/weights/best.pt datadataset.yamlmAP50 到 0.85 以上就能交差。损失曲线直接读训练记录画出来import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/trainX/results.csv) df.columns [c.strip() for c in df.columns] plt.plot(df[epoch], df[train/box_loss], labeltrain) plt.plot(df[epoch], df[val/box_loss], labelval) plt.legend() plt.show()val 曲线后期明显回升就是过拟合信号这时候别说 mAP泛化都成问题。答辩最容易被问你的改进有效吗拿不出曲线和指标老师默认你只跑了默认配置。从那以后我每次拿别人的项目第一件事都是先看 results.csv 和 val 指标确认 baseline 干净可复现再动手改结构。这份资源把数据集、权重和界面都备齐了省掉的是整理标注和环境折腾的时间但结构、参数这些依然值得你亲手改一遍。希望帮到你。本文还有配套的精品资源点击获取