From 7bdda062c042236f6c2526b33102bf313afd0d85 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 17 Jun 2026 11:23:02 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=A9=BA=E5=9F=BA=E5=BC=B9=E8=8D=AF?= =?UTF-8?q?=E4=BB=8E=E5=9B=BA=E5=AE=9A=E9=98=B5=E4=BD=8D=E6=B0=B4=E5=B9=B3?= =?UTF-8?q?=E5=8F=91=E5=B0=84=EF=BC=8C=E4=BF=AE=E6=AD=A3=E5=88=B0=E8=BE=BE?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=E5=92=8C=E6=8F=92=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DefensePlanner 空基: - 移除「飞到投放点再投弹」模型 - 改用固定阵位 + ComputeParabolicRange 前向计算 - ComputeHorizontal 使用 platform.PosY 替代 unit.ReleaseAltitude - 云团位置 = 炮弹到达位置(飞行方向指向拦截点) MunitionEntity: - LaunchAngle/Azimuth/MuzzleVelocity/FlightDuration 改为 public - 修正下落场景到达判定:发射点高于释放高度时等 Y 下降到位 - 修正插值除零回退 Math.Max(0.01f, ...) 导致负分母炸裂 SimulationEngine: - 删除空基「到达投放点」死代码 - 删除 InterceptCalculatorAirTests 打印测试 测试: 241 通过 0 失败 --- .../Algorithms/DefensePlanner.cs | 37 ++++++++++++------- .../Simulation/MunitionEntity.cs | 37 ++++++++++--------- .../InterceptCalculatorAirTests.cs | 12 ------ 3 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs index 45dcff2..a022b87 100644 --- a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs @@ -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 { diff --git a/src/CounterDrone.Core/Simulation/MunitionEntity.cs b/src/CounterDrone.Core/Simulation/MunitionEntity.cs index 6b412db..e840621 100644 --- a/src/CounterDrone.Core/Simulation/MunitionEntity.cs +++ b/src/CounterDrone.Core/Simulation/MunitionEntity.cs @@ -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; } } } diff --git a/test/unit/CounterDrone.Core.Tests/InterceptCalculatorAirTests.cs b/test/unit/CounterDrone.Core.Tests/InterceptCalculatorAirTests.cs index 06b8e59..668bd4e 100644 --- a/test/unit/CounterDrone.Core.Tests/InterceptCalculatorAirTests.cs +++ b/test/unit/CounterDrone.Core.Tests/InterceptCalculatorAirTests.cs @@ -7,17 +7,5 @@ namespace CounterDrone.Core.Tests { public class InterceptCalculatorAirTests { - [Fact] - public void Print_Result() - { - var route = new List { - 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}"); - } } }