From f131d0f8b74f6bd49d564f6c342e45443d39082d Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 11 Sep 2025 12:38:31 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=9C=E7=BA=BF=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=BC=98=E5=8C=96=E6=9C=89=E5=B1=80=E9=83=A8=E9=94=AF?= =?UTF-8?q?=E9=BD=BF=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/design/2026/NavisworksAPI使用方法.md | 15 +++ src/PathPlanning/AutoPathFinder.cs | 117 +++++++++++++++++++---- 2 files changed, 111 insertions(+), 21 deletions(-) diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md index e5b6039..d767f41 100644 --- a/doc/design/2026/NavisworksAPI使用方法.md +++ b/doc/design/2026/NavisworksAPI使用方法.md @@ -2,6 +2,21 @@ 基于真实官方示例的正确API用法总结 + 在线资源链接 + + 1. Autodesk Platform Services (APS) + - 主要开发者门户: + - 提供Navisworks集成工具和SDK + 2. AEC DevBlog + - 官方开发博客: + - 包含2026版本新功能和技术文章 + 3. Autodesk Developer Network + - 开发者网络: + - 提供开发资源和支持 + 4. 非官方在线API文档 + - ApiDocs.co: + - (注:目前仅覆盖2017-2018版本) + ## 参考示例来源 基于以下官方示例文件的真实API用法: diff --git a/src/PathPlanning/AutoPathFinder.cs b/src/PathPlanning/AutoPathFinder.cs index 1c834ae..c082aae 100644 --- a/src/PathPlanning/AutoPathFinder.cs +++ b/src/PathPlanning/AutoPathFinder.cs @@ -751,10 +751,26 @@ namespace NavisworksTransport.PathPlanning // 🔥 更激进的斜线检测:从P+2开始尝试,直到找到最远可连接点 for (int testIndex = currentIndex + 2; testIndex < optimizedPath.Count; testIndex++) { - if (IsDirectPathClear(optimizedPath[currentIndex], optimizedPath[testIndex], gridMap)) + var startPoint = optimizedPath[currentIndex]; + var endPoint = optimizedPath[testIndex]; + var distance = Math.Sqrt(Math.Pow(endPoint.X - startPoint.X, 2) + + Math.Pow(endPoint.Y - startPoint.Y, 2)); + + if (IsDirectPathClear(startPoint, endPoint, gridMap)) { // 可以直线连接,更新最远索引 farthestIndex = testIndex; + LogManager.Debug($"[斜线优化] ✓ 成功连接:点{currentIndex}→点{testIndex},距离={distance:F2}m"); + } + else + { + // 新增:记录失败详情 + var startGrid = gridMap.WorldToGrid(startPoint); + var endGrid = gridMap.WorldToGrid(endPoint); + LogManager.Debug($"[斜线优化] ✗ 连接失败:" + + $"点{currentIndex}[网格({startGrid.X},{startGrid.Y})]→点{testIndex}[网格({endGrid.X},{endGrid.Y})]," + + $"世界坐标:({startPoint.X:F2},{startPoint.Y:F2})→({endPoint.X:F2},{endPoint.Y:F2})," + + $"距离={distance:F2}m,跨越{Math.Abs(endGrid.X-startGrid.X)+Math.Abs(endGrid.Y-startGrid.Y)}个网格"); } // 🔧 关键改进:不像现有算法那样遇到失败就停止,而是继续尝试更远的点 // 这样可以发现更多斜线连接机会 @@ -816,6 +832,9 @@ namespace NavisworksTransport.PathPlanning // 最少采样5个点,最多10000个点(提高性能限制) samples = Math.Max(5, Math.Min(samples, 10000)); + LogManager.Debug($"[斜线检查] 开始检查路径:起点({start.X:F2},{start.Y:F2})→终点({end.X:F2},{end.Y:F2})," + + $"距离={distance:F2}m,采样数={samples}"); + for (int i = 0; i <= samples; i++) { // 线性插值计算采样点 @@ -830,12 +849,46 @@ namespace NavisworksTransport.PathPlanning var gridPos = gridMap.WorldToGrid(samplePoint); if (!gridMap.IsValidGridPosition(gridPos) || !gridMap.IsWalkable(gridPos)) { + // 🔥 修复:使用Origin计算网格左下角,而不是Bounds.Min + var gridMinX = gridMap.Origin.X + gridPos.X * gridMap.CellSize; + var gridMinY = gridMap.Origin.Y + gridPos.Y * gridMap.CellSize; + + // 计算网格中心点世界坐标(基于左下角) + var gridCenterX = gridMinX + 0.5 * gridMap.CellSize; + var gridCenterY = gridMinY + 0.5 * gridMap.CellSize; + var gridCenterZ = samplePoint.Z; // Z保持不变 + + // 计算偏差 + var deltaX = samplePoint.X - gridCenterX; + var deltaY = samplePoint.Y - gridCenterY; + + LogManager.Debug($"[斜线检查] 采样点{i}/{samples}失败:" + + $"采样点({samplePoint.X:F3},{samplePoint.Y:F3},{samplePoint.Z:F3})," + + $"网格({gridPos.X},{gridPos.Y})左下角({gridMinX:F3},{gridMinY:F3})," + + $"网格中心({gridCenterX:F3},{gridCenterY:F3},{gridCenterZ:F3})," + + $"偏差(ΔX={deltaX:F3}, ΔY={deltaY:F3})," + + $"原因:{(!gridMap.IsValidGridPosition(gridPos) ? "网格无效" : "不可通行")}"); return false; } // 🔥 新增:邻居障碍位置检查 if (!IsSamplePointSafeFromNeighborObstacles(samplePoint, gridPos, gridMap)) { + // 🔥 修复:使用Origin计算网格左下角 + var gridMinX = gridMap.Origin.X + gridPos.X * gridMap.CellSize; + var gridMinY = gridMap.Origin.Y + gridPos.Y * gridMap.CellSize; + + // 计算网格中心点世界坐标 + var gridCenterX = gridMinX + 0.5 * gridMap.CellSize; + var gridCenterY = gridMinY + 0.5 * gridMap.CellSize; + var deltaX = samplePoint.X - gridCenterX; + var deltaY = samplePoint.Y - gridCenterY; + + LogManager.Debug($"[斜线检查] 采样点{i}/{samples}邻居障碍检查失败:" + + $"采样点({samplePoint.X:F3},{samplePoint.Y:F3})," + + $"网格左下角({gridMinX:F3},{gridMinY:F3})," + + $"网格中心({gridCenterX:F3},{gridCenterY:F3})," + + $"偏差(ΔX={deltaX:F3}, ΔY={deltaY:F3})"); return false; } } @@ -860,9 +913,10 @@ namespace NavisworksTransport.PathPlanning { try { + // 🔥 修复:使用Origin计算网格左下角,而不是Bounds.Min // 计算采样点在所在网格内的相对位置 (0.0 到 1.0) - var gridMinX = gridMap.Bounds.Min.X + gridPos.X * gridMap.CellSize; - var gridMinY = gridMap.Bounds.Min.Y + gridPos.Y * gridMap.CellSize; + var gridMinX = gridMap.Origin.X + gridPos.X * gridMap.CellSize; + var gridMinY = gridMap.Origin.Y + gridPos.Y * gridMap.CellSize; var relativeX = (samplePoint.X - gridMinX) / gridMap.CellSize; var relativeY = (samplePoint.Y - gridMinY) / gridMap.CellSize; @@ -898,6 +952,20 @@ namespace NavisworksTransport.PathPlanning if (!constraintSatisfied) { + // 🔥 修复:基于正确的网格左下角计算网格中心点 + var gridCenterX = gridMinX + 0.5 * gridMap.CellSize; + var gridCenterY = gridMinY + 0.5 * gridMap.CellSize; + var deltaX = samplePoint.X - gridCenterX; + var deltaY = samplePoint.Y - gridCenterY; + + LogManager.Debug($"[邻居障碍] 位置约束失败:" + + $"{directionNames[i]}方向有障碍," + + $"网格({gridPos.X},{gridPos.Y})," + + $"采样点({samplePoint.X:F3},{samplePoint.Y:F3})," + + $"网格左下角({gridMinX:F3},{gridMinY:F3})," + + $"网格中心({gridCenterX:F3},{gridCenterY:F3})," + + $"偏差(ΔX={deltaX:F3}, ΔY={deltaY:F3})," + + $"相对位置({relativeX:F3},{relativeY:F3})"); return false; } } @@ -921,43 +989,50 @@ namespace NavisworksTransport.PathPlanning /// 是否满足位置约束 private bool CheckPositionConstraintForObstacleNeighbor(int neighborDirection, double relativeX, double relativeY) { - // 🔧 定义容差值来处理浮点数精度问题 + // 容差值仅用于处理浮点数精度问题 const double tolerance = 1e-3; - bool result; switch (neighborDirection) { - case 0: // 左上邻居是障碍 → 排除左上象限,允许其他3个象限 - result = (relativeX >= 0.5 - tolerance) || (relativeY >= 0.5 - tolerance); + case 0: // 左上邻居是障碍 + // 采样点不能同时X<0且Y>1 + result = (relativeX >= -tolerance) || (relativeY <= 1.0 + tolerance); break; - case 1: // 上邻居是障碍 → 采样点必须在下半部分 - result = relativeY >= 0.5 - tolerance; + case 1: // 上邻居是障碍 + // 采样点不能Y>1(允许Y=1) + result = relativeY <= 1.0 + tolerance; break; - case 2: // 右上邻居是障碍 → 排除右上象限,允许其他3个象限 - result = (relativeX <= 0.5 + tolerance) || (relativeY >= 0.5 - tolerance); + case 2: // 右上邻居是障碍 + // 采样点不能同时X>1且Y>1 + result = (relativeX <= 1.0 + tolerance) || (relativeY <= 1.0 + tolerance); break; - case 3: // 左邻居是障碍 → 采样点必须在右半部分 - result = relativeX >= 0.5 - tolerance; + case 3: // 左邻居是障碍 + // 采样点不能X<0(允许X=0) + result = relativeX >= -tolerance; break; - case 4: // 右邻居是障碍 → 采样点必须在左半部分 - result = relativeX <= 0.5 + tolerance; + case 4: // 右邻居是障碍 + // 采样点不能X>1(允许X=1) + result = relativeX <= 1.0 + tolerance; break; - case 5: // 左下邻居是障碍 → 排除左下象限,允许其他3个象限 - result = (relativeX >= 0.5 - tolerance) || (relativeY <= 0.5 + tolerance); + case 5: // 左下邻居是障碍 + // 采样点不能同时X<0且Y<0 + result = (relativeX >= -tolerance) || (relativeY >= -tolerance); break; - case 6: // 下邻居是障碍 → 采样点必须在上半部分 - result = relativeY <= 0.5 + tolerance; + case 6: // 下邻居是障碍 + // 采样点不能Y<0(允许Y=0) + result = relativeY >= -tolerance; break; - case 7: // 右下邻居是障碍 → 排除右下象限,允许其他3个象限 - result = (relativeX <= 0.5 + tolerance) || (relativeY <= 0.5 + tolerance); + case 7: // 右下邻居是障碍 + // 采样点不能同时X>1且Y<0 + result = (relativeX <= 1.0 + tolerance) || (relativeY >= -tolerance); break; default: