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

381 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 多层建筑楼层过滤优化方案
## 背景
在多层建筑的路径规划中,当前系统处理所有楼层的障碍物,导致大量无关数据参与计算。如果能快速过滤掉其他楼层的障碍物,性能提升会非常显著。
## 当前实现状态
### 1. 楼层过滤已被禁用
```csharp
// GridMapGenerator.cs line 157-158
// TODO: 楼层过滤功能已暂时注释,需要时可重新启用
var modelItems = allModelItems; // 直接使用所有模型项,不进行楼层过滤
```
### 2. 现有方法的局限性
- **基于属性的过滤**`ApplyFloorAwareFilteringWithSearchAPI`
- 依赖模型的"层"属性
- 不是所有BIM模型都有规范的楼层属性
- 需要手动维护属性映射
- **简化高程过滤**`ApplySimplifiedFloorFiltering`
- 使用±10米容差
- 过于粗糙会包含2-3个楼层
- 无法精确定位单个楼层
## 优化方案
### 方案1基于Z轴高度的智能楼层识别
#### 1.1 自动检测楼层高度
```csharp
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 快速楼层过滤
```csharp
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 分层索引结构
```csharp
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 优化的垂直扫描
```csharp
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 三级过滤策略
```csharp
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倍的性能提升。建议根据实际需求逐步实施先验证简单方案的效果再考虑复杂优化。