586 lines
25 KiB
C#
586 lines
25 KiB
C#
using ThreatSource.Simulation;
|
||
using ThreatSource.Equipment;
|
||
using ThreatSource.Utils;
|
||
using ThreatSource.Jammer;
|
||
using System.Diagnostics;
|
||
|
||
namespace ThreatSource.Guidance
|
||
{
|
||
/// <summary>
|
||
/// 红外成像引导系统类,实现了基于红外图像的目标探测和跟踪功能
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 该类提供了红外成像制导系统的核心功能:
|
||
/// - 目标自动探测
|
||
/// - 图像识别处理
|
||
/// - 信噪比计算
|
||
/// - 比例导引控制
|
||
/// 用于实现自主寻的制导
|
||
/// </remarks>
|
||
public class InfraredImagingGuidanceSystem : BaseGuidanceSystem
|
||
{
|
||
/// <summary>
|
||
/// 工作模式枚举
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 定义了制导系统的三种工作模式:
|
||
/// - Search: 搜索模式,大视场角搜索目标
|
||
/// - Track: 跟踪模式,小视场角精确跟踪,同时进行目标识别
|
||
/// - Lock: 锁定模式,已确认目标类型,只进行跟踪
|
||
/// </remarks>
|
||
private enum WorkMode
|
||
{
|
||
Search, // 搜索模式
|
||
Track, // 跟踪模式
|
||
Lock // 锁定模式
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前工作模式
|
||
/// </summary>
|
||
private WorkMode currentMode = WorkMode.Search;
|
||
|
||
/// <summary>
|
||
/// 获取设备支持的阻塞干扰类型
|
||
/// </summary>
|
||
public override IEnumerable<JammingType> SupportedBlockingJammingTypes => [JammingType.Infrared];
|
||
|
||
/// <summary>
|
||
/// 获取设备支持的干扰类型
|
||
/// </summary>
|
||
public override IEnumerable<JammingType> SupportedJammingTypes => [JammingType.Infrared, JammingType.SmokeGrenade];
|
||
|
||
/// <summary>
|
||
/// 是否已探测到目标
|
||
/// </summary>
|
||
public bool HasTarget { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 当前是否处于锁定模式
|
||
/// </summary>
|
||
public bool IsInLockMode => currentMode == WorkMode.Lock;
|
||
|
||
/// <summary>
|
||
/// 上一次探测到的目标位置 (使用可空类型)
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 记录目标的历史位置
|
||
/// 用于计算目标速度
|
||
/// </remarks>
|
||
private Vector3D? LastTargetPosition { get; set; } // Changed to nullable Vector3D?
|
||
|
||
/// <summary>
|
||
/// 红外图像生成器
|
||
/// </summary>
|
||
private InfraredImageGenerator imageGenerator;
|
||
|
||
/// <summary>
|
||
/// 目标识别器
|
||
/// </summary>
|
||
private readonly InfraredTargetRecognizer targetRecognizer;
|
||
|
||
/// <summary>
|
||
/// 要攻击的目标类型
|
||
/// </summary>
|
||
private readonly EquipmentType targetType;
|
||
|
||
/// <summary>
|
||
/// 红外成像制导系统配置
|
||
/// </summary>
|
||
private readonly InfraredImagingGuidanceConfig config;
|
||
|
||
/// <summary>
|
||
/// 目标丢失计时器
|
||
/// </summary>
|
||
private double targetLostTimer = 0;
|
||
|
||
/// <summary>
|
||
/// 锁定确认计时器
|
||
/// </summary>
|
||
private double lockConfirmationTimer = 0;
|
||
|
||
/// <summary>
|
||
/// 初始化红外成像制导系统的新实例
|
||
/// </summary>
|
||
/// <param name="id">系统标识</param>
|
||
/// <param name="missileId">导弹ID</param>
|
||
/// <param name="guidanceConfig">红外成像制导系统配置</param>
|
||
/// <param name="targetType">要攻击的目标类型</param>
|
||
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
|
||
/// <param name="proportionalNavigationCoefficient">比例导引系数</param>
|
||
/// <param name="simulationManager">仿真管理器实例</param>
|
||
/// <remarks>
|
||
/// 构造过程:
|
||
/// - 初始化基类参数
|
||
/// - 设置仿真管理器
|
||
/// - 初始化目标位置
|
||
/// - 初始化红外图像生成器和目标识别器
|
||
/// </remarks>
|
||
public InfraredImagingGuidanceSystem(
|
||
string id,
|
||
string missileId,
|
||
InfraredImagingGuidanceConfig guidanceConfig,
|
||
EquipmentType targetType,
|
||
double maxAcceleration,
|
||
double proportionalNavigationCoefficient,
|
||
ISimulationManager simulationManager
|
||
)
|
||
: base(id, missileId, maxAcceleration, proportionalNavigationCoefficient, simulationManager)
|
||
{
|
||
this.targetType = targetType;
|
||
config = guidanceConfig;
|
||
targetRecognizer = new InfraredTargetRecognizer();
|
||
|
||
// 首先创建图像生成器
|
||
imageGenerator = new InfraredImageGenerator(
|
||
imageWidth: guidanceConfig.ImageWidth,
|
||
imageHeight: guidanceConfig.ImageHeight,
|
||
fieldOfView: guidanceConfig.SearchFieldOfView,
|
||
backgroundIntensity: guidanceConfig.BackgroundIntensity,
|
||
wavelength: guidanceConfig.Wavelength
|
||
);
|
||
InitializeJamming(guidanceConfig.JammingResistanceThreshold, SupportedJammingTypes, SupportedBlockingJammingTypes);
|
||
SwitchToSearchMode(); // 初始化为搜索模式
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理系统被干扰的事件
|
||
/// </summary>
|
||
/// <param name="parameters">干扰参数</param>
|
||
protected override void HandleJammingApplied(JammingParameters parameters)
|
||
{
|
||
base.HandleJammingApplied(parameters);
|
||
|
||
if (parameters.Type == JammingType.Infrared)
|
||
{
|
||
Debug.WriteLine($"红外引导系统受到红外干扰,功率:{parameters.Power}瓦特");
|
||
|
||
if (currentMode != WorkMode.Search)
|
||
{
|
||
SwitchToSearchMode();
|
||
}
|
||
}
|
||
else if (parameters.Type == JammingType.SmokeGrenade)
|
||
{
|
||
Debug.WriteLine($"[IR IMAGING] 烟幕干扰事件 {parameters.JammerId} 应用。", "Jamming");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理系统干扰被清除的事件
|
||
/// </summary>
|
||
/// <param name="parameters">干扰参数</param>
|
||
protected override void HandleJammingCleared(JammingParameters parameters)
|
||
{
|
||
base.HandleJammingCleared(parameters);
|
||
|
||
if (parameters.Type == JammingType.Infrared)
|
||
{
|
||
Debug.WriteLine("红外引导系统干扰已清除");
|
||
// 干扰清除后的恢复逻辑
|
||
}
|
||
else if (parameters.Type == JammingType.SmokeGrenade)
|
||
{
|
||
Debug.WriteLine($"[IR IMAGING] 烟幕干扰事件 {parameters.JammerId} 清除。", "Jamming");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 激活引导系统
|
||
/// </summary>
|
||
public override void Activate()
|
||
{
|
||
base.Activate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停用引导系统
|
||
/// </summary>
|
||
public override void Deactivate()
|
||
{
|
||
base.Deactivate();
|
||
LastTargetPosition = null; // Add reset to null
|
||
HasTarget = false;
|
||
HasGuidance = false;
|
||
GuidanceAcceleration = Vector3D.Zero;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新引导系统的状态和计算结果
|
||
/// </summary>
|
||
/// <param name="deltaTime">自上次更新以来的时间间隔,单位:秒</param>
|
||
/// <remarks>
|
||
/// 更新过程:
|
||
/// - 探测目标位置
|
||
/// - 计算目标速度
|
||
/// - 生成制导指令
|
||
/// - 限制最大加速度
|
||
/// </remarks>
|
||
public override void Update(double deltaTime)
|
||
{
|
||
base.Update(deltaTime);
|
||
if (!IsBlockingJammed)
|
||
{
|
||
if (TryDetectAndIdentifyTarget(KState.Position, KState.Velocity, deltaTime, out Vector3D currentTargetPosition))
|
||
{
|
||
targetLostTimer = 0; // 重置丢失计时器
|
||
Vector3D? currentTargetVelocity = null; // Initialize as nullable
|
||
if(LastTargetPosition != null && deltaTime > 0)
|
||
{
|
||
currentTargetVelocity = (currentTargetPosition - LastTargetPosition) / deltaTime;
|
||
}
|
||
|
||
LastTargetPosition = currentTargetPosition;
|
||
|
||
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
|
||
ProportionalNavigationCoefficient,
|
||
KState.Position,
|
||
KState.Velocity,
|
||
currentTargetPosition, // Non-nullable from out parameter
|
||
currentTargetVelocity ?? Vector3D.Zero // Provide default if null
|
||
);
|
||
// 限制最大加速度
|
||
if (GuidanceAcceleration.Magnitude() > MaxAcceleration)
|
||
{
|
||
GuidanceAcceleration = GuidanceAcceleration.Normalize() * MaxAcceleration;
|
||
}
|
||
|
||
HasGuidance = true;
|
||
}
|
||
else
|
||
{
|
||
// 在跟踪或锁定模式下,增加丢失计时器
|
||
if (currentMode != WorkMode.Search)
|
||
{
|
||
targetLostTimer += deltaTime;
|
||
// 只有当超过容忍时间才认为真正丢失目标
|
||
if (targetLostTimer >= config.TargetLostTolerance)
|
||
{
|
||
HasGuidance = false;
|
||
// 切换回搜索模式
|
||
Debug.WriteLine($"目标丢失 {targetLostTimer:F3}s, 切换回搜索模式");
|
||
SwitchToSearchMode();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
HasGuidance = false;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GuidanceAcceleration = Vector3D.Zero;
|
||
HasGuidance = false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换到搜索模式
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 切换过程:
|
||
/// - 设置大视场角
|
||
/// - 重置目标状态
|
||
/// - 更新图像生成器参数
|
||
/// </remarks>
|
||
public void SwitchToSearchMode()
|
||
{
|
||
currentMode = WorkMode.Search;
|
||
HasTarget = false;
|
||
targetLostTimer = 0; // 重置丢失计时器
|
||
LastTargetPosition = null; // Add reset to null
|
||
|
||
// 创建图像生成器
|
||
imageGenerator = new InfraredImageGenerator(
|
||
imageWidth: config.ImageWidth,
|
||
imageHeight: config.ImageHeight,
|
||
fieldOfView: config.SearchFieldOfView,
|
||
backgroundIntensity: config.BackgroundIntensity,
|
||
wavelength: config.Wavelength
|
||
);
|
||
Debug.WriteLine($"切换到搜索模式, 视场角: {config.SearchFieldOfView * 180 / Math.PI} 度");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换到跟踪模式
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 切换过程:
|
||
/// - 设置小视场角
|
||
/// - 更新图像生成器参数
|
||
/// - 提高目标识别概率要求
|
||
/// </remarks>
|
||
public void SwitchToTrackMode()
|
||
{
|
||
currentMode = WorkMode.Track;
|
||
lockConfirmationTimer = 0; // 重置锁定确认计时器
|
||
|
||
// 创建图像生成器
|
||
imageGenerator = new InfraredImageGenerator(
|
||
imageWidth: config.ImageWidth,
|
||
imageHeight: config.ImageHeight,
|
||
fieldOfView: config.TrackFieldOfView,
|
||
backgroundIntensity: config.BackgroundIntensity,
|
||
wavelength: config.Wavelength
|
||
);
|
||
Debug.WriteLine($"切换到跟踪模式, 视场角: {config.TrackFieldOfView * 180 / Math.PI} 度");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换到锁定模式
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 切换过程:
|
||
/// - 设置锁定状态
|
||
/// - 保持当前图像生成器参数
|
||
/// - 不再进行目标类型识别
|
||
/// </remarks>
|
||
public void SwitchToLockMode()
|
||
{
|
||
currentMode = WorkMode.Lock;
|
||
Debug.WriteLine("切换到锁定模式 - 目标类型确认");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试探测和识别目标
|
||
/// </summary>
|
||
/// <param name="missilePosition">导弹当前位置</param>
|
||
/// <param name="missileVelocity">导弹当前速度</param>
|
||
/// <param name="deltaTime">时间间隔,单位:秒</param>
|
||
/// <param name="targetPosition">输出目标位置</param>
|
||
/// <returns>是否成功探测到指定类型的目标</returns>
|
||
/// <remarks>
|
||
/// 探测和识别过程:
|
||
/// 1. 遍历场景中的目标
|
||
/// 2. 检查目标是否在探测范围内
|
||
/// 3. 生成目标红外图像
|
||
/// 4. 识别目标类型
|
||
/// 5. 判断是否是要攻击的目标类型
|
||
/// </remarks>
|
||
private bool TryDetectAndIdentifyTarget(Vector3D missilePosition, Vector3D missileVelocity, double deltaTime, out Vector3D targetPosition)
|
||
{
|
||
targetPosition = Vector3D.Zero;
|
||
double minDistance = double.MaxValue;
|
||
bool foundTarget = false;
|
||
|
||
// 根据当前模式选择视场角和识别策略
|
||
double currentFov = currentMode == WorkMode.Search ? config.SearchFieldOfView : config.TrackFieldOfView;
|
||
|
||
foreach (var element in SimulationManager.GetEntitiesByType<SimulationElement>())
|
||
{
|
||
if (element is BaseEquipment target)
|
||
{
|
||
Vector3D toTarget = target.KState.Position - missilePosition;
|
||
double distance = toTarget.Magnitude();
|
||
|
||
// 检查距离条件
|
||
if (distance <= config.MaxDetectionRange)
|
||
{
|
||
// 检查视线角条件
|
||
double angle = Math.Acos(Vector3D.DotProduct(toTarget.Normalize(), missileVelocity.Normalize()));
|
||
if (angle <= currentFov / 2)
|
||
{
|
||
// 在生成红外图像前,检查目标是否被烟幕几何遮挡
|
||
bool isTargetGeometricallyObscured = CheckIfTargetObscuredBySmoke(missilePosition, target);
|
||
if (isTargetGeometricallyObscured)
|
||
{
|
||
Debug.WriteLine($"[红外成像制导系统] 目标 {target.Id} 被烟幕几何遮挡,跳过图像生成");
|
||
continue; // 如果目标被完全遮挡,跳过此目标
|
||
}
|
||
|
||
// 实时计算当前目标到导弹之间的烟幕透过率
|
||
double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, target.KState.Position);
|
||
|
||
// 生成红外图像
|
||
var image = imageGenerator.GenerateImage(target, missilePosition, liveSmokeTransmittance, SimulationManager);
|
||
|
||
switch (currentMode)
|
||
{
|
||
case WorkMode.Search:
|
||
// 搜索模式:使用较低阈值进行目标识别
|
||
var searchResult = targetRecognizer.RecognizeTarget(image, target);
|
||
if (searchResult.Type == targetType && searchResult.Confidence >= config.SearchRecognitionProbability)
|
||
{
|
||
if (distance < minDistance)
|
||
{
|
||
targetPosition = target.KState.Position;
|
||
minDistance = distance;
|
||
foundTarget = true;
|
||
// 切换到跟踪模式
|
||
SwitchToTrackMode();
|
||
}
|
||
}
|
||
break;
|
||
|
||
case WorkMode.Track:
|
||
// 跟踪模式:使用较高阈值确认目标
|
||
var trackResult = targetRecognizer.RecognizeTarget(image, target);
|
||
if (trackResult.Type == targetType && trackResult.Confidence >= config.TrackRecognitionProbability)
|
||
{
|
||
if (distance < minDistance)
|
||
{
|
||
targetPosition = target.KState.Position;
|
||
minDistance = distance;
|
||
foundTarget = true;
|
||
|
||
// 增加锁定确认时间
|
||
lockConfirmationTimer += deltaTime;
|
||
if (lockConfirmationTimer >= config.LockConfirmationTime)
|
||
{
|
||
// 持续高置信度跟踪足够时间后,切换到锁定模式
|
||
SwitchToLockMode();
|
||
}
|
||
}
|
||
}
|
||
else if (trackResult.Type == targetType && trackResult.Confidence >= config.SearchRecognitionProbability)
|
||
{
|
||
// 如果置信度达到搜索阈值但未达到跟踪阈值,重置锁定计时器
|
||
lockConfirmationTimer = 0;
|
||
if (distance < minDistance)
|
||
{
|
||
targetPosition = target.KState.Position;
|
||
minDistance = distance;
|
||
foundTarget = true;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 目标置信度不足,重置锁定计时器
|
||
lockConfirmationTimer = 0;
|
||
}
|
||
break;
|
||
|
||
case WorkMode.Lock:
|
||
// 锁定模式:只进行位置跟踪,不做类型识别
|
||
if (distance < minDistance)
|
||
{
|
||
targetPosition = target.KState.Position;
|
||
minDistance = distance;
|
||
foundTarget = true;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
HasTarget = foundTarget;
|
||
return foundTarget;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查目标是否被烟幕完全遮挡
|
||
/// </summary>
|
||
/// <param name="observerPosition">观察者位置(导弹)</param>
|
||
/// <param name="target">目标</param>
|
||
/// <returns>如果目标被烟幕遮挡超过阈值,返回true</returns>
|
||
private bool CheckIfTargetObscuredBySmoke(Vector3D observerPosition, BaseEquipment target)
|
||
{
|
||
// 获取所有活动的烟幕弹
|
||
var smokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
|
||
.Where(sg => sg.IsActive && sg.config != null)
|
||
.ToList();
|
||
|
||
if (smokeGrenades.Count == 0)
|
||
{
|
||
return false; // 没有活动的烟幕
|
||
}
|
||
|
||
const double ObscurationThreshold = 0.8; // 80%遮挡视为完全遮挡
|
||
Vector3D targetCenter = target.KState.Position;
|
||
Orientation targetOrient = target.KState.Orientation;
|
||
Vector3D targetDims = new(target.Properties.Width, target.Properties.Height, target.Properties.Length);
|
||
|
||
foreach (var smoke in smokeGrenades)
|
||
{
|
||
try
|
||
{
|
||
// 对于墙状烟幕,定义其尺寸和方向
|
||
Vector3D smokeDims;
|
||
if (smoke.config.SmokeType == SmokeScreenType.Cloud)
|
||
{
|
||
smokeDims = new Vector3D(smoke.config.CloudDiameter, smoke.config.Thickness, smoke.config.CloudDiameter);
|
||
}
|
||
else // Wall
|
||
{
|
||
smokeDims = new Vector3D(smoke.config.Thickness, smoke.config.WallHeight, smoke.config.WallWidth);
|
||
}
|
||
|
||
// 使用ObscurationUtils计算烟幕对目标的遮挡比例
|
||
double overlapRatio = ObscurationUtils.CalculateProjectedOverlapRatio(
|
||
observerPosition, // 观察者位置(导弹)
|
||
smoke.KState.Position, smokeDims, smoke.KState.Orientation, // 烟幕数据(前景)
|
||
targetCenter, targetDims, targetOrient // 目标数据(背景)
|
||
);
|
||
|
||
Debug.WriteLine($"[红外成像制导系统] 目标 {target.Id} 被烟幕 {smoke.Id} 遮挡比例: {overlapRatio:P2}");
|
||
|
||
// 如果任何一个烟幕的遮挡比例超过阈值,则认为目标被遮挡
|
||
if (overlapRatio >= ObscurationThreshold)
|
||
{
|
||
Debug.WriteLine($"[红外成像制导系统] 目标 {target.Id} 被烟幕 {smoke.Id} 遮挡超过阈值 {ObscurationThreshold:P0},视为完全遮挡");
|
||
return true;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"[红外成像制导系统] 计算烟幕遮挡时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
return false; // 没有烟幕能有效遮挡目标
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算给定观察点和目标点之间的总烟幕透过率
|
||
/// </summary>
|
||
/// <param name="observerPosition">观察者位置</param>
|
||
/// <param name="targetEndPosition">目标位置</param>
|
||
/// <returns>总透过率 (0.0 到 1.0)</returns>
|
||
private double CalculateLiveSmokeTransmittance(Vector3D observerPosition, Vector3D targetEndPosition)
|
||
{
|
||
double totalTransmittance = 1.0;
|
||
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
|
||
.Where(sg => sg.IsActive && sg.config != null && sg.IsJamming) // 确保烟幕弹已激活并正在干扰
|
||
.ToList();
|
||
|
||
if (!activeSmokeGrenades.Any())
|
||
{
|
||
return 1.0; // 没有活动的、正在干扰的烟幕,无衰减
|
||
}
|
||
|
||
foreach (var smokeGrenade in activeSmokeGrenades)
|
||
{
|
||
// 调用 SmokeGrenade 实例的方法来计算其对视线的透过率
|
||
double transmittanceForThisSmoke = smokeGrenade.GetSmokeTransmittanceOnLine(observerPosition, targetEndPosition, config.Wavelength);
|
||
totalTransmittance *= transmittanceForThisSmoke; // 叠加衰减效应(透过率相乘)
|
||
|
||
// 如果透过率已经很低,可以提前退出以优化
|
||
if (totalTransmittance < 0.001)
|
||
{
|
||
return 0.0;
|
||
}
|
||
}
|
||
return Math.Max(0.0, totalTransmittance); //确保不为负
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取红外图像制导系统的详细状态信息
|
||
/// </summary>
|
||
/// <returns>包含完整状态参数的字符串</returns>
|
||
/// <remarks>
|
||
/// 特有的返回信息:
|
||
/// - 上次发现的目标位置
|
||
/// </remarks>
|
||
public override ElementStatusInfo GetStatusInfo()
|
||
{
|
||
var statusInfo = base.GetStatusInfo();
|
||
string lastPosStr = LastTargetPosition.HasValue ? LastTargetPosition.Value.ToString() : "null";
|
||
statusInfo.ExtendedProperties["lastPosStr"] = lastPosStr;
|
||
return statusInfo;
|
||
}
|
||
}
|
||
}
|