陕西省网站建设_网站建设公司_Sketch_seo优化
2026/1/2 14:23:47 网站建设 项目流程

摘要

在工业自动化、智能制造和设备维护领域,快速准确地识别各类机械器件是实现智能化管理的关键步骤。传统的人工检测方式效率低下、易疲劳且一致性差。本文详细阐述了一个基于YOLO(You Only Look Once)系列目标检测算法(涵盖YOLOv5、YOLOv6、YOLOv7和YOLOv8)构建的机械器件智能识别系统。系统集成了模型训练、推理预测的完整后端流程,并利用PySide6开发了用户友好的图形界面(GUI),支持图像、视频和实时摄像头流的目标检测。本文提供了完整的Python实现代码,并详细介绍了数据集的构建与处理方法,旨在为工业视觉检测项目提供一个可快速复现、易于二次开发的解决方案。

关键词: 目标检测;YOLO系列;工业视觉;机械器件识别;PySide6;深度学习;智能制造


目录

摘要

1. 引言

1.1 研究背景与意义

1.2 YOLO系列算法演进简述

1.3 系统目标

2. 参考数据集介绍与准备

2.1 公开数据集推荐

2.2 数据格式与标注

2.3 数据集目录结构

2.4 data.yaml 配置文件

3. 系统设计与代码实现

3.1 环境配置

3.2 模型训练代码 (train.py)

3.3 图形界面应用 (main_app.py)

3.4 推理接口封装 (inference_yolov8.py, inference_yolov5.py)

4. 系统部署与优化建议

4.1 部署流程

4.2 生产环境建议

5. 结论与展望


1. 引言

1.1 研究背景与意义

随着工业4.0和智能制造的深入推进,工厂生产线对自动化、智能化水平的要求日益提高。机械器件,如轴承、齿轮、螺栓、垫片、联轴器、传动轴等,是机械设备的基础组成部分。在装配线质量检查、仓库物料管理、设备预防性维护以及自动化分拣等场景中,对这些器件进行快速、准确的识别与定位至关重要。

传统基于规则或传统图像处理(如边缘检测、模板匹配)的方法,在光照变化、器件重叠、背景复杂、型号多样等条件下,鲁棒性较差。深度学习,尤其是以YOLO为代表的一阶段目标检测算法,凭借其端到端的训练方式、卓越的检测速度和良好的精度,已成为工业视觉领域的主流技术。

1.2 YOLO系列算法演进简述

  • YOLOv5: Ultralytics公司发布,以工程化友好著称,具有易训练、易部署、速度快的特点,社区生态极其丰富。

  • YOLOv6: 美团视觉智能部发布,在backbone和neck设计上进行了优化,专注于工业应用场景。

  • YOLOv7: 通过扩展高效聚合网络和模型缩放技术,在精度和速度上达到了当时的最优平衡。

  • YOLOv8: Ultralytics公司最新力作,在YOLOv5的基础上进行了架构重设计,提供了更灵活的模型尺寸(n, s, m, l, x),并集成了分类、分割、姿态估计等多种任务。

本系统将兼容这四个版本的YOLO模型,用户可以根据自身对速度、精度和部署环境的要求灵活选择。

1.3 系统目标

本文旨在构建一个完整的、可落地的机械器件识别系统,该系统具备以下功能:

  1. 多模型支持: 无缝切换YOLOv5/v6/v7/v8模型进行推理。

  2. 多源输入: 支持图片文件、视频文件、USB摄像头和RTSP网络流的实时检测。

  3. 可视化交互: 提供基于PySide6的图形界面,直观展示检测结果、统计信息和控制功能。

  4. 模型训练能力: 提供清晰的训练代码和数据准备脚本,方便用户使用自有数据进行模型迭代。

  5. 可扩展性: 代码结构清晰,模块化设计,便于添加新的功能或模型。


2. 参考数据集介绍与准备

一个高质量的数据集是模型成功的基础。针对机械器件识别,我们可以使用以下公开数据集或构建自己的数据集。

2.1 公开数据集推荐

  1. “机械零件”数据集 (MVTec AD 的子集或类似): MVTec AD虽然主要用于缺陷检测,但其正常样本包含了多种工业零件,可以重新标注用于识别任务。

  2. “螺母螺栓数据集” (Nut-Bolt Dataset): 网上可找到一些针对螺母、螺栓、垫片的小型数据集。

  3. “工业零件检测数据集” (Industrial Parts Detection Dataset): 在Kaggle、Roboflow Universe等平台上,可以搜索到相关数据集,例如industrial-parts-dataset

作为本文的示例,我们将使用一个自构建的合成数据集,包含以下5类常见机械器件:

  • Bearing(轴承)

  • Gear(齿轮)

  • Screw(螺丝)

  • Washer(垫片)

  • Nut(螺母)

2.2 数据格式与标注

YOLO系列使用TXT格式的标注文件。每个图像对应一个同名的TXT文件。
每一行代表一个目标物体,格式为:
<class_id> <x_center> <y_center> <width> <height>
坐标和尺寸均为相对于图像宽度和高度的归一化值(0到1之间)。

例如,图像part_001.jpg的标注文件part_001.txt内容可能为:

text

0 0.345 0.512 0.12 0.09 1 0.678 0.234 0.15 0.18 2 0.123 0.789 0.05 0.05

2.3 数据集目录结构

按照YOLO标准格式组织数据集:

text

datasets/ └── MechanicalParts/ ├── train/ │ ├── images/ # 存放训练图片 │ └── labels/ # 存放对应的TXT标注文件 ├── val/ │ ├── images/ │ └── labels/ └── data.yaml # 数据集配置文件

2.4data.yaml配置文件

yaml

# datasets/MechanicalParts/data.yaml path: ../datasets/MechanicalParts # 数据集根目录 train: train/images # 训练集路径(相对path) val: val/images # 验证集路径(相对path) # 类别数量 nc: 5 # 类别名称列表 names: ['Bearing', 'Gear', 'Screw', 'Washer', 'Nut']

3. 系统设计与代码实现

系统分为两大模块:模型训练模块图形界面应用模块

3.1 环境配置

首先创建并激活Python环境,安装必要依赖。

bash

# 创建conda环境 (可选) conda create -n yolo_mech python=3.8 conda activate yolo_mech # 安装核心库 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 根据CUDA版本调整 pip install ultralytics # 用于YOLOv8, 也包含YOLOv5的部分API # 对于YOLOv5,可能需要单独克隆其仓库(我们这里选择兼容方式) pip install opencv-python pillow matplotlib seaborn tqdm pandas pip install pyside6 # 用于GUI pip install pyqtgraph # 用于可能的高级图表显示

3.2 模型训练代码 (train.py)

本训练脚本支持YOLOv5/v8。YOLOv6/v7需要参考其官方仓库的特定训练命令,但数据格式相同。

python

# train.py import argparse import os import sys import yaml from pathlib import Path import torch def train_yolov8(data_yaml, epochs=100, imgsz=640, batch=16, weights='yolov8n.pt', project='runs/train'): """ 使用Ultralytics YOLOv8 API进行训练 """ from ultralytics import YOLO # 加载模型 model = YOLO(weights) # 开始训练 results = model.train( data=data_yaml, epochs=epochs, imgsz=imgsz, batch=batch, project=project, name='exp', save=True, save_period=10, visualize=False, exist_ok=True ) return results def train_yolov5(data_yaml, epochs=100, imgsz=640, batch=16, weights='yolov5s.pt', project='runs/train'): """ 使用YOLOv5官方仓库风格进行训练(通过ultralytics兼容或直接运行train.py) 此处演示通过克隆YOLOv5仓库的方式(更可控) """ # 假设YOLOv5代码已克隆到当前目录的 `yolov5/` 文件夹下 yolov5_dir = Path('./yolov5') if not yolov5_dir.exists(): print("YOLOv5 directory not found. Cloning...") os.system('git clone https://github.com/ultralytics/yolov5.git') sys.path.insert(0, str(yolov5_dir)) import train as yolov5_train # 注意:这里需要根据YOLOv5的train.py接受参数的方式进行调用 # 通常使用argparse传递参数。这里简化为系统命令调用,更稳定。 cmd = f'python {yolov5_dir}/train.py --data {data_yaml} --epochs {epochs} --imgsz {imgsz} --batch-size {batch} --weights {weights} --project {project} --name yolov5_exp --exist-ok' print(f"Running command: {cmd}") os.system(cmd) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Train YOLO model for mechanical parts detection.') parser.add_argument('--model', type=str, default='yolov8n', choices=['yolov5s', 'yolov8n'], help='Model version to train') parser.add_argument('--data', type=str, default='datasets/MechanicalParts/data.yaml', help='Path to data.yaml') parser.add_argument('--epochs', type=int, default=100, help='Number of training epochs') parser.add_argument('--imgsz', type=int, default=640, help='Image size for training') parser.add_argument('--batch', type=int, default=16, help='Batch size') parser.add_argument('--weights', type=str, default='', help='Pretrained weights path') parser.add_argument('--project', type=str, default='runs/train', help='Project directory') args = parser.parse_args() # 设置默认权重 if not args.weights: if args.model.startswith('yolov5'): args.weights = 'yolov5s.pt' elif args.model.startswith('yolov8'): args.weights = 'yolov8n.pt' print(f"Starting training with {args.model}...") print(f"Data config: {args.data}") print(f"Epochs: {args.epochs}") if args.model.startswith('yolov8'): train_yolov8(args.data, args.epochs, args.imgsz, args.batch, args.weights, args.project) elif args.model.startswith('yolov5'): train_yolov5(args.data, args.epochs, args.imgsz, args.batch, args.weights, args.project) else: print(f"Model {args.model} training script not implemented in this example.") print("Training finished.")

训练命令示例:

bash

# 训练YOLOv8n模型 python train.py --model yolov8n --data datasets/MechanicalParts/data.yaml --epochs 50 --batch 8 # 训练YOLOv5s模型 python train.py --model yolov5s --data datasets/MechanicalParts/data.yaml --epochs 50 --batch 16

3.3 图形界面应用 (main_app.py)

这是系统的核心交互部分,使用PySide6构建。

python

# main_app.py import sys import os import cv2 import time import torch from pathlib import Path from datetime import datetime import numpy as np from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog, QComboBox, QSpinBox, QDoubleSpinBox, QTextEdit, QGroupBox, QMessageBox, QSlider, QProgressBar) from PySide6.QtCore import Qt, QThread, Signal, Slot, QTimer from PySide6.QtGui import QImage, QPixmap, QFont, QIcon # 导入不同的YOLO模型推理类(需要提前实现或适配) from inference_yolov8 import YOLOv8Detector from inference_yolov5 import YOLOv5Detector # 假设我们有一个统一的推理接口 class DetectorThread(QThread): """ 用于异步执行检测任务的线程,避免界面卡顿 """ send_result = Signal(np.ndarray, list, float) # 图像,检测结果列表,处理时间 send_fps = Signal(float) finished = Signal() def __init__(self, detector, source=0, conf_thres=0.25, iou_thres=0.45): super().__init__() self.detector = detector self.source = source # 可以是摄像头ID、视频路径、图片路径 self.conf_thres = conf_thres self.iou_thres = iou_thres self.is_running = False self.is_video = False self.cap = None def run(self): self.is_running = True frame_count = 0 start_time = time.time() # 初始化视频捕获 if isinstance(self.source, (str, Path)) and os.path.exists(self.source): self.cap = cv2.VideoCapture(self.source) self.is_video = True elif isinstance(self.source, int): self.cap = cv2.VideoCapture(self.source) self.is_video = True else: # 单张图片处理 self.is_video = False img = cv2.imread(self.source) if img is not None: results, infer_time = self.detector.detect(img, self.conf_thres, self.iou_thres) self.send_result.emit(img, results, infer_time) self.finished.emit() return # 视频流处理循环 while self.is_running and self.cap.isOpened(): ret, frame = self.cap.read() if not ret: break # 执行检测 results, infer_time = self.detector.detect(frame, self.conf_thres, self.iou_thres) # 计算FPS frame_count += 1 if frame_count % 30 == 0: fps = frame_count / (time.time() - start_time) self.send_fps.emit(fps) # 发送结果 self.send_result.emit(frame, results, infer_time) # 控制帧率,避免过快 (非必要,取决于摄像头) # time.sleep(0.03) # 清理 if self.cap: self.cap.release() self.finished.emit() def stop(self): self.is_running = False self.wait() class MainWindow(QMainWindow): def __init__(self): super().__init__() self.detector = None self.detector_thread = None self.current_model = None self.model_list = { 'YOLOv5s': 'weights/yolov5s_mech.pt', 'YOLOv8n': 'weights/yolov8n_mech.pt', 'YOLOv6n': 'weights/yolov6n_mech.pt', 'YOLOv7': 'weights/yolov7_mech.pt', } self.class_names = ['Bearing', 'Gear', 'Screw', 'Washer', 'Nut'] self.class_colors = [(0, 255, 0), (255, 0, 0), (0, 0, 255), (255, 255, 0), (255, 0, 255)] self.initUI() self.initDetector('YOLOv8n') # 默认加载一个模型 def initUI(self): self.setWindowTitle("工业机械器件智能识别系统 v1.0") self.setGeometry(100, 100, 1600, 900) # 中心部件 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QHBoxLayout(central_widget) # 左侧控制面板 control_panel = QGroupBox("控制面板") control_layout = QVBoxLayout() control_panel.setLayout(control_layout) control_panel.setFixedWidth(300) # 模型选择 model_label = QLabel("选择模型:") self.model_combo = QComboBox() self.model_combo.addItems(self.model_list.keys()) self.model_combo.currentTextChanged.connect(self.onModelChanged) # 置信度阈值 conf_label = QLabel("置信度阈值:") self.conf_slider = QSlider(Qt.Horizontal) self.conf_slider.setRange(10, 90) # 0.1 to 0.9 self.conf_slider.setValue(25) # 0.25 self.conf_value_label = QLabel("0.25") self.conf_slider.valueChanged.connect(self.onConfChanged) # NMS IoU阈值 iou_label = QLabel("IoU 阈值:") self.iou_spinbox = QDoubleSpinBox() self.iou_spinbox.setRange(0.1, 0.9) self.iou_spinbox.setSingleStep(0.05) self.iou_spinbox.setValue(0.45) # 输入源选择 source_group = QGroupBox("输入源") source_layout = QVBoxLayout() self.btn_image = QPushButton("打开图片") self.btn_video = QPushButton("打开视频") self.btn_camera = QPushButton("打开摄像头") self.btn_rtsp = QPushButton("RTSP流") self.btn_stop = QPushButton("停止检测") self.btn_stop.setEnabled(False) self.btn_save = QPushButton("保存结果") self.btn_image.clicked.connect(self.openImage) self.btn_video.clicked.connect(self.openVideo) self.btn_camera.clicked.connect(self.openCamera) self.btn_rtsp.clicked.connect(self.openRTSP) self.btn_stop.clicked.connect(self.stopDetection) self.btn_save.clicked.connect(self.saveResult) source_layout.addWidget(self.btn_image) source_layout.addWidget(self.btn_video) source_layout.addWidget(self.btn_camera) source_layout.addWidget(self.btn_rtsp) source_layout.addWidget(self.btn_stop) source_layout.addWidget(self.btn_save) source_group.setLayout(source_layout) # 统计信息显示 stats_group = QGroupBox("检测统计") stats_layout = QVBoxLayout() self.fps_label = QLabel("FPS: --") self.infer_time_label = QLabel("推理时间: -- ms") self.obj_count_label = QLabel("检测数量: --") self.detail_text = QTextEdit() self.detail_text.setMaximumHeight(150) self.detail_text.setReadOnly(True) stats_layout.addWidget(self.fps_label) stats_layout.addWidget(self.infer_time_label) stats_layout.addWidget(self.obj_count_label) stats_layout.addWidget(QLabel("检测详情:")) stats_layout.addWidget(self.detail_text) stats_group.setLayout(stats_layout) # 将所有控件添加到控制面板 control_layout.addWidget(model_label) control_layout.addWidget(self.model_combo) control_layout.addSpacing(10) control_layout.addWidget(conf_label) control_layout.addWidget(self.conf_slider) control_layout.addWidget(self.conf_value_label) control_layout.addWidget(iou_label) control_layout.addWidget(self.iou_spinbox) control_layout.addSpacing(20) control_layout.addWidget(source_group) control_layout.addSpacing(20) control_layout.addWidget(stats_group) control_layout.addStretch() # 右侧图像显示区域 display_panel = QWidget() display_layout = QVBoxLayout() display_panel.setLayout(display_layout) self.image_label = QLabel() self.image_label.setAlignment(Qt.AlignCenter) self.image_label.setMinimumSize(800, 600) self.image_label.setStyleSheet("border: 2px solid gray; background-color: black;") display_layout.addWidget(self.image_label) # 状态栏 self.statusBar().showMessage("就绪") # 将左右面板添加到主布局 main_layout.addWidget(control_panel) main_layout.addWidget(display_panel) def initDetector(self, model_name): """ 初始化选中的检测器 """ model_path = self.model_list.get(model_name) if not model_path or not os.path.exists(model_path): QMessageBox.warning(self, "警告", f"模型权重文件不存在: {model_path}") return self.current_model = model_name try: if model_name.startswith('YOLOv8'): self.detector = YOLOv8Detector(model_path) elif model_name.startswith('YOLOv5'): self.detector = YOLOv5Detector(model_path) # ... 其他YOLO版本类似 else: self.detector = YOLOv8Detector(model_path) # 默认 self.statusBar().showMessage(f"模型加载成功: {model_name}") except Exception as e: QMessageBox.critical(self, "错误", f"模型加载失败: {str(e)}") def onModelChanged(self, model_name): self.initDetector(model_name) def onConfChanged(self, value): conf = value / 100.0 self.conf_value_label.setText(f"{conf:.2f}") def openImage(self): file_path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "Image Files (*.jpg *.png *.bmp *.jpeg)") if file_path: self.stopDetection() # 停止任何正在进行的检测 self.processImage(file_path) def processImage(self, img_path): img = cv2.imread(img_path) if img is None: return # 调整显示大小 display_img = self.resizeToDisplay(img) # 执行检测 if self.detector: conf = self.conf_slider.value() / 100.0 iou = self.iou_spinbox.value() results, infer_time = self.detector.detect(img, conf, iou) # 绘制结果 annotated_img = self.drawDetections(display_img.copy() if display_img is not img else img.copy(), results) # 更新显示 self.displayImage(annotated_img) # 更新统计 self.updateStatistics(results, infer_time) self.obj_count_label.setText(f"检测数量: {len(results)}") self.updateDetailText(results) def openVideo(self): file_path, _ = QFileDialog.getOpenFileName(self, "选择视频", "", "Video Files (*.mp4 *.avi *.mov *.mkv)") if file_path: self.startDetection(source=file_path) def openCamera(self): # 简单起见,使用默认摄像头 (ID=0) self.startDetection(source=0) def openRTSP(self): rtsp_url, ok = QInputDialog.getText(self, "RTSP流", "请输入RTSP URL:", text="rtsp://username:password@ip:port/stream") if ok and rtsp_url: self.startDetection(source=rtsp_url) def startDetection(self, source): if not self.detector: QMessageBox.warning(self, "警告", "请先加载模型!") return self.stopDetection() # 确保之前的线程停止 conf = self.conf_slider.value() / 100.0 iou = self.iou_spinbox.value() self.detector_thread = DetectorThread(self.detector, source, conf, iou) self.detector_thread.send_result.connect(self.onDetectionResult) self.detector_thread.send_fps.connect(self.onFPSUpdate) self.detector_thread.finished.connect(self.onDetectionFinished) self.btn_stop.setEnabled(True) self.btn_image.setEnabled(False) self.btn_video.setEnabled(False) self.btn_camera.setEnabled(False) self.btn_rtsp.setEnabled(False) self.detector_thread.start() self.statusBar().showMessage("检测进行中...") def stopDetection(self): if self.detector_thread and self.detector_thread.isRunning(): self.detector_thread.stop() self.detector_thread.wait() self.resetControlState() def onDetectionResult(self, frame, results, infer_time): # 调整显示大小并绘制检测框 display_frame = self.resizeToDisplay(frame) annotated_frame = self.drawDetections(display_frame, results) self.displayImage(annotated_frame) self.updateStatistics(results, infer_time) def onFPSUpdate(self, fps): self.fps_label.setText(f"FPS: {fps:.1f}") def onDetectionFinished(self): self.resetControlState() self.statusBar().showMessage("检测停止") def resetControlState(self): self.btn_stop.setEnabled(False) self.btn_image.setEnabled(True) self.btn_video.setEnabled(True) self.btn_camera.setEnabled(True) self.btn_rtsp.setEnabled(True) def drawDetections(self, image, results): """ 在图像上绘制检测框和标签 """ h, w = image.shape[:2] for det in results: # det: [x1, y1, x2, y2, conf, cls] x1, y1, x2, y2, conf, cls_id = map(int, det[:6]) cls_id = int(cls_id) label = f"{self.class_names[cls_id]} {conf:.2f}" # 颜色 color = self.class_colors[cls_id % len(self.class_colors)] # 画框 cv2.rectangle(image, (x1, y1), (x2, y2), color, 2) # 画标签背景 (label_w, label_h), baseline = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2) cv2.rectangle(image, (x1, y1-label_h-5), (x1+label_w, y1), color, -1) cv2.putText(image, label, (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2) return image def displayImage(self, image): """ 将OpenCV图像转换为QPixmap并显示 """ if len(image.shape) == 3: h, w, ch = image.shape bytes_per_line = ch * w qt_image = QImage(image.data, w, h, bytes_per_line, QImage.Format_BGR888) else: # 灰度图 h, w = image.shape qt_image = QImage(image.data, w, h, w, QImage.Format_Grayscale8) pixmap = QPixmap.fromImage(qt_image) scaled_pixmap = pixmap.scaled(self.image_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation) self.image_label.setPixmap(scaled_pixmap) def resizeToDisplay(self, img, max_width=1200, max_height=800): h, w = img.shape[:2] if w > max_width or h > max_height: scale = min(max_width/w, max_height/h) new_w, new_h = int(w*scale), int(h*scale) return cv2.resize(img, (new_w, new_h)) return img def updateStatistics(self, results, infer_time): self.infer_time_label.setText(f"推理时间: {infer_time*1000:.1f} ms") self.obj_count_label.setText(f"检测数量: {len(results)}") def updateDetailText(self, results): text = "" count_dict = {} for det in results: cls_id = int(det[5]) class_name = self.class_names[cls_id] count_dict[class_name] = count_dict.get(class_name, 0) + 1 for name, count in count_dict.items(): text += f"{name}: {count}个\n" self.detail_text.setText(text) def saveResult(self): if hasattr(self, 'last_annotated_image'): file_path, _ = QFileDialog.getSaveFileName(self, "保存结果", f"result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg", "JPEG (*.jpg);;PNG (*.png)") if file_path: cv2.imwrite(file_path, self.last_annotated_image) QMessageBox.information(self, "成功", f"结果已保存至: {file_path}") def closeEvent(self, event): self.stopDetection() event.accept() # YOLO检测器封装类示例 (YOLOv8) class YOLOv8Detector: def __init__(self, model_path, device='cuda' if torch.cuda.is_available() else 'cpu'): from ultralytics import YOLO self.model = YOLO(model_path) self.device = device self.names = self.model.names def detect(self, image, conf_thres=0.25, iou_thres=0.45): start_time = time.time() results = self.model(image, conf=conf_thres, iou=iou_thres, device=self.device, verbose=False)[0] infer_time = time.time() - start_time detections = [] if results.boxes is not None: boxes = results.boxes.xyxy.cpu().numpy() confs = results.boxes.conf.cpu().numpy() cls_ids = results.boxes.cls.cpu().numpy().astype(int) for box, conf, cls_id in zip(boxes, confs, cls_ids): detections.append([*box, conf, cls_id]) return detections, infer_time # YOLOv5检测器封装类示例 class YOLOv5Detector: def __init__(self, model_path, device='cuda' if torch.cuda.is_available() else 'cpu'): # 这里使用YOLOv5官方仓库中的加载方式 import torch self.model = torch.hub.load('ultralytics/yolov5', 'custom', path=model_path, force_reload=False) self.model.to(device) self.device = device self.names = self.model.names def detect(self, image, conf_thres=0.25, iou_thres=0.45): self.model.conf = conf_thres self.model.iou = iou_thres start_time = time.time() results = self.model(image) infer_time = time.time() - start_time detections = [] if results.xyxy[0].shape[0] > 0: tensor_results = results.xyxy[0].cpu().numpy() for det in tensor_results: # det: [x1, y1, x2, y2, conf, cls] detections.append(det.tolist()) return detections, infer_time if __name__ == '__main__': app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec())

3.4 推理接口封装 (inference_yolov8.py,inference_yolov5.py)

上面已经在主代码中给出了示例。对于YOLOv6和YOLOv7,需要根据其官方仓库的推理方式进行类似封装。


4. 系统部署与优化建议

4.1 部署流程

  1. 环境打包: 使用pyinstallercx_Freeze将Python应用打包成可执行文件。

    bash

pyinstaller --onefile --windowed --add-data "weights;weights" --add-data "datasets;datasets" main_app.py
  1. 模型优化

    • 模型量化: 使用PyTorch的量化工具将FP32模型转换为INT8,显著减少模型大小并提升推理速度,精度损失较小。

    • 模型剪枝: 移除网络中不重要的权重,减少计算量。

    • TensorRT加速: 对于NVIDIA GPU,可使用TensorRT将模型转换为高度优化的引擎,获得极致的推理性能。

4.2 生产环境建议

  • 硬件: 根据需求选择硬件。轻量级模型(YOLOv8n, YOLOv5s)可在Jetson Nano、NVIDIA Xavier NX等边缘设备上运行。高精度场景可选择服务器级GPU。

  • 软件架构: 对于多摄像头、高并发场景,考虑将检测服务容器化(Docker),并通过消息队列(如RabbitMQ)或gRPC接口提供检测服务,界面作为客户端调用。

  • 数据回流与持续学习: 系统应具备将疑难样本(低置信度、误检、漏检)自动保存的功能,用于后续模型迭代优化,形成闭环。


5. 结论与展望

本文详细介绍了基于YOLO系列目标检测算法构建的工业机械器件智能识别系统的完整实现方案。该系统具备模型训练、多源推理、可视化交互三大核心功能,并提供了完整的代码实现。通过使用PySide6,我们构建了一个专业、易用的桌面应用程序。

本系统的优势在于:

  1. 灵活性: 支持主流YOLO版本,用户可根据需求选择。

  2. 易用性: 图形界面降低了使用门槛,无需编写代码即可进行检测。

  3. 完整性: 提供了从数据准备、模型训练到应用部署的全链路指南。

  4. 可扩展性: 代码结构清晰,易于添加新的器件类别、新的算法或新的功能模块(如缺陷检测尺寸测量等)。

未来工作展望

  • 集成YOLO-World等开放词汇检测模型,实现零样本或小样本的新器件识别。

  • 增加DeepSORT等跟踪算法,实现对视频中器件的连续跟踪与计数。

  • 开发基于Web的B/S架构系统,便于远程访问和协同管理。

  • 结合3D视觉,获取器件的三维位姿信息,用于机器人抓取引导。

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

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

立即咨询