# 空轨支持方案
## 一、需求分析
### 1.1 核心需求
1. **新增空轨属性**:在物流属性系统中增加"空轨"类型
2. **空轨路径生成**:
- 空轨走向固定,只需设置起点和终点
- 自动提取空轨几何体的下表面中心线作为路径
3. **路径类型区分**:路径需要标记为"地面路径"或"空轨路径"
4. **动画运行模式**:
- 地面路径:物体中心点在路径点上
- 空轨路径:物体悬挂在空轨下方运行(物体中心 = 路径点 - 物体高度/2)
5. **碰撞检测**:
- 统一使用3D碰撞检测
- 空轨路径需要排除空轨本身
6. **限制条件**:空轨暂时只支持人工路径,不支持自动路径规划
### 1.2 使用场景
- **地面路径**:转运车在楼面、走廊、通道上运行
- **空轨路径**:悬挂式输送设备在空轨上运行
### 1.3 已确认信息
1. ✅ **空轨模型**:已存在,不需要建模规范
2. ✅ **空轨类型**:不需要分类型,只有一个"空轨"类型
3. ✅ **物体悬挂**:物体悬挂在空轨下方
4. ✅ **碰撞检测**:需要排除空轨本身
5. ✅ **数据库迁移**:不需要,新字段使用默认值
6. ✅ **向后兼容**:不需要向后兼容性
7. ✅ **碰撞检测统一**:地面路径和空轨路径都使用3D碰撞检测
8. ✅ **轨道几何**:轨道不是水平的,需要精确计算几何体数据
---
## 二、架构影响分析
### 2.1 涉及的核心模块
| 模块 | 影响程度 | 改动内容 |
|------|---------|---------|
| **物流属性系统** | 🟢 低 | 新增"空轨"枚举值 |
| **路径数据模型** | 🟡 中 | 新增路径类型字段 |
| **路径规划系统** | 🟡 中 | 新增空轨路径生成逻辑 |
| **动画系统** | 🟡 中 | 根据路径类型调整物体位置 |
| **碰撞检测系统** | 🟢 低 | 空轨路径排除空轨本身 |
| **UI界面** | 🟡 中 | 路径类型选择、空轨路径生成 |
---
## 三、详细实施方案
### 3.1 第一阶段:基础数据结构(1天)
#### 3.1.1 新增空轨属性
**文件**:`CategoryAttributeManager.cs`
```csharp
public enum LogisticsElementType
{
// ... 现有类型 ...
///
/// 空轨 - 空中运输路径,物体悬挂运行,权重0.7
///
空轨 = 9,
}
```
#### 3.1.2 路径类型枚举
**文件**:`PathPlanningModels.cs`
```csharp
///
/// 路径类型
///
public enum PathType
{
///
/// 地面路径 - 物体在地面运行
///
Ground = 0,
///
/// 空轨路径 - 物体悬挂在空轨下方运行
///
Rail = 1,
}
```
#### 3.1.3 路径数据模型扩展
**文件**:`PathRouteViewModel.cs`
```csharp
private PathType _pathType = PathType.Ground;
public PathType PathType
{
get => _pathType;
set => SetProperty(ref _pathType, value);
}
```
#### 3.1.4 路径数据库适配
**文件**:`PathDatabase.cs`
```csharp
// 保存路径时,保存路径类型
public void SavePath(PathRouteViewModel path)
{
// ... 现有逻辑 ...
// 新增:保存路径类型
}
// 加载路径时,读取路径类型
public PathRouteViewModel LoadPath(string pathId)
{
// ... 现有逻辑 ...
// 新增:读取路径类型
}
```
---
### 3.2 第二阶段:空轨路径生成(2天)
#### 3.2.1 复用现有几何计算
**现有代码复用**:
- `ChannelHeightDetector.AnalyzeChannelGeometry()` - 分析通道几何
- `ChannelHeightDetector.CalculatePreciseHeightAtPosition()` - 计算精确高度
- `GeometryHelper.ExtractTriangles()` - 提取三角形
- `GeometryHelper.RayTriangleIntersect()` - 射线-三角形交点检测
#### 3.2.2 新增底部高度检测方法
**文件**:`ChannelHeightDetector.cs`
新增方法:
- `GetChannelBottomHeight(Point3D position, IEnumerable channelItems)` - 获取通道底面高度
- `CalculateBottomPreciseHeightAtPosition(Point3D position, ChannelHeightInfo heightInfo, ModelItem channel)` - 计算底面精确高度
- `TryRaycastFromBelow(Point3D position, ModelItem channel, double floorHeight, out double height)` - 从下方射线投射
- `PerformRayTriangleIntersectionFromBelow(Point3D position, List triangles)` - 从下往上射线-三角形交点检测
- `TryGetGeometricBottomHeight(Point3D position, ModelItem channel, out double height)` - View API获取底面高度
- `AnalyzeBottomSurfaceHeightAtPosition(Point3D position, ModelItem channel, double floorHeight, double ceilingHeight)` - 分析底面表面高度
- `TryMultiPointBottomSampling(Point3D position, ModelItem channel)` - 多点采样获取底面高度
#### 3.2.3 空轨中心线提取工具
**文件**:`RailGeometryHelper.cs`(新建)
```csharp
///
/// 空轨几何体辅助工具
/// 负责提取空轨的下表面中心线
///
public static class RailGeometryHelper
{
///
/// 从空轨几何体提取下表面中心线
///
/// 空轨模型项
/// 起点(2D坐标)
/// 终点(2D坐标)
/// 路径点列表(包含精确的Z坐标)
public static List ExtractRailCenterLine(
ModelItem railModel,
Point3D startPoint,
Point3D endPoint)
{
var pathPoints = new List();
try
{
LogManager.Debug($"[空轨] 开始提取空轨中心线: {railModel.DisplayName}");
// 1. 创建高度检测器实例
var heightDetector = new ChannelHeightDetector();
var railItems = new List { railModel };
// 2. 计算起点和终点在X-Y平面上的投影距离
double distance2D = Math.Sqrt(
Math.Pow(endPoint.X - startPoint.X, 2) +
Math.Pow(endPoint.Y - startPoint.Y, 2)
);
if (distance2D < 0.01)
{
LogManager.Warning("[空轨] 起点和终点重合");
return pathPoints;
}
// 3. 在起点和终点之间采样,计算每个采样点的精确Z坐标
int sampleCount = Math.Max(2, (int)Math.Ceiling(distance2D / 0.5)); // 每0.5米一个采样点
for (int i = 0; i < sampleCount; i++)
{
double t = (double)i / (sampleCount - 1);
// 线性插值计算X、Y坐标
double x = startPoint.X + t * (endPoint.X - startPoint.X);
double y = startPoint.Y + t * (endPoint.Y - startPoint.Y);
double z = startPoint.Z; // 初始Z坐标
// 调用现有的GetChannelBottomHeight方法获取精确底面高度
var samplePoint = new Point3D(x, y, z); // Z坐标会被方法更新
var bottomHeight = heightDetector.GetChannelBottomHeight(samplePoint, railItems);
pathPoints.Add(new Point3D(x, y, bottomHeight));
}
LogManager.Debug($"[空轨] 生成 {pathPoints.Count} 个路径点,总长度: {distance2D:F2}m");
}
catch (Exception ex)
{
LogManager.Error($"[空轨] 提取空轨中心线失败: {ex.Message}");
}
return pathPoints;
}
}
```
#### 3.2.4 路径编辑器扩展
**文件**:`PathEditingViewModel.cs`
新增方法:
- `GeneratePathFromRail(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 从空轨生成路径
- `SelectRailModelCommand` - 选择空轨命令
- `SetPathStartPointCommand` - 设置起点命令
- `SetPathEndPointCommand` - 设置终点命令
- `GeneratePathFromRailCommand` - 生成路径命令
新增属性:
- `SelectedPathType` - 选中的路径类型
- `PathTypes` - 路径类型数组
- `IsRailPathType` - 是否为空轨路径类型
- `SelectedRailModel` - 选中的空轨模型
- `SelectedRailModelName` - 选中的空轨模型名称
- `PathStartPoint` - 路径起点
- `PathStartPointDisplay` - 路径起点显示
- `PathEndPoint` - 路径终点
- `PathEndPointDisplay` - 路径终点显示
---
### 3.3 第三阶段:动画系统适配(1天)
#### 3.3.1 物体位置计算
**文件**:`PathAnimationManager.cs`
```csharp
///
/// 计算物体在路径点上的位置
///
private Point3D CalculateObjectPosition(Point3D pathPoint, PathType pathType)
{
if (pathType == PathType.Rail)
{
// 空轨路径:物体悬挂在空轨下方
// 物体中心点 = 空轨点 - 物体高度的一半
double objectCenterZ = pathPoint.Z - _virtualObjectHeight / 2;
return new Point3D(pathPoint.X, pathPoint.Y, objectCenterZ);
}
else
{
// 地面路径:物体中心点在路径点上
return pathPoint;
}
}
```
#### 3.3.2 传递路径类型
**文件**:`PathAnimationManager.cs`
修改方法:
- `GenerateAnimationFrames(List pathPoints, PathType pathType)` - 传递路径类型参数
- `PrecomputeCollisions(List pathPoints, PathType pathType, List railModels)` - 传递路径类型和空轨模型列表
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List collisionExclusions)` - 传递路径类型和排除列表
---
### 3.4 第四阶段:碰撞检测适配(1天)
#### 3.4.1 空轨排除逻辑
**文件**:`PathAnimationManager.cs`
修改方法:
- `PrecomputeCollisions(List pathPoints, PathType pathType, List railModels)` - 新增空轨排除列表
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List collisionExclusions)` - 使用排除列表过滤
**核心逻辑**:
```csharp
// 创建碰撞排除列表
var collisionExclusions = new List();
// 如果是空轨路径,将空轨模型加入排除列表
if (pathType == PathType.Rail && railModels != null)
{
collisionExclusions.AddRange(railModels);
LogManager.Info($"[碰撞检测] 已排除 {railModels.Count} 个空轨模型");
}
// 在碰撞检测循环中跳过排除列表中的项目
foreach (var modelItem in allModelItems)
{
// 跳过排除列表中的项目(包括空轨本身)
if (collisionExclusions.Contains(modelItem))
{
continue;
}
// 统一使用3D碰撞检测
double distance = BoundingBoxGeometryUtils.CalculateDistance(objectBoundingBox, modelBoundingBox);
if (distance <= 0) // 相交或接触
{
collisions.Add(new CollisionResult
{
Item1 = _animatedObject,
Item2 = modelItem,
Distance = distance
});
}
}
```
---
### 3.5 第五阶段:UI界面适配(1天)
#### 3.5.1 路径类型选择
**文件**:`PathEditingViewModel.cs`
新增属性:
- `PathTypes` - 路径类型数组
- `SelectedPathType` - 选中的路径类型
- `IsRailPathType` - 是否为空轨路径类型
#### 3.5.2 命令
**文件**:`PathEditingViewModel.cs`
新增命令:
- `GeneratePathFromRailCommand` - 从空轨生成路径命令
- `SelectRailModelCommand` - 选择空轨命令
- `SetPathStartPointCommand` - 设置起点命令
- `SetPathEndPointCommand` - 设置终点命令
#### 3.5.3 UI布局
**文件**:`LogisticsControlPanel.xaml`
新增控件:
- 路径类型选择 ComboBox
- 空轨选择 StackPanel(仅在选择空轨类型时显示)
- 选择空轨按钮
- 设置起点按钮
- 设置终点按钮
- 生成路径按钮
---
## 四、实施步骤
### 第一阶段:基础数据结构(1天)
1. ✅ 新增"空轨"物流属性类型
2. ✅ 新增路径类型枚举
3. ✅ 扩展路径数据模型
4. ✅ 适配数据库读写
### 第二阶段:空轨路径生成(2天)
1. ✅ 在 `ChannelHeightDetector` 中新增底部高度检测方法
2. ✅ 复用现有的几何计算代码
3. ✅ 实现 `RailGeometryHelper`(调用现有方法)
4. ✅ 扩展路径编辑器
5. ✅ UI界面适配
6. ✅ 集成测试
### 第三阶段:动画系统适配(1天)
1. ✅ 物体位置计算适配
2. ✅ 传递路径类型参数
3. ✅ 动画播放测试
### 第四阶段:碰撞检测适配(1天)
1. ✅ 实现空轨排除逻辑
2. ✅ 统一使用3D碰撞检测
3. ✅ 碰撞报告适配
4. ✅ 集成测试
### 第五阶段:测试和优化(1天)
1. ✅ 功能测试
2. ✅ 性能测试
3. ✅ 用户验收测试
**总计**:6天
---
## 五、风险评估
| 风险项 | 风险等级 | 缓解措施 |
|--------|---------|---------|
| 空轨几何体提取失败 | 🟢 低 | 空轨模型已存在,直接使用 |
| 物体悬挂位置计算错误 | 🟡 中 | 充分测试,提供可视化验证 |
| 碰撞检测误报 | 🟡 中 | 排除空轨本身,调整检测参数 |
| 性能下降 | 🟢 低 | 空轨路径数量少,影响有限 |
| 几何计算性能 | 🟡 中 | 复用现有代码,优化采样策略 |
---
## 六、总结
### 6.1 方案优势
- ✅ **代码复用最大化**:充分利用现有的几何计算代码
- ✅ **架构统一**:空轨和地面路径使用相同的高度检测框架
- ✅ **改动最小化**:只需新增底部检测方法,其他全部复用
- ✅ **易于维护**:几何计算逻辑集中管理
- ✅ **向后兼容**:不需要迁移,新字段使用默认值
- ✅ **碰撞检测统一**:统一使用3D检测,简化逻辑
### 6.2 关键技术点
1. **复用现有代码**:`ChannelHeightDetector` 的几何计算方法
2. **新增底部检测**:`GetChannelBottomHeight()` 方法
3. **物体悬挂计算**:物体中心 = 空轨点 - 物体高度/2
4. **碰撞检测统一**:统一使用3D检测
5. **排除空轨本身**:通过排除列表过滤空轨模型
6. **精确几何计算**:支持倾斜、弯曲的轨道
### 6.3 技术难点
- 需要处理大量三角形面片(性能优化)
- 需要处理重叠三角形(选择最高点)
- 需要处理采样点不在任何三角形内的情况(使用默认值)
### 6.4 预期成果
- ✅ 支持空轨路径生成
- ✅ 支持物体悬挂运行
- ✅ 支持空轨碰撞检测(排除空轨本身)
- ✅ 保持与现有地面路径功能的兼容性
---
## 七、附录
### 7.1 文件清单
#### 新增文件
- `RailGeometryHelper.cs` - 空轨几何体辅助工具
#### 修改文件
- `CategoryAttributeManager.cs` - 新增空轨枚举
- `PathPlanningModels.cs` - 新增路径类型枚举
- `PathRouteViewModel.cs` - 新增路径类型字段
- `PathDatabase.cs` - 适配路径类型读写
- `ChannelHeightDetector.cs` - 新增底部高度检测方法
- `PathEditingViewModel.cs` - 扩展路径编辑功能
- `PathAnimationManager.cs` - 适配物体位置和碰撞检测
- `LogisticsControlPanel.xaml` - UI界面适配
### 7.2 方法清单
#### 新增方法(ChannelHeightDetector.cs)
- `GetChannelBottomHeight(Point3D position, IEnumerable channelItems)` - 获取通道底面高度
- `CalculateBottomPreciseHeightAtPosition(Point3D position, ChannelHeightInfo heightInfo, ModelItem channel)` - 计算底面精确高度
- `TryRaycastFromBelow(Point3D position, ModelItem channel, double floorHeight, out double height)` - 从下方射线投射
- `PerformRayTriangleIntersectionFromBelow(Point3D position, List triangles)` - 从下往上射线-三角形交点检测
- `TryGetGeometricBottomHeight(Point3D position, ModelItem channel, out double height)` - View API获取底面高度
- `AnalyzeBottomSurfaceHeightAtPosition(Point3D position, ModelItem channel, double floorHeight, double ceilingHeight)` - 分析底面表面高度
- `TryMultiPointBottomSampling(Point3D position, ModelItem channel)` - 多点采样获取底面高度
#### 新增方法(RailGeometryHelper.cs)
- `ExtractRailCenterLine(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 提取空轨中心线
#### 新增方法(PathEditingViewModel.cs)
- `GeneratePathFromRail(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 从空轨生成路径
#### 新增方法(PathAnimationManager.cs)
- `CalculateObjectPosition(Point3D pathPoint, PathType pathType)` - 计算物体位置
#### 修改方法(PathAnimationManager.cs)
- `GenerateAnimationFrames(List pathPoints, PathType pathType)` - 传递路径类型参数
- `PrecomputeCollisions(List pathPoints, PathType pathType, List railModels)` - 传递路径类型和空轨模型列表
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List collisionExclusions)` - 传递路径类型和排除列表
---