修改了寻路算法穿洞的bug
This commit is contained in:
parent
289eff5554
commit
fb8d52398b
@ -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. 优化性能和内存使用
|
||||
|
||||
## 总结
|
||||
|
||||
这个双属性设计方案既解决了当前的空洞穿越问题,又为未来的功能扩展奠定了坚实基础。通过清晰的职责分离和灵活的类型系统,系统能够支持从简单楼板到复杂物流场景的各种需求。
|
||||
这个双属性设计方案既解决了当前的空洞穿越问题,又为未来的功能扩展奠定了坚实基础。通过清晰的职责分离和灵活的类型系统,系统能够支持从简单楼板到复杂物流场景的各种需求。
|
||||
|
||||
@ -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 = "障碍";
|
||||
|
||||
@ -41,15 +41,55 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
public enum LogisticsElementType
|
||||
{
|
||||
门,
|
||||
电梯,
|
||||
楼梯,
|
||||
通道,
|
||||
走廊,
|
||||
楼板,
|
||||
装卸区,
|
||||
停车位,
|
||||
障碍物
|
||||
/// <summary>
|
||||
/// 未知/空洞 - 没有任何几何覆盖,不可通行(默认值)
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 障碍物 - 墙体、柱子、设备等,不可通行
|
||||
/// </summary>
|
||||
障碍物 = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 楼板 - 基础可通行区域,权重1.0
|
||||
/// </summary>
|
||||
楼板 = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 门 - 可通行,权重1.2
|
||||
/// </summary>
|
||||
门 = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 电梯 - 可通行,权重2.0
|
||||
/// </summary>
|
||||
电梯 = 4,
|
||||
|
||||
/// <summary>
|
||||
/// 楼梯 - 可通行,权重3.0
|
||||
/// </summary>
|
||||
楼梯 = 5,
|
||||
|
||||
/// <summary>
|
||||
/// 通道 - 可通行,优先路径,权重0.5
|
||||
/// </summary>
|
||||
通道 = 6,
|
||||
|
||||
/// <summary>
|
||||
/// 走廊 - 可通行,权重0.6
|
||||
/// </summary>
|
||||
走廊 = 7,
|
||||
|
||||
/// <summary>
|
||||
/// 装卸区 - 可通行,权重0.8
|
||||
/// </summary>
|
||||
装卸区 = 8,
|
||||
|
||||
/// <summary>
|
||||
/// 停车位 - 可通行,权重0.9
|
||||
/// </summary>
|
||||
停车位 = 9
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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<HeightInterval>(),
|
||||
ChannelType = ChannelType.Other,
|
||||
WorldPosition = worldPos
|
||||
};
|
||||
cell.Cost = cell.GetCost(); // 使用GetCost方法计算成本
|
||||
Cells[x, y] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -275,22 +277,36 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="isWalkable">是否可通行</param>
|
||||
/// <param name="cost">遍历成本</param>
|
||||
/// <param name="cellType">单元格类型</param>
|
||||
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<HeightInterval>(),
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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<HeightInterval>(),
|
||||
ChannelType = channelType,
|
||||
WorldPosition = worldPos,
|
||||
RelatedModelItem = relatedItem
|
||||
};
|
||||
cell.Cost = cell.GetCost(); // 用GetCost方法计算通道成本
|
||||
Cells[gridPosition.X, gridPosition.Y] = cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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
|
||||
/// <returns>统计信息字符串</returns>
|
||||
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
|
||||
/// <summary>
|
||||
/// 单元格类型
|
||||
/// </summary>
|
||||
public ElementType CellType { get; set; }
|
||||
public CategoryAttributeManager.LogisticsElementType CellType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 相关的模型项引用(可选)
|
||||
@ -581,21 +658,60 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
public Point3D WorldPosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 根据物流类型获取通行成本
|
||||
/// </summary>
|
||||
/// <returns>通行成本</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建障碍物单元格
|
||||
/// </summary>
|
||||
/// <returns>障碍物单元格</returns>
|
||||
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<HeightInterval>(),
|
||||
ChannelType = ChannelType.Other
|
||||
};
|
||||
cell.Cost = cell.GetCost();
|
||||
return cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -604,15 +720,16 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <returns>开放空间单元格</returns>
|
||||
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<HeightInterval>(),
|
||||
ChannelType = ChannelType.Other
|
||||
};
|
||||
cell.Cost = cell.GetCost();
|
||||
return cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -622,15 +739,16 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <returns>门单元格</returns>
|
||||
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<HeightInterval>(),
|
||||
ChannelType = ChannelType.Other
|
||||
};
|
||||
cell.Cost = cell.GetCost();
|
||||
return cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -640,49 +758,19 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <returns>通道单元格</returns>
|
||||
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<HeightInterval>(),
|
||||
ChannelType = channelType
|
||||
};
|
||||
cell.Cost = cell.GetCost();
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 元素类型枚举
|
||||
/// 定义网格单元格可能的类型
|
||||
/// </summary>
|
||||
public enum ElementType
|
||||
{
|
||||
/// <summary>
|
||||
/// 未知/空洞 - 没有任何几何覆盖,不可通行
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 开放空间 - 默认可通行区域
|
||||
/// </summary>
|
||||
OpenSpace = 1,
|
||||
|
||||
/// <summary>
|
||||
/// 障碍物 - 不可通行
|
||||
/// </summary>
|
||||
Obstacle = 2,
|
||||
|
||||
/// <summary>
|
||||
/// 门 - 条件性可通行
|
||||
/// </summary>
|
||||
Door = 3,
|
||||
|
||||
/// <summary>
|
||||
/// 通道 - 优先通行路线
|
||||
/// </summary>
|
||||
Channel = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 高度区间结构体 - 用于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++;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user