fix: 空基弹药从固定阵位水平发射,修正到达判定和插值
DefensePlanner 空基: - 移除「飞到投放点再投弹」模型 - 改用固定阵位 + ComputeParabolicRange 前向计算 - ComputeHorizontal 使用 platform.PosY 替代 unit.ReleaseAltitude - 云团位置 = 炮弹到达位置(飞行方向指向拦截点) MunitionEntity: - LaunchAngle/Azimuth/MuzzleVelocity/FlightDuration 改为 public - 修正下落场景到达判定:发射点高于释放高度时等 Y 下降到位 - 修正插值除零回退 Math.Max(0.01f, ...) 导致负分母炸裂 SimulationEngine: - 删除空基「到达投放点」死代码 - 删除 InterceptCalculatorAirTests 打印测试 测试: 241 通过 0 失败
This commit is contained in:
parent
e1f576fbe5
commit
7bdda062c0
@ -517,7 +517,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
var (ia, _, la) = InterceptCalculator.ComputeHorizontal(
|
||||
wps, threat.DetectArc, typicalSpeed,
|
||||
_config.ReactionTime + expansionTime, unit.Position, unit.CruiseSpeed,
|
||||
unit.ReleaseAltitude, (float)threat.Target.TypicalAltitude);
|
||||
unit.Position.Y, (float)threat.Target.TypicalAltitude);
|
||||
crossArc = ia;
|
||||
launchAngle = la;
|
||||
}
|
||||
@ -556,19 +556,28 @@ namespace CounterDrone.Core.Algorithms
|
||||
float deliveryTime;
|
||||
if (unit.Type == PlatformType.AirBased)
|
||||
{
|
||||
if (unit.ReleaseAltitude <= 0 || unit.CruiseSpeed <= 0)
|
||||
throw new InvalidOperationException($"空基单元 {unit.Id}: ReleaseAltitude={unit.ReleaseAltitude}, CruiseSpeed={unit.CruiseSpeed} 必须>0");
|
||||
float releaseAlt = unit.ReleaseAltitude;
|
||||
float cruiseSpd = unit.CruiseSpeed;
|
||||
float fallTime = Kinematics.AirDropFallTime(releaseAlt, (float)threat.Target.TypicalAltitude);
|
||||
float driftDist = cruiseSpd * fallTime;
|
||||
float distToCloud = Kinematics.Distance3D(
|
||||
unit.Position.X, unit.Position.Y, unit.Position.Z,
|
||||
cloudGenX, releaseAlt, cloudGenZ);
|
||||
float flightDist = Math.Max(0f, distToCloud - driftDist);
|
||||
float flightTime = flightDist / cruiseSpd;
|
||||
deliveryTime = flightTime + fallTime;
|
||||
fireTime = recommendedTiming - deliveryTime;
|
||||
if (unit.CruiseSpeed <= 0)
|
||||
throw new InvalidOperationException($"空基单元 {unit.Id}: CruiseSpeed={unit.CruiseSpeed} 必须>0");
|
||||
|
||||
float targetAlt = (float)threat.Target.TypicalAltitude;
|
||||
float heightDiff = targetAlt - unit.Position.Y;
|
||||
if (heightDiff >= 0)
|
||||
return (events, $"空基单元 {unit.Id}: 平台高度({unit.Position.Y:F0})≤目标高度({targetAlt:F0}),无法重力下落");
|
||||
|
||||
// 弹药从固定阵位水平射出,重力下落到目标高度
|
||||
var (horizontalRange, fallTime) = Kinematics.ComputeParabolicRange(unit.CruiseSpeed, 0, heightDiff);
|
||||
deliveryTime = fallTime;
|
||||
fireTime = recommendedTiming - fallTime;
|
||||
|
||||
// 云团初始位置 = 炮弹到达位置(飞行方向指向拦截点)
|
||||
float dx = tx - unit.Position.X;
|
||||
float dz = tz - unit.Position.Z;
|
||||
float dist = (float)Math.Sqrt(dx * dx + dz * dz);
|
||||
if (dist > 0.001f)
|
||||
{
|
||||
cloudGenX = unit.Position.X + dx / dist * horizontalRange;
|
||||
cloudGenZ = unit.Position.Z + dz / dist * horizontalRange;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -19,19 +19,21 @@ namespace CounterDrone.Core.Simulation
|
||||
public bool HasArrived { get; private set; }
|
||||
public float ArrivalTime { get; private set; }
|
||||
|
||||
private readonly float _muzzleVelocity;
|
||||
private float _time;
|
||||
private readonly float _launchAngle;
|
||||
private readonly float _azimuth;
|
||||
public float LaunchAngle { get; }
|
||||
public float Azimuth { get; }
|
||||
public float MuzzleVelocity { get; }
|
||||
public float FlightDuration { get; private set; }
|
||||
private readonly float _startX, _startY, _startZ;
|
||||
private readonly float _launchTime;
|
||||
private float _flightDuration;
|
||||
|
||||
// 空基投放:继承载机速度
|
||||
private readonly float _carrierVelX, _carrierVelY, _carrierVelZ;
|
||||
|
||||
// 地基:标记是否已超过释放高度
|
||||
private bool _hasExceededReleaseAltitude;
|
||||
// 发射点高于释放高度时,炮弹下降到达
|
||||
private bool _arrivesDescending;
|
||||
|
||||
public MunitionEntity(string id, PlatformType launchMode, AerosolType aerosolType,
|
||||
float startX, float startY, float startZ,
|
||||
@ -47,7 +49,7 @@ namespace CounterDrone.Core.Simulation
|
||||
_startX = startX; _startY = startY; _startZ = startZ;
|
||||
PosX = startX; PosY = startY; PosZ = startZ;
|
||||
TargetX = targetX; TargetY = targetY; TargetZ = targetZ;
|
||||
_muzzleVelocity = muzzleVelocity;
|
||||
MuzzleVelocity = muzzleVelocity;
|
||||
ReleaseAltitude = releaseAltitude;
|
||||
_carrierVelX = carrierVelX;
|
||||
_carrierVelY = carrierVelY;
|
||||
@ -63,16 +65,18 @@ namespace CounterDrone.Core.Simulation
|
||||
|
||||
var dx = targetX - startX;
|
||||
var dz = targetZ - startZ;
|
||||
_azimuth = (float)System.Math.Atan2(dx, dz);
|
||||
Azimuth = (float)System.Math.Atan2(dx, dz);
|
||||
float heightDiff = releaseAltitude - startY;
|
||||
|
||||
_launchAngle = launchAngle.Value;
|
||||
var (_, tof) = Kinematics.ComputeParabolicRange(muzzleVelocity, _launchAngle, heightDiff);
|
||||
_flightDuration = tof;
|
||||
LaunchAngle = launchAngle.Value;
|
||||
var (_, tof) = Kinematics.ComputeParabolicRange(muzzleVelocity, LaunchAngle, heightDiff);
|
||||
FlightDuration = tof;
|
||||
// 发射点高于释放高度 → 下降到达;否则上升到达
|
||||
_arrivesDescending = heightDiff < 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_flightDuration = Kinematics.AirDropFallTime(startY, releaseAltitude);
|
||||
FlightDuration = Kinematics.AirDropFallTime(startY, releaseAltitude);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,22 +88,21 @@ namespace CounterDrone.Core.Simulation
|
||||
if (LaunchMode == PlatformType.GroundBased)
|
||||
{
|
||||
(float x, float y, float z) = Kinematics.ParabolicPosition(
|
||||
_startX, _startY, _startZ, _launchAngle, _azimuth, _muzzleVelocity, _time);
|
||||
_startX, _startY, _startZ, LaunchAngle, Azimuth, MuzzleVelocity, _time);
|
||||
PosX = x; PosY = y; PosZ = z;
|
||||
|
||||
if (PosY >= ReleaseAltitude && !_hasExceededReleaseAltitude)
|
||||
if (((y >= ReleaseAltitude && !_arrivesDescending) || (y <= ReleaseAltitude && _arrivesDescending)) && !_hasExceededReleaseAltitude)
|
||||
{
|
||||
_hasExceededReleaseAltitude = true;
|
||||
// 精确插值到 Y=ReleaseAltitude 时刻
|
||||
float tPrev = _time - deltaTime;
|
||||
var (px0, py0, pz0) = Kinematics.ParabolicPosition(
|
||||
_startX, _startY, _startZ, _launchAngle, _azimuth, _muzzleVelocity, tPrev);
|
||||
float frac = (ReleaseAltitude - py0) / System.Math.Max(0.01f, PosY - py0);
|
||||
_startX, _startY, _startZ, LaunchAngle, Azimuth, MuzzleVelocity, tPrev);
|
||||
float frac = (ReleaseAltitude - py0) / (PosY - py0);
|
||||
PosX = px0 + (PosX - px0) * frac;
|
||||
PosY = ReleaseAltitude;
|
||||
PosZ = pz0 + (PosZ - pz0) * frac;
|
||||
HasArrived = true;
|
||||
ArrivalTime = _launchTime + _flightDuration;
|
||||
ArrivalTime = _launchTime + FlightDuration;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -114,7 +117,7 @@ namespace CounterDrone.Core.Simulation
|
||||
{
|
||||
PosY = ReleaseAltitude;
|
||||
HasArrived = true;
|
||||
ArrivalTime = _launchTime + _flightDuration;
|
||||
ArrivalTime = _launchTime + FlightDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,17 +7,5 @@ namespace CounterDrone.Core.Tests
|
||||
{
|
||||
public class InterceptCalculatorAirTests
|
||||
{
|
||||
[Fact]
|
||||
public void Print_Result()
|
||||
{
|
||||
var route = new List<Waypoint> {
|
||||
new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 },
|
||||
new() { PosX = 20000, PosY = 500, PosZ = 0, Speed = 200 },
|
||||
};
|
||||
var unit = new Vector3(6000, 1000, 0); // 平台实际位置
|
||||
var (arc, ts, angle) = InterceptCalculator.ComputeHorizontal(
|
||||
route, 0, 200, 32, unit, 200, 1500, 500);
|
||||
Assert.True(false, $"arc={arc:F0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user