refactor: 移除隐藏 Math.Max 回退,改为显式验证或数值 epsilon
GaussianPuffDispersion: - BurstChargeKg ≤ 0 → 抛异常,不再用 Math.Max(0.01, ...) 掩护 - Phase3 扩散距离 x ≤ 0 → 跳过,不再用 Math.Max(1f, ...) - Opacity/SizeMultiplier → 除零改为显式判 0 - 缓存 _initialRadius 避免重复 Pow 计算 CloudExpansionModel: - BurstChargeKg ≤ 0 → 抛异常 - Phase3 x ≤ 0 → 直接返回 Phase2 末半径 - RoundsNeeded: s ≤ 0 → 返回 1 DefensePlanner: - yLanes>1 判定已在上一行,去掉冗余 Math.Max(1, ...) InterceptCalculator: - 保留 +0.001f epsilon(确保 ts>0),但不作为隐藏回退 - 改用显式加偏移替代 Math.Max(0.001f, ...) DetectionCalculator.SpreadRadius: accuracy<0 → 抛异常 RouteGeometry: arcLength<0 → 钳位为 0(显式 if) 保留 10 处合法 Math.Max(取两值中较大者或几何钳位)
This commit is contained in:
parent
7bdda062c0
commit
da4e8d5c63
@ -8,15 +8,21 @@ namespace CounterDrone.Core.Algorithms
|
||||
{
|
||||
private readonly AmmunitionSpec _ammo;
|
||||
private readonly CombatScene _env;
|
||||
private readonly float _initialRadius;
|
||||
|
||||
public CloudExpansionModel(AmmunitionSpec ammo, CombatScene env)
|
||||
{
|
||||
_ammo = ammo;
|
||||
_env = env;
|
||||
|
||||
var w = (float)ammo.BurstChargeKg;
|
||||
if (w <= 0)
|
||||
throw new ArgumentException($"BurstChargeKg 必须 > 0,实际: {w}", nameof(ammo));
|
||||
_initialRadius = 3.3f * (float)Math.Pow(w, 0.32);
|
||||
}
|
||||
|
||||
/// <summary>Phase 1 爆轰初始半径 (m)</summary>
|
||||
public float InitialRadius => 3.3f * (float)Math.Pow(Math.Max(0.01, (float)_ammo.BurstChargeKg), 0.32f);
|
||||
public float InitialRadius => _initialRadius;
|
||||
/// <summary>Phase 2 结束时的有效半径</summary>
|
||||
public float TurbulentRadius => RadiusAt(Phase2Duration);
|
||||
|
||||
@ -29,21 +35,22 @@ namespace CounterDrone.Core.Algorithms
|
||||
/// <summary>指定时刻湍流膨胀半径 R(t) = R₀ + k√t</summary>
|
||||
public float RadiusAt(float elapsedSeconds)
|
||||
{
|
||||
if (elapsedSeconds <= 0) return InitialRadius;
|
||||
if (elapsedSeconds <= 0) return _initialRadius;
|
||||
float p2 = Phase2Duration;
|
||||
bool inPhase3 = elapsedSeconds > p2;
|
||||
if (!inPhase3)
|
||||
return InitialRadius + TurbulentExpansionK * (float)Math.Sqrt(elapsedSeconds);
|
||||
return _initialRadius + TurbulentExpansionK * (float)Math.Sqrt(elapsedSeconds);
|
||||
|
||||
// Phase 3: 高斯扩散
|
||||
float windSpeed = (float)_env.WindSpeed;
|
||||
float x = Math.Max(1f, windSpeed * (elapsedSeconds - p2));
|
||||
float x = windSpeed * (elapsedSeconds - p2);
|
||||
if (x <= 0) return _initialRadius + TurbulentExpansionK * (float)Math.Sqrt(p2);
|
||||
var cls = Kinematics.GetStabilityClass((WeatherType)_env.WeatherType, windSpeed);
|
||||
float sY = Kinematics.SigmaY(cls, x);
|
||||
float sZ = Kinematics.SigmaZ(cls, x);
|
||||
float peakC = Kinematics.GaussianPeakConcentration((float)_ammo.SourceStrength, sY, sZ);
|
||||
float effConc = (float)_ammo.EffectiveConcentration;
|
||||
float rPhase2 = InitialRadius + TurbulentExpansionK * (float)Math.Sqrt(p2);
|
||||
float rPhase2 = _initialRadius + TurbulentExpansionK * (float)Math.Sqrt(p2);
|
||||
if (peakC <= effConc) return rPhase2;
|
||||
float sigma = (sY + sZ) / 2f;
|
||||
return rPhase2 + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / effConc));
|
||||
@ -54,26 +61,25 @@ namespace CounterDrone.Core.Algorithms
|
||||
{
|
||||
float r = RadiusAt(elapsedSeconds);
|
||||
float volume = (4f / 3f) * (float)Math.PI * r * r * r;
|
||||
return volume > 0.001f ? (float)_ammo.SourceStrength / volume : (float)_ammo.CoreDensity;
|
||||
if (volume <= 0.001f) return (float)_ammo.CoreDensity;
|
||||
return (float)_ammo.SourceStrength / volume;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>覆盖指定距离需要的云团数量</summary>
|
||||
/// <param name="requiredCoverage">需要覆盖的距离 (m)</param>
|
||||
/// <param name="spacing">相邻云团中心间距 (m),默认=2R(相切)。重叠时由调用方传入更小值。</param>
|
||||
public int RoundsNeeded(float requiredCoverage, float? spacing = null)
|
||||
{
|
||||
float s = spacing ?? 2f * TurbulentRadius;
|
||||
return Math.Max(1, (int)Math.Ceiling(requiredCoverage / s));
|
||||
if (s <= 0) return 1;
|
||||
int n = (int)Math.Ceiling(requiredCoverage / s);
|
||||
return n < 1 ? 1 : n;
|
||||
}
|
||||
|
||||
/// <summary>达到指定半径所需时间 (s)</summary>
|
||||
public float TimeToReach(float radius)
|
||||
{
|
||||
float r0 = InitialRadius;
|
||||
if (radius <= r0) return 0;
|
||||
if (radius <= _initialRadius) return 0;
|
||||
float k = TurbulentExpansionK;
|
||||
return (radius - r0) * (radius - r0) / (k * k);
|
||||
return (radius - _initialRadius) * (radius - _initialRadius) / (k * k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -527,7 +527,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
// 法向 = 切向旋转 90°,用 RouteGeometry.TangentAt 取穿越点处航路方向
|
||||
var (tanX, tanZ) = RouteGeometry.TangentAt(wps, crossArc);
|
||||
float normalX = -tanZ, normalZ = tanX; // 切向逆时针 90° = 左侧法向
|
||||
float laneSpacing = yLanes > 1 ? formationWidth / Math.Max(1, yLanes - 1) : 0f;
|
||||
float laneSpacing = yLanes > 1 ? formationWidth / (yLanes - 1) : 0f;
|
||||
float laneOffset = yLane * laneSpacing;
|
||||
|
||||
// 穿越点 = 航路上 crossArc 处 + 横向车道偏移
|
||||
|
||||
@ -137,7 +137,9 @@ namespace CounterDrone.Core.Algorithms
|
||||
/// 当前模型:散布半径 = 精度值(如精度 100m → 散布 ±100m)。</summary>
|
||||
public static float SpreadRadius(float accuracy)
|
||||
{
|
||||
return Math.Max(0f, accuracy);
|
||||
if (accuracy < 0)
|
||||
throw new ArgumentException($"accuracy 必须 ≥ 0,实际: {accuracy}", nameof(accuracy));
|
||||
return accuracy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,11 +19,11 @@ namespace CounterDrone.Core.Algorithms
|
||||
private Vector3 _center;
|
||||
private Vector3 _windVelocity;
|
||||
private bool _inPhase3;
|
||||
private float _initialRadius;
|
||||
|
||||
public Vector3 Center => _center;
|
||||
public float Radius => _currentRadius;
|
||||
public float CoreDensity => _currentDensity;
|
||||
/// <summary>云团中心峰值浓度(无人机穿过位置)</summary>
|
||||
public float PeakDensity { get; private set; }
|
||||
public float EffectiveRadius => _currentRadius;
|
||||
public ParticleParams Particles { get; } = new();
|
||||
@ -38,9 +38,11 @@ namespace CounterDrone.Core.Algorithms
|
||||
_elapsed = 0f;
|
||||
_inPhase3 = false;
|
||||
|
||||
// Phase 1: 爆轰膨胀 → 初始半径
|
||||
var w = (float)ammo.BurstChargeKg;
|
||||
_currentRadius = 3.3f * (float)Math.Pow(Math.Max(0.01, w), 0.32);
|
||||
if (w <= 0)
|
||||
throw new ArgumentException($"BurstChargeKg 必须 > 0,实际: {w}", nameof(ammo));
|
||||
_initialRadius = 3.3f * (float)Math.Pow(w, 0.32);
|
||||
_currentRadius = _initialRadius;
|
||||
_currentDensity = (float)ammo.CoreDensity;
|
||||
PeakDensity = (float)ammo.CoreDensity;
|
||||
_center = releasePos;
|
||||
@ -59,7 +61,6 @@ namespace CounterDrone.Core.Algorithms
|
||||
var (vx, vy, vz) = Kinematics.WindToVector(windDir, windSpeed);
|
||||
_windVelocity = new Vector3(vx, vy, vz);
|
||||
|
||||
// 判断阶段切换
|
||||
if (!_inPhase3 && _elapsed >= 30f)
|
||||
_inPhase3 = true;
|
||||
|
||||
@ -67,8 +68,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
{
|
||||
// Phase 2: 湍流扩散 R(t) = R₀ + k × √t
|
||||
var k = (float)_ammo.TurbulentExpansionK;
|
||||
_currentRadius = 3.3f * (float)Math.Pow(Math.Max(0.01, (float)_ammo.BurstChargeKg), 0.32)
|
||||
+ k * (float)Math.Sqrt(_elapsed);
|
||||
_currentRadius = _initialRadius + k * (float)Math.Sqrt(_elapsed);
|
||||
// 密度 = 源强 / 体积
|
||||
var volume = (4f / 3f) * (float)Math.PI * _currentRadius * _currentRadius * _currentRadius;
|
||||
_currentDensity = volume > 0.001f ? (float)_ammo.SourceStrength / volume : 0f;
|
||||
@ -76,12 +76,12 @@ namespace CounterDrone.Core.Algorithms
|
||||
else
|
||||
{
|
||||
// Phase 3: 高斯扩散
|
||||
var x = Math.Max(1f, windSpeed * (_elapsed - 30f));
|
||||
float x = windSpeed * (_elapsed - 30f);
|
||||
if (x <= 0) return; // 尚未进入有效扩散距离
|
||||
var cls = Kinematics.GetStabilityClass((WeatherType)_env.WeatherType, windSpeed);
|
||||
var sY = Kinematics.SigmaY(cls, x);
|
||||
var sZ = Kinematics.SigmaZ(cls, x);
|
||||
_currentDensity = Kinematics.GaussianPeakConcentration((float)_ammo.SourceStrength, sY, sZ);
|
||||
// 有效半径从浓度反推
|
||||
var effConc = (float)_ammo.EffectiveConcentration;
|
||||
if (_currentDensity > effConc)
|
||||
{
|
||||
@ -95,11 +95,11 @@ namespace CounterDrone.Core.Algorithms
|
||||
_center.Y += _windVelocity.Y * deltaTime;
|
||||
_center.Z += _windVelocity.Z * deltaTime;
|
||||
|
||||
// 粒子参数
|
||||
Particles.Opacity = Math.Max(0.1f, _currentDensity / (float)_ammo.CoreDensity);
|
||||
Particles.SizeMultiplier = _currentRadius / Math.Max(0.5f, 3.3f * (float)Math.Pow(Math.Max(0.01, (float)_ammo.BurstChargeKg), 0.32));
|
||||
// 粒子参数(仅用于可视化,不参与物理计算)
|
||||
float coreDensity = (float)_ammo.CoreDensity;
|
||||
Particles.Opacity = coreDensity > 0 ? _currentDensity / coreDensity : 0f;
|
||||
Particles.SizeMultiplier = _initialRadius > 0 ? _currentRadius / _initialRadius : 0f;
|
||||
|
||||
// 消散
|
||||
if (_elapsed >= (float)_ammo.MaxDuration || _currentRadius >= (float)_ammo.MaxRadius)
|
||||
{
|
||||
IsDissipated = true;
|
||||
|
||||
@ -75,7 +75,8 @@ namespace CounterDrone.Core.Algorithms
|
||||
return MathF.Atan2(sinA, cosA);
|
||||
}
|
||||
|
||||
float lo = detectArc + Math.Max(0.001f, vd * reactionTime + 0.001f);
|
||||
// +0.001f 确保 ts > 0,否则 F() 返回 MaxValue 导致搜索失败
|
||||
float lo = detectArc + vd * reactionTime + 0.001f;
|
||||
if (lo >= totalArc) return (0, 0, 0);
|
||||
|
||||
float fLo = F(lo);
|
||||
|
||||
@ -108,7 +108,8 @@ namespace CounterDrone.Core.Algorithms
|
||||
{
|
||||
if (wps == null || wps.Count < 2) return (1f, 0f);
|
||||
|
||||
float remaining = Math.Max(0f, arcLength);
|
||||
if (arcLength < 0) arcLength = 0;
|
||||
float remaining = arcLength;
|
||||
for (int i = 0; i < wps.Count - 1; i++)
|
||||
{
|
||||
float dx = (float)wps[i + 1].PosX - (float)wps[i].PosX;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user