
简介面向计算机视觉与人工智能方向的毕业设计及课程设计开发者这份基于YOLOv8的校园安全监控系统资源提供了从源码、完整数据集到可视化界面与部署教程的一站式方案。代码经个人毕业设计实测运行通过可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率召回率曲线、验证集预测结果及标签分布图适合快速搭建演示或二次开发。压缩包共97个文件以70个Python脚本为主辅以12个pyc缓存文件、4个PyTorch权重文件、5个XML配置文件、2个TXT说明文件及部分图标视频素材整体大小24.21MB目录涵盖检测服务、模型训练、UI界面、配置项与README便于按模块查阅。目前已有51人学习下载适合计算机、人工智能、通信、自动化等专业学生用于毕业设计、课程设计或项目初期立项演示也可作为目标检测入门到进阶的参考。1. 校园安全监控为什么绕不开YOLOv8从毕设题目到一个能跑的系统毕设题目里写下“基于YOLOv8的校园安全监控系统”时大多数人最担心的不是模型训练而是这东西最后能不能在答辩现场跑起来。实际做下来你会发现YOLOv8把目标检测压缩到了“下预训练权重→标数据→finetune→部署”四步真正耗时的反而是界面、报警、记录这些看起来不起眼的工程活。这篇笔记就是把整条路完整走一遍从模型选型、数据集构建、训练参数到可视化界面和部署排错每一步都给出可以直接照做的命令和代码。适合课程设计和本科毕设也适合第一次接触YOLO系项目的同学照着搭一套能演示、能扩展的监控系统。那些在真实项目里用血泪经验换来的边界条件我也会一并说清楚。2. 认识YOLOv8与模型选型动手前先想清楚的四个问题2.1 五个模型规格怎么选n/s/m/l/x不是越大越好YOLOv8按网络深度和宽度分成n、s、m、l、x五个规格。n的参数量大约3.2Ms大约11.2Mm大约25.9Ml大约43.7Mx大约68.2M。从n到x参数量差了二十倍平均精度mAP50-95大概从37%涨到54%但推理延迟也从1毫秒级涨到接近10毫秒级。校园监控场景里主要目标是完整尺度的人、自行车、汽车而不是无人机视角下的小目标密集场景所以精度差距带来的收益并不明显。规格参数量(约)COCO mAP50-95(约)单张GPU推理耗时(约)适用场景YOLOv8n3.2M37.31.2msCPU推理、低配机器YOLOv8s11.2M44.92.1ms毕设演示、通用监控YOLOv8m25.9M50.24.0ms检测精度优先YOLOv8l43.7M52.96.5ms离线分析、不赶实时YOLOv8x68.2M53.99.0ms学术刷分、暂不建议我的选型逻辑比较固定做毕设演示选s追求交互流畅选n答辩想拿高分选m。l和x留给COCO级别的对比实验校园监控用不上。很多人有个误区以为规格越大效果越好实际在几百张自建数据集上训练s和m的差距通常是1到2个百分点的mAP但推理速度差距接近一倍。与其纠结模型大小不如把精力放在数据集质量上数据干净对精度的提升远大于换大模型。2.2 CPU训练还是GPU训练显存预算与训练时长不能拍脑袋很多同学在Windows笔记本上配好了ultralytics环境训练epochs设置成100发现一个epoch要跑20分钟一觉睡醒还在第30个epoch。这是因为CPU训练时数据增强、前向传播、反向传播都压在处理器上。GPU明显更省时间但显存是硬约束。yolov8n在batch size 16、imgsz 640下的显存占用大约4GByolov8s同样设置要6到8GByolov8m直接超过10GB。如果确实只有CPU常见的做法是把imgsz降到480、batch size降到4、epochs降到50并关闭Mosaic增强来缩短训练时间。这样能跑通流程但收敛效果差一些。另一个常见选择是在云GPU上完成训练再把best.pt权重下载到本地做推理。Google Colab免费版给一张T4显卡AutoDL按小时计费训练一个s规格的模型成本在十几到几十元。这里有个容易忽略的点本地推理对显卡要求远低于训练一张老旧的GTX 1660 Ti跑yolov8s也能有20FPS左右所以训练和推理可以用完全不同的机器。2.3 检测、跟踪与行为判断校园监控到底需要什么能力一个能交付的校园安全监控系统核心能力不只是“把人物框出来”而是跟踪和计数。YOLOv8本身是纯检测模型每帧独立推理不保留目标的身份信息。要做人数统计、轨迹绘制、人员滞留报警就需要搭配ByteTrack或BoT-SORT这类跟踪器。ultralytics框架集成了ByteTrack实现detect track命令返回的results对象里直接带track_id这就是在检测基础上做跟踪的最小闭环。行为判断是另一个层级。摔倒检测、区域入侵、打架识别这些“行为”并不能靠YOLOv8一个模型完成。常见做法是再接一个分类模型或者用yolov8s-pose关键点模型提取人体骨架再按几何关系判断是否摔倒。如果题目叫“校园安全监控系统”检测加跟踪已经覆盖了大部分评分点行为判断属于加分项不要把它当成必需模块。把检测精度和跟踪稳定性做到位比硬塞一个不成熟的行为识别更有说服力。2.4 项目目录怎么规划源码、权重、数据集、部署脚本各就各位我见过太多人把所有文件堆在桌面训练三个月后才发现数据集路径写死在自己电脑上换一台机器就全线崩盘。拿到一个YOLOv8校园监控方向的源码包后第一件事不是看代码而是把目录结构理顺。一个干净的项目目录应该是这样campus_security/ ├── app/ # 可视化界面代码 │ ├── main.py # PyQt5入口 │ ├── detector.py # 检测与跟踪封装 │ └── ui/ # 界面资源文件 ├── weights/ │ ├── yolov8s.pt # 预训练权重 │ └── best.pt # 微调后的权重 ├── dataset/ │ ├── images/train/ │ ├── images/val/ │ ├── labels/train/ │ └── labels/val/ ├── runs/ # 训练输出目录 ├── deploy/ │ ├── requirements.txt # 依赖清单 │ └── start.bat # 一键启动脚本 └── README.md这样规划的好处是训练输出统一在runs下界面代码只从weights/best.pt加载模型换数据集时不用改代码。start.bat做成双击启动requirements.txt固定版本能避免大量环境问题。deploy目录里的requirements.txt要写明torch和ultralytics的对应版本这两个库的兼容性直接决定部署是否顺利后面避坑章节会细说。3. 数据集构建与YOLOv8训练从标注到跑通一次完整训练3.1 数据集从哪里来自建标注与公开数据集的取舍标题里提到“完整数据集”落地时有两种路线。第一种是基于公开行人或校园场景数据集做扩充比如从COCO里抽取person类别或使用CrowdHuman这类密集行人数据第二种是自己用labelme标注学校特定场景教室、宿舍、食堂、操场各拍一段视频抽帧后标注person、cellphone、knife、car这几类。毕设评分更看重“对问题的理解”而不是“数据规模”所以自建一部分数据会让整个项目更有说服力。自建数据集的常见做法是用手机拍20到30分钟视频每隔10到15帧抽一帧得到2000到3000张图片。抽帧脚本用OpenCV就能实现不需要额外工具。我一般把训练集和验证集按8比2划分。如果类别不均衡比如knife样本很少就把该类别的图片多复制几份或用简单的复制粘贴增强把正样本数量拉上来。再提醒一句标注质量比数量重要错框、漏框超过5%的数据集训练出来的模型mAP一定上不去。3.2 标注格式转换labelme的JSON怎么变成YOLO能用的txtlabelme标注完每张图会生成一个同名JSON文件里面是shapes数组每个元素是一个多边形。YOLO格式需要的是归一化的中心点x、y和宽高。每个YOLO项目都绕不开这个转换脚本以下代码可以批量处理import json import os import glob def labelme_to_yolo(json_path, output_dir, class_names): os.makedirs(output_dir, exist_okTrue) with open(json_path, r, encodingutf-8) as f: data json.load(f) img_w, img_h data[imageWidth], data[imageHeight] txt_name os.path.splitext(os.path.basename(json_path))[0] .txt lines [] for shape in data[shapes]: label shape[label] if label not in class_names: continue points shape[points] xs [p[0] for p in points] ys [p[1] for p in points] x_min, x_max min(xs), max(xs) y_min, y_max min(ys), max(ys) cx (x_min x_max) / 2 / img_w cy (y_min y_max) / 2 / img_h bw (x_max - x_min) / img_w bh (y_max - y_min) / img_h cx max(0, min(1, cx)) cy max(0, min(1, cy)) bw max(0, min(1, bw)) bh max(0, min(1, bh)) lines.append(f{class_names.index(label)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(output_dir, txt_name), w) as f: f.write(\n.join(lines)) if __name__ __main__: class_names [person, cellphone, knife, car] for json_file in glob.glob(labeled/*.json): labelme_to_yolo(json_file, yolo_labels/, class_names)脚本取多边形所有顶点的最小外接矩形作为检测框中心点和宽高全部除以图片尺寸进行归一化这样训练时无论imgsz设640还是480坐标都不受影响。代码里对cx、cy、bw、bh做了一次0到1的截断防止标注框压到图片边缘时产生超出边界的坐标。还有一个容易踩的坑转换后如果发现某个txt文件是零字节说明那张图没有标注出任何有效类别这类图片在训练中会被当作负样本如果数量多了会压低recall最好单独挪出去。3.3 训练配置data.yaml、batch size与epochs怎么定训练前要写好数据集描述文件它告诉YOLOv8去哪里找图片和标签# dataset/campus.yaml path: ./dataset train: images/train val: images/val nc: 4 names: [person, cellphone, knife, car]path是数据集根目录train和val是相对根目录的子目录。nc必须和names列表长度一致这两处对不上会在训练开始时直接报错。我习惯用绝对路径写path尤其在Jupyter里跑的时候相对路径容易受到当前工作目录影响。启动训练的命令如下yolo detect train \ datacampus.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ patience20 \ projectruns/train \ namecampus_exp1model指定预训练权重如果改成yolov8m.pt就会在s基础上做迁移学习。patience20表示连续20个epoch验证集mAP没有提升就提前终止这能省不少训练时间。batch受显存约束8GB显存跑s规格时可以从16开始报CUDA out of memory就降到8或4。如果训练过程中loss曲线剧烈震荡优先怀疑batch太小其次再看学习率设置。3.4 训练启动与损失曲线判读别等到训练完才发现模型没收敛用上面命令启动训练后整个过程通常会自动输出进度条。完成后的结果在runs/train/campus_exp1目录下重点看两个东西best.pt和results.csv。results.csv每一行是一个epoch的loss和mAP用下面这段脚本可以快速检查训练是否收敛import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/campus_exp1/results.csv) df.columns [c.strip() for c in df.columns] plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.plot(df[epoch], df[train/box_loss], labelbox_loss) plt.legend() plt.subplot(1, 2, 2) plt.plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) plt.legend() plt.tight_layout() plt.savefig(loss_curve.png, dpi150)判断标准很简单box_loss逐步下降且没有在后期大幅反弹mAP50曲线趋于平缓说明训练正常。如果mAP50在训练末尾还在明显爬升说明epochs不够可以继续加到150或200。如果mAP50从第20个epoch就开始原地抖动多半是学习率太大或数据集噪声太多可以把lr0从默认的0.01调到0.005再训练一次。这里还有个常见误用只看训练集loss不看验证集指标训练loss降得很低但验证mAP上不去就是过拟合了需要加数据增强或减小模型规格。4. 可视化界面与系统集成把模型封装成能交付的监控系统4.1 界面技术选型PyQt5加OpenCV为什么比Web方案更适合毕设做界面主流选择是PyQt5加OpenCV。PyQt5负责窗口、按钮、表格这些UI元素OpenCV负责视频帧读取和格式转换。为什么不选Web方案因为课程设计和毕设的部署环境通常是Windows单机浏览器方案要额外起Flask或Django服务前端还要写页面工作量翻倍。PyQt5的QMainWindow配合Qt Designer拖控件半天就能搭出一个监控后台的样子。界面布局建议至少包含三个区域左侧实时视频画面右上角检测结果列表右下角统计面板。视频画面用QLabel显示每次把OpenCV的BGR帧转成RGB再setPixmap。如果觉得这些细节麻烦可以直接用ultralytics的predict方法返回的results[0].plot()已经画好了框省去手动画框的步骤。判断界面是否合格的标准很简单切换视频源不用重启程序报警时能在画面上看到高亮框统计数字实时更新。4.2 摄像头接入与视频流处理别把主线程卡死最常见的翻车现场是把视频循环写进QMainWindow的主线程结果界面假死。正确做法是开一个QThread子线程做视频读取和检测主线程只负责接收信号刷新画面。核心代码如下# app/detector_thread.py from PyQt5.QtCore import QThread, pyqtSignal import cv2 from ultralytics import YOLO class DetectThread(QThread): frame_ready pyqtSignal(object) def __init__(self, model_path, source0, conf0.5): super().__init__() self.model YOLO(model_path) self.cap cv2.VideoCapture(source) # 0表示本机摄像头也可传视频文件路径 self.conf conf self.running True def run(self): while self.running and self.cap.isOpened(): ret, frame self.cap.read() if not ret: break results self.model.predict(frame, confself.conf, verboseFalse) annotated results[0].plot() self.frame_ready.emit(annotated) self.cap.release() def stop(self): self.running False self.wait()run方法里read一帧、predict一帧、emit一帧循环速度受推理时间限制。verboseFalse让控制台不再刷上千行推理日志。信号里传object类型是因为annotated是numpy数组PyQt的信号类型匹配容易出问题传object最省事。在应用加载的时候把模型初始化放在__init__里而不是run里否则每次启动线程都会重新加载权重启动时间会翻好几倍。主线程收到信号后用QLabel的setPixmap显示画面槽函数里不要再做重计算。4.3 功能模块拆解检测、报警、记录、统计怎么组织代码一个能毕业的可视化界面至少要有报警和记录两个模块。报警通常是指定类别检测到时播放提示音或弹窗记录则是把检测结果写入CSV并保存截图。这两件事如果在检测线程里直接做会拖低帧率。常见做法是单独维护一个事件队列检测线程只往里塞事件主线程定时器统一消费。# app/alert_manager.py import csv import time import cv2 class AlertManager: def __init__(self, alert_classes, log_pathalerts.csv): self.alert_classes alert_classes self.log_path log_path def handle(self, detections, frame): triggered [d for d in detections if d.cls in self.alert_classes] if not triggered: return with open(self.log_path, a, newline) as f: writer csv.writer(f) for d in triggered: writer.writerow([time.time(), d.cls, d.conf]) x1, y1, x2, y2 map(int, d.xyxy[0].tolist()) cv2.imwrite(fcapture_{time.time()}.jpg, frame[y1:y2, x1:x2])这个Manager类的实例放在主线程检测线程只传回检测结果和当前帧避免把文件IO压到视频循环里。统计模块最实用的是人数统计在跟踪模式下track_id相同且类别为person的框视为同一人用一个set按track_id去重就能得到当前画面人数。如果想做区域人数统计用鼠标画一个ROI多边形再用cv2.pointPolygonTest判断检测框中心是否落在区域内。这个功能演示效果很好代码量也不大。4.4 打包与交付PyInstaller打包成exe的几个细节代码在自己电脑跑得好好的拿到答辩机器上发现缺库、版本冲突这种场面每年毕业季都要重演无数次。提前用PyInstaller打包成exe能避免大部分环境问题。打包命令如下pip install pyinstaller pyinstaller -D -w -n CampusSecurity \ --collect-all ultralytics \ --add-data weights/best.pt;weights \ app/main.py-D生成文件夹模式而不是单文件模式单文件模式启动时要解压校园监控这种带模型权重的项目启动能慢十几秒。--collect-all ultralytics把检测库的依赖资源全部收进去少了这步打包后的exe经常在运行时提示找不到模型文件或字体资源。打包完成后exe所在目录下会多出库文件夹和资源文件夹把weights目录复制到exe同级即可。目标机器上没有显卡也能跑CPU推理yolov8s在CPU上大概3到5FPS演示时选静态图片或视频文件作输入源更稳妥。如果目标机器带宽受限先在打包机上跑一遍test模式确认模型、摄像头、日志目录三个路径都没问题。这里我给个后悔药的建议打包前在requirements.txt里固定ultralytics的版本比如ultralytics8.2.x不同版本的输出接口有小差异固定版本能避免重装后代码接口不匹配。5. 部署与运行避坑指南环境、训练、推理与打包的五个高频问题5.1 环境装好却用CPU推理GPU完全不生效现象训练或推理时控制台输出Using CPU明明装了CUDA和cuDNN。原因多数情况是torch安装的版本是CPU版。Ultralytics本身是纯Python库它调用的是torch的底层接口如果torch在安装时没有匹配的CUDA编译版本就只能退到CPU计算。另外CUDA驱动版本和torch编译版本不匹配也会导致同样的问题比如torch的cu121版本要求驱动支持CUDA 12.1驱动太老就检测不到显卡。解决先跑一行命令确认问题。python -c import torch; print(torch.cuda.is_available())输出False就说明torch没有吃到GPU。最稳的修复方案是卸载torch后用PyTorch官网给的命令重装比如安装支持CUDA 12.1的版本pip uninstall torch torchvision pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121安装完成后再次验证is_available是否为True。如果已经是True但ultralytics仍显示CPU检查一下环境变量CUDA_VISIBLE_DEVICES有没有被意外设置这个变量会把GPU藏起来。5.2 训练时显存溢出batch改小还是报错现象训练脚本跑起来几秒钟就弹出RuntimeError: CUDA out of memory把batch从16降到8仍然报错。原因显存占用由batch、imgsz、模型规格三者共同决定很多人只降batch不降imgsz在imgsz640时即使batch4yolov8s也要占5GB左右。如果数据集图片比较大数据加载阶段还要额外占一部分显存。另一个常见因素是机器上其他程序占用了显存Windows上浏览器看视频、设计软件后台都会吃GPU显存。解决按顺序做三件事。第一关闭所有无关软件后重试。第二把imgsz降到480这一步对显存影响最直接。第三把workers设为0避免数据加载子进程额外开辟显存。如果还不行把模型规格降为yolov8n再试。还有一个判断技巧看报错日志里的Free Memory提示如果剩余显存显示几MB或者干脆为0说明是进程占用问题如果剩余好几个GB还报错那大概率是PyTorch显存碎片问题重启内核再训练就能解决。5.3 界面卡顿视频画面明显延迟现象画面能出来但像放幻灯片点击按钮要等一两秒才响应视频流延迟越来越大。原因检测帧率本身低加上检测线程和主线程之间信号发送频率太高UI事件循环来不及处理。PyQt5的信号槽机制是队列模式如果每帧都emit一次主线程槽函数还在处理上一帧后面的信号就会积压最终表现为延迟和内存缓慢增长。解决在检测线程里加限频逻辑每隔2帧才emit一次。同时把输入帧用cv2.resize统一缩到1280或960宽度再送入模型推理耗时能明显下降。界面卡顿是PyQt5做实时视频的通病不要追求每帧都刷新人眼能接受的流畅度大约15到20FPS就够用。如果坚持要更高帧率可以把视频显示分辨率降为640宽检测分辨率保持1280两套分辨率互不影响。5.4 同一目标反复框选人数统计乱跳现象画面中的同一个行人track_id在几秒内从3跳到27再跳到40统计面板的人数频繁变化实际画面里只有两个人。原因ByteTrack匹配的底层逻辑依赖检测置信度。置信度阈值设得太低低质量检测框会不断产生新的track_id置信度设得太高目标短暂遮挡会导致跟踪丢失重新出现时又会分配新id。跟踪参数和检测阈值没有联动调整就会出现id抖动。解决把置信度阈值从默认的0.25提高到0.4或0.45先过滤掉低质量的检测框。然后调整跟踪器的track_buffer参数track_buffer表示目标消失多少帧后删除轨迹默认值是30调大到60可以让目标在短暂遮挡后维持原id。调用方式如下results model.track(frame, conf0.4, persistTrue, trackerbytetrack.yaml, track_buffer60)persistTrue在视频流里能保持各帧的跟踪状态不写这个参数的话每帧都是一次全新跟踪id会全乱。这里也提醒一下教室门口有柜机遮挡时id跳变无法完全消除这是单目视觉跟踪的固有局限答辩时可以坦诚说明。5.5 打包后的exe被杀毒误报或目标机器打不开现象PyInstaller打包的exe在部分电脑上被拦截双击直接没有反应另一部分机器提示缺少DLL。原因PyInstaller打出来的exe经常被Windows Defender误判尤其是用-F单文件模式打包时解压到临时目录并运行的特性很容易触发启发式检测。缺少DLL则通常是因为打包机上缺少Microsoft Visual C Redistributable运行库exe启动时找不到对应的运行时组件。解决改用-D文件夹模式打包误报率会低一些。目标机器安装VC_redist.x64.exe运行库这个在微软官网能直接下载。如果还是被误报把整个打包目录加入杀毒软件信任区。遇到exe双击没反应打开命令行手动运行exe80%的情况下能看到明确的报错信息这是排查这类问题最快的路径。部署前先在干净虚拟机上做一遍完整测试确认没有依赖当前机器的环境变量。6. 答辩前的最后一公里用验证集数据说话6.1 val模式跑一遍mAP50、mAP50-95、precision、recall拿到模型后先别急着演示用验证集给模型一个客观评价yolo detect val \ modelweights/best.pt \ datacampus.yaml \ batch16 \ projectruns/val \ namefinal_check跑完看runs/val/final_check目录下的results.csv重点记录mAP50、mAP50-95、precision和recall。同时把验证集里预测错误的图片翻出来看漏检的多是小目标或被遮挡目标误检多是把书包或柱子当成人。这些案例在答辩时反而是加分项因为它们能说明你知道模型边界在哪里而不是把模型当黑匣子。6.2 场景化验证三个典型场景的FPS与误报率校园监控系统要证明自己“能用”建议实测三种场景教室门口静态场景、走廊走动场景、夜间低照度场景。录制视频后跑推理统计每段视频的FPS和误报次数。实测数据通常是这样yolov8s在RTX 3060上能到45到60FPSCPU上只有3到6FPS夜间场景由于训练数据里夜视图少误报率明显翻倍。这不是模型坏了是数据分布问题说明训练集需要补充夜间样本。把这三个场景的数据整理成表格放进论文里比百分之二点几的mAP提升更有说服力。6.3 我自己的一点习惯与提醒每次训练新模型我都会把训练日志、loss曲线图、验证集结果csv和三个场景的实测视频放到同一个目录下命名带上日期和模型规格。两周后翻出来还能记得当时改了什么参数答辩被问到“为什么选这个置信度阈值”时也能拿出对比数据。做监控系统这类毕设最容易翻车的地方不是模型训练而是演示环节的环境不一致提前一天在答辩机器上把整套流程跑一遍这个习惯帮我避开了太多答辩现场的意外。希望帮到你。本文还有配套的精品资源点击获取