From 6a184d9bf893062a58d6f03e98c281e124a1f4be Mon Sep 17 00:00:00 2001
From: tian <11429339@qq.com>
Date: Fri, 12 Jun 2026 11:03:30 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E8=8D=90=E7=AE=97=E6=B3=95ComputeEffe?=
=?UTF-8?q?ctiveRadius=E7=94=A8=E9=AB=98=E6=96=AF=E5=85=AC=E5=BC=8F?=
=?UTF-8?q?=EF=BC=8CBuildFireSchedule=E5=8E=BB=E6=8E=89=E7=AE=97=E6=B3=95?=
=?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C129=E6=B5=8B=E8=AF=95=E9=80=9A?=
=?UTF-8?q?=E8=BF=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Algorithms/DefaultDefenseAdvisor.cs | 27 +++++++-
.../Simulation/SimulationEngine.cs | 2 +-
.../Scripts/Managers/ManagerVerification.cs | 2 +-
.../Scripts/Managers/SimulationBootstrap.cs | 2 +-
.../Scripts/Managers/SimulationRunner.cs | 40 ++++--------
.../CounterDrone.Core.Tests/EdgeCaseTests.cs | 12 +---
.../FullPipelineTests.cs | 64 ++++---------------
.../SimulationEngineTests.cs | 13 +---
8 files changed, 52 insertions(+), 110 deletions(-)
diff --git a/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs b/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs
index 7212912..3707f96 100644
--- a/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs
+++ b/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs
@@ -87,10 +87,10 @@ namespace CounterDrone.Core.Algorithms
var r0 = 3.3f * (float)Math.Pow(Math.Max(0.01, (float)ammo.BurstChargeKg), 0.32);
var k = (float)ammo.TurbulentExpansionK;
var maxDur = (float)ammo.MaxDuration;
- // 无人机到达中点时云团已膨胀了 halfTime 秒
var halfTime = totalFlightTime / 2f;
- var effectiveR = r0 + k * (float)Math.Sqrt(Math.Min(halfTime, 30f)); // 不超过 Phase 2
- if (halfTime > 30f) effectiveR += k * (float)Math.Sqrt(halfTime - 30f) * 0.3f; // Phase 3 增速变慢
+ var windSpeed = (float)threat.Environment.WindSpeed;
+ var weather = (WeatherType)threat.Environment.WeatherType;
+ var effectiveR = ComputeEffectiveRadius(r0, k, halfTime, windSpeed, weather, ammo);
var crossTime = (2f * effectiveR) / avgSpeed;
var neededExposure = aerosolType == AerosolType.ActiveMaterial ? 2f : 6f;
@@ -188,5 +188,26 @@ namespace CounterDrone.Core.Algorithms
}
private static Vector3 ToV3(Waypoint wp) => new((float)wp.PosX, (float)wp.PosY, (float)wp.PosZ);
+
+ /// 计算云团在目标时刻的有效半径
+ private static float ComputeEffectiveRadius(float r0, float k, float halfTime,
+ float windSpeed, WeatherType weather, AmmunitionSpec ammo)
+ {
+ // Phase 2 结束时的半径
+ var rPhase2 = r0 + k * (float)Math.Sqrt(Math.Min(halfTime, 30f));
+ if (halfTime <= 30f) return rPhase2;
+
+ // Phase 3:用高斯扩散公式计算浓度场下的有效半径
+ var x = Math.Max(1f, windSpeed * (halfTime - 30f));
+ var cls = Kinematics.GetStabilityClass(weather, windSpeed);
+ var sY = Kinematics.SigmaY(cls, x);
+ var sZ = Kinematics.SigmaZ(cls, x);
+ var peakC = Kinematics.GaussianPeakConcentration((float)ammo.SourceStrength, sY, sZ);
+ var threshold = (float)ammo.EffectiveConcentration;
+ if (peakC <= threshold) return rPhase2;
+
+ var sigma = (sY + sZ) / 2f;
+ return rPhase2 + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / threshold));
+ }
}
}
diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs
index 3a65d10..e72d02f 100644
--- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs
+++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs
@@ -89,7 +89,7 @@ namespace CounterDrone.Core.Simulation
{
_ammoSpec = ammoDb.Table()
.FirstOrDefault(a => a.AerosolType == config.Cloud.AerosolType)
- ?? new AmmunitionSpec { InitialRadius = 50, CoreDensity = 1.0f, DispersionRateBase = 5, MaxRadius = 200, MaxDuration = 60 };
+ ?? new AmmunitionSpec { InitialRadius = 50, CoreDensity = 1.0f, SourceStrength = 1.0, MaxRadius = 200, MaxDuration = 60 };
}
// 无人机
diff --git a/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs b/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs
index 1a9d7ef..6aec82d 100644
--- a/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs
+++ b/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs
@@ -49,7 +49,7 @@ namespace CounterDrone.Unity
db.Insert(new AmmunitionSpec
{
AerosolType = (int)AerosolType.InertGas, InitialRadius = 50,
- CoreDensity = 1.0, DispersionRateBase = 5, MaxRadius = 500,
+ CoreDensity = 1.0, SourceStrength = 50.0, DispersionRateBase = 3.0, MaxRadius = 500,
MaxDuration = 120, EffectiveConcentration = 0.05,
});
var ammoCatalog = db.Table().ToList();
diff --git a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
index a71493d..f3c3032 100644
--- a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
+++ b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs
@@ -37,7 +37,7 @@ namespace CounterDrone.Unity
db.Insert(new AmmunitionSpec
{
AerosolType = (int)AerosolType.InertGas, Name = "惰性气体弹",
- InitialRadius = 50, CoreDensity = 1.0, DispersionRateBase = 5,
+ InitialRadius = 50, CoreDensity = 1.0, SourceStrength = 50.0, DispersionRateBase = 3.0,
MaxRadius = 500, MaxDuration = 120, EffectiveConcentration = 0.05,
});
diff --git a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs
index 3f204cc..7bddc70 100644
--- a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs
+++ b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs
@@ -186,48 +186,30 @@ namespace CounterDrone.Unity
var cloud = detail.Cloud;
if (!cloud.RecommendedTiming.HasValue || cloud.RecommendedTiming <= 0) return schedule;
- var platforms = detail.Equipment
- .FindAll(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform);
+ // 每个发射平台打一发,目标云团位置(平台已由推荐算法部署到最佳位置)
+ var platforms = detail.Equipment.FindAll(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform);
if (platforms.Count == 0) return schedule;
- var ammo = ammoCatalog.Find(a => a.AerosolType == cloud.AerosolType);
- if (ammo == null) return schedule;
-
- var target = detail.Targets[0];
- var avgSpeed = (float)target.TypicalSpeed / 3.6f;
- var neededExposure = cloud.AerosolType == (int)AerosolType.ActiveMaterial ? 2f : 6f;
- var spacing = (float)ammo.InitialRadius * 1.5f;
- var diameter = 2f * (float)ammo.InitialRadius;
- var roundsNeeded = Mathf.Max(1, Mathf.CeilToInt((neededExposure * avgSpeed - diameter) / spacing) + 1);
-
- var p0 = platforms[0];
- var mv = (float)(p0.MuzzleVelocity ?? 800);
var releaseAlt = (float)cloud.DisperseHeight;
+ if (releaseAlt < 10) releaseAlt = 300f;
- for (int r = 0; r < roundsNeeded; r++)
+ for (int i = 0; i < platforms.Count; i++)
{
- var offset = (r - (roundsNeeded - 1) / 2f) * spacing;
- var tx = (float)cloud.PositionX + offset; var ty = (float)cloud.PositionY; var tz = (float)cloud.PositionZ;
- var dx = tx - (float)p0.PositionX; var dz = tz - (float)p0.PositionZ;
+ var p = platforms[i];
+ var mv = (float)(p.MuzzleVelocity ?? 800);
+ var tx = (float)cloud.PositionX; var ty = (float)cloud.PositionY; var tz = (float)cloud.PositionZ;
+ var dx = tx - (float)p.PositionX; var dz = tz - (float)p.PositionZ;
var dist = Mathf.Sqrt(dx * dx + dz * dz);
-
var angle = Kinematics.CalculateLaunchAngle(dist, mv, releaseAlt);
var sinA = Mathf.Sin(angle);
var tUp = mv * sinA / 9.81f;
var peakH = mv * mv * sinA * sinA / (2f * 9.81f);
var dropH = Mathf.Max(0, peakH - releaseAlt);
- var flightTime = tUp + Mathf.Sqrt(2f * dropH / 9.81f);
-
- var fireTime = (float)cloud.RecommendedTiming.Value - flightTime;
+ var ft = tUp + Mathf.Sqrt(2f * dropH / 9.81f);
+ var fireTime = (float)cloud.RecommendedTiming.Value - ft;
if (fireTime < 0) fireTime = 0.1f;
-
- schedule.Add(new FireEvent
- {
- FireTime = fireTime, PlatformIndex = r % platforms.Count,
- TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = mv,
- });
+ schedule.Add(new FireEvent { FireTime = fireTime, PlatformIndex = i, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = mv });
}
-
return schedule;
}
}
diff --git a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs
index 954b18b..7482d10 100644
--- a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs
+++ b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs
@@ -25,17 +25,7 @@ namespace CounterDrone.Core.Tests
var paths = new TestPathProvider(_testDir);
var dbm = new DatabaseManager(paths);
_db = dbm.OpenMainDb();
-
- _db.Insert(new AmmunitionSpec
- {
- AerosolType = (int)AerosolType.InertGas,
- InitialRadius = 50,
- CoreDensity = 1.0,
- SourceStrength = 50.0, DispersionRateBase = 3.0,
- MaxRadius = 500,
- MaxDuration = 120,
- EffectiveConcentration = 0.05,
- });
+ foreach (var a in DefaultAmmunition.GetAll()) _db.Insert(a);
_scenario = new ScenarioService(
new SimTaskRepository(_db), new CombatSceneRepository(_db),
diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs
index e36c088..914de85 100644
--- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs
+++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs
@@ -21,7 +21,6 @@ namespace CounterDrone.Core.Tests
private readonly FrameDataStore _frameStore;
private readonly SQLiteConnection _mainDb;
private List _ammoCatalog;
- private DefenseRecommendation _lastRecommendation;
private string _taskId = string.Empty;
public FullPipelineTests()
@@ -31,44 +30,10 @@ namespace CounterDrone.Core.Tests
var dbm = new DatabaseManager(_paths);
_mainDb = dbm.OpenMainDb();
- _mainDb.Insert(new AmmunitionSpec
- {
- Id = "ammo-inert",
- AerosolType = (int)AerosolType.InertGas,
- Name = "惰性气体弹",
- InitialRadius = 50,
- CoreDensity = 1.0,
- SourceStrength = 500.0,
- MaxRadius = 500,
- MaxDuration = 120,
- EffectiveConcentration = 0.05,
- });
- _mainDb.Insert(new AmmunitionSpec
- {
- Id = "ammo-active",
- AerosolType = (int)AerosolType.ActiveMaterial,
- Name = "活性材料弹",
- InitialRadius = 30,
- CoreDensity = 2.0,
- SourceStrength = 500.0,
- MaxRadius = 400,
- MaxDuration = 90,
- EffectiveConcentration = 0.1,
- });
- _mainDb.Insert(new AmmunitionSpec
- {
- Id = "ammo-fuel",
- AerosolType = (int)AerosolType.ActiveFuel,
- Name = "活性燃料弹",
- InitialRadius = 40,
- CoreDensity = 1.5,
- SourceStrength = 500.0,
- MaxRadius = 450,
- MaxDuration = 100,
- EffectiveConcentration = 0.03,
- });
+ // 使用默认弹药规格
+ foreach (var a in DefaultAmmunition.GetAll()) _mainDb.Insert(a);
- _ammoCatalog = _mainDb.Table().ToList();
+ _ammoCatalog = new List(DefaultAmmunition.GetAll());
_scenario = new ScenarioService(
new SimTaskRepository(_mainDb), new CombatSceneRepository(_mainDb),
@@ -122,30 +87,26 @@ namespace CounterDrone.Core.Tests
.ToList();
if (platforms.Count == 0) return schedule;
- // 直接从推荐方案读取齐射参数
- var roundsNeeded = _lastRecommendation.Best.SalvoRounds;
- var spacing = _lastRecommendation.Best.SalvoSpacing;
-
- var p = platforms[0];
- var muzzleV = (float)(p.MuzzleVelocity ?? 800);
+ // 每个部署平台打一发(平台数量由推荐算法确定)
var releaseAlt = (float)cloud.DisperseHeight;
if (releaseAlt < 10) releaseAlt = 300f;
- for (int r = 0; r < roundsNeeded; r++)
+ for (int i = 0; i < platforms.Count; i++)
{
- var offset = (r - (roundsNeeded - 1) / 2f) * spacing;
- var tx = (float)cloud.PositionX + offset; var ty = (float)cloud.PositionY; var tz = (float)cloud.PositionZ;
+ var p = platforms[i];
+ var mv = (float)(p.MuzzleVelocity ?? 800);
+ var tx = (float)cloud.PositionX; var ty = (float)cloud.PositionY; var tz = (float)cloud.PositionZ;
var dx = tx - (float)p.PositionX; var dz = tz - (float)p.PositionZ;
var dist = (float)Math.Sqrt(dx * dx + dz * dz);
- var angle = Kinematics.CalculateLaunchAngle(dist, muzzleV, releaseAlt);
+ var angle = Kinematics.CalculateLaunchAngle(dist, mv, releaseAlt);
var sinA = (float)Math.Sin(angle);
- var tUp = muzzleV * sinA / 9.81f;
- var peakH = muzzleV * muzzleV * sinA * sinA / (2f * 9.81f);
+ var tUp = mv * sinA / 9.81f;
+ var peakH = mv * mv * sinA * sinA / (2f * 9.81f);
var dropH = Math.Max(0, peakH - releaseAlt);
var ft = tUp + (float)Math.Sqrt(2f * dropH / 9.81f);
var fireTime = (float)cloud.RecommendedTiming!.Value - ft;
if (fireTime < 0) fireTime = 0.1f;
- schedule.Add(new FireEvent { FireTime = fireTime, PlatformIndex = r % platforms.Count, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = muzzleV });
+ schedule.Add(new FireEvent { FireTime = fireTime, PlatformIndex = i, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = mv });
}
return schedule;
}
@@ -192,7 +153,6 @@ namespace CounterDrone.Core.Tests
});
_scenario.SaveDeployment(_taskId, equips);
- _lastRecommendation = rec;
return rec;
}
diff --git a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs
index 15c5b64..741636b 100644
--- a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs
+++ b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs
@@ -27,18 +27,7 @@ namespace CounterDrone.Core.Tests
var paths = new TestPathProvider(_testDir);
var dbManager = new DatabaseManager(paths);
_mainDb = dbManager.OpenMainDb();
-
- _mainDb.Insert(new AmmunitionSpec
- {
- AerosolType = (int)AerosolType.InertGas,
- Name = "Test Ammo",
- InitialRadius = 50,
- CoreDensity = 1.0,
- SourceStrength = 50.0, DispersionRateBase = 3.0,
- MaxRadius = 500,
- MaxDuration = 120,
- EffectiveConcentration = 0.05,
- });
+ foreach (var a in DefaultAmmunition.GetAll()) _mainDb.Insert(a);
_scenarioService = new ScenarioService(
new SimTaskRepository(_mainDb), new CombatSceneRepository(_mainDb),