From fb8d52398b545f30d58a96938475b8a5155d41b2 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Sat, 6 Sep 2025 20:47:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E5=AF=BB=E8=B7=AF?= =?UTF-8?q?=E7=AE=97=E6=B3=95=E7=A9=BF=E6=B4=9E=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/design/2026/GridCellTypeDesign.md | 17 +- src/Core/PathPlanningManager.cs | 8 +- .../Properties/CategoryAttributeManager.cs | 58 ++++- src/PathPlanning/AutoPathFinder.cs | 82 +++++- src/PathPlanning/GridMap.cs | 242 ++++++++++++------ src/PathPlanning/GridMapGenerator.cs | 30 +-- 6 files changed, 321 insertions(+), 116 deletions(-) diff --git a/doc/design/2026/GridCellTypeDesign.md b/doc/design/2026/GridCellTypeDesign.md index eae51ed..4bb518d 100644 --- a/doc/design/2026/GridCellTypeDesign.md +++ b/doc/design/2026/GridCellTypeDesign.md @@ -7,9 +7,11 @@ ## 问题背景 ### 当前问题 + 在当前的自动寻路系统中,发现了路径会穿越空洞(没有几何覆盖的区域)的问题。从3D可视化中可以看到,黄色的寻路路径直接穿过了黑色的空洞区域,而不是沿着绿色的通行网格前进。 ### 根本原因分析 + 通过代码分析,发现问题出现在网格初始化和A*算法转换的逻辑中: 1. **网格初始化问题** (`GridMap.InitializeCells`) @@ -29,6 +31,7 @@ ### 双属性设计理念 每个网格单元包含两个独立的属性: + 1. **可通行性** (`IsWalkable`) - 布尔值,决定是否能够通行 2. **物流类型** (`LogisticsType`) - 枚举值,决定通行成本和特殊处理 @@ -200,13 +203,15 @@ private Grid ConvertToAStarGrid(GridMap gridMap) ## 使用场景示例 ### 场景1:最简单 - 只有楼板 + - **输入**: 只标记楼板几何,无物流属性 -- **结果**: +- **结果**: - 楼板区域 → `Floor` + `IsWalkable=true` (权重1.0) - 其他区域 → `Unknown` + `IsWalkable=false` (不可通行) - **路径**: 在楼板范围内找几何最短路径 ### 场景2:楼板 + 通道 + - **输入**: 标记楼板几何 + 部分区域标记为通道 - **结果**: - 通道区域 → `Channel` + `IsWalkable=true` (权重0.5) @@ -215,6 +220,7 @@ private Grid ConvertToAStarGrid(GridMap gridMap) - **路径**: 优先走通道,必要时走普通楼板,绝不穿越空洞 ### 场景3:复杂场景 + - **输入**: 楼板 + 通道 + 门 + 楼梯 + 障碍物等混合 - **结果**: 各种类型有不同的权重 - **路径**: A*算法综合考虑距离和成本找最优路径 @@ -222,38 +228,45 @@ private Grid ConvertToAStarGrid(GridMap gridMap) ## 关键改进点 ### 1. 解决空洞穿越问题 + - **根本解决**: `Unknown` 类型 + `IsWalkable=false` 确保空洞区域不可通行 - **可视化一致**: 黑色空洞区域在算法中也确实不可通行 ### 2. 逻辑清晰 + - **职责分离**: 通行性和类型分开处理,逻辑更清晰 - **易于调试**: 可以分别检查通行性和类型,快速定位问题 ### 3. 灵活扩展 + - **新类型**: 轻松添加新的物流类型和权重 - **特殊场景**: 支持"不可通行的通道"(施工中)等特殊组合 ### 4. 向后兼容 + - **现有工作流**: 继续支持只设置通道的工作方式 - **渐进迁移**: 可以逐步引入更复杂的几何和属性设置 ## 实施优先级 ### 第一阶段:修复空洞问题 + 1. 修改 `GridMap.InitializeCells()` - 默认类型改为 `Unknown` 2. 修改 `AutoPathFinder.ConvertToAStarGridWith2_5D()` - 基于 `IsWalkable` 判断 3. 测试验证空洞不再可穿越 ### 第二阶段:完善类型系统 + 1. 实现完整的 `LogisticsType` 枚举 2. 实现权重计算系统 3. 支持楼板几何投影 ### 第三阶段:扩展功能 + 1. 支持更多物流属性类型 2. 实现复杂场景的自动分类 3. 优化性能和内存使用 ## 总结 -这个双属性设计方案既解决了当前的空洞穿越问题,又为未来的功能扩展奠定了坚实基础。通过清晰的职责分离和灵活的类型系统,系统能够支持从简单楼板到复杂物流场景的各种需求。 \ No newline at end of file +这个双属性设计方案既解决了当前的空洞穿越问题,又为未来的功能扩展奠定了坚实基础。通过清晰的职责分离和灵活的类型系统,系统能够支持从简单楼板到复杂物流场景的各种需求。 diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs index 2d58214..c15977e 100644 --- a/src/Core/PathPlanningManager.cs +++ b/src/Core/PathPlanningManager.cs @@ -3141,25 +3141,25 @@ namespace NavisworksTransport string gridTypeName = ""; // 根据网格类型确定目标路径和名称 - if (cell.IsWalkable && cell.CellType == ElementType.Channel) + if (cell.IsWalkable && cell.CellType == CategoryAttributeManager.LogisticsElementType.通道) { targetRoute = channelRoute; gridTypeName = "通道"; channelCells++; } - else if (cell.IsWalkable && cell.CellType == ElementType.OpenSpace) + else if (cell.IsWalkable && cell.CellType == CategoryAttributeManager.LogisticsElementType.楼板) { targetRoute = channelRoute; // 开放空间也用绿色显示 gridTypeName = "开放"; openSpaceCells++; } - else if (cell.CellType == ElementType.Unknown) + else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown) { targetRoute = unknownRoute; gridTypeName = "空洞"; unknownCells++; } - else if (cell.CellType == ElementType.Obstacle) + else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.障碍物) { targetRoute = obstacleRoute; gridTypeName = "障碍"; diff --git a/src/Core/Properties/CategoryAttributeManager.cs b/src/Core/Properties/CategoryAttributeManager.cs index 95d4359..f59b7bf 100644 --- a/src/Core/Properties/CategoryAttributeManager.cs +++ b/src/Core/Properties/CategoryAttributeManager.cs @@ -41,15 +41,55 @@ namespace NavisworksTransport /// public enum LogisticsElementType { - 门, - 电梯, - 楼梯, - 通道, - 走廊, - 楼板, - 装卸区, - 停车位, - 障碍物 + /// + /// 未知/空洞 - 没有任何几何覆盖,不可通行(默认值) + /// + Unknown = 0, + + /// + /// 障碍物 - 墙体、柱子、设备等,不可通行 + /// + 障碍物 = 1, + + /// + /// 楼板 - 基础可通行区域,权重1.0 + /// + 楼板 = 2, + + /// + /// 门 - 可通行,权重1.2 + /// + 门 = 3, + + /// + /// 电梯 - 可通行,权重2.0 + /// + 电梯 = 4, + + /// + /// 楼梯 - 可通行,权重3.0 + /// + 楼梯 = 5, + + /// + /// 通道 - 可通行,优先路径,权重0.5 + /// + 通道 = 6, + + /// + /// 走廊 - 可通行,权重0.6 + /// + 走廊 = 7, + + /// + /// 装卸区 - 可通行,权重0.8 + /// + 装卸区 = 8, + + /// + /// 停车位 - 可通行,权重0.9 + /// + 停车位 = 9 } /// diff --git a/src/PathPlanning/AutoPathFinder.cs b/src/PathPlanning/AutoPathFinder.cs index ac4f594..255173c 100644 --- a/src/PathPlanning/AutoPathFinder.cs +++ b/src/PathPlanning/AutoPathFinder.cs @@ -106,12 +106,10 @@ namespace NavisworksTransport.PathPlanning // 6. 精确高度修正 var heightCorrectedPath = ApplyPreciseHeightCorrection(enhancedPath, gridMap); - // 7. 替换起点和终点为原始用户指定的坐标(与FindPathCore保持一致) - var finalPath = CorrectStartEndPoints(heightCorrectedPath, originalStart, originalEnd); + // 7. 直接返回高度校正后的路径(不再修改XY坐标) + var finalPath = heightCorrectedPath; LogManager.Info($"[2.5D路径规划] 路径生成完成,最终包含 {finalPath.Count} 个路径点"); - LogManager.Info($"[2.5D路径规划] 起点坐标修正: 网格转换({heightCorrectedPath[0].X:F2}, {heightCorrectedPath[0].Y:F2}) -> 原始坐标({originalStart.X:F2}, {originalStart.Y:F2})"); - LogManager.Info($"[2.5D路径规划] 终点坐标修正: 网格转换({heightCorrectedPath[heightCorrectedPath.Count-1].X:F2}, {heightCorrectedPath[heightCorrectedPath.Count-1].Y:F2}) -> 原始坐标({originalEnd.X:F2}, {originalEnd.Y:F2})"); return finalPath; } @@ -134,6 +132,9 @@ namespace NavisworksTransport.PathPlanning try { LogManager.Info($"[A*转换-2.5D] 开始转换网格格式: {gridMap.Width}x{gridMap.Height},车辆高度: {vehicleHeight}m"); + + // 输出详细的网格统计信息 + LogManager.Info($"[A*转换-2.5D] 输入网格统计信息:\n{gridMap.GetStatistics()}"); // 单位转换 double cellSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize); @@ -158,21 +159,54 @@ namespace NavisworksTransport.PathPlanning // 只连接可通行的节点 int connectedCells = 0; + int totalWalkableCells = 0; + int totalNonWalkableCells = 0; + int heightConstrainedCells = 0; // 本来可通行但被高度约束排除的 + for (int x = 0; x < gridMap.Width; x++) { for (int y = 0; y < gridMap.Height; y++) { var cell = gridMap.Cells[x, y]; + // 统计基本可通行性 + if (cell.IsWalkable) + { + totalWalkableCells++; + } + else + { + totalNonWalkableCells++; + } + // 检查基本可通行性和高度约束 bool isPassable = cell.IsWalkable; + bool heightRestricted = false; + if (isPassable && cell.PassableHeights != null && cell.PassableHeights.Any()) { - isPassable = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); + bool heightOk = cell.PassableHeights.Any(interval => interval.GetSpan() >= vehicleHeight); + if (!heightOk) + { + heightConstrainedCells++; + heightRestricted = true; + + // 详细日志:记录被高度约束排除的单元格(仅记录前10个避免日志过多) + if (heightConstrainedCells <= 10) + { + LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:车辆高度{vehicleHeight}m,可用区间:{string.Join(", ", cell.PassableHeights.Select(i => $"{i.MinZ:F2}-{i.MaxZ:F2}(跨度{i.GetSpan():F2})"))}"); + } + else if (heightConstrainedCells == 11) + { + LogManager.Info($"[A*高度约束] 还有更多单元格被高度约束排除,不再详细记录..."); + } + } + isPassable = heightOk; } if (isPassable) { + // 直接使用网格索引(RoyT.AStar内部处理米坐标转换) var pos = new GridPosition(x, y); connectedCells++; @@ -213,7 +247,13 @@ namespace NavisworksTransport.PathPlanning } } - LogManager.Info($"[A*转换-2.5D] 网格转换完成,已连接 {connectedCells} 个可通行节点"); + LogManager.Info($"[A*转换-2.5D] 网格转换完成,A*节点连接统计:"); + LogManager.Info($"[A*转换-2.5D] - 总网格单元格: {gridMap.Width * gridMap.Height}"); + LogManager.Info($"[A*转换-2.5D] - 基本可通行单元格: {totalWalkableCells}"); + LogManager.Info($"[A*转换-2.5D] - 基本不可通行单元格: {totalNonWalkableCells}"); + LogManager.Info($"[A*转换-2.5D] - 被高度约束排除: {heightConstrainedCells}"); + LogManager.Info($"[A*转换-2.5D] - 最终连接到A*网格的节点: {connectedCells}"); + LogManager.Info($"[A*转换-2.5D] - A*可用率: {(double)connectedCells / (gridMap.Width * gridMap.Height) * 100:F1}%"); return grid; } catch (Exception ex) @@ -588,29 +628,49 @@ namespace NavisworksTransport.PathPlanning int gridX = (int)Math.Floor((point.X - gridMap.Bounds.Min.X) / gridMap.CellSize); int gridY = (int)Math.Floor((point.Y - gridMap.Bounds.Min.Y) / gridMap.CellSize); + LogManager.Info($"[高度检查] 检查点 ({point.X:F2}, {point.Y:F2}, {point.Z:F2}) -> 网格坐标 ({gridX}, {gridY})"); + // 确保坐标在有效范围内 if (gridX < 0 || gridX >= gridMap.Width || gridY < 0 || gridY >= gridMap.Height) { + LogManager.Warning($"[高度检查] 网格坐标超出范围:({gridX}, {gridY}),网格大小:{gridMap.Width}x{gridMap.Height}"); return false; } var cell = gridMap.Cells[gridX, gridY]; + LogManager.Info($"[高度检查] 单元格状态:IsWalkable={cell.IsWalkable}, CellType={cell.CellType}, PassableHeights计数={cell.PassableHeights?.Count ?? 0}"); // 检查基本可行走性 if (!cell.IsWalkable) { + LogManager.Info($"[高度检查] 单元格不可通行:({gridX}, {gridY}),类型:{cell.CellType}"); return false; } // 检查高度约束 if (cell.PassableHeights != null && cell.PassableHeights.Any()) { - return cell.PassableHeights.Any(interval => - interval.GetSpan() >= vehicleHeight && - interval.Contains(point.Z)); + LogManager.Info($"[高度检查] 检查 {cell.PassableHeights.Count} 个高度区间,车辆高度:{vehicleHeight}m"); + + foreach (var interval in cell.PassableHeights) + { + bool spanOk = interval.GetSpan() >= vehicleHeight; + bool containsHeight = interval.Contains(point.Z); + LogManager.Info($"[高度检查] 区间 {interval}: 跨度={interval.GetSpan():F2}m (需要≥{vehicleHeight}m): {spanOk}, 包含Z={point.Z:F2}: {containsHeight}"); + + if (spanOk && containsHeight) + { + LogManager.Info($"[高度检查] ✅ 找到合适的高度区间,通过检查"); + return true; + } + } + + LogManager.Warning($"[高度检查] ❌ 所有高度区间都不满足要求"); + return false; } // 如果没有高度信息,认为可通行(向后兼容) + LogManager.Info($"[高度检查] 无高度信息,默认可通行(向后兼容模式)"); return true; } catch (Exception ex) @@ -739,6 +799,10 @@ namespace NavisworksTransport.PathPlanning LogManager.Info($"[A*执行] 网格坐标转换 - 起点: ({startGrid.X}, {startGrid.Y}), 终点: ({endGrid.X}, {endGrid.Y})"); + // 获取单位转换因子 + double cellSizeInMeters = UnitsConverter.ConvertToMeters(gridMap.CellSize); + + // 正确:直接使用网格索引(RoyT.AStar内部处理米坐标转换) var startPos = new GridPosition(startGrid.X, startGrid.Y); var endPos = new GridPosition(endGrid.X, endGrid.Y); diff --git a/src/PathPlanning/GridMap.cs b/src/PathPlanning/GridMap.cs index a37fb70..17c2c6a 100644 --- a/src/PathPlanning/GridMap.cs +++ b/src/PathPlanning/GridMap.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Autodesk.Navisworks.Api; using NavisworksTransport.Utils; +using NavisworksTransport; namespace NavisworksTransport.PathPlanning { @@ -155,16 +156,17 @@ namespace NavisworksTransport.PathPlanning // 计算世界坐标位置 var worldPos = GridToWorld(new Point2D(x, y)); - Cells[x, y] = new GridCell + var cell = new GridCell { IsWalkable = false, - Cost = double.MaxValue, - CellType = ElementType.Unknown, // 修改:默认为未知/空洞类型 + CellType = CategoryAttributeManager.LogisticsElementType.Unknown, // 修改:默认为未知/空洞类型 IsInChannel = false, PassableHeights = new List(), ChannelType = ChannelType.Other, WorldPosition = worldPos }; + cell.Cost = cell.GetCost(); // 使用GetCost方法计算成本 + Cells[x, y] = cell; } } } @@ -275,22 +277,36 @@ namespace NavisworksTransport.PathPlanning /// 是否可通行 /// 遍历成本 /// 单元格类型 - public void SetCell(Point2D gridPosition, bool isWalkable, double cost, ElementType cellType) + public void SetCell(Point2D gridPosition, bool isWalkable, double cost, CategoryAttributeManager.LogisticsElementType cellType) { if (!IsValidGridPosition(gridPosition)) return; var worldPos = GridToWorld(gridPosition); - Cells[gridPosition.X, gridPosition.Y] = new GridCell + var cell = new GridCell { IsWalkable = isWalkable, - Cost = cost, CellType = cellType, - IsInChannel = cellType == ElementType.Channel, + IsInChannel = cellType == CategoryAttributeManager.LogisticsElementType.通道, PassableHeights = new List(), - ChannelType = cellType == ElementType.Channel ? ChannelType.Corridor : ChannelType.Other, + ChannelType = cellType == CategoryAttributeManager.LogisticsElementType.通道 ? ChannelType.Corridor : ChannelType.Other, WorldPosition = worldPos }; + + // 🔥 关键验证:确保Unknown和障碍物类型永远不可通行 + if (cellType == CategoryAttributeManager.LogisticsElementType.Unknown || + cellType == CategoryAttributeManager.LogisticsElementType.障碍物) + { + cell.IsWalkable = false; + cell.Cost = double.MaxValue; + } + else + { + // 优先使用传入的cost参数,如果为默认值则用GetCost + cell.Cost = (cost == double.MaxValue || cost == 0) ? cell.GetCost() : cost; + } + + Cells[gridPosition.X, gridPosition.Y] = cell; } /// @@ -305,17 +321,18 @@ namespace NavisworksTransport.PathPlanning return; var worldPos = GridToWorld(gridPosition); - Cells[gridPosition.X, gridPosition.Y] = new GridCell + var cell = new GridCell { IsWalkable = true, - Cost = 0.5, // 通道成本较低 - CellType = ElementType.Channel, + CellType = CategoryAttributeManager.LogisticsElementType.通道, IsInChannel = true, PassableHeights = new List(), ChannelType = channelType, WorldPosition = worldPos, RelatedModelItem = relatedItem }; + cell.Cost = cell.GetCost(); // 用GetCost方法计算通道成本 + Cells[gridPosition.X, gridPosition.Y] = cell; } /// @@ -373,7 +390,7 @@ namespace NavisworksTransport.PathPlanning for (int y = 0; y < Height; y++) { var cell = Cells[x, y]; - if (cell.IsInChannel || cell.CellType == ElementType.Channel) + if (cell.IsInChannel || cell.CellType == CategoryAttributeManager.LogisticsElementType.通道) { channelCells.Add((new Point2D(x, y), cell)); } @@ -484,54 +501,114 @@ namespace NavisworksTransport.PathPlanning /// 统计信息字符串 public string GetStatistics() { - int walkableCount = 0; - int obstacleCount = 0; - int doorCount = 0; - int channelCount = 0; - int unknownCount = 0; + // 总计数 + int totalWalkable = 0; + int totalNonWalkable = 0; + + // 按类型统计 - 可通行的 + int walkable_楼板 = 0; + int walkable_门 = 0; + int walkable_通道 = 0; + int walkable_装卸区 = 0; + int walkable_停车位 = 0; + int walkable_楼梯 = 0; + int walkable_电梯 = 0; + int walkable_走廊 = 0; + int walkable_Other = 0; + + // 按类型统计 - 不可通行的 + int nonWalkable_Unknown = 0; + int nonWalkable_障碍物 = 0; + int nonWalkable_楼板 = 0; + int nonWalkable_门 = 0; + int nonWalkable_通道 = 0; + int nonWalkable_Other = 0; for (int x = 0; x < Width; x++) { for (int y = 0; y < Height; y++) { var cell = Cells[x, y]; + if (cell.IsWalkable) { + totalWalkable++; switch (cell.CellType) { - case ElementType.OpenSpace: - walkableCount++; + case CategoryAttributeManager.LogisticsElementType.楼板: + walkable_楼板++; break; - case ElementType.Door: - doorCount++; + case CategoryAttributeManager.LogisticsElementType.门: + walkable_门++; break; - case ElementType.Channel: - channelCount++; + case CategoryAttributeManager.LogisticsElementType.通道: + walkable_通道++; + break; + case CategoryAttributeManager.LogisticsElementType.装卸区: + walkable_装卸区++; + break; + case CategoryAttributeManager.LogisticsElementType.停车位: + walkable_停车位++; + break; + case CategoryAttributeManager.LogisticsElementType.楼梯: + walkable_楼梯++; + break; + case CategoryAttributeManager.LogisticsElementType.电梯: + walkable_电梯++; + break; + case CategoryAttributeManager.LogisticsElementType.走廊: + walkable_走廊++; + break; + default: + walkable_Other++; break; } } else { + totalNonWalkable++; switch (cell.CellType) { - case ElementType.Unknown: - unknownCount++; + case CategoryAttributeManager.LogisticsElementType.Unknown: + nonWalkable_Unknown++; break; - case ElementType.Obstacle: - obstacleCount++; + case CategoryAttributeManager.LogisticsElementType.障碍物: + nonWalkable_障碍物++; + break; + case CategoryAttributeManager.LogisticsElementType.楼板: + nonWalkable_楼板++; + break; + case CategoryAttributeManager.LogisticsElementType.门: + nonWalkable_门++; + break; + case CategoryAttributeManager.LogisticsElementType.通道: + nonWalkable_通道++; break; default: - obstacleCount++; + nonWalkable_Other++; break; } } } } - return $"网格统计: {Width}x{Height} ({Width * Height}个单元格), " + - $"可通行: {walkableCount}, 障碍物: {obstacleCount}, 未知: {unknownCount}, " + - $"门: {doorCount}, 通道: {channelCount}, " + - $"内存使用: {GetMemoryUsage() / 1024.0:F1} KB"; + var stats = $"=== 网格统计详细信息 ===\n"; + stats += $"网格尺寸: {Width}x{Height} (总计 {Width * Height} 个单元格)\n"; + stats += $"总体分布: 可通行 {totalWalkable} 个, 不可通行 {totalNonWalkable} 个\n\n"; + + stats += $"【可通行单元格详细统计】\n"; + stats += $" 楼板: {walkable_楼板}, 通道: {walkable_通道}, 走廊: {walkable_走廊}\n"; + stats += $" 门: {walkable_门}, 装卸区: {walkable_装卸区}, 停车位: {walkable_停车位}\n"; + stats += $" 楼梯: {walkable_楼梯}, 电梯: {walkable_电梯}, 其他: {walkable_Other}\n\n"; + + stats += $"【不可通行单元格详细统计】\n"; + stats += $" Unknown/空洞: {nonWalkable_Unknown}, 障碍物: {nonWalkable_障碍物}\n"; + stats += $" 不可通行楼板: {nonWalkable_楼板}, 不可通行门: {nonWalkable_门}\n"; + stats += $" 不可通行通道: {nonWalkable_通道}, 其他不可通行: {nonWalkable_Other}\n\n"; + + stats += $"内存使用: {GetMemoryUsage() / 1024.0:F1} KB"; + + return stats; } } @@ -554,7 +631,7 @@ namespace NavisworksTransport.PathPlanning /// /// 单元格类型 /// - public ElementType CellType { get; set; } + public CategoryAttributeManager.LogisticsElementType CellType { get; set; } /// /// 相关的模型项引用(可选) @@ -581,21 +658,60 @@ namespace NavisworksTransport.PathPlanning /// public Point3D WorldPosition { get; set; } + /// + /// 根据物流类型获取通行成本 + /// + /// 通行成本 + public double GetCost() + { + if (!IsWalkable) + return double.MaxValue; + + switch (CellType) + { + case CategoryAttributeManager.LogisticsElementType.通道: + return 0.5; + case CategoryAttributeManager.LogisticsElementType.装卸区: + return 0.8; + case CategoryAttributeManager.LogisticsElementType.停车位: + return 0.9; + case CategoryAttributeManager.LogisticsElementType.楼板: + return 1.0; + case CategoryAttributeManager.LogisticsElementType.门: + return 1.2; + case CategoryAttributeManager.LogisticsElementType.楼梯: + return 3.0; + case CategoryAttributeManager.LogisticsElementType.电梯: + return 2.0; + case CategoryAttributeManager.LogisticsElementType.走廊: + return 0.6; + case CategoryAttributeManager.LogisticsElementType.Unknown: + // Unknown类型(空洞)永远不可通行 + return double.MaxValue; + case CategoryAttributeManager.LogisticsElementType.障碍物: + // 障碍物永远不可通行 + return double.MaxValue; + default: + return 1.0; + } + } + /// /// 创建障碍物单元格 /// /// 障碍物单元格 public static GridCell CreateObstacle() { - return new GridCell + var cell = new GridCell { IsWalkable = false, - Cost = double.MaxValue, - CellType = ElementType.Obstacle, + CellType = CategoryAttributeManager.LogisticsElementType.障碍物, IsInChannel = false, PassableHeights = new List(), ChannelType = ChannelType.Other }; + cell.Cost = cell.GetCost(); + return cell; } /// @@ -604,15 +720,16 @@ namespace NavisworksTransport.PathPlanning /// 开放空间单元格 public static GridCell CreateOpenSpace() { - return new GridCell + var cell = new GridCell { IsWalkable = true, - Cost = 1.0, - CellType = ElementType.OpenSpace, + CellType = CategoryAttributeManager.LogisticsElementType.楼板, IsInChannel = false, PassableHeights = new List(), ChannelType = ChannelType.Other }; + cell.Cost = cell.GetCost(); + return cell; } /// @@ -622,15 +739,16 @@ namespace NavisworksTransport.PathPlanning /// 门单元格 public static GridCell CreateDoor(bool isOpen = true) { - return new GridCell + var cell = new GridCell { IsWalkable = isOpen, - Cost = isOpen ? 5.0 : double.MaxValue, // 门的通过成本较高 - CellType = ElementType.Door, + CellType = CategoryAttributeManager.LogisticsElementType.门, IsInChannel = isOpen, PassableHeights = new List(), ChannelType = ChannelType.Other }; + cell.Cost = cell.GetCost(); + return cell; } /// @@ -640,49 +758,19 @@ namespace NavisworksTransport.PathPlanning /// 通道单元格 public static GridCell CreateChannel(ChannelType channelType = ChannelType.Corridor) { - return new GridCell + var cell = new GridCell { IsWalkable = true, - Cost = 0.5, // 通道成本较低,优先选择 - CellType = ElementType.Channel, + CellType = CategoryAttributeManager.LogisticsElementType.通道, IsInChannel = true, PassableHeights = new List(), ChannelType = channelType }; + cell.Cost = cell.GetCost(); + return cell; } } - /// - /// 元素类型枚举 - /// 定义网格单元格可能的类型 - /// - public enum ElementType - { - /// - /// 未知/空洞 - 没有任何几何覆盖,不可通行 - /// - Unknown = 0, - - /// - /// 开放空间 - 默认可通行区域 - /// - OpenSpace = 1, - - /// - /// 障碍物 - 不可通行 - /// - Obstacle = 2, - - /// - /// 门 - 条件性可通行 - /// - Door = 3, - - /// - /// 通道 - 优先通行路线 - /// - Channel = 4 - } /// /// 高度区间结构体 - 用于2.5D路径规划 @@ -794,7 +882,7 @@ namespace NavisworksTransport.PathPlanning { for (int y = 0; y < GridMap.Height; y++) { - if (GridMap.Cells[x, y].CellType == ElementType.Channel) + if (GridMap.Cells[x, y].CellType == CategoryAttributeManager.LogisticsElementType.通道) { channelCellCount++; } diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs index 2b4867e..4448318 100644 --- a/src/PathPlanning/GridMapGenerator.cs +++ b/src/PathPlanning/GridMapGenerator.cs @@ -300,7 +300,7 @@ namespace NavisworksTransport.PathPlanning var cell = gridMap.Cells[x, y]; // 只为通道类型的网格生成扫描点 - if (cell.CellType == ElementType.Channel && cell.IsInChannel) + if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel) { // 使用标准GridToWorld方法确保坐标系统一致 var worldPos = gridMap.GridToWorld(new Point2D(x, y)); @@ -388,7 +388,7 @@ namespace NavisworksTransport.PathPlanning // 关键修复:只处理通道单元格的高度信息集成 // 非通道单元格不应该通过高度扫描而改变其类型或可通行性 - if (cell.CellType == ElementType.Channel && cell.IsInChannel) + if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel) { channelCellsWithHeightData++; @@ -397,7 +397,7 @@ namespace NavisworksTransport.PathPlanning // 通道区域有足够净空高度,设置为可通行 // 🔥 关键修复:使用SetCell方法正确更新网格统计 var gridPos = new Point2D(gridX, gridY); - gridMap.SetCell(gridPos, true, 1.0, ElementType.Channel); + gridMap.SetCell(gridPos, true, 1.0, CategoryAttributeManager.LogisticsElementType.通道); // 设置世界坐标和高度信息 var updatedCell = gridMap.Cells[gridX, gridY]; @@ -410,7 +410,7 @@ namespace NavisworksTransport.PathPlanning { // 通道区域但净空高度不足,标记为不可通行但保持通道类型 var gridPos = new Point2D(gridX, gridY); - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Channel); + gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.通道); // 设置世界坐标和高度信息 var updatedCell = gridMap.Cells[gridX, gridY]; @@ -466,20 +466,20 @@ namespace NavisworksTransport.PathPlanning walkableCount++; switch (cell.CellType) { - case ElementType.Channel: + case CategoryAttributeManager.LogisticsElementType.通道: channelCount++; break; - case ElementType.OpenSpace: + case CategoryAttributeManager.LogisticsElementType.楼板: openSpaceCount++; break; - case ElementType.Door: + case CategoryAttributeManager.LogisticsElementType.门: doorCount++; break; } } else { - if (cell.CellType == ElementType.Obstacle) + if (cell.CellType == CategoryAttributeManager.LogisticsElementType.障碍物) { obstacleCount++; } @@ -769,7 +769,7 @@ namespace NavisworksTransport.PathPlanning var gridPos = new Point2D(x, y); if (gridMap.IsWalkable(gridPos)) { - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); + gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物); newlyMarkedCells++; } totalMarkedCells++; @@ -781,7 +781,7 @@ namespace NavisworksTransport.PathPlanning var gridPos = new Point2D(x, y); if (gridMap.IsWalkable(gridPos)) { - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); + gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物); newlyMarkedCells++; } totalMarkedCells++; @@ -797,7 +797,7 @@ namespace NavisworksTransport.PathPlanning var gridPos = new Point2D(x, y); if (gridMap.IsWalkable(gridPos)) { - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); + gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物); newlyMarkedCells++; } totalMarkedCells++; @@ -809,7 +809,7 @@ namespace NavisworksTransport.PathPlanning var gridPos = new Point2D(x, y); if (gridMap.IsWalkable(gridPos)) { - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); + gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物); newlyMarkedCells++; } totalMarkedCells++; @@ -1019,7 +1019,7 @@ namespace NavisworksTransport.PathPlanning distanceMap[x, y] > 0 && distanceMap[x, y] <= inflationRadius) { - gridMap.SetCell(gridPos, false, double.MaxValue, ElementType.Obstacle); + gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物); inflatedCells++; } else if (distanceMap[x, y] == int.MaxValue || distanceMap[x, y] > inflationRadius) @@ -1175,11 +1175,11 @@ namespace NavisworksTransport.PathPlanning var cell = gridMap.Cells[x, y]; // 只更新通道网格,跳过已是障碍物的网格 - if (cell.CellType == ElementType.Channel && cell.IsInChannel) + if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel) { // 标记为障碍物 cell.IsWalkable = false; - cell.CellType = ElementType.Obstacle; + cell.CellType = CategoryAttributeManager.LogisticsElementType.障碍物; cell.Cost = double.MaxValue; cell.IsInChannel = false; cell.RelatedModelItem = item;