11 KiB
11 KiB
多层建筑楼层过滤优化方案
背景
在多层建筑的路径规划中,当前系统处理所有楼层的障碍物,导致大量无关数据参与计算。如果能快速过滤掉其他楼层的障碍物,性能提升会非常显著。
当前实现状态
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/楼层
实施建议
第一阶段(立即可行)
- 重新启用现有的
GetFloorFilteredModelItems方法 - 将高程容差从±10米优化到±2米
- 添加楼层检测日志,收集实际使用数据
第二阶段(短期优化)
- 实现自动楼层检测
- 基于Z轴的精确楼层过滤
- 缓存楼层信息避免重复计算
第三阶段(长期优化)
- 实现分层空间哈希索引
- 支持跨楼层路径规划
- 优化楼梯/电梯节点处理
风险与对策
风险1:楼层识别错误
- 对策:提供手动楼层选择界面
风险2:跨楼层构件处理
- 对策:将跨楼层构件添加到所有相关楼层
风险3:非标准楼层高度
- 对策:自适应楼层高度检测
总结
楼层过滤是多层建筑路径规划的关键优化点。通过智能楼层识别和分层索引,可以实现10-40倍的性能提升。建议根据实际需求逐步实施,先验证简单方案的效果,再考虑复杂优化。