|
|
<!DOCTYPE html>
|
|
|
<html lang="zh-CN">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<title>智途投送系统 - 软件体系结构图</title>
|
|
|
<style>
|
|
|
body {
|
|
|
font-family: "Microsoft YaHei", "SimHei", sans-serif;
|
|
|
background: #f5f6fa;
|
|
|
margin: 0;
|
|
|
padding: 20px;
|
|
|
color: #2c3e50;
|
|
|
}
|
|
|
h1 {
|
|
|
text-align: center;
|
|
|
color: #1a237e;
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
.subtitle {
|
|
|
text-align: center;
|
|
|
color: #555;
|
|
|
margin-bottom: 30px;
|
|
|
font-size: 14px;
|
|
|
}
|
|
|
.diagram-container {
|
|
|
background: #fff;
|
|
|
border-radius: 12px;
|
|
|
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
|
|
margin: 30px auto;
|
|
|
max-width: 1100px;
|
|
|
padding: 30px;
|
|
|
}
|
|
|
.diagram-title {
|
|
|
font-size: 18px;
|
|
|
font-weight: bold;
|
|
|
color: #1a237e;
|
|
|
margin-bottom: 20px;
|
|
|
border-left: 4px solid #3949ab;
|
|
|
padding-left: 12px;
|
|
|
}
|
|
|
.diagram-note {
|
|
|
font-size: 12px;
|
|
|
color: #666;
|
|
|
margin-top: 15px;
|
|
|
line-height: 1.6;
|
|
|
}
|
|
|
svg {
|
|
|
display: block;
|
|
|
margin: 0 auto;
|
|
|
}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
|
|
|
<h1>智途投送系统 — 软件体系结构图</h1>
|
|
|
<p class="subtitle">按《软件体系结构》课程规范绘制 | 管道-过滤器 / 分层 / 客户端-服务器 / 发布-订阅</p>
|
|
|
|
|
|
<!-- ===== 图1:系统整体架构(分层 + C/S) ===== -->
|
|
|
<div class="diagram-container">
|
|
|
<div class="diagram-title">图1 系统整体体系结构 — 分层 + 客户端-服务器风格</div>
|
|
|
<svg width="1000" height="520" viewBox="0 0 1000 520">
|
|
|
<defs>
|
|
|
<marker id="arrow" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#555"/>
|
|
|
</marker>
|
|
|
<marker id="arrow-dashed" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#888"/>
|
|
|
</marker>
|
|
|
</defs>
|
|
|
|
|
|
<!-- 背景层框 -->
|
|
|
<!-- 单兵终端 -->
|
|
|
<rect x="30" y="50" width="280" height="420" rx="10" fill="#e3f2fd" stroke="#1976d2" stroke-width="2"/>
|
|
|
<text x="170" y="80" text-anchor="middle" font-size="16" font-weight="bold" fill="#0d47a1">单兵终端APP(客户端)</text>
|
|
|
<text x="170" y="100" text-anchor="middle" font-size="11" fill="#555">Client-Server 架构 / 分层结构</text>
|
|
|
|
|
|
<!-- 后勤保障 -->
|
|
|
<rect x="360" y="50" width="280" height="420" rx="10" fill="#e8f5e9" stroke="#388e3c" stroke-width="2"/>
|
|
|
<text x="500" y="80" text-anchor="middle" font-size="16" font-weight="bold" fill="#1b5e20">后勤保障系统(服务器端)</text>
|
|
|
<text x="500" y="100" text-anchor="middle" font-size="11" fill="#555">Layered 分层架构 / REST API</text>
|
|
|
|
|
|
<!-- 无人机 -->
|
|
|
<rect x="690" y="50" width="280" height="420" rx="10" fill="#fff3e0" stroke="#f57c00" stroke-width="2"/>
|
|
|
<text x="830" y="80" text-anchor="middle" font-size="16" font-weight="bold" fill="#e65100">无人机软件系统</text>
|
|
|
<text x="830" y="100" text-anchor="middle" font-size="11" fill="#555">Pipe-Filter + Pub-Sub 混合架构</text>
|
|
|
|
|
|
<!-- ===== 单兵终端内部层次 ===== -->
|
|
|
<rect x="60" y="130" width="220" height="60" rx="6" fill="#bbdefb" stroke="#1976d2" stroke-width="1.5"/>
|
|
|
<text x="170" y="155" text-anchor="middle" font-size="13" font-weight="bold" fill="#0d47a1">表示层 (Presentation)</text>
|
|
|
<text x="170" y="175" text-anchor="middle" font-size="11" fill="#333">UI界面 / 地图 / 交互控件</text>
|
|
|
|
|
|
<rect x="60" y="210" width="220" height="60" rx="6" fill="#90caf9" stroke="#1976d2" stroke-width="1.5"/>
|
|
|
<text x="170" y="235" text-anchor="middle" font-size="13" font-weight="bold" fill="#0d47a1">业务逻辑层 (Service)</text>
|
|
|
<text x="170" y="255" text-anchor="middle" font-size="11" fill="#333">需求上报 / 位置同步 / 策略选择</text>
|
|
|
|
|
|
<rect x="60" y="290" width="220" height="60" rx="6" fill="#64b5f6" stroke="#1976d2" stroke-width="1.5"/>
|
|
|
<text x="170" y="315" text-anchor="middle" font-size="13" font-weight="bold" fill="#0d47a1">数据访问层 (Data Access)</text>
|
|
|
<text x="170" y="335" text-anchor="middle" font-size="11" fill="#333">REST API调用 / 本地存储</text>
|
|
|
|
|
|
<rect x="60" y="370" width="220" height="60" rx="6" fill="#42a5f5" stroke="#1976d2" stroke-width="1.5"/>
|
|
|
<text x="170" y="395" text-anchor="middle" font-size="13" font-weight="bold" fill="#fff">硬件抽象层 (HAL)</text>
|
|
|
<text x="170" y="415" text-anchor="middle" font-size="11" fill="#fff">GPS / 网络 / 摄像头</text>
|
|
|
|
|
|
<!-- 层间箭头 -->
|
|
|
<line x1="170" y1="190" x2="170" y2="210" stroke="#1976d2" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
<line x1="170" y1="270" x2="170" y2="290" stroke="#1976d2" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
<line x1="170" y1="350" x2="170" y2="370" stroke="#1976d2" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
|
|
|
<!-- ===== 后勤保障内部层次 ===== -->
|
|
|
<rect x="390" y="130" width="220" height="60" rx="6" fill="#c8e6c9" stroke="#388e3c" stroke-width="1.5"/>
|
|
|
<text x="500" y="155" text-anchor="middle" font-size="13" font-weight="bold" fill="#1b5e20">表示层 (Web前端)</text>
|
|
|
<text x="500" y="175" text-anchor="middle" font-size="11" fill="#333">HTML/CSS/JS / 地图可视化</text>
|
|
|
|
|
|
<rect x="390" y="210" width="220" height="60" rx="6" fill="#a5d6a7" stroke="#388e3c" stroke-width="1.5"/>
|
|
|
<text x="500" y="235" text-anchor="middle" font-size="13" font-weight="bold" fill="#1b5e20">应用层 (Flask API)</text>
|
|
|
<text x="500" y="255" text-anchor="middle" font-size="11" fill="#333">REST路由 / 任务调度 / 身份认证</text>
|
|
|
|
|
|
<rect x="390" y="290" width="220" height="60" rx="6" fill="#81c784" stroke="#388e3c" stroke-width="1.5"/>
|
|
|
<text x="500" y="315" text-anchor="middle" font-size="13" font-weight="bold" fill="#1b5e20">业务逻辑层 (Services)</text>
|
|
|
<text x="500" y="335" text-anchor="middle" font-size="11" fill="#333">路径规划 / 资源分配 / 威胁融合</text>
|
|
|
|
|
|
<rect x="390" y="370" width="220" height="60" rx="6" fill="#66bb6a" stroke="#388e3c" stroke-width="1.5"/>
|
|
|
<text x="500" y="395" text-anchor="middle" font-size="13" font-weight="bold" fill="#fff">数据层 (Data)</text>
|
|
|
<text x="500" y="415" text-anchor="middle" font-size="11" fill="#fff">SQLite / 内存数据 / 日志</text>
|
|
|
|
|
|
<!-- 层间箭头 -->
|
|
|
<line x1="500" y1="190" x2="500" y2="210" stroke="#388e3c" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
<line x1="500" y1="270" x2="500" y2="290" stroke="#388e3c" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
<line x1="500" y1="350" x2="500" y2="370" stroke="#388e3c" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
|
|
|
<!-- ===== 无人机内部 ===== -->
|
|
|
<rect x="720" y="130" width="220" height="70" rx="6" fill="#ffe0b2" stroke="#f57c00" stroke-width="1.5"/>
|
|
|
<text x="830" y="155" text-anchor="middle" font-size="13" font-weight="bold" fill="#e65100">ROS 节点层 (Pub-Sub)</text>
|
|
|
<text x="830" y="175" text-anchor="middle" font-size="11" fill="#333">威胁发布 / 航点订阅 / 状态广播</text>
|
|
|
|
|
|
<rect x="720" y="220" width="220" height="70" rx="6" fill="#ffcc80" stroke="#f57c00" stroke-width="1.5"/>
|
|
|
<text x="830" y="245" text-anchor="middle" font-size="13" font-weight="bold" fill="#e65100">感知流水线层 (Pipe-Filter)</text>
|
|
|
<text x="830" y="265" text-anchor="middle" font-size="11" fill="#333">声学/视觉/热成像 → 威胁地图</text>
|
|
|
|
|
|
<rect x="720" y="310" width="220" height="70" rx="6" fill="#ffb74d" stroke="#f57c00" stroke-width="1.5"/>
|
|
|
<text x="830" y="335" text-anchor="middle" font-size="13" font-weight="bold" fill="#e65100">算法核心层 (Core)</text>
|
|
|
<text x="830" y="355" text-anchor="middle" font-size="11" fill="#333">GCC-PHAT / CNN-GRU / SPL</text>
|
|
|
|
|
|
<rect x="720" y="400" width="220" height="50" rx="6" fill="#ffa726" stroke="#f57c00" stroke-width="1.5"/>
|
|
|
<text x="830" y="420" text-anchor="middle" font-size="13" font-weight="bold" fill="#fff">硬件接口层 (Drivers)</text>
|
|
|
<text x="830" y="438" text-anchor="middle" font-size="11" fill="#fff">麦克风 / 相机 / IMU / GPS</text>
|
|
|
|
|
|
<!-- 层间箭头 -->
|
|
|
<line x1="830" y1="200" x2="830" y2="220" stroke="#f57c00" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
<line x1="830" y1="290" x2="830" y2="310" stroke="#f57c00" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
<line x1="830" y1="380" x2="830" y2="400" stroke="#f57c00" stroke-width="2" marker-end="url(#arrow)"/>
|
|
|
|
|
|
<!-- ===== 子系统间通信 ===== -->
|
|
|
<!-- C/S 双向箭头 -->
|
|
|
<line x1="310" y1="250" x2="360" y2="250" stroke="#555" stroke-width="2" marker-end="url(#arrow)" marker-start="url(#arrow)"/>
|
|
|
<text x="335" y="240" text-anchor="middle" font-size="10" fill="#333">HTTP/REST</text>
|
|
|
|
|
|
<line x1="640" y1="250" x2="690" y2="250" stroke="#555" stroke-width="2" marker-end="url(#arrow)" marker-start="url(#arrow)"/>
|
|
|
<text x="665" y="240" text-anchor="middle" font-size="10" fill="#333">ROS/WebSocket</text>
|
|
|
|
|
|
<!-- 单兵-无人机 虚线 -->
|
|
|
<line x1="170" y1="450" x2="830" y2="450" stroke="#888" stroke-width="1.5" stroke-dasharray="6,4"/>
|
|
|
<text x="500" y="470" text-anchor="middle" font-size="10" fill="#666"> rosbridge / WebSocket(间接通信,经由后勤保障系统或直接)</text>
|
|
|
</svg>
|
|
|
<p class="diagram-note">
|
|
|
<b>设计说明:</b>三大子系统各自内部采用分层架构,子系统间采用客户端-服务器风格交互。
|
|
|
上层(单兵APP、Web前端)通过 REST API 与后勤保障系统通信;后勤保障系统通过 ROS 网络与无人机交互。
|
|
|
分层架构的约束:每一层只使用直接下层提供的服务,层与层之间通过定义好的接口协议交互。
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<!-- ===== 图2:管道-过滤器(核心) ===== -->
|
|
|
<div class="diagram-container">
|
|
|
<div class="diagram-title">图2 声源分析模块 — 管道-过滤器体系结构(我负责部分)</div>
|
|
|
<svg width="1000" height="380" viewBox="0 0 1000 380">
|
|
|
<defs>
|
|
|
<marker id="arrow2" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#555"/>
|
|
|
</marker>
|
|
|
<filter id="shadow" x="-5%" y="-5%" width="110%" height="110%">
|
|
|
<feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.12"/>
|
|
|
</filter>
|
|
|
</defs>
|
|
|
|
|
|
<!-- 标题框 -->
|
|
|
<rect x="20" y="15" width="960" height="40" rx="6" fill="#fff8e1" stroke="#ff8f00" stroke-width="2"/>
|
|
|
<text x="500" y="42" text-anchor="middle" font-size="15" font-weight="bold" fill="#e65100">
|
|
|
数据流:原始音频 → [缓冲 → 特征 → 分类 → 定位/测距 → 跟踪] → 威胁事件
|
|
|
</text>
|
|
|
|
|
|
<!-- ===== 数据源 ===== -->
|
|
|
<rect x="30" y="100" width="100" height="70" rx="8" fill="#eceff1" stroke="#546e7a" stroke-width="2" stroke-dasharray="4,2"/>
|
|
|
<text x="80" y="125" text-anchor="middle" font-size="12" font-weight="bold" fill="#37474f">数据源</text>
|
|
|
<text x="80" y="142" text-anchor="middle" font-size="10" fill="#555">麦克风阵列</text>
|
|
|
<text x="80" y="157" text-anchor="middle" font-size="10" fill="#555">WAV文件</text>
|
|
|
|
|
|
<!-- ===== Filter 1: AudioBuffer ===== -->
|
|
|
<rect x="170" y="90" width="130" height="90" rx="8" fill="#e3f2fd" stroke="#1565c0" stroke-width="2" filter="url(#shadow)"/>
|
|
|
<text x="235" y="118" text-anchor="middle" font-size="13" font-weight="bold" fill="#0d47a1">Filter 1</text>
|
|
|
<text x="235" y="138" text-anchor="middle" font-size="12" font-weight="bold" fill="#1565c0">AudioBuffer</text>
|
|
|
<text x="235" y="158" text-anchor="middle" font-size="10" fill="#555">循环缓冲区</text>
|
|
|
<text x="235" y="170" text-anchor="middle" font-size="9" fill="#777">滑动窗口管理</text>
|
|
|
|
|
|
<!-- ===== Filter 2: FeatureExtractor ===== -->
|
|
|
<rect x="340" y="90" width="130" height="90" rx="8" fill="#e8f5e9" stroke="#2e7d32" stroke-width="2" filter="url(#shadow)"/>
|
|
|
<text x="405" y="118" text-anchor="middle" font-size="13" font-weight="bold" fill="#1b5e20">Filter 2</text>
|
|
|
<text x="405" y="138" text-anchor="middle" font-size="12" font-weight="bold" fill="#2e7d32">FeatureExtractor</text>
|
|
|
<text x="405" y="158" text-anchor="middle" font-size="10" fill="#555">Mel频谱图提取</text>
|
|
|
<text x="405" y="170" text-anchor="middle" font-size="9" fill="#777">FFT + Mel滤波器组</text>
|
|
|
|
|
|
<!-- ===== Filter 3: GunshotClassifier ===== -->
|
|
|
<rect x="510" y="90" width="130" height="90" rx="8" fill="#fff3e0" stroke="#ef6c00" stroke-width="2" filter="url(#shadow)"/>
|
|
|
<text x="575" y="118" text-anchor="middle" font-size="13" font-weight="bold" fill="#e65100">Filter 3</text>
|
|
|
<text x="575" y="138" text-anchor="middle" font-size="12" font-weight="bold" fill="#ef6c00">GunshotClassifier</text>
|
|
|
<text x="575" y="158" text-anchor="middle" font-size="10" fill="#555">CNN-GRU分类</text>
|
|
|
<text x="575" y="170" text-anchor="middle" font-size="9" fill="#777">ONNX Runtime推理</text>
|
|
|
|
|
|
<!-- ===== Filter 4a: GccPhatLocalizer ===== -->
|
|
|
<rect x="680" y="90" width="130" height="90" rx="8" fill="#fce4ec" stroke="#c2185b" stroke-width="2" filter="url(#shadow)"/>
|
|
|
<text x="745" y="118" text-anchor="middle" font-size="13" font-weight="bold" fill="#880e4f">Filter 4a</text>
|
|
|
<text x="745" y="138" text-anchor="middle" font-size="12" font-weight="bold" fill="#c2185b">GccPhatLocalizer</text>
|
|
|
<text x="745" y="158" text-anchor="middle" font-size="10" fill="#555">GCC-PHAT定位</text>
|
|
|
<text x="745" y="170" text-anchor="middle" font-size="9" fill="#777">时延估计 → 方位角/俯仰角</text>
|
|
|
|
|
|
<!-- ===== Filter 4b: DistanceEstimator ===== -->
|
|
|
<rect x="680" y="210" width="130" height="90" rx="8" fill="#f3e5f5" stroke="#7b1fa2" stroke-width="2" filter="url(#shadow)"/>
|
|
|
<text x="745" y="238" text-anchor="middle" font-size="13" font-weight="bold" fill="#4a148c">Filter 4b</text>
|
|
|
<text x="745" y="258" text-anchor="middle" font-size="12" font-weight="bold" fill="#7b1fa2">DistanceEstimator</text>
|
|
|
<text x="745" y="278" text-anchor="middle" font-size="10" fill="#555">SPL距离估计</text>
|
|
|
<text x="745" y="290" text-anchor="middle" font-size="9" fill="#777">声压级衰减 + Kalman滤波</text>
|
|
|
|
|
|
<!-- ===== Filter 5: ThreatTracker ===== -->
|
|
|
<rect x="850" y="150" width="130" height="90" rx="8" fill="#e0f2f1" stroke="#00695c" stroke-width="2" filter="url(#shadow)"/>
|
|
|
<text x="915" y="178" text-anchor="middle" font-size="13" font-weight="bold" fill="#004d40">Filter 5</text>
|
|
|
<text x="915" y="198" text-anchor="middle" font-size="12" font-weight="bold" fill="#00695c">ThreatTracker</text>
|
|
|
<text x="915" y="218" text-anchor="middle" font-size="10" fill="#555">威胁跟踪/去重</text>
|
|
|
<text x="915" y="230" text-anchor="middle" font-size="9" fill="#777">时域去重 + ID分配</text>
|
|
|
|
|
|
<!-- ===== 数据汇点 ===== -->
|
|
|
<rect x="850" y="280" width="130" height="60" rx="8" fill="#eceff1" stroke="#546e7a" stroke-width="2" stroke-dasharray="4,2"/>
|
|
|
<text x="915" y="305" text-anchor="middle" font-size="12" font-weight="bold" fill="#37474f">数据汇点</text>
|
|
|
<text x="915" y="325" text-anchor="middle" font-size="10" fill="#555">AcousticThreat 事件</text>
|
|
|
|
|
|
<!-- ===== 管道连接(带标签) ===== -->
|
|
|
<!-- 源 → Buffer -->
|
|
|
<line x1="130" y1="135" x2="170" y2="135" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="150" y="128" text-anchor="middle" font-size="9" fill="#666">float[]</text>
|
|
|
|
|
|
<!-- Buffer → Feature -->
|
|
|
<line x1="300" y1="135" x2="340" y2="135" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="320" y="128" text-anchor="middle" font-size="9" fill="#666">float[]</text>
|
|
|
|
|
|
<!-- Feature → Classifier -->
|
|
|
<line x1="470" y1="135" x2="510" y2="135" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="490" y="128" text-anchor="middle" font-size="9" fill="#666">MatrixXf</text>
|
|
|
|
|
|
<!-- Classifier → Localizer -->
|
|
|
<line x1="640" y1="135" x2="680" y2="135" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="660" y="128" text-anchor="middle" font-size="9" fill="#666">label+conf</text>
|
|
|
|
|
|
<!-- Classifier → DistanceEstimator (branch) -->
|
|
|
<path d="M 575 180 L 575 255 L 680 255" fill="none" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="630" y="248" text-anchor="middle" font-size="9" fill="#666">label</text>
|
|
|
|
|
|
<!-- Localizer → Tracker -->
|
|
|
<line x1="810" y1="135" x2="850" y2="175" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="840" y="148" text-anchor="middle" font-size="9" fill="#666">azimuth,elevation</text>
|
|
|
|
|
|
<!-- Distance → Tracker -->
|
|
|
<line x1="810" y1="255" x2="850" y2="215" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="830" y="240" text-anchor="middle" font-size="9" fill="#666">distance</text>
|
|
|
|
|
|
<!-- Tracker → Sink -->
|
|
|
<line x1="915" y1="240" x2="915" y2="280" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="940" y="262" text-anchor="middle" font-size="9" fill="#666">AcousticFrame</text>
|
|
|
|
|
|
<!-- ===== VAD 门控标注 ===== -->
|
|
|
<rect x="180" y="220" width="110" height="40" rx="5" fill="#fff" stroke="#999" stroke-width="1" stroke-dasharray="3,2"/>
|
|
|
<text x="235" y="238" text-anchor="middle" font-size="10" fill="#666">VAD门控</text>
|
|
|
<text x="235" y="252" text-anchor="middle" font-size="9" fill="#888">能量+过零率检测</text>
|
|
|
<line x1="235" y1="220" x2="235" y2="180" stroke="#999" stroke-width="1" stroke-dasharray="3,2"/>
|
|
|
|
|
|
<!-- ===== 时序平滑标注 ===== -->
|
|
|
<rect x="530" y="220" width="110" height="40" rx="5" fill="#fff" stroke="#999" stroke-width="1" stroke-dasharray="3,2"/>
|
|
|
<text x="585" y="238" text-anchor="middle" font-size="10" fill="#666">时序平滑</text>
|
|
|
<text x="585" y="252" text-anchor="middle" font-size="9" fill="#888">滑动窗口平均</text>
|
|
|
<line x1="585" y1="220" x2="585" y2="180" stroke="#999" stroke-width="1" stroke-dasharray="3,2"/>
|
|
|
|
|
|
<!-- 图例 -->
|
|
|
<rect x="30" y="320" width="140" height="40" rx="5" fill="#e3f2fd" stroke="#1565c0" stroke-width="1.5"/>
|
|
|
<text x="100" y="335" text-anchor="middle" font-size="10" fill="#333">过滤器组件</text>
|
|
|
<text x="100" y="350" text-anchor="middle" font-size="9" fill="#555">(独立处理单元)</text>
|
|
|
|
|
|
<rect x="190" y="320" width="140" height="40" rx="5" fill="#eceff1" stroke="#546e7a" stroke-width="1.5" stroke-dasharray="4,2"/>
|
|
|
<text x="260" y="335" text-anchor="middle" font-size="10" fill="#333">数据源/汇点</text>
|
|
|
<text x="260" y="350" text-anchor="middle" font-size="9" fill="#555">(系统边界)</text>
|
|
|
|
|
|
<line x1="350" y1="340" x2="400" y2="340" stroke="#555" stroke-width="2" marker-end="url(#arrow2)"/>
|
|
|
<text x="430" y="345" text-anchor="start" font-size="10" fill="#333">管道 (数据流)</text>
|
|
|
</svg>
|
|
|
<p class="diagram-note">
|
|
|
<b>管道-过滤器风格的四个核心特征在本模块的体现:</b><br>
|
|
|
① <b>过滤器独立性</b>:每个 Filter(如 FeatureExtractor、GunshotClassifier)都是独立的类,不与其他 Filter 共享状态;<br>
|
|
|
② <b>数据流驱动</b>:音频数据沿管道单向流动,无循环(符合「不允许出现环」的约束);<br>
|
|
|
③ <b>局部变换</b>:每个 Filter 只负责一种局部变换(时域→频域→概率→方位→距离);<br>
|
|
|
④ <b>黑盒复用</b>:GunshotClassifier 可独立替换为其他模型(如 Transformer),只要输入输出 Mel 频谱图格式不变。
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<!-- ===== 图3:分层结构图 ===== -->
|
|
|
<div class="diagram-container">
|
|
|
<div class="diagram-title">图3 声源分析模块 — 分层体系结构(静态视角)</div>
|
|
|
<svg width="900" height="400" viewBox="0 0 900 400">
|
|
|
<defs>
|
|
|
<marker id="arrow3" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#555"/>
|
|
|
</marker>
|
|
|
</defs>
|
|
|
|
|
|
<!-- 层背景 -->
|
|
|
<rect x="50" y="50" width="800" height="80" rx="8" fill="#e8eaf6" stroke="#3949ab" stroke-width="2"/>
|
|
|
<text x="70" y="75" font-size="14" font-weight="bold" fill="#1a237e">Layer 3:应用/集成层 (Application/Integration)</text>
|
|
|
<text x="70" y="95" font-size="11" fill="#555">ROS 包装器 / 节点生命周期管理 / 话题发布订阅</text>
|
|
|
<text x="70" y="110" font-size="11" fill="#555">acoustic_node.cpp | threat_publisher.cpp</text>
|
|
|
|
|
|
<rect x="50" y="150" width="800" height="80" rx="8" fill="#c5cae9" stroke="#3949ab" stroke-width="2"/>
|
|
|
<text x="70" y="175" font-size="14" font-weight="bold" fill="#1a237e">Layer 2:业务逻辑层 (Business Logic / Pipeline)</text>
|
|
|
<text x="70" y="195" font-size="11" fill="#555">流水线编排 (Pipeline) / 配置管理 / 数据类型定义</text>
|
|
|
<text x="70" y="210" font-size="11" fill="#555">pipeline.cpp | types.h | 配置解析 (YAML)</text>
|
|
|
|
|
|
<rect x="50" y="250" width="800" height="110" rx="8" fill="#9fa8da" stroke="#3949ab" stroke-width="2"/>
|
|
|
<text x="70" y="275" font-size="14" font-weight="bold" fill="#1a237e">Layer 1:算法核心层 (Algorithm Core)</text>
|
|
|
<text x="70" y="295" font-size="11" fill="#333">音频缓冲 (AudioBuffer) | 特征提取 (FeatureExtractor) | 分类器 (GunshotClassifier)</text>
|
|
|
<text x="70" y="310" font-size="11" fill="#333">声源定位 (GccPhatLocalizer) | 距离估计 (DistanceEstimator) | 威胁跟踪 (ThreatTracker)</text>
|
|
|
<text x="70" y="330" font-size="11" fill="#333">FFT工具 (fft_utils) | IO适配 (WavFileSource, MobilePhoneSource)</text>
|
|
|
|
|
|
<!-- 层间依赖箭头 -->
|
|
|
<line x1="450" y1="130" x2="450" y2="150" stroke="#3949ab" stroke-width="2.5" marker-end="url(#arrow3)"/>
|
|
|
<line x1="450" y1="230" x2="450" y2="250" stroke="#3949ab" stroke-width="2.5" marker-end="url(#arrow3)"/>
|
|
|
|
|
|
<!-- 依赖规则说明 -->
|
|
|
<rect x="520" y="135" width="300" height="40" rx="5" fill="#fff" stroke="#3949ab" stroke-width="1" stroke-dasharray="3,2"/>
|
|
|
<text x="530" y="152" font-size="10" fill="#3949ab">上层 → 下层:只允许向下依赖(依赖倒置原则)</text>
|
|
|
<text x="530" y="166" font-size="10" fill="#3949ab">core 层完全不依赖 ROS / yaml-cpp / 操作系统</text>
|
|
|
|
|
|
<!-- 跨平台标注 -->
|
|
|
<rect x="600" y="60" width="230" height="55" rx="5" fill="#fff" stroke="#2e7d32" stroke-width="1.5" stroke-dasharray="4,2"/>
|
|
|
<text x="715" y="80" text-anchor="middle" font-size="11" font-weight="bold" fill="#1b5e20">Ubuntu (实机部署)</text>
|
|
|
<text x="715" y="95" text-anchor="middle" font-size="10" fill="#333">BUILD_ROS_WRAPPER=ON</text>
|
|
|
<text x="715" y="108" text-anchor="middle" font-size="10" fill="#333">ROS节点 + 麦克风阵列驱动</text>
|
|
|
|
|
|
<rect x="600" y="160" width="230" height="55" rx="5" fill="#fff" stroke="#1565c0" stroke-width="1.5" stroke-dasharray="4,2"/>
|
|
|
<text x="715" y="180" text-anchor="middle" font-size="11" font-weight="bold" fill="#0d47a1">Windows / Linux (仿真调试)</text>
|
|
|
<text x="715" y="195" text-anchor="middle" font-size="10" fill="#333">BUILD_ROS_WRAPPER=OFF</text>
|
|
|
<text x="715" y="208" text-anchor="middle" font-size="10" fill="#333">离线WAV测试 + 单元测试</text>
|
|
|
|
|
|
<!-- 图例说明 -->
|
|
|
<rect x="50" y="360" width="800" height="30" rx="4" fill="#f5f5f5" stroke="#ccc" stroke-width="1"/>
|
|
|
<text x="60" y="380" font-size="11" fill="#555">
|
|
|
分层架构约束:① 每一层只使用直接下层的服务 ② 层间通过接口交互,不直接访问实现 ③ 下层修改不影响上层(只要接口不变)
|
|
|
</text>
|
|
|
</svg>
|
|
|
<p class="diagram-note">
|
|
|
<b>设计说明:</b>通过分层隔离,算法核心层(Layer 1)完全与 ROS、操作系统解耦。
|
|
|
这意味着同一套 C++ 声学算法既可以在 Ubuntu 上作为 ROS 节点运行(控制真实无人机),
|
|
|
也可以在 Windows 上编译为独立可执行文件做离线仿真测试,实现了"一次开发,多处部署"。
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<!-- ===== 图4:ROS 发布-订阅运行时图 ===== -->
|
|
|
<div class="diagram-container">
|
|
|
<div class="diagram-title">图4 无人机感知系统 — 发布-订阅(Pub-Sub)运行时交互</div>
|
|
|
<svg width="1000" height="420" viewBox="0 0 1000 420">
|
|
|
<defs>
|
|
|
<marker id="arrow4" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#555"/>
|
|
|
</marker>
|
|
|
</defs>
|
|
|
|
|
|
<!-- ROS Master -->
|
|
|
<rect x="400" y="20" width="200" height="50" rx="25" fill="#fce4ec" stroke="#ad1457" stroke-width="2"/>
|
|
|
<text x="500" y="40" text-anchor="middle" font-size="13" font-weight="bold" fill="#880e4f">ROS Master</text>
|
|
|
<text x="500" y="58" text-anchor="middle" font-size="10" fill="#555">节点注册 / 话题匹配</text>
|
|
|
|
|
|
<!-- 话题总线 -->
|
|
|
<rect x="80" y="100" width="840" height="50" rx="6" fill="#f3e5f5" stroke="#7b1fa2" stroke-width="2" stroke-dasharray="6,3"/>
|
|
|
<text x="500" y="120" text-anchor="middle" font-size="12" font-weight="bold" fill="#4a148c">ROS Topic 总线(异步消息通道)</text>
|
|
|
<text x="500" y="138" text-anchor="middle" font-size="10" fill="#555">/microphone_array/audio | /acoustic_threat | /threat_map | /dynamic_waypoints</text>
|
|
|
|
|
|
<!-- Publisher 节点:麦克风驱动 -->
|
|
|
<rect x="80" y="190" width="180" height="90" rx="8" fill="#e3f2fd" stroke="#1565c0" stroke-width="2"/>
|
|
|
<text x="170" y="215" text-anchor="middle" font-size="12" font-weight="bold" fill="#0d47a1">Publisher</text>
|
|
|
<text x="170" y="235" text-anchor="middle" font-size="11" fill="#333">麦克风阵列驱动节点</text>
|
|
|
<text x="170" y="255" text-anchor="middle" font-size="10" fill="#555">发布:/microphone_array/audio</text>
|
|
|
<text x="170" y="270" text-anchor="middle" font-size="10" fill="#555">数据类型:Float32MultiArray</text>
|
|
|
|
|
|
<!-- Publisher 节点:声学分析(我负责) -->
|
|
|
<rect x="300" y="190" width="180" height="90" rx="8" fill="#fff3e0" stroke="#ef6c00" stroke-width="2"/>
|
|
|
<text x="390" y="215" text-anchor="middle" font-size="12" font-weight="bold" fill="#e65100">Publisher(我负责)</text>
|
|
|
<text x="390" y="235" text-anchor="middle" font-size="11" fill="#333">声学分析节点 (acoustic_node)</text>
|
|
|
<text x="390" y="255" text-anchor="middle" font-size="10" fill="#555">订阅:/microphone_array/audio</text>
|
|
|
<text x="390" y="270" text-anchor="middle" font-size="10" fill="#555">发布:/acoustic_threat</text>
|
|
|
|
|
|
<!-- Subscriber 节点:多模态融合 -->
|
|
|
<rect x="520" y="190" width="180" height="90" rx="8" fill="#e8f5e9" stroke="#2e7d32" stroke-width="2"/>
|
|
|
<text x="610" y="215" text-anchor="middle" font-size="12" font-weight="bold" fill="#1b5e20">Subscriber + Publisher</text>
|
|
|
<text x="610" y="235" text-anchor="middle" font-size="11" fill="#333">多模态融合节点</text>
|
|
|
<text x="610" y="255" text-anchor="middle" font-size="10" fill="#555">订阅:/acoustic_threat + /vision_threat</text>
|
|
|
<text x="610" y="270" text-anchor="middle" font-size="10" fill="#555">发布:/threat_map</text>
|
|
|
|
|
|
<!-- Subscriber 节点:动态规划 -->
|
|
|
<rect x="740" y="190" width="180" height="90" rx="8" fill="#fce4ec" stroke="#c2185b" stroke-width="2"/>
|
|
|
<text x="830" y="215" text-anchor="middle" font-size="12" font-weight="bold" fill="#880e4f">Subscriber</text>
|
|
|
<text x="830" y="235" text-anchor="middle" font-size="11" fill="#333">动态路径规划节点</text>
|
|
|
<text x="830" y="255" text-anchor="middle" font-size="10" fill="#555">订阅:/threat_map</text>
|
|
|
<text x="830" y="270" text-anchor="middle" font-size="10" fill="#555">发布:/dynamic_waypoints</text>
|
|
|
|
|
|
<!-- 消息流向箭头 -->
|
|
|
<line x1="170" y1="280" x2="170" y2="330" stroke="#1565c0" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<line x1="170" y1="330" x2="350" y2="330" stroke="#1565c0" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<line x1="350" y1="330" x2="350" y2="280" stroke="#1565c0" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<text x="260" y="345" text-anchor="middle" font-size="10" fill="#1565c0">音频数据流</text>
|
|
|
|
|
|
<line x1="390" y1="280" x2="390" y2="330" stroke="#ef6c00" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<line x1="390" y1="330" x2="570" y2="330" stroke="#ef6c00" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<line x1="570" y1="330" x2="570" y2="280" stroke="#ef6c00" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<text x="480" y="345" text-anchor="middle" font-size="10" fill="#ef6c00">AcousticThreat 事件</text>
|
|
|
|
|
|
<line x1="610" y1="280" x2="610" y2="330" stroke="#2e7d32" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<line x1="610" y1="330" x2="790" y2="330" stroke="#2e7d32" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<line x1="790" y1="330" x2="790" y2="280" stroke="#2e7d32" stroke-width="2" marker-end="url(#arrow4)"/>
|
|
|
<text x="700" y="345" text-anchor="middle" font-size="10" fill="#2e7d32">融合后的 ThreatMap</text>
|
|
|
|
|
|
<!-- 特性说明 -->
|
|
|
<rect x="80" y="370" width="840" height="35" rx="5" fill="#fafafa" stroke="#999" stroke-width="1"/>
|
|
|
<text x="90" y="392" font-size="11" fill="#555">
|
|
|
发布-订阅特征:① 发布者与订阅者完全解耦,互不知道对方存在 ② 支持一对多广播(一个威胁事件可被多个消费者处理) ③ 异步非阻塞,适合实时感知系统的低延迟要求
|
|
|
</text>
|
|
|
</svg>
|
|
|
<p class="diagram-note">
|
|
|
<b>设计说明:</b>发布-订阅风格使得声学分析节点无需关心"谁会使用威胁结果"。
|
|
|
在仿真阶段,可以订阅 /acoustic_threat 做可视化验证;在实机阶段,同一个话题被多模态融合节点订阅。
|
|
|
这种「隐式调用」机制大幅降低了系统各模块间的耦合度。
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<!-- ===== 图5:PIMPL 信息隐藏示意图 ===== -->
|
|
|
<div class="diagram-container">
|
|
|
<div class="diagram-title">图5 PIMPL 惯用法 — 信息隐藏与编译隔离</div>
|
|
|
<svg width="900" height="320" viewBox="0 0 900 320">
|
|
|
<defs>
|
|
|
<marker id="arrow5" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
|
|
|
<path d="M0,0 L0,6 L9,3 z" fill="#555"/>
|
|
|
</marker>
|
|
|
</defs>
|
|
|
|
|
|
<!-- 左侧:对外接口头文件 -->
|
|
|
<rect x="50" y="50" width="280" height="220" rx="10" fill="#e8eaf6" stroke="#3949ab" stroke-width="2"/>
|
|
|
<text x="190" y="78" text-anchor="middle" font-size="14" font-weight="bold" fill="#1a237e">公开头文件 (Public API)</text>
|
|
|
<text x="190" y="98" text-anchor="middle" font-size="11" fill="#555">pipeline.h / feature_extractor.h</text>
|
|
|
|
|
|
<rect x="80" y="120" width="220" height="120" rx="6" fill="#fff" stroke="#3949ab" stroke-width="1.5"/>
|
|
|
<text x="100" y="145" font-size="12" font-family="monospace" fill="#333">class Pipeline {</text>
|
|
|
<text x="100" y="165" font-size="12" font-family="monospace" fill="#333">public:</text>
|
|
|
<text x="120" y="185" font-size="12" font-family="monospace" fill="#2e7d32"> Process(audio)</text>
|
|
|
<text x="120" y="205" font-size="12" font-family="monospace" fill="#2e7d32"> FromYaml(path)</text>
|
|
|
<text x="120" y="225" font-size="12" font-family="monospace" fill="#2e7d32"> Reset()</text>
|
|
|
<text x="100" y="245" font-size="12" font-family="monospace" fill="#c62828">private:</text>
|
|
|
<text x="120" y="260" font-size="12" font-family="monospace" fill="#c62828"> Impl* impl_;</text>
|
|
|
<text x="100" y="275" font-size="12" font-family="monospace" fill="#333">};</text>
|
|
|
|
|
|
<!-- 右侧:实现文件 -->
|
|
|
<rect x="560" y="50" width="280" height="220" rx="10" fill="#fff3e0" stroke="#ef6c00" stroke-width="2"/>
|
|
|
<text x="700" y="78" text-anchor="middle" font-size="14" font-weight="bold" fill="#e65100">实现文件 (Private Implementation)</text>
|
|
|
<text x="700" y="98" text-anchor="middle" font-size="11" fill="#555">pipeline.cpp</text>
|
|
|
|
|
|
<rect x="590" y="120" width="220" height="120" rx="6" fill="#fff" stroke="#ef6c00" stroke-width="1.5"/>
|
|
|
<text x="610" y="145" font-size="12" font-family="monospace" fill="#333">struct Pipeline::Impl {</text>
|
|
|
<text x="610" y="165" font-size="12" font-family="monospace" fill="#555"> AudioBuffer*</text>
|
|
|
<text x="610" y="185" font-size="12" font-family="monospace" fill="#555"> FeatureExtractor*</text>
|
|
|
<text x="610" y="205" font-size="12" font-family="monospace" fill="#555"> GunshotClassifier*</text>
|
|
|
<text x="610" y="225" font-size="12" font-family="monospace" fill="#555"> GccPhatLocalizer*</text>
|
|
|
<text x="610" y="245" font-size="12" font-family="monospace" fill="#555"> ...</text>
|
|
|
<text x="610" y="260" font-size="12" font-family="monospace" fill="#333">};</text>
|
|
|
|
|
|
<!-- 中间箭头 -->
|
|
|
<line x1="350" y1="160" x2="560" y2="160" stroke="#555" stroke-width="2" marker-end="url(#arrow5)"/>
|
|
|
<text x="455" y="150" text-anchor="middle" font-size="11" fill="#555">编译依赖</text>
|
|
|
<text x="455" y="180" text-anchor="middle" font-size="10" fill="#888">(.cpp 包含所有头文件)</text>
|
|
|
|
|
|
<line x1="560" y1="200" x2="350" y2="200" stroke="#999" stroke-width="1.5" stroke-dasharray="5,3"/>
|
|
|
<text x="455" y="220" text-anchor="middle" font-size="10" fill="#888">无反向依赖(接口不感知实现细节)</text>
|
|
|
|
|
|
<!-- 编译边界 -->
|
|
|
<rect x="390" y="100" width="130" height="30" rx="4" fill="#e8f5e9" stroke="#2e7d32" stroke-width="1"/>
|
|
|
<text x="455" y="120" text-anchor="middle" font-size="10" fill="#1b5e20">编译防火墙</text>
|
|
|
|
|
|
<!-- 底部说明 -->
|
|
|
<rect x="50" y="290" width="790" height="22" rx="4" fill="#f5f5f5" stroke="#ccc" stroke-width="1"/>
|
|
|
<text x="60" y="306" font-size="11" fill="#555">
|
|
|
效果:修改 Impl 内部(如替换 FFT 库、新增滤波器)→ 无需重新编译依赖 pipeline.h 的其他模块 → 大幅缩短编译时间,降低模块耦合
|
|
|
</text>
|
|
|
</svg>
|
|
|
<p class="diagram-note">
|
|
|
<b>设计说明:</b>PIMPL(Pointer to Implementation)是 C++ 中实现「信息隐藏」的经典惯用法。
|
|
|
它将类的实现细节完全移入 .cpp 文件,头文件中只暴露接口指针。
|
|
|
这符合软件体系结构「接口与实现分离」的原则,也提升了系统的可修改性质量属性。
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
<div style="text-align:center; margin: 40px 0; color:#888; font-size:12px;">
|
|
|
智途投送系统 — 软件体系结构汇报用图 | 绘制规范参考《软件体系结构》课程
|
|
|
</div>
|
|
|
|
|
|
</body>
|
|
|
</html>
|