将虚拟车辆改成虚拟物体

This commit is contained in:
tian 2026-02-16 09:02:50 +08:00
parent 8378872b8d
commit fdcd8edfc0
115 changed files with 1701 additions and 1699 deletions

View File

@ -411,7 +411,7 @@ Visibility="{Binding HasItems, Converter={StaticResource BoolToVisibilityConvert
|------|------|-------|
| 预计算碰撞 | Material Purple | (156, 39, 176) |
| 手工指定对象 | 橙色 | (255, 170, 0) |
| 动画车辆 | Amber/Yellow | (255, 193, 7) |
| 动画物体 | Amber/Yellow | (255, 193, 7) |
| ClashDetective结果 | 红色 | Color.Red |
| 通道预览 | 绿色 | Color.Green |
@ -430,9 +430,9 @@ Visibility="{Binding HasItems, Converter={StaticResource BoolToVisibilityConvert
[path_editing]
cell_size_meters = 0.5 # 网格单元大小(米)
max_height_diff_meters = 0.35 # 最大高度差(米)
vehicle_length_meters = 1.5 # 车辆长度
vehicle_width_meters = 1.0 # 车辆宽度
vehicle_height_meters = 2.0 # 车辆高度
object_length_meters = 1.5 # 物体长度
object_width_meters = 1.0 # 物体宽度
object_height_meters = 2.0 # 物体高度
safety_margin_meters = 0.1 # 安全间隙
default_path_turn_radius = 2.5 # 默认转弯半径
arc_sampling_step = 0.05 # 圆弧采样步长

View File

@ -55,7 +55,7 @@
- **自定义空间哈希网格**: 实现SpatialHashGrid提供高效的空间数据查询和管理
- **空间索引优化**: 集成空间索引到碰撞检测系统,优化检测性能和准确性
- **几何对象缓存**: 优化空间索引构建,直接使用预过滤的非通道缓存,提高处理效率
- **格子大小动态调整**: 优化空间索引格子大小参数,使用车辆宽度参数进行动态调整
- **格子大小动态调整**: 优化空间索引格子大小参数,使用物体宽度参数进行动态调整
##### 碰撞检测性能提升
@ -133,7 +133,7 @@
- **文档关联数据库**: 增加文档关联的sqlite数据库支持
- **路径分析功能**: 实现基本的路径分析功能,统计路径信息
- **碰撞报告存储**: 把碰撞报告保存到数据库,优化报告格式
- **参数持久化**: 将车辆最大宽度、长度、高度、安全间隙、网格尺寸加入路径保存内容
- **参数持久化**: 将物体最大宽度、长度、高度、安全间隙、网格尺寸加入路径保存内容
##### 导航地图生成
@ -154,7 +154,7 @@
- **斜面路径规划**: 实现路径在斜面(如楼梯)上的规划
- **通道网格高度**: 修改通道网格生成的高度设置,能在斜面上生成网格
- **障碍物扫描**: 使用包围盒中心下方通道网格z高度进行范围筛选
- **高度范围优化**: 用通道高度范围+车辆高度+安全间隙作为过滤范围
- **高度范围优化**: 用通道高度范围+物体高度+安全间隙作为过滤范围
- **z值判断改进**: 修改z值判断为通道网格z值的最小和最大值
#### 物流分类系统
@ -436,10 +436,10 @@
- 为后续算法扩展提供可扩展架构
- **参数系统完善**
- AutoPathPlanningParameters增加车辆长度、宽度、高度参数
- AutoPathPlanningParameters增加物体长度、宽度、高度参数
- 完善参数验证机制,确保输入合法性
- 实现参数持久化和配置管理
- 提供车辆尺寸的智能默认值
- 提供物体尺寸的智能默认值
#### 用户体验革命性提升
@ -449,7 +449,7 @@
- 新算法生成的路径更符合人类直觉导航习惯
- 减少90%以上的不必要转弯,路径更加简洁高效
- 室内导航的自然度和实用性大幅提升
- 适配大型车辆的路径规划需求
- 适配大型物体的路径规划需求
- **用户操作简化**
- 策略选择一键切换,无需复杂配置
@ -478,7 +478,7 @@
- ✅ 局部直线优先算法生成完美L型路径消除锯齿问题
- ✅ 路径策略选择功能正常,支持实时算法切换
- ✅ 网格可视化系统稳定运行,提供直观的算法理解
- ✅ 车辆参数系统完善,支持长、宽、高度的独立配置
- ✅ 物体参数系统完善,支持长、宽、高度的独立配置
- ✅ Idle事件UI更新机制高效运行界面响应流畅
- ✅ COM API内存管理优化完成长时间运行稳定无泄漏
- ✅ 所有功能完整测试通过,系统稳定性达到生产级标准
@ -614,7 +614,7 @@
- 完全重写`CategoryAttributeManager`中的三个关键过滤方法
- `FilterByLogisticsType()`: 从逐个遍历优化为SearchAPI直接搜索指定类型
- `FilterTraversableItems()`: 使用SearchAPI直接搜索可通行属性为"是"的项目
- `FilterByVehicleSize()`: 采用混合策略先用SearchAPI获取物流项目再进行尺寸过滤
- `FilterByObjectSize()`: 采用混合策略先用SearchAPI获取物流项目再进行尺寸过滤
- **性能提升**: 大型模型处理速度提升10-50倍消除嵌套循环性能瓶颈
- **智能API使用与错误处理**
@ -1411,7 +1411,7 @@ var stats = gridMap.GetHeightCalculationStats();
- 新增 `TimeLinerIntegrationManager` 类,实现运输任务与 TimeLiner 的无缝集成
- 动画播放时自动在 TimeLiner 中创建对应的运输任务
- 支持任务状态实时同步(播放中、暂停、停止、已完成)
- 任务显示名称包含车辆信息、时间戳和进度状态
- 任务显示名称包含物体信息、时间戳和进度状态
### 技术突破 🔧
@ -1463,7 +1463,7 @@ var stats = gridMap.GetHeightCalculationStats();
- ✅ TimeLiner 窗口成功显示运输任务:"运输任务SHINYSTE13_运输_131040"
- ✅ 任务创建使用 `TaskAddCopy` 方法成功,任务数量从 0 增加到 1
- ✅ 车辆关联正确:关联了 SHINYSTE13 车辆模型
- ✅ 物体关联正确:关联了 SHINYSTE13 物体模型
- ✅ 路径参数准确5 个路径点10 秒持续时间
- ✅ 原有动画功能完全正常,无任何影响
- ✅ 编译成功,在 Navisworks 2017 中稳定运行
@ -1620,7 +1620,7 @@ var stats = gridMap.GetHeightCalculationStats();
- 实现了InwGUIPropertyNode2的自定义属性设置
- **属性管理系统**
- 支持5种物流属性类型、可通行性、优先级、适用车辆尺寸、速度限制
- 支持5种物流属性类型、可通行性、优先级、适用物体尺寸、速度限制
- 实现了属性检查、获取和筛选功能
- 提供了完整的属性值验证机制

View File

@ -69,7 +69,7 @@ public GridMap GenerateFromBIM(BoundingBox3D bounds, double cellSize, ...)
### 必须使用模型单位
GridMapGenerator 和 AutoPathFinder 中所有参数CellSize, VehicleRadius, SafetyMargin, VehicleHeight, ScanHeight, InflationRadius, MaxHeightDiff
GridMapGenerator 和 AutoPathFinder 中所有参数CellSize, ObjectRadius, SafetyMargin, ObjectHeight, ScanHeight, InflationRadius, MaxHeightDiff
### 命名约定

View File

@ -61,7 +61,7 @@ NavisworksTransport 是一个用于 Autodesk Navisworks Manage 2026 的插件,
- SQLite数据库存储路径数据
- 路径信息统计和分析
- 路径参数持久化(车辆尺寸、安全间隙、网格尺寸等)
- 路径参数持久化(物体尺寸、安全间隙、网格尺寸等)
### 3. 动画与仿真
@ -157,7 +157,7 @@ NavisworksTransport/
│ │ ├── UIStateManager.cs
│ │ ├── IdleEventManager.cs
│ │ ├── DocumentStateManager.cs
│ │ ├── VirtualVehicleManager.cs
│ │ ├── VirtualObjectManager.cs
│ │ ├── ModelSplitterManager.cs
│ │ ├── NavigationMapGenerator.cs
│ │ ├── PathPointRenderPlugin.cs
@ -304,7 +304,7 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
2. **错误:没有深入查看渲染代码**
- 虽然找到了 `PathPointRenderPlugin.cs`,但没有仔细看它的渲染逻辑
- 应该先看渲染插件如何渲染路径,发现 `PathVisualizationMode.VehicleSpace` 模式
- 应该先看渲染插件如何渲染路径,发现 `PathVisualizationMode.ObjectSpace` 模式
3. **错误:被表面信息误导**
- 日志显示"[网格可视化] 无缓存网格地图",就以为需要生成网格地图
@ -314,7 +314,7 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
1. **先看渲染代码**:对于可视化功能,应该先看渲染插件的代码(如 `PathPointRenderPlugin.cs`
2. **理解本质**:问自己"这个功能是什么",而不是"怎么做"。通行空间可视化是"改变渲染风格",而不是"生成网格地图"
3. **查看现有实现**:如果日志中已经有相关提示(如"路径可视化模式已更改: 车辆通行空间"),说明功能已经存在,只需要在合适的时机调用
3. **查看现有实现**:如果日志中已经有相关提示(如"路径可视化模式已更改: 物体通行空间"),说明功能已经存在,只需要在合适的时机调用
4. **不要只看方法名**:方法名可能误导,要看实际实现代码
5. **深入查看核心代码**:对于可视化、动画等功能,应该先看对应的渲染/动画管理器的代码
@ -334,7 +334,7 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
- **UIStateManager**: UI状态和线程安全更新管理
- **IdleEventManager**: Idle事件管理,优化UI性能
- **DocumentStateManager**: 文档状态管理
- **VirtualVehicleManager**: 虚拟车辆管理
- **VirtualObjectManager**: 虚拟物体管理
- **PathAnimationManager**: 动画管理
- **TimeLinerIntegrationManager**: TimeLiner集成
- **ClashDetectiveIntegration**: 碰撞检测集成
@ -354,22 +354,22 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
- 查找物流属性模型时,使用 `CategoryAttributeManager.GetLogisticsItemsByType()` 而不是手动创建 Search 对象
- 提取几何数据时,使用 `GeometryHelper` 中的现有方法
- 计算距离时,使用 `GeometryHelper.CalculatePointDistance()` 等工具方法
- **车辆尺寸与XYZ轴关系** (关键约定):
- **X轴**: 长度方向(车辆前进方向,车头到车尾)
- **Y轴**: 宽度方向(车辆侧面,左右两侧)
- **物体尺寸与XYZ轴关系** (关键约定):
- **X轴**: 长度方向(物体前进方向,车头到车尾)
- **Y轴**: 宽度方向(物体侧面,左右两侧)
- **Z轴**: 高度方向(垂直方向)
- **朝向规则**: 车辆沿路径移动时X轴长度方向指向路径前进方向
- **尺寸参数**: `VehicleLength` = X轴尺寸`VehicleWidth` = Y轴尺寸`VehicleHeight` = Z轴尺寸
- **朝向规则**: 物体沿路径移动时X轴长度方向指向路径前进方向
- **尺寸参数**: `ObjectLength` = X轴尺寸`ObjectWidth` = Y轴尺寸`ObjectHeight` = Z轴尺寸
- **通行空间计算**(路径周围的可通行区域,沿路径延伸的"管道":
- `passageAlongPath`: 沿路径方向的长度 = 路径本身的长度(渲染时使用)
- `passageAcrossPath`: 垂直于路径方向的截面尺寸1
- `passageNormalToPath`: 垂直于路径方向的截面尺寸2
- **水平路径**(地面、空轨)截面:使用车辆的**宽度**和**高度**
- `passageAcrossPath` = 车辆宽度Y轴+ 2×间隙
- `passageNormalToPath` = 车辆高度Z轴+ 间隙
- **垂直路径**(吊装)截面:使用车辆的**宽度**和**长度**
- `passageAcrossPath` = 车辆宽度Y轴+ 2×间隙
- `passageNormalToPath` = 车辆长度X轴+ 2×间隙
- **水平路径**(地面、空轨)截面:使用物体的**宽度**和**高度**
- `passageAcrossPath` = 物体宽度Y轴+ 2×间隙
- `passageNormalToPath` = 物体高度Z轴+ 间隙
- **垂直路径**(吊装)截面:使用物体的**宽度**和**长度**
- `passageAcrossPath` = 物体宽度Y轴+ 2×间隙
- `passageNormalToPath` = 物体长度X轴+ 2×间隙
- **单位系统**:
- **内部计算**: 统一使用模型单位(避免频繁转换,提高性能)
- **对外输出**: 转换为米(m)单位(用户界面、日志、数据库存储等)
@ -418,7 +418,7 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
2. 选择路径策略(最短路径或直线优先)
3. 在3D视图中点击添加路径点
4. 点击"自动规划"生成路径
5. 调整路径参数(车辆尺寸、网格大小等)
5. 调整路径参数(物体尺寸、网格大小等)
#### 动画仿真
@ -443,9 +443,9 @@ msiexec /i NavisworksTransport.Setup.msi /qn /l*v install.log
```toml
[path_editing]
cell_size_meters = 0.5
vehicle_length_meters = 1.0
vehicle_width_meters = 1.0
vehicle_height_meters = 2.0
object_length_meters = 1.0
object_width_meters = 1.0
object_height_meters = 2.0
safety_margin_meters = 0.05
[animation]
@ -553,7 +553,7 @@ detection_gap_meters = 0.05
1. **插件未加载**: 确保Navisworks Manage 2026已正确安装,插件目录路径正确
2. **崩溃问题**: 查看日志文件,检查Navisworks API调用线程安全性
3. **路径规划失败**: 检查物流属性是否正确设置,通道是否连续
4. **碰撞检测不准确**: 调整检测间隙参数,检查车辆尺寸配置
4. **碰撞检测不准确**: 调整检测间隙参数,检查物体尺寸配置
### 日志位置

View File

@ -132,8 +132,8 @@
<Compile Include="src\Core\IdleEventManager.cs" />
<!-- Core - Document State Management -->
<Compile Include="src\Core\DocumentStateManager.cs" />
<!-- Core - Virtual Vehicle Management -->
<Compile Include="src\Core\VirtualVehicleManager.cs" />
<!-- Core - Virtual Object Management -->
<Compile Include="src\Core\VirtualObjectManager.cs" />
<!-- Core - Section Box Export -->
<Compile Include="src\Core\SectionBoxExporter.cs" />
<!-- Core - Configuration Management -->

View File

@ -90,20 +90,20 @@ Navisworks作为项目审阅软件能够聚合来自多种设计软件如A
* **路径生成:** 插件将执行路径查找算法如A\*算法生成一系列3D坐标点作为路径 28。
* **模型动画化:**
* **Animator集成** Navisworks的Animator工具允许创建对象动画通过关键帧控制对象的移动和旋转 45。虽然Navisworks API对Animator的直接编程控制例如直接创建动画集和关键帧可能有限 44但可以通过以下策略实现
* **手动创建动画集(演示阶段):** 在演示中可以预先在Navisworks中为代表运输车辆的模型手动创建动画集,并将其设置为可由插件控制。
* **通过API控制对象变换** 插件可以根据计算出的路径点在TimeLiner模拟的每个时间步长通过API动态更新代表运输车辆的ModelItem的变换位置和方向从而模拟其沿路径移动 45。
* **TimeLiner集成** Navisworks的TimeLiner工具可以用于4D模拟将模型与时间表关联 6。插件可以将动画化的模型运输车辆与TimeLiner任务关联起来 45。
* **手动创建动画集(演示阶段):** 在演示中可以预先在Navisworks中为代表运输物体的模型手动创建动画集,并将其设置为可由插件控制。
* **通过API控制对象变换** 插件可以根据计算出的路径点在TimeLiner模拟的每个时间步长通过API动态更新代表运输物体的ModelItem的变换位置和方向从而模拟其沿路径移动 45。
* **TimeLiner集成** Navisworks的TimeLiner工具可以用于4D模拟将模型与时间表关联 6。插件可以将动画化的模型运输物体与TimeLiner任务关联起来 45。
* **动态碰撞检测:**
* **TimeLiner与Clash Detective联动** Navisworks Manage版本支持TimeLiner与Clash Detective的联动实现基于时间的碰撞检测 37。这是实现动态碰撞检测的关键。
* **工作流程:**
1. 插件将创建一个新的Clash Test。
2. 将“运输车辆模型”设置为选择集A。
2. 将“运输物体模型”设置为选择集A。
3. 将“门、通道、其他模型”等障碍物设置为选择集B。
4. 将此Clash Test的“Link”选项设置为“TimeLiner” 18。
5. 当TimeLiner模拟播放时Clash Detective将在每个时间步长自动运行碰撞检测并记录碰撞结果 18。
* **碰撞高亮显示:**
* **获取碰撞结果:** 插件将通过Navisworks API访问Clash Detective的碰撞结果ClashResult对象 17。
* **视觉反馈:** 当检测到碰撞时插件将使用DocumentModels.OverrideTemporaryColor方法 30将发生碰撞的模型运输车辆和障碍物)醒目地高亮显示(例如,变为红色或闪烁)。这种临时颜色覆盖可以在碰撞结束后或模拟停止时重置,使用
* **视觉反馈:** 当检测到碰撞时插件将使用DocumentModels.OverrideTemporaryColor方法 30将发生碰撞的模型运输物体和障碍物)醒目地高亮显示(例如,变为红色或闪烁)。这种临时颜色覆盖可以在碰撞结束后或模拟停止时重置,使用
DocumentModels.ResetTemporaryMaterials或DocumentModels.ResetAllTemporaryMaterials 53。
**路径规划输出基础可视化与DELMIA导出**
@ -243,7 +243,7 @@ Navisworks-Net-Plugin-Property-Database-Example 41展示了类似的方法来显
构建体素网格的过程将涉及:
1. **几何数据提取:** 遍历Navisworks模型中的ModelItem并使用COM API的InwOaFragment3.GenerateSimplePrimitives方法提取其底层的三角形、顶点等几何原始数据 17。
2. **体素化:** 将提取的几何数据投影到预定义的体素网格上。如果任何几何图元占据了某个体素,则该体素将被标记为“障碍物”。为了应对不同尺寸的运输车辆不小于10种尺寸规格体素网格的分辨率需要足够精细或者在路径规划时考虑车辆的包络体积,以确保规划的路径能够容纳车辆通过 \[用户查询\]。
2. **体素化:** 将提取的几何数据投影到预定义的体素网格上。如果任何几何图元占据了某个体素,则该体素将被标记为“障碍物”。为了应对不同尺寸的运输物体不小于10种尺寸规格体素网格的分辨率需要足够精细或者在路径规划时考虑物体的包络体积,以确保规划的路径能够容纳物体通过 \[用户查询\]。
3. **属性映射:** 结合之前为物流元素分配的自定义属性(例如,“门”、“电梯”、“通道”、“障碍物”),这些语义信息可以进一步丰富体素网格。例如,标记为“通道”的区域将被视为可通行,而标记为“障碍物”的区域则被视为不可通行。这使得路径规划算法能够理解模型的语义,而不仅仅是几何形状。
**动态碰撞检测与动画集成**
@ -252,14 +252,14 @@ Navisworks-Net-Plugin-Property-Database-Example 41展示了类似的方法来显
1. **路径生成与动画创建:**
* 插件首先利用A\*算法生成一条离散的3D路径一系列坐标点
* 然后插件将利用Navisworks的Animator功能为代表“运输车辆”的模型创建一个对象动画Object Animation。这可以通过在路径的关键点处设置模型的变换位置和旋转关键帧来实现 45。虽然Navisworks API对Animator的直接编程控制例如直接创建动画集和关键帧可能有限 44但可以通过在TimeLiner模拟的每个时间步长动态更新模型位置来模拟动画效果或者利用Navisworks内置的动画功能如通过保存视点创建动画 55
* 然后插件将利用Navisworks的Animator功能为代表“运输物体”的模型创建一个对象动画Object Animation。这可以通过在路径的关键点处设置模型的变换位置和旋转关键帧来实现 45。虽然Navisworks API对Animator的直接编程控制例如直接创建动画集和关键帧可能有限 44但可以通过在TimeLiner模拟的每个时间步长动态更新模型位置来模拟动画效果或者利用Navisworks内置的动画功能如通过保存视点创建动画 55
2. **TimeLiner集成**
* 将创建的动画与TimeLiner任务关联 45。TimeLiner允许将模型与时间表链接并模拟其在不同时间点的状态。
* 插件可以创建或修改TimeLiner任务并将运输车辆模型分配给这些任务。
* 插件可以创建或修改TimeLiner任务并将运输物体模型分配给这些任务。
3. **Time-Based Clash Detection设置**
* 通过Navisworks API访问Document.Clash属性获取Clash Detective功能 37。
* 创建一个新的ClashTest 37。
* 将“运输车辆模型”定义为碰撞测试的“选择集A”ClashSelection
* 将“运输物体模型”定义为碰撞测试的“选择集A”ClashSelection
* 将“门、通道、其他模型”等障碍物通过自定义属性筛选获得定义为碰撞测试的“选择集B”。
* **关键步骤:** 将ClashTest的Link属性设置为TimeLiner 37。这将使碰撞检测与TimeLiner的模拟进度同步。
* 运行TimeLiner模拟Navisworks将自动在每个时间步长执行碰撞检测 18。
@ -305,9 +305,9 @@ Navisworks-Net-Plugin-Property-Database-Example 41展示了类似的方法来显
* 实现ModelItem的交互式选择并获取起点/终点坐标。
* 实现通过COM API向ModelItem添加自定义“物流”属性例如类型、可通行性
* 实现基于自定义属性的ModelItem隐藏/显示功能。
* 构建基础的体素网格环境表示,并集成考虑车辆尺寸的A\*路径规划算法。
* 构建基础的体素网格环境表示,并集成考虑物体尺寸的A\*路径规划算法。
* 在Navisworks视图中可视化生成的路径临时图形
* **核心:** 实现将运输车辆模型沿规划路径进行动画化通过API控制变换或利用现有动画功能并将其与TimeLiner任务关联。
* **核心:** 实现将运输物体模型沿规划路径进行动画化通过API控制变换或利用现有动画功能并将其与TimeLiner任务关联。
* **核心:** 设置TimeLiner与Clash Detective的联动运行时间线模拟并获取碰撞结果。
* **核心:** 在检测到碰撞时使用DocumentModels.OverrideTemporaryColor醒目高亮显示碰撞对象并在模拟结束后重置颜色。
* 实现路径规划结果到简单XML文件的导出以供DELMIA导入进行概念验证。

View File

@ -228,7 +228,7 @@ public class TransportCollisionDetector
{
// 创建运输路径碰撞检测
public List<CollisionResult> DetectPathCollisions(List<Point3D> pathPoints,
TransportVehicle vehicle)
TransportObject object)
{
List<CollisionResult> collisions = new List<CollisionResult>();
@ -238,8 +238,8 @@ public class TransportCollisionDetector
// 为每个路径段创建碰撞测试
for (int i = 0; i < pathPoints.Count - 1; i++)
{
// 创建虚拟运输车辆几何体
var vehicleGeometry = CreateVehicleGeometry(pathPoints[i], pathPoints[i + 1], vehicle);
// 创建虚拟运输物体几何体
var objectGeometry = CreateObjectGeometry(pathPoints[i], pathPoints[i + 1], object);
// 创建碰撞测试
ClashTest test = new ClashTest();
@ -247,7 +247,7 @@ public class TransportCollisionDetector
// 设置检测对象
test.SelectionA.Selection.SelectAll(); // 选择所有模型
test.SelectionB.Selection.SelectGeometry(vehicleGeometry); // 虚拟车辆
test.SelectionB.Selection.SelectGeometry(objectGeometry); // 虚拟物体
// 运行检测
test.Run();
@ -277,13 +277,13 @@ public class PathAnimationController
{
private Timer animationTimer;
private List<Point3D> pathPoints;
private ModelItem transportVehicle;
private ModelItem transportObject;
private int currentSegment;
private float animationProgress;
public void StartAnimation(ModelItem vehicle, List<Point3D> path, TimeSpan duration)
public void StartAnimation(ModelItem object, List<Point3D> path, TimeSpan duration)
{
transportVehicle = vehicle;
transportObject = object;
pathPoints = path;
currentSegment = 0;
animationProgress = 0f;
@ -312,8 +312,8 @@ public class PathAnimationController
pathPoints[currentSegment + 1],
animationProgress);
// 更新车辆位置
UpdateVehiclePosition(transportVehicle, currentPos);
// 更新物体位置
UpdateObjectPosition(transportObject, currentPos);
// 更新进度
animationProgress += 0.05f; // 每帧5%进度
@ -327,17 +327,17 @@ public class PathAnimationController
Application.ActiveDocument.CurrentViewpoint.Redraw();
}
private void UpdateVehiclePosition(ModelItem vehicle, Point3D position)
private void UpdateObjectPosition(ModelItem object, Point3D position)
{
// 创建变换矩阵
Transform3D transform = Transform3D.CreateTranslation(position.ToVector3D());
// 应用变换需要COM API
ComApi.InwOaPath vehiclePath = ComApiBridge.ComApiBridge.ToInwOaPath(vehicle);
ComApi.InwOaNode vehicleNode = vehiclePath.Nodes().Last() as ComApi.InwOaNode;
ComApi.InwOaPath objectPath = ComApiBridge.ComApiBridge.ToInwOaPath(object);
ComApi.InwOaNode objectNode = objectPath.Nodes().Last() as ComApi.InwOaNode;
// 设置变换
vehicleNode.SetAttribute("LcOaNodeBaseTransform", transform.ToComMatrix());
objectNode.SetAttribute("LcOaNodeBaseTransform", transform.ToComMatrix());
}
}
```

View File

@ -221,7 +221,7 @@ grid.AddEdge(from, to, velocity);
// 检查节点是否满足高度约束
if (cell.PassableHeights != null && cell.PassableHeights.Any()) {
bool heightOk = cell.PassableHeights.Any(
interval => interval.GetSpan() >= vehicleHeight
interval => interval.GetSpan() >= objectHeight
);
if (!heightOk) {
grid.DisconnectNode(position); // 不满足高度要求,断开连接

View File

@ -70,7 +70,7 @@ A\*的核心区别在于其“启发式”特性,这意味着它使用启发
* 大型搜索空间
现代应用程序尤其是在游戏开发例如开放世界环境、大型策略地图、机器人导航和复杂网络路由中涉及包含数百万个节点的地图或图。未经优化的A\*算法可能需要数秒甚至数分钟才能计算出路径,使其无法使用 1。
* 实时性要求
许多应用程序要求即时寻路。游戏、自动驾驶车辆和实时策略 (RTS) 模拟需要路径在毫秒内计算完成,以确保流畅的游戏体验、响应式导航或即时决策。延迟可能导致糟糕的用户体验或关键系统故障 1。
许多应用程序要求即时寻路。游戏、自动驾驶物体和实时策略 (RTS) 模拟需要路径在毫秒内计算完成,以确保流畅的游戏体验、响应式导航或即时决策。延迟可能导致糟糕的用户体验或关键系统故障 1。
* 高智能体密度
在涉及大量智能体(例如人群模拟、多机器人协调)的场景中,每个智能体都需要频繁地重新计算路径,即使是中等效率的算法,其累积计算负载也可能变得不堪重负。这会导致系统变慢、帧率下降或智能体“卡住”。
* 动态环境(即使有重新规划)
@ -317,7 +317,7 @@ D\*算法是A\*算法的扩展,它结合了前向和后向搜索,以高效
D\*及其变体已被广泛用于自主机器人包括火星探测器“机遇号”和“勇气号”。Field D\*是D\*的一种基于插值的变体它将节点定义在网格的角点上并使用线性插值使路径点可以位于网格边的任何位置。这样它可以在非均匀环境中生成直接、低成本和平滑的路径解决了传统网格寻路算法将机器人运动限制在少数几个离散方向例如0、45、90度的问题从而产生非自然、次优的路径。
D\* Lite是D\*算法的简化版本,更高效且易于实现。它在自主车辆和机器人领域得到了应用。与A\*不同D\* Lite在动态环境中表现出卓越的适应性通过实时重新计算路线在所有测试场景中都成功完成任务而没有失败这表明它适用于需要对环境变化有高成功率的应用。
D\* Lite是D\*算法的简化版本,更高效且易于实现。它在自主物体和机器人领域得到了应用。与A\*不同D\* Lite在动态环境中表现出卓越的适应性通过实时重新计算路线在所有测试场景中都成功完成任务而没有失败这表明它适用于需要对环境变化有高成功率的应用。
### **C\# D\* Lite实现**
@ -382,7 +382,7 @@ D\* Lite是D\*算法的简化版本,更高效且易于实现。它在自主车
* 多智能体寻路 (MAPF)
在许多真实世界场景中多个智能体需要同时找到路径并避免相互碰撞。MAPF算法旨在解决这一复杂问题例如基于冲突搜索 (CBS) 的方法。
* 连续空间寻路
传统的A\*通常在离散网格上操作。然而,对于机器人和自动驾驶车辆在连续空间中进行寻路以生成更平滑、更自然的轨迹变得越来越重要。Field D\*就是其中一种尝试解决此问题的算法。
传统的A\*通常在离散网格上操作。然而,对于机器人和自动驾驶物体在连续空间中进行寻路以生成更平滑、更自然的轨迹变得越来越重要。Field D\*就是其中一种尝试解决此问题的算法。
* 深度学习与强化学习
人工智能领域的最新进展正在影响寻路。深度学习模型可以学习复杂的环境表示,而强化学习可以训练智能体在动态环境中找到最优策略,尽管这些方法通常需要大量数据和计算资源。
* 分层寻路

View File

@ -3,7 +3,7 @@
## **1\. 使用场景**
1. 在Navisworks中打开物流相关的楼层nwd文件前提将建筑按单独的楼层保存
2. 在物流控件中新建任务,任务包含一个物流场景的所有信息,支持保存和打开
2. 选择物流运动模型(在物流模型文件列表中选择,可以是车辆、人员等,也可以选择自定义模型,指定长宽高),设置速度限制、安全距离等信息。
2. 选择物流运动模型(在物流模型文件列表中选择,可以是物体、人员等,也可以选择自定义模型,指定长宽高),设置速度限制、安全距离等信息。
3. 可以对运动模型经过的物流组件,指定物流类型(门、通道、电梯等),可以设置限高、限宽、限速等参数。
4. 用物流导航控件指定物流起点和终点点自动吸附在通道表面用颜色球表示。移动过程可以使用Navisworks的漫游/飞行工具)
5. 在物流导航控件中选择【生成路径】,自动生成从起点到终点的物流路径,包含一系列路径点和之间的连线,用颜色球和圆柱线表示。用标签显示路径的名称、长度、路径点数量等。

View File

@ -2955,18 +2955,18 @@ public ModelItem LoadCollisionObject(int? modelIndex, string pathId)
```csharp
// ✅ 保存碰撞检测结果
public void SaveClashDetectiveResults(List<CollisionResult> collisions, ModelItem vehicle)
public void SaveClashDetectiveResults(List<CollisionResult> collisions, ModelItem object)
{
var document = Application.ActiveDocument;
// 1. 保存车辆对象信息
var vehiclePathId = document.Models.CreatePathId(vehicle);
// 1. 保存物体对象信息
var objectPathId = document.Models.CreatePathId(object);
var testRecord = new ClashDetectiveResultRecord
{
TestName = GenerateTestName(),
IsVirtualVehicle = false,
VehicleModelIndex = vehiclePathId.ModelIndex,
VehiclePathId = vehiclePathId.PathId,
IsVirtualObject = false,
ObjectModelIndex = objectPathId.ModelIndex,
ObjectPathId = objectPathId.PathId,
// ... 其他字段
};
@ -3007,21 +3007,21 @@ public List<CollisionResult> LoadClashDetectiveResults(string testName)
throw new InvalidOperationException($"未找到测试记录: {testName}");
}
// 2. 恢复车辆对象
ModelItem vehicle;
if (testInfo.IsVirtualVehicle)
// 2. 恢复物体对象
ModelItem object;
if (testInfo.IsVirtualObject)
{
// 虚拟车辆:创建新的虚拟车辆对象
vehicle = VirtualVehicleManager.Instance.CreateVirtualVehicle(
testInfo.VirtualVehicleLength,
testInfo.VirtualVehicleWidth,
testInfo.VirtualVehicleHeight
// 虚拟物体:创建新的虚拟物体对象
object = VirtualObjectManager.Instance.CreateVirtualObject(
testInfo.VirtualObjectLength,
testInfo.VirtualObjectWidth,
testInfo.VirtualObjectHeight
);
}
else
{
// 真实车辆:使用 ResolvePathId 恢复
vehicle = LoadCollisionObject(testInfo.VehicleModelIndex, testInfo.VehiclePathId);
// 真实物体:使用 ResolvePathId 恢复
object = LoadCollisionObject(testInfo.ObjectModelIndex, testInfo.ObjectPathId);
}
// 3. 恢复所有被撞对象并重建碰撞结果
@ -3037,12 +3037,12 @@ public List<CollisionResult> LoadClashDetectiveResults(string testName)
ClashGuid = Guid.NewGuid(),
DisplayName = $"历史碰撞: {obj.DisplayName}",
Status = ClashResultStatus.Active,
Item1 = vehicle,
Item1 = object,
Item2 = collidedObject,
Center = collidedObject.BoundingBox().Center,
Distance = 0.0,
CreatedTime = DateTime.Now,
OriginalItem1 = vehicle,
OriginalItem1 = object,
OriginalItem2 = collidedObject,
HasContainerMapping = true
};

View File

@ -21,12 +21,12 @@
1. **数据获取 (Navisworks API)**:
* 遍历模型,获取关键构件信息:
* **地面/楼板 (Floor/Slab)**: 获取其几何形状(面片或网格),用于定义基础行驶区域。
* **障碍物 (Obstacles)**: 获取所有可能阻挡车辆的构件(如管道 `Pipe`、风管 `Duct`、横梁 `Beam`、设备 `Equipment` 等)的**三维包围盒 (Bounding Box)** 或精确网格数据。
* **障碍物 (Obstacles)**: 获取所有可能阻挡物体的构件(如管道 `Pipe`、风管 `Duct`、横梁 `Beam`、设备 `Equipment` 等)的**三维包围盒 (Bounding Box)** 或精确网格数据。
* **连接构件 (Connections)**: 获取门 `Door`、楼梯 `Stairs`、电梯 `Elevator` 等信息,用于处理特殊通行规则或楼层间移动。
* 获取构件的属性如类别、名称、ID以便于分类和处理。
2. **3D空间分析与“可行驶区域”定义**:
* **定义车辆参数**: 确定车辆的尺寸(长、宽、高 `H_vehicle`)和最大行驶高度 `H_max`
* **定义物体参数**: 确定物体的尺寸(长、宽、高 `H_object`)和最大行驶高度 `H_max`
* **方法A - 2.5D栅格化 (推荐)**:
* 在地面平面上建立一个2D规则网格。
* 对网格中的每个单元格 `(x, y)`沿Z轴扫描从地面到 `H_max` 的空间。
@ -55,7 +55,7 @@
#### 2.3 优点
* 精度最高,能真实反映三维空间的可通行性。
* 为未来扩展(如多层车辆、更复杂的避障规则)奠定坚实基础。
* 为未来扩展(如多层物体、更复杂的避障规则)奠定坚实基础。
* 一次计算,多次高效寻路。
#### 2.4 缺点
@ -74,7 +74,7 @@
1. **执行二维寻路**: 运行当前基于地面投影的A*算法,得到一条二维路径。
2. **三维碰撞检测**:
* 遍历路径上的关键点或按固定步长取点。
* 在每个点上,根据预设的车辆尺寸,在三维空间中生成一个代表车辆的包围盒AABB或OBB
* 在每个点上,根据预设的物体尺寸,在三维空间中生成一个代表物体的包围盒AABB或OBB
* 利用Navisworks API的碰撞检测功能检查此包围盒是否与模型中的任何构件发生碰撞。
3. **处理与反馈**:
* **无冲突**: 路径可行,直接输出。
@ -105,41 +105,41 @@
#### 3.2 实施步骤
1. **定义车辆参数**:
* 明确车辆的尺寸(长、宽、高 `H_vehicle`)。
* 确定车辆的最大行驶高度 `H_max`(例如,车辆底盘离地高度 + H_vehicle)。
1. **定义物体参数**:
* 明确物体的尺寸(长、宽、高 `H_object`)。
* 确定物体的最大行驶高度 `H_max`(例如,物体底盘离地高度 + H_object)。
2. **生成基础二维网格**:
* **范围**: 基于整个建筑地面或您关心的区域定义一个二维规则网格。网格的分辨率需要权衡精度和性能例如单元格大小可以是0.5m x 0.5m)。
* **初始状态**: 此时,网格单元格的状态是**未知**的。
3. **构建“地面区域图层”**:
* **目标**: 确定哪些网格单元格是**理论上**可供车辆**安全停放或通过中心点**的区域(即有地面存在的区域)。
* **目标**: 确定哪些网格单元格是**理论上**可供物体**安全停放或通过中心点**的区域(即有地面存在的区域)。
* **方法 (利用Navisworks API)**:
* 遍历模型找到所有被分类为“Floor”、“Slab”或类似类别的构件。
* 获取它们的几何信息(面片)。
* 对于每个“地面”面片,判断其覆盖了哪些网格单元格(例如,单元格中心点是否在面片内,或单元格大部分区域在面片内)。
* **结果**: 得到一个“地面图层”网格,其中标记了所有有地面的单元格。这表示车辆**有可能**在这些区域活动。
* **结果**: 得到一个“地面图层”网格,其中标记了所有有地面的单元格。这表示物体**有可能**在这些区域活动。
4. **构建“障碍物投影图层”**:
* **目标**: 识别并标记所有会**阻挡车辆通行**的区域。
* **目标**: 识别并标记所有会**阻挡物体通行**的区域。
* **方法 (利用Navisworks API)**:
* 遍历模型找到所有可能构成障碍的构件Pipe, Duct, Beam, Column, Equipment, Wall等
* **方法 A: 简单包围盒投影 (快速但粗糙)**:
* 获取每个障碍物的三维Axis-Aligned Bounding Box (AABB)。
* 计算每个障碍物AABB在地面车辆行驶平面 `H_max`)上的二维投影区域。
* 计算每个障碍物AABB在地面物体行驶平面 `H_max`)上的二维投影区域。
* **方法 B: 精确切片投影 (推荐,精度高)**:
* **核心思想**: 在车辆行驶高度 `H_vehicle` 对障碍物进行水平“切片”,得到其在该高度的精确二维截面,再将此截面投影到地面网格。
* **核心思想**: 在物体行驶高度 `H_object` 对障碍物进行水平“切片”,得到其在该高度的精确二维截面,再将此截面投影到地面网格。
* **步骤**:
1. **几何获取**: 使用Navisworks API获取障碍物的精确三角网格 (`Mesh`) 数据。
2. **执行切片**: 计算水平平面 `Z = H_vehicle` 与障碍物网格的交集。这通常需要实现一个算法或使用第三方计算几何库如CGAL来计算网格与平面的交线得到一组表示截面轮廓的二维线段。
3. **投影**: 由于截面本身就是高度为 `H_vehicle` 的二维形状将其Z坐标统一设为地面高度即可视为投影。
2. **执行切片**: 计算水平平面 `Z = H_object` 与障碍物网格的交集。这通常需要实现一个算法或使用第三方计算几何库如CGAL来计算网格与平面的交线得到一组表示截面轮廓的二维线段。
3. **投影**: 由于截面本身就是高度为 `H_object` 的二维形状将其Z坐标统一设为地面高度即可视为投影。
4. **栅格化**: 将这些精确的二维截面线段“绘制”到基础二维网格上,标记被覆盖的单元格。
* **方法 C: 视图导出与图像处理 (变通方法,实现相对简单)**:
* **核心思想**: 利用Navisworks强大的渲染和剪裁功能通过图像处理间接获得投影。
* **步骤**:
1. **设置视图**: 在Navisworks中设置一个俯视的正交视图 (Orthographic View)。
2. **设置剪裁**: 应用剪裁平面,只显示模型在高度 `Z = H_vehicle` 到 `Z = H_vehicle + delta` (delta为很小的正值) 之间的“薄片”。
2. **设置剪裁**: 应用剪裁平面,只显示模型在高度 `Z = H_object` 到 `Z = H_object + delta` (delta为很小的正值) 之间的“薄片”。
3. **隐藏地面**: (可选)隐藏地面构件,使视图只显示障碍物薄片。
4. **导出图像**: 使用API将此特定视图导出为二维图像如PNG
5. **图像处理**: 在插件或外部程序中,使用图像处理库识别图像中的障碍物像素,并将这些像素区域映射回原始的二维寻路网格,标记对应的单元格。
@ -187,12 +187,12 @@
1. **数据获取 (Navisworks API)**:
* 遍历模型,获取关键构件信息:
* **地面/楼板 (Floor/Slab)**: 获取其几何形状(面片或网格),用于定义基础行驶区域。
* **障碍物 (Obstacles)**: 获取所有可能阻挡车辆的构件(如管道 `Pipe`、风管 `Duct`、横梁 `Beam`、设备 `Equipment` 等)的**三维包围盒 (Bounding Box)** 或精确网格数据。
* **障碍物 (Obstacles)**: 获取所有可能阻挡物体的构件(如管道 `Pipe`、风管 `Duct`、横梁 `Beam`、设备 `Equipment` 等)的**三维包围盒 (Bounding Box)** 或精确网格数据。
* **连接构件 (Connections)**: 获取门 `Door`、楼梯 `Stairs`、电梯 `Elevator` 等信息,用于处理特殊通行规则或楼层间移动。
* 获取构件的属性如类别、名称、ID以便于分类和处理。
2. **3D空间分析与“可行驶区域”定义**:
* **定义车辆参数**: 确定车辆的尺寸(长、宽、高 `H_vehicle`)和最大行驶高度 `H_max`
* **定义物体参数**: 确定物体的尺寸(长、宽、高 `H_object`)和最大行驶高度 `H_max`
* **方法A - 2.5D栅格化 (推荐)**:
* 在地面平面上建立一个2D规则网格。
* 对网格中的每个单元格 `(x, y)`沿Z轴扫描从地面到 `H_max` 的空间。
@ -221,7 +221,7 @@
#### 2.3 优点
* 精度最高,能真实反映三维空间的可通行性。
* 为未来扩展(如多层车辆、更复杂的避障规则)奠定坚实基础。
* 为未来扩展(如多层物体、更复杂的避障规则)奠定坚实基础。
* 一次计算,多次高效寻路。
#### 2.4 缺点
@ -240,7 +240,7 @@
1. **执行二维寻路**: 运行当前基于地面投影的A*算法,得到一条二维路径。
2. **三维碰撞检测**:
* 遍历路径上的关键点或按固定步长取点。
* 在每个点上,根据预设的车辆尺寸,在三维空间中生成一个代表车辆的包围盒AABB或OBB
* 在每个点上,根据预设的物体尺寸,在三维空间中生成一个代表物体的包围盒AABB或OBB
* 利用Navisworks API的碰撞检测功能检查此包围盒是否与模型中的任何构件发生碰撞。
3. **处理与反馈**:
* **无冲突**: 路径可行,直接输出。

View File

@ -278,7 +278,7 @@ List<T> FindPointsInBox(AxisAlignedBox3d box);
```csharp
// === 初始化阶段(动画开始前)===
var spatialGrid = new PointHashGrid3d<ModelItem>(
cellSize: vehicleRadius * 2, // 格子大小 = 车辆直径
cellSize: objectRadius * 2, // 格子大小 = 物体直径
invalidValue: null
);
@ -292,7 +292,7 @@ foreach (var obstacle in potentialColliders)
// 快速查询当前位置附近的障碍物
var nearbyObstacles = spatialGrid.FindNearestInRadius(
currentPosition,
vehicleRadius + safetyMargin,
objectRadius + safetyMargin,
(item) => Vector3d.Distance(item.Position, currentPosition)
);
@ -333,9 +333,9 @@ spatialGrid.RemovePoint(removedObstacle, removedObstacle.Position);
```csharp
// 推荐设置
double vehicleRadius = 0.3; // 米
double objectRadius = 0.3; // 米
double safetyMargin = 0.1; // 米
double detectionRadius = vehicleRadius + safetyMargin;
double detectionRadius = objectRadius + safetyMargin;
// cellSize = 查询半径 * 1.5
double cellSize = detectionRadius * 1.5; // 约 0.6 米

View File

@ -581,8 +581,8 @@ public class VoxelGridGenerator
#region 配置参数
private readonly double voxelSize; // 体素大小(米)
private readonly double vehicleRadius; // 车辆半径(米)
private readonly double vehicleHeight; // 车辆高度(米)
private readonly double objectRadius; // 物体半径(米)
private readonly double objectHeight; // 物体高度(米)
private readonly double safetyMargin; // 安全间隙(米)
#endregion
@ -591,13 +591,13 @@ public class VoxelGridGenerator
public VoxelGridGenerator(
double voxelSizeMeters,
double vehicleRadiusMeters,
double vehicleHeightMeters,
double objectRadiusMeters,
double objectHeightMeters,
double safetyMarginMeters)
{
this.voxelSize = voxelSizeMeters;
this.vehicleRadius = vehicleRadiusMeters;
this.vehicleHeight = vehicleHeightMeters;
this.objectRadius = objectRadiusMeters;
this.objectHeight = objectHeightMeters;
this.safetyMargin = safetyMarginMeters;
}
@ -714,7 +714,7 @@ public class VoxelGridGenerator
/// </summary>
private void MarkVoxels(VoxelGrid grid, MeshSignedDistanceGrid sdf)
{
double minPassableDistance = vehicleRadius + safetyMargin;
double minPassableDistance = objectRadius + safetyMargin;
for (int x = 0; x < grid.SizeX; x++)
{

View File

@ -2,7 +2,7 @@
## 概述
吊装路径Hoisting Path采用门式起重机Gantry Crane的运动方式车辆沿地面路径行驶不同,物体在平移过程中**不发生旋转**。
吊装路径Hoisting Path采用门式起重机Gantry Crane的运动方式物体沿地面路径行驶不同,物体在平移过程中**不发生旋转**。
## 运动阶段
@ -104,12 +104,12 @@ else
- 转折90度width = 物体长度(侧面朝向路径垂直方向)
- **原因**门式起重机平移时物体不旋转路径转折90度后物体相对于路径的朝向发生变化通行空间需要相应调整以准确描述物体占据的空间
## 与车辆路径的区别
## 与物体路径的区别
| 特性 | 车辆路径(地面) | 吊装路径(空中) |
| 特性 | 物体路径(地面) | 吊装路径(空中) |
|------|----------------|----------------|
| 运动方式 | 沿路径行驶,随路径转向 | 平移,姿态保持不变 |
| 转向行为 | 车辆旋转以适应路径 | 物体不旋转 |
| 转向行为 | 物体旋转以适应路径 | 物体不旋转 |
| 通行空间方向 | 跟随路径方向 | 保持物体固有方向 |
| 适用场景 | 地面物流运输 | 空中吊装作业 |
@ -134,7 +134,7 @@ if (horizontalDirection != null)
```csharp
// 通行空间包裹调整:水平段起点向反方向、终点向正方向各延伸半个物体尺寸
if (_visualizationMode == PathVisualizationMode.VehicleSpace && !isVerticalSegment)
if (_visualizationMode == PathVisualizationMode.ObjectSpace && !isVerticalSegment)
{
double offset = alongPathSize / 2.0; // 沿路径方向的物体尺寸的一半
@ -158,7 +158,7 @@ if (_visualizationMode == PathVisualizationMode.VehicleSpace && !isVerticalSegme
### 渲染模式
使用 `VehicleSpace` 可视化模式显示吊装路径的通行空间:
使用 `ObjectSpace` 可视化模式显示吊装路径的通行空间:
- 起吊/下降段:垂直长方体,截面为物体宽度 × 物体长度
- 平移段(初始方向):水平长方体,截面为物体宽度 × 物体高度

View File

@ -25,14 +25,14 @@ D\*算法Dynamic A\*是A\*算法的扩展,专门用于在**未知或动
* **复杂性高:** 相较于A\*D\*算法的实现更为复杂需要维护更多的内部信息如OPEN/CLOSED集合、路径树和g值 [3, 8]。
* **计算成本和内存开销:** 尽管能够高效重新规划但D\*的初始计算和维护成本通常高于A\* [3, 8]。
* **路径平滑性:** 类似于A\*D\*在离散网格上生成的路径也可能存在“锯齿状”问题,因为它仍然基于网格单元格之间的离散移动。
* **变体:** D\* Lite是D\*的一个简化版本,更高效且易于实现,常用于自主车辆和机器人领域 [8]。研究表明D\* Lite在处理高密度动态环境如交通拥堵时表现出卓越的适应性和鲁棒性能够动态重新计算路线并成功完成任务而A\*在某些情况下可能会失败或卡住 [9]。
* **变体:** D\* Lite是D\*的一个简化版本,更高效且易于实现,常用于自主物体和机器人领域 [8]。研究表明D\* Lite在处理高密度动态环境如交通拥堵时表现出卓越的适应性和鲁棒性能够动态重新计算路线并成功完成任务而A\*在某些情况下可能会失败或卡住 [9]。
## 3. Field D\*算法
Field D\*算法是D\*算法的一种变体它在D\*处理动态环境能力的基础上,进一步优化了路径的平滑性和自然度 [7]。它通过**插值**技术,允许路径上的航点位于网格边缘的任意位置,而不是局限于网格单元格的中心,从而生成更直接、低成本且平滑的路径,尤其适用于非均匀成本环境 [7]。
* **优点:**
* **路径平滑性:** 这是Field D\*最显著的优势。它解决了传统网格路径规划器中离散状态转换导致路径不自然的问题,能够生成更适合车辆运动的平滑路径 [7]。
* **路径平滑性:** 这是Field D\*最显著的优势。它解决了传统网格路径规划器中离散状态转换导致路径不自然的问题,能够生成更适合物体运动的平滑路径 [7]。
* **处理非均匀成本:** 能够很好地处理网格中不同区域的非均匀移动成本,这对于您项目中门(高成本)和通道(低成本)的属性设置非常有利 [7]。
* **动态环境适应性:** 继承了D\*算法处理动态环境和高效重新规划的能力 [7, 8]。
* **缺点:**
@ -51,14 +51,14 @@ Field D\*算法是D\*算法的一种变体它在D\*处理动态环境能力
| **计算成本** | 对于单一目标搜索,在大图上效率较低 | 对于单一目标搜索比Dijkstra高效 | 比A\*复杂,维护成本和内存开销更高 | 最复杂,插值计算可能增加计算量 |
| **主要优势** | 保证找到所有最短路径 | 静态环境中单目标路径规划的效率和最优性 | 环境变化时高效重新规划,适应动态环境 | 生成高度平滑的路径,处理非均匀成本,适应动态环境 |
| **主要劣势** | 不适合单目标搜索,效率低 | 环境变化需完全重新计算,路径可能“锯齿状” | 实现复杂,资源消耗相对较高 | 实现最复杂,计算开销可能最高,可能超出项目简化需求 |
| **项目适用性** | 不推荐(过于通用) | **推荐作为首选**:高效、成熟,适合大部分静态障碍物场景,可通过重新运行应对门状态变化。 | **可选**如果门状态频繁变化或有临时障碍物D\* Lite可能是一个折衷方案提供动态适应性。 | **有潜力,但需权衡**:能生成更适合车辆的平滑路径,并处理门/通道的非均匀成本。但其复杂性和潜在的计算开销可能与“减少计算量”的目标相悖。 |
| **项目适用性** | 不推荐(过于通用) | **推荐作为首选**:高效、成熟,适合大部分静态障碍物场景,可通过重新运行应对门状态变化。 | **可选**如果门状态频繁变化或有临时障碍物D\* Lite可能是一个折衷方案提供动态适应性。 | **有潜力,但需权衡**:能生成更适合物体的平滑路径,并处理门/通道的非均匀成本。但其复杂性和潜在的计算开销可能与“减少计算量”的目标相悖。 |
## Field D\*在本项目中的适用性评估
根据您的项目需求,即“检测车辆能否无碰撞的到达终点”、“2.5D场景尽量简化减少计算量”并考虑到“路径中经过的门、通道等可以设置通过性和限制性属性”以下是Field D\*的适用性分析:
根据您的项目需求,即“检测物体能否无碰撞的到达终点”、“2.5D场景尽量简化减少计算量”并考虑到“路径中经过的门、通道等可以设置通过性和限制性属性”以下是Field D\*的适用性分析:
1. **路径平滑性:** Field D\*能够生成平滑的路径 [7],这对于物流车辆在大型建筑中行驶非常有利,因为实际车辆的运动不是严格的90度或45度转弯。如果路径的“自然度”和可行驶性是关键要求Field D\*具有明显优势。
2. **非均匀成本环境:** 项目中门和通道的“通过性和限制性属性”可以很好地映射到Field D\*处理非均匀成本网格的能力 [7]。例如,通过门可以设置更高的成本,而通过指定通道可以设置更低的成本,从而引导车辆优先选择更优的路线。
1. **路径平滑性:** Field D\*能够生成平滑的路径 [7],这对于物流物体在大型建筑中行驶非常有利,因为实际物体的运动不是严格的90度或45度转弯。如果路径的“自然度”和可行驶性是关键要求Field D\*具有明显优势。
2. **非均匀成本环境:** 项目中门和通道的“通过性和限制性属性”可以很好地映射到Field D\*处理非均匀成本网格的能力 [7]。例如,通过门可以设置更高的成本,而通过指定通道可以设置更低的成本,从而引导物体优先选择更优的路线。
3. **动态环境适应性:** 尽管大型建筑的结构(墙壁、柱子)是静态的,但门的状态(开/关或临时障碍物的出现可以被视为动态变化。Field D\*继承了D\*的动态重新规划能力 [8],这意味着当这些属性或环境发生变化时,插件可以更高效地更新路径,而无需完全重新计算。
然而Field D\*的**复杂性和潜在的计算开销**是需要重点考虑的因素 [8]。您明确要求“尽量简化减少计算量”。Field D\*的插值计算和更复杂的节点定义可能会增加计算负担这可能与您的简化目标相悖。对于一个主要由静态障碍物构成的2.5D环境且动态变化如门状态相对较少或可控的场景一个经过优化的A\*算法(例如,使用高效的优先队列和结构体节点 [5])可能已经足够,并且在计算量上更具优势。
@ -66,7 +66,7 @@ Field D\*算法是D\*算法的一种变体它在D\*处理动态环境能力
**结论:**
* **A\*算法**是您项目的**首选**。它在静态环境中表现出色计算效率高且实现相对简单。对于门的状态变化可以通过在每次状态改变时重新运行A\*来处理,或者在网格构建时动态调整相关单元格的成本。
* **Field D\*算法**在生成**平滑路径**和处理**非均匀成本**方面具有显著优势,这对于物流车辆的实际行驶非常理想。如果路径的平滑性和自然度是项目的核心且不可妥协的需求并且您有能力投入更多精力处理其更高的实现复杂性和潜在的计算开销那么Field D\*是一个值得深入研究的**高级选项**。
* **Field D\*算法**在生成**平滑路径**和处理**非均匀成本**方面具有显著优势,这对于物流物体的实际行驶非常理想。如果路径的平滑性和自然度是项目的核心且不可妥协的需求并且您有能力投入更多精力处理其更高的实现复杂性和潜在的计算开销那么Field D\*是一个值得深入研究的**高级选项**。
* **D\* Lite**可以作为A\*和Field D\*之间的一个**折衷方案**。它提供了D\*的动态重新规划能力但比完整的Field D\*更简单、更高效,适用于门状态频繁变化或有少量临时障碍物的场景。
在项目初期建议从实现一个高效的A\*算法开始因为它能满足核心的无碰撞路径规划需求并符合“减少计算量”的目标。如果后续测试发现路径平滑性或对动态变化的响应速度成为瓶颈再考虑升级到Field D\*或D\* Lite以逐步增加复杂性。

View File

@ -33,7 +33,7 @@
## **3. 基于 VDA 5050 标准的路径数据结构**
为了实现仿真系统与真实车辆控制、以及 DELMIA 等平台的深度集成,本方案参考 **VDA 5050 协议** 定义了标准化的路径数据模型。VDA 5050 将路径描述为一个由**节点Nodes**和**边Edges**组成的有向图,并允许通过轨迹属性精确定义运动曲线。
为了实现仿真系统与真实物体控制、以及 DELMIA 等平台的深度集成,本方案参考 **VDA 5050 协议** 定义了标准化的路径数据模型。VDA 5050 将路径描述为一个由**节点Nodes**和**边Edges**组成的有向图,并允许通过轨迹属性精确定义运动曲线。
### **3.1 节点 (Node) 数据结构**
@ -43,7 +43,7 @@
| :---- | :---- | :---- |
| nodeId | String | 节点的唯一标识符。 |
| sequenceId | Integer | 在当前订单路径中的序列号(递增)。 |
| nodePosition | Object | 包含 x、y 坐标及车辆目标航向角 theta弧度。 |
| nodePosition | Object | 包含 x、y 坐标及物体目标航向角 theta弧度。 |
| actions | Array | 在该位置执行的动作(如:举升、等待)。 |
### **3.2 边 (Edge) 数据结构**
@ -155,18 +155,18 @@ namespace AGVSim.Core {
#### **Works cited**
1. Turning Vehicle Simulation: Interactive Computer-Aided Design and Drafting Application, accessed December 30, 2025, [https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf](https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf)
1. Turning Object Simulation: Interactive Computer-Aided Design and Drafting Application, accessed December 30, 2025, [https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf](https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf)
2. Step-by-Step Guide to Conducting a Swept Path Analysis - Quantum Traffic, accessed December 30, 2025, [https://www.quantumtraffic.com.au/step-by-step-guide-to-conducting-a-swept-path-analysis](https://www.quantumtraffic.com.au/step-by-step-guide-to-conducting-a-swept-path-analysis)
3. Smoothed A* Algorithm for Practical Unmanned Surface Vehicle Path Planning - UCL Discovery, accessed December 30, 2025, [https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface vehicle path planning.pdf](https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface vehicle path planning.pdf)
3. Smoothed A* Algorithm for Practical Unmanned Surface Object Path Planning - UCL Discovery, accessed December 30, 2025, [https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface object path planning.pdf](https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface object path planning.pdf)
4. AGV PATH PLANNING BASED ON SMOOTHING A* ALGORITHM, accessed December 30, 2025, [https://airccse.org/journal/ijsea/papers/6515ijsea01.pdf](https://airccse.org/journal/ijsea/papers/6515ijsea01.pdf)
5. Iterative Learning-Based Path and Speed Profile Optimization for an Unmanned Surface Vehicle - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/](https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/)
5. Iterative Learning-Based Path and Speed Profile Optimization for an Unmanned Surface Object - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/](https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/)
6. Curved Paths - Red Blob Games, accessed December 30, 2025, [https://www.redblobgames.com/articles/curved-paths/](https://www.redblobgames.com/articles/curved-paths/)
7. PRM Path Smoothening by Circular Arc Fillet Method for Mobile Robot Navigation - DergiPark, accessed December 30, 2025, [https://dergipark.org.tr/tr/download/article-file/3067231](https://dergipark.org.tr/tr/download/article-file/3067231)
8. Solved: Fillet Two line c# - Autodesk Community, accessed December 30, 2025, [https://forums.autodesk.com/t5/net-forum/fillet-two-line-c/td-p/13101919](https://forums.autodesk.com/t5/net-forum/fillet-two-line-c/td-p/13101919)
9. AG Corners Live Effect | Astute Graphics Documentation, accessed December 30, 2025, [https://docs.astutegraphics.com/vectorscribe/ag-corners-live-effect](https://docs.astutegraphics.com/vectorscribe/ag-corners-live-effect)
10. javascript - Applying rounded corners to paths/polygons - Stack Overflow, accessed December 30, 2025, [https://stackoverflow.com/questions/19269622/applying-rounded-corners-to-paths-polygons](https://stackoverflow.com/questions/19269622/applying-rounded-corners-to-paths-polygons)
11. Path planning for autonomous vehicles using clothoid based smoothing of A* generated paths and optimal control - DiVA portal, accessed December 30, 2025, [https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf](https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf)
11. Path planning for autonomous objects using clothoid based smoothing of A* generated paths and optimal control - DiVA portal, accessed December 30, 2025, [https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf](https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf)
12. Collision Detection - Cepton, accessed December 30, 2025, [https://developer.cepton.com/blog/2d_collision_detection](https://developer.cepton.com/blog/2d_collision_detection)
13. Exact Mathematical Solution for Maximum Transient Offtracking Calculation of a Single-Unit Vehicle Negotiating Circular Curves - MDPI, accessed December 30, 2025, [https://www.mdpi.com/2076-3417/14/13/5570](https://www.mdpi.com/2076-3417/14/13/5570)
13. Exact Mathematical Solution for Maximum Transient Offtracking Calculation of a Single-Unit Object Negotiating Circular Curves - MDPI, accessed December 30, 2025, [https://www.mdpi.com/2076-3417/14/13/5570](https://www.mdpi.com/2076-3417/14/13/5570)
14. OrientedBoundingBox(OBB)Collision · SimonDarksideJ ... - GitHub, accessed December 30, 2025, [https://github.com/SimonDarksideJ/XNAGameStudio/wiki/OrientedBoundingBox(OBB)Collision](https://github.com/SimonDarksideJ/XNAGameStudio/wiki/OrientedBoundingBox(OBB)Collision)
15. Collision Detection Using the Separating Axis Theorem | Envato Tuts+, accessed December 30, 2025, [https://code.tutsplus.com/collision-detection-using-the-separating-axis-theorem--gamedev-169t](https://code.tutsplus.com/collision-detection-using-the-separating-axis-theorem--gamedev-169t)

View File

@ -2,20 +2,20 @@
## **离散路径规划在复杂物流环境中的局限性与平滑需求分析**
在当代工业自动化领域无人物流车AGV/AMR的路径规划通常始于基于图搜索如 A* 算法)或采样(如 RRT 算法)的离散路径生成过程 1。这些算法生成的原始路径通常由一系列离散的路径点Waypoints及连接它们的直线段组成。然而这种简化的几何表达在高性能碰撞仿真和真实车辆控制中存在显著的物理性缺陷。
在当代工业自动化领域无人物流车AGV/AMR的路径规划通常始于基于图搜索如 A* 算法)或采样(如 RRT 算法)的离散路径生成过程 1。这些算法生成的原始路径通常由一系列离散的路径点Waypoints及连接它们的直线段组成。然而这种简化的几何表达在高性能碰撞仿真和真实物体控制中存在显著的物理性缺陷。
首先,离散路径在路径点处存在明显的几何不连续性,即切向量在折点处发生突变,这种现象在运动学上表现为车辆航向角Heading Angle的瞬时改变 3。由于真实的物流车辆受限于非圆约束Non-holonomic Constraints如轴距、转向机构的最大偏转角等车辆无法在静止状态外实现零半径的瞬时转向 5。如果仿真系统仅基于直线段进行碰撞检测系统将完全忽略车辆在转弯过程中由于旋转而产生的扫掠区域Swept Path变化。对于精度要求极高的车间环境这种“运动学失真”可能导致严重的仿真偏差例如车辆在转弯时车尾摆动Tail-swing可能擦撞侧方货架而传统的直线连接模型由于无法描述转弯曲线将导致此类碰撞事件的漏检 7。
首先,离散路径在路径点处存在明显的几何不连续性,即切向量在折点处发生突变,这种现象在运动学上表现为物体航向角Heading Angle的瞬时改变 3。由于真实的物流物体受限于非圆约束Non-holonomic Constraints如轴距、转向机构的最大偏转角等物体无法在静止状态外实现零半径的瞬时转向 5。如果仿真系统仅基于直线段进行碰撞检测系统将完全忽略物体在转弯过程中由于旋转而产生的扫掠区域Swept Path变化。对于精度要求极高的车间环境这种“运动学失真”可能导致严重的仿真偏差例如物体在转弯时车尾摆动Tail-swing可能擦撞侧方货架而传统的直线连接模型由于无法描述转弯曲线将导致此类碰撞事件的漏检 7。
其次,碰撞仿真的保真度直接取决于路径的几何连续性等级。在路径点处,简单的 C^0 连续(仅位置连续)会导致车辆执行器产生无限大的向心加速度需求 4。通过引入平滑曲线如圆弧或贝塞尔曲线可以将路径提升至 G^1切线连续甚至 G^2曲率连续级别。这种提升不仅是为了视觉上的平滑更是为了在碰撞算法中能够以解析或高频采样的方式计算车体包围盒在每一时刻的精确位姿Pose 10。
其次,碰撞仿真的保真度直接取决于路径的几何连续性等级。在路径点处,简单的 C^0 连续(仅位置连续)会导致物体执行器产生无限大的向心加速度需求 4。通过引入平滑曲线如圆弧或贝塞尔曲线可以将路径提升至 G^1切线连续甚至 G^2曲率连续级别。这种提升不仅是为了视觉上的平滑更是为了在碰撞算法中能够以解析或高频采样的方式计算车体包围盒在每一时刻的精确位姿Pose 10。
## **车辆运动学模型与平滑曲线的技术关联**
## **物体运动学模型与平滑曲线的技术关联**
要实现符合真实场景的路径平滑,必须首先确立车辆的运动学基础。物流车的最小转弯半径 R 是由其物理结构决定的关键参数,它与车辆轴距 L 和转向轮最大偏角 delta 存在明确的数学关系:
要实现符合真实场景的路径平滑,必须首先确立物体的运动学基础。物流车的最小转弯半径 R 是由其物理结构决定的关键参数,它与物体轴距 L 和转向轮最大偏角 delta 存在明确的数学关系:
R = frac{L}{tan(delta)}
在车间环境的路径优化中,任何平滑技术都必须将此半径作为硬性约束,确保生成的曲线在任何一点的曲率半径 rho 均满足 rho ge R_{min} 5。
在碰撞仿真中,车辆通常被视为一个具有固定长度和宽度的刚体多边形最常见的是导向包围盒OBB, Oriented Bounding Box 14。当路径从离散折线变为连续曲线后OBB 的中心坐标 (x, y) 和旋转角 theta 成为路径参数 s弧长的连续函数。这意味着碰撞检测引擎不再仅仅检查静态线段之间的距离而是需要通过对曲线进行精细采样生成一系列在时间或空间上密集的位姿实例并对每一对实例执行分离轴定理SAT检测 16。这种从“线段检测”向“位姿序列检测”的转变是提升碰撞仿真精度的核心机制。
在碰撞仿真中,物体通常被视为一个具有固定长度和宽度的刚体多边形最常见的是导向包围盒OBB, Oriented Bounding Box 14。当路径从离散折线变为连续曲线后OBB 的中心坐标 (x, y) 和旋转角 theta 成为路径参数 s弧长的连续函数。这意味着碰撞检测引擎不再仅仅检查静态线段之间的距离而是需要通过对曲线进行精细采样生成一系列在时间或空间上密集的位姿实例并对每一对实例执行分离轴定理SAT检测 16。这种从“线段检测”向“位姿序列检测”的转变是提升碰撞仿真精度的核心机制。
## **路径平滑算法的对比与选型**
@ -28,7 +28,7 @@ R = frac{L}{tan(delta)}
| 三次 B 样条 (B-Spline) | C^2 | 高 | 曲率连续,适合高速 AGV但局部控制参数调整复杂 4。 | 极高(平滑度最好) |
| 回旋曲线 (Clothoid) | G^2 | 极高 | 曲率随弧长线性变化,计算极其复杂,涉及菲涅耳积分 12。 | 极高(符合真实驾驶习惯) |
针对“局部功能优化”且“满足仿真要求”的前提本方案选择圆弧过渡法Arc Fillet作为核心算法 19。该算法在两个切线段之间插入一段半径为 R 的圆弧。其优势在于:圆弧上的曲率半径是恒定的,这使得在仿真中可以非常容易地计算出车辆在转弯时的固定偏转角,且圆弧的偏置线(考虑车体宽度后的扫掠轨迹)依然是简单的圆弧或线段,极大地降低了碰撞检测的数学开销。
针对“局部功能优化”且“满足仿真要求”的前提本方案选择圆弧过渡法Arc Fillet作为核心算法 19。该算法在两个切线段之间插入一段半径为 R 的圆弧。其优势在于:圆弧上的曲率半径是恒定的,这使得在仿真中可以非常容易地计算出物体在转弯时的固定偏转角,且圆弧的偏置线(考虑车体宽度后的扫掠轨迹)依然是简单的圆弧或线段,极大地降低了碰撞检测的数学开销。
## **关键算法实现与路径拓扑重构**
@ -53,10 +53,10 @@ L_t = frac{R}{tan(frac{alpha}{2})}
在曲线平滑后,原始路径点 mathbf{P}_i 将不再位于物理路径上。在路径规划逻辑中,应按以下策略处理
* **角色定义**:将原始路径点定义为**虚拟控制点 (Control Points)**。它们用于定义路径的宏观拓扑,不直接参与车辆的运动执行。
* **角色定义**:将原始路径点定义为**虚拟控制点 (Control Points)**。它们用于定义路径的宏观拓扑,不直接参与物体的运动执行。
* **特征点生成**:算法会在每个折点处生成两个新的物理特征点——**进入切点 (Tangent Start, T_s)** 和 **退出切点 (Tangent End, T_e)**
* **段序列结构**:将路径的数据结构由 List<Point> 升级为 List<PathSegment>。路径变为直线段与圆弧段交替的序列P_0 to T_{s1} to text{Arc}(T_{s1}, T_{e1}) to T_{s2} dots 。
* **状态切换逻辑**车辆控制器在直线段行驶时处于“巡线模式”,一旦坐标到达 T_s立即切换为“定曲率转向模式”直至到达 T_e 后恢复直线巡线。
* **状态切换逻辑**物体控制器在直线段行驶时处于“巡线模式”,一旦坐标到达 T_s立即切换为“定曲率转向模式”直至到达 T_e 后恢复直线巡线。
## **控制参数描述与可视化方案**
@ -64,7 +64,7 @@ L_t = frac{R}{tan(frac{alpha}{2})}
| 参数名称 | 变量类型 | 单位 | 描述说明 | 典型取值范围 |
| :---- | :---- | :---- | :---- | :---- |
| 期望转弯半径 (R) | Double | 米 | 车辆执行转弯的曲率半径。 | 0.5 - 5.0 |
| 期望转弯半径 (R) | Double | 米 | 物体执行转弯的曲率半径。 | 0.5 - 5.0 |
| 最小半径限制 (R_min) | Double | 米 | 转向机构物理极限,作为强制约束。 | 0.8 - 1.5 |
| 采样步长 (Delta s) | Double | 米 | 碰撞离散化采样的空间分辨率 7。 | 0.01 - 0.1 |
| 切线占比上限 (eta) | Double | % | 防止圆弧重叠的最大比例 24。 | 10% - 45% |
@ -86,7 +86,7 @@ using System;
using System.Collections.Generic;
using System.Windows; // 提供 Vector 和 Point 计算支持
namespace LogisticsVehicleSimulation.Geometry
namespace LogisticsObjectSimulation.Geometry
{
/// <summary>
/// 圆弧平滑路径段计算结果
@ -177,9 +177,9 @@ namespace LogisticsVehicleSimulation.Geometry
/// <summary>
/// 生成用于碰撞仿真的采样位姿序列 [7, 16, 26]
/// </summary>
public List<VehiclePose> GenerateSimulationPoses(ArcSegment arc, double samplingStep)
public List<ObjectPose> GenerateSimulationPoses(ArcSegment arc, double samplingStep)
{
var poses = new List<VehiclePose>();
var poses = new List<ObjectPose>();
double arcLength = Math.Abs(arc.SweepAngle) * arc.Radius;
int stepCount = Math.Max(2, (int)(arcLength / samplingStep));
@ -196,17 +196,17 @@ namespace LogisticsVehicleSimulation.Geometry
// 切线方向 = 半径方向 + 90度 (取决于旋转方向)
double heading = currentAngle + (arc.SweepAngle > 0? Math.PI / 2.0 : -Math.PI / 2.0);
poses.Add(new VehiclePose(new Point(x, y), heading));
poses.Add(new ObjectPose(new Point(x, y), heading));
}
return poses;
}
}
public struct VehiclePose
public struct ObjectPose
{
public Point Position;
public double HeadingRadians; // 弧度单位
public VehiclePose(Point p, double h) { Position = p; HeadingRadians = h; }
public ObjectPose(Point p, double h) { Position = p; HeadingRadians = h; }
}
}
```
@ -301,11 +301,11 @@ namespace AGVSimulation.Geometry
## **高精度碰撞仿真的深度优化:扫掠路径分析 (Swept Path Analysis)**
在无人物流车的实际运行中,仅仅平滑路径中心点是不够的。高精度仿真的核心挑战在于如何处理转弯时车辆的“内轮差”和“车尾摆动” 7。
在无人物流车的实际运行中,仅仅平滑路径中心点是不够的。高精度仿真的核心挑战在于如何处理转弯时物体的“内轮差”和“车尾摆动” 7。
### **扫掠区域几何特性**
当物流车沿着圆弧路径行驶时其内侧后轮的行驶半径要小于前轮转向轮的行驶半径。在仿真中这种现象被称为“Off-tracking” 7。如果不考虑这一点仿真可能显示路径安全但实际车辆转弯时内侧车体会撞击立柱。
当物流车沿着圆弧路径行驶时其内侧后轮的行驶半径要小于前轮转向轮的行驶半径。在仿真中这种现象被称为“Off-tracking” 7。如果不考虑这一点仿真可能显示路径安全但实际物体转弯时内侧车体会撞击立柱。
高精度仿真方案应采用以下改进措施:
@ -321,20 +321,20 @@ namespace AGVSimulation.Geometry
#### **Works cited**
1. Smoothed A* Algorithm for Practical Unmanned Surface Vehicle Path Planning - UCL Discovery, accessed December 30, 2025, [https://discovery.ucl.ac.uk/10064237/3/Liu%20Smoothed%20A%20algorithm%20for%20practical%20unmanned%20surface%20vehicle%20path%20planning.pdf](https://discovery.ucl.ac.uk/10064237/3/Liu%20Smoothed%20A%20algorithm%20for%20practical%20unmanned%20surface%20vehicle%20path%20planning.pdf)
1. Smoothed A* Algorithm for Practical Unmanned Surface Object Path Planning - UCL Discovery, accessed December 30, 2025, [https://discovery.ucl.ac.uk/10064237/3/Liu%20Smoothed%20A%20algorithm%20for%20practical%20unmanned%20surface%20object%20path%20planning.pdf](https://discovery.ucl.ac.uk/10064237/3/Liu%20Smoothed%20A%20algorithm%20for%20practical%20unmanned%20surface%20object%20path%20planning.pdf)
2. AGV Path Planning Based on Smoothing A* Algorithm - ResearchGate, accessed December 30, 2025, [https://www.researchgate.net/publication/282775416_AGV_Path_Planning_Based_on_Smoothing_A_Algorithm](https://www.researchgate.net/publication/282775416_AGV_Path_Planning_Based_on_Smoothing_A_Algorithm)
3. AGV PATH PLANNING BASED ON SMOOTHING A* ALGORITHM, accessed December 30, 2025, [https://airccse.org/journal/ijsea/papers/6515ijsea01.pdf](https://airccse.org/journal/ijsea/papers/6515ijsea01.pdf)
4. Bézier Splines: Continuity, accessed December 30, 2025, [https://www.csc.kth.se/~weinkauf/notes/beziersplinecontinuity.html](https://www.csc.kth.se/~weinkauf/notes/beziersplinecontinuity.html)
5. Iterative Learning-Based Path and Speed Profile Optimization for an Unmanned Surface Vehicle - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/](https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/)
5. Iterative Learning-Based Path and Speed Profile Optimization for an Unmanned Surface Object - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/](https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/)
6. Turning radius - Grokipedia, accessed December 30, 2025, [https://grokipedia.com/page/Turning_radius](https://grokipedia.com/page/Turning_radius)
7. Turning Vehicle Simulation: Interactive Computer-Aided Design and Drafting Application, accessed December 30, 2025, [https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf](https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf)
7. Turning Object Simulation: Interactive Computer-Aided Design and Drafting Application, accessed December 30, 2025, [https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf](https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf)
8. Step-by-Step Guide to Conducting a Swept Path Analysis - Quantum Traffic, accessed December 30, 2025, [https://www.quantumtraffic.com.au/step-by-step-guide-to-conducting-a-swept-path-analysis](https://www.quantumtraffic.com.au/step-by-step-guide-to-conducting-a-swept-path-analysis)
9. Path Smoothing Techniques in Robot Navigation: State-of-the-Art, Current and Future Challenges - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC6165411/](https://pmc.ncbi.nlm.nih.gov/articles/PMC6165411/)
10. Smooth path planning with safety margins using Piece-Wise Bezier curves - arXiv, accessed December 30, 2025, [https://arxiv.org/pdf/2510.24972](https://arxiv.org/pdf/2510.24972)
11. Collision Avoidance Path Planning for Automated Vehicles Using Prediction Information and Artificial Potential Field - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC11598233/](https://pmc.ncbi.nlm.nih.gov/articles/PMC11598233/)
12. Path planning for autonomous vehicles using clothoid based smoothing of A* generated paths and optimal control - DiVA portal, accessed December 30, 2025, [https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf](https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf)
11. Collision Avoidance Path Planning for Automated Objects Using Prediction Information and Artificial Potential Field - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC11598233/](https://pmc.ncbi.nlm.nih.gov/articles/PMC11598233/)
12. Path planning for autonomous objects using clothoid based smoothing of A* generated paths and optimal control - DiVA portal, accessed December 30, 2025, [https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf](https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf)
13. Article 99 | Turning Radius - Virtual CRASH, accessed December 30, 2025, [https://www.vcrashusa.com/kb-vc-article99](https://www.vcrashusa.com/kb-vc-article99)
14. (PDF) An Efficient Centralized Planner for Multiple Automated Guided Vehicles at the Crossroad of Polynomial Curves - ResearchGate, accessed December 30, 2025, [https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Vehicles_at_the_Crossroad_of_Polynomial_Curves](https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Vehicles_at_the_Crossroad_of_Polynomial_Curves)
14. (PDF) An Efficient Centralized Planner for Multiple Automated Guided Objects at the Crossroad of Polynomial Curves - ResearchGate, accessed December 30, 2025, [https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Objects_at_the_Crossroad_of_Polynomial_Curves](https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Objects_at_the_Crossroad_of_Polynomial_Curves)
15. Oriented bounding box (OBB): All you need to know - Mindkosh AI, accessed December 30, 2025, [https://mindkosh.com/blog/oriented-bounding-box-annotation-all-you-need-to-know/](https://mindkosh.com/blog/oriented-bounding-box-annotation-all-you-need-to-know/)
16. The Math Behind Bounding Box Collision Detection - AABB vs OBB(Separate Axis Theorem), accessed December 30, 2025, [https://dev.to/pratyush_mohanty_6b8f2749/the-math-behind-bounding-box-collision-detection-aabb-vs-obbseparate-axis-theorem-1gdn](https://dev.to/pratyush_mohanty_6b8f2749/the-math-behind-bounding-box-collision-detection-aabb-vs-obbseparate-axis-theorem-1gdn)
17. OrientedBoundingBox(OBB)Collision · SimonDarksideJ ... - GitHub, accessed December 30, 2025, [https://github.com/SimonDarksideJ/XNAGameStudio/wiki/OrientedBoundingBox(OBB)Collision](https://github.com/SimonDarksideJ/XNAGameStudio/wiki/OrientedBoundingBox(OBB)Collision)
@ -348,7 +348,7 @@ namespace AGVSimulation.Geometry
25. javascript - Applying rounded corners to paths/polygons - Stack Overflow, accessed December 30, 2025, [https://stackoverflow.com/questions/19269622/applying-rounded-corners-to-paths-polygons](https://stackoverflow.com/questions/19269622/applying-rounded-corners-to-paths-polygons)
26. How to detect collisions with a curve - Stack Overflow, accessed December 30, 2025, [https://stackoverflow.com/questions/39541744/how-to-detect-collisions-with-a-curve](https://stackoverflow.com/questions/39541744/how-to-detect-collisions-with-a-curve)
27. turning radius calculations based on average of inner and outer front wheel cramp angles, accessed December 30, 2025, [https://www.mvfpd.org/files/ee1fa2b8f/turn+radii+for+developers.pdf](https://www.mvfpd.org/files/ee1fa2b8f/turn+radii+for+developers.pdf)
28. Exact Mathematical Solution for Maximum Transient Offtracking Calculation of a Single-Unit Vehicle Negotiating Circular Curves - MDPI, accessed December 30, 2025, [https://www.mdpi.com/2076-3417/14/13/5570](https://www.mdpi.com/2076-3417/14/13/5570)
28. Exact Mathematical Solution for Maximum Transient Offtracking Calculation of a Single-Unit Object Negotiating Circular Curves - MDPI, accessed December 30, 2025, [https://www.mdpi.com/2076-3417/14/13/5570](https://www.mdpi.com/2076-3417/14/13/5570)
29. Preparing Swept Path Analysis' | Invarion Help Center, accessed December 30, 2025, [https://help.invarion.com/rapidpath-online/swept-path-analysis/preparing-swept-path-analysis/](https://help.invarion.com/rapidpath-online/swept-path-analysis/preparing-swept-path-analysis/)
30. Physics Tutorial 4: Collision Detection, accessed December 30, 2025, [https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/previousinformation/physics4collisiondetection/2017%20Tutorial%204%20-%20Collision%20Detection.pdf](https://research.ncl.ac.uk/game/mastersdegree/gametechnologies/previousinformation/physics4collisiondetection/2017%20Tutorial%204%20-%20Collision%20Detection.pdf)
31. Simple Oriented Bounding Box OBB collision detection explaining - Stack Overflow, accessed December 30, 2025, [https://stackoverflow.com/questions/47866571/simple-oriented-bounding-box-obb-collision-detection-explaining](https://stackoverflow.com/questions/47866571/simple-oriented-bounding-box-obb-collision-detection-explaining)

View File

@ -2,7 +2,7 @@
## **1. 核心需求与思路分析**
当前基于直线连接的路径在转弯处存在“瞬间转向”的非物理现象,导致仿真系统漏掉车辆在转弯时由于旋转Swept Path产生的碰撞 1。
当前基于直线连接的路径在转弯处存在“瞬间转向”的非物理现象,导致仿真系统漏掉物体在转弯时由于旋转Swept Path产生的碰撞 1。
简化策略采用圆弧过渡法Arc Fillet。在任意两个路径段之间插入一段切圆弧。这种方法数学计算最少且能完美描述 AGV 的恒定转向状态 3。
## **2. 关键算法实现逻辑**
@ -38,7 +38,7 @@
| 参数类别 | 参数名称 | 说明 | 典型值 |
| :---- | :---- | :---- | :---- |
| **车辆参数** | 转向半径 (R) | 车辆物理允许的转弯半径。 | 1.5 m |
| **物体参数** | 转向半径 (R) | 物体物理允许的转弯半径。 | 1.5 m |
| **仿真精度** | 采样步长 (Delta s) | 碰撞检测时圆弧离散化的间距。 | 0.05 m |
| **局部控制** | 半径覆盖 | 在点列表中,允许单独为某个急转弯设置小半径。 | - |
| **导出设置** | 文件格式 | 导出 DELMIA XML 或 CSV 坐标序列。 | XML |
@ -89,9 +89,9 @@ public class PathEngine {
## **6. 高精度碰撞仿真策略:扫掠分析 (Swept Path)**
在 Navisworks 中,不仅检查中心线,还要利用车辆宽度(轮距)生成 OBB 包围盒序列 1
在 Navisworks 中,不仅检查中心线,还要利用物体宽度(轮距)生成 OBB 包围盒序列 1
1. **位姿切片**:在圆弧段按“采样步长”生成密集的车辆外框实例。
1. **位姿切片**:在圆弧段按“采样步长”生成密集的物体外框实例。
2. **SAT 判定**:对每个实例执行分离轴定理检测。由于圆弧段是连续生成的,这能有效检出内轮差碰撞风险。
## **7. 结论**
@ -100,15 +100,15 @@ public class PathEngine {
#### **Works cited**
1. Turning Vehicle Simulation: Interactive Computer-Aided Design and Drafting Application, accessed December 30, 2025, [https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf](https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf)
1. Turning Object Simulation: Interactive Computer-Aided Design and Drafting Application, accessed December 30, 2025, [https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf](https://onlinepubs.trb.org/Onlinepubs/trr/1995/1500/1500-001.pdf)
2. Step-by-Step Guide to Conducting a Swept Path Analysis - Quantum Traffic, accessed December 30, 2025, [https://www.quantumtraffic.com.au/step-by-step-guide-to-conducting-a-swept-path-analysis](https://www.quantumtraffic.com.au/step-by-step-guide-to-conducting-a-swept-path-analysis)
3. Curved Paths - Red Blob Games, accessed December 30, 2025, [https://www.redblobgames.com/articles/curved-paths/](https://www.redblobgames.com/articles/curved-paths/)
4. PRM Path Smoothening by Circular Arc Fillet Method for Mobile Robot Navigation - DergiPark, accessed December 30, 2025, [https://dergipark.org.tr/tr/download/article-file/3067231](https://dergipark.org.tr/tr/download/article-file/3067231)
5. Smoothed A* Algorithm for Practical Unmanned Surface Vehicle Path Planning - UCL Discovery, accessed December 30, 2025, [https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface vehicle path planning.pdf](https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface vehicle path planning.pdf)
5. Smoothed A* Algorithm for Practical Unmanned Surface Object Path Planning - UCL Discovery, accessed December 30, 2025, [https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface object path planning.pdf](https://discovery.ucl.ac.uk/10064237/3/Liu Smoothed A algorithm for practical unmanned surface object path planning.pdf)
6. AGV PATH PLANNING BASED ON SMOOTHING A* ALGORITHM, accessed December 30, 2025, [https://airccse.org/journal/ijsea/papers/6515ijsea01.pdf](https://airccse.org/journal/ijsea/papers/6515ijsea01.pdf)
7. Iterative Learning-Based Path and Speed Profile Optimization for an Unmanned Surface Vehicle - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/](https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/)
7. Iterative Learning-Based Path and Speed Profile Optimization for an Unmanned Surface Object - PMC - NIH, accessed December 30, 2025, [https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/](https://pmc.ncbi.nlm.nih.gov/articles/PMC7014130/)
8. An Algorithm for Polygons with Rounded Corners - Gorilla Sun, accessed December 30, 2025, [https://www.gorillasun.de/blog/an-algorithm-for-polygons-with-rounded-corners/](https://www.gorillasun.de/blog/an-algorithm-for-polygons-with-rounded-corners/)
9. AG Corners Live Effect | Astute Graphics Documentation, accessed December 30, 2025, [https://docs.astutegraphics.com/vectorscribe/ag-corners-live-effect](https://docs.astutegraphics.com/vectorscribe/ag-corners-live-effect)
10. Path planning for autonomous vehicles using clothoid based smoothing of A* generated paths and optimal control - DiVA portal, accessed December 30, 2025, [https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf](https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf)
11. (PDF) An Efficient Centralized Planner for Multiple Automated Guided Vehicles at the Crossroad of Polynomial Curves - ResearchGate, accessed December 30, 2025, [https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Vehicles_at_the_Crossroad_of_Polynomial_Curves](https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Vehicles_at_the_Crossroad_of_Polynomial_Curves)
10. Path planning for autonomous objects using clothoid based smoothing of A* generated paths and optimal control - DiVA portal, accessed December 30, 2025, [https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf](https://www.diva-portal.org/smash/get/diva2:1150741/FULLTEXT01.pdf)
11. (PDF) An Efficient Centralized Planner for Multiple Automated Guided Objects at the Crossroad of Polynomial Curves - ResearchGate, accessed December 30, 2025, [https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Objects_at_the_Crossroad_of_Polynomial_Curves](https://www.researchgate.net/publication/355863030_An_Efficient_Centralized_Planner_for_Multiple_Automated_Guided_Objects_at_the_Crossroad_of_Polynomial_Curves)
12. Preparing Swept Path Analysis' | Invarion Help Center, accessed December 30, 2025, [https://help.invarion.com/rapidpath-online/swept-path-analysis/preparing-swept-path-analysis/](https://help.invarion.com/rapidpath-online/swept-path-analysis/preparing-swept-path-analysis/)

View File

@ -1,16 +1,16 @@
# **Navisworks 2026物流路径规划插件BIM环境中无碰撞车辆导航的高级实现指南**
# **Navisworks 2026物流路径规划插件BIM环境中无碰撞物体导航的高级实现指南**
## **I. 执行摘要**
本报告旨在详细阐述在Navisworks 2026中开发C\#插件的综合方案,以实现大型建筑模型内物流车辆的自动化、无碰撞路径规划。该解决方案的核心在于智能模型数据提取、基于网格的A\*路径规划算法、简化的包围盒碰撞检测以及强大的路径可视化和数据导出功能。本插件能够克服原生模型属性中缺乏明确物流属性的挑战,显著降低计算开销,并生成可操作的导航地图,从而大幅提升大型建筑环境中的运营效率。通过精确识别和分类模型元素、高效计算最优路径以及直观地展示结果,本插件为现代物流管理提供了关键的数字工具。
本报告旨在详细阐述在Navisworks 2026中开发C\#插件的综合方案,以实现大型建筑模型内物流物体的自动化、无碰撞路径规划。该解决方案的核心在于智能模型数据提取、基于网格的A\*路径规划算法、简化的包围盒碰撞检测以及强大的路径可视化和数据导出功能。本插件能够克服原生模型属性中缺乏明确物流属性的挑战,显著降低计算开销,并生成可操作的导航地图,从而大幅提升大型建筑环境中的运营效率。通过精确识别和分类模型元素、高效计算最优路径以及直观地展示结果,本插件为现代物流管理提供了关键的数字工具。
## **II. BIM中的物流路径规划概述**
### **问题陈述与项目目标**
在大型多层建筑(如仓库、工厂或大型商业综合体)中,物流运营面临着优化车辆移动的严峻挑战。手动路径规划耗时、易出错并且通常无法有效适应动态变化或识别最优路线。用户提出的核心问题是在Navisworks模型中针对特定楼层内的物流车辆例如叉车、托盘搬运车需要实现自动化的路径规划以确保无碰撞。鉴于原生BIM数据中缺少明确的“物流”属性这要求从建筑元素的几何和通用BIM属性中推断出它们的角色如墙壁、柱子、门、通道
在大型多层建筑(如仓库、工厂或大型商业综合体)中,物流运营面临着优化物体移动的严峻挑战。手动路径规划耗时、易出错并且通常无法有效适应动态变化或识别最优路线。用户提出的核心问题是在Navisworks模型中针对特定楼层内的物流物体例如叉车、托盘搬运车需要实现自动化的路径规划以确保无碰撞。鉴于原生BIM数据中缺少明确的“物流”属性这要求从建筑元素的几何和通用BIM属性中推断出它们的角色如墙壁、柱子、门、通道
本插件的主要目标包括:
@ -22,7 +22,7 @@
### **插件范围**
本插件专为Navisworks 2026设计并使用C\#.NET开发。其初始范围仅限于**2.5D路径规划**,这意味着它在建筑物的特定楼层内运行。这种简化通过消除垂直导航(楼梯、电梯、楼层之间的坡道)的考虑,显著降低了路径规划问题的复杂性。重点在于以车辆为中心的规划,其中车辆的物理尺寸通过简化的包围盒表示来考虑。碰撞检测被简化为包围盒交叉这是一种在精度和计算效率之间取得平衡的实用选择适用于此特定的2.5D场景。
本插件专为Navisworks 2026设计并使用C\#.NET开发。其初始范围仅限于**2.5D路径规划**,这意味着它在建筑物的特定楼层内运行。这种简化通过消除垂直导航(楼梯、电梯、楼层之间的坡道)的考虑,显著降低了路径规划问题的复杂性。重点在于以物体为中心的规划,其中物体的物理尺寸通过简化的包围盒表示来考虑。碰撞检测被简化为包围盒交叉这是一种在精度和计算效率之间取得平衡的实用选择适用于此特定的2.5D场景。
### **Navisworks API在空间分析中的概述**
@ -101,12 +101,12 @@ SelectionSet的功能使其与模型的当前状态保持一致。这可能
1. **定义网格范围:** 确定活动楼层的最小和最大X和Y坐标。这可以从整个楼层的包围盒或其中与物流相关的元素集合中得出。
2. **选择网格分辨率:** 选择一个单元格大小例如0.5米x 0.5米)。这是一个关键参数,影响路径精度和计算负荷。
3. **初始化网格单元格:** 创建一个2D数组例如GridCell\[,\]其中每个GridCell对象包含IsWalkable或IsTraversable、Cost等属性以及对占据该单元格的ModelItem的引用。
4. **填充障碍物:** 遍历所有已识别的障碍物ModelItem。对于每个障碍物将其BoundingBox3D投影到2D平面上并将所有相交的网格单元格标记为IsWalkable \= false或Cost \= infinity。此步骤应考虑车辆的尺寸(参见下文的“膨胀障碍物”)。
4. **填充障碍物:** 遍历所有已识别的障碍物ModelItem。对于每个障碍物将其BoundingBox3D投影到2D平面上并将所有相交的网格单元格标记为IsWalkable \= false或Cost \= infinity。此步骤应考虑物体的尺寸(参见下文的“膨胀障碍物”)。
5. **填充通道/可通行区域:** 将剩余的单元格标记为IsWalkable \= true并赋予默认Cost例如1。被识别为“通道”的单元格可能会获得较低的成本。
A\*等路径规划算法在离散图上运行 17。此网格作为图结构将复杂的BIM几何体抽象为简化的、可搜索的表示。
更精细的网格分辨率(更小的单元格大小)允许更精确的路径,这些路径更紧密地遵循建筑物和障碍物的轮廓。然而,它会指数级地增加图中的节点数量,导致内存消耗和路径规划计算时间显著增加 17 中关于“网格大小”影响渲染和计算时间的说明。因此,插件必须提供一个用户可配置的网格分辨率参数。这允许用户平衡路径精度(例如,对于狭窄的走廊)与可用的计算资源。对于典型的物流场景,通常会找到一个平衡点,其中单元格大小是车辆最小尺寸或最窄可通行路径的一小部分。
更精细的网格分辨率(更小的单元格大小)允许更精确的路径,这些路径更紧密地遵循建筑物和障碍物的轮廓。然而,它会指数级地增加图中的节点数量,导致内存消耗和路径规划计算时间显著增加 17 中关于“网格大小”影响渲染和计算时间的说明。因此,插件必须提供一个用户可配置的网格分辨率参数。这允许用户平衡路径精度(例如,对于狭窄的走廊)与可用的计算资源。对于典型的物流场景,通常会找到一个平衡点,其中单元格大小是物体最小尺寸或最窄可通行路径的一小部分。
如果选择的网格单元格大小相对于模型的特征过大薄障碍物例如小管道、栏杆甚至薄墙可能会被网格离散化完全忽略或者狭窄的通道可能被错误地标记为不可通行。网格生成逻辑需要智能地处理如何将ModelItem包围盒映射到网格单元格。即使障碍物的包围盒小于单个网格单元格如果它有效地阻挡了通行该单元格或相邻单元格也应标记为障碍物。这可能涉及一个“膨胀”或“扩张”步骤其中障碍物稍微扩大以确保它们被网格捕获或者确保网格分辨率始终小于最小的关键特征。
@ -121,7 +121,7 @@ Navisworks API没有直接用于“门”或“通道”识别的API但ModelI
1. **自动识别:** 结合基于属性的过滤例如ModelItem.Name或ModelItem.Type包含“门”、“闸门”、“通道”、“走廊”和包围盒尺寸分析例如墙洞中薄的矩形包围盒来自动识别潜在的门和通道。
2. **默认属性:**
* **门:** 默认情况下识别出的门应被视为“可通行”但其遍历成本应略高于开放空间例如成本为5而开放空间为1。这鼓励路径规划算法使用门但如果存在同样短的开放路径则不鼓励不必要地穿过它们。
* **通道:** 识别出的通道(例如,主要走廊、指定车辆车道应分配比普通开放空间更低的遍历成本例如成本为0.5或0.8。这使得A\*算法偏向于这些指定的路线。
* **通道:** 识别出的通道(例如,主要走廊、指定物体车道应分配比普通开放空间更低的遍历成本例如成本为0.5或0.8。这使得A\*算法偏向于这些指定的路线。
3. **手动覆盖:** 插件的用户界面WPF应允许用户
* 审查自动识别的门/通道。
* 手动将其他ModelItem分类为门、通道或障碍物使用SelectionSet 9
@ -130,22 +130,22 @@ Navisworks API没有直接用于“门”或“通道”识别的API但ModelI
门并非静态障碍物;其可通行性取决于其状态(开/关)。虽然查询暗示默认的“可通行”属性,但更高级的系统会考虑这种动态状态。在网格表示中,门应由具有特定“门”属性的单元格(或一系列单元格)表示。其遍历成本应是条件性的:当“打开”时成本较低,当“关闭”时成本实际上为无限。手动覆盖功能应允许用户更改此状态,在路径规划之前动态更新网格。这通过允许实际操作考虑直接支持了“手动设置补充”要求。
“通道”一词暗示了物流的优选或指定路线。路径规划算法理想情况下应偏好这些路线。通过为被识别为“通道”一部分的网格单元格分配较低的遍历成本A\*算法(它最小化总成本 18将自然地优先选择这些路线。这允许优化不仅限于最短距离还包括物流车辆的“最有效”或“最实用”路径,这可能涉及稍微长一点但更不拥堵或更宽的路线。
“通道”一词暗示了物流的优选或指定路线。路径规划算法理想情况下应偏好这些路线。通过为被识别为“通道”一部分的网格单元格分配较低的遍历成本A\*算法(它最小化总成本 18将自然地优先选择这些路线。这允许优化不仅限于最短距离还包括物流物体的“最有效”或“最实用”路径,这可能涉及稍微长一点但更不拥堵或更宽的路线。
### **提取几何体和包围盒数据用于碰撞代理**
用户明确要求“用包围盒的方式简化碰撞检测”。这意味着虽然原始几何体可用但碰撞的主要数据将是包围盒。WCS转换对于整个模型中一致的空间计算至关重要。
1. **车辆包围盒:** 根据物流车辆例如叉车的实际尺寸定义一个BoundingBox3D。这将是一个常量或用户可配置的参数。
1. **物体包围盒:** 根据物流物体例如叉车的实际尺寸定义一个BoundingBox3D。这将是一个常量或用户可配置的参数。
2. **障碍物包围盒提取:** 对于所有被分类为障碍物的ModelItem使用ModelItem.BoundingBox() 2 检索其
BoundingBox3D。这些包围盒代表静态碰撞几何体。
3. **坐标系一致性:** 确保所有包围盒(车辆、障碍物、楼层范围)都在相同的坐标系(世界坐标系 \- WCS中。ModelItem.BoundingBox()通常返回WCS包围盒。如果通过COM API (GenerateSimplePrimitives()) 提取任何几何图元则其LCS坐标需要使用GetLocalToWorldMatrix() 3 进行显式转换。
3. **坐标系一致性:** 确保所有包围盒(物体、障碍物、楼层范围)都在相同的坐标系(世界坐标系 \- WCS中。ModelItem.BoundingBox()通常返回WCS包围盒。如果通过COM API (GenerateSimplePrimitives()) 提取任何几何图元则其LCS坐标需要使用GetLocalToWorldMatrix() 3 进行显式转换。
用户指令是“减少计算量”。片段显示通过COM API访问原始几何体可能比使用.NET API属性显著慢 1313显示COM几何体提取对于大型模型可能需要更长时间。因此插件应专门使用
ModelItem.BoundingBox()进行碰撞检测,因为它是一个直接的.NET API调用并且为此目的比提取和处理原始几何图元更有效。这与性能优化目标完全一致。
在路径规划过程中,将执行许多碰撞检查(车辆的包围盒与多个静态障碍物之间的检查。每次检查都重复查询或遍历所有障碍物将效率低下。因此在识别所有静态障碍物并提取其包围盒后应将它们存储在优化的空间数据结构中例如简单的2D网格覆盖、四叉树或k-d树以便快速查询特定区域内的潜在碰撞。这种预计算显著降低了路径规划算法执行期间碰撞检查的成本。
在路径规划过程中,将执行许多碰撞检查(物体的包围盒与多个静态障碍物之间的检查。每次检查都重复查询或遍历所有障碍物将效率低下。因此在识别所有静态障碍物并提取其包围盒后应将它们存储在优化的空间数据结构中例如简单的2D网格覆盖、四叉树或k-d树以便快速查询特定区域内的潜在碰撞。这种预计算显著降低了路径规划算法执行期间碰撞检查的成本。
## **IV. 路径规划算法选择与实现**
@ -201,7 +201,7 @@ Math.Abs(a.x \- b.x) \+ Math.Abs(a.y \- b.y)和欧几里得距离DistanceF
* 如果只允许4方向水平/垂直)移动,则**曼哈顿距离**是合适的HCost \= Math.Abs(node.X \- end.X) \+ Math.Abs(node.Y \- end.Y)。
* 启发式必须与GCost实际成本值保持一致的比例 18。
启发式函数的选择直接影响A\*的效率和路径的最优性。对于基于网格的路径规划启发式应反映单元格之间移动的实际成本。如果允许对角线移动且成本均匀例如基数方向为1对角线方向为sqrt(2)),则欧几里得距离是更准确的启发式。如果对角线移动受到惩罚或不允许,则曼哈顿距离更合适。对于物流车辆而言通常允许对角线移动。因此建议使用欧几里得距离作为启发式。启发式还在“破局”tie-breaker中发挥作用 17以确保当多条路径具有相同成本时生成更平滑的路径。
启发式函数的选择直接影响A\*的效率和路径的最优性。对于基于网格的路径规划启发式应反映单元格之间移动的实际成本。如果允许对角线移动且成本均匀例如基数方向为1对角线方向为sqrt(2)),则欧几里得距离是更准确的启发式。如果对角线移动受到惩罚或不允许,则曼哈顿距离更合适。对于物流物体而言通常允许对角线移动。因此建议使用欧几里得距离作为启发式。启发式还在“破局”tie-breaker中发挥作用 17以确保当多条路径具有相同成本时生成更平滑的路径。
#### **成本计算**
@ -218,11 +218,11 @@ Math.Abs(a.x \- b.x) \+ Math.Abs(a.y \- b.y)和欧几里得距离DistanceF
* **方向成本:**
* 基数(水平/垂直)移动:成本 \= BaseCost。
* 对角线移动:成本 \= BaseCost \* Math.Sqrt(2)(如果允许对角线且不“重”)。
* **“惩罚转向”:** 每当车辆从前一个路径段改变其基数方向时增加少量额外成本例如0.1)。这鼓励更直的路径,这对于大型车辆通常更实用。
* **“惩罚转向”:** 每当物体从前一个路径段改变其基数方向时增加少量额外成本例如0.1)。这鼓励更直的路径,这对于大型物体通常更实用。
“惩罚转向”功能 17 直接解决了物流中的一个实际问题:车辆,特别是大型车辆,受益于更平滑、转弯更少的路径。此功能虽然可能增加绝对最短路径的长度,但会产生更“自然”且操作上可行的路线。这种成本调整与物流路径规划高度相关,因为它允许算法生成的路径不仅在几何上最短,而且在实践中对车辆操作而言是高效和安全的。
“惩罚转向”功能 17 直接解决了物流中的一个实际问题:物体,特别是大型物体,受益于更平滑、转弯更少的路径。此功能虽然可能增加绝对最短路径的长度,但会产生更“自然”且操作上可行的路线。这种成本调整与物流路径规划高度相关,因为它允许算法生成的路径不仅在几何上最短,而且在实践中对物体操作而言是高效和安全的。
除了静态成本,更高级的系统可以根据实时交通(如果集成)、临时障碍物,甚至通道宽度(例如,较窄的通道会产生更高的成本)等因素动态调整成本。这允许“智能路由”,根据不断变化的条件或根据车辆类型(例如,大型车辆可能更喜欢更宽、成本更低的通道)优先选择路径。这需要持续更新网格的成本值。
除了静态成本,更高级的系统可以根据实时交通(如果集成)、临时障碍物,甚至通道宽度(例如,较窄的通道会产生更高的成本)等因素动态调整成本。这允许“智能路由”,根据不断变化的条件或根据物体类型(例如,大型物体可能更喜欢更宽、成本更低的通道)优先选择路径。这需要持续更新网格的成本值。
#### **优先队列优化**
@ -243,19 +243,19 @@ CodeProject文章 17 明确指出,用优先队列和“计算网格”(用
List进行排序将严重影响大型网格的性能。开发人员*必须*使用高效的、基于堆的优先队列。
#### **处理动态障碍物和车辆尺寸**
#### **处理动态障碍物和物体尺寸**
查询明确提到对“物流车辆、通道和障碍物”使用“包围盒的方式简化碰撞检测”。
查询明确提到对“物流物体、通道和障碍物”使用“包围盒的方式简化碰撞检测”。
路径规划算法通常作用于“点”代理。为了确保具有实际尺寸的车辆无碰撞路径,必须调整环境(障碍物)以考虑车辆的尺寸。
路径规划算法通常作用于“点”代理。为了确保具有实际尺寸的物体无碰撞路径,必须调整环境(障碍物)以考虑物体的尺寸。
实现细节:
* **障碍物膨胀Minkowski Sum** 在运行A\*算法之前,将网格上的所有静态障碍物膨胀车辆最大尺寸的一半(例如,其宽度或对角线的一半)。这意味着如果一个网格单元格被障碍物占据,或者在障碍物包围盒的某个半径内,它也应被标记为障碍物。这有效地将非点车辆的路径规划问题转换为在“膨胀”地图上为点进行路径规划。
* **车辆包围盒用于路径验证:** 虽然网格是膨胀的,但车辆的实际包围盒将在碰撞检测阶段用于根据障碍物的原始、未膨胀几何体验证生成的路径,确保不会因网格近似而产生误报或漏报。
* **障碍物膨胀Minkowski Sum** 在运行A\*算法之前,将网格上的所有静态障碍物膨胀物体最大尺寸的一半(例如,其宽度或对角线的一半)。这意味着如果一个网格单元格被障碍物占据,或者在障碍物包围盒的某个半径内,它也应被标记为障碍物。这有效地将非点物体的路径规划问题转换为在“膨胀”地图上为点进行路径规划。
* **物体包围盒用于路径验证:** 虽然网格是膨胀的,但物体的实际包围盒将在碰撞检测阶段用于根据障碍物的原始、未膨胀几何体验证生成的路径,确保不会因网格近似而产生误报或漏报。
通过在A\*路径规划算法运行之前,将障碍物膨胀以考虑车辆尺寸,而不是在算法的每一步都执行复杂的车辆与障碍物包围盒交叉检查可以简化运行时碰撞逻辑使其仅需检查网格单元格是否可通行。这种“Minkowski和”方法显著降低了A\*搜索期间的计算负担,直接支持了“减少计算量”的目标。路径规划算法随后只需考虑一个点是否可以通过膨胀的网格移动。
通过在A\*路径规划算法运行之前,将障碍物膨胀以考虑物体尺寸,而不是在算法的每一步都执行复杂的物体与障碍物包围盒交叉检查可以简化运行时碰撞逻辑使其仅需检查网格单元格是否可通行。这种“Minkowski和”方法显著降低了A\*搜索期间的计算负担,直接支持了“减少计算量”的目标。路径规划算法随后只需考虑一个点是否可以通过膨胀的网格移动。
虽然包围盒膨胀处理了车辆的整体占地面积,但它并未固有地考虑车辆的*方向*或*最小转弯半径*。在膨胀网格上找到的路径可能在几何上是清晰的,但由于急转弯而无法由实际车辆执行。对于需要更高保真度(例如,非常狭窄、蜿蜒的路径)的场景,这种简化方法可能需要增强。这可能涉及为急转弯增加额外的成本惩罚(如“惩罚转向” 17或者在高级情况下使用包含运动学约束的更复杂路径规划算法。对于当前的2.5D简化目标,包围盒膨胀是一种实用且高效的解决方案。
虽然包围盒膨胀处理了物体的整体占地面积,但它并未固有地考虑物体的*方向*或*最小转弯半径*。在膨胀网格上找到的路径可能在几何上是清晰的,但由于急转弯而无法由实际物体执行。对于需要更高保真度(例如,非常狭窄、蜿蜒的路径)的场景,这种简化方法可能需要增强。这可能涉及为急转弯增加额外的成本惩罚(如“惩罚转向” 17或者在高级情况下使用包含运动学约束的更复杂路径规划算法。对于当前的2.5D简化目标,包围盒膨胀是一种实用且高效的解决方案。
## **V. 碰撞检测策略**
@ -272,14 +272,14 @@ BoundingBoxIntersectsFilter 6 可用于过滤。
这种方法直接满足了用户对“包围盒的方式简化碰撞检测”以“减少计算量”的要求。它是一种计算开销较小的方法,适用于路径规划过程中频繁的检查。
实现细节:
1. **车辆包围盒:** 为每个路径段检查创建一个代表车辆尺寸的BoundingBox3D。其位置将更新以匹配计算路径上的当前点。
1. **物体包围盒:** 为每个路径段检查创建一个代表物体尺寸的BoundingBox3D。其位置将更新以匹配计算路径上的当前点。
2. **障碍物包围盒缓存:** 在“模型数据准备”阶段第三节所有静态障碍物墙壁、柱子、固定设备的BoundingBox3D将使用ModelItem.BoundingBox()提取并存储在可访问的集合或空间索引中。
3. **交集测试:** 对于路径的每个建议步骤(或段),将检查车辆当前的BoundingBox3D是否与所有相关静态障碍物的包围盒发生交集使用BoundingBox3D.Intersects()。如果检测到交集,则该路径段(或整个路径,取决于验证阶段)被视为无效。
4. **2.5D简化:** 由于规划是2.5D的包围盒的Z轴尺寸高度可以简化或忽略主要关注X-Y平面交集。然而确保车辆高度能通过上方障碍物例如低梁、风管可能仍需要Z轴检查。
3. **交集测试:** 对于路径的每个建议步骤(或段),将检查物体当前的BoundingBox3D是否与所有相关静态障碍物的包围盒发生交集使用BoundingBox3D.Intersects()。如果检测到交集,则该路径段(或整个路径,取决于验证阶段)被视为无效。
4. **2.5D简化:** 由于规划是2.5D的包围盒的Z轴尺寸高度可以简化或忽略主要关注X-Y平面交集。然而确保物体高度能通过上方障碍物例如低梁、风管可能仍需要Z轴检查。
BoundingBox3D.Intersects()方法 24 是Navisworks核心API的一部分。这种方法很可能是在优化的原生代码中实现的因此比任何涉及迭代几何图元的C\#自定义交集逻辑要快得多如COM API几何体提取的性能差异所示 13。利用这个原生API方法是执行所需碰撞检查最有效的方式直接符合“减少计算量”的目标。它避免了复杂几何计算的开销。
尽管高效,但包围盒碰撞检测是一种近似方法。车辆的轴对齐包围盒可能与障碍物的包围盒相交,即使它们的实际几何体并未碰撞(例如,车辆穿过U形柱包围盒内的“空隙”。反之对于复杂的、非轴对齐的几何体包围盒可能无法完全包围对象导致遗漏碰撞。本报告应明确承认这种权衡。对于2.5D简化场景和减少计算量的目标,这种近似通常是可接受的。然而,对于要求绝对精度的应用,可能需要更复杂的基于几何体的交集测试 25但由于其高计算成本这些明确超出了当前项目的范围。
尽管高效,但包围盒碰撞检测是一种近似方法。物体的轴对齐包围盒可能与障碍物的包围盒相交,即使它们的实际几何体并未碰撞(例如,物体穿过U形柱包围盒内的“空隙”。反之对于复杂的、非轴对齐的几何体包围盒可能无法完全包围对象导致遗漏碰撞。本报告应明确承认这种权衡。对于2.5D简化场景和减少计算量的目标,这种近似通常是可接受的。然而,对于要求绝对精度的应用,可能需要更复杂的基于几何体的交集测试 25但由于其高计算成本这些明确超出了当前项目的范围。
### **利用Navisworks碰撞检测API进行预计算或验证可选高级用途**
@ -295,9 +295,9 @@ TestsRunTest 12并检索
ClashResult 11。
碰撞检测工具是Navisworks的原生、强大的干扰识别工具。尽管功能强大但其设计主要用于静态碰撞分析和报告不适用于路径规划算法所需的实时、迭代检查。运行ClashTest是一个相对繁重的操作涉及复杂的几何计算13暗示了这一点显示碰撞的几何体提取可能很慢。在A\*路径规划循环中,对每个潜在的车辆位置重复运行碰撞测试将导致计算量过大,并直接违背“减少计算量”的要求。
碰撞检测工具是Navisworks的原生、强大的干扰识别工具。尽管功能强大但其设计主要用于静态碰撞分析和报告不适用于路径规划算法所需的实时、迭代检查。运行ClashTest是一个相对繁重的操作涉及复杂的几何计算13暗示了这一点显示碰撞的几何体提取可能很慢。在A\*路径规划循环中,对每个潜在的物体位置重复运行碰撞测试将导致计算量过大,并直接违背“减少计算量”的要求。
因此碰撞检测API不应用于A\*路径规划过程中的实时碰撞检查。相反其效用在于预计算或后验证。如果ClashTest作为预处理步骤*一次性*运行,它可以识别车辆包围盒甚至其实际几何体如果配置了更精确的碰撞测试将与静态建筑元素永久碰撞的区域。此类预计算碰撞测试的结果可用于优化路径规划网格。例如如果ClashResult指示特定区域存在干扰则网格上的该区域或更大的周围区域可以被标记为不可通行的“禁行区”即使初始包围盒分析未将其标记为主要障碍物。这使得能够利用Navisworks强大的碰撞检测功能来创建更准确的初始网格地图。或者碰撞检测工具可以用作生成路径的*最终验证步骤*,对车辆(沿其整个路径)与所有障碍物运行一次碰撞测试,以捕获简化包围盒检查遗漏的任何细微干扰。
因此碰撞检测API不应用于A\*路径规划过程中的实时碰撞检查。相反其效用在于预计算或后验证。如果ClashTest作为预处理步骤*一次性*运行,它可以识别物体包围盒甚至其实际几何体如果配置了更精确的碰撞测试将与静态建筑元素永久碰撞的区域。此类预计算碰撞测试的结果可用于优化路径规划网格。例如如果ClashResult指示特定区域存在干扰则网格上的该区域或更大的周围区域可以被标记为不可通行的“禁行区”即使初始包围盒分析未将其标记为主要障碍物。这使得能够利用Navisworks强大的碰撞检测功能来创建更准确的初始网格地图。或者碰撞检测工具可以用作生成路径的*最终验证步骤*,对物体(沿其整个路径)与所有障碍物运行一次碰撞测试,以捕获简化包围盒检查遗漏的任何细微干扰。
## **VI. 路径可视化与导航地图输出**
@ -316,7 +316,7 @@ Application.ActiveDocument.ActiveView.RequestDelayedRedraw() 3 用于触发视
1. **自定义ToolPlugin或RenderPlugin** 路径可视化逻辑将驻留在自定义ToolPlugin或RenderPlugin的重写OverlayRenderModel方法中。
2. **绘制路径段:** 遍历计算出的Point3D坐标列表即路径。使用graphics.Line()绘制连接连续点的线,形成路径。可以使用不同的颜色或线型来区分路径。
3. **车辆表示:** 可选地可以在起点、终点或沿路径绘制一个graphics.Cuboid() 3 来表示车辆的包围盒,清晰地显示其占地面积。
3. **物体表示:** 可选地可以在起点、终点或沿路径绘制一个graphics.Cuboid() 3 来表示物体的包围盒,清晰地显示其占地面积。
4. **动态更新:** 路径计算后应调用RequestDelayedRedraw()以更新视口。对于交互式元素(例如,手动门状态更改),应重新计算并重新绘制路径。
对于非常长的路径或频繁的路径重新计算在OverlayRenderModel中持续重绘每个段可能会影响Navisworks的响应能力。RequestDelayedRedraw 3 意味着重绘是受管理的。可视化优化策略可能包括:
@ -351,12 +351,12 @@ System.Text.Json库在性能方面表现出色其Serialize()方法在基准
实现细节:
* **起点和终点:** 明确包含规划路径的起始和结束坐标。
* **总距离:** 计算并包含路径的总长度,这对于物流车辆的里程估算至关重要。
* **估计旅行时间:** 根据路径长度和预设的车辆平均速度(或可配置的速度)计算估计的旅行时间。
* **总距离:** 计算并包含路径的总长度,这对于物流物体的里程估算至关重要。
* **估计旅行时间:** 根据路径长度和预设的物体平均速度(或可配置的速度)计算估计的旅行时间。
* **障碍物和通道信息:** 可选地可以包含沿路径遇到的主要障碍物或通过的通道的简化信息例如它们的ID或类型以便外部系统进行更丰富的上下文理解。
* **其他相关属性:** 根据外部系统的具体需求可以添加其他属性如路径ID、规划日期等。
这些附加信息使得导出的JSON文件不仅仅是简单的坐标列表而是一个功能齐全的导航地图能够直接集成到物流管理系统、车辆调度系统或数字孪生平台中,从而实现更高级的分析和决策。
这些附加信息使得导出的JSON文件不仅仅是简单的坐标列表而是一个功能齐全的导航地图能够直接集成到物流管理系统、物体调度系统或数字孪生平台中,从而实现更高级的分析和决策。
## **VII. 插件架构与用户界面考虑**
@ -385,15 +385,15 @@ Execute方法是插件的入口点当用户从Navisworks界面调用插件时
典型的用户交互流程将包括:
1. **插件启动:** 用户从Navisworks界面启动插件。
2. **起点/终点选择:** 用户通过在Navisworks视口中点击或通过输入坐标来指定物流车辆的起点和终点。插件将捕获这些点。
3. **参数配置:** 用户可以在插件UI中配置路径规划参数例如车辆尺寸、网格分辨率、门和通道的成本设置、以及手动补充的障碍物或通道SelectionSet。
2. **起点/终点选择:** 用户通过在Navisworks视口中点击或通过输入坐标来指定物流物体的起点和终点。插件将捕获这些点。
3. **参数配置:** 用户可以在插件UI中配置路径规划参数例如物体尺寸、网格分辨率、门和通道的成本设置、以及手动补充的障碍物或通道SelectionSet。
4. **路径计算:** 用户触发路径计算。插件执行模型数据准备、网格构建和A\*路径规划算法。
5. **路径可视化:** 计算出的路径自动在Navisworks视口中高亮显示供用户审查。
6. **导航地图导出:** 用户可以选择将计算出的路径及其元数据导出为JSON文件。
## **VIII. 结论与建议**
本报告详细阐述了在Navisworks 2026中开发C\#物流路径规划插件的全面方案。通过结合智能模型数据提取、高效的A\*路径规划算法、简化的包围盒碰撞检测以及直观的WPF用户界面该插件能够解决BIM模型中物流属性缺失的痛点实现大型建筑内部物流车辆的自动化、无碰撞导航。
本报告详细阐述了在Navisworks 2026中开发C\#物流路径规划插件的全面方案。通过结合智能模型数据提取、高效的A\*路径规划算法、简化的包围盒碰撞检测以及直观的WPF用户界面该插件能够解决BIM模型中物流属性缺失的痛点实现大型建筑内部物流物体的自动化、无碰撞导航。
**核心结论包括:**
@ -405,7 +405,7 @@ Execute方法是插件的入口点当用户从Navisworks界面调用插件时
**本插件的价值体现在:**
* **自动化路径规划:** 减少了人工规划的时间和错误,提高了效率。
* **无碰撞保障:** 通过精确的碰撞检测,确保物流车辆安全运行,降低了事故风险。
* **无碰撞保障:** 通过精确的碰撞检测,确保物流物体安全运行,降低了事故风险。
* **计算效率优化:** 采用轻量级算法和预处理技术,确保在大型复杂模型中也能实现流畅的用户体验。
* **可操作的导航地图:** 导出的JSON数据可无缝集成到其他物流或导航系统中实现更高级的自动化和分析。
@ -413,8 +413,8 @@ Execute方法是插件的入口点当用户从Navisworks界面调用插件时
为进一步增强插件的功能和性能,建议考虑以下方向:
* **分层路径规划HPA\*** 对于极其庞大和复杂的建筑模型可以研究并实现HPA\*算法,以在更高层次上进行路径规划,从而实现显著的性能提升。
* **动态障碍物处理:** 引入对临时障碍物(例如,临时施工区域、停放的车辆)的动态识别和路径调整能力,以适应实时变化的建筑环境。
* **车辆运动学约束:** 在路径规划中融入更复杂的车辆运动学模型,例如最小转弯半径和速度限制,以生成更符合实际车辆行驶特性的路径。
* **动态障碍物处理:** 引入对临时障碍物(例如,临时施工区域、停放的物体)的动态识别和路径调整能力,以适应实时变化的建筑环境。
* **物体运动学约束:** 在路径规划中融入更复杂的物体运动学模型,例如最小转弯半径和速度限制,以生成更符合实际物体行驶特性的路径。
* **多楼层路径规划:** 扩展插件功能,支持跨楼层(例如,通过电梯或坡道)的路径规划,以满足更复杂的物流场景需求。
* **与外部系统集成:** 探索与实时定位系统RTLS或仓库管理系统WMS的更深层次集成以实现路径规划的自动化触发和反馈循环。

View File

@ -73,7 +73,7 @@
- CollisionClashDetectiveIntegration碰撞集成动画期缓存 + 结束时统一测试)
- ModelSplitterManager模型切分含简化实现
- PathPlanning 算法层:
- GridMapGenerator网格生成ChannelBased 2.5D/BoundingBox车辆膨胀优化
- GridMapGenerator网格生成ChannelBased 2.5D/BoundingBox物体膨胀优化
- AutoPathFinderA* 路径搜索、路径优化/插值、高度贴合
- ChannelBasedGridBuilder、VerticalScanProcessor、TriangleSpatialHash 等支撑
- Utils日志、坐标/单位转换、Navisworks API 辅助
@ -96,7 +96,7 @@
- 语言/框架C#.NET Framework 4.8
- UIWPF + MVVMWinForms 仅用于部分对话框/遗留界面);
- 外部 APIAutodesk Navisworks API2017 目标COM API属性写入、TimeLiner API、Clash API
- 路径算法RoyT.AStar格网 A*),自研 2.5D 高度约束与车辆膨胀优化;
- 路径算法RoyT.AStar格网 A*),自研 2.5D 高度约束与物体膨胀优化;
- 序列化XML、JSON简化加载/CSV结果
- 日志/异常LogManager、GlobalExceptionHandler。
@ -135,7 +135,7 @@
- 路径点自动贴合:高度修正在 AutoPathFinder.ApplyPreciseHeightCorrection 中贴合通道表面(有通道数据时);
- 自动规划(复杂环境):
- 网格生成GridMapGenerator.GenerateFromBIM
- First 尝试 ChannelBased2_5D通道优先、垂直扫描高度区间、车辆高度校验);
- First 尝试 ChannelBased2_5D通道优先、垂直扫描高度区间、物体高度校验);
- Fallback 到 BoundingBox 模式(保持向后兼容);
- A*AutoPathFinder.FindPath/ExecuteAStarAlgorithm
- 路径优化去冗余点、直线可达校验、2.5D 高度插值与起终点原位修正;
@ -149,7 +149,7 @@
3.5 物流“类别”设置
- 类别:为模型添加“物流属性”类别(类型、可通行、优先级、车辆尺寸、速度限制、宽度限制等);
- 类别:为模型添加“物流属性”类别(类型、可通行、优先级、物体尺寸、速度限制、宽度限制等);
- 批量处理Add/Update/Remove使用 COM API 正确索引方式避免覆盖错误;
- 识别筛选FilterTraversableItems/FilterByLogisticsType/FilterPassableAreas供通道选择与可见性使用。
@ -220,7 +220,7 @@
- 日志输出:%AppData%\Autodesk Navisworks Manage 2017\PluginsData\NavisworksTransportPlugin按 QWEN 中 2026 路径作适配);
- 配置切换:
- 自动路径规划模式网格参数cellSize、vehicleSize、safetyMargin、vehicleHeight
- 自动路径规划模式网格参数cellSize、objectSize、safetyMargin、objectHeight
- 2.5D/BoundingBox 模式选择(自动/强制选项)。
4.5 验证计划
@ -237,7 +237,7 @@
- 导航输出:视频/图片占位与结构化结果XML/CSV
- 性能验证:
- 大模型下网格生成耗时ChannelBased 2.5D 与 BoundingBox 比对);
- 车辆膨胀优化(距离变换算法)前后对比;
- 物体膨胀优化(距离变换算法)前后对比;
- PathPoint 渲染与状态事件对 UI 延迟的影响;
- 兼容性验证:
- Navisworks NWD/NWF/NWC 模型;
@ -263,7 +263,7 @@
- 网格生成:
- ChannelBased 2.5D 优先(垂直扫描、空间哈希、通道优先),失败回退 BoundingBox
- 车辆膨胀:距离变换 O(n*m) 优化,跳过无障碍场景;
- 物体膨胀:距离变换 O(n*m) 优化,跳过无障碍场景;
- UI
- UIStateManager/QueueUIUpdate 降低 UI 阻塞;
- WPF 绑定优化与虚拟化集合;

View File

@ -323,7 +323,7 @@
3) 文件导入:选择 XML/JSON/CSV 文件,解析字段,按通道贴合重绘路径;
4) 文件导出:支持“导出全部/导出选中路径”,命名规范与状态提示;
5) 历史记录:对导入/导出、增删改动作记录摘要(时间、人员、文件名、结果);
- DELMIA 对接:依据目标字段映射生成 CSV/JSON路径点序列、坐标、类型、时间标签、车辆/安全参数摘要)。
- DELMIA 对接:依据目标字段映射生成 CSV/JSON路径点序列、坐标、类型、时间标签、物体/安全参数摘要)。
3.3.3.2.1.3. 输入数据(详细规格)
@ -338,7 +338,7 @@
- XMLUTF-8无BOMXSD可选校验根元素
- JSONapplication/jsonUTF-8与示例键名一致
- CSV分隔符=逗号,首行表头=index,name,type,x,y,z,timeTag小数用点
- meta参数可选vehicle.length/width/height:double(m)safetyMargin:double(m)gridSize:double(m)
- meta参数可选object.length/width/height:double(m)safetyMargin:double(m)gridSize:double(m)
- 通道集合(可选但建议):类型=ModelItem[];用于导入后点位贴合校正;
3.3.3.2.1.4. 输出数据(详细规格)
@ -603,14 +603,14 @@
3.3.3.5.1.2. 实现方案
- 起终点选择:在“自动路径规划”区点击“选择起点/选择终点”在3D视图点选回显到只读文本框
- 自动路径:综合车辆尺寸(长/宽/高)与安全间隙,结合网格精度进行路径求解;
- 自动路径:综合物体尺寸(长/宽/高)与安全间隙,结合网格精度进行路径求解;
- 高级设置可启用“手动设置网格大小”滑块范围0.15.0米;
- 引导配合“3D交互操作指南”完成选择/参数/提交全流程。
3.3.3.5.1.3. 输入数据(详细规格)
- 起点/终点必需RaycastHit/坐标{ x,y,z:double(m) }
- 车辆参数可选length/width/height:double(m);范围>0
- 物体参数可选length/width/height:double(m);范围>0
- 安全间隙可选double(m)默认0.20.5
- 网格精度可选double(m)0.15.0;启用“手动设置网格大小”时必填;
@ -624,7 +624,7 @@
- 选择起点/终点Button + 只读TextBox触发3D拾取并回显坐标/对象信息。
- 自动规划路径Button按参数求解路径失败给出原因如无连通通道
- 重置参数Button清空起终点与车辆/网格参数。
- 重置参数Button清空起终点与物体/网格参数。
- 高级设置Expander + Slider/Text控制网格精度显示建议区间。
- 3D交互操作指南说明文本提供从选择到提交的分步提示。
- 截图占位:
@ -675,7 +675,7 @@
- 字段规范:
- 基本routeName(text)、createdAt(datetime)、version(text)
- points[]index(int)、name(text)、type(enum)、x/y/z(double, m)、orientation(y/p/r, 可选)、timeTag(double, s, 可选)
- metavehicle.length/width/height(double, m)、safetyMargin(double, m)、gridSize(double, m)、categorySummary(text)
- metaobject.length/width/height(double, m)、safetyMargin(double, m)、gridSize(double, m)、categorySummary(text)
- 模板与字典:统一键名与单位说明,附样例文件以便对接。
3.3.3.5.3.3. 输入数据(详细规格)

View File

@ -12,7 +12,7 @@
</Obstacle>
<Obstacle id="obs-2" name="Storage Area"> <BoundingBox> <Min x="60.0" y="0.0" z="-20.0"/> <Max x="100.0" y="10.0" z="20.0"/> </BoundingBox> </Obstacle>
</StaticElements> <!-- 定义需要规划路径的移动对象 -->
<MovingObjects> <Object id="obj-1" name="Delivery Truck #1" type="Vehicle"> <Geometry>
<MovingObjects> <Object id="obj-1" name="Delivery Truck #1" type="Object"> <Geometry>
<!-- 可以是简单的包围盒或更复杂的引用 --> <BoundingBox> <Min x="-2.5" y="0.0" z="-1.0"/> <Max x="2.5" y="3.0" z="1.0"/> </BoundingBox> </Geometry>
<Trajectory id="traj-1" name="Route to Loading Bay">
<Waypoints>

View File

@ -22,7 +22,7 @@
<xs:complexType name="ObjectType"> <xs:sequence>
<xs:element name="Geometry" type="pp:GeometryType"/> <xs:element name="Trajectory" type="pp:TrajectoryType" maxOccurs="unbounded"/>
</xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="name" type="xs:string" use="required"/> <xs:attribute name="type" type="xs:string" use="required"/> <!-- e.g., Vehicle, Crane, Robot -->
</xs:sequence> <xs:attribute name="id" type="xs:ID" use="required"/> <xs:attribute name="name" type="xs:string" use="required"/> <xs:attribute name="type" type="xs:string" use="required"/> <!-- e.g., Object, Crane, Robot -->
</xs:complexType> <xs:complexType name="GeometryType">
<xs:sequence> <xs:element name="BoundingBox" type="pp:BoundingBoxType"/> <!-- 可以扩展以包含更复杂的几何引用或数据 -->
</xs:sequence>

View File

@ -34,7 +34,7 @@
### 2. 设置动画
1. 在控制面板中选择要动画的车辆对象
1. 在控制面板中选择要动画的物体对象
2. 选择预定义的路径或创建新路径
3. 设置动画参数(时长、速度等)

View File

@ -738,15 +738,15 @@ public static class StructuredLogger
var stopwatch = Stopwatch.StartNew();
try
{
var result = AutoPlanPath(start, end, vehicleSize);
var result = AutoPlanPath(start, end, objectSize);
StructuredLogger.LogOperation("AutoPlanPath",
new { start, end, vehicleSize },
new { start, end, objectSize },
stopwatch.Elapsed, true);
}
catch (Exception ex)
{
StructuredLogger.LogOperation("AutoPlanPath",
new { start, end, vehicleSize },
new { start, end, objectSize },
stopwatch.Elapsed, false, ex.Message);
}
```
@ -883,7 +883,7 @@ public class PerformanceMonitor : IDisposable
// 使用方式
using (new PerformanceMonitor("自动路径规划"))
{
var result = AutoPlanPath(start, end, vehicleSize);
var result = AutoPlanPath(start, end, objectSize);
}
```
@ -1159,12 +1159,12 @@ private Point2D? FindNearestWalkablePosition(GridMap gridMap, Point2D originalPo
#### 12.3 渐进式错误处理
```csharp
public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint,
double vehicleSize = 1.0, double safetyMargin = 0.5)
double objectSize = 1.0, double safetyMargin = 0.5)
{
try
{
// 第一次尝试:使用原始参数
return PlanPathInternal(startPoint, endPoint, vehicleSize, safetyMargin);
return PlanPathInternal(startPoint, endPoint, objectSize, safetyMargin);
}
catch (AutoPathPlanningException ex) when (ex.Message.Contains("位于障碍物上"))
{
@ -1173,7 +1173,7 @@ public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint,
try
{
// 第二次尝试:启用智能修正
return PlanPathWithCorrection(startPoint, endPoint, vehicleSize, safetyMargin);
return PlanPathWithCorrection(startPoint, endPoint, objectSize, safetyMargin);
}
catch (Exception innerEx)
{
@ -1181,9 +1181,9 @@ public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint,
// 第三次尝试:降低精度重试
var reducedMargin = safetyMargin * 0.5;
var reducedSize = vehicleSize * 0.8;
var reducedSize = objectSize * 0.8;
LogManager.Info($"降低参数重试: 车辆尺寸{reducedSize:F1}m, 安全边距{reducedMargin:F1}m");
LogManager.Info($"降低参数重试: 物体尺寸{reducedSize:F1}m, 安全边距{reducedMargin:F1}m");
return PlanPathInternal(startPoint, endPoint, reducedSize, reducedMargin);
}
}

View File

@ -54,7 +54,7 @@
### [2026/1/18]
1. [x] 功能实现吊装路径支持3步吊运过程
2. [x] (功能)给移动到起点的虚拟车辆或物体增加调整角度能力
2. [x] (功能)给移动到起点的虚拟物体或物体增加调整角度能力
3. [x] (优化)在自动路径规划的几何体获取过程中,过滤隐藏项,提高性能
4. [x] (功能)在碰撞检测报告中,增加碰撞结果截图
5. [x] (功能)实现多条路径碰撞检测的批处理
@ -66,7 +66,7 @@
### [2026/1/6]
1. [x] BUG虚拟车辆模型每次点击都重建
1. [x] BUG虚拟物体模型每次点击都重建
2. [x] BUG碰撞结果高亮应该显示clashdetective检测的结果
3. [x] 优化对clashdetective的检测结果进行向上合并找到集合对象。
4. [x] (功能)碰撞检测结果保存数据库,列表展示
@ -80,7 +80,7 @@
5. [ ] 测试路径规划文件能导入DELMIA
6. [ ] (优化)优化路径规划分析和分析报告
7. [ ] (功能)增加物流属性自定义
8. [x] BUG 动画时物流车在起点时应该朝向路径方向,切换虚拟车辆和指定物体时,原有的要归位
8. [x] BUG 动画时物流车在起点时应该朝向路径方向,切换虚拟物体和指定物体时,原有的要归位
9. [x] (功能)物流车在路径点转弯时,设置转弯半径等参数,将路径变成曲线
### [2025/12/18]
@ -96,7 +96,7 @@
1. [x] (功能)碰撞检测时,增加手工指定被检测构件,用特殊颜色标识
2. [x] (功能)动画时,物流模型朝向随路径变化
3. [x] (功能)动画生成,增加使用模拟物流车辆立方体选项
3. [x] (功能)动画生成,增加使用模拟物流物体立方体选项
4. [x] (功能)动画检测时,过滤门和其他可通行构件
5. [x] BUG只有手动路径时导出路径按钮没激活
6. [x] BUG重复打开模型有时程序崩溃需要先关闭物流插件窗口
@ -235,7 +235,7 @@
### [2025/08/28]
1. [x](优化)将“自动规划路径”中的车辆长度. 宽度的默认值改为1米安全间隙改为0.25米。高级设置中,网格的大小
1. [x](优化)将“自动规划路径”中的物体长度. 宽度的默认值改为1米安全间隙改为0.25米。高级设置中,网格的大小
默认值改为0.5米。
2. [x](功能)将这些参数,作为插件配置文件的参数,在系统管理的插件管理中,统一管理
3. [x]优化修改分层预览的业务逻辑从一级节点开始遍历每个节点查找指定的分层属性如果找到记录下来作为一个分层不再遍历其下级节点如果没找到继续遍历其子节点直到找到为止遍历深度受到用户指定的遍历深度限制。特殊处理1. 对于智能检测使用一组候选的分层属性对每个节点进行查找按属性的评分优先级确定选择何种属性。2. 对于自定义查找,用指定的分层属性,在每个节点的"分层信息“属性类别中查找。

View File

@ -26,7 +26,7 @@
- **目标**:在 TimeLiner 中展示运输任务
- **功能**
- 创建运输任务到 TimeLiner
- 显示任务基本信息(名称、时间、关联车辆
- 显示任务基本信息(名称、时间、关联物体
- 任务状态同步显示
- 任务层次结构管理
@ -56,7 +56,7 @@ public class TimeLinerIntegrationManager
// 任务管理
public TimelinerTask CreateTransportTask(string taskName, List<Point3D> pathPoints,
TimeSpan duration, ModelItem vehicle);
TimeSpan duration, ModelItem object);
public void UpdateTaskProgress(string taskId, double progress);
public void RemoveTransportTask(string taskId);
@ -71,7 +71,7 @@ public class TimeLinerIntegrationManager
#### 1. TimeLiner 任务创建
```csharp
public TimelinerTask CreateTransportTask(string taskName, List<Point3D> pathPoints,
TimeSpan duration, ModelItem vehicle)
TimeSpan duration, ModelItem object)
{
var task = new TimelinerTask();
task.DisplayName = $"运输任务:{taskName}";
@ -81,7 +81,7 @@ public TimelinerTask CreateTransportTask(string taskName, List<Point3D> pathPoin
// 关联模型元素
var selection = new Selection();
selection.Add(vehicle);
selection.Add(object);
task.Selection.CopyFrom(selection);
// 添加到文档
@ -124,17 +124,17 @@ public class TimeLinerIntegratedAnimationManager : PathAnimationManager
{
private TimeLinerIntegrationManager _timeLinerManager;
public override void StartAnimation(ModelItem vehicle, List<Point3D> pathPoints, TimeSpan duration)
public override void StartAnimation(ModelItem object, List<Point3D> pathPoints, TimeSpan duration)
{
// 创建 TimeLiner 任务
var task = _timeLinerManager.CreateTransportTask(
$"运输路径_{DateTime.Now:HHmmss}",
pathPoints,
duration,
vehicle);
object);
// 启动自制动画系统
base.StartAnimation(vehicle, pathPoints, duration);
base.StartAnimation(object, pathPoints, duration);
// 同步初始状态
_timeLinerManager.SyncAnimationToTimeLiner(AnimationState.Playing, 0.0);

View File

@ -45,7 +45,7 @@
- 元素类型:选择新的物流类型
- 可通行:设置是否可通行
- 优先级设置1-10的优先级值
- 车辆尺寸:选择适用的车辆尺寸
- 物体尺寸:选择适用的物体尺寸
- 速度限制:设置速度限制值
- 点击"确定"保存修改
@ -100,7 +100,7 @@
- **类型**8种预定义的物流元素类型
- **可通行**:布尔值,表示是否允许通行
- **优先级**1-10的数值用于路径规划
- **车辆尺寸**:适用的车辆尺寸限制
- **物体尺寸**:适用的物体尺寸限制
- **速度限制**通行速度限制km/h
### 可视化支持

View File

@ -5,7 +5,7 @@
# 任务描述
用户要求开发Navisworks插件的动画创建功能具体需求是
- 插件利用Navisworks的Animator功能为代表"运输车辆"的模型创建对象动画
- 插件利用Navisworks的Animator功能为代表"运输物体"的模型创建对象动画
- 路径使用已开发的3D路径选择功能基于通道模型上选择的起点、终点和路径点
- 首先研究能否使用API沿着路径创建动画如果不能则研究通过TimeLiner模拟的每个时间步长动态更新模型位置来模拟动画效果

View File

@ -18,11 +18,11 @@
- **扩展性差**: 难以支持复杂的动画场景和高级功能
### 1.2 用户需求重新梳理
- **核心需求**: 为代表"运输车辆"的模型创建沿路径的动画
- **核心需求**: 为代表"运输物体"的模型创建沿路径的动画
- **碰撞需求**: 实时检测动画过程中的碰撞并提供专业分析
- **时间轴需求**: 支持复杂的时间轴控制和关键帧管理
- **可视化需求**: 提供丰富的动画状态和碰撞结果可视化
- **扩展需求**: 支持多车辆、多路径的复杂场景
- **扩展需求**: 支持多物体、多路径的复杂场景
---
@ -51,7 +51,7 @@ public class TransportAnimationController
**核心功能**:
- 动画场景创建和管理
- 多车辆动画协调
- 多物体动画协调
- 动画状态统一管理
- 异常处理和恢复
@ -112,13 +112,13 @@ public class AnimationStateManager
- UI状态同步
- 状态持久化
#### 2.2.5 VehicleAnimationModel车辆动画模型)
**职责**: 单个车辆的动画数据和行为封装
#### 2.2.5 ObjectAnimationModel物体动画模型)
**职责**: 单个物体的动画数据和行为封装
```csharp
public class VehicleAnimationModel
public class ObjectAnimationModel
{
// 车辆动画数据
- 车辆模型引用
// 物体动画数据
- 物体模型引用
- 路径数据
- 动画参数
- 当前状态
@ -126,7 +126,7 @@ public class VehicleAnimationModel
```
**核心功能**:
- 车辆动画配置
- 物体动画配置
- 路径跟踪计算
- 变换矩阵管理
- 动画参数调整
@ -158,7 +158,7 @@ using Autodesk.Navisworks.Api.Timeliner;
public class NavisTimelineManager
{
private DocumentTimeliner _timelineDocument;
private TimelinerTask _vehicleAnimationTask;
private TimelinerTask _objectAnimationTask;
public void InitializeTimeline()
{
@ -166,13 +166,13 @@ public class NavisTimelineManager
_timelineDocument = (DocumentTimeliner)doc.Timeliner;
// 创建动画任务
_vehicleAnimationTask = _timelineDocument.Tasks.AddNew();
_vehicleAnimationTask.DisplayName = "车辆运输动画";
_vehicleAnimationTask.StartDate = DateTime.Now;
_vehicleAnimationTask.EndDate = DateTime.Now.AddMinutes(1);
_objectAnimationTask = _timelineDocument.Tasks.AddNew();
_objectAnimationTask.DisplayName = "物体运输动画";
_objectAnimationTask.StartDate = DateTime.Now;
_objectAnimationTask.EndDate = DateTime.Now.AddMinutes(1);
}
public void CreateVehicleAnimation(VehicleAnimationModel vehicle)
public void CreateObjectAnimation(ObjectAnimationModel object)
{
// 创建TimeLiner动画序列
// 基于路径点创建关键帧
@ -205,10 +205,10 @@ public class NavisClashManager
_clashDocument.TestsData.Tests.Add(_dynamicClashTest);
}
public List<ClashResult> DetectRealTimeClashes(ModelItem vehicle)
public List<ClashResult> DetectRealTimeClashes(ModelItem object)
{
// 配置碰撞测试选择集
_dynamicClashTest.SelectionA.Selection.Add(vehicle);
_dynamicClashTest.SelectionA.Selection.Add(object);
_dynamicClashTest.SelectionB.Selection.Clear();
_dynamicClashTest.SelectionB.Selection.AddRange(GetObstacleModels());
@ -227,7 +227,7 @@ public class NavisClashManager
### 4.1 动画创建工作流
```
1. 用户选择车辆模型
1. 用户选择物体模型
2. 用户定义运输路径(使用现有路径规划功能)
@ -276,7 +276,7 @@ TimeLiner控制时间轴 → 实时位置计算 → 模型变换更新
**新增功能**:
- **时间轴视图**: 显示完整的时间轴和关键帧
- **碰撞监控面板**: 实时显示碰撞检测结果
- **车辆状态面板**: 显示每个车辆的当前状态
- **物体状态面板**: 显示每个物体的当前状态
- **路径调整器**: 允许实时调整路径参数
### 5.2 碰撞检测界面
@ -398,14 +398,14 @@ TimeLiner控制时间轴 → 实时位置计算 → 模型变换更新
- ✅ 支持基于TimeLiner的专业动画创建
- ✅ 实现基于Clash Detective的准确碰撞检测
- ✅ 提供流畅的动画播放和控制体验
- ✅ 支持多车辆和复杂路径场景
- ✅ 支持多物体和复杂路径场景
- ✅ 提供丰富的可视化和交互功能
### 8.2 性能标准
- ✅ 动画播放帧率 > 15 FPS
- ✅ 碰撞检测响应时间 < 200ms
- ✅ 界面响应时间 < 100ms
- ✅ 支持同时处理 > 5个车辆对象
- ✅ 支持同时处理 > 5个物体对象
### 8.3 质量标准
- ✅ 代码覆盖率 > 80%

View File

@ -2,15 +2,15 @@
## 项目信息
- **创建时间**: 2025-06-21
- **目标**: 实现单路径单车辆的基础动画功能
- **目标**: 实现单路径单物体的基础动画功能
- **范围**: 不包括碰撞检测使用系统TimeLiner窗口
---
## 核心功能需求MVP版本
1. ✅ **用户提示**: UI提示用户在选择树中选择车辆
1. ✅ **用户提示**: UI提示用户在选择树中选择物体
2. ✅ **路径选择**: 提供路径列表供用户选择
3. ✅ **动画生成**: 基于选中车辆和路径生成动画
3. ✅ **动画生成**: 基于选中物体和路径生成动画
4. ✅ **动画播放**: 实现基本的动画播放功能
5. ✅ **TimeLiner集成**: 如果简单则集成,复杂则后续处理
@ -34,26 +34,26 @@
- [ ] 确认TimeLiner API可用性
- [ ] 决定是否使用TimeLiner
#### 任务1.2:简化车辆选择UI
#### 任务1.2:简化物体选择UI
**优先级**: 🔴 高
**预计工时**: 2小时
**描述**: 在现有动画控制面板中添加简单的车辆选择提示
**描述**: 在现有动画控制面板中添加简单的物体选择提示
**具体步骤**:
1. 修改MainPlugin.cs中的CreateAnimationControls方法
2. 添加车辆选择提示标签
3. 添加"获取选中车辆"按钮
4. 添加车辆状态显示
2. 添加物体选择提示标签
3. 添加"获取选中物体"按钮
4. 添加物体状态显示
**简化UI组件**:
- 提示标签:"请在选择树中选择车辆模型"
- "获取选中车辆"按钮
- 车辆状态显示(简单文本)
- 提示标签:"请在选择树中选择物体模型"
- "获取选中物体"按钮
- 物体状态显示(简单文本)
**验收标准**:
- [ ] 用户能看到清晰的车辆选择提示
- [ ] 能够获取当前选中的车辆
- [ ] 显示基本的车辆信息
- [ ] 用户能看到清晰的物体选择提示
- [ ] 能够获取当前选中的物体
- [ ] 显示基本的物体信息
### 阶段2核心动画功能第2周
@ -80,7 +80,7 @@
**具体步骤**:
1. 保留现有的Timer-based动画逻辑
2. 添加简单的车辆获取功能
2. 添加简单的物体获取功能
3. 优化动画生成和播放逻辑
4. 如果TimeLiner简单尝试集成如果复杂保持现有方案
@ -88,8 +88,8 @@
```csharp
public class PathAnimationManager
{
// 简化的车辆获取
private ModelItem GetSelectedVehicle()
// 简化的物体获取
private ModelItem GetSelectedObject()
{
var selection = Application.ActiveDocument.CurrentSelection.SelectedItems;
return selection.FirstOrDefault(item => item.HasGeometry);
@ -106,7 +106,7 @@ public class PathAnimationManager
```
**验收标准**:
- [ ] 能够获取选中的车辆模型
- [ ] 能够获取选中的物体模型
- [ ] 能够基于路径点创建动画
- [ ] 保持现有的播放控制功能
- [ ] 动画播放流畅自然
@ -119,7 +119,7 @@ public class PathAnimationManager
**描述**: 在现有动画控制面板中集成所有必要功能
**具体步骤**:
1. 添加车辆选择提示和状态显示
1. 添加物体选择提示和状态显示
2. 集成路径选择下拉框
3. 添加"生成动画"按钮
4. 优化现有的播放控制按钮
@ -128,9 +128,9 @@ public class PathAnimationManager
```
动画控制面板:
┌─────────────────────────────┐
车辆选择: │
│ [ 请在选择树中选择车辆 ] │
│ [获取选中车辆] 状态: 未选择 │
物体选择: │
│ [ 请在选择树中选择物体 ] │
│ [获取选中物体] 状态: 未选择 │
│ │
│ 路径选择: │
│ [路径下拉框▼] 点数: 0 │
@ -151,10 +151,10 @@ public class PathAnimationManager
#### 任务4.1:完整工作流实现
**优先级**: 🔴 高
**预计工时**: 4小时
**描述**: 实现从车辆选择到动画播放的完整工作流
**描述**: 实现从物体选择到动画播放的完整工作流
**具体步骤**:
1. 集成车辆获取、路径选择、动画生成的完整流程
1. 集成物体获取、路径选择、动画生成的完整流程
2. 实现"生成动画"按钮的完整逻辑
3. 确保所有组件之间的数据传递正确
4. 添加基本的错误处理和用户提示
@ -162,8 +162,8 @@ public class PathAnimationManager
**核心工作流**:
```
用户操作流程:
1. 在选择树中选择车辆
2. 点击"获取选中车辆"
1. 在选择树中选择物体
2. 点击"获取选中物体"
3. 从下拉框选择路径
4. 点击"生成动画"
5. 使用播放控制按钮
@ -196,7 +196,7 @@ public class PathAnimationManager
**测试内容**:
1. 完整用户工作流测试
2. 不同车辆和路径的兼容性测试
2. 不同物体和路径的兼容性测试
3. 性能测试(动画流畅度)
4. 错误处理测试
@ -215,7 +215,7 @@ public class PathAnimationManager
var doc = Application.ActiveDocument;
var timeliner = (DocumentTimeliner)doc.Timeliner;
var task = timeliner.Tasks.AddNew();
task.DisplayName = "车辆运输动画";
task.DisplayName = "物体运输动画";
```
### 2. 路径到动画转换
@ -223,9 +223,9 @@ task.DisplayName = "车辆运输动画";
- 计算路径长度和时间分配
- 实现平滑的插值算法
### 3. 车辆变换计算
- 基于路径方向计算车辆朝向
- 处理车辆的位置和旋转变换
### 3. 物体变换计算
- 基于路径方向计算物体朝向
- 处理物体的位置和旋转变换
- 确保动画的连续性和自然性
### 4. UI集成策略
@ -259,10 +259,10 @@ task.DisplayName = "车辆运输动画";
## 验收标准总结
### 核心功能验收
- [ ] 用户能够选择路径和车辆
- [ ] 用户能够选择路径和物体
- [ ] 能够生成基于TimeLiner的动画
- [ ] 动画在系统TimeLiner窗口中正确播放
- [ ] 车辆沿路径自然流畅移动
- [ ] 物体沿路径自然流畅移动
### 性能验收
- [ ] 动画生成时间 < 3秒
@ -276,10 +276,10 @@ task.DisplayName = "车辆运输动画";
---
## 真实车辆模型方案的技术说明
## 真实物体模型方案的技术说明
### 方案优势
1. **用户体验**: 直接使用真实车辆模型,更直观和实用
1. **用户体验**: 直接使用真实物体模型,更直观和实用
2. **技术简化**: 经分析发现复杂度并不高,无需中间过渡
3. **一步到位**: 避免后续的重构和二次开发
4. **充分利用API**: 发挥Navisworks原生功能的优势
@ -288,31 +288,31 @@ task.DisplayName = "车辆运输动画";
| 挑战 | 复杂度 | 解决方案 | 预计工时 |
|------|--------|----------|----------|
| **车辆选择** | 🟢 低 | 使用Selection API | 1小时 |
| **物体选择** | 🟢 低 | 使用Selection API | 1小时 |
| **基本移动** | 🟢 低 | 现有OverridePermanentTransform | 1小时 |
| **中心点定位** | 🟡 中 | 包围盒中心计算 | 2小时 |
| **车辆朝向** | 🟡 中 | 路径方向计算和旋转变换 | 3小时 |
| **物体朝向** | 🟡 中 | 路径方向计算和旋转变换 | 3小时 |
| **TimeLiner集成** | 🟡 中 | API学习和集成 | 2小时 |
### 核心技术实现
#### 1. 车辆选择(简单)
#### 1. 物体选择(简单)
```csharp
// 获取用户选择的车辆
// 获取用户选择的物体
var selection = Application.ActiveDocument.CurrentSelection.SelectedItems;
var vehicle = selection.FirstOrDefault(item => item.HasGeometry);
var object = selection.FirstOrDefault(item => item.HasGeometry);
```
#### 2. 车辆中心点定位
#### 2. 物体中心点定位
```csharp
// 获取车辆包围盒中心
var boundingBox = vehicle.BoundingBox();
var vehicleCenter = boundingBox.Center;
// 获取物体包围盒中心
var boundingBox = object.BoundingBox();
var objectCenter = boundingBox.Center;
```
#### 3. 车辆朝向计算(需要实现)
#### 3. 物体朝向计算(需要实现)
```csharp
// 计算车辆沿路径的朝向
// 计算物体沿路径的朝向
public Vector3D CalculateDirection(Point3D currentPos, Point3D nextPos)
{
return new Vector3D(nextPos.X - currentPos.X, nextPos.Y - currentPos.Y, nextPos.Z - currentPos.Z);
@ -329,13 +329,13 @@ var finalTransform = translationTransform * rotationTransform;
### 验证计划
1. **第1周**: 验证TimeLiner API对复杂ModelItem的支持
2. **第2周**: 测试车辆朝向计算的准确性
3. **第3周**: 性能测试(复杂车辆模型的动画流畅度)
2. **第2周**: 测试物体朝向计算的准确性
3. **第3周**: 性能测试(复杂物体模型的动画流畅度)
### 风险缓解
- **备用方案**: 如果车辆朝向计算复杂,初期可以只做位置移动
- **备用方案**: 如果物体朝向计算复杂,初期可以只做位置移动
- **性能保障**: 提供动画质量设置(高/中/低精度)
- [ ] 确保对不同类型的车辆模型都能正常工作
- [ ] 确保对不同类型的物体模型都能正常工作
这个方案让我们直接实现最终目标,避免了中间步骤的浪费。准备开始执行吗?
@ -352,17 +352,17 @@ var finalTransform = translationTransform * rotationTransform;
**总计: 20小时**约2-3个工作日
### 简化说明:
- 移除了复杂的VehicleSelectionManager改为简单的选择获取
- 移除了复杂的ObjectSelectionManager改为简单的选择获取
- 直接使用现有的路径选择功能
- TimeLiner集成作为可选项简单就做复杂就跳过
- 专注于核心MVP功能确保基本工作流畅通
## 核心技术要点
### 1. 简化的车辆处理
- **车辆选择**: 使用`Application.ActiveDocument.CurrentSelection.SelectedItems`获取
- **UI提示**: 简单的标签提示用户在选择树中选择车辆
- **状态显示**: 基本的文本状态显示,不需要复杂的车辆信息面板
### 1. 简化的物体处理
- **物体选择**: 使用`Application.ActiveDocument.CurrentSelection.SelectedItems`获取
- **UI提示**: 简单的标签提示用户在选择树中选择物体
- **状态显示**: 基本的文本状态显示,不需要复杂的物体信息面板
### 2. 现有功能复用
- **路径选择**: 直接使用现有的PathPlanningManager功能
@ -370,7 +370,7 @@ var finalTransform = translationTransform * rotationTransform;
- **UI集成**: 在现有动画控制面板中添加必要组件
### 3. MVP工作流
- **操作流程**: 选择车辆 → 获取选中车辆 → 选择路径 → 生成动画 → 播放控制
- **操作流程**: 选择物体 → 获取选中物体 → 选择路径 → 生成动画 → 播放控制
- **核心功能**: 确保基本的动画生成和播放功能正常工作
- **可选功能**: TimeLiner集成根据复杂度决定是否实现
@ -391,12 +391,12 @@ var finalTransform = translationTransform * rotationTransform;
- 结果TimeLiner API基本可用但结构较简单
- 决定暂时保持现有Timer方案TimeLiner作为后续优化项
**任务1.2:简化车辆选择UI**
**任务1.2:简化物体选择UI**
- 状态:已完成
- 修改在MainPlugin.cs的CreateAnimationControls方法中添加了
- 车辆选择提示标签
- "获取选中车辆"按钮
- 车辆状态显示
- 物体选择提示标签
- "获取选中物体"按钮
- 物体状态显示
- 路径选择下拉框
- 路径信息显示
- 重新布局了所有控件
@ -409,14 +409,14 @@ var finalTransform = translationTransform * rotationTransform;
**任务2.2简化PathAnimationManager**
- 状态:已完成
- 实现添加了SetupSimpleAnimation和GetSelectedVehicle静态方法
- 实现添加了SetupSimpleAnimation和GetSelectedObject静态方法
#### ✅ 阶段3完整UI集成已完成
**任务3.1:完善动画控制面板**
- 状态:已完成
- 实现完整的MVP UI布局包括
- 车辆选择提示和获取按钮
- 物体选择提示和获取按钮
- 路径选择下拉框和刷新按钮
- 动画控制按钮(生成、播放、停止、重置)
- 状态显示和反馈
@ -431,7 +431,7 @@ var finalTransform = translationTransform * rotationTransform;
**任务4.1:完整工作流实现**
- 状态:已完成
- 实现完整的MVP工作流已实现
- 车辆选择→路径选择→动画生成→播放的完整流程
- 物体选择→路径选择→动画生成→播放的完整流程
- 错误处理和用户反馈
- TimeLiner API快速测试集成
@ -449,23 +449,23 @@ var finalTransform = translationTransform * rotationTransform;
## 🎉 MVP开发完成总结
### 实现的功能
1. ✅ **车辆选择UI**:提示用户在选择树中选择车辆,提供获取按钮
1. ✅ **物体选择UI**:提示用户在选择树中选择物体,提供获取按钮
2. ✅ **路径列表选择**:下拉框显示现有路径,支持刷新
3. ✅ **动画生成**:基于选中车辆和路径生成动画
3. ✅ **动画生成**:基于选中物体和路径生成动画
4. ✅ **动画播放**:完整的播放控制(播放、停止、重置)
5. ✅ **错误处理**:完善的错误提示和状态反馈
6. ✅ **TimeLiner测试**API可用性验证
### 技术实现
- **UI集成**:在现有动画控制面板中完整集成所有功能
- **简化API**添加了SetupSimpleAnimation和GetSelectedVehicle方法
- **简化API**添加了SetupSimpleAnimation和GetSelectedObject方法
- **工作流优化**:清晰的操作步骤和状态反馈
- **编译状态**:代码完全编译成功,无语法错误
### 用户操作流程
```
1. 在选择树中选择车辆模型
2. 点击"获取选中车辆"按钮
1. 在选择树中选择物体模型
2. 点击"获取选中物体"按钮
3. 从路径下拉框选择现有路径
4. 点击"生成动画"按钮
5. 使用播放控制按钮(播放/停止/重置)

View File

@ -63,7 +63,7 @@ public class LogisticsAttributeInfo
public string ElementType { get; set; }
public bool IsTraversable { get; set; }
public int Priority { get; set; }
public string VehicleSize { get; set; }
public string ObjectSize { get; set; }
public double SpeedLimit { get; set; }
}
```
@ -75,7 +75,7 @@ public class LogisticsAttributeInfo
- **技术栈**Windows Forms
- **功能特性**
- 支持所有8种物流元素类型选择
- 可编辑所有属性值(可通行、优先级、车辆尺寸、速度限制)
- 可编辑所有属性值(可通行、优先级、物体尺寸、速度限制)
- 支持现有属性值的回显和编辑
- 输入验证和错误处理
@ -83,7 +83,7 @@ public class LogisticsAttributeInfo
- ComboBox元素类型选择
- CheckBox可通行设置
- NumericUpDown优先级和速度限制
- ComboBox车辆尺寸选择
- ComboBox物体尺寸选择
- 确定/取消按钮
## 3. 重构MainPlugin.cs方法

View File

@ -149,7 +149,7 @@ public class PathRoute
* [2024-12-19 23:15]
* 步骤8. 实现通道选择/筛选功能PathPlanningManager.cs + PathPlanningModels.cs
* 修改为PathPlanningManager添加智能通道选择功能在PathPlanningModels添加通道选择相关数据模型
* 更改摘要:实现自动检测、手动选择、基于属性筛选等多种通道选择方式,支持车辆尺寸匹配和通道高亮显示
* 更改摘要:实现自动检测、手动选择、基于属性筛选等多种通道选择方式,支持物体尺寸匹配和通道高亮显示
* 原因:执行计划步骤 [8]
* 阻碍:无
* 用户确认状态:待确认

View File

@ -43,7 +43,7 @@
public struct HeightLayer
{
public double Z; // 该层的Z坐标
public HeightInterval PassableHeight; // 可通行高度范围(用于车辆检查)
public HeightInterval PassableHeight; // 可通行高度范围(用于物体检查)
public ModelItem SourceItem; // 来源模型项
public double SpeedLimit; // 限速
public CategoryAttributeManager.LogisticsElementType Type; // 层类型

View File

@ -140,7 +140,7 @@ var result = await command.ExecuteAsync();
var collisionParams = new CollisionDetectionParameters
{
TargetRoute = specificRoute, // 或设置 CheckAllRoutes = true
VehicleSize = 1.8, // 车辆尺寸(米)
ObjectSize = 1.8, // 物体尺寸(米)
SafetyMargin = 0.5, // 安全边距(米)
GenerateReport = true
};
@ -152,7 +152,7 @@ var result = await command.ExecuteAsync();
**检测功能**
- 单路径检测
- 全部路径批量检测
- 可配置车辆尺寸和安全边距
- 可配置物体尺寸和安全边距
- 详细碰撞报告
### 6. StartAnimationCommand - 启动动画

View File

@ -40,7 +40,7 @@ public class AutoPathPlanningParameters
{
public Point3D StartPoint { get; set; }
public Point3D EndPoint { get; set; }
public double VehicleSize { get; set; } = 1.0;
public double ObjectSize { get; set; } = 1.0;
public double SafetyMargin { get; set; } = 0.5;
public double GridSize { get; set; } = -1;
public string PathName { get; set; }
@ -75,7 +75,7 @@ RegisterCommand("AutoPathPlanning", (object[] args) =>
{
StartPoint = args[0] as Point3D,
EndPoint = args[1] as Point3D,
VehicleSize = args.Length > 2 ? (double)args[2] : 1.0,
ObjectSize = args.Length > 2 ? (double)args[2] : 1.0,
SafetyMargin = args.Length > 3 ? (double)args[3] : 0.5,
GridSize = args.Length > 4 ? (double)args[4] : -1,
PathName = args.Length > 5 ? args[5]?.ToString() : null,
@ -89,10 +89,10 @@ RegisterCommand("AutoPathPlanningQuick", (object[] args) =>
{
var startPoint = args[0] as Point3D;
var endPoint = args[1] as Point3D;
var vehicleSize = args.Length > 2 ? (double)args[2] : 1.0;
var objectSize = args.Length > 2 ? (double)args[2] : 1.0;
var pathName = args.Length > 3 ? args[3]?.ToString() : null;
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, vehicleSize, pathName);
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, objectSize, pathName);
});
```
@ -152,7 +152,7 @@ public PathPlanningResult ValidateParameters()
if (StartPoint == null) errors.Add("起点不能为空");
if (EndPoint == null) errors.Add("终点不能为空");
if (VehicleSize <= 0 || VehicleSize > 10) errors.Add("车辆尺寸必须在0-10米之间");
if (ObjectSize <= 0 || ObjectSize > 10) errors.Add("物体尺寸必须在0-10米之间");
// ... 更多验证逻辑
return errors.Count == 0

View File

@ -63,7 +63,7 @@ private void RegisterControlsToCoordinator()
{
["PathListView"] = _pathListView,
["MemoryLabel"] = _memoryLabel,
["VehicleStatusLabel"] = _vehicleStatusLabel,
["ObjectStatusLabel"] = _objectStatusLabel,
["CreateAnimationButton"] = _createAnimationButton,
// ... 其他控件
};

View File

@ -328,7 +328,7 @@ public class LogisticsControlViewModel : ViewModelBase
```csharp
public interface IPathPlanningService
{
Task<PathRoute> AutoPlanPathAsync(Point3D start, Point3D end, double vehicleSize, double safetyMargin);
Task<PathRoute> AutoPlanPathAsync(Point3D start, Point3D end, double objectSize, double safetyMargin);
Task<bool> ValidatePathAsync(PathRoute route);
}
@ -337,7 +337,7 @@ public class PathPlanningManager : IPathPlanningService
// 移除所有UI相关事件
// 只保留纯业务逻辑
public async Task<PathRoute> AutoPlanPathAsync(Point3D start, Point3D end, double vehicleSize, double safetyMargin)
public async Task<PathRoute> AutoPlanPathAsync(Point3D start, Point3D end, double objectSize, double safetyMargin)
{
// 纯后台线程执行不触发任何UI事件
return await Task.Run(() => {

View File

@ -318,7 +318,7 @@ var rotatedBBox = CreateVirtualBoundingBox(
- 对比旋转前/后的预计算碰撞结果
- 在狭窄转弯处确保碰撞被正确捕获
- 验证非正方形物体(如长方形车辆)的碰撞检测准确性
- 验证非正方形物体(如长方形物体)的碰撞检测准确性
### 4. 初始化测试

View File

@ -99,7 +99,7 @@ public struct GridCell
**集成点**
- `AutoPathPlanning()`方法中调用新的网格生成模式
- 传递车辆高度参数给网格生成器
- 传递物体高度参数给网格生成器
- 保持现有的UI事件机制不变
### 第四阶段:性能优化和测试 2天
@ -145,7 +145,7 @@ public struct GridCell
- **通道投影精度提升**: 从包围盒误差→精确几何投影
- **性能优化**: 通道投影提升200-1000倍障碍检测提升10000倍以上
- **功能增强**: 支持不同车辆高度的2.5D路径规划
- **功能增强**: 支持不同物体高度的2.5D路径规划
- **质量改进**: 避免L型、T型通道中的不必要绕行
这个方案充分利用了现有代码的几何处理、属性管理等核心功能,只需要新增网格生成的改进算法,实现成本低且风险可控。

View File

@ -319,7 +319,7 @@ public class PathfindingContext
```csharp
public class PathfindingConfig
{
public double DefaultVehicleHeight { get; set; } = 3.0;
public double DefaultObjectHeight { get; set; } = 3.0;
public double CollinearTolerance { get; set; } = 0.01;
public int MaxPathPoints { get; set; } = 1000;
public bool EnableHeightConstraints { get; set; } = true;

View File

@ -20,10 +20,10 @@
- `_channelBuilder.BuildChannelCoverage()` - 构建通道覆盖网格
### 4. 车辆膨胀
### 4. 物体膨胀
- `ApplyVehicleInflation()` - 应用车辆膨胀
- `ApplyVehicleInflationOptimized()` - 优化版膨胀算法
- `ApplyObjectInflation()` - 应用物体膨胀
- `ApplyObjectInflationOptimized()` - 优化版膨胀算法
### 5. 数据结构
@ -40,11 +40,11 @@ private GridMap GenerateBoundingBoxBased2_5D(
Document document,
BoundingBox3D bounds,
double cellSize,
double vehicleRadius,
double objectRadius,
double safetyMargin,
Point3D planningStartPoint,
Point3D planningEndPoint,
double vehicleHeight)
double objectHeight)
```
### 2. 新增辅助方法
@ -73,10 +73,10 @@ private GridMap GenerateBoundingBoxBased2_5D(
var channelCoverage = _channelBuilder.BuildChannelCoverage(cellSizeInModelUnits, document);
2. 直接遍历处理障碍物
ProcessObstaclesWithBoundingBox(document, channelCoverage.GridMap, vehicleHeight, safetyMargin);
ProcessObstaclesWithBoundingBox(document, channelCoverage.GridMap, objectHeight, safetyMargin);
3. 应用车辆膨胀(复用现有)
ApplyVehicleInflation(channelCoverage.GridMap, totalInflationRadius);
3. 应用物体膨胀(复用现有)
ApplyObjectInflation(channelCoverage.GridMap, totalInflationRadius);
// ProcessObstaclesWithBoundingBox 实现逻辑
- 获取所有模型项

View File

@ -157,7 +157,7 @@ public class SlopeSegment
### 2. 动画质量改善
- 车辆在斜坡上的倾斜角度准确
- 物体在斜坡上的倾斜角度准确
- 平滑的坡度过渡动画
- 真实的物理运动模拟

View File

@ -409,7 +409,7 @@ namespace NavisworksTransport.Utils.CoordinateSystem
| **PathPointRenderPlugin** | 渲染时坐标转换 | 较小 | P1 |
| **GeometryHelper** | 几何提取时考虑坐标系 | 中等 | P1 |
| **ChannelHeightDetector** | 垂直射线方向适配 | 较小 | P0 |
| **Animation/TimeLiner** | 车辆移动方向适配 | 中等 | P2 |
| **Animation/TimeLiner** | 物体移动方向适配 | 中等 | P2 |
| **SlopeAnalyzer** | 坡度计算适配 | 较小 | P1 |
---

View File

@ -29,7 +29,7 @@ try
document,
bounds,
gridSize,
vehicleSize,
objectSize,
safetyMargin,
startPoint.Position,
endPoint.Position,
@ -45,7 +45,7 @@ catch (Exception modeEx)
document,
bounds,
gridSize,
vehicleSize,
objectSize,
safetyMargin,
startPoint.Position,
endPoint.Position,
@ -64,7 +64,7 @@ catch (Exception modeEx)
- 将原有的简化路径规划实现替换为真实的A*算法调用
- 直接调用PathPlanningManager.AutoPlanPath方法执行完整的路径规划逻辑
- 改进了参数处理,包括车辆尺寸到半径的转换
- 改进了参数处理,包括物体尺寸到半径的转换
- 增强了错误处理和日志记录
**关键代码**:
@ -75,15 +75,15 @@ try
{
LogInfo("开始调用PathPlanningManager进行A*路径规划");
// 计算车辆半径(从尺寸)
var vehicleRadius = _parameters.VehicleSize / 2.0;
LogInfo($"使用车辆半径: {vehicleRadius}m基于车辆尺寸{_parameters.VehicleSize}m");
// 计算物体半径(从尺寸)
var objectRadius = _parameters.ObjectSize / 2.0;
LogInfo($"使用物体半径: {objectRadius}m基于物体尺寸{_parameters.ObjectSize}m");
// 调用PathPlanningManager的AutoPlanPath方法执行真实的A*算法
var pathPlanTask = _pathPlanningManager.AutoPlanPath(
startPathPoint,
endPathPoint,
vehicleRadius,
objectRadius,
_parameters.SafetyMargin,
actualGridSize);
@ -129,7 +129,7 @@ catch (Exception ex)
- 增加了详细的日志记录,包括:
- 网格生成参数
- 起点和终点坐标
- 车辆参数
- 物体参数
- 性能统计信息
- 错误和警告信息

View File

@ -12,7 +12,7 @@
新增了以下JSON数据模型类用于序列化和反序列化
- **JsonPathPoint** - 路径点数据模型
- **JsonVehicleLimits** - 车辆限制数据模型
- **JsonObjectLimits** - 物体限制数据模型
- **JsonPathRoute** - 路径数据模型
- **JsonProjectInfo** - 项目信息数据模型
- **JsonPathPlanningData** - 路径规划数据根对象
@ -34,7 +34,7 @@
- ✅ 跳过无效路径点,继续处理其他点
- ✅ 跳过无效路径,继续处理其他路径
- ✅ 路径点按索引排序
- ✅ 默认值处理(车辆参数、网格大小等)
- ✅ 默认值处理(物体参数、网格大小等)
- ✅ 异常类型捕获和转换
#### 日志记录
@ -60,7 +60,7 @@
- **2条完整路径**
- **7个有效路径点**
- **3种路径点类型**StartPoint、WayPoint、EndPoint
- **完整的车辆参数**
- **完整的物体参数**
- **标准JSON格式**
---
@ -80,7 +80,7 @@ JSON导入功能与现有JSON导出功能完全对应
| 功能 | 导出 | 导入 |
|------|------|------|
| 路径点 | ✅ 支持 | ✅ 支持 |
| 车辆参数 | ✅ 支持 | ✅ 支持 |
| 物体参数 | ✅ 支持 | ✅ 支持 |
| 路径属性 | ✅ 支持 | ✅ 支持 |
| 项目信息 | ✅ 支持 | ✅ 支持 |
| 坐标数据 | ✅ 支持 | ✅ 支持 |

View File

@ -491,7 +491,7 @@ public class PathOptionsViewModel
else if (option.Strategy == PathStrategy.Safest)
sb.AppendLine("✓ 远离危险区域,安全可靠");
else if (option.Strategy == PathStrategy.Straightest)
sb.AppendLine("✓ 转弯最少,便于大型车辆");
sb.AppendLine("✓ 转弯最少,便于大型物体");
return sb.ToString();
}

View File

@ -586,7 +586,7 @@ public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context
|-----|------|------|
| 默认配置 | `resources/default_config.toml` | 系统默认配置模板 |
| 插件名称 | `resources/TransportPlugin.name.txt` | 插件显示名称 |
| 单位立方体 | `resources/unit_cube.nwc` | 虚拟车辆碰撞检测用 |
| 单位立方体 | `resources/unit_cube.nwc` | 虚拟物体碰撞检测用 |
**部署说明**: 编译时资源文件会自动复制到输出目录,部署脚本会将整个 `resources` 文件夹复制到插件目录。

View File

@ -288,7 +288,7 @@ public class PathAnalysisReportGenerator
4. 同终点组分析 - 组内对比表格
5. 柱状图 - CSS实现的对比图
6. 优化建议 - 分类建议列表
7. 技术参数 - 车辆参数、检测参数
7. 技术参数 - 物体参数、检测参数
```
**验收标准**:

View File

@ -201,7 +201,7 @@ public class VerticalScanProcessor
public void ScanForObstacles(
GridMap channelGrid,
Document document,
double vehicleHeight)
double objectHeight)
{
LogManager.Info("=== 开始障碍物检测(多级筛选优化)===");
@ -215,7 +215,7 @@ public class VerticalScanProcessor
var relevantObstacles = FilterByHeight(
nearbyObstacles,
minHeight: 0,
maxHeight: vehicleHeight + 2.0);
maxHeight: objectHeight + 2.0);
LogManager.Info($"第2级筛选高度相关物体{relevantObstacles.Count}个");
// 第3级提取三角形并构建空间哈希
@ -235,7 +235,7 @@ public class VerticalScanProcessor
var localTriangles = obstacleHash.GetTrianglesAt(cell.WorldPos.X, cell.WorldPos.Y);
// 向上扫描,计算可通行高度区间
var intervals = ScanUpward(cell.WorldPos, localTriangles, 0, vehicleHeight + 10);
var intervals = ScanUpward(cell.WorldPos, localTriangles, 0, objectHeight + 10);
cell.PassableHeights = intervals;
});
@ -453,7 +453,7 @@ public class GeometryExtractorCallback : COMApi.InwSimplePrimitivesCB
Document doc,
BoundingBox3D bounds,
double gridSize,
double vehicleSize,
double objectSize,
double safetyMargin,
Point3D startPoint,
Point3D endPoint,
@ -467,10 +467,10 @@ public class GeometryExtractorCallback : COMApi.InwSimplePrimitivesCB
// 步骤2障碍物检测多级筛选优化
var scanner = new VerticalScanProcessor();
scanner.ScanForObstacles(channelGrid, doc, vehicleSize);
scanner.ScanForObstacles(channelGrid, doc, objectSize);
// 步骤3根据车辆高度生成最终可通行网格
return GenerateFinalGrid(channelGrid, vehicleSize);
// 步骤3根据物体高度生成最终可通行网格
return GenerateFinalGrid(channelGrid, objectSize);
}
else
{
@ -598,7 +598,7 @@ L型走廊处理对比:
1. ✅ 通道区域精确识别(垂直投影采样,不再是简单包围盒)
2. ✅ 垂直扫描准确计算可通行高度2.5D高度区间)
3. ✅ 支持不同车辆高度的路径规划
3. ✅ 支持不同物体高度的路径规划
4. ✅ 通道投影性能提升100-1000倍
5. ✅ 障碍物检测性能提升10000-40000倍
6. ✅ 路径质量明显改善(避免不必要绕行)
@ -608,7 +608,7 @@ L型走廊处理对比:
```xml
<PathPlanning>
<GridGeneration mode="ChannelBased2_5D">
<VehicleHeight>2.0</VehicleHeight>
<ObjectHeight>2.0</ObjectHeight>
<GridSize>0.5</GridSize>
<ScanPrecision>High</ScanPrecision>
<EnableCache>true</EnableCache>

View File

@ -15,7 +15,7 @@
"name": "测试路径1",
"description": "从入口到仓库的路径",
"totalLength": 45.6,
"vehicleLimits": {
"objectLimits": {
"maxLength": 2.0,
"maxWidth": 1.5,
"maxHeight": 2.0,
@ -71,7 +71,7 @@
"name": "测试路径2",
"description": "从仓库到出口的路径",
"totalLength": 32.4,
"vehicleLimits": {
"objectLimits": {
"maxLength": 2.0,
"maxWidth": 1.5,
"maxHeight": 2.0,

View File

@ -1,59 +1,59 @@
# 车辆参数同步问题修复
# 物体参数同步问题修复
## 问题描述
PathPointRenderPlugin.cs 中使用了硬编码的默认车辆参数 (2.0m × 1.5m × 2.0m),而用户在 PathEditingViewModel 中配置的参数 (1.0m × 1.0m × 2.0m) 没有被正确同步到渲染插件,导致车辆通行空间模式显示的尺寸不正确。
PathPointRenderPlugin.cs 中使用了硬编码的默认物体参数 (2.0m × 1.5m × 2.0m),而用户在 PathEditingViewModel 中配置的参数 (1.0m × 1.0m × 2.0m) 没有被正确同步到渲染插件,导致物体通行空间模式显示的尺寸不正确。
## 根本原因
1. **PathPointRenderPlugin.cs** 中有硬编码的默认车辆参数
2. **PathEditingViewModel.cs**车辆参数属性setter只更新了UI没有同步到渲染插件
3. **SystemManagementViewModel** 在切换可视化模式时没有同步最新的车辆参数
4. **PathPointRenderPlugin.cs** 中的 `SetVehicleParameters()` 方法从未被调用
1. **PathPointRenderPlugin.cs** 中有硬编码的默认物体参数
2. **PathEditingViewModel.cs**物体参数属性setter只更新了UI没有同步到渲染插件
3. **SystemManagementViewModel** 在切换可视化模式时没有同步最新的物体参数
4. **PathPointRenderPlugin.cs** 中的 `SetObjectParameters()` 方法从未被调用
## 修复方案
### 1. PathEditingViewModel.cs 修改
**修改内容:**
- 为所有车辆参数属性setter添加 `SyncVehicleParametersToRenderPlugin()` 调用
- 添加 `SyncVehicleParametersToRenderPlugin()` 方法
- 为所有物体参数属性setter添加 `SyncObjectParametersToRenderPlugin()` 调用
- 添加 `SyncObjectParametersToRenderPlugin()` 方法
- 在 `UpdatePathVisualization()` 方法中添加参数同步
- 在两个构造函数中添加初始化参数同步
**代码变化:**
```csharp
// 车辆参数setter现在会自动同步到渲染插件
public double VehicleLength
// 物体参数setter现在会自动同步到渲染插件
public double ObjectLength
{
get => _vehicleLength;
get => _objectLength;
set
{
if (SetProperty(ref _vehicleLength, value))
if (SetProperty(ref _objectLength, value))
{
SyncVehicleParametersToRenderPlugin();
SyncObjectParametersToRenderPlugin();
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
}
}
// 同步方法
private void SyncVehicleParametersToRenderPlugin()
private void SyncObjectParametersToRenderPlugin()
{
try
{
if (PathPointRenderPlugin.Instance != null)
{
PathPointRenderPlugin.Instance.SetVehicleParameters(
VehicleLength,
VehicleWidth,
VehicleHeight,
PathPointRenderPlugin.Instance.SetObjectParameters(
ObjectLength,
ObjectWidth,
ObjectHeight,
SafetyMargin);
}
}
catch (Exception ex)
{
LogManager.Error($"[车辆参数同步] 同步车辆参数失败: {ex.Message}", ex);
LogManager.Error($"[物体参数同步] 同步物体参数失败: {ex.Message}", ex);
}
}
```
@ -61,9 +61,9 @@ private void SyncVehicleParametersToRenderPlugin()
### 2. SystemManagementViewModel.cs 修改
**修改内容:**
- 修改 `OnPathVisualizationModeChanged()` 方法,在切换到车辆通行空间模式时同步参数
- 修改 `OnVehicleHeightModeChanged()` 方法,在高度模式变更时同步参数
- 添加 `SyncVehicleParametersFromPathEditingViewModel()` 方法
- 修改 `OnPathVisualizationModeChanged()` 方法,在切换到物体通行空间模式时同步参数
- 修改 `OnObjectHeightModeChanged()` 方法,在高度模式变更时同步参数
- 添加 `SyncObjectParametersFromPathEditingViewModel()` 方法
- 添加 `GetPathEditingViewModel()` 方法
**代码变化:**
@ -73,27 +73,27 @@ private void OnPathVisualizationModeChanged()
{
// ... 现有逻辑 ...
// 在切换到车辆通行空间模式时,同步车辆参数
if (IsVehicleSpaceMode)
// 在切换到物体通行空间模式时,同步物体参数
if (IsObjectSpaceMode)
{
SyncVehicleParametersFromPathEditingViewModel();
SyncObjectParametersFromPathEditingViewModel();
}
}
```
## 修复效果
1. **实时同步**:用户在路径编辑界面修改车辆参数时,参数会立即同步到渲染插件
2. **模式切换同步**:在系统管理界面切换可视化模式时,会自动同步当前的车辆参数
1. **实时同步**:用户在路径编辑界面修改物体参数时,参数会立即同步到渲染插件
2. **模式切换同步**:在系统管理界面切换可视化模式时,会自动同步当前的物体参数
3. **初始化同步**:插件启动时会使用正确的用户配置参数,而不是硬编码默认值
4. **多层保障**:提供了多种获取车辆参数的方式,确保同步的可靠性
4. **多层保障**:提供了多种获取物体参数的方式,确保同步的可靠性
## 测试步骤
1. **启动插件**打开NavisworksTransport插件
2. **检查默认参数**:验证车辆通行空间模式使用的是 1.0m × 1.0m × 2.0m 而不是 2.0m × 1.5m × 2.0m
3. **修改参数**:在路径编辑界面修改车辆参数
4. **验证同步**:切换到车辆通行空间模式,确认显示的尺寸与设置的参数一致
2. **检查默认参数**:验证物体通行空间模式使用的是 1.0m × 1.0m × 2.0m 而不是 2.0m × 1.5m × 2.0m
3. **修改参数**:在路径编辑界面修改物体参数
4. **验证同步**:切换到物体通行空间模式,确认显示的尺寸与设置的参数一致
5. **模式切换测试**:在系统管理界面切换可视化模式,确认参数正确同步
## 技术细节

View File

@ -1,8 +1,8 @@
# 虚拟车辆立方体碰撞检测功能设计方案
# 虚拟物体立方体碰撞检测功能设计方案
## 需求背景
当前项目中,检测动画用的移动物体是手工选择的。需要增加一种方式,按照路径编辑中车辆的长宽高,动态生成一个立方体,用它来进行碰撞检测。
当前项目中,检测动画用的移动物体是手工选择的。需要增加一种方式,按照路径编辑中物体的长宽高,动态生成一个立方体,用它来进行碰撞检测。
**需求来源**`doc/requirement/todo_features.md` - 2025/12/08 功能点3
> (功能)动画检测,增加使用模拟物流构件立方体选项
@ -20,12 +20,12 @@
## 核心思路
**关键洞察**:虚拟车辆本质上就是一个动态创建的立方体ModelItem一旦创建完成后续流程与手动选择物体**完全一致**。
**关键洞察**:虚拟物体本质上就是一个动态创建的立方体ModelItem一旦创建完成后续流程与手动选择物体**完全一致**。
```
用户选择"虚拟车辆模式"
用户选择"虚拟物体模式"
根据车辆尺寸创建立方体几何体NWD/NWC文件或内存几何体
根据物体尺寸创建立方体几何体NWD/NWC文件或内存几何体
将创建的立方体作为 _animatedObject
@ -64,34 +64,34 @@ Navisworks API有以下几种方式创建几何体
### 2. 实现方案详细设计
#### 2.1 创建 VirtualVehicleManager 类
#### 2.1 创建 VirtualObjectManager 类
```csharp
/// <summary>
/// 虚拟车辆管理器 - 负责创建和管理虚拟车辆几何体
/// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体
/// </summary>
public class VirtualVehicleManager
public class VirtualObjectManager
{
private static VirtualVehicleManager _instance;
public static VirtualVehicleManager Instance => _instance ?? (_instance = new VirtualVehicleManager());
private static VirtualObjectManager _instance;
public static VirtualObjectManager Instance => _instance ?? (_instance = new VirtualObjectManager());
private ModelItem _virtualVehicleModelItem;
private ModelItem _virtualObjectModelItem;
private string _tempFilePath;
/// <summary>
/// 创建虚拟车辆立方体
/// 创建虚拟物体立方体
/// </summary>
/// <param name="lengthMeters">长度(米)</param>
/// <param name="widthMeters">宽度(米)</param>
/// <param name="heightMeters">高度(米)</param>
/// <param name="position">初始位置</param>
/// <returns>创建的ModelItem</returns>
public ModelItem CreateVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters, Point3D position)
public ModelItem CreateVirtualObject(double lengthMeters, double widthMeters, double heightMeters, Point3D position)
{
try
{
// 1. 清理之前的虚拟车辆
RemoveVirtualVehicle();
// 1. 清理之前的虚拟物体
RemoveVirtualObject();
// 2. 创建临时NWD文件包含立方体
_tempFilePath = CreateCubeNwdFile(lengthMeters, widthMeters, heightMeters);
@ -101,31 +101,31 @@ public class VirtualVehicleManager
doc.AppendFile(_tempFilePath);
// 4. 获取追加后的ModelItem最后一个模型的根节点
_virtualVehicleModelItem = GetLastAppendedModel();
_virtualObjectModelItem = GetLastAppendedModel();
// 5. 移动到指定位置
MoveToPosition(_virtualVehicleModelItem, position);
MoveToPosition(_virtualObjectModelItem, position);
LogManager.Info($"虚拟车辆创建成功: {lengthMeters:F1}m × {widthMeters:F1}m × {heightMeters:F1}m");
return _virtualVehicleModelItem;
LogManager.Info($"虚拟物体创建成功: {lengthMeters:F1}m × {widthMeters:F1}m × {heightMeters:F1}m");
return _virtualObjectModelItem;
}
catch (Exception ex)
{
LogManager.Error($"创建虚拟车辆失败: {ex.Message}");
LogManager.Error($"创建虚拟物体失败: {ex.Message}");
throw;
}
}
/// <summary>
/// 移除虚拟车辆
/// 移除虚拟物体
/// </summary>
public void RemoveVirtualVehicle()
public void RemoveVirtualObject()
{
if (_virtualVehicleModelItem != null)
if (_virtualObjectModelItem != null)
{
// 从文档中移除模型
// ...
_virtualVehicleModelItem = null;
_virtualObjectModelItem = null;
}
// 清理临时文件
@ -137,9 +137,9 @@ public class VirtualVehicleManager
}
/// <summary>
/// 获取当前虚拟车辆
/// 获取当前虚拟物体
/// </summary>
public ModelItem CurrentVirtualVehicle => _virtualVehicleModelItem;
public ModelItem CurrentVirtualObject => _virtualObjectModelItem;
}
```
@ -152,18 +152,18 @@ private void ExecuteGenerateAnimation()
{
ModelItem animatedObject;
if (UseVirtualVehicle)
if (UseVirtualObject)
{
// 创建虚拟车辆几何体
// 创建虚拟物体几何体
var startPosition = CurrentPathRoute.Points.First();
animatedObject = VirtualVehicleManager.Instance.CreateVirtualVehicle(
VirtualVehicleLength,
VirtualVehicleWidth,
VirtualVehicleHeight,
animatedObject = VirtualObjectManager.Instance.CreateVirtualObject(
VirtualObjectLength,
VirtualObjectWidth,
VirtualObjectHeight,
new Point3D(startPosition.X, startPosition.Y, startPosition.Z)
);
LogManager.Info($"使用虚拟车辆: {VirtualVehicleLength:F1}m × {VirtualVehicleWidth:F1}m × {VirtualVehicleHeight:F1}m");
LogManager.Info($"使用虚拟物体: {VirtualObjectLength:F1}m × {VirtualObjectWidth:F1}m × {VirtualObjectHeight:F1}m");
}
else
{
@ -174,7 +174,7 @@ private void ExecuteGenerateAnimation()
var pathPoints = CurrentPathRoute.Points.Select(p => new Point3D(p.X, p.Y, p.Z)).ToList();
_pathAnimationManager.CreateAnimation(
animatedObject, // 无论是手动选择的还是虚拟车辆都是真实的ModelItem
animatedObject, // 无论是手动选择的还是虚拟物体都是真实的ModelItem
pathPoints,
AnimationDuration,
CurrentPathRoute.Name,
@ -198,12 +198,12 @@ private void OnAnimationCompleted()
{
// ... 现有的报告生成和Clash测试创建 ...
// 如果使用了虚拟车辆,询问用户是否保留
if (UseVirtualVehicle && VirtualVehicleManager.Instance.CurrentVirtualVehicle != null)
// 如果使用了虚拟物体,询问用户是否保留
if (UseVirtualObject && VirtualObjectManager.Instance.CurrentVirtualObject != null)
{
// 可选:提示用户是否保留虚拟车辆
// 可选:提示用户是否保留虚拟物体
// 或者自动移除
// VirtualVehicleManager.Instance.RemoveVirtualVehicle();
// VirtualObjectManager.Instance.RemoveVirtualObject();
}
}
```
@ -254,10 +254,10 @@ private ModelItem CreateScaledCube(double length, double width, double height)
| 文件 | 修改类型 | 主要改动 |
|------|----------|----------|
| `VirtualVehicleManager.cs` | **新增** | 虚拟车辆几何体创建和管理 |
| `AnimationControlView.xaml` | 修改 | 添加RadioButton选择组和虚拟车辆尺寸显示 |
| `AnimationControlViewModel.cs` | 修改 | 调用VirtualVehicleManager创建虚拟车辆 |
| `LogisticsControlPanel.xaml.cs` | 修改 | 添加车辆参数同步逻辑 |
| `VirtualObjectManager.cs` | **新增** | 虚拟物体几何体创建和管理 |
| `AnimationControlView.xaml` | 修改 | 添加RadioButton选择组和虚拟物体尺寸显示 |
| `AnimationControlViewModel.cs` | 修改 | 调用VirtualObjectManager创建虚拟物体 |
| `LogisticsControlPanel.xaml.cs` | 修改 | 添加物体参数同步逻辑 |
| `resources/unit_cube.nwc` | **新增** | 预制的单位立方体模型文件 |
## 关键优势
@ -269,7 +269,7 @@ private ModelItem CreateScaledCube(double length, double width, double height)
## 测试要点
1. **几何体创建**
- 虚拟车辆立方体正确创建
- 虚拟物体立方体正确创建
- 尺寸和位置正确
2. **碰撞检测**
@ -278,11 +278,11 @@ private ModelItem CreateScaledCube(double length, double width, double height)
3. **清理**
- 动画结束后正确清理临时文件
- 用户可选择是否保留虚拟车辆
- 用户可选择是否保留虚拟物体
## 实现步骤
1. 准备预制的单位立方体NWC文件
2. 创建VirtualVehicleManager类
3. 修改AnimationControlViewModel调用VirtualVehicleManager
2. 创建VirtualObjectManager类
3. 修改AnimationControlViewModel调用VirtualObjectManager
4. 测试完整流程

View File

@ -183,7 +183,7 @@ refactor(voxel): 重构 VoxelGrid 数据结构
- 包含功能:
- 包围盒体素化算法
- 物流类型自动识别(从属性或名称推断)
- 障碍物膨胀(车辆半径)
- 障碍物膨胀(物体半径)
- 单位自动转换(米→模型单位)
- 详细的性能统计和日志
@ -414,7 +414,7 @@ git push
2. [ ] 实现精确体素化
- 使用 MeshSignedDistanceGrid
- 距离场查询和体素标记
- 考虑车辆半径和安全间隙
- 考虑物体半径和安全间隙
3. [ ] 实现特殊元素处理
- 门元素标记(可通行 + 速度限制)
- 通道元素标记
@ -682,7 +682,7 @@ git push
**备注**:
- geometry4Sharp 的 MeshSignedDistanceGrid 支持窄带模式
- 窄带宽度建议设为车辆半径的 3-5 倍
- 窄带宽度建议设为物体半径的 3-5 倍
---
@ -1303,7 +1303,7 @@ git push origin --delete feature/voxel-pathfinding
- **任务 1.3 完成**: 实现简单体素化原型 VoxelGridGeneratorcommit 064945b
- VoxelGridGenerator.cs: 360 行,基于包围盒的简单体素化
- 物流类型自动识别(从属性或名称推断)
- 障碍物膨胀处理(车辆半径)
- 障碍物膨胀处理(物体半径)
- 单位自动转换(米→模型单位)
- 详细的性能统计日志
- 测试方法 CreateTestGrid() 用于快速验证

View File

@ -81,14 +81,14 @@ h(u) ≤ cost(u,v) + h(v)
#### 3.2.2 基于精确欧几里得距离变换的膨胀算法
为了考虑车辆尺寸和安全间隙插件实现了基于精确欧几里得距离变换Exact Euclidean Distance Transform, EEDT的多层膨胀算法。该算法基于计算几何中的距离场理论在障碍物周围扩展不可通行区域确保生成的路径能够满足车辆的物理约束。
为了考虑物体尺寸和安全间隙插件实现了基于精确欧几里得距离变换Exact Euclidean Distance Transform, EEDT的多层膨胀算法。该算法基于计算几何中的距离场理论在障碍物周围扩展不可通行区域确保生成的路径能够满足物体的物理约束。
**距离变换的数学定义**给定二值图像I: ℤ² → {0,1}其中I(x,y) = 1表示障碍物I(x,y) = 0表示自由空间距离变换函数D: ℤ² → ℝ⁺定义为:
D(x,y) = min_{(i,j):I(i,j)=1} √[(x-i)² + (y-j)²]
**多层膨胀算法流程**
1. **障碍物膨胀**:从障碍物外围开始,计算每个自由网格到最近障碍物的欧几里得距离。对于车辆半径为r的膨胀条件网格(x,y)的膨胀判定为:
1. **障碍物膨胀**:从障碍物外围开始,计算每个自由网格到最近障碍物的欧几里得距离。对于物体半径为r的膨胀条件网格(x,y)的膨胀判定为:
Inflated(x,y) = D(x,y) < r
2. **边界膨胀**:从边界网格本身开始,考虑边界约束条件。边界膨胀函数定义为:
@ -194,7 +194,7 @@ Collision(C) = _{o₁,o₂∈Objects} (o₁(t) ∩ o₂(t) ≠ ∅)
**缓存键设计**
缓存键采用多重哈希函数组合生成:
Key = Hash₁(bounds) ⊕ Hash₂(grid_size) ⊕ Hash₃(vehicle_radius) ⊕ Hash₄(layer_config)
Key = Hash₁(bounds) ⊕ Hash₂(grid_size) ⊕ Hash₃(object_radius) ⊕ Hash₄(layer_config)
其中Hash₁、Hash₂、Hash₃、Hash₄为不同的哈希函数⊕为异或运算。这种设计确保了不同参数组合的网格地图能够被正确区分和缓存。

View File

@ -91,7 +91,7 @@ public class ArcTrajectory
### 2.1 核心目标
1. **动画帧预计算使用路径**: 替代原有的直线插值
2. **圆弧处方向计算准确**: 确保车辆在圆弧段正确朝向
2. **圆弧处方向计算准确**: 确保物体在圆弧段正确朝向
3. **性能优化**: 避免重复计算采样点
### 2.2 数据流设计

View File

@ -14,7 +14,7 @@
2. **参数配置**
- 使用系统参数作为默认值(帧率、时长、检测间隙等)
- 支持对每条路径进行单独配置(覆盖系统参数)
- 支持指定运动物体/虚拟车辆
- 支持指定运动物体/虚拟物体
- 支持指定被检测项(可选)
3. **队列执行**:简单队列,按顺序执行任务
4. **结果查看**
@ -80,8 +80,8 @@ CREATE TABLE BatchTaskItems (
EndTime TEXT, -- 结束时间
ErrorMessage TEXT, -- 错误信息
ConfigOverride TEXT, -- 配置覆盖JSON格式
VehicleObjectId TEXT, -- 运动物体ID虚拟车辆或真实物体)
VehicleObjectName TEXT, -- 运动物体名称
ObjectObjectId TEXT, -- 运动物体ID虚拟物体或真实物体)
ObjectObjectName TEXT, -- 运动物体名称
DetectionItems TEXT, -- 被检测项列表JSON格式可选
ClashDetectiveTestName TEXT, -- 关联的ClashDetective测试名称复用现有表
FOREIGN KEY (BatchTaskId) REFERENCES BatchTasks(Id) ON DELETE CASCADE
@ -177,8 +177,8 @@ public class BatchTaskItem
public DateTime? EndTime { get; set; }
public string ErrorMessage { get; set; }
public CollisionDetectionConfig ConfigOverride { get; set; }
public string VehicleObjectId { get; set; }
public string VehicleObjectName { get; set; }
public string ObjectObjectId { get; set; }
public string ObjectObjectName { get; set; }
public List<string> DetectionItems { get; set; } = new List<string>();
public string ClashDetectiveTestName { get; set; } // 关联到ClashDetectiveResults.TestName
}
@ -240,10 +240,10 @@ namespace NavisworksTransport.Core.Collision
List<AnimationFrame> PrecomputeFrames(
PathRoute route,
ModelItem animatedObject,
bool isVirtualVehicle,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
bool isVirtualObject,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
int frameRate,
double duration,
double detectionGap,
@ -259,12 +259,12 @@ namespace NavisworksTransport.Core.Collision
string pathName,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight);
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight);
}
}
```
@ -393,17 +393,17 @@ namespace NavisworksTransport.Core.Collision
// 获取运动物体
ModelItem animatedObject = null;
bool isVirtualVehicle = false;
if (!string.IsNullOrEmpty(item.VehicleObjectId))
bool isVirtualObject = false;
if (!string.IsNullOrEmpty(item.ObjectObjectId))
{
// 从Navisworks文档中查找物体
animatedObject = FindModelItemById(item.VehicleObjectId);
isVirtualVehicle = false;
animatedObject = FindModelItemById(item.ObjectObjectId);
isVirtualObject = false;
}
else
{
// 使用虚拟车辆
isVirtualVehicle = true;
// 使用虚拟物体
isVirtualObject = true;
}
// 在主线程执行Navisworks API调用但无UI操作
@ -413,8 +413,8 @@ namespace NavisworksTransport.Core.Collision
var frames = _processor.PrecomputeFrames(
pathRoute,
animatedObject,
isVirtualVehicle,
config.DetectionGapMeters, // 虚拟车辆尺寸从配置获取
isVirtualObject,
config.DetectionGapMeters, // 虚拟物体尺寸从配置获取
config.DetectionGapMeters,
config.DetectionGapMeters,
config.FrameRate,
@ -433,7 +433,7 @@ namespace NavisworksTransport.Core.Collision
pathRoute.Name,
pathRoute.Id,
animatedObject,
isVirtualVehicle,
isVirtualObject,
config.FrameRate,
config.DurationSeconds,
config.DetectionGapMeters,
@ -590,8 +590,8 @@ namespace NavisworksTransport.Core
SequenceNumber = sequence++,
Status = BatchTaskItemStatus.Pending,
ConfigOverride = itemConfig.ConfigOverride,
VehicleObjectId = itemConfig.VehicleObjectId,
VehicleObjectName = itemConfig.VehicleObjectName,
ObjectObjectId = itemConfig.ObjectObjectId,
ObjectObjectName = itemConfig.ObjectObjectName,
DetectionItems = itemConfig.DetectionItems
};
await _database.CreateBatchTaskItemAsync(item);

View File

@ -25,21 +25,21 @@
新增方法ConvertToAStarGridWithStrategy()
private Grid ConvertToAStarGridWithStrategy(GridMap gridMap, Point3D startPos, Point3D endPos, PathStrategy strategy,
double vehicleHeight)
double objectHeight)
{
if (strategy == PathStrategy.Shortest) {
// 调用现有方法保证100%兼容
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight);
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, objectHeight);
}
// 直线优先实现
return ConvertToAStarGridStraightest(gridMap, startPos, endPos, vehicleHeight);
return ConvertToAStarGridStraightest(gridMap, startPos, endPos, objectHeight);
}
4. 直线优先算法核心
基于文档第127行的AddEdge模式
private Grid ConvertToAStarGridStraightest(GridMap gridMap, Point3D start, Point3D end, double vehicleHeight)
private Grid ConvertToAStarGridStraightest(GridMap gridMap, Point3D start, Point3D end, double objectHeight)
{
// 1. 计算主方向
double dx = Math.Abs(end.X - start.X);
@ -64,11 +64,11 @@
// 4. 智能连接根据方向设置不同速度文档第115-126行模式
for (int x = 0; x < gridMap.Width; x++) {
for (int y = 0; y < gridMap.Height; y++) {
if (IsPassableWithHeight(gridMap.Cells[x, y], vehicleHeight)) {
if (IsPassableWithHeight(gridMap.Cells[x, y], objectHeight)) {
var pos = new GridPosition(x, y);
// 水平连接
if (x + 1 < gridMap.Width && IsPassableWithHeight(gridMap.Cells[x + 1, y], vehicleHeight)) {
if (x + 1 < gridMap.Width && IsPassableWithHeight(gridMap.Cells[x + 1, y], objectHeight)) {
var rightPos = new GridPosition(x + 1, y);
var velocity = preferHorizontal ? mainDirectionVelocity : crossDirectionVelocity;
grid.AddEdge(pos, rightPos, velocity);
@ -76,7 +76,7 @@
}
// 垂直连接
if (y + 1 < gridMap.Height && IsPassableWithHeight(gridMap.Cells[x, y + 1], vehicleHeight)) {
if (y + 1 < gridMap.Height && IsPassableWithHeight(gridMap.Cells[x, y + 1], objectHeight)) {
var bottomPos = new GridPosition(x, y + 1);
var velocity = preferHorizontal ? crossDirectionVelocity : mainDirectionVelocity;
grid.AddEdge(pos, bottomPos, velocity);
@ -85,7 +85,7 @@
// 对角线连接(可选)
if (x + 1 < gridMap.Width && y + 1 < gridMap.Height &&
IsPassableWithHeight(gridMap.Cells[x + 1, y + 1], vehicleHeight)) {
IsPassableWithHeight(gridMap.Cells[x + 1, y + 1], objectHeight)) {
var diagonalPos = new GridPosition(x + 1, y + 1);
grid.AddEdge(pos, diagonalPos, diagonalVelocity);
grid.AddEdge(diagonalPos, pos, diagonalVelocity);
@ -112,7 +112,7 @@
// 现有逻辑保持不变...
// 策略感知的路径查找
pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, vehicleHeight,
pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, objectHeight,
strategy);
}

View File

@ -14,7 +14,7 @@
[2025-12-08 10:05:28.751] [INFO] [动画生成] 碰撞检测缓存构建完成,耗时: 176911.7ms
[2025-12-08 10:05:28.774] [INFO] === 构建全局空间索引 ===
[2025-12-08 10:05:28.776] [INFO] [空间索引] 车辆宽度: 1.00米 → 格子大小: 3.28模型单位
[2025-12-08 10:05:28.776] [INFO] [空间索引] 物体宽度: 1.00米 → 格子大小: 3.28模型单位
[2025-12-08 10:05:28.781] [INFO] [空间索引] 开始构建全局空间索引
[2025-12-08 10:05:28.782] [INFO] [空间索引] 格子大小: 3.28 模型单位
[2025-12-08 10:05:28.807] [INFO] [空间索引] 从缓存获取 374422 个非通道几何对象(已过滤通道)

View File

@ -10,8 +10,8 @@
- 自动提取空轨几何体的下表面中心线作为路径
3. **路径类型区分**:路径需要标记为"地面路径"或"空轨路径"
4. **动画运行模式**
- 地面路径:车辆中心点在路径点上
- 空轨路径:车辆悬挂在空轨下方运行(车辆中心 = 路径点 - 车辆高度/2
- 地面路径:物体中心点在路径点上
- 空轨路径:物体悬挂在空轨下方运行(物体中心 = 路径点 - 物体高度/2
5. **碰撞检测**
- 统一使用3D碰撞检测
- 空轨路径需要排除空轨本身
@ -26,7 +26,7 @@
1. ✅ **空轨模型**:已存在,不需要建模规范
2. ✅ **空轨类型**:不需要分类型,只有一个"空轨"类型
3. ✅ **车辆悬挂**:车辆悬挂在空轨下方
3. ✅ **物体悬挂**:物体悬挂在空轨下方
4. ✅ **碰撞检测**:需要排除空轨本身
5. ✅ **数据库迁移**:不需要,新字段使用默认值
6. ✅ **向后兼容**:不需要向后兼容性
@ -44,7 +44,7 @@
| **物流属性系统** | 🟢 低 | 新增"空轨"枚举值 |
| **路径数据模型** | 🟡 中 | 新增路径类型字段 |
| **路径规划系统** | 🟡 中 | 新增空轨路径生成逻辑 |
| **动画系统** | 🟡 中 | 根据路径类型调整车辆位置 |
| **动画系统** | 🟡 中 | 根据路径类型调整物体位置 |
| **碰撞检测系统** | 🟢 低 | 空轨路径排除空轨本身 |
| **UI界面** | 🟡 中 | 路径类型选择、空轨路径生成 |
@ -64,7 +64,7 @@ public enum LogisticsElementType
// ... 现有类型 ...
/// <summary>
/// 空轨 - 空中运输路径,车辆悬挂运行权重0.7
/// 空轨 - 空中运输路径,物体悬挂运行权重0.7
/// </summary>
空轨 = 9,
}
@ -81,12 +81,12 @@ public enum LogisticsElementType
public enum PathType
{
/// <summary>
/// 地面路径 - 车辆在地面运行
/// 地面路径 - 物体在地面运行
/// </summary>
Ground = 0,
/// <summary>
/// 空轨路径 - 车辆悬挂在空轨下方运行
/// 空轨路径 - 物体悬挂在空轨下方运行
/// </summary>
Rail = 1,
}
@ -257,26 +257,26 @@ public static class RailGeometryHelper
### 3.3 第三阶段动画系统适配1天
#### 3.3.1 车辆位置计算
#### 3.3.1 物体位置计算
**文件**`PathAnimationManager.cs`
```csharp
/// <summary>
/// 计算车辆在路径点上的位置
/// 计算物体在路径点上的位置
/// </summary>
private Point3D CalculateVehiclePosition(Point3D pathPoint, PathType pathType)
private Point3D CalculateObjectPosition(Point3D pathPoint, PathType pathType)
{
if (pathType == PathType.Rail)
{
// 空轨路径:车辆悬挂在空轨下方
// 车辆中心点 = 空轨点 - 车辆高度的一半
double vehicleCenterZ = pathPoint.Z - _virtualVehicleHeight / 2;
return new Point3D(pathPoint.X, pathPoint.Y, vehicleCenterZ);
// 空轨路径:物体悬挂在空轨下方
// 物体中心点 = 空轨点 - 物体高度的一半
double objectCenterZ = pathPoint.Z - _virtualObjectHeight / 2;
return new Point3D(pathPoint.X, pathPoint.Y, objectCenterZ);
}
else
{
// 地面路径:车辆中心点在路径点上
// 地面路径:物体中心点在路径点上
return pathPoint;
}
}
@ -328,7 +328,7 @@ foreach (var modelItem in allModelItems)
}
// 统一使用3D碰撞检测
double distance = BoundingBoxGeometryUtils.CalculateDistance(vehicleBoundingBox, modelBoundingBox);
double distance = BoundingBoxGeometryUtils.CalculateDistance(objectBoundingBox, modelBoundingBox);
if (distance <= 0) // 相交或接触
{
@ -402,7 +402,7 @@ foreach (var modelItem in allModelItems)
### 第三阶段动画系统适配1天
1. ✅ 车辆位置计算适配
1. ✅ 物体位置计算适配
2. ✅ 传递路径类型参数
3. ✅ 动画播放测试
@ -428,7 +428,7 @@ foreach (var modelItem in allModelItems)
| 风险项 | 风险等级 | 缓解措施 |
|--------|---------|---------|
| 空轨几何体提取失败 | 🟢 低 | 空轨模型已存在,直接使用 |
| 车辆悬挂位置计算错误 | 🟡 中 | 充分测试,提供可视化验证 |
| 物体悬挂位置计算错误 | 🟡 中 | 充分测试,提供可视化验证 |
| 碰撞检测误报 | 🟡 中 | 排除空轨本身,调整检测参数 |
| 性能下降 | 🟢 低 | 空轨路径数量少,影响有限 |
| 几何计算性能 | 🟡 中 | 复用现有代码,优化采样策略 |
@ -450,7 +450,7 @@ foreach (var modelItem in allModelItems)
1. **复用现有代码**`ChannelHeightDetector` 的几何计算方法
2. **新增底部检测**`GetChannelBottomHeight()` 方法
3. **车辆悬挂计算**:车辆中心 = 空轨点 - 车辆高度/2
3. **物体悬挂计算**:物体中心 = 空轨点 - 物体高度/2
4. **碰撞检测统一**统一使用3D检测
5. **排除空轨本身**:通过排除列表过滤空轨模型
6. **精确几何计算**:支持倾斜、弯曲的轨道
@ -464,7 +464,7 @@ foreach (var modelItem in allModelItems)
### 6.4 预期成果
- ✅ 支持空轨路径生成
- ✅ 支持车辆悬挂运行
- ✅ 支持物体悬挂运行
- ✅ 支持空轨碰撞检测(排除空轨本身)
- ✅ 保持与现有地面路径功能的兼容性
@ -486,7 +486,7 @@ foreach (var modelItem in allModelItems)
- `PathDatabase.cs` - 适配路径类型读写
- `ChannelHeightDetector.cs` - 新增底部高度检测方法
- `PathEditingViewModel.cs` - 扩展路径编辑功能
- `PathAnimationManager.cs` - 适配车辆位置和碰撞检测
- `PathAnimationManager.cs` - 适配物体位置和碰撞检测
- `LogisticsControlPanel.xaml` - UI界面适配
### 7.2 方法清单
@ -511,7 +511,7 @@ foreach (var modelItem in allModelItems)
#### 新增方法PathAnimationManager.cs
- `CalculateVehiclePosition(Point3D pathPoint, PathType pathType)` - 计算车辆位置
- `CalculateObjectPosition(Point3D pathPoint, PathType pathType)` - 计算物体位置
#### 修改方法PathAnimationManager.cs

View File

@ -56,14 +56,14 @@ public enum ElementType
### 1.2 创建网格地图生成器 ✅
**新文件**: `src/PathPlanning/GridMapGenerator.cs`
**完成状态**: ✅ 已实现,支持障碍物识别和车辆膨胀
**完成状态**: ✅ 已实现,支持障碍物识别和物体膨胀
```csharp
public class GridMapGenerator
{
public GridMap GenerateFromBIM(Document document, BoundingBox3D bounds, double cellSize = 0.5)
public List<ModelItem> GetObstacleItems(Document document, BoundingBox3D bounds)
public void ApplyVehicleInflation(GridMap gridMap, double vehicleRadius)
public void ApplyObjectInflation(GridMap gridMap, double objectRadius)
private void MarkObstacleCells(GridMap gridMap, List<ModelItem> obstacles)
}
```
@ -103,7 +103,7 @@ public class AutoPathFinder
/// <summary>
/// 自动规划路径
/// </summary>
public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint, double vehicleSize = 1.0)
public PathRoute AutoPlanPath(Point3D startPoint, Point3D endPoint, double objectSize = 1.0)
{
// 1. 生成网格地图
// 2. 调用A*算法
@ -166,8 +166,8 @@ private static bool IsLikelyObstacle(ModelItem item)
<StackPanel>
<Label>网格精度(米):</Label>
<Slider Name="GridSizeSlider" Minimum="0.1" Maximum="2.0" Value="0.5"/>
<Label>车辆尺寸(米):</Label>
<Slider Name="VehicleSizeSlider" Minimum="0.5" Maximum="3.0" Value="1.0"/>
<Label>物体尺寸(米):</Label>
<Slider Name="ObjectSizeSlider" Minimum="0.5" Maximum="3.0" Value="1.0"/>
</StackPanel>
</GroupBox>
```
@ -181,7 +181,7 @@ private static bool IsLikelyObstacle(ModelItem item)
```csharp
public RelayCommand AutoPlanPathCommand { get; set; }
public double GridSize { get; set; } = 0.5;
public double VehicleSize { get; set; } = 1.0;
public double ObjectSize { get; set; } = 1.0;
private void ExecuteAutoPlanPath()
{

View File

@ -102,7 +102,7 @@ public class SlopeAnalyzer
- 减少路径中的急转弯和不必要的路径点
- 优化路径曲线,提供更自然的运动轨迹
- 考虑车辆转弯半径等物理约束
- 考虑物体转弯半径等物理约束
#### 2.2 多目标路径优化 📋 **[待开始]**

View File

@ -49,7 +49,7 @@ public class GridMapGenerator
{
public GridMap GenerateFromBIM(Document document, double cellSize = 0.5)
public bool[,] CreateObstacleGrid(List<ModelItem> obstacles)
public void ApplyVehicleInflation(bool[,] grid, double vehicleWidth)
public void ApplyObjectInflation(bool[,] grid, double objectWidth)
}
```
@ -58,7 +58,7 @@ public class GridMapGenerator
- 将BIM模型转换为2D网格表示
- 基于包围盒检测障碍物,标记可通行/不可通行区域
- 支持用户配置网格分辨率默认0.5m×0.5m
- 实现障碍物膨胀算法,考虑车辆尺寸
- 实现障碍物膨胀算法,考虑物体尺寸
#### 1.2 **集成RoyT.AStar算法**
@ -93,7 +93,7 @@ public List<ModelItem> GetObstacleItems(Document document)
**功能描述**
- 利用现有的CategoryAttributeManager识别墙体、柱子等障碍物
- 实现包围盒膨胀算法考虑车辆尺寸
- 实现包围盒膨胀算法考虑物体尺寸
- **简化策略**:暂时忽略门状态,视所有门为可通行(降低复杂度)
#### 1.4 **集成到现有UI**
@ -250,7 +250,7 @@ public class PathOptimizer
{
public List<Point3D> ApplyTurnPenalty(List<Point3D> path)
public PathComparisonResult ComparePaths(List<PathRoute> alternatives)
public bool ValidatePathClearance(List<Point3D> path, double vehicleWidth)
public bool ValidatePathClearance(List<Point3D> path, double objectWidth)
}
```
@ -258,7 +258,7 @@ public class PathOptimizer
- **转向惩罚机制**:为急转弯添加成本,生成更平滑路径
- **多方案对比**:提供最短距离 vs 最少转弯 vs 最低成本的路径选项
- **路径验证**:确保路径满足车辆尺寸要求
- **路径验证**:确保路径满足物体尺寸要求
- **冲突检测**:集成基础的路径可行性验证
#### 3.3 **高级可视化功能**
@ -363,7 +363,7 @@ public class HierarchicalPathPlanner // 可选实现
### 🔧 **核心技术栈**
- **路径规划算法**A*RoyT.AStar库作为核心满足效率要求
- **碰撞检测方法**:包围盒简化方法,支持车辆尺寸膨胀
- **碰撞检测方法**:包围盒简化方法,支持物体尺寸膨胀
- **网格规划方式**2.5D网格离散化,用户可配置分辨率
- **UI框架**继续使用WPF集成到现有界面
- **数据持久化**扩展现有JSON序列化机制

View File

@ -1,7 +1,7 @@
无人物流车转弯路径曲线化功能 - 实施方案
1. 目标概述
实现基于圆弧过渡Arc Fillet的路径曲线化功能替代现有的直线连接方式确保仿真系统能准确检测车辆转弯时的扫掠路径Swept Path碰撞。
实现基于圆弧过渡Arc Fillet的路径曲线化功能替代现有的直线连接方式确保仿真系统能准确检测物体转弯时的扫掠路径Swept Path碰撞。
核心原理
控制点与物理点分离:用户操作的
@ -366,9 +366,9 @@ public double ArcSamplingStep { get; set; } = 0.05;
[PathEditing]
CellSizeMeters = 0.5
MaxHeightDiffMeters = 0.35
VehicleLengthMeters = 1.0
VehicleWidthMeters = 1.0
VehicleHeightMeters = 2.0
ObjectLengthMeters = 1.0
ObjectWidthMeters = 1.0
ObjectHeightMeters = 2.0
SafetyMarginMeters = 0.05
# 路径曲线化配置

View File

@ -8,14 +8,14 @@ cell_size_meters = 0.5
# 最大高度差(米)- 楼梯、坡道可接受的高度阈值
max_height_diff_meters = 0.35
# 车辆长度(米)
vehicle_length_meters = 1.5
# 物体长度(米)
object_length_meters = 1.5
# 车辆宽度(米)
vehicle_width_meters = 1.0
# 物体宽度(米)
object_width_meters = 1.0
# 车辆高度(米)
vehicle_height_meters = 2.0
# 物体高度(米)
object_height_meters = 2.0
# 安全间隙(米)
safety_margin_meters = 0.1

View File

@ -24,19 +24,19 @@ namespace NavisworksTransport.Commands
public Point3D EndPoint { get; set; }
/// <summary>
/// 车辆长度(米)
/// 物体长度(米)
/// </summary>
public double VehicleLength { get; set; } = 1.0;
public double ObjectLength { get; set; } = 1.0;
/// <summary>
/// 车辆宽度(米)
/// 物体宽度(米)
/// </summary>
public double VehicleWidth { get; set; } = 1.0;
public double ObjectWidth { get; set; } = 1.0;
/// <summary>
/// 车辆高度(米)
/// 物体高度(米)
/// </summary>
public double VehicleHeight { get; set; } = 2.0;
public double ObjectHeight { get; set; } = 2.0;
/// <summary>
/// 安全边距(米)
@ -76,14 +76,14 @@ namespace NavisworksTransport.Commands
if (EndPoint == null)
errors.Add("终点不能为空");
if (VehicleLength <= 0 || VehicleLength > 20)
errors.Add("车辆长度必须在0-20米之间");
if (ObjectLength <= 0 || ObjectLength > 20)
errors.Add("物体长度必须在0-20米之间");
if (VehicleWidth <= 0 || VehicleWidth > 10)
errors.Add("车辆宽度必须在0-10米之间");
if (ObjectWidth <= 0 || ObjectWidth > 10)
errors.Add("物体宽度必须在0-10米之间");
if (VehicleHeight <= 0 || VehicleHeight > 10)
errors.Add("车辆高度必须在0-10米之间");
if (ObjectHeight <= 0 || ObjectHeight > 10)
errors.Add("物体高度必须在0-10米之间");
if (SafetyMargin < 0 || SafetyMargin > 5)
errors.Add("安全边距必须在0-5米之间");
@ -279,7 +279,7 @@ namespace NavisworksTransport.Commands
var actualGridSize = _parameters.GridSize > 0 ? _parameters.GridSize : -1;
LogInfo($"使用参数 - 起点: ({_parameters.StartPoint.X:F2}, {_parameters.StartPoint.Y:F2}), " +
$"终点: ({_parameters.EndPoint.X:F2}, {_parameters.EndPoint.Y:F2}), " +
$"车辆宽度: {_parameters.VehicleWidth}米, 安全边距: {_parameters.SafetyMargin}米, " +
$"物体宽度: {_parameters.ObjectWidth}米, 安全边距: {_parameters.SafetyMargin}米, " +
$"网格大小: {(actualGridSize > 0 ? $" {actualGridSize:F2}" : "")}");
// 第三阶段执行路径规划30% - 80%
@ -308,18 +308,18 @@ namespace NavisworksTransport.Commands
Type = PathPointType.EndPoint
};
// 计算车辆半径:基于长度和宽度的较大值的一半
var vehicleRadius = Math.Max(_parameters.VehicleLength, _parameters.VehicleWidth) / 2.0;
LogInfo($"使用车辆半径: {vehicleRadius}m基于车辆长度{_parameters.VehicleLength}m和宽度{_parameters.VehicleWidth}m的较大值");
// 计算物体半径:基于长度和宽度的较大值的一半
var objectRadius = Math.Max(_parameters.ObjectLength, _parameters.ObjectWidth) / 2.0;
LogInfo($"使用物体半径: {objectRadius}m基于物体长度{_parameters.ObjectLength}m和宽度{_parameters.ObjectWidth}m的较大值");
// 调用PathPlanningManager的AutoPlanPath方法执行真实的A*算法
var pathPlanTask = _pathPlanningManager.AutoPlanPath(
startPathPoint,
endPathPoint,
vehicleRadius,
objectRadius,
_parameters.SafetyMargin,
actualGridSize,
_parameters.VehicleHeight, // 使用参数中的车辆高度
_parameters.ObjectHeight, // 使用参数中的物体高度
_parameters.Strategy); // 使用参数中指定的路径策略
generatedRoute = await pathPlanTask;
@ -454,21 +454,21 @@ namespace NavisworksTransport.Commands
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="vehicleSize">车辆尺寸(应用于长度和宽度)</param>
/// <param name="objectSize">物体尺寸(应用于长度和宽度)</param>
/// <param name="pathName">路径名称</param>
/// <param name="vehicleHeight">车辆高度可选默认2.0米)</param>
/// <param name="objectHeight">物体高度可选默认2.0米)</param>
/// <param name="strategy">路径策略(可选,默认最短路径)</param>
/// <returns>自动路径规划命令实例</returns>
public static AutoPathPlanningCommand CreateQuick(Point3D startPoint, Point3D endPoint,
double vehicleSize = 1.0, string pathName = null, double vehicleHeight = 2.0, PathStrategy strategy = PathStrategy.Shortest)
double objectSize = 1.0, string pathName = null, double objectHeight = 2.0, PathStrategy strategy = PathStrategy.Shortest)
{
var parameters = new AutoPathPlanningParameters
{
StartPoint = startPoint,
EndPoint = endPoint,
VehicleLength = vehicleSize,
VehicleWidth = vehicleSize,
VehicleHeight = vehicleHeight,
ObjectLength = objectSize,
ObjectWidth = objectSize,
ObjectHeight = objectHeight,
SafetyMargin = 0.5,
GridSize = -1, // 自动选择
PathName = pathName,
@ -487,7 +487,7 @@ namespace NavisworksTransport.Commands
{
return $"自动路径规划命令 - 起点:({_parameters.StartPoint?.X:F1}, {_parameters.StartPoint?.Y:F1}), " +
$"终点:({_parameters.EndPoint?.X:F1}, {_parameters.EndPoint?.Y:F1}), " +
$"车辆尺寸:长{_parameters.VehicleLength}×宽{_parameters.VehicleWidth}×高{_parameters.VehicleHeight}米, " +
$"物体尺寸:长{_parameters.ObjectLength}×宽{_parameters.ObjectWidth}×高{_parameters.ObjectHeight}米, " +
$"状态:{Status}";
}

View File

@ -160,7 +160,7 @@ namespace NavisworksTransport.Commands
{
StartPoint = startPoint,
EndPoint = endPoint,
VehicleWidth = args.Length > 2 && args[2] is double v ? v : 1.0,
ObjectWidth = args.Length > 2 && args[2] is double v ? v : 1.0,
SafetyMargin = args.Length > 3 && args[3] is double v1 ? v1 : 0.5,
GridSize = args.Length > 4 && args[4] is double v2 ? v2 : -1,
PathName = args.Length > 5 ? args[5]?.ToString() : null,
@ -182,10 +182,10 @@ namespace NavisworksTransport.Commands
if (startPoint == null || endPoint == null)
throw new ArgumentException("起点和终点必须是 Point3D 类型");
var vehicleSize = args.Length > 2 && args[2] is double v ? v : 1.0;
var objectSize = args.Length > 2 && args[2] is double v ? v : 1.0;
var pathName = args.Length > 3 ? args[3]?.ToString() : null;
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, vehicleSize, pathName);
return AutoPathPlanningCommand.CreateQuick(startPoint, endPoint, objectSize, pathName);
});
// 注册模拟路径规划命令(保留用于测试)- 暂时注释掉避免类型推断问题

View File

@ -80,11 +80,11 @@ namespace NavisworksTransport.Commands
LastModified = DateTime.Now
};
// 设置车辆参数(从配置获取)
// 设置物体参数(从配置获取)
var config = ConfigManager.Instance.Current;
route.MaxVehicleLength = config.PathEditing.VehicleLengthMeters;
route.MaxVehicleWidth = config.PathEditing.VehicleWidthMeters;
route.MaxVehicleHeight = config.PathEditing.VehicleHeightMeters;
route.MaxObjectLength = config.PathEditing.ObjectLengthMeters;
route.MaxObjectWidth = config.PathEditing.ObjectWidthMeters;
route.MaxObjectHeight = config.PathEditing.ObjectHeightMeters;
route.SafetyMargin = config.PathEditing.SafetyMarginMeters;
// 注意TotalLength 由计算属性实时提供,无需手动设置

View File

@ -683,7 +683,7 @@ namespace NavisworksTransport.Commands
{
report.AppendLine($"⚠️ 发现 {collisions.Count} 个碰撞问题,建议采取以下措施:");
report.AppendLine(" 1. 调整路径规划,避开碰撞区域");
report.AppendLine(" 2. 检查车辆尺寸和安全边距设置");
report.AppendLine(" 2. 检查物体尺寸和安全边距设置");
report.AppendLine(" 3. 考虑增加临时障碍物标记");
report.AppendLine(" 4. 重新评估物流流程设计");

View File

@ -17,22 +17,22 @@ namespace NavisworksTransport.Commands
private readonly Document _document;
private readonly double _cellSize;
private readonly int _samplingRate;
private readonly double _vehicleRadius;
private readonly double _vehicleHeight;
private readonly double _objectRadius;
private readonly double _objectHeight;
public VoxelGridSDFTestCommand(
Document document,
double cellSize,
int samplingRate,
double vehicleRadius,
double vehicleHeight)
double objectRadius,
double objectHeight)
: base("VoxelGridSDFTest", "体素网格SDF测试", "使用签名距离场进行精确体素化")
{
_document = document ?? throw new ArgumentNullException(nameof(document));
_cellSize = cellSize;
_samplingRate = samplingRate;
_vehicleRadius = vehicleRadius;
_vehicleHeight = vehicleHeight;
_objectRadius = objectRadius;
_objectHeight = objectHeight;
}
protected override PathPlanningResult ValidateParameters()
@ -56,8 +56,8 @@ namespace NavisworksTransport.Commands
{
LogInfo("=== 开始体素网格 SDF 测试 ===");
LogInfo($"体素大小: {_cellSize}米");
LogInfo($"车辆半径: {_vehicleRadius}米");
LogInfo($"车辆高度: {_vehicleHeight}米");
LogInfo($"物体半径: {_objectRadius}米");
LogInfo($"物体高度: {_objectHeight}米");
LogInfo($"采样率: {_samplingRate}");
UpdateProgress(5, "正在获取模型空间范围...");
@ -90,8 +90,8 @@ namespace NavisworksTransport.Commands
var voxelGrid = generator.GenerateFromBIMWithSDF(
spaceBounds,
_cellSize,
_vehicleRadius,
_vehicleHeight,
_objectRadius,
_objectHeight,
allModelItems);
LogInfo($"体素网格生成完成: {voxelGrid.SizeX}×{voxelGrid.SizeY}×{voxelGrid.SizeZ} = {voxelGrid.TotalVoxels} 个体素");

View File

@ -15,24 +15,24 @@ namespace NavisworksTransport.Commands
{
private readonly Document _document;
private readonly double _cellSize;
private readonly double _vehicleRadius;
private readonly double _vehicleHeight;
private readonly double _objectRadius;
private readonly double _objectHeight;
private readonly Point3D _startPoint;
private readonly Point3D _endPoint;
public VoxelPathFindingTestCommand(
Document document,
double cellSize,
double vehicleRadius,
double vehicleHeight,
double objectRadius,
double objectHeight,
Point3D startPoint,
Point3D endPoint)
: base("VoxelPathFindingTest", "体素路径规划测试", "测试VoxelPathFinder的3D A*路径规划")
{
_document = document ?? throw new ArgumentNullException(nameof(document));
_cellSize = cellSize;
_vehicleRadius = vehicleRadius;
_vehicleHeight = vehicleHeight;
_objectRadius = objectRadius;
_objectHeight = objectHeight;
_startPoint = startPoint;
_endPoint = endPoint;
}
@ -58,8 +58,8 @@ namespace NavisworksTransport.Commands
{
LogInfo("=== 开始体素路径规划测试 ===");
LogInfo($"体素大小: {_cellSize}米");
LogInfo($"车辆半径: {_vehicleRadius}米");
LogInfo($"车辆高度: {_vehicleHeight}米");
LogInfo($"物体半径: {_objectRadius}米");
LogInfo($"物体高度: {_objectHeight}米");
LogInfo($"起点: ({_startPoint.X:F2}, {_startPoint.Y:F2}, {_startPoint.Z:F2})");
LogInfo($"终点: ({_endPoint.X:F2}, {_endPoint.Y:F2}, {_endPoint.Z:F2})");
@ -92,8 +92,8 @@ namespace NavisworksTransport.Commands
var voxelGrid = generator.GenerateFromBIMWithSDF(
spaceBounds,
_cellSize,
_vehicleRadius,
_vehicleHeight,
_objectRadius,
_objectHeight,
allModelItems);
LogInfo($"体素网格生成完成: {voxelGrid.SizeX}×{voxelGrid.SizeY}×{voxelGrid.SizeZ} = {voxelGrid.TotalVoxels:N0} 个体素");

View File

@ -89,10 +89,10 @@ namespace NavisworksTransport.Core.Animation
private static readonly Dictionary<string, List<AnimationFrame>> _animationFrameCache = new Dictionary<string, List<AnimationFrame>>(); // 动画帧缓存
private static readonly Dictionary<string, List<CollisionResult>> _collisionResultCache = new Dictionary<string, List<CollisionResult>>(); // 碰撞结果缓存
private ModelItem _animatedObject;
private bool _isVirtualVehicle = false; // 是否使用虚拟车辆
private double _virtualVehicleLength = 0; // 虚拟车辆长度(米)
private double _virtualVehicleWidth = 0; // 虚拟车辆宽度(米)
private double _virtualVehicleHeight = 0; // 虚拟车辆高度(米)
private bool _isVirtualObject = false; // 是否使用虚拟物体
private double _virtualObjectLength = 0; // 虚拟物体长度(米)
private double _virtualObjectWidth = 0; // 虚拟物体宽度(米)
private double _virtualObjectHeight = 0; // 虚拟物体高度(米)
private List<Point3D> _pathPoints;
private List<ModelItem> _manualCollisionTargets = new List<ModelItem>();
private bool _manualCollisionOverrideEnabled = false;
@ -350,7 +350,7 @@ namespace NavisworksTransport.Core.Animation
/// </summary>
public void SyncToPathStart(ModelItem item, List<Point3D> points)
{
MoveVehicleToPathStart(item, points);
MoveObjectToPathStart(item, points);
}
/// <summary>
@ -364,16 +364,16 @@ namespace NavisworksTransport.Core.Animation
var doc = NavisApplication.ActiveDocument;
if (doc == null) return;
// 🔥 支持虚拟车辆的恢复
// 🔥 支持虚拟物体的恢复
ModelItem objectToRestore = null;
bool isVirtual = _isVirtualVehicle;
bool isVirtual = _isVirtualObject;
if (isVirtual)
{
objectToRestore = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
objectToRestore = VirtualObjectManager.Instance.CurrentVirtualObject;
if (objectToRestore == null)
{
LogManager.Warning("[归位] 虚拟车辆未激活,跳过恢复");
LogManager.Warning("[归位] 虚拟物体未激活,跳过恢复");
return;
}
}
@ -403,7 +403,7 @@ namespace NavisworksTransport.Core.Animation
originalBoundingBox.Min.Z
);
string objectName = isVirtual ? "虚拟车辆" : objectToRestore.DisplayName;
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
}
catch (Exception ex)
@ -429,18 +429,18 @@ namespace NavisworksTransport.Core.Animation
public void SetAnimatedObject(ModelItem animatedObject)
{
_animatedObject = animatedObject;
_isVirtualVehicle = (animatedObject == null);
_isVirtualObject = (animatedObject == null);
}
/// <summary>
/// 设置虚拟车辆参数(批处理专用)
/// 设置虚拟物体参数(批处理专用)
/// </summary>
public void SetVirtualVehicleParameters(bool isVirtualVehicle, double length, double width, double height)
public void SetVirtualObjectParameters(bool isVirtualObject, double length, double width, double height)
{
_isVirtualVehicle = isVirtualVehicle;
_virtualVehicleLength = length;
_virtualVehicleWidth = width;
_virtualVehicleHeight = height;
_isVirtualObject = isVirtualObject;
_virtualObjectLength = length;
_virtualObjectWidth = width;
_virtualObjectHeight = height;
}
/// <summary>
@ -561,18 +561,18 @@ namespace NavisworksTransport.Core.Animation
}
/// <summary>
/// 将车辆移动到路径起点
/// 将物体移动到路径起点
/// </summary>
/// <param name="animatedObject">动画对象可选如果不提供则使用当前的_animatedObject</param>
/// <param name="pathPoints">路径点可选如果不提供则使用当前的_pathPoints</param>
public void MoveVehicleToPathStart(ModelItem animatedObject = null, List<Point3D> pathPoints = null)
public void MoveObjectToPathStart(ModelItem animatedObject = null, List<Point3D> pathPoints = null)
{
try
{
// 🔥 重要先恢复物体到原始状态CAD位置和原始朝向确保每次计算都从相同的状态开始
// 这样可以避免之前旋转导致的包围盒尺寸计算不准确的问题
// 注意:也要处理虚拟车辆_isVirtualVehicle为true时_animatedObject可能为null
if (_animatedObject != null || _isVirtualVehicle)
// 注意:也要处理虚拟物体_isVirtualObject为true时_animatedObject可能为null
if (_animatedObject != null || _isVirtualObject)
{
RestoreObjectToCADPosition();
LogManager.Info($"[移动到起点] 已恢复物体到原始状态, _currentYaw={_currentYaw * 180 / Math.PI:F2}°");
@ -586,7 +586,7 @@ namespace NavisworksTransport.Core.Animation
_originalCenter = animatedObject.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z);
// 统一逻辑:从当前 Transform 中提取实际朝向(无论虚拟车辆还是普通物体)
// 统一逻辑:从当前 Transform 中提取实际朝向(无论虚拟物体还是普通物体)
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
}
@ -636,19 +636,19 @@ namespace NavisworksTransport.Core.Animation
if (_route?.PathType == PathType.Hoisting)
{
// 吊装路径:第一个路径点(起吊点)是地面位置,物体底面应该在这里
// 不需要向下移动车辆高度
// 不需要向下移动物体高度
LogManager.Debug($"[移动到起点] 吊装路径起吊点是地面位置物体底面Z={startPosition.Z:F2}");
}
else if (_route?.PathType == PathType.Rail)
{
// 空轨路径:路径点是悬挂点,物体悬挂在下方
double vehicleHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
startPosition = new Point3D(startPosition.X, startPosition.Y, startPosition.Z - vehicleHeight);
LogManager.Debug($"[移动到起点] 空轨路径调整: 悬挂点Z={_pathPoints[0].Z:F2}, 物体底面Z={startPosition.Z:F2}, 物体高度={vehicleHeight:F2}");
double objectHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
startPosition = new Point3D(startPosition.X, startPosition.Y, startPosition.Z - objectHeight);
LogManager.Debug($"[移动到起点] 空轨路径调整: 悬挂点Z={_pathPoints[0].Z:F2}, 物体底面Z={startPosition.Z:F2}, 物体高度={objectHeight:F2}");
}
else
{
// 地面路径points[0] 是地面位置,车辆底面应该在这里
// 地面路径points[0] 是地面位置,物体底面应该在这里
LogManager.Debug($"[移动到起点] 地面路径: pos=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2})");
}
@ -680,16 +680,16 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info($"实际物体位置: X={actualCenter.X:F2}, Y={actualCenter.Y:F2}, Z={actualCenter.Z:F2}");
LogManager.Info($"实际物体朝向: {actualYaw * 180 / Math.PI:F2}度");
}
else if (_isVirtualVehicle)
else if (_isVirtualObject)
{
var virtualVehicle = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
if (virtualVehicle != null)
var virtualObject = VirtualObjectManager.Instance.CurrentVirtualObject;
if (virtualObject != null)
{
var bbox = virtualVehicle.BoundingBox();
var bbox = virtualObject.BoundingBox();
var actualCenter = bbox.Center;
var actualYaw = ModelItemTransformHelper.GetYawFromTransform(virtualVehicle.Transform);
LogManager.Info($"虚拟车辆位置: X={actualCenter.X:F2}, Y={actualCenter.Y:F2}, Z={actualCenter.Z:F2}");
LogManager.Info($"虚拟车辆朝向: {actualYaw * 180 / Math.PI:F2}度");
var actualYaw = ModelItemTransformHelper.GetYawFromTransform(virtualObject.Transform);
LogManager.Info($"虚拟物体位置: X={actualCenter.X:F2}, Y={actualCenter.Y:F2}, Z={actualCenter.Z:F2}");
LogManager.Info($"虚拟物体朝向: {actualYaw * 180 / Math.PI:F2}度");
}
}
}
@ -712,7 +712,7 @@ namespace NavisworksTransport.Core.Animation
if (_animatedObject != null && _route != null && _route.Points != null && _route.Points.Count > 0)
{
var pathPoints = _route.Points.Select(p => p.Position).ToList();
MoveVehicleToPathStart(_animatedObject, pathPoints);
MoveObjectToPathStart(_animatedObject, pathPoints);
LogManager.Info("[预计算] 物体已移动到路径起点");
}
@ -849,7 +849,7 @@ namespace NavisworksTransport.Core.Animation
LogManager.Info(spatialIndexManager.GetStatistics());
// 🔥 优化使用AABB查询代替球形查询查询范围更精确
// 不再预计算球形半径,改为每帧计算车辆AABB + 安全间隙的搜索AABB
// 不再预计算球形半径,改为每帧计算物体AABB + 安全间隙的搜索AABB
LogManager.Info($"空间查询使用AABB模式安全间隙: {_safetyMargin:F4}模型单位");
}
@ -889,14 +889,14 @@ namespace NavisworksTransport.Core.Animation
// 计算朝向(使用当前线段的方向)
yawRadians = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
// 吊装路径所有线段都使用水平吊运方向与MoveVehicleToPathStart保持一致
// 吊装路径所有线段都使用水平吊运方向与MoveObjectToPathStart保持一致
if (_route.PathType == PathType.Hoisting)
{
yawRadians = Math.Atan2(_pathPoints[2].Y - _pathPoints[1].Y, _pathPoints[2].X - _pathPoints[1].X);
}
// 🔥 空中路径:根据路径类型和线段索引调整物体位置
double vehicleHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
double objectHeight = _animatedObject.BoundingBox().Max.Z - _animatedObject.BoundingBox().Min.Z;
if (_route.PathType == PathType.Hoisting)
{
@ -919,25 +919,25 @@ namespace NavisworksTransport.Core.Animation
if (segmentIndex == firstSegment)
{
// 起吊段:从地面逐渐上升到悬挂点-车辆高度
// 起吊段:从地面逐渐上升到悬挂点-物体高度
// 进度0时地面物体底面在地面不向下移动
// 进度1时悬挂点物体顶面在悬挂点物体底面=悬挂点-车辆高度
double heightOffset = segmentProgress * vehicleHeight;
// 进度1时悬挂点物体顶面在悬挂点物体底面=悬挂点-物体高度
double heightOffset = segmentProgress * objectHeight;
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - heightOffset);
LogManager.Debug($"[吊装路径-起吊段] 进度={segmentProgress:F2}, 高度偏移={heightOffset:F2}, 物体底面Z={framePosition.Z:F2}");
}
else if (segmentIndex > firstSegment && segmentIndex < lastSegment)
{
// 平移段(中间的所有线段):全程都是悬挂点,物体顶面在悬挂点
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - vehicleHeight);
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - objectHeight);
LogManager.Debug($"[吊装路径-平移段] 线段{segmentIndex}, 进度={segmentProgress:F2}, 物体底面Z={framePosition.Z:F2}");
}
else if (segmentIndex == lastSegment)
{
// 下降段:从悬挂点-车辆高度逐渐下降到地面
// 进度0时悬挂点物体顶面在悬挂点物体底面=悬挂点-车辆高度
// 下降段:从悬挂点-物体高度逐渐下降到地面
// 进度0时悬挂点物体顶面在悬挂点物体底面=悬挂点-物体高度
// 进度1时地面物体底面在地面不向下移动
double heightOffset = (1 - segmentProgress) * vehicleHeight;
double heightOffset = (1 - segmentProgress) * objectHeight;
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - heightOffset);
LogManager.Debug($"[吊装路径-下降段] 进度={segmentProgress:F2}, 高度偏移={heightOffset:F2}, 物体底面Z={framePosition.Z:F2}");
}
@ -946,8 +946,8 @@ namespace NavisworksTransport.Core.Animation
{
// 空轨路径:路径点是悬挂点,物体悬挂在下方
// 物体顶面应该在路径点,所以物体底面 = 路径点 - 物体高度
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - vehicleHeight);
LogManager.Debug($"[空轨路径] 调整物体位置: 悬挂点Z={framePosition.Z + vehicleHeight:F2}, 物体底面Z={framePosition.Z:F2}");
framePosition = new Point3D(framePosition.X, framePosition.Y, framePosition.Z - objectHeight);
LogManager.Debug($"[空轨路径] 调整物体位置: 悬挂点Z={framePosition.Z + objectHeight:F2}, 物体底面Z={framePosition.Z:F2}");
}
}
else
@ -994,30 +994,30 @@ namespace NavisworksTransport.Core.Animation
// 虚拟碰撞检测
// 计算车辆包围盒相对于framePosition的偏移
var currentVehicleBoundingBox = _animatedObject.BoundingBox();
// 计算物体包围盒相对于framePosition的偏移
var currentObjectBoundingBox = _animatedObject.BoundingBox();
var originalCenter = new Point3D(
(currentVehicleBoundingBox.Min.X + currentVehicleBoundingBox.Max.X) / 2,
(currentVehicleBoundingBox.Min.Y + currentVehicleBoundingBox.Max.Y) / 2,
(currentVehicleBoundingBox.Min.Z + currentVehicleBoundingBox.Max.Z) / 2
(currentObjectBoundingBox.Min.X + currentObjectBoundingBox.Max.X) / 2,
(currentObjectBoundingBox.Min.Y + currentObjectBoundingBox.Max.Y) / 2,
(currentObjectBoundingBox.Min.Z + currentObjectBoundingBox.Max.Z) / 2
);
// 计算偏移量当前包围盒中心到framePosition的偏移
var offsetX = framePosition.X - originalCenter.X;
var offsetY = framePosition.Y - originalCenter.Y;
var offsetZ = framePosition.Z - currentVehicleBoundingBox.Min.Z; // Z方向framePosition是底面originalCenter是中心
var offsetZ = framePosition.Z - currentObjectBoundingBox.Min.Z; // Z方向framePosition是底面originalCenter是中心
// 创建新的包围盒将当前包围盒移动到framePosition
var virtualBoundingBox = new BoundingBox3D(
new Point3D(
currentVehicleBoundingBox.Min.X + offsetX,
currentVehicleBoundingBox.Min.Y + offsetY,
currentObjectBoundingBox.Min.X + offsetX,
currentObjectBoundingBox.Min.Y + offsetY,
framePosition.Z // 底面Z坐标
),
new Point3D(
currentVehicleBoundingBox.Max.X + offsetX,
currentVehicleBoundingBox.Max.Y + offsetY,
framePosition.Z + (currentVehicleBoundingBox.Max.Z - currentVehicleBoundingBox.Min.Z) // 保持高度
currentObjectBoundingBox.Max.X + offsetX,
currentObjectBoundingBox.Max.Y + offsetY,
framePosition.Z + (currentObjectBoundingBox.Max.Z - currentObjectBoundingBox.Min.Z) // 保持高度
)
);
@ -1034,7 +1034,7 @@ namespace NavisworksTransport.Core.Animation
else
{
// 🔥 优化使用AABB查询代替球形查询避免球体扩大的无效范围
// 查询AABB = 车辆AABB + 安全间隙扩展
// 查询AABB = 物体AABB + 安全间隙扩展
var searchBounds = new BoundingBox3D(
new Point3D(
virtualBoundingBox.Min.X - _safetyMargin,
@ -1088,12 +1088,12 @@ namespace NavisworksTransport.Core.Animation
(virtualBoundingBox.Min.Y + virtualBoundingBox.Max.Y + colliderBox.Min.Y + colliderBox.Max.Y) / 4,
(virtualBoundingBox.Min.Z + virtualBoundingBox.Max.Z + colliderBox.Min.Z + colliderBox.Max.Z) / 4
),
// 🔥 重要:记录虚拟车辆的包围盒中心,而不是底面中心
// 🔥 重要:记录虚拟物体的包围盒中心,而不是底面中心
// 原因:
// 1. framePosition是路径点代表车辆在地面上的位置(底面中心)
// 1. framePosition是路径点代表物体在地面上的位置(底面中心)
// 2. ClashDetective移动逻辑使用包围盒中心进行定位
// 3. 如果记录底面中心ClashDetective会把包围盒中心移动到底面位置导致车辆下沉半个高度
// 4. 记录包围盒中心可以确保ClashDetective移动后的位置与预计算时的虚拟车辆位置一致
// 3. 如果记录底面中心ClashDetective会把包围盒中心移动到底面位置导致物体下沉半个高度
// 4. 记录包围盒中心可以确保ClashDetective移动后的位置与预计算时的虚拟物体位置一致
Item1Position = new Point3D(
framePosition.X,
framePosition.Y,
@ -1355,7 +1355,7 @@ namespace NavisworksTransport.Core.Animation
if (distanceToStart > 0.1 || yawDiff > 0.01)
{
LogManager.Info($"[动画开始] 物体不在起点,重置到起点: 距离={distanceToStart:F2}m, 角度差={yawDiff * 180 / Math.PI:F2}°");
MoveVehicleToPathStart();
MoveObjectToPathStart();
}
}
@ -1423,10 +1423,10 @@ namespace NavisworksTransport.Core.Animation
{
var firstFrame = _animationFrames[0];
LogManager.Debug($"[动画开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
LogManager.Debug($"[动画开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualObject={_isVirtualObject}");
// 🔥 确保物体在起点位置和朝向
// 注意MoveVehicleToPathStart 已在 StartAnimation 中调用,这里只是日志记录
// 注意MoveObjectToPathStart 已在 StartAnimation 中调用,这里只是日志记录
LogManager.Debug($"[动画开始] 物体应在起点: pos=({firstFrame.Position.X:F2},{firstFrame.Position.Y:F2}), yaw={firstFrame.YawRadians:F3}rad");
}
else
@ -1446,12 +1446,12 @@ namespace NavisworksTransport.Core.Animation
ModelHighlightHelper.ClearCollisionHighlights();
LogManager.Debug("[动画开始] 已清除所有碰撞高亮");
// 不再高亮车辆对象,保持原始外观(客户要求)
// 不再高亮物体对象,保持原始外观(客户要求)
// if (_animatedObject != null)
// {
// var vehicleItems = new List<ModelItem> { _animatedObject };
// ModelHighlightHelper.HighlightItems(ModelHighlightHelper.AnimatedObjectCategory, vehicleItems);
// LogManager.Debug("[动画开始] 已高亮车辆对象为绿色");
// var objectItems = new List<ModelItem> { _animatedObject };
// ModelHighlightHelper.HighlightItems(ModelHighlightHelper.AnimatedObjectCategory, objectItems);
// LogManager.Debug("[动画开始] 已高亮物体对象为绿色");
// }
// 启动动画播放
@ -1572,7 +1572,7 @@ namespace NavisworksTransport.Core.Animation
_pausedProgress = 0.0; // 重置暂停进度
_currentFrameIndex = 0; // 重置帧索引
// 清除所有高亮(包括车辆和碰撞)
// 清除所有高亮(包括物体和碰撞)
ModelHighlightHelper.ClearAllHighlights();
LogManager.Info("动画停止:已清除所有高亮");
@ -1658,7 +1658,7 @@ namespace NavisworksTransport.Core.Animation
// 直接设置为完成状态,避免中间状态切换
SetState(AnimationState.Finished);
// 清除所有高亮(包括车辆和碰撞)
// 清除所有高亮(包括物体和碰撞)
ModelHighlightHelper.ClearAllHighlights();
LogManager.Info("动画完成:已清除所有高亮");
@ -1690,12 +1690,12 @@ namespace NavisworksTransport.Core.Animation
_detectionTolerance,
_currentRouteId,
_animatedObject,
_isVirtualVehicle,
_isVirtualObject,
_animationFrameRate,
_animationDuration,
_virtualVehicleLength,
_virtualVehicleWidth,
_virtualVehicleHeight,
_virtualObjectLength,
_virtualObjectWidth,
_virtualObjectHeight,
_pathPoints
);
}
@ -1820,7 +1820,7 @@ namespace NavisworksTransport.Core.Animation
// 第二步:如果已经生成了动画路径,则重新对齐到起点(带旋转)
if (_animationFrames != null && _animationFrames.Count > 0)
{
MoveVehicleToPathStart();
MoveObjectToPathStart();
LogManager.Info("已重新对齐到路径起点");
}
}
@ -2821,15 +2821,15 @@ namespace NavisworksTransport.Core.Animation
/// <param name="durationSeconds">动画持续时间(秒)</param>
/// <param name="pathName">路径名称</param>
/// <param name="routeId">路由ID</param>
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0, string pathName = "未知路径", string routeId = null, bool isVirtualVehicle = false,
double virtualVehicleLength = 0, double virtualVehicleWidth = 0, double virtualVehicleHeight = 0, double safetyMargin = 0)
public void CreateAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0, string pathName = "未知路径", string routeId = null, bool isVirtualObject = false,
double virtualObjectLength = 0, double virtualObjectWidth = 0, double virtualObjectHeight = 0, double safetyMargin = 0)
{
_pathName = pathName;
_currentRouteId = routeId;
_isVirtualVehicle = isVirtualVehicle; // 设置是否使用虚拟车辆
_virtualVehicleLength = virtualVehicleLength; // 模型单位
_virtualVehicleWidth = virtualVehicleWidth; // 模型单位
_virtualVehicleHeight = virtualVehicleHeight; // 模型单位
_isVirtualObject = isVirtualObject; // 设置是否使用虚拟物体
_virtualObjectLength = virtualObjectLength; // 模型单位
_virtualObjectWidth = virtualObjectWidth; // 模型单位
_virtualObjectHeight = virtualObjectHeight; // 模型单位
_safetyMargin = safetyMargin; // 模型单位
_pathPoints = pathPoints;
_animatedObject = animatedObject;
@ -2841,14 +2841,14 @@ namespace NavisworksTransport.Core.Animation
_originalCenter = animatedObject.BoundingBox().Center;
_currentPosition = new Point3D(_originalCenter.X, _originalCenter.Y, animatedObject.BoundingBox().Min.Z);
// 保持当前的 _currentYaw因为物体可能已经被 MoveVehicleToPathStart 旋转)
// 保持当前的 _currentYaw因为物体可能已经被 MoveObjectToPathStart 旋转)
// 不要从 Transform 中提取,因为 Transform 返回的是原始值,不是当前值
LogManager.Debug($"[CreateAnimation] 保持_currentYaw={_currentYaw * 180 / Math.PI:F2}度不变, _isVirtualVehicle={_isVirtualVehicle}");
LogManager.Debug($"[CreateAnimation] 保持_currentYaw={_currentYaw * 180 / Math.PI:F2}度不变, _isVirtualObject={_isVirtualObject}");
}
// 设置动画参数并预计算动画帧
// 注意物体应该在调用CreateAnimation之前已经通过MoveVehicleToPathStart旋转到起点
LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualVehicle={_isVirtualVehicle}");
// 注意物体应该在调用CreateAnimation之前已经通过MoveObjectToPathStart旋转到起点
LogManager.Debug($"[CreateAnimation开始] _currentYaw之前={_currentYaw * 180 / Math.PI:F2}度, _isVirtualObject={_isVirtualObject}");
SetupAnimation(animatedObject, durationSeconds, _route);
LogManager.Debug($"[CreateAnimation结束] _currentYaw之后={_currentYaw * 180 / Math.PI:F2}度");
// 设置动画状态为Ready动画已生成可以播放
@ -2870,7 +2870,7 @@ namespace NavisworksTransport.Core.Animation
try
{
// 重新计算并应用朝向
MoveVehicleToPathStart();
MoveObjectToPathStart();
LogManager.Info($"[角度修正] 物体角度已更新: {_objectRotationCorrection:F1}°");
}
catch (Exception ex)
@ -2942,7 +2942,7 @@ namespace NavisworksTransport.Core.Animation
}
else
{
// 无碰撞:清除碰撞高亮(保留车辆高亮)
// 无碰撞:清除碰撞高亮(保留物体高亮)
ModelHighlightHelper.ClearCategory(ModelHighlightHelper.PrecomputeCollisionResultsCategory);
LogManager.Debug($"[高亮状态] 帧{_currentFrameIndex}: 清除碰撞高亮");
}
@ -3143,10 +3143,10 @@ namespace NavisworksTransport.Core.Animation
sb.Append($"|DetectionTolerance:{_detectionTolerance:F4}");
sb.Append("|");
// 包含虚拟车辆尺寸(确保车辆尺寸改变时重新检测)
// 使用传入的车辆尺寸参数而不是从ConfigManager读取
var vehicleSizeString = $"Vehicle:{_virtualVehicleLength:F2}x{_virtualVehicleWidth:F2}x{_virtualVehicleHeight:F2}";
sb.Append(vehicleSizeString);
// 包含虚拟物体尺寸(确保物体尺寸改变时重新检测)
// 使用传入的物体尺寸参数而不是从ConfigManager读取
var objectSizeString = $"Object:{_virtualObjectLength:F2}x{_virtualObjectWidth:F2}x{_virtualObjectHeight:F2}";
sb.Append(objectSizeString);
sb.Append("|");
// 包含角度修正(确保角度修正改变时重新检测)

View File

@ -134,9 +134,9 @@ namespace NavisworksTransport.Core.Animation
/// <param name="taskName">任务名称</param>
/// <param name="pathPoints">路径点集合</param>
/// <param name="duration">动画持续时间</param>
/// <param name="vehicle">运输车辆</param>
/// <param name="movingObject">运输物体</param>
/// <returns>任务ID如果创建失败返回null</returns>
public string CreateTransportTask(string taskName, List<Point3D> pathPoints, TimeSpan duration, ModelItem vehicle)
public string CreateTransportTask(string taskName, List<Point3D> pathPoints, TimeSpan duration, ModelItem movingObject)
{
try
{
@ -152,9 +152,9 @@ namespace NavisworksTransport.Core.Animation
return null;
}
if (vehicle == null)
if (movingObject== null)
{
LogManager.Warning("未指定运输车辆,无法创建任务");
LogManager.Warning("未指定运输物体,无法创建任务");
return null;
}
@ -165,9 +165,11 @@ namespace NavisworksTransport.Core.Animation
task.DisplayName = $"运输任务:{taskName}";
LogManager.Info($"设置任务显示名称: {task.DisplayName}");
// 关联运输车辆
var modelItems = new ModelItemCollection();
modelItems.Add(vehicle);
// 关联运输物体
var modelItems = new ModelItemCollection
{
movingObject
};
task.Selection.CopyFrom(modelItems);
// 将任务添加到 TimeLiner - 使用正确的API方法

View File

@ -398,26 +398,26 @@ namespace NavisworksTransport.Core
// 获取运动物体
ModelItem animatedObject = null;
bool isVirtualVehicle = item.IsVirtualVehicle;
bool isVirtualObject = item.IsVirtualObject;
if (isVirtualVehicle)
if (isVirtualObject)
{
// 使用现有的虚拟车辆,不创建和清理
// ShowVirtualVehicle 会显示虚拟车辆并更新尺寸(如果已存在)
VirtualVehicleManager.Instance.ShowVirtualVehicle(
item.VirtualVehicleLength,
item.VirtualVehicleWidth,
item.VirtualVehicleHeight
// 使用现有的虚拟物体,不创建和清理
// ShowVirtualObject 会显示虚拟物体并更新尺寸(如果已存在)
VirtualObjectManager.Instance.ShowVirtualObject(
item.VirtualObjectLength,
item.VirtualObjectWidth,
item.VirtualObjectHeight
);
animatedObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle;
animatedObject = VirtualObjectManager.Instance.CurrentVirtualObject;
if (animatedObject == null)
{
throw new InvalidOperationException("虚拟车辆显示失败");
throw new InvalidOperationException("虚拟物体显示失败");
}
LogManager.Info($"[批处理] 使用虚拟车辆: {animatedObject.DisplayName}");
LogManager.Info($"[批处理] 使用虚拟物体: {animatedObject.DisplayName}");
}
else
{
@ -516,19 +516,19 @@ namespace NavisworksTransport.Core
FrameRate = item.FrameRate,
DurationSeconds = item.DurationSeconds,
DetectionToleranceMeters = item.DetectionToleranceMeters,
VirtualVehicleLength = item.VirtualVehicleLength,
VirtualVehicleWidth = item.VirtualVehicleWidth,
VirtualVehicleHeight = item.VirtualVehicleHeight,
VirtualObjectLength = item.VirtualObjectLength,
VirtualObjectWidth = item.VirtualObjectWidth,
VirtualObjectHeight = item.VirtualObjectHeight,
CollisionDetectionEnabled = true,
ReportGenerationEnabled = true
};
var frames = _processor.PrecomputeFrames(
pathRoute,
animatedObject,
isVirtualVehicle,
config.VirtualVehicleLength,
config.VirtualVehicleWidth,
config.VirtualVehicleHeight,
isVirtualObject,
config.VirtualObjectLength,
config.VirtualObjectWidth,
config.VirtualObjectHeight,
config.FrameRate,
config.DurationSeconds,
config.DetectionToleranceMeters,
@ -555,12 +555,12 @@ namespace NavisworksTransport.Core
pathRoute.Name,
pathRoute.Id,
animatedObject,
isVirtualVehicle,
isVirtualObject,
item.FrameRate,
item.DurationSeconds,
item.VirtualVehicleLength,
item.VirtualVehicleWidth,
item.VirtualVehicleHeight,
item.VirtualObjectLength,
item.VirtualObjectWidth,
item.VirtualObjectHeight,
pathPoints
);

View File

@ -26,10 +26,10 @@ namespace NavisworksTransport.Core.Collision
public List<AnimationFrame> PrecomputeFrames(
PathRoute route,
ModelItem animatedObject,
bool isVirtualVehicle,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
bool isVirtualObject,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
int frameRate,
double duration,
double detectionGap,
@ -43,7 +43,7 @@ namespace NavisworksTransport.Core.Collision
// 设置动画管理器参数但不触发UI操作
_animationManager.SetRoute(route);
_animationManager.SetAnimatedObject(animatedObject);
_animationManager.SetVirtualVehicleParameters(isVirtualVehicle, virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight);
_animationManager.SetVirtualObjectParameters(isVirtualObject, virtualObjectLength, virtualObjectWidth, virtualObjectHeight);
_animationManager.SetAnimationParameters(frameRate, duration, detectionGap);
_animationManager.SetObjectRotationCorrectionDirect(objectRotationCorrection);
@ -75,12 +75,12 @@ namespace NavisworksTransport.Core.Collision
string pathName,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null)
{
try
@ -93,12 +93,12 @@ namespace NavisworksTransport.Core.Collision
detectionGap,
routeId,
animatedObject,
isVirtualVehicle,
isVirtualObject,
frameRate,
duration,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
pathPoints
);

View File

@ -222,8 +222,8 @@ namespace NavisworksTransport
/// </summary>
private void SaveClashDetectiveResultToDatabase(string routeId, List<CollisionResult> clashResults,
int frameRate, double duration, double detectionGap,
ModelItem animatedObject, bool isVirtualVehicle,
double virtualVehicleLength, double virtualVehicleWidth, double virtualVehicleHeight,
ModelItem animatedObject, bool isVirtualObject,
double virtualObjectLength, double virtualObjectWidth, double virtualObjectHeight,
int precomputedCollisionCount = 0)
{
try
@ -233,24 +233,24 @@ namespace NavisworksTransport
{
// 获取动画对象名称和路径信息
string animatedObjectName = "未知对象";
int? vehicleModelIndex = null;
string vehiclePathId = null;
int? objectModelIndex = null;
string objectPathId = null;
if (!isVirtualVehicle && animatedObject != null)
if (!isVirtualObject && animatedObject != null)
{
animatedObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(animatedObject);
// 获取真实车辆的 PathId 信息
// 获取真实物体的 PathId 信息
var pathId = Application.ActiveDocument.Models.CreatePathId(animatedObject);
vehicleModelIndex = pathId.ModelIndex;
vehiclePathId = pathId.PathId;
objectModelIndex = pathId.ModelIndex;
objectPathId = pathId.PathId;
}
else if (isVirtualVehicle)
else if (isVirtualObject)
{
animatedObjectName = "虚拟车辆";
animatedObjectName = "虚拟物体";
}
// 打印虚拟车辆尺寸(用于调试)
LogManager.Info($"[SaveClashDetectiveResultToDatabase] IsVirtualVehicle={isVirtualVehicle}, 虚拟车辆尺寸: Length={virtualVehicleLength:F2}m, Width={virtualVehicleWidth:F2}m, Height={virtualVehicleHeight:F2}m");
// 打印虚拟物体尺寸(用于调试)
LogManager.Info($"[SaveClashDetectiveResultToDatabase] IsVirtualObject={isVirtualObject}, 虚拟物体尺寸: Length={virtualObjectLength:F2}m, Width={virtualObjectWidth:F2}m, Height={virtualObjectHeight:F2}m");
var record = new ClashDetectiveResultRecord
{
@ -263,18 +263,18 @@ namespace NavisworksTransport
Duration = duration,
DetectionGap = detectionGap,
AnimatedObjectName = animatedObjectName,
IsVirtualVehicle = isVirtualVehicle,
VehicleModelIndex = vehicleModelIndex,
VehiclePathId = vehiclePathId,
VirtualVehicleLength = virtualVehicleLength,
VirtualVehicleWidth = virtualVehicleWidth,
VirtualVehicleHeight = virtualVehicleHeight,
IsVirtualObject = isVirtualObject,
ObjectModelIndex = objectModelIndex,
ObjectPathId = objectPathId,
VirtualObjectLength = virtualObjectLength,
VirtualObjectWidth = virtualObjectWidth,
VirtualObjectHeight = virtualObjectHeight,
CreatedAt = DateTime.Now
};
var resultId = pathDatabase.SaveClashDetectiveResult(record);
LogManager.Info($"ClashDetective结果已保存到数据库Id={resultId}, IsVirtualVehicle={isVirtualVehicle}");
LogManager.Info($"ClashDetective结果已保存到数据库Id={resultId}, IsVirtualObject={isVirtualObject}");
// 保存被撞物体信息只保存Item2不保存车辆Item1
// 保存被撞物体信息只保存Item2不保存物体Item1
// 同时保存碰撞时运动物体的位置和朝向,用于还原碰撞场景
var collisionObjects = new List<ClashDetectiveCollisionObjectRecord>();
@ -371,8 +371,8 @@ namespace NavisworksTransport
// 1. 从数据库读取测试信息添加JOIN获取PathName
var testInfoSql = @"
SELECT cdr.Id, pr.Name AS PathName, cdr.RouteId, cdr.IsVirtualVehicle, cdr.VehicleModelIndex, cdr.VehiclePathId,
cdr.VirtualVehicleLength, cdr.VirtualVehicleWidth, cdr.VirtualVehicleHeight
SELECT cdr.Id, pr.Name AS PathName, cdr.RouteId, cdr.IsVirtualObject, cdr.ObjectModelIndex, cdr.ObjectPathId,
cdr.VirtualObjectLength, cdr.VirtualObjectWidth, cdr.VirtualObjectHeight
FROM ClashDetectiveResults cdr
INNER JOIN PathRoutes pr ON cdr.RouteId = pr.Id
WHERE cdr.TestName = @testName
@ -391,12 +391,12 @@ namespace NavisworksTransport
Id = Convert.ToInt32(reader["Id"]),
PathName = reader["PathName"].ToString(),
RouteId = reader["RouteId"]?.ToString(),
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
VehicleModelIndex = reader["VehicleModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["VehicleModelIndex"]) : (int?)null,
VehiclePathId = reader["VehiclePathId"] != DBNull.Value ? reader["VehiclePathId"].ToString() : null,
VirtualVehicleLength = reader["VirtualVehicleLength"] != DBNull.Value ? Convert.ToDouble(reader["VirtualVehicleLength"]) : 0.0,
VirtualVehicleWidth = reader["VirtualVehicleWidth"] != DBNull.Value ? Convert.ToDouble(reader["VirtualVehicleWidth"]) : 0.0,
VirtualVehicleHeight = reader["VirtualVehicleHeight"] != DBNull.Value ? Convert.ToDouble(reader["VirtualVehicleHeight"]) : 0.0
IsVirtualObject = Convert.ToBoolean(reader["IsVirtualObject"]),
ObjectModelIndex = reader["ObjectModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ObjectModelIndex"]) : (int?)null,
ObjectPathId = reader["ObjectPathId"] != DBNull.Value ? reader["ObjectPathId"].ToString() : null,
VirtualObjectLength = reader["VirtualObjectLength"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectLength"]) : 0.0,
VirtualObjectWidth = reader["VirtualObjectWidth"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectWidth"]) : 0.0,
VirtualObjectHeight = reader["VirtualObjectHeight"] != DBNull.Value ? Convert.ToDouble(reader["VirtualObjectHeight"]) : 0.0
};
}
}
@ -408,46 +408,46 @@ namespace NavisworksTransport
return null;
}
// 2. 重建车辆对象
ModelItem vehicleObject = null;
if (testInfo.IsVirtualVehicle)
// 2. 重建物体对象
ModelItem objectObject = null;
if (testInfo.IsVirtualObject)
{
// 显示虚拟车辆
// 显示虚拟物体
var modelToMeters = UnitsConverter.GetUnitsToMetersConversionFactor();
VirtualVehicleManager.Instance.ShowVirtualVehicle(
testInfo.VirtualVehicleLength * modelToMeters,
testInfo.VirtualVehicleWidth * modelToMeters,
testInfo.VirtualVehicleHeight * modelToMeters
VirtualObjectManager.Instance.ShowVirtualObject(
testInfo.VirtualObjectLength * modelToMeters,
testInfo.VirtualObjectWidth * modelToMeters,
testInfo.VirtualObjectHeight * modelToMeters
);
vehicleObject = VirtualVehicleManager.Instance.CurrentVirtualVehicle
?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 显示虚拟车辆失败");
objectObject = VirtualObjectManager.Instance.CurrentVirtualObject
?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 显示虚拟物体失败");
}
else if (testInfo.VehicleModelIndex.HasValue && !string.IsNullOrEmpty(testInfo.VehiclePathId))
else if (testInfo.ObjectModelIndex.HasValue && !string.IsNullOrEmpty(testInfo.ObjectPathId))
{
// 通过 PathId 查找真实车辆
// 通过 PathId 查找真实物体
try
{
var pathIdObj = new Autodesk.Navisworks.Api.DocumentParts.ModelItemPathId
{
ModelIndex = testInfo.VehicleModelIndex.Value,
PathId = testInfo.VehiclePathId
ModelIndex = testInfo.ObjectModelIndex.Value,
PathId = testInfo.ObjectPathId
};
vehicleObject = Application.ActiveDocument.Models.ResolvePathId(pathIdObj) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过 PathId 找到车辆对象: ModelIndex={testInfo.VehicleModelIndex}, PathId={testInfo.VehiclePathId}");
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已找到真实车辆: {ModelItemAnalysisHelper.GetSafeDisplayName(vehicleObject)}");
objectObject = Application.ActiveDocument.Models.ResolvePathId(pathIdObj) ?? throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过 PathId 找到物体对象: ModelIndex={testInfo.ObjectModelIndex}, PathId={testInfo.ObjectPathId}");
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 已找到真实物体: {ModelItemAnalysisHelper.GetSafeDisplayName(objectObject)}");
}
catch (Exception ex)
{
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] ResolvePathId 失败: ModelIndex={testInfo.VehicleModelIndex}, PathId={testInfo.VehiclePathId}", ex);
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] ResolvePathId 失败: ModelIndex={testInfo.ObjectModelIndex}, PathId={testInfo.ObjectPathId}", ex);
}
}
if (vehicleObject == null)
if (objectObject == null)
{
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法重建车辆对象: IsVirtualVehicle={testInfo.IsVirtualVehicle}, VehicleModelIndex={testInfo.VehicleModelIndex}, VehiclePathId={testInfo.VehiclePathId}");
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法重建物体对象: IsVirtualObject={testInfo.IsVirtualObject}, ObjectModelIndex={testInfo.ObjectModelIndex}, ObjectPathId={testInfo.ObjectPathId}");
}
// 如果是虚拟车辆,将其移动到路径起点
if (testInfo.IsVirtualVehicle && !string.IsNullOrEmpty(testInfo.RouteId))
// 如果是虚拟物体,将其移动到路径起点
if (testInfo.IsVirtualObject && !string.IsNullOrEmpty(testInfo.RouteId))
{
try
{
@ -460,15 +460,15 @@ namespace NavisworksTransport
var startPoint = route.Points.FirstOrDefault(p => p.Type == PathPointType.StartPoint) ?? route.Points[0];
var startPointPosition = startPoint.Position;
// 使用 PathAnimationManager 将车辆移动到起点
// 使用 PathAnimationManager 将物体移动到起点
var pathAnimationManager = PathAnimationManager.GetInstance();
pathAnimationManager.MoveVehicleToPathStart(vehicleObject, new List<Point3D> { startPointPosition, startPointPosition });
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟车辆已移动到路径起点: ({startPointPosition.X:F2}, {startPointPosition.Y:F2}, {startPointPosition.Z:F2})");
pathAnimationManager.MoveObjectToPathStart(objectObject, new List<Point3D> { startPointPosition, startPointPosition });
LogManager.Info($"[LoadClashDetectiveResultsFromDatabase] 虚拟物体已移动到路径起点: ({startPointPosition.X:F2}, {startPointPosition.Y:F2}, {startPointPosition.Z:F2})");
}
}
catch (Exception ex)
{
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 移动虚拟车辆到起点失败: {ex.Message}");
LogManager.Warning($"[LoadClashDetectiveResultsFromDatabase] 移动虚拟物体到起点失败: {ex.Message}");
}
}
@ -529,7 +529,7 @@ namespace NavisworksTransport
if (collidedObject == null)
{
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过路径找到车辆对象: {obj.ModelIndex},{obj.PathId}");
throw new InvalidOperationException($"[LoadClashDetectiveResultsFromDatabase] 无法通过路径找到物体对象: {obj.ModelIndex},{obj.PathId}");
}
if (!ModelItemAnalysisHelper.IsModelItemValid(collidedObject))
@ -545,7 +545,7 @@ namespace NavisworksTransport
ClashGuid = Guid.NewGuid(),
DisplayName = $"历史碰撞: {collidedObjectName}",
Status = ClashResultStatus.Active,
Item1 = vehicleObject,
Item1 = objectObject,
Item2 = collidedObject,
Center = collidedObject.BoundingBox().Center,
Distance = 0.0,
@ -685,12 +685,12 @@ namespace NavisworksTransport
double detectionGap,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
Progress progress = null,
int precomputedCollisionCount = 0)
{
@ -754,10 +754,10 @@ namespace NavisworksTransport
duration,
detectionGap,
animatedObject,
isVirtualVehicle,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
isVirtualObject,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
precomputedCollisionCount
);
@ -1114,8 +1114,8 @@ namespace NavisworksTransport
_clashDetectiveCollisionCount = clashResults.Count;
// 保存到数据库(使用去重后的结果)
SaveClashDetectiveResultToDatabase(routeId, finalClashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle,
virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight, precomputedCollisionCount);
SaveClashDetectiveResultToDatabase(routeId, finalClashResults, frameRate, duration, detectionGap, animatedObject, isVirtualObject,
virtualObjectLength, virtualObjectWidth, virtualObjectHeight, precomputedCollisionCount);
LogManager.Info($"[ClashDetective] 结果已保存到数据库");
@ -1129,13 +1129,13 @@ namespace NavisworksTransport
/// <param name="precomputedCollisions">预计算碰撞结果</param>
/// <param name="detectionGap">检测间隙容差</param>
/// <param name="routeId">路由ID</param>
/// <param name="animatedObject">动画对象(如果是真实车辆</param>
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="animatedObject">动画对象(如果是真实物体</param>
/// <param name="isVirtualObject">是否使用虚拟物体</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">动画时长</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
/// <param name="virtualObjectLength">虚拟物体长度(米)</param>
/// <param name="virtualObjectWidth">虚拟物体宽度(米)</param>
/// <param name="virtualObjectHeight">虚拟物体高度(米)</param>
/// <param name="pathPoints">路径点列表(用于测试完成后恢复物体位置)</param>
/// <summary>
/// 创建并运行ClashDetective碰撞测试
@ -1146,12 +1146,12 @@ namespace NavisworksTransport
double detectionGap,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null)
{
// 重置取消标志
@ -1198,12 +1198,12 @@ namespace NavisworksTransport
detectionGap,
routeId,
animatedObject,
isVirtualVehicle,
isVirtualObject,
frameRate,
duration,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
progress,
precomputedCollisionCount
);
@ -1761,7 +1761,7 @@ namespace NavisworksTransport
/// <summary>
/// 清除移动物体引用(不清除基础缓存)
/// 🔥 优化:用于虚拟车辆切换时只清除引用而不重建80秒的基础缓存
/// 🔥 优化:用于虚拟物体切换时只清除引用而不重建80秒的基础缓存
/// </summary>
public static void ClearAnimatedObject()
{
@ -2114,12 +2114,12 @@ namespace NavisworksTransport
double detectionGap,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null)
{
try
@ -2138,12 +2138,12 @@ namespace NavisworksTransport
detectionGap,
routeId,
animatedObject,
isVirtualVehicle,
isVirtualObject,
frameRate,
duration,
virtualVehicleLength,
virtualVehicleWidth,
virtualVehicleHeight,
virtualObjectLength,
virtualObjectWidth,
virtualObjectHeight,
null,
precomputedCollisionCount
);
@ -2172,7 +2172,7 @@ namespace NavisworksTransport
// {
// try
// {
// PathAnimationManager.GetInstance().MoveVehicleToPathStart(animatedObject, pathPoints);
// PathAnimationManager.GetInstance().MoveObjectToPathStart(animatedObject, pathPoints);
// LogManager.Info($"[批处理] 已将 {animatedObject.DisplayName} 恢复到路径起点位置");
// }
// catch (Exception restoreEx)

View File

@ -15,11 +15,11 @@ namespace NavisworksTransport.Core.Collision
/// 预计算动画帧和碰撞纯计算无UI操作
/// </summary>
/// <param name="route">路径</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟车辆</param>
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟物体</param>
/// <param name="isVirtualObject">是否使用虚拟物体</param>
/// <param name="virtualObjectLength">虚拟物体长度(米)</param>
/// <param name="virtualObjectWidth">虚拟物体宽度(米)</param>
/// <param name="virtualObjectHeight">虚拟物体高度(米)</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">持续时间(秒)</param>
/// <param name="detectionGap">检测间隙(米)</param>
@ -28,10 +28,10 @@ namespace NavisworksTransport.Core.Collision
List<AnimationFrame> PrecomputeFrames(
PathRoute route,
ModelItem animatedObject,
bool isVirtualVehicle,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
bool isVirtualObject,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
int frameRate,
double duration,
double detectionGap,
@ -46,13 +46,13 @@ namespace NavisworksTransport.Core.Collision
/// <param name="detectionGap">检测间隙(米)</param>
/// <param name="pathName">路径名称</param>
/// <param name="routeId">路径ID</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟车辆</param>
/// <param name="isVirtualVehicle">是否使用虚拟车辆</param>
/// <param name="animatedObject">动画对象可为null表示使用虚拟物体</param>
/// <param name="isVirtualObject">是否使用虚拟物体</param>
/// <param name="frameRate">帧率</param>
/// <param name="duration">持续时间(秒)</param>
/// <param name="virtualVehicleLength">虚拟车辆长度(米)</param>
/// <param name="virtualVehicleWidth">虚拟车辆宽度(米)</param>
/// <param name="virtualVehicleHeight">虚拟车辆高度(米)</param>
/// <param name="virtualObjectLength">虚拟物体长度(米)</param>
/// <param name="virtualObjectWidth">虚拟物体宽度(米)</param>
/// <param name="virtualObjectHeight">虚拟物体高度(米)</param>
/// <param name="pathPoints">路径点列表(用于测试完成后恢复物体位置)</param>
/// <returns>ClashDetective测试名称</returns>
string CreateAndRunClashDetectiveTest(
@ -61,12 +61,12 @@ namespace NavisworksTransport.Core.Collision
string pathName,
string routeId,
ModelItem animatedObject,
bool isVirtualVehicle,
bool isVirtualObject,
int frameRate,
double duration,
double virtualVehicleLength,
double virtualVehicleWidth,
double virtualVehicleHeight,
double virtualObjectLength,
double virtualObjectWidth,
double virtualObjectHeight,
List<Point3D> pathPoints = null);
}
}

View File

@ -388,9 +388,9 @@ namespace NavisworksTransport.Core.Config
// 使用带默认值的读取方法,不抛出异常
config.PathEditing.CellSizeMeters = GetDoubleValueWithDefault(pathEdit, "cell_size_meters", 0.5, missingItems);
config.PathEditing.MaxHeightDiffMeters = GetDoubleValueWithDefault(pathEdit, "max_height_diff_meters", 0.35, missingItems);
config.PathEditing.VehicleLengthMeters = GetDoubleValueWithDefault(pathEdit, "vehicle_length_meters", 1.5, missingItems);
config.PathEditing.VehicleWidthMeters = GetDoubleValueWithDefault(pathEdit, "vehicle_width_meters", 1.0, missingItems);
config.PathEditing.VehicleHeightMeters = GetDoubleValueWithDefault(pathEdit, "vehicle_height_meters", 2.0, missingItems);
config.PathEditing.ObjectLengthMeters = GetDoubleValueWithDefault(pathEdit, "object_length_meters", 1.5, missingItems);
config.PathEditing.ObjectWidthMeters = GetDoubleValueWithDefault(pathEdit, "object_width_meters", 1.0, missingItems);
config.PathEditing.ObjectHeightMeters = GetDoubleValueWithDefault(pathEdit, "object_height_meters", 2.0, missingItems);
config.PathEditing.SafetyMarginMeters = GetDoubleValueWithDefault(pathEdit, "safety_margin_meters", 0.1, missingItems);
config.PathEditing.DefaultPathTurnRadiusMeters = GetDoubleValueWithDefault(pathEdit, "default_path_turn_radius", 2.5, missingItems);
config.PathEditing.ArcSamplingStepMeters = GetDoubleValueWithDefault(pathEdit, "arc_sampling_step", 0.05, missingItems);
@ -613,9 +613,9 @@ namespace NavisworksTransport.Core.Config
{
pathEdit["cell_size_meters"] = config.PathEditing.CellSizeMeters;
pathEdit["max_height_diff_meters"] = config.PathEditing.MaxHeightDiffMeters;
pathEdit["vehicle_length_meters"] = config.PathEditing.VehicleLengthMeters;
pathEdit["vehicle_width_meters"] = config.PathEditing.VehicleWidthMeters;
pathEdit["vehicle_height_meters"] = config.PathEditing.VehicleHeightMeters;
pathEdit["object_length_meters"] = config.PathEditing.ObjectLengthMeters;
pathEdit["object_width_meters"] = config.PathEditing.ObjectWidthMeters;
pathEdit["object_height_meters"] = config.PathEditing.ObjectHeightMeters;
pathEdit["safety_margin_meters"] = config.PathEditing.SafetyMarginMeters;
pathEdit["default_path_turn_radius"] = config.PathEditing.DefaultPathTurnRadiusMeters;
pathEdit["arc_sampling_step"] = config.PathEditing.ArcSamplingStepMeters;
@ -714,7 +714,7 @@ namespace NavisworksTransport.Core.Config
var newConfig = ParseTomlToConfig(templateContent);
LogManager.Info($"解析后的配置 - CellSizeMeters: {newConfig.PathEditing.CellSizeMeters}");
LogManager.Info($"解析后的配置 - VehicleLengthMeters: {newConfig.PathEditing.VehicleLengthMeters}");
LogManager.Info($"解析后的配置 - ObjectLengthMeters: {newConfig.PathEditing.ObjectLengthMeters}");
_currentConfig = newConfig;

View File

@ -72,19 +72,19 @@ namespace NavisworksTransport.Core.Config
public double MaxHeightDiffMeters { get; set; }
/// <summary>
/// 车辆长度(米)
/// 物体长度(米)
/// </summary>
public double VehicleLengthMeters { get; set; }
public double ObjectLengthMeters { get; set; }
/// <summary>
/// 车辆宽度(米)
/// 物体宽度(米)
/// </summary>
public double VehicleWidthMeters { get; set; }
public double ObjectWidthMeters { get; set; }
/// <summary>
/// 车辆高度(米)
/// 物体高度(米)
/// </summary>
public double VehicleHeightMeters { get; set; }
public double ObjectHeightMeters { get; set; }
/// <summary>
/// 安全间隙(米)
@ -114,19 +114,19 @@ namespace NavisworksTransport.Core.Config
public double MaxHeightDiff => MaxHeightDiffMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 车辆长度(模型单位)
/// 物体长度(模型单位)
/// </summary>
public double VehicleLength => VehicleLengthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
public double ObjectLength => ObjectLengthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 车辆宽度(模型单位)
/// 物体宽度(模型单位)
/// </summary>
public double VehicleWidth => VehicleWidthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
public double ObjectWidth => ObjectWidthMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 车辆高度(模型单位)
/// 物体高度(模型单位)
/// </summary>
public double VehicleHeight => VehicleHeightMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
public double ObjectHeight => ObjectHeightMeters * Utils.UnitsConverter.GetMetersToUnitsConversionFactor();
/// <summary>
/// 安全间隙(模型单位)

View File

@ -387,8 +387,8 @@ namespace NavisworksTransport
if (activeDoc != null && activeDoc.Models != null && activeDoc.Models.Count > 0)
{
LogManager.Info("[文档管理] 插件加载时发现包含模型的活动文档,立即初始化管理器");
// 🔥 插件加载时不是虚拟车辆变化,传入 false
InitializeManagers(isVirtualVehicleChange: false);
// 🔥 插件加载时不是虚拟物体变化,传入 false
InitializeManagers(isVirtualObjectChange: false);
}
else if (activeDoc != null)
{
@ -495,14 +495,14 @@ namespace NavisworksTransport
LogManager.Warning($"[文档管理] 订阅新文档事件时出现警告: {ex.Message}");
}
// 🔥 优化:检测是否是虚拟车辆引起的文档变化
bool isVirtualVehicleChange = IsVirtualVehicleChange(activeDoc);
if (isVirtualVehicleChange)
// 🔥 优化:检测是否是虚拟物体引起的文档变化
bool isVirtualObjectChange = IsVirtualObjectChange(activeDoc);
if (isVirtualObjectChange)
{
LogManager.Info("[文档管理] 检测到虚拟车辆文档变化,使用快速初始化模式");
LogManager.Info("[文档管理] 检测到虚拟物体文档变化,使用快速初始化模式");
}
InitializeManagers(isVirtualVehicleChange);
InitializeManagers(isVirtualObjectChange);
}
else
{
@ -513,29 +513,29 @@ namespace NavisworksTransport
}
/// <summary>
/// 检测是否是虚拟车辆引起的文档变化
/// 🔥 优化:通过 VirtualVehicleManager 的标志精确检测,避免误触发缓存重建
/// 检测是否是虚拟物体引起的文档变化
/// 🔥 优化:通过 VirtualObjectManager 的标志精确检测,避免误触发缓存重建
/// </summary>
private bool IsVirtualVehicleChange(Document doc)
private bool IsVirtualObjectChange(Document doc)
{
try
{
// 🔥 优先检查 VirtualVehicleManager 的更新标志(最准确)
if (VirtualVehicleManager.Instance.IsUpdatingVirtualVehicle)
// 🔥 优先检查 VirtualObjectManager 的更新标志(最准确)
if (VirtualObjectManager.Instance.IsUpdatingVirtualObject)
{
LogManager.Debug("[文档管理] VirtualVehicleManager 正在更新,判定为虚拟车辆变化");
LogManager.Debug("[文档管理] VirtualObjectManager 正在更新,判定为虚拟物体变化");
return true;
}
if (doc?.Models == null) return false;
// 备选:检查是否包含虚拟车辆模型
// 备选:检查是否包含虚拟物体模型
foreach (var model in doc.Models)
{
var modelName = model.RootItem?.DisplayName ?? "";
if (modelName.Contains("unit_cube") || modelName.Contains("VirtualVehicle"))
if (modelName.Contains("unit_cube") || modelName.Contains("VirtualObject"))
{
LogManager.Debug($"[文档管理] 检测到虚拟车辆模型: {modelName}");
LogManager.Debug($"[文档管理] 检测到虚拟物体模型: {modelName}");
return true;
}
}
@ -544,7 +544,7 @@ namespace NavisworksTransport
}
catch (Exception ex)
{
LogManager.Debug($"[文档管理] 检测虚拟车辆变化时出错: {ex.Message}");
LogManager.Debug($"[文档管理] 检测虚拟物体变化时出错: {ex.Message}");
return false;
}
}
@ -693,8 +693,8 @@ namespace NavisworksTransport
/// <summary>
/// 初始化各个管理器
/// </summary>
/// <param name="isVirtualVehicleChange">是否由虚拟车辆变化引起</param>
private void InitializeManagers(bool isVirtualVehicleChange = false)
/// <param name="isVirtualObjectChange">是否由虚拟物体变化引起</param>
private void InitializeManagers(bool isVirtualObjectChange = false)
{
try
{
@ -713,10 +713,10 @@ namespace NavisworksTransport
// 通知DocumentStateManager文档已就绪
DocumentStateManager.Instance.OnDocumentReady();
// 🔥 优化:虚拟车辆变化时不清除缓存避免80秒重建
if (isVirtualVehicleChange)
// 🔥 优化:虚拟物体变化时不清除缓存避免80秒重建
if (isVirtualObjectChange)
{
LogManager.Info("[文档管理] 检测到虚拟车辆变化,保留现有缓存,仅更新运动物体引用");
LogManager.Info("[文档管理] 检测到虚拟物体变化,保留现有缓存,仅更新运动物体引用");
// 只清除运动物体引用,不清除基础缓存
ClashDetectiveIntegration.ClearAnimatedObject();
}
@ -811,15 +811,15 @@ namespace NavisworksTransport
LogManager.Warning($"[文档管理] 清理Idle事件管理器时出现警告: {ex.Message}");
}
// 清理虚拟车辆
// 清理虚拟物体
try
{
VirtualVehicleManager.Instance.Cleanup();
LogManager.Info("[文档管理] 已清理虚拟车辆");
VirtualObjectManager.Instance.Cleanup();
LogManager.Info("[文档管理] 已清理虚拟物体");
}
catch (Exception ex)
{
LogManager.Warning($"[文档管理] 清理虚拟车辆时出现警告: {ex.Message}");
LogManager.Warning($"[文档管理] 清理虚拟物体时出现警告: {ex.Message}");
}
// 清除临时材质

View File

@ -25,11 +25,11 @@ namespace NavisworksTransport.Core.Models
public double DetectionToleranceMeters { get; set; }
// 运动物体配置
public bool IsVirtualVehicle { get; set; }
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
public string MovingObjectName { get; set; } // 运动物体名称,虚拟车辆时为 null
public bool IsVirtualObject { get; set; }
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
public string MovingObjectName { get; set; } // 运动物体名称,虚拟物体时为 null
// 碰撞检测配置
public bool DetectAllObjects { get; set; } = true;

View File

@ -11,10 +11,10 @@ namespace NavisworksTransport.Core.Models
public bool CollisionDetectionEnabled { get; set; }
public bool ReportGenerationEnabled { get; set; }
// 虚拟车辆参数
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
// 虚拟物体参数
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
/// <summary>
/// 序列化为JSON字符串简化版

View File

@ -607,28 +607,28 @@ namespace NavisworksTransport.Core.Models
public string DocumentName { get; set; }
/// <summary>
/// 车辆参数信息
/// 物体参数信息
/// </summary>
public VehicleParameters VehicleParams { get; set; }
public ObjectParameters ObjectParams { get; set; }
}
/// <summary>
/// 车辆参数
/// 物体参数
/// </summary>
public class VehicleParameters
public class ObjectParameters
{
/// <summary>
/// 车辆长度(米)
/// 物体长度(米)
/// </summary>
public double Length { get; set; }
/// <summary>
/// 车辆宽度(米)
/// 物体宽度(米)
/// </summary>
public double Width { get; set; }
/// <summary>
/// 车辆高度(米)
/// 物体高度(米)
/// </summary>
public double Height { get; set; }

View File

@ -601,10 +601,10 @@ namespace NavisworksTransport.Core
// 简单扣分制:基于通道窄宽缩减量扣分
// 每缩减10%扣1分
// 如果路径没有车辆参数,使用配置默认值
double vehicleWidth = route.MaxVehicleWidth > 0 ? route.MaxVehicleWidth : 1.0;
// 如果路径没有物体参数,使用配置默认值
double objectWidth = route.MaxObjectWidth > 0 ? route.MaxObjectWidth : 1.0;
double safetyMargin = route.SafetyMargin > 0 ? route.SafetyMargin : 0.1;
double effectiveVehicleWidth = vehicleWidth + safetyMargin * 2;
double effectiveObjectWidth = objectWidth + safetyMargin * 2;
double channelWidth;
if (channels == null || channels.Count == 0)
@ -618,7 +618,7 @@ namespace NavisworksTransport.Core
}
// 计算宽度缩减率(正值表示有冗余,负值表示不足)
double widthMargin = (channelWidth - effectiveVehicleWidth) / effectiveVehicleWidth;
double widthMargin = (channelWidth - effectiveObjectWidth) / effectiveObjectWidth;
// 每缩减10%扣1分反向计算越宽越高分
double score = 100 - widthMargin * 100 * 0.1;

View File

@ -436,20 +436,20 @@ namespace NavisworksTransport.Core
sb.AppendLine(" <div class=\"card\">");
sb.AppendLine(" <div class=\"card-title\">⚙️ 技术参数</div>");
if (data.VehicleParams != null)
if (data.ObjectParams != null)
{
sb.AppendLine(" <div class=\"grid-3\">");
sb.AppendLine(" <div class=\"metric-card\">");
sb.AppendLine(" <div class=\"metric-label\">车辆长度</div>");
sb.AppendLine($" <div class=\"metric-value\">{data.VehicleParams.Length:F2}m</div>");
sb.AppendLine(" <div class=\"metric-label\">物体长度</div>");
sb.AppendLine($" <div class=\"metric-value\">{data.ObjectParams.Length:F2}m</div>");
sb.AppendLine(" </div>");
sb.AppendLine(" <div class=\"metric-card\">");
sb.AppendLine(" <div class=\"metric-label\">车辆宽度</div>");
sb.AppendLine($" <div class=\"metric-value\">{data.VehicleParams.Width:F2}m</div>");
sb.AppendLine(" <div class=\"metric-label\">物体宽度</div>");
sb.AppendLine($" <div class=\"metric-value\">{data.ObjectParams.Width:F2}m</div>");
sb.AppendLine(" </div>");
sb.AppendLine(" <div class=\"metric-card\">");
sb.AppendLine(" <div class=\"metric-label\">车辆高度</div>");
sb.AppendLine($" <div class=\"metric-value\">{data.VehicleParams.Height:F2}m</div>");
sb.AppendLine(" <div class=\"metric-label\">物体高度</div>");
sb.AppendLine($" <div class=\"metric-value\">{data.ObjectParams.Height:F2}m</div>");
sb.AppendLine(" </div>");
sb.AppendLine(" </div>");
}
@ -570,6 +570,6 @@ namespace NavisworksTransport.Core
public PathDetailedAnalysis OverallBestPath { get; set; }
public List<CategorizedSuggestion> AllSuggestions { get; set; }
public string DocumentName { get; set; }
public VehicleParameters VehicleParams { get; set; }
public ObjectParameters ObjectParams { get; set; }
}
}

View File

@ -29,9 +29,9 @@ namespace NavisworksTransport
}
/// <summary>
/// JSON格式的车辆限制数据
/// JSON格式的物体限制数据
/// </summary>
public class JsonVehicleLimits
public class JsonObjectLimits
{
public double maxLength { get; set; }
public double maxWidth { get; set; }
@ -49,7 +49,7 @@ namespace NavisworksTransport
public string description { get; set; }
public string pathType { get; set; }
public double totalLength { get; set; }
public JsonVehicleLimits vehicleLimits { get; set; }
public JsonObjectLimits objectLimits { get; set; }
public double gridSize { get; set; }
public double liftHeightMeters { get; set; }
public string created { get; set; }
@ -270,11 +270,11 @@ namespace NavisworksTransport
description = route.Description ?? "",
pathType = route.PathType.ToString(),
totalLength = Math.Round(route.TotalLength, exportSettings?.Precision ?? 3),
vehicleLimits = new
objectLimits = new
{
maxLength = Math.Round(route.MaxVehicleLength, exportSettings?.Precision ?? 3),
maxWidth = Math.Round(route.MaxVehicleWidth, exportSettings?.Precision ?? 3),
maxHeight = Math.Round(route.MaxVehicleHeight, exportSettings?.Precision ?? 3),
maxLength = Math.Round(route.MaxObjectLength, exportSettings?.Precision ?? 3),
maxWidth = Math.Round(route.MaxObjectWidth, exportSettings?.Precision ?? 3),
maxHeight = Math.Round(route.MaxObjectHeight, exportSettings?.Precision ?? 3),
safetyMargin = Math.Round(route.SafetyMargin, exportSettings?.Precision ?? 3)
},
gridSize = Math.Round(route.GridSize, exportSettings?.Precision ?? 3),
@ -476,10 +476,10 @@ namespace NavisworksTransport
Id = jsonRoute.id,
Description = jsonRoute.description ?? "",
CreatedTime = ParseJsonDateTime(jsonRoute.created),
MaxVehicleLength = jsonRoute.vehicleLimits?.maxLength ?? 0,
MaxVehicleWidth = jsonRoute.vehicleLimits?.maxWidth ?? 0,
MaxVehicleHeight = jsonRoute.vehicleLimits?.maxHeight ?? 0,
SafetyMargin = jsonRoute.vehicleLimits?.safetyMargin ?? 0,
MaxObjectLength = jsonRoute.objectLimits?.maxLength ?? 0,
MaxObjectWidth = jsonRoute.objectLimits?.maxWidth ?? 0,
MaxObjectHeight = jsonRoute.objectLimits?.maxHeight ?? 0,
SafetyMargin = jsonRoute.objectLimits?.safetyMargin ?? 0,
GridSize = jsonRoute.gridSize,
LiftHeightMeters = jsonRoute.liftHeightMeters
};
@ -989,9 +989,9 @@ namespace NavisworksTransport
routeElement.SetAttribute("description", route.Description ?? "");
routeElement.SetAttribute("pathType", route.PathType.ToString());
routeElement.SetAttribute("totalLength", route.TotalLength.ToString("F3"));
routeElement.SetAttribute("maxVehicleLength", route.MaxVehicleLength.ToString("F3"));
routeElement.SetAttribute("maxVehicleWidth", route.MaxVehicleWidth.ToString("F3"));
routeElement.SetAttribute("maxVehicleHeight", route.MaxVehicleHeight.ToString("F3"));
routeElement.SetAttribute("maxObjectLength", route.MaxObjectLength.ToString("F3"));
routeElement.SetAttribute("maxObjectWidth", route.MaxObjectWidth.ToString("F3"));
routeElement.SetAttribute("maxObjectHeight", route.MaxObjectHeight.ToString("F3"));
routeElement.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
routeElement.SetAttribute("gridSize", route.GridSize.ToString("F3"));
routeElement.SetAttribute("liftHeightMeters", route.LiftHeightMeters.ToString("F3"));
@ -1112,9 +1112,9 @@ namespace NavisworksTransport
Name = routeNode.Attributes?["name"]?.Value ?? "导入路径",
Description = routeNode.Attributes?["description"]?.Value ?? "",
PathType = pathType,
MaxVehicleLength = double.TryParse(routeNode.Attributes?["maxVehicleLength"]?.Value, out double maxLength) ? maxLength : 0,
MaxVehicleWidth = double.TryParse(routeNode.Attributes?["maxVehicleWidth"]?.Value, out double maxWidth) ? maxWidth : 0,
MaxVehicleHeight = double.TryParse(routeNode.Attributes?["maxVehicleHeight"]?.Value, out double maxHeight) ? maxHeight : 0,
MaxObjectLength = double.TryParse(routeNode.Attributes?["maxObjectLength"]?.Value, out double maxLength) ? maxLength : 0,
MaxObjectWidth = double.TryParse(routeNode.Attributes?["maxObjectWidth"]?.Value, out double maxWidth) ? maxWidth : 0,
MaxObjectHeight = double.TryParse(routeNode.Attributes?["maxObjectHeight"]?.Value, out double maxHeight) ? maxHeight : 0,
SafetyMargin = double.TryParse(routeNode.Attributes?["safetyMargin"]?.Value, out double safetyMargin) ? safetyMargin : 0,
GridSize = double.TryParse(routeNode.Attributes?["gridSize"]?.Value, out double gridSize) ? gridSize : 0,
LiftHeightMeters = double.TryParse(routeNode.Attributes?["liftHeightMeters"]?.Value, out double liftHeight) ? liftHeight : 0
@ -1211,14 +1211,14 @@ namespace NavisworksTransport
pathElement.SetAttribute("type", "Transportation");
pathElement.SetAttribute("length", route.TotalLength.ToString("F3"));
// 添加车辆限制元素
var vehicleConstraints = xmlDoc.CreateElement("VehicleConstraints", _delmiaNamespace);
vehicleConstraints.SetAttribute("maxLength", route.MaxVehicleLength.ToString("F3"));
vehicleConstraints.SetAttribute("maxWidth", route.MaxVehicleWidth.ToString("F3"));
vehicleConstraints.SetAttribute("maxHeight", route.MaxVehicleHeight.ToString("F3"));
vehicleConstraints.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
vehicleConstraints.SetAttribute("gridSize", route.GridSize.ToString("F3"));
pathElement.AppendChild(vehicleConstraints);
// 添加物体限制元素
var objectConstraints = xmlDoc.CreateElement("ObjectConstraints", _delmiaNamespace);
objectConstraints.SetAttribute("maxLength", route.MaxObjectLength.ToString("F3"));
objectConstraints.SetAttribute("maxWidth", route.MaxObjectWidth.ToString("F3"));
objectConstraints.SetAttribute("maxHeight", route.MaxObjectHeight.ToString("F3"));
objectConstraints.SetAttribute("safetyMargin", route.SafetyMargin.ToString("F3"));
objectConstraints.SetAttribute("gridSize", route.GridSize.ToString("F3"));
pathElement.AppendChild(objectConstraints);
// 添加路径段
var segmentsElement = xmlDoc.CreateElement("Segments", _delmiaNamespace);

View File

@ -87,9 +87,9 @@ namespace NavisworksTransport
EstimatedTime REAL,
TurnRadius REAL,
IsCurved INTEGER,
MaxVehicleLength REAL,
MaxVehicleWidth REAL,
MaxVehicleHeight REAL,
MaxObjectLength REAL,
MaxObjectWidth REAL,
MaxObjectHeight REAL,
SafetyMargin REAL,
GridSize REAL,
PathType INTEGER,
@ -191,12 +191,12 @@ namespace NavisworksTransport
Duration REAL,
DetectionGap REAL,
AnimatedObjectName TEXT,
IsVirtualVehicle INTEGER,
VehicleModelIndex INTEGER,
VehiclePathId TEXT,
VirtualVehicleLength REAL,
VirtualVehicleWidth REAL,
VirtualVehicleHeight REAL,
IsVirtualObject INTEGER,
ObjectModelIndex INTEGER,
ObjectPathId TEXT,
VirtualObjectLength REAL,
VirtualObjectWidth REAL,
VirtualObjectHeight REAL,
CreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
)
");
@ -267,10 +267,10 @@ namespace NavisworksTransport
FrameRate INTEGER NOT NULL,
DurationSeconds REAL NOT NULL,
DetectionToleranceMeters REAL NOT NULL,
IsVirtualVehicle INTEGER NOT NULL,
VirtualVehicleLength REAL,
VirtualVehicleWidth REAL,
VirtualVehicleHeight REAL,
IsVirtualObject INTEGER NOT NULL,
VirtualObjectLength REAL,
VirtualObjectWidth REAL,
VirtualObjectHeight REAL,
DetectAllObjects INTEGER NOT NULL DEFAULT 1,
ClashDetectiveTestName TEXT,
CollisionCount INTEGER,
@ -361,7 +361,7 @@ namespace NavisworksTransport
// 路径长度由 PathRoute.TotalLength 计算属性实时从 Edges/Points 计算
var sql = @"
INSERT OR REPLACE INTO PathRoutes
(Id, Name, EstimatedTime, TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight, SafetyMargin, GridSize, PathType, LiftHeightMeters, CreatedTime, LastModified)
(Id, Name, EstimatedTime, TurnRadius, IsCurved, MaxObjectLength, MaxObjectWidth, MaxObjectHeight, SafetyMargin, GridSize, PathType, LiftHeightMeters, CreatedTime, LastModified)
VALUES (@id, @name, @time, @turnRadius, @isCurved, @maxLength, @maxWidth, @maxHeight, @safetyMargin, @gridSize, @pathType, @liftHeightMeters, @created, @modified)
";
@ -372,9 +372,9 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@time", route.EstimatedTime);
cmd.Parameters.AddWithValue("@turnRadius", route.TurnRadius);
cmd.Parameters.AddWithValue("@isCurved", route.IsCurved ? 1 : 0);
cmd.Parameters.AddWithValue("@maxLength", route.MaxVehicleLength);
cmd.Parameters.AddWithValue("@maxWidth", route.MaxVehicleWidth);
cmd.Parameters.AddWithValue("@maxHeight", route.MaxVehicleHeight);
cmd.Parameters.AddWithValue("@maxLength", route.MaxObjectLength);
cmd.Parameters.AddWithValue("@maxWidth", route.MaxObjectWidth);
cmd.Parameters.AddWithValue("@maxHeight", route.MaxObjectHeight);
cmd.Parameters.AddWithValue("@safetyMargin", route.SafetyMargin);
cmd.Parameters.AddWithValue("@gridSize", route.GridSize);
cmd.Parameters.AddWithValue("@pathType", (int)route.PathType);
@ -763,11 +763,11 @@ namespace NavisworksTransport
var sql = @"
INSERT INTO ClashDetectiveResults
(TestName, RouteId, TestTime, CollisionCount, AnimationCollisionCount,
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelIndex, VehiclePathId,
VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt)
FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualObject, ObjectModelIndex, ObjectPathId,
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, CreatedAt)
VALUES (@testName, @routeId, @testTime, @collisionCount, @animationCollisionCount,
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualVehicle, @vehicleModelIndex, @vehiclePathId,
@virtualVehicleLength, @virtualVehicleWidth, @virtualVehicleHeight, @createdAt)
@frameRate, @duration, @detectionGap, @animatedObjectName, @isVirtualObject, @objectModelIndex, @objectPathId,
@virtualObjectLength, @virtualObjectWidth, @virtualObjectHeight, @createdAt)
";
long newId = 0;
@ -782,12 +782,12 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@duration", record.Duration);
cmd.Parameters.AddWithValue("@detectionGap", record.DetectionGap);
cmd.Parameters.AddWithValue("@animatedObjectName", record.AnimatedObjectName ?? "");
cmd.Parameters.AddWithValue("@isVirtualVehicle", record.IsVirtualVehicle);
cmd.Parameters.AddWithValue("@vehicleModelIndex", record.VehicleModelIndex.HasValue ? (object)record.VehicleModelIndex.Value : DBNull.Value);
cmd.Parameters.AddWithValue("@vehiclePathId", record.VehiclePathId ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@virtualVehicleLength", record.VirtualVehicleLength);
cmd.Parameters.AddWithValue("@virtualVehicleWidth", record.VirtualVehicleWidth);
cmd.Parameters.AddWithValue("@virtualVehicleHeight", record.VirtualVehicleHeight);
cmd.Parameters.AddWithValue("@isVirtualObject", record.IsVirtualObject);
cmd.Parameters.AddWithValue("@objectModelIndex", record.ObjectModelIndex.HasValue ? (object)record.ObjectModelIndex.Value : DBNull.Value);
cmd.Parameters.AddWithValue("@objectPathId", record.ObjectPathId ?? (object)DBNull.Value);
cmd.Parameters.AddWithValue("@virtualObjectLength", record.VirtualObjectLength);
cmd.Parameters.AddWithValue("@virtualObjectWidth", record.VirtualObjectWidth);
cmd.Parameters.AddWithValue("@virtualObjectHeight", record.VirtualObjectHeight);
cmd.Parameters.AddWithValue("@createdAt", record.CreatedAt);
cmd.ExecuteNonQuery();
newId = _connection.LastInsertRowId;
@ -1328,9 +1328,9 @@ namespace NavisworksTransport
EstimatedTime = Convert.ToDouble(reader["EstimatedTime"]),
TurnRadius = Convert.ToDouble(reader["TurnRadius"]),
IsCurved = Convert.ToInt32(reader["IsCurved"]) == 1,
MaxVehicleLength = Convert.ToDouble(reader["MaxVehicleLength"]),
MaxVehicleWidth = Convert.ToDouble(reader["MaxVehicleWidth"]),
MaxVehicleHeight = Convert.ToDouble(reader["MaxVehicleHeight"]),
MaxObjectLength = Convert.ToDouble(reader["MaxObjectLength"]),
MaxObjectWidth = Convert.ToDouble(reader["MaxObjectWidth"]),
MaxObjectHeight = Convert.ToDouble(reader["MaxObjectHeight"]),
SafetyMargin = Convert.ToDouble(reader["SafetyMargin"]),
GridSize = Convert.ToDouble(reader["GridSize"]),
PathType = (PathType)Convert.ToInt32(reader["PathType"]),
@ -2111,7 +2111,7 @@ namespace NavisworksTransport
using (var cmd = new SQLiteCommand(_connection))
{
cmd.CommandText = @"
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount, FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualVehicle, VehicleModelIndex, VehiclePathId, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight, CreatedAt
SELECT Id, TestName, PathName, RouteId, TestTime, CollisionCount, AnimationCollisionCount, FrameRate, Duration, DetectionGap, AnimatedObjectName, IsVirtualObject, ObjectModelIndex, ObjectPathId, VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight, CreatedAt
FROM ClashDetectiveResults
WHERE TestName = @TestName";
@ -2134,12 +2134,12 @@ namespace NavisworksTransport
Duration = Convert.ToDouble(reader["Duration"]),
DetectionGap = Convert.ToDouble(reader["DetectionGap"]),
AnimatedObjectName = reader["AnimatedObjectName"].ToString(),
IsVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]),
VehicleModelIndex = !Convert.IsDBNull(reader["VehicleModelIndex"]) ? (int?)Convert.ToInt32(reader["VehicleModelIndex"]) : null,
VehiclePathId = reader["VehiclePathId"].ToString(),
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
IsVirtualObject = Convert.ToBoolean(reader["IsVirtualObject"]),
ObjectModelIndex = !Convert.IsDBNull(reader["ObjectModelIndex"]) ? (int?)Convert.ToInt32(reader["ObjectModelIndex"]) : null,
ObjectPathId = reader["ObjectPathId"].ToString(),
VirtualObjectLength = Convert.ToDouble(reader["VirtualObjectLength"]),
VirtualObjectWidth = Convert.ToDouble(reader["VirtualObjectWidth"]),
VirtualObjectHeight = Convert.ToDouble(reader["VirtualObjectHeight"]),
CreatedAt = DateTime.Parse(reader["CreatedAt"].ToString())
};
}
@ -2170,7 +2170,7 @@ namespace NavisworksTransport
// 注意TotalLength 字段已从数据库中删除
cmd.CommandText = @"
SELECT Id, Name, CreatedTime, LastModified,
TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight,
TurnRadius, IsCurved, MaxObjectLength, MaxObjectWidth, MaxObjectHeight,
SafetyMargin, GridSize, PathType, LiftHeightMeters
FROM PathRoutes
WHERE Id = @Id";
@ -2189,9 +2189,9 @@ namespace NavisworksTransport
// TotalLength由 PathRoute.TotalLength 计算属性实时提供
TurnRadius = Convert.ToDouble(reader["TurnRadius"]),
IsCurved = Convert.ToInt32(reader["IsCurved"]) == 1,
MaxVehicleLength = Convert.ToDouble(reader["MaxVehicleLength"]),
MaxVehicleWidth = Convert.ToDouble(reader["MaxVehicleWidth"]),
MaxVehicleHeight = Convert.ToDouble(reader["MaxVehicleHeight"]),
MaxObjectLength = Convert.ToDouble(reader["MaxObjectLength"]),
MaxObjectWidth = Convert.ToDouble(reader["MaxObjectWidth"]),
MaxObjectHeight = Convert.ToDouble(reader["MaxObjectHeight"]),
SafetyMargin = Convert.ToDouble(reader["SafetyMargin"]),
GridSize = Convert.ToDouble(reader["GridSize"]),
PathType = (PathType)Convert.ToInt32(reader["PathType"]),
@ -2225,14 +2225,14 @@ namespace NavisworksTransport
INSERT INTO BatchQueueItems (
RouteId, Status, CreatedTime, StartTime, EndTime, ErrorMessage,
FrameRate, DurationSeconds, DetectionToleranceMeters,
IsVirtualVehicle, VirtualVehicleLength, VirtualVehicleWidth, VirtualVehicleHeight,
IsVirtualObject, VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight,
DetectAllObjects,
ClashDetectiveTestName, CollisionCount, ObjectRotationCorrection
)
VALUES (
@RouteId, @Status, @CreatedTime, @StartTime, @EndTime, @ErrorMessage,
@FrameRate, @DurationSeconds, @DetectionToleranceMeters,
@IsVirtualVehicle, @VirtualVehicleLength, @VirtualVehicleWidth, @VirtualVehicleHeight,
@IsVirtualObject, @VirtualObjectLength, @VirtualObjectWidth, @VirtualObjectHeight,
@DetectAllObjects,
@ClashDetectiveTestName, @CollisionCount, @ObjectRotationCorrection
);
@ -2247,10 +2247,10 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@FrameRate", item.FrameRate);
cmd.Parameters.AddWithValue("@DurationSeconds", item.DurationSeconds);
cmd.Parameters.AddWithValue("@DetectionToleranceMeters", item.DetectionToleranceMeters);
cmd.Parameters.AddWithValue("@IsVirtualVehicle", item.IsVirtualVehicle ? 1 : 0);
cmd.Parameters.AddWithValue("@VirtualVehicleLength", item.VirtualVehicleLength);
cmd.Parameters.AddWithValue("@VirtualVehicleWidth", item.VirtualVehicleWidth);
cmd.Parameters.AddWithValue("@VirtualVehicleHeight", item.VirtualVehicleHeight);
cmd.Parameters.AddWithValue("@IsVirtualObject", item.IsVirtualObject ? 1 : 0);
cmd.Parameters.AddWithValue("@VirtualObjectLength", item.VirtualObjectLength);
cmd.Parameters.AddWithValue("@VirtualObjectWidth", item.VirtualObjectWidth);
cmd.Parameters.AddWithValue("@VirtualObjectHeight", item.VirtualObjectHeight);
cmd.Parameters.AddWithValue("@DetectAllObjects", item.DetectAllObjects ? 1 : 0);
cmd.Parameters.AddWithValue("@ClashDetectiveTestName", item.ClashDetectiveTestName ?? "");
cmd.Parameters.AddWithValue("@CollisionCount", item.CollisionCount ?? (object)DBNull.Value);
@ -2274,7 +2274,7 @@ namespace NavisworksTransport
private BatchQueueItem ReadBatchQueueItemFromReader(SQLiteDataReader reader)
{
var itemId = Convert.ToInt32(reader["Id"]);
var isVirtualVehicle = Convert.ToBoolean(reader["IsVirtualVehicle"]);
var isVirtualObject = Convert.ToBoolean(reader["IsVirtualObject"]);
var item = new BatchQueueItem
{
@ -2290,10 +2290,10 @@ namespace NavisworksTransport
FrameRate = Convert.ToInt32(reader["FrameRate"]),
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
DetectionToleranceMeters = Convert.ToDouble(reader["DetectionToleranceMeters"]),
IsVirtualVehicle = isVirtualVehicle,
VirtualVehicleLength = Convert.ToDouble(reader["VirtualVehicleLength"]),
VirtualVehicleWidth = Convert.ToDouble(reader["VirtualVehicleWidth"]),
VirtualVehicleHeight = Convert.ToDouble(reader["VirtualVehicleHeight"]),
IsVirtualObject = isVirtualObject,
VirtualObjectLength = Convert.ToDouble(reader["VirtualObjectLength"]),
VirtualObjectWidth = Convert.ToDouble(reader["VirtualObjectWidth"]),
VirtualObjectHeight = Convert.ToDouble(reader["VirtualObjectHeight"]),
DetectAllObjects = Convert.ToBoolean(reader["DetectAllObjects"]),
ClashDetectiveTestName = reader["ClashDetectiveTestName"].ToString(),
CollisionCount = !Convert.IsDBNull(reader["CollisionCount"]) ? (int?)Convert.ToInt32(reader["CollisionCount"]) : null,
@ -2301,7 +2301,7 @@ namespace NavisworksTransport
};
// 填充 MovingObjectName 属性
if (!isVirtualVehicle)
if (!isVirtualObject)
{
// 从 ModelItemReferences 表查询运动物体名称
var movingObjectReferences = GetModelItemReferencesSync(itemId, "BatchQueueItem", "MovingObject");
@ -2310,7 +2310,7 @@ namespace NavisworksTransport
item.MovingObjectName = movingObjectReferences[0].Item3; // Item3 是 DisplayName
}
}
// 虚拟车辆MovingObjectName 保持 null
// 虚拟物体MovingObjectName 保持 null
return item;
}
@ -2328,7 +2328,7 @@ namespace NavisworksTransport
var sql = @"
SELECT bqi.Id, bqi.RouteId, pr.Name AS PathRouteName, pr.PathType, bqi.Status, bqi.CreatedTime, bqi.StartTime, bqi.EndTime, bqi.ErrorMessage,
bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
bqi.IsVirtualVehicle, bqi.VirtualVehicleLength, bqi.VirtualVehicleWidth, bqi.VirtualVehicleHeight,
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
bqi.DetectAllObjects,
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection
FROM BatchQueueItems bqi
@ -2387,7 +2387,7 @@ namespace NavisworksTransport
cmd.CommandText = @"
SELECT bqi.Id, bqi.RouteId, pr.Name AS PathRouteName, pr.PathType, bqi.Status, bqi.CreatedTime, bqi.StartTime, bqi.EndTime, bqi.ErrorMessage,
bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
bqi.IsVirtualVehicle, bqi.VirtualVehicleLength, bqi.VirtualVehicleWidth, bqi.VirtualVehicleHeight,
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
bqi.DetectAllObjects,
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection
FROM BatchQueueItems bqi
@ -2772,12 +2772,12 @@ namespace NavisworksTransport
public double Duration { get; set; }
public double DetectionGap { get; set; }
public string AnimatedObjectName { get; set; }
public bool IsVirtualVehicle { get; set; }
public int? VehicleModelIndex { get; set; }
public string VehiclePathId { get; set; }
public double VirtualVehicleLength { get; set; }
public double VirtualVehicleWidth { get; set; }
public double VirtualVehicleHeight { get; set; }
public bool IsVirtualObject { get; set; }
public int? ObjectModelIndex { get; set; }
public string ObjectPathId { get; set; }
public double VirtualObjectLength { get; set; }
public double VirtualObjectWidth { get; set; }
public double VirtualObjectHeight { get; set; }
public DateTime CreatedAt { get; set; }
}

View File

@ -74,7 +74,7 @@ namespace NavisworksTransport
private bool _showUnknownGrid = false;
private bool _showDoorGrid = false;
private GridMap _currentGridMap = null; // 保存当前网格地图用于刷新
private double _currentVehicleHeight = 2.0; // 保存当前车辆高度(米)用于网格刷新
private double _currentObjectHeight = 2.0; // 保存当前物体高度(米)用于网格刷新
private bool _isPreviewMode = false;
private int _previewInsertIndex = -1; // 保存预览点应该插入的索引位置
@ -141,7 +141,7 @@ namespace NavisworksTransport
LogManager.Info("[路径管理] ✅ PathPointRenderPlugin实例获取成功");
LogManager.Debug($"[路径管理] 渲染插件状态 - 启用: {_renderPlugin.IsEnabled}, 标记数量: {_renderPlugin.MarkerCount}");
// 推送默认的网格大小和车辆参数,确保渲染插件有合理的初始值
// 推送默认的网格大小和物体参数,确保渲染插件有合理的初始值
InitializeRenderPluginDefaults();
}
else
@ -205,7 +205,7 @@ namespace NavisworksTransport
/// <summary>
/// 初始化渲染插件的默认值
/// 从配置文件读取网格大小和车辆参数,确保渲染插件在任何情况下都有正确的可视化效果
/// 从配置文件读取网格大小和物体参数,确保渲染插件在任何情况下都有正确的可视化效果
/// </summary>
private void InitializeRenderPluginDefaults()
{
@ -1011,13 +1011,13 @@ namespace NavisworksTransport
/// </summary>
/// <param name="startPoint">起点</param>
/// <param name="endPoint">终点</param>
/// <param name="vehicleRadius">车辆尺寸(米)</param>
/// <param name="objectRadius">物体尺寸(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="gridSize">网格精度(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="objectHeight">物体高度(米)</param>
/// <param name="strategy">路径规划策略</param>
/// <returns>规划结果</returns>
public Task<PathRoute> AutoPlanPath(PathPoint startPoint, PathPoint endPoint, double vehicleRadius, double safetyMargin, double gridSize, double vehicleHeight, PathStrategy strategy)
public Task<PathRoute> AutoPlanPath(PathPoint startPoint, PathPoint endPoint, double objectRadius, double safetyMargin, double gridSize, double objectHeight, PathStrategy strategy)
{
try
{
@ -1029,7 +1029,7 @@ namespace NavisworksTransport
LogManager.Info($"开始自动路径规划: {startPoint.Name} -> {endPoint.Name}");
LogManager.Info($"起点坐标: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})");
LogManager.Info($"终点坐标: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})");
LogManager.Info($"车辆半径: {vehicleRadius}m, 安全间隙: {safetyMargin}m, 车辆高度: {vehicleHeight}m");
LogManager.Info($"物体半径: {objectRadius}m, 安全间隙: {safetyMargin}m, 物体高度: {objectHeight}m");
RaiseStatusChanged("正在进行自动路径规划...", PathPlanningStatusType.Info);
@ -1060,7 +1060,7 @@ namespace NavisworksTransport
// 获取当前文档
var document = Application.ActiveDocument;
LogManager.Info($"网格生成参数 - 边界: {bounds.Min.X:F2},{bounds.Min.Y:F2} -> {bounds.Max.X:F2},{bounds.Max.Y:F2}");
LogManager.Info($"网格生成参数 - 网格大小: {gridSize}m, 车辆半径: {vehicleRadius}m, 安全边距: {safetyMargin}m");
LogManager.Info($"网格生成参数 - 网格大小: {gridSize}m, 物体半径: {objectRadius}m, 安全边距: {safetyMargin}m");
LogManager.Info($"网格生成参数 - 起点: ({startPoint.Position.X:F2}, {startPoint.Position.Y:F2}, {startPoint.Position.Z:F2})");
LogManager.Info($"网格生成参数 - 终点: ({endPoint.Position.X:F2}, {endPoint.Position.Y:F2}, {endPoint.Position.Z:F2})");
@ -1069,11 +1069,11 @@ namespace NavisworksTransport
gridMap = gridMapGenerator.GenerateFromBIM(
bounds,
gridSize,
vehicleRadius,
objectRadius,
safetyMargin,
startPoint.Position,
endPoint.Position,
vehicleHeight
objectHeight
);
LogManager.Info("✅ 网格地图生成成功");
}
@ -1096,7 +1096,7 @@ namespace NavisworksTransport
if (_showWalkableGrid || _showObstacleGrid || _showUnknownGrid || _showDoorGrid)
{
LogManager.Info("开始网格可视化,显示所有可通行网格单元");
VisualizeGridCells(gridMap, vehicleHeight);
VisualizeGridCells(gridMap, objectHeight);
}
// 3. 获取通道高度数据
@ -1132,9 +1132,9 @@ namespace NavisworksTransport
try
{
pathFinder = new AutoPathFinder();
double vehicleHeightInModelUnits = vehicleHeight * UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
LogManager.Info($"使用2.5D模式进行路径查找,车辆高度: {vehicleHeight}m ({vehicleHeightInModelUnits:F2}模型单位),策略: {strategy}");
pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, vehicleHeightInModelUnits, strategy);
double objectHeightInModelUnits = objectHeight * UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
LogManager.Info($"使用2.5D模式进行路径查找,物体高度: {objectHeight}m ({objectHeightInModelUnits:F2}模型单位),策略: {strategy}");
pathResult = pathFinder.FindPath(startPoint.Position, endPoint.Position, gridMap, channelCoverage, objectHeightInModelUnits, strategy);
LogManager.Info("FindPath方法调用完成");
}
catch (Exception ex)
@ -1170,10 +1170,10 @@ namespace NavisworksTransport
// 保存GridMap和参数到PathRoute以便后续恢复网格可视化
autoRoute.AssociatedGridMap = gridMap;
autoRoute.GridSize = gridSize;
// 将vehicleRadius拆分为长宽暂时使用相同值后续可以传入更详细的参数
autoRoute.MaxVehicleLength = vehicleRadius * 2; // 车辆半径转换为长度
autoRoute.MaxVehicleWidth = vehicleRadius * 2; // 车辆半径转换为宽度
autoRoute.MaxVehicleHeight = vehicleHeight;
// 将objectRadius拆分为长宽暂时使用相同值后续可以传入更详细的参数
autoRoute.MaxObjectLength = objectRadius * 2; // 物体半径转换为长度
autoRoute.MaxObjectWidth = objectRadius * 2; // 物体半径转换为宽度
autoRoute.MaxObjectHeight = objectHeight;
autoRoute.SafetyMargin = safetyMargin;
LogManager.Info($"已保存GridMap到路径: {routeName}, 网格大小: {gridSize}米");
@ -4532,7 +4532,7 @@ namespace NavisworksTransport
if (_currentGridMap != null && IsAnyGridVisualizationEnabled)
{
LogManager.Info("[网格可视化] 使用缓存的网格地图重新渲染");
VisualizeGridCells(_currentGridMap, _currentVehicleHeight);
VisualizeGridCells(_currentGridMap, _currentObjectHeight);
}
else if (_currentGridMap == null)
{
@ -4554,8 +4554,8 @@ namespace NavisworksTransport
/// 在每个可通行网格的中心绘制一个绿色小球
/// </summary>
/// <param name="gridMap">要可视化的网格地图</param>
/// <param name="vehicleHeight">车辆高度(米),用于判断层高是否足够</param>
public void VisualizeGridCells(GridMap gridMap, double vehicleHeight = 2.0)
/// <param name="objectHeight">物体高度(米),用于判断层高是否足够</param>
public void VisualizeGridCells(GridMap gridMap, double objectHeight = 2.0)
{
if (gridMap == null)
{
@ -4565,10 +4565,10 @@ namespace NavisworksTransport
try
{
// 保存当前网格地图和车辆高度用于后续刷新
// 保存当前网格地图和物体高度用于后续刷新
_currentGridMap = gridMap;
_currentVehicleHeight = vehicleHeight;
LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}, 车辆高度:{vehicleHeight}m");
_currentObjectHeight = objectHeight;
LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}, 物体高度:{objectHeight}m");
// 获取渲染插件
var renderPlugin = PathPointRenderPlugin.Instance;

View File

@ -52,7 +52,7 @@ namespace NavisworksTransport
public enum PathType
{
/// <summary>
/// 地面路径 - 车辆在地面运行
/// 地面路径 - 物体在地面运行
/// </summary>
Ground = 0,
@ -188,7 +188,7 @@ namespace NavisworksTransport
Straightest = 1,
/// <summary>
/// 安全优先路径 - 基于障碍物距离选择安全路径,适合大型车辆居中行驶
/// 安全优先路径 - 基于障碍物距离选择安全路径,适合大型物体居中行驶
/// </summary>
SafestCenter = 2
}
@ -626,19 +626,19 @@ namespace NavisworksTransport
public double GridSize { get; set; }
/// <summary>
/// 最大车辆长度(米) - 路径适用的车辆长度限制
/// 最大物体长度(米) - 路径适用的物体长度限制
/// </summary>
public double MaxVehicleLength { get; set; }
public double MaxObjectLength { get; set; }
/// <summary>
/// 最大车辆宽度(米) - 路径适用的车辆宽度限制
/// 最大物体宽度(米) - 路径适用的物体宽度限制
/// </summary>
public double MaxVehicleWidth { get; set; }
public double MaxObjectWidth { get; set; }
/// <summary>
/// 最大车辆高度(米) - 路径适用的车辆高度限制
/// 最大物体高度(米) - 路径适用的物体高度限制
/// </summary>
public double MaxVehicleHeight { get; set; }
public double MaxObjectHeight { get; set; }
/// <summary>
/// 安全间隙(米)
@ -647,7 +647,7 @@ namespace NavisworksTransport
/// <summary>
/// 路径转弯半径(米)- 路径允许的最大转弯半径
/// 车辆的最小转弯半径必须小于等于此值
/// 物体的最小转弯半径必须小于等于此值
/// </summary>
public double TurnRadius { get; set; }
@ -932,9 +932,9 @@ namespace NavisworksTransport
CompletionPercentage = CompletionPercentage,
AssociatedGridMap = AssociatedGridMap, // 共享引用,不深拷贝
GridSize = GridSize,
MaxVehicleLength = MaxVehicleLength,
MaxVehicleWidth = MaxVehicleWidth,
MaxVehicleHeight = MaxVehicleHeight,
MaxObjectLength = MaxObjectLength,
MaxObjectWidth = MaxObjectWidth,
MaxObjectHeight = MaxObjectHeight,
SafetyMargin = SafetyMargin
};
@ -1356,7 +1356,7 @@ namespace NavisworksTransport
channelListView.Columns.Add("名称", 200);
channelListView.Columns.Add("类型", 80);
channelListView.Columns.Add("可通行", 60);
channelListView.Columns.Add("车辆尺寸", 80);
channelListView.Columns.Add("物体尺寸", 80);
channelListView.Columns.Add("长度(m)", 80);
channelListView.Columns.Add("宽度(m)", 80);
channelListView.Columns.Add("高度(m)", 80);
@ -1454,7 +1454,7 @@ namespace NavisworksTransport
// 获取通道属性信息
string channelType = "通道";
string traversable = "是";
string vehicleSize = "标准";
string objectSize = "标准";
try
{
@ -1482,7 +1482,7 @@ namespace NavisworksTransport
// 添加子项
item.SubItems.Add(channelType);
item.SubItems.Add(traversable);
item.SubItems.Add(vehicleSize);
item.SubItems.Add(objectSize);
item.SubItems.Add(length.ToString("F2"));
item.SubItems.Add(width.ToString("F2"));
item.SubItems.Add(height.ToString("F2"));

View File

@ -25,9 +25,9 @@ namespace NavisworksTransport
RibbonLine,
/// <summary>
/// 车辆通行空间模式 - 矩形通道
/// 物体通行空间模式 - 矩形通道
/// </summary>
VehicleSpace
ObjectSpace
}
/// <summary>
@ -78,9 +78,9 @@ namespace NavisworksTransport
Line,
/// <summary>
/// 车辆通行空间样式(灰色)
/// 物体通行空间样式(灰色)
/// </summary>
VehicleSpace,
ObjectSpace,
/// <summary>
/// 预览点样式(白色)
@ -166,9 +166,9 @@ namespace NavisworksTransport
}
/// <summary>
/// 车辆通行空间标记,用于渲染车辆通道空间
/// 物体通行空间标记,用于渲染物体通道空间
/// </summary>
public class VehicleSpaceMarker
public class ObjectSpaceMarker
{
/// <summary>
/// 通道起点
@ -212,7 +212,7 @@ namespace NavisworksTransport
public override string ToString()
{
return $"VehicleSpaceMarker[{FromIndex}->{ToIndex}, 宽度={Width:F2}m, 高度={Height:F2}m, 透明度={Alpha:F1}]";
return $"ObjectSpaceMarker[{FromIndex}->{ToIndex}, 宽度={Width:F2}m, 高度={Height:F2}m, 透明度={Alpha:F1}]";
}
}
@ -267,12 +267,12 @@ namespace NavisworksTransport
public List<Point3D> SampledPoints { get; set; }
/// <summary>
/// 带状连线高度(仅用于 RibbonLine 和 VehicleSpace 模式)
/// 带状连线高度(仅用于 RibbonLine 和 ObjectSpace 模式)
/// </summary>
public double Height { get; set; }
/// <summary>
/// 带状连线宽度(仅用于 VehicleSpace 模式)
/// 带状连线宽度(仅用于 ObjectSpace 模式)
/// </summary>
public double Width { get; set; }
@ -328,9 +328,9 @@ namespace NavisworksTransport
public List<SquareMarker> TangentMarkers { get; set; }
/// <summary>
/// 车辆通行空间标记集合
/// 物体通行空间标记集合
/// </summary>
public List<VehicleSpaceMarker> VehicleSpaceMarkers { get; set; }
public List<ObjectSpaceMarker> ObjectSpaceMarkers { get; set; }
/// <summary>
/// 是否显示控制点可视化(用户意图)
@ -351,7 +351,7 @@ namespace NavisworksTransport
ControlLineMarkers = new List<LineMarker>();
PathLineMarkers = new List<LineMarker>();
TangentMarkers = new List<SquareMarker>();
VehicleSpaceMarkers = new List<VehicleSpaceMarker>();
ObjectSpaceMarkers = new List<ObjectSpaceMarker>();
LastUpdated = DateTime.Now;
}
@ -388,7 +388,7 @@ namespace NavisworksTransport
// 独立可视化开关(替代单一模式)
private bool _showPathLines = true; // 显示路径线(地面连线/带状线)
private bool _showVehicleSpace = false; // 显示通行空间
private bool _showObjectSpace = false; // 显示通行空间
// 网格点类型配置
private GridPointType _gridPointType = GridPointType.Rectangle;
@ -464,14 +464,14 @@ namespace NavisworksTransport
public static PathPointRenderPlugin Instance => _instance;
/// <summary>
/// 路径可视化模式(向后兼容,根据 ShowPathLines 和 ShowVehicleSpace 计算)
/// 路径可视化模式(向后兼容,根据 ShowPathLines 和 ShowObjectSpace 计算)
/// </summary>
public PathVisualizationMode VisualizationMode
{
get
{
// 根据新的独立开关计算模式
if (_showVehicleSpace) return PathVisualizationMode.VehicleSpace;
if (_showObjectSpace) return PathVisualizationMode.ObjectSpace;
if (_showPathLines) return PathVisualizationMode.RibbonLine;
return PathVisualizationMode.StandardLine;
}
@ -479,7 +479,7 @@ namespace NavisworksTransport
{
_visualizationMode = value;
// 同步到新的独立开关
_showVehicleSpace = (value == PathVisualizationMode.VehicleSpace);
_showObjectSpace = (value == PathVisualizationMode.ObjectSpace);
_showPathLines = (value == PathVisualizationMode.RibbonLine || value == PathVisualizationMode.StandardLine);
// 模式改变时刷新所有普通路径(排除网格)
RefreshNormalPaths();
@ -500,14 +500,14 @@ namespace NavisworksTransport
}
/// <summary>
/// 是否显示通行空间(车辆通行空间)
/// 是否显示通行空间(物体通行空间)
/// </summary>
public bool ShowVehicleSpace
public bool ShowObjectSpace
{
get { return _showVehicleSpace; }
get { return _showObjectSpace; }
set
{
_showVehicleSpace = value;
_showObjectSpace = value;
RefreshNormalPaths();
}
}
@ -654,9 +654,9 @@ namespace NavisworksTransport
}
// 再渲染通行空间(可以在路径线之上)
if (_showVehicleSpace)
if (_showObjectSpace)
{
// VehicleSpace 模式:使用车辆通行空间渲染(高高度长方体)
// ObjectSpace 模式:使用物体通行空间渲染(高高度长方体)
foreach (var pathLineMarker in visualization.PathLineMarkers)
{
RenderRibbonLineMarker(graphics, pathLineMarker);
@ -1290,7 +1290,7 @@ namespace NavisworksTransport
visualization.ControlLineMarkers.Clear();
visualization.PathLineMarkers.Clear();
visualization.TangentMarkers.Clear();
visualization.VehicleSpaceMarkers.Clear();
visualization.ObjectSpaceMarkers.Clear();
var points = visualization.PathRoute.Points;
if (points.Count == 0) return;
@ -1315,7 +1315,7 @@ namespace NavisworksTransport
{
// 空中路径:默认只显示控制点连线
// 但如果启用了通行空间可视化,则构建路径连线以显示通行空间
if (_showVehicleSpace)
if (_showObjectSpace)
{
string aerialSubTypeName = visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail ? "空轨" : "吊装";
BuildPathLines(visualization, sortedPoints);
@ -1406,7 +1406,7 @@ namespace NavisworksTransport
Color lineColor;
double lineOpacity;
if (_showVehicleSpace)
if (_showObjectSpace)
{
// 通行空间模式:根据路径类型和段类型计算通行空间尺寸
// _passage* 和 _object* 字段已经是模型单位(由 SetPassageSpaceParameters 转换)
@ -1430,7 +1430,7 @@ namespace NavisworksTransport
height = _passageNormalToPath;
}
var renderStyle = GetRenderStyle(RenderStyleName.VehicleSpace);
var renderStyle = GetRenderStyle(RenderStyleName.ObjectSpace);
lineColor = renderStyle.Color;
lineOpacity = renderStyle.Alpha;
}
@ -1456,7 +1456,7 @@ namespace NavisworksTransport
// 计算通行空间的垂直偏移(根据路径类型)
double verticalOffset = 0;
if (_showVehicleSpace)
if (_showObjectSpace)
{
if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Ground)
{
@ -1549,7 +1549,7 @@ namespace NavisworksTransport
Point3D adjustedStartPoint;
Point3D adjustedEndPoint;
if (_showVehicleSpace && Math.Abs(verticalOffset) > 0.001)
if (_showObjectSpace && Math.Abs(verticalOffset) > 0.001)
{
// 通行空间模式且有垂直偏移时沿着up向量方向平移
adjustedStartPoint = new Point3D(
@ -1571,9 +1571,9 @@ namespace NavisworksTransport
}
// 🔥 地面/空轨路径水平段:起点和终点向路径方向延伸半个物体尺寸(仅通行空间)
if (_showVehicleSpace)
if (_showObjectSpace)
{
ExtendLineSegmentForVehicleSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
ExtendLineSegmentForObjectSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
}
var lineMarker = new LineMarker
@ -1620,7 +1620,7 @@ namespace NavisworksTransport
Point3D adjustedEndPoint;
List<Point3D> adjustedSampledPoints;
if (_showVehicleSpace && Math.Abs(verticalOffset) > 0.001)
if (_showObjectSpace && Math.Abs(verticalOffset) > 0.001)
{
// 通行空间模式且有垂直偏移时沿着up向量方向平移
adjustedStartPoint = new Point3D(
@ -1657,9 +1657,9 @@ namespace NavisworksTransport
}
// 🔥 圆弧段:起点和终点向路径方向延伸半个物体尺寸(仅通行空间)
if (_showVehicleSpace)
if (_showObjectSpace)
{
ExtendLineSegmentForVehicleSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
ExtendLineSegmentForObjectSpace(ref adjustedStartPoint, ref adjustedEndPoint, _passageAlongPath);
}
var arcMarker = new LineMarker
@ -1847,7 +1847,7 @@ namespace NavisworksTransport
Point3D adjustedStartPoint;
Point3D adjustedEndPoint;
if (_showVehicleSpace && Math.Abs(verticalOffset) > 0.001)
if (_showObjectSpace && Math.Abs(verticalOffset) > 0.001)
{
// 通行空间模式且有垂直偏移时沿着up向量方向平移
adjustedStartPoint = new Point3D(
@ -1869,7 +1869,7 @@ namespace NavisworksTransport
}
// 🔥 通行空间包裹调整:水平段起点和终点延伸以完全包裹物体(仅通行空间)
if (_showVehicleSpace && !isVerticalSegment)
if (_showObjectSpace && !isVerticalSegment)
{
// 沿路径方向的物体尺寸吊装路径根据是否转折90度选择
double alongPathSize = _passageAlongPath;
@ -1877,7 +1877,7 @@ namespace NavisworksTransport
{
alongPathSize = isTurn90Horizontal ? _passageAcrossPath : _objectLength;
}
ExtendLineSegmentForVehicleSpace(ref adjustedStartPoint, ref adjustedEndPoint, alongPathSize);
ExtendLineSegmentForObjectSpace(ref adjustedStartPoint, ref adjustedEndPoint, alongPathSize);
}
var lineMarker = new LineMarker
@ -1995,21 +1995,21 @@ namespace NavisworksTransport
}
/// <summary>
/// 创建车辆通行空间标记
/// 创建物体通行空间标记
/// </summary>
/// <param name="fromPoint">起点</param>
/// <param name="toPoint">终点</param>
/// <returns>车辆通行空间标记</returns>
private VehicleSpaceMarker CreateVehicleSpaceMarker(PathPoint fromPoint, PathPoint toPoint)
/// <returns>物体通行空间标记</returns>
private ObjectSpaceMarker CreateObjectSpaceMarker(PathPoint fromPoint, PathPoint toPoint)
{
var style = GetRenderStyle(RenderStyleName.VehicleSpace);
return new VehicleSpaceMarker
var style = GetRenderStyle(RenderStyleName.ObjectSpace);
return new ObjectSpaceMarker
{
StartPoint = fromPoint.Position,
EndPoint = toPoint.Position,
Width = _passageAcrossPath, // 直接使用模型单位
Height = _passageNormalToPath, // 直接使用模型单位
Color = style.Color, // 车辆通行空间颜色
Color = style.Color, // 物体通行空间颜色
Alpha = style.Alpha, // 透明度,统一管理
FromIndex = fromPoint.Index,
ToIndex = toPoint.Index
@ -2102,11 +2102,11 @@ namespace NavisworksTransport
}
/// <summary>
/// 设置车辆参数从PathPlanningManager同步
/// 设置物体参数从PathPlanningManager同步
/// </summary>
/// <param name="vehicleLength">车辆长度(米)</param>
/// <param name="vehicleWidth">车辆宽度(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="objectLength">物体长度(米)</param>
/// <param name="objectWidth">物体宽度(米)</param>
/// <param name="objectHeight">物体高度(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="passageNormalToPathVertical">垂直段的高度(物体长度 + 2×安全间隙模型单位</param>
/// <param name="passageNormalToPathHorizontal">水平段的高度(物体高度 + 2×安全间隙模型单位</param>
@ -2125,7 +2125,7 @@ namespace NavisworksTransport
_objectLength = passageNormalToPathVerticalInMeters * factor;
_objectHeight = passageNormalToPathHorizontalInMeters * factor;
// 参数改变时刷新所有普通路径因为RibbonLine模式也使用车辆宽度)
// 参数改变时刷新所有普通路径因为RibbonLine模式也使用物体宽度)
RefreshNormalPaths();
}
@ -2152,7 +2152,7 @@ namespace NavisworksTransport
case RenderStyleName.Line:
return new RenderStyle(Color.FromByteRGB(255, 152, 0), 0.8); // Material Orange连线20%透明
case RenderStyleName.VehicleSpace:
case RenderStyleName.ObjectSpace:
{
// 从配置读取通行空间透明度默认为0.440%不透明60%透明)
double opacity = ConfigManager.Instance.Current.Visualization.PassageSpaceOpacity;
@ -2759,7 +2759,7 @@ namespace NavisworksTransport
lineMarker.SampledPoints != null &&
lineMarker.SampledPoints.Count >= 2)
{
// 圆弧段:在每个采样点渲染完整的车辆长方体
// 圆弧段:在每个采样点渲染完整的物体长方体
RenderArcSegmentCuboids(graphics, lineMarker, width);
}
else
@ -2779,8 +2779,8 @@ namespace NavisworksTransport
}
/// <summary>
/// 渲染圆弧段的车辆长方体
/// 在每个采样点处渲染一个完整的车辆长方体,中心在采样点,向前后各延伸 L/2
/// 渲染圆弧段的物体长方体
/// 在每个采样点处渲染一个完整的物体长方体,中心在采样点,向前后各延伸 L/2
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="lineMarker">连线标记(必须是圆弧段)</param>
@ -2790,7 +2790,7 @@ namespace NavisworksTransport
// 设置颜色和透明度
graphics.Color(lineMarker.Color, lineMarker.Opacity);
// 在每个采样点处渲染完整的车辆长方体
// 在每个采样点处渲染完整的物体长方体
for (int i = 0; i < lineMarker.SampledPoints.Count; i++)
{
// 计算当前采样点的切线方向
@ -2992,10 +2992,10 @@ namespace NavisworksTransport
private void RenderLineMarker(Graphics graphics, LineMarker lineMarker)
{
// 🔥 路径线渲染逻辑:保持原有行为
// 注意ShowVehicleSpace 的通行空间使用 RenderRibbonLineMarker 单独渲染
if (_visualizationMode == PathVisualizationMode.VehicleSpace || _visualizationMode == PathVisualizationMode.RibbonLine)
// 注意ShowObjectSpace 的通行空间使用 RenderRibbonLineMarker 单独渲染
if (_visualizationMode == PathVisualizationMode.ObjectSpace || _visualizationMode == PathVisualizationMode.RibbonLine)
{
// VehicleSpace 或 RibbonLine 模式:使用长方体渲染
// ObjectSpace 或 RibbonLine 模式:使用长方体渲染
RenderRibbonLineMarker(graphics, lineMarker);
}
else
@ -3039,14 +3039,14 @@ namespace NavisworksTransport
}
/// <summary>
/// 延伸线段起点和终点以完全包裹物体(VehicleSpace模式
/// 延伸线段起点和终点以完全包裹物体(ObjectSpace模式
/// </summary>
/// <param name="startPoint">起点(会被修改)</param>
/// <param name="endPoint">终点(会被修改)</param>
private void ExtendLineSegmentForVehicleSpace(ref Point3D startPoint, ref Point3D endPoint, double alongPathSize)
private void ExtendLineSegmentForObjectSpace(ref Point3D startPoint, ref Point3D endPoint, double alongPathSize)
{
// 注意:调用此方法前应该检查 _showVehicleSpace
if (!_showVehicleSpace)
// 注意:调用此方法前应该检查 _showObjectSpace
if (!_showObjectSpace)
return;
var segDx = endPoint.X - startPoint.X;

View File

@ -90,7 +90,7 @@ namespace NavisworksTransport
= 9,
/// <summary>
/// 空轨 - 空中运输路径,车辆悬挂运行权重0.7
/// 空轨 - 空中运输路径,物体悬挂运行权重0.7
/// </summary>
= 10,

View File

@ -70,7 +70,7 @@ namespace NavisworksTransport.Core.Spatial
/// <summary>
/// 构建全局空间索引
/// </summary>
/// <param name="cellSizeInModelUnits">格子大小(模型单位),建议设置为车辆半径的2倍</param>
/// <param name="cellSizeInModelUnits">格子大小(模型单位),建议设置为物体半径的2倍</param>
public void BuildGlobalIndex(double cellSizeInModelUnits = 1.0)
{
var sw = Stopwatch.StartNew();

View File

@ -7,18 +7,18 @@ using Autodesk.Navisworks.Api;
namespace NavisworksTransport.Core
{
/// <summary>
/// 虚拟车辆管理器 - 负责创建和管理虚拟车辆几何体
/// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟车辆
/// 虚拟物体管理器 - 负责创建和管理虚拟物体几何体
/// 通过追加预制的单位立方体NWC文件并缩放来创建指定尺寸的虚拟物体
/// </summary>
public class VirtualVehicleManager
public class VirtualObjectManager
{
private static VirtualVehicleManager _instance;
private static VirtualObjectManager _instance;
private static readonly object _lock = new object();
/// <summary>
/// 单例实例
/// </summary>
public static VirtualVehicleManager Instance
public static VirtualObjectManager Instance
{
get
{
@ -28,7 +28,7 @@ namespace NavisworksTransport.Core
{
if (_instance == null)
{
_instance = new VirtualVehicleManager();
_instance = new VirtualObjectManager();
}
}
}
@ -36,167 +36,167 @@ namespace NavisworksTransport.Core
}
}
private ModelItem _virtualVehicleModelItem;
private Model _virtualVehicleModel;
private bool _isVirtualVehicleActive;
private ModelItem _virtualObjectModelItem;
private Model _virtualObjectModel;
private bool _isVirtualObjectActive;
// 🔥 新增:标记是否正在更新虚拟车辆,用于避免触发缓存重建
private bool _isUpdatingVirtualVehicle = false;
// 🔥 新增:标记是否正在更新虚拟物体,用于避免触发缓存重建
private bool _isUpdatingVirtualObject = false;
/// <summary>
/// 是否正在更新虚拟车辆(用于外部检测是否应该跳过缓存重建)
/// 是否正在更新虚拟物体(用于外部检测是否应该跳过缓存重建)
/// </summary>
public bool IsUpdatingVirtualVehicle => _isUpdatingVirtualVehicle;
public bool IsUpdatingVirtualObject => _isUpdatingVirtualObject;
// 🔥 记住虚拟车辆的尺寸变换参数(避免动态计算)
// 🔥 记住虚拟物体的尺寸变换参数(避免动态计算)
private double _currentLengthMeters = 0;
private double _currentWidthMeters = 0;
private double _currentHeightMeters = 0;
/// <summary>
/// 当前虚拟车辆ModelItem
/// 当前虚拟物体ModelItem
/// </summary>
public ModelItem CurrentVirtualVehicle => _virtualVehicleModelItem;
public ModelItem CurrentVirtualObject => _virtualObjectModelItem;
/// <summary>
/// 虚拟车辆是否激活
/// 虚拟物体是否激活
/// </summary>
public bool IsVirtualVehicleActive => _isVirtualVehicleActive;
public bool IsVirtualObjectActive => _isVirtualObjectActive;
private VirtualVehicleManager()
private VirtualObjectManager()
{
_isVirtualVehicleActive = false;
_isVirtualObjectActive = false;
}
/// <summary>
/// 显示虚拟车辆
/// 显示虚拟物体
/// </summary>
/// <param name="lengthMeters">长度(米)</param>
/// <param name="widthMeters">宽度(米)</param>
/// <param name="heightMeters">高度(米)</param>
public void ShowVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters)
public void ShowVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{
// 🔥 设置标志,防止文档变化事件触发缓存重建
_isUpdatingVirtualVehicle = true;
_isUpdatingVirtualObject = true;
try
{
var doc = Application.ActiveDocument;
// 检查虚拟车辆模型是否已存在
if (_virtualVehicleModel != null)
// 检查虚拟物体模型是否已存在
if (_virtualObjectModel != null)
{
int currentIndex = doc.Models.IndexOf(_virtualVehicleModel);
int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
if (currentIndex >= 0)
{
// 模型存在,显示它并更新尺寸
LogManager.Info($"虚拟车辆模型已存在(索引: {currentIndex}),显示并更新尺寸");
LogManager.Info($"虚拟物体模型已存在(索引: {currentIndex}),显示并更新尺寸");
var modelItems = new ModelItemCollection { _virtualVehicleModelItem };
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
doc.Models.SetHidden(modelItems, false);
// 获取实际的几何体项
_virtualVehicleModelItem = _virtualVehicleModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualVehicleModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualVehicleModelItem))
_virtualObjectModelItem = _virtualObjectModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
{
_virtualVehicleModelItem = geometryItem;
_virtualObjectModelItem = geometryItem;
}
// 清除覆盖变换(之前动画移动和旋转的累积)
modelItems.Clear();
modelItems.Add(_virtualVehicleModelItem);
modelItems.Add(_virtualObjectModelItem);
doc.Models.ResetPermanentTransform(modelItems);
// 重置变换并缩放到目标尺寸
ResetVirtualVehicleTransform();
ScaleVirtualVehicle(lengthMeters, widthMeters, heightMeters);
ResetVirtualObjectTransform();
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
_isVirtualVehicleActive = true;
LogManager.Info($"虚拟车辆更新成功");
_isVirtualObjectActive = true;
LogManager.Info($"虚拟物体更新成功");
return;
}
else
{
LogManager.Warning($"已保存的虚拟车辆模型不在文档中,需要重新创建");
LogManager.Warning($"已保存的虚拟物体模型不在文档中,需要重新创建");
}
}
// 模型不存在,创建新的
CreateVirtualVehicleInternal(doc, lengthMeters, widthMeters, heightMeters);
CreateVirtualObjectInternal(doc, lengthMeters, widthMeters, heightMeters);
}
catch (Exception ex)
{
LogManager.Error($"显示虚拟车辆失败: {ex.Message}");
LogManager.Error($"显示虚拟物体失败: {ex.Message}");
}
finally
{
// 🔥 清除标志
_isUpdatingVirtualVehicle = false;
_isUpdatingVirtualObject = false;
}
}
/// <summary>
/// 隐藏虚拟车辆
/// 隐藏虚拟物体
/// </summary>
public void HideVirtualVehicle()
public void HideVirtualObject()
{
try
{
if (_virtualVehicleModelItem != null)
if (_virtualObjectModelItem != null)
{
var doc = Application.ActiveDocument;
var modelItems = new ModelItemCollection { _virtualVehicleModelItem };
var modelItems = new ModelItemCollection { _virtualObjectModelItem };
doc.Models.SetHidden(modelItems, true);
LogManager.Info($"已隐藏虚拟车辆模型");
LogManager.Info($"已隐藏虚拟物体模型");
}
// 隐藏时设置为非激活状态,但保留模型引用
_isVirtualVehicleActive = false;
_isVirtualObjectActive = false;
}
catch (Exception ex)
{
LogManager.Error($"隐藏虚拟车辆失败: {ex.Message}");
_isVirtualVehicleActive = false;
LogManager.Error($"隐藏虚拟物体失败: {ex.Message}");
_isVirtualObjectActive = false;
}
}
/// <summary>
/// 创建虚拟车辆(内部方法)
/// 创建虚拟物体(内部方法)
/// </summary>
private void CreateVirtualVehicleInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
private void CreateVirtualObjectInternal(Document doc, double lengthMeters, double widthMeters, double heightMeters)
{
LogManager.Info($"=== 创建虚拟车辆 ===");
LogManager.Info($"=== 创建虚拟物体 ===");
LogManager.Info($"目标尺寸: {lengthMeters:F2}m × {widthMeters:F2}m × {heightMeters:F2}m");
// 加载新的虚拟车辆模型
LoadNewVirtualVehicleModel(doc);
// 加载新的虚拟物体模型
LoadNewVirtualObjectModel(doc);
// 获取实际的几何体项
_virtualVehicleModelItem = _virtualVehicleModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualVehicleModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualVehicleModelItem))
_virtualObjectModelItem = _virtualObjectModel.RootItem;
var geometryItem = FindFirstGeometryItem(_virtualObjectModelItem);
if (geometryItem != null && !geometryItem.Equals(_virtualObjectModelItem))
{
_virtualVehicleModelItem = geometryItem;
_virtualObjectModelItem = geometryItem;
}
// 重置变换并缩放到目标尺寸
ResetVirtualVehicleTransform();
ScaleVirtualVehicle(lengthMeters, widthMeters, heightMeters);
ResetVirtualObjectTransform();
ScaleVirtualObject(lengthMeters, widthMeters, heightMeters);
// 设置状态
_isVirtualVehicleActive = true;
_isVirtualObjectActive = true;
LogManager.Info($"虚拟车辆创建成功");
LogManager.Info($"虚拟物体创建成功");
}
/// <summary>
/// 加载新的虚拟车辆模型
/// 加载新的虚拟物体模型
/// </summary>
private void LoadNewVirtualVehicleModel(Document doc)
private void LoadNewVirtualObjectModel(Document doc)
{
LogManager.Info($"需要加载新的虚拟车辆模型");
LogManager.Info($"需要加载新的虚拟物体模型");
// 1. 获取单位立方体文件路径
var unitCubePath = GetUnitCubeFilePath();
@ -227,19 +227,19 @@ namespace NavisworksTransport.Core
}
// 保存模型引用(最后一个模型就是刚追加的)
_virtualVehicleModel = doc.Models.Last();
_virtualObjectModel = doc.Models.Last();
LogManager.Info($"虚拟车辆模型: {_virtualVehicleModel.FileName}");
LogManager.Info($"虚拟车辆根项: {_virtualVehicleModel.RootItem.DisplayName}");
LogManager.Info($"虚拟物体模型: {_virtualObjectModel.FileName}");
LogManager.Info($"虚拟物体根项: {_virtualObjectModel.RootItem.DisplayName}");
}
/// <summary>
/// <summary>
/// 重置虚拟车辆变换为单位矩阵(公开方法,供批处理使用)
/// 重置虚拟物体变换为单位矩阵(公开方法,供批处理使用)
/// </summary>
public void ResetVirtualVehicleTransform()
public void ResetVirtualObjectTransform()
{
if (_virtualVehicleModel == null) return;
if (_virtualObjectModel == null) return;
var doc = Application.ActiveDocument;
@ -248,19 +248,19 @@ namespace NavisworksTransport.Core
Transform3DComponents identityComponents = identityTransform.Factor();
// 应用单位变换
Units currentUnits = _virtualVehicleModel.Units;
doc.Models.SetModelUnitsAndTransform(_virtualVehicleModel, currentUnits, identityTransform, false);
Units currentUnits = _virtualObjectModel.Units;
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, identityTransform, false);
LogManager.Info("已重置虚拟车辆变换");
LogManager.Info("已重置虚拟物体变换");
}
/// <summary>
/// 缩放虚拟车辆到目标尺寸
/// 缩放虚拟物体到目标尺寸
/// 使用DocumentModels.SetModelUnitsAndTransform方法进行模型级缩放
/// </summary>
private void ScaleVirtualVehicle(double lengthMeters, double widthMeters, double heightMeters)
private void ScaleVirtualObject(double lengthMeters, double widthMeters, double heightMeters)
{
if (_virtualVehicleModel == null || _virtualVehicleModelItem == null) return;
if (_virtualObjectModel == null || _virtualObjectModelItem == null) return;
var doc = Application.ActiveDocument;
@ -272,21 +272,21 @@ namespace NavisworksTransport.Core
// 获取单位转换因子(米到模型单位)
double metersToUnits = Utils.UnitsConverter.GetMetersToUnitsConversionFactor(doc.Units);
// 🔥 对于虚拟车辆,直接应用缩放比例(不动态读取当前尺寸)
// 原因:虚拟车辆可能被旋转,导致包围盒尺寸不准确
// 🔥 对于虚拟物体,直接应用缩放比例(不动态读取当前尺寸)
// 原因:虚拟物体可能被旋转,导致包围盒尺寸不准确
// 单位立方体是 0.01m × 0.01m × 0.01m
// X方向 = 车辆长度前进方向Y方向 = 车辆宽度侧面Z方向 = 车辆高度
// X方向 = 物体长度前进方向Y方向 = 物体宽度侧面Z方向 = 物体高度
double baseSizeMeters = 0.01;
double scaleX = lengthMeters / baseSizeMeters; // X方向 = 车辆长度(前进方向)
double scaleY = widthMeters / baseSizeMeters; // Y方向 = 车辆宽度(侧面)
double scaleZ = heightMeters / baseSizeMeters; // Z方向 = 车辆高度
double scaleX = lengthMeters / baseSizeMeters; // X方向 = 物体长度(前进方向)
double scaleY = widthMeters / baseSizeMeters; // Y方向 = 物体宽度(侧面)
double scaleZ = heightMeters / baseSizeMeters; // Z方向 = 物体高度
LogManager.Info($"目标尺寸: 长度={lengthMeters:F2}m, 宽度={widthMeters:F2}m, 高度={heightMeters:F2}m");
LogManager.Info($"缩放比例: X(长度)={scaleX:F2}, Y(宽度)={scaleY:F2}, Z(高度)={scaleZ:F2}");
// 使用Transform3DComponents进行缩放
// 获取当前模型的变换并分解
Transform3D currentTransform = _virtualVehicleModel.Transform;
Transform3D currentTransform = _virtualObjectModel.Transform;
Transform3DComponents transformComponents = currentTransform.Factor();
// 获取当前值
@ -304,13 +304,13 @@ namespace NavisworksTransport.Core
Transform3D newTransform = transformComponents.Combine();
// 获取当前模型单位
Units currentUnits = _virtualVehicleModel.Units;
Units currentUnits = _virtualObjectModel.Units;
// 应用新的变换
doc.Models.SetModelUnitsAndTransform(_virtualVehicleModel, currentUnits, newTransform, false);
doc.Models.SetModelUnitsAndTransform(_virtualObjectModel, currentUnits, newTransform, false);
// 验证缩放结果
var newBoundingBox = _virtualVehicleModelItem.BoundingBox();
var newBoundingBox = _virtualObjectModelItem.BoundingBox();
double newLength = newBoundingBox.Max.X - newBoundingBox.Min.X;
double newWidth = newBoundingBox.Max.Y - newBoundingBox.Min.Y;
double newHeight = newBoundingBox.Max.Z - newBoundingBox.Min.Z;
@ -323,18 +323,18 @@ namespace NavisworksTransport.Core
}
/// <summary>
/// 移除虚拟车辆(使用 TryRemoveFile 真正删除模型)
/// 移除虚拟物体(使用 TryRemoveFile 真正删除模型)
/// </summary>
public void RemoveVirtualVehicle()
public void RemoveVirtualObject()
{
try
{
var doc = Application.ActiveDocument;
if (_virtualVehicleModel != null)
if (_virtualObjectModel != null)
{
// 动态查找模型索引
int currentIndex = doc.Models.IndexOf(_virtualVehicleModel);
int currentIndex = doc.Models.IndexOf(_virtualObjectModel);
if (currentIndex >= 0)
{
@ -343,30 +343,30 @@ namespace NavisworksTransport.Core
if (removed)
{
LogManager.Info($"已删除虚拟车辆模型(索引: {currentIndex}");
LogManager.Info($"已删除虚拟物体模型(索引: {currentIndex}");
}
else
{
LogManager.Warning($"删除虚拟车辆模型失败(索引: {currentIndex}");
LogManager.Warning($"删除虚拟物体模型失败(索引: {currentIndex}");
}
}
else
{
LogManager.Warning($"虚拟车辆模型不在文档中,无法删除");
LogManager.Warning($"虚拟物体模型不在文档中,无法删除");
}
}
// 清理引用
_virtualVehicleModel = null;
_virtualVehicleModelItem = null;
_isVirtualVehicleActive = false;
_virtualObjectModel = null;
_virtualObjectModelItem = null;
_isVirtualObjectActive = false;
}
catch (Exception ex)
{
LogManager.Error($"删除虚拟车辆失败: {ex.Message}");
_virtualVehicleModel = null;
_virtualVehicleModelItem = null;
_isVirtualVehicleActive = false;
LogManager.Error($"删除虚拟物体失败: {ex.Message}");
_virtualObjectModel = null;
_virtualObjectModelItem = null;
_isVirtualObjectActive = false;
}
}
@ -429,7 +429,7 @@ namespace NavisworksTransport.Core
/// </summary>
public void Cleanup()
{
RemoveVirtualVehicle();
RemoveVirtualObject();
}
}
}

View File

@ -123,14 +123,14 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 真3D路径规划使用通道覆盖数据、高度约束和路径策略
/// </summary>
public PathFindingResult FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, PathStrategy strategy = PathStrategy.Shortest)
public PathFindingResult FindPath(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, PathStrategy strategy = PathStrategy.Shortest)
{
try
{
LogManager.Info("[3D路径规划] 开始执行");
// 构建3D图
var graph3D = BuildGraphWithStrategy(gridMap, channelCoverage, vehicleHeight, start, end, strategy);
var graph3D = BuildGraphWithStrategy(gridMap, channelCoverage, objectHeight, start, end, strategy);
// 执行A*搜索
var pathResult = ExecuteAStarOnGraph(start, end, gridMap, graph3D);
@ -159,7 +159,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 构建3D图每个(x,y,layerIndex)作为独立节点)
/// </summary>
private Graph3DResult BuildGraph3D(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos, SpeedCalculator speedCalculator = null)
private Graph3DResult BuildGraph3D(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos, SpeedCalculator speedCalculator = null)
{
LogManager.Info("[3D图构建] 开始构建真3D图");
@ -200,7 +200,7 @@ namespace NavisworksTransport.PathPlanning
continue;
}
if (layer.PassableHeight.GetSpan() < vehicleHeight)
if (layer.PassableHeight.GetSpan() < objectHeight)
continue;
// 使用2D位置用于A*启发式)
@ -239,7 +239,7 @@ namespace NavisworksTransport.PathPlanning
for (int li = 0; li < cell.HeightLayers.Count; li++)
{
var currentLayer = cell.HeightLayers[li];
if (currentLayer.PassableHeight.GetSpan() < vehicleHeight)
if (currentLayer.PassableHeight.GetSpan() < objectHeight)
continue;
if (!nodes.TryGetValue((x, y, li), out var currentNode))
@ -263,7 +263,7 @@ namespace NavisworksTransport.PathPlanning
for (int nli = 0; nli < neighborCell.HeightLayers.Count; nli++)
{
var neighborLayer = neighborCell.HeightLayers[nli];
if (neighborLayer.PassableHeight.GetSpan() < vehicleHeight)
if (neighborLayer.PassableHeight.GetSpan() < objectHeight)
continue;
double heightDiff = Math.Abs(neighborLayer.Z - currentZ);
@ -331,8 +331,8 @@ namespace NavisworksTransport.PathPlanning
layer1.Type != CategoryAttributeManager.LogisticsElementType.)
continue;
if (layer1.PassableHeight.GetSpan() < vehicleHeight ||
layer2.PassableHeight.GetSpan() < vehicleHeight)
if (layer1.PassableHeight.GetSpan() < objectHeight ||
layer2.PassableHeight.GetSpan() < objectHeight)
continue;
double heightDiff = Math.Abs(layer2.Z - layer1.Z);
@ -390,7 +390,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 根据策略构建图
/// </summary>
private Graph3DResult BuildGraphWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
private Graph3DResult BuildGraphWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
{
SpeedCalculator speedCalc = null;
@ -415,7 +415,7 @@ namespace NavisworksTransport.PathPlanning
var currentPos = new GridPoint2D(cx, cy);
// 计算沿该方向能够直走的距离
int straightDistance = CalculateStraightDistance(currentPos, direction, gridMap, vehicleHeight);
int straightDistance = CalculateStraightDistance(currentPos, direction, gridMap, objectHeight);
// 直走距离越长,速度加成越高(最多+30 km/h
float bonusSpeed = Math.Min(straightDistance * 0.5f, 30.0f);
@ -458,7 +458,7 @@ namespace NavisworksTransport.PathPlanning
break;
}
return BuildGraph3D(gridMap, channelCoverage, vehicleHeight, startPos, endPos, speedCalc);
return BuildGraph3D(gridMap, channelCoverage, objectHeight, startPos, endPos, speedCalc);
}
/// <summary>
@ -723,7 +723,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 2.5D路径规划(使用通道覆盖数据、高度约束和路径策略)[已废弃,保留作为备份]
/// </summary>
private PathFindingResult FindPath_Legacy_2_5D(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, PathStrategy strategy = PathStrategy.Shortest)
private PathFindingResult FindPath_Legacy_2_5D(Point3D start, Point3D end, GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, PathStrategy strategy = PathStrategy.Shortest)
{
try
{
@ -735,19 +735,19 @@ namespace NavisworksTransport.PathPlanning
// 1. 检查起点和终点的高度约束
var startGridPos = gridMap.WorldToGrid(start);
if (!IsPointPassableAtHeight(startGridPos, start, gridMap, vehicleHeight))
if (!IsPointPassableAtHeight(startGridPos, start, gridMap, objectHeight))
{
LogManager.Warning("[2.5D路径规划] 起点位置不满足高度约束");
}
var endGridPos = gridMap.WorldToGrid(end);
if (!IsPointPassableAtHeight(endGridPos, end, gridMap, vehicleHeight))
if (!IsPointPassableAtHeight(endGridPos, end, gridMap, objectHeight))
{
LogManager.Warning("[2.5D路径规划] 终点位置不满足高度约束");
}
// 2. 根据策略选择合适的网格转换方法返回A*网格和激活层字典)
var (astarGrid, activeLayerZ) = ConvertToAStarGridWithStrategy(gridMap, channelCoverage, vehicleHeight, start, end, strategy);
var (astarGrid, activeLayerZ) = ConvertToAStarGridWithStrategy(gridMap, channelCoverage, objectHeight, start, end, strategy);
// 3. 执行A*算法
var pathResult = ExecuteAStarAlgorithm(start, end, gridMap, astarGrid);
@ -794,13 +794,13 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="channelCoverage">通道覆盖数据</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>A*算法网格</returns>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWith2_5D(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWith2_5D(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos)
{
try
{
LogManager.Info($"[A*转换-2.5D] 开始转换网格格式: {gridMap.Width}x{gridMap.Height}车辆高度: {vehicleHeight}m");
LogManager.Info($"[A*转换-2.5D] 开始转换网格格式: {gridMap.Width}x{gridMap.Height}物体高度: {objectHeight}m");
// 初始化激活层字典
var activeLayerZ = new Dictionary<GridPoint2D, double>();
@ -871,8 +871,8 @@ namespace NavisworksTransport.PathPlanning
totalNonWalkableCells++;
}
// 检查是否有满足车辆高度的层
if (!HasCompatibleLayer(cell, vehicleHeight))
// 检查是否有满足物体高度的层
if (!HasCompatibleLayer(cell, objectHeight))
{
if (cell.HasAnyWalkableLayer())
{
@ -882,7 +882,7 @@ namespace NavisworksTransport.PathPlanning
var layerInfo = cell.HeightLayers != null && cell.HeightLayers.Count > 0
? $"有{cell.HeightLayers.Count}个高度层"
: "无高度层";
LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:车辆高度{vehicleHeight:F2}模型单位,{layerInfo}");
LogManager.Info($"[A*高度约束] 单元格({x},{y})被高度约束排除:物体高度{objectHeight:F2}模型单位,{layerInfo}");
}
else if (heightConstrainedCells == 11)
{
@ -893,7 +893,7 @@ namespace NavisworksTransport.PathPlanning
}
// 为当前单元格选择最优高度层
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, vehicleHeight);
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, objectHeight);
if (!currentZ.HasValue)
continue;
@ -908,7 +908,7 @@ namespace NavisworksTransport.PathPlanning
if (x + 1 < gridMap.Width)
{
var rightCell = gridMap.Cells[x + 1, y];
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, vehicleHeight);
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, objectHeight);
if (rightZ.HasValue)
{
var rightPos = new GridPosition(x + 1, y);
@ -935,7 +935,7 @@ namespace NavisworksTransport.PathPlanning
if (y + 1 < gridMap.Height)
{
var bottomCell = gridMap.Cells[x, y + 1];
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, vehicleHeight);
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, objectHeight);
if (bottomZ.HasValue)
{
var bottomPos = new GridPosition(x, y + 1);
@ -954,7 +954,7 @@ namespace NavisworksTransport.PathPlanning
if (x - 1 >= 0)
{
var leftCell = gridMap.Cells[x - 1, y];
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, vehicleHeight);
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, objectHeight);
if (leftZ.HasValue)
{
var leftPos = new GridPosition(x - 1, y);
@ -973,7 +973,7 @@ namespace NavisworksTransport.PathPlanning
if (y - 1 >= 0)
{
var topCell = gridMap.Cells[x, y - 1];
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, vehicleHeight);
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, objectHeight);
if (topZ.HasValue)
{
var topPos = new GridPosition(x, y - 1);
@ -1013,30 +1013,30 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="channelCoverage">通道覆盖数据</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <param name="startPos">起点坐标</param>
/// <param name="endPos">终点坐标</param>
/// <param name="strategy">路径规划策略</param>
/// <returns>A*网格和激活层字典的元组</returns>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridWithStrategy(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos, PathStrategy strategy)
{
switch (strategy)
{
case PathStrategy.Shortest:
LogManager.Info($"[策略路由] 使用最短路径策略");
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, objectHeight, startPos, endPos);
case PathStrategy.Straightest:
LogManager.Info($"[策略路由] 使用直线优先策略");
return ConvertToAStarGridStraightest(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridStraightest(gridMap, channelCoverage, objectHeight, startPos, endPos);
case PathStrategy.SafestCenter:
LogManager.Info($"[策略路由] 使用安全优先策略");
return ConvertToAStarGridSafestCenter(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridSafestCenter(gridMap, channelCoverage, objectHeight, startPos, endPos);
default:
LogManager.Warning($"[策略路由] 未知策略 {strategy},使用默认最短路径");
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, vehicleHeight, startPos, endPos);
return ConvertToAStarGridWith2_5D(gridMap, channelCoverage, objectHeight, startPos, endPos);
}
}
@ -1046,11 +1046,11 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="channelCoverage">通道覆盖数据</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <param name="startPos">起点坐标</param>
/// <param name="endPos">终点坐标</param>
/// <returns>A*网格</returns>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridStraightest(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridStraightest(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos)
{
try
{
@ -1097,7 +1097,7 @@ namespace NavisworksTransport.PathPlanning
var cell = gridMap.Cells[x, y];
// 检查是否有兼容的高度层
if (!HasCompatibleLayer(cell, vehicleHeight))
if (!HasCompatibleLayer(cell, objectHeight))
{
if (cell.HasAnyWalkableLayer())
{
@ -1107,7 +1107,7 @@ namespace NavisworksTransport.PathPlanning
}
// 选择当前网格的最佳高度层
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, vehicleHeight);
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, objectHeight);
if (!currentZ.HasValue)
{
continue;
@ -1126,10 +1126,10 @@ namespace NavisworksTransport.PathPlanning
var rightCell = gridMap.Cells[x + 1, y];
// 检查右侧网格是否有兼容的高度层
if (HasCompatibleLayer(rightCell, vehicleHeight))
if (HasCompatibleLayer(rightCell, objectHeight))
{
// 选择右侧网格的最佳高度层
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, vehicleHeight);
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, objectHeight);
if (rightZ.HasValue)
{
var rightPos = new GridPosition(x + 1, y);
@ -1139,7 +1139,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[rightGridPos] = rightZ.Value;
// 计算沿右方向的直线距离
var rightDistance = CalculateStraightDistance(gridPos, new GridPoint2D(1, 0), gridMap, vehicleHeight);
var rightDistance = CalculateStraightDistance(gridPos, new GridPoint2D(1, 0), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1165,10 +1165,10 @@ namespace NavisworksTransport.PathPlanning
var bottomCell = gridMap.Cells[x, y + 1];
// 检查下方网格是否有兼容的高度层
if (HasCompatibleLayer(bottomCell, vehicleHeight))
if (HasCompatibleLayer(bottomCell, objectHeight))
{
// 选择下方网格的最佳高度层
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, vehicleHeight);
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, objectHeight);
if (bottomZ.HasValue)
{
var bottomPos = new GridPosition(x, y + 1);
@ -1178,7 +1178,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[bottomGridPos] = bottomZ.Value;
// 计算沿下方向的直线距离
var downDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, 1), gridMap, vehicleHeight);
var downDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, 1), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1204,10 +1204,10 @@ namespace NavisworksTransport.PathPlanning
var leftCell = gridMap.Cells[x - 1, y];
// 检查左侧网格是否有兼容的高度层
if (HasCompatibleLayer(leftCell, vehicleHeight))
if (HasCompatibleLayer(leftCell, objectHeight))
{
// 选择左侧网格的最佳高度层
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, vehicleHeight);
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, objectHeight);
if (leftZ.HasValue)
{
var leftPos = new GridPosition(x - 1, y);
@ -1217,7 +1217,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[leftGridPos] = leftZ.Value;
// 计算沿左方向的直线距离
var leftDistance = CalculateStraightDistance(gridPos, new GridPoint2D(-1, 0), gridMap, vehicleHeight);
var leftDistance = CalculateStraightDistance(gridPos, new GridPoint2D(-1, 0), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1243,10 +1243,10 @@ namespace NavisworksTransport.PathPlanning
var topCell = gridMap.Cells[x, y - 1];
// 检查上方网格是否有兼容的高度层
if (HasCompatibleLayer(topCell, vehicleHeight))
if (HasCompatibleLayer(topCell, objectHeight))
{
// 选择上方网格的最佳高度层
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, vehicleHeight);
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, objectHeight);
if (topZ.HasValue)
{
var topPos = new GridPosition(x, y - 1);
@ -1256,7 +1256,7 @@ namespace NavisworksTransport.PathPlanning
activeLayerZ[topGridPos] = topZ.Value;
// 计算沿上方向的直线距离
var upDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, -1), gridMap, vehicleHeight);
var upDistance = CalculateStraightDistance(gridPos, new GridPoint2D(0, -1), gridMap, objectHeight);
// 计算基础速度(基于直线距离)
float baseSpeed = 5.0f;
@ -1281,9 +1281,9 @@ namespace NavisworksTransport.PathPlanning
if (x + 1 < gridMap.Width && y + 1 < gridMap.Height)
{
var diagonalCell = gridMap.Cells[x + 1, y + 1];
if (HasCompatibleLayer(diagonalCell, vehicleHeight))
if (HasCompatibleLayer(diagonalCell, objectHeight))
{
var diagonalZ = SelectBestLayerForTarget(diagonalCell.HeightLayers, targetZ, vehicleHeight);
var diagonalZ = SelectBestLayerForTarget(diagonalCell.HeightLayers, targetZ, objectHeight);
if (diagonalZ.HasValue)
{
var diagonalPos = new GridPosition(x + 1, y + 1);
@ -1317,19 +1317,19 @@ namespace NavisworksTransport.PathPlanning
/// 检查单元格是否满足高度约束
/// </summary>
/// <param name="cell">网格单元格</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>是否可通行</returns>
private bool IsPassableWithHeight(GridCell cell, double vehicleHeight)
private bool IsPassableWithHeight(GridCell cell, double objectHeight)
{
if (!cell.HasAnyWalkableLayer())
return false;
// 检查是否有任何高度层满足车辆高度要求
// 检查是否有任何高度层满足物体高度要求
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
{
foreach (var layer in cell.HeightLayers)
{
if (layer.PassableHeight.GetSpan() >= vehicleHeight)
if (layer.PassableHeight.GetSpan() >= objectHeight)
return true;
}
return false;
@ -1339,12 +1339,12 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 检查单元格是否有满足车辆高度的高度层
/// 检查单元格是否有满足物体高度的高度层
/// </summary>
/// <param name="cell">网格单元格</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>是否有兼容的高度层</returns>
private bool HasCompatibleLayer(GridCell cell, double vehicleHeight)
private bool HasCompatibleLayer(GridCell cell, double objectHeight)
{
if (!cell.HasAnyWalkableLayer())
return false;
@ -1354,7 +1354,7 @@ namespace NavisworksTransport.PathPlanning
foreach (var layer in cell.HeightLayers)
{
if (layer.PassableHeight.GetSpan() >= vehicleHeight)
if (layer.PassableHeight.GetSpan() >= objectHeight)
return true;
}
@ -1362,13 +1362,13 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 从高度层列表中选择最接近目标高度且满足车辆高度的层
/// 从高度层列表中选择最接近目标高度且满足物体高度的层
/// </summary>
/// <param name="layers">高度层列表</param>
/// <param name="targetZ">目标高度(米)</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>选中的高度层Z坐标如果没有合适的返回null</returns>
private double? SelectBestLayerForTarget(List<HeightLayer> layers, double targetZ, double vehicleHeight)
private double? SelectBestLayerForTarget(List<HeightLayer> layers, double targetZ, double objectHeight)
{
if (layers == null || layers.Count == 0)
return null;
@ -1378,8 +1378,8 @@ namespace NavisworksTransport.PathPlanning
foreach (var layer in layers)
{
// 必须满足车辆高度
if (layer.PassableHeight.GetSpan() < vehicleHeight)
// 必须满足物体高度
if (layer.PassableHeight.GetSpan() < objectHeight)
continue;
// 选择最接近目标高度的层
@ -1414,16 +1414,16 @@ namespace NavisworksTransport.PathPlanning
/// <param name="startPos">起始网格位置</param>
/// <param name="direction">方向 (1,0)=右, (0,1)=下, (-1,0)=左, (0,-1)=上</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>直线距离(网格单位)</returns>
private int CalculateStraightDistance(GridPoint2D startPos, GridPoint2D direction,
GridMap gridMap, double vehicleHeight)
GridMap gridMap, double objectHeight)
{
int distance = 0;
var currentPos = new GridPoint2D(startPos.X + direction.X, startPos.Y + direction.Y);
// 沿方向前进,直到遇到障碍
while (IsValidAndPassable(currentPos, gridMap, vehicleHeight))
while (IsValidAndPassable(currentPos, gridMap, objectHeight))
{
distance++;
currentPos = new GridPoint2D(currentPos.X + direction.X, currentPos.Y + direction.Y);
@ -1440,9 +1440,9 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="pos">网格位置</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>是否有效且可通行</returns>
private bool IsValidAndPassable(GridPoint2D pos, GridMap gridMap, double vehicleHeight)
private bool IsValidAndPassable(GridPoint2D pos, GridMap gridMap, double objectHeight)
{
// 检查边界
if (pos.X < 0 || pos.X >= gridMap.Width || pos.Y < 0 || pos.Y >= gridMap.Height)
@ -1450,7 +1450,7 @@ namespace NavisworksTransport.PathPlanning
// 检查可通行性
var cell = gridMap.Cells[pos.X, pos.Y];
return IsPassableWithHeight(cell, vehicleHeight);
return IsPassableWithHeight(cell, objectHeight);
}
/// <summary>
@ -2034,9 +2034,9 @@ namespace NavisworksTransport.PathPlanning
/// <param name="gridPos">网格坐标</param>
/// <param name="point">检查点(世界坐标)</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <returns>是否可通行</returns>
private bool IsPointPassableAtHeight(GridPoint2D gridPos, Point3D point, GridMap gridMap, double vehicleHeight)
private bool IsPointPassableAtHeight(GridPoint2D gridPos, Point3D point, GridMap gridMap, double objectHeight)
{
try
{
@ -2062,19 +2062,19 @@ namespace NavisworksTransport.PathPlanning
// 检查高度层约束
if (cell.HeightLayers != null && cell.HeightLayers.Count > 0)
{
// 查找包含指定Z坐标且满足车辆高度的层
// 查找包含指定Z坐标且满足物体高度的层
var matchingLayer = gridMap.FindLayerContainingZ(gridPos, point.Z, tolerance: 0.5);
if (matchingLayer.HasValue)
{
bool heightOk = matchingLayer.Value.PassableHeight.GetSpan() >= vehicleHeight;
bool heightOk = matchingLayer.Value.PassableHeight.GetSpan() >= objectHeight;
if (heightOk)
{
return true;
}
else
{
LogManager.Warning($"[高度检查] ❌ 找到匹配Z={point.Z:F2}的层Z={matchingLayer.Value.Z:F2},但高度不足:车辆高度{vehicleHeight:F2},层高度跨度{matchingLayer.Value.PassableHeight.GetSpan():F2}");
LogManager.Warning($"[高度检查] ❌ 找到匹配Z={point.Z:F2}的层Z={matchingLayer.Value.Z:F2},但高度不足:物体高度{objectHeight:F2},层高度跨度{matchingLayer.Value.PassableHeight.GetSpan():F2}");
}
}
else
@ -2099,11 +2099,11 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="pathWithGridCoords">路径点和对应的网格坐标</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
/// <param name="objectHeight">物体高度(模型单位)</param>
/// <param name="originalStart">用户指定的原始起点保留原始Z坐标</param>
/// <param name="originalEnd">用户指定的原始终点保留原始Z坐标</param>
/// <returns>调整后的路径</returns>
private List<Point3D> ApplyGridHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double vehicleHeight, Point3D originalStart, Point3D originalEnd)
private List<Point3D> ApplyGridHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double objectHeight, Point3D originalStart, Point3D originalEnd)
{
LogManager.Info($"[智能选层] 开始为路径点选择最优高度层,路径点数: {pathWithGridCoords.Count}");
@ -2148,7 +2148,7 @@ namespace NavisworksTransport.PathPlanning
// 起点和终点锁定到包含其Z坐标的层
if (i == 0)
{
var selectedLayer = SelectBestLayer(cell.HeightLayers, startZ, vehicleHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
var selectedLayer = SelectBestLayer(cell.HeightLayers, startZ, objectHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
if (selectedLayer.HasValue)
{
adjustedPath.Add(new Point3D(point.X, point.Y, selectedLayer.Value.Z));
@ -2161,7 +2161,7 @@ namespace NavisworksTransport.PathPlanning
}
else if (i == pathWithGridCoords.Count - 1)
{
var selectedLayer = SelectBestLayer(cell.HeightLayers, endZ, vehicleHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
var selectedLayer = SelectBestLayer(cell.HeightLayers, endZ, objectHeight, startZ, endZ, i, pathWithGridCoords.Count, true);
if (selectedLayer.HasValue)
{
adjustedPath.Add(new Point3D(point.X, point.Y, selectedLayer.Value.Z));
@ -2176,7 +2176,7 @@ namespace NavisworksTransport.PathPlanning
{
// 中间点:根据高度趋势选择最佳层
var prevZ = adjustedPath[i - 1].Z;
var selectedLayer = SelectBestLayer(cell.HeightLayers, prevZ, vehicleHeight, startZ, endZ, i, pathWithGridCoords.Count, false);
var selectedLayer = SelectBestLayer(cell.HeightLayers, prevZ, objectHeight, startZ, endZ, i, pathWithGridCoords.Count, false);
if (selectedLayer.HasValue)
{
@ -2260,14 +2260,14 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="layers">可用的高度层列表</param>
/// <param name="referenceZ">参考Z坐标起点/终点为目标Z中间点为前一点Z</param>
/// <param name="vehicleHeight">车辆高度</param>
/// <param name="objectHeight">物体高度</param>
/// <param name="startZ">路径起点Z</param>
/// <param name="endZ">路径终点Z</param>
/// <param name="currentIndex">当前点索引</param>
/// <param name="totalPoints">总点数</param>
/// <param name="exactMatch">是否精确匹配(起点/终点使用)</param>
/// <returns>选中的高度层如果没有合适的返回null</returns>
private HeightLayer? SelectBestLayer(List<HeightLayer> layers, double referenceZ, double vehicleHeight,
private HeightLayer? SelectBestLayer(List<HeightLayer> layers, double referenceZ, double objectHeight,
double startZ, double endZ, int currentIndex, int totalPoints, bool exactMatch)
{
if (layers == null || layers.Count == 0)
@ -2277,7 +2277,7 @@ namespace NavisworksTransport.PathPlanning
if (layers.Count == 1)
{
var layer = layers[0];
if (layer.PassableHeight.GetSpan() >= vehicleHeight)
if (layer.PassableHeight.GetSpan() >= objectHeight)
return layer;
return null;
}
@ -2307,13 +2307,13 @@ namespace NavisworksTransport.PathPlanning
}
}
// 选择最接近目标Z且满足车辆高度要求的层
// 选择最接近目标Z且满足物体高度要求的层
HeightLayer? bestLayer = null;
double minDistance = double.MaxValue;
foreach (var layer in layers)
{
if (layer.PassableHeight.GetSpan() < vehicleHeight)
if (layer.PassableHeight.GetSpan() < objectHeight)
continue;
double distance = Math.Abs(layer.Z - targetZ);
@ -2439,8 +2439,8 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="pathWithGridCoords">路径点和对应的网格坐标</param>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleHeight">车辆高度(模型单位)</param>
private void ValidatePathHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double vehicleHeight)
/// <param name="objectHeight">物体高度(模型单位)</param>
private void ValidatePathHeightConstraints(List<(Point3D point, GridPoint2D gridPos)> pathWithGridCoords, GridMap gridMap, double objectHeight)
{
LogManager.Info($"[路径高度验证] 开始验证 {pathWithGridCoords.Count} 个路径点的高度约束");
@ -2466,7 +2466,7 @@ namespace NavisworksTransport.PathPlanning
}
// 进行高度约束检查
bool isPassable = IsPointPassableAtHeight(gridPos, point, gridMap, vehicleHeight);
bool isPassable = IsPointPassableAtHeight(gridPos, point, gridMap, objectHeight);
if (isPassable)
{
passedPoints++;
@ -2595,7 +2595,7 @@ namespace NavisworksTransport.PathPlanning
/// 安全优先的A*网格转换方法
/// 🔥 重写:参照局部直线优先算法的成功模式,重新构建网格连接
/// </summary>
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridSafestCenter(GridMap gridMap, ChannelCoverage channelCoverage, double vehicleHeight, Point3D startPos, Point3D endPos)
private (Grid grid, Dictionary<GridPoint2D, double> activeLayerZ) ConvertToAStarGridSafestCenter(GridMap gridMap, ChannelCoverage channelCoverage, double objectHeight, Point3D startPos, Point3D endPos)
{
try
{
@ -2648,7 +2648,7 @@ namespace NavisworksTransport.PathPlanning
var cell = gridMap.Cells[x, y];
// 检查是否有兼容的高度层
if (!HasCompatibleLayer(cell, vehicleHeight))
if (!HasCompatibleLayer(cell, objectHeight))
{
if (cell.HasAnyWalkableLayer())
{
@ -2658,7 +2658,7 @@ namespace NavisworksTransport.PathPlanning
}
// 选择当前网格的最佳高度层
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, vehicleHeight);
var currentZ = SelectBestLayerForTarget(cell.HeightLayers, targetZ, objectHeight);
if (!currentZ.HasValue)
{
continue;
@ -2678,10 +2678,10 @@ namespace NavisworksTransport.PathPlanning
var rightCell = gridMap.Cells[x + 1, y];
// 检查右侧网格是否有兼容的高度层
if (HasCompatibleLayer(rightCell, vehicleHeight))
if (HasCompatibleLayer(rightCell, objectHeight))
{
// 选择右侧网格的最佳高度层
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, vehicleHeight);
var rightZ = SelectBestLayerForTarget(rightCell.HeightLayers, targetZ, objectHeight);
if (rightZ.HasValue)
{
var rightPos = new GridPosition(x + 1, y);
@ -2720,10 +2720,10 @@ namespace NavisworksTransport.PathPlanning
var bottomCell = gridMap.Cells[x, y + 1];
// 检查下方网格是否有兼容的高度层
if (HasCompatibleLayer(bottomCell, vehicleHeight))
if (HasCompatibleLayer(bottomCell, objectHeight))
{
// 选择下方网格的最佳高度层
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, vehicleHeight);
var bottomZ = SelectBestLayerForTarget(bottomCell.HeightLayers, targetZ, objectHeight);
if (bottomZ.HasValue)
{
var bottomPos = new GridPosition(x, y + 1);
@ -2762,10 +2762,10 @@ namespace NavisworksTransport.PathPlanning
var leftCell = gridMap.Cells[x - 1, y];
// 检查左侧网格是否有兼容的高度层
if (HasCompatibleLayer(leftCell, vehicleHeight))
if (HasCompatibleLayer(leftCell, objectHeight))
{
// 选择左侧网格的最佳高度层
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, vehicleHeight);
var leftZ = SelectBestLayerForTarget(leftCell.HeightLayers, targetZ, objectHeight);
if (leftZ.HasValue)
{
var leftPos = new GridPosition(x - 1, y);
@ -2804,10 +2804,10 @@ namespace NavisworksTransport.PathPlanning
var topCell = gridMap.Cells[x, y - 1];
// 检查上方网格是否有兼容的高度层
if (HasCompatibleLayer(topCell, vehicleHeight))
if (HasCompatibleLayer(topCell, objectHeight))
{
// 选择上方网格的最佳高度层
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, vehicleHeight);
var topZ = SelectBestLayerForTarget(topCell.HeightLayers, targetZ, objectHeight);
if (topZ.HasValue)
{
var topPos = new GridPosition(x, y - 1);

View File

@ -13,7 +13,7 @@ namespace NavisworksTransport.PathPlanning
///
/// 术语说明:
/// - "通道"指所有可通行的建筑结构(楼板、走廊、过道等)
/// - 楼板:车辆在其顶面行驶,底面用于结构支撑
/// - 楼板:物体在其顶面行驶,底面用于结构支撑
/// - 走廊/过道:传统意义的通道空间
/// </summary>
public class ChannelBasedGridBuilder

View File

@ -45,7 +45,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 获取指定位置的通道地面高度(底面高度)
/// 注意:此方法返回楼板/通道结构的底面高度,用于传统的高度检测
/// 对于车辆路径规划建议使用GetChannelTopHeight获取顶面行驶表面高度
/// 对于物体路径规划建议使用GetChannelTopHeight获取顶面行驶表面高度
/// </summary>
/// <param name="position">世界坐标位置</param>
/// <param name="channelItems">通道模型项集合(可能包含楼板、走廊等可通行结构)</param>
@ -101,17 +101,17 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 获取通道顶面高度(车辆行驶表面)
/// 专门用于车辆路径规划的高度校正,返回楼板/通道结构的顶面高度
/// 获取通道顶面高度(物体行驶表面)
/// 专门用于物体路径规划的高度校正,返回楼板/通道结构的顶面高度
///
/// 应用场景:
/// - 楼板结构:车辆在楼板顶面行驶,而不是底面
/// - 楼板结构:物体在楼板顶面行驶,而不是底面
/// - 走廊/通道:返回可行驶表面的高度
/// - 路径规划:确保车辆路径点位于正确的行驶表面
/// - 路径规划:确保物体路径点位于正确的行驶表面
/// </summary>
/// <param name="position">位置坐标</param>
/// <param name="channelItems">通道模型项集合(包含楼板、走廊等可通行结构)</param>
/// <returns>通道顶面高度(米),即车辆实际行驶的表面高度</returns>
/// <returns>通道顶面高度(米),即物体实际行驶的表面高度</returns>
public double GetChannelTopHeight(Point3D position, IEnumerable<ModelItem> channelItems)
{
try
@ -144,7 +144,7 @@ namespace NavisworksTransport.PathPlanning
{
_heightCache[cacheKey] = heightInfo;
// 🔥 关键修改:返回通道顶面高度(楼板表面,车辆行驶面)
// 🔥 关键修改:返回通道顶面高度(楼板表面,物体行驶面)
var topHeight = heightInfo.CeilingHeight;
//LogManager.Info($"[高度检测] 计算得到通道顶面高度: {topHeight:F2}");
return topHeight;

View File

@ -60,7 +60,7 @@ namespace NavisworksTransport.PathPlanning
/// <summary>
/// 通道模型项集合(用于高度检测)
/// 包含所有可通行的建筑结构:楼板、走廊、过道等
/// 注意:楼板类通道的高度计算应使用顶面(车辆行驶面)
/// 注意:楼板类通道的高度计算应使用顶面(物体行驶面)
/// </summary>
public IEnumerable<ModelItem> ChannelItems { get; set; }
@ -860,7 +860,7 @@ namespace NavisworksTransport.PathPlanning
public double Z { get; set; }
/// <summary>
/// 可通行高度范围(用于车辆检查)
/// 可通行高度范围(用于物体检查)
/// </summary>
public HeightInterval PassableHeight { get; set; }

View File

@ -8,7 +8,7 @@ namespace NavisworksTransport.PathPlanning
{
/// <summary>
/// GridMap缓存键用于标识唯一的GridMap生成配置
/// 基于所有物流属性构件、车辆尺寸、网格尺寸等关键参数
/// 基于所有物流属性构件、物体尺寸、网格尺寸等关键参数
/// </summary>
public class GridMapCacheKey : IEquatable<GridMapCacheKey>
{
@ -28,9 +28,9 @@ namespace NavisworksTransport.PathPlanning
public double CellSize { get; set; }
/// <summary>
/// 车辆半径(米)
/// 物体半径(米)
/// </summary>
public double VehicleRadius { get; set; }
public double ObjectRadius { get; set; }
/// <summary>
/// 安全间隙(米)
@ -38,22 +38,22 @@ namespace NavisworksTransport.PathPlanning
public double SafetyMargin { get; set; }
/// <summary>
/// 车辆高度(米)
/// 物体高度(米)
/// </summary>
public double VehicleHeight { get; set; }
public double ObjectHeight { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public GridMapCacheKey(string logisticsDataHash, string boundsHash, double cellSize,
double vehicleRadius, double safetyMargin, double vehicleHeight)
double objectRadius, double safetyMargin, double objectHeight)
{
LogisticsDataHash = logisticsDataHash ?? throw new ArgumentNullException(nameof(logisticsDataHash));
BoundsHash = boundsHash ?? throw new ArgumentNullException(nameof(boundsHash));
CellSize = cellSize;
VehicleRadius = vehicleRadius;
ObjectRadius = objectRadius;
SafetyMargin = safetyMargin;
VehicleHeight = vehicleHeight;
ObjectHeight = objectHeight;
}
/// <summary>
@ -67,9 +67,9 @@ namespace NavisworksTransport.PathPlanning
return LogisticsDataHash == other.LogisticsDataHash &&
BoundsHash == other.BoundsHash &&
Math.Abs(CellSize - other.CellSize) < 1e-6 &&
Math.Abs(VehicleRadius - other.VehicleRadius) < 1e-6 &&
Math.Abs(ObjectRadius - other.ObjectRadius) < 1e-6 &&
Math.Abs(SafetyMargin - other.SafetyMargin) < 1e-6 &&
Math.Abs(VehicleHeight - other.VehicleHeight) < 1e-6;
Math.Abs(ObjectHeight - other.ObjectHeight) < 1e-6;
}
/// <summary>
@ -91,9 +91,9 @@ namespace NavisworksTransport.PathPlanning
hash = hash * 23 + (LogisticsDataHash?.GetHashCode() ?? 0);
hash = hash * 23 + (BoundsHash?.GetHashCode() ?? 0);
hash = hash * 23 + CellSize.GetHashCode();
hash = hash * 23 + VehicleRadius.GetHashCode();
hash = hash * 23 + ObjectRadius.GetHashCode();
hash = hash * 23 + SafetyMargin.GetHashCode();
hash = hash * 23 + VehicleHeight.GetHashCode();
hash = hash * 23 + ObjectHeight.GetHashCode();
return hash;
}
}
@ -111,7 +111,7 @@ namespace NavisworksTransport.PathPlanning
"NULL";
return $"GridMapKey[Logistics={logisticsPreview}, Bounds={boundsPreview}, " +
$"Cell={CellSize:F2}, VR={VehicleRadius:F1}m, SM={SafetyMargin:F1}m, VH={VehicleHeight:F1}m]";
$"Cell={CellSize:F2}, OR={ObjectRadius:F1}m, SM={SafetyMargin:F1}m, OH={ObjectHeight:F1}m]";
}
/// <summary>
@ -119,19 +119,19 @@ namespace NavisworksTransport.PathPlanning
/// </summary>
/// <param name="bounds">边界范围</param>
/// <param name="cellSize">网格大小</param>
/// <param name="vehicleRadius">车辆半径(米)</param>
/// <param name="objectRadius">物体半径(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="objectHeight">物体高度(米)</param>
/// <returns>缓存键</returns>
public static GridMapCacheKey CreateFrom(BoundingBox3D bounds,
double cellSize, double vehicleRadius,
double safetyMargin, double vehicleHeight)
double cellSize, double objectRadius,
double safetyMargin, double objectHeight)
{
var logisticsDataHash = ComputeLogisticsDataHash();
var boundsHash = ComputeBoundsHash(bounds);
return new GridMapCacheKey(logisticsDataHash, boundsHash, cellSize,
vehicleRadius, safetyMargin, vehicleHeight);
objectRadius, safetyMargin, objectHeight);
}
/// <summary>

View File

@ -58,35 +58,35 @@ namespace NavisworksTransport.PathPlanning
/// <param name="document">Navisworks文档</param>
/// <param name="bounds">扫描边界</param>
/// <param name="cellSize">网格单元大小(米)</param>
/// <param name="vehicleRadius">车辆半径(米)</param>
/// <param name="objectRadius">物体半径(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
/// <param name="planningStartPoint">规划起点</param>
/// <param name="planningEndPoint">规划终点</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="objectHeight">物体高度(米)</param>
/// <returns>生成的网格地图</returns>
public GridMap GenerateFromBIM(BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double vehicleHeight)
public GridMap GenerateFromBIM(BoundingBox3D bounds, double cellSize, double objectRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double objectHeight)
{
try
{
// 第一步:统一转换所有米制参数为模型单位
double metersToModelUnitsConversionFactor = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
double cellSizeInModelUnits = cellSize * metersToModelUnitsConversionFactor;
double vehicleRadiusInModelUnits = vehicleRadius * metersToModelUnitsConversionFactor;
double objectRadiusInModelUnits = objectRadius * metersToModelUnitsConversionFactor;
double safetyMarginInModelUnits = safetyMargin * metersToModelUnitsConversionFactor;
double vehicleHeightInModelUnits = vehicleHeight * metersToModelUnitsConversionFactor;
double scanHeightInModelUnits = vehicleHeightInModelUnits + safetyMarginInModelUnits; // 扫描高度 = 车辆高度 + 安全间隙
double totalInflationRadiusInModelUnits = vehicleRadiusInModelUnits + safetyMarginInModelUnits; // 膨胀半径 = 车辆半径 + 安全间隙
double objectHeightInModelUnits = objectHeight * metersToModelUnitsConversionFactor;
double scanHeightInModelUnits = objectHeightInModelUnits + safetyMarginInModelUnits; // 扫描高度 = 物体高度 + 安全间隙
double totalInflationRadiusInModelUnits = objectRadiusInModelUnits + safetyMarginInModelUnits; // 膨胀半径 = 物体半径 + 安全间隙
LogManager.Info($"【生成网格地图】参数单位转换完成 (转换系数: {metersToModelUnitsConversionFactor:F2}):");
LogManager.Info($" 网格大小: {cellSize}米 → {cellSizeInModelUnits:F2}模型单位");
LogManager.Info($" 车辆半径: {vehicleRadius}米 → {vehicleRadiusInModelUnits:F2}模型单位");
LogManager.Info($" 物体半径: {objectRadius}米 → {objectRadiusInModelUnits:F2}模型单位");
LogManager.Info($" 安全间隙: {safetyMargin}米 → {safetyMarginInModelUnits:F2}模型单位");
LogManager.Info($" 车辆高度: {vehicleHeight}米 → {vehicleHeightInModelUnits:F2}模型单位");
LogManager.Info($" 物体高度: {objectHeight}米 → {objectHeightInModelUnits:F2}模型单位");
LogManager.Info($" 扫描高度: {scanHeightInModelUnits:F2}模型单位");
LogManager.Info($" 膨胀半径: {totalInflationRadiusInModelUnits:F2}模型单位");
// 生成缓存键(使用转换后的模型单位参数确保一致性)
var cacheKey = GridMapCacheKey.CreateFrom(bounds, cellSizeInModelUnits, vehicleRadiusInModelUnits, safetyMarginInModelUnits, vehicleHeightInModelUnits);
var cacheKey = GridMapCacheKey.CreateFrom(bounds, cellSizeInModelUnits, objectRadiusInModelUnits, safetyMarginInModelUnits, objectHeightInModelUnits);
// 尝试从缓存获取
var cachedGridMap = GlobalGridMapCache.Instance.Get(cacheKey, 5000); // 预估生成需要5秒
@ -162,13 +162,13 @@ namespace NavisworksTransport.PathPlanning
MarkBoundaryLayers(channelCoverage.GridMap, maxHeightDiff);
LogManager.Info($"【阶段2.7完成】边界层标记后网格统计: {channelCoverage.GridMap.GetStatistics()}");
// 3. 应用车辆尺寸膨胀
if (vehicleRadius > 0 || safetyMargin > 0)
// 3. 应用物体尺寸膨胀
if (objectRadius > 0 || safetyMargin > 0)
{
LogManager.Info("【生成网格地图】步骤3: 应用车辆膨胀");
ApplyVehicleInflation(channelCoverage.GridMap, totalInflationRadiusInModelUnits);
LogManager.Info($"【生成网格地图】车辆膨胀完成,膨胀半径: {totalInflationRadiusInModelUnits:F2}模型单位");
LogManager.Info($"【阶段3完成】车辆膨胀后网格统计: {channelCoverage.GridMap.GetStatistics()}");
LogManager.Info("【生成网格地图】步骤3: 应用物体膨胀");
ApplyObjectInflation(channelCoverage.GridMap, totalInflationRadiusInModelUnits);
LogManager.Info($"【生成网格地图】物体膨胀完成,膨胀半径: {totalInflationRadiusInModelUnits:F2}模型单位");
LogManager.Info($"【阶段3完成】物体膨胀后网格统计: {channelCoverage.GridMap.GetStatistics()}");
}
// 4. 设置规划起点和终点信息
@ -830,28 +830,28 @@ namespace NavisworksTransport.PathPlanning
}
/// <summary>
/// 应用车辆尺寸膨胀
/// 在障碍物周围扩展不可通行区域,考虑车辆尺寸
/// 应用物体尺寸膨胀
/// 在障碍物周围扩展不可通行区域,考虑物体尺寸
/// </summary>
/// <param name="gridMap">网格地图</param>
/// <param name="vehicleRadiusInModelUnits">车辆半径(模型单位)</param>
private void ApplyVehicleInflation(GridMap gridMap, double vehicleRadiusInModelUnits)
/// <param name="objectRadiusInModelUnits">物体半径(模型单位)</param>
private void ApplyObjectInflation(GridMap gridMap, double objectRadiusInModelUnits)
{
if (vehicleRadiusInModelUnits <= 0) return;
if (objectRadiusInModelUnits <= 0) return;
try
{
// 计算膨胀半径(网格单元格数)
int inflationRadius = (int)Math.Ceiling(vehicleRadiusInModelUnits / gridMap.CellSize);
int inflationRadius = (int)Math.Ceiling(objectRadiusInModelUnits / gridMap.CellSize);
LogManager.Info($"[高效膨胀] 膨胀半径: {inflationRadius}个网格单元");
// 使用高效的距离变换算法
ApplyVehicleInflation(gridMap, inflationRadius);
ApplyObjectInflation(gridMap, inflationRadius);
}
catch (Exception ex)
{
LogManager.Error($"[高效膨胀] 应用车辆膨胀时发生错误: {ex.Message}");
throw new AutoPathPlanningException($"车辆膨胀失败: {ex.Message}", ex);
LogManager.Error($"[高效膨胀] 应用物体膨胀时发生错误: {ex.Message}");
throw new AutoPathPlanningException($"物体膨胀失败: {ex.Message}", ex);
}
}
@ -859,7 +859,7 @@ namespace NavisworksTransport.PathPlanning
/// 多层膨胀算法 - 分两次处理:障碍物膨胀 + 边界膨胀
/// 核心原则:障碍物从外围膨胀(不含本身),边界从边界本身膨胀(包含边界)
/// </summary>
private void ApplyVehicleInflation(GridMap gridMap, int inflationRadius)
private void ApplyObjectInflation(GridMap gridMap, int inflationRadius)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
@ -1313,7 +1313,7 @@ namespace NavisworksTransport.PathPlanning
var walkableGridCount = channelCoverage.WalkableGridCount;
var walkableHeightRange = walkableMaxZ - walkableMinZ;
LogManager.Info($"[高性能障碍物处理] 可通行网格高度信息 - 最低点: {walkableMinZ:F2}, 最高点: {walkableMaxZ:F2}");
LogManager.Info($"[高性能障碍物处理] 可通行网格数量: {walkableGridCount}, 高度范围: {walkableHeightRange:F2}, 加车辆高度后扫描范围: [{walkableMinZ:F2}, {walkableMaxZ + scanHeightInModelUnits:F2}]");
LogManager.Info($"[高性能障碍物处理] 可通行网格数量: {walkableGridCount}, 高度范围: {walkableHeightRange:F2}, 加物体高度后扫描范围: [{walkableMinZ:F2}, {walkableMaxZ + scanHeightInModelUnits:F2}]");
// 阶段4网格覆盖计算并行几何计算- 使用50%CPU核心优化 + 基于通道的精确高度检查
LogManager.Info("[高性能障碍物处理] 阶段4: 并行网格覆盖计算 + 基于通道高度的精确高度检查");
@ -1337,7 +1337,7 @@ namespace NavisworksTransport.PathPlanning
var centerCell = gridMap.Cells[centerGridPos.X, centerGridPos.Y];
// 使用可通行网格高度范围计算扫描范围
// 扫描范围:从可通行网格最低点开始,到可通行网格最高点+车辆高度+安全间隙
// 扫描范围:从可通行网格最低点开始,到可通行网格最高点+物体高度+安全间隙
var scanMin = walkableMinZ;
var scanMax = walkableMaxZ + scanHeightInModelUnits;

Some files were not shown because too many files have changed in this diff Show More