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;