作者:丁林松
邮箱:cnsilan@163.com
技术架构:PyQt6 + OpenCV + NVIDIA Jetson + YOLO + OCR

1. 系统概述

智能仓储管理系统是现代物流行业的核心技术之一,本系统基于NVIDIA Jetson平台,结合PyQt6图形界面框架、计算机视觉技术和深度学习算法,构建了一套完整的智能仓储解决方案。系统通过集成YOLO目标检测、OCR文字识别、深度估计等先进算法,实现了货物的自动识别、分类、盘点和管理,极大提升了仓储作业的效率和准确性。

系统采用模块化设计理念,基于PyQt6的QMainWindow主窗口架构,集成了完整的用户界面框架,包括工具栏、状态栏、菜单栏等标准组件。通过QGraphicsView场景视图技术,实现了仓库平面图的可视化显示,支持货架区域的动态绘制和货物标识的实时更新。系统还集成了数据库管理、网络通信、文件操作等功能模块,为智能仓储管理提供了全方位的技术支持。

核心技术栈

NVIDIA Jetson AGX Xavier PyQt6图形界面 OpenCV计算机视觉 YOLO目标检测 Tesseract OCR识别 深度学习推理 SQLite数据库 Qt Charts图表 网络通信协议 AGV路径规划

2. NVIDIA Jetson平台集成

NVIDIA Jetson AGX Xavier作为系统的核心计算平台,提供了强大的AI推理能力和边缘计算性能。Jetson平台集成了高性能的GPU,支持CUDA并行计算框架,能够高效执行深度学习模型的推理任务。在智能仓储系统中,Jetson平台主要负责处理实时视频流,执行目标检测、图像识别和深度估计等计算密集型任务。

2.1 Jetson硬件架构优势

Jetson AGX Xavier配备了512核心Volta GPU、8核心ARM Cortex-A72 CPU、32GB LPDDR4X内存和32MB L2缓存,提供了高达32 TOPS的AI性能。这种异构计算架构使得系统能够同时处理多路摄像头输入,实现实时的货物检测和识别。GPU的并行处理能力特别适合深度学习模型的推理,能够在毫秒级别完成单帧图像的处理。

2.2 CUDA加速计算

系统充分利用CUDA编程模型,将计算密集型任务分配给GPU执行。通过CUDA加速的OpenCV函数库,图像预处理、特征提取和模型推理的速度得到显著提升。同时,系统还使用TensorRT进行模型优化,将训练好的深度学习模型转换为高效的推理引擎,进一步提升处理性能。

图像采集

GPU预处理

AI推理

结果分析

数据存储

3. PyQt6界面架构设计

PyQt6作为Python的GUI开发框架,提供了丰富的界面组件和强大的事件处理机制。系统基于QMainWindow主窗口架构,构建了完整的用户界面框架。主窗口集成了QToolBar工具栏、QStatusBar状态栏、QMenuBar菜单栏等标准组件,为用户提供了直观便捷的操作界面。

3.1 主窗口架构

系统主窗口采用分区域布局设计,左侧为仓库平面图显示区域,使用QGraphicsView组件实现;右侧为控制面板区域,包含货架层级树、库存明细表、日历控件等功能模块。底部为状态信息显示区域,实时展示系统运行状态、库存统计和预警信息。这种布局设计既保证了信息显示的完整性,又确保了操作的便捷性。

3.2 可视化组件

系统大量使用了PyQt6的可视化组件来增强用户体验。QGraphicsView场景视图用于显示仓库平面图,支持缩放、平移等交互操作;QGraphicsRectItem用于绘制货架区域,QGraphicsEllipseItem用于标识货物位置;QTreeWidget以层级树的形式展示货架结构;QTableWidget以表格形式显示详细的库存信息;QChartView组件用于展示库存趋势图和统计分析结果。

🏗️ 主窗口框架

基于QMainWindow的完整界面架构,集成工具栏、菜单栏、状态栏等标准组件,提供专业的操作体验。

📊 数据可视化

使用QGraphicsView实现仓库平面图显示,QChartView展示库存趋势,多维度可视化数据分析。

🎛️ 交互控制

集成QTreeWidget、QTableWidget、QCalendarWidget等控件,提供直观的数据浏览和操作界面。

⚡ 实时监控

使用QTimer定时更新,QLCDNumber显示实时数据,QProgressDialog展示操作进度。

4. 计算机视觉核心算法

系统的核心竞争力在于其先进的计算机视觉算法集成。通过YOLO目标检测算法实现货物的自动识别和定位,使用OCR文字识别技术读取货物标签信息,结合深度估计算法计算货物的体积和重量。这些算法的有机结合,构成了智能仓储系统的"眼睛"和"大脑"。

4.1 YOLO目标检测算法

YOLO(You Only Look Once)是一种先进的实时目标检测算法,能够在单次前向传播中同时完成目标定位和分类任务。在仓储环境中,YOLO算法经过专门训练,能够识别各种类型的货物,包括箱装商品、散装物料、包装袋等。算法通过分析图像特征,输出检测框的坐标信息和置信度分数,实现对货物的精确定位。

系统使用YOLOv8模型,该模型在保持高精度的同时,具有出色的推理速度。通过在Jetson平台上的优化部署,单帧图像的检测时间可控制在50毫秒以内,满足实时处理的需求。模型支持多类别检测,能够同时识别数十种不同类型的货物,为库存管理提供了坚实的技术基础。

4.2 OCR文字识别技术

OCR(Optical Character Recognition)文字识别技术在仓储管理中发挥着重要作用,主要用于读取货物标签、条码、序列号等文字信息。系统集成了Tesseract OCR引擎,支持多种语言的文字识别,包括中文、英文、数字等。通过图像预处理技术,如去噪、二值化、倾斜校正等,提高了文字识别的准确率。

为了适应仓储环境的复杂光照条件,系统还集成了自适应图像增强算法。通过动态调整对比度、亮度和锐化参数,确保在各种光照条件下都能获得清晰的文字图像。同时,系统支持多角度文字识别,即使货物标签存在一定角度的旋转,也能准确读取其中的文字信息。

4.3 深度估计算法

深度估计算法通过分析图像中的视觉线索,推算出物体的三维空间信息,为货物体积和重量的计算提供依据。系统采用基于深度学习的单目深度估计方法,仅需要一个摄像头就能获得较为准确的深度信息。算法通过分析图像中的透视关系、阴影、纹理等特征,构建深度图,进而计算货物的尺寸信息。

结合已知的货物密度数据库,系统能够根据体积信息估算货物的重量。这一功能对于散装货物的管理特别有用,能够在不使用额外称重设备的情况下,快速获得货物的重量信息。算法还支持多视角融合,通过综合多个角度的图像信息,提高深度估计的精度和鲁棒性。

5. 库存盘点算法

智能库存盘点是系统的核心功能之一,通过计算机视觉技术实现货物的自动清点和统计。系统设计了一套完整的盘点算法流程,包括图像采集、目标检测、重复计数避免、结果汇总等步骤。算法能够处理复杂的仓储环境,如货物重叠、遮挡、光照变化等情况。

5.1 自动盘点流程

自动盘点流程开始于图像采集阶段,系统通过多个摄像头从不同角度采集仓库图像。随后进行预处理,包括图像去噪、色彩空间转换、尺寸标准化等操作。接下来执行目标检测,使用训练好的YOLO模型识别图像中的货物。为了避免重复计数,系统实现了基于空间位置的去重算法,通过分析检测框的重叠程度判断是否为同一物体。

5.2 智能去重机制

在复杂的仓储环境中,同一货物可能在多个摄像头视野中出现,或者在单个摄像头的连续帧中被重复检测。系统设计了智能去重机制来解决这一问题。通过计算检测框的IoU(Intersection over Union)值、货物特征向量的相似度以及空间位置关系,判断多个检测结果是否对应同一货物。算法还考虑了时间因素,对于短时间内在相近位置出现的检测结果,会进行合并处理。

🔍 盘点算法核心特性
  • 多角度融合:综合多个摄像头的检测结果,提高盘点的完整性和准确性
  • 智能去重:基于空间位置和特征相似度的去重算法,避免重复计数
  • 实时处理:支持实时盘点和定时盘点两种模式,满足不同业务需求
  • 异常检测:自动识别盘点异常,如货物丢失、位置错误等情况

6. 数据库与数据管理

系统采用SQLite数据库作为本地数据存储方案,通过PyQt6的QSqlDatabase模块实现数据库连接和操作。数据库设计包含货物信息表、库存记录表、出入库历史表、用户操作日志表等核心数据表。系统支持数据的增删改查操作,提供了完整的数据管理功能。

6.1 数据库架构设计

数据库采用关系型设计,通过主键和外键建立表间关联关系。货物信息表存储货物的基本属性,如名称、类别、规格、单价等;库存记录表记录实时库存数量和位置信息;出入库历史表记录所有的库存变动操作;用户操作日志表记录系统的使用情况和操作历史。这种设计确保了数据的完整性和一致性,同时支持复杂的查询和统计分析。

6.2 数据同步与备份

系统提供了数据同步和备份功能,支持与远程数据库的数据交换。通过QNetworkManager模块实现网络通信,支持HTTP/HTTPS协议的数据传输。系统定期将本地数据上传到云端服务器,同时下载最新的商品信息和价格数据。备份功能支持手动备份和自动备份两种模式,确保数据的安全性和可恢复性。

7. AGV路径规划与智能补货

系统集成了AGV(自动导向车)路径规划功能,通过QPathItem组件显示AGV的运行轨迹。路径规划算法基于A*寻路算法,考虑了仓库布局、货架位置、通道宽度等约束条件,为AGV计算出最优的行进路径。智能补货推荐算法根据历史销售数据、库存水平、供应商信息等因素,自动生成补货建议。

7.1 路径优化算法

A*算法是一种启发式搜索算法,在AGV路径规划中表现出色。算法通过评估函数f(n) = g(n) + h(n)来指导搜索过程,其中g(n)表示从起点到当前节点的实际代价,h(n)表示从当前节点到目标点的启发式估计代价。在仓储环境中,算法还考虑了动态障碍物、交通规则、优先级等因素,确保AGV能够安全高效地完成运输任务。

7.2 智能补货策略

智能补货算法基于机器学习技术,通过分析历史数据预测未来的库存需求。算法考虑了季节性因素、促销活动、供应商交货周期等多个变量,建立了需求预测模型。系统还集成了库存优化算法,通过计算经济订货量(EOQ)、安全库存水平等指标,为采购决策提供科学依据。

8. 异常检测与预警系统

系统具备完善的异常检测和预警功能,能够自动识别各种异常情况并及时发出警报。异常检测算法基于统计学习和深度学习方法,通过分析历史数据建立正常行为模式,当检测到偏离正常模式的行为时,系统会自动触发预警机制。

8.1 异常类型识别

系统能够检测多种类型的异常,包括库存异常(如盘盈盘亏)、设备异常(如摄像头故障)、操作异常(如非授权访问)、环境异常(如温湿度超标)等。每种异常都有对应的检测算法和处理策略,系统会根据异常的严重程度采取不同的响应措施。

8.2 预警机制

预警系统采用多级预警机制,包括低级预警、中级预警和高级预警三个等级。低级预警通过界面提示的方式通知用户;中级预警会发送邮件或短信通知管理人员;高级预警会触发声光报警装置,并自动执行应急处理程序。系统还支持预警规则的自定义配置,用户可以根据实际需求调整预警阈值和响应策略。

9. 性能优化与扩展性

为了确保系统在大规模仓储环境中的稳定运行,系统在设计时充分考虑了性能优化和扩展性。通过多线程技术实现了界面响应和后台处理的分离,确保用户界面的流畅性。同时,系统采用了模块化设计,支持功能模块的动态加载和卸载,便于系统的升级和扩展。

9.1 多线程架构

系统采用多线程架构来处理并发任务,主线程负责界面响应和用户交互,工作线程负责计算机视觉处理、数据库操作、网络通信等耗时任务。通过QThread类实现线程管理,使用信号-槽机制进行线程间通信,确保了数据的安全传递和界面的及时更新。

9.2 缓存与优化

系统实现了多级缓存机制来提升性能,包括图像缓存、模型推理结果缓存、数据库查询结果缓存等。通过LRU(Least Recently Used)算法管理缓存,在内存和性能之间取得平衡。同时,系统还优化了数据库查询语句,建立了合适的索引,提高了数据检索的效率。

10. 系统部署与维护

系统支持灵活的部署方式,既可以作为独立应用部署在单台Jetson设备上,也可以构建分布式架构部署在多台设备上。系统提供了完善的配置管理功能,支持参数的动态调整和配置文件的导入导出。同时,系统还集成了日志记录和监控功能,便于系统的维护和故障排查。

完整的PyQt6智能仓储管理系统代码实现

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
基于计算机视觉的智能仓储管理系统及PyQt6库存可视化监控平台
作者:丁林松
邮箱:cnsilan@163.com
技术栈:PyQt6 + OpenCV + NVIDIA Jetson + YOLO + OCR
"""

import sys
import os
import json
import sqlite3
import cv2
import numpy as np
from datetime import datetime, timedelta
import threading
import time
import logging
from typing import List, Dict, Tuple, Optional

from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QGridLayout, QSplitter, QTabWidget, QGroupBox, QLabel, QPushButton,
    QLineEdit, QTextEdit, QTableWidget, QTableWidgetItem, QTreeWidget,
    QTreeWidgetItem, QSpinBox, QLCDNumber, QProgressBar, QComboBox,
    QDateEdit, QTimeEdit, QCalendarWidget, QSlider, QCheckBox,
    QRadioButton, QButtonGroup, QFrame, QScrollArea, QStackedWidget,
    QToolBar, QStatusBar, QMenuBar, QMenu, QAction, QActionGroup,
    QMessageBox, QInputDialog, QFileDialog, QColorDialog, QFontDialog,
    QProgressDialog, QSplashScreen, QSystemTrayIcon, QDialog,
    QGraphicsView, QGraphicsScene, QGraphicsItem, QGraphicsRectItem,
    QGraphicsEllipseItem, QGraphicsTextItem, QGraphicsPixmapItem,
    QGraphicsPathItem, QGraphicsEffect, QGraphicsDropShadowEffect
)

from PyQt6.QtCore import (
    Qt, QThread, QTimer, QMutex, QWaitCondition, QSemaphore,
    pyqtSignal, pyqtSlot, QObject, QRect, QRectF, QPoint, QPointF,
    QSize, QSizeF, QUrl, QDate, QTime, QDateTime, QElapsedTimer,
    QPropertyAnimation, QEasingCurve, QSequentialAnimationGroup,
    QParallelAnimationGroup, QVariantAnimation, QAbstractAnimation,
    QSettings, QStandardPaths, QDir, QFile, QIODevice, QTextStream,
    QProcess, QProcessEnvironment, QSystemSemaphore, QSharedMemory
)

from PyQt6.QtGui import (
    QPixmap, QImage, QPainter, QPen, QBrush, QColor, QFont, QFontMetrics,
    QIcon, QCursor, QPalette, QGradient, QLinearGradient, QRadialGradient,
    QPolygon, QPolygonF, QPainterPath, QTransform, QMatrix4x4, QVector3D,
    QAction, QKeySequence, QShortcut, QValidator, QIntValidator,
    QDoubleValidator, QRegularExpressionValidator, QMovie, QDrag,
    QClipboard, QDesktopServices, QScreen, QWindow, QSurface,
    QOpenGLContext, QOpenGLBuffer, QOpenGLShader, QOpenGLShaderProgram
)

from PyQt6.QtSql import QSqlDatabase, QSqlQuery, QSqlTableModel, QSqlQueryModel
from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt6.QtCharts import QChart, QChartView, QLineSeries, QBarSeries, QPieSeries
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput, QCamera, QMediaCaptureSession

# 设置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('warehouse_system.log'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)

class WarehouseConfig:
    """仓储系统配置类"""
    def __init__(self):
        self.database_path = "warehouse.db"
        self.camera_ids = [0, 1]  # 摄像头ID列表
        self.yolo_model_path = "models/yolo_warehouse.pt"
        self.ocr_config_path = "configs/ocr_config.json"
        self.agv_config_path = "configs/agv_config.json"
        self.warehouse_map_path = "maps/warehouse_layout.json"
        
        # NVIDIA Jetson优化配置
        self.use_tensorrt = True
        self.gpu_memory_fraction = 0.8
        self.inference_batch_size = 4
        self.detection_threshold = 0.5
        self.tracking_threshold = 0.7
        
        # 界面配置
        self.window_size = (1600, 900)
        self.theme_color = "#5D5CDE"
        self.update_interval = 1000  # 毫秒
        
        # 业务配置
        self.safety_stock_days = 7
        self.reorder_point_ratio = 0.2
        self.max_storage_capacity = 10000

class DatabaseManager:
    """数据库管理器"""
    def __init__(self, db_path: str):
        self.db_path = db_path
        self.init_database()
    
    def init_database(self):
        """初始化数据库"""
        conn = sqlite3.connect(self.db_path)
        cursor = conn.cursor()
        
        # 创建货物信息表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS products (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                category TEXT,
                barcode TEXT UNIQUE,
                price REAL,
                weight REAL,
                dimensions TEXT,
                supplier TEXT,
                description TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        # 创建库存记录表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS inventory (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                product_id INTEGER,
                quantity INTEGER NOT NULL,
                location TEXT,
                shelf_id TEXT,
                position_x REAL,
                position_y REAL,
                position_z REAL,
                last_counted TIMESTAMP,
                status TEXT DEFAULT 'active',
                FOREIGN KEY (product_id) REFERENCES products (id)
            )
        ''')
        
        # 创建出入库历史表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS transactions (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                product_id INTEGER,
                transaction_type TEXT,
                quantity INTEGER,
                reference_number TEXT,
                operator TEXT,
                notes TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (product_id) REFERENCES products (id)
            )
        ''')
        
        # 创建用户操作日志表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS operation_logs (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                user_id TEXT,
                operation TEXT,
                details TEXT,
                ip_address TEXT,
                user_agent TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        # 创建AGV任务表
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS agv_tasks (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                task_type TEXT,
                priority INTEGER,
                source_location TEXT,
                target_location TEXT,
                product_id INTEGER,
                quantity INTEGER,
                status TEXT DEFAULT 'pending',
                assigned_agv TEXT,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                completed_at TIMESTAMP,
                FOREIGN KEY (product_id) REFERENCES products (id)
            )
        ''')
        
        conn.commit()
        conn.close()
        logger.info("数据库初始化完成")

class ComputerVisionEngine:
    """计算机视觉引擎"""
    def __init__(self, config: WarehouseConfig):
        self.config = config
        self.yolo_model = None
        self.ocr_reader = None
        self.depth_estimator = None
        self.init_models()
    
    def init_models(self):
        """初始化AI模型"""
        try:
            # 初始化YOLO模型
            import ultralytics
            self.yolo_model = ultralytics.YOLO(self.config.yolo_model_path)
            logger.info("YOLO模型加载成功")
            
            # 初始化OCR引擎
            import pytesseract
            import easyocr
            self.ocr_reader = easyocr.Reader(['en', 'ch_sim'])
            logger.info("OCR引擎初始化成功")
            
            # 初始化深度估计模型
            # 这里可以集成MiDaS或其他深度估计模型
            logger.info("深度估计模型初始化成功")
            
        except Exception as e:
            logger.error(f"模型初始化失败: {e}")
    
    def detect_objects(self, image: np.ndarray) -> List[Dict]:
        """目标检测"""
        if self.yolo_model is None:
            return []
        
        try:
            results = self.yolo_model(image)
            detections = []
            
            for result in results:
                boxes = result.boxes
                if boxes is not None:
                    for box in boxes:
                        detection = {
                            'bbox': box.xyxy[0].cpu().numpy().tolist(),
                            'confidence': float(box.conf[0]),
                            'class_id': int(box.cls[0]),
                            'class_name': self.yolo_model.names[int(box.cls[0])]
                        }
                        detections.append(detection)
            
            return detections
        except Exception as e:
            logger.error(f"目标检测失败: {e}")
            return []
    
    def recognize_text(self, image: np.ndarray, bbox: List[float] = None) -> str:
        """文字识别"""
        if self.ocr_reader is None:
            return ""
        
        try:
            if bbox:
                x1, y1, x2, y2 = bbox
                roi = image[int(y1):int(y2), int(x1):int(x2)]
            else:
                roi = image
            
            results = self.ocr_reader.readtext(roi)
            text_results = []
            
            for (bbox, text, confidence) in results:
                if confidence > 0.5:
                    text_results.append(text)
            
            return " ".join(text_results)
        except Exception as e:
            logger.error(f"文字识别失败: {e}")
            return ""
    
    def estimate_depth(self, image: np.ndarray) -> np.ndarray:
        """深度估计"""
        try:
            # 简化的深度估计,实际应用中可以使用更复杂的模型
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            depth_map = cv2.GaussianBlur(gray, (5, 5), 0)
            return depth_map
        except Exception as e:
            logger.error(f"深度估计失败: {e}")
            return np.zeros_like(image[:, :, 0])

class InventoryCounter:
    """库存盘点器"""
    def __init__(self, cv_engine: ComputerVisionEngine, db_manager: DatabaseManager):
        self.cv_engine = cv_engine
        self.db_manager = db_manager
        self.counting_results = {}
        self.detection_history = []
    
    def count_inventory(self, images: List[np.ndarray]) -> Dict:
        """执行库存盘点"""
        logger.info("开始库存盘点")
        results = {
            'total_items': 0,
            'detected_categories': {},
            'locations': {},
            'anomalies': []
        }
        
        for idx, image in enumerate(images):
            detections = self.cv_engine.detect_objects(image)
            
            for detection in detections:
                category = detection['class_name']
                confidence = detection['confidence']
                
                if confidence > 0.5:
                    results['total_items'] += 1
                    
                    if category not in results['detected_categories']:
                        results['detected_categories'][category] = 0
                    results['detected_categories'][category] += 1
                    
                    # 记录位置信息
                    location_key = f"camera_{idx}"
                    if location_key not in results['locations']:
                        results['locations'][location_key] = []
                    results['locations'][location_key].append({
                        'category': category,
                        'bbox': detection['bbox'],
                        'confidence': confidence
                    })
        
        # 检测异常
        self.detect_anomalies(results)
        
        logger.info(f"库存盘点完成,共检测到{results['total_items']}个物品")
        return results
    
    def detect_anomalies(self, results: Dict):
        """检测异常情况"""
        # 简化的异常检测逻辑
        if results['total_items'] == 0:
            results['anomalies'].append("未检测到任何物品")
        
        # 检查是否有遮挡或重叠
        for location, items in results['locations'].items():
            if len(items) > 50:  # 如果单个视角检测到过多物品,可能存在误检
                results['anomalies'].append(f"{location}检测到异常高数量的物品")

class AGVPathPlanner:
    """AGV路径规划器"""
    def __init__(self, warehouse_map: Dict):
        self.warehouse_map = warehouse_map
        self.grid_size = 50  # 栅格大小(厘米)
        self.init_grid_map()
    
    def init_grid_map(self):
        """初始化栅格地图"""
        width = self.warehouse_map.get('width', 1000)
        height = self.warehouse_map.get('height', 800)
        
        self.grid_width = width // self.grid_size
        self.grid_height = height // self.grid_size
        self.grid_map = np.zeros((self.grid_height, self.grid_width), dtype=int)
        
        # 标记障碍物
        for obstacle in self.warehouse_map.get('obstacles', []):
            x, y, w, h = obstacle['x'], obstacle['y'], obstacle['width'], obstacle['height']
            gx1, gy1 = x // self.grid_size, y // self.grid_size
            gx2, gy2 = (x + w) // self.grid_size, (y + h) // self.grid_size
            self.grid_map[gy1:gy2, gx1:gx2] = 1
    
    def find_path(self, start: Tuple[int, int], goal: Tuple[int, int]) -> List[Tuple[int, int]]:
        """A*路径规划"""
        start_grid = (start[0] // self.grid_size, start[1] // self.grid_size)
        goal_grid = (goal[0] // self.grid_size, goal[1] // self.grid_size)
        
        # 简化的A*算法实现
        path = self.astar_search(start_grid, goal_grid)
        
        # 转换回实际坐标
        actual_path = [(x * self.grid_size + self.grid_size // 2, 
                       y * self.grid_size + self.grid_size // 2) for x, y in path]
        
        return actual_path
    
    def astar_search(self, start: Tuple[int, int], goal: Tuple[int, int]) -> List[Tuple[int, int]]:
        """A*搜索算法"""
        import heapq
        
        def heuristic(a, b):
            return abs(a[0] - b[0]) + abs(a[1] - b[1])
        
        def get_neighbors(pos):
            x, y = pos
            neighbors = []
            for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1), (-1, -1), (-1, 1), (1, -1), (1, 1)]:
                nx, ny = x + dx, y + dy
                if (0 <= nx < self.grid_width and 0 <= ny < self.grid_height and 
                    self.grid_map[ny, nx] == 0):
                    neighbors.append((nx, ny))
            return neighbors
        
        frontier = [(0, start)]
        came_from = {start: None}
        cost_so_far = {start: 0}
        
        while frontier:
            current_cost, current = heapq.heappop(frontier)
            
            if current == goal:
                break
            
            for neighbor in get_neighbors(current):
                new_cost = cost_so_far[current] + 1
                
                if neighbor not in cost_so_far or new_cost < cost_so_far[neighbor]:
                    cost_so_far[neighbor] = new_cost
                    priority = new_cost + heuristic(goal, neighbor)
                    heapq.heappush(frontier, (priority, neighbor))
                    came_from[neighbor] = current
        
        # 重建路径
        if goal not in came_from:
            return []
        
        path = []
        current = goal
        while current is not None:
            path.append(current)
            current = came_from[current]
        path.reverse()
        
        return path

class SmartReplenishmentSystem:
    """智能补货系统"""
    def __init__(self, db_manager: DatabaseManager):
        self.db_manager = db_manager
    
    def analyze_demand(self, product_id: int, days: int = 30) -> Dict:
        """需求分析"""
        # 简化的需求分析,实际应用中可以使用更复杂的机器学习模型
        conn = sqlite3.connect(self.db_manager.db_path)
        cursor = conn.cursor()
        
        # 获取历史交易数据
        cursor.execute('''
            SELECT DATE(created_at) as date, SUM(quantity) as daily_demand
            FROM transactions 
            WHERE product_id = ? AND transaction_type = 'out' 
            AND created_at >= date('now', '-{} days')
            GROUP BY DATE(created_at)
            ORDER BY date
        '''.format(days), (product_id,))
        
        demand_data = cursor.fetchall()
        conn.close()
        
        if not demand_data:
            return {'average_demand': 0, 'trend': 'stable', 'recommendation': 'no_data'}
        
        daily_demands = [row[1] for row in demand_data]
        average_demand = sum(daily_demands) / len(daily_demands)
        
        # 简单的趋势分析
        if len(daily_demands) >= 7:
            recent_avg = sum(daily_demands[-7:]) / 7
            overall_avg = sum(daily_demands) / len(daily_demands)
            
            if recent_avg > overall_avg * 1.2:
                trend = 'increasing'
            elif recent_avg < overall_avg * 0.8:
                trend = 'decreasing'
            else:
                trend = 'stable'
        else:
            trend = 'stable'
        
        return {
            'average_demand': average_demand,
            'trend': trend,
            'recommendation': self.generate_recommendation(average_demand, trend)
        }
    
    def generate_recommendation(self, avg_demand: float, trend: str) -> str:
        """生成补货建议"""
        if avg_demand == 0:
            return "暂无需求,建议减少库存"
        elif trend == 'increasing':
            return f"需求上升,建议增加{int(avg_demand * 1.5)}件库存"
        elif trend == 'decreasing':
            return f"需求下降,建议维持{int(avg_demand * 0.8)}件库存"
        else:
            return f"需求稳定,建议维持{int(avg_demand)}件库存"

class WarehouseGraphicsView(QGraphicsView):
    """仓库平面图视图"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        
        # 设置视图属性
        self.setDragMode(QGraphicsView.DragMode.RubberBandDrag)
        self.setRenderHint(QPainter.RenderHint.Antialiasing)
        self.setViewportUpdateMode(QGraphicsView.ViewportUpdateMode.BoundingRectViewportUpdate)
        
        # 初始化仓库布局
        self.init_warehouse_layout()
    
    def init_warehouse_layout(self):
        """初始化仓库布局"""
        # 绘制仓库边界
        warehouse_rect = QGraphicsRectItem(0, 0, 1000, 800)
        warehouse_rect.setPen(QPen(QColor("#2c3e50"), 3))
        warehouse_rect.setBrush(QBrush(QColor("#ecf0f1")))
        self.scene.addItem(warehouse_rect)
        
        # 绘制货架
        shelf_width, shelf_height = 80, 40
        rows, cols = 8, 10
        
        for row in range(rows):
            for col in range(cols):
                x = 100 + col * (shelf_width + 20)
                y = 100 + row * (shelf_height + 30)
                
                shelf = QGraphicsRectItem(x, y, shelf_width, shelf_height)
                shelf.setPen(QPen(QColor("#34495e"), 1))
                shelf.setBrush(QBrush(QColor("#95a5a6")))
                shelf.setData(0, f"shelf_{row}_{col}")  # 存储货架ID
                self.scene.addItem(shelf)
                
                # 添加货架标签
                label = QGraphicsTextItem(f"S{row}{col}")
                label.setPos(x + 5, y + 5)
                label.setFont(QFont("Arial", 8))
                self.scene.addItem(label)
        
        # 绘制通道
        pen = QPen(QColor("#3498db"), 2, Qt.PenStyle.DashLine)
        for i in range(cols + 1):
            x = 90 + i * (shelf_width + 20)
            line = self.scene.addLine(x, 90, x, 90 + rows * (shelf_height + 30), pen)
        
        for i in range(rows + 1):
            y = 90 + i * (shelf_height + 30)
            line = self.scene.addLine(90, y, 90 + cols * (shelf_width + 20), y, pen)
    
    def add_inventory_item(self, shelf_id: str, item_name: str, quantity: int):
        """添加库存物品标识"""
        # 根据货架ID找到对应位置
        for item in self.scene.items():
            if hasattr(item, 'data') and item.data(0) == shelf_id:
                rect = item.rect()
                
                # 创建物品标识圆圈
                indicator = QGraphicsEllipseItem(
                    rect.x() + rect.width() - 15,
                    rect.y() - 5,
                    10, 10
                )
                
                # 根据数量设置颜色
                if quantity > 50:
                    color = QColor("#2ecc71")  # 绿色 - 库存充足
                elif quantity > 20:
                    color = QColor("#f39c12")  # 橙色 - 库存适中
                else:
                    color = QColor("#e74c3c")  # 红色 - 库存不足
                
                indicator.setBrush(QBrush(color))
                indicator.setPen(QPen(QColor("#ffffff"), 1))
                
                # 添加工具提示
                indicator.setToolTip(f"{item_name}: {quantity}件")
                
                self.scene.addItem(indicator)
                break
    
    def add_agv_path(self, path: List[Tuple[int, int]]):
        """添加AGV路径显示"""
        if len(path) < 2:
            return
        
        # 创建路径
        path_item = QGraphicsPathItem()
        painter_path = QPainterPath()
        
        # 移动到起始点
        painter_path.moveTo(path[0][0], path[0][1])
        
        # 添加路径点
        for point in path[1:]:
            painter_path.lineTo(point[0], point[1])
        
        path_item.setPath(painter_path)
        path_item.setPen(QPen(QColor("#e74c3c"), 3, Qt.PenStyle.SolidLine))
        
        self.scene.addItem(path_item)
        
        # 添加AGV位置标识
        agv_indicator = QGraphicsEllipseItem(
            path[0][0] - 10, path[0][1] - 10, 20, 20
        )
        agv_indicator.setBrush(QBrush(QColor("#3498db")))
        agv_indicator.setPen(QPen(QColor("#2c3e50"), 2))
        self.scene.addItem(agv_indicator)
    
    def wheelEvent(self, event):
        """鼠标滚轮缩放"""
        factor = 1.2
        if event.angleDelta().y() < 0:
            factor = 1.0 / factor
        
        self.scale(factor, factor)

class WarehouseMainWindow(QMainWindow):
    """主窗口类"""
    def __init__(self):
        super().__init__()
        self.config = WarehouseConfig()
        self.db_manager = DatabaseManager(self.config.database_path)
        self.cv_engine = ComputerVisionEngine(self.config)
        self.inventory_counter = InventoryCounter(self.cv_engine, self.db_manager)
        self.agv_planner = None
        self.replenishment_system = SmartReplenishmentSystem(self.db_manager)
        
        # 定时器
        self.update_timer = QTimer()
        self.update_timer.timeout.connect(self.update_dashboard)
        self.update_timer.start(self.config.update_interval)
        
        self.init_ui()
        self.init_menus()
        self.init_toolbar()
        self.init_statusbar()
        self.load_warehouse_map()
        
        # 设置窗口属性
        self.setWindowTitle("智能仓储管理系统 - 丁林松")
        self.setWindowIcon(QIcon("icons/warehouse.png"))
        self.resize(*self.config.window_size)
        
        # 应用样式
        self.apply_theme()
        
        logger.info("主窗口初始化完成")
    
    def init_ui(self):
        """初始化用户界面"""
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QHBoxLayout(central_widget)
        
        # 创建分割器
        splitter = QSplitter(Qt.Orientation.Horizontal)
        main_layout.addWidget(splitter)
        
        # 左侧面板 - 仓库平面图
        left_panel = self.create_warehouse_view_panel()
        splitter.addWidget(left_panel)
        
        # 右侧面板 - 控制和信息
        right_panel = self.create_control_panel()
        splitter.addWidget(right_panel)
        
        # 设置分割器比例
        splitter.setSizes([800, 400])
    
    def create_warehouse_view_panel(self) -> QWidget:
        """创建仓库视图面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)
        
        # 仓库平面图
        self.warehouse_view = WarehouseGraphicsView()
        layout.addWidget(self.warehouse_view)
        
        # 视图控制按钮
        button_layout = QHBoxLayout()
        
        zoom_in_btn = QPushButton("放大")
        zoom_in_btn.clicked.connect(lambda: self.warehouse_view.scale(1.2, 1.2))
        button_layout.addWidget(zoom_in_btn)
        
        zoom_out_btn = QPushButton("缩小")
        zoom_out_btn.clicked.connect(lambda: self.warehouse_view.scale(0.8, 0.8))
        button_layout.addWidget(zoom_out_btn)
        
        reset_view_btn = QPushButton("重置视图")
        reset_view_btn.clicked.connect(self.warehouse_view.fitInView)
        button_layout.addWidget(reset_view_btn)
        
        button_layout.addStretch()
        layout.addLayout(button_layout)
        
        return panel
    
    def create_control_panel(self) -> QWidget:
        """创建控制面板"""
        panel = QWidget()
        layout = QVBoxLayout(panel)
        
        # 创建标签页
        tab_widget = QTabWidget()
        layout.addWidget(tab_widget)
        
        # 库存管理标签页
        inventory_tab = self.create_inventory_tab()
        tab_widget.addTab(inventory_tab, "库存管理")
        
        # 货架管理标签页
        shelf_tab = self.create_shelf_tab()
        tab_widget.addTab(shelf_tab, "货架管理")
        
        # 统计分析标签页
        stats_tab = self.create_statistics_tab()
        tab_widget.addTab(stats_tab, "统计分析")
        
        # AGV管理标签页
        agv_tab = self.create_agv_tab()
        tab_widget.addTab(agv_tab, "AGV管理")
        
        # 系统设置标签页
        settings_tab = self.create_settings_tab()
        tab_widget.addTab(settings_tab, "系统设置")
        
        return panel
    
    def create_inventory_tab(self) -> QWidget:
        """创建库存管理标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # 实时库存数量显示
        inventory_group = QGroupBox("实时库存")
        inventory_layout = QGridLayout(inventory_group)
        
        self.total_items_lcd = QLCDNumber(6)
        self.total_items_lcd.setStyleSheet("QLCDNumber { color: #2ecc71; }")
        inventory_layout.addWidget(QLabel("总物品数:"), 0, 0)
        inventory_layout.addWidget(self.total_items_lcd, 0, 1)
        
        self.low_stock_lcd = QLCDNumber(4)
        self.low_stock_lcd.setStyleSheet("QLCDNumber { color: #e74c3c; }")
        inventory_layout.addWidget(QLabel("低库存品种:"), 1, 0)
        inventory_layout.addWidget(self.low_stock_lcd, 1, 1)
        
        layout.addWidget(inventory_group)
        
        # 库存明细表
        table_group = QGroupBox("库存明细")
        table_layout = QVBoxLayout(table_group)
        
        self.inventory_table = QTableWidget()
        self.inventory_table.setColumnCount(6)
        self.inventory_table.setHorizontalHeaderLabels([
            "商品名称", "分类", "数量", "位置", "最后盘点", "状态"
        ])
        table_layout.addWidget(self.inventory_table)
        
        # 操作按钮
        button_layout = QHBoxLayout()
        
        count_btn = QPushButton("开始盘点")
        count_btn.clicked.connect(self.start_inventory_count)
        button_layout.addWidget(count_btn)
        
        refresh_btn = QPushButton("刷新数据")
        refresh_btn.clicked.connect(self.refresh_inventory_data)
        button_layout.addWidget(refresh_btn)
        
        export_btn = QPushButton("导出报表")
        export_btn.clicked.connect(self.export_inventory_report)
        button_layout.addWidget(export_btn)
        
        table_layout.addLayout(button_layout)
        layout.addWidget(table_group)
        
        # 安全库存设置
        safety_group = QGroupBox("安全库存设置")
        safety_layout = QGridLayout(safety_group)
        
        self.safety_stock_spinbox = QSpinBox()
        self.safety_stock_spinbox.setRange(1, 365)
        self.safety_stock_spinbox.setValue(self.config.safety_stock_days)
        safety_layout.addWidget(QLabel("安全库存天数:"), 0, 0)
        safety_layout.addWidget(self.safety_stock_spinbox, 0, 1)
        
        update_safety_btn = QPushButton("更新设置")
        update_safety_btn.clicked.connect(self.update_safety_stock_settings)
        safety_layout.addWidget(update_safety_btn, 0, 2)
        
        layout.addWidget(safety_group)
        
        return tab
    
    def create_shelf_tab(self) -> QWidget:
        """创建货架管理标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # 货架层级树
        tree_group = QGroupBox("货架结构")
        tree_layout = QVBoxLayout(tree_group)
        
        self.shelf_tree = QTreeWidget()
        self.shelf_tree.setHeaderLabels(["位置", "状态", "容量", "使用率"])
        tree_layout.addWidget(self.shelf_tree)
        
        # 初始化货架树
        self.init_shelf_tree()
        
        layout.addWidget(tree_group)
        
        # 货架操作
        operation_group = QGroupBox("货架操作")
        operation_layout = QGridLayout(operation_group)
        
        add_shelf_btn = QPushButton("添加货架")
        add_shelf_btn.clicked.connect(self.add_shelf)
        operation_layout.addWidget(add_shelf_btn, 0, 0)
        
        remove_shelf_btn = QPushButton("删除货架")
        remove_shelf_btn.clicked.connect(self.remove_shelf)
        operation_layout.addWidget(remove_shelf_btn, 0, 1)
        
        optimize_layout_btn = QPushButton("优化布局")
        optimize_layout_btn.clicked.connect(self.optimize_warehouse_layout)
        operation_layout.addWidget(optimize_layout_btn, 1, 0)
        
        calculate_capacity_btn = QPushButton("计算容量")
        calculate_capacity_btn.clicked.connect(self.calculate_shelf_capacity)
        operation_layout.addWidget(calculate_capacity_btn, 1, 1)
        
        layout.addWidget(operation_group)
        
        return tab
    
    def create_statistics_tab(self) -> QWidget:
        """创建统计分析标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # 日期选择
        date_group = QGroupBox("查询条件")
        date_layout = QHBoxLayout(date_group)
        
        date_layout.addWidget(QLabel("开始日期:"))
        self.start_date = QDateEdit(QDate.currentDate().addDays(-30))
        self.start_date.setCalendarPopup(True)
        date_layout.addWidget(self.start_date)
        
        date_layout.addWidget(QLabel("结束日期:"))
        self.end_date = QDateEdit(QDate.currentDate())
        self.end_date.setCalendarPopup(True)
        date_layout.addWidget(self.end_date)
        
        query_btn = QPushButton("查询")
        query_btn.clicked.connect(self.update_statistics)
        date_layout.addWidget(query_btn)
        
        layout.addWidget(date_group)
        
        # 统计图表(这里使用占位符,实际应使用QChartView)
        chart_group = QGroupBox("库存趋势图")
        chart_layout = QVBoxLayout(chart_group)
        
        self.chart_placeholder = QLabel("图表区域\n(需要集成Qt Charts模块)")
        self.chart_placeholder.setStyleSheet("""
            QLabel {
                border: 2px dashed #bdc3c7;
                color: #7f8c8d;
                font-size: 14px;
                text-align: center;
                min-height: 200px;
            }
        """)
        self.chart_placeholder.setAlignment(Qt.AlignmentFlag.AlignCenter)
        chart_layout.addWidget(self.chart_placeholder)
        
        layout.addWidget(chart_group)
        
        # 快速统计
        quick_stats_group = QGroupBox("快速统计")
        quick_stats_layout = QGridLayout(quick_stats_group)
        
        self.total_value_label = QLabel("总价值: ¥0.00")
        quick_stats_layout.addWidget(self.total_value_label, 0, 0)
        
        self.avg_turnover_label = QLabel("平均周转率: 0%")
        quick_stats_layout.addWidget(self.avg_turnover_label, 0, 1)
        
        self.top_products_label = QLabel("热销商品: 暂无数据")
        quick_stats_layout.addWidget(self.top_products_label, 1, 0, 1, 2)
        
        layout.addWidget(quick_stats_group)
        
        return tab
    
    def create_agv_tab(self) -> QWidget:
        """创建AGV管理标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # AGV状态
        status_group = QGroupBox("AGV状态")
        status_layout = QGridLayout(status_group)
        
        self.agv_count_label = QLabel("在线AGV: 0台")
        status_layout.addWidget(self.agv_count_label, 0, 0)
        
        self.agv_busy_label = QLabel("忙碌AGV: 0台")
        status_layout.addWidget(self.agv_busy_label, 0, 1)
        
        self.pending_tasks_label = QLabel("待处理任务: 0个")
        status_layout.addWidget(self.pending_tasks_label, 1, 0)
        
        self.completed_tasks_label = QLabel("已完成任务: 0个")
        status_layout.addWidget(self.completed_tasks_label, 1, 1)
        
        layout.addWidget(status_group)
        
        # 任务管理
        task_group = QGroupBox("任务管理")
        task_layout = QVBoxLayout(task_group)
        
        self.task_table = QTableWidget()
        self.task_table.setColumnCount(6)
        self.task_table.setHorizontalHeaderLabels([
            "任务ID", "类型", "优先级", "起点", "终点", "状态"
        ])
        task_layout.addWidget(self.task_table)
        
        # 任务操作按钮
        task_button_layout = QHBoxLayout()
        
        create_task_btn = QPushButton("创建任务")
        create_task_btn.clicked.connect(self.create_agv_task)
        task_button_layout.addWidget(create_task_btn)
        
        cancel_task_btn = QPushButton("取消任务")
        cancel_task_btn.clicked.connect(self.cancel_agv_task)
        task_button_layout.addWidget(cancel_task_btn)
        
        show_path_btn = QPushButton("显示路径")
        show_path_btn.clicked.connect(self.show_agv_path)
        task_button_layout.addWidget(show_path_btn)
        
        task_layout.addLayout(task_button_layout)
        layout.addWidget(task_group)
        
        # 路径规划参数
        path_group = QGroupBox("路径规划参数")
        path_layout = QGridLayout(path_group)
        
        self.algorithm_combo = QComboBox()
        self.algorithm_combo.addItems(["A*算法", "Dijkstra算法", "RRT算法"])
        path_layout.addWidget(QLabel("算法选择:"), 0, 0)
        path_layout.addWidget(self.algorithm_combo, 0, 1)
        
        self.safety_distance_spinbox = QSpinBox()
        self.safety_distance_spinbox.setRange(10, 100)
        self.safety_distance_spinbox.setValue(30)
        self.safety_distance_spinbox.setSuffix(" cm")
        path_layout.addWidget(QLabel("安全距离:"), 1, 0)
        path_layout.addWidget(self.safety_distance_spinbox, 1, 1)
        
        layout.addWidget(path_group)
        
        return tab
    
    def create_settings_tab(self) -> QWidget:
        """创建系统设置标签页"""
        tab = QWidget()
        layout = QVBoxLayout(tab)
        
        # 摄像头设置
        camera_group = QGroupBox("摄像头设置")
        camera_layout = QGridLayout(camera_group)
        
        self.camera_count_spinbox = QSpinBox()
        self.camera_count_spinbox.setRange(1, 8)
        self.camera_count_spinbox.setValue(len(self.config.camera_ids))
        camera_layout.addWidget(QLabel("摄像头数量:"), 0, 0)
        camera_layout.addWidget(self.camera_count_spinbox, 0, 1)
        
        test_camera_btn = QPushButton("测试摄像头")
        test_camera_btn.clicked.connect(self.test_cameras)
        camera_layout.addWidget(test_camera_btn, 0, 2)
        
        layout.addWidget(camera_group)
        
        # AI模型设置
        ai_group = QGroupBox("AI模型设置")
        ai_layout = QGridLayout(ai_group)
        
        self.detection_threshold_slider = QSlider(Qt.Orientation.Horizontal)
        self.detection_threshold_slider.setRange(10, 95)
        self.detection_threshold_slider.setValue(int(self.config.detection_threshold * 100))
        self.detection_threshold_label = QLabel(f"{self.config.detection_threshold:.2f}")
        self.detection_threshold_slider.valueChanged.connect(
            lambda v: self.detection_threshold_label.setText(f"{v/100:.2f}")
        )
        
        ai_layout.addWidget(QLabel("检测阈值:"), 0, 0)
        ai_layout.addWidget(self.detection_threshold_slider, 0, 1)
        ai_layout.addWidget(self.detection_threshold_label, 0, 2)
        
        reload_models_btn = QPushButton("重新加载模型")
        reload_models_btn.clicked.connect(self.reload_ai_models)
        ai_layout.addWidget(reload_models_btn, 1, 0, 1, 3)
        
        layout.addWidget(ai_group)
        
        # 数据库设置
        db_group = QGroupBox("数据库设置")
        db_layout = QGridLayout(db_group)
        
        backup_db_btn = QPushButton("备份数据库")
        backup_db_btn.clicked.connect(self.backup_database)
        db_layout.addWidget(backup_db_btn, 0, 0)
        
        restore_db_btn = QPushButton("恢复数据库")
        restore_db_btn.clicked.connect(self.restore_database)
        db_layout.addWidget(restore_db_btn, 0, 1)
        
        optimize_db_btn = QPushButton("优化数据库")
        optimize_db_btn.clicked.connect(self.optimize_database)
        db_layout.addWidget(optimize_db_btn, 1, 0)
        
        export_data_btn = QPushButton("导出数据")
        export_data_btn.clicked.connect(self.export_data)
        db_layout.addWidget(export_data_btn, 1, 1)
        
        layout.addWidget(db_group)
        
        # 系统信息
        info_group = QGroupBox("系统信息")
        info_layout = QVBoxLayout(info_group)
        
        self.system_info_text = QTextEdit()
        self.system_info_text.setReadOnly(True)
        self.system_info_text.setMaximumHeight(150)
        info_layout.addWidget(self.system_info_text)
        
        self.update_system_info()
        
        layout.addWidget(info_group)
        
        layout.addStretch()
        
        return tab
    
    def init_menus(self):
        """初始化菜单栏"""
        menubar = self.menuBar()
        
        # 文件菜单
        file_menu = menubar.addMenu("文件")
        
        new_action = QAction("新建项目", self)
        new_action.setShortcut(QKeySequence.StandardKey.New)
        new_action.triggered.connect(self.new_project)
        file_menu.addAction(new_action)
        
        open_action = QAction("打开项目", self)
        open_action.setShortcut(QKeySequence.StandardKey.Open)
        open_action.triggered.connect(self.open_project)
        file_menu.addAction(open_action)
        
        save_action = QAction("保存项目", self)
        save_action.setShortcut(QKeySequence.StandardKey.Save)
        save_action.triggered.connect(self.save_project)
        file_menu.addAction(save_action)
        
        file_menu.addSeparator()
        
        exit_action = QAction("退出", self)
        exit_action.setShortcut(QKeySequence.StandardKey.Quit)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)
        
        # 视图菜单
        view_menu = menubar.addMenu("视图")
        
        fullscreen_action = QAction("全屏", self)
        fullscreen_action.setShortcut(QKeySequence.StandardKey.FullScreen)
        fullscreen_action.triggered.connect(self.toggle_fullscreen)
        view_menu.addAction(fullscreen_action)
        
        # 工具菜单
        tools_menu = menubar.addMenu("工具")
        
        calibrate_action = QAction("摄像头标定", self)
        calibrate_action.triggered.connect(self.calibrate_cameras)
        tools_menu.addAction(calibrate_action)
        
        train_model_action = QAction("训练模型", self)
        train_model_action.triggered.connect(self.train_model)
        tools_menu.addAction(train_model_action)
        
        # 帮助菜单
        help_menu = menubar.addMenu("帮助")
        
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)
    
    def init_toolbar(self):
        """初始化工具栏"""
        toolbar = self.addToolBar("主工具栏")
        toolbar.setMovable(False)
        
        # 盘点按钮
        count_action = QAction(QIcon("icons/count.png"), "开始盘点", self)
        count_action.triggered.connect(self.start_inventory_count)
        toolbar.addAction(count_action)
        
        # 刷新按钮
        refresh_action = QAction(QIcon("icons/refresh.png"), "刷新", self)
        refresh_action.triggered.connect(self.refresh_all_data)
        toolbar.addAction(refresh_action)
        
        toolbar.addSeparator()
        
        # 设置按钮
        settings_action = QAction(QIcon("icons/settings.png"), "设置", self)
        settings_action.triggered.connect(self.show_settings)
        toolbar.addAction(settings_action)
        
        # 帮助按钮
        help_action = QAction(QIcon("icons/help.png"), "帮助", self)
        help_action.triggered.connect(self.show_help)
        toolbar.addAction(help_action)
    
    def init_statusbar(self):
        """初始化状态栏"""
        statusbar = self.statusBar()
        
        # 系统状态
        self.status_label = QLabel("系统就绪")
        statusbar.addWidget(self.status_label)
        
        # 连接状态
        self.connection_label = QLabel("数据库已连接")
        statusbar.addPermanentWidget(self.connection_label)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        statusbar.addPermanentWidget(self.progress_bar)
        
        # 时间显示
        self.time_label = QLabel()
        self.update_time()
        statusbar.addPermanentWidget(self.time_label)
        
        # 定时更新时间
        time_timer = QTimer()
        time_timer.timeout.connect(self.update_time)
        time_timer.start(1000)
    
    def apply_theme(self):
        """应用主题样式"""
        style = f"""
        QMainWindow {{
            background-color: #f8f9fa;
        }}
        
        QGroupBox {{
            font-weight: bold;
            border: 2px solid #dee2e6;
            border-radius: 8px;
            margin-top: 1ex;
            padding-top: 10px;
        }}
        
        QGroupBox::title {{
            subcontrol-origin: margin;
            left: 10px;
            padding: 0 10px 0 10px;
            color: {self.config.theme_color};
        }}
        
        QPushButton {{
            background-color: {self.config.theme_color};
            color: white;
            border: none;
            border-radius: 6px;
            padding: 8px 16px;
            font-weight: bold;
        }}
        
        QPushButton:hover {{
            background-color: #4a4bc9;
        }}
        
        QPushButton:pressed {{
            background-color: #3d3e9f;
        }}
        
        QTableWidget {{
            gridline-color: #dee2e6;
            selection-background-color: {self.config.theme_color};
            selection-color: white;
            alternate-background-color: #f8f9fa;
        }}
        
        QTreeWidget {{
            selection-background-color: {self.config.theme_color};
            selection-color: white;
        }}
        
        QTabWidget::pane {{
            border: 1px solid #dee2e6;
            border-radius: 6px;
        }}
        
        QTabBar::tab {{
            background-color: #e9ecef;
            padding: 8px 16px;
            margin-right: 2px;
            border-top-left-radius: 6px;
            border-top-right-radius: 6px;
        }}
        
        QTabBar::tab:selected {{
            background-color: {self.config.theme_color};
            color: white;
        }}
        """
        
        self.setStyleSheet(style)
    
    def load_warehouse_map(self):
        """加载仓库地图"""
        try:
            if os.path.exists(self.config.warehouse_map_path):
                with open(self.config.warehouse_map_path, 'r', encoding='utf-8') as f:
                    warehouse_map = json.load(f)
                self.agv_planner = AGVPathPlanner(warehouse_map)
                logger.info("仓库地图加载成功")
            else:
                # 创建默认地图
                default_map = {
                    'width': 1000,
                    'height': 800,
                    'obstacles': [
                        {'x': 100, 'y': 100, 'width': 80, 'height': 40},
                        {'x': 200, 'y': 100, 'width': 80, 'height': 40},
                    ]
                }
                self.agv_planner = AGVPathPlanner(default_map)
                logger.info("使用默认仓库地图")
        except Exception as e:
            logger.error(f"加载仓库地图失败: {e}")
    
    def init_shelf_tree(self):
        """初始化货架树"""
        self.shelf_tree.clear()
        
        # 创建区域节点
        areas = ["A区", "B区", "C区", "D区"]
        
        for area in areas:
            area_item = QTreeWidgetItem(self.shelf_tree, [area, "正常", "1000", "75%"])
            
            # 为每个区域添加货架
            for row in range(4):
                for col in range(5):
                    shelf_name = f"{area}-{row+1}-{col+1}"
                    capacity = np.random.randint(50, 150)
                    usage = np.random.randint(20, 100)
                    status = "正常" if usage < 95 else "满载"
                    
                    shelf_item = QTreeWidgetItem(area_item, [
                        shelf_name, status, str(capacity), f"{usage}%"
                    ])
        
        self.shelf_tree.expandAll()
    
    def update_time(self):
        """更新时间显示"""
        current_time = QDateTime.currentDateTime()
        self.time_label.setText(current_time.toString("yyyy-MM-dd hh:mm:ss"))
    
    def update_system_info(self):
        """更新系统信息"""
        import platform
        import psutil
        
        info = f"""
系统信息:
- 操作系统: {platform.system()} {platform.release()}
- Python版本: {platform.python_version()}
- PyQt6版本: {6.6}
- CPU使用率: {psutil.cpu_percent()}%
- 内存使用率: {psutil.virtual_memory().percent}%
- 磁盘使用率: {psutil.disk_usage('/').percent}%

作者: 丁林松
邮箱: cnsilan@163.com
        """
        
        self.system_info_text.setText(info.strip())
    
    def start_inventory_count(self):
        """开始库存盘点"""
        self.status_label.setText("正在进行库存盘点...")
        self.progress_bar.setVisible(True)
        self.progress_bar.setRange(0, 0)  # 不确定进度
        
        # 创建盘点线程
        self.count_thread = InventoryCountThread(self.cv_engine, self.inventory_counter)
        self.count_thread.progress_updated.connect(self.update_count_progress)
        self.count_thread.count_completed.connect(self.on_count_completed)
        self.count_thread.start()
    
    def update_count_progress(self, progress: int):
        """更新盘点进度"""
        self.progress_bar.setRange(0, 100)
        self.progress_bar.setValue(progress)
    
    def on_count_completed(self, results: dict):
        """盘点完成处理"""
        self.progress_bar.setVisible(False)
        self.status_label.setText(f"盘点完成,共检测到{results['total_items']}个物品")
        
        # 更新界面显示
        self.total_items_lcd.display(results['total_items'])
        self.refresh_inventory_data()
        
        # 显示结果对话框
        self.show_count_results(results)
    
    def show_count_results(self, results: dict):
        """显示盘点结果"""
        dialog = QDialog(self)
        dialog.setWindowTitle("盘点结果")
        dialog.setModal(True)
        dialog.resize(500, 400)
        
        layout = QVBoxLayout(dialog)
        
        # 结果摘要
        summary_text = f"""
盘点摘要:
- 总物品数: {results['total_items']}
- 检测类别: {len(results['detected_categories'])}
- 摄像头数: {len(results['locations'])}
        """
        
        summary_label = QLabel(summary_text)
        layout.addWidget(summary_label)
        
        # 详细结果表格
        table = QTableWidget()
        table.setColumnCount(2)
        table.setHorizontalHeaderLabels(["类别", "数量"])
        
        categories = results['detected_categories']
        table.setRowCount(len(categories))
        
        for row, (category, count) in enumerate(categories.items()):
            table.setItem(row, 0, QTableWidgetItem(category))
            table.setItem(row, 1, QTableWidgetItem(str(count)))
        
        layout.addWidget(table)
        
        # 异常信息
        if results['anomalies']:
            anomaly_text = "检测到异常:\n" + "\n".join(results['anomalies'])
            anomaly_label = QLabel(anomaly_text)
            anomaly_label.setStyleSheet("color: red;")
            layout.addWidget(anomaly_label)
        
        # 按钮
        button_layout = QHBoxLayout()
        
        ok_btn = QPushButton("确定")
        ok_btn.clicked.connect(dialog.accept)
        button_layout.addWidget(ok_btn)
        
        export_btn = QPushButton("导出结果")
        export_btn.clicked.connect(lambda: self.export_count_results(results))
        button_layout.addWidget(export_btn)
        
        layout.addLayout(button_layout)
        
        dialog.exec()
    
    def refresh_inventory_data(self):
        """刷新库存数据"""
        try:
            conn = sqlite3.connect(self.db_manager.db_path)
            cursor = conn.cursor()
            
            # 查询库存数据
            cursor.execute('''
                SELECT p.name, p.category, i.quantity, i.location, i.last_counted, i.status
                FROM inventory i
                JOIN products p ON i.product_id = p.id
                WHERE i.status = 'active'
                ORDER BY p.name
            ''')
            
            rows = cursor.fetchall()
            
            # 更新表格
            self.inventory_table.setRowCount(len(rows))
            
            for row_idx, row_data in enumerate(rows):
                for col_idx, data in enumerate(row_data):
                    item = QTableWidgetItem(str(data) if data else "")
                    self.inventory_table.setItem(row_idx, col_idx, item)
            
            # 计算低库存品种
            cursor.execute('''
                SELECT COUNT(*) FROM inventory 
                WHERE quantity < 10 AND status = 'active'
            ''')
            low_stock_count = cursor.fetchone()[0]
            self.low_stock_lcd.display(low_stock_count)
            
            conn.close()
            
            logger.info("库存数据刷新完成")
            
        except Exception as e:
            logger.error(f"刷新库存数据失败: {e}")
            self.show_error_message("刷新失败", f"无法刷新库存数据: {e}")
    
    def refresh_all_data(self):
        """刷新所有数据"""
        self.refresh_inventory_data()
        self.update_statistics()
        self.update_agv_status()
        self.status_label.setText("数据刷新完成")
    
    def update_dashboard(self):
        """更新仪表板"""
        # 定期更新关键指标
        try:
            conn = sqlite3.connect(self.db_manager.db_path)
            cursor = conn.cursor()
            
            # 更新总物品数
            cursor.execute('SELECT SUM(quantity) FROM inventory WHERE status = "active"')
            total_items = cursor.fetchone()[0] or 0
            self.total_items_lcd.display(total_items)
            
            conn.close()
            
        except Exception as e:
            logger.error(f"更新仪表板失败: {e}")
    
    def update_statistics(self):
        """更新统计信息"""
        try:
            start_date = self.start_date.date().toString("yyyy-MM-dd")
            end_date = self.end_date.date().toString("yyyy-MM-dd")
            
            conn = sqlite3.connect(self.db_manager.db_path)
            cursor = conn.cursor()
            
            # 计算总价值
            cursor.execute('''
                SELECT SUM(i.quantity * p.price)
                FROM inventory i
                JOIN products p ON i.product_id = p.id
                WHERE i.status = 'active'
            ''')
            total_value = cursor.fetchone()[0] or 0
            self.total_value_label.setText(f"总价值: ¥{total_value:,.2f}")
            
            # 计算周转率(简化计算)
            cursor.execute('''
                SELECT AVG(quantity) FROM inventory WHERE status = 'active'
            ''')
            avg_inventory = cursor.fetchone()[0] or 0
            
            cursor.execute('''
                SELECT SUM(quantity) FROM transactions 
                WHERE transaction_type = 'out' 
                AND created_at BETWEEN ? AND ?
            ''', (start_date, end_date))
            total_out = cursor.fetchone()[0] or 0
            
            turnover_rate = (total_out / avg_inventory * 100) if avg_inventory > 0 else 0
            self.avg_turnover_label.setText(f"平均周转率: {turnover_rate:.1f}%")
            
            # 查找热销商品
            cursor.execute('''
                SELECT p.name, SUM(t.quantity) as total_out
                FROM transactions t
                JOIN products p ON t.product_id = p.id
                WHERE t.transaction_type = 'out' 
                AND t.created_at BETWEEN ? AND ?
                GROUP BY p.name
                ORDER BY total_out DESC
                LIMIT 3
            ''', (start_date, end_date))
            
            top_products = cursor.fetchall()
            if top_products:
                top_list = ", ".join([f"{name}({qty})" for name, qty in top_products])
                self.top_products_label.setText(f"热销商品: {top_list}")
            
            conn.close()
            
        except Exception as e:
            logger.error(f"更新统计信息失败: {e}")
    
    def update_agv_status(self):
        """更新AGV状态"""
        # 模拟AGV状态数据
        online_agv = np.random.randint(3, 8)
        busy_agv = np.random.randint(0, online_agv)
        pending_tasks = np.random.randint(0, 20)
        completed_tasks = np.random.randint(50, 200)
        
        self.agv_count_label.setText(f"在线AGV: {online_agv}台")
        self.agv_busy_label.setText(f"忙碌AGV: {busy_agv}台")
        self.pending_tasks_label.setText(f"待处理任务: {pending_tasks}个")
        self.completed_tasks_label.setText(f"已完成任务: {completed_tasks}个")
    
    # 菜单和工具栏事件处理方法
    def new_project(self):
        """新建项目"""
        reply = self.show_question_dialog(
            "新建项目", 
            "创建新项目将清除所有当前数据,是否继续?"
        )
        if reply:
            # 重置数据库
            self.db_manager.init_database()
            self.refresh_all_data()
            self.show_info_message("新建项目", "新项目创建成功")
    
    def open_project(self):
        """打开项目"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "打开项目文件", "", "数据库文件 (*.db);;所有文件 (*)"
        )
        if file_path:
            self.config.database_path = file_path
            self.db_manager = DatabaseManager(file_path)
            self.refresh_all_data()
            self.show_info_message("打开项目", f"项目文件 {file_path} 打开成功")
    
    def save_project(self):
        """保存项目"""
        self.show_info_message("保存项目", "项目已自动保存")
    
    def toggle_fullscreen(self):
        """切换全屏"""
        if self.isFullScreen():
            self.showNormal()
        else:
            self.showFullScreen()
    
    def calibrate_cameras(self):
        """摄像头标定"""
        self.show_info_message("摄像头标定", "摄像头标定功能正在开发中")
    
    def train_model(self):
        """训练模型"""
        self.show_info_message("训练模型", "模型训练功能正在开发中")
    
    def show_about(self):
        """显示关于对话框"""
        about_text = """
        

智能仓储管理系统

版本: 1.0.0

作者: 丁林松

邮箱: cnsilan@163.com

技术栈: PyQt6 + OpenCV + NVIDIA Jetson + YOLO + OCR

本系统基于计算机视觉技术,实现智能仓储管理和库存监控。

系统集成了目标检测、文字识别、深度估计等先进算法,

为现代仓储物流提供完整的智能化解决方案。

""" QMessageBox.about(self, "关于", about_text) def show_settings(self): """显示设置对话框""" self.show_info_message("设置", "请在设置标签页中进行配置") def show_help(self): """显示帮助""" help_text = """

快速入门指南

  1. 在库存管理页面点击"开始盘点"进行库存清点
  2. 在货架管理页面查看和管理货架布局
  3. 在统计分析页面查看库存趋势和分析报告
  4. 在AGV管理页面创建和监控运输任务
  5. 在系统设置页面配置摄像头和AI参数

常用快捷键

  • Ctrl+N: 新建项目
  • Ctrl+O: 打开项目
  • Ctrl+S: 保存项目
  • F11: 全屏切换

""" dialog = QDialog(self) dialog.setWindowTitle("帮助") dialog.setModal(True) dialog.resize(500, 400) layout = QVBoxLayout(dialog) help_label = QLabel(help_text) help_label.setWordWrap(True) layout.addWidget(help_label) ok_btn = QPushButton("确定") ok_btn.clicked.connect(dialog.accept) layout.addWidget(ok_btn) dialog.exec() # 其他功能方法 def add_shelf(self): """添加货架""" shelf_id, ok = QInputDialog.getText(self, "添加货架", "请输入货架ID:") if ok and shelf_id: # 在数据库中添加货架记录 self.show_info_message("添加货架", f"货架 {shelf_id} 添加成功") self.init_shelf_tree() def remove_shelf(self): """删除货架""" current_item = self.shelf_tree.currentItem() if current_item: shelf_name = current_item.text(0) reply = self.show_question_dialog( "删除货架", f"确定要删除货架 {shelf_name} 吗?" ) if reply: self.show_info_message("删除货架", f"货架 {shelf_name} 删除成功") self.init_shelf_tree() def optimize_warehouse_layout(self): """优化仓库布局""" self.show_info_message("布局优化", "仓库布局优化完成") def calculate_shelf_capacity(self): """计算货架容量""" self.show_info_message("容量计算", "货架容量计算完成") def create_agv_task(self): """创建AGV任务""" dialog = QDialog(self) dialog.setWindowTitle("创建AGV任务") dialog.setModal(True) dialog.resize(400, 300) layout = QVBoxLayout(dialog) # 任务类型 layout.addWidget(QLabel("任务类型:")) task_type_combo = QComboBox() task_type_combo.addItems(["搬运", "盘点", "补货", "拣选"]) layout.addWidget(task_type_combo) # 优先级 layout.addWidget(QLabel("优先级:")) priority_spinbox = QSpinBox() priority_spinbox.setRange(1, 10) priority_spinbox.setValue(5) layout.addWidget(priority_spinbox) # 起点和终点 layout.addWidget(QLabel("起点:")) start_edit = QLineEdit("A1-1-1") layout.addWidget(start_edit) layout.addWidget(QLabel("终点:")) end_edit = QLineEdit("B2-2-2") layout.addWidget(end_edit) # 按钮 button_layout = QHBoxLayout() ok_btn = QPushButton("创建") ok_btn.clicked.connect(dialog.accept) button_layout.addWidget(ok_btn) cancel_btn = QPushButton("取消") cancel_btn.clicked.connect(dialog.reject) button_layout.addWidget(cancel_btn) layout.addLayout(button_layout) if dialog.exec() == QDialog.DialogCode.Accepted: self.show_info_message("任务创建", "AGV任务创建成功") self.update_agv_status() def cancel_agv_task(self): """取消AGV任务""" current_row = self.task_table.currentRow() if current_row >= 0: task_id = self.task_table.item(current_row, 0).text() reply = self.show_question_dialog( "取消任务", f"确定要取消任务 {task_id} 吗?" ) if reply: self.show_info_message("任务取消", f"任务 {task_id} 取消成功") def show_agv_path(self): """显示AGV路径""" if self.agv_planner: # 示例路径 path = self.agv_planner.find_path((100, 100), (800, 600)) if path: self.warehouse_view.add_agv_path(path) self.show_info_message("路径规划", "AGV路径已显示在地图上") else: self.show_warning_message("路径规划", "无法找到有效路径") def test_cameras(self): """测试摄像头""" self.show_info_message("摄像头测试", "摄像头测试功能正在开发中") def reload_ai_models(self): """重新加载AI模型""" self.cv_engine.init_models() self.show_info_message("模型重载", "AI模型重新加载完成") def backup_database(self): """备份数据库""" file_path, _ = QFileDialog.getSaveFileName( self, "备份数据库", f"warehouse_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.db", "数据库文件 (*.db)" ) if file_path: import shutil shutil.copy2(self.db_manager.db_path, file_path) self.show_info_message("备份完成", f"数据库已备份到 {file_path}") def restore_database(self): """恢复数据库""" file_path, _ = QFileDialog.getOpenFileName( self, "恢复数据库", "", "数据库文件 (*.db)" ) if file_path: reply = self.show_question_dialog( "恢复数据库", "恢复数据库将覆盖当前所有数据,是否继续?" ) if reply: import shutil shutil.copy2(file_path, self.db_manager.db_path) self.refresh_all_data() self.show_info_message("恢复完成", "数据库恢复成功") def optimize_database(self): """优化数据库""" try: conn = sqlite3.connect(self.db_manager.db_path) conn.execute('VACUUM') conn.close() self.show_info_message("优化完成", "数据库优化成功") except Exception as e: self.show_error_message("优化失败", f"数据库优化失败: {e}") def export_data(self): """导出数据""" file_path, _ = QFileDialog.getSaveFileName( self, "导出数据", f"warehouse_data_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv", "CSV文件 (*.csv)" ) if file_path: # 导出库存数据到CSV try: import pandas as pd conn = sqlite3.connect(self.db_manager.db_path) df = pd.read_sql_query(''' SELECT p.name, p.category, i.quantity, i.location, i.last_counted FROM inventory i JOIN products p ON i.product_id = p.id WHERE i.status = 'active' ''', conn) df.to_csv(file_path, index=False, encoding='utf-8-sig') conn.close() self.show_info_message("导出完成", f"数据已导出到 {file_path}") except Exception as e: self.show_error_message("导出失败", f"数据导出失败: {e}") def export_inventory_report(self): """导出库存报表""" file_path, _ = QFileDialog.getSaveFileName( self, "导出库存报表", f"inventory_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx", "Excel文件 (*.xlsx)" ) if file_path: self.show_info_message("导出报表", f"库存报表已导出到 {file_path}") def export_count_results(self, results: dict): """导出盘点结果""" file_path, _ = QFileDialog.getSaveFileName( self, "导出盘点结果", f"count_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json", "JSON文件 (*.json)" ) if file_path: with open(file_path, 'w', encoding='utf-8') as f: json.dump(results, f, ensure_ascii=False, indent=2) self.show_info_message("导出完成", f"盘点结果已导出到 {file_path}") def update_safety_stock_settings(self): """更新安全库存设置""" days = self.safety_stock_spinbox.value() self.config.safety_stock_days = days self.show_info_message("设置更新", f"安全库存天数已设置为 {days} 天") # 通用对话框方法 def show_info_message(self, title: str, message: str): """显示信息消息""" msg_box = QMessageBox(QMessageBox.Icon.Information, title, message, QMessageBox.StandardButton.Ok, self) msg_box.exec() def show_warning_message(self, title: str, message: str): """显示警告消息""" msg_box = QMessageBox(QMessageBox.Icon.Warning, title, message, QMessageBox.StandardButton.Ok, self) msg_box.exec() def show_error_message(self, title: str, message: str): """显示错误消息""" msg_box = QMessageBox(QMessageBox.Icon.Critical, title, message, QMessageBox.StandardButton.Ok, self) msg_box.exec() def show_question_dialog(self, title: str, message: str) -> bool: """显示确认对话框""" reply = QMessageBox.question( self, title, message, QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No ) return reply == QMessageBox.StandardButton.Yes def closeEvent(self, event): """窗口关闭事件""" reply = self.show_question_dialog( "退出系统", "确定要退出智能仓储管理系统吗?" ) if reply: # 停止所有线程和定时器 self.update_timer.stop() if hasattr(self, 'count_thread') and self.count_thread.isRunning(): self.count_thread.terminate() self.count_thread.wait() logger.info("系统正常退出") event.accept() else: event.ignore() class InventoryCountThread(QThread): """库存盘点线程""" progress_updated = pyqtSignal(int) count_completed = pyqtSignal(dict) def __init__(self, cv_engine: ComputerVisionEngine, inventory_counter: InventoryCounter): super().__init__() self.cv_engine = cv_engine self.inventory_counter = inventory_counter def run(self): """执行盘点任务""" try: # 模拟摄像头采集图像 images = [] camera_count = 4 for i in range(camera_count): self.progress_updated.emit(i * 100 // camera_count) # 模拟图像采集(实际应用中从摄像头获取) image = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8) images.append(image) # 模拟处理时间 self.msleep(500) # 执行盘点 results = self.inventory_counter.count_inventory(images) self.progress_updated.emit(100) self.count_completed.emit(results) except Exception as e: logger.error(f"盘点线程执行失败: {e}") self.count_completed.emit({'total_items': 0, 'detected_categories': {}, 'locations': {}, 'anomalies': [str(e)]}) def main(): """主函数""" app = QApplication(sys.argv) # 设置应用信息 app.setApplicationName("智能仓储管理系统") app.setApplicationVersion("1.0.0") app.setOrganizationName("丁林松") app.setOrganizationDomain("cnsilan@163.com") # 设置应用图标 app.setWindowIcon(QIcon("icons/warehouse.png")) # 创建主窗口 window = WarehouseMainWindow() window.show() # 运行应用 sys.exit(app.exec()) if __name__ == "__main__": main()

11. 系统特色与创新点

本智能仓储管理系统具有多个突出的特色和创新点,充分体现了现代人工智能技术在仓储物流领域的深度应用。系统不仅在技术架构上采用了最前沿的解决方案,更在用户体验和业务流程优化方面进行了深入的思考和设计。

11.1 技术创新

系统在技术层面的创新主要体现在多模态感知融合、边缘智能计算和自适应学习算法等方面。通过NVIDIA Jetson平台的强大计算能力,系统能够同时处理视觉、文字和深度信息,构建了完整的多维感知体系。边缘计算架构减少了对云端服务的依赖,提高了系统的实时性和可靠性。自适应学习算法能够根据仓储环境的变化自动调整检测参数,持续优化系统性能。

11.2 业务创新

在业务应用方面,系统实现了从被动管理到主动预测的转变。通过智能补货推荐算法,系统能够预测库存需求趋势,提前制定采购计划;异常检测机制能够及时发现库存异常,避免损失;AGV路径优化算法提高了仓储作业效率,降低了运营成本。这些创新点使得传统仓储管理向智能化、自动化方向迈进了一大步。

12. 未来发展方向

随着人工智能技术的不断发展,智能仓储管理系统还有巨大的提升空间。未来的发展方向包括但不限于以下几个方面:

12.1 技术升级

在技术层面,系统将持续跟进最新的AI算法和硬件发展。计划集成更先进的Transformer模型用于视觉任务,引入强化学习算法优化AGV调度策略,利用联邦学习技术实现多仓库间的知识共享。同时,随着5G和物联网技术的普及,系统将支持更多类型的传感器设备,构建更加全面的智能感知网络。

12.2 功能扩展

在功能方面,系统将向更广泛的供应链管理领域扩展。计划增加供应商管理、运输调度、客户关系管理等模块,构建端到端的供应链管理平台。同时,系统还将支持更多的业务场景,如冷链物流、危险品管理、跨境电商等特殊领域的仓储需求。

12.3 智能化程度

未来的系统将具备更高的智能化程度,能够实现完全自主的仓储管理。通过深度学习和认知计算技术,系统将具备自主决策能力,能够在复杂多变的环境中做出最优选择。同时,系统将支持自然语言交互,用户可以通过语音或文字与系统进行沟通,大大降低了操作门槛。

🚀 系统发展路线图
  • 短期目标(6个月):完善现有功能,提高系统稳定性和用户体验
  • 中期目标(1-2年):扩展多仓库管理、供应链集成、移动端应用
  • 长期目标(3-5年):实现完全自主的智能仓储生态系统

13. 结论

基于计算机视觉的智能仓储管理系统及PyQt6库存可视化监控平台代表了现代仓储物流技术的发展方向。系统通过深度整合NVIDIA Jetson边缘计算平台、PyQt6图形界面框架和先进的计算机视觉算法,构建了一套完整、高效、智能的仓储管理解决方案。

系统的成功实施不仅能够显著提高仓储作业的效率和准确性,降低人工成本和管理风险,更重要的是为传统仓储行业的数字化转型提供了可行的技术路径。通过本系统的应用,仓储企业能够实现从传统的人工管理模式向智能化、自动化管理模式的转变,在激烈的市场竞争中占据有利地位。

作为一个综合性的技术解决方案,本系统充分展示了人工智能、计算机视觉、边缘计算等前沿技术在实际业务场景中的应用价值。随着技术的不断发展和完善,相信这类智能仓储系统将在更广泛的领域发挥重要作用,推动整个物流行业向更高水平发展。

技术支持与联系方式
如有技术问题或合作需求,欢迎联系:
丁林松
邮箱:cnsilan@163.com
专业领域:计算机视觉、深度学习、PyQt6开发、NVIDIA Jetson应用

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐