简介:面向计算机视觉、人工智能等专业毕业设计或课程设计场景,这是一套基于YOLOv8的智能门禁系统,自带源码、数据集、可视化界面与部署教程,可快速搭建完整应用。压缩包共97个文件,以70个Python源码文件为主体,覆盖目标检测模型训练、推理服务、UI交互等模块,另含4个预训练权重、标注与配置文件、演示视频等,整体仅24.21MB,轻量易部署。资源目前已有52人学习下载,代码均由作者实测通过,运行后即可生成核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图等关键数据,足以支撑毕设答辩的高质量展示。配套的README与部署说明,加上界面录屏视频,能帮助不同基础的学习者从数据准备一路推进到界面启动,拿来即用,也方便在此基础上做功能扩展。
1. 为什么门禁系统都开始用YOLOv8:一个能演示、能部署的检测底座
答辩前一周,手里这套“基于YOLOv8的智能门禁系统”最怕的不是模型不收敛,而是解压后第一步就跑不起来。这类压缩包通常包含训练好的权重、门禁场景数据集、PyQt可视化界面和一份部署文档,理想状态是装好环境、点开运行、摄像头一开就能演示。但真正卡人的往往是 CUDA 版本、数据集路径、界面线程这些看不见的细节。YOLOv8 在门禁系统里的职责很清晰:对摄像头画面里的人脸或人体做实时目标检测,把检测框交给后续名单比对逻辑,再在界面上完成开门记录。它解决的是“门口有没有人、是谁”的前置感知问题,也是整个系统能不能动起来的核心。这篇笔记适合正在做毕设或课程设计的同学,也适合想从训练走到部署的入门开发者,目标是用一条最短路径把项目跑通、调好、演示成功。
2. 先把环境立住:CPU/GPU 选型与最小推理命令
2.1 先定环境组合:别一上来就装 CUDA
门禁系统的检测模型不算重,常见做法是用 YOLOv8n 或 YOLOv8s 这种轻量权重,所以环境选型的首要原则是“够用就好,别追求顶配”。我见过太多人被 CUDA 版本折磨:显卡驱动是新的,pytorch 装成 CPU 版,训练半天一看 loss 不降。动手之前先执行 nvidia-smi 看驱动支持的 CUDA 版本,再决定 pytorch 怎么装,这一步能省下大半天。
如果你是 ubuntu20.04 搭 yolov8 环境做练习,手头又没有独立显卡,直接装 CPU 版 pytorch 是可行的,推理一张 640x640 的图大约 300 到 500ms,跑通流程没问题,只是训练会慢到让人怀疑人生。反过来,如果有一张 GTX1660Ti 这种 6GB 显存的卡,就可以正常训练和演示了。这里给一个参考表:
| 环境组合 | 推理 640x640 延迟 | 适合做的事 |
|---|---|---|
| 笔记本 CPU | 300ms 以上 | 验证代码、跑通流程、调界面 |
| GTX1660Ti / 6GB | 30~60ms | 正常训练、本地演示 |
| 无显卡但内存大 | 慢但能跑 | 纯代码学习、数据集处理 |
确定硬件后,下一步是建环境。我一般用 conda 管理,避免和系统 Python 打架。以下命令在 Windows 和 Ubuntu 上都适用,唯一区别是 pytorch 的安装源。
# 创建独立环境,Python 3.10 对 ultralytics 兼容性最稳 conda create -n yolodoor python=3.10 -y conda activate yolodoor # CPU 版直接这样装;有 NVIDIA 显卡就先按官网装对应 CUDA 版本 pip install ultralytics # 检查安装是否完整 python -c "import ultralytics; print(ultralytics.__version__)"这里的关键是环境隔离。课程设计和毕设项目往往同时依赖多个框架,TensorFlow 和 PyTorch 混装是常见翻车现场,用独立环境能少很多口头纠纷。pip install ultralytics 会自动带 torch、torchvision、opencv 这些核心依赖,不需要手动逐个装。装完先打一条 import 语句确认没有报错,再做下一步。
2.2 最小推理命令:先证明链路是通的
环境装好之后,不要急着训练,先跑一次官方预训练权重推理。这一步的目标不是精度,而是验证“模型能加载、图像能读取、结果能输出”这条链路。我把这步叫作“先跑通再优化”,它也是整份项目压缩包能否做到“简单部署即可运行”的试金石。
# 用官方 yolov8n 权重对一张测试图做检测 yolo detect predict \ model=yolov8n.pt \ source=test_face.jpg \ conf=0.25 \ imgsz=640 \ save=True第一次执行会自动下载 yolov8n.pt。conf 是置信度阈值,0.25 是官方推荐的默认值,门禁场景下我一般不会调高到 0.5 以上,否则逆光或半遮挡的人脸容易被滤掉。save=True 会在当前目录生成 runs/detect/predict 文件夹,里面是带框的结果图。看到这张图,说明你的环境和模型链路已经通了,后面所有步骤都有地基。
跑通之后,再看手头这份项目 zip 的结构。这类压缩包的目录划分通常是 models(权重)、datasets(数据)、ui(界面)、docs(部署文档)四部分,具体文件名以包内说明为准。我的建议是按“先看 docs 里的环境要求,再跑 datasets 上的推理脚本,最后打开 ui 入口文件”的顺序检查,比直接双击 main.py 更不容易踩坑。顺序反了,你会以为是源码坏了,其实只是环境没对齐。
3. 门禁数据集:类别设计、标注转换与训练集划分
3.1 门禁场景要检测什么:单类还是多类
数据集是这类项目里最容易被低估的部分。很多人拿到一个通用人脸数据集就开跑,结果发现门禁摄像头是俯视角、门口有逆光、人脸占比忽大忽小,模型的检测能力在演示现场全部失效。门禁场景的数据设计,核心问题是“你希望模型输出什么”。
常见的做法有两种。第一种是只检测“人”,用 YOLOv8 做行人检测,再配合位置判断“人是否站在门口区域”,这种方案数据好标、模型稳,适合课程设计演示。第二种是检测“人脸”,再叠加人脸比对实现“是谁”,数据要求更高,但更像真实门禁。两种方案在 YOLOv8 里没有本质区别,差异全在标注类别上。我一般建议毕设选第一种,把识别逻辑放在后处理里,检测模型只负责“有没有人”,这样训练简单、演示也稳定。
公开的人脸数据集很多,但直接拿来做门禁训练要留意视角问题。我见过一个翻车案例:训练集全是正面大头照,测试时摄像头装在门上方,拍到的是头顶和肩膀,模型完全认不出来。解决办法是用实验室、宿舍门口这类场景自己拍 200 到 300 张图补充进数据集,比单纯堆公开数据有效得多,这部分血泪经验在你做完第一次现场演示后会体会更深。
3.2 用 Labelme 标注并转成 YOLO 格式
Labelme 在标注工具里属于“上手零门槛”的那一类,画框、切下一张、导出 JSON 的流程不赘述。但 Labelme 导出的 JSON 和 YOLOv8 需要的 txt 格式不一样,一个是多边形点列表,一个是归一化中心点加宽高,必须做转换。这也是检索里“labelme 标注用于 yolov8”出现频率高的原因。
# labelme_to_yolo.py # 把 labelme 的 rectangle 标注转成 YOLO 格式的 txt import json, os, glob # 类别顺序必须和 datasets/data.yaml 保持一致 class_map = {"person": 0} img_w = 1920 # 图片宽度,改成你自己数据集的 img_h = 1080 # 图片高度 img_dir = "labelme_output" # 存放 json 的目录 for json_path in glob.glob(os.path.join(img_dir, "*.json")): with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) txt_path = json_path.replace(".json", ".txt") lines = [] for shape in data["shapes"]: if shape["label"] not in class_map: continue if shape["shape_type"] != "rectangle": continue x1, y1 = shape["points"][0] x2, y2 = shape["points"][1] cx = ((x1 + x2) / 2) / img_w cy = ((y1 + y2) / 2) / img_h w = abs(x2 - x1) / img_w h = abs(y2 - y1) / img_h # 过滤掉太小的框,减少无效目标对训练的干扰 if w < 0.01 or h < 0.01: continue lines.append(f"{class_map[shape['label']]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}") with open(txt_path, "w", encoding="utf-8") as f: f.write("\n".join(lines)) print(f"converted: {json_path}")这个脚本的要点有三个:一是类别映射,class_map 里的数字必须和 data.yaml 里 classes 的索引完全一致,否则训练时 loss 直接起飞;二是坐标归一化,YOLO 要求 cx/cy/w/h 都在 0 到 1 之间,分母必须是图片实际宽高,不能用统一假值;三是过滤小目标,门禁俯视角下远处的人可能只有几十像素,这类框转出来后 w/h 小于 0.01,留着只会增加正样本噪声。
3.3 训练集划分:固定随机种子才能复现
数据集划分看起来简单,实际上一句 random.shuffle 就能埋坑。如果不固定随机种子,每次划分结果都不同,你实验 A 得到的 val 精度和实验 B 之间根本没有可比性,这就是典型的“换了数据分法,结果对不上”的玄学现场。固定 seed 并做 70/20/10 划分是通用惯例。
# split_dataset.py import os, random, shutil img_dir = "datasets/wall_door" # 原始图片 label_dir = "datasets/labels" # 转换好的 txt out_root = "datasets" imgs = [f for f in os.listdir(img_dir) if f.endswith(".jpg")] random.seed(42) # 固定种子,保证每次划分结果一致 random.shuffle(imgs) train = imgs[: int(len(imgs) * 0.7)] val = imgs[int(len(imgs) * 0.7): int(len(imgs) * 0.9)] test = imgs[int(len(imgs) * 0.9):] for split, names in [("train", train), ("val", val), ("test", test)]: out_img = os.path.join(out_root, split, "images") out_lab = os.path.join(out_root, split, "labels") os.makedirs(out_img, exist_ok=True) os.makedirs(out_lab, exist_ok=True) for name in names: shutil.copy(os.path.join(img_dir, name), os.path.join(out_img, name)) txt_name = name.replace(".jpg", ".txt") shutil.copy(os.path.join(label_dir, txt_name), os.path.join(out_lab, txt_name))划分后需要检查一件事:每个子集里 txt 和 jpg 一一对应,不能出现有图没标注、有标注没图的情况。YOLOv8 对缺失标注的处理方式是跳过这张图,你以为是脏数据,实际上是数据没对齐。检查方法很简单,在终端里对每个子集数一下文件数量,jpg 和 txt 数量不一致的就是有问题的那一组。
数据增强我建议交给训练参数,而不是自己写脚本。YOLOv8 内置了 mosaic、hsv_h、hsv_s、fliplr 等增强,门禁场景下把 hsv_h 调大一点能提升对不同环境光照的鲁棒性,这部分在第 4 章参数里会再展开。数据量只有几百张的项目,增强不是越多越好,翻转和色彩扰动够用就行。
4. 训练与调参:让 YOLOv8 在门禁数据上收敛
4.1 训练命令:从预训练权重开始迁移
“yolov8 训练自己的数据集”是检索频率最高的问题,本质上是三步:准备 data.yaml、放好目录结构、跑训练命令。data.yaml 是训练入口,告诉模型类别数、类别名和三个子集的位置。注意 data 路径建议写绝对路径,写相对路径换目录跑会报 dataset not found。
yolo detect train \ model=yolov8n.pt \ data=datasets/data.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=15 \ project=runs/detect \ name=door_v1model 指定 yolov8n.pt 而不是从头训练,好处是用了 COCO 预训练权重里学到的底层特征。门禁数据集通常只有几百张,从头训练基本不可能收敛到可用精度。epochs 100 对门禁这种小数据集偏多,配合 patience=15 做早停,也就是连续 15 个 epoch 验证集指标不提升就自动结束,省时间也能防止过拟合。project 和 name 用来指定输出目录,训练日志和权重都会落在 runs/detect/door_v1 下。
4.2 门禁场景的必调参数
参数是训练环节里最容易被玄学化的部分。我的观点是:先记住一组能用的默认组合,再按自己数据集的痛点去改,不要每个参数都动一下。下面是门禁场景的参考参数表:
| 参数 | 建议值 | 调整逻辑 |
|---|---|---|
| imgsz | 640 起步,人脸小就上 960 | 分辨率翻倍,显存和耗时约翻四倍 |
| batch | 显存不够就减半 | 优先不 OOM,GTX166Ti 这类 6GB 卡用 16 即可 |
| conf | 推理时 0.25 | 门禁逆光场景调太高会漏检 |
| hsv_h | 0.015 起,可加到 0.05 | 模拟早晚不同色温,提升场景泛化 |
| fliplr | 0.5 | 门口左右对称,水平翻转几乎无损 |
| close_mosaic | 10 | 最后 10 个 epoch 关闭 mosaic,让目标特征学习更充分 |
单独说一下 imgsz。门禁摄像头画面里,人脸占比通常不大,如果 imgsz 只有 320,人脸可能连 20 个像素都不到,检测器基本看不见。把训练和推理的 imgsz 同时调到 960,对小目标检测有明显改善,代价是训练时间和显存占用上升。如果你用的是 YOLOv8n 这种轻量模型,960 分辨率在 GTX166Ti 上是能跑动的。
4.3 损失曲线:训练完再看都是事后后悔药
训练结束不等于万事大吉,还要学会看训练日志。YOLOv8 每个 epoch 都会把 loss 和 mAP 写进 runs/detect/door_v1/results.csv,画出来比裸看打印日志直观得多,这也是“yolov8 画损失函数曲线图”这个检索需求最常见的实现方式。
# plot_loss.py import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/detect/door_v1/results.csv") # ultralytics 的列名带空格,先清理再取列 df.columns = [c.strip() for c in df.columns] fig, ax = plt.subplots(1, 3, figsize=(12, 3.5)) for i, col in enumerate(["train/box_loss", "train/cls_loss", "train/dfl_loss"]): ax[i].plot(df["epoch"], df[col], label=col) ax[i].set_xlabel("epoch") ax[i].set_title(col) ax[i].legend() plt.tight_layout() plt.savefig("loss_curves.png", dpi=150) print("loss curves saved")怎么看这张图:box_loss 和 dfl_loss 应该是单调下降后趋于平缓,如果 box_loss 降到底部后开始反弹,说明训练过头了,应该用早停前的权重,也就是 best.pt 而不是 last.pt。single-class 检测里 cls_loss 参考意义不大,更多是看真实场景的误检率。把 best.pt 和 last.pt 分别跑一遍验证集,mAP 差很多的那一份就是过拟合的产物。
5. 可视化界面与高频问题排查:从控件布局到线程卡死
5.1 界面功能拆解:门禁界面最少要有什么
可视化界面是这类项目的门面,答辩时老师打开界面第一眼看到的是“能不能实时显示画面、有没有识别框、有没有记录表”。常见实现以 PyQt5 为主,组件层面需要一个摄像头画面区、一个开门状态指示(允许或拒绝)、一个进出记录表格,再加一个模型加载和启动按钮,就足以覆盖演示场景。
我见过不少界面上堆了十几个控件,结果核心识别逻辑反而没做通。界面设计的原则应该是“功能完整、操作简单”,这八个字也是这类项目简介里出现频率最高的描述。下面是一个最小组件清单:
| 控件 | 作用 | 关键参数 |
|---|---|---|
| QLabel | 显示摄像头画面帧 | setPixmap 转 QImage |
| QPushButton | 启动或停止识别 | 信号连接 worker 启停 |
| QTableWidget | 进出记录 | 行数随时间增长,注意清理旧行 |
| QLineEdit | 门口区域范围(可选) | 决定“是否处于门口”的判断阈值 |
组件顺序建议从上到下:画面、状态、记录、按钮。这样演示时老师第一眼看到的是识别效果,而不是一堆输入框。按钮状态要处理:识别中禁止重复点击“启动”,否则会同时开两个摄像头线程,画面直接花掉。
5.2 推理线程与信号槽:界面卡死的标准解法
界面最容易翻车的地方不是控件布局,而是把模型推理写进了 UI 线程。YOLOv8 在 CPU 上推理一帧 640x640 的图要几百毫秒,如果点击按钮后界面直接卡住,大概率是这里出了问题。正确做法是把推理放进 QThread,通过信号把画面和结果传回主线程,这也是可视化界面实现里最容易踩的坑。
# detection_worker.py import cv2 from PyQt5.QtCore import QThread, pyqtSignal from ultralytics import YOLO class DetectionWorker(QThread): frame_ready = pyqtSignal(object) # 传处理后的画面到 UI log_msg = pyqtSignal(str) # 传日志文本 def __init__(self, model_path, conf=0.25): super().__init__() self.model = YOLO(model_path) self.conf = conf self.running = True def run(self): cap = cv2.VideoCapture(0) # 0 是默认摄像头,1 是多摄像头下的第二个 if not cap.isOpened(): self.log_msg.emit("摄像头打开失败,请检查设备") return while self.running: ret, frame = cap.read() if not ret: continue results = self.model.predict(frame, conf=self.conf, verbose=False) annotated = results[0].plot() # 把框画到原图上 self.frame_ready.emit(annotated) # 这里可以继续从 results 提取框坐标,做名单比对和记录 cap.release()核心逻辑在 run 方法里:循环读帧、推理、画框、发信号。界面部分只需要把 frame_ready 信号连接到 QLabel 的更新槽函数。需要注意 predict 里的 verbose=False,否则终端会被检测日志刷屏;results[0].plot() 返回的是画好框的 BGR 图像,转 QImage 前要先用 cvtColor 转成 RGB。线程退出时调 stop() 把 running 置 False,否则关闭窗口后摄像头还在工作。
5.3 界面与部署中的高频问题排查
数据路径带中文导致训练失败。现象:数据加载阶段直接报错或者 loss 异常;原因:ultralytics 读取路径时对非 ASCII 字符处理不友好;解决:所有目录改成英文,解压后先检查路径是否含中文和空格。
界面点击“开始识别”即卡死。现象:窗口无响应,任务管理器显示 CPU 占满;原因:model.predict 写在了 UI 线程;解决:按 5.2 的做法把所有推理放到 QThread,主线程只负责刷新界面。
现场人脸经常漏检。现象:离摄像头稍远的人脸没有框;原因:imgsz 太小或 conf 设太高;解决:推理时 imgsz 提到 960,conf 降到 0.25,必要时在数据集中补充小尺寸人脸样本。
RKNN 导出后检测框漂移。现象:框的位置对,但类别置信度普遍偏低;原因:opset 版本不匹配,或量化没有做数据校准;解决:导出时固定 opset=12,量化前准备 100 张以上真实门禁图作为校准集。
6. 边缘部署与验收:从 RKNN 转换到答辩前验证
6.1 边缘部署:PyTorch 到 ONNX 再到 RKNN
真实门禁系统绝大多数跑在边缘设备上,检索里高频出现的“rk3588 部署 yolov8”就是典型方案,RK3588 自带 NPU,跑 YOLOv8n 可以做到实时。第一步先把 PyTorch 权重导出为 ONNX。
yolo export model=runs/detect/door_v1/weights/best.pt \ format=onnx \ imgsz=640 \ opset=12 \ simplify=Trueopset 版本是这里最关键的参数,RKNN 工具链对过高的 opset 支持不完整,opset=12 是目前兼容性最好的选择。导出后在板上用官方 RKNN 工具做量化,量化需要校准集,我一般从 val 里抽 100 到 200 张真实门禁图,覆盖多个时段的亮度分布,比用训练集效果好。量化后的 mAP 掉 1% 到 3% 是正常范围,别因为这个数字翻车。
6.2 答辩前三个验收技巧
第一个是固定视频代替实时摄像头。现场摄像头一旦逆光或角度不对,漏检率会直线上升,而答辩现场没有时间调参数。我会先在本地录一段包含多人进出的门禁视频,用这段视频反复测试,确认稳定后再上摄像头。
第二个是实测推理耗时。
yolo detect predict \ model=runs/detect/door_v1/weights/best.pt \ source=/path/to/test_door.mp4 \ conf=0.25 \ imgsz=640 \ save=True跑完看 save 目录下的耗时统计,640 分辨率下如果低于 10 FPS,就要考虑换 YOLOv8n 或降低 imgsz。第三个是边界 case 检查:半身入镜、两人并行、逆光、低头看手机,这四类场景各准备十张图跑一遍。门禁演示的翻车几乎都发生在这四个 case 上,提前测完并调整 conf,现场才不慌。
我现在的习惯是每次拿到这类项目都在本地先跑通官方推理,再动自己的数据,最后才碰界面和部署,步骤少一个,现场翻车的概率就高一截。希望这套从环境到部署的路径能帮到你,用最短时间把 YOLOv8 门禁系统真正跑起来。
本文还有配套的精品资源,点击获取