修改碰撞间隙不一致的问题,自动规划显示网格大小

This commit is contained in:
tian 2025-08-30 14:56:37 +08:00
parent f91d142bc7
commit 5938c817a4
11 changed files with 301 additions and 37 deletions

View File

@ -2,6 +2,11 @@
## 功能点
### [2025/08/30]
1. [ ]性能优化用几何方法识别通道的坡度变化侧面上表面轮廓线给通道网格准确的z坐标
2. [ ]BUG动画中的包围盒检测和ClashDetective检测结果不一致是因为碰撞间隙不同造成的。Autodesk官方建议保守测试+0公差解决碰撞检测结果不准确的问题
### [2025/08/29]
1. [ ]BUG路径导出只有一条路径时导出按钮不能点击导出的内容有时不完整只导出一个包含起点、一个路径点、终点的不存在的路径。

View File

@ -0,0 +1,174 @@
# 通道坡度轮廓分析技术方案
## 问题背景
在路径规划过程中,我们遇到了斜坡通道的高度计算问题:
### 当前系统的局限性
1. **网格构建阶段**使用线性插值计算Z坐标无法准确反映斜坡的真实高度变化
2. **混合通道处理**:对于"一段斜坡+一段平面"的组合通道,现有的`SlopeAnalyzer`会计算平均坡度,丢失局部变化信息
3. **路径规划精度**:在斜坡上可能产生高度计算错误,影响碰撞检测和动画效果
### 具体问题表现
- 斜坡通道的不同XY位置有不同的Z坐标表面高度
- 网格构建时使用传统插值无法反映斜坡的真实高度变化
- 组合坡度通道(斜坡+平面)被简化为单一坡度处理
## 核心技术思路
### 🎯 关键洞察
**从3D分析降维到2D轮廓分析**
- 通道侧面轮廓清晰显示坡度变化
- 只关注**通道上表面边缘线**,问题简化为**2D曲线分析**
- 沿通道长度方向的Z坐标变化就是坡度信息
### 💡 方法优势
1. **精度高**:直接基于实际几何形状,不受平均化影响
2. **效率好**2D轮廓分析比3D体积分析计算量小
3. **适应性强**:支持复杂的多段坡度和弯曲通道
## 技术实现方案
### 方案1边缘轮廓采样法
```csharp
// 实现步骤伪代码
var channelBounds = channel.Geometry.BoundingBox;
// 1. 确定通道主轴方向
var mainDirection = DetermineChannelMainDirection(channelBounds);
// 2. 提取上表面边缘轮廓
var topSurfaceEdges = ExtractTopSurfaceEdges(channelGeometry);
// 3. 沿主轴方向高密度采样
var profilePoints = SampleEdgeProfile(topSurfaceEdges, samplingDensity: 0.2); // 每20cm采样
// 4. 分析坡度变化
var slopeSegments = AnalyzeHeightProfile(profilePoints);
```
### 方案2几何面分析法
```csharp
// 分析通道轮廓的坡度分段
var slopeChanges = new List<SlopeChangePoint>();
for (int i = 1; i < profileLine.Points.Count; i++)
{
var localSlope = CalculateLocalSlope(profileLine.Points[i-1], profileLine.Points[i]);
// 检测坡度突变点
if (Math.Abs(localSlope - previousSlope) > SLOPE_CHANGE_THRESHOLD)
{
slopeChanges.Add(new SlopeChangePoint(position, localSlope));
}
}
```
### 坡度分段识别算法
1. **局部坡度计算**`slope = (z2-z1)/distance`
2. **变化点检测**:坡度变化 > 阈值的位置
3. **自动分割**平面段slope < 1°)、斜坡段过渡段
4. **区段映射**:为每个网格分配对应区段的坡度信息
## 现有技术基础
### 可利用的组件
1. **GeometryExtractor.cs**:已有空间分析功能
2. **COM API**:可以提取通道的几何面信息和边界数据
3. **SlopeAnalyzer.cs**:现有的坡度分析框架,可扩展支持分段分析
4. **ChannelHeightDetector.cs**:精确高度计算能力
### 扩展点
```csharp
// 扩展ChannelSlopeInfo支持多段坡度
public class ChannelSlopeInfo
{
// 现有字段...
// 新增:坡度段列表
public List<SlopeSegment> SlopeSegments { get; set; }
}
public class SlopeSegment
{
public Point3D StartPoint { get; set; }
public Point3D EndPoint { get; set; }
public double LocalSlopeAngle { get; set; }
public SlopeType SegmentType { get; set; } // Flat, Ramp, etc.
}
```
## 实施计划
### 阶段1轮廓提取验证
- [ ] 研究Navisworks COM API的几何提取能力
- [ ] 实现通道上表面边缘线提取
- [ ] 验证轮廓采样的精度和性能
### 阶段2坡度分析增强
- [ ] 扩展SlopeAnalyzer支持分段分析
- [ ] 实现坡度变化点自动检测
- [ ] 开发区段到网格的映射机制
### 阶段3集成和优化
- [ ] 集成到GridMapGenerator的通道处理流程
- [ ] 为网格单元记录精确的坡度信息
- [ ] 性能优化和缓存机制
## 技术挑战
### 1. 几何提取复杂性
- 正确识别"上表面"vs侧面、底面
- 处理复杂几何形状和不规则边界
- 边缘线可能不连续或有噪声
### 2. 采样和平滑策略
- 采样密度影响精度和性能的平衡
- 处理小幅波动和测量噪声
- 坡度变化阈值的合理设定
### 3. 集成复杂度
- 与现有网格生成流程的集成
- 缓存机制和性能优化
- 多种通道类型的统一处理
## 预期收益
### 1. 路径规划精度提升
- 斜坡通道的高度计算准确
- 支持复杂的组合坡度模式
- 避免路径规划中的"飞行"或"穿地"问题
### 2. 动画质量改善
- 车辆在斜坡上的倾斜角度准确
- 平滑的坡度过渡动画
- 真实的物理运动模拟
### 3. 系统扩展性
- 为未来的高级路径优化奠定基础
- 支持更复杂的通道类型分析
- 提供精确的空间几何数据
---
**记录时间**2025-08-29
**状态**:技术方案设计完成,待验证实施
**优先级**:高(影响路径规划核心精度)

View File

@ -372,7 +372,7 @@ namespace NavisworksTransport.Commands
PathLength = generatedRoute.TotalLength,
PathPointCount = generatedRoute.Points.Count,
ComputationTimeMs = (long)elapsedMs,
UsedGridSize = actualGridSize > 0 ? actualGridSize : 1.0, // 实际使用的网格大小
UsedGridSize = actualGridSize > 0 ? actualGridSize : 0.5, // 实际使用的网格大小
AlgorithmStatistics = $"A*算法路径规划完成,生成{generatedRoute.Points.Count}个路径点"
},
$"自动路径规划成功完成:{generatedRoute.Name}"

View File

@ -41,7 +41,7 @@ namespace NavisworksTransport.Core.Animation
private Timer _collisionTimer;
private double _collisionDetectionAccuracy = 0.1; // 检测精度默认0.1米)
private double _movementSpeed = 1.0; // 运动速度默认1米/秒)
private double _detectionGap = 0.2; // 检测间隙默认0.2米)
private double _detectionGap = 0.05; // 检测间隙默认0.05米)
private Transform3D _originalTransform;
private Point3D _originalCenter; // 存储部件的原始中心位置
@ -364,8 +364,8 @@ namespace NavisworksTransport.Core.Animation
}
// 动画结束后统一创建所有碰撞测试(基于官方示例的批量处理)
LogManager.Info("动画播放完成,开始创建最终的碰撞测试汇总...");
ClashDetectiveIntegration.Instance.CreateAllAnimationCollisionTests();
LogManager.Info($"动画播放完成,开始创建最终的碰撞测试汇总(检测间隙: {_detectionGap}米)...");
ClashDetectiveIntegration.Instance.CreateAllAnimationCollisionTests(_detectionGap);
}
catch (Exception ex)
{
@ -1134,8 +1134,13 @@ namespace NavisworksTransport.Core.Animation
/// </summary>
public void SetDetectionGap(double gap)
{
_detectionGap = Math.Max(0.0, gap);
LogManager.Info($"检测间隙设置为: {_detectionGap}m");
var roundedGap = Math.Round(Math.Max(0.0, gap), 2);
// 只在值真正改变时才更新和记录日志
if (Math.Abs(_detectionGap - roundedGap) > 0.001)
{
_detectionGap = roundedGap;
LogManager.Info($"间隙={_detectionGap:F2}m");
}
}
/// <summary>

View File

@ -500,11 +500,11 @@ namespace NavisworksTransport
/// <summary>
/// 动画结束后统一创建和运行所有碰撞测试 - 基于官方示例
/// </summary>
public void CreateAllAnimationCollisionTests()
public void CreateAllAnimationCollisionTests(double detectionGap = 0.05)
{
try
{
LogManager.Info("=== 动画结束,开始创建所有碰撞测试 ===");
LogManager.Info($"=== 动画结束,开始创建所有碰撞测试(容差: {detectionGap}米) ===");
if (_documentClash == null || _cachedResults.Count == 0)
{
@ -646,6 +646,7 @@ namespace NavisworksTransport
LogManager.Info($"[测试创建-{createdCount:00}] 碰撞对象位置: ({collision.Item2Position.X:F3},{collision.Item2Position.Y:F3},{collision.Item2Position.Z:F3})");
LogManager.Info($"[测试创建-{createdCount:00}] 原始距离: {collision.Distance:F4}");
LogManager.Info($"[测试创建-{createdCount:00}] 创建时间: {collision.CreatedTime:HH:mm:ss.fff}");
LogManager.Info($"[测试创建-{createdCount:00}] 使用容差: {detectionGap}米");
// 将对象移动到碰撞位置
var testAnimatedObject = collision.Item1;
@ -679,7 +680,7 @@ namespace NavisworksTransport
collisionTest.DisplayName = testName;
collisionTest.TestType = ClashTestType.Hard;
collisionTest.Tolerance = 0.01;
collisionTest.Tolerance = detectionGap; // 使用传入的检测间隙参数
collisionTest.Guid = Guid.Empty;
// 设置选择集A
@ -811,7 +812,7 @@ namespace NavisworksTransport
}
}
LogManager.Info($"=== 位置恢复方案完成:成功创建并运行 {createdCount - 1} 个碰撞测试 ===");
LogManager.Info($"=== 位置恢复方案完成:成功创建并运行 {createdCount - 1} 个碰撞测试(容差: {detectionGap}米) ===");
if (createdCount > 1)
{

View File

@ -397,13 +397,13 @@ namespace NavisworksTransport
}
}
private void RaiseRouteGenerated(PathRoute route, RouteGenerationMethod generationMethod)
private void RaiseRouteGenerated(PathRoute route, RouteGenerationMethod generationMethod, double gridSize = -1)
{
try
{
LogManager.Info($"*** RaiseRouteGenerated被调用: {route?.Name}, Method: {generationMethod} ***");
LogManager.Info($"*** RaiseRouteGenerated被调用: {route?.Name}, Method: {generationMethod}, GridSize: {gridSize} ***");
var eventArgs = new RouteGeneratedEventArgs(route, generationMethod, _managerId);
var eventArgs = new RouteGeneratedEventArgs(route, generationMethod, gridSize, _managerId);
// 检查是否有订阅者
if (RouteGenerated != null)
@ -897,10 +897,10 @@ namespace NavisworksTransport
DrawRouteVisualization(autoRoute, isAutoPath: true);
// 8. 触发事件
RaiseRouteGenerated(autoRoute, RouteGenerationMethod.AutoPlanning);
RaiseStatusChanged($"自动路径规划完成: {routeName}", PathPlanningStatusType.Success);
RaiseRouteGenerated(autoRoute, RouteGenerationMethod.AutoPlanning, gridSize);
RaiseStatusChanged($"自动路径规划完成: {routeName},使用网格大小 {gridSize:F2}米", PathPlanningStatusType.Success);
LogManager.Info($"自动路径规划成功完成: 路径长度 {autoRoute.TotalLength:F2}米,包含 {autoRoute.Points.Count} 个点");
LogManager.Info($"自动路径规划成功完成: 路径长度 {autoRoute.TotalLength:F2}米,包含 {autoRoute.Points.Count} 个点,使用网格大小 {gridSize:F2}米");
return Task.FromResult(autoRoute);
}
catch (Exception ex)
@ -2238,11 +2238,11 @@ namespace NavisworksTransport
if (maxDimension > 1000) return 5.0; // 大型模型5米
if (maxDimension > 500) return 2.0; // 中型模型2米
if (maxDimension > 100) return 1.0; // 小型模型1米
return 0.5; // 微型模型0.5
return 1.0; // 微型模型1.0
}
catch
{
return 0.5; // 默认值改为0.5与UI默认设置保持一致
return 1.0; // 默认值改为1.0与UI默认设置保持一致
}
}

View File

@ -187,12 +187,18 @@ namespace NavisworksTransport.Core
/// </summary>
public string Name { get; }
public RouteGeneratedEventArgs(PathRoute route, RouteGenerationMethod generationMethod = RouteGenerationMethod.Manual, string managerId = null)
/// <summary>
/// 使用的网格大小(米),-1表示自动选择用于自动路径规划
/// </summary>
public double GridSize { get; }
public RouteGeneratedEventArgs(PathRoute route, RouteGenerationMethod generationMethod = RouteGenerationMethod.Manual, double gridSize = -1, string managerId = null)
: base(managerId)
{
Route = route;
GenerationMethod = generationMethod;
Name = route?.Name ?? string.Empty;
GridSize = gridSize;
}
}

View File

@ -65,7 +65,7 @@ namespace NavisworksTransport.PathPlanning
PerformanceSuggestions = new List<string>();
EstimatedCalculationTime = 0;
EstimatedMemoryUsage = 0;
GridResolution = 0.5;
GridResolution = 0.0;
SearchAreaSize = 0;
}

View File

@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Clash;
using NavisworksTransport.Commands;
@ -92,10 +93,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 碰撞检测参数字段
private double _collisionDetectionAccuracy = 0.1; // 检测精度(米)
private double _movementSpeed = 1.0; // 运动速度(米/秒)
private double _detectionGap = 0.2; // 检测间隙(米)
private double _detectionGap = 0.05; // 检测间隙(米)
private int _animationFrameRate = 30; // 动画帧率(FPS)
private double _collisionDetectionFrequency = 10.0; // 检测频率(次/秒)
// 防抖机制相关字段
private DispatcherTimer _parameterUpdateTimer;
private readonly object _parameterUpdateLock = new object();
private volatile bool _hasParameterChanges = false;
// 当前选中路径
private PathRouteViewModel _currentPathRoute;
@ -239,10 +245,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _collisionDetectionAccuracy;
set
{
if (SetProperty(ref _collisionDetectionAccuracy, value))
// 限制精度到2位小数
var roundedValue = Math.Round(value, 2);
if (SetProperty(ref _collisionDetectionAccuracy, roundedValue))
{
UpdateCollisionDetectionFrequency();
UpdatePathAnimationManagerSettings();
ScheduleParameterUpdate();
}
}
}
@ -255,10 +263,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _movementSpeed;
set
{
if (SetProperty(ref _movementSpeed, value))
// 限制精度到1位小数
var roundedValue = Math.Round(value, 1);
if (SetProperty(ref _movementSpeed, roundedValue))
{
UpdateCollisionDetectionFrequency();
UpdatePathAnimationManagerSettings();
ScheduleParameterUpdate();
}
}
}
@ -271,9 +281,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _detectionGap;
set
{
if (SetProperty(ref _detectionGap, value))
// 限制精度到2位小数
var roundedValue = Math.Round(value, 2);
if (SetProperty(ref _detectionGap, roundedValue))
{
UpdatePathAnimationManagerSettings();
ScheduleParameterUpdate();
}
}
}
@ -409,6 +421,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 初始化碰撞检测集成
InitializeClashIntegration();
// 初始化防抖定时器
InitializeParameterUpdateTimer();
LogManager.Info("AnimationControlViewModel初始化完成含缓存管理");
}
@ -457,7 +472,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 初始化碰撞检测参数
CollisionDetectionAccuracy = 0.1; // 0.1米
MovementSpeed = 1.0; // 1米/秒
DetectionGap = 0.2; // 0.2
DetectionGap = 0.05; // 0.05
AnimationFrameRate = 30; // 30FPS
UpdateCollisionDetectionFrequency(); // 计算初始检测频率
@ -1127,6 +1142,47 @@ namespace NavisworksTransport.UI.WPF.ViewModels
CollisionDetectionFrequency = 10.0; // 默认值
}
}
/// <summary>
/// 初始化参数更新防抖定时器
/// </summary>
private void InitializeParameterUpdateTimer()
{
_parameterUpdateTimer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(150) // 150毫秒防抖延迟
};
_parameterUpdateTimer.Tick += OnParameterUpdateTimerTick;
}
/// <summary>
/// 定时器触发时执行参数更新
/// </summary>
private void OnParameterUpdateTimerTick(object sender, EventArgs e)
{
_parameterUpdateTimer.Stop();
lock (_parameterUpdateLock)
{
if (_hasParameterChanges)
{
_hasParameterChanges = false;
UpdatePathAnimationManagerSettings();
}
}
}
/// <summary>
/// 计划参数更新(防抖机制)
/// </summary>
private void ScheduleParameterUpdate()
{
lock (_parameterUpdateLock)
{
_hasParameterChanges = true;
_parameterUpdateTimer.Stop();
_parameterUpdateTimer.Start();
}
}
/// <summary>
/// 更新PathAnimationManager的设置
@ -1142,7 +1198,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
_pathAnimationManager.SetMovementSpeed(_movementSpeed);
_pathAnimationManager.SetDetectionGap(_detectionGap);
LogManager.Debug($"碰撞检测参数已更新: 帧率={_animationFrameRate}FPS, 精度={_collisionDetectionAccuracy}m, 速度={_movementSpeed}m/s, 间隙={_detectionGap}m");
LogManager.Debug($"碰撞检测参数已更新: 帧率={_animationFrameRate}FPS, 精度={_collisionDetectionAccuracy:F2}m, 速度={_movementSpeed:F1}m/s, 间隙={_detectionGap:F2}m");
}
}
catch (Exception ex)
@ -1447,6 +1503,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
_clashIntegration.CollisionDetected -= OnCollisionDetected;
}
// 清理防抖定时器
if (_parameterUpdateTimer != null)
{
_parameterUpdateTimer.Stop();
_parameterUpdateTimer.Tick -= OnParameterUpdateTimerTick;
_parameterUpdateTimer = null;
}
LogManager.Info("AnimationControlViewModel资源清理完成");
}
catch (Exception ex)

View File

@ -207,7 +207,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _safetyMargin;
set
{
if (SetProperty(ref _safetyMargin, value))
// 限制精度到2位小数
var roundedValue = Math.Round(value, 2);
if (SetProperty(ref _safetyMargin, roundedValue))
{
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
@ -231,7 +233,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _gridSize;
set
{
if (SetProperty(ref _gridSize, value))
// 限制精度到1位小数
var roundedValue = Math.Round(value, 1);
if (SetProperty(ref _gridSize, roundedValue))
{
OnPropertyChanged(nameof(CanExecuteAutoPlanPath));
}
@ -723,7 +727,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 不在这里直接操作UI让RouteGenerated事件处理UI更新
// 这避免了重复的路径添加和UI更新冲突
AutoPathStatus = $"✅ 路径规划完成!共 {pathRoute.Points.Count} 个路径点,长度 {pathRoute.TotalLength:F2}米";
// 获取实际使用的网格大小信息
var actualGridSize = IsGridSizeManuallyEnabled ? GridSize : -1;
var gridInfo = actualGridSize > 0 ? $"网格{actualGridSize:F1}m" : "自动网格";
AutoPathStatus = $"✅ 路径规划完成!共 {pathRoute.Points.Count} 个路径点,长度 {pathRoute.TotalLength:F2}米({gridInfo}";
LogManager.Info($"✅ 自动路径规划完成RouteGenerated事件将处理UI更新");
}
else
@ -1914,7 +1921,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private async void OnRouteGenerated(object sender, RouteGeneratedEventArgs e)
{
// 添加调试日志确认事件是否被调用
LogManager.Info($"*** OnRouteGenerated被调用Sender: {sender?.GetType().Name}, Route: {e?.Route?.Name}, Method: {e?.GenerationMethod} ***");
LogManager.Info($"*** OnRouteGenerated被调用Sender: {sender?.GetType().Name}, Route: {e?.Route?.Name}, Method: {e?.GenerationMethod}, GridSize: {e?.GridSize} ***");
if (e?.Route == null)
{
@ -1980,7 +1987,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"*** 自动路径已更新UI列表: {e.Route.Name} ***");
}
AutoPathStatus = $"✅ 自动路径规划完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点";
// 构建包含网格信息的完整状态消息,使用事件参数中的网格大小信息
var gridInfo = e.GridSize > 0 ? $"网格{e.GridSize:F1}m" : "自动网格";
AutoPathStatus = $"✅ 自动路径规划完成: {e.Route.Name},共 {e.Route.Points.Count} 个路径点,长度 {e.Route.TotalLength:F2}米({gridInfo}";
LogManager.Info($"*** AutoPathStatus已更新: {AutoPathStatus} ***");
}
else if (e.GenerationMethod == RouteGenerationMethod.Manual)

View File

@ -153,11 +153,11 @@ NavisworksTransport 检测动画页签视图 - 采用与类别设置和分层管
<Slider Grid.Column="0"
Value="{Binding DetectionGap}"
Minimum="0.0"
Maximum="1.0"
TickFrequency="0.1"
Maximum="0.2"
TickFrequency="0.01"
Style="{StaticResource SliderStyle}"/>
<TextBlock Grid.Column="1"
Text="{Binding DetectionGap, StringFormat={}{0:F1}}"
Text="{Binding DetectionGap, StringFormat={}{0:F2}}"
VerticalAlignment="Center"
Margin="5,0,0,0"
Width="40"/>