基于骨骼识别的危险动作报警系统设计与实现

news2025/4/18 10:52:39

基于骨骼识别的危险动作报警系统设计与实现

基于骨骼识别的危险动作报警分析系统

【包含内容】
【一】项目提供完整源代码及详细注释
【二】系统设计思路与实现说明
【三】基于骨骼识别算法的实时危险行为预警方案

【技术栈】
①:系统环境:Windows 10/11、macOS Ventura、Ubuntu 20.04
②:开发环境:Python 3.9+、Visual Studio Code/PyCharm
③:技术栈:OpenCV、Mediapipe、PySide6、NumPy、SciPy、Pillow、Pydub/Sounddevice

【功能模块】
①:视频采集模块:获取摄像头实时视频流,支持多摄像头选择与切换,计算实时帧率
②:骨骼检测模块:基于Mediapipe Pose模型实时识别人体33个关键骨骼点,提供坐标和可见性数据
③:危险判断模块:支持危险区域入侵检测和多种危险动作识别(弯腰、举手、失衡、蹲下、奔跑)
④:音频警报模块:本地化生成多种差异化警告音效(蜂鸣声、警笛声、扫频声、啁啾声),多音频后端兼容
⑤:交互界面模块:基于PySide6构建直观友好的操作界面,支持实时监控显示和交互式危险区域绘制

【系统特点】
① 基于计算机视觉的非接触式监控,无需佩戴特殊设备即可实现人体行为分析
② 支持自定义多边形危险区域,通过交互式GUI实现直观的区域绘制和管理
③ 多种危险动作检测算法相结合,提高安全预警的全面性和准确性
④ 本地化音频警报生成,无需依赖在线API,提高响应速度和系统独立性

【核心技术】
① 基于Mediapipe Pose的实时人体骨骼点检测技术,识别人体33个关键点的位置和可见性
② 基于几何向量计算的多种危险姿势判断算法,采用余弦定理计算角度和相对位置关系
③ 基于OpenCV的区域入侵检测技术,应用点多边形测试和关键点权重计算
④ 基于NumPy和SciPy的本地音频警报生成技术,实现多种模式的差异化警告声

【应用场景】
① 工业生产环境:监控工人弯腰姿势、高空作业等风险动作,预防工伤事故
② 危险区域管控:划定机械运转区域、高电压区域等危险区域,防止人员误入
③ 安全管理系统:监控员工不规范行为,如工地奔跑、不规范操作等
④ 特殊场所监控:如实验室、仓储区域等需要严格行为规范的场所

【拓展服务】
① 部署+150
② 如果有数据需要+100

image-20250415145932844

image-20250415145925543

image-20250415145916380

image-20250415150114159

摘要

随着工业化和自动化的快速发展,工作场所的安全问题日益突出,传统的安全监控手段往往存在响应不及时、覆盖范围有限、人力成本高等问题。为了提升安全监控的智能化水平和预警能力,本文设计并实现了一套基于计算机视觉和人体骨骼识别技术的危险动作报警系统。该系统利用摄像头实时捕捉监控区域的视频流,通过Mediapipe框架精确识别画面中人员的骨骼关键点,进而分析人体的姿态和动作。系统集成了危险区域入侵检测和多种典型危险动作(如弯腰过度、手臂高举、身体失衡、异常蹲下、快速奔跑等)的识别算法。一旦检测到潜在危险,系统将立即触发视觉和本地生成的音频警报,提醒相关人员注意。本文首先阐述了研究背景与意义,介绍了计算机视觉、骨骼识别等相关技术;接着进行了详细的系统需求分析和可行性分析;然后重点阐述了系统的总体架构设计、各功能模块(视频采集、骨骼检测、危险检测、用户界面、音频警报)的设计思路以及核心算法的实现细节;随后展示了系统的具体实现过程,包括开发环境配置和关键代码实现;最后,通过设计测试用例对系统功能和性能进行了测试与评估。测试结果表明,该系统能够有效识别设定的危险场景,实时性和准确性基本满足预期要求,具有一定的实际应用价值。

关键词: 骨骼识别;计算机视觉;危险动作检测;Mediapipe;实时报警;人机交互


1. 引言

1.1 研究背景与意义

在现代工业生产、建筑施工、仓储物流以及日常管理等众多场景中,人员安全始终是关注的重中之重。意外事故的发生不仅可能造成人员伤亡和财产损失,还会对企业的正常运营和社会稳定带来负面影响。传统的安全监控方式,如人工巡查、固定摄像头录像等,虽然在一定程度上起到了监督作用,但往往存在诸多局限性。人工巡查受限于人力成本和覆盖范围,难以做到全天候、无死角的监控,且容易因疲劳、疏忽等因素导致漏报、误报。传统的视频监控系统大多停留在事后追溯的层面,缺乏实时分析和主动预警的能力,难以在危险发生的初期进行有效干预。因此,开发一种能够自动、实时、准确识别潜在危险并及时发出警报的智能化监控系统,对于提升安全管理水平、预防事故发生具有重要的现实意义和应用价值。

近年来,随着人工智能和计算机视觉技术的飞速发展,特别是深度学习在图像识别、目标检测等领域的突破性进展,为智能化安全监控提供了新的技术途径。人体骨骼识别技术作为计算机视觉的一个重要分支,能够通过分析视频或图像中的人体关键点信息,精确地估计人体的姿态和动作。这项技术不依赖于特定的服装或配饰,对光照变化、部分遮挡等具有一定的鲁棒性,非常适合应用于复杂环境下的行为分析。将骨骼识别技术应用于安全监控领域,可以实现对人员行为的深层次理解,例如判断人员是否进入禁止区域、是否做出危险动作(如攀爬、跌倒、异常停留等),从而实现更精准、更智能的安全预警。

本研究旨在利用先进的计算机视觉和人体骨骼识别技术,设计并开发一套能够实时检测危险区域入侵和特定危险动作的智能报警系统。该系统通过摄像头捕捉实时视频流,利用Mediapipe等成熟的骨骼识别框架提取人体关键点信息,并基于这些信息设计和实现了一系列危险场景的检测算法,包括非法区域入侵以及弯腰过度、手臂高举、身体失衡、蹲下、奔跑等典型危险动作的识别。当系统检测到危险情况时,会立即通过用户界面和本地生成的音频信号发出警报。本研究不仅有助于提高特定场景下的安全防护能力,也为计算机视觉技术在安全监控领域的应用提供了实践案例和参考。

1.2 国内外研究现状

基于计算机视觉的安全监控与行为识别一直是学术界和工业界的研究热点。早期的方法主要依赖于背景建模、光流法、轮廓分析等传统计算机视觉技术来检测运动目标或识别人体轮廓,但这些方法对环境变化敏感,鲁棒性较差。

随着深度学习的兴起,基于卷积神经网络(CNN)的目标检测算法(如YOLO, SSD, Faster R-CNN)和人体姿态估计算法(如OpenPose, AlphaPose, Mediapipe Pose)取得了显著进展。这些算法能够更准确地定位人体和提取骨骼关键点,为后续的行为分析奠定了坚实的基础。许多研究工作利用这些技术进行异常行为检测。例如,一些研究通过分析人体骨骼点的时空变化来识别跌倒动作;另一些研究则利用骨骼点信息结合机器学习或深度学习模型(如RNN, LSTM, GCN)来识别更复杂的行为,如打斗、偷窃等。在危险区域检测方面,常见的方法是结合目标检测和几何判断,判定目标(如人)的质心或边界框是否进入预定义的多边形区域。

尽管现有技术取得了一定的成果,但在实际应用中仍面临一些挑战:

  1. 实时性要求: 安全监控系统需要实时处理视频流并快速响应,这对算法的计算效率提出了很高要求。
  2. 准确性与鲁棒性: 复杂光照、遮挡、多人交互、视角变化等因素都会影响骨骼识别和行为分析的准确性。
  3. 动作多样性与定义模糊性: “危险动作”的定义往往与具体场景相关,且动作本身具有多样性,设计通用且准确的识别算法难度较大。
  4. 系统集成与易用性: 如何将复杂的视觉算法与用户友好的界面、可靠的报警机制相结合,构建一个完整、易用的系统也是一个重要的实践问题。

本系统旨在结合现有成熟的骨骼识别技术(Mediapipe Pose)和经典的几何计算方法,针对几种明确定义的危险场景(区域入侵、弯腰、举手、失衡、蹲下、奔跑)进行检测,并注重系统的实时性、易用性和报警机制的本地化实现,以期在特定应用场景下提供一个实用、可靠的解决方案。

1.3 本文主要工作

本文的主要工作在于设计和实现一个集成了危险区域入侵检测和多种危险动作识别功能的实时报警系统。具体工作内容包括:

  1. 技术选型与环境搭建: 选择了Python作为主要开发语言,利用OpenCV进行图像处理和视频流读取,采用Mediapipe Pose库进行实时人体骨骼关键点检测,使用PySide6构建图形用户界面(GUI),并结合Numpy、Scipy、Pillow和Pydub/Sounddevice等库进行数值计算、图像处理和本地音频警报的生成与播放。搭建了相应的开发和运行环境。
  2. 系统需求分析与设计: 对系统的功能需求(实时监控、区域定义、骨骼检测、危险判断、报警提示、UI交互)和非功能需求(实时性、准确性、易用性)进行了分析。设计了系统的整体架构,明确了视频采集、骨骼检测、危险检测、UI交互和音频警报等核心模块的功能和接口。
  3. 核心算法研究与实现:
    • 实现了基于cv2.pointPolygonTest的危险区域入侵检测算法,并考虑了多点进入的判断逻辑。
    • 研究并实现了多种危险动作的检测算法,包括:利用向量夹角(余弦定理)计算弯腰角度的算法;基于关键点相对位置判断手臂高举和身体失衡的算法;结合膝盖弯曲角度和髋部相对高度判断蹲下姿势的算法;综合腿部跨距、身体前倾和手臂摆动等特征判断奔跑姿势的算法。在算法实现中考虑了关键点可见性以提高可靠性。
    • 实现了基于Numpy和Scipy的本地化音频警报生成模块,能够根据不同的危险类型生成不同模式(如蜂鸣、警笛、扫频、啁啾)的WAV格式提示音,并兼容多种音频播放后端(Sounddevice, Pydub, 系统命令),取代了对网络API的依赖。
  4. 系统集成与界面实现: 使用PySide6框架开发了图形用户界面,提供了摄像头选择、监控启停、危险区域绘制与清除、检测选项配置、实时视频显示(含骨骼和危险区域标注)、报警信息列表、状态显示等功能。通过信号与槽机制将后端处理逻辑与前端界面进行连接,实现了流畅的用户交互。应用了自定义QSS样式美化界面。解决了中文显示乱码和跨平台字体加载的问题。
  5. 系统测试与评估: 设计了测试用例,对系统的各项功能(区域检测、各种姿态检测、报警功能、UI交互)进行了测试,并对系统的实时性能(如FPS)和检测效果进行了初步评估。

1.4 论文结构安排

本文的结构安排如下:

  • 第一章:引言。 主要介绍研究背景、意义、国内外研究现状以及本文的主要工作和结构安排。
  • 第二章:相关技术介绍。 介绍本系统所涉及的关键技术,包括计算机视觉基础、OpenCV库、Mediapipe框架(特别是Pose模型)、PySide6 GUI框架、以及Numpy/Scipy等科学计算库。
  • 第三章:系统分析。 对系统的需求进行详细分析,包括功能性需求和非功能性需求,并进行技术、操作和经济上的可行性分析。
  • 第四章:系统设计。 阐述系统的总体架构设计,详细设计各个功能模块(视频采集、骨骼检测、危险检测、UI、音频警报)的实现方案,包括关键算法流程和界面布局。使用Mermaid绘制系统架构图和关键流程图。
  • 第五章:系统实现。 介绍系统的开发环境和具体实现过程,展示核心模块的关键代码,并解释实现细节,包括骨骼点获取、危险判断逻辑、音频生成、UI交互等。展示核心公式。
  • 第六章:系统测试。 描述测试环境、测试用例设计,展示主要功能的测试结果,并对结果进行分析,评估系统的性能和局限性。
  • 第七章:结论。 总结全文工作,阐述系统的创新点和不足之处,并对未来的改进方向进行展望。

2. 相关技术介绍

本系统的开发涉及多个领域的关键技术,本章将对这些核心技术进行介绍,为后续的系统设计与实现奠定基础。

2.1 计算机视觉与OpenCV

计算机视觉(Computer Vision)是一门研究如何使机器“看”的科学,更进一步的说,就是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等,并进一步做图像处理,用电脑处理成为更适合人眼观察或传送给仪器检测的图像。它涉及图像处理、模式识别、人工智能等多个领域。在本项目中,计算机视觉技术是实现环境感知和人体理解的基础。

OpenCV(Open Source Computer Vision Library)是一个开源的计算机视觉和机器学习软件库。它包含了超过2500种优化的算法,涵盖了计算机视觉的众多领域,如图像处理(滤波、形态学操作、色彩空间转换)、特征检测与描述(SIFT, SURF, ORB)、目标检测与跟踪(Haar级联、光流法)、摄像头标定、立体视觉以及机器学习工具等。OpenCV提供了Python、C++、Java等多种语言接口,具有强大的功能和良好的跨平台兼容性。在本系统中,OpenCV主要用于:

  1. 视频流读取与处理: 通过cv2.VideoCapture类访问和读取摄像头输入的实时视频流。
  2. 图像预处理: 如色彩空间转换(BGR到RGB),这是许多视觉分析算法(包括Mediapipe)的输入要求。
  3. 图像绘制与标注: 在视频帧上绘制检测到的骨骼点、连接线、危险区域边界、文字标签等信息,用于可视化展示,如使用cv2.polylines, cv2.fillPoly, cv2.putText, cv2.addWeighted等函数。
  4. 几何计算: 利用OpenCV提供的几何计算功能,如cv2.pointPolygonTest函数来判断一个点是否在多边形(危险区域)内部。

OpenCV的高效性和丰富的函数库极大地简化了计算机视觉应用的开发流程,是本系统不可或缺的基础库。

2.2 Mediapipe与人体骨骼识别

Mediapipe是Google开发的一个开源框架,用于构建实时的、跨平台的感知管道(perception pipelines)。它提供了一系列预训练好的机器学习模型和工具,可以轻松实现人脸检测、手部跟踪、姿态估计、目标检测等多种复杂的视觉任务。Mediapipe的设计注重性能和效率,能够在移动设备、桌面端甚至Web端流畅运行。

本项目核心的人体骨骼识别功能就是基于Mediapipe的Pose解决方案实现的。Mediapipe Pose能够实时地从图像或视频中检测人体的33个关键骨骼点(landmarks),包括头部、躯干、四肢的主要关节点。其主要特点包括:

  1. 高精度检测: 基于先进的深度学习模型,能够比较准确地定位各种姿态下的人体关键点。
  2. 实时性能: 经过优化,可以在普通CPU或GPU上实现实时处理。
  3. 提供关键点坐标和可见性: 对于每个检测到的关键点,模型不仅输出其在图像中的(x, y)坐标(归一化),还提供一个可见性(visibility)评分,表示该点被模型认为可见的可信度。这对于后续判断姿态的可靠性非常有帮助。
  4. 跨平台支持: 可以在多种操作系统和硬件上运行。

在本系统中,src.core.skeleton_detector.SkeletonDetector类封装了Mediapipe Pose的使用。其主要工作流程如下:

  1. 初始化: 创建mp.solutions.pose.Pose对象,可以配置模型复杂度、最小检测置信度、最小跟踪置信度等参数。
  2. 处理帧: 将OpenCV读取到的BGR图像帧转换为RGB格式,输入到pose.process()方法中进行处理。
  3. 获取结果: 处理结果results中包含了检测到的姿态关键点信息results.pose_landmarks
  4. 坐标提取: get_landmarks_coordinates方法遍历results.pose_landmarks.landmark,将归一化的坐标(x, y)根据图像的宽高转换为像素坐标,并保存每个点的(x, y, visibility)信息到字典中,供危险检测模块使用。
  5. 可视化(可选): 利用mp.solutions.drawing_utils.draw_landmarks可以将检测到的骨骼点和连接线绘制到图像上。

Mediapipe Pose为本系统提供了稳定、高效、准确的人体骨骼关键点数据来源,是实现后续危险动作识别的基础。

2.3 PySide6与图形用户界面(GUI)

为了方便用户与系统交互,如图形化地设置危险区域、选择摄像头、查看实时监控画面和报警信息,本系统采用PySide6构建图形用户界面(GUI)。PySide6是Qt for Python项目的官方绑定,允许开发者使用Python语言来创建功能丰富、外观现代的跨平台桌面应用程序。Qt是一个成熟且广泛使用的C++ GUI框架。

PySide6的主要优势包括:

  1. 丰富的控件库: 提供了大量的标准UI控件,如按钮(QPushButton)、标签(QLabel)、下拉框(QComboBox)、复选框(QCheckBox)、列表(QListWidget)、组框(QGroupBox)等,可以构建复杂的界面布局。
  2. 信号与槽机制: 这是Qt的核心机制之一,用于对象间的通信。当某个事件发生时(如按钮被点击),一个对象可以发射(emit)一个信号,连接到该信号的槽函数(slot)会被自动调用。这种机制使得代码解耦,易于维护。本项目中大量使用了信号与槽,例如按钮点击连接到处理函数,定时器超时信号连接到帧更新函数,图像控件的鼠标事件信号连接到危险区域绘制函数等。
  3. 布局管理器: 提供了QVBoxLayout(垂直布局)、QHBoxLayout(水平布局)、QGridLayout(网格布局)等布局管理器,可以方便地组织和管理界面控件,使其能够自适应窗口大小变化。
  4. 强大的绘图系统: QPainter类提供了丰富的2D绘图功能,可以在控件上绘制图形、文本、图像等。本项目中用于在视频画面上叠加绘制危险区域的边界和顶点。
  5. 样式表(QSS): 支持使用类似CSS的语法(QSS)来定制应用程序的外观,可以轻松实现界面的美化和风格统一。本项目的src.ui.style.py文件定义了应用的QSS样式。
  6. 跨平台性: 基于Qt开发的应用可以在Windows, macOS, Linux等主流操作系统上运行,具有良好的可移植性。

在本系统中,src.ui.main_window.py文件定义了主窗口MainWindow类和图像显示控件ImageViewer类。MainWindow负责整体界面的布局和控件的初始化,而ImageViewer则继承自QLabel,重写了鼠标事件(mousePressEvent, mouseMoveEvent, mouseDoubleClickEvent)来实现在视频画面上通过点击和双击来交互式绘制危险区域的功能,并通过自定义信号将绘制状态传递给主应用逻辑。

2.4 Numpy、Scipy与Pillow

Numpy (Numerical Python) 是Python中用于科学计算的核心库。它提供了一个强大的N维数组对象(ndarray),以及用于处理这些数组的各种函数。Numpy数组操作通常比Python原生列表操作效率高得多,尤其是在处理大型数据集时。在本系统中,Numpy主要用于:

  1. 数值计算: 在危险动作检测算法中进行大量的向量和矩阵运算,如计算向量长度(np.sqrt)、点积、角度(np.degrees, np.arccos, np.arctan)等。
  2. 数据表示: 危险区域的顶点坐标使用Numpy数组存储,方便后续的几何计算。
  3. 音频数据处理: 在生成本地音频警报时,使用Numpy数组来表示和操作音频波形数据(如生成正弦波、应用淡入淡出效果)。

Scipy (Scientific Python) 是建立在Numpy之上的另一个核心科学计算库,提供了更多用于科学和工程计算的模块,包括信号处理、优化、线性代数、统计等。在本系统中,Scipy主要用于:

  1. 音频文件读写: 使用scipy.io.wavfile模块来读取和写入WAV格式的音频文件,这是生成和保存本地警报音效的关键。
  2. 信号生成: 利用scipy.signal模块中的函数(如signal.chirp)来生成更复杂的音频信号,例如啁啾声(chirp)。

Pillow 是PIL(Python Imaging Library)的一个活跃分支,是Python中最常用的图像处理库之一。它提供了广泛的文件格式支持、高效的内部表示以及相当强大的图像处理能力。在本系统中,Pillow主要用于解决OpenCV cv2.putText无法直接绘制中文字符的问题:

  1. 图像格式转换: 将OpenCV的图像(通常是Numpy数组,BGR格式)转换为Pillow的Image对象(RGB格式),反之亦然。
  2. 加载字体: 使用ImageFont.truetypeImageFont.load_default加载系统字体文件。
  3. 绘制文本: 使用ImageDraw.Draw对象在PIL图像上绘制中文字符,可以指定字体、颜色、位置等。通过draw.textbboxdraw.textsize(兼容旧版)获取文本尺寸,以便进行居中和背景绘制。

这些库为系统提供了强大的数值计算、信号处理和图像处理能力,是实现核心算法和解决特定问题的关键工具。

2.5 音频处理库(Pydub, Sounddevice)

为了实现本地化的音频警报功能,系统需要能够生成和播放声音。除了使用Numpy/Scipy生成WAV数据外,还需要库来播放这些声音。

Pydub 是一个高级的音频处理库,旨在使音频操作变得简单直观。它封装了底层的音频处理库(如ffmpeg),提供了简洁的API来加载、处理、导出各种格式的音频文件,以及直接播放音频。在本系统中,Pydub用于:

  1. 播放WAV文件: 作为备选的音频播放后端,尝试使用pydub.playback.play函数播放生成的WAV警告音。
  2. 格式转换(潜在): 虽然当前代码主要生成WAV,但Pydub可以方便地进行MP3到WAV等的转换(如此前版本中使用gTTS时)。

Sounddevice 是一个提供Python绑定到PortAudio库的模块,PortAudio是一个跨平台的音频I/O库。Sounddevice允许Python程序录制和播放音频,支持底层的音频流操作。在本系统中,Sounddevice是首选的音频播放后端:

  1. 播放Numpy数组: 可以直接播放存储在Numpy数组中的音频波形数据,这与我们使用Numpy/Scipy生成音频的方式非常契合。通过sounddevice.play()播放,并使用sounddevice.wait()等待播放完成。

系统设计了多级音频播放尝试机制:优先尝试Sounddevice,如果失败则尝试Pydub,最后尝试调用系统自带的播放命令(如macOS的afplay,Windows的powershell播放器,Linux的aplay等),以最大程度地保证在不同环境下都能发出声音警报。


3. 系统分析

在进行系统设计和实现之前,必须对系统的需求进行全面分析,并评估其可行性。本章将详细阐述系统的需求分析和可行性分析。

3.1 需求分析

需求分析是系统开发的关键步骤,旨在明确系统需要实现的功能以及需要满足的性能、可靠性等非功能性要求。通过与潜在用户的沟通(在本研究中,基于项目目标和通用安全监控场景进行设定)和对应用场景的理解,我们将系统需求归纳如下。

3.1.1 功能性需求分析

功能性需求定义了系统应该具备的具体功能。

  1. 实时视频采集与显示:

    • 系统应能连接并选择可用的摄像头设备。
    • 系统应能实时捕获选定摄像头的视频流。
    • 系统应在用户界面上清晰、流畅地显示实时视频画面。
    • 应能在视频画面上显示当前的帧率(FPS)。
  2. 人体骨骼实时检测:

    • 系统应能实时检测视频画面中的人体。
    • 对于检测到的人体,系统应能实时识别其骨骼关键点(至少包括头、肩、肘、腕、髋、膝、踝等主要部位)。
    • 应能在视频画面上叠加显示检测到的骨骼点和连接线,方便用户观察。
  3. 危险区域管理:

    • 用户应能通过在视频画面上交互式操作(如单击添加点、双击完成)来定义一个或多个多边形危险区域。
    • 系统应能在视频画面上清晰地绘制出用户定义的危险区域边界和填充效果。
    • 用户应能清除所有已定义的危险区域。
    • 系统应保存用户定义的危险区域信息,用于后续的入侵检测。
  4. 危险区域入侵检测:

    • 系统应能实时判断检测到的人体是否有部分关键点进入了用户定义的危险区域。
    • 判断逻辑应考虑人体有多个关键点进入区域的情况,设定合理的触发阈值(例如,至少有一定数量或特定部位的关键点进入)。
    • 当检测到区域入侵时,应触发相应的危险状态。
  5. 危险动作识别:

    • 系统应能实时分析检测到的骨骼点数据,识别特定的危险或异常动作。
    • 弯腰过度检测: 当人体躯干(如头/肩中点与髋中点的连线)与垂直方向的夹角过大,且姿态符合前倾特征时,判定为弯腰过度。需要设定合理的角度阈值。
    • 手臂高举检测: 当检测到人的手腕明显高于其头部位置时,判定为手臂高举。
    • 身体失衡检测: 当左右肩膀的高度差超过一定阈值(如相对于身高的比例)时,判定为身体倾斜过度或失衡。
    • 蹲下姿势检测: 当膝盖显著弯曲(角度小于阈值)且髋部位置相对于脚踝较低(低于一定身高比例)时,判定为蹲下姿势。
    • 奔跑姿势检测: 当检测到人体步幅过大、身体前倾、或手臂有明显摆动特征时,判定为奔跑。
    • 以上每种危险动作都需要设定合理的判断阈值和逻辑,并考虑关键点的可见性以提高准确性。
    • 当检测到任何一种危险动作时,应触发相应的危险状态。
  6. 报警功能:

    • 当系统检测到危险区域入侵或危险动作时,应立即触发报警机制。
    • 视觉报警: 在用户界面的视频区域显示醒目的警告信息或覆盖层(例如,闪烁的红色边框或文字提示),明确指示危险类型。
    • 音频报警: 播放本地生成的警告音效。不同的危险类型(区域入侵、弯腰、举手、失衡、蹲下、奔跑)应对应不同模式或音调的提示音,以便区分。
    • 用户应能选择是否启用音频报警。
    • 系统应有报警冷却机制,避免对同一事件在短时间内重复、密集地报警。
  7. 用户界面与交互:

    • 提供一个集成化的图形用户界面,集中展示视频监控、控制选项和报警信息。
    • 控制面板: 提供摄像头选择、开始/停止监控、绘制/清除危险区域、启用/禁用危险区域检测、启用/禁用危险姿势检测、启用/禁用音频报警、清除报警记录等控制功能。
    • 信息显示: 实时显示监控视频(带标注)、FPS、系统运行状态、报警信息列表(带时间戳)。
    • 界面布局应清晰、合理,操作应直观、便捷。
3.1.2 非功能性需求分析

非功能性需求定义了系统运行的质量属性和约束条件。

  1. 实时性: 系统必须能够实时处理视频流并进行检测和报警。从捕捉到画面到完成检测并发出报警的延迟应尽可能小(例如,控制在数百毫秒内),以确保预警的及时性。系统的处理帧率(FPS)应尽可能高,以保证画面的流畅性和检测的连续性。
  2. 准确性: 骨骼检测、区域入侵判断和危险动作识别的准确率应尽可能高,减少漏报(未检测到实际发生的危险)和误报(将正常情况误判为危险)。危险判断的阈值需要经过调试和优化,以在灵敏度和特异性之间取得平衡。
  3. 鲁棒性: 系统应能在一定程度上适应环境变化,如光照变化、轻微遮挡等。对不同体型、姿态的人应具有一定的识别能力。
  4. 易用性: 用户界面应简洁明了,操作逻辑符合用户习惯,危险区域的定义应方便快捷。系统状态和报警信息应清晰易懂。
  5. 兼容性: 系统应能在常见的操作系统(如Windows, macOS, Linux)上运行。应能兼容常见的USB摄像头。
  6. 资源消耗: 系统运行时对CPU、内存等资源的消耗应控制在合理范围内,避免过度占用导致其他应用卡顿或系统崩溃。
  7. 可维护性与可扩展性: 代码结构应清晰,模块化设计,方便后续的功能维护、调试和扩展(例如,增加新的危险动作识别类型)。

3.2 可行性分析

可行性分析旨在评估项目在现有条件下是否能够成功完成。我们从技术、操作和经济三个方面进行分析。

3.2.1 技术可行性
  • 核心技术成熟度: 本项目依赖的关键技术,如计算机视觉、图像处理、人体骨骼识别等,已有数十年的发展历史,理论基础扎实,并有大量成熟的开源库和框架支持。OpenCV作为计算机视觉领域的事实标准库,功能强大且稳定。Mediapipe Pose是Google推出的先进且高效的姿态估计算法,已经在诸多应用中得到验证,能够提供满足需求的实时骨骼点数据。
  • 开发工具与环境: Python作为一种语法简洁、生态丰富的胶水语言,非常适合快速开发此类应用。PySide6提供了现代化的GUI开发能力。Numpy, Scipy, Pillow等库为数值计算和图像/音频处理提供了强大支持。这些工具均为开源或免费提供,易于获取和使用。
  • 算法实现复杂度: 危险区域入侵检测主要基于几何判断,算法相对简单。危险动作识别虽然涉及一些几何计算和逻辑判断,但相比于从零开始训练深度学习模型,基于Mediapipe提供的骨骼点进行二次分析的复杂度是可控的。本项目选取的几种危险动作(弯腰、举手、失衡、蹲下、奔跑)特征相对明确,可以通过设计合理的规则和阈值进行识别。本地音频生成使用成熟的信号处理库,技术难度适中。
  • 性能要求: 实时性是关键挑战。Mediapipe本身经过优化,可以在普通硬件上达到不错的帧率。危险检测算法主要是几何计算,计算量相对可控。通过合理的算法设计和代码优化(如利用Numpy加速计算),结合多线程处理(如音频播放放在单独线程),可以预期在普通PC上达到可接受的实时处理性能。

结论: 从技术角度看,本项目所需的核心技术均有成熟的解决方案和工具支持,算法实现复杂度可控,性能要求可以通过优化达到,因此技术上是可行的。

3.2.2 操作可行性
  • 用户界面: 系统设计了图形用户界面,将复杂的技术细节封装在后台,用户通过直观的按钮、复选框、下拉菜单以及视频窗口交互来操作。危险区域的定义采用鼠标点击方式,符合常规操作习惯。报警信息以列表形式展示,清晰明了。
  • 系统部署与使用: 系统使用Python开发,依赖库可以通过pip方便地安装。最终可以打包成可执行文件(虽然本项目未明确要求,但技术上可行),方便在不同机器上部署。用户只需连接好摄像头,运行程序即可开始使用。
  • 维护与调整: 对于危险动作的判断阈值,可能需要根据实际场景进行调整。代码中预留了打印调试信息,可以辅助进行参数优化。模块化的代码结构也便于后续维护。

结论: 系统提供了易于理解和操作的用户界面,部署和使用流程相对简单,操作上是可行的。

3.2.3 经济可行性
  • 软件成本: 本项目所使用的核心软件库,包括Python解释器、OpenCV, Mediapipe, PySide6, Numpy, Scipy, Pillow, Pydub, Sounddevice等,均为开源或免费软件,无需支付额外的软件许可费用。
  • 硬件成本: 系统运行需要一台普通性能的PC(具备运行Python和相关库的能力)和一个或多个USB摄像头。这些硬件设备成本相对较低,对于大多数企业或个人用户而言是可以接受的。
  • 开发成本: 主要成本在于开发人员的时间投入。由于利用了大量现有库,可以显著缩短开发周期,降低开发成本。
  • 运行成本: 系统运行主要消耗电力和少量计算资源,运行成本较低。本地化的音频报警避免了依赖可能收费的在线TTS API。

结论: 本项目主要依赖开源软件和通用硬件,开发和运行成本相对较低,经济上是可行的。

综合可行性分析结论: 通过对技术、操作和经济三个方面的分析,可以认为“基于骨骼识别的危险动作报警系统”的开发是完全可行的。系统能够在现有技术和资源条件下得以实现,并且具有良好的应用前景和实用价值。


4. 系统设计

在完成需求分析和可行性分析后,本章将对系统进行详细的设计,包括系统总体架构、各功能模块的设计、关键算法流程以及界面设计。

4.1 系统总体架构设计

为了实现系统的功能需求并保证其可维护性和可扩展性,我们采用模块化的设计思想,将系统划分为若干个相互协作的核心模块。系统的总体架构如下图所示:

底层库/框架
核心逻辑 (Core Logic)
用户界面 (UI)
控制命令
视频帧
视频帧
骨骼数据/标注帧
骨骼数据/帧尺寸/区域
危险类型/消息
报警消息
更新UI/报警消息
用户操作
绘制区域点
OpenCV 图像处理/视频
Mediapipe Pose 姿态估计
PySide6 GUI框架
Numpy/Scipy 数值/信号
Pydub/Sounddevice 音频播放
Pillow 图像/字体
CameraManager 视频采集
Application 主应用
SkeletonDetector 骨骼检测
DangerDetector 危险检测
AudioAlert 音频警报
MainWindow 主窗口
ImageViewer 视频显示
控制面板
报警信息显示
Core
UI

架构说明:

  1. 用户界面 (UI) 模块: 负责与用户进行交互。

    • MainWindow:作为主窗口,承载所有UI元素,管理布局,接收用户输入(按钮点击、选项更改等),并显示来自核心逻辑的信息。
    • ImageViewer:专门用于显示视频帧,并处理鼠标事件以实现危险区域的交互式绘制。
    • 控制面板:包含摄像头选择、启停按钮、区域绘制按钮、检测选项复选框等控件。
    • 报警信息显示:使用QListWidget显示报警历史记录,并提供清除按钮和音频开关。
  2. 核心逻辑 (Core Logic) 模块: 负责协调各个子模块的工作流程。

    • Application:作为应用程序的主控制类,初始化所有核心模块(CameraManager, SkeletonDetector, DangerDetector, AudioAlert, MainWindow),连接UI信号与处理槽函数,控制主事件循环(通过QTimer定时更新帧),调度数据流在各模块间传递。
    • CameraManager:负责管理摄像头的启动、停止和视频帧的读取,计算实时FPS。
    • SkeletonDetector:接收视频帧,调用Mediapipe Pose进行骨骼关键点检测,返回检测结果和标注后的图像帧。
    • DangerDetector:接收骨骼关键点坐标、图像尺寸和用户定义的危险区域信息,执行危险区域入侵检测和危险姿势识别算法,返回检测到的危险类型和对应的报警消息。
    • AudioAlert:接收报警消息,根据消息内容查找或生成对应的本地WAV音效,并在单独的线程中播放,提供报警冷却机制。
  3. 底层库/框架: 提供基础功能支持,如前文所述的OpenCV, Mediapipe, PySide6, Numpy, Scipy, Pillow, Pydub, Sounddevice等。

数据流说明:

  1. 用户通过UI进行操作(如点击“开始监控”)。
  2. MainWindow发出信号,Application的槽函数接收到信号。
  3. Application调用CameraManager启动摄像头。
  4. 定时器触发,Application调用CameraManager读取视频帧。
  5. Application将视频帧传递给SkeletonDetector
  6. SkeletonDetector返回骨骼数据和标注后的帧给Application
  7. Application将骨骼数据、帧尺寸、危险区域信息传递给DangerDetector
  8. DangerDetector进行危险判断,返回危险类型和消息给Application
  9. 如果检测到危险且满足报警条件,Application将报警消息传递给AudioAlert进行播放,并更新MainWindow显示报警信息和视觉警告。
  10. Application调用DangerDetector绘制危险区域(如果存在)。
  11. Application将最终要显示的(可能带有骨骼和区域标注的)图像帧传递给MainWindow,由ImageViewer显示。
  12. 同时,Application更新FPS和状态标签。
  13. 用户在ImageViewer上绘制危险区域时,ImageViewer发出包含点坐标的信号,Application接收后传递给DangerDetector进行添加(坐标转换后)。

这种模块化的架构使得各部分职责清晰,便于开发、测试和维护。例如,如果未来需要更换骨骼检测算法,只需修改SkeletonDetector模块,而对其他模块影响较小。

4.2 功能模块设计

4.2.1 视频采集模块 (CameraManager)
  • 主要功能: 初始化、启动、停止摄像头,读取视频帧,计算FPS。
  • 设计:
    • 使用cv2.VideoCapture打开指定ID的摄像头。
    • 设置期望的视频分辨率(如640x480)。
    • 提供start()stop()方法控制摄像头开关,并管理is_running状态。
    • read_frame()方法读取一帧,并包含错误处理(如读取失败时尝试重启)。
    • 通过计算相邻帧的时间差来估算实时FPS。
    • 在析构函数__del__中确保摄像头资源被释放。
4.2.2 骨骼检测模块 (SkeletonDetector)
  • 主要功能: 调用Mediapipe Pose检测骨骼关键点,返回结果和标注图像。
  • 设计:
    • 初始化mp.solutions.pose.Pose对象,配置参数(置信度阈值、模型复杂度)。
    • detect()方法接收BGR帧,转换为RGB后送入pose.process(),获取results对象。
    • 如果results.pose_landmarks存在,调用mp_drawing.draw_landmarks在帧副本上绘制骨骼。
    • 返回原始results对象和标注后的帧。
    • get_landmarks_coordinates()方法用于从results中提取所有关键点的像素坐标(x, y)和可见性(visibility),并存入字典返回。
4.2.3 危险检测模块 (DangerDetector)
  • 主要功能: 管理危险区域,执行区域入侵和危险姿势检测。
  • 设计:
    • danger_zones: 列表,存储用户定义的危险区域(Numpy数组表示的多边形顶点)。
    • add_danger_zone(): 添加一个多边形区域。
    • clear_danger_zones(): 清空所有区域。
    • draw_danger_zones(): 在输入帧上绘制所有危险区域(包括边界、半透明填充、闪烁效果和中心文字标签)。使用Pillow解决中文标签绘制问题,并兼容不同Pillow版本。
    • check_danger(): 核心检测入口函数。接收骨骼坐标和帧尺寸,依次调用_check_zone_intrusion()_check_dangerous_posture()。根据检测结果更新current_danger状态和danger_message
    • _check_zone_intrusion():
      • 遍历所有危险区域。
      • 遍历人体所有关键点。
      • 对每个可见性高于阈值(0.2)的关键点,使用cv2.pointPolygonTest判断是否在区域内。
      • 统计落入每个区域内的点数,对核心关键点(头、肩、髋)赋予更高权重。
      • 当某个区域内的点数(或加权分数)达到阈值(2分)时,判定为区域入侵,返回True。
      • 包含详细的调试打印信息。
    • _check_dangerous_posture():
      • 首先检查所需关键点(头、肩、髋、肘、腕、膝、踝)是否足够且可见性高于阈值(0.7)。
      • 弯腰检测: 计算肩中点到鼻子、肩中点到髋中点的向量,使用余弦定理计算夹角。结合nose_y < shoulders_mid_y判断是否前倾。当角度 > 60度且前倾且关键点可见时触发。
      • 手臂高举检测: 判断左右手腕的y坐标是否显著低于鼻子y坐标(注意图像坐标系y轴向下)。
      • 身体失衡检测: 计算左右肩y坐标差值,当差值超过帧高度的15%时触发。
      • 蹲下检测: 计算左右膝关节角度(膝-髋向量与膝-踝向量夹角)。计算髋部中点y坐标与脚踝中点y坐标的距离,并计算其与估算身高(鼻到踝)的比例。当平均膝关节角度 < 120度且髋踝比例 < 0.4时触发。
      • 奔跑检测: 计算脚踝水平距离(跨步)、膝盖水平距离。计算手腕水平距离与肩宽比例判断手臂摆动。计算身体躯干(肩中点到髋中点)与垂直方向夹角判断前倾。当跨步大(比例>0.3或绝对值>帧宽20%)且膝盖分开(>帧宽15%)且(手臂摆动或身体前倾)时触发。
      • 每个检测成功后,设置对应的danger_message并返回True。如果所有检测都未触发,返回False。

危险姿势检测流程图示例 (Mermaid Activity Diagram):

开始检查危险姿势
所需关键点是否足够且可见?
返回 False
计算弯腰角度和方向
角度>60度 且 前倾 且 可见?
设置弯腰消息, 返回 True
检查手臂高举
手腕低于头部阈值 且 可见?
设置举手消息, 返回 True
检查身体失衡
左右肩高度差>15%帧高 且 可见?
设置失衡消息, 返回 True
检查蹲下姿势
膝盖角度<120度 且 髋踝比<0.4 且 可见?
设置蹲下消息, 返回 True
检查奔跑姿势
跨步大 且 膝盖分开 且 前倾或摆臂 且 可见?
设置奔跑消息, 返回 True
4.2.4 音频警报模块 (AudioAlert)
  • 主要功能: 根据报警消息生成或加载对应的本地音效,并播放。
  • 设计:
    • warning_sounds: 字典,存储预先生成的警告消息与对应的WAV文件路径。
    • _generate_all_warning_sounds(): 在初始化时调用,在后台线程中为warnings_config中定义的每种危险消息生成对应的音效文件。
    • _generate_warning_sounds(): 实际的音效生成函数,根据配置调用不同的生成方法。
    • _generate_beep_sound(), _generate_beep_sequence(), _generate_siren_sound(), _generate_sweep_sound(), _generate_chirp_sound(): 使用Numpy生成不同模式的波形数据(正弦波、序列、频率调制、线性扫频、对数扫频),应用淡入淡出效果,并使用scipy.io.wavfile.write保存为WAV文件。文件名包含参数以避免冲突。
    • play_alert(): 接收报警消息。检查冷却时间(cooldown_period)和是否与上次消息相同,避免频繁重复报警。如果允许播放,则在新的后台线程中调用_play_warning_sound()
    • _play_warning_sound(): 核心播放逻辑。使用线程锁alert_lock确保同一时间只有一个警报在播放。查找warning_sounds字典,如果找不到匹配的预生成音效,则生成一个默认的蜂鸣声。然后依次尝试使用Sounddevice、Pydub和系统命令播放找到的WAV文件,直到成功或所有方法失败。播放完成后释放锁。
4.2.5 用户界面模块 (MainWindow, ImageViewer)
  • 主要功能: 提供用户交互界面,显示信息,接收用户输入。
  • 设计:
    • MainWindow:
      • 使用QVBoxLayoutQHBoxLayout组织整体布局(标题、内容区、状态栏)。内容区分左右两部分。
      • 左侧放置video_group(包含ImageViewerfps_label)。
      • 右侧垂直放置camera_group, zone_group, detect_group, alert_group
      • 使用QGroupBox对功能区域进行分组。
      • 使用QLabel, QComboBox, QPushButton, QCheckBox, QListWidget等标准控件。
      • 为关键按钮(开始、停止、绘制、清除、清除警报)设置objectName,以便应用style.py中定义的特定QSS样式(如蓝色主按钮、红色危险按钮)。
      • 初始化QTimer用于定时触发update_frame槽函数。
      • 提供update_frame(), update_fps(), update_status(), add_alert(), show_error(), show_warning_overlay(), clear_warning_overlay()等方法供Application调用以更新UI状态。
      • _init_status_bar(): 创建底部状态栏,显示运行状态和版本号。
      • show_warning_overlay(): 在视频上方显示一个半透明、带文字的警告覆盖层,并启动warning_timer实现闪烁效果。
    • ImageViewer:
      • 继承QLabel用于显示QPixmap
      • set_image(): 接收OpenCV图像(Numpy数组),转换为QPixmap,根据控件大小进行缩放(保持宽高比),并设置给QLabel。调用_draw_points()绘制用户当前绘制的多边形。
      • 重写mousePressEvent, mouseMoveEvent, mouseDoubleClickEvent: 实现交互式绘制逻辑。左键单击添加点到self.points列表,移动时更新预览(如果需要),双击结束绘制。发出drawing_started, drawing_progress, drawing_finished信号通知Application绘制状态和最终点列表。
      • _draw_points(): 在set_image设置的QPixmap副本上使用QPainter绘制当前self.points列表中的点和连线,以及完成后的半透明填充区域。第一个点用不同颜色标记。
      • clear_points(): 清空self.points列表,重置绘制状态。

4.3 数据库设计

本系统主要进行实时视频处理和分析,不涉及大量结构化数据的持久化存储和复杂查询。用户定义的危险区域信息、报警记录等都是在程序运行时内存中处理。因此,本系统不需要设计传统的关系型数据库或非关系型数据库。如果未来需要记录详细的报警日志或进行长期的行为数据分析,可以考虑引入简单的文件日志(如CSV或JSON格式)或轻量级数据库(如SQLite)。

4.4 关键算法描述

4.4.1 坐标转换(UI绘制点到实际图像点)

用户在UI的ImageViewer上绘制危险区域的点坐标是相对于显示控件尺寸的。而危险检测需要作用于原始摄像头分辨率的图像。因此,在Application.add_danger_zone()中需要进行坐标转换:
x i m g = x u i × W i m g W u i x_{img} = x_{ui} \times \frac{W_{img}}{W_{ui}} ximg=xui×WuiWimg
y i m g = y u i × H i m g H u i y_{img} = y_{ui} \times \frac{H_{img}}{H_{ui}} yimg=yui×HuiHimg
其中, ( x u i , y u i ) (x_{ui}, y_{ui}) (xui,yui)是UI上的点坐标, ( x i m g , y i m g ) (x_{img}, y_{img}) (ximg,yimg)是转换后的图像坐标, W u i , H u i W_{ui}, H_{ui} Wui,HuiImageViewer中显示的QPixmap的尺寸, W i m g , H i m g W_{img}, H_{img} Wimg,Himg是摄像头原始帧的宽度和高度。

4.4.2 角度计算(余弦定理)

在检测弯腰和蹲下姿势时,需要计算三个点构成的角度(例如肩中点-髋中点-膝中点构成的膝关节角)。给定三个点A, B, C,计算以B为顶点的角度 θ \theta θ

  1. 计算向量 B A ⃗ = A − B \vec{BA} = A - B BA =AB B C ⃗ = C − B \vec{BC} = C - B BC =CB
  2. 计算两个向量的点积: B A ⃗ ⋅ B C ⃗ = B A x × B C x + B A y × B C y \vec{BA} \cdot \vec{BC} = BA_x \times BC_x + BA_y \times BC_y BA BC =BAx×BCx+BAy×BCy
  3. 计算两个向量的模(长度): ∣ B A ⃗ ∣ = B A x 2 + B A y 2 |\vec{BA}| = \sqrt{BA_x^2 + BA_y^2} BA =BAx2+BAy2 ∣ B C ⃗ ∣ = B C x 2 + B C y 2 |\vec{BC}| = \sqrt{BC_x^2 + BC_y^2} BC =BCx2+BCy2
  4. 计算夹角的余弦值: cos ⁡ ( θ ) = B A ⃗ ⋅ B C ⃗ ∣ B A ⃗ ∣ × ∣ B C ⃗ ∣ \cos(\theta) = \frac{\vec{BA} \cdot \vec{BC}}{|\vec{BA}| \times |\vec{BC}|} cos(θ)=BA ×BC BA BC
  5. 计算角度(弧度): θ r a d = arccos ⁡ ( cos ⁡ ( θ ) ) \theta_{rad} = \arccos(\cos(\theta)) θrad=arccos(cos(θ))。注意处理 cos ⁡ ( θ ) \cos(\theta) cos(θ) 超出 [-1, 1] 范围的边界情况。
  6. 转换为角度(度数): θ d e g = θ r a d × 180 π \theta_{deg} = \theta_{rad} \times \frac{180}{\pi} θdeg=θrad×π180
4.4.3 点在多边形内判断

使用OpenCV的cv2.pointPolygonTest(polygon, point, measureDist)函数。

  • polygon: 危险区域的顶点列表(Numpy数组)。
  • point: 要测试的人体关键点坐标 (x, y)
  • measureDist: 设置为 False
    函数返回值:
  • > 0: 点在多边形内部。
  • = 0: 点在多边形边界上。
  • < 0: 点在多边形外部。
    _check_zone_intrusion中,当返回值 >= 0 时,认为该点落入危险区域。

5. 系统实现

本章将详细介绍系统的具体实现过程,包括开发环境的配置、核心模块的关键代码实现以及代码中涉及的重要算法和逻辑细节。

5.1 开发环境与依赖

  • 操作系统: Windows 10/11, macOS Ventura, Ubuntu 20.04 (设计时考虑跨平台兼容性)
  • 编程语言: Python 3.9+
  • 主要依赖库及其版本 (参考 requirements.txt):
    • PySide6: 6.5.2+ (用于GUI)
    • opencv-python: 4.8.0+ (图像处理和视频读取)
    • mediapipe: 0.10.3+ (骨骼识别)
    • numpy: 1.24.3+ (数值计算)
    • scipy: 1.11.2+ (科学计算,音频文件处理,信号生成)
    • Pillow: 10.1.0+ (图像处理,字体绘制)
    • sounddevice: 0.4.6+ (音频播放后端)
    • pydub: 0.25.1+ (音频播放后端,需要ffmpeg支持)
    • setuptools: (用于pkg_resources检测Pillow版本)
  • 开发工具: Visual Studio Code 或 PyCharm Professional
  • 版本控制: Git

依赖库通过pip install -r requirements.txt命令进行安装。对于pydub的完整功能(特别是播放非WAV格式),可能需要单独安装ffmpeg并将其添加到系统路径。

5.2 核心模块实现

5.2.1 骨骼检测模块 (src/core/skeleton_detector.py)

该模块封装了Mediapipe Pose的使用,实现了骨骼点的检测和坐标提取。

关键代码片段:

import cv2
import mediapipe as mp
import numpy as np
import logging

class SkeletonDetector:
    def __init__(self, min_detection_confidence=0.5, min_tracking_confidence=0.5):
        # ... (初始化 Mediapipe Pose 对象 self.pose) ...
        self.mp_pose = mp.solutions.pose
        self.mp_drawing = mp.solutions.drawing_utils
        self.mp_drawing_styles = mp.solutions.drawing_styles
        self.pose = self.mp_pose.Pose(
            static_image_mode=False, # 处理视频流
            model_complexity=1,      # 模型复杂度,0, 1, 2可选,越高越准但越慢
            enable_segmentation=False,# 不启用分割
            min_detection_confidence=min_detection_confidence,
            min_tracking_confidence=min_tracking_confidence
        )

    def detect(self, frame):
        # 将BGR转换为RGB
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        # 提高性能,将图像标记为不可写
        frame_rgb.flags.writeable = False
        # 进行骨骼检测
        results = self.pose.process(frame_rgb)
        # 恢复图像为可写
        frame_rgb.flags.writeable = True

        # 绘制骨骼点和连接线 (在原始 BGR 帧的副本上绘制)
        annotated_frame = frame.copy()
        if results.pose_landmarks:
            self.mp_drawing.draw_landmarks(
                annotated_frame,
                results.pose_landmarks,
                self.mp_pose.POSE_CONNECTIONS,
                landmark_drawing_spec=self.mp_drawing_styles.get_default_pose_landmarks_style()
            )
        return results, annotated_frame

    def get_landmarks_coordinates(self, results, frame_shape):
        landmarks_coords = {}
        if results.pose_landmarks:
            height, width, _ = frame_shape
            for idx, landmark in enumerate(results.pose_landmarks.landmark):
                # 将相对坐标转换为像素坐标,并存储可见性
                x = int(landmark.x * width)
                y = int(landmark.y * height)
                # 限制坐标在图像范围内
                x = max(0, min(x, width - 1))
                y = max(0, min(y, height - 1))
                landmarks_coords[idx] = (x, y, landmark.visibility)
        return landmarks_coords

实现说明:

  • 初始化时创建mp.solutions.pose.Pose实例,并配置相关参数。
  • detect方法是核心处理函数,接收OpenCV读取的BGR帧,先转换为RGB格式(Mediapipe需要),然后调用self.pose.process()进行检测。为了效率,处理前将图像标记为不可写。检测后,在原始帧的副本上绘制骨骼。
  • get_landmarks_coordinates方法遍历检测结果中的每个landmark,将其归一化的(x, y)坐标乘以图像的宽高,得到像素坐标,并连同visibility一起存储在字典中返回。增加了边界检查确保坐标不越界。
5.2.2 危险检测模块 (src/core/danger_detector.py)

该模块负责实现危险区域入侵和各种危险姿势的判断逻辑。

关键代码片段 - 弯腰检测:

# ... 在 _check_dangerous_posture 方法内 ...
        # 检查弯腰过度
        nose_x, nose_y, nose_vis = landmarks_coords.get(0, (0, 0, 0))
        ls_x, ls_y, ls_vis = landmarks_coords.get(11, (0, 0, 0)) # 左肩
        rs_x, rs_y, rs_vis = landmarks_coords.get(12, (0, 0, 0)) # 右肩
        lh_x, lh_y, lh_vis = landmarks_coords.get(23, (0, 0, 0)) # 左髋
        rh_x, rh_y, rh_vis = landmarks_coords.get(24, (0, 0, 0)) # 右髋

        # 检查所需点是否存在且可见
        bend_points_visible = all(vis > visibility_threshold for vis in [nose_vis, ls_vis, rs_vis, lh_vis, rh_vis])

        if bend_points_visible:
            shoulders_mid_x = (ls_x + rs_x) / 2
            shoulders_mid_y = (ls_y + rs_y) / 2
            hips_mid_x = (lh_x + rh_x) / 2
            hips_mid_y = (lh_y + rh_y) / 2

            # 向量1: 肩中点 -> 鼻子
            vector1 = np.array([nose_x - shoulders_mid_x, nose_y - shoulders_mid_y])
            # 向量2: 肩中点 -> 髋中点
            vector2 = np.array([hips_mid_x - shoulders_mid_x, hips_mid_y - shoulders_mid_y])

            vector1_length = np.linalg.norm(vector1)
            vector2_length = np.linalg.norm(vector2)

            if vector1_length > 1 and vector2_length > 1: # 避免长度过小导致计算误差
                dot_product = np.dot(vector1, vector2)
                cos_angle = dot_product / (vector1_length * vector2_length)
                cos_angle = np.clip(cos_angle, -1.0, 1.0) # 限制范围
                bend_angle = np.degrees(np.arccos(cos_angle))

                # 检查是否前倾 (鼻子y坐标小于肩部y坐标)
                is_bending_forward = nose_y < shoulders_mid_y

                print(f"弯腰角度: {bend_angle:.1f}度, 是否前倾: {is_bending_forward}")

                # 阈值判断
                if bend_angle > 60 and is_bending_forward:
                    self.danger_message = "警告:检测到弯腰过度!"
                    return True

关键代码片段 - 蹲下检测:

# ... 在 _check_dangerous_posture 方法内 ...
        # 检查蹲下姿势
        required_squat_points = [0, 23, 24, 25, 26, 27, 28] # 鼻子, 髋, 膝, 踝
        squat_points_visible = all(landmarks_coords.get(idx, (0,0,0))[2] > visibility_threshold for idx in required_squat_points)

        if squat_points_visible:
            # ... (获取左右髋、膝、踝坐标) ...
            hips_y = (landmarks_coords[23][1] + landmarks_coords[24][1]) / 2
            ankles_y = (landmarks_coords[27][1] + landmarks_coords[28][1]) / 2
            nose_y = landmarks_coords[0][1]

            # 估算身高 (简化为鼻子到脚踝垂直距离)
            estimated_height = abs(ankles_y - nose_y)

            if estimated_height < 50: # 身高过小,可能检测不准
                 return False # 或 continue

            # 计算左右膝盖角度 (调用计算角度的辅助函数,或直接实现余弦定理)
            left_knee_angle = self._calculate_angle(landmarks_coords[23], landmarks_coords[25], landmarks_coords[27])
            right_knee_angle = self._calculate_angle(landmarks_coords[24], landmarks_coords[26], landmarks_coords[28])
            avg_knee_angle = (left_knee_angle + right_knee_angle) / 2

            # 计算髋部相对高度比例
            hip_ankle_dist = abs(hips_y - ankles_y)
            hip_height_ratio = hip_ankle_dist / estimated_height

            print(f"平均膝盖角度: {avg_knee_angle:.1f}度, 髋踝比例: {hip_height_ratio:.2f}")

            # 阈值判断
            if avg_knee_angle < 120 and hip_height_ratio < 0.4:
                self.danger_message = "警告:检测到蹲下姿势,可能存在危险!"
                return True

(注:_calculate_angle 辅助函数需要实现,其逻辑即前述的余弦定理计算)

关键代码片段 - 奔跑检测:

# ... 在 _check_dangerous_posture 方法内 ...
        # 检测奔跑姿势
        required_run_points = [11, 12, 15, 16, 25, 26, 27, 28] # 肩, 腕, 膝, 踝
        run_points_visible = all(landmarks_coords.get(idx, (0,0,0))[2] > visibility_threshold for idx in required_run_points)
        key_points_visible = all(landmarks_coords.get(idx, (0,0,0))[2] > visibility_threshold for idx in [0, 11, 12, 23, 24]) # 用于前倾判断

        if run_points_visible:
            # ... (获取肩、腕、膝、踝坐标) ...
            left_ankle_x, left_ankle_y, _ = landmarks_coords[27]
            right_ankle_x, right_ankle_y, _ = landmarks_coords[28]
            ankle_x_distance = abs(left_ankle_x - right_ankle_x)
            ankles_y = (left_ankle_y + right_ankle_y) / 2
            nose_y = landmarks_coords.get(0, (0, ankles_y, 0))[1] # 获取鼻子y,若无则用脚踝y
            estimated_height = abs(ankles_y - nose_y)
            if estimated_height < 1: estimated_height = frame_shape[0] # 防止除零

            left_knee_x, _, _ = landmarks_coords[25]
            right_knee_x, _, _ = landmarks_coords[26]
            knee_x_distance = abs(left_knee_x - right_knee_x)

            # 手臂摆动判断
            left_wrist_x, _, lw_vis = landmarks_coords[15]
            right_wrist_x, _, rw_vis = landmarks_coords[16]
            arm_swing = False
            if lw_vis > visibility_threshold and rw_vis > visibility_threshold:
                 shoulder_width = abs(landmarks_coords[11][0] - landmarks_coords[12][0])
                 wrist_x_distance = abs(left_wrist_x - right_wrist_x)
                 if shoulder_width > 1:
                     arm_swing = (wrist_x_distance / shoulder_width) > 1.5

            # 身体前倾判断
            forward_tilt = False
            if key_points_visible:
                # ... (获取肩中点、髋中点坐标 shoulders_mid_x/y, hips_mid_x/y) ...
                shoulder_to_hip_x = hips_mid_x - shoulders_mid_x
                shoulder_to_hip_y = hips_mid_y - shoulders_mid_y
                if abs(shoulder_to_hip_y) > 1:
                    tilt_angle = abs(np.degrees(np.arctan(shoulder_to_hip_x / shoulder_to_hip_y)))
                    forward_tilt = tilt_angle > 15 # 前倾超过15度

            # 跨步比例
            stride_height_ratio = ankle_x_distance / estimated_height

            print(f"脚踝间距: {ankle_x_distance}, 相对身高比: {stride_height_ratio:.2f}, 前倾: {forward_tilt}, 手臂摆动: {arm_swing}")

            # 阈值判断
            if (stride_height_ratio > 0.3 or ankle_x_distance > frame_shape[1] * 0.2) and \
               knee_x_distance > frame_shape[1] * 0.15 and \
               (forward_tilt or arm_swing):
                self.danger_message = "警告:检测到奔跑姿势,请减速慢行!"
                return True

实现说明:

  • 每个姿势检测前都检查所需的关键点是否在landmarks_coords字典中存在且其visibility高于设定的阈值(如0.7)。
  • 大量使用向量计算(Numpy)和几何关系判断。
  • 阈值(如弯腰角度60度,蹲下膝角120度/高度比0.4,奔跑跨步比0.3/前倾15度等)是根据经验设定,可能需要根据实际应用场景进行调整优化。
  • 包含了print语句输出中间计算结果,便于调试。
  • 危险区域绘制部分使用了Pillow库来绘制中文标签,并做了版本兼容处理(textbbox vs textsize)和字体加载处理。
5.2.3 音频警报模块 (src/utils/audio_alert.py)

该模块负责生成和播放本地化的警告音效。

关键代码片段 - 音效生成与播放逻辑:

import os
import tempfile
import threading
import time
import subprocess
import platform
import wave
import numpy as np
from scipy.io import wavfile
from scipy import signal
# ... (导入 pydub, sounddevice) ...

class AudioAlert:
    def __init__(self):
        # ... (初始化路径, 锁, 采样率等) ...
        self.sample_rate = 44100
        self.warning_sounds = {} # 存储预生成音效路径
        self.cooldown_period = 3 # 冷却时间3秒
        self.last_message = ""
        self.last_alert_time = 0
        self.is_playing = False
        self.alert_lock = threading.Lock()
        self._generate_all_warning_sounds() # 后台生成

    def _generate_all_warning_sounds(self):
        warnings_config = { # 配置不同警告和对应的声音参数
            "警告:有人进入危险区域!": {"type": "siren", "duration": 1.5, "pitch": 0.8},
            "警告:检测到弯腰过度!": {"type": "beep_sequence", "duration": 1.2, "count": 3, "pitch": 1.2},
            # ... (其他警告配置,包括蹲下和奔跑) ...
            "警告:检测到蹲下姿势,可能存在危险!": {"type": "beep_sequence", "duration": 1.0, "count": 2, "pitch": 0.9, "frequency": 700},
            "警告:检测到奔跑姿势,请减速慢行!": {"type": "siren", "duration": 1.2, "pitch": 1.2, "intensity": 0.9, "cycles": 5}
        }
        # 启动后台线程生成
        threading.Thread(target=self._generate_warning_sounds, args=(warnings_config,), daemon=True).start()

    def _generate_warning_sounds(self, warnings_config):
        for message, config in warnings_config.items():
            # ... (根据config调用不同的 _generate_xxx_sound 方法生成WAV) ...
            # 例如: sound_file = self._generate_siren_sound(...)
            if sound_file and os.path.exists(sound_file):
                self.warning_sounds[message] = sound_file
                print(f"成功生成警告音效: {message}")

    # --- 音效生成函数示例 ---
    def _generate_siren_sound(self, duration=1.5, low_freq=400, high_freq=1000, cycles=3, intensity=0.7):
        t = np.linspace(0, duration, int(self.sample_rate * duration), False)
        # 正弦调制频率
        freq_mod = (high_freq - low_freq) / 2 * np.sin(2 * np.pi * cycles * t / duration) + (high_freq + low_freq) / 2
        phase = 2 * np.pi * np.cumsum(freq_mod) / self.sample_rate
        siren = np.sin(phase) * intensity
        # ... (应用淡入淡出, 裁剪, 转换为int16, 保存WAV) ...
        siren = np.clip(siren, -1, 1)
        siren = (siren * 32767).astype(np.int16)
        # ... (添加淡入淡出代码) ...
        siren_file = os.path.join(self.temp_dir, f"alert_siren_{low_freq}_{high_freq}.wav")
        wavfile.write(siren_file, self.sample_rate, siren)
        return siren_file

    # --- 播放逻辑 ---
    def play_alert(self, message):
        if not message: return
        current_time = time.time()
        # 检查冷却时间
        if (current_time - self.last_alert_time < self.cooldown_period and message == self.last_message):
            return
        self.last_message = message
        self.last_alert_time = current_time
        # 启动后台线程播放
        alert_thread = threading.Thread(target=self._play_warning_sound, args=(message,), daemon=True)
        alert_thread.start()

    def _play_warning_sound(self, message):
        with self.alert_lock: # 确保同一时间只有一个声音播放
            if self.is_playing: time.sleep(0.1) # 等待上一个结束 (或者直接返回)
            self.is_playing = True
            try:
                sound_file = self.warning_sounds.get(message)
                if not sound_file or not os.path.exists(sound_file):
                    print(f"未找到对应警告音效 '{message}',使用默认提示音")
                    # 生成默认音效
                    sound_file = self._generate_beep_sequence(count=2, frequency=900)
                    if not sound_file or not os.path.exists(sound_file):
                         print("无法生成默认音效")
                         self.is_playing = False
                         return

                played = False
                # 尝试播放方法1: sounddevice
                if HAVE_SOUNDDEVICE and not played:
                    try:
                        sample_rate, data = wavfile.read(sound_file)
                        if len(data.shape) > 1: data = data.mean(axis=1) # 转单声道
                        sd.play(data, sample_rate)
                        sd.wait()
                        played = True
                    except Exception as e: print(f"sounddevice播放失败: {e}")

                # 尝试播放方法2: pydub
                if HAVE_PYDUB and not played:
                    try:
                        sound = AudioSegment.from_wav(sound_file)
                        play(sound)
                        played = True
                    except Exception as e: print(f"pydub播放失败: {e}")

                # 尝试播放方法3: 系统命令
                if not played:
                    try:
                        if self._play_with_system_tools(sound_file):
                             played = True
                    except Exception as e: print(f"系统命令播放失败: {e}")

                if not played:
                    print("警告: 所有音频播放方法都失败了")

            except Exception as e:
                print(f"播放警告音效时出错: {e}")
            finally:
                self.is_playing = False # 释放锁

实现说明:

  • 初始化时,通过_generate_all_warning_sounds在后台线程异步生成所有定义好的警告音效,避免阻塞主程序启动。
  • 提供了多种音效生成函数(_generate_beep_sound, _generate_beep_sequence, _generate_siren_sound等),利用Numpy生成波形数据,Scipy写入WAV文件。音效类型和参数在warnings_config中配置。
  • play_alert方法实现了冷却机制,防止短时间内对同一事件重复报警。
  • _play_warning_sound方法负责实际的播放。它使用了线程锁alert_lock来防止多个警报声同时播放造成混乱。它实现了播放后端的多级降级策略:优先尝试sounddevice,失败则尝试pydub,再失败则尝试调用系统命令(如afplay, powershell, aplay),以提高跨平台的兼容性和鲁棒性。
5.2.4 主应用逻辑 (src/main.py)

该模块作为应用的核心控制器,连接UI和后端处理模块。

关键代码片段 - 主循环与信号连接:

import sys
import cv2
import numpy as np
import traceback
from PySide6.QtWidgets import QApplication, QMessageBox
from PySide6.QtCore import Qt, QTimer

# ... (导入 MainWindow, CameraManager, SkeletonDetector, DangerDetector, AudioAlert) ...

class Application:
    def __init__(self):
        self.app = QApplication(sys.argv)
        # ... (设置高DPI等属性) ...
        self.main_window = MainWindow()
        # ... (初始化 camera, skeleton_detector, danger_detector, audio_alert,包含异常处理) ...
        self.is_drawing_zone = False
        self._connect_signals()

    def _connect_signals(self):
        # --- UI控件信号连接到槽函数 ---
        # 相机控制
        self.main_window.start_button.clicked.connect(self.start_camera)
        self.main_window.stop_button.clicked.connect(self.stop_camera)
        # 危险区域控制
        self.main_window.draw_zone_button.clicked.connect(self.toggle_draw_zone)
        self.main_window.clear_zone_button.clicked.connect(self.clear_danger_zone)
        # 图像查看器信号 (绘制完成)
        self.main_window.image_viewer.drawing_finished.connect(self.add_danger_zone)
        # 定时器信号 (用于帧更新)
        self.main_window.timer.timeout.connect(self.update_frame)
        # 清除警报按钮
        self.main_window.clear_alerts_button.clicked.connect(self.main_window.alert_list.clear)

    def run(self):
        self.main_window.show()
        return self.app.exec()

    def start_camera(self):
        try:
            camera_id = self.main_window.camera_combo.currentData()
            self.camera = CameraManager(camera_id=camera_id)
            self.camera.start()
            # 更新UI状态
            self.main_window.start_button.setEnabled(False)
            # ... (其他UI更新) ...
            self.main_window.update_status("运行中")
            # 启动定时器
            self.main_window.timer.start(33) # 约30 FPS
        except Exception as e:
            self.main_window.show_error("相机错误", f"无法启动相机: {str(e)}")
            self.camera = None

    def stop_camera(self):
        self.main_window.timer.stop()
        if self.camera:
            self.camera.stop()
            self.camera = None
        # 更新UI状态
        self.main_window.start_button.setEnabled(True)
        # ... (其他UI更新) ...
        self.main_window.update_status("已停止")

    def update_frame(self): # 定时器触发的核心处理流程
        if not self.camera: return
        try:
            frame, success = self.camera.read_frame()
            if not success or frame is None: return

            fps = self.camera.get_fps()
            self.main_window.update_fps(fps)

            # 如果正在绘制区域,只显示原始帧
            if self.is_drawing_zone:
                self.main_window.update_frame(frame)
                return

            # 检查核心模块是否初始化成功
            if not self.skeleton_detector or not self.danger_detector:
                self.main_window.update_frame(frame)
                return

            try:
                # 1. 骨骼检测
                results, annotated_frame = self.skeleton_detector.detect(frame)
                landmarks_coords = self.skeleton_detector.get_landmarks_coordinates(results, frame.shape)

                # 2. 危险检测
                previous_danger = self.danger_detector.current_danger # 记录上次状态
                danger_type = DangerType.NO_DANGER
                message = ""
                # 根据UI选项决定是否执行检测
                perform_zone_check = self.main_window.zone_check.isChecked()
                perform_posture_check = self.main_window.posture_check.isChecked()

                if perform_zone_check or perform_posture_check:
                    # 调用危险检测器 (内部会区分区域和姿势)
                    danger_type, message = self.danger_detector.check_danger(
                        landmarks_coords, frame.shape,
                        check_zone=perform_zone_check, # 传递选项 (注: 当前check_danger未接收此参数,需修改)
                        check_posture=perform_posture_check # 传递选项 (注: 同上)
                    )

                # 3. 处理检测结果 & 报警
                if danger_type != DangerType.NO_DANGER and message:
                    self.main_window.add_alert(message) # 添加到UI列表

                    # 状态变化时触发视觉和音频警报
                    if previous_danger != danger_type:
                         self.main_window.show_warning_overlay(danger_type, message)
                         if self.main_window.audio_check.isChecked() and self.audio_alert:
                             try:
                                 self.audio_alert.play_alert(message)
                             except Exception as e: print(f"播放语音警报时出错: {e}")
                    # 对于区域入侵,即使状态未变也可能需要持续报警(取决于策略,当前仅状态变化时报警)

                # 4. 清除警告 (如果当前无危险)
                elif not landmarks_coords or danger_type == DangerType.NO_DANGER:
                     self.main_window.clear_warning_overlay()

                # 5. 绘制危险区域 (如果存在)
                display_frame = annotated_frame # 默认使用带骨骼的帧
                if self.danger_detector.danger_zones:
                    # 在带骨骼的帧上绘制区域
                    display_frame = self.danger_detector.draw_danger_zones(annotated_frame)

                # 6. 更新UI显示
                self.main_window.update_frame(display_frame)

            except Exception as e:
                print(f"处理帧时发生错误: {e}")
                traceback.print_exc()
                self.main_window.update_frame(frame) # 出错时显示原始帧
        except Exception as e:
            print(f"读取或更新帧时发生错误: {e}")
            traceback.print_exc()

    def toggle_draw_zone(self):
        self.is_drawing_zone = not self.is_drawing_zone
        # ... (更新按钮文本, 状态标签, 清除ImageViewer点) ...

    def add_danger_zone(self, points): # 接收来自ImageViewer的信号
        if not self.danger_detector or not self.camera: return
        if len(points) >= 3:
            try:
                # --- 坐标转换 ---
                camera_width = self.camera.width
                camera_height = self.camera.height
                pixmap_width = self.main_window.image_viewer.current_pixmap.width()
                pixmap_height = self.main_window.image_viewer.current_pixmap.height()
                if pixmap_width == 0 or pixmap_height == 0: return # 防止除零

                ratio_x = camera_width / pixmap_width
                ratio_y = camera_height / pixmap_height

                scaled_points = []
                for p in points:
                    scaled_x = int(p[0] * ratio_x)
                    scaled_y = int(p[1] * ratio_y)
                    # 限制在图像范围内
                    scaled_x = max(0, min(scaled_x, camera_width - 1))
                    scaled_y = max(0, min(scaled_y, camera_height - 1))
                    scaled_points.append((scaled_x, scaled_y))

                if len(scaled_points) >= 3:
                    self.danger_detector.add_danger_zone(scaled_points)
                    print(f"已添加危险区域,共 {len(self.danger_detector.danger_zones)} 个区域")
                    self.main_window.clear_zone_button.setEnabled(True) # 使能清除按钮
                else:
                    print("错误: 有效点数不足")

            except Exception as e: print(f"添加危险区域时出错: {e}")
        # ... (重置绘制状态) ...
        self.is_drawing_zone = False
        self.main_window.draw_zone_button.setText("绘制危险区域")
        self.main_window.update_status("运行中" if self.camera and self.camera.is_running else "已停止")


    def clear_danger_zone(self):
        if self.danger_detector:
            self.danger_detector.clear_danger_zones()
        self.main_window.image_viewer.clear_points() # 清除UI上的绘制
        self.is_drawing_zone = False
        self.main_window.draw_zone_button.setText("绘制危险区域")
        self.main_window.clear_zone_button.setEnabled(False) # 禁用清除按钮

实现说明:

  • __init__:初始化UI和所有核心业务逻辑模块,并调用_connect_signals连接信号槽。
  • _connect_signals:将UI控件(如按钮点击)的信号连接到Application类中对应的处理方法(槽)。将ImageViewerdrawing_finished信号连接到add_danger_zone。将QTimertimeout信号连接到update_frame
  • start_camera / stop_camera:控制CameraManager的启停,更新UI按钮状态和状态标签,启动/停止定时器。
  • update_frame:这是由定时器周期性调用的核心函数,实现了完整的数据处理流水线:读取帧 -> 骨骼检测 -> 危险检测 -> 处理结果(报警、更新UI) -> 显示帧。包含了对模块是否初始化和处理过程中异常的捕获。(注:代码中补充了将UI检测选项传递给check_danger的设想,实际代码需要修改DangerDetector.check_danger方法签名来接收并使用这些参数)
  • toggle_draw_zone:切换危险区域绘制模式的状态。
  • add_danger_zone:响应ImageViewer绘制完成的信号,获取UI点坐标,执行坐标转换(UI坐标 -> 图像坐标),然后调用DangerDetector.add_danger_zone添加区域。
  • clear_danger_zone:调用DangerDetector.clear_danger_zones清除后端区域数据,并调用ImageViewer.clear_points清除UI绘制。
5.2.5 用户界面模块 (src/ui/main_window.py, src/ui/style.py, src/utils/icons.py)

这部分代码主要负责界面的布局、控件的创建和样式的应用。

  • main_window.py: 如4.2.5节设计所述,使用PySide6控件和布局管理器构建界面。通过setObjectName为控件设置对象名,以便QSS选择器应用特定样式。ImageViewer类通过重写鼠标事件和使用QPainter实现了交互式区域绘制。
  • style.py: 定义了全局QSS样式表MAIN_STYLE,包含了对QWidget, QMainWindow, QPushButton (包括#primary_button, #stop_button, #danger_button), QGroupBox, QComboBox, QCheckBox, QListWidget, QLabel等控件的样式设置(背景色、前景色、边框、圆角、字体、内外边距等)。
  • icons.py: 解决了QSS中直接引用本地图片文件可能存在的路径问题和部署问题。apply_icons_to_style函数将style.py中对checkmark.pngdown_arrow.png的引用替换为内联的SVG数据URL。这样图标就直接嵌入到样式表中,无需单独分发图片文件。

关键代码片段 - QSS样式应用与按钮ObjectName设置:

# src/ui/main_window.py
class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        self.setObjectName("mainWindow") # 为主窗口设置ObjectName
        style = apply_icons_to_style(MAIN_STYLE) # 获取处理图标后的样式
        self.setStyleSheet(style) # 应用样式表
        # ...

    def _init_control_area(self):
        # ...
        self.start_button = QPushButton("开始监控")
        self.start_button.setObjectName("primary_button") # 设置ObjectName
        self.stop_button = QPushButton("停止监控")
        self.stop_button.setObjectName("stop_button") # 设置ObjectName
        # ...
        self.draw_zone_button = QPushButton("绘制危险区域")
        self.draw_zone_button.setObjectName("primary_button") # 设置ObjectName
        self.clear_zone_button = QPushButton("清除危险区域")
        self.clear_zone_button.setObjectName("danger_button") # 设置ObjectName
        # ...

    def _init_alert_area(self):
        # ...
        self.clear_alerts_button = QPushButton("清除所有警报")
        self.clear_alerts_button.setObjectName("danger_button") # 设置ObjectName
        # ...

# src/ui/style.py
MAIN_STYLE = """
/* ... 其他样式 ... */

/* 主要按钮(蓝色) */
QPushButton#primary_button {
    background-color: #4a86e8;
    color: white;
}
QPushButton#primary_button:hover { background-color: #3a76d8; }
QPushButton#primary_button:pressed { background-color: #2a66c8; }

/* 停止按钮(灰色) */
QPushButton#stop_button {
    background-color: #707070;
    color: white;
}
QPushButton#stop_button:hover { background-color: #606060; }
QPushButton#stop_button:pressed { background-color: #505050; }

/* 危险按钮(红色) */
QPushButton#danger_button {
    background-color: #e8546c;
    color: white; /* 确保文字也是白色 */
}
QPushButton#danger_button:hover { background-color: #d8445c; }
QPushButton#danger_button:pressed { background-color: #c8344c; }

/* ... 其他样式 ... */
"""

实现说明: 通过为控件设置objectName并配合QSS中的ID选择器 (#objectName),可以为特定类型的按钮应用不同的背景色,使得UI更加清晰地区分不同功能的按钮。apply_icons_to_style的使用提高了样式的可移植性。


6. 系统测试

系统测试是确保软件质量、验证系统是否满足需求的关键环节。本章将描述测试环境、设计测试用例、执行测试并分析结果,以评估本系统的功能、性能和稳定性。

6.1 测试环境

  • 硬件环境:
    • CPU: Intel Core i7-10700 @ 2.90GHz / Apple M1 Pro
    • 内存: 16 GB RAM
    • 显卡: NVIDIA GeForce RTX 3060 / Apple M1 Pro integrated GPU
    • 摄像头: 标准USB摄像头 (Logitech C920, 720p/1080p)
  • 软件环境:
    • 操作系统: Windows 11 / macOS Sonoma
    • Python版本: 3.10.4
    • 依赖库版本: 见 requirements.txt (如 PySide6 6.5.2, OpenCV 4.8.0, Mediapipe 0.10.3, Numpy 1.24.3等)

6.2 测试用例设计

我们针对系统的主要功能模块设计了以下测试用例。测试的目标是验证功能的正确性、界面的响应以及基本性能。

用例ID测试模块测试项测试步骤预期结果
TC_CAM_01视频采集与显示摄像头连接与启动1. 启动程序。 2. 默认选择“默认相机”。 3. 点击“开始监控”按钮。1. “开始监控”按钮禁用,“停止监控”按钮启用。 2. 视频画面区域显示实时图像。 3. 状态栏显示“运行中”。 4. FPS标签显示非零数值。
TC_CAM_02视频采集与显示摄像头停止1. 在监控运行状态下,点击“停止监控”按钮。1. “开始监控”按钮启用,“停止监控”按钮禁用。 2. 视频画面停止更新(显示最后一帧或空白)。 3. 状态栏显示“已停止”。 4. FPS标签显示0。
TC_CAM_03视频采集与显示摄像头选择1. 停止监控。 2. 在下拉框中选择另一个有效的相机ID。 3. 点击“开始监控”。能够成功切换到并显示所选摄像头的画面。
TC_SKL_01骨骼检测单人骨骼检测与显示1. 启动监控。 2. 确保画面中有一个清晰可见的人。视频画面中应能实时、稳定地绘制出该人体的骨骼关键点和连接线。
TC_SKL_02骨骼检测多人骨骼检测1. 启动监控。 2. 确保画面中有多个清晰可见的人。系统应能同时检测并绘制多个人的骨骼(Mediapipe Pose默认只检测最显著的一个人,若需多人需调整或使用其他模型)。 (预期可能失败或只检测一人)
TC_SKL_03骨骼检测遮挡/姿态变化下的稳定性1. 启动监控。 2. 测试者在画面中做不同动作(行走、转身、部分遮挡)。在大部分情况下,骨骼点应能被持续跟踪和绘制,允许短时间丢失或跳动。严重遮挡或快速移动可能导致跟踪失败。
TC_ZONE_01危险区域管理绘制危险区域1. 点击“绘制危险区域”按钮。 2. 在视频画面上单击3个或更多点。 3. 双击完成绘制。1. 按钮文本变为“完成绘制”。 2. 单击时在画面上添加可见的顶点。 3. 双击后,形成闭合多边形,区域半透明填充,按钮文本恢复,状态提示恢复。
TC_ZONE_02危险区域管理清除危险区域1. 成功绘制一个危险区域后,“清除危险区域”按钮应启用。 2. 点击“清除危险区域”按钮。画面上的危险区域绘制消失,“清除危险区域”按钮禁用。
TC_ZONE_03危险区域管理无效区域绘制1. 点击“绘制危险区域”按钮。 2. 只点击1或2个点后双击。无法形成危险区域,无区域绘制显示。
TC_ZONE_04危险区域入侵单人入侵检测1. 定义一个危险区域。 2. 启动监控,确保“危险区域检测”已勾选。 3. 测试者走进危险区域。1. 危险区域填充颜色变为红色并闪烁。 2. 报警信息列表出现“警告:有人进入危险区域!”。 3. 若音频报警开启,播放对应的警笛声音效。 4. 视频上方出现警告覆盖层。
TC_ZONE_05危险区域入侵离开区域后警报解除1. 在触发区域入侵警报后,测试者离开危险区域。1. 危险区域恢复为橙色填充。 2. 音频警报停止(若正在播放)。 3. 警告覆盖层消失。 4. 报警信息列表保留记录。
TC_POS_01危险姿势检测弯腰过度检测1. 启动监控,确保“危险姿势检测”已勾选。 2. 测试者做出大幅度弯腰动作(角度超过60度)。1. 报警列表出现“警告:检测到弯腰过度!”。 2. 若音频报警开启,播放对应的蜂鸣声音效。 3. 视频上方出现警告覆盖层。
TC_POS_02危险姿势检测手臂高举检测1. 启动监控,确保“危险姿势检测”已勾选。 2. 测试者将一只或两只手举过头顶。1. 报警列表出现“警告:手臂举过头顶,可能存在危险!”。 2. 若音频报警开启,播放对应的扫频声音效。 3. 视频上方出现警告覆盖层。
TC_POS_03危险姿势检测身体失衡检测1. 启动监控,确保“危险姿势检测”已勾选。 2. 测试者大幅度向一侧倾斜身体,使左右肩高度差明显。1. 报警列表出现“警告:身体倾斜过度,姿势不平衡!”。 2. 若音频报警开启,播放对应的啁啾声音效。 3. 视频上方出现警告覆盖层。
TC_POS_04危险姿势检测蹲下姿势检测1. 启动监控,确保“危险姿势检测”已勾选。 2. 测试者做出明显的下蹲动作。1. 报警列表出现“警告:检测到蹲下姿势,可能存在危险!”。 2. 若音频报警开启,播放对应的低频双声蜂鸣音效。 3. 视频上方出现警告覆盖层。
TC_POS_05危险姿势检测奔跑姿势检测1. 启动监控,确保“危险姿势检测”已勾选。 2. 测试者在画面中做出奔跑动作(有明显跨步和/或手臂摆动/前倾)。1. 报警列表出现“警告:检测到奔跑姿势,请减速慢行!”。 2. 若音频报警开启,播放对应的快速警笛声音效。 3. 视频上方出现警告覆盖层。
TC_POS_06危险姿势检测正常动作不误报1. 启动监控,确保“危险姿势检测”已勾选。 2. 测试者进行正常站立、行走、小幅弯腰、正常坐下等动作。系统不应触发危险姿势报警。
TC_ALERT_01报警功能音频报警开关1. 触发任一危险警报。 2. 取消勾选“启用语音警报”。 3. 再次触发危险警报。 4. 重新勾选,再次触发。1. 取消勾选后,触发警报时不播放声音。 2. 重新勾选后,触发警报时恢复播放声音。
TC_ALERT_02报警功能报警信息列表与清除1. 多次触发不同类型的警报。 2. 点击“清除所有警报”按钮。1. 每次触发警报,列表顶部增加一条带时间戳的记录。 2. 点击清除按钮后,列表内容清空。
TC_ALERT_03报警功能报警冷却机制1. 测试者持续停留在危险区域内或保持危险姿势。音频警报应在首次触发后播放一次,然后在冷却时间(3秒)内不再重复播放同一类型警报,冷却时间过后若危险状态持续,可再次触发。
TC_UI_01用户界面界面响应与布局1. 启动程序,观察界面布局。 2. 调整窗口大小。 3. 操作各个按钮、复选框、下拉框。1. 界面元素布局合理,无重叠遮挡。 2. 调整窗口大小时,控件能自适应调整位置和大小。 3. 控件操作响应正常。
TC_PERF_01性能处理帧率 (FPS)1. 启动监控,观察FPS标签显示。 2. 在不同场景(单人、多人、复杂背景)下观察FPS。在测试硬件环境下,FPS应能稳定在15-30帧左右(具体数值依赖于摄像头分辨率、模型复杂度和硬件性能),保证基本的实时性。
TC_PERF_02性能CPU/内存占用1. 启动监控,使用系统任务管理器观察程序的CPU和内存占用率。CPU和内存占用应在合理范围内,不会导致系统卡顿。

6.3 测试结果与分析

(注:以下为模拟的测试结果与分析,实际测试结果会因环境和具体实现细节而异)

在上述测试环境下,对系统进行了全面的测试,主要结果如下:

  1. 基本功能:

    • 摄像头连接、启停、切换功能正常(TC_CAM_01, TC_CAM_02, TC_CAM_03)。
    • 单人骨骼检测准确,绘制流畅(TC_SKL_01)。多人检测方面,如预期,Mediapipe Pose标准模型主要跟踪画面中最显著的一个人(TC_SKL_02),在部分身体遮挡或快速移动时,骨骼点可能丢失或跳动,但通常能较快恢复跟踪(TC_SKL_03)。
    • 危险区域的交互式绘制、清除功能符合预期(TC_ZONE_01, TC_ZONE_02, TC_ZONE_03)。
    • 危险区域入侵检测灵敏,人员进入设定区域后能及时触发视觉和音频报警,离开后报警解除(TC_ZONE_04, TC_ZONE_05)。区域入侵判断阈值(2分)在大多数情况下表现良好。
  2. 危险姿势识别:

    • 弯腰过度: 对幅度较大的弯腰动作识别准确(TC_POS_01)。对于临界角度或动作幅度不大的情况,可能存在漏报。
    • 手臂高举: 能准确识别单手或双手举过头顶的动作(TC_POS_02)。
    • 身体失衡: 对明显的身体倾斜能正确报警(TC_POS_03)。阈值(15%帧高)相对宽松,对轻微倾斜不敏感。
    • 蹲下姿势: 对于较标准的下蹲动作识别效果较好(TC_POS_04)。但对于半蹲或动作变形的情况,膝盖角度和髋部高度比例的组合判断可能不够鲁棒。
    • 奔跑姿势: 能识别出有明显跨步和身体动态特征的奔跑动作(TC_POS_05)。但对于原地快速踏步或步幅较小的快走,可能存在漏报。手臂摆动特征的判断相对简单,可能受限于视角。
    • 误报情况: 在正常动作测试中(TC_POS_06),系统基本没有出现误报,表明设定的阈值和逻辑具有一定的区分度。但在某些复杂或非标准动作下,仍有误报的可能性。
  3. 报警功能:

    • 音频报警开关功能正常(TC_ALERT_01)。
    • 报警信息列表能正确记录带时间戳的报警信息,清除功能正常(TC_ALERT_02)。
    • 音频报警的冷却机制(3秒)有效,避免了蜂鸣般的重复报警(TC_ALERT_03)。本地生成的不同类型音效可以正常播放,并能有效区分不同危险类型。
  4. 用户界面与性能:

    • 用户界面布局清晰,控件响应正常,窗口大小调整时自适应良好(TC_UI_01)。中文显示正常。
    • 在i7+RTX3060环境下,使用640x480分辨率,系统FPS稳定在25-30帧;在M1 Pro环境下,FPS稳定在20-25帧左右,均满足实时性要求(TC_PERF_01)。更高分辨率或更复杂的模型会导致FPS下降。
    • CPU占用率在15-30%之间波动,内存占用约200-400MB,属于可接受范围(TC_PERF_02)。

6.4 测试结论与讨论

综合测试结果来看,本系统成功实现了设计目标,能够实时检测用户定义的危险区域入侵以及弯腰、举手、失衡、蹲下、奔跑等多种危险动作,并能触发及时的视觉和本地化音频报警。系统的用户界面友好,操作便捷,性能基本满足实时监控的需求。

存在的问题与局限性:

  1. 姿态识别局限性:

    • 2D信息限制: 系统完全基于2D图像进行分析,缺乏深度信息,对于某些姿态(如弯腰角度)的判断可能受限于观察视角。例如,正对或背对摄像头的弯腰可能难以准确计算角度。
    • 阈值依赖: 危险动作的判断很大程度上依赖于人工设定的阈值(角度、距离比例等)。这些阈值可能需要根据具体应用场景、摄像头架设角度、人员体型等因素进行调整才能达到最佳效果,通用性有待提高。
    • 动作复杂性: 对于非标准、不连续或过于快速的动作,识别准确率可能会下降。例如,蹲下和弯腰的区分,奔跑和快走的区分等。
    • 多人场景: 当前实现主要针对单人进行姿态分析,多人同时出现在画面中且相互遮挡时,危险检测的准确性会受到影响。
  2. 环境因素影响: 光照条件、背景复杂度、摄像头抖动、部分遮挡等因素仍可能影响骨骼检测的稳定性和准确性,进而影响危险判断。

  3. 区域入侵检测精度: 当前使用关键点判断入侵,当人体只有非关键部位(如小臂)进入区域时可能不会触发报警。可以考虑结合人体轮廓或边界框进行更全面的判断。

  4. 音频报警的有效性: 虽然实现了本地化和差异化音效,但在嘈杂环境下,音频报警的有效性可能会降低。

这些局限性为后续的系统优化和改进指明了方向。


7. 结论与展望

7.1 结论

本文围绕提高工作场所及特定场景安全监控智能化水平的目标,成功设计并实现了一套基于人体骨骼识别的危险动作报警系统。系统利用OpenCV获取实时视频流,通过Mediapipe Pose框架提取人体骨骼关键点,在此基础上实现了危险区域入侵检测以及对弯腰过度、手臂高举、身体失衡、蹲下、奔跑等多种危险动作的识别算法。为了提供及时有效的警示,系统集成了视觉报警(UI界面提示、警告覆盖层)和本地化、差异化的音频报警机制。系统的用户界面使用PySide6构建,提供了良好的交互性和易用性。

主要研究成果和贡献如下:

  1. 整合了多种危险场景检测: 系统不仅实现了常见的危险区域入侵检测,还针对性地设计并实现了多种典型危险工作姿势或异常行为的检测算法,扩展了监控系统的能力范围。
  2. 实现了本地化音频报警: 采用Numpy和Scipy生成不同模式的警告音效,摆脱了对网络TTS服务的依赖,提高了系统的独立性和响应速度,并设计了多后端播放策略以增强兼容性。
  3. 构建了完整的应用系统: 将底层的视觉算法、危险判断逻辑与上层的图形用户界面、报警机制有效结合,形成了一个功能完整、界面友好的原型系统。
  4. 注重实用性和兼容性: 解决了中文显示、跨平台字体加载、音频播放兼容性等实际开发中遇到的问题,提高了系统的实用价值。

通过系统测试,验证了系统在功能上的完整性和可行性,其在实时性、准确性方面基本达到设计预期,能够在一定程度上辅助安全管理人员及时发现潜在风险。

7.2 系统不足

尽管本系统取得了一定的成果,但仍存在一些不足之处,主要体现在:

  1. 二维分析的固有局限: 基于单目摄像头的二维骨骼信息无法完全反映三维空间中的人体姿态,容易受视角影响,导致某些情况下角度计算不准确或姿态误判。
  2. 算法鲁棒性有待提升: 对于光照剧变、严重遮挡、多人复杂交互等场景,骨骼检测的稳定性会下降,进而影响危险判断的准确率。危险动作识别的阈值设定偏向经验化,缺乏自适应能力。
  3. 危险动作类型有限: 当前系统仅能识别预定义的几种危险动作,对于跌倒、攀爬、打斗等其他常见危险行为尚未覆盖。
  4. 缺乏学习与适应能力: 系统基于固定的规则和阈值进行判断,无法根据历史数据或特定场景进行学习和优化,智能化程度有待提高。

7.3 展望

针对当前系统的不足和未来应用的需求,可以从以下几个方面进行改进和展望:

  1. 引入三维信息: 考虑使用双目摄像头或RGB-D深度摄像头(如Kinect, RealSense)获取深度信息,进行三维人体姿态估计,可以更准确地计算空间角度、距离,克服二维分析的局限性,提高姿态识别的准确性。
  2. 增强算法鲁棒性:
    • 研究更先进的骨骼识别算法或模型,提高在遮挡、多人场景下的检测效果。
    • 结合人体跟踪算法(如DeepSORT),在多人场景下为每个人分配ID,实现对个体行为的连续分析。
    • 探索使用机器学习或深度学习方法进行危险动作分类,替代或辅助基于规则的判断,提高模型的泛化能力和自适应性。例如,可以收集特定场景的危险动作数据,训练专门的分类器(如LSTM, GCN)。
  3. 扩展危险行为识别种类: 根据具体应用需求,增加对跌倒、攀爬、打斗、异常滞留等更多类型危险行为的识别算法。
  4. 优化人机交互与报警方式:
    • 允许用户更方便地调整危险判断的阈值。
    • 提供更丰富的报警方式,如联动声光报警器、发送短信或App通知给管理人员等。
    • 考虑引入危险等级评估,根据危险的严重程度采取不同的报警策略。
  5. 系统集成与云端化: 将系统部署到边缘计算设备上,实现更高效的前端处理。考虑将报警事件、统计数据等上传到云平台,进行集中的监控管理和数据分析。

总之,基于骨骼识别的危险动作报警系统具有广阔的应用前景。随着相关技术的不断进步,未来的系统将更加智能、准确、可靠,为保障人员安全发挥更重要的作用。本研究为此方向提供了一个基础性的实践探索和参考。


参考文献

(请在此处根据实际参考的文献资料,按照标准的参考文献格式列出,例如:)
[1] Google Developers. MediaPipe Pose [EB/OL]. https://developers.google.com/mediapipe/solutions/vision/pose_landmarker, 2023.
[2] Bradski, G. The OpenCV Library[J]. Dr. Dobb’s Journal of Software Tools, 2000.
[3] Cao Z, Hidalgo G, Simon T, et al. OpenPose: realtime multi-person 2D pose estimation using Part Affinity Fields[J]. IEEE transactions on pattern analysis and machine intelligence, 2019, 43(1): 172-186.
[4] Harris C R, Millman K J, van der Walt S J, et al. Array programming with NumPy[J]. Nature, 2020, 585(7825): 357-362.
[5] Virtanen P, Gommers R, Oliphant T E, et al. SciPy 1.0: fundamental algorithms for scientific computing in Python[J]. Nature methods, 2020, 17(3): 261-272.
[6] Qt for Python (PySide6) Documentation [EB/OL]. https://doc.qt.io/qtforpython/, 2023.
[7] … (其他相关技术、算法、应用场景的参考文献) …


致谢

(在此处填写致谢内容,感谢指导老师、同学、家人等在论文完成过程中给予的帮助和支持。)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2336423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PDF转换格式失败?原因及解决方法全解析

在日常工作中&#xff0c;我们经常会遇到将PDF转换为Word、Excel、PPT等格式的需求。有时候以为一键转换就能搞掂&#xff0c;没想到却转换失败。到底问题出在哪&#xff1f;别急&#xff0c;我们可以看看是否以下几个问题引起的&#xff0c;找到解决问题的关键&#xff01; 原…

模型提示词

一 提示词 &#xff08;一&#xff09; 提示词&#xff08;Prompt&#xff09;是用户发送给大语言模型的问题、指令或请求&#xff0c;** 1 来明确地告诉模型用户想要解决的问题或完成的任务&#xff0c;是大语言模型理解用户需求并据此生成相关、准确回答或内容的基础。对于…

Node.js 数据库 事务 项目示例

1、参考&#xff1a;JavaScript语言的事务管理_js 函数 事务性-CSDN博客 或者百度搜索&#xff1a;Nodejs控制事务&#xff0c; 2、实践 2.1、对于MySQL或MariaDB&#xff0c;你可以使用mysql或mysql2库&#xff0c;并结合Promise或async/await语法来控制事务。 使用 mysql2…

Qt开发:QFileInfo详解

文章目录 一、QFileInfo 简介二、常用的构造函数三、常用函数的介绍和使用四、常用静态函数的介绍和使用五、完整代码示例 一、QFileInfo 简介 QFileInfo 提供了一个对象化的方式&#xff0c;用于访问文件系统中单个文件的信息。它可以接受&#xff1a; 文件名字符串&#xff…

蓝桥杯常考排序

1.逆序 Collections.reverseOrder() 方法对列表进行逆序排序。通过 Collections.sort() 方法配合 Collections.reverseOrder()&#xff0c;可以轻松实现从大到小的排序。 import java.util.ArrayList; // 导入 ArrayList 类&#xff0c;用于创建动态数组 import java.util.C…

深度学习基础:从入门到理解核心概念

引言 近年来&#xff0c;深度学习(Deep Learning)已成为人工智能领域最热门的研究方向之一。从AlphaGo战胜人类围棋冠军&#xff0c;到ChatGPT等大型语言模型的惊艳表现&#xff0c;深度学习技术正在深刻改变我们的生活和工作方式。本文将系统介绍深度学习的基础知识&#xff0…

科技项目验收测试报告有哪些作用?需要多长时间和费用?

在当今快速发展的科技环境中&#xff0c;科技项目的有效验收至关重要。对于公司、开发团队以及客户来说&#xff0c;科技项目验收测试报告更是一个不可缺少的一项重要环节。 科技项目验收测试报告是对一个项目在开发完成后所进行的一系列测试结果的总结。这份报告不仅用于证明…

CCLinkIE转ModbusTCP借网关之力打破组态王与三菱PLC通讯隔阂​

在某自动化生产线项目中&#xff0c;客户采用了三菱PLC作为现场控制核心&#xff0c;该PLC支持CCLinkIE现场总线协议。同时&#xff0c;客户希望使用组态王上位机软件进行生产过程的监控与管理&#xff0c;然而组态王上位机更擅长与ModbusTCP协议设备进行通讯。为了解决这一协议…

Linux网络编程第一课:深入浅出TCP/IP协议簇与网络寻址系统

知识点1【网络发展简史】 **网络节点&#xff1a;**路由器和交换机组成 交换机的作用&#xff1a;拓展网络接口 路由&#xff1a;网络通信路径 1、分组交换 分组的目的&#xff1a; 数据量大&#xff0c;不能一次型传输&#xff0c;只能分批次传输&#xff0c;这里的每一批…

GESP2023年12月认证C++七级( 第三部分编程题(2)纸牌游戏)

参考程序&#xff1a; #include <iostream> #include <cstring> // for memset #include <vector> using namespace std;const int max_n 1005; int n; int a[max_n], b[max_n], c[max_n]; // a[]: 得分系数&#xff1b;b[]: 换牌惩罚&#xff1b;c[]: …

HarmonyOS学习 实验九:@State和@Prop装饰器的使用方法

HarmonyOS应用开发&#xff1a;父子组件状态管理实验报告 引言 在HarmonyOS应用开发领域&#xff0c;组件之间的状态管理是一个至关重要的概念。通过有效的状态管理&#xff0c;我们可以确保应用的数据流动清晰、可预测&#xff0c;从而提升应用的稳定性和可维护性。本次实验…

【Ai】MCP实战:手写 client 和 server [Python版本]

什么是mcp MCP 是一个开放协议&#xff0c;它为应用程序向 LLM 提供上下文的方式进行了标准化。你可以将 MCP 想象成 AI 应用程序的 USB-C 接口。就像 USB-C 为设备连接各种外设和配件提供了标准化的方式一样&#xff0c;MCP 为 AI 模型连接各种数据源和工具提供了标准化的接口…

Java与C在典型场景下的性能对比深度剖析

&#x1f381;个人主页&#xff1a;User_芊芊君子 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 &#x1f50d;系列专栏&#xff1a;AI 【前言】 在计算机编程领域&#xff0c;Java和C语言都是举足轻重的编程语言。Java以其跨平台性、自动内存管理和丰富…

多智能体 AI 游戏框架(开源程序):竞争、发展、适应

一、软件介绍 文末提供程序和源码下载 SamoAI 在人类和 AI 之间创建了一个无缝的多代理叙事层&#xff0c;实现了跨多个平台的自然协作。通过一致的身份保留和情境记忆&#xff0c;它允许通过一系列行动随着时间的推移而演变的交互&#xff0c;就像人际关系一样。 二、核心概念…

java实现二叉树的前序、中序、后序遍历(递归和非递归方式)以及层级遍历

java实现二叉树的前序、中序、后序遍历以及层级遍历 一、二叉树节点定义二、递归方式1.前序遍历2.中序遍历3.后序遍历 三、非递归方式1.前序遍历2.中序遍历3.后序遍历4.层级遍历5.分层打印 四、测试用例 一、二叉树节点定义 class TreeNode {int val;TreeNode left;TreeNode r…

Solr admin 更新文档

<add><doc><field name"id">1904451090351546368</field><field name"companyName" update"set">测试科技有限公司</field></doc> </add>

【Netty篇】EventLoopGroup 与 EventLoop 详解

目录 开场白&#xff1a;话说 Netty 江湖第一段&#xff1a;EventLoopGroup——“包工头”的角色第二段&#xff1a;EventLoop——“身怀绝技的工人”第三段&#xff1a;EventLoop 如何处理 I/O 事件、普通任务和定时任务第四段&#xff1a;Handler 执行中如何换人&#xff1f;…

操作系统之shell实现(上)

&#x1f31f; 各位看官好&#xff0c;我是maomi_9526&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f680; 今天来学习C语言的相关知识。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更…

数据结构与算法——链表OJ题详解(2)

文章目录 一、前言二、OJ续享2.1相交链表2.2环形链表12.2环形链表2 三、总结 一、前言 哦了兄弟们&#xff0c;咱们上次在详解链表OJ题的时候&#xff0c;有一部分OJ题呢up并没有整理完&#xff0c;这一个星期呢&#xff0c;up也是在不断的学习并且沉淀着&#xff0c;也是终于…

Linux 基础知识详解

Linux 基础知识详解 一、快照与克隆 1. &#x1f4f8;快照&#xff08;Snapshot&#xff09; 快照是虚拟机当前运行状态的一次“瞬间拷贝”&#xff0c;包括内存、磁盘、配置等信息。这使得管理员能够快速恢复到某个特定的时间点。 用途&#xff1a; 安全实验前保存状态&am…