采用滑动窗口和预热帧机制完善了毫米波导引系统

This commit is contained in:
Tian jianyong 2025-05-22 21:20:31 +08:00
parent a780126d32
commit 5f76e5b067
8 changed files with 449 additions and 497 deletions

View File

@ -14,9 +14,9 @@ MaxAcceleration = 100.0
ProportionalNavigationCoefficient = 4.0
LaunchAcceleration = 100.0
MaxEngineBurnTime = 2.5
CruiseTime = 5.0
CruiseTime = 4.0
Mass = 25.0
ExplosionRadius = 5.5
ExplosionRadius = 5.0
HitProbability = 0.9
SelfDestructHeight = 0.0
InfraredRadiationIntensity = 96.0 # 红外辐射强度 (瓦特/球面度)
@ -69,31 +69,23 @@ FieldOfViewAngle = 45.0 # 视场角 (度)
TargetRecognitionProbability = 0.95 # 目标识别概率
WaveFrequency = 9.4e10 # 波频率 (赫兹)
PulseDuration = 1.0e-6 # 脉冲持续时间 (秒)
SearchBeamWidth = 5.0 # 搜索波束宽度 (度)
TrackBeamWidth = 3.0 # 跟踪波束宽度 (度)
LockBeamWidth = 2.0 # 锁定波束宽度 (度)
TrackBeamWidth = 4.0 # 跟踪波束宽度 (度)
LockBeamWidth = 3.0 # 锁定波束宽度 (度)
ScanAngularSpeedDeg = 360.0 # 扫描角速度 (度/秒)
ScanRadiusGrowthRateDeg = 22.5 # 扫描半径增长率 (度)
RecognitionSNRThreshold = -25.0 # 识别信噪比阈值 (分贝)
LockSNRThreshold = -10.0 # 锁定信噪比阈值 (分贝)
TargetLostTolerance = 0.2 # 目标丢失容忍时间 (秒)
LockConfirmationTime = 0.3 # 锁定确认时间 (秒)
LockConfirmationTime = 0.3 # 锁定确认时间 (秒)
PulseRepetitionFrequency = 1.0e-4 # 脉冲重复间隔 (秒)
TransmitPower = 0.3 # 发射功率 (瓦特)
DopplerVelocityResolution = 1.0 # 多普勒速度分辨率 (米/秒)
MaxMeasurableVelocity = 1000.0 # 最大可测量速度 (米/秒)
AntennaGainDB = 23.0 # 天线增益 (分贝)
NoiseFigureDB = 7.0 # 噪声系数 (分贝)
SystemLossDB = 6.0 # 系统损耗 (分贝)
MonopulseSensitivity = 1.0 # 单脉冲灵敏度
YawControlEffectiveness = 120.0 # 偏航控制有效性 (度/秒^2 或类似单位)
PitchControlEffectiveness = 150.0 # 俯仰控制有效性 (度/秒^2 或类似单位)
JammingResistanceThreshold = 1.0e-5 # 干扰抗性阈值 (瓦特)

View File

@ -14,24 +14,24 @@ MaxAcceleration = 100.0
ProportionalNavigationCoefficient = 4.0
LaunchAcceleration = 100.0
MaxEngineBurnTime = 2.5
CruiseTime = 5.0
CruiseTime = 4.0
Mass = 25.0
ExplosionRadius = 5.5
ExplosionRadius = 5.0
HitProbability = 0.9
SelfDestructHeight = 0.0
InfraredRadiationIntensity = 96.0 # 红外辐射强度 (瓦特/球面度)
UltravioletRadiationIntensity = 100.0 # 紫外辐射强度 (瓦特/球面度)
[InfraredImagingGuidanceConfig]
MaxDetectionRange = 1000.0 # 最大探测距离 (米), JSON中为1000对应C#默认为1000.0
SearchFieldOfView = 12.0 # 搜索视场角 (度), 对应C#默认为12.0度
TrackFieldOfView = 6.0 # 跟踪视场角 (度), 对应C#默认为3.0度
MaxDetectionRange = 1000.0 # 最大探测距离 (米), 默认为1000.0
SearchFieldOfView = 12.0 # 搜索视场角 (度), 默认为12.0度
TrackFieldOfView = 6.0 # 跟踪视场角 (度), 默认为3.0度
ImageWidth = 640 # 图像宽度 (像素)
ImageHeight = 480 # 图像高度 (像素)
BackgroundIntensity = 1.0e-4 # 背景辐射强度 (瓦特/球面度), JSON中为1e-4, C#默认为0.01
BackgroundIntensity = 1.0e-4 # 背景辐射强度 (瓦特/球面度), 默认为1e-4
SearchRecognitionProbability = 0.6 # 搜索模式目标识别概率阈值
TrackRecognitionProbability = 0.8 # 跟踪模式目标识别概率阈值
TargetLostTolerance = 0.3 # 目标丢失容忍时间 (秒)
LockConfirmationTime = 0.5 # 锁定确认时间 (秒)
JammingResistanceThreshold = 1.0e-5 # 干扰抗性阈值 (瓦特)
Wavelength = 3.0 # 波长 (微米), C#中属性名为Wavelength, JSON中为waveLength
Wavelength = 3.0 # 波长 (微米)

View File

@ -28,31 +28,23 @@ FieldOfViewAngle = 45.0 # 视场角 (度)
TargetRecognitionProbability = 0.95 # 目标识别概率
WaveFrequency = 9.4e10 # 波频率 (赫兹, JSON中为94e9)
PulseDuration = 1.0e-6 # 脉冲持续时间 (秒)
SearchBeamWidth = 5.0 # 搜索波束宽度 (度)
TrackBeamWidth = 3.0 # 跟踪波束宽度 (度)
LockBeamWidth = 2.0 # 锁定波束宽度 (度)
TrackBeamWidth = 4.0 # 跟踪波束宽度 (度)
LockBeamWidth = 3.0 # 锁定波束宽度 (度)
ScanAngularSpeedDeg = 360.0 # 扫描角速度 (度/秒)
ScanRadiusGrowthRateDeg = 22.5 # 扫描半径增长率 (度)
RecognitionSNRThreshold = -25.0 # 识别信噪比阈值 (分贝)
LockSNRThreshold = -10.0 # 锁定信噪比阈值 (分贝)
TargetLostTolerance = 0.2 # 目标丢失容忍时间 (秒)
LockConfirmationTime = 0.3 # 锁定确认时间 (秒)
PulseRepetitionFrequency = 1.0e-4 # 脉冲重复频率 (秒, JSON中为1e-4通常PRT单位是秒PRF是Hz。这里JSON的注释可能不准确按数值和C#模型属性名推断这里应为PulseRepetitionTime即脉冲重复间隔)
PulseRepetitionFrequency = 1.0e-4 # 脉冲重复频率 (秒)
TransmitPower = 0.3 # 发射功率 (瓦特)
DopplerVelocityResolution = 1.0 # 多普勒速度分辨率 (米/秒)
MaxMeasurableVelocity = 1000.0 # 最大可测量速度 (米/秒)
AntennaGainDB = 23.0 # 天线增益 (分贝)
NoiseFigureDB = 7.0 # 噪声系数 (分贝)
SystemLossDB = 6.0 # 系统损耗 (分贝)
MonopulseSensitivity = 1.0 # 单脉冲灵敏度 (单位取决于具体实现JSON中为1)
YawControlEffectiveness = 120.0 # 偏航控制有效性 (度/秒^2 或类似单位)
PitchControlEffectiveness = 150.0 # 俯仰控制有效性 (度/秒^2 或类似单位)
JammingResistanceThreshold = 1.0e-5 # 干扰抗性阈值 (瓦特)

View File

@ -95,17 +95,17 @@ namespace ThreatSource.Guidance
/// <summary>
/// 当前正在跟踪或已锁定的目标ID
/// </summary>
private string? _currentlyTrackedTargetId;
private string? currentlyTrackedTargetId;
/// <summary>
/// Search模式下当前关注的候选目标ID
/// </summary>
private string? _searchModeFocusCandidateId;
private string? searchModeFocusCandidateId;
/// <summary>
/// 当前活动模式 (搜索焦点或跟踪稳定) 的识别历史队列
/// </summary>
private Queue<bool> _activeRecognitionHistory = new();
private readonly Queue<bool> activeRecognitionHistory = new();
/// <summary>
/// 识别历史窗口大小 (搜索和跟踪阶段共用)
@ -120,7 +120,7 @@ namespace ThreatSource.Guidance
/// <summary>
/// 锁定模式下目标丢失计时器
/// </summary>
private double _lockModeTargetLostTimer = 0;
private double lockModeTargetLostTimer = 0;
/// <summary>
/// 初始化红外成像制导系统的新实例
@ -222,7 +222,7 @@ namespace ThreatSource.Guidance
public override void Deactivate()
{
base.Deactivate();
LastTargetPosition = null; // Add reset to null
LastTargetPosition = null;
HasTarget = false;
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
@ -241,91 +241,78 @@ namespace ThreatSource.Guidance
/// </remarks>
public override void Update(double deltaTime)
{
bool tryDetectSucceeded = false;
base.Update(deltaTime);
bool targetFoundAndIdentifiedThisFrame = false;
Vector3D currentTargetPosition = Vector3D.Zero; // 初始化
if (IsBlockingJammed)
{
targetFoundAndIdentifiedThisFrame = false;
Debug.WriteLine($"[IR IMAGING] 系统被阻塞干扰,无法探测目标。当前模式: {currentMode}");
}
else
{
if (TryDetectAndIdentifyTarget(KState.Position, KState.Velocity, deltaTime, out currentTargetPosition))
if (TryDetectAndIdentifyTarget(KState.Position, KState.Velocity, deltaTime, out Vector3D detectedTargetPosition))
{
targetFoundAndIdentifiedThisFrame = true;
// 目标成功探测/跟踪/锁定
tryDetectSucceeded = true;
Vector3D? currentTargetVelocity = null;
if(LastTargetPosition != null && deltaTime > 0)
{
currentTargetVelocity = (currentTargetPosition - LastTargetPosition.Value) / deltaTime; // 使用 .Value
if (LastTargetPosition != null && deltaTime > 0) {
currentTargetVelocity = (detectedTargetPosition - LastTargetPosition.Value) / deltaTime;
}
LastTargetPosition = currentTargetPosition;
LastTargetPosition = detectedTargetPosition;
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
ProportionalNavigationCoefficient,
KState.Position,
KState.Velocity,
currentTargetPosition,
currentTargetVelocity ?? Vector3D.Zero
ProportionalNavigationCoefficient, KState.Position, KState.Velocity,
detectedTargetPosition, currentTargetVelocity ?? Vector3D.Zero
);
if (GuidanceAcceleration.Magnitude() > MaxAcceleration)
{
if (GuidanceAcceleration.Magnitude() > MaxAcceleration) {
GuidanceAcceleration = GuidanceAcceleration.Normalize() * MaxAcceleration;
}
HasGuidance = true;
if (currentMode == WorkMode.Lock)
{
_lockModeTargetLostTimer = 0; // 目标在锁定模式下重新确认,重置计时器
if (currentMode == WorkMode.Lock) {
lockModeTargetLostTimer = 0;
}
}
else // TryDetectAndIdentifyTarget 返回 false (目标未找到/不稳定)
else
{
targetFoundAndIdentifiedThisFrame = false;
// 不更新 LastTargetPosition
Debug.WriteLine($"[IR IMAGING] TryDetectAndIdentifyTarget 返回 false。当前模式: {currentMode}");
}
}
// 处理目标未识别/丢失的后果
if (!targetFoundAndIdentifiedThisFrame)
if (!tryDetectSucceeded)
{
GuidanceAcceleration = Vector3D.Zero;
HasGuidance = false;
// 模式切换逻辑
if (currentMode == WorkMode.Track)
{
if (currentMode == WorkMode.Track) {
Debug.WriteLine($"[IR IMAGING] 目标在 Track 模式下丢失/不稳定 (或受干扰),立即切换回搜索模式。");
SwitchToSearchMode();
}
else if (currentMode == WorkMode.Lock)
{
if (IsBlockingJammed)
{
Debug.WriteLine($"[IR IMAGING] 因阻塞干扰,在 Lock 模式下丢失目标,立即切换回搜索模式。");
SwitchToSearchMode();
}
else
{
_lockModeTargetLostTimer += deltaTime;
Debug.WriteLine($"[IR IMAGING] 目标在 Lock 模式下丢失。计时器: {_lockModeTargetLostTimer:F2}s / {config.TargetLostTolerance:F2}s");
if (_lockModeTargetLostTimer >= config.TargetLostTolerance)
{
Debug.WriteLine($"[IR IMAGING] Lock 模式目标丢失超过容限,切换回搜索模式。");
SwitchToSearchMode();
}
// else 容限未到,保持在 Lock 模式,但 HasGuidance 已为 false
}
else if (currentMode == WorkMode.Lock) {
HandleLockModeTargetLoss(deltaTime);
}
}
}
private void HandleLockModeTargetLoss(double deltaTime)
{
if (IsBlockingJammed)
{
Debug.WriteLine($"[IR IMAGING] 因阻塞干扰,在 Lock 模式下丢失目标,立即切换回搜索模式。");
SwitchToSearchMode();
}
else
{
lockModeTargetLostTimer += deltaTime;
Debug.WriteLine($"[IR IMAGING] 目标在 Lock 模式下丢失。计时器: {lockModeTargetLostTimer:F2}s / {config.TargetLostTolerance:F2}s");
if (lockModeTargetLostTimer >= config.TargetLostTolerance)
{
Debug.WriteLine($"[IR IMAGING] Lock 模式目标丢失超过容限,切换回搜索模式。");
SwitchToSearchMode();
}
// 如果在Search模式下目标丢失通常意味着本帧未找到任何满足条件的目标
// TryDetectAndIdentifyTarget内部逻辑会处理是否重置焦点等此处无需额外操作
}
// 如果 targetFoundAndIdentifiedThisFrame 为 true则制导参数已设置模式状态由 TryDetectAndIdentifyTarget 内部逻辑如切换到Track/Lock或 Update 稍早部分重置Lock模式计时器处理
}
/// <summary>
@ -341,12 +328,11 @@ namespace ThreatSource.Guidance
{
currentMode = WorkMode.Search;
HasTarget = false;
// targetLostTimer = 0; // 已移除
LastTargetPosition = null;
_currentlyTrackedTargetId = null;
_searchModeFocusCandidateId = null;
_activeRecognitionHistory.Clear();
_lockModeTargetLostTimer = 0; // 清理状态
currentlyTrackedTargetId = null;
searchModeFocusCandidateId = null;
activeRecognitionHistory.Clear();
lockModeTargetLostTimer = 0;
// 创建图像生成器
imageGenerator = new InfraredImageGenerator(
@ -371,13 +357,11 @@ namespace ThreatSource.Guidance
public void SwitchToTrackMode()
{
currentMode = WorkMode.Track;
lockConfirmationTimer = 0; // 重置锁定确认计时器
// _lockModeTargetLostTimer 在切换到Track时不需要重置因为它特定于Lock模式
// 并且在从Lock切换到Search再可能到Track时应由SwitchToSearchMode清理。
lockConfirmationTimer = 0;
// 清理搜索阶段的状态
_searchModeFocusCandidateId = null;
_activeRecognitionHistory.Clear(); // Track模式应从零开始积累历史
searchModeFocusCandidateId = null;
activeRecognitionHistory.Clear();
// 创建图像生成器 (通常视场角会变化)
imageGenerator = new InfraredImageGenerator(
@ -402,7 +386,7 @@ namespace ThreatSource.Guidance
public void SwitchToLockMode()
{
currentMode = WorkMode.Lock;
_lockModeTargetLostTimer = 0; // 进入锁定模式时重置计时器
lockModeTargetLostTimer = 0;
Debug.WriteLine("切换到锁定模式 - 目标类型确认");
}
@ -425,14 +409,12 @@ namespace ThreatSource.Guidance
private bool TryDetectAndIdentifyTarget(Vector3D missilePosition, Vector3D missileVelocity, double deltaTime, out Vector3D targetPosition)
{
targetPosition = Vector3D.Zero;
double minDistance = double.MaxValue;
bool foundThisFrame = false;
bool currentFrameDirectRecognitionSuccess = false; // 新增:用于记录当前帧的直接识别/探测成功状态
bool isRecognizedThisFrame = false;
double searchFovRadians = config.SearchFieldOfView * Math.PI / 180.0;
double trackFovRadians = config.TrackFieldOfView * Math.PI / 180.0;
double currentFovRadians = currentMode == WorkMode.Search ? searchFovRadians : trackFovRadians;
foreach (var element in SimulationManager.GetEntitiesByType<SimulationElement>())
{
if (element is BaseEquipment currentEntity)
@ -440,224 +422,182 @@ namespace ThreatSource.Guidance
Vector3D toTarget = currentEntity.KState.Position - missilePosition;
double distanceToCurrentEntity = toTarget.Magnitude();
// 调用新的统一烟幕评估方法
var smokeImpact = EvaluateSmokeImpact(missilePosition, currentEntity);
bool isGeometricallyObscuredBySmoke = smokeImpact.IsGeometricallyObscured;
double liveTransmittanceForImage = smokeImpact.OverallTransmittance; // 重命名以避免与旧变量名冲突
var (isGeometricallyObscured, overallTransmittance) = EvaluateSmokeImpact(missilePosition, currentEntity);
// 根据当前模式调整FOV检查并将烟幕几何遮挡作为主要过滤条件之一
bool passesBasicChecks = false;
if (currentMode == WorkMode.Search)
{
passesBasicChecks = distanceToCurrentEntity <= config.MaxDetectionRange &&
Math.Acos(Vector3D.DotProduct(toTarget.Normalize(), missileVelocity.Normalize())) <= (searchFovRadians / 2) &&
!isGeometricallyObscuredBySmoke;
}
else if (currentMode == WorkMode.Track || currentMode == WorkMode.Lock)
{ // Track 和 Lock 模式使用 TrackFieldOfView
passesBasicChecks = distanceToCurrentEntity <= config.MaxDetectionRange &&
Math.Acos(Vector3D.DotProduct(toTarget.Normalize(), missileVelocity.Normalize())) <= (trackFovRadians / 2) &&
!isGeometricallyObscuredBySmoke;
}
bool isWithinFovAndRange;
double relevantFovRadians = currentMode == WorkMode.Search ? searchFovRadians : trackFovRadians;
if (passesBasicChecks)
isWithinFovAndRange = distanceToCurrentEntity <= config.MaxDetectionRange &&
Math.Acos(Vector3D.DotProduct(toTarget.Normalize(), missileVelocity.Normalize())) <= (relevantFovRadians / 2);
if (!isWithinFovAndRange || isGeometricallyObscured)
{
switch (currentMode)
// 如果是 Search 模式下的焦点目标,但它不再满足基本条件,则清除焦点
if (currentMode == WorkMode.Search && currentEntity.Id == searchModeFocusCandidateId)
{
case WorkMode.Search:
// 几何和主要烟幕检查已在上方 passesBasicChecks 中完成
// if (distanceToCurrentEntity > config.MaxDetectionRange || ... || CheckIfTargetObscuredBySmoke(...))
// 上述条件已由 passesBasicChecks 处理
if (currentEntity.Id == _searchModeFocusCandidateId && !passesBasicChecks)
{
// 如果是焦点目标,但它不再满足基本条件 (例如进入了烟幕或飞出FOV)
_searchModeFocusCandidateId = null;
_activeRecognitionHistory.Clear();
continue; // 跳过此不合格的焦点实体
}
if (!passesBasicChecks) { continue; } // 如果任何实体不通过基本检查,直接跳过
// 确定或确认焦点目标
if (_searchModeFocusCandidateId == null)
{
_searchModeFocusCandidateId = currentEntity.Id;
_activeRecognitionHistory.Clear();
}
else if (currentEntity.Id != _searchModeFocusCandidateId)
{
continue;
}
// 此处意味着 currentEntity.Id == _searchModeFocusCandidateId处理焦点目标
// double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, currentEntity.KState.Position);
var image = imageGenerator.GenerateImage(currentEntity, missilePosition, liveTransmittanceForImage, SimulationManager);
var searchResult = targetRecognizer.RecognizeTarget(image, currentEntity);
bool currentRecognitionSuccessInSearch = (searchResult.Type == targetType && searchResult.Confidence >= config.SearchRecognitionProbability);
currentFrameDirectRecognitionSuccess = currentRecognitionSuccessInSearch; // 赋值给提升作用域的变量
_activeRecognitionHistory.Enqueue(currentRecognitionSuccessInSearch);
while (_activeRecognitionHistory.Count > COMMON_RECOGNITION_WINDOW_SIZE)
{
_activeRecognitionHistory.Dequeue();
}
// 只有当窗口填满时才做决策
if (_activeRecognitionHistory.Count == COMMON_RECOGNITION_WINDOW_SIZE)
{
double successRate = (double)_activeRecognitionHistory.Count(s => s) / COMMON_RECOGNITION_WINDOW_SIZE;
if (successRate >= COMMON_RECOGNITION_SUCCESS_RATE_THRESHOLD)
{
Vector3D toTargetForTrackCheck = currentEntity.KState.Position - missilePosition;
double angleToTargetForTrackCheck = Math.Acos(Vector3D.DotProduct(toTargetForTrackCheck.Normalize(), missileVelocity.Normalize()));
if (angleToTargetForTrackCheck <= (trackFovRadians / 2))
{
_currentlyTrackedTargetId = _searchModeFocusCandidateId;
targetPosition = currentEntity.KState.Position;
minDistance = distanceToCurrentEntity;
foundThisFrame = true;
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {_searchModeFocusCandidateId} 滑动窗口达标 (率: {successRate:P2}) 且在Track FOV内. 切换到Track模式。");
SwitchToTrackMode();
break; // 跳出 foreach, 完成搜索并切换
}
else
{
_searchModeFocusCandidateId = null;
_activeRecognitionHistory.Clear();
foundThisFrame = false; // 仅对当前焦点而言,未通过最终检查
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {currentEntity.Id} 滑动窗口达标但不满足Track FOV. 清除焦点,继续搜索。");
}
}
else
{
_searchModeFocusCandidateId = null;
_activeRecognitionHistory.Clear();
foundThisFrame = false; // 明确当前焦点未通过滑动窗口
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {currentEntity.Id} 滑动窗口未达标 (率: {successRate:P2}). 清除焦点,继续搜索。");
}
}
else // 窗口未满foundThisFrame取决于本帧的直接识别结果但不做模式切换
{
foundThisFrame = currentRecognitionSuccessInSearch;
if (currentRecognitionSuccessInSearch) targetPosition = currentEntity.KState.Position; // 如果本帧识别到,更新临时位置
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {_searchModeFocusCandidateId} 正在收集数据,窗口 ({_activeRecognitionHistory.Count}/{COMMON_RECOGNITION_WINDOW_SIZE}). 本帧识别: {currentRecognitionSuccessInSearch}");
}
break; // Search case 的 break
case WorkMode.Track:
if (_currentlyTrackedTargetId != null && currentEntity.Id == _currentlyTrackedTargetId)
{
bool actualRecognitionSuccessInTrack = false; // 重命名避免与search模式的变量混淆
if (!passesBasicChecks)
{
actualRecognitionSuccessInTrack = false;
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 未通过基本检查. 本帧失败。");
}
else
{
var image_track = imageGenerator.GenerateImage(currentEntity, missilePosition, liveTransmittanceForImage, SimulationManager);
var trackResult = targetRecognizer.RecognizeTarget(image_track, currentEntity);
bool recognitionSucceededThisFrame = trackResult.Type == targetType && trackResult.Confidence >= config.TrackRecognitionProbability;
if (recognitionSucceededThisFrame)
{
actualRecognitionSuccessInTrack = true;
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 本帧识别成功 (置信度 {trackResult.Confidence:P2})。");
}
else
{
actualRecognitionSuccessInTrack = false;
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 本帧识别失败 (置信度: {trackResult.Confidence:P2})。");
}
}
currentFrameDirectRecognitionSuccess = actualRecognitionSuccessInTrack; // 赋值给提升作用域的变量
_activeRecognitionHistory.Enqueue(actualRecognitionSuccessInTrack); // 使用 track 模式下的直接识别结果
while (_activeRecognitionHistory.Count > COMMON_RECOGNITION_WINDOW_SIZE)
{
_activeRecognitionHistory.Dequeue();
}
if (_activeRecognitionHistory.Count == COMMON_RECOGNITION_WINDOW_SIZE)
{
double trackSuccessRate = (double)_activeRecognitionHistory.Count(s => s) / COMMON_RECOGNITION_WINDOW_SIZE;
if (trackSuccessRate >= COMMON_RECOGNITION_SUCCESS_RATE_THRESHOLD)
{
foundThisFrame = true;
if (actualRecognitionSuccessInTrack)
{
targetPosition = currentEntity.KState.Position;
minDistance = distanceToCurrentEntity;
}
else if (LastTargetPosition.HasValue) // 滑动窗口稳定,但本帧识别失败,使用上一位置
{
targetPosition = LastTargetPosition.Value;
// minDistance 不更新,因为这是基于旧位置的
Debug.WriteLine($"[IR IMAGING TRACK] 滑动窗口稳定但本帧识别失败。使用LastTargetPosition: {targetPosition}");
}
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 滑动窗口稳定 (成功率 {trackSuccessRate:P2})。foundThisFrame = true。");
}
else
{
foundThisFrame = false;
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 滑动窗口不稳定 (成功率 {trackSuccessRate:P2})。foundThisFrame = false。");
}
}
else // 滑动窗口未满
{
foundThisFrame = passesBasicChecks;
if (passesBasicChecks)
{
if (actualRecognitionSuccessInTrack) // 如果本帧具体识别也成功了
{
targetPosition = currentEntity.KState.Position;
minDistance = distanceToCurrentEntity;
}
else if (LastTargetPosition.HasValue) // 基本检查通过,本帧识别失败,使用上一位置
{
targetPosition = LastTargetPosition.Value;
// minDistance 不更新
Debug.WriteLine($"[IR IMAGING TRACK] 滑动窗口未满本帧识别失败。使用LastTargetPosition: {targetPosition}");
}
}
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 正在收集数据,窗口 ({_activeRecognitionHistory.Count}/{COMMON_RECOGNITION_WINDOW_SIZE}). 本帧passesBasicChecks:{passesBasicChecks}, 本帧识别:{actualRecognitionSuccessInTrack}. foundThisFrame set to {foundThisFrame}");
}
break;
}
break;
case WorkMode.Lock:
if (_currentlyTrackedTargetId != null && currentEntity.Id == _currentlyTrackedTargetId)
{
if (!passesBasicChecks)
{
foundThisFrame = false;
currentFrameDirectRecognitionSuccess = false;
Debug.WriteLine($"[IR IMAGING LOCK] 锁定目标 {_currentlyTrackedTargetId} 未通过基本检查 (FOV, 范围, 烟幕几何遮挡)。");
}
else
{
targetPosition = currentEntity.KState.Position;
minDistance = distanceToCurrentEntity;
foundThisFrame = true;
currentFrameDirectRecognitionSuccess = true; // Lock模式下通过基础检查即认为本帧直接探测成功
}
break;
}
break;
searchModeFocusCandidateId = null;
activeRecognitionHistory.Clear();
Debug.WriteLine($"[IR IMAGING SEARCH] 焦点目标 {currentEntity.Id} 不再满足基本条件,清除焦点。");
}
continue; // 不满足基本FOV、距离或被烟幕遮挡处理下一个实体
}
switch (currentMode)
{
case WorkMode.Search:
// 处理焦点
if (searchModeFocusCandidateId == null)
{
searchModeFocusCandidateId = currentEntity.Id;
activeRecognitionHistory.Clear();
Debug.WriteLine($"[IR IMAGING SEARCH] 选取新的焦点目标: {searchModeFocusCandidateId}");
}
else if (currentEntity.Id != searchModeFocusCandidateId)
{
continue; // 不是当前选定的焦点目标,跳过
}
// 当前是焦点目标,且通过了基本检查
var image = imageGenerator.GenerateImage(currentEntity, missilePosition, overallTransmittance, SimulationManager);
var searchResult = targetRecognizer.RecognizeTarget(image, currentEntity);
isRecognizedThisFrame = (searchResult.Type == targetType && searchResult.Confidence >= config.SearchRecognitionProbability);
activeRecognitionHistory.Enqueue(isRecognizedThisFrame);
while (activeRecognitionHistory.Count > COMMON_RECOGNITION_WINDOW_SIZE)
{
activeRecognitionHistory.Dequeue();
}
if (activeRecognitionHistory.Count == COMMON_RECOGNITION_WINDOW_SIZE)
{
double successRate = (double)activeRecognitionHistory.Count(s => s) / COMMON_RECOGNITION_WINDOW_SIZE;
if (successRate >= COMMON_RECOGNITION_SUCCESS_RATE_THRESHOLD)
{
// 检查是否也在Track模式的FOV内 (因为search FOV可能比track FOV宽)
double angleToTargetForTrackCheck = Math.Acos(Vector3D.DotProduct(toTarget.Normalize(), missileVelocity.Normalize()));
if (angleToTargetForTrackCheck <= (trackFovRadians / 2))
{
currentlyTrackedTargetId = searchModeFocusCandidateId;
targetPosition = currentEntity.KState.Position;
foundThisFrame = true;
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {searchModeFocusCandidateId} 滑动窗口达标 (率: {successRate:P2}) 且在Track FOV内. 切换到Track模式。");
SwitchToTrackMode();
}
else
{
searchModeFocusCandidateId = null; // 不在Track FOV内放弃此焦点
activeRecognitionHistory.Clear();
foundThisFrame = false;
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {currentEntity.Id} 滑动窗口达标但不满足切换到Track模式的FOV条件. 清除焦点。");
}
}
else
{
searchModeFocusCandidateId = null; // 滑动窗口未达标,放弃此焦点
activeRecognitionHistory.Clear();
foundThisFrame = false;
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {currentEntity.Id} 滑动窗口未达标 (率: {successRate:P2}). 清除焦点。");
}
}
else // 窗口未满
{
foundThisFrame = isRecognizedThisFrame; // foundThisFrame反映本帧直接识别结果
if (isRecognizedThisFrame) targetPosition = currentEntity.KState.Position;
Debug.WriteLine($"[IR IMAGING SEARCH] 目标 {searchModeFocusCandidateId} 正在收集数据,窗口 ({activeRecognitionHistory.Count}/{COMMON_RECOGNITION_WINDOW_SIZE}). 本帧识别: {isRecognizedThisFrame}");
}
break;
case WorkMode.Track:
if (currentlyTrackedTargetId == null || currentEntity.Id != currentlyTrackedTargetId)
{
continue; // 不是当前正在跟踪的目标
}
// 当前是跟踪目标,且已通过外层基本检查
var image_track = imageGenerator.GenerateImage(currentEntity, missilePosition, overallTransmittance, SimulationManager);
var trackResult = targetRecognizer.RecognizeTarget(image_track, currentEntity);
isRecognizedThisFrame = trackResult.Type == targetType && trackResult.Confidence >= config.TrackRecognitionProbability;
if (isRecognizedThisFrame)
{
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 本帧识别成功 (置信度 {trackResult.Confidence:P2})。");
}
else
{
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 本帧识别失败 (置信度: {trackResult.Confidence:P2})。");
}
activeRecognitionHistory.Enqueue(isRecognizedThisFrame);
while (activeRecognitionHistory.Count > COMMON_RECOGNITION_WINDOW_SIZE)
{
activeRecognitionHistory.Dequeue();
}
if (activeRecognitionHistory.Count == COMMON_RECOGNITION_WINDOW_SIZE)
{
double trackSuccessRate = (double)activeRecognitionHistory.Count(s => s) / COMMON_RECOGNITION_WINDOW_SIZE;
if (trackSuccessRate >= COMMON_RECOGNITION_SUCCESS_RATE_THRESHOLD)
{
foundThisFrame = true;
if (isRecognizedThisFrame)
{
targetPosition = currentEntity.KState.Position;
}
else if (LastTargetPosition.HasValue)
{
targetPosition = LastTargetPosition.Value;
Debug.WriteLine($"[IR IMAGING TRACK] 滑动窗口稳定但本帧识别失败。使用LastTargetPosition: {targetPosition}");
}
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 滑动窗口稳定 (成功率 {trackSuccessRate:P2})。foundThisFrame = true。");
}
else
{
foundThisFrame = false; // 滑动窗口不稳定
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 滑动窗口不稳定 (成功率 {trackSuccessRate:P2})。foundThisFrame = false。");
}
}
else // 滑动窗口未满
{
foundThisFrame = isRecognizedThisFrame;
if (isRecognizedThisFrame)
{
targetPosition = currentEntity.KState.Position;
}
else if (LastTargetPosition.HasValue)
{
targetPosition = LastTargetPosition.Value;
Debug.WriteLine($"[IR IMAGING TRACK] 滑动窗口未满本帧识别失败。使用LastTargetPosition: {targetPosition}");
}
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 正在收集数据,窗口 ({activeRecognitionHistory.Count}/{COMMON_RECOGNITION_WINDOW_SIZE}). 本帧识别:{isRecognizedThisFrame}. foundThisFrame set to {foundThisFrame}");
}
break;
case WorkMode.Lock:
if (currentlyTrackedTargetId == null || currentEntity.Id != currentlyTrackedTargetId)
{
continue;
}
targetPosition = currentEntity.KState.Position;
foundThisFrame = true;
isRecognizedThisFrame = true;
Debug.WriteLine($"[IR IMAGING LOCK] 锁定目标 {currentlyTrackedTargetId} 通过基本检查。");
break;
}
// 如果在Search模式下成功切换到Track或者在Track/Lock模式下处理了目标无论成功与否则不再继续遍历此帧中的其他实体
if (foundThisFrame || currentMode != WorkMode.Search || (currentMode == WorkMode.Search && currentlyTrackedTargetId != null) )
{
break;
}
}
}
if (currentMode == WorkMode.Track)
{
if (foundThisFrame && currentFrameDirectRecognitionSuccess)
if (foundThisFrame && isRecognizedThisFrame)
{
lockConfirmationTimer += deltaTime;
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 跟踪中,累积锁定确认时间: {lockConfirmationTimer:F2}s / {config.LockConfirmationTime:F2}s");
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 跟踪中,累积锁定确认时间: {lockConfirmationTimer:F2}s / {config.LockConfirmationTime:F2}s");
if (lockConfirmationTimer >= config.LockConfirmationTime)
{
SwitchToLockMode();
@ -666,22 +606,19 @@ namespace ThreatSource.Guidance
else
{
lockConfirmationTimer = 0;
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {_currentlyTrackedTargetId} 跟踪条件不满足 (foundThisFrame:{foundThisFrame} 或本帧直接识别失败:{currentFrameDirectRecognitionSuccess}),重置锁定确认计时器。");
Debug.WriteLine($"[IR IMAGING TRACK] 目标 {currentlyTrackedTargetId} 跟踪条件不满足 (foundThisFrame:{foundThisFrame} 或本帧直接识别失败:{isRecognizedThisFrame}),重置锁定确认计时器。");
}
}
else if (currentMode == WorkMode.Lock)
{
if (!foundThisFrame)
{
// 在Lock模式下如果 TryDetectAndIdentifyTarget 返回 false (即 foundThisFrame 为 false),
// _currentlyTrackedTargetId 暂时不清除,而是由 Update 方法中的 _lockModeTargetLostTimer 机制来决定何时因超时而真正丢失目标并切换模式。
// 此时 _currentlyTrackedTargetId 仍然保留,以便在容限时间内如果目标重现,可以继续锁定。
Debug.WriteLine($"[IR IMAGING LOCK] 锁定目标 {_currentlyTrackedTargetId} 在本帧未被找到。等待容限超时...");
Debug.WriteLine($"[IR IMAGING LOCK] 锁定目标 {currentlyTrackedTargetId} 在本帧未被找到。等待容限超时...");
}
}
HasTarget = foundThisFrame; // HasTarget 反映的是本帧是否成功探测/识别/跟踪到目标
return foundThisFrame; // 返回本帧的探测/跟踪状态
HasTarget = foundThisFrame;
return foundThisFrame;
}
private (bool IsGeometricallyObscured, double OverallTransmittance) EvaluateSmokeImpact(Vector3D observerPosition, BaseEquipment target)

View File

@ -66,15 +66,6 @@ namespace ThreatSource.Guidance
/// </remarks>
private Vector3D? LastTargetVelocity { get; set; }
/// <summary>
/// 目标丢失计时器
/// </summary>
/// <remarks>
/// 记录目标丢失的时间
/// 用于切换到搜索模式
/// </remarks>
private double targetLostTimer = 0;
/// <summary>
/// 锁定确认计时器
/// </summary>
@ -94,6 +85,26 @@ namespace ThreatSource.Guidance
/// </summary>
private WorkMode currentMode = WorkMode.Search;
/// <summary>
/// 当前正在跟踪或已锁定的目标ID (由Search模式切换时设置)
/// </summary>
private string? currentlyTrackedTargetId;
/// <summary>
/// 当前活动模式 (Search焦点、Track、Lock) 的探测历史队列
/// </summary>
private readonly Queue<bool> activeDetectionHistory = new();
/// <summary>
/// Search到Track模式切换的滑动窗口大小
/// </summary>
private const int COMMON_DETECTION_WINDOW_SIZE = 10;
/// <summary>
/// Search到Track模式切换的成功率阈值
/// </summary>
private const double COMMON_DETECTION_SUCCESS_RATE_THRESHOLD = 0.8;
/// <summary>
/// 是否有目标
/// </summary>
@ -129,6 +140,11 @@ namespace ThreatSource.Guidance
/// </summary>
private readonly SwerlingRcsModel swerlingRcsModel;
private int _framesInTrackMode = 0;
private const int MIN_FRAMES_FOR_TRACK_STABILITY_CHECK = 5;
private double lastDetailSnrForDecision; // 新增类成员存储SNR供决策
/// <summary>
/// 初始化毫米波制导系统的新实例
/// </summary>
@ -169,12 +185,15 @@ namespace ThreatSource.Guidance
currentMode = WorkMode.Search;
HasTarget = false;
HasGuidance = false;
targetLostTimer = 0;
lockConfirmationTimer = 0;
currentScanAngle = 0;
currentScanRadius = config.SearchBeamWidth * Math.PI / 720.0; // 从半个波束宽度的一半开始
LastTargetPosition = null;
LastTargetVelocity = null;
activeDetectionHistory.Clear();
currentlyTrackedTargetId = null;
Trace.TraceInformation($"切换到搜索模式,波束宽度: {config.SearchBeamWidth}度");
}
@ -186,6 +205,10 @@ namespace ThreatSource.Guidance
currentMode = WorkMode.Track;
lockConfirmationTimer = 0;
HasGuidance = true;
activeDetectionHistory.Clear();
_framesInTrackMode = 0; // 重置计数器
Trace.TraceInformation($"切换到跟踪模式,波束宽度: {config.TrackBeamWidth}度");
}
@ -207,7 +230,6 @@ namespace ThreatSource.Guidance
if (!IsActive)
{
IsActive = true;
// 在这里订阅事件,确保只订阅一次
SimulationManager.SubscribeToEvent<JammingEvent>(HandleJammingEvent);
}
base.Activate();
@ -243,7 +265,6 @@ namespace ThreatSource.Guidance
if (parameters.Type == JammingType.MillimeterWave)
{
Debug.WriteLine($"[MMW_GUIDANCE] 受到毫米波干扰,功率:{parameters.Power} W。切换到搜索模式。", "Jamming");
// 干扰时切换到搜索模式
if (currentMode != WorkMode.Search)
{
SwitchToSearchMode();
@ -252,7 +273,6 @@ namespace ThreatSource.Guidance
else if (parameters.Type == JammingType.SmokeGrenade)
{
Debug.WriteLine($"[MMW_GUIDANCE] 受到烟幕干扰影响。Jammer ID: {parameters.JammerId}", "Jamming");
// 烟幕主要通过影响SNR体现不直接触发模式切换
}
}
@ -267,11 +287,9 @@ namespace ThreatSource.Guidance
if (parameters.Type == JammingType.MillimeterWave)
{
Debug.WriteLine($"[MMW_GUIDANCE] 毫米波干扰已清除。", "Jamming");
// 干扰清除后是否需要切换模式取决于当前是否有目标。Update 会处理。
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
// _currentSmokeTransmittance = 1.0; // Field removed, no longer needed here
Debug.WriteLine($"[MMW_GUIDANCE] 烟幕干扰解除。Jammer ID: {parameters.JammerId}", "Jamming");
}
}
@ -283,17 +301,13 @@ namespace ThreatSource.Guidance
/// <returns>如果应该处理则返回 true否则返回 false</returns>
protected override bool ShouldHandleJamming(JammingParameters parameters)
{
// 首先调用基类检查是否支持该干扰类型
if (!base.ShouldHandleJamming(parameters))
{
return false;
}
// 对毫米波干扰添加波长检查
if (parameters.Type == JammingType.MillimeterWave)
{
// 检查波长是否与工作波长匹配 (单位: 毫米 mm)
// 计算配置波长 (单位: mm), 直接使用光速值 3e8 m/s
const double speedOfLight = 3e8;
double configWavelength_mm = speedOfLight / config.WaveFrequency * 1000.0; // m/s / Hz * 1000 = mm
@ -304,7 +318,6 @@ namespace ThreatSource.Guidance
}
}
// 如果是支持的非毫米波干扰 (例如烟幕) 或通过了毫米波检查,则返回 true
return true;
}
@ -467,69 +480,52 @@ namespace ThreatSource.Guidance
/// </remarks>
public override void Update(double deltaTime)
{
base.Update(deltaTime); // 更新基类状态和干扰状态
base.Update(deltaTime);
// 在执行核心逻辑前检查阻塞性干扰状态
if (IsBlockingJammed)
{
// 如果被阻塞性干扰,确保处于搜索模式并且没有制导
if (currentMode != WorkMode.Search)
{
SwitchToSearchMode();
}
HasGuidance = false;
if (currentMode != WorkMode.Search) SwitchToSearchMode();
this.HasTarget = false;
GuidanceAcceleration = Vector3D.Zero;
HasGuidance = false;
return;
}
// 更新扫描参数
UpdateConicalScan(deltaTime);
if (TryDetectAndTrackTarget(KState.Position, KState.Velocity, deltaTime, out Vector3D currentTargetPosition))
TryDetectAndTrackTarget(KState.Position, KState.Velocity, deltaTime, out Vector3D currentTargetPositionFromDetector);
HasGuidance = HasTarget;
if (HasGuidance)
{
targetLostTimer = 0;
Vector3D? currentTargetVelocity = null;
if (LastTargetPosition != null && deltaTime > 0)
if (LastTargetPosition.HasValue && deltaTime > 0 && currentTargetPositionFromDetector != Vector3D.Zero)
{
currentTargetVelocity = (currentTargetPosition - LastTargetPosition) / deltaTime;
currentTargetVelocity = (currentTargetPositionFromDetector - LastTargetPosition.Value) / deltaTime;
}
LastTargetPosition = currentTargetPosition;
LastTargetVelocity = currentTargetVelocity;
LastTargetVelocity = currentTargetVelocity;
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
ProportionalNavigationCoefficient,
KState.Position,
KState.Velocity,
currentTargetPosition,
currentTargetVelocity ?? Vector3D.Zero
currentTargetPositionFromDetector,
LastTargetVelocity ?? Vector3D.Zero
);
if (GuidanceAcceleration.Magnitude() > MaxAcceleration)
{
GuidanceAcceleration = GuidanceAcceleration.Normalize() * MaxAcceleration;
}
Debug.WriteLine($"制导加速度: {GuidanceAcceleration}");
HasGuidance = true;
}
else
else
{
if (currentMode != WorkMode.Search)
{
targetLostTimer += deltaTime;
if (targetLostTimer >= config.TargetLostTolerance)
{
HasGuidance = false;
Trace.TraceInformation($"目标丢失 {targetLostTimer:F3}秒,切换回搜索模式");
SwitchToSearchMode();
}
}
else
{
HasGuidance = false;
}
GuidanceAcceleration = Vector3D.Zero;
LastTargetVelocity = null;
}
HasGuidance = HasTarget;
}
/// <summary>
@ -551,178 +547,209 @@ namespace ThreatSource.Guidance
private bool TryDetectAndTrackTarget(Vector3D missilePosition, Vector3D missileVelocity, double deltaTime, out Vector3D targetPosition)
{
targetPosition = Vector3D.Zero;
double minDistance = double.MaxValue;
bool foundTarget = false;
double currentSNR_dB = double.MinValue;
foreach (var element in SimulationManager.GetEntitiesByType<SimulationElement>())
{
if (element is BaseEquipment target && target.Id != MissileId && target.IsActive)
if (!(element is BaseEquipment target && target.Id != MissileId && target.IsActive))
{
Vector3D toTarget = target.KState.Position - missilePosition;
double distance = toTarget.Magnitude();
continue; // 卫语句: 跳过自身或无效目标
}
if (distance <= config.MaxDetectionRange && distance > 0)
Vector3D toTarget = target.KState.Position - missilePosition;
double distance = toTarget.Magnitude();
// 卫语句: 超出最大探测距离或距离无效
if (!(distance <= config.MaxDetectionRange && distance > 0))
{
if ((currentMode == WorkMode.Track || currentMode == WorkMode.Lock) && target.Id == currentlyTrackedTargetId)
{
double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, target.KState.Position);
// --- 开始RCS获取逻辑 ---
double rcsDbSm = RcsPattern.DEFAULT_RCS_DBSM;
double rcsLinear;
Vector3D targetForward = Vector3D.Zero;
Vector3D targetRight = Vector3D.Zero;
Vector3D targetUp = Vector3D.Zero;
Vector3D observerVector = Vector3D.Zero;
bool isTargetOrientationValid = false;
MotionStateType motionState = MotionStateType.Static;
Debug.WriteLine($"[MMW {currentMode.ToString().ToUpper()}] 当前跟踪目标 {currentlyTrackedTargetId} 超出最大探测距离 ({distance:F0}m > {config.MaxDetectionRange:F0}m) 或距离无效。");
}
else
{
continue;
}
}
if (target.KState != null)
double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, target.KState.Position);
double rcsDbSm = RcsPattern.DEFAULT_RCS_DBSM;
double rcsLinear;
Vector3D targetForward = Vector3D.Zero;
Vector3D targetRight = Vector3D.Zero;
Vector3D targetUp = Vector3D.Zero;
bool isTargetOrientationValid = false;
MotionStateType motionState = MotionStateType.Static;
if (target.KState != null)
{
targetForward = target.KState.Orientation.ToVector().Normalize();
if (targetForward.MagnitudeSquared() > 1e-10)
{
Vector3D worldUpRef = Vector3D.UnitY;
if (Math.Abs(Vector3D.DotProduct(targetForward, worldUpRef)) > 0.99) worldUpRef = Vector3D.UnitZ;
targetRight = Vector3D.CrossProduct(targetForward, worldUpRef).Normalize();
if (targetRight.MagnitudeSquared() > 1e-10)
{
targetForward = target.KState.Orientation.ToVector().Normalize();
if (targetForward.MagnitudeSquared() > 1e-10) // 确保从Orientation得到的Forward有效
{
Vector3D worldUpRef = Vector3D.UnitY;
if (Math.Abs(Vector3D.DotProduct(targetForward, worldUpRef)) > 0.99) // 目标几乎垂直运动
{
worldUpRef = Vector3D.UnitZ;
}
targetRight = Vector3D.CrossProduct(targetForward, worldUpRef).Normalize();
if (targetRight.MagnitudeSquared() > 1e-10) // 确保Right向量有效
{
targetUp = Vector3D.CrossProduct(targetRight, targetForward).Normalize();
if (targetUp.MagnitudeSquared() > 1e-10) // 确保Up向量有效
{
isTargetOrientationValid = true;
}
}
}
if(target.KState.Speed >0.1)
{
motionState = MotionStateType.ConstantVelocity;
}
targetUp = Vector3D.CrossProduct(targetRight, targetForward).Normalize();
if (targetUp.MagnitudeSquared() > 1e-10) isTargetOrientationValid = true;
}
}
if (target.KState.Speed > 0.1) motionState = MotionStateType.ConstantVelocity;
}
if (!isTargetOrientationValid) Debug.WriteLine($"[MMW_GUIDANCE] Target {target.Id}: 方向无效或 KState 为 null. 使用默认RCS.");
if (target.Properties.RcsPattern != null)
{
if (isTargetOrientationValid) rcsDbSm = target.Properties.RcsPattern.GetRcsValueDbSm(missilePosition - target.KState!.Position, targetForward, targetUp, targetRight);
}
else Debug.WriteLine($"[MMW_GUIDANCE] Target {target.Id}: 没有 RcsPattern 对象. 使用默认RCS.");
rcsLinear = Math.Pow(10, rcsDbSm / 10.0);
Debug.WriteLine($"[MMW_GUIDANCE] 目标 {target.Id}: 原始RCS: {rcsLinear:F2} dBsm.");
bool isNewScanPeriodForRcs = currentMode == WorkMode.Track || currentMode == WorkMode.Lock;
rcsLinear = swerlingRcsModel.GetRealtimeRcs(target.Id, target.Properties.Type, motionState, rcsLinear, isNewScanPeriodForRcs);
Debug.WriteLine($"[MMW_GUIDANCE] 目标 {target.Id}: 实时RCS: {rcsLinear:F2} dBsm.");
double localSnrDb = CalculateSNR(distance, rcsLinear, liveSmokeTransmittance);
switch (currentMode)
{
case WorkMode.Search:
if (IsTargetInBeam(missileVelocity, toTarget) && localSnrDb >= config.RecognitionSNRThreshold)
{
Debug.WriteLine($"[MMW SEARCH] TryDetect发现目标: {target.Id}, SNR: {localSnrDb:F2} dB.");
targetPosition = target.KState!.Position;
UpdateStateAndPotentiallySwitchMode(true, localSnrDb, target.Id, targetPosition, deltaTime);
return true;
}
break;
case WorkMode.Track:
if (currentlyTrackedTargetId == null || target.Id != currentlyTrackedTargetId)
{
continue;
}
if (!isTargetOrientationValid)
{
Debug.WriteLine($"[MMW_GUIDANCE] Target {target.Id}: 方向无效或 KState 为 null. 使用默认RCS ({RcsPattern.DEFAULT_RCS_DBSM} dBsm) 进行线性转换.");
}
bool currentFrameSuccessForTrack = IsTargetInBeam(missileVelocity, toTarget) && localSnrDb >= config.RecognitionSNRThreshold;
activeDetectionHistory.Enqueue(currentFrameSuccessForTrack);
while (activeDetectionHistory.Count > COMMON_DETECTION_WINDOW_SIZE) activeDetectionHistory.Dequeue();
Debug.WriteLine($"[MMW TRACK] TryDetect目标 {currentlyTrackedTargetId} 本帧探测: {currentFrameSuccessForTrack}, SNR: {localSnrDb:F2} dB. 窗口 ({activeDetectionHistory.Count}/{COMMON_DETECTION_WINDOW_SIZE}), 预热帧: {_framesInTrackMode}/{MIN_FRAMES_FOR_TRACK_STABILITY_CHECK}.");
if (target.Properties.RcsPattern != null)
bool trackOverallSuccess;
if (_framesInTrackMode < MIN_FRAMES_FOR_TRACK_STABILITY_CHECK)
{
if (isTargetOrientationValid)
{
observerVector = missilePosition - target.KState!.Position; // 从目标指向导弹 (观察者)
Debug.WriteLine($"[RCS获取] Target {target.Id}: observerVector: {observerVector}, targetForward: {targetForward}, targetUp: {targetUp}, targetRight: {targetRight}");
rcsDbSm = target.Properties.RcsPattern.GetRcsValueDbSm(observerVector, targetForward, targetUp, targetRight);
}
_framesInTrackMode++;
trackOverallSuccess = true;
}
else
{
Debug.WriteLine($"[MMW_GUIDANCE] Target {target.Id}: 没有 RcsPattern 对象. 使用默认RCS ({RcsPattern.DEFAULT_RCS_DBSM} dBsm) 进行线性转换.");
if (activeDetectionHistory.Count == COMMON_DETECTION_WINDOW_SIZE)
{
double successRate = (double)activeDetectionHistory.Count(s => s) / COMMON_DETECTION_WINDOW_SIZE;
trackOverallSuccess = successRate >= COMMON_DETECTION_SUCCESS_RATE_THRESHOLD;
if(trackOverallSuccess) Debug.WriteLine($"[MMW TRACK] TryDetect目标 {currentlyTrackedTargetId} 滑动窗口稳定 (率: {successRate:P2}).");
else Debug.WriteLine($"[MMW TRACK] TryDetect目标 {currentlyTrackedTargetId} 滑动窗口不稳定 (率: {successRate:P2}).");
}
else
{
trackOverallSuccess = currentFrameSuccessForTrack;
Debug.WriteLine($"[MMW TRACK] TryDetect目标 {currentlyTrackedTargetId} 预热期后窗口未满. trackOverallSuccess = currentFrameSuccessForTrack ({currentFrameSuccessForTrack}).");
}
}
targetPosition = trackOverallSuccess ?
(currentFrameSuccessForTrack ? target.KState!.Position : (LastTargetPosition ?? Vector3D.Zero)) :
Vector3D.Zero;
UpdateStateAndPotentiallySwitchMode(trackOverallSuccess, localSnrDb, currentlyTrackedTargetId, targetPosition, deltaTime);
return trackOverallSuccess;
// 单位转换 (dBsm -> 平方米)
rcsLinear = Math.Pow(10, rcsDbSm / 10.0);
Debug.WriteLine($"[RCS获取] Target {target.Id}: RCS_dBsm: {rcsDbSm}, RCS_Linear: {rcsLinear}, ");
// 采用Swerling模型计算RCS
rcsLinear = swerlingRcsModel.GetRealtimeRcs(target.Id, target.Properties.Type, motionState, rcsLinear, false);
Debug.WriteLine($"[RCS获取-Swerling模型] Target {target.Id}: RCS_Linear: {rcsLinear} ");
double snr_dB = CalculateSNR(distance, rcsLinear, liveSmokeTransmittance);
if (currentMode == WorkMode.Search)
case WorkMode.Lock:
if (currentlyTrackedTargetId == null || target.Id != currentlyTrackedTargetId)
{
if (IsTargetInBeam(missileVelocity, toTarget) && snr_dB >= config.RecognitionSNRThreshold)
{
if (distance < minDistance)
{
targetPosition = target.KState!.Position;
minDistance = distance;
foundTarget = true;
currentSNR_dB = snr_dB;
}
}
continue;
}
else if (currentMode == WorkMode.Track || currentMode == WorkMode.Lock)
{
bool isPotentiallyTrackedTarget = true;
if (LastTargetPosition != null)
{
double proximityThreshold = (target.Properties.Length > 0) ? target.Properties.Length * 5.0 : 100.0;
isPotentiallyTrackedTarget = Vector3D.Distance(target.KState!.Position, LastTargetPosition.Value) < proximityThreshold;
}
if (IsTargetInBeam(missileVelocity, toTarget) && isPotentiallyTrackedTarget)
{
double requiredSNR_dB = (currentMode == WorkMode.Track) ?
config.RecognitionSNRThreshold : config.LockSNRThreshold;
if (snr_dB >= requiredSNR_dB)
{
targetPosition = target.KState!.Position;
foundTarget = true;
currentSNR_dB = snr_dB;
break;
}
}
bool currentFrameSuccessForLock = IsTargetInBeam(missileVelocity, toTarget) && localSnrDb >= config.LockSNRThreshold;
activeDetectionHistory.Enqueue(currentFrameSuccessForLock);
while (activeDetectionHistory.Count > COMMON_DETECTION_WINDOW_SIZE) activeDetectionHistory.Dequeue();
Debug.WriteLine($"[MMW LOCK] TryDetect目标 {currentlyTrackedTargetId} 本帧探测: {currentFrameSuccessForLock}, SNR: {localSnrDb:F2} dB. 窗口 ({activeDetectionHistory.Count}/{COMMON_DETECTION_WINDOW_SIZE}).");
bool lockOverallSuccess;
if (activeDetectionHistory.Count == COMMON_DETECTION_WINDOW_SIZE)
{
double successRate = (double)activeDetectionHistory.Count(s => s) / COMMON_DETECTION_WINDOW_SIZE;
lockOverallSuccess = successRate >= COMMON_DETECTION_SUCCESS_RATE_THRESHOLD;
if(lockOverallSuccess) Debug.WriteLine($"[MMW LOCK] TryDetect目标 {currentlyTrackedTargetId} 滑动窗口稳定 (率: {successRate:P2}).");
else Debug.WriteLine($"[MMW LOCK] TryDetect目标 {currentlyTrackedTargetId} 滑动窗口不稳定 (率: {successRate:P2}).");
}
}
else
{
lockOverallSuccess = currentFrameSuccessForLock;
Debug.WriteLine($"[MMW LOCK] TryDetect目标 {currentlyTrackedTargetId} 窗口未满. lockOverallSuccess = currentFrameSuccessForLock ({currentFrameSuccessForLock}).");
}
targetPosition = lockOverallSuccess ?
(currentFrameSuccessForLock ? target.KState!.Position : (LastTargetPosition ?? Vector3D.Zero)) :
Vector3D.Zero;
UpdateStateAndPotentiallySwitchMode(lockOverallSuccess, localSnrDb, currentlyTrackedTargetId, targetPosition, deltaTime);
return lockOverallSuccess;
}
}
UpdateSystemState(foundTarget, currentSNR_dB, deltaTime);
HasTarget = foundTarget;
return foundTarget;
Debug.WriteLine($"[MMW {currentMode.ToString().ToUpper()}] TryDetect循环结束未在switch中返回。");
UpdateStateAndPotentiallySwitchMode(false, double.MinValue, currentlyTrackedTargetId, Vector3D.Zero, deltaTime);
targetPosition = Vector3D.Zero;
return false;
}
/// <summary>
/// 更新系统状态
/// </summary>
private void UpdateSystemState(bool hasTarget, double snr, double deltaTime)
private void UpdateStateAndPotentiallySwitchMode(bool frameSuccess, double snr, string? newOrCurrentTargetId, Vector3D newOrCurrentPosition, double deltaTime)
{
if (!hasTarget)
HasTarget = frameSuccess;
lastDetailSnrForDecision = snr;
if (frameSuccess && newOrCurrentPosition != Vector3D.Zero)
{
targetLostTimer += deltaTime;
if (targetLostTimer >= config.TargetLostTolerance)
LastTargetPosition = newOrCurrentPosition;
}
if (!frameSuccess)
{
if (currentMode == WorkMode.Track || currentMode == WorkMode.Lock)
{
Debug.WriteLine($"[MMW {currentMode.ToString().ToUpper()}] UpdateState判定目标丢失/不稳定 (frameSuccess=false),切换到搜索模式。");
SwitchToSearchMode();
}
return;
}
targetLostTimer = 0;
switch (currentMode)
{
case WorkMode.Search:
if (snr >= config.RecognitionSNRThreshold)
if (newOrCurrentTargetId != null)
{
currentlyTrackedTargetId = newOrCurrentTargetId;
Debug.WriteLine($"[MMW SEARCH] UpdateState确认目标 {newOrCurrentTargetId}准备切换到Track。");
SwitchToTrackMode();
}
else
{
Debug.WriteLine("[MMW SEARCH] Warning: UpdateState收到Search成功但无TargetId。");
}
break;
case WorkMode.Track:
if (snr >= config.LockSNRThreshold)
lockConfirmationTimer += deltaTime;
Debug.WriteLine($"[MMW TRACK] UpdateState目标 {currentlyTrackedTargetId} 跟踪稳定. Lock确认计时: {lockConfirmationTimer:F2}s / {config.LockConfirmationTime:F2}s. SNR决策值: {snr:F2}dB");
if (lockConfirmationTimer >= config.LockConfirmationTime)
{
lockConfirmationTimer += deltaTime;
if (lockConfirmationTimer >= config.LockConfirmationTime)
{
SwitchToLockMode();
}
}
else
{
lockConfirmationTimer = 0;
Debug.WriteLine($"[MMW TRACK] UpdateState达到锁定持续时间. 切换到Lock模式.");
SwitchToLockMode();
}
break;
case WorkMode.Lock:
if (snr < config.LockSNRThreshold)
{
SwitchToTrackMode();
}
Debug.WriteLine($"[MMW LOCK] UpdateState目标 {currentlyTrackedTargetId} 锁定稳定. SNR决策值: {snr:F2}dB");
break;
}
}
@ -877,3 +904,7 @@ namespace ThreatSource.Guidance
}
}

View File

@ -431,7 +431,7 @@ namespace ThreatSource.Missile
/// </summary>
protected virtual void UpdateCruiseStage(double deltaTime)
{
if (FlightTime >= Properties.CruiseTime)
if (FlightTime >= Properties.CruiseTime + Properties.MaxEngineBurnTime)
{
currentFlightStage = CompositeFlightStage.Terminal;
}

View File

@ -168,7 +168,7 @@ namespace ThreatSource.Missile
private void UpdateCruiseStage(double deltaTime)
{
// 如果巡航时间达到,切换到末制导搜索阶段
if (FlightTime > Properties.CruiseTime)
if (FlightTime > Properties.CruiseTime + Properties.MaxEngineBurnTime)
{
currentStage = IRTG_Stage.Terminal;
guidanceSystem.Activate();

View File

@ -161,7 +161,7 @@ namespace ThreatSource.Missile
/// </remarks>
private void UpdateCruiseStage(double deltaTime)
{
if (FlightTime > Properties.CruiseTime)
if (FlightTime > Properties.CruiseTime + Properties.MaxEngineBurnTime)
{
currentStage = MWTG_Stage.Terminal;
guidanceSystem.Activate(); // 激活制导系统