增加一个“无关项”的物流类型,用于过滤如地基、建筑结构等与物流无关的构件

This commit is contained in:
tian 2025-10-06 21:31:09 +08:00
parent 8cd988279f
commit f5d1361146
3 changed files with 73 additions and 42 deletions

3
.claude/config.json Normal file
View File

@ -0,0 +1,3 @@
{
"primaryApiKey": "api"
}

View File

@ -86,7 +86,12 @@ namespace NavisworksTransport
/// <summary>
/// 停车位 - 可通行权重0.9
/// </summary>
= 9
= 9,
/// <summary>
/// 无关项 - 不参与网格生成的大型构件(如地基、结构基础等)
/// </summary>
= 10
}
/// <summary>

View File

@ -111,16 +111,27 @@ namespace NavisworksTransport.PathPlanning
LogManager.Info("【生成网格地图】步骤1.5: 智能收集通道相关节点");
// 直接获取所有可通行的物流模型项
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems();
LogManager.Info($"【生成网格地图】直接获取到 {allChannelItems.Count} 个可通行物流元素");
var allTraversableItems = CategoryAttributeManager.GetAllTraversableLogisticsItems();
LogManager.Info($"【生成网格地图】直接获取到 {allTraversableItems.Count} 个可通行物流元素");
// 智能收集可通行模型相关节点(包括父节点、同级节点等)
var channelRelatedItems = CollectChannelRelatedItems(allChannelItems.ToList());
LogManager.Info($"【生成网格地图】智能收集到 {channelRelatedItems.Count} 个通道相关节点");
var traversableRelatedItems = CollectRelatedItems(allTraversableItems.ToList());
LogManager.Info($"【生成网格地图】智能收集到 {traversableRelatedItems.Count} 个可通行元素相关节点");
// 1.6. 批量获取所有"无关项"(不参与网格生成的大型构件)
LogManager.Info("【生成网格地图】步骤1.6: 批量获取无关项并收集相关节点");
var irrelevantItems = CategoryAttributeManager.GetLogisticsItemsByType(
CategoryAttributeManager.LogisticsElementType.);
LogManager.Info($"【生成网格地图】直接获取到 {irrelevantItems.Count} 个无关项");
// 智能收集无关项相关节点(包括子节点等)
var irrelevantRelatedItems = CollectRelatedItems(irrelevantItems);
LogManager.Info($"【生成网格地图】智能收集到 {irrelevantRelatedItems.Count} 个无关项相关节点(将被排除)");
var irrelevantItemsSet = irrelevantRelatedItems; // 使用收集后的完整集合
// 2. 直接遍历处理障碍物(包围盒检测) - 使用高性能优化版本
LogManager.Info("【生成网格地图】步骤2: 高性能包围盒遍历处理障碍物");
ProcessObstaclesWithBoundingBox(channelCoverage.GridMap, channelRelatedItems, scanHeightInModelUnits, channelCoverage);
ProcessObstaclesWithBoundingBox(channelCoverage.GridMap, traversableRelatedItems, scanHeightInModelUnits, channelCoverage, irrelevantItemsSet);
LogManager.Info($"【阶段2完成】障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
@ -131,7 +142,7 @@ namespace NavisworksTransport.PathPlanning
// 2.6. 单独处理门元素,设置为可通行,并设置通行高度
LogManager.Info("【生成网格地图】步骤2.6: 处理门元素");
ProcessDoorElements(channelCoverage.GridMap, allChannelItems);
ProcessDoorElements(channelCoverage.GridMap, allTraversableItems);
LogManager.Info($"【阶段2.6完成】门元素处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
// 3. 应用车辆尺寸膨胀(如果需要)
@ -156,10 +167,10 @@ namespace NavisworksTransport.PathPlanning
}
// 将通道数据设置到GridMap中供后续PathPlanningManager使用
if (channelRelatedItems != null && channelRelatedItems.Any())
if (traversableRelatedItems != null && traversableRelatedItems.Any())
{
channelCoverage.GridMap.ChannelItems = channelRelatedItems.ToList();
LogManager.Info($"【生成网格地图】已将 {channelRelatedItems.Count} 个通道数据设置到GridMap中");
channelCoverage.GridMap.ChannelItems = traversableRelatedItems.ToList();
LogManager.Info($"【生成网格地图】已将 {traversableRelatedItems.Count} 个通道数据设置到GridMap中");
}
else if (channelCoverage.ChannelItems != null && channelCoverage.ChannelItems.Any())
{
@ -214,12 +225,12 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 处理门元素,将其设置为可通行网格
/// </summary>
private void ProcessDoorElements(GridMap gridMap, ICollection<ModelItem> allChannelItems)
private void ProcessDoorElements(GridMap gridMap, ICollection<ModelItem> allTraversableItems)
{
try
{
// 过滤出门类型的元素
var doorItems = FilterDoorItems(allChannelItems);
var doorItems = FilterDoorItems(allTraversableItems);
LogManager.Info($"【门元素处理】找到 {doorItems.Count} 个门元素");
if (!doorItems.Any())
@ -920,23 +931,23 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 收集通道相关节点
/// 收集物流项目的相关节点
/// 根据节点类型决定收集策略:几何体节点收集父节点和同级节点,集合节点收集子节点
/// </summary>
/// <param name="channelItems">通道节点列表</param>
/// <param name="items">物流项目节点列表</param>
/// <returns>包含所有相关节点的集合</returns>
private HashSet<ModelItem> CollectChannelRelatedItems(List<ModelItem> channelItems)
private HashSet<ModelItem> CollectRelatedItems(List<ModelItem> items)
{
var relatedItems = new HashSet<ModelItem>();
foreach (var channelItem in channelItems)
foreach (var item in items)
{
try
{
LogManager.Info($"[通道收集] 开始处理通道节点: '{channelItem.DisplayName}'");
LogManager.Info($"[节点收集] 开始处理节点: '{item.DisplayName}'");
// 使用 ModelItemAnalysisHelper 的通用方法收集相关节点
var itemRelatedNodes = ModelItemAnalysisHelper.CollectRelatedNodes(channelItem);
var itemRelatedNodes = ModelItemAnalysisHelper.CollectRelatedNodes(item);
// 将结果添加到 HashSet 中
foreach (var node in itemRelatedNodes)
@ -944,17 +955,17 @@ namespace NavisworksTransport.PathPlanning
relatedItems.Add(node);
}
LogManager.Info($"[通道收集] 从 '{channelItem.DisplayName}' 收集到 {itemRelatedNodes.Count} 个相关节点");
LogManager.Info($"[节点收集] 从 '{item.DisplayName}' 收集到 {itemRelatedNodes.Count} 个相关节点");
}
catch (Exception ex)
{
LogManager.Warning($"[通道收集] 处理通道节点 '{channelItem?.DisplayName ?? "NULL"}' 时出错: {ex.Message}");
// 出错时至少保证通道本身被添加
relatedItems.Add(channelItem);
LogManager.Warning($"[节点收集] 处理节点 '{item?.DisplayName ?? "NULL"}' 时出错: {ex.Message}");
// 出错时至少保证节点本身被添加
relatedItems.Add(item);
}
}
LogManager.Info($"[通道收集] 共收集到 {relatedItems.Count} 个通道相关节点");
LogManager.Info($"[节点收集] 共收集到 {relatedItems.Count} 个相关节点");
return relatedItems;
}
@ -1088,9 +1099,10 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
private Dictionary<ModelItem, ItemProperties> PostProcessGeometryItems(
List<ModelItem> geometryItems,
HashSet<ModelItem> channelItemsSet,
HashSet<ModelItem> traversableItemsSet,
GridMap gridMap,
double scanHeightInModelUnits)
double scanHeightInModelUnits,
HashSet<ModelItem> irrelevantItemsSet)
{
LogManager.Info("[轻量级后处理] 开始处理Search API筛选结果");
var startTime = DateTime.Now;
@ -1099,6 +1111,7 @@ namespace NavisworksTransport.PathPlanning
var totalItems = geometryItems.Count;
var apiCalls = 0;
var skippedByChannel = 0;
var skippedByIrrelevant = 0;
var skippedByBoundingBox = 0;
foreach (var item in geometryItems)
@ -1111,7 +1124,7 @@ namespace NavisworksTransport.PathPlanning
HasGeometry = true,
// 快速筛除1通道相关项目HashSet查找极快
IsChannelItem = channelItemsSet.Contains(item)
IsChannelItem = traversableItemsSet.Contains(item)
};
if (properties.IsChannelItem)
{
@ -1119,13 +1132,21 @@ namespace NavisworksTransport.PathPlanning
continue; // 跳过通道项目不进行昂贵的API调用
}
properties.IsChildOfChannel = IsChildOfChannelItems(item, channelItemsSet);
properties.IsChildOfChannel = IsChildOfChannelItems(item, traversableItemsSet);
if (properties.IsChildOfChannel)
{
skippedByChannel++;
continue; // 跳过通道子项目
}
// 快速筛除2无关项不参与网格生成的大型构件
properties.IsIrrelevantItem = irrelevantItemsSet.Contains(item);
if (properties.IsIrrelevantItem)
{
skippedByIrrelevant++;
continue; // 跳过无关项避免昂贵的BoundingBox API调用
}
// 设为已知值(避免后续筛选中的判断错误)
properties.IsContainer = false; // 基于测试验证,几何体项目都不是容器
@ -1153,7 +1174,7 @@ namespace NavisworksTransport.PathPlanning
var elapsed = (DateTime.Now - startTime).TotalMilliseconds;
LogManager.Info($"[轻量级后处理] 完成,输入 {totalItems} 项,有效结果 {itemCache.Count} 项API调用 {apiCalls} 次,耗时: {elapsed:F1}ms");
LogManager.Info($"[轻量级后处理] 筛除统计 - 通道相关: {skippedByChannel}, 无边界框: {skippedByBoundingBox}");
LogManager.Info($"[轻量级后处理] 筛除统计 - 通道相关: {skippedByChannel}, 无关项: {skippedByIrrelevant}, 无边界框: {skippedByBoundingBox}");
LogManager.Info("[轻量级后处理] 已移除全局高度预过滤,将在障碍物遍历阶段进行精确高度检查");
return itemCache;
@ -1167,22 +1188,23 @@ namespace NavisworksTransport.PathPlanning
/// <param name="channelItems">通道模型项列表</param>
/// <param name="scanHeightInModelUnits">扫描高度范围(模型单位)</param>
/// <param name="channelCoverage">通道覆盖信息,包含通道边界</param>
private void ProcessObstaclesWithBoundingBox(GridMap gridMap, HashSet<ModelItem> channelItems, double scanHeightInModelUnits, ChannelCoverage channelCoverage)
/// <param name="irrelevantItemsSet">无关项集合(不参与网格生成的大型构件)</param>
private void ProcessObstaclesWithBoundingBox(GridMap gridMap, HashSet<ModelItem> channelItems, double scanHeightInModelUnits, ChannelCoverage channelCoverage, HashSet<ModelItem> irrelevantItemsSet)
{
try
{
LogManager.Info("[高性能障碍物处理] 开始处理障碍物 - Search API优化版本");
var startTime = DateTime.Now;
var channelItemsSet = channelItems ?? new HashSet<ModelItem>();
var traversableItemsSet = channelItems ?? new HashSet<ModelItem>();
// 阶段1Search API预筛选一次性批量获取几何体项目
var geometryItems = GetGeometryItemsUsingSearchAPI();
LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {channelItemsSet.Count}");
LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {traversableItemsSet.Count}");
// 阶段2轻量级后处理只对预筛选结果进行必要的API调用
var itemCache = PostProcessGeometryItems(geometryItems, channelItemsSet, gridMap, scanHeightInModelUnits);
var itemCache = PostProcessGeometryItems(geometryItems, traversableItemsSet, gridMap, scanHeightInModelUnits, irrelevantItemsSet);
// 阶段3条件过滤并行数值计算- 使用50%CPU核心优化
LogManager.Info("[高性能障碍物处理] 阶段3: 并行条件过滤");
@ -1490,16 +1512,16 @@ namespace NavisworksTransport.PathPlanning
/// 检查模型项是否是通道项的子项
/// </summary>
/// <param name="item">要检查的模型项</param>
/// <param name="channelItemsSet">通道项集合</param>
/// <param name="traversableItemsSet">通道项集合</param>
/// <returns>是否是通道项的子项</returns>
private bool IsChildOfChannelItems(ModelItem item, HashSet<ModelItem> channelItemsSet)
private bool IsChildOfChannelItems(ModelItem item, HashSet<ModelItem> traversableItemsSet)
{
try
{
var parent = item.Parent;
while (parent != null)
{
if (channelItemsSet.Contains(parent))
if (traversableItemsSet.Contains(parent))
{
return true;
}
@ -1526,6 +1548,7 @@ namespace NavisworksTransport.PathPlanning
public BoundingBox3D BoundingBox { get; set; }
public bool IsChannelItem { get; set; }
public bool IsChildOfChannel { get; set; }
public bool IsIrrelevantItem { get; set; }
public bool IsInScanHeightRange { get; set; }
}