
简介一份基于YOLOv8的实验室防护服穿戴规范检测项目面向计算机、自动化等专业的毕业设计、课程设计及入门进阶人群专注解决安全着装自动识别与可视化评估问题。压缩包仅8个文件包含3个Python脚本、3个PyTorch权重文件与2个说明文档分别负责模型训练、视频检测、可视化页面、预训练/最优权重及部署指引整体大小15.91MB结构紧凑便于快速上手。项目可直接运行训练后可产出核心指标曲线、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图满足答辩评审对完整性和规范性的要求。配套完整数据集与README部署说明按步骤配置即可复现便于在现有代码基础上扩展其他检测场景。目前已有47人学习下载适合拿来即用并在此基础上做功能扩展。1. 从“毕设.zip”到能演示的穿戴检测这套YOLOv8项目解决了什么问题拿到《基于YOLOv8的实验室防护服穿戴规范检测》这类资源包很多人第一反应是“解压、跑通、答辩”。这个方向本身不复杂YOLOv8做目标检测配合一个可视化界面检测实验室里有没有按要求穿防护服、戴手套和护目镜。真正让它成为高校项目常客的是“规范”两个字——它不是简单判断图片里有没有人而是要把穿戴状态拆开、定位到具体的人身上再给结论。这也决定了你在训练和部署时不能用最省事的“单标签分类”思路否则演示现场大概率翻车。这个标题的落点很清晰适合做毕设或课程设计的学生也适合实验室安全巡检场景里想快速搭 Demo 的工程师。一套可跑的项目包通常包含源码、可视化界面、完整数据集和部署教程听起来省事但实际落地时环境依赖、数据格式、阈值设置都会成为坑。下面我按“原理→数据→训练→界面→排错→验证”的顺序把这条链路拆开讲透。2. 先理顺原理YOLOv8做穿戴检测拆任务比调模型更重要很多拿 YOLOv8 做穿戴检测的人动手第一件事就是下载预训练权重、找数据集、开训练。结果训练出来 loss 降得挺好验证集 mAP 也有 0.8一接到摄像头就飙出大量“假阳性”最后只能靠调低置信度阈值勉强压下去。问题不在模型而在于任务定义从一开始就是错的。“穿没穿防护服”看着像二分类放到真实实验室里却是一个多目标检测加规则判断的问题任务不拆开后续调参全是玄学。2.1 穿戴规范不是“一张图分对错”而是多目标检测加规则判断如果你把整张图直接分类成“合规/违规”模型能学到的是场景全局特征环境背景、桌面设备、光影都会参与决策。一旦换到另一间实验室背景一变预测就乱套。更重要的是分类给不出“谁违规、哪个部位违规”的信息这在多人共存的实验室里没法用。正确做法是先把目标拆成检测框person人的位置protective_suit防护服区域gloves手套区域goggles护目镜区域safety_helmet安全帽区域如果需要。模型负责找出这些物体的类别和位置再用一段简单后处理规则判断“这个人是不是穿戴齐全”。例如一个 person 框内如果没有检测到 protective_suit就判定为“未穿防护服”人手的区域附近没有 gloves 框就判定为“未戴手套”。这样拆每个检测类别的特征差异更大YOLOv8 的收敛难度反而低而且在答辩时能讲出“规则可解释”的亮点。这里还有一个常见误用有人把“穿防护服”和“未穿防护服”都做成正样本类别让模型直接分类。实际项目里“未穿防护服”的负样本形态太开放几乎无法标全模型很容易把背景里类似白色物体当漏检目标。毕设项目一般只检测穿戴物违规判断交给规则层。2.2 YOLOv8 网络结构里三个关键参数输入分辨率、anchor-free 设计、后处理阈值搜索“yolov8网络结构图”的人常看到 backboen、C2f、SPPF、anchor-free 的 Detection Head。你不用把每个模块都背下来但三个和“能不能检测好防护服”强相关的点必须理解。第一个是输入分辨率。YOLOv8 默认训练尺寸 imgsz640对画面里占比较大的防护服很宽松但护目镜、手套这类小目标经常只有几十个像素。把 imgsz 提到 960 能明显改善小目标召回代价是显存占用上升。如果你的数据集只有一千张左右我不建议直接上 960容易在小数据集上过拟合先用 640 跑通 baseline再针对性用小目标增强。第二个是 anchor-free 设计。YOLOv8 把检测头改成了解耦头分类和回归分支分开输出并在训练时使用 TaskAlignedAssigner 分配样本。它不再依赖手工锚框统计算法你几乎不用像 YOLOv5 那样跑 anchor cluster。这对新手是友好的少一个调参步骤。第三个是推理时的 conf 和 iou 阈值。conf 控制“置信度多高才算检出”界面演示时我一般从 0.35 起步穿戴检测里人体和装备大量重叠NMS 的 IoU 阈值用默认 0.45 容易把重叠的手套框抑制掉建议调到 0.5 或 0.6保留更多真实重叠目标。毕设项目里这两个值经常被写成界面里的控件后面部署时会专门讲。2.3 环境配置的版本搭配CUDA、PyTorch、ultralytics 的坑标题里的“简单部署即可运行”最容易卡住人的不是模型是环境。很多资源包自带 requirements.txt但它往往只列 ultralytics 和 opencv不会告诉你 PyTorch 要和 CUDA 匹配到什么程度。先检查机器再装东西# 查看 NVIDIA 驱动支持的 CUDA 版本 nvidia-smi # 安装 PyTorch 后验证能否调用 GPU python -c import torch; print(torch.__version__, torch.cuda.is_available())第一行命令看右上角“CUDA Version”那表示驱动最高支持的版本。PyTorch 的 CUDA 构建版本只要不高于它就行。第二行输出应为2.x.x True如果显示 False说明 PyTorch 装成了 CPU 版训练会慢到怀疑人生。我的习惯是用 conda 建独立环境Python 3.9 或 3.10然后先装 PyTorch再执行pip install ultralytics。不建议直接pip install -r requirements.txt因为如果它固定了旧版 PyTorch可能和你的 CUDA 驱动冲突装上了也 ImportError。组件推荐范围说明Python3.9 ~ 3.10太老的新包不支持太新的项目包没适配PyTorch1.13 或 2.x按 nvidia-smi 驱动选择ultralytics8.x 固定小版本项目中已训练好的权重不大受影响训练脚本需匹配显存4G 以上可跑 n/s6G 如 GTX 1660 Ti 跑 n/s 没问题3. 把数据集整理成 YOLO 格式标注转换、划分与类别统计“完整数据集”在毕设包里听起来是现成的但打开后经常有两种情况一种是已经转换成 YOLO txt 格式直接能训练另一种给的是 VOC XML 或 LabelImg 导出的 XML需要预处理。很多学生卡在数据这一步不是不会写循环而是没搞清楚 YOLO 格式对路径、归一化和类别索引的要求。3.1 防护服数据集的构成从 XML 到 YOLO txtYOLOv8 的数据集不需要 Coco JSON只需要每个图片对应一个 txt 文件文件名和图片名一致。txt 每行表示一个目标格式是class_id x_center y_center width height其中坐标都是相对于图片宽高的归一化浮点数。比如一张宽 1920 的图上一个 xmin960 的框中心 x 坐标就是 960/19200.5。同一个 txt 里可以有多行表示一个图里多个目标。同时需要一个 data.yamlpath: ../lab_ppe_dataset train: images/train val: images/val nc: 4 names: 0: person 1: protective_suit 2: gloves 3: goggles这里的 names 顺序不是随便写的它必须和所有 txt 里的 class_id 对应。常见翻车现场是转换脚本里类别顺序是[protective_suit, person, ...]而 data.yaml 里又是另一种顺序训练时模型学的东西全乱了。拿到任何数据集先打印一个标签文件的第一行再和 yaml 对照。3.2 VOC 转 YOLO 标注的 Python 脚本坐标归一化与边界裁剪如果资源包给的是 VOC XML我一般会写下面这个脚本统一转换。它做了两件关键事坐标归一化和边界裁剪。import xml.etree.ElementTree as ET from pathlib import Path def voc2yolo(xml_file, out_dir, class_names): root ET.parse(xml_file).getroot() img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_names: continue # 不需要的类别直接跳过 cls_id class_names.index(name) box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 裁剪到图片范围内避免负数或越界 x1 max(0.0, min(x1, img_w - 1)) x2 max(0.0, min(x2, img_w - 1)) y1 max(0.0, min(y1, img_h - 1)) y2 max(0.0, min(y2, img_h - 1)) if x2 x1 or y2 y1: continue # 无效框丢弃 cx (x1 x2) / 2.0 / img_w cy (y1 y2) / 2.0 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) if lines: out_file out_dir / (Path(xml_file).stem .txt) out_file.write_text(\n.join(lines), encodingutf-8)这里class_names列表必须和 data.yaml 完全一致。很多数据集会把“手套”标注成 glove 和 gloves 两种转换前最好做一次别名映射比如统一成 glove。边界裁剪容易被忽略数据集中偶尔会有人把 bbox 左上角标到图片外部如果不处理训练时 YOLOv8 内部算损失可能报负数或产生 NaN。运行脚本后建议再写一段校验随机抽几张图把 txt 坐标画回图片上人工看一眼。这一步能发现标签错位和类别名不一致比自己盲训快得多。3.3 train/val 划分与每类样本数检查划分数据集也要写脚本而不是手动拖拽。核心是按图片划分不能按图片区域切图。同一张原图切出的多个子图如果同时出现在训练集和验证集会造成数据泄漏验证指标虚高。下面是一个保守版本import random from pathlib import Path from collections import Counter img_dir Path(images) label_dir Path(labels) train_ratio 0.8 imgs list(img_dir.glob(*.jpg)) list(img_dir.glob(*.png)) random.seed(42) random.shuffle(imgs) split int(len(imgs) * train_ratio) train_imgs imgs[:split] val_imgs imgs[split:] for split_dir, split_list in [(images/train, train_imgs), (images/val, val_imgs)]: Path(split_dir).mkdir(parentsTrue, exist_okTrue) # 迁移图片并同步迁移同名的 txt 标签 for img in train_imgs: img.rename(Path(images/train) / img.name) label label_dir / (img.stem .txt) if label.exists(): label.rename(Path(labels/train) / label.name) # 统计每个类别的标注数量确认正负样本是否离谱 counter Counter() for label_file in Path(labels/train).glob(*.txt): for line in label_file.read_text(encodingutf-8).strip().splitlines(): if line.strip(): counter[int(line.split()[0])] 1 print(counter)这段脚本把图片和标签分到了独立目录。统计出的 Counter 是训练阶段最直观的信息如果 person 有 2000 个框而 gloves 只有 80 个那小目标类别基本没法收敛。这时优先考虑补数据其次才是换模型。不要在只有 80 个手套样本的情况下去调 YOLOv8 的网络结构先把数据量做上去。4. 训练、损失曲线与可视化界面从 best.pt 到可演示的 App数据集格式正确后训练模型反而是最机械的一步。YOLOv8 的命令行和 API 都封装得很干净百度一搜一大把但真正决定项目能不能演示通过的是超参、界面线程模型和部署教程里的运行环境配套。这一章重点讲这三个地方。4.1 训练命令epochs、batch、imgsz 怎么定才不浪费显存用 ultralytics 官方方式训练自己的数据集最小命令是yolo train datalab_ppe.yaml modelyolov8n.pt epochs120 imgsz640 batch16 patience20 namelab_ppe参数含义modelyolov8n.ptn 是 nano显存占用最小的版本。毕设场景用 n 或 s 足够m/l 在 1000 张量级的数据集上提升不明显。epochs120别只看“官方预训练能跑 300”小项目 80~120 轮看 loss 收敛就可以早停。batch16这是单卡 8G 显存的保守值。如果你的显卡只有 6G比如 GTX 1660 Ti直接把 batch 降到 8否则会在第一个 epoch 就 OOM。patience20验证集指标连续 20 轮不升就自动停止防止后期过拟合。namelab_ppe模型输出到runs/train/lab_ppe/不会覆盖其他实验。训练结束后看runs/train/lab_ppe/results.png。它会画出 train/val 的 box_loss、cls_loss、dfl_loss 和 mAP 曲线。如果 loss 曲线没有明显下降先别调参回去检查数据标签如果 val/cls_loss 在后期反弹说明过拟合把 epochs 降下来或加数据增强。要画自定义损失函数曲线的话ultralytics 会把每个 epoch 的指标存到results.csv用 pandas 读取绘制即可不用从训练日志里手动解析。4.2 可视化界面怎么接模型把推理放到 QThread 里可视化界面这个需求不是花架子它是答辩和演示的“面子工程”。常见做法是用 PyQt5 或 Tkinter 写一个窗口左边放摄像头或视频画面右边显示检测结果再加开始、暂停按钮。新手最容易犯的错是把 YOLO 推理放在 UI 主线程里。摄像头读取一帧、模型推理、画框、刷新界面全部串行结果就是界面卡死鼠标拖不动。正确做法是把摄像头和推理封装进一个 QThread 子线程通过信号把 QImage 传给主线程刷新from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage from ultralytics import YOLO import cv2 class InferThread(QThread): change_pixmap pyqtSignal(QImage) def __init__(self, model_path, conf0.35, iou0.5, source0): super().__init__() self.model YOLO(model_path) self.conf conf self.iou iou self.cap cv2.VideoCapture(source) def run(self): while self.cap.isOpened(): ret, frame self.cap.read() if not ret: break res self.model(frame, confself.conf, iouself.iou, verboseFalse) frame res[0].plot() rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape bytes_per_line ch * w self.change_pixmap.emit( QImage(rgb.data, w, h, bytes_per_line, QImage.Format_RGB888) )这里几个参数值得说明conf0.35界面演示我倾向偏高背景里穿白大褂的人容易误报成防护服0.35 起步能少很多假阳性。iou0.5比默认 0.45 高一点因为防护服和人体、手套和人手重叠得很厉害阈值太低会互相抑制。verboseFalse关掉 ultralytics 的每帧日志不然控制台刷屏程序越跑越慢。QImage(rgb.data, ...)这一步是拷贝像素rgb 是局部变量但 QImage 在 PyQt5 里持有数据引用必须保证 rgb 在信号处理前不被回收。稳妥做法是信号里传QImage的拷贝实际项目中我会先用cv2.cvtColor保证格式正确再用rgb.copy()垫一份。4.3 部署教程里的可靠套路先锁版本再考虑 exe“部署教程”写得好不好关键在于能不能让一个只装了 Anaconda 的人从零跑到看到界面。通常资源包会要求conda create -n lab_ppe python3.9 -y conda activate lab_ppe pip install ultralytics8.1.0 python run.py我在实际交付时会多做一个动作把当前环境导出成 requirements.txt 更全的清单而不是只装 ultralytics。因为 YOLO 项目还依赖 PyQt5、opencv-python、numpy、torch漏掉任何一个都会让“简单部署”变成“简单报错”。如果要把项目打包成 exe 带给评委看用 PyInstaller 打包时注意模型路径。很多人的源码里直接写了model YOLO(C:/Users/xxx/best.pt)换机器就废。更稳妥的是用相对路径定位from pathlib import Path BASE_DIR Path(__file__).resolve().parent model_path BASE_DIR / weights / best.pt这样无论项目放在哪个目录只要weights/best.pt相对位置不变就能正常加载。5. 避坑与排查复现防护服穿戴检测最容易翻车的 5 个地方这部分内容来自我和学生们反复“填坑”的经验按“现象 → 原因 → 解决”写每一条都能帮你节省至少半天调试时间。5.1 现象训练到一半程序被杀显示 Killed 或 CUDA out of memory原因显存不足多发生在 batch 太大、imgsz 太大、模型尺寸和显存不匹配同时发生时。6G 显存强行跑 yolov8m batch 16 imgsz 640几乎必挂。解决先降到yolov8n.pt batch 8 imgsz 640。如果还不行把训练时数据加载线程workers从默认 8 降到 2。很多新手忽略 workers 也会占内存尤其当图片很大时DataLoader 预取的数据能把内存吃满。再用nvidia-smi盯着显存确认占用稳定后再慢慢加 batch。5.2 现象训练结束 mAP 有 0.8但界面里反复把白色柜子、白大褂识别成防护服原因大概率不是模型没训练好而是数据集中防护服的背景非常单一模型学到了“白色大块区域”这个特征。另一个常见原因是手套或护目镜样本太少模型被迫用防护服特征去近似其他类。解决先看验证集里误报目标的置信度分布。把 conf 从 0.35 提到 0.5能压掉一部分但根因是数据多样性不足需要补充不同角度、不同光线、不同实验台背景下的防护服样本。这是毕设项目里最值得花时间的部分。5.3 现象可视化界面点“打开摄像头”后卡住窗口一直转圈几秒后无响应原因推理线程没有和 UI 线程分离摄像头读取和模型推理堵住了 Qt 的事件循环。前面介绍的 QThread 写法就是为了解决这个问题。还有一个隐蔽原因res[0].plot()返回的图片是 BGR 的而 QImage 默认按 RGB 解释不转色彩空间就会得到蓝紫偏色严重时看起来像“检测失败”。解决检测逻辑放进 QThread 子线程图像转 QImage 前用cv2.cvtColor转成 RGB并确保bytes_per_line用对齐后的步长。Qt 对行字节数有 4 字节对齐要求摄像头分辨率不规则时会出现图像拉伸或错位最常见的分辨率 1280x720 一般没事。5.4 现象换一台电脑后按部署教程操作显示 ModuleNotFoundError / AttributeError原因版本不一致。ultralytics 升级很快8.0 和 8.2 的接口有差异比如某个版本的YOLO构造函数换了参数名如果 requirements.txt 没固定版本pip 默认装最新代码就可能崩。解决在项目目录里生成锁版本的清单并让部署教程明确写一行版本号。例如pip freeze requirements_lock.txt交付时不要只给requirements.txt这样的宽泛清单建议在 README 里写三行Python 版本、PyTorch 版本、ultralytics 版本。我踩过最亏的一次是模型在 TensorRT 导出时因为 PyTorch 版本从 2.0 升到 2.1opset 兼容性问题浪费了整整一天。5.5 现象护目镜和手套经常漏检戴没戴判断不准原因这类小目标在原图里像素少且训练集里数量偏低。YOLOv8 对小目标不是不能检测而是需要更多有效样本。另一个原因是摄像头取流分辨率过高比如 1080p 传进模型前直接缩到 640手套信息已经丢了。解决摄像头端使用 640x480 或 720p 抓流不要用 1080p 再缩减少缩放损失。训练时把 imgsz 提到 960并且对包含护目镜、手套的图片做随机裁剪增强模拟“局部放大”的效果。调参优先级是补数据 改输入尺寸 调模型结构。6. 进阶验证错例回放与困难样本阈值校准训练完 best.pt很多人直接进界面演示结果仅靠“看起来还行”的数据写论文答辩被问两句就露馅。我多花一个晚上做的验证工作只有两件事错例回放和阈值校准但效果立竿见影。所谓错例回放就是把验证集图片用模型批量跑一遍把预测框和真值框没有匹配上的图片按“置信度从低到高”输出到一个文件夹。置信度最低的前几十张图基本就是模型最犹豫的困难样本。逐张看你会发现三类问题标注框和实际物体对不上、物体被严重遮挡、类别定义有歧义比如手套露出三个指头到底算不算戴好。这些问题在你调参时看不到只有回放才暴露。阈值校准也很适合做成界面里的一个参数下拉框。我一般跑三组对比conf 0.25、0.35、0.5分别统计真阳性率和假阳性率。取一个现场可接受的平衡值通常 0.35 左右。不要追求完美检测穿戴检测的核心是“明显违规能被发现”而不是把每一帧都标出所有装备。如果项目还能继续推进可以按“时间段”把现场视频拆成训练集和跨天验证集。同一个上午的视频既进训练又进验证mAP 会虚高用第二天拍的新视频验证才更接近真实部署效果。这也是答辩时一个很好讲的技术亮点。我自己早年接过类似项目总想赶紧把界面跑起来拍照结果现场阈值设得太低摄像头扫过一排白色实验台时框了一片误报。那次之后我养成了一个习惯训练完先花一小时做错例回放再拿 3 段不同场景的视频做冒烟测试全部通过才叫“部署完成”。这个习惯让我后面交付的每个检测项目都稳了不少希望帮到你。本文还有配套的精品资源点击获取