推荐算法ComputeEffectiveRadius用高斯公式,BuildFireSchedule去掉算法逻辑,129测试通过
This commit is contained in:
parent
c060030e0a
commit
6a184d9bf8
@ -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);
|
||||
|
||||
/// <summary>计算云团在目标时刻的有效半径</summary>
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ namespace CounterDrone.Core.Simulation
|
||||
{
|
||||
_ammoSpec = ammoDb.Table<AmmunitionSpec>()
|
||||
.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 };
|
||||
}
|
||||
|
||||
// 无人机
|
||||
|
||||
@ -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<AmmunitionSpec>().ToList();
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -21,7 +21,6 @@ namespace CounterDrone.Core.Tests
|
||||
private readonly FrameDataStore _frameStore;
|
||||
private readonly SQLiteConnection _mainDb;
|
||||
private List<AmmunitionSpec> _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<AmmunitionSpec>().ToList();
|
||||
_ammoCatalog = new List<AmmunitionSpec>(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;
|
||||
}
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user