195 lines
5.7 KiB
Markdown
195 lines
5.7 KiB
Markdown
# 多高度层GridMap解决方案
|
||
|
||
**日期**: 2025-01-08
|
||
**问题**: 楼梯与楼板在同一(x,y)坐标重叠时,2D网格投影导致互相覆盖
|
||
**目标**: 支持多高度层,同时保留RoyT.AStar高性能算法
|
||
|
||
---
|
||
|
||
## 问题分析
|
||
|
||
### 场景描述
|
||
- **楼梯**: 倾斜通道,在(x,y)位置有不同Z高度
|
||
- **楼梯下方楼板**: 水平通道,在相同(x,y)位置也可通行
|
||
- **当前问题**: 两者投影到同一2D网格坐标(x,y),后处理的覆盖前处理的
|
||
|
||
### 核心矛盾
|
||
- 2D网格投影系统无法处理垂直空间层叠
|
||
- 起点在楼面(Z=3.0m),终点在楼梯(Z=5.0m)的路径无法正确规划
|
||
|
||
---
|
||
|
||
## 解决方案
|
||
|
||
### 核心思路
|
||
1. **保留RoyT.AStar**: 2D A*算法不变,继续产生最优2D路径
|
||
2. **扩展数据结构**: GridCell支持多个HeightLayer
|
||
3. **投影改为追加**: 通道投影不覆盖,而是添加新层
|
||
4. **路径后处理选层**: A*产生2D路径后,为每个路径点智能选择高度层
|
||
|
||
### 关键洞察
|
||
- **2D路径已经最优**: RoyT.AStar产生的2D网格序列`[(x1,y1), (x2,y2), ..., (xn,yn)]`不需要改变
|
||
- **只需选Z坐标**: 为每个2D点选择正确的高度层,形成3D路径
|
||
- **基于高度趋势**: 根据当前位置到终点的剩余高度变化,选择最合适的层
|
||
|
||
---
|
||
|
||
## 技术设计
|
||
|
||
### 1. 数据结构扩展
|
||
|
||
#### HeightLayer结构
|
||
```csharp
|
||
public struct HeightLayer
|
||
{
|
||
public double Z; // 该层的Z坐标
|
||
public HeightInterval PassableHeight; // 可通行高度范围(用于物体检查)
|
||
public ModelItem SourceItem; // 来源模型项
|
||
public double SpeedLimit; // 限速
|
||
public CategoryAttributeManager.LogisticsElementType Type; // 层类型
|
||
}
|
||
```
|
||
|
||
#### GridCell扩展
|
||
```csharp
|
||
public struct GridCell
|
||
{
|
||
// ... 保留所有现有字段 ...
|
||
|
||
// 新增:多高度层支持
|
||
public List<HeightLayer> HeightLayers { get; set; }
|
||
}
|
||
```
|
||
|
||
### 2. 通道投影改为追加模式
|
||
|
||
**文件**: `src/PathPlanning/ChannelBasedGridBuilder.cs`
|
||
**方法**: `RasterizeTriangleToGrid()`
|
||
|
||
**修改前**:
|
||
```csharp
|
||
var cell = GridCellBuilder.Channel(preciseWorldPosition, channel, channelSpeedLimit);
|
||
gridMap.PlaceCell(gridPos, cell); // 覆盖
|
||
```
|
||
|
||
**修改后**:
|
||
```csharp
|
||
var layer = new HeightLayer
|
||
{
|
||
Z = gridCenterHeight,
|
||
PassableHeight = new HeightInterval(0, 0), // 初始化,后续设置
|
||
SourceItem = channel,
|
||
SpeedLimit = channelSpeedLimit,
|
||
Type = CategoryAttributeManager.GetLogisticsElementType(channel)
|
||
};
|
||
gridMap.AddHeightLayer(gridPos, layer); // 追加
|
||
```
|
||
|
||
### 3. PassableHeight计算适配
|
||
|
||
**保留现有逻辑**: `SetChannelPassableHeights()` 已经很完善
|
||
**修改点**: 从设置单层GridCell改为设置多个HeightLayer
|
||
|
||
```csharp
|
||
// 为每个高度层设置PassableHeight
|
||
if (cell.HeightLayers != null && cell.HeightLayers.Any())
|
||
{
|
||
for (int i = 0; i < cell.HeightLayers.Count; i++)
|
||
{
|
||
var layer = cell.HeightLayers[i];
|
||
layer.PassableHeight = new HeightInterval(0, scanHeightInModelUnits);
|
||
cell.HeightLayers[i] = layer;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 路径后处理智能选层
|
||
|
||
**核心算法**: `AssignLayerBased3DCoordinates()`
|
||
|
||
**选层策略**:
|
||
```
|
||
1. 起点锁定: 选择包含start.Z的层
|
||
2. 终点锁定: 选择包含end.Z的层
|
||
3. 中间点选层:
|
||
- 计算剩余高度变化: remainingHeight = end.Z - current.Z
|
||
- 计算期望每步变化: desiredChange = remainingHeight / remainingSteps
|
||
- 目标Z: targetZ = prevZ + desiredChange
|
||
- 选择最接近targetZ且净空足够的层
|
||
```
|
||
|
||
**示例执行**:
|
||
```
|
||
场景: 起点楼面(3.0m) → 终点楼梯(5.0m)
|
||
2D路径: [(0,0), (1,1), (2,2), (3,3)]
|
||
|
||
赋值过程:
|
||
1. (0,0): Z=3.0 (起点锁定楼面层)
|
||
2. (1,1): remainingHeight=2.0, steps=2, desiredChange=1.0
|
||
targetZ=4.0, 选择楼梯层3.5m (更接近4.0)
|
||
3. (2,2): remainingHeight=1.5, steps=1, desiredChange=1.5
|
||
targetZ=5.0, 选择楼梯层4.5m
|
||
4. (3,3): Z=5.0 (终点锁定楼梯层)
|
||
|
||
最终路径: [(0,0,3.0), (1,1,3.5), (2,2,4.5), (3,3,5.0)]
|
||
✅ 平滑爬升!
|
||
```
|
||
|
||
---
|
||
|
||
## 实施计划
|
||
|
||
### 步骤1: 扩展GridCell支持多层
|
||
- **文件**: `src/PathPlanning/GridMap.cs`
|
||
- **内容**: 新增HeightLayer结构、扩展GridCell、添加AddHeightLayer()和FindLayerContainingZ()方法
|
||
|
||
### 步骤2: 通道投影改为追加模式
|
||
- **文件**: `src/PathPlanning/ChannelBasedGridBuilder.cs`
|
||
- **内容**: 修改RasterizeTriangleToGrid(),从PlaceCell改为AddHeightLayer
|
||
|
||
### 步骤3: 适配PassableHeight计算
|
||
- **文件**: `src/PathPlanning/GridMapGenerator.cs`
|
||
- **内容**: 修改SetChannelPassableHeights()支持多层
|
||
|
||
### 步骤4: 实现智能选层算法
|
||
- **文件**: `src/PathPlanning/AutoPathFinder.cs`
|
||
- **内容**: 新增AssignLayerBased3DCoordinates()和SelectBestLayer()方法,替换现有路径后处理
|
||
|
||
### 步骤5: 测试验证
|
||
- 起点楼面 → 终点楼梯(验证爬升)
|
||
- 起点楼梯 → 终点楼面(验证下降)
|
||
- 楼梯下方通行(验证多层并存)
|
||
|
||
---
|
||
|
||
## 关键约束
|
||
|
||
1. **保留RoyT.AStar**: 不修改A*算法,保持高性能
|
||
2. **不考虑向后兼容**: 直接改为多层架构
|
||
3. **保留现有PassableHeight逻辑**: 只适配到多层结构
|
||
4. **最小化改动**: 只修改必要的数据结构和投影逻辑
|
||
|
||
---
|
||
|
||
## 预期效果
|
||
|
||
- ✅ 解决楼梯-楼板重叠问题
|
||
- ✅ 自动选择最优高度层
|
||
- ✅ 平滑的高度过渡
|
||
- ✅ 保持RoyT.AStar高性能
|
||
- ✅ 代码改动最小化
|
||
|
||
---
|
||
|
||
## 文件修改清单
|
||
|
||
1. `src/PathPlanning/GridMap.cs` - 数据结构扩展
|
||
2. `src/PathPlanning/ChannelBasedGridBuilder.cs` - 投影逻辑修改
|
||
3. `src/PathPlanning/GridMapGenerator.cs` - PassableHeight计算适配
|
||
4. `src/PathPlanning/AutoPathFinder.cs` - 路径后处理智能选层
|
||
|
||
---
|
||
|
||
**状态**: 设计完成,待实施
|
||
**预计工作量**: 4个文件,约200行代码修改
|