381 lines
11 KiB
Markdown
381 lines
11 KiB
Markdown
# 多层建筑楼层过滤优化方案
|
||
|
||
## 背景
|
||
|
||
在多层建筑的路径规划中,当前系统处理所有楼层的障碍物,导致大量无关数据参与计算。如果能快速过滤掉其他楼层的障碍物,性能提升会非常显著。
|
||
|
||
## 当前实现状态
|
||
|
||
### 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倍的性能提升。建议根据实际需求逐步实施,先验证简单方案的效果,再考虑复杂优化。
|