refactor: 推荐算法统一处理空基/地基平台类型
- ThreatProfile 新增 PreferredPlatformType 字段 - DefaultDefenseAdvisor 根据平台类型正确计算 FireTime/CruiseSpeed/ReleaseAltitude - 空基:FireTime 提前扣除飞行+下落时间;地基:保持原逻辑 - 删除 SimulationEngine.AdjustFireTimesForAirBased(引擎不再做适配) - 集成测试直接用 PreferredPlatformType 驱动空基方案 - 实施文档同步
This commit is contained in:
parent
c4d9ccc4fa
commit
0a3c444a4b
@ -160,7 +160,7 @@ Phase 8 ⬜ 待开发
|
||||
|---|------|------|
|
||||
| 8.1.1 | 探测设备搜索逻辑 | `DetectionEntity` 类已存在,需接入 `SimulationEngine` 实现探测→火控链路闭环 |
|
||||
| 8.1.2 | 蜂群运动模型 | `FormationMode.Swarm` 枚举已定义,需差异化行为(随机扰动、个体差异) |
|
||||
| 8.1.3 | 空基平台飞行动态 | `PlatformEntity` 状态机(Idle→FlyingToTarget→ReadyToRelease),巡航飞行,弹药继承载机速度下落,`Kinematics` 工具类支撑,集成测试通过 | ✅ |
|
||||
| 8.1.3 | 空基平台飞行动态 | ✅ `PlatformEntity` 状态机,巡航飞行,弹药继承载机速度。**推荐算法统一处理平台类型**,`ThreatProfile.PreferredPlatformType` 驱动,`DefaultDefenseAdvisor` 根据类型正确计算 `FireTime`/`CruiseSpeed`/`ReleaseAltitude`。引擎零适配逻辑 | ✅ |
|
||||
| 8.1.4 | 预置典型目标库 | 具体无人机型号 JSON 配置(如 DJI Mavic 3、Shahed-136 等),导入 `TargetConfig` 默认值 |
|
||||
| 8.1.5 | 毁伤曲线参数校准 | 三种毁伤模型的 damageRate/threshold/burstDamage 需甲方/领域专家确认后写入 |
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ namespace CounterDrone.Core.Algorithms
|
||||
public RoutePlan Route { get; set; } = new();
|
||||
public List<Waypoint> Waypoints { get; set; } = new();
|
||||
public List<ControlZone> ControlZones { get; set; } = new();
|
||||
/// <summary>偏好平台类型(null = 算法自行决定,当前默认地基)</summary>
|
||||
public PlatformType? PreferredPlatformType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>防御推荐方案</summary>
|
||||
|
||||
@ -105,20 +105,58 @@ namespace CounterDrone.Core.Algorithms
|
||||
var expansionTime = (float)Math.Pow((effectiveR - r0) / k, 2);
|
||||
var recommendedTiming = halfTime - expansionTime;
|
||||
|
||||
// 生成发射计划
|
||||
// 平台类型
|
||||
var platType = threat.PreferredPlatformType ?? PlatformType.GroundBased;
|
||||
var isAirBased = platType == PlatformType.AirBased;
|
||||
|
||||
// 空基参数
|
||||
var cruiseSpeed = 55f; // ~200 km/h
|
||||
var releaseAlt = isAirBased ? (float)target.TypicalAltitude + 500f : 0f;
|
||||
var fallTime = isAirBased ? Kinematics.AirDropFallTime(releaseAlt, (float)target.TypicalAltitude) : 0f;
|
||||
|
||||
// 生成发射计划和平台部署
|
||||
var fireSchedule = new List<FireEvent>();
|
||||
var platforms = new List<RecommendedPlatform>();
|
||||
var gunCount = roundsNeeded;
|
||||
|
||||
for (int i = 0; i < roundsNeeded; i++)
|
||||
{
|
||||
var offset = (i - (roundsNeeded - 1) / 2f) * spacing;
|
||||
fireSchedule.Add(new FireEvent
|
||||
var tx = mid.X + offset; var ty = mid.Y; var tz = mid.Z;
|
||||
|
||||
if (isAirBased)
|
||||
{
|
||||
FireTime = recommendedTiming,
|
||||
PlatformIndex = i % roundsNeeded,
|
||||
TargetX = mid.X + offset,
|
||||
TargetY = mid.Y,
|
||||
TargetZ = mid.Z,
|
||||
MuzzleVelocity = 800f,
|
||||
});
|
||||
// 巡逻点:防御圈边缘,偏离目标侧方
|
||||
var patrolX = mid.X - 2000;
|
||||
var patrolY = 2500f;
|
||||
var patrolZ = mid.Z - 2000;
|
||||
var flightDist = Kinematics.Distance3D(patrolX, patrolY, patrolZ, tx, releaseAlt, tz);
|
||||
var flightTime = flightDist / cruiseSpeed;
|
||||
var fireTime = recommendedTiming - flightTime - fallTime;
|
||||
if (fireTime < 0.1f) fireTime = 0.1f;
|
||||
|
||||
fireSchedule.Add(new FireEvent { FireTime = fireTime, PlatformIndex = i, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = 0f });
|
||||
platforms.Add(new RecommendedPlatform
|
||||
{
|
||||
Type = PlatformType.AirBased,
|
||||
Position = new Vector3(patrolX + i * 30, patrolY, patrolZ),
|
||||
Quantity = 1, MunitionCount = 1,
|
||||
CruiseSpeed = cruiseSpeed, ReleaseAltitude = releaseAlt,
|
||||
Cooldown = 5f, CoverageVolume = (float)ammo.InitialVolume,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
fireSchedule.Add(new FireEvent { FireTime = recommendedTiming, PlatformIndex = i, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = 800f });
|
||||
platforms.Add(new RecommendedPlatform
|
||||
{
|
||||
Type = PlatformType.GroundBased,
|
||||
Position = new Vector3(mid.X + i * 50, 0, 50),
|
||||
Quantity = 1, MunitionCount = 1,
|
||||
MuzzleVelocity = 800f, Cooldown = 5f,
|
||||
CoverageVolume = (float)ammo.InitialVolume,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var bestCloud = new CloudDispersal
|
||||
@ -138,22 +176,6 @@ namespace CounterDrone.Core.Algorithms
|
||||
EstimatedProbability = prob,
|
||||
};
|
||||
|
||||
// 平台部署:假设齐射(同时发射),每门炮打一发后冷却 5s
|
||||
// 需要 roundsNeeded 门炮同时发射
|
||||
var gunCount = roundsNeeded;
|
||||
var platforms = new List<RecommendedPlatform>();
|
||||
for (int i = 0; i < gunCount; i++)
|
||||
platforms.Add(new RecommendedPlatform
|
||||
{
|
||||
Type = PlatformType.GroundBased,
|
||||
Position = new Vector3(mid.X + i * 50, 0, 50),
|
||||
Quantity = 1,
|
||||
MunitionCount = 1,
|
||||
CoverageVolume = (float)ammo.InitialVolume,
|
||||
Cooldown = 5f,
|
||||
MuzzleVelocity = 800f,
|
||||
});
|
||||
|
||||
result.Best = new DefenseSolution
|
||||
{
|
||||
RecommendedAerosolType = aerosolType,
|
||||
|
||||
@ -114,7 +114,6 @@ namespace CounterDrone.Core.Simulation
|
||||
SimulationTime = 0;
|
||||
_entityCounter = 0;
|
||||
_nextFireIndex = 0;
|
||||
AdjustFireTimesForAirBased();
|
||||
_frameDb = _frameStore.CreateOrOpen(_taskId);
|
||||
State = SimulationState.Running;
|
||||
}
|
||||
@ -265,31 +264,6 @@ namespace CounterDrone.Core.Simulation
|
||||
public void Resume() { if (State == SimulationState.Paused) State = SimulationState.Running; }
|
||||
public void Stop() { State = SimulationState.Stopped; _frameDb?.Close(); }
|
||||
|
||||
/// <summary>为空基平台提前发射时间,补偿飞行耗时</summary>
|
||||
private void AdjustFireTimesForAirBased()
|
||||
{
|
||||
for (int i = 0; i < _fireSchedule.Count; i++)
|
||||
{
|
||||
var fe = _fireSchedule[i];
|
||||
if (fe.PlatformIndex >= _platforms.Count) continue;
|
||||
var p = _platforms[fe.PlatformIndex];
|
||||
if (p.PlatformType != Models.PlatformType.AirBased) continue;
|
||||
|
||||
var releaseAlt = p.ReleaseAltitude > 0 ? p.ReleaseAltitude : _disperseHeight;
|
||||
float dx = fe.TargetX - p.PatrolX;
|
||||
float dy = releaseAlt - p.PatrolY;
|
||||
float dz = fe.TargetZ - p.PatrolZ;
|
||||
float flightDist = (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
float flightTime = flightDist / (p.CruiseSpeed > 0.1f ? p.CruiseSpeed : 55f);
|
||||
float fallTime = Kinematics.AirDropFallTime(releaseAlt, _disperseHeight);
|
||||
|
||||
fe.FireTime -= flightTime + fallTime;
|
||||
if (fe.FireTime < 0) fe.FireTime = 0.1f;
|
||||
}
|
||||
_fireSchedule.Sort((a, b) => a.FireTime.CompareTo(b.FireTime));
|
||||
_nextFireIndex = 0;
|
||||
}
|
||||
|
||||
private List<EntitySnapshot> CollectSnapshots()
|
||||
{
|
||||
var list = new List<EntitySnapshot>();
|
||||
|
||||
@ -78,7 +78,7 @@ namespace CounterDrone.Core.Tests
|
||||
}
|
||||
|
||||
/// <summary>将 DefenseAdvisor 推荐方案写入想定</summary>
|
||||
private DefenseRecommendation GetAndApplyRecommendation()
|
||||
private DefenseRecommendation GetAndApplyRecommendation(PlatformType? preferredPlatform = null)
|
||||
{
|
||||
var detail = _scenario.GetTaskDetail(_taskId)!;
|
||||
var threat = new ThreatProfile
|
||||
@ -87,6 +87,7 @@ namespace CounterDrone.Core.Tests
|
||||
Targets = detail.Targets,
|
||||
Route = detail.Routes[0],
|
||||
Waypoints = detail.WaypointGroups["default"],
|
||||
PreferredPlatformType = preferredPlatform,
|
||||
};
|
||||
var rec = new DefaultDefenseAdvisor(_ammoCatalog).Recommend(threat);
|
||||
|
||||
@ -115,7 +116,9 @@ namespace CounterDrone.Core.Tests
|
||||
AerosolType = (int)rec.Best.RecommendedAerosolType,
|
||||
MunitionCount = p.MunitionCount,
|
||||
Cooldown = p.Cooldown,
|
||||
MuzzleVelocity = p.MuzzleVelocity,
|
||||
MuzzleVelocity = p.MuzzleVelocity > 0 ? p.MuzzleVelocity : null,
|
||||
CruiseSpeed = p.CruiseSpeed > 0 ? p.CruiseSpeed : null,
|
||||
ReleaseAltitude = p.ReleaseAltitude > 0 ? p.ReleaseAltitude : null,
|
||||
});
|
||||
_scenario.SaveDeployment(_taskId, equips);
|
||||
_lastFireSchedule = rec.Best.FireSchedule;
|
||||
@ -364,30 +367,13 @@ namespace CounterDrone.Core.Tests
|
||||
new Waypoint { PosX = 15000, PosY = 500, PosZ = 0, Speed = 300 },
|
||||
});
|
||||
|
||||
// 先用推荐算法生成方案
|
||||
var rec = GetAndApplyRecommendation();
|
||||
// 用推荐算法生成空基方案
|
||||
var rec = GetAndApplyRecommendation(PlatformType.AirBased);
|
||||
Assert.NotNull(rec.Best.FireSchedule);
|
||||
Assert.True(rec.Best.FireSchedule.Count > 0);
|
||||
Assert.Equal(PlatformType.AirBased, rec.Best.Platforms[0].Type);
|
||||
Assert.True(rec.Best.Platforms[0].CruiseSpeed > 0);
|
||||
|
||||
// 将推荐的地基平台改为空基:巡逻点在防御圈边缘空中
|
||||
var saved = _scenario.GetTaskDetail(_taskId)!;
|
||||
var midX = (float)(saved.Cloud.PositionX);
|
||||
var airEquips = new List<EquipmentDeployment>();
|
||||
foreach (var e in saved.Equipment.Where(eq => eq.EquipmentRole == (int)EquipmentRole.LaunchPlatform))
|
||||
{
|
||||
e.PlatformType = (int)Models.PlatformType.AirBased;
|
||||
// 巡逻点:防御圈边缘,侧方 2000m,高度 2500m 巡航
|
||||
e.PositionX = midX - 2000;
|
||||
e.PositionY = 2500;
|
||||
e.PositionZ = -2000;
|
||||
e.CruiseSpeed = 55; // ~200 km/h
|
||||
e.ReleaseAltitude = 1500; // 投放高度
|
||||
e.MuzzleVelocity = null; // 空基不需要炮口初速
|
||||
airEquips.Add(e);
|
||||
}
|
||||
_scenario.SaveDeployment(_taskId, airEquips);
|
||||
|
||||
// 更新 FireSchedule:引擎初始化时会自动 AdjustFireTimesForAirBased
|
||||
_lastFireSchedule = rec.Best.FireSchedule;
|
||||
|
||||
var eng = RunSimulation(8000);
|
||||
@ -396,7 +382,7 @@ namespace CounterDrone.Core.Tests
|
||||
var cloudsGenerated = eng.Events.Count(e => e.Type == SimEventType.CloudGenerated);
|
||||
var drone = eng.Drones[0];
|
||||
|
||||
var msg = $"AirBased: platforms={airEquips.Count}, launched={launched}, clouds={cloudsGenerated}, " +
|
||||
var msg = $"AirBased: platforms={rec.Best.Platforms.Count}, launched={launched}, clouds={cloudsGenerated}, " +
|
||||
$"droneStatus={drone.Status}, hp={drone.Hp:F3}, simTime={eng.SimulationTime:F1}s";
|
||||
|
||||
Assert.True(launched > 0, msg);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user