☰
基于YOLOv8的智慧工厂危险区域闯入识别实战:从训练到PyQt5界面部署
2026/9/28 6:29:25 网站建设 项目流程

简介:这份资源面向计算机、人工智能、自动化等专业的在校学生与教师,提供一套基于YOLOv8的智慧工厂危险区域闯入识别系统完整方案,可用于毕业设计、课程设计或大作业。压缩包共8个文件,约15.91MB,包含3个Python脚本、3个模型权重文件与2个说明文本,分别对应可视化界面、模型训练与视频检测推理等核心环节,部署流程简单,开箱即可运行。项目附带完整数据集与可视化页面,能够生成核心指标曲线图、混淆矩阵、F1分数曲线、精确率-召回率曲线、验证集预测结果及标签分布图,便于答辩展示与结果分析。目前已有30人学习关注。读者可直接获得经过测试的源码、训练好的权重、数据集与部署说明,既能快速复现危险区域闯入识别效果,也可在此基础上修改扩展功能,适合作为毕设保底项目或入门目标检测的实践案例。

1. 智慧工厂危险区域闯入识别:为什么YOLOv8成了产线安全的第一道防线

在智慧工厂的落地场景里,危险区域闯入识别几乎是每个集成商都会碰到的需求:冲压机床周边、高压配电柜前方、AGV 充电区、高温熔炉半径内,这些区域一旦有人员误入,轻则停机停产,重则人身伤害。传统做法靠物理围栏加红外对射,问题是误报率高、无法区分人和叉车、也没法留证据。用 YOLOv8 做视觉闯入识别,本质是把「区域入侵检测」拆成两个可工程化的子问题:先稳定检测出画面里的人,再用多边形或矩形区域做空间判定。这套方案适合做毕设或课程设计的同学,也适合工厂里想快速验证视觉安全方案的工程师——因为 YOLOv8 的预训练权重开箱即用,数据集标注成本可控,可视化界面又能直接演示给非技术方看。我见过太多人卡在环境配置和数据集格式转换上,其实真正跑通一条「训练→推理→区域判定→界面展示」的链路,比想象中短得多。

2. 从零搭起 YOLOv8 闯入识别的最小可运行环境

2.1 环境配置:CPU 版本也能先跑通,别一上来就折腾 CUDA

很多人第一反应是装 GPU 环境,结果卡在驱动版本和 CUDA 兼容性上,三天没跑出一行结果。我的建议是先用 CPU 版本把整条链路跑通,确认代码逻辑没问题,再换 GPU 加速训练。Ubuntu 20.04 和 CentOS 7 都能装,Windows 下用 conda 建虚拟环境同样可行。核心依赖就三个:ultralytics、opencv-python、torch。注意 torch 的 CPU 版本和 GPU 版本安装命令不同,装错了会报Torch not compiled with CUDA enabled。

# 创建虚拟环境,Python 版本建议 3.8 到 3.10 conda create -n yolov8_factory python=3.9 -y conda activate yolov8_factory # 安装 CPU 版本 torch(先确认能跑通再换 GPU) pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu # 安装 ultralytics 和可视化依赖 pip install ultralytics opencv-python pillow matplotlib

这段命令的逻辑是:先隔离环境避免污染系统 Python,再装 CPU 版 torch 降低首次运行门槛,最后装 ultralytics 这个包——它同时提供了 YOLOv8 的训练、推理和导出接口。参数上唯一要注意的是 Python 版本,3.11 以上某些依赖轮子还没跟上,3.9 最稳。装完后用yolo checks验证,能看到环境摘要就说明基础通了。

2.2 数据集准备:Labelme 标注转 YOLO 格式的完整脚本

危险区域闯入识别的数据集通常只有一类目标:person。但工厂场景下的人可能戴安全帽、穿反光衣、被机器部分遮挡,所以标注时要尽量覆盖这些形态。用 Labelme 标注会生成 JSON 文件,而 YOLOv8 需要的是每张图对应一个 txt,每行格式为class_id x_center y_center width height,且坐标要归一化到 0 到 1 之间。下面这个转换脚本我用了很多次,直接改路径就能跑。

import json import os from pathlib import Path def labelme_to_yolo(json_dir, output_dir, class_names): """ json_dir: Labelme 标注文件所在目录 output_dir: 输出 txt 标签的目录 class_names: 类别名称列表,如 ['person'] """ json_dir = Path(json_dir) output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) for json_file in json_dir.glob('*.json'): with open(json_file, 'r', encoding='utf-8') as f: data = json.load(f) img_w = data['imageWidth'] img_h = data['imageHeight'] lines = [] for shape in data['shapes']: label = shape['label'] if label not in class_names: continue class_id = class_names.index(label) 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) # 归一化中心点和宽高 x_center = (x_min + x_max) / 2.0 / img_w y_center = (y_min + y_max) / 2.0 / img_h width = (x_max - x_min) / img_w height = (y_max - y_min) / img_h lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") # 写入同名 txt txt_path = output_dir / (json_file.stem + '.txt') with open(txt_path, 'w') as f: f.write('\n'.join(lines)) print(f"转换完成,共处理 {len(list(json_dir.glob('*.json')))} 个文件") # 使用示例 labelme_to_yolo( json_dir='./raw_annotations', output_dir='./labels', class_names=['person'] )

逻辑说明:脚本遍历每个 JSON,读取图像宽高用于归一化,把 Labelme 的多边形点集转成外接矩形,再按 YOLO 格式写入 txt。参数上class_names必须和后续训练时的data.yaml里的names顺序一致,否则类别会错位。转换完成后要检查两点:一是空 txt 文件(表示该图没有目标,训练时会被当作背景图,这是允许的),二是坐标是否都在 0 到 1 之间,超出说明标注时点到了图像外面。

2.3 训练配置:data.yaml 写法和三个必调参数

数据集目录结构建议按 YOLOv8 官方推荐来:images/train、images/val、labels/train、labels/val四个文件夹。然后写一个data.yaml:

path: /home/user/factory_dataset train: images/train val: images/val names: 0: person

训练命令用命令行或 Python 脚本都行,我习惯用脚本方便记录参数:

from ultralytics import YOLO model = YOLO('yolov8n.pt') # 从预训练权重开始,小模型适合快速验证 results = model.train( data='data.yaml', epochs=100, imgsz=640, batch=16, lr0=0.01, patience=20, device='cpu' # 有 GPU 改成 0 )

三个必调参数:imgsz决定输入分辨率,工厂监控画面通常 1080P,但训练时缩到 640 能在精度和速度间取得平衡;batch在 CPU 上设 8 或 16,太大内存扛不住;patience是早停轮数,设 20 表示验证集损失 20 轮不降就停,避免过拟合。lr0初始学习率默认 0.01 对小数据集够用,如果 loss 震荡厉害降到 0.001。

3. 危险区域判定逻辑:从检测框到闯入报警的工程实现

3.1 多边形区域定义与点框相交判定

检测出人只是第一步,真正决定是否报警的是「人有没有进入危险区域」。工厂里的危险区域往往不是矩形,比如机械臂旋转半径是扇形,配电柜前方是梯形。所以要用多边形定义区域,再判断检测框的底边中心点是否落在多边形内——用底边中心点而不是框中心,是因为人脚的位置才代表实际站立点。

import cv2 import numpy as np def is_inside_zone(bbox, polygon): """ bbox: [x1, y1, x2, y2] 检测框 polygon: np.array([[x1,y1], [x2,y2], ...]) 多边形顶点 返回: 底边中心点是否在多边形内 """ x1, y1, x2, y2 = bbox # 取底边中心点 foot_point = ((x1 + x2) / 2, y2) # cv2.pointPolygonTest 返回正数表示在内,负数在外,0 在边上 result = cv2.pointPolygonTest(polygon, foot_point, False) return result >= 0 # 定义一个梯形危险区域 danger_zone = np.array([ [200, 300], [500, 300], [600, 600], [100, 600] ], dtype=np.int32)

逻辑说明:pointPolygonTest是 OpenCV 自带的几何判定函数,第三个参数False表示只返回位置关系不返回距离。参数上要注意多边形顶点顺序必须是顺时针或逆时针连续排列,不能交叉,否则判定结果会乱。实际部署时这个多边形坐标应该做成可配置的,因为摄像头安装位置一变,区域坐标就得重新标定。

3.2 可视化界面:用 PyQt5 做一个能演示的闯入报警窗口

毕设和课程设计最需要的就是「能演示」。一个简单的 PyQt5 界面,左边显示视频流和检测框,右边显示报警日志和区域开关,足够让答辩老师看明白。核心是用 QTimer 定时抓帧,把 YOLOv8 推理结果画上去。

import sys import cv2 from PyQt5.QtWidgets import QApplication, QMainWindow, QLabel, QVBoxLayout, QWidget, QPushButton from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtCore import QTimer from ultralytics import YOLO import numpy as np class FactoryMonitor(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('危险区域闯入识别系统') self.model = YOLO('runs/detect/train/weights/best.pt') self.cap = cv2.VideoCapture(0) # 换成视频文件路径也行 self.danger_zone = np.array([[200,300],[500,300],[600,600],[100,600]], dtype=np.int32) self.label = QLabel() self.btn = QPushButton('暂停/继续') self.btn.clicked.connect(self.toggle) layout = QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.btn) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.timer = QTimer() self.timer.timeout.connect(self.update_frame) self.timer.start(30) # 约 33fps def toggle(self): if self.timer.isActive(): self.timer.stop() else: self.timer.start(30) def update_frame(self): ret, frame = self.cap.read() if not ret: return results = self.model(frame, verbose=False)[0] alarm = False for box in results.boxes: x1, y1, x2, y2 = map(int, box.xyxy[0]) conf = float(box.conf[0]) if conf < 0.5: continue foot = ((x1+x2)//2, y2) inside = cv2.pointPolygonTest(self.danger_zone, foot, False) >= 0 color = (0,0,255) if inside else (0,255,0) if inside: alarm = True cv2.rectangle(frame, (x1,y1), (x2,y2), color, 2) cv2.putText(frame, f'person {conf:.2f}', (x1,y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.polylines(frame, [self.danger_zone], True, (0,255,255), 2) if alarm: cv2.putText(frame, 'ALARM: INTRUSION', (50,50), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0,0,255), 3) rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb.shape qt_img = QImage(rgb.data, w, h, ch*w, QImage.Format_RGB888) self.label.setPixmap(QPixmap.fromImage(qt_img)) if __name__ == '__main__': app = QApplication(sys.argv) win = FactoryMonitor() win.show() sys.exit(app.exec_())

逻辑说明:QTimer 每 30 毫秒触发一次 update_frame,抓帧后送入模型推理,遍历检测框判断脚点是否在危险区域内,是则画红框并显示 ALARM。参数上conf < 0.5是置信度过滤,工厂场景建议设 0.5 到 0.6,太低会误报,太高会漏检远处的人。self.cap = cv2.VideoCapture(0)里的 0 是默认摄像头,换成 RTSP 地址就能接网络摄像头。

4. 避坑与排查:训练和部署中最容易翻车的五个地方

4.1 现象:训练 loss 一直不降,mAP 始终在 0.1 以下

原因通常是 data.yaml 里的路径写错了,或者 labels 目录和 images 目录没有对应上。YOLOv8 找不到标签文件时不会报错,而是把所有图当背景训练,loss 自然降不下去。解决方法是训练前用model.train的verbose=True看数据集加载数量,如果train和val的图片数为 0 就是路径问题。另外检查 txt 文件名是否和图片名完全一致(包括大小写)。

4.2 现象:推理时检测框位置偏移严重,框比人小一圈

这是标注格式转换时坐标没归一化,或者归一化时除错了宽高。Labelme 的坐标是绝对像素值,YOLO 需要除以图像宽高。如果转换脚本里把 x 除以了高度、y 除以了宽度,框就会错位。排查方法是拿一张训练集里的图跑推理,对比标注框和预测框,如果偏移方向有规律就是归一化轴搞反了。

4.3 现象:CPU 推理速度只有 2 到 3 FPS,界面卡死

YOLOv8n 在 CPU 上单帧推理大约 80 到 150 毫秒,加上 PyQt5 界面刷新和画框,30 毫秒的定时器根本来不及。解决办法是把推理放到独立线程,或者降低输入分辨率到 320,再或者换 yolov8n 而不是 s/m/l。如果必须实时,考虑用 OpenVINO 导出模型,CPU 上能提速 2 到 3 倍。

4.4 现象:危险区域判定时人明明在外面却报警

多边形顶点顺序错了,导致pointPolygonTest把区域内部判定成了外部。OpenCV 要求多边形顶点按顺时针或逆时针连续排列,不能交叉。排查方法是用cv2.polylines把多边形画出来,肉眼看形状是否和预期一致。如果画出来是蝴蝶结形状,就是顶点顺序交叉了。

4.5 现象:换了一个摄像头后所有检测框都偏了

不同摄像头的视场角和安装高度不同,同一个多边形坐标在不同画面里对应的实际物理区域不一样。这不是代码 bug,是标定问题。解决方法是每个摄像头单独配置一套区域坐标,或者做一个标定界面让用户手动点选多边形顶点。我一般会在配置文件里按摄像头 ID 存多套区域坐标。

5. 进阶技巧:用跟踪 ID 过滤误报和导出 ONNX 提速

5.1 用 ByteTrack 给每个人分配 ID,连续闯入才报警

单帧检测有个玄学问题:偶尔一帧把机器上的反光误检成人,就会触发一次报警。加跟踪后可以要求同一个人连续 5 帧都在危险区域内才报警,误报率大幅下降。Ultralytics 内置了 ByteTrack,只需要在推理时加tracker='bytetrack.yaml'。

from ultralytics import YOLO model = YOLO('best.pt') results = model.track(source='factory.mp4', tracker='bytetrack.yaml', persist=True) for r in results: if r.boxes.id is not None: ids = r.boxes.id.cpu().numpy() boxes = r.boxes.xyxy.cpu().numpy() for track_id, box in zip(ids, boxes): print(f'ID {track_id}: {box}')

逻辑说明:persist=True表示在视频流中保持跟踪状态,r.boxes.id就是每个人的跟踪 ID。参数上 ByteTrack 的配置文件可以调track_high_thresh和track_low_thresh,工厂场景建议把低阈值调高一点,减少 ID 切换。

5.2 导出 ONNX 并在 CPU 上提速

训练完的 .pt 文件在 CPU 上推理慢,导出 ONNX 后用 onnxruntime 能快不少。导出命令一行:

yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640

导出后在 Python 里用 onnxruntime 加载推理,注意输入需要归一化到 0 到 1 并转成 NCHW 格式。我实测 yolov8n 在 i5 上从 120ms 降到 60ms 左右,对演示场景够用了。

5.3 一个容易被忽略的细节:报警日志要落盘

演示时报警一闪而过,答辩老师问「刚才那次报警是什么时候」就答不上来。加一个简单的日志写入,每次报警记录时间戳和跟踪 ID,存成 CSV。这个习惯是我踩过坑之后养成的——有次工厂那边要查一周前的闯入记录,结果什么都没存,只能重新部署。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询