# 空轨支持方案 ## 一、需求分析 ### 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)` - 传递路径类型和排除列表 ---