删除多余文件

luogang_branch
AetherPendragon 2 weeks ago
parent 704b32335b
commit a328093dbb

@ -1,250 +0,0 @@
# 智途投送系统 — UML 设计规格说明书
> 本文档配合 `uml_diagrams.drawio` 使用,共包含 6 张 UML 图覆盖「声源分析模块」和「单兵终端APP」两个核心子系统。
> 所有 UML 图均按《软件体系结构》课程规范绘制,可直接导入 draw.iodiagrams.net编辑。
---
## 📁 文件说明
| 文件名 | 说明 |
|--------|------|
| `uml_diagrams.drawio` | draw.io 源文件6 个 Page直接拖拽到 https://app.diagrams.net 即可打开 |
| `UML图设计说明.md` | 本文件,解释每张图的设计意图和体系结构映射 |
---
## 图1类图 — 声源分析模块核心类结构
### 为什么画这张图?
**类图Class Diagram** 是面向对象设计的核心静态结构图。软件设计规格说明书SDS必须包含类图因为它
- 展示系统的**静态结构**——有哪些类、类的属性和方法
- 展示类之间的关系——**组合、依赖、继承**
- 是编码实现的直接依据,体现了「从设计到代码」的映射
### 这张图展示了什么设计思路?
#### 1. 组合关系Composition体现「管道-过滤器」架构
- `Pipeline` 通过 **组合**(菱形实心箭头)持有 6 个核心子模块:
- `AudioBuffer`(音频循环缓冲)
- `FeatureExtractor`Mel 频谱特征提取)
- `GunshotClassifier`(枪声分类器)
- `GccPhatLocalizer`GCC-PHAT 声源定位)
- `DistanceEstimator`SPL 距离估计)
- `ThreatTracker`(威胁跟踪去重)
> **设计意图**:组合关系表明这些子模块的生命周期由 `Pipeline` 管理,`Pipeline` 销毁时子模块也随之销毁。这与「管道-过滤器」架构中「过滤器由管道统一管理」的思想一致。
#### 2. PIMPL 惯用法的信息隐藏
- `Pipeline``FeatureExtractor` 的私有属性只有 `-impl_: Impl*`,真正的实现细节被隐藏在 `.cpp` 文件中。
> **设计意图**:类图只暴露公共接口(`Process`、`FromYaml`、`Reset`),隐藏实现细节。这符合**信息隐藏原则**Information Hiding降低模块间的耦合度提升可修改性。
#### 3. 跨平台分层设计
- `AcousticNode`ROS 层)依赖 `Pipeline`(业务层),但不直接依赖 `AudioBuffer` 等核心类
- `WavFileSource` 作为独立 IO 类,可被 `AcousticNode` 直接组合
> **设计意图**:通过引入独立的 IO 适配层,核心算法与数据源解耦。同一套算法既可以读取 WAV 文件做离线测试,也可以接收 ROS Topic 做在线推理。
---
## 图2顺序图 — Pipeline::Process 音频处理调用链
### 为什么画这张图?
**顺序图Sequence Diagram** 展示对象之间的**动态交互**和**消息时序**。在 SDS 中,顺序图用于:
- 验证类图设计的可行性——类之间能否协作完成业务
- 展示关键用例的详细流程——一次音频分析涉及哪些对象、调用顺序如何
- 发现设计缺陷——是否存在循环依赖、重复调用、性能瓶颈
### 这张图展示了什么设计思路?
#### 1. 同步阻塞调用链确保数据一致性
`AcousticNode``Pipeline::Process` → 各子模块的方法调用都是**同步阻塞**的(实线箭头)。
> **设计意图**:单线程同步执行避免了多线程竞争条件和锁开销,对于「实时音频流处理」场景,简化了并发控制,确保了数据在流水线中的顺序一致性。
#### 2. 严格的单向数据流
消息严格从上到下传递,不存在对象 A 调用 B、B 又回调 A 的循环。
> **设计意图**:符合管道-过滤器架构「无环」的约束。数据从源端(麦克风)单向流向汇点(威胁事件),每个过滤器只接收上游输出、产生下游输入,不反向依赖。
#### 3. 返回值与发布解耦
`Pipeline::Process` 返回 `AcousticFrame` 后,`AcousticNode` 再调用 `ThreatPublisher::Publish`
> **设计意图**「计算」与「通信」分离。Pipeline 只负责纯计算逻辑,不关心 ROS 通信细节AcousticNode 作为适配层,负责将计算结果转换为 ROS 消息发布。这实现了**关注点分离**Separation of Concerns
---
## 图3组件图 — 声源分析模块分层组件结构
### 为什么画这张图?
**组件图Component Diagram** 展示系统的**物理/逻辑组件**及其依赖关系。在 SDS 中,组件图用于:
- 展示系统的模块化划分——系统由哪些可替换的组件构成
- 展示组件依赖——哪个组件依赖哪个组件、依赖哪些外部库
- 指导构建系统——CMake/Makefile 的模块划分依据
### 这张图展示了什么设计思路?
#### 1. 三层组件隔离
| 组件 | 职责 | 可替换性 |
|------|------|---------|
| `acoustic_analyzer_core` | 核心算法,平台无关 | 可在任何操作系统编译 |
| `acoustic_analyzer_io` | 音频输入适配 | 新增音频源只需扩展此层 |
| `acoustic_analyzer_ros` | ROS 集成包装 | 换用 DDS 时只需替换此层 |
> **设计意图**:严格的分层使得「替换 ROS 框架」或「新增音频源」不会影响核心算法。这是**分层架构**的核心价值——修改局部,不影响全局。
#### 2. 依赖外部库通过「虚线箭头」标注
- `core` → ONNX Runtime模型推理
- `core` → Eigen 3.4.0(矩阵运算)
- `core` → yaml-cpp配置解析
- `ros` → ROS (roscpp)(通信框架)
> **设计意图**:虚线箭头表示「依赖/使用」关系UML 中的 `«use»`)。这些外部库是第三方组件,不是自研代码,但需要明确标注以说明编译依赖和环境要求。
#### 3. 接口抽象IAcousticSource
`acoustic_analyzer_io` 组件向外暴露 `IAcousticSource` 接口,`core` 层通过该接口获取音频数据。
> **设计意图**依赖倒置原则DIP——高层模块core不依赖低层模块的具体实现WavFileSource而是依赖抽象接口。这支持了「开闭原则」新增音频源不需要修改 core 代码。
---
## 图4用例图 — 单兵终端APP功能需求
### 为什么画这张图?
**用例图Use Case Diagram** 是需求分析阶段的核心产物,在 SDS 中用于:
- 从**用户视角**描述系统功能边界——系统能做什么、为谁做
- 识别参与者Actor和用例Use Case的交互关系
- 作为功能验收的基准——每个用例对应一个可测试的场景
### 这张图展示了什么设计思路?
#### 1. 明确的系统边界
用一个大矩形框住所有用例标注「单兵终端APP」表示这是系统的功能边界。框外的「前线士兵」是参与者框内是用例。
> **设计意图**软件体系结构设计的第一步是定义系统边界。用例图清晰地表达了「单兵终端APP的职责范围」——它不负责无人机控制、不负责路径规划计算只负责「前端交互和信息上报」。
#### 2. `«include»` 关系:必要子流程
「上报物资需求」`«include»`「选择投放点」。
> **设计意图**include 表示被包含用例是主用例的**必要组成部分**。士兵上报需求时,**必须**选择投放点,这不是可选的。这对应了代码中 `submitDemand()` 必须调用投放点选择逻辑。
#### 3. `«extend»` 关系:可选扩展
「SOS一键求救」`«extend»`「实时位置上报」。
> **设计意图**extend 表示扩展用例在特定条件下才会触发。SOS 求救时,系统自动触发位置上报(扩展行为),但位置上报本身也可以独立运行。这对应了代码中 `triggerSOS()` 内部调用 `LocationModule.getCurrentPosition()` 的设计。
---
## 图5活动图 — 物资需求上报业务流程
### 为什么画这张图?
**活动图Activity Diagram** 描述业务过程或操作的工作流。在 SDS 中,活动图用于:
- 展示用例的**内部流程**——用例图只说了「能做什么」,活动图说「怎么做」
- 识别分支、合并、并发——哪些步骤有判断条件、哪些可以并行
- 发现异常路径——失败时如何处理、是否有回退机制
### 这张图展示了什么设计思路?
#### 1. 支持多种投放点选择模式
从「查看推荐投放点」分支到「地图选点/搜索」:
> **设计意图**:活动图展示了投放点选择的两种入口——列表推荐(后端计算)和地图自由选点(高德 API。这体现了**灵活性**设计:既给士兵提供「一键选择安全点」的便捷,也支持「自定义精确位置」的精细化需求。
#### 2. 失败处理与降级策略
在「提交成功?」判断分支:
- **成功分支**:显示成功提示,跳转首页
- **失败分支**:显示错误提示,**支持重试**
> **设计意图**:体现了**可用性**质量属性。APP 在网络不稳定时不会崩溃,而是给出明确反馈并提供重试路径。代码中 `API.postDemand()` 被 try-catch 包裹,且内置 Mock 数据保证演示可用性。
#### 3. 回退路径Cancel → Return
如果士兵点击「否」(不确认提交),流程回退到「选择物资类型」步骤。
> **设计意图**:活动图中的回退边(从 Cancel 回到 Type展示了系统的**容错性**。用户可以在提交前任意步骤返回修改,不会丢失已填写的信息(因为状态保存在内存中)。
---
## 图6部署图 — 系统物理部署拓扑
### 为什么画这张图?
**部署图Deployment Diagram** 展示系统的**物理节点**和**运行时部署**。在 SDS 中,部署图用于:
- 展示软件构件运行在哪些硬件/操作系统上
- 展示节点间的通信协议和网络拓扑
- 指导运维部署——需要哪些机器、装什么系统、开放哪些端口
### 这张图展示了什么设计思路?
#### 1. 三层物理分离
| 节点 | 部署位置 | 运行环境 |
|------|---------|---------|
| 士兵手机 | 前线 | Android 12+ / Capacitor WebView |
| 后方服务器 | 指挥所 | Ubuntu 22.04 / Python Flask |
| 无人机机载计算机 | 无人机 | Ubuntu 20.04 / ROS Noetic |
> **设计意图**:物理分离对应了**逻辑子系统分离**。单兵APP、后勤系统、无人机软件分别运行在不同硬件上通过定义好的网络协议通信。这种分离确保了「前线设备轻量化」手机即可、「后端集中化」服务器统一管理、「机载实时化」ROS 硬实时)。
#### 2. 服务器作为「消息中转站」
单兵APP 不直接与无人机通信,而是通过 HTTP/REST 与服务器交互,服务器再通过 ROS/WebSocket 与无人机交互。
> **设计意图**:这是**中介者模式**Mediator Pattern在物理层的体现。服务器作为中介者解耦了前线士兵与无人机控制
> - 士兵不需要知道无人机的 IP 地址或 ROS 网络配置
> - 无人机不需要暴露接口给外部互联网
> - 服务器可以做身份认证、日志审计、任务调度
#### 3. 可选直连通道(虚线)
单兵APP → 无人机之间有一条虚线标注「rosbridge可选直连」。
> **设计意图**:虚线表示「非必须但支持的通信路径」。在演示或局域网环境下,前端可以通过 rosbridge 直接订阅无人机状态,绕过 Flask 后端,降低延迟。这是**灵活性**与**安全性**的权衡——平时走服务器(安全),紧急时直连(快速)。
---
## 📊 六张图的体系结构映射总结
| UML 图 | 视角 | 体系结构知识点 | 在我们项目中的体现 |
|--------|------|--------------|-----------------|
| **类图** | 静态结构 | 信息隐藏、组合复用 | Pipeline 组合 6 个子模块PIMPL 隐藏实现 |
| **顺序图** | 动态交互 | 管道-过滤器约束 | 同步阻塞调用链,单向无环数据流 |
| **组件图** | 模块组织 | 分层架构、依赖倒置 | core/io/ros 三层,依赖抽象接口 |
| **用例图** | 用户功能 | 系统边界、include/extend | 单兵APP功能边界SOS扩展位置上报 |
| **活动图** | 业务流程 | 可用性、容错设计 | 失败重试、回退路径、Mock降级 |
| **部署图** | 物理拓扑 | 中介者模式、物理分离 | 服务器中转、三层硬件分离、可选直连 |
---
## 🎯 汇报建议
下节课汇报时,建议按以下顺序展示这 6 张图:
1. **先放部署图图6** —— 让听众快速理解「系统由哪些部分组成、运行在哪里」
2. **再放用例图图4** —— 说明「单兵终端APP能做什么、为谁服务」
3. **聚焦声源分析模块放类图图1** —— 展示核心类的静态结构
4. **用顺序图图2解释类如何协作** —— 动态验证静态设计的可行性
5. **用组件图图3总结分层设计** —— 强调跨平台可移植性和可替换性
6. **用活动图图5展示单兵APP的典型流程** —— 以「物资需求上报」为例,说明用户体验设计
> 每张图讲 1-2 分钟即可,重点突出「**这张图对应课程中的哪个知识点**」以及「**我们在实践中如何体现这个知识点**」。

@ -1,591 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
生成 draw.io (diagrams.net) 可用的 UML XML 文件
包含 6 张图类图顺序图组件图用例图活动图部署图
"""
import xml.etree.ElementTree as ET
import uuid
import html
def make_mxfile(pages):
root = ET.Element("mxfile")
root.set("host", "app.diagrams.net")
root.set("modified", "2026-05-19T00:00:00.000Z")
root.set("agent", "PythonScript")
root.set("version", "24.0.0")
root.set("type", "device")
root.set("pages", str(len(pages)))
for i, (name, graph_model) in enumerate(pages):
diag = ET.SubElement(root, "diagram")
diag.set("name", name)
diag.set("id", str(uuid.uuid4()))
diag.append(graph_model)
return root
def make_graph_model(cells, w=1200, h=900):
gm = ET.Element("mxGraphModel")
gm.set("dx", "1434")
gm.set("dy", "780")
gm.set("grid", "1")
gm.set("gridSize", "10")
gm.set("guides", "1")
gm.set("tooltips", "1")
gm.set("connect", "1")
gm.set("arrows", "1")
gm.set("fold", "1")
gm.set("page", "1")
gm.set("pageScale", "1")
gm.set("pageWidth", str(w))
gm.set("pageHeight", str(h))
gm.set("math", "0")
gm.set("shadow", "0")
root = ET.SubElement(gm, "root")
ET.SubElement(root, "mxCell", {"id":"0"})
ET.SubElement(root, "mxCell", {"id":"1", "parent":"0"})
for cell in cells:
root.append(cell)
return gm
def cell_style(shape, extras=""):
base = {
"class": "swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;",
"class_attr": "text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;",
"actor": "shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;",
"usecase": "ellipse;whiteSpace=wrap;html=1;",
"boundary": "shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;",
"rectangle": "rounded=0;whiteSpace=wrap;html=1;",
"rounded": "rounded=1;whiteSpace=wrap;html=1;",
"lifeline": "shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;",
"activation": "shape=umlDestroy;whiteSpace=wrap;html=1;strokeWidth=3;",
"activation2": "rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;",
"component": "shape=component;align=left;spacingLeft=36;verticalAlign=top;whiteSpace=wrap;html=1;",
"interface": "shape=providedRequiredInterface;verticalAlign=top;spacingTop=0;whiteSpace=wrap;html=1;",
"start": "ellipse;whiteSpace=wrap;html=1;fillColor=#000000;",
"end": "ellipse;whiteSpace=wrap;html=1;fillColor=#000000;strokeColor=#ff0000;",
"activity": "rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;",
"decision": "rhombus;whiteSpace=wrap;html=1;fillColor=#ffffcc;strokeColor=#b3b3b3;",
"node": "shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;",
"artifact": "shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;",
"arrow": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;",
"dashed_arrow": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;",
"open_arrow": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;",
"diamond": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;",
"async": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;endArrow=open;endFill=0;",
"message": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;",
"return": "edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;verticalAlign=bottom;",
"text": "text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;",
"title": "text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1",
}
s = base.get(shape, shape)
if extras:
s += extras
return s
def add_cell(cells, cid, parent="1", style="", value="", x=0, y=0, w=0, h=0, source=None, target=None, edge=False):
attrs = {"id": str(cid), "parent": str(parent), "style": style, "value": value}
if edge:
attrs["edge"] = "1"
if source: attrs["source"] = str(source)
if target: attrs["target"] = str(target)
else:
attrs["vertex"] = "1"
if x is not None: attrs["x"] = str(x)
if y is not None: attrs["y"] = str(y)
if w is not None: attrs["width"] = str(w)
if h is not None: attrs["height"] = str(h)
cell = ET.Element("mxCell", attrs)
if edge:
geo = ET.SubElement(cell, "mxGeometry", {"relative":"1", "as":"geometry"})
if source and target:
ET.SubElement(geo, "Array", {"as":"points"})
else:
geo = ET.SubElement(cell, "mxGeometry", {"x":str(x), "y":str(y), "width":str(w), "height":str(h), "as":"geometry"})
cells.append(cell)
return cid
_class_id_counter = 1000
def add_uml_class(cells, cid, name, attrs, methods, x, y, w=200, h=None):
global _class_id_counter
line_h = 18
attr_h = len(attrs) * line_h if attrs else line_h
meth_h = len(methods) * line_h if methods else line_h
sep = 6
total_h = 26 + attr_h + sep + meth_h + 10
if h and h > total_h:
total_h = h
# class box
add_cell(cells, cid, "1", cell_style("class"), name, x, y, w, total_h)
# separator line
_class_id_counter += 1
add_cell(cells, f"{cid}_sep1", cid, cell_style("rectangle","fillColor=none;strokeColor=none;"), "", 0, 26, w, 0)
# attrs
ay = 26
for i, a in enumerate(attrs):
_class_id_counter += 1
add_cell(cells, f"{cid}_attr{i}", cid, cell_style("class_attr"), a, 0, ay, w, line_h)
ay += line_h
if not attrs:
_class_id_counter += 1
add_cell(cells, f"{cid}_attr0", cid, cell_style("class_attr"), "", 0, ay, w, line_h)
ay += line_h
# separator
_class_id_counter += 1
add_cell(cells, f"{cid}_sep2", cid, cell_style("rectangle","fillColor=none;strokeColor=none;"), "", 0, ay, w, 0)
ay += sep
# methods
for i, m in enumerate(methods):
_class_id_counter += 1
add_cell(cells, f"{cid}_meth{i}", cid, cell_style("class_attr"), m, 0, ay, w, line_h)
ay += line_h
if not methods:
_class_id_counter += 1
add_cell(cells, f"{cid}_meth0", cid, cell_style("class_attr"), "", 0, ay, w, line_h)
return cid
# ============================================================
# 图1类图 — 声源分析模块
# ============================================================
def build_class_diagram():
cells = []
# title
add_cell(cells, "title1", "1", cell_style("title"),
"图1 类图 — 声源分析模块核心类结构(静态视角)", 20, 10, 600, 30)
# Pipeline (center)
add_uml_class(cells, "Pipeline", "Pipeline",
["-impl_: Impl*"],
["+Process(audio): AcousticFrame", "+FromYaml(path): PipelineConfig", "+Reset()", "+Config(): const PipelineConfig&"],
420, 80, 260, 160)
# AudioBuffer
add_uml_class(cells, "AudioBuffer", "AudioBuffer",
["-capacity_frames_: size_t", "-num_channels_: size_t", "-buffer_: vector<float>", "-head_, tail_, size_: size_t"],
["+Push(samples): size_t", "+Pop(n): vector<float>", "+Get(off,n): vector<float>", "+Size(): size_t", "+Clear()"],
40, 80, 200, 170)
# FeatureExtractor
add_uml_class(cells, "FeatureExtractor", "FeatureExtractor",
["-impl_: Impl*"],
["+MelSpectrogram(audio): MatrixXf", "+MelSpectrogramMultiChannel(audio,n): vector<MatrixXf>"],
40, 300, 220, 110)
# GunshotClassifier
add_uml_class(cells, "GunshotClassifier", "GunshotClassifier",
["-session_: Ort::Session*", "-env_: Ort::Env*", "-labels_: vector<string>"],
["+Predict(mel): pair<string,float>", "+Labels(): const vector<string>&"],
300, 300, 220, 110)
# GccPhatLocalizer
add_uml_class(cells, "GccPhatLocalizer", "GccPhatLocalizer",
["-mic_config_: MicArrayConfig", "-sample_rate_: int", "-max_tdoa_: float"],
["+Localize(audio_mat): pair<float,float>"],
560, 300, 200, 90)
# DistanceEstimator
add_uml_class(cells, "DistanceEstimator", "DistanceEstimator",
["-config_: DistanceConfig", "-kalman_state_: float"],
["+ComputeSpl(audio): float", "+Estimate(spl,label): float", "+UpdateKalman(d): float", "+Reset()"],
800, 300, 220, 110)
# ThreatTracker
add_uml_class(cells, "ThreatTracker", "ThreatTracker",
["-min_interval_: float", "-history_: vector<AcousticThreat>"],
["+Update(threats): vector<AcousticThreat>", "+Reset()"],
800, 80, 200, 90)
# AcousticNode (ROS)
add_uml_class(cells, "AcousticNode", "AcousticNode (ROS层)",
["-nh_, pnh_: NodeHandle", "-pipeline_: Pipeline*", "-source_type_: string"],
["+run()", "-on_mic_array_audio(msg)", "-process_wav_source()", "-load_params()"],
40, 480, 240, 120)
# WavFileSource
add_uml_class(cells, "WavFileSource", "WavFileSource",
["-file_path_: string", "-sample_rate_: int", "-num_channels_: int"],
["+open(): bool", "+read(audio,n): size_t", "+num_channels(): int"],
340, 480, 200, 100)
# Types
add_uml_class(cells, "AcousticThreat", "AcousticThreat (struct)",
["+timestamp: Timestamp", "+threat_id: string", "+sound_type: string", "+confidence: float", "+azimuth: float", "+elevation: float", "+distance: float"],
[],
600, 480, 220, 130)
# PipelineConfig
add_uml_class(cells, "PipelineConfig", "PipelineConfig (struct)",
["+sample_rate: uint32_t", "+chunk_duration: float", "+n_mels: uint32_t", "+classifier: ClassifierConfig", "+mic_array: MicArrayConfig", "+distance: DistanceConfig"],
[],
860, 480, 220, 130)
# Relationships (edges)
# Pipeline --diamond--> components
add_cell(cells, "e1", "1", cell_style("diamond"), "", edge=True, source="Pipeline", target="AudioBuffer")
add_cell(cells, "e2", "1", cell_style("diamond"), "", edge=True, source="Pipeline", target="FeatureExtractor")
add_cell(cells, "e3", "1", cell_style("diamond"), "", edge=True, source="Pipeline", target="GunshotClassifier")
add_cell(cells, "e4", "1", cell_style("diamond"), "", edge=True, source="Pipeline", target="GccPhatLocalizer")
add_cell(cells, "e5", "1", cell_style("diamond"), "", edge=True, source="Pipeline", target="DistanceEstimator")
add_cell(cells, "e6", "1", cell_style("diamond"), "", edge=True, source="Pipeline", target="ThreatTracker")
# AcousticNode --> Pipeline
add_cell(cells, "e7", "1", cell_style("open_arrow"), "", edge=True, source="AcousticNode", target="Pipeline")
# AcousticNode --> WavFileSource
add_cell(cells, "e8", "1", cell_style("open_arrow"), "", edge=True, source="AcousticNode", target="WavFileSource")
# Pipeline --> PipelineConfig (dependency)
add_cell(cells, "e9", "1", cell_style("dashed_arrow"), "uses", edge=True, source="Pipeline", target="PipelineConfig")
# ThreatTracker --> AcousticThreat
add_cell(cells, "e10", "1", cell_style("open_arrow"), "", edge=True, source="ThreatTracker", target="AcousticThreat")
# Labels for relationships
add_cell(cells, "l1", "1", cell_style("text"), "组合", 230, 90, 50, 20)
add_cell(cells, "l2", "1", cell_style("text"), "组合", 230, 310, 50, 20)
add_cell(cells, "l3", "1", cell_style("text"), "组合", 520, 310, 50, 20)
add_cell(cells, "l4", "1", cell_style("text"), "组合", 780, 310, 50, 20)
add_cell(cells, "l5", "1", cell_style("text"), "组合", 780, 100, 50, 20)
return make_graph_model(cells, w=1200, h=700)
# ============================================================
# 图2顺序图 — Pipeline::Process 调用链
# ============================================================
def build_sequence_diagram():
cells = []
add_cell(cells, "title2", "1", cell_style("title"),
"图2 顺序图 — Pipeline::Process 音频处理调用链(动态视角)", 20, 10, 700, 30)
# Lifelines
lx = 60
lifelines = [
("AcousticNode", lx),
("Pipeline", lx+180),
("AudioBuffer", lx+340),
("FeatureExtractor", lx+500),
("GunshotClassifier", lx+660),
("GccPhatLocalizer", lx+820),
("DistanceEstimator", lx+980),
("ThreatTracker", lx+1140),
("ThreatPublisher", lx+1300),
]
for name, x in lifelines:
add_cell(cells, f"ll_{name}", "1", cell_style("lifeline"), name, x, 60, 100, 520)
# Messages
y = 100
def msg(cid, src, tgt, text, ypos, dashed=False):
style = cell_style("return") if dashed else cell_style("message")
add_cell(cells, cid, "1", style, text, edge=True, source=f"ll_{src}", target=f"ll_{tgt}")
# label
add_cell(cells, f"{cid}_lab", "1", cell_style("text"), text,
(lifelines_dict[src] + lifelines_dict[tgt])//2 - 60, ypos-15, 120, 20)
lifelines_dict = {name:x for name,x in lifelines}
msg("m1", "AcousticNode", "Pipeline", "Process(audio_samples)", y)
y += 50
msg("m2", "Pipeline", "AudioBuffer", "Push(samples)", y)
y += 40
msg("m3", "Pipeline", "AudioBuffer", "Get(offset, chunk)", y)
y += 40
msg("m4", "Pipeline", "FeatureExtractor", "MelSpectrogramMultiChannel(...)", y)
y += 40
msg("m5", "Pipeline", "GunshotClassifier", "Predict(avg_mel)", y)
y += 40
msg("m6", "Pipeline", "GccPhatLocalizer", "Localize(audio_mat)", y)
y += 40
msg("m7", "Pipeline", "DistanceEstimator", "Estimate(spl, label)", y)
y += 40
msg("m8", "Pipeline", "ThreatTracker", "Update(threat)", y)
y += 40
msg("m9", "Pipeline", "AcousticNode", "return AcousticFrame", y, dashed=True)
y += 40
msg("m10", "AcousticNode", "ThreatPublisher", "Publish(frame)", y)
# activation bars (simplified as small rectangles)
for name, x in lifelines:
add_cell(cells, f"act_{name}", "1", cell_style("activation2"), "", x+40, 100, 20, y-60)
# note
add_cell(cells, "note1", "1", cell_style("boundary"),
"设计思路:每个调用都是同步阻塞调用,数据沿调用链逐层传递。这种设计确保了单线程内数据一致性,简化了实时系统的并发控制。",
40, y+40, 400, 60)
return make_graph_model(cells, w=1500, h=700)
# ============================================================
# 图3组件图 — 声源分析模块分层
# ============================================================
def build_component_diagram():
cells = []
add_cell(cells, "title3", "1", cell_style("title"),
"图3 组件图 — 声源分析模块分层组件结构", 20, 10, 600, 30)
# Core component
add_cell(cells, "comp_core", "1", cell_style("component"),
"«component»\nacoustic_analyzer_core\n\n• AudioBuffer\n• FeatureExtractor\n• GunshotClassifier\n• GccPhatLocalizer\n• DistanceEstimator\n• ThreatTracker\n• Pipeline",
80, 80, 220, 200)
# IO component
add_cell(cells, "comp_io", "1", cell_style("component"),
"«component»\nacoustic_analyzer_io\n\n• AudioSource (interface)\n• WavFileSource\n• MobilePhoneSource",
80, 320, 220, 120)
# ROS component
add_cell(cells, "comp_ros", "1", cell_style("component"),
"«component»\nacoustic_analyzer_ros\n\n• AcousticNode\n• ThreatPublisher",
400, 80, 200, 100)
# External libraries
add_cell(cells, "ext_onnx", "1", cell_style("artifact"),
"«library»\nONNX Runtime", 400, 220, 140, 60)
add_cell(cells, "ext_eigen", "1", cell_style("artifact"),
"«library»\nEigen 3.4.0", 400, 300, 140, 60)
add_cell(cells, "ext_yaml", "1", cell_style("artifact"),
"«library»\nyaml-cpp", 400, 380, 140, 60)
add_cell(cells, "ext_ros", "1", cell_style("artifact"),
"«framework»\nROS (roscpp)", 680, 80, 140, 60)
# Dependencies
add_cell(cells, "d1", "1", cell_style("open_arrow"), "", edge=True, source="comp_ros", target="comp_core")
add_cell(cells, "d2", "1", cell_style("open_arrow"), "", edge=True, source="comp_ros", target="comp_io")
add_cell(cells, "d3", "1", cell_style("dashed_arrow"), "uses", edge=True, source="comp_core", target="ext_onnx")
add_cell(cells, "d4", "1", cell_style("dashed_arrow"), "uses", edge=True, source="comp_core", target="ext_eigen")
add_cell(cells, "d5", "1", cell_style("dashed_arrow"), "uses", edge=True, source="comp_core", target="ext_yaml")
add_cell(cells, "d6", "1", cell_style("dashed_arrow"), "uses", edge=True, source="comp_ros", target="ext_ros")
# Interface port
add_cell(cells, "iface1", "1", cell_style("interface"), "IAcousticSource", 300, 350, 40, 30)
add_cell(cells, "d7", "1", cell_style("open_arrow"), "", edge=True, source="comp_io", target="iface1")
add_cell(cells, "d8", "1", cell_style("open_arrow"), "", edge=True, source="iface1", target="comp_core")
# note
add_cell(cells, "note3", "1", cell_style("boundary"),
"设计思路core 层仅依赖第三方数学库Eigen/ONNX完全不依赖 ROS 和 yaml-cpp。\n这使得核心算法可以在 Windows/Linux 上独立编译测试,实现跨平台复用。",
680, 200, 320, 70)
return make_graph_model(cells, w=1100, h=520)
# ============================================================
# 图4用例图 — 单兵终端APP
# ============================================================
def build_usecase_diagram():
cells = []
add_cell(cells, "title4", "1", cell_style("title"),
"图4 用例图 — 单兵终端APP功能需求用户视角", 20, 10, 600, 30)
# Actor
add_cell(cells, "actor", "1", cell_style("actor"), "前线士兵", 60, 200, 40, 80)
# System boundary
add_cell(cells, "boundary", "1", cell_style("rectangle","fillColor=#f5f5f5;strokeColor=#666;"),
"单兵终端APP", 150, 80, 500, 420)
usecases = [
("UC1", "登录认证", 260, 120),
("UC2", "上报物资需求", 400, 120),
("UC3", "选择投放点", 260, 200),
("UC4", "查看任务状态", 400, 200),
("UC5", "查看无人机状态", 260, 280),
("UC6", "实时位置上报", 400, 280),
("UC7", "SOS一键求救", 260, 360),
("UC8", "服务器配置", 400, 360),
]
for uid, label, x, y in usecases:
add_cell(cells, uid, "1", cell_style("usecase"), label, x, y, 120, 50)
# Include / Extend relationships
add_cell(cells, "inc1", "1", cell_style("dashed_arrow"), "«include»", edge=True, source="UC2", target="UC3")
add_cell(cells, "ext1", "1", cell_style("dashed_arrow"), "«extend»", edge=True, source="UC7", target="UC6")
# Actor connections
for uid in ["UC1","UC2","UC3","UC4","UC5","UC6","UC7","UC8"]:
add_cell(cells, f"a_{uid}", "1", cell_style("arrow"), "", edge=True, source="actor", target=uid)
# note
add_cell(cells, "note4", "1", cell_style("boundary"),
"设计思路:用例图从用户视角描述了系统功能边界。\n"+
"「上报物资需求」包含「选择投放点」(必须),\n"+
"「SOS求救」扩展「实时位置上报」自动触发位置发送",
700, 120, 280, 80)
return make_graph_model(cells, w=1100, h=600)
# ============================================================
# 图5活动图 — 物资需求上报流程
# ============================================================
def build_activity_diagram():
cells = []
add_cell(cells, "title5", "1", cell_style("title"),
"图5 活动图 — 物资需求上报业务流程(行为视角)", 20, 10, 600, 30)
y = 60
# start
add_cell(cells, "a_start", "1", cell_style("start"), "", 400, y, 20, 20)
y += 40
add_cell(cells, "a_login", "1", cell_style("activity"), "登录认证", 360, y, 100, 40)
y += 60
add_cell(cells, "a_home", "1", cell_style("activity"), "进入首页", 360, y, 100, 40)
y += 60
add_cell(cells, "a_dec1", "1", cell_style("decision"), "选择功能", 360, y, 100, 50)
y += 70
# Branch: submit demand
add_cell(cells, "a_type", "1", cell_style("activity"), "选择物资类型", 180, y, 120, 40)
add_cell(cells, "a_demand", "1", cell_style("activity"), "输入数量/紧急程度", 340, y, 140, 40)
add_cell(cells, "a_drop", "1", cell_style("activity"), "查看推荐投放点", 520, y, 140, 40)
add_cell(cells, "a_map", "1", cell_style("activity"), "地图选点/搜索", 700, y, 140, 40)
# Edges from decision
add_cell(cells, "e_d1", "1", cell_style("arrow"), "上报需求", edge=True, source="a_dec1", target="a_type")
add_cell(cells, "e_d2", "1", cell_style("arrow"), "", edge=True, source="a_type", target="a_demand")
add_cell(cells, "e_d3", "1", cell_style("arrow"), "", edge=True, source="a_demand", target="a_drop")
add_cell(cells, "e_d4", "1", cell_style("arrow"), "", edge=True, source="a_drop", target="a_map")
y += 60
add_cell(cells, "a_confirm", "1", cell_style("decision"), "确认提交?", 360, y, 100, 50)
add_cell(cells, "e_d5", "1", cell_style("arrow"), "", edge=True, source="a_map", target="a_confirm")
y += 70
add_cell(cells, "a_submit", "1", cell_style("activity"), "调用 API.postDemand()", 340, y, 140, 40)
add_cell(cells, "a_cancel", "1", cell_style("activity"), "返回修改", 560, y, 100, 40)
add_cell(cells, "e_yes", "1", cell_style("arrow"), "", edge=True, source="a_confirm", target="a_submit")
add_cell(cells, "e_no", "1", cell_style("arrow"), "", edge=True, source="a_confirm", target="a_cancel")
add_cell(cells, "e_back", "1", cell_style("arrow"), "", edge=True, source="a_cancel", target="a_type")
y += 60
add_cell(cells, "a_dec2", "1", cell_style("decision"), "提交成功?", 360, y, 100, 50)
add_cell(cells, "e_d6", "1", cell_style("arrow"), "", edge=True, source="a_submit", target="a_dec2")
y += 70
add_cell(cells, "a_success", "1", cell_style("activity"), "显示成功提示\n跳转首页", 180, y, 140, 50)
add_cell(cells, "a_fail", "1", cell_style("activity"), "显示错误提示\n支持重试", 520, y, 140, 50)
add_cell(cells, "e_ok", "1", cell_style("arrow"), "成功", edge=True, source="a_dec2", target="a_success")
add_cell(cells, "e_err", "1", cell_style("arrow"), "失败", edge=True, source="a_dec2", target="a_fail")
y += 70
# merge
add_cell(cells, "a_merge", "1", cell_style("activity"), "结束", 360, y, 100, 40)
add_cell(cells, "e_m1", "1", cell_style("arrow"), "", edge=True, source="a_success", target="a_merge")
add_cell(cells, "e_m2", "1", cell_style("arrow"), "", edge=True, source="a_fail", target="a_merge")
y += 60
add_cell(cells, "a_end", "1", cell_style("end"), "", 400, y, 20, 20)
add_cell(cells, "e_end", "1", cell_style("arrow"), "", edge=True, source="a_merge", target="a_end")
# Swimlane labels
add_cell(cells, "sw1", "1", cell_style("text","fontStyle=1;fontSize=12;"), "【士兵操作】", 40, 80, 100, 20)
add_cell(cells, "sw2", "1", cell_style("text","fontStyle=1;fontSize=12;"), "【APP处理】", 40, 200, 100, 20)
add_cell(cells, "sw3", "1", cell_style("text","fontStyle=1;fontSize=12;"), "【后端交互】", 40, 400, 100, 20)
# note
add_cell(cells, "note5", "1", cell_style("boundary"),
"设计思路:活动图展示了物资需求上报的完整业务流程。\n"+
"关键设计:① 投放点选择支持「列表推荐」和「地图自由选点」两种模式;\n"+
"② API 调用失败时返回模拟数据Mock保证演示可用性\n"+
"③ 全流程有明确的成功/失败分支和回退路径。",
40, 520, 420, 80)
return make_graph_model(cells, w=900, h=700)
# ============================================================
# 图6部署图 — 系统物理部署
# ============================================================
def build_deployment_diagram():
cells = []
add_cell(cells, "title6", "1", cell_style("title"),
"图6 部署图 — 智途投送系统物理部署拓扑", 20, 10, 600, 30)
# Node 1: Soldier Phone
add_cell(cells, "node_phone", "1", cell_style("node"),
"«device»\n士兵手机\nAndroid 12+", 60, 80, 180, 120)
add_cell(cells, "art_app", "1", cell_style("artifact"),
"单兵终端APP\n(Capacitor/WebView)", 80, 130, 140, 50)
# Node 2: Server
add_cell(cells, "node_server", "1", cell_style("node"),
"«device»\n后方指挥所服务器\nUbuntu 22.04", 340, 80, 220, 140)
add_cell(cells, "art_flask", "1", cell_style("artifact"),
"Flask 后端\n(Python 3.10)", 360, 130, 180, 40)
add_cell(cells, "art_web", "1", cell_style("artifact"),
"Web 监控界面\n(HTML/JS/Leaflet)", 360, 180, 180, 30)
# Node 3: UAV
add_cell(cells, "node_uav", "1", cell_style("node"),
"«device»\n无人机机载计算机\nUbuntu 20.04 + ROS Noetic", 660, 80, 240, 180)
add_cell(cells, "art_ros", "1", cell_style("artifact"),
"ROS 节点网络\n(roscore + 多节点)", 680, 130, 200, 40)
add_cell(cells, "art_acoustic", "1", cell_style("artifact"),
"声源分析模块\n(C++17 / ONNX)", 680, 180, 200, 40)
add_cell(cells, "art_other", "1", cell_style("artifact"),
"视觉/热成像/路径规划节点", 680, 230, 200, 30)
# Communication links
add_cell(cells, "c1", "1", cell_style("arrow"),
"HTTP/REST\n(4G/WiFi)", edge=True, source="node_phone", target="node_server")
add_cell(cells, "c2", "1", cell_style("arrow"),
"ROS Topic\n(WebSocket/局域网)", edge=True, source="node_server", target="node_uav")
add_cell(cells, "c3", "1", cell_style("dashed_arrow"),
"rosbridge\n(可选直连)", edge=True, source="node_phone", target="node_uav")
# Hardware inside UAV
add_cell(cells, "hw_mic", "1", cell_style("rectangle","fillColor=#e0e0e0;"),
"麦克风阵列", 700, 300, 80, 40)
add_cell(cells, "hw_cam", "1", cell_style("rectangle","fillColor=#e0e0e0;"),
"可见光相机", 800, 300, 80, 40)
add_cell(cells, "hw_gps", "1", cell_style("rectangle","fillColor=#e0e0e0;"),
"GPS/IMU", 700, 360, 80, 40)
add_cell(cells, "c_hw1", "1", cell_style("arrow"), "", edge=True, source="hw_mic", target="node_uav")
add_cell(cells, "c_hw2", "1", cell_style("arrow"), "", edge=True, source="hw_cam", target="node_uav")
add_cell(cells, "c_hw3", "1", cell_style("arrow"), "", edge=True, source="hw_gps", target="node_uav")
# note
add_cell(cells, "note6", "1", cell_style("boundary"),
"设计思路:部署图展示了系统的物理分布和通信路径。\n"+
"关键设计:① 单兵APP通过 4G/WiFi 与后方服务器通信,不直接依赖无人机;\n"+
"② 服务器作为「消息中转站」,解耦了前线士兵与无人机控制;\n"+
"③ 无人机机载端运行 Ubuntu+ROS通过局域网与机载传感器直连。",
60, 400, 520, 80)
return make_graph_model(cells, w=1000, h=560)
# ============================================================
# Main
# ============================================================
pages = [
("01-类图-声源分析模块", build_class_diagram()),
("02-顺序图-Pipeline调用链", build_sequence_diagram()),
("03-组件图-声源分析分层", build_component_diagram()),
("04-用例图-单兵终端APP", build_usecase_diagram()),
("05-活动图-物资需求上报", build_activity_diagram()),
("06-部署图-系统物理拓扑", build_deployment_diagram()),
]
root = make_mxfile(pages)
# Pretty print
def indent(elem, level=0):
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for child in elem:
indent(child, level+1)
if not child.tail or not child.tail.strip():
child.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
indent(root)
tree = ET.ElementTree(root)
tree.write("uml_diagrams.drawio", encoding="utf-8", xml_declaration=True)
print("Generated: uml_diagrams.drawio (6 pages)")

@ -1,911 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<mxfile host="app.diagrams.net" modified="2026-05-19T00:00:00.000Z" agent="PythonScript" version="24.0.0" type="device" pages="6">
<diagram name="01-类图-声源分析模块" id="91a4988f-8a31-4878-809d-d1708378bc84">
<mxGraphModel dx="1434" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="700" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title1" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1" value="图1 类图 — 声源分析模块核心类结构(静态视角)" vertex="1" x="20" y="10" width="600" height="30">
<mxGeometry x="20" y="10" width="600" height="30" as="geometry" />
</mxCell>
<mxCell id="Pipeline" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="Pipeline" vertex="1" x="420" y="80" width="260" height="160">
<mxGeometry x="420" y="80" width="260" height="160" as="geometry" />
</mxCell>
<mxCell id="Pipeline_sep1" parent="Pipeline" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="260" height="0">
<mxGeometry x="0" y="26" width="260" height="0" as="geometry" />
</mxCell>
<mxCell id="Pipeline_attr0" parent="Pipeline" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-impl_: Impl*" vertex="1" x="0" y="26" width="260" height="18">
<mxGeometry x="0" y="26" width="260" height="18" as="geometry" />
</mxCell>
<mxCell id="Pipeline_sep2" parent="Pipeline" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="44" width="260" height="0">
<mxGeometry x="0" y="44" width="260" height="0" as="geometry" />
</mxCell>
<mxCell id="Pipeline_meth0" parent="Pipeline" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Process(audio): AcousticFrame" vertex="1" x="0" y="50" width="260" height="18">
<mxGeometry x="0" y="50" width="260" height="18" as="geometry" />
</mxCell>
<mxCell id="Pipeline_meth1" parent="Pipeline" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+FromYaml(path): PipelineConfig" vertex="1" x="0" y="68" width="260" height="18">
<mxGeometry x="0" y="68" width="260" height="18" as="geometry" />
</mxCell>
<mxCell id="Pipeline_meth2" parent="Pipeline" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Reset()" vertex="1" x="0" y="86" width="260" height="18">
<mxGeometry x="0" y="86" width="260" height="18" as="geometry" />
</mxCell>
<mxCell id="Pipeline_meth3" parent="Pipeline" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Config(): const PipelineConfig&amp;" vertex="1" x="0" y="104" width="260" height="18">
<mxGeometry x="0" y="104" width="260" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="AudioBuffer" vertex="1" x="40" y="80" width="200" height="204">
<mxGeometry x="40" y="80" width="200" height="204" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_sep1" parent="AudioBuffer" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="200" height="0">
<mxGeometry x="0" y="26" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_attr0" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-capacity_frames_: size_t" vertex="1" x="0" y="26" width="200" height="18">
<mxGeometry x="0" y="26" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_attr1" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-num_channels_: size_t" vertex="1" x="0" y="44" width="200" height="18">
<mxGeometry x="0" y="44" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_attr2" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-buffer_: vector&amp;lt;float&amp;gt;" vertex="1" x="0" y="62" width="200" height="18">
<mxGeometry x="0" y="62" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_attr3" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-head_, tail_, size_: size_t" vertex="1" x="0" y="80" width="200" height="18">
<mxGeometry x="0" y="80" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_sep2" parent="AudioBuffer" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="98" width="200" height="0">
<mxGeometry x="0" y="98" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_meth0" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Push(samples): size_t" vertex="1" x="0" y="104" width="200" height="18">
<mxGeometry x="0" y="104" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_meth1" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Pop(n): vector&amp;lt;float&amp;gt;" vertex="1" x="0" y="122" width="200" height="18">
<mxGeometry x="0" y="122" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_meth2" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Get(off,n): vector&amp;lt;float&amp;gt;" vertex="1" x="0" y="140" width="200" height="18">
<mxGeometry x="0" y="140" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_meth3" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Size(): size_t" vertex="1" x="0" y="158" width="200" height="18">
<mxGeometry x="0" y="158" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AudioBuffer_meth4" parent="AudioBuffer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Clear()" vertex="1" x="0" y="176" width="200" height="18">
<mxGeometry x="0" y="176" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="FeatureExtractor" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="FeatureExtractor" vertex="1" x="40" y="300" width="220" height="110">
<mxGeometry x="40" y="300" width="220" height="110" as="geometry" />
</mxCell>
<mxCell id="FeatureExtractor_sep1" parent="FeatureExtractor" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="220" height="0">
<mxGeometry x="0" y="26" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="FeatureExtractor_attr0" parent="FeatureExtractor" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-impl_: Impl*" vertex="1" x="0" y="26" width="220" height="18">
<mxGeometry x="0" y="26" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="FeatureExtractor_sep2" parent="FeatureExtractor" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="44" width="220" height="0">
<mxGeometry x="0" y="44" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="FeatureExtractor_meth0" parent="FeatureExtractor" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+MelSpectrogram(audio): MatrixXf" vertex="1" x="0" y="50" width="220" height="18">
<mxGeometry x="0" y="50" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="FeatureExtractor_meth1" parent="FeatureExtractor" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+MelSpectrogramMultiChannel(audio,n): vector&amp;lt;MatrixXf&amp;gt;" vertex="1" x="0" y="68" width="220" height="18">
<mxGeometry x="0" y="68" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="GunshotClassifier" vertex="1" x="300" y="300" width="220" height="132">
<mxGeometry x="300" y="300" width="220" height="132" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_sep1" parent="GunshotClassifier" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="220" height="0">
<mxGeometry x="0" y="26" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_attr0" parent="GunshotClassifier" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-session_: Ort::Session*" vertex="1" x="0" y="26" width="220" height="18">
<mxGeometry x="0" y="26" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_attr1" parent="GunshotClassifier" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-env_: Ort::Env*" vertex="1" x="0" y="44" width="220" height="18">
<mxGeometry x="0" y="44" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_attr2" parent="GunshotClassifier" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-labels_: vector&amp;lt;string&amp;gt;" vertex="1" x="0" y="62" width="220" height="18">
<mxGeometry x="0" y="62" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_sep2" parent="GunshotClassifier" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="80" width="220" height="0">
<mxGeometry x="0" y="80" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_meth0" parent="GunshotClassifier" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Predict(mel): pair&amp;lt;string,float&amp;gt;" vertex="1" x="0" y="86" width="220" height="18">
<mxGeometry x="0" y="86" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="GunshotClassifier_meth1" parent="GunshotClassifier" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Labels(): const vector&amp;lt;string&amp;gt;&amp;amp;" vertex="1" x="0" y="104" width="220" height="18">
<mxGeometry x="0" y="104" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="GccPhatLocalizer" vertex="1" x="560" y="300" width="200" height="114">
<mxGeometry x="560" y="300" width="200" height="114" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer_sep1" parent="GccPhatLocalizer" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="200" height="0">
<mxGeometry x="0" y="26" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer_attr0" parent="GccPhatLocalizer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-mic_config_: MicArrayConfig" vertex="1" x="0" y="26" width="200" height="18">
<mxGeometry x="0" y="26" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer_attr1" parent="GccPhatLocalizer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-sample_rate_: int" vertex="1" x="0" y="44" width="200" height="18">
<mxGeometry x="0" y="44" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer_attr2" parent="GccPhatLocalizer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-max_tdoa_: float" vertex="1" x="0" y="62" width="200" height="18">
<mxGeometry x="0" y="62" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer_sep2" parent="GccPhatLocalizer" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="80" width="200" height="0">
<mxGeometry x="0" y="80" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="GccPhatLocalizer_meth0" parent="GccPhatLocalizer" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Localize(audio_mat): pair&amp;lt;float,float&amp;gt;" vertex="1" x="0" y="86" width="200" height="18">
<mxGeometry x="0" y="86" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="DistanceEstimator" vertex="1" x="800" y="300" width="220" height="150">
<mxGeometry x="800" y="300" width="220" height="150" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_sep1" parent="DistanceEstimator" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="220" height="0">
<mxGeometry x="0" y="26" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_attr0" parent="DistanceEstimator" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-config_: DistanceConfig" vertex="1" x="0" y="26" width="220" height="18">
<mxGeometry x="0" y="26" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_attr1" parent="DistanceEstimator" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-kalman_state_: float" vertex="1" x="0" y="44" width="220" height="18">
<mxGeometry x="0" y="44" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_sep2" parent="DistanceEstimator" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="62" width="220" height="0">
<mxGeometry x="0" y="62" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_meth0" parent="DistanceEstimator" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+ComputeSpl(audio): float" vertex="1" x="0" y="68" width="220" height="18">
<mxGeometry x="0" y="68" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_meth1" parent="DistanceEstimator" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Estimate(spl,label): float" vertex="1" x="0" y="86" width="220" height="18">
<mxGeometry x="0" y="86" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_meth2" parent="DistanceEstimator" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+UpdateKalman(d): float" vertex="1" x="0" y="104" width="220" height="18">
<mxGeometry x="0" y="104" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="DistanceEstimator_meth3" parent="DistanceEstimator" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Reset()" vertex="1" x="0" y="122" width="220" height="18">
<mxGeometry x="0" y="122" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="ThreatTracker" vertex="1" x="800" y="80" width="200" height="114">
<mxGeometry x="800" y="80" width="200" height="114" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker_sep1" parent="ThreatTracker" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="200" height="0">
<mxGeometry x="0" y="26" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker_attr0" parent="ThreatTracker" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-min_interval_: float" vertex="1" x="0" y="26" width="200" height="18">
<mxGeometry x="0" y="26" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker_attr1" parent="ThreatTracker" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-history_: vector&amp;lt;AcousticThreat&amp;gt;" vertex="1" x="0" y="44" width="200" height="18">
<mxGeometry x="0" y="44" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker_sep2" parent="ThreatTracker" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="62" width="200" height="0">
<mxGeometry x="0" y="62" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker_meth0" parent="ThreatTracker" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Update(threats): vector&amp;lt;AcousticThreat&amp;gt;" vertex="1" x="0" y="68" width="200" height="18">
<mxGeometry x="0" y="68" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="ThreatTracker_meth1" parent="ThreatTracker" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+Reset()" vertex="1" x="0" y="86" width="200" height="18">
<mxGeometry x="0" y="86" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="AcousticNode (ROS层)" vertex="1" x="40" y="480" width="240" height="168">
<mxGeometry x="40" y="480" width="240" height="168" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_sep1" parent="AcousticNode" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="240" height="0">
<mxGeometry x="0" y="26" width="240" height="0" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_attr0" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-nh_, pnh_: NodeHandle" vertex="1" x="0" y="26" width="240" height="18">
<mxGeometry x="0" y="26" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_attr1" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-pipeline_: Pipeline*" vertex="1" x="0" y="44" width="240" height="18">
<mxGeometry x="0" y="44" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_attr2" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-source_type_: string" vertex="1" x="0" y="62" width="240" height="18">
<mxGeometry x="0" y="62" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_sep2" parent="AcousticNode" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="80" width="240" height="0">
<mxGeometry x="0" y="80" width="240" height="0" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_meth0" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+run()" vertex="1" x="0" y="86" width="240" height="18">
<mxGeometry x="0" y="86" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_meth1" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-on_mic_array_audio(msg)" vertex="1" x="0" y="104" width="240" height="18">
<mxGeometry x="0" y="104" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_meth2" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-process_wav_source()" vertex="1" x="0" y="122" width="240" height="18">
<mxGeometry x="0" y="122" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticNode_meth3" parent="AcousticNode" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-load_params()" vertex="1" x="0" y="140" width="240" height="18">
<mxGeometry x="0" y="140" width="240" height="18" as="geometry" />
</mxCell>
<mxCell id="WavFileSource" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="WavFileSource" vertex="1" x="340" y="480" width="200" height="150">
<mxGeometry x="340" y="480" width="200" height="150" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_sep1" parent="WavFileSource" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="200" height="0">
<mxGeometry x="0" y="26" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_attr0" parent="WavFileSource" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-file_path_: string" vertex="1" x="0" y="26" width="200" height="18">
<mxGeometry x="0" y="26" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_attr1" parent="WavFileSource" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-sample_rate_: int" vertex="1" x="0" y="44" width="200" height="18">
<mxGeometry x="0" y="44" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_attr2" parent="WavFileSource" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="-num_channels_: int" vertex="1" x="0" y="62" width="200" height="18">
<mxGeometry x="0" y="62" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_sep2" parent="WavFileSource" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="80" width="200" height="0">
<mxGeometry x="0" y="80" width="200" height="0" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_meth0" parent="WavFileSource" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+open(): bool" vertex="1" x="0" y="86" width="200" height="18">
<mxGeometry x="0" y="86" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_meth1" parent="WavFileSource" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+read(audio,n): size_t" vertex="1" x="0" y="104" width="200" height="18">
<mxGeometry x="0" y="104" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="WavFileSource_meth2" parent="WavFileSource" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+num_channels(): int" vertex="1" x="0" y="122" width="200" height="18">
<mxGeometry x="0" y="122" width="200" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="AcousticThreat (struct)" vertex="1" x="600" y="480" width="220" height="186">
<mxGeometry x="600" y="480" width="220" height="186" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_sep1" parent="AcousticThreat" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="220" height="0">
<mxGeometry x="0" y="26" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr0" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+timestamp: Timestamp" vertex="1" x="0" y="26" width="220" height="18">
<mxGeometry x="0" y="26" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr1" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+threat_id: string" vertex="1" x="0" y="44" width="220" height="18">
<mxGeometry x="0" y="44" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr2" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+sound_type: string" vertex="1" x="0" y="62" width="220" height="18">
<mxGeometry x="0" y="62" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr3" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+confidence: float" vertex="1" x="0" y="80" width="220" height="18">
<mxGeometry x="0" y="80" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr4" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+azimuth: float" vertex="1" x="0" y="98" width="220" height="18">
<mxGeometry x="0" y="98" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr5" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+elevation: float" vertex="1" x="0" y="116" width="220" height="18">
<mxGeometry x="0" y="116" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_attr6" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+distance: float" vertex="1" x="0" y="134" width="220" height="18">
<mxGeometry x="0" y="134" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_sep2" parent="AcousticThreat" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="152" width="220" height="0">
<mxGeometry x="0" y="152" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="AcousticThreat_meth0" parent="AcousticThreat" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="" vertex="1" x="0" y="158" width="220" height="18">
<mxGeometry x="0" y="158" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig" parent="1" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;" value="PipelineConfig (struct)" vertex="1" x="860" y="480" width="220" height="168">
<mxGeometry x="860" y="480" width="220" height="168" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_sep1" parent="PipelineConfig" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="26" width="220" height="0">
<mxGeometry x="0" y="26" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_attr0" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+sample_rate: uint32_t" vertex="1" x="0" y="26" width="220" height="18">
<mxGeometry x="0" y="26" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_attr1" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+chunk_duration: float" vertex="1" x="0" y="44" width="220" height="18">
<mxGeometry x="0" y="44" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_attr2" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+n_mels: uint32_t" vertex="1" x="0" y="62" width="220" height="18">
<mxGeometry x="0" y="62" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_attr3" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+classifier: ClassifierConfig" vertex="1" x="0" y="80" width="220" height="18">
<mxGeometry x="0" y="80" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_attr4" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+mic_array: MicArrayConfig" vertex="1" x="0" y="98" width="220" height="18">
<mxGeometry x="0" y="98" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_attr5" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="+distance: DistanceConfig" vertex="1" x="0" y="116" width="220" height="18">
<mxGeometry x="0" y="116" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_sep2" parent="PipelineConfig" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;" value="" vertex="1" x="0" y="134" width="220" height="0">
<mxGeometry x="0" y="134" width="220" height="0" as="geometry" />
</mxCell>
<mxCell id="PipelineConfig_meth0" parent="PipelineConfig" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;" value="" vertex="1" x="0" y="140" width="220" height="18">
<mxGeometry x="0" y="140" width="220" height="18" as="geometry" />
</mxCell>
<mxCell id="e1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;" value="" edge="1" source="Pipeline" target="AudioBuffer">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;" value="" edge="1" source="Pipeline" target="FeatureExtractor">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;" value="" edge="1" source="Pipeline" target="GunshotClassifier">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e4" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;" value="" edge="1" source="Pipeline" target="GccPhatLocalizer">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e5" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;" value="" edge="1" source="Pipeline" target="DistanceEstimator">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e6" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=diamondThin;endFill=1;" value="" edge="1" source="Pipeline" target="ThreatTracker">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e7" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="AcousticNode" target="Pipeline">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e8" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="AcousticNode" target="WavFileSource">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e9" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="uses" edge="1" source="Pipeline" target="PipelineConfig">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e10" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="ThreatTracker" target="AcousticThreat">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="l1" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="组合" vertex="1" x="230" y="90" width="50" height="20">
<mxGeometry x="230" y="90" width="50" height="20" as="geometry" />
</mxCell>
<mxCell id="l2" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="组合" vertex="1" x="230" y="310" width="50" height="20">
<mxGeometry x="230" y="310" width="50" height="20" as="geometry" />
</mxCell>
<mxCell id="l3" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="组合" vertex="1" x="520" y="310" width="50" height="20">
<mxGeometry x="520" y="310" width="50" height="20" as="geometry" />
</mxCell>
<mxCell id="l4" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="组合" vertex="1" x="780" y="310" width="50" height="20">
<mxGeometry x="780" y="310" width="50" height="20" as="geometry" />
</mxCell>
<mxCell id="l5" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="组合" vertex="1" x="780" y="100" width="50" height="20">
<mxGeometry x="780" y="100" width="50" height="20" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="02-顺序图-Pipeline调用链" id="c17d570c-bc1b-486d-a283-3369f75c9f8d">
<mxGraphModel dx="1434" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1500" pageHeight="700" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title2" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1" value="图2 顺序图 — Pipeline::Process 音频处理调用链(动态视角)" vertex="1" x="20" y="10" width="700" height="30">
<mxGeometry x="20" y="10" width="700" height="30" as="geometry" />
</mxCell>
<mxCell id="ll_AcousticNode" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="AcousticNode" vertex="1" x="60" y="60" width="100" height="520">
<mxGeometry x="60" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_Pipeline" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="Pipeline" vertex="1" x="240" y="60" width="100" height="520">
<mxGeometry x="240" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_AudioBuffer" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="AudioBuffer" vertex="1" x="400" y="60" width="100" height="520">
<mxGeometry x="400" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_FeatureExtractor" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="FeatureExtractor" vertex="1" x="560" y="60" width="100" height="520">
<mxGeometry x="560" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_GunshotClassifier" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="GunshotClassifier" vertex="1" x="720" y="60" width="100" height="520">
<mxGeometry x="720" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_GccPhatLocalizer" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="GccPhatLocalizer" vertex="1" x="880" y="60" width="100" height="520">
<mxGeometry x="880" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_DistanceEstimator" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="DistanceEstimator" vertex="1" x="1040" y="60" width="100" height="520">
<mxGeometry x="1040" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_ThreatTracker" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="ThreatTracker" vertex="1" x="1200" y="60" width="100" height="520">
<mxGeometry x="1200" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="ll_ThreatPublisher" parent="1" style="shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;" value="ThreatPublisher" vertex="1" x="1360" y="60" width="100" height="520">
<mxGeometry x="1360" y="60" width="100" height="520" as="geometry" />
</mxCell>
<mxCell id="m1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Process(audio_samples)" edge="1" source="ll_AcousticNode" target="ll_Pipeline">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m1_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Process(audio_samples)" vertex="1" x="90" y="85" width="120" height="20">
<mxGeometry x="90" y="85" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Push(samples)" edge="1" source="ll_Pipeline" target="ll_AudioBuffer">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m2_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Push(samples)" vertex="1" x="260" y="135" width="120" height="20">
<mxGeometry x="260" y="135" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Get(offset, chunk)" edge="1" source="ll_Pipeline" target="ll_AudioBuffer">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m3_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Get(offset, chunk)" vertex="1" x="260" y="175" width="120" height="20">
<mxGeometry x="260" y="175" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m4" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="MelSpectrogramMultiChannel(...)" edge="1" source="ll_Pipeline" target="ll_FeatureExtractor">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m4_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="MelSpectrogramMultiChannel(...)" vertex="1" x="340" y="215" width="120" height="20">
<mxGeometry x="340" y="215" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m5" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Predict(avg_mel)" edge="1" source="ll_Pipeline" target="ll_GunshotClassifier">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m5_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Predict(avg_mel)" vertex="1" x="420" y="255" width="120" height="20">
<mxGeometry x="420" y="255" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m6" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Localize(audio_mat)" edge="1" source="ll_Pipeline" target="ll_GccPhatLocalizer">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m6_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Localize(audio_mat)" vertex="1" x="500" y="295" width="120" height="20">
<mxGeometry x="500" y="295" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m7" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Estimate(spl, label)" edge="1" source="ll_Pipeline" target="ll_DistanceEstimator">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m7_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Estimate(spl, label)" vertex="1" x="580" y="335" width="120" height="20">
<mxGeometry x="580" y="335" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m8" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Update(threat)" edge="1" source="ll_Pipeline" target="ll_ThreatTracker">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m8_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Update(threat)" vertex="1" x="660" y="375" width="120" height="20">
<mxGeometry x="660" y="375" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m9" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;verticalAlign=bottom;" value="return AcousticFrame" edge="1" source="ll_Pipeline" target="ll_AcousticNode">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m9_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="return AcousticFrame" vertex="1" x="90" y="415" width="120" height="20">
<mxGeometry x="90" y="415" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="m10" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;verticalAlign=bottom;" value="Publish(frame)" edge="1" source="ll_AcousticNode" target="ll_ThreatPublisher">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="m10_lab" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" value="Publish(frame)" vertex="1" x="650" y="455" width="120" height="20">
<mxGeometry x="650" y="455" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="act_AcousticNode" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="100" y="100" width="20" height="410">
<mxGeometry x="100" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_Pipeline" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="280" y="100" width="20" height="410">
<mxGeometry x="280" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_AudioBuffer" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="440" y="100" width="20" height="410">
<mxGeometry x="440" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_FeatureExtractor" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="600" y="100" width="20" height="410">
<mxGeometry x="600" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_GunshotClassifier" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="760" y="100" width="20" height="410">
<mxGeometry x="760" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_GccPhatLocalizer" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="920" y="100" width="20" height="410">
<mxGeometry x="920" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_DistanceEstimator" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="1080" y="100" width="20" height="410">
<mxGeometry x="1080" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_ThreatTracker" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="1240" y="100" width="20" height="410">
<mxGeometry x="1240" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="act_ThreatPublisher" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" value="" vertex="1" x="1400" y="100" width="20" height="410">
<mxGeometry x="1400" y="100" width="20" height="410" as="geometry" />
</mxCell>
<mxCell id="note1" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="设计思路:每个调用都是同步阻塞调用,数据沿调用链逐层传递。这种设计确保了单线程内数据一致性,简化了实时系统的并发控制。" vertex="1" x="40" y="510" width="400" height="60">
<mxGeometry x="40" y="510" width="400" height="60" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="03-组件图-声源分析分层" id="440fdc91-3b30-4dd8-af5c-17d1f6dfe9b4">
<mxGraphModel dx="1434" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="520" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title3" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1" value="图3 组件图 — 声源分析模块分层组件结构" vertex="1" x="20" y="10" width="600" height="30">
<mxGeometry x="20" y="10" width="600" height="30" as="geometry" />
</mxCell>
<mxCell id="comp_core" parent="1" style="shape=component;align=left;spacingLeft=36;verticalAlign=top;whiteSpace=wrap;html=1;" value="«component»&#10;acoustic_analyzer_core&#10;&#10;• AudioBuffer&#10;• FeatureExtractor&#10;• GunshotClassifier&#10;• GccPhatLocalizer&#10;• DistanceEstimator&#10;• ThreatTracker&#10;• Pipeline" vertex="1" x="80" y="80" width="220" height="200">
<mxGeometry x="80" y="80" width="220" height="200" as="geometry" />
</mxCell>
<mxCell id="comp_io" parent="1" style="shape=component;align=left;spacingLeft=36;verticalAlign=top;whiteSpace=wrap;html=1;" value="«component»&#10;acoustic_analyzer_io&#10;&#10;• AudioSource (interface)&#10;• WavFileSource&#10;• MobilePhoneSource" vertex="1" x="80" y="320" width="220" height="120">
<mxGeometry x="80" y="320" width="220" height="120" as="geometry" />
</mxCell>
<mxCell id="comp_ros" parent="1" style="shape=component;align=left;spacingLeft=36;verticalAlign=top;whiteSpace=wrap;html=1;" value="«component»&#10;acoustic_analyzer_ros&#10;&#10;• AcousticNode&#10;• ThreatPublisher" vertex="1" x="400" y="80" width="200" height="100">
<mxGeometry x="400" y="80" width="200" height="100" as="geometry" />
</mxCell>
<mxCell id="ext_onnx" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="«library»&#10;ONNX Runtime" vertex="1" x="400" y="220" width="140" height="60">
<mxGeometry x="400" y="220" width="140" height="60" as="geometry" />
</mxCell>
<mxCell id="ext_eigen" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="«library»&#10;Eigen 3.4.0" vertex="1" x="400" y="300" width="140" height="60">
<mxGeometry x="400" y="300" width="140" height="60" as="geometry" />
</mxCell>
<mxCell id="ext_yaml" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="«library»&#10;yaml-cpp" vertex="1" x="400" y="380" width="140" height="60">
<mxGeometry x="400" y="380" width="140" height="60" as="geometry" />
</mxCell>
<mxCell id="ext_ros" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="«framework»&#10;ROS (roscpp)" vertex="1" x="680" y="80" width="140" height="60">
<mxGeometry x="680" y="80" width="140" height="60" as="geometry" />
</mxCell>
<mxCell id="d1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="comp_ros" target="comp_core">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="d2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="comp_ros" target="comp_io">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="d3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="uses" edge="1" source="comp_core" target="ext_onnx">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="d4" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="uses" edge="1" source="comp_core" target="ext_eigen">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="d5" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="uses" edge="1" source="comp_core" target="ext_yaml">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="d6" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="uses" edge="1" source="comp_ros" target="ext_ros">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="iface1" parent="1" style="shape=providedRequiredInterface;verticalAlign=top;spacingTop=0;whiteSpace=wrap;html=1;" value="IAcousticSource" vertex="1" x="300" y="350" width="40" height="30">
<mxGeometry x="300" y="350" width="40" height="30" as="geometry" />
</mxCell>
<mxCell id="d7" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="comp_io" target="iface1">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="d8" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=open;endFill=0;" value="" edge="1" source="iface1" target="comp_core">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="note3" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="设计思路core 层仅依赖第三方数学库Eigen/ONNX完全不依赖 ROS 和 yaml-cpp。&#10;这使得核心算法可以在 Windows/Linux 上独立编译测试,实现跨平台复用。" vertex="1" x="680" y="200" width="320" height="70">
<mxGeometry x="680" y="200" width="320" height="70" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="04-用例图-单兵终端APP" id="f6ec9b00-729a-4fde-aebb-ec0edbc1f7b2">
<mxGraphModel dx="1434" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="600" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title4" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1" value="图4 用例图 — 单兵终端APP功能需求用户视角" vertex="1" x="20" y="10" width="600" height="30">
<mxGeometry x="20" y="10" width="600" height="30" as="geometry" />
</mxCell>
<mxCell id="actor" parent="1" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" value="前线士兵" vertex="1" x="60" y="200" width="40" height="80">
<mxGeometry x="60" y="200" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="boundary" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666;" value="单兵终端APP" vertex="1" x="150" y="80" width="500" height="420">
<mxGeometry x="150" y="80" width="500" height="420" as="geometry" />
</mxCell>
<mxCell id="UC1" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="登录认证" vertex="1" x="260" y="120" width="120" height="50">
<mxGeometry x="260" y="120" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC2" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="上报物资需求" vertex="1" x="400" y="120" width="120" height="50">
<mxGeometry x="400" y="120" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC3" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="选择投放点" vertex="1" x="260" y="200" width="120" height="50">
<mxGeometry x="260" y="200" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC4" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="查看任务状态" vertex="1" x="400" y="200" width="120" height="50">
<mxGeometry x="400" y="200" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC5" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="查看无人机状态" vertex="1" x="260" y="280" width="120" height="50">
<mxGeometry x="260" y="280" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC6" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="实时位置上报" vertex="1" x="400" y="280" width="120" height="50">
<mxGeometry x="400" y="280" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC7" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="SOS一键求救" vertex="1" x="260" y="360" width="120" height="50">
<mxGeometry x="260" y="360" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="UC8" parent="1" style="ellipse;whiteSpace=wrap;html=1;" value="服务器配置" vertex="1" x="400" y="360" width="120" height="50">
<mxGeometry x="400" y="360" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="inc1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="«include»" edge="1" source="UC2" target="UC3">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="ext1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="«extend»" edge="1" source="UC7" target="UC6">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC1">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC2">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC3">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC4" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC4">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC5" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC5">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC6" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC6">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC7" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC7">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_UC8" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="actor" target="UC8">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="note4" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="设计思路:用例图从用户视角描述了系统功能边界。&#10;「上报物资需求」包含「选择投放点」(必须),&#10;「SOS求救」扩展「实时位置上报」自动触发位置发送。" vertex="1" x="700" y="120" width="280" height="80">
<mxGeometry x="700" y="120" width="280" height="80" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="05-活动图-物资需求上报" id="8bf22029-4d10-4566-98ef-64b3e3a187f0">
<mxGraphModel dx="1434" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="900" pageHeight="700" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title5" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1" value="图5 活动图 — 物资需求上报业务流程(行为视角)" vertex="1" x="20" y="10" width="600" height="30">
<mxGeometry x="20" y="10" width="600" height="30" as="geometry" />
</mxCell>
<mxCell id="a_start" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#000000;" value="" vertex="1" x="400" y="60" width="20" height="20">
<mxGeometry x="400" y="60" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="a_login" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="登录认证" vertex="1" x="360" y="100" width="100" height="40">
<mxGeometry x="360" y="100" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="a_home" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="进入首页" vertex="1" x="360" y="160" width="100" height="40">
<mxGeometry x="360" y="160" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="a_dec1" parent="1" style="rhombus;whiteSpace=wrap;html=1;fillColor=#ffffcc;strokeColor=#b3b3b3;" value="选择功能" vertex="1" x="360" y="220" width="100" height="50">
<mxGeometry x="360" y="220" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="a_type" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="选择物资类型" vertex="1" x="180" y="290" width="120" height="40">
<mxGeometry x="180" y="290" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="a_demand" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="输入数量/紧急程度" vertex="1" x="340" y="290" width="140" height="40">
<mxGeometry x="340" y="290" width="140" height="40" as="geometry" />
</mxCell>
<mxCell id="a_drop" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="查看推荐投放点" vertex="1" x="520" y="290" width="140" height="40">
<mxGeometry x="520" y="290" width="140" height="40" as="geometry" />
</mxCell>
<mxCell id="a_map" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="地图选点/搜索" vertex="1" x="700" y="290" width="140" height="40">
<mxGeometry x="700" y="290" width="140" height="40" as="geometry" />
</mxCell>
<mxCell id="e_d1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="上报需求" edge="1" source="a_dec1" target="a_type">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_d2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_type" target="a_demand">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_d3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_demand" target="a_drop">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_d4" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_drop" target="a_map">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_confirm" parent="1" style="rhombus;whiteSpace=wrap;html=1;fillColor=#ffffcc;strokeColor=#b3b3b3;" value="确认提交?" vertex="1" x="360" y="350" width="100" height="50">
<mxGeometry x="360" y="350" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="e_d5" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_map" target="a_confirm">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_submit" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="调用 API.postDemand()" vertex="1" x="340" y="420" width="140" height="40">
<mxGeometry x="340" y="420" width="140" height="40" as="geometry" />
</mxCell>
<mxCell id="a_cancel" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="返回修改" vertex="1" x="560" y="420" width="100" height="40">
<mxGeometry x="560" y="420" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="e_yes" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="是" edge="1" source="a_confirm" target="a_submit">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_no" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="否" edge="1" source="a_confirm" target="a_cancel">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_back" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_cancel" target="a_type">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_dec2" parent="1" style="rhombus;whiteSpace=wrap;html=1;fillColor=#ffffcc;strokeColor=#b3b3b3;" value="提交成功?" vertex="1" x="360" y="480" width="100" height="50">
<mxGeometry x="360" y="480" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="e_d6" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_submit" target="a_dec2">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_success" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="显示成功提示&#10;跳转首页" vertex="1" x="180" y="550" width="140" height="50">
<mxGeometry x="180" y="550" width="140" height="50" as="geometry" />
</mxCell>
<mxCell id="a_fail" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="显示错误提示&#10;支持重试" vertex="1" x="520" y="550" width="140" height="50">
<mxGeometry x="520" y="550" width="140" height="50" as="geometry" />
</mxCell>
<mxCell id="e_ok" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="成功" edge="1" source="a_dec2" target="a_success">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_err" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="失败" edge="1" source="a_dec2" target="a_fail">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_merge" parent="1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" value="结束" vertex="1" x="360" y="620" width="100" height="40">
<mxGeometry x="360" y="620" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="e_m1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_success" target="a_merge">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="e_m2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_fail" target="a_merge">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="a_end" parent="1" style="ellipse;whiteSpace=wrap;html=1;fillColor=#000000;strokeColor=#ff0000;" value="" vertex="1" x="400" y="680" width="20" height="20">
<mxGeometry x="400" y="680" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="e_end" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="a_merge" target="a_end">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="sw1" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=12;" value="【士兵操作】" vertex="1" x="40" y="80" width="100" height="20">
<mxGeometry x="40" y="80" width="100" height="20" as="geometry" />
</mxCell>
<mxCell id="sw2" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=12;" value="【APP处理】" vertex="1" x="40" y="200" width="100" height="20">
<mxGeometry x="40" y="200" width="100" height="20" as="geometry" />
</mxCell>
<mxCell id="sw3" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontStyle=1;fontSize=12;" value="【后端交互】" vertex="1" x="40" y="400" width="100" height="20">
<mxGeometry x="40" y="400" width="100" height="20" as="geometry" />
</mxCell>
<mxCell id="note5" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="设计思路:活动图展示了物资需求上报的完整业务流程。&#10;关键设计:① 投放点选择支持「列表推荐」和「地图自由选点」两种模式;&#10;② API 调用失败时返回模拟数据Mock保证演示可用性&#10;③ 全流程有明确的成功/失败分支和回退路径。" vertex="1" x="40" y="520" width="420" height="80">
<mxGeometry x="40" y="520" width="420" height="80" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
<diagram name="06-部署图-系统物理拓扑" id="3c436c9e-6f9b-4f24-8b0d-6a519e8a8f82">
<mxGraphModel dx="1434" dy="780" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="560" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title6" parent="1" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1" value="图6 部署图 — 智途投送系统物理部署拓扑" vertex="1" x="20" y="10" width="600" height="30">
<mxGeometry x="20" y="10" width="600" height="30" as="geometry" />
</mxCell>
<mxCell id="node_phone" parent="1" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;" value="«device»&#10;士兵手机&#10;Android 12+" vertex="1" x="60" y="80" width="180" height="120">
<mxGeometry x="60" y="80" width="180" height="120" as="geometry" />
</mxCell>
<mxCell id="art_app" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="单兵终端APP&#10;(Capacitor/WebView)" vertex="1" x="80" y="130" width="140" height="50">
<mxGeometry x="80" y="130" width="140" height="50" as="geometry" />
</mxCell>
<mxCell id="node_server" parent="1" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;" value="«device»&#10;后方指挥所服务器&#10;Ubuntu 22.04" vertex="1" x="340" y="80" width="220" height="140">
<mxGeometry x="340" y="80" width="220" height="140" as="geometry" />
</mxCell>
<mxCell id="art_flask" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="Flask 后端&#10;(Python 3.10)" vertex="1" x="360" y="130" width="180" height="40">
<mxGeometry x="360" y="130" width="180" height="40" as="geometry" />
</mxCell>
<mxCell id="art_web" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="Web 监控界面&#10;(HTML/JS/Leaflet)" vertex="1" x="360" y="180" width="180" height="30">
<mxGeometry x="360" y="180" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="node_uav" parent="1" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;" value="«device»&#10;无人机机载计算机&#10;Ubuntu 20.04 + ROS Noetic" vertex="1" x="660" y="80" width="240" height="180">
<mxGeometry x="660" y="80" width="240" height="180" as="geometry" />
</mxCell>
<mxCell id="art_ros" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="ROS 节点网络&#10;(roscore + 多节点)" vertex="1" x="680" y="130" width="200" height="40">
<mxGeometry x="680" y="130" width="200" height="40" as="geometry" />
</mxCell>
<mxCell id="art_acoustic" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="声源分析模块&#10;(C++17 / ONNX)" vertex="1" x="680" y="180" width="200" height="40">
<mxGeometry x="680" y="180" width="200" height="40" as="geometry" />
</mxCell>
<mxCell id="art_other" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="视觉/热成像/路径规划节点" vertex="1" x="680" y="230" width="200" height="30">
<mxGeometry x="680" y="230" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="c1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="HTTP/REST&#10;(4G/WiFi)" edge="1" source="node_phone" target="node_server">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="c2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="ROS Topic&#10;(WebSocket/局域网)" edge="1" source="node_server" target="node_uav">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="c3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;" value="rosbridge&#10;(可选直连)" edge="1" source="node_phone" target="node_uav">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="hw_mic" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e0e0e0;" value="麦克风阵列" vertex="1" x="700" y="300" width="80" height="40">
<mxGeometry x="700" y="300" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="hw_cam" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e0e0e0;" value="可见光相机" vertex="1" x="800" y="300" width="80" height="40">
<mxGeometry x="800" y="300" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="hw_gps" parent="1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e0e0e0;" value="GPS/IMU" vertex="1" x="700" y="360" width="80" height="40">
<mxGeometry x="700" y="360" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="c_hw1" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="hw_mic" target="node_uav">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="c_hw2" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="hw_cam" target="node_uav">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="c_hw3" parent="1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" value="" edge="1" source="hw_gps" target="node_uav">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="note6" parent="1" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" value="设计思路:部署图展示了系统的物理分布和通信路径。&#10;关键设计:① 单兵APP通过 4G/WiFi 与后方服务器通信,不直接依赖无人机;&#10;② 服务器作为「消息中转站」,解耦了前线士兵与无人机控制;&#10;③ 无人机机载端运行 Ubuntu+ROS通过局域网与机载传感器直连。" vertex="1" x="60" y="400" width="520" height="80">
<mxGeometry x="60" y="400" width="520" height="80" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Loading…
Cancel
Save