FireEvent移入Algorithms, Advisor生成FireSchedule, 引擎/测试零对齐逻辑, 129测试通过

This commit is contained in:
tian 2026-06-12 11:36:15 +08:00
parent 98e16ea720
commit e3486e5bdf
6 changed files with 41 additions and 79 deletions

View File

@ -3,7 +3,7 @@ using CounterDrone.Core.Models;
namespace CounterDrone.Core.Algorithms
{
/// <summary>威胁画像 — 防御推荐算法的输入</summary>
/// <summary>威胁画像</summary>
public class ThreatProfile
{
public CombatScene Environment { get; set; } = new();
@ -26,19 +26,15 @@ namespace CounterDrone.Core.Algorithms
public AerosolType RecommendedAerosolType { get; set; }
public string AerosolRationale { get; set; } = string.Empty;
public CloudDispersal RecommendedCloud { get; set; } = new();
/// <summary>多轮次云团列表(含主云团)</summary>
public List<CloudDispersal> CloudSalvo { get; set; } = new();
/// <summary>齐射参数:弹药数,由算法填充</summary>
public int SalvoRounds { get; set; } = 1;
/// <summary>齐射参数云团间隔m由算法填充</summary>
public float SalvoSpacing { get; set; }
public List<RecommendedPlatform> Platforms { get; set; } = new();
public List<RecommendedDetection> Detections { get; set; } = new();
public float InterceptProbability { get; set; }
public string SummaryRationale { get; set; } = string.Empty;
/// <summary>由推荐方案生成的发射计划,仿真器直接执行</summary>
public List<FireEvent> FireSchedule { get; set; } = new();
}
public class RecommendedPlatform
@ -80,4 +76,13 @@ namespace CounterDrone.Core.Algorithms
public string ColorHex { get; set; } = "#FFFFFF";
public float SizeMultiplier { get; set; } = 1.0f;
}
/// <summary>发射计划中的单次事件(算法生成 → 引擎执行)</summary>
public class FireEvent
{
public float FireTime;
public int PlatformIndex;
public float TargetX, TargetY, TargetZ;
public float MuzzleVelocity;
}
}

View File

@ -105,6 +105,22 @@ namespace CounterDrone.Core.Algorithms
var expansionTime = (float)Math.Pow((effectiveR - r0) / k, 2);
var recommendedTiming = halfTime - expansionTime;
// 生成发射计划
var fireSchedule = new List<FireEvent>();
for (int i = 0; i < roundsNeeded; i++)
{
var offset = (i - (roundsNeeded - 1) / 2f) * spacing;
fireSchedule.Add(new FireEvent
{
FireTime = recommendedTiming,
PlatformIndex = i % roundsNeeded,
TargetX = mid.X + offset,
TargetY = mid.Y,
TargetZ = mid.Z,
MuzzleVelocity = 800f,
});
}
var bestCloud = new CloudDispersal
{
AerosolType = (int)aerosolType,
@ -117,8 +133,6 @@ namespace CounterDrone.Core.Algorithms
Source = "Algorithm",
PositionMode = (int)PositionMode.AlgorithmRecommended,
RecommendedTiming = recommendedTiming,
SalvoRounds = roundsNeeded,
SalvoSpacing = spacing,
};
// 平台部署:假设齐射(同时发射),每门炮打一发后冷却 5s
@ -142,8 +156,7 @@ namespace CounterDrone.Core.Algorithms
RecommendedAerosolType = aerosolType,
AerosolRationale = rationale,
RecommendedCloud = bestCloud,
SalvoRounds = roundsNeeded,
SalvoSpacing = spacing,
FireSchedule = fireSchedule,
Platforms = platforms,
Detections = new List<RecommendedDetection>
{

View File

@ -45,10 +45,5 @@ namespace CounterDrone.Core.Models
public double? CriticalPosY { get; set; }
public double? CriticalPosZ { get; set; }
public double? CriticalTiming { get; set; }
/// <summary>齐射弹药数(推荐算法填充)</summary>
public int SalvoRounds { get; set; }
/// <summary>云团间隔m推荐算法填充</summary>
public double SalvoSpacing { get; set; }
}
}

View File

@ -9,14 +9,6 @@ using SQLite;
namespace CounterDrone.Core.Simulation
{
public class FireEvent
{
public float FireTime;
public int PlatformIndex;
public float TargetX, TargetY, TargetZ;
public float MuzzleVelocity;
}
/// <summary>仿真引擎 — 纯执行器,接收发射计划按时间表执行</summary>
public class SimulationEngine
{

View File

@ -1,11 +1,11 @@
using System.Collections.Generic;
using System.Linq;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using Xunit;
namespace CounterDrone.Core.Tests
{
/// <summary>验证三阶段扩散模型下推荐算法的参数合理性</summary>
public class DispersionAdvisorRealityTests
{
private ThreatProfile CreatePistonThreat(float speed = 200, float length = 20000)
@ -33,24 +33,15 @@ namespace CounterDrone.Core.Tests
var advisor = new DefaultDefenseAdvisor(DefaultAmmunition.GetAll());
var rec = advisor.Recommend(CreatePistonThreat());
// 基本断言
Assert.Equal(AerosolType.InertGas, rec.Best.RecommendedAerosolType);
Assert.True(rec.Best.SalvoRounds >= 1, $"齐射数={rec.Best.SalvoRounds}应≥1");
Assert.True(rec.Best.SalvoSpacing > 0, $"间隔={rec.Best.SalvoSpacing}m应>0");
Assert.True(rec.Best.RecommendedCloud.RecommendedTiming > 0,
$"推荐时机={rec.Best.RecommendedCloud.RecommendedTiming}s应>0");
Assert.NotEmpty(rec.Best.FireSchedule);
Assert.True(rec.Best.FireSchedule.Count >= 1, $"齐射数={rec.Best.FireSchedule.Count}应≥1");
// 检查膨胀时间
var ammo = DefaultAmmunition.GetByType(AerosolType.InertGas);
var r0 = 3.3f * System.Math.Pow(ammo.BurstChargeKg, 0.32);
var k = ammo.TurbulentExpansionK;
var halfTime = 20000f / (200f / 3.6f) / 2f;
var effectiveR = r0 + k * System.Math.Sqrt(System.Math.Min(halfTime, 30));
if (halfTime > 30) effectiveR += k * System.Math.Sqrt(halfTime - 30) * 0.3f;
// 膨胀应在合理范围
Assert.True(effectiveR > 10, $"有效半径={effectiveR:F1}m应>10m初始{r0:F1}m");
Assert.True(effectiveR < 200, $"有效半径={effectiveR:F1}m应<200m");
// 发射计划中的云团位置应在航路范围内且沿航路展开
var positions = rec.Best.FireSchedule.Select(f => f.TargetX).OrderBy(x => x).ToList();
Assert.True(positions.First() > 0, "第一个云团应在航路起点之后");
Assert.True(positions.Last() < 20000, "最后一个云团应在航路终点之前");
Assert.True(positions.Last() - positions.First() > 10, "云团应沿航路展开");
}
[Fact]
@ -62,7 +53,6 @@ namespace CounterDrone.Core.Tests
var timing = rec.Best.RecommendedCloud.RecommendedTiming!.Value;
var halfTime = 20000f / (200f / 3.6f) / 2f;
// 推荐时机应明显早于无人机到达中点(因为有飞行时间和膨胀时间)
Assert.True(timing < halfTime - 5,
$"推荐时机={timing:F1}s 应早于中点={halfTime:F1}s预留飞行+膨胀时间)");
}

View File

@ -21,6 +21,7 @@ namespace CounterDrone.Core.Tests
private readonly FrameDataStore _frameStore;
private readonly SQLiteConnection _mainDb;
private List<AmmunitionSpec> _ammoCatalog;
private List<FireEvent> _lastFireSchedule;
private string _taskId = string.Empty;
public FullPipelineTests()
@ -73,42 +74,7 @@ namespace CounterDrone.Core.Tests
/// <summary>从推荐方案的时机参数生成发射计划</summary>
private List<FireEvent> BuildFireSchedule(TaskFullConfig detail)
{
var schedule = new List<FireEvent>();
var cloud = detail.Cloud;
if (!cloud.RecommendedTiming.HasValue || cloud.RecommendedTiming.Value <= 0)
return schedule;
var platforms = detail.Equipment
.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform)
.ToList();
if (platforms.Count == 0) return schedule;
// 齐射展开:按推荐间隔沿航路排列云团位置
var spacing = (float)cloud.SalvoSpacing;
if (spacing <= 0) spacing = 50f;
var releaseAlt = (float)cloud.DisperseHeight;
if (releaseAlt < 10) releaseAlt = 300f;
for (int i = 0; i < platforms.Count; i++)
{
var p = platforms[i];
var mv = (float)(p.MuzzleVelocity ?? 800);
var offset = (i - (platforms.Count - 1) / 2f) * spacing;
var tx = (float)cloud.PositionX + offset; 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, mv, releaseAlt);
var sinA = (float)Math.Sin(angle);
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 = i, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = mv });
}
return schedule;
return _lastFireSchedule ?? new List<FireEvent>();
}
/// <summary>将 DefenseAdvisor 推荐方案写入想定</summary>
@ -152,6 +118,7 @@ namespace CounterDrone.Core.Tests
MuzzleVelocity = p.MuzzleVelocity,
});
_scenario.SaveDeployment(_taskId, equips);
_lastFireSchedule = rec.Best.FireSchedule;
return rec;
}