From 6030b8de0727b6f29ac542c4ded5fc80799c5e8d Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Fri, 12 Jun 2026 15:46:05 +0800 Subject: [PATCH] =?UTF-8?q?RecommendMultiGroup:=20=E5=A4=9A=E7=BC=96?= =?UTF-8?q?=E9=98=9F=E7=81=AB=E5=8A=9B=E5=88=86=E9=85=8D=20+=20=E4=B8=93?= =?UTF-8?q?=E4=B8=80=E5=BC=B9=E8=8D=AF=E4=BC=98=E5=85=88=20+=20130?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=80=9A=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Algorithms/AlgorithmTypes.cs | 46 +++++++++++++- .../Algorithms/DefaultDefenseAdvisor.cs | 57 ++++++++++++++++++ .../Algorithms/IDefenseAdvisor.cs | 7 +++ .../CounterDrone.Core/CounterDrone.Core.dll | Bin 100864 -> 105472 bytes .../MultiGroupAdvisorTests.cs | 47 +++++++++++++++ 5 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 test/unit/CounterDrone.Core.Tests/MultiGroupAdvisorTests.cs diff --git a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs index 6d7beb2..daa7a74 100644 --- a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs +++ b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs @@ -33,8 +33,52 @@ namespace CounterDrone.Core.Algorithms public float InterceptProbability { get; set; } public string SummaryRationale { get; set; } = string.Empty; - /// 由推荐方案生成的发射计划,仿真器直接执行 public List FireSchedule { get; set; } = new(); + + /// 此方案占用的平台起始索引(多编队合并用) + public int PlatformOffset { get; set; } + } + + /// 多编队推荐结果 + public class MultiGroupRecommendation + { + /// 每组的独立方案 + public List GroupSolutions { get; set; } = new(); + /// 合并后的总发射计划 + public List MergedFireSchedule { get; set; } = new(); + } + + /// 火力单元(装备编组) + public class FireUnit + { + public string GroupId { get; set; } = string.Empty; + public AerosolType LoadedAmmo { get; set; } + public int PlatformCount { get; set; } + public float PositionX, PositionY, PositionZ; + public float MuzzleVelocity { get; set; } = 800f; + public float Cooldown { get; set; } = 5f; + public float AmmoChangeTime { get; set; } = 300f; + // 弹药库:可以装填的弹药类型 + public List AvailableAmmo { get; set; } = new(); + } + + /// 无人机编队(多编队推荐输入) + public class DroneGroup + { + public string GroupId { get; set; } = string.Empty; + public TargetConfig Target { get; set; } = new(); + public RoutePlan Route { get; set; } = new(); + public List Waypoints { get; set; } = new(); + + /// 预计到达航路中点的时间(秒) + public float GetArrivalTime() + { + if (Waypoints.Count < 2) return 0; + var start = new Vector3((float)Waypoints[0].PosX, (float)Waypoints[0].PosY, (float)Waypoints[0].PosZ); + var end = new Vector3((float)Waypoints[^1].PosX, (float)Waypoints[^1].PosY, (float)Waypoints[^1].PosZ); + var speed = (float)Target.TypicalSpeed / 3.6f; + return start.DistanceTo(end) / speed / 2f; + } } public class RecommendedPlatform diff --git a/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs b/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs index 93e5b3b..177234c 100644 --- a/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs +++ b/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs @@ -219,5 +219,62 @@ namespace CounterDrone.Core.Algorithms var sigma = (sY + sZ) / 2f; return rPhase2 + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / threshold)); } + + /// 多编队推荐 — 每组独立方案,按时间分配火力单元,合并发射计划 + public MultiGroupRecommendation RecommendMultiGroup( + List droneGroups, List fireUnits, CombatScene environment) + { + var result = new MultiGroupRecommendation(); + if (droneGroups.Count == 0 || fireUnits.Count == 0) return result; + + var sorted = droneGroups.OrderBy(g => g.GetArrivalTime()).ToList(); + var usedUntil = new Dictionary(); + int offset = 0; + + foreach (var group in sorted) + { + var powerType = (PowerType)group.Target.PowerType; + var neededAmmo = MatchTable.GetValueOrDefault(powerType, AerosolType.InertGas); + + FireUnit assigned = null; + // 优先匹配专一弹药的火力单元,避免多用途单元被抢占 + var candidates = fireUnits + .Where(u => u.AvailableAmmo.Contains(neededAmmo)) + .OrderBy(u => u.AvailableAmmo.Count) // 少弹种的优先 + .ThenBy(u => usedUntil.ContainsKey(u.GroupId) ? 1 : 0); // 不忙的优先 + + foreach (var unit in candidates) + { + if (!unit.AvailableAmmo.Contains(neededAmmo)) continue; + if (!usedUntil.TryGetValue(unit.GroupId, out var busyUntil)) + { assigned = unit; break; } + if (unit.LoadedAmmo == neededAmmo && busyUntil <= group.GetArrivalTime()) + { assigned = unit; break; } + if (busyUntil + unit.AmmoChangeTime <= group.GetArrivalTime()) + { assigned = unit; break; } + } + if (assigned == null) continue; + + var threat = new ThreatProfile + { + Environment = environment, + Targets = new List { group.Target }, + Route = group.Route, + Waypoints = group.Waypoints, + }; + var sol = Recommend(threat).Best; + foreach (var fe in sol.FireSchedule) fe.PlatformIndex += offset; + + result.GroupSolutions.Add(sol); + result.MergedFireSchedule.AddRange(sol.FireSchedule); + + assigned.LoadedAmmo = sol.RecommendedAerosolType; + usedUntil[assigned.GroupId] = (sol.FireSchedule.Count > 0 + ? sol.FireSchedule.Max(f => f.FireTime) : group.GetArrivalTime()) + assigned.Cooldown; + offset += sol.Platforms.Count; + } + + return result; + } } } diff --git a/src/CounterDrone.Core/Algorithms/IDefenseAdvisor.cs b/src/CounterDrone.Core/Algorithms/IDefenseAdvisor.cs index 7e31ed4..5ed3aab 100644 --- a/src/CounterDrone.Core/Algorithms/IDefenseAdvisor.cs +++ b/src/CounterDrone.Core/Algorithms/IDefenseAdvisor.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using CounterDrone.Core.Models; namespace CounterDrone.Core.Algorithms @@ -6,5 +7,11 @@ namespace CounterDrone.Core.Algorithms public interface IDefenseAdvisor { DefenseRecommendation Recommend(ThreatProfile threat); + + /// 多编队推荐 — 为每组分配火力单元,合并发射计划 + MultiGroupRecommendation RecommendMultiGroup( + List droneGroups, + List fireUnits, + CombatScene environment); } } diff --git a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll index fef60979894b1099341d4558cfa85f400530e0c5..8bf3c8d3f7fa2808e3f6cfe9f2a8d0e7d1a44d88 100644 GIT binary patch delta 45139 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 diff --git a/test/unit/CounterDrone.Core.Tests/MultiGroupAdvisorTests.cs b/test/unit/CounterDrone.Core.Tests/MultiGroupAdvisorTests.cs new file mode 100644 index 0000000..0d78a77 --- /dev/null +++ b/test/unit/CounterDrone.Core.Tests/MultiGroupAdvisorTests.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using CounterDrone.Core.Algorithms; +using CounterDrone.Core.Models; +using Xunit; + +namespace CounterDrone.Core.Tests +{ + public class MultiGroupAdvisorTests + { + [Fact] + public void TwoSeparateUnits_TwoGroups_BothGetSolutions() + { + var ammoCatalog = DefaultAmmunition.GetAll(); + Console.WriteLine($"Ammo: inert={ammoCatalog[0].BurstChargeKg}kg, active={ammoCatalog[1].BurstChargeKg}kg"); + + var advisor = new DefaultDefenseAdvisor(ammoCatalog); + + var g1 = new DroneGroup + { + GroupId = "g1", Target = new TargetConfig { PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500 }, + Route = new RoutePlan { FormationMode = (int)FormationMode.Single }, + Waypoints = new List { new() { PosX = 0, PosY = 500, PosZ = 0 }, new() { PosX = 20000, PosY = 500, PosZ = 0 } }, + }; + var g2 = new DroneGroup + { + GroupId = "g2", Target = new TargetConfig { PowerType = (int)PowerType.Jet, Quantity = 1, TypicalSpeed = 500, TypicalAltitude = 800 }, + Route = new RoutePlan { FormationMode = (int)FormationMode.Single }, + Waypoints = new List { new() { PosX = 0, PosY = 800, PosZ = 0 }, new() { PosX = 30000, PosY = 800, PosZ = 0 } }, + }; + + var u1 = new FireUnit { GroupId = "u1", AvailableAmmo = new() { AerosolType.InertGas, AerosolType.ActiveMaterial }, PlatformCount = 5, MuzzleVelocity = 800 }; + var u2 = new FireUnit { GroupId = "u2", AvailableAmmo = new() { AerosolType.ActiveMaterial }, PlatformCount = 4, MuzzleVelocity = 800 }; + + var rec = advisor.RecommendMultiGroup( + new List { g1, g2 }, + new List { u1, u2 }, + new CombatScene { WindSpeed = 3 }); + + var msg = $"Solutions={rec.GroupSolutions.Count}, Merged={rec.MergedFireSchedule.Count}"; + foreach (var s in rec.GroupSolutions) + msg += $" | {s.RecommendedAerosolType}: plat={s.Platforms.Count} fire={s.FireSchedule.Count}"; + Assert.True(rec.GroupSolutions.Count == 2, msg); + Assert.NotEmpty(rec.MergedFireSchedule); + } + } +}