修改红外目标识别算法和烟幕弹作用算法
This commit is contained in:
parent
3d635f6180
commit
27060ee124
20
ThreatSource/data/jammers/smoke_grenades/infrared.json
Normal file
20
ThreatSource/data/jammers/smoke_grenades/infrared.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": {
|
||||
"zh": "周边烟幕弹",
|
||||
"en": "Surround Smoke Grenade"
|
||||
},
|
||||
"type": "SmokeGrenade",
|
||||
"SmokeGrenadeConfig": {
|
||||
"smokeType": "Wall",
|
||||
"IsObscuring": true,
|
||||
"RadiationTemperature": 373.15,
|
||||
"concentration": 0.2,
|
||||
"duration": 60.0,
|
||||
"wallWidth": 50.0,
|
||||
"wallHeight": 10.0,
|
||||
"cloudDiameter": 0.0,
|
||||
"thickness": 5.0,
|
||||
"formationDelay": 2.0,
|
||||
"expansionRate": 5.0
|
||||
}
|
||||
}
|
||||
@ -6,13 +6,15 @@
|
||||
"type": "SmokeGrenade",
|
||||
"SmokeGrenadeConfig": {
|
||||
"smokeType": "Wall",
|
||||
"IsObscuring": false,
|
||||
"RadiationTemperature": 293.15,
|
||||
"concentration": 0.2,
|
||||
"duration": 60.0,
|
||||
"wallWidth": 50.0,
|
||||
"wallHeight": 10.0,
|
||||
"cloudDiameter": 0.0,
|
||||
"thickness": 5.0,
|
||||
"formationDelay": 1.5,
|
||||
"formationDelay": 2.0,
|
||||
"expansionRate": 5.0
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,8 @@
|
||||
"type": "SmokeGrenade",
|
||||
"SmokeGrenadeConfig": {
|
||||
"smokeType": "Cloud",
|
||||
"IsObscuring": true,
|
||||
"RadiationTemperature": 373.15,
|
||||
"concentration": 0.2,
|
||||
"duration": 60.0,
|
||||
"wallWidth": 0.0,
|
||||
|
||||
@ -24,11 +24,12 @@
|
||||
"trackFieldOfView": 0.052,
|
||||
"imageWidth": 640,
|
||||
"imageHeight": 512,
|
||||
"backgroundIntensity": 0.01,
|
||||
"backgroundIntensity": 1e-4,
|
||||
"searchRecognitionProbability": 0.6,
|
||||
"trackRecognitionProbability": 0.8,
|
||||
"targetLostTolerance": 0.2,
|
||||
"lockConfirmationTime": 0.3,
|
||||
"jammingResistanceThreshold": 1e-3
|
||||
"jammingResistanceThreshold": 1e-3,
|
||||
"waveLength": 3.0
|
||||
}
|
||||
}
|
||||
@ -40,6 +40,11 @@ namespace ThreatSource.Guidance
|
||||
/// </summary>
|
||||
public Vector3D LineOfSight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 烟幕覆盖掩码 (true 表示被遮蔽烟幕覆盖)
|
||||
/// </summary>
|
||||
public bool[,] SmokeCoverageMask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化红外图像的新实例
|
||||
/// </summary>
|
||||
@ -47,13 +52,16 @@ namespace ThreatSource.Guidance
|
||||
/// <param name="height">图像高度</param>
|
||||
/// <param name="pixelSize">像素物理尺寸</param>
|
||||
/// <param name="lineOfSight">视线方向</param>
|
||||
public InfraredImage(int width, int height, double pixelSize, Vector3D lineOfSight)
|
||||
/// <param name="smokeCoverageMask">烟幕覆盖掩码 (可选)</param>
|
||||
public InfraredImage(int width, int height, double pixelSize, Vector3D lineOfSight, bool[,] smokeCoverageMask = null)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
PixelSize = pixelSize;
|
||||
LineOfSight = lineOfSight;
|
||||
Intensity = new double[height, width];
|
||||
|
||||
SmokeCoverageMask = smokeCoverageMask ?? new bool[height, width];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Linq; // Add Linq for FirstOrDefault
|
||||
using ThreatSource.Equipment;
|
||||
using ThreatSource.Utils;
|
||||
using AirTransmission; // 添加引用
|
||||
using AirTransmission;
|
||||
using ThreatSource.Jammer; // 添加引用
|
||||
using ThreatSource.Simulation; // Add Simulation namespace
|
||||
|
||||
namespace ThreatSource.Guidance
|
||||
{
|
||||
@ -47,6 +50,11 @@ namespace ThreatSource.Guidance
|
||||
/// </summary>
|
||||
private readonly Random random = new();
|
||||
|
||||
/// <summary>
|
||||
/// 烟幕温度转换为强度的比例因子 (简化 T^4 关系)
|
||||
/// </summary>
|
||||
private const double SMOKE_TEMPERATURE_FACTOR = 1e-11;
|
||||
|
||||
// 视线坐标系
|
||||
private Vector3D forward; // 视线方向
|
||||
private Vector3D right; // 图像平面右方向
|
||||
@ -138,44 +146,165 @@ namespace ThreatSource.Guidance
|
||||
/// </summary>
|
||||
/// <param name="distance">距离(米)</param>
|
||||
/// <param name="weather">天气条件</param>
|
||||
/// <param name="smokeAttenuation">烟幕衰减</param>
|
||||
/// <returns>大气透过率(0-1之间)</returns>
|
||||
private double CalculateAtmosphericTransmittance(double distance, Weather? weather)
|
||||
private double CalculateAtmosphericTransmittance(double distance, Weather weather, double smokeAttenuation)
|
||||
{
|
||||
double transmittance;
|
||||
|
||||
if (weather == null)
|
||||
{
|
||||
// 如果没有天气信息,使用基于距离的简化模型
|
||||
transmittance = Math.Exp(-0.2 * distance / 1000.0); // 0.2/km的衰减率
|
||||
return transmittance;
|
||||
}
|
||||
|
||||
// 使用AtmosphereDllWrapper计算红外波段的透过率
|
||||
transmittance = AtmosphereDllWrapper.CalculateTransmittance(
|
||||
distance,
|
||||
RadiationType.Infrared,
|
||||
wavelength,
|
||||
weather);
|
||||
|
||||
// 考虑烟幕衰减
|
||||
transmittance*= smokeAttenuation;
|
||||
|
||||
return transmittance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用遮蔽型烟幕效果 (根据类型区分形状)
|
||||
/// </summary>
|
||||
/// <param name="smokeIntensityLayer">存储烟幕强度的图层</param>
|
||||
/// <param name="missilePosition">导弹当前位置</param>
|
||||
/// <param name="simulationManager">仿真管理器实例</param>
|
||||
private void ApplyObscuringSmokeOverlay(double[,] smokeIntensityLayer, Vector3D missilePosition, ISimulationManager simulationManager)
|
||||
{
|
||||
const int minPixelSize = 3; // 最小像素尺寸 (直径或边长)
|
||||
|
||||
var smokeGrenades = simulationManager.GetEntitiesByType<SmokeGrenade>();
|
||||
|
||||
foreach (var smoke in smokeGrenades)
|
||||
{
|
||||
// 检查是否是遮蔽型烟幕 - 通过 config 访问
|
||||
// 确保 config 和 RadiationTemperature 存在
|
||||
if (smoke.IsActive ==false || smoke.config == null || !smoke.config.IsObscuring || smoke.config.RadiationTemperature <= 0) continue;
|
||||
|
||||
Vector3D toSmoke = smoke.Position - missilePosition;
|
||||
double distance = toSmoke.Magnitude();
|
||||
|
||||
// 粗略检查烟幕是否在前方视野内
|
||||
if (Vector3D.DotProduct(toSmoke.Normalize(), forward) < Math.Cos(fieldOfView / 2.0)) continue; // Use half FOV
|
||||
|
||||
// 计算烟幕强度 (基于 T^4 简化模型) - 通过 config 访问
|
||||
// 注意: 这里没有考虑从烟幕到导弹的大气衰减,可以根据需要添加
|
||||
double smokeIntensity = SMOKE_TEMPERATURE_FACTOR * Math.Pow(smoke.config.RadiationTemperature, 4);
|
||||
|
||||
// 投影烟幕中心
|
||||
var (angleX, angleY) = ProjectToImagePlane(smoke.Position, missilePosition);
|
||||
var (smokeCenterX, smokeCenterY) = AngleToPixel(angleX, angleY);
|
||||
|
||||
// 检查投影中心是否大致在图像内 (允许部分超出)
|
||||
// if (smokeCenterX < -imageWidth/2 || smokeCenterX >= imageWidth*1.5 || smokeCenterY < -imageHeight/2 || smokeCenterY >= imageHeight*1.5) continue;
|
||||
// More robust check needed? For now, let's proceed if center is roughly projectable.
|
||||
|
||||
// 根据烟幕类型计算尺寸并填充 - 通过 config 访问
|
||||
switch (smoke.config.SmokeType)
|
||||
{
|
||||
|
||||
case SmokeScreenType.Wall:
|
||||
// 墙状烟幕,近似为水平矩形
|
||||
double wallWidth = smoke.config.WallWidth; // 通过 config 访问
|
||||
double wallHeight = smoke.config.WallHeight; // 通过 config 访问
|
||||
int pixelWidth = (int)Math.Max(minPixelSize, (wallWidth / distance) * imageWidth / fieldOfView);
|
||||
int pixelHeight = (int)Math.Max(minPixelSize, (wallHeight / distance) * imageHeight / fieldOfView); // Use imageHeight for vertical FOV estimate
|
||||
|
||||
int halfPixelWidth = pixelWidth / 2;
|
||||
int halfPixelHeight = pixelHeight / 2;
|
||||
|
||||
// 确定绘制边界,确保在图像范围内
|
||||
int minX_rect = Math.Max(0, smokeCenterX - halfPixelWidth);
|
||||
int maxX_rect = Math.Min(imageWidth - 1, smokeCenterX + halfPixelWidth);
|
||||
int minY_rect = Math.Max(0, smokeCenterY - halfPixelHeight);
|
||||
int maxY_rect = Math.Min(imageHeight - 1, smokeCenterY + halfPixelHeight);
|
||||
|
||||
for (int y = minY_rect; y <= maxY_rect; y++)
|
||||
{
|
||||
for (int x = minX_rect; x <= maxX_rect; x++)
|
||||
{
|
||||
// 存储烟幕强度,处理重叠(取最大值)
|
||||
smokeIntensityLayer[y, x] = Math.Max(smokeIntensityLayer[y, x], smokeIntensity);
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"记录遮蔽烟幕(Wall): 中心({smokeCenterX},{smokeCenterY}), 尺寸{pixelWidth}x{pixelHeight}px, 强度{smokeIntensity:F6}");
|
||||
break;
|
||||
|
||||
case SmokeScreenType.Cloud:
|
||||
// 云状烟幕,近似为圆形
|
||||
double cloudDiameter = smoke.config.CloudDiameter; // 通过 config 访问
|
||||
int pixelDiameter = (int)Math.Max(minPixelSize, (cloudDiameter / distance) * imageWidth / fieldOfView);
|
||||
int pixelRadius = pixelDiameter / 2;
|
||||
int radiusSquared = pixelRadius * pixelRadius;
|
||||
|
||||
// 确定绘制边界,确保在图像范围内
|
||||
int minX_circle = Math.Max(0, smokeCenterX - pixelRadius);
|
||||
int maxX_circle = Math.Min(imageWidth - 1, smokeCenterX + pixelRadius);
|
||||
int minY_circle = Math.Max(0, smokeCenterY - pixelRadius);
|
||||
int maxY_circle = Math.Min(imageHeight - 1, smokeCenterY + pixelRadius);
|
||||
|
||||
for (int y = minY_circle; y <= maxY_circle; y++)
|
||||
{
|
||||
for (int x = minX_circle; x <= maxX_circle; x++)
|
||||
{
|
||||
int dx = x - smokeCenterX;
|
||||
int dy = y - smokeCenterY;
|
||||
if (dx * dx + dy * dy <= radiusSquared)
|
||||
{
|
||||
// 存储烟幕强度,处理重叠(取最大值)
|
||||
smokeIntensityLayer[y, x] = Math.Max(smokeIntensityLayer[y, x], smokeIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"记录遮蔽烟幕(Cloud): 中心({smokeCenterX},{smokeCenterY}), 半径{pixelRadius}px, 强度{smokeIntensity:F6}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成目标的红外图像
|
||||
/// </summary>
|
||||
/// <param name="target">目标对象</param>
|
||||
/// <param name="missilePosition">导弹位置</param>
|
||||
/// <param name="missileVelocity">导弹速度</param>
|
||||
/// <param name="weather">天气条件</param>
|
||||
/// <param name="smokeAttenuation">衰减型烟幕透过率</param>
|
||||
/// <param name="simulationManager">仿真管理器实例</param>
|
||||
/// <returns>红外图像</returns>
|
||||
public InfraredImage GenerateImage(BaseEquipment target, Vector3D missilePosition, Vector3D missileVelocity, Weather? weather)
|
||||
public InfraredImage GenerateImage(BaseEquipment target, Vector3D missilePosition, double smokeAttenuation, ISimulationManager simulationManager)
|
||||
{
|
||||
// 更新视线坐标系
|
||||
UpdateLineOfSightFrame(missilePosition, target.Position);
|
||||
|
||||
// 创建图像
|
||||
var image = new InfraredImage(imageWidth, imageHeight, fieldOfView / imageWidth, forward);
|
||||
|
||||
|
||||
// 创建并初始化烟幕强度图层 (-1表示无烟幕)
|
||||
double[,] smokeIntensityLayer = new double[imageHeight, imageWidth];
|
||||
for (int y = 0; y < imageHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < imageWidth; x++)
|
||||
{
|
||||
smokeIntensityLayer[y, x] = -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// 应用(记录)遮蔽型烟幕效果到图层
|
||||
ApplyObscuringSmokeOverlay(smokeIntensityLayer, missilePosition, simulationManager);
|
||||
|
||||
// --- 新增:根据烟幕强度图层创建烟幕覆盖掩码 ---
|
||||
bool[,] smokeCoverageMask = new bool[imageHeight, imageWidth];
|
||||
for (int y = 0; y < imageHeight; y++)
|
||||
{
|
||||
for (int x = 0; x < imageWidth; x++)
|
||||
{
|
||||
smokeCoverageMask[y, x] = (smokeIntensityLayer[y, x] >= 0); // >= 0 表示被覆盖
|
||||
}
|
||||
}
|
||||
// --- 结束新增 ---
|
||||
|
||||
// --- 新增:在此处创建 image 对象并传入掩码 ---
|
||||
var image = new InfraredImage(imageWidth, imageHeight, fieldOfView / imageWidth, forward, smokeCoverageMask);
|
||||
// --- 结束新增 ---
|
||||
|
||||
// 计算目标中心投影
|
||||
var (angleX, angleY) = ProjectToImagePlane(target.Position, missilePosition);
|
||||
var (centerX, centerY) = AngleToPixel(angleX, angleY);
|
||||
@ -193,77 +322,106 @@ namespace ThreatSource.Guidance
|
||||
pixelWidth = Math.Max(1, pixelWidth);
|
||||
|
||||
Console.WriteLine($"生成目标 {target.Id} 的图像,尺寸: {pixelLength}x{pixelWidth} 像素,目标中心: ({centerX}, {centerY})");
|
||||
|
||||
// 计算大气和衰减型烟幕的总透过率
|
||||
double transmittance = CalculateAtmosphericTransmittance(distance, simulationManager.CurrentWeather, smokeAttenuation);
|
||||
Console.WriteLine($"大气及衰减烟幕透过率: {transmittance}");
|
||||
|
||||
// 生成目标图像强度,考虑遮蔽烟幕覆盖
|
||||
GenerateTargetIntensity(image, smokeIntensityLayer, centerX, centerY, pixelLength, pixelWidth, target, distance, transmittance);
|
||||
|
||||
// 生成目标图像,传递距离参数
|
||||
GenerateTargetIntensity(image, centerX, centerY, pixelLength, pixelWidth, target, distance, weather);
|
||||
|
||||
// 添加背景和噪声
|
||||
AddBackgroundAndNoise(image, weather);
|
||||
// 添加背景和噪声(这会影响到烟幕和目标区域)
|
||||
AddBackgroundAndNoise(image, simulationManager.CurrentWeather);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成目标的红外强度分布
|
||||
/// 生成目标的红外强度分布,考虑遮蔽烟幕覆盖
|
||||
/// </summary>
|
||||
private void GenerateTargetIntensity(
|
||||
/// <param name="image">红外图像对象</param>
|
||||
/// <param name="smokeIntensityLayer">遮蔽烟幕强度图层</param>
|
||||
/// <param name="centerX">目标中心X像素坐标</param>
|
||||
/// <param name="centerY">目标中心Y像素坐标</param>
|
||||
/// <param name="pixelLength">目标像素长度</param>
|
||||
/// <param name="pixelWidth">目标像素宽度</param>
|
||||
/// <param name="target">目标设备对象</param>
|
||||
/// <param name="distance">目标距离</param>
|
||||
/// <param name="transmittance">大气和衰减烟幕透过率</param>
|
||||
private static void GenerateTargetIntensity(
|
||||
InfraredImage image,
|
||||
double[,] smokeIntensityLayer, // 新增参数
|
||||
int centerX,
|
||||
int centerY,
|
||||
int pixelLength,
|
||||
int pixelWidth,
|
||||
BaseEquipment target,
|
||||
double distance,
|
||||
Weather? weather)
|
||||
double transmittance)
|
||||
{
|
||||
// 计算大气透过率
|
||||
double transmittance = CalculateAtmosphericTransmittance(distance, weather);
|
||||
|
||||
// 计算目标辐射强度,考虑距离和大气透过率的影响
|
||||
double targetIntensity = target.Properties.InfraredRadiationIntensity * transmittance / Math.Pow(distance, 1.8);
|
||||
// 计算目标自身应有的辐射强度(未被遮挡时)
|
||||
double targetBaseIntensity = target.Properties.InfraredRadiationIntensity * transmittance / Math.Pow(distance, 1.8);
|
||||
|
||||
// 计算分布参数
|
||||
double sigmaX = pixelLength / 6.0;
|
||||
// 计算高斯分布参数
|
||||
double sigmaX = pixelLength / 6.0; // 标准差设为长度/宽度的1/6,覆盖约99.7%范围
|
||||
double sigmaY = pixelWidth / 6.0;
|
||||
|
||||
|
||||
// 避免除以零
|
||||
sigmaX = Math.Max(sigmaX, 1e-6);
|
||||
sigmaY = Math.Max(sigmaY, 1e-6);
|
||||
|
||||
int halfLength = pixelLength / 2;
|
||||
int halfWidth = pixelWidth / 2;
|
||||
|
||||
|
||||
int pixelsSet = 0;
|
||||
double maxSetIntensity = 0;
|
||||
|
||||
|
||||
// 设置强度分布
|
||||
for (int dy = -halfWidth; dy <= halfWidth; dy++)
|
||||
{
|
||||
int y = centerY + dy;
|
||||
if (y < 0 || y >= image.Height) continue;
|
||||
|
||||
|
||||
for (int dx = -halfLength; dx <= halfLength; dx++)
|
||||
{
|
||||
int x = centerX + dx;
|
||||
if (x < 0 || x >= image.Width) continue;
|
||||
|
||||
// 计算归一化距离
|
||||
double normalizedX = dx / sigmaX;
|
||||
double normalizedY = dy / sigmaY;
|
||||
double r2 = normalizedX * normalizedX + normalizedY * normalizedY;
|
||||
|
||||
// 高斯分布
|
||||
double intensity = targetIntensity * Math.Exp(-r2 / 2.0);
|
||||
|
||||
image.SetIntensity(y, x, intensity);
|
||||
|
||||
double finalIntensity;
|
||||
double smokeIntensity = smokeIntensityLayer[y, x];
|
||||
|
||||
if (smokeIntensity >= 0) // 检查此像素是否被遮蔽烟幕覆盖
|
||||
{
|
||||
// 如果被覆盖,使用烟幕的强度
|
||||
finalIntensity = smokeIntensity;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果未被覆盖,计算目标的强度
|
||||
// 计算归一化距离 (相对于高斯分布中心)
|
||||
double normalizedX = dx / sigmaX;
|
||||
double normalizedY = dy / sigmaY;
|
||||
double r2 = normalizedX * normalizedX + normalizedY * normalizedY;
|
||||
|
||||
// 计算高斯分布下的目标强度
|
||||
finalIntensity = targetBaseIntensity * Math.Exp(-r2 / 2.0);
|
||||
// Console.WriteLine($"像素 ({x},{y}) 未被烟幕覆盖,使用目标强度: {finalIntensity:F6}");
|
||||
}
|
||||
|
||||
// 设置最终计算出的强度到图像
|
||||
image.SetIntensity(y, x, finalIntensity);
|
||||
pixelsSet++;
|
||||
maxSetIntensity = Math.Max(maxSetIntensity, intensity);
|
||||
maxSetIntensity = Math.Max(maxSetIntensity, finalIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"目标强度分布: 像素设置: {pixelsSet}, 最大强度: {maxSetIntensity:F6} W/sr");
|
||||
|
||||
Console.WriteLine($"目标/烟幕强度分布: 像素设置: {pixelsSet}, 最大强度: {maxSetIntensity:F6} W/sr");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加背景辐射和噪声
|
||||
/// </summary>
|
||||
private void AddBackgroundAndNoise(InfraredImage image, Weather? weather)
|
||||
private void AddBackgroundAndNoise(InfraredImage image, Weather weather)
|
||||
{
|
||||
double maxIntensityBefore = double.MinValue;
|
||||
double maxIntensityAfter = double.MinValue;
|
||||
@ -273,25 +431,24 @@ namespace ThreatSource.Guidance
|
||||
double noiseLevel = 0.2; // 默认噪声水平
|
||||
double bgIntensity = backgroundIntensity;
|
||||
|
||||
if (weather != null)
|
||||
|
||||
// 雾、雨、雪等天气增加背景噪声
|
||||
if (weather.Type == WeatherType.Fog)
|
||||
{
|
||||
// 雾、雨、雪等天气增加背景噪声
|
||||
if (weather.Type == WeatherType.Fog)
|
||||
noiseLevel = 0.5;
|
||||
bgIntensity *= (1.0 + (1.0 - weather.Visibility / 10.0)); // 雾增加背景辐射
|
||||
}
|
||||
else if (weather.Type == WeatherType.Rain || weather.Type == WeatherType.Snow)
|
||||
{
|
||||
noiseLevel = 0.3;
|
||||
// 降水增加噪声水平
|
||||
if (weather.Precipitation > 0)
|
||||
{
|
||||
noiseLevel = 0.5;
|
||||
bgIntensity *= (1.0 + (1.0 - weather.Visibility / 10.0)); // 雾增加背景辐射
|
||||
}
|
||||
else if (weather.Type == WeatherType.Rain || weather.Type == WeatherType.Snow)
|
||||
{
|
||||
noiseLevel = 0.3;
|
||||
// 降水增加噪声水平
|
||||
if (weather.Precipitation > 0)
|
||||
{
|
||||
noiseLevel += 0.1 * Math.Min(1.0, weather.Precipitation / 10.0);
|
||||
}
|
||||
noiseLevel += 0.1 * Math.Min(1.0, weather.Precipitation / 10.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
@ -300,14 +457,16 @@ namespace ThreatSource.Guidance
|
||||
maxIntensityBefore = Math.Max(maxIntensityBefore, currentIntensity);
|
||||
|
||||
// 只在非目标区域添加背景
|
||||
if (currentIntensity < bgIntensity * 0.1)
|
||||
if (currentIntensity < bgIntensity)
|
||||
{
|
||||
currentIntensity = bgIntensity;
|
||||
pixelsModified++;
|
||||
}
|
||||
|
||||
// 添加高斯噪声
|
||||
double noise = (random.NextDouble() - 0.5) * bgIntensity * noiseLevel;
|
||||
// 添加高斯噪声 (降低噪声水平)
|
||||
double noiseReductionFactor = 0.0001; // 从 0.001 改为 0.0001
|
||||
double reducedNoiseLevel = noiseLevel * noiseReductionFactor;
|
||||
double noise = (random.NextDouble() - 0.5) * bgIntensity * reducedNoiseLevel;
|
||||
currentIntensity += noise;
|
||||
|
||||
// 确保非负
|
||||
|
||||
@ -91,6 +91,11 @@ namespace ThreatSource.Guidance
|
||||
/// </summary>
|
||||
private double lockConfirmationTimer = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 烟幕衰减
|
||||
/// </summary>
|
||||
private double SmokeAttenuation { get; set; } = 1.0;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化红外成像制导系统的新实例
|
||||
@ -128,9 +133,10 @@ namespace ThreatSource.Guidance
|
||||
imageWidth: guidanceConfig.ImageWidth,
|
||||
imageHeight: guidanceConfig.ImageHeight,
|
||||
fieldOfView: guidanceConfig.SearchFieldOfView,
|
||||
backgroundIntensity: guidanceConfig.BackgroundIntensity
|
||||
backgroundIntensity: guidanceConfig.BackgroundIntensity,
|
||||
wavelength: guidanceConfig.WaveLength
|
||||
);
|
||||
InitializeJamming(guidanceConfig.JammingResistanceThreshold, [JammingType.Infrared]);
|
||||
InitializeJamming(guidanceConfig.JammingResistanceThreshold, [JammingType.Infrared, JammingType.SmokeScreen]);
|
||||
SwitchToSearchMode(); // 初始化为搜索模式
|
||||
}
|
||||
|
||||
@ -170,11 +176,11 @@ namespace ThreatSource.Guidance
|
||||
/// <param name="parameters">干扰参数</param>
|
||||
protected override void HandleJammingApplied(JammingParameters parameters)
|
||||
{
|
||||
base.HandleJammingApplied(parameters);
|
||||
|
||||
if (parameters.Type == JammingType.Infrared)
|
||||
{
|
||||
Debug.WriteLine($"红外引导系统受到红外干扰,功率:{parameters.Power}瓦特");
|
||||
// 确保基类的处理逻辑被调用,设置HasGuidance = false
|
||||
base.HandleJammingApplied(parameters);
|
||||
|
||||
// 在强干扰下切换到搜索模式
|
||||
if (currentMode != WorkMode.Search)
|
||||
@ -182,6 +188,14 @@ namespace ThreatSource.Guidance
|
||||
SwitchToSearchMode();
|
||||
}
|
||||
}
|
||||
else if (parameters.Type == JammingType.SmokeScreen)
|
||||
{
|
||||
if (SimulationManager.GetEntityById(parameters.JammerId) is SmokeGrenade smokeGrenade)
|
||||
{
|
||||
// 计算烟幕衰减
|
||||
SmokeAttenuation = smokeGrenade.GetSmokeTransmittanceOnLine(Position, lastTargetPosition, config.WaveLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -204,13 +218,12 @@ namespace ThreatSource.Guidance
|
||||
/// </summary>
|
||||
public override void Activate()
|
||||
{
|
||||
if (!IsActive)
|
||||
{
|
||||
IsActive = true;
|
||||
// 在这里订阅事件,确保只订阅一次
|
||||
SimulationManager.SubscribeToEvent<InfraredJammingEvent>(OnInfraredJamming);
|
||||
}
|
||||
base.Activate();
|
||||
// 在这里订阅事件,确保只订阅一次
|
||||
SimulationManager.SubscribeToEvent<InfraredJammingEvent>(OnInfraredJamming);
|
||||
|
||||
// 订阅烟幕事件
|
||||
SimulationManager.SubscribeToEvent<SmokeScreenEvent>(OnSmokeScreen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -218,14 +231,46 @@ namespace ThreatSource.Guidance
|
||||
/// </summary>
|
||||
public override void Deactivate()
|
||||
{
|
||||
if (IsActive)
|
||||
{
|
||||
IsActive = false;
|
||||
SimulationManager.UnsubscribeFromEvent<InfraredJammingEvent>(OnInfraredJamming);
|
||||
}
|
||||
base.Deactivate();
|
||||
SimulationManager.UnsubscribeFromEvent<InfraredJammingEvent>(OnInfraredJamming);
|
||||
SimulationManager.UnsubscribeFromEvent<SmokeScreenEvent>(OnSmokeScreen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理烟幕事件
|
||||
/// </summary>
|
||||
/// <param name="evt">烟幕事件</param>
|
||||
private void OnSmokeScreen(SmokeScreenEvent evt)
|
||||
{
|
||||
if (evt != null && evt.SmokeGrenadeId != null)
|
||||
{
|
||||
// 获取烟幕弹的配置
|
||||
if (SimulationManager.GetEntityById(evt.SmokeGrenadeId) is SmokeGrenade smokeGrenade)
|
||||
{
|
||||
var config = smokeGrenade.config;
|
||||
// 创建干扰参数
|
||||
var parameters = new JammingParameters
|
||||
{
|
||||
Type = JammingType.SmokeScreen,
|
||||
JammerId = smokeGrenade.Id,
|
||||
SourcePosition = smokeGrenade.Position,
|
||||
Direction = smokeGrenade.Orientation.ToVector(),
|
||||
AngleRange = config.SmokeType == SmokeScreenType.Wall ? Math.PI : Math.PI * 2, // 墙状烟幕为半球形覆盖,云状为全方位
|
||||
SmokeConcentration = config.Concentration,
|
||||
SmokeType = config.SmokeType,
|
||||
SmokeThickness = config.Thickness,
|
||||
Duration = config.Duration,
|
||||
Mode = JammingMode.Obscuration
|
||||
};
|
||||
|
||||
// 使用JammableComponent进行干扰判断
|
||||
ApplyJamming(parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新引导系统的状态和计算结果
|
||||
/// </summary>
|
||||
@ -242,7 +287,7 @@ namespace ThreatSource.Guidance
|
||||
public override void Update(double deltaTime, Vector3D missilePosition, Vector3D missileVelocity)
|
||||
{
|
||||
base.Update(deltaTime, missilePosition, missileVelocity);
|
||||
if (!IsJammed)
|
||||
if (!IsHardJammed)
|
||||
{
|
||||
if (TryDetectAndIdentifyTarget(missilePosition, missileVelocity, deltaTime, out Vector3D tankPosition))
|
||||
{
|
||||
@ -284,7 +329,6 @@ namespace ThreatSource.Guidance
|
||||
}
|
||||
else
|
||||
{
|
||||
HasGuidance = false;
|
||||
GuidanceAcceleration = Vector3D.Zero;
|
||||
}
|
||||
}
|
||||
@ -309,7 +353,8 @@ namespace ThreatSource.Guidance
|
||||
imageWidth: config.ImageWidth,
|
||||
imageHeight: config.ImageHeight,
|
||||
fieldOfView: config.SearchFieldOfView,
|
||||
backgroundIntensity: config.BackgroundIntensity
|
||||
backgroundIntensity: config.BackgroundIntensity,
|
||||
wavelength: config.WaveLength
|
||||
);
|
||||
Console.WriteLine($"切换到搜索模式, 视场角: {config.SearchFieldOfView * 180 / Math.PI} 度");
|
||||
}
|
||||
@ -333,7 +378,8 @@ namespace ThreatSource.Guidance
|
||||
imageWidth: config.ImageWidth,
|
||||
imageHeight: config.ImageHeight,
|
||||
fieldOfView: config.TrackFieldOfView,
|
||||
backgroundIntensity: config.BackgroundIntensity
|
||||
backgroundIntensity: config.BackgroundIntensity,
|
||||
wavelength: config.WaveLength
|
||||
);
|
||||
Console.WriteLine($"切换到跟踪模式, 视场角: {config.TrackFieldOfView * 180 / Math.PI} 度");
|
||||
}
|
||||
@ -393,7 +439,7 @@ namespace ThreatSource.Guidance
|
||||
if (angle <= currentFov / 2)
|
||||
{
|
||||
// 生成红外图像
|
||||
var image = imageGenerator.GenerateImage(target, missilePosition, missileVelocity, SimulationManager.CurrentWeather );
|
||||
var image = imageGenerator.GenerateImage(target, missilePosition, SmokeAttenuation, SimulationManager);
|
||||
|
||||
switch (currentMode)
|
||||
{
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using ThreatSource.Equipment;
|
||||
using ThreatSource.Simulation;
|
||||
using System.Collections.Generic; // For Queue and List
|
||||
using System.Linq; // For OrderByDescending
|
||||
|
||||
namespace ThreatSource.Guidance
|
||||
{
|
||||
@ -74,7 +76,7 @@ namespace ThreatSource.Guidance
|
||||
/// <summary>
|
||||
/// 背景辐射强度,用于阈值计算
|
||||
/// </summary>
|
||||
private const double BACKGROUND_INTENSITY = 0.01;
|
||||
private const double BACKGROUND_INTENSITY = 0.0001;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化红外图像目标识别器
|
||||
@ -149,51 +151,139 @@ namespace ThreatSource.Guidance
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图像分割,提取目标区域
|
||||
/// 图像分割,提取目标区域 - **重写:使用连通区域分析**
|
||||
/// </summary>
|
||||
private ImageSegment SegmentTarget(InfraredImage image)
|
||||
{
|
||||
// 使用简单的阈值分割方法
|
||||
double threshold = CalculateThreshold(image);
|
||||
int minX = image.Width, minY = image.Height, maxX = 0, maxY = 0;
|
||||
bool found = false;
|
||||
int pixelsAboveThreshold = 0;
|
||||
int width = image.Width;
|
||||
int height = image.Height;
|
||||
bool[,] visited = new bool[height, width];
|
||||
List<Blob> blobs = new List<Blob>();
|
||||
Queue<(int x, int y)> queue = new Queue<(int x, int y)>();
|
||||
|
||||
// 寻找目标边界
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
Console.WriteLine($"开始连通区域分析,阈值: {threshold:F6}");
|
||||
|
||||
// 1. 查找所有连通区域 (Blobs)
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
double intensity = image.GetIntensity(y, x);
|
||||
if (intensity > threshold)
|
||||
if (!visited[y, x] && image.GetIntensity(y, x) > threshold)
|
||||
{
|
||||
minX = Math.Min(minX, x);
|
||||
minY = Math.Min(minY, y);
|
||||
maxX = Math.Max(maxX, x);
|
||||
maxY = Math.Max(maxY, y);
|
||||
found = true;
|
||||
pixelsAboveThreshold++;
|
||||
// 发现新 Blob 的起点
|
||||
var currentBlob = new Blob
|
||||
{
|
||||
Label = blobs.Count + 1,
|
||||
Pixels = new List<(int x, int y)>(),
|
||||
MinX = x, MinY = y, MaxX = x, MaxY = y
|
||||
};
|
||||
|
||||
queue.Enqueue((x, y));
|
||||
visited[y, x] = true;
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
var (qx, qy) = queue.Dequeue();
|
||||
currentBlob.Pixels.Add((qx, qy));
|
||||
|
||||
// 更新边界框
|
||||
currentBlob.MinX = Math.Min(currentBlob.MinX, qx);
|
||||
currentBlob.MinY = Math.Min(currentBlob.MinY, qy);
|
||||
currentBlob.MaxX = Math.Max(currentBlob.MaxX, qx);
|
||||
currentBlob.MaxY = Math.Max(currentBlob.MaxY, qy);
|
||||
|
||||
// 检查 4-邻域
|
||||
int[] dx = { 0, 0, 1, -1 };
|
||||
int[] dy = { 1, -1, 0, 0 };
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
int nx = qx + dx[i];
|
||||
int ny = qy + dy[i];
|
||||
|
||||
if (nx >= 0 && nx < width && ny >= 0 && ny < height &&
|
||||
!visited[ny, nx] && image.GetIntensity(ny, nx) > threshold)
|
||||
{
|
||||
visited[ny, nx] = true;
|
||||
queue.Enqueue((nx, ny));
|
||||
}
|
||||
}
|
||||
}
|
||||
blobs.Add(currentBlob);
|
||||
Console.WriteLine($" 发现 Blob {currentBlob.Label}: 面积={currentBlob.Area}, 边界=({currentBlob.MinX},{currentBlob.MinY})-({currentBlob.MaxX},{currentBlob.MaxY})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"分割结果:");
|
||||
Console.WriteLine($" 像素超过阈值: {pixelsAboveThreshold}");
|
||||
if (found)
|
||||
Console.WriteLine($"发现 {blobs.Count} 个 Blobs");
|
||||
|
||||
// 2. 过滤 Blobs
|
||||
List<Blob> filteredBlobs = new List<Blob>();
|
||||
int minArea = 10; // 最小面积阈值 (过滤噪声)
|
||||
int maxArea = (int)(width * height * 0.25); // 最大面积阈值 (过滤背景或巨大干扰, 25%)
|
||||
const double SMOKE_COVERAGE_THRESHOLD = 0.75; // 烟幕覆盖率阈值
|
||||
|
||||
foreach (var blob in blobs)
|
||||
{
|
||||
Console.WriteLine($" 目标边界: ({minX},{minY}) 到 ({maxX},{maxY})");
|
||||
Console.WriteLine($" 目标尺寸: {maxX - minX + 1}x{maxY - minY + 1} 像素");
|
||||
// 检查面积
|
||||
if (blob.Area < minArea || blob.Area > maxArea)
|
||||
{
|
||||
Console.WriteLine($" Blob {blob.Label} 因面积 ({blob.Area}) 不符被过滤 (允许范围: {minArea}-{maxArea})");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查烟幕覆盖率 (使用 Blob 边界框)
|
||||
int smokeCoveredPixelsInBox = 0;
|
||||
int totalPixelsInBox = 0; // 只统计边界框内高于阈值的像素
|
||||
bool[,] smokeMask = image.SmokeCoverageMask;
|
||||
for (int by = blob.MinY; by <= blob.MaxY; by++)
|
||||
{
|
||||
for (int bx = blob.MinX; bx <= blob.MaxX; bx++)
|
||||
{
|
||||
// 检查像素是否属于该 Blob (近似检查:在边界框内且高于阈值)
|
||||
if (image.GetIntensity(by, bx) > threshold)
|
||||
{
|
||||
totalPixelsInBox++;
|
||||
if (smokeMask[by, bx])
|
||||
{
|
||||
smokeCoveredPixelsInBox++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (totalPixelsInBox > 0)
|
||||
{
|
||||
double smokeCoverageRatio = (double)smokeCoveredPixelsInBox / totalPixelsInBox;
|
||||
Console.WriteLine($" Blob {blob.Label} 烟幕覆盖率: {smokeCoverageRatio:P2} ({smokeCoveredPixelsInBox}/{totalPixelsInBox})");
|
||||
if (smokeCoverageRatio >= SMOKE_COVERAGE_THRESHOLD)
|
||||
{
|
||||
Console.WriteLine($" Blob {blob.Label} 因烟幕覆盖率过高被过滤");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 通过所有检查,添加到过滤后的列表
|
||||
filteredBlobs.Add(blob);
|
||||
Console.WriteLine($" Blob {blob.Label} 通过过滤");
|
||||
}
|
||||
|
||||
if (!found)
|
||||
// 3. 选择最佳 Blob
|
||||
if (filteredBlobs.Count == 0)
|
||||
{
|
||||
return new ImageSegment();
|
||||
Console.WriteLine("没有找到有效的 Blob");
|
||||
return new ImageSegment(); // 返回无效分割
|
||||
}
|
||||
|
||||
// 选择面积最大的 Blob 作为目标
|
||||
Blob targetBlob = filteredBlobs.OrderByDescending(b => b.Area).First();
|
||||
Console.WriteLine($"选择 Blob {targetBlob.Label} 作为目标 (面积: {targetBlob.Area})");
|
||||
|
||||
// 4. 返回基于选定 Blob 的 ImageSegment
|
||||
return new ImageSegment(
|
||||
isValid: true,
|
||||
center: ((minX + maxX) / 2, (minY + maxY) / 2),
|
||||
size: (maxX - minX + 1, maxY - minY + 1)
|
||||
center: targetBlob.Center,
|
||||
size: (targetBlob.Width, targetBlob.Height)
|
||||
);
|
||||
}
|
||||
|
||||
@ -212,31 +302,44 @@ namespace ThreatSource.Guidance
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
double value = image.GetIntensity(y, x);
|
||||
maxIntensity = Math.Max(maxIntensity, value);
|
||||
minIntensity = Math.Min(minIntensity, value);
|
||||
sum += value;
|
||||
count++;
|
||||
// Skip completely black pixels if necessary, or handle potential division by zero later
|
||||
if (value > 0) // Avoid issues if minIntensity remains double.MaxValue
|
||||
{
|
||||
maxIntensity = Math.Max(maxIntensity, value);
|
||||
minIntensity = Math.Min(minIntensity, value);
|
||||
sum += value;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle case where image is entirely black or has no valid pixels
|
||||
if (count == 0 || maxIntensity <= minIntensity)
|
||||
{
|
||||
Console.WriteLine("图像全黑或无有效强度信息,无法计算阈值,返回0");
|
||||
return 0; // Or handle appropriately
|
||||
}
|
||||
|
||||
|
||||
double mean = sum / count;
|
||||
|
||||
// 计算背景基准阈值
|
||||
double backgroundThreshold = BACKGROUND_INTENSITY * 1.2;
|
||||
|
||||
// 计算目标基准阈值
|
||||
double targetThreshold = maxIntensity * 0.2;
|
||||
|
||||
// 取两个基准中的较大值作为最终阈值
|
||||
double threshold = Math.Max(backgroundThreshold, targetThreshold);
|
||||
|
||||
// --- 修改为基于动态范围的阈值计算 ---
|
||||
double thresholdFactor = 0.001; // 基于动态范围的因子 (初始值设为0.15, 从 0.05 改为 0.01)
|
||||
double dynamicRange = maxIntensity - minIntensity;
|
||||
double threshold = minIntensity + dynamicRange * thresholdFactor;
|
||||
|
||||
// 添加一个小的偏移量,以确保阈值严格大于最小强度,避免将所有背景像素包含进来
|
||||
// double epsilon = 1e-9;
|
||||
// threshold = Math.Max(threshold, minIntensity + epsilon); // 确保阈值至少比最小值大一点点
|
||||
|
||||
Console.WriteLine($"图像统计:");
|
||||
Console.WriteLine($" 最大强度: {maxIntensity:F6}");
|
||||
Console.WriteLine($" 最小强度: {minIntensity:F6}");
|
||||
Console.WriteLine($" 平均强度: {mean:F6}");
|
||||
Console.WriteLine($" 背景阈值: {backgroundThreshold:F6}");
|
||||
Console.WriteLine($" 目标阈值: {targetThreshold:F6}");
|
||||
Console.WriteLine($" 动态范围: {dynamicRange:F6}");
|
||||
Console.WriteLine($" 动态阈值因子: {thresholdFactor:P1}");
|
||||
Console.WriteLine($" 最终阈值: {threshold:F6}");
|
||||
// --- 结束修改 ---
|
||||
|
||||
return threshold;
|
||||
}
|
||||
@ -309,28 +412,32 @@ namespace ThreatSource.Guidance
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算温度梯度特征
|
||||
/// 计算温度梯度特征 - **修正:强制基于图像计算**
|
||||
/// </summary>
|
||||
private double CalculateTemperatureGradient(InfraredImage image, ImageSegment segment, IEquipment target)
|
||||
{
|
||||
// 直接从目标获取温度分布数据
|
||||
double[,] thermalPattern = target.GetCurrentThermalPattern();
|
||||
if (thermalPattern == null)
|
||||
{
|
||||
Console.WriteLine("没有温度分布数据, 使用图像计算梯度");
|
||||
return CalculateImageBasedGradient(image, segment);
|
||||
}
|
||||
// -- 移除直接从目标获取理想温度分布数据的逻辑 --
|
||||
// double[,] thermalPattern = target.GetCurrentThermalPattern(); // 错误:这会绕过烟幕遮蔽效果
|
||||
// if (thermalPattern == null)
|
||||
// {
|
||||
// Console.WriteLine("没有温度分布数据, 使用图像计算梯度");
|
||||
// return CalculateImageBasedGradient(image, segment);
|
||||
// }
|
||||
//
|
||||
// var pattern = new ThermalPattern(thermalPattern, thermalPattern);
|
||||
//
|
||||
// // 判断目标是否在运动 (这个判断可能仍然有用,但基于图像的梯度计算目前不使用它)
|
||||
// bool isMoving = IsTargetMoving(image, segment);
|
||||
// Console.WriteLine($"目标运动状态: {(isMoving ? "移动" : "静止")}");
|
||||
//
|
||||
// // 使用理想温度分布模式计算梯度特征 (错误)
|
||||
// double gradientFeature = pattern.CalculateGradientFeature(isMoving);
|
||||
//
|
||||
// return gradientFeature;
|
||||
|
||||
var pattern = new ThermalPattern(thermalPattern, thermalPattern);
|
||||
|
||||
// 判断目标是否在运动
|
||||
bool isMoving = IsTargetMoving(image, segment);
|
||||
Console.WriteLine($"目标运动状态: {(isMoving ? "移动" : "静止")}");
|
||||
|
||||
// 使用温度分布模式计算梯度特征
|
||||
double gradientFeature = pattern.CalculateGradientFeature(isMoving);
|
||||
|
||||
return gradientFeature;
|
||||
// **修正:始终基于传入的红外图像计算梯度特征**
|
||||
Console.WriteLine("计算基于图像的温度梯度特征");
|
||||
return CalculateImageBasedGradient(image, segment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -428,14 +535,14 @@ namespace ThreatSource.Guidance
|
||||
// 计算中部区域的梯度特征
|
||||
double middleAreaGradient = avgGridGradients[1, 1];
|
||||
|
||||
// 计算梯度分布特征
|
||||
double gradientDistribution = engineAreaGradient / (frontAreaGradient + 0.1);
|
||||
double gradientContrast = engineAreaGradient / (middleAreaGradient + 0.1);
|
||||
// 计算梯度分布特征 (调整除零保护)
|
||||
double gradientDistribution = engineAreaGradient / (frontAreaGradient + 1e-6);
|
||||
double gradientContrast = engineAreaGradient / (middleAreaGradient + 1e-6);
|
||||
|
||||
// 最终得分:后部梯度强度(0.3) + 梯度分布特征(0.4) + 梯度对比度(0.3)
|
||||
return engineAreaGradient * 0.3 +
|
||||
Math.Min(gradientDistribution, 3.0) / 3.0 * 0.4 +
|
||||
Math.Min(gradientContrast, 3.0) / 3.0 * 0.3;
|
||||
// 最终得分:调整权重 (engine*0.5 + dist*0.3 + contrast*0.2)
|
||||
return engineAreaGradient * 0.5 +
|
||||
Math.Min(gradientDistribution, 3.0) / 3.0 * 0.3 +
|
||||
Math.Min(gradientContrast, 3.0) / 3.0 * 0.2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -525,20 +632,21 @@ namespace ThreatSource.Guidance
|
||||
histogram[i] /= totalPixels;
|
||||
}
|
||||
|
||||
// 计算集中度得分:高强度bin的占比越高,得分越高
|
||||
// 计算集中度得分:改为计算最高 30% bin 的占比
|
||||
double concentrationScore = 0;
|
||||
for (int i = 0; i < BINS; i++)
|
||||
int topBinsStart = (int)(BINS * 0.7); // Start from the 70th percentile bin (e.g., index 7 for BINS=10)
|
||||
for (int i = topBinsStart; i < BINS; i++)
|
||||
{
|
||||
// 使用指数权重,使高强度bin的影响更大
|
||||
double weight = Math.Pow(2, i) / Math.Pow(2, BINS-1); // 指数增长的权重
|
||||
concentrationScore += histogram[i] * weight;
|
||||
concentrationScore += histogram[i];
|
||||
}
|
||||
|
||||
// 计算中心区域相对强度比
|
||||
double centerIntensityRatio = avgCenterIntensity / avgTotalIntensity;
|
||||
double centerIntensityRatio = avgTotalIntensity > 0 ? avgCenterIntensity / avgTotalIntensity : 0; // Avoid division by zero
|
||||
|
||||
// 最终得分:集中度得分(0.6)和中心强度比(0.4)的加权和
|
||||
return concentrationScore * 0.6 + centerIntensityRatio * 0.4;
|
||||
// 同时确保最终结果在 [0, 1] 范围内
|
||||
double finalScore = concentrationScore * 0.6 + centerIntensityRatio * 0.4;
|
||||
return Math.Max(0, Math.Min(1, finalScore)); // Clamp result to [0, 1]
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -578,13 +686,15 @@ namespace ThreatSource.Guidance
|
||||
private double[] CalculateFeatureWeights(TargetFeature features)
|
||||
{
|
||||
// 使用固定权重,基于特征的重要性
|
||||
return new[]
|
||||
{
|
||||
0.3, // 长宽比权重
|
||||
0.2, // 相对尺寸权重
|
||||
0.25, // 强度模式权重
|
||||
0.25 // 温度梯度权重
|
||||
};
|
||||
// **重新平衡权重:降低梯度权重,提升形状和尺寸权重**
|
||||
return
|
||||
[
|
||||
0.30, // 长宽比权重 (原 0.15, 最初 0.3)
|
||||
0.25, // 相对尺寸权重 (原 0.15, 最初 0.2)
|
||||
0.15, // 强度模式权重 (原 0.20, 最初 0.25)
|
||||
0.30 // 温度梯度权重 (原 0.50, 最初 0.25)
|
||||
// 总和为 1.00
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -673,5 +783,21 @@ namespace ThreatSource.Guidance
|
||||
Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 新增:Blob 结构体,用于存储连通区域信息 ---
|
||||
private struct Blob
|
||||
{
|
||||
public int Label { get; set; }
|
||||
public List<(int x, int y)> Pixels { get; set; }
|
||||
public int MinX { get; set; }
|
||||
public int MinY { get; set; }
|
||||
public int MaxX { get; set; }
|
||||
public int MaxY { get; set; }
|
||||
public int Area => Pixels?.Count ?? 0;
|
||||
public int Width => MaxX - MinX + 1;
|
||||
public int Height => MaxY - MinY + 1;
|
||||
public (int X, int Y) Center => ((MinX + MaxX) / 2, (MinY + MaxY) / 2);
|
||||
}
|
||||
// --- 结束新增 ---
|
||||
}
|
||||
}
|
||||
@ -123,7 +123,7 @@ namespace ThreatSource.Simulation
|
||||
/// <summary>
|
||||
/// 获取当前天气系统
|
||||
/// </summary>
|
||||
Weather? CurrentWeather { get; }
|
||||
Weather CurrentWeather { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前天气
|
||||
|
||||
@ -592,6 +592,11 @@ namespace ThreatSource.Simulation
|
||||
/// </remarks>
|
||||
public double JammingResistanceThreshold { get; set; } = 1e-3;
|
||||
|
||||
/// <summary>
|
||||
/// 波长,单位:微米
|
||||
/// </summary>
|
||||
public double WaveLength { get; set; } = 8.0;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化红外成像导引配置的新实例
|
||||
/// </summary>
|
||||
@ -1621,6 +1626,14 @@ namespace ThreatSource.Simulation
|
||||
/// </remarks>
|
||||
public SmokeScreenType SmokeType { get; set; } = SmokeScreenType.Cloud;
|
||||
|
||||
/// <summary>
|
||||
/// 是否是遮蔽型烟幕
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 遮蔽型烟幕会使用固定高强度值 (W/sr)
|
||||
/// </remarks>
|
||||
public bool IsObscuring { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 烟幕浓度,单位:g/m³
|
||||
/// </summary>
|
||||
@ -1630,6 +1643,15 @@ namespace ThreatSource.Simulation
|
||||
/// </remarks>
|
||||
public double Concentration { get; set; } = 0.2;
|
||||
|
||||
/// <summary>
|
||||
/// 辐射温度,单位:K
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 烟幕的辐射温度,影响其热辐射特性
|
||||
/// 标准辐射温度范围:200-300 K
|
||||
/// </remarks>
|
||||
public double RadiationTemperature { get; set; } = 273.15;
|
||||
|
||||
/// <summary>
|
||||
/// 烟幕持续时间,单位:秒
|
||||
/// </summary>
|
||||
|
||||
@ -125,7 +125,10 @@ namespace ThreatSource.Simulation
|
||||
/// </remarks>
|
||||
public virtual void Activate()
|
||||
{
|
||||
IsActive = true;
|
||||
if (!IsActive)
|
||||
{
|
||||
IsActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -139,7 +142,10 @@ namespace ThreatSource.Simulation
|
||||
/// </remarks>
|
||||
public virtual void Deactivate()
|
||||
{
|
||||
IsActive = false;
|
||||
if (IsActive)
|
||||
{
|
||||
IsActive = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,13 +72,14 @@ namespace ThreatSource.Simulation
|
||||
|
||||
/// <summary>
|
||||
/// 当前天气系统
|
||||
/// 默认为无风晴朗天气
|
||||
/// </summary>
|
||||
private Weather? _currentWeather;
|
||||
private Weather _currentWeather = new(WeatherType.Clear, 25, 50, 23, 0, 415, 1013, 0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前天气系统
|
||||
/// </summary>
|
||||
public Weather? CurrentWeather => _currentWeather;
|
||||
public Weather CurrentWeather => _currentWeather;
|
||||
|
||||
/// <summary>
|
||||
/// 启动仿真系统
|
||||
|
||||
@ -136,7 +136,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(100, 5, 0),
|
||||
Position = new Vector3D(50, 5, 0),
|
||||
Orientation = Orientation.FromVector(Vector3D.UnitX),
|
||||
InitialSpeed = 0.0
|
||||
};
|
||||
@ -148,6 +148,24 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
jammers[smokeGrenadeId] = smokeGrenade as BaseJammer;
|
||||
Console.WriteLine($"注册烟幕弹 {smokeGrenadeId}");
|
||||
}
|
||||
|
||||
smokeGrenadeId = "SG_2";
|
||||
smokeGrenade = _threatSourceFactory.CreateJammer(smokeGrenadeId, "infrared", motionParameters, "Tank_1");
|
||||
if (smokeGrenade != null)
|
||||
{
|
||||
simulationManager.RegisterEntity(smokeGrenadeId, smokeGrenade);
|
||||
jammers[smokeGrenadeId] = smokeGrenade as BaseJammer;
|
||||
Console.WriteLine($"注册烟幕弹 {smokeGrenadeId}");
|
||||
}
|
||||
|
||||
smokeGrenadeId = "SG_3";
|
||||
smokeGrenade = _threatSourceFactory.CreateJammer(smokeGrenadeId, "top", motionParameters, "Tank_1");
|
||||
if (smokeGrenade != null)
|
||||
{
|
||||
simulationManager.RegisterEntity(smokeGrenadeId, smokeGrenade);
|
||||
jammers[smokeGrenadeId] = smokeGrenade as BaseJammer;
|
||||
Console.WriteLine($"注册烟幕弹 {smokeGrenadeId}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -157,7 +175,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2000, 1, 0),
|
||||
Position = new Vector3D(2000, 1, 20),
|
||||
Orientation = new Orientation(Math.PI, 0.01, 0),
|
||||
InitialSpeed = 700
|
||||
};
|
||||
@ -175,8 +193,8 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2000, 10, 100),
|
||||
Orientation = new Orientation(Math.PI, 0.0, 0.0),
|
||||
Position = new Vector3D(2000, 1, 20),
|
||||
Orientation = new Orientation(Math.PI, 0.01, 0.0),
|
||||
InitialSpeed = 300
|
||||
};
|
||||
string missileId = "LBRM_1";
|
||||
@ -193,7 +211,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(3000, 0, 100),
|
||||
Position = new Vector3D(3000, 0, 20),
|
||||
Orientation = new Orientation(Math.PI, 0, 0),
|
||||
InitialSpeed = 800
|
||||
};
|
||||
@ -213,8 +231,8 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2000, 10, 100),
|
||||
Orientation = new Orientation(Math.PI, 0.0, 0),
|
||||
Position = new Vector3D(2000, 1, 20),
|
||||
Orientation = new Orientation(Math.PI, 0.01, 0),
|
||||
InitialSpeed = 300
|
||||
};
|
||||
|
||||
@ -232,8 +250,8 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2000, 20, 50),
|
||||
Orientation = new Orientation(Math.PI, 0.0, 0),
|
||||
Position = new Vector3D(2000, 1, 0),
|
||||
Orientation = new Orientation(Math.PI, 0.02, 0),
|
||||
InitialSpeed = 300
|
||||
};
|
||||
string missileId = "ITGM_1";
|
||||
@ -250,8 +268,8 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
{
|
||||
var motionParameters = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(3000, 20, 100),
|
||||
Orientation = new Orientation(Math.PI, 0.0, 0),
|
||||
Position = new Vector3D(3000, 1, 20),
|
||||
Orientation = new Orientation(Math.PI, 0.01, 0),
|
||||
InitialSpeed = 300
|
||||
};
|
||||
string missileId = "MMWG_1";
|
||||
@ -270,8 +288,8 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
string laserDesignatorId = "LD_1";
|
||||
var laserDesignatorLaunchParams = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2100, 10, 100),
|
||||
Orientation = new Orientation(Math.PI, 0, 0),
|
||||
Position = new Vector3D(2100, 1, 100),
|
||||
Orientation = new Orientation(Math.PI, 0.01, 0),
|
||||
InitialSpeed = 0
|
||||
};
|
||||
var laserDesignator = _threatSourceFactory.CreateIndicator(laserDesignatorId, "ld_001", "Tank_1", "LSGM_1", laserDesignatorLaunchParams);
|
||||
@ -283,7 +301,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
string laserBeamRiderId = "LBR_1";
|
||||
var laserBeamRiderLaunchParams = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2100, 10, 100),
|
||||
Position = new Vector3D(2100, 1, 20),
|
||||
Orientation = new Orientation(Math.PI, 0, 0),
|
||||
InitialSpeed = 0
|
||||
};
|
||||
@ -296,7 +314,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
string infraredTrackerId = "IT_1";
|
||||
var infraredTrackerLaunchParams = new MotionParameters
|
||||
{
|
||||
Position = new Vector3D(2100, 10, 100),
|
||||
Position = new Vector3D(2100, 1, 100),
|
||||
Orientation = new Orientation(Math.PI, 0, 0),
|
||||
InitialSpeed = 0
|
||||
};
|
||||
@ -372,7 +390,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
// }
|
||||
if (jammers.ContainsKey("SG_1"))
|
||||
{
|
||||
jammers["SG_1"].Activate();
|
||||
//jammers["SG_1"].Activate();
|
||||
Console.WriteLine($"激活烟幕弹 {jammers["SG_1"].Id}");
|
||||
}
|
||||
break;
|
||||
@ -413,8 +431,8 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
switch (type)
|
||||
{
|
||||
case JammingType.SmokeScreen:
|
||||
jammingParams.JammerId = "SG_1";
|
||||
jammers["SG_1"].Activate();
|
||||
jammingParams.JammerId = "SG_2";
|
||||
jammers["SG_2"].Activate();
|
||||
break;
|
||||
case JammingType.Decoy:
|
||||
jammingParams.JammerId = "LDY_1";
|
||||
|
||||
@ -174,7 +174,7 @@ namespace ThreatSource.Tools.MissileSimulation
|
||||
(JammingType.Infrared, "红外干扰"),
|
||||
(JammingType.MillimeterWave, "毫米波干扰"),
|
||||
(JammingType.Laser, "激光干扰"),
|
||||
(JammingType.SmokeScreen, "烟幕干扰"),
|
||||
(JammingType.SmokeScreen, "烟幕弹"),
|
||||
(JammingType.Decoy, "假目标干扰")
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user