扫描障碍物时,使用包围盒中心下面的通道网格z高度,进行高度范围筛选。
几何体除了三角形外,还有线形(也许有点、SnapPoints、文字等),要过滤掉,否则也被当成障碍物。
This commit is contained in:
parent
8c8ce89978
commit
504a2c9862
@ -204,4 +204,5 @@ public class PathClickToolPlugin : ToolPlugin { }
|
||||
- 在编码中,不要用回退或向后兼容的思路和步骤
|
||||
- 程序的日志在:C:\ProgramData\Autodesk\Navisworks Manage 2026\NavisworksTransport\logs\debug.log
|
||||
- 不要搞向后兼容
|
||||
- 使用agent完成任务前,一定要先用Plan模式设计好方案和任务清单,并征得我同意。
|
||||
- 使用agent完成任务前,一定要先用Plan模式设计好方案和任务清单,并征得我同意。
|
||||
- 网格坐标代表的是网格单元的左下角,而不是中心点!
|
||||
@ -3291,8 +3291,12 @@ namespace NavisworksTransport
|
||||
|
||||
// 计算网格单元格的世界中心位置
|
||||
var gridPos = new NavisworksTransport.PathPlanning.GridPoint2D(x, y);
|
||||
var worldCenter = gridMap.GridToWorld3D(gridPos, cell.WorldPosition.Z);
|
||||
var visualizationPoint = worldCenter;
|
||||
var gridCorner = gridMap.GridToWorld3D(gridPos, cell.WorldPosition.Z);
|
||||
var gridCenter = new Point3D(
|
||||
gridCorner.X + gridMap.CellSize / 2,
|
||||
gridCorner.Y + gridMap.CellSize / 2,
|
||||
gridCorner.Z
|
||||
);
|
||||
|
||||
PathRoute targetRoute = null;
|
||||
string gridTypeName = "";
|
||||
@ -3349,7 +3353,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
var gridPoint = new PathPoint
|
||||
{
|
||||
Position = visualizationPoint,
|
||||
Position = gridCenter,
|
||||
Name = $"网格_{gridTypeName}({x},{y})",
|
||||
Type = PathPointType.WayPoint,
|
||||
Index = totalVisualized,
|
||||
|
||||
@ -82,16 +82,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 3. 创建网格地图
|
||||
var gridMap = new GridMap(totalBounds, gridSize);
|
||||
|
||||
// 新增:跟踪最小通道顶面高度
|
||||
double minChannelTopZ = double.MaxValue;
|
||||
|
||||
// 4. 为每个通道生成精确投影
|
||||
var processedChannels = new List<ModelItem>();
|
||||
foreach (var channel in channelItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProjectChannelToGrid(channel, gridMap, ref minChannelTopZ);
|
||||
ProjectChannelToGrid(channel, gridMap);
|
||||
processedChannels.Add(channel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -101,14 +98,12 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 成功投影 {processedChannels.Count}/{channelItems.Count} 个通道");
|
||||
LogManager.Info($"[通道网格构建器] 最低通道顶面高度: {minChannelTopZ:F2}");
|
||||
|
||||
return new ChannelCoverage
|
||||
{
|
||||
GridMap = gridMap,
|
||||
ChannelItems = processedChannels,
|
||||
TotalBounds = totalBounds,
|
||||
MinChannelTopZ = minChannelTopZ
|
||||
TotalBounds = totalBounds
|
||||
};
|
||||
}
|
||||
|
||||
@ -119,8 +114,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
/// <param name="channel">通道物品</param>
|
||||
/// <param name="gridMap">目标网格地图</param>
|
||||
/// <param name="minChannelTopZ">最小通道顶面高度的引用,用于跟踪所有通道中的最低顶面</param>
|
||||
private void ProjectChannelToGrid(ModelItem channel, GridMap gridMap, ref double minChannelTopZ)
|
||||
private void ProjectChannelToGrid(ModelItem channel, GridMap gridMap)
|
||||
{
|
||||
LogManager.Info($"[通道网格构建器] 投影通道: {channel.DisplayName}");
|
||||
|
||||
@ -162,13 +156,14 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 根据法向量决定处理方式
|
||||
if (normal.Z > 0.7) // 朝上的水平面
|
||||
{
|
||||
RasterizeTriangleToGrid(gridMap, triangle, ref minChannelTopZ);
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
processedTriangles++;
|
||||
}
|
||||
else if (normal.Z > 0.2) // 可行走的斜面(楼梯、坡道)
|
||||
{
|
||||
// 对于斜面,也进行投影,适用于楼梯等倾斜通道
|
||||
RasterizeTriangleToGrid(gridMap, triangle, ref minChannelTopZ);
|
||||
// [TODO] 斜面应该用不同的计算顶面高度方法
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
processedTriangles++;
|
||||
}
|
||||
// 垂直面和朝下的面不投影
|
||||
@ -216,8 +211,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="triangle">三角形</param>
|
||||
/// <param name="minChannelTopZ">最小通道顶面高度的引用,用于跟踪所有通道中的最低顶面</param>
|
||||
private void RasterizeTriangleToGrid(GridMap gridMap, Triangle3D triangle, ref double minChannelTopZ)
|
||||
private void RasterizeTriangleToGrid(GridMap gridMap, Triangle3D triangle)
|
||||
{
|
||||
// 获取三角形的2D投影边界框
|
||||
var minX = Math.Min(triangle.Point1.X, Math.Min(triangle.Point2.X, triangle.Point3.X));
|
||||
@ -231,9 +225,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 计算三角形的平均高度作为通道顶面高度
|
||||
double triangleHeight = (triangle.Point1.Z + triangle.Point2.Z + triangle.Point3.Z) / 3.0;
|
||||
|
||||
// 更新最小通道顶面高度
|
||||
minChannelTopZ = Math.Min(minChannelTopZ, triangleHeight);
|
||||
|
||||
// 遍历边界框内的所有网格点
|
||||
for (int x = minGrid.X; x <= maxGrid.X; x++)
|
||||
{
|
||||
@ -335,7 +326,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <returns>格式化字符串</returns>
|
||||
private string FormatBounds(BoundingBox3D bounds)
|
||||
{
|
||||
return $"[{bounds.Min.X:F1},{bounds.Min.Y:F1},{bounds.Min.Z:F1}] - [{bounds.Max.X:F1},{bounds.Max.Y:F1},{bounds.Max.Z:F1}]";
|
||||
return $"[{bounds.Min.X:F2},{bounds.Min.Y:F2},{bounds.Min.Z:F2}] - [{bounds.Max.X:F2},{bounds.Max.Y:F2},{bounds.Max.Z:F2}]";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
double gridX = (worldPosition.X - Origin.X) / CellSize;
|
||||
double gridY = (worldPosition.Y - Origin.Y) / CellSize;
|
||||
|
||||
|
||||
return new GridPoint2D((int)Math.Floor(gridX), (int)Math.Floor(gridY));
|
||||
}
|
||||
|
||||
@ -843,11 +843,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
public BoundingBox3D TotalBounds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最低通道顶面高度,用作障碍物扫描的基准Z值
|
||||
/// </summary>
|
||||
public double MinChannelTopZ { get; set; } = double.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// 获取统计信息
|
||||
/// </summary>
|
||||
|
||||
@ -100,8 +100,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
// 2. 直接遍历处理障碍物(包围盒检测) - 使用高性能优化版本
|
||||
LogManager.Info("【生成网格地图】步骤2: 高性能包围盒遍历处理障碍物");
|
||||
double minChannelTopZ = channelCoverage.MinChannelTopZ; // 提取最小通道顶面高度(模型单位)
|
||||
ProcessObstaclesWithBoundingBoxOptimized(document, channelCoverage.GridMap, channelRelatedItems, scanHeightInModelUnits, minChannelTopZ);
|
||||
ProcessObstaclesWithBoundingBoxOptimized(document, channelCoverage.GridMap, channelRelatedItems, scanHeightInModelUnits);
|
||||
|
||||
LogManager.Info($"【阶段2完成】障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
@ -590,137 +589,105 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化的车辆膨胀算法 - 使用距离变换方法
|
||||
/// 优化的车辆膨胀算法 - 使用正确的8连通距离变换方法
|
||||
/// 时间复杂度: O(n*m) 而不是 O(n*m*r^2)
|
||||
/// </summary>
|
||||
private void ApplyVehicleInflationOptimized(GridMap gridMap, int inflationRadius)
|
||||
{
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
LogManager.Info($"【高效膨胀】使用距离变换算法,网格大小: {gridMap.Width}x{gridMap.Height}");
|
||||
|
||||
// 创建距离矩阵
|
||||
var distanceMap = new int[gridMap.Width, gridMap.Height];
|
||||
|
||||
// 初始化距离矩阵 - 膨胀源点:障碍物、Unknown网格
|
||||
|
||||
LogManager.Info($"【8连通膨胀】使用正确的8连通距离变换算法,网格大小: {gridMap.Width}x{gridMap.Height}");
|
||||
|
||||
// 使用浮点数距离矩阵支持√2对角线距离
|
||||
var distanceMap = new double[gridMap.Width, gridMap.Height];
|
||||
const double SQRT2 = 1.414213562373095; // √2
|
||||
|
||||
// 初始化距离矩阵
|
||||
int obstacleCount = 0;
|
||||
int unknownCount = 0;
|
||||
|
||||
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 1. 障碍物作为膨胀源点
|
||||
|
||||
// 障碍物和Unknown网格作为膨胀源点
|
||||
if (!cell.IsWalkable)
|
||||
{
|
||||
distanceMap[x, y] = 0;
|
||||
distanceMap[x, y] = 0.0;
|
||||
obstacleCount++;
|
||||
}
|
||||
// 2. Unknown网格作为膨胀源点
|
||||
else if (cell.CellType == CategoryAttributeManager.LogisticsElementType.Unknown)
|
||||
{
|
||||
distanceMap[x, y] = 0;
|
||||
distanceMap[x, y] = 0.0;
|
||||
unknownCount++;
|
||||
}
|
||||
// 3. 其他可通行区域(包括边界通道)距离为无穷大
|
||||
else
|
||||
{
|
||||
distanceMap[x, y] = int.MaxValue;
|
||||
distanceMap[x, y] = double.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【统一膨胀】膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}");
|
||||
|
||||
LogManager.Info($"【高效膨胀】距离矩阵初始化完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 正向扫描(从左上到右下)
|
||||
|
||||
LogManager.Info($"【8连通膨胀】膨胀源点统计 - 障碍物: {obstacleCount}, Unknown: {unknownCount}");
|
||||
|
||||
// 使用标准的距离变换算法 - 正向扫描
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
if (distanceMap[x, y] != 0 && distanceMap[x, y] != int.MaxValue)
|
||||
if (distanceMap[x, y] == double.MaxValue)
|
||||
{
|
||||
// 检查左侧和上方的邻居
|
||||
int minDist = distanceMap[x, y];
|
||||
|
||||
if (x > 0 && distanceMap[x - 1, y] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y] + 1);
|
||||
if (y > 0 && distanceMap[x, y - 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y - 1] + 1);
|
||||
if (x > 0 && y > 0 && distanceMap[x - 1, y - 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y - 1] + 1);
|
||||
if (x > 0 && y < gridMap.Height - 1 && distanceMap[x - 1, y + 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y + 1] + 1);
|
||||
|
||||
distanceMap[x, y] = minDist;
|
||||
}
|
||||
else if (distanceMap[x, y] == int.MaxValue)
|
||||
{
|
||||
// 对于可通行区域,计算到最近障碍物的距离
|
||||
int minDist = int.MaxValue;
|
||||
|
||||
if (x > 0 && distanceMap[x - 1, y] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y] + 1);
|
||||
if (y > 0 && distanceMap[x, y - 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y - 1] + 1);
|
||||
if (x > 0 && y > 0 && distanceMap[x - 1, y - 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y - 1] + 1);
|
||||
if (x > 0 && y < gridMap.Height - 1 && distanceMap[x - 1, y + 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y + 1] + 1);
|
||||
|
||||
if (minDist != int.MaxValue)
|
||||
double minDist = double.MaxValue;
|
||||
|
||||
// 检查左侧和上方的邻居(4连通)
|
||||
if (x > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y] + 1.0);
|
||||
if (y > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y - 1] + 1.0);
|
||||
|
||||
// 检查对角线邻居(8连通扩展)
|
||||
if (x > 0 && y > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y - 1] + SQRT2);
|
||||
if (x > 0 && y < gridMap.Height - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x - 1, y + 1] + SQRT2);
|
||||
|
||||
if (minDist != double.MaxValue)
|
||||
distanceMap[x, y] = minDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【高效膨胀】正向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 反向扫描(从右下到左上)
|
||||
|
||||
LogManager.Info($"【8连通膨胀】正向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 反向扫描
|
||||
for (int x = gridMap.Width - 1; x >= 0; x--)
|
||||
{
|
||||
for (int y = gridMap.Height - 1; y >= 0; y--)
|
||||
{
|
||||
if (distanceMap[x, y] != 0 && distanceMap[x, y] != int.MaxValue)
|
||||
if (distanceMap[x, y] != 0.0)
|
||||
{
|
||||
// 检查右侧和下方的邻居
|
||||
int minDist = distanceMap[x, y];
|
||||
|
||||
if (x < gridMap.Width - 1 && distanceMap[x + 1, y] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y] + 1);
|
||||
if (y < gridMap.Height - 1 && distanceMap[x, y + 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y + 1] + 1);
|
||||
if (x < gridMap.Width - 1 && y < gridMap.Height - 1 && distanceMap[x + 1, y + 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y + 1] + 1);
|
||||
if (x < gridMap.Width - 1 && y > 0 && distanceMap[x + 1, y - 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y - 1] + 1);
|
||||
|
||||
double minDist = distanceMap[x, y];
|
||||
|
||||
// 检查右侧和下方的邻居(4连通)
|
||||
if (x < gridMap.Width - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y] + 1.0);
|
||||
if (y < gridMap.Height - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y + 1] + 1.0);
|
||||
|
||||
// 检查对角线邻居(8连通扩展)
|
||||
if (x < gridMap.Width - 1 && y < gridMap.Height - 1)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y + 1] + SQRT2);
|
||||
if (x < gridMap.Width - 1 && y > 0)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y - 1] + SQRT2);
|
||||
|
||||
distanceMap[x, y] = minDist;
|
||||
}
|
||||
else if (distanceMap[x, y] == int.MaxValue)
|
||||
{
|
||||
// 对于可通行区域,计算到最近障碍物的距离
|
||||
int minDist = int.MaxValue;
|
||||
|
||||
if (x < gridMap.Width - 1 && distanceMap[x + 1, y] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y] + 1);
|
||||
if (y < gridMap.Height - 1 && distanceMap[x, y + 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x, y + 1] + 1);
|
||||
if (x < gridMap.Width - 1 && y < gridMap.Height - 1 && distanceMap[x + 1, y + 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y + 1] + 1);
|
||||
if (x < gridMap.Width - 1 && y > 0 && distanceMap[x + 1, y - 1] != int.MaxValue)
|
||||
minDist = Math.Min(minDist, distanceMap[x + 1, y - 1] + 1);
|
||||
|
||||
if (minDist != int.MaxValue)
|
||||
distanceMap[x, y] = minDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"【高效膨胀】反向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
LogManager.Info($"【8连通膨胀】反向扫描完成,耗时: {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
// 应用膨胀结果
|
||||
int inflatedCells = 0;
|
||||
@ -728,9 +695,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
int totalCells = gridMap.Width * gridMap.Height;
|
||||
int skippedCells = 0;
|
||||
int protectedDoorCells = 0;
|
||||
|
||||
LogManager.Info($"【高效膨胀】开始应用膨胀结果,总单元格数: {totalCells}");
|
||||
|
||||
|
||||
LogManager.Info($"【8连通膨胀】开始应用膨胀结果,总单元格数: {totalCells}");
|
||||
|
||||
try
|
||||
{
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
@ -738,26 +705,26 @@ namespace NavisworksTransport.PathPlanning
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
processedCells++;
|
||||
|
||||
|
||||
// 每处理50万个单元格输出一次进度
|
||||
if (processedCells % 500000 == 0)
|
||||
{
|
||||
double progress = (double)processedCells / totalCells * 100;
|
||||
LogManager.Info($"【高效膨胀】应用进度: {progress:F1}% ({processedCells}/{totalCells})");
|
||||
LogManager.Info($"【8连通膨胀】应用进度: {progress:F1}% ({processedCells}/{totalCells})");
|
||||
}
|
||||
|
||||
|
||||
var gridPos = new GridPoint2D(x, y);
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
|
||||
// 只有满足以下所有条件才进行膨胀:
|
||||
// 1. 当前位置原本是可通行的
|
||||
// 2. 距离值不是无穷大(有有效的距离计算)
|
||||
// 3. 距离小于等于膨胀半径
|
||||
// 3. 距离小于等于膨胀半径(使用浮点数比较)
|
||||
// 4. 距离值大于0(不是原始障碍物)
|
||||
// 5. 不是门类型(门保护)
|
||||
if (gridMap.IsWalkable(gridPos) &&
|
||||
distanceMap[x, y] != int.MaxValue &&
|
||||
distanceMap[x, y] > 0 &&
|
||||
if (gridMap.IsWalkable(gridPos) &&
|
||||
distanceMap[x, y] != double.MaxValue &&
|
||||
distanceMap[x, y] > 0.0 &&
|
||||
distanceMap[x, y] <= inflationRadius)
|
||||
{
|
||||
// 门元素保护:跳过门类型的网格,不将其膨胀为障碍物
|
||||
@ -767,11 +734,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
// 跳过门网格,保持其可通行状态
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
gridMap.SetCell(gridPos, false, double.MaxValue, CategoryAttributeManager.LogisticsElementType.障碍物);
|
||||
inflatedCells++;
|
||||
}
|
||||
else if (distanceMap[x, y] == int.MaxValue || distanceMap[x, y] > inflationRadius)
|
||||
else if (distanceMap[x, y] == double.MaxValue || distanceMap[x, y] > inflationRadius)
|
||||
{
|
||||
skippedCells++;
|
||||
}
|
||||
@ -780,15 +747,15 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"【高效膨胀】应用膨胀结果时发生错误: {ex.Message},已处理{processedCells}/{totalCells}个单元格");
|
||||
LogManager.Error($"【8连通膨胀】应用膨胀结果时发生错误: {ex.Message},已处理{processedCells}/{totalCells}个单元格");
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
// 单独处理包围盒边界膨胀
|
||||
int boundaryInflatedCells = ApplyBoundaryInflationPost(gridMap, inflationRadius);
|
||||
|
||||
|
||||
stopwatch.Stop();
|
||||
LogManager.Info($"【高效膨胀】车辆膨胀完成: 膨胀半径={inflationRadius}格, 新增障碍物={inflatedCells}个, 边界膨胀={boundaryInflatedCells}个, 跳过={skippedCells}个, 保护门网格={protectedDoorCells}个, 总耗时={stopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($"【8连通膨胀】车辆膨胀完成: 膨胀半径={inflationRadius}格, 新增障碍物={inflatedCells}个, 边界膨胀={boundaryInflatedCells}个, 跳过={skippedCells}个, 保护门网格={protectedDoorCells}个, 总耗时={stopwatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -809,7 +776,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
int layerInflatedCount = 0;
|
||||
|
||||
// 处理上边界 (y = layer)
|
||||
// 处理下边界 (y = layer)
|
||||
if (layer < gridMap.Height)
|
||||
{
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
@ -823,7 +790,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
}
|
||||
|
||||
// 处理下边界 (y = maxY - layer)
|
||||
// 处理上边界 (y = maxY - layer)
|
||||
if (gridMap.Height - 1 - layer >= 0 && gridMap.Height - 1 - layer != layer) // 避免重复处理
|
||||
{
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
@ -953,9 +920,72 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info($"[Search API优化] Search API阶段完成: 获取 {geometryItems.Count()} 个几何体项目,耗时: {searchElapsed:F1}ms");
|
||||
LogManager.Info($"[Search API优化] 几何体筛选效果: 过滤掉 {filteredByGeometry} 个无几何体项目,筛选率: {geometryFilterRatio:P1}");
|
||||
|
||||
// 🔥 关键优化:直接返回Search API结果,移除所有昂贵的LINQ统计调用
|
||||
// 🔥 关键优化:使用PrimitiveTypes精确过滤线性几何体
|
||||
// 基于多模型测试验证,LINQ筛选条件(IsCollection, IsComposite, Children.Any)都返回0项
|
||||
var filteredItems = geometryItems.ToList();
|
||||
LogManager.Info("[Search API优化] 开始使用PrimitiveTypes过滤线性几何体");
|
||||
var primitiveFilterStart = DateTime.Now;
|
||||
|
||||
int triangleCount = 0;
|
||||
int lineOnlyCount = 0;
|
||||
int pointOnlyCount = 0;
|
||||
int textOnlyCount = 0;
|
||||
int mixedCount = 0;
|
||||
int noGeometryCount = 0;
|
||||
|
||||
var filteredItems = geometryItems.Where(item =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var geometry = item.Geometry;
|
||||
if (geometry == null)
|
||||
{
|
||||
noGeometryCount++;
|
||||
return false;
|
||||
}
|
||||
|
||||
var primitiveTypes = geometry.PrimitiveTypes;
|
||||
|
||||
// 统计各种图元类型
|
||||
if (primitiveTypes.HasFlag(PrimitiveTypes.Triangles))
|
||||
{
|
||||
if (primitiveTypes == PrimitiveTypes.Triangles)
|
||||
triangleCount++;
|
||||
else
|
||||
mixedCount++;
|
||||
return true; // 包含三角形的几何体都保留
|
||||
}
|
||||
else if (primitiveTypes == PrimitiveTypes.Lines)
|
||||
{
|
||||
lineOnlyCount++;
|
||||
return false; // 纯线条几何体过滤掉
|
||||
}
|
||||
else if (primitiveTypes == PrimitiveTypes.Points)
|
||||
{
|
||||
pointOnlyCount++;
|
||||
return false; // 纯点几何体过滤掉
|
||||
}
|
||||
else if (primitiveTypes == PrimitiveTypes.Text)
|
||||
{
|
||||
textOnlyCount++;
|
||||
return false; // 纯文本几何体过滤掉
|
||||
}
|
||||
else
|
||||
{
|
||||
mixedCount++;
|
||||
return true; // 其他混合类型保留
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false; // 出错时跳过该项目
|
||||
}
|
||||
}).ToList();
|
||||
|
||||
var primitiveFilterElapsed = (DateTime.Now - primitiveFilterStart).TotalMilliseconds;
|
||||
var filteredByPrimitives = geometryItems.Count() - filteredItems.Count;
|
||||
|
||||
LogManager.Info($"[Search API优化] PrimitiveTypes过滤完成: 过滤掉 {filteredByPrimitives} 个线性几何体,耗时: {primitiveFilterElapsed:F1}ms");
|
||||
LogManager.Info($"[Search API优化] 图元类型统计 - 纯三角形: {triangleCount}, 纯线条: {lineOnlyCount}, 纯点: {pointOnlyCount}, 纯文本: {textOnlyCount}, 混合类型: {mixedCount}, 无几何体: {noGeometryCount}");
|
||||
|
||||
var totalElapsed = (DateTime.Now - startTime).TotalMilliseconds;
|
||||
var totalFilteredCount = totalModelItems - filteredItems.Count;
|
||||
@ -982,8 +1012,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
List<ModelItem> geometryItems,
|
||||
HashSet<ModelItem> channelItemsSet,
|
||||
GridMap gridMap,
|
||||
double scanHeightInModelUnits,
|
||||
double minChannelTopZ)
|
||||
double scanHeightInModelUnits)
|
||||
{
|
||||
LogManager.Info("[轻量级后处理] 开始处理Search API筛选结果");
|
||||
var startTime = DateTime.Now;
|
||||
@ -993,7 +1022,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
var apiCalls = 0;
|
||||
var skippedByChannel = 0;
|
||||
var skippedByBoundingBox = 0;
|
||||
var skippedByHeight = 0;
|
||||
|
||||
foreach (var item in geometryItems)
|
||||
{
|
||||
@ -1019,16 +1047,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
continue; // 跳过通道子项目
|
||||
}
|
||||
|
||||
// 移除容器检查:基于4个模型测试验证,所有几何体项目的容器检查结果都是0
|
||||
// 注释:原有的容器检查代码
|
||||
// API调用1: Children.Any() - 检查是否为容器
|
||||
// properties.IsContainer = item.Children?.Any() ?? false;
|
||||
// apiCalls++;
|
||||
// if (properties.IsContainer) {
|
||||
// skippedByContainer++;
|
||||
// continue; // 跳过容器项目
|
||||
// }
|
||||
|
||||
// 设为已知值(避免后续筛选中的判断错误)
|
||||
properties.IsContainer = false; // 基于测试验证,几何体项目都不是容器
|
||||
|
||||
@ -1041,14 +1059,9 @@ namespace NavisworksTransport.PathPlanning
|
||||
skippedByBoundingBox++;
|
||||
continue; // 跳过无边界框项目
|
||||
}
|
||||
|
||||
// 快速计算:高度范围检查(纯数值计算,无API调用)
|
||||
properties.IsInScanHeightRange = IsInScanHeightRange(properties.BoundingBox, minChannelTopZ, scanHeightInModelUnits);
|
||||
if (!properties.IsInScanHeightRange)
|
||||
{
|
||||
skippedByHeight++;
|
||||
continue; // 跳过高度范围外项目
|
||||
}
|
||||
|
||||
// 设置为默认值,实际的高度检查在后续阶段进行
|
||||
properties.IsInScanHeightRange = true;
|
||||
|
||||
// 只有通过所有筛选的项目才加入结果
|
||||
itemCache[item] = properties;
|
||||
@ -1061,7 +1074,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
|
||||
LogManager.Info($"[轻量级后处理] 完成,输入 {totalItems} 项,有效结果 {itemCache.Count} 项,API调用 {apiCalls} 次,耗时: {elapsed:F1}ms");
|
||||
LogManager.Info($"[轻量级后处理] 筛除统计 - 通道相关: {skippedByChannel}, 无边界框: {skippedByBoundingBox}, 高度范围外: {skippedByHeight}");
|
||||
LogManager.Info($"[轻量级后处理] 筛除统计 - 通道相关: {skippedByChannel}, 无边界框: {skippedByBoundingBox}");
|
||||
LogManager.Info("[轻量级后处理] 已移除全局高度预过滤,将在障碍物遍历阶段进行精确高度检查");
|
||||
|
||||
return itemCache;
|
||||
}
|
||||
@ -1074,8 +1088,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="channelItems">通道模型项列表</param>
|
||||
/// <param name="scanHeightInModelUnits">扫描高度范围(模型单位)</param>
|
||||
/// <param name="minChannelTopZ">最小通道顶面高度,用作扫描基准</param>
|
||||
private void ProcessObstaclesWithBoundingBoxOptimized(Document document, GridMap gridMap, HashSet<ModelItem> channelItems, double scanHeightInModelUnits, double minChannelTopZ)
|
||||
private void ProcessObstaclesWithBoundingBoxOptimized(Document document, GridMap gridMap, HashSet<ModelItem> channelItems, double scanHeightInModelUnits)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -1090,7 +1103,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {channelItemsSet.Count}");
|
||||
|
||||
// 阶段2:轻量级后处理(只对预筛选结果进行必要的API调用)
|
||||
var itemCache = PostProcessGeometryItems(geometryItems, channelItemsSet, gridMap, scanHeightInModelUnits, minChannelTopZ);
|
||||
var itemCache = PostProcessGeometryItems(geometryItems, channelItemsSet, gridMap, scanHeightInModelUnits);
|
||||
|
||||
// 阶段3:条件过滤(并行数值计算)- 使用50%CPU核心优化
|
||||
LogManager.Info("[高性能障碍物处理] 阶段3: 并行条件过滤");
|
||||
@ -1113,11 +1126,38 @@ namespace NavisworksTransport.PathPlanning
|
||||
var filterElapsed = (DateTime.Now - filterStart).TotalMilliseconds;
|
||||
LogManager.Info($"[高性能障碍物处理] 阶段3完成: 从缓存中筛选出 {validItems.Count} 个有效项,耗时: {filterElapsed:F1}ms");
|
||||
|
||||
// 阶段4:网格覆盖计算(并行几何计算)- 使用50%CPU核心优化
|
||||
LogManager.Info("[高性能障碍物处理] 阶段4: 并行网格覆盖计算");
|
||||
// 阶段4:网格覆盖计算(并行几何计算)- 使用50%CPU核心优化 + 精确高度检查
|
||||
LogManager.Info("[高性能障碍物处理] 阶段4: 并行网格覆盖计算 + 精确高度检查");
|
||||
var gridCalcStart = DateTime.Now;
|
||||
|
||||
var gridUpdates = validItems.AsParallel()
|
||||
|
||||
var heightCheckedItems = validItems.AsParallel()
|
||||
.WithDegreeOfParallelism(optimalParallelism)
|
||||
.Where(kvp =>
|
||||
{
|
||||
// 精确高度检查:使用几何体中心点对应的网格高度
|
||||
var bbox = kvp.Value.BoundingBox;
|
||||
var centerX = (bbox.Min.X + bbox.Max.X) / 2;
|
||||
var centerY = (bbox.Min.Y + bbox.Max.Y) / 2;
|
||||
|
||||
var centerGridPos = gridMap.WorldToGrid(new Point3D(centerX, centerY, 0));
|
||||
if (!gridMap.IsValidGridPosition(centerGridPos))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var centerCell = gridMap.Cells[centerGridPos.X, centerGridPos.Y];
|
||||
var referenceZ = centerCell.WorldPosition.Z;
|
||||
var scanMin = referenceZ;
|
||||
var scanMax = referenceZ + scanHeightInModelUnits;
|
||||
|
||||
// 只有与该网格高度范围重叠的几何体才进入处理
|
||||
bool isInRange = !(bbox.Max.Z < scanMin || bbox.Min.Z > scanMax);
|
||||
|
||||
return isInRange;
|
||||
})
|
||||
.ToList();
|
||||
|
||||
var gridUpdates = heightCheckedItems.AsParallel()
|
||||
.WithDegreeOfParallelism(optimalParallelism)
|
||||
.SelectMany(kvp =>
|
||||
{
|
||||
@ -1131,9 +1171,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
});
|
||||
})
|
||||
.ToList();
|
||||
|
||||
|
||||
var gridCalcElapsed = (DateTime.Now - gridCalcStart).TotalMilliseconds;
|
||||
var totalCheckedItems = validItems.Count;
|
||||
var filteredItems = totalCheckedItems - heightCheckedItems.Count;
|
||||
|
||||
LogManager.Info($"[高性能障碍物处理] 阶段4完成: 生成 {gridUpdates.Count} 个网格更新操作,耗时: {gridCalcElapsed:F1}ms");
|
||||
LogManager.Info($"[精确高度检查] 检查了 {totalCheckedItems} 个几何体,过滤掉 {filteredItems} 个高度范围外的几何体,保留 {heightCheckedItems.Count} 个");
|
||||
|
||||
// 阶段5:网格状态更新(单线程写入)
|
||||
LogManager.Info("[高性能障碍物处理] 阶段5: 单线程网格状态更新");
|
||||
@ -1184,6 +1228,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
// 性能对比统计
|
||||
LogManager.Info($"[性能优化统计] 处理几何体项目: {geometryItems.Count}, 最终有效项目: {validItems.Count}, 优化比率: {(1.0 - (double)validItems.Count / geometryItems.Count):P1}");
|
||||
LogManager.Info($"[精确高度过滤统计] 高度检查项目: {totalCheckedItems}, 高度过滤项目: {filteredItems}, 精确过滤率: {(double)filteredItems / Math.Max(1, totalCheckedItems):P1}");
|
||||
LogManager.Info($"[并行优化统计] 使用 {optimalParallelism}/{Environment.ProcessorCount} CPU核心,每核心处理 {Math.Ceiling((double)validItems.Count / optimalParallelism)} 个项目");
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1196,6 +1241,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
/// <summary>
|
||||
/// 计算包围盒在网格上覆盖的单元坐标
|
||||
/// 严格按照"网格坐标代表左下角"的设计原则进行边界处理
|
||||
/// </summary>
|
||||
/// <param name="bbox">包围盒</param>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
@ -1203,19 +1249,27 @@ namespace NavisworksTransport.PathPlanning
|
||||
private List<(int x, int y)> CalculateBoundingBoxGridCoverage(BoundingBox3D bbox, GridMap gridMap)
|
||||
{
|
||||
var coveredCells = new List<(int x, int y)>();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// 将包围盒的世界坐标转换为网格坐标
|
||||
// 网格坐标(x,y)代表左下角,覆盖世界坐标范围:
|
||||
// X: [Origin.X + x*CellSize, Origin.X + (x+1)*CellSize)
|
||||
// Y: [Origin.Y + y*CellSize, Origin.Y + (y+1)*CellSize)
|
||||
|
||||
// 直接使用WorldToGrid进行坐标转换
|
||||
var minGridPos = gridMap.WorldToGrid(new Point3D(bbox.Min.X, bbox.Min.Y, 0));
|
||||
var maxGridPos = gridMap.WorldToGrid(new Point3D(bbox.Max.X, bbox.Max.Y, 0));
|
||||
|
||||
|
||||
// 确保坐标在有效范围内
|
||||
int minX = Math.Max(0, Math.Min(minGridPos.X, maxGridPos.X));
|
||||
int minY = Math.Max(0, Math.Min(minGridPos.Y, maxGridPos.Y));
|
||||
int maxX = Math.Min(gridMap.Width - 1, Math.Max(minGridPos.X, maxGridPos.X));
|
||||
int maxY = Math.Min(gridMap.Height - 1, Math.Max(minGridPos.Y, maxGridPos.Y));
|
||||
|
||||
int minX = Math.Max(0, minGridPos.X);
|
||||
int minY = Math.Max(0, minGridPos.Y);
|
||||
int maxX = Math.Min(gridMap.Width - 1, maxGridPos.X);
|
||||
int maxY = Math.Min(gridMap.Height - 1, maxGridPos.Y);
|
||||
|
||||
// 确保至少覆盖一个网格(防止空包围盒)
|
||||
if (maxX < minX) maxX = minX;
|
||||
if (maxY < minY) maxY = minY;
|
||||
|
||||
// 遍历覆盖的矩形区域
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
@ -1224,12 +1278,14 @@ namespace NavisworksTransport.PathPlanning
|
||||
coveredCells.Add((x, y));
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Debug($"[包围盒覆盖计算] 包围盒[{bbox.Min.X:F2},{bbox.Min.Y:F2}]-[{bbox.Max.X:F2},{bbox.Max.Y:F2}] → 网格[{minX},{minY}]-[{maxX},{maxY}],覆盖{coveredCells.Count}个网格");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Debug($"[包围盒覆盖计算] 计算失败: {ex.Message}");
|
||||
}
|
||||
|
||||
|
||||
return coveredCells;
|
||||
}
|
||||
|
||||
@ -1310,29 +1366,21 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
LogManager.Debug($"【门开口计算】{doorItem.DisplayName} 开口区域坐标: [{minX:F2},{minY:F2}]-[{maxX:F2},{maxY:F2}]");
|
||||
|
||||
// 7. 将开口区域坐标直接转换为网格坐标
|
||||
// 7. 将开口区域坐标转换为网格坐标(严格按照左下角原则)
|
||||
|
||||
var minGridPos = gridMap.WorldToGrid(new Point3D(minX, minY, 0));
|
||||
|
||||
var maxGridPos = gridMap.WorldToGrid(new Point3D(maxX, maxY, 0));
|
||||
|
||||
|
||||
// 确保坐标在有效范围内
|
||||
int gridMinX = Math.Max(0, Math.Min(minGridPos.X, maxGridPos.X));
|
||||
int gridMinY = Math.Max(0, Math.Min(minGridPos.Y, maxGridPos.Y));
|
||||
int gridMaxX = Math.Min(gridMap.Width - 1, Math.Max(minGridPos.X, maxGridPos.X));
|
||||
int gridMaxY = Math.Min(gridMap.Height - 1, Math.Max(minGridPos.Y, maxGridPos.Y));
|
||||
int gridMinX = Math.Max(0, minGridPos.X);
|
||||
int gridMinY = Math.Max(0, minGridPos.Y);
|
||||
int gridMaxX = Math.Min(gridMap.Width - 1, maxGridPos.X);
|
||||
int gridMaxY = Math.Min(gridMap.Height - 1, maxGridPos.Y);
|
||||
|
||||
// 确保至少有1个网格可通行
|
||||
if (gridMinX >= gridMaxX)
|
||||
{
|
||||
int centerX = (gridMinX + gridMaxX) / 2;
|
||||
gridMinX = Math.Max(0, centerX);
|
||||
gridMaxX = Math.Min(gridMap.Width - 1, centerX);
|
||||
}
|
||||
if (gridMinY >= gridMaxY)
|
||||
{
|
||||
int centerY = (gridMinY + gridMaxY) / 2;
|
||||
gridMinY = Math.Max(0, centerY);
|
||||
gridMaxY = Math.Min(gridMap.Height - 1, centerY);
|
||||
}
|
||||
if (gridMaxX < gridMinX) gridMaxX = gridMinX;
|
||||
if (gridMaxY < gridMinY) gridMaxY = gridMinY;
|
||||
|
||||
// 8. 生成覆盖的网格单元列表
|
||||
for (int x = gridMinX; x <= gridMaxX; x++)
|
||||
@ -1354,32 +1402,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
return coveredCells;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 检查包围盒是否在扫描高度范围内
|
||||
/// </summary>
|
||||
/// <param name="bbox">包围盒</param>
|
||||
/// <param name="minChannelTopZ">最小通道顶面高度,用作扫描基准(模型单位)</param>
|
||||
/// <param name="scanHeightInModelUnits">扫描高度范围(模型单位)</param>
|
||||
/// <returns>是否在高度范围内</returns>
|
||||
private bool IsInScanHeightRange(BoundingBox3D bbox, double minChannelTopZ, double scanHeightInModelUnits)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用最低通道顶面高度作为扫描基准,避免门高度干扰
|
||||
var scanMinZ = minChannelTopZ; // 最低通道顶面(模型单位)
|
||||
var scanMaxZ = minChannelTopZ + scanHeightInModelUnits; // 从最低通道顶面向上扫描(模型单位)
|
||||
|
||||
// 检查包围盒的Z范围是否与扫描范围有重叠
|
||||
return !(bbox.Max.Z < scanMinZ || bbox.Min.Z > scanMaxZ);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Debug($"[高度范围检查] 检查失败: {ex.Message}");
|
||||
return true; // 出错时保守处理,包含该项
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查模型项是否是通道项的子项
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user