CounterDroneBackend/docs/design/architecture/总体架构设计.md
tian ad0eaf47da 完善模型属性:加 Model/Description 字段,TaskNumber→ScenarioNumber,Task→Info,Task→Scenario 方法名
- DroneProfile/DroneSpec: 加 Model、Description
- Scenario: 加 Description,TaskNumber→ScenarioNumber
- EquipmentDeployment/FireUnitSpec: 加 Description
- ScenarioConfig: Task→Info
- ScenarioService: CreateTask→CreateScenario 等方法名统一
- 238 单元测试通过
2026-06-18 15:15:41 +08:00

93 KiB
Raw Blame History

反无人机仿真系统 — 总体架构设计

版本V14
日期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 数据模型变更清单EquipmentDeployment 表加 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 桥接层示例(极薄)

// 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 测试无需 Unitydotnet test 即可运行全部测试,适合 CI 流水线。


三、枚举定义

// === 任务 ===
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, Electric = 2, Piston = 3, HighSpeed = 4 }
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 }

// === 核心作战概念 ===
// 火力单元FireUnit能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。
//   包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环系统。
//   配置层 = EquipmentDeployment(LaunchPlatform),含探测字段 + 打击字段。
// 独立探测节点DetectionNode只探测不打击的侦查节点如前沿警戒雷达
//   配置层 = EquipmentDeployment(Detection),仅含探测字段。
//   两者都往统一信息网络送探测信息planner 基于融合后的探测边界规划。
// 无人机批次DroneWave有共同航路的一组无人机攻击方
//   批次关联航路但不独占(多个批次可共享同一条航路)。
//   配置层 = DroneProfile(含 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
  DroneProfile         — 步骤2目标配置含 WaveId 关联批次)
  EquipmentDeployment  — 步骤3装备部署火力单元含探测+打击,或独立探测节点)
  CloudDispersal       — 步骤4云团抛撒配置
  RoutePlan            — 步骤5航路规划1:N Scenario多批次批次关联但不独占航路FK 关联 DroneProfile.WaveId
  Waypoint             — 航路点
  SimulationReport     — 仿真报告
  SimEvent             — 仿真事件(时序图数据源)

任务分库frames/{scenarioId}.db— 6 个月 TTL
  SimFrameRecord       — 逐帧位置记录(回放用)

核心作战概念

  • 火力单元FireUnit:能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环。配置层 = EquipmentDeployment(LaunchPlatform)
  • 独立探测节点DetectionNode:只探测不打击的侦查节点(如前沿警戒雷达)。配置层 = EquipmentDeployment(Detection)
  • 无人机批次DroneWave:有共同航路的一组无人机(攻击方)。批次关联航路但不独占(多个批次可共享同一条航路)。配置层 = DroneProfile(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
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 事件 → 该无人机任务失败。

DroneProfile — 步骤2目标配置

字段 类型 约束 说明
Id TEXT PK GUID
ScenarioId TEXT FK
WaveId TEXT FK 关联无人机批次(同一 WaveId 的无人机共享航路)
DroneType INTEGER 0旋翼 1固定翼 2电推 3活塞 4高速
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.8 100 500
活塞 2.5 120 800
高速目标 1.5 300 2000

EquipmentDeployment — 步骤3装备部署火力单元 + 独立探测节点)

字段 类型 约束 说明
Id TEXT PK GUID
ScenarioId TEXT FK
EquipmentRole INTEGER 0=独立探测节点 1=火力单元(发射平台)
Quantity INTEGER DEFAULT 1 部署数量
WaveId TEXT NULLABLE 可选关联批次(用于 UI 分组,火力单元是独立作战单位,不强依赖批次)
// 以下为火力单元(发射平台)专用字段
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
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:由 DroneProfile(WaveId) + RoutePlan(ScenarioId, WaveId) + Waypoints 隐式表达。批次关联航路但不独占(多个批次可共享同一条航路)。
  • EquipmentGroup(装备编组)→ 不再需要。火力单元是独立作战的最小单位,不需要上层编组。

数据层:Group 表保留但不再主动使用(向后兼容)。DroneProfile.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 中模板严格一致):

// 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实时探测尚未接入 TickTargetDetected 事件已定义但未触发。详见第十五章探测设备设计。

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 算法工厂与注册

/// <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 第三方提供的数据

/// <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 接口(不变)

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 共同接口

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 三种机制的差异

机制 气溶胶类型 毁伤原理 毁伤曲线特征
吸入式灭火 惰性气体 发动机吸入惰性气体 → 氧气浓度下降 → 熄火 阈值型:密度达标后快速生效,延迟短
爆燃式 活性材料 材料接触高温发动机表面 → 化学反应爆燃 触发型:密度 + 温度双条件,瞬间高伤害
吸入式爆炸 活性燃料 燃料被吸入进气道 → 内部爆炸 累积型:随暴露时间指数增长
// 实现示例伪代码

// 吸入式灭火:阈值判定
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气溶胶选型

基于规则匹配,根据目标动力类型推荐最佳毁伤机制:

目标动力 推荐气溶胶 理由
电推式 惰性气体(吸入式灭火) 电动机无高温,爆燃/爆炸不适用;惰性气体使电调/散热失效
活塞式 惰性气体(吸入式灭火) 活塞发动机依赖氧气,窒息最有效
喷吸气式 活性材料(爆燃式) 喷气发动机高温表面触发爆燃
活性燃料(吸入式爆炸) 适用于高价值高速目标,追求一击必杀
// 选型规则表(可配置)
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×dtPositionAt)。
  • 云团重叠:间距 = 2R × (1 CloudOverlapRatio),重叠比例由 planner_config.json 驱动(默认 20%)。CloudExpansionModel.RoundsNeeded 接受 spacing 参数公式仍在共享模块planner 只传配置值。
配置外置planner_config.json

planner 所有策略参数从 data/planner_config.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 接口

// === 防御规划 ===
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

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) 双路径:
    • 传入 frameStoreLiveFrames 非空 → 直接使用内存数据(仿真刚结束,零延迟)
    • 否则 → 从 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 / Word默认 PDF
  • 模板占位符填充后输出
  • 技术选型QuestPDF 或其他 .NET Standard 2.1 兼容方案(待调研)

九、服务接口概要

// === 模型管理 ===
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 SaveTarget(string scenarioId, DroneProfile target);
    void SaveDeployment(string scenarioId, List<EquipmentDeployment> 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);
    SimulationReport GetReport(string id);
    void DeleteReport(string id);
    byte[] ExportReport(string id, string format); // "pdf" | "word"
    PagedResult<SimulationReport> SearchReports(string kw, DateTime? from, DateTime? to, int page, int size);
}

九-A、多批次多编队设计

数据模型

一个仿真任务可包含多种类型、多批次的无人机,每批次有独立的航路:

Scenario "城市防御演习"
├── CombatScene共享
├── ControlZone[](共享)
├── CloudDispersal共享云团参数具体拦截方案由算法确定
│
├── 批次 A活塞×3              ← WaveId
│   ├── DroneProfileWaveId=A类型=活塞,数量=3
│   ├── RoutePlanScenarioId + WaveId=A航路 A北→南
│   └── Waypoint[]
│
├── 批次 B喷气×2              ← WaveId
│   ├── DroneProfileWaveId=B类型=高速,数量=2
│   ├── RoutePlanScenarioId + WaveId=B航路 B西→东
│   └── Waypoint[]
│
└── 批次 C旋翼×5              ← WaveId
    ├── DroneProfileWaveId=C类型=旋翼,数量=5
    ├── RoutePlanScenarioId + WaveId=C航路 C
    └── Waypoint[]

关键变更RoutePlan 主键为 (ScenarioId, WaveId) 复合键。Waypoint 通过 (ScenarioId, WaveId) 关联航路。

推荐算法

算法需要同时考虑所有无人机批次可用火力单元,自动匹配分配:

// 输入:所有无人机编队 + 已部署的火力单元
// 输出:每个编队的拦截方案 + 合并的发射计划

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) 复合键
DroneProfile 不变(已有 WaveId
Waypoint 关联 (ScenarioId, WaveId) 定位航路

IScenarioServiceSaveRoute 签名:

void SaveRoute(string scenarioId, string waveId, RoutePlan route, List<Waypoint> wps);  // waveId 参数

报告

报告新增多编队汇总:

## 编队汇总
| 批次 | 目标 | 数量 | 拦截方案 | 结果 |
|------|------|------|------|------|
| 批次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) 全扫求和)+ PositionAtO(N) 重扫定位),航路静态却每帧重算。

优化:构造时一次性预计算 _totalArc(总弧长)、_segLen[](各段段长)、_cumArc[](累积弧长)。Update()_traveledArc += speed×dt,在 _cumArc[] 线性定位所在段(航点数通常 ≤10后插值。

收益:每架无人机每帧从 O(N) ×2 降为 O(N) ×1仅定位无重算总长。无人机数 × 航点数 × FPS 规模下收益显著。

B. 仿真期零字符串分配 + JSON 推迟到 Flush最大收益项

问题:原实现每帧每实体在 CollectSnapshots 拼 JSON 字符串(StateDataUnity 端 SimulationRunner 又对每个云团 JsonDocument.Parse 解析 —— 拼装 + 解析两端分配,是帧率杀手。

优化

  • EntitySnapshot 新增强类型字段(DamageStage/Hp/CloudRadius/CloudOpacity/CloudPhase/CloudElapsed/PlatformStateStrCollectSnapshots 直接填标量,零字符串分配。
  • 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 与事务开销。

优化FrameDataStoreList<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.GetFrameWhere().ToList() 线性扫描 → 改为按 FrameIndex 建索引或分组字典。
  • 极长回放全量加载内存 → 分段/流式读取。

按 YAGNI上述在当前规模<30 FPS 需求、200+ FPS 实测)下不做,仅记录备查。


十四、探测设备设计(统一信息网络)

探测设备是"统一信息网络"的感知层,负责对来袭威胁的搜索、发现与跟踪。本系统支持三种探测手段,统一抽象为 DetectionSource,由独立探测节点(EquipmentRole.Detection)和火力单元自带的探测能力共同构成探测网。

14.1 设计现状:两阶段角色

探测设备在系统中承担两个阶段的角色,两者共用同一套物理公式DetectionCalculator)以保证一致:

阶段 角色 状态 输入 输出
事前规划 静态估算最早发现点 已实现 威胁航路 + 探测网 + 能见度 每个威胁的最早发现弧长 DetectArc + 精度
运行时实时 扫描→发现→跟踪 待实现 飞行无人机实时位置 + 探测网 TargetDetected 事件 + 跟踪坐标流
  • 事前规划(链路 ADefensePlanner.PlanDetectionCalculator.EarliestDetection,沿威胁航路求其进入探测网范围的最早点,反推拦截时机。这是当前拦截时机的唯一来源(开环)。
  • 运行时实时(链路 BDetectionEntity 在 Tick 内扫描,产生"何时真正发现"的事件流与可视化。当前未接入 TickTargetDetected 事件已定义(SimEventType.TargetDetected = 0)但全代码库未触发。

14.2 三种探测手段

手段 字段 特性 受能见度影响
雷达 RadarRange 全天候,三维波束
光电EO EORange 可见光成像 是:有效 = 基准 × min(1, Visibility/基准)
红外IR IRRange 热源探测,夜间可用

综合有效距离取三者最大(任一手段发现即算发现):

// 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 与实时探测共用):

// 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 探测精度与抛撒散布

  • DetectionAccuracym探测设备的位置误差。
  • 精度越差 → planner 增大横向覆盖范围,对冲定位不确定性:
    // DetectionCalculator.SpreadRadius
    float SpreadRadius(float accuracy) => Math.Max(0f, accuracy);  // 散布半径 ≈ 精度值
    
  • 运行时实时探测输出目标坐标时,同样按此散布半径叠加位置扰动(待实现)。

14.5 实时探测设计(待实现)

14.5.1 DetectionSource / DetectionEntity

DetectionSource 需扩展三维几何参数(决策 5

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 球冠所需的数据模型改动,集中在此便于落地追踪:

改动项 位置 内容 状态
EquipmentDeployment 表加字段 4.3 MinElevation/MaxElevation(度)、MinDetectAlt/MaxDetectAltm均可空 待加列
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 待实现

数据库迁移EquipmentDeployment 新增 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 导出库选型 QuestPDF 或其他 .NET Standard 2.1 方案
9 预置典型目标库内容 具体包含哪些无人机型号及参数
10 扩散模型的大气稳定度参数 不同天气条件下 σ 的增长速率如何量化
11 毁伤曲线的具体数值 三种机制的 damageRate、threshold、burstDamage 等参数取值
12 时空交汇优化的精度 离散采样步长 vs 解析解的选择