From d3feaa7fc0d7f06c9d99bb7a21ff795e20469a4f Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Wed, 10 Sep 2025 02:55:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=89=E5=85=A8=E4=BC=98?= =?UTF-8?q?=E5=85=88=E8=B7=AF=E5=BE=84=E7=AE=97=E6=B3=95=EF=BC=88=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E7=89=88=EF=BC=8C=E5=8F=AA=E8=AE=A1=E7=AE=97=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E8=B7=9D=E7=A6=BB=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/requirement/todo_features.md | 10 + src/Core/PathPlanningModels.cs | 7 +- src/PathPlanning/AutoPathFinder.cs | 258 +++++++++--------- src/UI/WPF/ViewModels/PathEditingViewModel.cs | 6 + 4 files changed, 148 insertions(+), 133 deletions(-) diff --git a/doc/requirement/todo_features.md b/doc/requirement/todo_features.md index e6a0d89..8a06cb9 100644 --- a/doc/requirement/todo_features.md +++ b/doc/requirement/todo_features.md @@ -2,6 +2,16 @@ ## 功能点 +### [2025/09/09] + +1. [ ] (功能)增加安全优先路径策略 + +### [2025/09/08] + +1. [x] (功能)增加局部直线优先路径策略 +2. [x] (性能优化) 提高网格地图创建性能(优化API的使用) +3. [x] (提高稳定性) 通过idle改进UI事件管理,把反射改成事件通知 + ### [2025/09/07] 1. [ ] (功能)增加插件系统参数配置和管理 diff --git a/src/Core/PathPlanningModels.cs b/src/Core/PathPlanningModels.cs index 602ae09..f97949b 100644 --- a/src/Core/PathPlanningModels.cs +++ b/src/Core/PathPlanningModels.cs @@ -140,7 +140,12 @@ namespace NavisworksTransport /// /// 直线优先路径 - 优先选择主方向的路径,减少转弯次数 /// - Straightest = 1 + Straightest = 1, + + /// + /// 安全优先路径 - 基于障碍物距离选择安全路径,适合大型车辆居中行驶 + /// + SafestCenter = 2 } /// diff --git a/src/PathPlanning/AutoPathFinder.cs b/src/PathPlanning/AutoPathFinder.cs index a2f4679..91b133b 100644 --- a/src/PathPlanning/AutoPathFinder.cs +++ b/src/PathPlanning/AutoPathFinder.cs @@ -1364,31 +1364,141 @@ namespace NavisworksTransport.PathPlanning /// /// 安全优先的A*网格转换方法 - /// 先创建标准网格,再根据障碍物距离调整速度权重 + /// 🔥 重写:参照局部直线优先算法的成功模式,重新构建网格连接 /// private Grid ConvertToAStarGridSafestCenter(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight) { try { - LogManager.Info($"[安全优先] 开始实施安全优先算法"); + LogManager.Info($"[安全优先] 开始实施安全优先算法,参照成功的直线优先模式"); // 清空日志记录集合,确保每次执行都能看到映射关系 _loggedDistances.Clear(); - // 1. 使用现有方法创建标准A*网格(完全不变) - var grid = ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight); + // 1. 创建网格基础结构(参照局部直线优先) + double cellSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize); + var gridSize = new GridSize(gridMap.Width, gridMap.Height); + var cellSize = new Size(Distance.FromMeters((float)cellSizeInMeters), Distance.FromMeters((float)cellSizeInMeters)); - LogManager.Info($"[安全优先] 标准A*网格创建完成"); + // 使用基础速度创建网格,后续会动态调整 + var baseVelocity = Velocity.FromKilometersPerHour(5); // 基础速度 + var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, baseVelocity); - // 2. 计算安全距离图(复用GridMapGenerator的距离变换算法) + LogManager.Info($"[安全优先] A*网格创建完成,尺寸: {gridMap.Width}x{gridMap.Height}"); + + // 2. 🔥 关键步骤:断开所有连接,准备重新按安全距离连接 + for (int x = 0; x < gridMap.Width; x++) + { + for (int y = 0; y < gridMap.Height; y++) + { + grid.DisconnectNode(new GridPosition(x, y)); + } + } + LogManager.Info($"[安全优先] 所有节点连接已断开,准备重新构建"); + + // 3. 计算安全距离图 var safetyDistanceMap = CalculateSafetyDistanceFromGridMap(gridMap); - LogManager.Info($"[安全优先] 安全距离计算完成"); - // 3. 根据安全距离调整边的速度权重 - ApplySafetyVelocityWeights(grid, gridMap, safetyDistanceMap); + // 4. 智能重连:基于安全距离重新构建连接 + int connectedCells = 0; + int heightConstrainedCells = 0; + int safetyConnections = 0; + var velocityStats = new Dictionary(); - LogManager.Info($"[安全优先] 速度权重调整完成"); + for (int x = 0; x < gridMap.Width; x++) + { + for (int y = 0; y < gridMap.Height; y++) + { + var cell = gridMap.Cells[x, y]; + + // 检查基本可通行性和高度约束 + bool isPassable = cell.IsWalkable; + if (isPassable && cell.PassableHeights != null && cell.PassableHeights.Any()) + { + bool heightOk = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); + if (!heightOk) + { + heightConstrainedCells++; + isPassable = false; + } + } + + if (isPassable) + { + var pos = new GridPosition(x, y); + var currentSafetyDistance = safetyDistanceMap[x, y]; + connectedCells++; + + // 🔥 核心改动:右方向连接(基于安全距离) + if (x + 1 < gridMap.Width) + { + var rightCell = gridMap.Cells[x + 1, y]; + if (IsPassableWithHeight(rightCell, vehicleHeight)) + { + var rightPos = new GridPosition(x + 1, y); + var rightSafetyDistance = safetyDistanceMap[x + 1, y]; + + // 使用两个位置中更安全的距离 + var maxSafetyDistance = Math.Max(currentSafetyDistance, rightSafetyDistance); + var speedValue = CalculateSpeedBySafetyDistance(maxSafetyDistance); + var rightVelocity = Velocity.FromKilometersPerHour(speedValue); + + // 统计速度分布 + if (velocityStats.ContainsKey(speedValue)) + velocityStats[speedValue]++; + else + velocityStats[speedValue] = 1; + + grid.AddEdge(pos, rightPos, rightVelocity); + grid.AddEdge(rightPos, pos, rightVelocity); + safetyConnections++; + } + } + + // 🔥 核心改动:下方向连接(基于安全距离) + if (y + 1 < gridMap.Height) + { + var bottomCell = gridMap.Cells[x, y + 1]; + if (IsPassableWithHeight(bottomCell, vehicleHeight)) + { + var bottomPos = new GridPosition(x, y + 1); + var bottomSafetyDistance = safetyDistanceMap[x, y + 1]; + + // 使用两个位置中更安全的距离 + var maxSafetyDistance = Math.Max(currentSafetyDistance, bottomSafetyDistance); + var speedValue = CalculateSpeedBySafetyDistance(maxSafetyDistance); + var bottomVelocity = Velocity.FromKilometersPerHour(speedValue); + + // 统计速度分布 + if (velocityStats.ContainsKey(speedValue)) + velocityStats[speedValue]++; + else + velocityStats[speedValue] = 1; + + grid.AddEdge(pos, bottomPos, bottomVelocity); + grid.AddEdge(bottomPos, pos, bottomVelocity); + safetyConnections++; + } + } + } + } + } + + // 输出统计信息 + LogManager.Info($"[安全优先] 网格重建完成,连接统计:"); + LogManager.Info($"[安全优先] - 连接的节点: {connectedCells}"); + LogManager.Info($"[安全优先] - 被高度约束排除: {heightConstrainedCells}"); + LogManager.Info($"[安全优先] - 安全连接数: {safetyConnections}"); + LogManager.Info($"[安全优先] - A*可用率: {(double)connectedCells / (gridMap.Width * gridMap.Height) * 100:F1}%"); + + // 输出速度统计信息 + if (velocityStats.Count > 0) + { + var sortedVelocityStats = velocityStats.OrderBy(kv => kv.Key); + var velocityStatsText = string.Join(", ", sortedVelocityStats.Select(kv => $"{kv.Key:F1}km/h:{kv.Value}条边")); + LogManager.Info($"[安全优先速度统计] {velocityStatsText}"); + } return grid; } @@ -1546,124 +1656,6 @@ namespace NavisworksTransport.PathPlanning return distanceMap; } - /// - /// 根据安全距离调整A*网格边的速度 - /// 🔥 修复:确保每条边只被设置一次,且双向速度完全一致 - /// - private void ApplySafetyVelocityWeights(Grid grid, GridMap gridMap, int[,] safetyDistanceMap) - { - int adjustedEdges = 0; - var velocityStats = new Dictionary(); - var processedEdges = new HashSet(); // 防止重复处理同一条边 - - LogManager.Info($"[安全优先] 开始调整边的速度权重,网格尺寸: {gridMap.Width}x{gridMap.Height}"); - - // 🔥 核心修复:单向扫描策略,只处理向右和向下的边 - for (int x = 0; x < gridMap.Width; x++) - { - for (int y = 0; y < gridMap.Height; y++) - { - if (!gridMap.Cells[x, y].IsWalkable) continue; - - var currentPos = new GridPosition(x, y); - var currentSafetyDistance = safetyDistanceMap[x, y]; - - // 处理向右的边(水平边) - if (x + 1 < gridMap.Width && gridMap.Cells[x + 1, y].IsWalkable) - { - var rightPos = new GridPosition(x + 1, y); - var rightSafetyDistance = safetyDistanceMap[x + 1, y]; - - // 使用两个位置中更安全的距离(取最大值) - var maxSafetyDistance = Math.Max(currentSafetyDistance, rightSafetyDistance); - var speedValue = CalculateSpeedBySafetyDistance(maxSafetyDistance); - var velocity = Velocity.FromKilometersPerHour(speedValue); - - // 统计速度分布(只统计一次) - if (velocityStats.ContainsKey(speedValue)) - velocityStats[speedValue]++; - else - velocityStats[speedValue] = 1; - - // 生成边的唯一标识符 - var edgeKey = $"{x},{y}-{x + 1},{y}"; - - if (!processedEdges.Contains(edgeKey)) - { - processedEdges.Add(edgeKey); - - try - { - // 🔥 核心修复:删除旧边,添加新边,确保双向速度一致 - grid.RemoveEdge(currentPos, rightPos); - grid.RemoveEdge(rightPos, currentPos); - grid.AddEdge(currentPos, rightPos, velocity); - grid.AddEdge(rightPos, currentPos, velocity); - adjustedEdges++; - - LogManager.Debug($"[安全优先] 水平边({x},{y})↔({x + 1},{y}): 安全距离{currentSafetyDistance}vs{rightSafetyDistance} -> 最大值{maxSafetyDistance} -> 速度{speedValue:F1}km/h"); - } - catch (Exception ex) - { - LogManager.Warning($"[安全优先] 水平边更新失败: ({x},{y})↔({x + 1},{y}): {ex.Message}"); - } - } - } - - // 处理向下的边(垂直边) - if (y + 1 < gridMap.Height && gridMap.Cells[x, y + 1].IsWalkable) - { - var downPos = new GridPosition(x, y + 1); - var downSafetyDistance = safetyDistanceMap[x, y + 1]; - - // 使用两个位置中更安全的距离(取最大值) - var maxSafetyDistance = Math.Max(currentSafetyDistance, downSafetyDistance); - var speedValue = CalculateSpeedBySafetyDistance(maxSafetyDistance); - var velocity = Velocity.FromKilometersPerHour(speedValue); - - // 统计速度分布(只统计一次) - if (velocityStats.ContainsKey(speedValue)) - velocityStats[speedValue]++; - else - velocityStats[speedValue] = 1; - - // 生成边的唯一标识符 - var edgeKey = $"{x},{y}-{x},{y + 1}"; - - if (!processedEdges.Contains(edgeKey)) - { - processedEdges.Add(edgeKey); - - try - { - // 🔥 核心修复:删除旧边,添加新边,确保双向速度一致 - grid.RemoveEdge(currentPos, downPos); - grid.RemoveEdge(downPos, currentPos); - grid.AddEdge(currentPos, downPos, velocity); - grid.AddEdge(downPos, currentPos, velocity); - adjustedEdges++; - - LogManager.Debug($"[安全优先] 垂直边({x},{y})↔({x},{y + 1}): 安全距离{currentSafetyDistance}vs{downSafetyDistance} -> 最大值{maxSafetyDistance} -> 速度{speedValue:F1}km/h"); - } - catch (Exception ex) - { - LogManager.Warning($"[安全优先] 垂直边更新失败: ({x},{y})↔({x},{y + 1}): {ex.Message}"); - } - } - } - } - } - - // 输出速度统计信息 - if (velocityStats.Count > 0) - { - var sortedVelocityStats = velocityStats.OrderBy(kv => kv.Key); - var velocityStatsText = string.Join(", ", sortedVelocityStats.Select(kv => $"{kv.Key:F1}km/h:{kv.Value}条边")); - LogManager.Info($"[速度权重统计] {velocityStatsText}"); - } - - LogManager.Info($"[安全优先] 速度权重调整完成,共处理了 {adjustedEdges} 条边,已处理边集合大小: {processedEdges.Count}"); - } // 静态集合用于记录已打印的速度映射,避免重复日志 private static readonly HashSet _loggedDistances = new HashSet(); @@ -1675,15 +1667,17 @@ namespace NavisworksTransport.PathPlanning { float finalSpeed; - // 🔥 激进映射:极大的速度差异,强制A*选择安全路径 + // 🔧 温和映射:参考局部直线优先算法,使用5-35km/h范围,减少网格敏感性 if (safetyDistance >= 5) - finalSpeed = 100.0f; // 100 km/h(极高速度,强制选择安全区域) + finalSpeed = 35.0f; // 35 km/h(最高速度,安全区域) else if (safetyDistance >= 3) - finalSpeed = 50.0f; // 50 km/h + finalSpeed = 25.0f; // 25 km/h else if (safetyDistance >= 2) + finalSpeed = 15.0f; // 15 km/h + else if (safetyDistance >= 1) finalSpeed = 10.0f; // 10 km/h else - finalSpeed = 1.0f; // 1 km/h(极低速度,避免危险区域) + finalSpeed = 5.0f; // 5 km/h(最低速度,但不是极端值) // 只在每种距离第一次出现时记录速度映射 if (!_loggedDistances.Contains(safetyDistance)) diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index ebf8763..1ce6eea 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -520,6 +520,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels Value = PathStrategy.Straightest, DisplayName = "直线优先", Description = "优先选择主方向直线路径,减少转弯次数" + }, + new PathStrategyOption + { + Value = PathStrategy.SafestCenter, + DisplayName = "安全优先", + Description = "基于障碍物距离选择安全路径,适合大型车辆居中行驶" } };