CounterDroneBackend/docs/design/architecture/总体架构设计.md
tian 8bcf4cf675 VERSION 0.12.0 — PDF 导出 + 报告模板架构 + 对接文档 V2.0
- PdfSharpCore 1.3.64 引入(纯托管,Unity IL2CPP 兼容,+9 DLL)
- ReportData 结构化模型 + MarkdownRenderer + StandardPdfTemplate
- IReportService.ExportReport(id, format) 按需导出 PDF/MD
- 仿真后自动生成 MD 到 reports 目录,PDF 按需调用
- CJK 字体嵌入(SimHei),IPathProvider + GetFontPath
- ReportGenerator 重构为构建 ReportData,去掉 emoji
- check_unity_build.ps1 修复 -quit 参数缺失导致超时
- 对接文档 V2.0:坐标系/3D 可视化/完整枚举/Manager 签名/模型字段
- 262 测试全部通过
2026-06-20 11:57:38 +08:00

1944 lines
94 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 反无人机仿真系统 — 总体架构设计
> **版本**V15
> **日期**2026-06-17
> **状态**:已实现
> **变更**V14 新增 DataService 基础数据 CRUD + FrameDataStore 内存回放架构
> **Unity 版本**22.3.62
> **.NET 版本**.NET Standard 2.1
> **变更**V13 探测行为决策固化——5 项规则确认(回退 Undetected / 融合取最早-同刻取精度高 / 不影响 FireSchedule / 盲区仅可视化 / 3D 球冠);新增 14.2.1 三维球冠几何IsInCoverage+ 14.7 数据模型变更清单ScenarioUnit 表加 4 个 3D 探测字段DetectionSource 同步扩展
> **历史变更**V12 探测双链路设计(静态规划+实时探测待实现V11 文档校准 + 第十三章性能设计V10 Group 表移除,批次+火力单元概念升级
---
## 一、系统概述
本系统对新型气溶胶武器的三种防空毁伤机制(吸入式灭火、爆燃式、吸入式爆炸)进行数字化仿真。通过参数化配置与可视化推演,量化评估新型气溶胶武器对不同类型空中威胁源目标(动力系统)的毁伤效能。
系统分为三大模块:
| 模块 | 核心功能 |
|------|----------|
| **模型管理** | 3D 模型导入/预览/删除 |
| **想定管理** | 仿真任务 CRUD步骤化配置向导管控区域设置搜索分页 |
| **仿真报告** | 报告列表,含时序图的完整报告预览,导出 PDF/Word删除 |
### 要求来源对照
本设计融合两份需求文档:
| 文档 | 侧重 |
|------|------|
| V1.0 反无人机仿真系统功能需求 | UI 交互、业务流程、业务字段 |
| 技术要求终版 | 毁伤机制、装备体系、可视化效果、性能指标、第三方算法集成 |
---
## 二、架构分层
```
┌──────────────────────────────────────────────────────────┐
│ Unity UI Layer │
│ 模型管理 │ 想定管理(含管控区域) │ 仿真运行&回放 │ 仿真报告 │
├──────────────────────────────────────────────────────────┤
│ Unity Application Layer │
│ ModelPanel | ScenarioWizard | SimulationRunner | │
│ ReplayController | ReportPanel │
├──────────────────────────────────────────────────────────┤
│ Service Layer (Pure C#) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ ModelService │ │ScenarioService│ │ SimulationEngine │ │
│ │ │ │ DefensePlanner │ │ ├ DroneEntity │ │
│ └──────────────┘ └──────────────┘ │ ├ PlatformEntity │ │
│ ┌──────────────┐ ┌──────────────┐ │ ├ MunitionEntity │ │
│ │FrameDataStore│ │ReportService │ │ ├ CloudEntity │ │
│ │ │ │ ├ ReportGen │ │ │ └ GaussianPuff│ │
│ └──────────────┘ │ └ Markdown │ │ ├ ControlZone │ │
│ └──────────────┘ │ ├ IDamageModel×3 │ │
│ │ └ FireSchedule │ │
│ ┌──────────────────────────────────────────────────────┐│
│ │ Algorithm Layer (自主实现) ││
│ │ GaussianPuffDispersion | DamageCalculator ││
│ │ DefaultDefensePlanner | Kinematics(PG稳定度) ││
│ └──────────────────────────────────────────────────────┘│
├──────────────────────────────────────────────────────────┤
│ Data Layer │
│ ┌────────────────────────────┐ ┌────────────────────┐ │
│ │ main.db (SQLite) │ │ frames/ (SQLite) │ │
│ │ 11张业务表 + SimEvent │ │ 每任务一个 .db │ │
│ │ + SimulationReport │ │ SimFrameRecord │ │
│ │ < 100MB永久保留 │ │ 6个月 TTL │ │
│ └────────────────────────────┘ └────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ FileSystem │ │
│ │ 模型文件(.fbx/.obj/.stl/.glb/.gltf) + 预览图 │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
### 各层职责
| 层 | 依赖 | 职责 |
|----|------|------|
| **Data Layer** | 主库 SQLite + 任务分库 SQLite + 文件系统 | 主库存元数据/事件/报告(<100MB永久保留分库按任务存帧数据6月TTL文件系统存模型文件 |
| **Algorithm Layer** | C#零外部依赖 | 气溶胶扩散模型毁伤计算防御推荐算法全部自主实现 |
| **Service Layer** | Data Layer + Algorithm Layer | 纯业务逻辑仿真引擎录制报告生成 |
| **Application Layer** | Service Layer + Unity | MonoBehaviour 桥接生命周期协调驱动仿真 |
| **UI Layer** | Application Layer | UGUI 页面弹窗3D 预览回放粒子参数传递 |
### 2.1 程序集拆分Core 独立于 Unity
后端代码拆分为两个程序集确保 Core 可脱离 Unity 独立编译和测试
```
CounterDrone.Core.dll ← .NET Standard 2.1 类库
零 UnityEngine 引用
可在任何 .NET 环境运行
├── /Models 数据模型POCO
├── /Repository 数据访问SQLite + 文件存储)
├── /Services 业务服务ModelService, ScenarioService 等)
├── /Simulation 仿真引擎 + 实体SimulationEngine, DroneEntity, ...
└── /Algorithms 扩散模型、毁伤模型、防御推荐
CounterDrone.Core.Tests.dll ← xUnit 测试项目
直接引用 Core.dll
dotnet test 即可运行
├── /Unit 单元测试(每个类独立测)
└── /Integration 集成测试(完整仿真流程)
Unity 项目/Assets/Scripts/ ← Unity 侧
├── /Managers MonoBehaviours 桥接层(薄)
└── /UI UGUI 绑定
引用 → CounterDrone.Core.dll放在 Assets/Plugins/
```
### 2.2 Unity 依赖隔离:关键设计
| 隔离点 | 做法 |
|------|------|
| **时间** | `engine.Tick(float deltaTime)` Unity `Time.deltaTime`测试传固定值 `0.05f` |
| **文件路径** | Core 使用接口 `IPathProvider` Unity 实现返回 `Application.persistentDataPath`测试返回 `./testdata/` |
| **日志** | Core 使用 `Microsoft.Extensions.Logging` 抽象 Unity 桥接到 `Debug.Log`测试用控制台 |
| **模型加载** | Core 不涉及 3D 渲染只管理文件路径和元数据 |
| **粒子参数** | Core 输出 `ParticleParams` 数据对象 Unity 读值设置 ParticleSystem |
### 2.3 Manager 桥接层示例(极薄)
```csharp
// Unity 侧 SimulationRunner.cs — 仅负责桥接
public class SimulationRunner : MonoBehaviour
{
private ISimulationEngine _engine;
private IRecordService _recorder;
void Start()
{
// 通过 DI 或工厂获取实例Core 创建)
_engine = SimulationEngineFactory.Create(scenarioId);
_recorder = ServiceFactory.CreateRecordService();
}
void Update()
{
if (_engine.State != SimulationState.Running) return;
// 核心只有这一行:把 Unity 时间传入纯 C# 引擎
var result = _engine.Tick(Time.deltaTime);
// 更新 Unity 侧的 3D 物体位置(根据 result 中的实体快照)
UpdateVisuals(result.EntitySnapshots);
}
void UpdateVisuals(List<EntitySnapshot> snapshots)
{
foreach (var s in snapshots)
{
// 找到对应 GameObject设置 transform
_entityMap[s.EntityId].transform.position = s.Position;
}
}
}
```
### 2.4 测试策略
| 测试类型 | 工具 | 内容 |
|------|------|------|
| **单元测试** | xUnit + Moq | Repository 增删改查Service 业务逻辑Algorithm 计算正确性 |
| **仿真确定性测试** | xUnit | 固定输入 + 固定 deltaTime 断言帧 N 的实体位置 = 预期值 |
| **集成测试** | xUnit + SQLite 文件 | 完整想定配置 启动仿真 1000 断言事件序列和对抗结果 |
| **性能验证** | 实测帧率 | Unity 实测满载场景渲染帧率从优化前 <20 FPS 提升至 200+ FPS详见第十四章 |
> Core 测试无需 Unity`dotnet test` 即可运行全部测试,适合 CI 流水线。
---
## 三、枚举定义
```csharp
// === 任务 ===
enum ScenarioStatus { Draft = 0, Configuring = 1, Completed = 2 }
// === 场景 ===
enum SceneType { Urban = 0, Plain = 1, Mountain = 2, Coast = 3 }
enum WeatherType { Sunny = 0, Overcast = 1, Fog = 2, Rain = 3, Night = 4 }
enum WindDirection { N = 0, NE = 1, E = 2, SE = 3, S = 4, SW = 5, W = 6, NW = 7 }
// === 目标 ===
enum DroneType { Rotor = 0, FixedWing = 1, HighSpeed = 2 }
enum PowerType { Electric = 0, Piston = 1, Jet = 2 }
// === 装备 & 搭载平台 ===
enum PlatformType { AirBased = 0, GroundBased = 1 } // 空基(大型无人机) / 地基(炮弹)
enum EquipmentRole { Detection = 0, LaunchPlatform = 1 } // 装备角色:探测 / 发射平台
// === 气溶胶 & 毁伤 ===
enum AerosolType { InertGas = 0, ActiveMaterial = 1, ActiveFuel = 2 }
// 对应的毁伤机制名称:吸入式灭火 / 爆燃式 / 吸入式爆炸
enum ReleaseMode { Single = 0, Continuous = 1, Pulse = 2 }
enum TriggerMode { Time = 0, Area = 1, Manual = 2 }
// === 毁伤状态阶段EntitySnapshot.DamageStage前端直读入库时进 StateData JSON===
enum DamageStage { Normal = 0, EngineAnomaly = 1, AttitudeLoss = 2, Destroyed = 3 }
// === 编队 ===
enum FormationMode { Single = 0, Formation = 1, Swarm = 2 }
// === 核心作战概念 ===
// 发射平台LaunchPlatform搭载弹药并实施打击的装备实体。
// 配置层 = LaunchPlatformSpec基础参数→ ScenarioUnit(LaunchPlatformSpecId, SensorSpecId)。
// 探测设备Sensor雷达/光电/红外传感器,可独立部署或随发射平台部署。
// 配置层 = SensorSpec基础参数→ ScenarioUnit(SensorSpecId)。
// 发射平台自带探测 = ScenarioUnit 同时设置 LaunchPlatformSpecId + SensorSpecId。
// 独立探测节点 = ScenarioUnit 仅设置 SensorSpecId + EquipmentRole=Detection。
// 两者都往统一信息网络送探测信息planner 基于融合后的探测边界规划。
// 无人机批次DroneWave有共同航路的一组无人机攻击方
// 批次关联航路但不独占(多个批次可共享同一条航路)。
// 配置层 = ScenarioDrone(含 WaveId) + RoutePlan(ScenarioId, WaveId) + Waypoints。
// === 运行时 ===
enum EntityType { Drone = 0, Platform = 1, DetectionEquip = 2, Cloud = 3, Munition = 4 }
enum SimEventType
{
TargetDetected = 0, // 探测发现目标【待实现,当前未触发,详见第十四章】
MunitionLaunched = 1,
CloudGenerated = 2,
DroneEnteredCloud = 3,
DroneDestroyed = 4,
DroneReachedTarget = 5,
ZoneIntruded = 6, // 无人机侵入管控区域
WaypointReached = 7,
SimulationEnd = 8
}
// === 报告 ===
enum InterceptResult { Success = 0, Partial = 1, Failed = 2 }
enum PositionMode { PointSelect = 0, ManualInput = 1 }
```
---
## 四、数据模型设计
### 4.1 存储架构:主库 + 任务分库
```
CounterDroneBackend_Data/
├── main.db ← 主库(< 100MB永久保留
│ ├── 11 张业务配置表
│ ├── SimEvent ← 事件表(~30 条/任务)
│ └── SimulationReport
├── frames/ ← 帧数据分库6 个月 TTL
│ ├── {scenarioId_001}.db ← 单任务 ~161 MB30min 仿真)
│ └── {scenarioId_002}.db
└── models/ ← 模型文件 + 预览图
```
### 4.2 数据表总览
11 张业务表主库 + 1 张运行时表主库 + 1 张运行时表任务分库
```
主库main.db— 永久保留:
ModelInfo — 模型元数据
AmmunitionSpec — 弹药基础参数第三方数据NEW
Scenario — 仿真任务主表
CombatScene — 步骤1作战场景含24h时间、天气
ControlZone — 步骤1+:管控区域/电子围栏NEW
ScenarioDrone — 步骤2目标配置含 WaveId 关联批次)
ScenarioUnit — 步骤3装备部署发射平台引用 LaunchPlatformSpec + 可选 SensorSpec 探测,或独立探测设备引用 SensorSpec
CloudDispersal — 步骤4云团抛撒配置
RoutePlan — 步骤5航路规划1:N Scenario多批次批次关联但不独占航路FK 关联 ScenarioDrone.WaveId
Waypoint — 航路点
SimulationReport — 仿真报告
SimEvent — 仿真事件(时序图数据源)
任务分库frames/{scenarioId}.db— 6 个月 TTL
SimFrameRecord — 逐帧位置记录(回放用)
```
> **核心作战概念**
> - **火力单元FireUnit**:能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环。配置层 = `ScenarioUnit(LaunchPlatform)`。
> - **独立探测节点DetectionNode**:只探测不打击的侦查节点(如前沿警戒雷达)。配置层 = `ScenarioUnit(Detection)`。
> - **无人机批次DroneWave**:有共同航路的一组无人机(攻击方)。批次关联航路但不独占(多个批次可共享同一条航路)。配置层 = `ScenarioDrone(WaveId)` + `RoutePlan(ScenarioId, WaveId)` + `Waypoints`。
> - 原来的 `Group`(编组)表已移除:无人机编队概念升级为"批次",装备编组概念不再需要(火力单元是独立作战单位)。
### 4.3 业务表详细设计
#### ModelInfo — 模型元数据
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| Name | TEXT | NOT NULL | 模型名称 |
| ModelType | TEXT | | 分类标签 |
| FilePath | TEXT | | persistentDataPath 下的相对路径 |
| FileSize | REAL | | MB |
| PreviewPath | TEXT | | 预览图路径 |
| HasHighResTexture | INTEGER | DEFAULT 0 | 是否含 2K+ 贴图 |
| CreatedAt | TEXT | | 导入时间 ISO8601 |
> 支持格式:**.fbx / .obj / .stl / .glb / .gltf**,单文件 ≤ 500MB支持 2K 及以上分辨率贴图。
#### Scenario — 仿真任务主表
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| Name | TEXT | NOT NULL | 任务名称 |
| ScenarioNumber | TEXT | UNIQUE | SIM-yyyyMMdd-xxx |
| Description | TEXT | | 想定描述 |
| Status | INTEGER | DEFAULT 0 | 0=草稿 1=配置中 2=已完成 |
| CurrentStep | INTEGER | DEFAULT 1 | 当前配置步骤 1-5 |
| TickRate | INTEGER | DEFAULT 20 | 仿真 Tick 频率Hz |
| CreatedAt | TEXT | | |
| UpdatedAt | TEXT | | |
#### CombatScene — 步骤1作战场景
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| ScenarioId | TEXT | PK, FK | 1:1 Scenario |
| SceneType | INTEGER | | 0城市 1平原 2山区 3海岸 |
| TimeOfDay | TEXT | DEFAULT "12:00" | **NEW** 24h 制时间 "14:30" |
| WeatherType | INTEGER | | 0晴 1阴 2雾 3雨 4夜 |
| WindSpeed | REAL | DEFAULT 5 | m/s范围 0-30 |
| WindDirection | INTEGER | DEFAULT 0 | 八方位 0-7 |
| Temperature | REAL | DEFAULT 20 | |
| Humidity | REAL | DEFAULT 60 | %RH |
| Pressure | REAL | DEFAULT 1013 | hPa |
| Visibility | REAL | DEFAULT 5000 | m范围 100-10000 |
| SceneWidth | REAL | DEFAULT 4000 | **NEW** 场景宽 m(≥4m |
| SceneLength | REAL | DEFAULT 10000 | **NEW** 场景纵深 m(≥100m |
| SceneHeight | REAL | DEFAULT 1000 | **NEW** 场景高 m(≥10m |
> 场景空间范围最低要求4m × 100m × 10m。默认值 4000m × 10000m × 1000m 可配置。
#### ControlZone — 步骤1扩展管控区域 / 电子围栏NEW
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| ScenarioId | TEXT | FK | |
| Name | TEXT | | 区域名称 |
| VerticesJson | TEXT | NOT NULL | 多边形顶点 JSON`[{"x":0,"y":0,"z":0},...]` |
| MinAltitude | REAL | | 区域底高 m |
| MaxAltitude | REAL | | 区域顶高 m |
| OrderIndex | INTEGER | | 多个区域的顺序 |
> 管控区域在场景设置(地形+天气)之后配置。无人机进入管控区域 → 触发 ZoneIntruded 事件 → 该无人机任务失败。
#### ScenarioDrone — 步骤2无人机批次参数
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| ScenarioId | TEXT | FK | |
| WaveId | TEXT | FK | 关联无人机批次同一 WaveId 的无人机共享航路 |
| DroneType | INTEGER | | 0旋翼 1固定翼 2高速 |
| Model | TEXT | | 型号 |
| Description | TEXT | | 描述/用途 |
| Quantity | INTEGER | DEFAULT 1 | 目标数量 |
| PowerType | INTEGER | | 0电推 1活塞 2喷吸气 |
| Wingspan | REAL | | m |
| TypicalSpeed | REAL | | km/h |
| TypicalAltitude | REAL | | m |
> 目标类型默认值:
| 目标类型 | 翼展(m) | 典型速度(km/h) | 典型飞行高度(m) |
|----------|---------|---------------|----------------|
| 旋翼 | 1.2 | 60 | 300 |
| 固定翼 | 3.5 | 150 | 1000 |
| 高速目标 | 1.5 | 300 | 2000 |
#### ScenarioUnit — 步骤3装备部署火力单元 + 独立探测节点)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| ScenarioId | TEXT | FK | |
| EquipmentRole | INTEGER | | 0=独立探测节点 1=火力单元(发射平台) |
| Quantity | INTEGER | DEFAULT 1 | 部署数量 |
| WaveId | TEXT | NULLABLE | 可选关联批次用于 UI 分组火力单元是独立作战单位不强依赖批次 |
| Description | TEXT | | 部署备注 |
| // 以下为火力单元发射平台专用字段 |
| PlatformType | INTEGER | NULLABLE | 0=空基(大型无人机) 1=地基(炮弹),独立探测节点为 NULL |
| PositionX | REAL | | 部署位置 |
| PositionY | REAL | | |
| PositionZ | REAL | | |
| AerosolType | INTEGER | NULLABLE | 挂载气溶胶类型独立探测节点为 NULL |
| MunitionCount | INTEGER | NULLABLE | 挂载弹药数量 |
| MuzzleVelocity | REAL | NULLABLE | **NEW** 弹药初速 m/s地基炮弹用 |
| ReleaseAltitude | REAL | NULLABLE | **NEW** 弹药释放高度 m空基平台用 |
| Cooldown | REAL | DEFAULT 5 | 发射冷却时间 s同弹种连发间隔 |
| AmmoChangeTime | REAL | DEFAULT 300 | **NEW** 更换弹种所需时间 s默认 5 分钟 |
| // 探测能力火力单元自带 + 独立探测节点均有 |
| RadarRange | REAL | NULLABLE | 雷达探测距离 m雨雾不衰减 |
| EORange | REAL | NULLABLE | 光电探测距离 m Visibility 衰减 |
| IRRange | REAL | NULLABLE | 红外探测距离 m不受 Visibility 影响 |
| DetectionAccuracy | REAL | NULLABLE | 探测精度 m位置误差影响抛撒散布范围 |
| // 三维球冠几何决策 5待新增字段详见第十四章 |
| MinElevation | REAL | NULLABLE | 俯仰角下限 |
| MaxElevation | REAL | NULLABLE | 俯仰角上限 |
| MinDetectAlt | REAL | NULLABLE | 可探测高度下界 m |
| MaxDetectAlt | REAL | NULLABLE | 可探测高度上界 m |
> **装备体系说明**
> - **搭载平台**2 种):
> - 空基(大型无人机):飞到释放点投放弹药 → 弹药自由落体/滑翔至云团生成点
> - 地基(炮弹):地面发射 → 抛物线弹道 → 到达释放点生成云团
> - **探测设备**:目标搜索发现与跟踪,输出目标坐标给发射平台
> - 弹药类型决定生成云团的 AerosolType 和覆盖体积
#### CloudDispersal — 步骤4云团抛撒配置
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| ScenarioId | TEXT | PK, FK | 1:1 Scenario |
| AerosolType | INTEGER | | 0吸入式灭火 1爆燃式 2吸入式爆炸 |
| DisperseHeight | REAL | DEFAULT 300 | m |
| TriggerMode | INTEGER | | 0时间 1区域 2手动 |
| SpreadAngle | REAL | DEFAULT 0 | |
| ReleaseMode | INTEGER | | 0单次 1连续 2脉冲 |
| InitialScale | REAL | DEFAULT 1000 | m³ |
| Duration | REAL | DEFAULT 60 | s |
| ReleaseInterval | REAL | DEFAULT 5 | s |
| TotalAmount | REAL | DEFAULT 50 | kg |
| PositionX | REAL | | 抛撒坐标 |
| PositionY | REAL | | |
| PositionZ | REAL | | |
| PositionMode | INTEGER | | 0三维点选 1手动输入 2算法推荐NEW |
| // 算法推荐结果NEW |
| RecommendedPosX | REAL | NULLABLE | 算法推荐最佳抛撒位置 |
| RecommendedPosY | REAL | NULLABLE | |
| RecommendedPosZ | REAL | NULLABLE | |
| RecommendedTiming | REAL | NULLABLE | 算法推荐最佳触发时机仿真秒 |
| CriticalPosX | REAL | NULLABLE | 最危险值抛撒位置 |
| CriticalPosY | REAL | NULLABLE | |
| CriticalPosZ | REAL | NULLABLE | |
| CriticalTiming | REAL | NULLABLE | 最危险值触发时机 |
#### RoutePlan — 步骤5航路规划
> **支持多批次**:通过 `ScenarioId + WaveId` 唯一标识一条航路,一个任务可有多个批次各自独立航路。
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| ScenarioId | TEXT | PK(复合) | |
| WaveId | TEXT | PK(复合) | 关联批次同一任务不同批次可有独立航路多个批次可共享同一条航路 |
| FormationMode | INTEGER | | 0单机 1编队 2蜂群 |
| FormationSpacing | REAL | DEFAULT 50 | m |
| ETA | TEXT | | 预计到达时间 |
#### Waypoint — 航路点
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| ScenarioId | TEXT | FK | |
| OrderIndex | INTEGER | NOT NULL | 航路顺序 |
| PosX | REAL | | 坐标支持三维点选 |
| PosY | REAL | | |
| PosZ | REAL | | |
| Altitude | REAL | | 该航点飞行高度 m |
| Speed | REAL | | 该段速度 km/h |
#### ~~Group~~ — 编组(已移除)
> **概念升级**:原 Group 表的两种类型已分别升级:
> - `DroneFleet`(无人机编队)→ **无人机批次DroneWave**:由 `ScenarioDrone(WaveId)` + `RoutePlan(ScenarioId, WaveId)` + `Waypoints` 隐式表达。批次关联航路但不独占(多个批次可共享同一条航路)。
> - `EquipmentGroup`(装备编组)→ **不再需要**。火力单元是独立作战的最小单位,不需要上层编组。
>
> 数据层:`Group` 表保留但不再主动使用(向后兼容)。`ScenarioDrone.WaveId` 语义为"批次 ID"。
### 4.4 运行时表
> **注意**SimEvent 和 SimulationReport 存储在主库 main.dbSimFrameRecord 存储在各任务的独立分库 frames/{scenarioId}.db 中。
#### SimFrameRecord — 逐帧记录
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | INTEGER | PK AUTOINCREMENT | |
| ScenarioId | TEXT | FK | |
| FrameIndex | INTEGER | | 帧序号 |
| Timestamp | REAL | | 仿真时间 |
| EntityId | TEXT | | |
| EntityType | INTEGER | | 实体类型枚举 |
| PosX | REAL | | |
| PosY | REAL | | |
| PosZ | REAL | | |
| RotX | REAL | | |
| RotY | REAL | | |
| RotZ | REAL | | |
| StateData | TEXT | | JSON EntityType 决定结构。** Flush 入库时生成**仿真期间保持空 |
> **StateData 双轨设计**:仿真热路径上 `EntitySnapshot` 携带**强类型字段**`Hp`/`CloudRadius`/`CloudOpacity` 等),前端直接读取,零 JSON 解析;仅 `FrameDataStore.Flush` 入库时按 `EntityType` 序列化为 JSON 字符串写入 `SimFrameRecord.StateData`,供历史回放解析。
>
> StateData JSON 按实体类型差异化(字段名与 Flush 中模板严格一致):
> ```json
> // Drone
> { "damageStage": 0, "hp": 0.75 } // DamageStage 枚举;剩余血量 0~1
> // Platform仅空基平台记录快照
> { "state": "Flying" } // 平台状态字符串
> // Cloud
> { "radius": 150.0, "opacity": 0.8, "phase": 2, "elapsed": 35.0 }
> // radius=当前半径m, opacity=透明度, phase=扩散阶段(1/2/3), elapsed=已存在秒数
> ```
#### SimEvent — 仿真事件(时序图数据源)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| ScenarioId | TEXT | FK | |
| OccurredAt | REAL | | 仿真时间 |
| EventType | INTEGER | | 事件类型枚举 |
| SourceId | TEXT | | 源实体 ID |
| TargetId | TEXT | | 目标实体 ID |
| DataJson | TEXT | | 事件携带的结构化数据 |
| Description | TEXT | | 事件描述 |
---
## 五、仿真引擎设计
### 5.1 核心实体关系
```
SimulationEngine
├── SimulationState ← 状态(运行/暂停/结束)
├── SceneConfig ← 配置快照(含场景边界、管控区域)
├── List<DroneEntity> ← 无人机(目标方)
│ └── 按批次组织,共享航路
├── List<PlatformEntity> ← 搭载平台(发射方)★重构
│ ├── AirBasedPlatform ← 空基(大型无人机)
│ └── GroundBasedPlatform ← 地基(火炮)
├── List<DetectionEntity> ← 探测设备(统一信息网络)★实时探测【待实现】
│ ├── 持有 DetectionSource位置/雷达/光电/红外距离/精度)
│ └── 每个威胁的探测状态机Undetected → Detected → Tracked
├── List<MunitionEntity> ← 飞行中的弹药
│ ├── 空基投放:短程自由落体/滑翔
│ └── 地基发射:抛物线弹道
├── List<CloudEntity> ← 气溶胶云团 ★核心毁伤载体★
│ └── GaussianPuffDispersion三阶段扩散
├── List<ControlZone> ← 管控区域(电子围栏)
├── FireSchedule ← 发射计划(推荐算法生成)
└── FrameDataStore ← 帧数据写入(任务分库)
```
> **探测设备的两阶段角色**(详见第十五章):
> - **事前规划阶段**(已实现):`DefensePlanner` 调用 `DetectionCalculator.EarliestDetection` 静态估算每个威胁的最早发现弧长,反推拦截时机。
> - **运行时实时探测**(待实现):`DetectionEntity` 在 Tick 内扫描飞行无人机,首次进入探测范围 → 触发 `TargetDetected` 事件。两阶段共用同一 `EffectiveRange` 物理公式保证一致。
### 5.2 拦截链路(完整流程)
系统存在**两条并行的探测链路**分别服务于"事前规划""运行时观测"
```
═══ 链路 A事前规划已实现开环═════════════════════════════
仿真启动前 DefensePlanner.Plan()
DetectionCalculator.EarliestDetection(threatRoute, sources, visibility)
│ 静态计算每个威胁航路进入探测网的最早弧长
拦截时机 = 最早发现弧长 + 弹药飞行时间反推
│ 一次性生成 FireSchedule开环不依赖运行时观测
SimulationEngine 按 FireSchedule 时间表执行发射
═══ 链路 B运行时实时探测待实现闭环观测═════════════════
Tick 内 DetectionEntity 扫描飞行无人机:
│ 首次进入 EffectiveRange → 状态 Undetected→Detected
触发 OnTargetDetectedTargetDetected 事件 → 写 SimEvent
│ 输出目标当前坐标(含探测精度散布)
持续跟踪Detected→Tracked→ 坐标更新
│ (未来:驱动动态火控重分配 / 自适应拦截)
与链路 A 的静态计划协同:观测印证规划 / 补救漏网目标
═══ 公共后段(两条链路最终汇入)═══════════════════════════════
火控分配(分配给可用搭载平台)
├── 空基平台:固定阵位发射(水平初速 + 重力下落)
│ │
│ ▼
│ 弹药飞行至目标高度
└── 地基平台:计算射击诸元
发射炮弹(抛物线弹道飞向目标区域)
弹药到达释放点 → 生成 CloudEntity
MunitionEntity 销毁
云团扩散膨胀(三阶段模型:爆轰→湍流→高斯)
│ 持续膨胀 + 消散计时
无人机进入云团范围
│ 施加毁伤IDamageModel 接口,三种机制自主实现)
│ 吸入式灭火 / 爆燃式 / 吸入式爆炸
无人机被摧毁 / 存活
│ 毁伤阶段状态输出到 StateData
Unity 渲染毁伤效果(尾焰异常、姿态失控、坠落)
```
> **当前实现状态**:链路 A静态规划已完整实现是拦截时机的唯一来源链路 B实时探测尚未接入 Tick`TargetDetected` 事件已定义但未触发。详见第十五章探测设备设计。
### 5.3 仿真 Tick 流程
```
1. 【发射阶段】
按 FireSchedule 执行:
├─ SimulationTime ≥ FireTime → 平台直接发射弹药
├─ 空基发射:弹药以巡航速度水平射出,重力下落到目标高度
└─ 触发 OnMunitionLaunched 事件
2. 【弹药飞行 → 云团生成】
FOR EACH MunitionEntity
├─ 抛物线弹道(地基)/ 自由落体+水平惯性(空基)
└─ 到达释放高度 → AlgorithmFactory.Create<ICloudDispersionModel>() → 生成 CloudEntity
→ 触发 OnCloudGenerated弹药移除倒序索引
3. 【云团演化】
FOR EACH CloudEntity
├─ GaussianPuffDispersion.Tick():三阶段扩散(爆轰→湍流→高斯)
├─ 更新粒子参数(半径、透明度)
└─ 消散条件满足 → 移除(倒序索引)
4. 【无人机移动】
├─ 预过滤 Flying 状态无人机到复用缓冲 _flyingDrones避免多次遍历
├─ SavePreviousPosition为毁伤路径积分保存上一帧位置
└─ FOR EACH 飞行 DroneEntityUpdate(deltaTime)(弧长驱动,详见 5.4
到达终点 → ReachedTarget → 触发 OnDroneReachedTarget
5. 【实时探测扫描】★待实现(见第十五章)
FOR EACH DetectionEntity × 飞行无人机:
├─ effRange = DetectionCalculator.EffectiveRange(radar, eo, ir, visibility)
├─ ‖drone - detector‖ ≤ effRange
├─ 是 且 drone 探测状态 == Undetected → 状态置 Detected
│ ├─ 触发 OnTargetDetectedTargetDetected 事件SourceId=探测设备, TargetId=无人机)
│ └─ 输出含精度散布的目标坐标(散布半径 = DetectionCalculator.SpreadRadius(accuracy)
├─ 持续在范围内 → 状态升 Tracked坐标持续刷新
└─ 离开范围 → (策略待定:保持 Tracked 或回退)
> 当前此步缺失:探测只在事前 planner 静态计算(链路 ATick 内无实时探测。
6. 【毁伤判定】 FOR EACH 飞行无人机 × CloudEntity
├─ 快速排斥 1cloud.CoreDensity < 有效浓度阈值 → 跳过
├─ 快速排斥 2‖drone - cloudCenter‖ > 2×EffectiveRadius → 跳过(不可能穿云)
├─ DamageAssessment.PathInSphere积分无人机本帧路径段在球体内的长度 inCloudDistance
│ (云团参考系修正:扣除本帧风偏位移 cloudD×dt使无人机表现为"穿云"
├─ exposureIncrement = inCloudDistance / (TypicalSpeed/3.6)
├─ drone.ExposureTime += exposureIncrement
├─ dmg = IDamageModel.CalculateDamage(...)drone.ApplyDamage(dmg)
└─ HP ≤ 0 → Destroyed → 触发 OnDroneDestroyed
7. 【平台冷却】
FOR EACH PlatformEntityUpdate(deltaTime)
8. 【管控区域判定】
FOR EACH 飞行无人机 × ControlZone
├─ 高度区间检查 + Point-in-Polygon顶点 2D 数组构造时缓存,每帧零分配)
└─ 侵入 → MarkZoneIntruded → 触发 OnZoneIntruded
9. 【结束判定】
所有无人机 Status ≠ Flying → SimulationState.Completed → 触发 OnSimulationEnded
10. 【录制】(若 RecordFrames
├─ CollectSnapshots填充强类型字段位置/Hp/云团半径等)到复用池,零字符串分配
└─ FrameDataStore.RecordFrame仅追加内存缓冲 List<FrameRecord>
```
### 5.4 实体行为模型
#### DroneEntity无人机
```
属性:
- 批次 WaveId、航路点队列 Waypoint[]
- 当前位置、速度、朝向
- HP0~1 归一化)
- DamageStageNormal → EngineAnomaly → AttitudeLoss → Destroyed
- 目标类型 DroneType、动力类型 PowerType
- 存活状态(飞行中/抵达目标/侵入管控区/被摧毁)
运动模型(弧长驱动,统一在 RouteGeometry
- 构造时一次性预计算航路几何缓存_totalArc总弧长、_segLen[]各段段长、_cumArc[](累积弧长)
- 每 Tick_traveledArc += (TypicalSpeed/3.6) × deltaTime
- 在 _cumArc[] 中定位所在段,分段线性插值得到 3D 位置
- 无人机严格按航路匀速飞行,**不受风偏影响**(真实无人机有飞控修正航向)
- 云团受风偏影响 → 毁伤判定中对云团做参考系修正(详见 5.3 第4步
- 到达判定_traveledArc ≥ _totalArc → ReachedTarget
结束条件(任一触发即该无人机结束):
- 被毁伤HP ≤ 0→ Destroyed
- 到达攻击目标 → ReachedTarget
- 侵入管控区域 → ZoneIntruded
```
#### PlatformEntity搭载平台★重构
```
空基平台AirBasedPlatform
- 固定阵位
- 挂载弹药类型 AerosolType、数量 MunitionCount
- 巡航速度 CruiseSpeed
- 冷却计时器
- 行为:按发射计划从固定位置水平射出弹药,弹药重力下落到目标高度
地基平台GroundBasedPlatform
- 固定位置
- 弹药初速 MuzzleVelocity
- 挂载弹药类型 AerosolType、数量 MunitionCount
- 冷却计时器
- 行为:接收目标坐标 → 计算射击诸元 → 发射炮弹
```
#### MunitionEntity飞行中弹药★重构
```
属性:
- 弹药类型 AerosolType
- 发射方式 LaunchModeAirDrop / GroundArtillery
- 当前位置、速度
- 目标坐标(云团生成点)
行为(统一抛物模型):
- 地基:初速 MuzzleVelocity + 发射角 → 抛物线弹道Kinematics.ParabolicPosition
- 空基:初速 CruiseSpeed + 水平发射(θ=0°→ 重力下落Kinematics.ParabolicPosition
- 到达条件Y 坐标到达释放高度
```
#### CloudEntity气溶胶云团
```
属性:
- 气溶胶类型 AerosolType
- 中心坐标
- 当前半径(调用第三方 DLL 扩散模型计算)
- 最大半径
- 持续时间、剩余时间
- 粒子系统参数(半径、透明度、颜色 → 输出到 StateData
行为(委托第三方 DLL
- 扩散膨胀GaussianPuffDispersion.Tick()(三阶段)
- 消散计时
- 毁伤判定IDamageModel.CalculateDamage()(三种机制自主实现)
```
---
## 六、算法设计 — 接口驱动 & 可替换架构 ★关键
### 设计原则
所有算法组件通过**接口隔离**SimulationEngine 只依赖接口不感知具体实现每个算法组件可独立替换为第三方实现无需修改引擎代码
```
SimulationEngine
┌─────┼─────┐
▼ ▼ ▼
ICloudDispersionModel ← 扩散模型接口
IDamageModel ← 毁伤模型接口
IDefensePlanner ← 防御规划接口
┌───────┼───────┐
▼ ▼ ▼
自主实现 第三方实现 混合(部分自研+部分外购)
(Default) (Future) (Future)
```
### 6.1 算法工厂与注册
```csharp
/// <summary>算法工厂 — 统一管理所有算法组件的创建与替换</summary>
public static class AlgorithmFactory
{
// 注册表:每个接口 → 创建委托Func<object>
private static readonly Dictionary<Type, Func<object>> _registry = new()
{
[typeof(ICloudDispersionModel)] = () => new GaussianPuffDispersion(),
[typeof(IDamageModel)] = () => new DamageModelRouter(),
};
/// <summary>替换某个算法实现(第三方接入点)</summary>
public static void Register<TInterface>(Func<object> factory)
{
_registry[typeof(TInterface)] = factory;
}
/// <summary>创建实例SimulationEngine 统一调用此方法</summary>
public static T Create<T>() where T : class
{
if (_registry.TryGetValue(typeof(T), out var factory))
return (T)factory();
throw new InvalidOperationException($"No implementation for {typeof(T).Name}");
}
}
```
> **注**`IDefensePlanner` 由 DI 直接注入 `SimulationEngine`(不经过 AlgorithmFactory因为它是单例策略对象不是每云团/每帧 new。AlgorithmFactory 仅管理**每实例**的算法组件(云团扩散、毁伤模型)。
>
> **替换示例**:将来第三方提供 `ThirdPartyDispersion.dll`,只需一行:
> `AlgorithmFactory.Register<ICloudDispersionModel>(() => new ThirdPartyDispersion());`
> 无需改动 SimulationEngine 任何代码。
### 6.2 第三方提供的数据
```csharp
/// <summary>第三方提供的弹药基础参数(数据配置,非运行时 DLL</summary>
public class AmmunitionSpec
{
public AerosolType AerosolType; // 气溶胶类型
public string Name; // 弹药名称
// 初始扩散状态
public float InitialRadius; // 初始云团半径m
public float InitialVolume; // 初始云团体积
public float CoreDensity; // 核心密度kg/m³
public float EdgeDensity; // 边缘密度kg/m³
public float InitialTemperature; // 初始温度K
public float BuoyancyFactor; // 浮力系数
// 弹药基础参数
public float EffectiveConcentration; // 有效毁伤浓度阈值kg/m³
public float DispersionRateBase; // 基础扩散速率m/s
public float MaxRadius; // 最大有效半径m
public float MaxDuration; // 最大持续时间s
public ParticleRenderParams Particles; // 粒子渲染参数
}
```
> 第三方以 JSON 配置文件形式交付 `AmmunitionSpec` 数组,系统启动时加载到 SQLite `AmmunitionSpec` 表。
### 6.2.1 系统内置默认值(第三方提供前使用)
基于 155mm 炮弹载荷的物理合理估计
| 参数 | 惰性气体弹 | 活性材料弹 | 活性燃料弹 |
|------|------|------|------|
| 药剂质量 (SourceStrength) | 10 kg | 12 kg | 10 kg |
| 爆发药 TNT 当量 (BurstChargeKg) | 1.5 kg | 4.0 kg | 1.5 kg |
| 有效浓度阈值 (EffectiveConcentration) | 0.0001 kg/m³ | 0.0002 kg/m³ | 0.0001 kg/m³ |
| 湍流扩散系数 (TurbulentExpansionK) | 3.0 | 4.0 | 3.0 |
| 最大半径 (MaxRadius) | 100 m | 80 m | 90 m |
| 最大持续时间 (MaxDuration) | 120 s | 90 s | 100 s |
| Phase 1 初始半径 (R₀) | 3.75 m | 5.14 m | 3.75 m |
> **数据来源**10kg烟幕剂抛射药/烟幕剂质量比 5%-20%TNT当量取 1.5kg(典型值)。活性材料弹取爆炸分散型上限 4kg。
### 6.3 扩散模型设计 — 高斯烟团模型Gaussian PuffV2
#### 6.3.1 模型选择
采用**高斯烟团扩散模型**使用**Pasquill-Gifford 稳定度分类**计算扩散系数 σ
#### 6.3.2 大气稳定度映射
从天气类型和风速推导稳定度等级无需额外参数
| 天气 | 风速 | 稳定度 | 太阳辐射 | 湍流 |
|------|------|------|------|------|
| 晴天 | 5 m/s | A极不稳定 | | |
| 晴天 | > 5 m/s | B不稳定 | 中 | 中 |
| 阴天 | 任意 | D中性 | 弱 | 中 |
| 雨天 | 任意 | D中性 | 弱 | 中 |
| 雾天 | 任意 | E稳定 | 无 | 弱 |
| 夜间 | 任意 | F极稳定 | 无 | 最弱 |
#### 6.3.3 σ 扩散系数Pasquill-Gifford
扩散距离 x 米后,水平扩散系数 σ_y、垂直扩散系数 σ_z单位 m
| 稳定度 | σ_y | σ_z |
|------|------|------|
| A | 0.22x / (1+0.0001x)^0.5 | 0.20x |
| B | 0.16x / (1+0.0001x)^0.5 | 0.12x |
| C | 0.11x / (1+0.0001x)^0.5 | 0.08x / (1+0.0002x)^0.5 |
| D | 0.08x / (1+0.0001x)^0.5 | 0.06x / (1+0.0015x)^0.5 |
| E | 0.06x / (1+0.0001x)^0.5 | 0.03x / (1+0.0003x)^0.5 |
| F | 0.04x / (1+0.0001x)^0.5 | 0.016x / (1+0.0003x)^0.5 |
> 对于瞬时释放的烟团Puff扩散距离 x 取云团中心随风漂移的距离x = u × t其中 u 为风速t 为经过时间。若风速为 0取 x ≈ 1m纯湍流扩散
#### 6.3.4 浓度场公式
云团内任意点 (x, y, z) 在时刻 t 的浓度:
```
C(x, y, z, t) = ────────────── × exp[ -½( (x-ut)/σy )² ]
(2π)^(3/2) σy σy σz
× exp[ -½( y/σy )² ]
× { exp[ -½( (z-H)/σz )² ] + exp[ -½( (z+H)/σz )² ] }
其中:
Q = 源强释放物质总量kg来自 AmmunitionSpec
u = 风速m/s
H = 有效释放高度m
σy, σz = 扩散系数(由 6.3.2 和 6.3.3 确定)
```
#### 6.3.5 可判定指标
| 指标 | 计算 | 说明 |
|------|------|------|
| 中心浓度 | C_max = Q / [(2π)^(3/2) σσz] | 云团最浓处 |
| 有效半径 | R_eff = min{ r | C(中心, r) < C_threshold } | 浓度超过阈值的范围 |
| 消散条件 | C_max < C_threshold / 10 经过时间 > MaxDuration | 浓度可忽略 |
> **简化**:实时判定时,无人机位于云团内当且仅当 C(无人机坐标) ≥ EffectiveConcentration。不需要球体假设直接用高斯公式计算。
#### 6.3.6 AmmunitionSpec 调整
| 字段 | 变更 | 说明 |
|------|------|------|
| `DispersionRateBase` | ❌ 删除 | 由 PG 稳定度代替 |
| `InitialRadius` | 保留 | 仍作初始 σ 参考 |
| `SourceStrength` | ✅ 新增 | 源强 Qkg取代线性模型 |
> 浓度 C 的单位为 kg/m³无人机是否受损取决于 C ≥ EffectiveConcentration。
#### 6.3.7 接口(不变)
```csharp
public interface ICloudDispersionModel
{
void Initialize(AmmunitionSpec ammo, CombatScene env, Vector3 releasePos, float releaseTime);
void Tick(float deltaTime, float windSpeed, WindDirection windDir);
Vector3 Center { get; }
float Radius { get; }
float CoreDensity { get; }
float EffectiveRadius { get; }
ParticleParams Particles { get; }
bool IsDissipated { get; }
}
```
### 6.4 毁伤模型设计
三种气溶胶对应三种毁伤机制,我们自主实现:
#### 6.4.1 共同接口
```csharp
public interface IDamageModel
{
/// <summary>每 Tick 计算毁伤值</summary>
/// <param name="droneType">目标类型</param>
/// <param name="powerType">动力类型(影响毁伤效率)</param>
/// <param name="aerosolType">气溶胶类型</param>
/// <param name="cloudDensity">当前位置云团密度kg/m³</param>
/// <param name="exposureTime">累计暴露时间s</param>
/// <param name="deltaTime">本次增量暴露时长s</param>
/// <returns>本次毁伤值0~1 归一化)</returns>
float CalculateDamage(DroneType droneType, PowerType powerType,
AerosolType aerosolType, float cloudDensity,
float exposureTime, float deltaTime);
/// <summary>达到 100% 毁伤所需的连续暴露时间 (s),供 planner 估算拦截可行性</summary>
float RequiredExposureSeconds(DroneType droneType, PowerType powerType, AerosolType aerosolType);
DamageStage GetDamageStage(float accumulatedDamage);
}
```
#### 6.4.2 三种机制的差异
| 机制 | 气溶胶类型 | 毁伤原理 | 毁伤曲线特征 |
|------|------|------|------|
| **吸入式灭火** | 惰性气体 | 发动机吸入惰性气体 → 氧气浓度下降 → 熄火 | 阈值型:密度达标后快速生效,延迟短 |
| **爆燃式** | 活性材料 | 材料接触高温发动机表面 → 化学反应爆燃 | 触发型:密度 + 温度双条件,瞬间高伤害 |
| **吸入式爆炸** | 活性燃料 | 燃料被吸入进气道 → 内部爆炸 | 累积型:随暴露时间指数增长 |
```csharp
// 实现示例伪代码
// 吸入式灭火:阈值判定
class InertGasDamageModel : IDamageModel
{
float CalculateDamage(...) {
if (cloudDensity < effectiveThreshold) return 0; // 浓度不够,无伤害
// 超过阈值后,线性累积毁伤
return damageRate * deltaTime;
}
}
// 爆燃式:瞬时触发
class ActiveMaterialDamageModel : IDamageModel
{
float CalculateDamage(...) {
if (cloudDensity < triggerThreshold) return 0;
if (!triggered) { triggered = true; return burstDamage; } // 一次性爆发
return residualDamage * deltaTime; // 后续余伤
}
}
// 吸入式爆炸:指数累积
class ActiveFuelDamageModel : IDamageModel
{
float CalculateDamage(...) {
if (cloudDensity < effectiveThreshold) return 0;
// 累积暴露 → 指数增长
return baseRate * Mathf.Exp(exposureTime * factor) * deltaTime;
}
}
```
#### 6.4.3 毁伤阶段判定
```
accumulatedDamage: 0.0 ─────── 0.25 ─────── 0.6 ─────── 1.0
DamageStage: Normal EngineAnomaly AttitudeLoss Destroyed
Normal: 正常运行
EngineAnomaly: 发动机异常Unity: 尾焰变色/不稳定)
AttitudeLoss: 姿态失控Unity: 偏航/滚转)
Destroyed: 被摧毁Unity: 坠落动画)
```
### 6.5 防御推荐算法设计
#### 6.5.1 Step A气溶胶选型
基于**规则匹配**,根据目标动力类型推荐最佳毁伤机制:
| 目标动力 | 推荐气溶胶 | 理由 |
|------|------|------|
| 电推式 | 惰性气体(吸入式灭火) | 电动机无高温,爆燃/爆炸不适用;惰性气体使电调/散热失效 |
| 活塞式 | 惰性气体(吸入式灭火) | 活塞发动机依赖氧气,窒息最有效 |
| 喷吸气式 | 活性材料(爆燃式) | 喷气发动机高温表面触发爆燃 |
| — | 活性燃料(吸入式爆炸) | 适用于高价值高速目标,追求一击必杀 |
```csharp
// 选型规则表(可配置)
static readonly Dictionary<PowerType, AerosolType> MatchTable = new()
{
{ PowerType.Electric, AerosolType.InertGas },
{ PowerType.Piston, AerosolType.InertGas },
{ PowerType.Jet, AerosolType.ActiveMaterial },
};
```
#### 6.5.2 Step B云团参数推荐 — 时空交汇优化
```
问题:
输入 = 目标航路 R(t)、风速 Vw、搭载平台位置 Pp、弹药飞行时间 Tf
输出 = 最优抛撒位置 Ps 和触发时机 Ts
方法:
1. 将目标航路离散为时间点 t₁, t₂, ..., tₙ
2. 对每个 tᵢ
a. 无人机位置 = R(tᵢ)
b. 考虑炮弹飞行时间 Tf抛撒时刻 = tᵢ - Tf
c. 云团在 [tᵢ - Tf, tᵢ - Tf + Duration] 内的扩散范围
d. 计算交汇窗口 = 无人机航路 ∩ 云团扩散范围的时空重叠
3. 选择交汇窗口最大的 (Ps, Ts) 作为最佳值
4. 选择交汇窗口刚好有效的边界作为最危险值
简化实现:
- 云团近似为匀速膨胀球体
- 无人机匀速直线飞行(取平均速度)
- 解析解:计算无人机航迹与云团膨胀球面的两个交点
- 最佳 = 无人机在云团中时间最长
- 最危险 = 无人机刚好擦边
```
##### 风偏补偿(天气纳入规划)
云团生成后会被风吹偏。为使云团中心在无人机到达穿越点时正好漂移回航路,抛撒点须**逆风预置**
```
穿越点 (tx, tz) = 无人机航路上的目标点(不变,用于 txArrival 计算)
抛撒点 (cloudGenX, cloudGenZ) = (tx, tz) windVec × expansionTime
windVec = Kinematics.WindToVector(WindDirection, WindSpeed) // 只影响 X/Z
expansionTime ≈ 30sPhase 2 湍流膨胀时长)
```
- **弹药瞄准抛撒点**:地基 `dist = |cloudGen platform|`;空基 `distToCloud = |cloudGen platform|`
- **FireEvent.TargetX/Z = cloudGen**:引擎据此生成云团,云团被风吹 `windVec × expansionTime` 后回到穿越点
- **天气(稳定度)间接影响**`CloudExpansionModel.RadiusAt` 已根据 `env.WeatherType` 推导 Pasquill 稳定度,影响 `expansionTime` 与有效半径估算;修复 `GaussianPuffDispersion` 写死 Sunny 的 bug 后,预估与运行时一致
> `tx, tz`(穿越点)与 `cloudGenX, cloudGenZ`(抛撒点)在有风时不同,无风时相同。原代码混淆二者,导致有风场景规划错误。
##### 物理模型统一原则planner 与引擎共用)
**planner 禁止写任何本地运动学/几何/毁伤公式**,全部调用与引擎共享的独立工具类。每个物理量只有一个实现:
```
共享工具类(唯一实例) planner 用途 引擎用途
─────────────────────────────────────────────────────────────────
Kinematics质点运动学 弹道/风矢量/到达时间 MunitionEntity/DroneEntity
RouteGeometry航路几何 穿越点弧长/位置/切向 DroneEntity 位置更新
CloudExpansionModel云团膨胀 有效半径/膨胀时间/弹药数 CloudEntity 半径演化
DamageAssessment毁伤几何 (暂未直接用,估算用模型) PathInSphere 穿云路径
```
- **RouteGeometry**:纯静态工具类。`TotalLength`/`PositionAt(arcLen)`/`ArcLengthNearestTo(x,z)`/`TangentAt(arcLen)`/`TravelTimeTo(arcLen,speed)`。planner 用它定位穿越点、沿航路分布多发、算到达时间DroneEntity 用它做弧长驱动的位置更新(`_traveledArc += speed×dt` → `PositionAt`)。
- **云团重叠**:间距 `= 2R × (1 CloudOverlapRatio)`,重叠比例由 `planner_config.json` 驱动(默认 20%)。`CloudExpansionModel.RoundsNeeded` 接受 `spacing` 参数公式仍在共享模块planner 只传配置值。
##### 配置外置planner_config.json
planner 所有策略参数从 `data/planner_config.json` 读取,代码零默认值:
```json
{
"CloudOverlapRatio": 0.2,
"CriticalProbabilityThreshold": 0.5,
"MaxInterceptProbability": 0.95,
"TypeCoefficient": { "HighSpeed": 4.0, "FixedWing": 2.0, ... },
"AmmoMatch": { "Electric": "InertGas", "Piston": "InertGas", "Jet": "ActiveMaterial" }
}
```
- `PlannerConfig.Load(IPathProvider)` 从 dataRoot 加载;文件缺失或字段非法即抛异常
- `DefaultDefensePlanner` 构造函数必传 `PlannerConfig`
#### 6.5.3 Step C反推平台部署
```
根据云团方案反推:
Q: 需要覆盖多大面积?
→ 根据目标数量、编队间距、航路宽度 → 需要的云团数
Q: 每个云团需要多少弹药?
→ 云团体积 / 单发覆盖体积 → 弹药数
Q: 用什么平台?
→ 抛撒点距目标 > 平台航程?→ 地基
→ 抛撒点距目标 ≤ 平台航程?→ 空基
→ 根据弹药初速 / 飞行时间校验可行性
Q: 探测设备需要多大半径?
→ 探测半径 ≥ 从平台发现目标到完成拦截期间目标飞过的距离
```
#### 6.5.4 接口
```csharp
// === 防御规划 ===
public interface IDefensePlanner
{
/// <param name="fireUnits">可用的火力单元(空基/地基统一表达)</param>
/// <param name="threats">威胁批次列表</param>
/// <param name="environment">作战环境(含天气,影响光电探测距离)</param>
/// <param name="detectionSources">统一信息网络的探测源列表(独立探测设备 + 火力单元自带探测)。可为空列表。</param>
PlannerResult Plan(List<FireUnit> fireUnits, List<DroneWave> threats,
CombatScene environment, List<DetectionSource> detectionSources);
}
// ThreatProfile / DefenseRecommendation / DefenseSolution 等模型见本节上文
```
### 6.6 新增数据表AmmunitionSpec
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| Id | TEXT | PK | GUID |
| AerosolType | INTEGER | | 气溶胶类型 |
| Name | TEXT | | 弹药名称 |
| InitialRadius | REAL | | 初始云团半径 m |
| InitialVolume | REAL | | 初始云团体积 m³ |
| CoreDensity | REAL | | 核心密度 kg/m³ |
| EdgeDensity | REAL | | 边缘密度 kg/m³ |
| InitialTemperature | REAL | | 初始温度 K |
| BuoyancyFactor | REAL | | 浮力系数 |
| EffectiveConcentration | REAL | | 有效毁伤浓度阈值 |
| DispersionRateBase | REAL | | 基础扩散速率 m/s |
| MaxRadius | REAL | | 最大有效半径 m |
| MaxDuration | REAL | | 最大持续时间 s |
| ParticlesJson | TEXT | | 粒子渲染参数 JSON |
> 第三方以 JSON 文件交付,导入到此表。系统启动时若表为空则从默认配置文件加载。
### 6.7 算法替换完整示例
以下是将来用第三方 DLL 替换扩散模型的完整流程,演示接口驱动架构的灵活性:
```
┌─ 步骤 1第三方提供实现 ─────────────────────────────┐
│ │
│ ThirdPartyDispersion.dll │
│ └─ class ThirdPartyPuff : ICloudDispersionModel │
│ { │
│ void Initialize(AmmunitionSpec, ...) → P/Invoke → native_init()
│ void Tick(float, float, WindDirection) → P/Invoke → native_tick()
│ Vector3 Center → P/Invoke → native_center()
│ float Radius → P/Invoke → native_radius()
│ ... │
│ } │
│ │
└────────────────────────────────────────────────────────┘
┌─ 步骤 2一行注册 ──────────────────────────────────┐
│ │
│ AlgorithmFactory.Register │
│ <ICloudDispersionModel, ThirdPartyPuff>(); │
│ │
└────────────────────────────────────────────────────────┘
┌─ 步骤 3SimulationEngine 无感知切换 ───────────────┐
│ │
│ var dispersion = AlgorithmFactory │
│ .Create<ICloudDispersionModel>(); │
│ dispersion.Initialize(ammo, env, pos, t); │
│ │
│ // 后续代码完全不变,无论底层是自主实现还是第三方 DLL │
│ dispersion.Tick(deltaTime, windSpeed, windDir); │
│ if (dispersion.IsDissipated) { ... } │
│ │
└────────────────────────────────────────────────────────┘
```
**替换粒度**:每个算法组件独立替换,可以实现如下组合:
```
组合 A当前
ICloudDispersionModel → GaussianPuffDispersion自主
IDamageModel → DamageModelRouter自主
IDefensePlanner → DefaultDefensePlanner自主
组合 B将来可能
ICloudDispersionModel → ThirdPartyPuff第三方 DLL
IDamageModel → DamageModelRouter自主保留
IDefensePlanner → DefaultDefensePlanner自主保留
组合 C将来可能
ICloudDispersionModel → ThirdPartyPuff第三方 DLL
IDamageModel → ThirdPartyDamage第三方 DLL
IDefensePlanner → DefaultDefensePlanner自主保留
```
---
## 七、数据录制与回放
### 7.1 FrameDataStore 实现
`FrameDataStore` 采用**双层存储**架构:仿真期间内存缓冲 → 仿真结束保留 LiveFrames 副本 + 批量写入 SQLite
```csharp
public class FrameDataStore
{
// 内存回放Flush 后保留副本,供回放直接读取(零磁盘 IO
public List<SimFrameRecord> LiveFrames { get; private set; }
void BeginRecording(string scenarioId); // 清空 buffer + LiveFrames
void RecordFrame(...); // 仅写内存 List<FrameRecord>
void Flush(); // 序列化 → LiveFrames 副本 + SQLite 入库
void Discard(); // 清空 buffer + LiveFrames
List<SimFrameRecord> ReadFrames(...); // 从 SQLite 读(历史仿真回放)
}
```
**LiveFrames 生命周期**
```
仿真运行 → Flush() → LiveFrames = 当前仿真数据(同时写 SQLite
回放:优先取 LiveFrames内存
新仿真开始 → BeginRecording() → LiveFrames.Clear()
如需回放旧仿真 → 回退读 SQLite
```
### 7.2 录制
- 仿真启动时 `BeginRecording` 清空内存缓冲区和 LiveFrames
- 录制帧率 = 仿真 TickRate每想定可配置默认 20Hz
- 每 Tick 调用 `RecordFrame()`,仅追加到内存 `List<FrameRecord>`,零磁盘 IO、零字符串分配
- 仿真结束 `Flush()`:序列化为 `SimFrameRecord` → 保留 `LiveFrames` 副本 → 单事务 `InsertAll` 写入 SQLite
- 仿真事件 `SimEvent` 直接写入主库 `main.db`
### 7.3 回放
- `ReplayController.LoadReplay(scenarioId, frameStore)` 双路径:
- 传入 `frameStore``LiveFrames` 非空 → 直接使用内存数据(仿真刚结束,零延迟)
- 否则 → 从 `frames/{scenarioId}.db` 读取(历史仿真)
- WHERE FrameIndex BETWEEN a AND b 命中索引,毫秒级
- 回放帧率由前端控制(可与录制不同)
- 支持暂停 / 快进 / 后退 / 跳转
### 7.4 容量估算
以默认 20Hz TickRate16 实体为例:
| 仿真时长 | 帧数 | 行数16实体 | 单任务大小 |
|------|------|------|------|
| 5 min | 6,000 | 96,000 | ~27 MB |
| 15 min | 18,000 | 288,000 | ~80 MB |
| 30 min | 36,000 | 576,000 | ~161 MB |
| 60 min | 72,000 | 1,152,000 | ~322 MB |
6 个月累计(假设日均 5 次仿真,每次 30min
| 项目 | 数据 |
|------|------|
| 仿真总次数 | 5 × 180 = 900 次 |
| 帧数据总大小 | 900 × 161 MB ≈ **145 GB** |
| main.db | < **100 MB** |
| 全部合计 | **~145 GB** |
### 7.5 TTL 清理策略
```
仿真完成 → 生成 frames/{scenarioId}.db
CleanupExpired() 每天执行一次:
├─ 扫描 frames/ 目录
├─ 检查 {scenarioId}.db 的创建时间
└─ 超过 6 个月 → 删除文件
可选扩展:
- 归档:移到 archive/ 目录并压缩(.db.gz
- 降采样:删除前先保留每 N 帧 1 帧的精简版
```
### 7.6 为什么分库而非单库
| 对比 | 单库(全部在 main.db | 分库(每任务一个 .db |
|------|------|------|
| 帧表行数6月 | 5.18 亿行 | 每库 57.6 万行 |
| 查询延迟 | 秒级(需精细索引) | 毫秒级 |
| 单文件体积 | > 100 GB | 每文件 ~161 MB |
| 备份/VACUUM | 灾难级 | 无痛 |
| 数据损坏影响 | 全部丢失 | 仅一个任务 |
| 运维(删除/归档) | 复杂 SQL | 直接删文件 |
---
## 八、报告生成
### 8.1 报告结构
技术要求终版要求报告包含**时序图**和三个分析维度:
| 阶段 | 内容 | 数据来源 |
|------|------|------|
| **仿真前** | 我方配置(装备类型/数量、火力单元部署、弹药类型/数量、部署位置、管控区域) | Scenario + 5 步配置 |
| **仿真中** | 关键事件时序表、各实体轨迹概要、云团演化数据 | SimEvent + SimFrameRecord |
| **仿真后** | 拦截成功率、各阶段耗时、毁伤效果评估、对抗结果判定 | SimEvent 汇总统计 |
### 8.2 对抗结果判定逻辑
```
成功拦截:所有无人机 被摧毁(未抵达目标、未侵入管控区)
部分拦截:部分无人机被摧毁,其余抵达目标或侵入管控区
拦截失败:所有无人机抵达目标或侵入管控区(无摧毁)
```
### 8.3 导出
- 格式PDF / Markdown仿真后自动生成 MDPDF 按需调用导出)
- 架构ReportGenerator 构建结构化 ReportData → MarkdownRenderer / StandardPdfTemplate 分别渲染
- 技术选型PdfSharpCore 1.3.64netstandard2.0纯托管Unity IL2CPP 兼容CJK 字体嵌入)
- 字体:随程序附带 CJK TrueType 字体(`data/fonts/CJK-Font.ttf`),通过 `IPathProvider.GetFontPath()` 获取路径
---
## 九、服务接口概要
```csharp
// === 模型管理 ===
interface IModelService
{
ModelInfo ImportModel(string filePath, string name);
void DeleteModel(string id);
List<ModelInfo> GetAllModels();
ModelInfo GetModel(string id);
}
// === 想定管理 ===
interface IScenarioService
{
Scenario CreateScenario(string name, string scenarioNumber);
void DeleteScenario(string id);
PagedResult<Scenario> SearchScenarios(string kw, DateTime? from, DateTime? to, int page, int size);
ScenarioConfig GetScenarioDetail(string id);
// 步骤保存
void SaveScene(string scenarioId, CombatScene scene);
void SaveControlZones(string scenarioId, List<ControlZone> zones); // NEW
void SaveScenarioDrone(string scenarioId, ScenarioDrone target);
void SaveDeployment(string scenarioId, List<ScenarioUnit> equips); // 重构
void SaveCloudDispersal(string scenarioId, CloudDispersal cloud);
void SaveRoute(string scenarioId, RoutePlan route, List<Waypoint> wps);
void UpdateStep(string scenarioId, int step);
// 综合防御推荐NEW — 多编队版)
DefenseRecommendation GetDefenseRecommendation(string scenarioId);
}
// === 仿真引擎 ===
interface ISimulationEngine
{
void Initialize(string scenarioId);
SimulationFrameResult Tick(float deltaTime); // 返回本帧快照+新事件(复用对象池)
void Pause();
void Resume();
void Stop();
SimulationState State { get; }
}
// === 数据录制 ===
// 说明:录制职责由具体类 FrameDataStore 承担(无 IRecordService 接口)。
// SimulationEngine 内部持有 FrameDataStore每 Tick 调 RecordFrame仅内存
// Stop/完成时调 Flush 批量落库。事件 SimEvent 由引擎直接写入主库。
class FrameDataStore
{
void BeginRecording(string scenarioId);
void RecordFrame(int frameIdx, float timestamp, List<EntitySnapshot> entities);
void Flush(); // 批量落库
void Discard();
List<SimFrameRecord> ReadFrames(string scenarioId, int fromFrame, int toFrame);
void DeleteScenarioDb(string scenarioId);
int CleanupExpired(); // TTL 清理RetentionDays默认 180
}
// === 仿真报告 ===
interface IReportService
{
SimulationReport Generate(string scenarioId, ScenarioConfig config,
List<SimEvent> events, string finalDroneStatus, float duration); // 仿真后调用,自动导出 MD
SimulationReport GetReport(string id);
void DeleteReport(string id);
byte[] ExportReport(string id, string format); // "pdf" | "md",按需导出
string ExportToFile(string reportId, string outputDir, string format); // 导出文件,返回路径
PagedResult<SimulationReport> SearchReports(string kw, string? from, string? to, int page, int size);
}
```
---
## 九-A、多批次多编队设计
### 数据模型
一个仿真任务可包含**多种类型、多批次**的无人机,每批次有独立的航路:
```
Scenario "城市防御演习"
├── CombatScene共享
├── ControlZone[](共享)
├── CloudDispersal共享云团参数具体拦截方案由算法确定
├── 批次 A活塞×3 ← WaveId
│ ├── ScenarioDroneWaveId=A类型=活塞,数量=3
│ ├── RoutePlanScenarioId + WaveId=A航路 A北→南
│ └── Waypoint[]
├── 批次 B喷气×2 ← WaveId
│ ├── ScenarioDroneWaveId=B类型=高速,数量=2
│ ├── RoutePlanScenarioId + WaveId=B航路 B西→东
│ └── Waypoint[]
└── 批次 C旋翼×5 ← WaveId
├── ScenarioDroneWaveId=C类型=旋翼,数量=5
├── RoutePlanScenarioId + WaveId=C航路 C
└── Waypoint[]
```
**关键变更**`RoutePlan` 主键为 `(ScenarioId, WaveId)` 复合键。`Waypoint` 通过 `(ScenarioId, WaveId)` 关联航路。
### 推荐算法
算法需要同时考虑**所有无人机批次**和**可用火力单元**,自动匹配分配:
```csharp
// 输入:所有无人机编队 + 已部署的火力单元
// 输出:每个编队的拦截方案 + 合并的发射计划
var allSolutions = new List<DefenseSolution>();
var usedFireUnits = new HashSet<string>();
foreach (var droneWave in threat.DroneWaves)
{
// 从可用火力单元中选择合适的(弹药类型匹配目标动力类型)
var availableUnits = fireUnits.Where(u => !usedFireUnits.Contains(u.Id)).ToList();
var solution = advisor.RecommendForWave(droneWave, environment, availableUnits);
// 标记占用的火力单元
foreach (var idx in solution.AssignedFireUnitIds)
usedFireUnits.Add(idx);
allSolutions.Add(solution);
}
// 复用判定(不写死"一对一"或"一对多"
// 同种弹药 → 只需冷却时间(秒级),可直接复用
// 异种弹药 → 需要 AmmoChangeTime默认 5 分钟),检查批次间隔是否够
// 弹药耗尽 → 不可复用
var merged = MergeFireSchedules(allSolutions);
engine.SetFireSchedule(merged);
```
**关键原则**
- 算法决定分配,而非写死"一对一"或"一对多"
- 决策依据:弹药类型匹配、时间窗口重叠、火力单元能力
- 人工可调整算法推荐结果
- 引擎不感知分组——只看合并后的 `FireSchedule`
### 数据表变更
| 表 | 变更 |
|------|------|
| `RoutePlan` | PK 为 (ScenarioId, WaveId) 复合键 |
| `ScenarioDrone` | 不变(已有 WaveId |
| `Waypoint` | 关联 `(ScenarioId, WaveId)` 定位航路 |
`IScenarioService``SaveRoute` 签名:
```csharp
void SaveRoute(string scenarioId, string waveId, RoutePlan route, List<Waypoint> wps); // waveId 参数
```
### 报告
报告新增多编队汇总:
```markdown
## 编队汇总
| 批次 | 目标 | 数量 | 拦截方案 | 结果 |
|------|------|------|------|------|
| 批次A | 活塞 | 3 | 惰性气体 12发 | ✅ 全毁 |
| 批次B | 喷气 | 2 | 活性材料 8发 | ⚠️ 1逃脱 |
```
---
## 十、配置向导步骤(整合后)
整合需求 V1.0 和技术要求终版后,配置向导保持 5 步但内容调整:
```
┌────────────────────────────────────────────────────────┐
│ 步骤 1作战场景设置 │
│ ├─ 场景类型(城市/平原/山区/海岸) │
│ ├─ 时间段24h 制 NEW
│ ├─ 天气 + 风速/风向/温度/湿度/气压/能见度 │
│ ├─ 场景空间范围(宽/纵深/高 NEW
│ └─ 管控区域(电子围栏 NEW
│ └─ 多边形绘制 / 编辑 / 删除 │
├────────────────────────────────────────────────────────┤
│ 步骤 2目标配置 │
│ ├─ 批次选择(已有批次/新建批次) │
│ ├─ 目标类型选择(自动填充默认参数) │
│ ├─ 数量 / 动力类型 / 翼展 / 速度 / 高度 │
│ └─ 预设典型目标库NEW
├────────────────────────────────────────────────────────┤
│ 步骤 3装备部署重构
│ ├─ 装备角色(探测设备 / 发射平台) │
│ ├─ 发射平台类型(空基 / 地基 NEW
│ ├─ 数量 / 部署位置 │
│ ├─ 挂载气溶胶类型 / 弹药数量 │
│ ├─ 弹药初速 / 释放高度 / 冷却时间 │
│ └─ 探测半径(探测设备) │
├────────────────────────────────────────────────────────┤
│ 步骤 4云团抛撒配置 │
│ ├─ 气溶胶类型(吸入式灭火/爆燃式/吸入式爆炸) │
│ ├─ 释放参数(高度/触发模式/扩散方向/规模/持续等) │
│ ├─ 抛撒位置(三维点选 / 手动输入 / 算法推荐 NEW
│ ├─ 算法推荐按钮 → 显示最佳值 & 最危险值NEW
│ └─ 可点选采纳推荐值 │
├────────────────────────────────────────────────────────┤
│ 步骤 5航路规划 │
│ ├─ 编队模式(单机/编队/蜂群) │
│ ├─ 编队间距 / 预计到达时间 │
│ ├─ 航路点画布(三维点选添加/调整/删除 NEW
│ └─ 航路点列表(坐标/高度/速度) │
└────────────────────────────────────────────────────────┘
```
---
## 十一、关键技术决策
| # | 决策 | 选择 | 理由 |
|---|------|------|------|
| 1 | 数据持久化 | SQLite (sqlite-net) | 支持复杂查询、筛选、分页 |
| 2 | 模型文件格式 | .fbx / .obj / .stl / .glb / .gltf | 合并两份需求≤500MB2K+贴图 |
| 3 | 模型文件存储 | Application.persistentDataPath | 不与 Unity Assets 耦合 |
| 4 | 序列化方案 | System.Text.Json | Unity 2022+ 内置支持 |
| 5 | Service/Data 层 | 零 UnityEngine 依赖 | 可独立单元测试 |
| 6 | 仿真驱动方式 | 固定 TickRate 帧驱动 | 每想定可配置(默认 20Hz保证确定性 |
| 7 | 仿真并发 | 单仿真 | 简化状态管理 |
| 8 | 拦截机制 | 多阶段链路 | 探测→火控→发射→弹药飞行→云团→毁伤 |
| 9 | 云团扩散模型 | **高斯烟团模型(自主实现)** | 简化球体膨胀 + 风速漂移 + 密度衰减,适合实时 20Hz |
| 10 | 毁伤模型 | **三种机制自主实现** | 吸入式灭火(阈值型)、爆燃式(触发型)、吸入式爆炸(指数累积型) |
| 11 | 防御方案推荐 | **IDefensePlanner自主实现** | 五步规划:威胁排序 → 弹药匹配 → 候选生成 → 贪心分配 → 时序生成 |
| 12 | 第三方数据 | **静态配置JSON → SQLite** | 仅提供弹药基础参数和初始扩散状态,不提供运行时算法 |
| 13 | 运动学 | 弧长驱动匀速运动 | 无人机严格按航路匀速飞行(不受风偏,飞控修正);云团受风偏,毁伤判定做参考系修正 |
| 14 | 弹药弹道 | 抛物线(地基)/ 自由落体(空基) | 基于初速、角度、释放高度计算 |
| 15 | 批次 / 火力单元 | WaveId + 火力单元独立作战 | 批次关联航路但不独占;火力单元是最小独立作战单位,不需要上层编组 |
| 16 | 管控区域 | ControlZone 表 + Tick 判定 | 电子围栏,侵入即任务失败 |
| 17 | 配置向导流程 | **威胁驱动三阶段** | Phase1 定义威胁 → Phase2 算法推荐 → Phase3 审阅确认 |
| 18 | 报告导出 | 模板填充 → Markdown | PDF/Word 待调研 |
| 19 | 坐标系统 | 右手系 Y-up米为单位 | 与 Unity 一致 |
| 20 | 空间范围 | 最低 4×100×10m默认更大 | 可配置 |
| 21 | 帧数据存储 | **主库 + 任务分库** | 主库存元数据+事件(<100MB永久分库每任务存帧数据6月TTL单文件~161MB直接删文件运维 |
| 22 | 程序集拆分 | **Core 独立于 Unity** | Core .NET Standard 2.1 纯类库 UnityEngine 依赖Unity 仅作桥接dotnet test 即可全量测试 |
---
## 十二、性能指标(来自技术要求终版)
| 指标 | 要求 | 说明 |
|------|------|------|
| 最大目标数 | 5 独立运动实体3D 显示 | 显示层面非逻辑上限 |
| 渲染帧率 | 30 FPS满载 + 粒子特效 | Unity 前端职责 |
| 仿真空间 | 4m × 100m × 10m | 默认更大可配置 |
| 粒子系统 | 云团扩散/流动/色彩 | Unity 根据后端参数渲染 |
| 毁伤效果 | 尾焰异常姿态失控坠落 | Unity 根据 DamageStage 渲染 |
---
## 十三、性能设计Tick 热路径优化)
> 本节记录仿真引擎 `SimulationEngine.Tick()` 热路径上的性能优化。实测Unity 端满载场景渲染帧率从优化前 **<20 FPS卡顿** 提升至 **200+ FPS**。
### 13.1 设计原则:零分配热路径
`Tick` 在仿真期间以 TickRate默认 20Hz Unity `Update` 反复调用且每帧需向 Unity 传回所有实体快照优化的核心原则是**仿真热路径上零或极少托管堆分配**把所有可复用的对象数组字符串构造移出热路径
| 原则 | 手段 |
|------|------|
| 对象池复用 | 快照列表事件列表帧结果移除索引缓冲飞行无人机缓冲均 `Clear()` 复用不每帧 new |
| 延迟序列化 | 强类型字段在仿真期流转JSON/字符串只在 `Flush` 一次性生成 |
| 几何预计算 | 航路多边形顶点等静态数据构造时算一次缓存供每帧查询 |
| 批量落库 | 帧数据先全量驻留内存仿真结束单事务写入杜绝每帧磁盘 IO |
### 13.2 关键优化点
#### A. DroneEntity 航路几何缓存(消除每帧重复几何运算)
**问题**原实现每帧对每架无人机调用 `RouteGeometry.TotalLength(Route)`O(N) 全扫求和+ `PositionAt`O(N) 重扫定位航路静态却每帧重算
**优化**构造时一次性预计算 `_totalArc`总弧长)、`_segLen[]`各段段长)、`_cumArc[]`累积弧长)。`Update()` `_traveledArc += speed×dt` `_cumArc[]` 线性定位所在段航点数通常 10后插值
**收益**每架无人机每帧从 O(N) ×2 降为 O(N) ×1仅定位无重算总长)。无人机数 × 航点数 × FPS 规模下收益显著
#### B. 仿真期零字符串分配 + JSON 推迟到 Flush最大收益项
**问题**原实现每帧每实体在 `CollectSnapshots` JSON 字符串`StateData`Unity `SimulationRunner` 又对每个云团 `JsonDocument.Parse` 解析 —— 拼装 + 解析两端分配是帧率杀手
**优化**
- `EntitySnapshot` 新增强类型字段`DamageStage`/`Hp`/`CloudRadius`/`CloudOpacity`/`CloudPhase`/`CloudElapsed`/`PlatformStateStr``CollectSnapshots` 直接填标量零字符串分配
- Unity 端直读 `snap.CloudRadius`/`snap.CloudOpacity`移除 `JsonDocument.Parse`
- JSON 序列化推迟到 `FrameDataStore.Flush` `EntityType` switch 拼装模板与历史格式逐字节一致单次完成
**收益**热路径上彻底消除字符串分配与 JSON 解析。**这是帧率从 <20 提升到 200+ 的主因**。
#### C. ControlZoneEntity 顶点 2D 缓存(消除每帧数组分配)
**问题**`ContainsPoint` tick `O(飞行无人机 × 管控区)` 调用原实现每次 `new (float X, float Z)[]` 并逐元素拷贝
**优化**构造时缓存 `_vertices2D`水平投影顶点数组`ContainsPoint` 直接传给 `Kinematics.PointInPolygon`数组隐式转 `ReadOnlySpan`零分配)。
#### D. 帧数据内存缓存 + 批量落库
**问题**每帧写 SQLite 会产生磁盘 IO 与事务开销
**优化**`FrameDataStore` `List<FrameRecord>`struct连续存储做内存缓冲`RecordFrame` 仅追加`Flush` 时序列化 + 单事务 `InsertAll` 批量入库仿真期间零磁盘 IO
#### E. 毁伤判定快速排斥
`O(无人机 × 云团)` 嵌套循环前两层裁剪
1. `cloud.CoreDensity < 有效浓度阈值` 跳过浓度不足不可能造成毁伤)。
2. `‖drone cloudCenter‖² > (2R)²` 跳过距离超 2 倍有效半径本帧不可能穿云避免进入 `PathInSphere` 解析计算
#### F. 移除索引复用 + 预过滤
- 弹药/云团移除用倒序索引缓冲 `_removalIndices`避免 `ToList()`
- 飞行无人机预过滤到复用缓冲 `_flyingDrones`避免后续多步重复 `Where`
### 13.3 优化前后对照
| 场景 | 优化前 | 优化后 |
|------|--------|--------|
| Unity 渲染帧率满载 | <20 FPS卡顿 | 200+ FPS |
| 每帧 GC 分配 | 每实体 1 JSON 字符串 + 解析端 DOM | 零字符串分配 |
| 每帧无人机几何运算 | O(无人机×航点) ×2 重算 | O(无人机×航点) ×1 定位 |
| 每帧管控区数组分配 | 每次调用 new 数组 | 构造时缓存零分配 |
| 帧数据磁盘 IO | 每帧写库 | 仿真结束单次批量 |
### 13.4 后续可扩展方向(当前规模无需)
当实体规模大幅增长如数百架无人机 + 数十云团可能出现的新瓶颈与应对
- 毁伤判定 `O(drone × cloud)` 嵌套 空间分区均匀网格 / AABB )。
- 回放 `ReplayController.GetFrame` `Where().ToList()` 线性扫描 改为按 FrameIndex 建索引或分组字典
- 极长回放全量加载内存 分段/流式读取
> 按 YAGNI上述在当前规模<30 FPS 需求、200+ FPS 实测)下不做,仅记录备查。
---
## 十四、探测设备设计(统一信息网络)
> 探测设备是"统一信息网络"的感知层,负责对来袭威胁的搜索、发现与跟踪。本系统支持三种探测手段,统一抽象为 `DetectionSource`,由独立探测节点(`EquipmentRole.Detection`)和火力单元自带的探测能力共同构成探测网。
### 14.1 设计现状:两阶段角色
探测设备在系统中承担两个阶段的角色**两者共用同一套物理公式**`DetectionCalculator`以保证一致
| 阶段 | 角色 | 状态 | 输入 | 输出 |
|------|------|------|------|------|
| **事前规划** | 静态估算最早发现点 | 已实现 | 威胁航路 + 探测网 + 能见度 | 每个威胁的最早发现弧长 `DetectArc` + 精度 |
| **运行时实时** | 扫描发现跟踪 | 待实现 | 飞行无人机实时位置 + 探测网 | `TargetDetected` 事件 + 跟踪坐标流 |
- **事前规划**链路 A`DefensePlanner.Plan` `DetectionCalculator.EarliestDetection`沿威胁航路求其进入探测网范围的最早点反推拦截时机这是当前拦截时机的唯一来源开环)。
- **运行时实时**链路 B`DetectionEntity` Tick 内扫描产生"何时真正发现"的事件流与可视化。**当前未接入 Tick**`TargetDetected` 事件已定义`SimEventType.TargetDetected = 0`)但全代码库未触发。
### 14.2 三种探测手段
| 手段 | 字段 | 特性 | 受能见度影响 |
|------|------|------|------|
| **雷达** | `RadarRange` | 全天候三维波束 | |
| **光电EO** | `EORange` | 可见光成像 | 有效 = 基准 × min(1, Visibility/基准) |
| **红外IR** | `IRRange` | 热源探测夜间可用 | |
综合有效距离取三者最大任一手段发现即算发现
```csharp
// DetectionCalculator.EffectiveRange —— planner 与运行时实时共用
// 注:返回的是"最大水平距离基准"3D 球冠判定在其上层(见 14.2.1
float EffectiveRange(float radarRange, float eoRange, float irRange, float visibility)
{
float effRadar = radarRange;
float effEO = eoRange > 0 ? eoRange * Math.Min(1f, visibility / eoRange) : 0f; // 光电衰减
float effIR = irRange;
return Math.Max(effRadar, Math.Max(effEO, effIR));
}
```
> **统一原则(正确性硬约束)**planner 的事前规划与运行时实时探测**必须用同一个 3D 球冠判定模型**`IsInCoverage`)。原因:仿真是按 planner 规划执行的planner 的探测判定就是仿真的事实依据——若 planner 用比真实探测能力更宽松的几何(如 2D 圆),会规划出"未探测却拦截"的物理错误结局(详见 14.2.1)。
### 14.2.1 三维球冠探测几何(决策 5
**当前实现**`EffectiveRange` 仅做水平面距离判定`EarliestEntryArc` 求线段-圆交点忽略高度与俯仰这是简化模型
**目标态**探测范围为以设备为中心的**三维球冠**——由最大水平距离俯仰角范围高度门限共同界定反映真实雷达/光电/红外的波束形状
```
MaxDetectAlt ─────────────── 高度上界
╲ ← 俯仰角上限 MaxElevation
球冠 ╲
设备 ●──────╱ ◌ ╲────── EffectiveRange最大水平距离
╲ 区域
← 俯仰角下限 MinElevation
MinDetectAlt ─────────────── 高度下界
```
**目标判定函数**待实现planner 与实时探测共用
```csharp
// DetectionCalculator.IsInCoverage —— 三维球冠判定
// ① 水平距离 ≤ EffectiveRange含能见度衰减后的综合距离
// ② 俯仰角 ∈ [MinElevation, MaxElevation](设备指向目标的高低角)
// ③ 目标高度 ∈ [MinDetectAlt, MaxDetectAlt]
bool IsInCoverage(
Vector3 target, Vector3 detector,
float effectiveRange,
float minElevation, float maxElevation,
float minDetectAlt, float maxDetectAlt)
{
float dx = target.X - detector.X;
float dy = target.Y - detector.Y; // 高度差
float dz = target.Z - detector.Z;
float horizDistSq = dx * dx + dz * dz;
if (horizDistSq > effectiveRange * effectiveRange) return false; // ①
if (target.Y < minDetectAlt || target.Y > maxDetectAlt) return false; // ③
float horizDist = MathF.Sqrt(horizDistSq);
if (horizDist < 0.0001f) return true; // 正顶/正下,按角度门限放行
float elevation = MathF.Atan2(dy, horizDist) * (180f / MathF.PI); // 俯仰角(度)
return elevation >= minElevation && elevation <= maxElevation; // ②
}
```
> **planner 必须升级到 3D正确性硬要求非精度优化**
> `EarliestDetection`/`EarliestEntryArc` 当前用线段-圆求交(纯 2D必须改为"沿航路逐采样点调用 `IsInCoverage`"。
> **原因**:仿真是按 planner 规划执行的——planner 判定"威胁能被发现"→ 据此规划拦截→ 仿真照此执行→ 威胁被摧毁。若 planner 用 2D 判定"能发现",但威胁实际高度超出 3D 球冠范围(探测设备真实能力达不到),就会出现**"根本没探测到、却被规划拦截并摧毁"的物理错误结局**——防空系统不可能拦截它没发现的目标。
>
> 因此 planner 与实时探测**必须共用同一个 3D 球冠判定**`IsInCoverage`),这不是为了"好看的统一",而是正确性硬约束。
>
> **采样法 vs 解析法**:三维球冠-折线解析求交复杂度高,改用沿航路采样(步长 ≤ 50m。采样引入的弧长误差 ≤ 一个步长,换算到推荐抛撒时机 ≤ ~1.5s(典型 120km/h远小于云团膨胀窗口~30s和云团重叠冗余**不影响火力计划结果**。所以采样误差可接受,但 2D→3D 的几何升级不可省略。
### 14.3 探测源构成
`DetectionSource` 来自两类装备`SimulationEngine.BuildDetectionSources`
```
统一信息网络探测源 = 独立探测节点 + 火力单元自带探测
(EquipmentRole.Detection) (LaunchPlatform 的 RadarRange/EO/IR 字段)
```
判定规则任一探测字段RadarRange/EORange/IRRange null > 0即为有效探测源。
### 14.4 探测精度与抛撒散布
- `DetectionAccuracy`m探测设备的位置误差。
- 精度越差 → planner 增大横向覆盖范围,对冲定位不确定性:
```csharp
// DetectionCalculator.SpreadRadius
float SpreadRadius(float accuracy) => Math.Max(0f, accuracy); // 散布半径 ≈ 精度值
```
- 运行时实时探测输出目标坐标时,同样按此散布半径叠加位置扰动(待实现)。
### 14.5 实时探测设计(待实现)
#### 14.5.1 DetectionSource / DetectionEntity
`DetectionSource` 需扩展三维几何参数(决策 5
```csharp
public class DetectionSource
{
public Vector3 Position { get; set; }
public float RadarRange { get; set; } // 雷达水平距离 m
public float EORange { get; set; } // 光电水平距离 m受 Visibility 衰减)
public float IRRange { get; set; } // 红外水平距离 m
public float Accuracy { get; set; } // 探测精度 m位置误差
// ── 三维球冠几何参数(决策 5待新增──
public float MinElevation { get; set; } // 俯仰角下限(度)
public float MaxElevation { get; set; } // 俯仰角上限(度)
public float MinDetectAlt { get; set; } // 可探测高度下界 m
public float MaxDetectAlt { get; set; } // 可探测高度上界 m
}
// 待新增:仿真运行时的探测设备实体
public class DetectionEntity
{
public string Id { get; }
public DetectionSource Source { get; }
// 每个威胁的探测状态机(按无人机 Id 索引)
// Undetected ⇄ Detected决策 1离开范围回退 Undetected再次进入重新触发事件
}
```
#### 14.5.2 Tick 内扫描逻辑(接入 5.3 第5步
```
FOR EACH 飞行无人机(维护"本帧已发现该目标的最优探测设备"暂存):
bestDetector = null; bestTime = +∞; bestAccuracy = +∞ // 决策 2最早发现同时则精度高
FOR EACH DetectionEntity
effRange = DetectionCalculator.EffectiveRange(雷达, 光电, 红外, 场景能见度)
inCover = DetectionCalculator.IsInCoverage( // 三维球冠判定14.2.1
drone.Pos, detector.Pos, effRange,
MinElevation, MaxElevation, MinDetectAlt, MaxDetectAlt)
IF inCover:
IF 该无人机对此设备状态 == Undetected:
标记"候选首次发现",按决策 2 比较更新 bestDetector最早同 tick 取精度高)
该无人机对此设备状态 → Detected
ELSE:
该无人机对此设备状态 → Undetected // 决策 1离开即回退
// 本帧所有设备遍历完后,统一触发至多一次 TargetDetected决策 2 融合)
IF bestDetector != null:
触发 OnTargetDetected(
SimEvent { Type = TargetDetected,
SourceId = bestDetector.Id, // 最早/精度最高者
TargetId = drone.Id,
OccurredAt = SimulationTime })
> 决策 3/4实时探测只产生事件与可视化不回调 planner、不改 FireSchedule、不触发额外拦截
```
#### 14.5.3 TargetDetected 事件契约
| 字段 | 取值 |
|------|------|
| `Type` | `SimEventType.TargetDetected` |
| `SourceId` | 探测设备 Id |
| `TargetId` | 被发现的无人机 Id |
| `OccurredAt` | 发现时刻(仿真秒) |
| `DataJson` | 发现点坐标 + 探测精度(待定义结构) |
| `Description` | 如"雷达 R1 发现 drone_3 @(x,y,z)" |
事件写入主库 `SimEvent` 表,是**时序图**的核心数据源之一(与 MunitionLaunched/DroneDestroyed 等并列)。
### 14.6 探测行为决策(已确认)
下列探测行为规则已确认,实现时严格遵循:
| # | 议题 | 决策 | 说明 |
|---|------|------|------|
| 1 | 目标离开探测范围后状态 | **回退 Undetected** | 失去接触即认为丢失目标。若再次进入(同一设备或另一设备)→ 重新置 Detected 并**再次触发 `TargetDetected`**(对应真实防空"失跟→重新捕获",事件流允许多次发现记录) |
| 2 | 多设备发现同一目标的融合 | **取最早发现者;同时发现取精度最高者** | `TargetDetected.SourceId` 取最早发现该目标的设备;若同一 tick 内多设备同时首次发现,取 `DetectionAccuracy` 值最小(精度最高)的设备 |
| 3 | 实时探测是否影响 FireSchedule | **不影响** | FireSchedule 保持 planner 一次性生成(链路 A。实时探测是**纯只读观测层**,不回调 planner、不改发射计划 |
| 4 | 探测盲区/漏网目标 | **仅可视化提示,不触发额外行为** | 配合决策 3实时探测整体定位为观测+展示(时序图/告警文字/UI 标记),不驱动任何拦截逻辑 |
| 5 | 高度与角度范围 | **支持3D 球冠几何** | 探测范围从纯水平面圆升级为以设备为中心的三维球冠(详见 14.2.1);几何参数随装备配置录入(详见 14.7 |
### 14.7 数据模型变更(实现清单)
实现实时探测 + 3D 球冠所需的数据模型改动,集中在此便于落地追踪:
| 改动项 | 位置 | 内容 | 状态 |
|--------|------|------|------|
| **ScenarioUnit 表加字段** | 4.3 | `MinElevation`/`MaxElevation`(度)、`MinDetectAlt`/`MaxDetectAlt`m均可空 | 待加列 |
| **DetectionSource 类加字段** | 14.5.1 | 同上 4 个三维几何字段 | 待加属性 |
| **DetectionCalculator.IsInCoverage** | 14.2.1 | 三维球冠判定(水平距离 + 俯仰角 + 高度门限planner 与实时探测共用 | 待实现 |
| **DetectionCalculator.EffectiveRange** | 14.2 | 现有签名不变返回综合水平距离基准3D 判定在其上层 | 已实现(基础) |
| **planner 适配 3D** | 14.2.1 | `EarliestDetection` 由"线段-圆求交"改为"沿航路采样点调 IsInCoverage"。**正确性硬要求**planner 判定=仿真事实,必须与真实 3D 探测能力一致,否则"未探测却拦截" | 待改造 |
| **SimulationEngine 加 _detectionEntities** | 5.1 | Initialize 时从配置构建 `List<DetectionEntity>`Tick 第5步遍历 | 待实现 |
| **Tick 第5步实时探测** | 5.3 / 14.5.2 | 扫描 + 状态机 + 决策1/2/3/4 落地,触发 `TargetDetected` | 待实现 |
> **数据库迁移**`ScenarioUnit` 新增 4 列均为 NULLABLE不影响存量数据`SimulationEngine.BuildDetectionSources` 读取新字段构造 `DetectionSource`(缺失时按"无角度/高度限制"的退化球冠处理,等价当前 2D 行为,保证平滑过渡)。
---
## 十五、待后续讨论
| # | 议题 | 说明 |
|---|------|------|
| 1 | 第三方 DLL 的具体接口规范 | 需与供应商对齐函数签名、数据结构、调用约定 |
| 2 | 3 种毁伤机制的差异化细节 | 吸入式灭火 vs 爆燃式 vs 吸入式爆炸的毁伤曲线差异 |
| 3 | 探测设备融合逻辑 | 多设备发现同一目标时的数据融合策略(→ 见 14.6 待决策 #2 |
| 4 | 火控分配策略 | 多目标时的优先级分配规则(→ 实时探测接入后见 14.6 待决策 #3 |
| 5 | 蜂群模式运动模型 | 随机扰动范围、个体行为差异 |
| 6 | 报告模板具体格式 | 统计维度、图表需求 |
| 7 | 场景类型对仿真的影响 | 山区是否影响探测/运动 |
| 8 | ~~PDF/Word 导出库选型~~ | ✅ 已选定 PdfSharpCore 1.3.64netstandard2.0纯托管Unity 兼容) |
| 9 | 预置典型目标库内容 | 具体包含哪些无人机型号及参数 |
| 10 | 扩散模型的大气稳定度参数 | 不同天气条件下 σ 的增长速率如何量化 |
| 11 | 毁伤曲线的具体数值 | 三种机制的 damageRate、threshold、burstDamage 等参数取值 |
| 12 | 时空交汇优化的精度 | 离散采样步长 vs 解析解的选择 |