NavisworksTransport/doc/working/多层建筑楼层过滤优化方案.md

11 KiB
Raw Blame History

多层建筑楼层过滤优化方案

背景

在多层建筑的路径规划中,当前系统处理所有楼层的障碍物,导致大量无关数据参与计算。如果能快速过滤掉其他楼层的障碍物,性能提升会非常显著。

当前实现状态

1. 楼层过滤已被禁用

// GridMapGenerator.cs line 157-158
// TODO: 楼层过滤功能已暂时注释,需要时可重新启用
var modelItems = allModelItems; // 直接使用所有模型项,不进行楼层过滤

2. 现有方法的局限性

  • 基于属性的过滤ApplyFloorAwareFilteringWithSearchAPI

    • 依赖模型的"层"属性
    • 不是所有BIM模型都有规范的楼层属性
    • 需要手动维护属性映射
  • 简化高程过滤ApplySimplifiedFloorFiltering

    • 使用±10米容差
    • 过于粗糙会包含2-3个楼层
    • 无法精确定位单个楼层

优化方案

方案1基于Z轴高度的智能楼层识别

1.1 自动检测楼层高度

private class FloorDetector
{
    // 自动识别建筑中的楼层
    public List<FloorInfo> DetectFloors(List<ModelItem> allItems)
    {
        var floors = new List<FloorInfo>();
        
        // 1. 收集所有楼板类构件
        var slabs = allItems.Where(item => IsFloorSlab(item)).ToList();
        
        // 2. 提取楼板顶面Z坐标
        var slabZValues = slabs
            .Select(s => s.BoundingBox()?.Max.Z)
            .Where(z => z.HasValue)
            .Select(z => z.Value)
            .OrderBy(z => z)
            .ToList();
        
        // 3. 聚类相近Z值识别独立楼层
        var floorLevels = ClusterZValues(slabZValues, tolerance: 0.5);
        
        // 4. 为每个楼层创建信息
        for (int i = 0; i < floorLevels.Count; i++)
        {
            floors.Add(new FloorInfo
            {
                Index = i,
                BaseZ = floorLevels[i],
                Height = i < floorLevels.Count - 1 
                    ? floorLevels[i + 1] - floorLevels[i]
                    : 4.0, // 默认楼层高度
                Name = $"第{i + 1}层"
            });
        }
        
        return floors;
    }
    
    // 判断是否为楼板
    private bool IsFloorSlab(ModelItem item)
    {
        var name = item.DisplayName?.ToLower() ?? "";
        var className = item.ClassName?.ToLower() ?? "";
        
        // 楼板关键词
        string[] slabKeywords = {
            "floor", "楼板", "slab", "板",
            "deck", "地面", "楼面"
        };
        
        if (slabKeywords.Any(k => name.Contains(k) || className.Contains(k)))
        {
            var bbox = item.BoundingBox();
            if (bbox != null)
            {
                // 楼板特征:水平、薄
                double thickness = bbox.Max.Z - bbox.Min.Z;
                double width = bbox.Max.X - bbox.Min.X;
                double length = bbox.Max.Y - bbox.Min.Y;
                
                // 厚度小,面积大
                return thickness < 0.5 && width > 2.0 && length > 2.0;
            }
        }
        
        return false;
    }
}

1.2 快速楼层过滤

private class FloorFilter
{
    private List<FloorInfo> _floors;
    private int _targetFloorIndex;
    
    public FloorFilter(List<FloorInfo> floors, Point3D startPoint, Point3D endPoint)
    {
        _floors = floors;
        _targetFloorIndex = DetermineTargetFloor(startPoint, endPoint);
    }
    
    // 确定路径所在楼层
    private int DetermineTargetFloor(Point3D startPoint, Point3D endPoint)
    {
        double avgZ = (startPoint.Z + endPoint.Z) / 2.0;
        
        // 找到最接近的楼层
        for (int i = 0; i < _floors.Count; i++)
        {
            var floor = _floors[i];
            if (avgZ >= floor.BaseZ && avgZ < floor.BaseZ + floor.Height)
            {
                return i;
            }
        }
        
        // 如果没找到,返回最接近的
        return _floors
            .Select((f, i) => new { Index = i, Distance = Math.Abs(f.BaseZ - avgZ) })
            .OrderBy(x => x.Distance)
            .First().Index;
    }
    
    // 快速判断模型项是否在目标楼层
    public bool IsOnTargetFloor(ModelItem item)
    {
        var bbox = item.BoundingBox();
        if (bbox == null) return false;
        
        var targetFloor = _floors[_targetFloorIndex];
        double floorBottom = targetFloor.BaseZ - 0.5; // 容差
        double floorTop = targetFloor.BaseZ + targetFloor.Height + 0.5;
        
        // 检查是否与楼层范围有交集
        return bbox.Max.Z >= floorBottom && bbox.Min.Z <= floorTop;
    }
    
    // 批量过滤
    public List<ModelItem> FilterItems(List<ModelItem> allItems)
    {
        return allItems.Where(IsOnTargetFloor).ToList();
    }
}

方案2分层空间哈希索引

2.1 分层索引结构

public class LayeredSpatialHashIndex
{
    // 每个楼层有独立的空间哈希表
    private Dictionary<int, SpatialHashTable> _floorHashTables;
    private List<FloorInfo> _floors;
    private double _hashCellSize;
    
    public LayeredSpatialHashIndex(double cellSize)
    {
        _hashCellSize = cellSize;
        _floorHashTables = new Dictionary<int, SpatialHashTable>();
    }
    
    // 构建分层索引
    public void BuildIndex(List<ModelItem> items, List<FloorInfo> floors)
    {
        _floors = floors;
        
        // 为每个楼层创建独立的哈希表
        foreach (var floor in floors)
        {
            _floorHashTables[floor.Index] = new SpatialHashTable(_hashCellSize);
        }
        
        // 将模型项分配到对应楼层
        foreach (var item in items)
        {
            var bbox = item.BoundingBox();
            if (bbox == null) continue;
            
            // 确定物体所属楼层(可能跨越多个楼层)
            var overlappingFloors = GetOverlappingFloors(bbox);
            
            foreach (var floorIndex in overlappingFloors)
            {
                _floorHashTables[floorIndex].Add(item, bbox);
            }
        }
        
        LogManager.Info($"[分层索引] 构建完成:{floors.Count}个楼层,{items.Count}个模型项");
    }
    
    // 查询特定楼层的候选项
    public IEnumerable<ModelItem> QueryFloor(Point3D point, int floorIndex)
    {
        if (_floorHashTables.TryGetValue(floorIndex, out var hashTable))
        {
            return hashTable.Query(point);
        }
        return Enumerable.Empty<ModelItem>();
    }
    
    // 获取物体跨越的楼层
    private List<int> GetOverlappingFloors(BoundingBox3D bbox)
    {
        var floors = new List<int>();
        
        for (int i = 0; i < _floors.Count; i++)
        {
            var floor = _floors[i];
            double floorBottom = floor.BaseZ - 0.5;
            double floorTop = floor.BaseZ + floor.Height + 0.5;
            
            if (bbox.Max.Z >= floorBottom && bbox.Min.Z <= floorTop)
            {
                floors.Add(i);
            }
        }
        
        return floors;
    }
}

2.2 优化的垂直扫描

public class FloorAwareVerticalScanner
{
    private LayeredSpatialHashIndex _layeredIndex;
    private int _targetFloorIndex;
    private FloorInfo _targetFloor;
    
    // 限制扫描高度到单个楼层
    public List<HeightInterval> ScanVerticalLine(Point3D basePoint)
    {
        // 只扫描目标楼层的高度范围
        double scanMinZ = _targetFloor.BaseZ;
        double scanMaxZ = _targetFloor.BaseZ + _targetFloor.Height;
        
        // 只从目标楼层的空间哈希获取候选项
        var candidates = _layeredIndex.QueryFloor(basePoint, _targetFloorIndex);
        
        // 高度筛选(范围已经大幅缩小)
        var filtered = candidates.Where(item =>
        {
            var bbox = item.BoundingBox();
            return bbox != null && 
                   bbox.Max.Z >= scanMinZ && 
                   bbox.Min.Z <= scanMaxZ;
        });
        
        // 执行相交测试(候选项大幅减少)
        return PerformIntersectionTests(filtered, basePoint, scanMaxZ - scanMinZ);
    }
}

方案3渐进式楼层过滤

3.1 三级过滤策略

public class ProgressiveFloorFilter
{
    // 第一级:粗略高程过滤(构建索引前)
    public List<ModelItem> CoarseFilter(List<ModelItem> items, double targetZ)
    {
        const double coarseTolerance = 5.0; // 5米容差
        
        return items.Where(item =>
        {
            var bbox = item.BoundingBox();
            if (bbox == null) return true; // 保留无边界框的项
            
            // 快速排除明显不在目标楼层的项
            return bbox.Max.Z >= targetZ - coarseTolerance && 
                   bbox.Min.Z <= targetZ + coarseTolerance;
        }).ToList();
    }
    
    // 第二级:精确楼层过滤(空间哈希构建时)
    public List<ModelItem> PreciseFilter(List<ModelItem> items, FloorInfo targetFloor)
    {
        return items.Where(item =>
        {
            var bbox = item.BoundingBox();
            if (bbox == null) return false;
            
            double floorBottom = targetFloor.BaseZ - 0.2;
            double floorTop = targetFloor.BaseZ + targetFloor.Height + 0.2;
            
            return bbox.Max.Z >= floorBottom && bbox.Min.Z <= floorTop;
        }).ToList();
    }
    
    // 第三级:垂直扫描时的高度限制
    public bool ShouldProcess(ModelItem item, double scanZ, double floorHeight)
    {
        var bbox = item.BoundingBox();
        if (bbox == null) return false;
        
        // 只处理与扫描高度相关的项
        return bbox.Max.Z >= scanZ && bbox.Min.Z <= scanZ + floorHeight;
    }
}

性能分析

不同场景的性能提升预期

建筑类型 楼层数 无过滤 简化过滤 精确过滤 分层索引
单层建筑 1 基准 无提升 无提升 无提升
低层建筑 3-5 基准 1.5-2倍 3-4倍 4-5倍
高层建筑 10-20 基准 2-3倍 10-15倍 15-20倍
超高层 30+ 基准 3-4倍 20-30倍 30-40倍

内存开销分析

  • 基础过滤:几乎无额外内存
  • 精确过滤楼层信息缓存约1KB/楼层
  • 分层索引每层独立哈希表约100KB/楼层

实施建议

第一阶段(立即可行)

  1. 重新启用现有的GetFloorFilteredModelItems方法
  2. 将高程容差从±10米优化到±2米
  3. 添加楼层检测日志,收集实际使用数据

第二阶段(短期优化)

  1. 实现自动楼层检测
  2. 基于Z轴的精确楼层过滤
  3. 缓存楼层信息避免重复计算

第三阶段(长期优化)

  1. 实现分层空间哈希索引
  2. 支持跨楼层路径规划
  3. 优化楼梯/电梯节点处理

风险与对策

风险1楼层识别错误

  • 对策:提供手动楼层选择界面

风险2跨楼层构件处理

  • 对策:将跨楼层构件添加到所有相关楼层

风险3非标准楼层高度

  • 对策:自适应楼层高度检测

总结

楼层过滤是多层建筑路径规划的关键优化点。通过智能楼层识别和分层索引可以实现10-40倍的性能提升。建议根据实际需求逐步实施先验证简单方案的效果再考虑复杂优化。