From e46931311f4022a74283ca6dc3f78799b49ee65f Mon Sep 17 00:00:00 2001
From: tian <11429339@qq.com>
Date: Sat, 4 Oct 2025 21:26:49 +0800
Subject: [PATCH] =?UTF-8?q?=E5=B0=86=E6=9C=80=E5=A4=A7=E5=AE=BD=E5=BA=A6?=
=?UTF-8?q?=E3=80=81=E9=95=BF=E5=BA=A6=E3=80=81=E9=AB=98=E5=BA=A6=E3=80=81?=
=?UTF-8?q?=E5=AE=89=E5=85=A8=E9=97=B4=E9=9A=99=E3=80=81=E7=BD=91=E6=A0=BC?=
=?UTF-8?q?=E5=B0=BA=E5=AF=B8=EF=BC=8C=E5=8A=A0=E5=85=A5=E8=B7=AF=E5=BE=84?=
=?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=86=85=E5=AE=B9=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
AStarTestRunner.csproj | 57 --
.../AStarDebuggingTest.cs | 867 ------------------
src/Core/PathDataManager.cs | 36 +-
src/Core/PathDatabase.cs | 19 +-
src/Core/PathPlanningManager.cs | 6 +-
src/Core/PathPlanningModels.cs | 26 +-
6 files changed, 71 insertions(+), 940 deletions(-)
delete mode 100644 AStarTestRunner.csproj
delete mode 100644 NavisworksTransport.UnitTests/AStarDebuggingTest.cs
diff --git a/AStarTestRunner.csproj b/AStarTestRunner.csproj
deleted file mode 100644
index d96ad8f..0000000
--- a/AStarTestRunner.csproj
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
- Debug
- AnyCPU
- {F2B4A8C1-2DEF-4765-8392-E67AD8372FG2}
- Exe
- Properties
- TestRunner
- AStarTestRunner
- v4.8
- 512
- true
- x64
-
-
-
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
-
-
-
-
-
-
- packages\RoyT.AStar.2.1.0\lib\netstandard2.0\Roy-T.AStar.dll
- True
-
-
-
-
- bin\Debug\NavisworksTransport.UnitTests.dll
- True
-
-
-
-
- bin\Debug\NavisworksTransportPlugin.dll
- True
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/NavisworksTransport.UnitTests/AStarDebuggingTest.cs b/NavisworksTransport.UnitTests/AStarDebuggingTest.cs
deleted file mode 100644
index c34495a..0000000
--- a/NavisworksTransport.UnitTests/AStarDebuggingTest.cs
+++ /dev/null
@@ -1,867 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Roy_T.AStar.Grids;
-using Roy_T.AStar.Primitives;
-using Roy_T.AStar.Paths;
-using NavisworksTransport.PathPlanning;
-using NavisworksTransport.Utils;
-using NavisworksTransport;
-
-namespace NavisworksTransport.UnitTests
-{
- ///
- /// 简化的3D点结构体(用于测试)
- ///
- public struct Point3D
- {
- public double X { get; set; }
- public double Y { get; set; }
- public double Z { get; set; }
-
- public Point3D(double x, double y, double z)
- {
- X = x;
- Y = y;
- Z = z;
- }
-
- public override string ToString() => $"({X:F2}, {Y:F2}, {Z:F2})";
- }
-
- ///
- /// 简化的2D点结构体(用于测试)
- ///
- public struct Point2D
- {
- public double X { get; set; }
- public double Y { get; set; }
-
- public Point2D(double x, double y)
- {
- X = x;
- Y = y;
- }
-
- public override string ToString() => $"({X:F2}, {Y:F2})";
- }
-
- ///
- /// 简化的网格点结构体(用于测试)
- ///
- public struct GridPoint2D
- {
- public int X { get; set; }
- public int Y { get; set; }
-
- public GridPoint2D(int x, int y)
- {
- X = x;
- Y = y;
- }
-
- public override string ToString() => $"({X}, {Y})";
- }
-
- ///
- /// 简化的边界框结构体(用于测试)
- ///
- public struct BoundingBox3D
- {
- public Point3D Min { get; set; }
- public Point3D Max { get; set; }
-
- public BoundingBox3D(Point3D min, Point3D max)
- {
- Min = min;
- Max = max;
- }
- }
-
- ///
- /// A*算法问题检测测试用例
- /// 专门用于调试A*路径穿越障碍物网格的问题
- ///
- [TestClass]
- public class AStarDebuggingTest
- {
- ///
- /// 基础障碍物避让测试
- /// 创建一个3x3网格,中间设置障碍物,测试A*是否能正确绕过
- ///
- [TestMethod]
- public void TestBasicObstacleAvoidance()
- {
- // 创建3x3网格
- var gridSize = new GridSize(3, 3);
- var cellSize = new Size(Distance.FromMeters(1.0f), Distance.FromMeters(1.0f));
- var velocity = Velocity.FromKilometersPerHour(5.0f);
-
- var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, velocity);
-
- // 断开中间位置(1,1),模拟障碍物
- var obstaclePos = new GridPosition(1, 1);
- grid.DisconnectNode(obstaclePos);
-
- // 验证障碍物位置确实被断开
- var obstacleNode = grid.GetNode(obstaclePos);
- var obstacleEdges = obstacleNode.Outgoing.Count();
- Console.WriteLine($"障碍物位置({obstaclePos.X},{obstaclePos.Y})的出边数: {obstacleEdges}");
- Assert.AreEqual(0, obstacleEdges, "障碍物节点应该没有出边");
-
- // 尝试从(0,0)到(2,2)寻路,必须绕过中间的障碍物
- var startPos = new GridPosition(0, 0);
- var endPos = new GridPosition(2, 2);
-
- var pathfinder = new PathFinder();
- var path = pathfinder.FindPath(startPos, endPos, grid);
-
- Assert.IsNotNull(path, "应该能找到绕过障碍物的路径");
- Assert.IsTrue(path.Edges.Count > 0, "路径应该包含边");
-
- // 关键检查:验证路径不经过障碍物位置
- var pathNodes = new List { startPos };
- foreach (var edge in path.Edges)
- {
- var nodeGridX = (int)Math.Floor(edge.End.Position.X / 1.0); // cellSize = 1米
- var nodeGridY = (int)Math.Floor(edge.End.Position.Y / 1.0);
- var nodeGridPos = new GridPosition(nodeGridX, nodeGridY);
- pathNodes.Add(nodeGridPos);
- }
-
- Console.WriteLine("路径经过的网格坐标:");
- foreach (var node in pathNodes)
- {
- Console.WriteLine($" 网格({node.X},{node.Y})");
- }
-
- // 验证路径不包含障碍物坐标
- bool pathContainsObstacle = pathNodes.Any(pos => pos.X == obstaclePos.X && pos.Y == obstaclePos.Y);
- Assert.IsFalse(pathContainsObstacle, $"路径不应该经过障碍物位置({obstaclePos.X},{obstaclePos.Y})");
- }
-
- ///
- /// 坐标映射验证测试
- /// 验证GridPosition和Node.Position之间的对应关系
- ///
- [TestMethod]
- public void TestCoordinateMapping()
- {
- var gridSize = new GridSize(5, 5);
- var cellSize = new Size(Distance.FromMeters(2.0f), Distance.FromMeters(2.0f));
- var velocity = Velocity.FromKilometersPerHour(5.0f);
-
- var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, velocity);
-
- // 测试几个关键坐标的映射关系
- var testPositions = new[]
- {
- new GridPosition(0, 0),
- new GridPosition(1, 1),
- new GridPosition(2, 3),
- new GridPosition(4, 4)
- };
-
- Console.WriteLine("GridPosition到Node.Position映射验证:");
- foreach (var gridPos in testPositions)
- {
- var node = grid.GetNode(gridPos);
- var nodePos = node.Position;
-
- Console.WriteLine($"网格({gridPos.X},{gridPos.Y}) -> 节点位置({nodePos.X:F2},{nodePos.Y:F2})");
-
- // 验证映射关系:GridPosition * CellSize = Node.Position
- var expectedX = gridPos.X * 2.0; // cellSize = 2米
- var expectedY = gridPos.Y * 2.0;
-
- Assert.AreEqual(expectedX, nodePos.X, 0.01, $"X坐标映射错误:网格{gridPos.X}应对应节点{expectedX}");
- Assert.AreEqual(expectedY, nodePos.Y, 0.01, $"Y坐标映射错误:网格{gridPos.Y}应对应节点{expectedY}");
- }
- }
-
- ///
- /// 实际问题场景重现测试
- /// 重现日志中出现的49,25到62,25障碍物路径问题
- ///
- [TestMethod]
- public void TestActualProblemScenario()
- {
- // 模拟实际场景:创建包含问题区域的网格
- var gridSize = new GridSize(120, 50); // 足够大的网格
- var cellSize = new Size(Distance.FromMeters(1.0f), Distance.FromMeters(1.0f));
- var velocity = Velocity.FromKilometersPerHour(5.0f);
-
- var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, velocity);
-
- // 根据日志,将50,25到61,25设置为障碍物
- var obstaclePositions = new List();
- for (int x = 50; x <= 61; x++)
- {
- obstaclePositions.Add(new GridPosition(x, 25));
- }
-
- Console.WriteLine("设置障碍物位置:");
- foreach (var obstaclePos in obstaclePositions)
- {
- grid.DisconnectNode(obstaclePos);
- Console.WriteLine($" 断开网格({obstaclePos.X},{obstaclePos.Y})");
-
- // 验证断开成功
- var node = grid.GetNode(obstaclePos);
- var edgeCount = node.Outgoing.Count();
- Assert.AreEqual(0, edgeCount, $"障碍物网格({obstaclePos.X},{obstaclePos.Y})应该没有出边,实际有{edgeCount}条");
- }
-
- // 尝试从49,25到62,25寻路(跨越障碍物区域)
- var startPos = new GridPosition(49, 25);
- var endPos = new GridPosition(62, 25);
-
- Console.WriteLine($"开始寻路:从({startPos.X},{startPos.Y})到({endPos.X},{endPos.Y})");
-
- var pathfinder = new PathFinder();
- var path = pathfinder.FindPath(startPos, endPos, grid);
-
- if (path != null && path.Edges.Count > 0)
- {
- Console.WriteLine($"找到路径,包含{path.Edges.Count}条边");
- Console.WriteLine($"路径类型: {path.Type}");
-
- // 提取路径经过的所有网格坐标
- var pathGridPositions = new List { startPos };
-
- foreach (var edge in path.Edges)
- {
- var nodeGridX = (int)Math.Floor(edge.End.Position.X / 1.0);
- var nodeGridY = (int)Math.Floor(edge.End.Position.Y / 1.0);
- var nodeGridPos = new GridPosition(nodeGridX, nodeGridY);
- pathGridPositions.Add(nodeGridPos);
- }
-
- Console.WriteLine("路径经过的网格坐标:");
- foreach (var pos in pathGridPositions)
- {
- Console.WriteLine($" 网格({pos.X},{pos.Y})");
- }
-
- // 关键验证:检查路径是否经过任何障碍物位置
- var pathThroughObstacles = new List();
- foreach (var pathPos in pathGridPositions)
- {
- if (obstaclePositions.Any(obs => obs.X == pathPos.X && obs.Y == pathPos.Y))
- {
- pathThroughObstacles.Add(pathPos);
- }
- }
-
- if (pathThroughObstacles.Any())
- {
- Console.WriteLine("❌ 发现问题:路径穿越了以下障碍物位置:");
- foreach (var obs in pathThroughObstacles)
- {
- Console.WriteLine($" 障碍物网格({obs.X},{obs.Y})");
- }
-
- Assert.Fail($"路径不应该穿越障碍物!发现{pathThroughObstacles.Count}个穿越点");
- }
- else
- {
- Console.WriteLine("✅ 路径正确避开了所有障碍物");
- }
- }
- else
- {
- Console.WriteLine("未找到路径");
- // 在这种情况下,应该尝试找到最近接近路径
- Assert.IsNotNull(path, "至少应该返回ClosestApproach类型的路径");
- }
- }
-
- ///
- /// 网格连接状态详细检查测试
- /// 验证断开节点后,相邻节点的连接状态
- ///
- [TestMethod]
- public void TestGridConnectionState()
- {
- var gridSize = new GridSize(5, 5);
- var cellSize = new Size(Distance.FromMeters(1.0f), Distance.FromMeters(1.0f));
- var velocity = Velocity.FromKilometersPerHour(5.0f);
-
- var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, velocity);
-
- // 断开中心位置(2,2)
- var centerPos = new GridPosition(2, 2);
- grid.DisconnectNode(centerPos);
-
- // 检查中心位置的连接状态
- var centerNode = grid.GetNode(centerPos);
- Console.WriteLine($"中心节点({centerPos.X},{centerPos.Y})出边数: {centerNode.Outgoing.Count()}");
- Console.WriteLine($"中心节点({centerPos.X},{centerPos.Y})入边数: {centerNode.Incoming.Count()}");
-
- // 检查相邻节点是否还指向已断开的中心节点
- var adjacentPositions = new[]
- {
- new GridPosition(1, 2), // 左
- new GridPosition(3, 2), // 右
- new GridPosition(2, 1), // 下
- new GridPosition(2, 3) // 上
- };
-
- Console.WriteLine("相邻节点连接检查:");
- foreach (var adjPos in adjacentPositions)
- {
- var adjNode = grid.GetNode(adjPos);
- var connectsToCenter = adjNode.Outgoing.Any(edge =>
- {
- var targetGridX = (int)Math.Floor(edge.End.Position.X / 1.0);
- var targetGridY = (int)Math.Floor(edge.End.Position.Y / 1.0);
- return targetGridX == centerPos.X && targetGridY == centerPos.Y;
- });
-
- Console.WriteLine($" 节点({adjPos.X},{adjPos.Y}) -> 中心节点: {(connectsToCenter ? "是" : "否")}");
- Assert.IsFalse(connectsToCenter, $"相邻节点({adjPos.X},{adjPos.Y})不应该连接到已断开的中心节点");
- }
- }
-
- ///
- /// 使用真实坐标系统的A*算法测试
- /// 集成实际系统的坐标转换方法,以复现真实环境中的问题
- ///
- [TestMethod]
- public void TestWithRealCoordinateSystem()
- {
- Console.WriteLine("=== 开始真实坐标系统A*测试 ===");
-
- // 创建模拟的GridMap - 使用实际系统的参数
- var mockGridMap = CreateRealGridMap();
-
- // 创建对应的Roy_T.AStar网格
- var astarGrid = CreateAStarGridFromGridMap(mockGridMap);
-
- // 设置障碍物 - 复现实际问题场景中的障碍物位置
- SetupObstaclesInMockGridMap(mockGridMap, astarGrid);
-
- // 定义测试起点和终点(使用实际世界坐标)
- var startWorld = new Point3D(25.0, 12.5, 0.0); // 对应网格(49,25)的世界坐标
- var endWorld = new Point3D(31.0, 12.5, 0.0); // 对应网格(62,25)的世界坐标
-
- Console.WriteLine($"测试场景: 从世界坐标({startWorld.X}, {startWorld.Y})到({endWorld.X}, {endWorld.Y})");
-
- // 执行真实的坐标转换和A*寻路
- var pathResult = ExecuteRealCoordinatePathfinding(startWorld, endWorld, mockGridMap, astarGrid);
-
- // 验证结果
- ValidateRealCoordinatePathResult(pathResult, startWorld, endWorld, mockGridMap);
- }
-
- ///
- /// 通道路径查找测试
- /// 创建一个有明确通道的场景,验证A*返回的路径坐标是否正确
- ///
- [TestMethod]
- public void TestChannelPathfinding()
- {
- Console.WriteLine("=== 开始通道路径查找测试 ===");
-
- // 创建测试场景:60x50网格,0.5米单元
- var gridSize = new GridSize(60, 50);
- var cellSize = new Size(Distance.FromMeters(0.5f), Distance.FromMeters(0.5f));
- var velocity = Velocity.FromKilometersPerHour(5.0f);
-
- var grid = Grid.CreateGridWithLateralConnections(gridSize, cellSize, velocity);
-
- // 设置障碍物:创建一个通道场景
- // 障碍物区域:X=48-54, Y=24和Y=27(上下墙)
- // X=48和X=54, Y=24-27(左右墙)
- // 通道区域:X=49-53, Y=25-26(可通行)
-
- var obstaclePositions = new List();
-
- // 上下墙
- for (int x = 48; x <= 54; x++)
- {
- obstaclePositions.Add(new GridPosition(x, 24)); // 下墙
- obstaclePositions.Add(new GridPosition(x, 27)); // 上墙
- }
-
- // 左右墙
- for (int y = 25; y <= 26; y++)
- {
- obstaclePositions.Add(new GridPosition(48, y)); // 左墙
- obstaclePositions.Add(new GridPosition(54, y)); // 右墙
- }
-
- Console.WriteLine("设置障碍物布局:");
- Console.WriteLine(" 上墙: X=48-54, Y=27");
- Console.WriteLine(" 下墙: X=48-54, Y=24");
- Console.WriteLine(" 左墙: X=48, Y=25-26");
- Console.WriteLine(" 右墙: X=54, Y=25-26");
- Console.WriteLine(" 通道: X=49-53, Y=25-26");
-
- // 设置障碍物
- foreach (var obstaclePos in obstaclePositions)
- {
- grid.DisconnectNode(obstaclePos);
- }
-
- // 验证障碍物设置
- Console.WriteLine("\n障碍物设置验证:");
- int obstacleCount = 0;
- foreach (var obstaclePos in obstaclePositions)
- {
- var node = grid.GetNode(obstaclePos);
- var edgeCount = node.Outgoing.Count();
- if (edgeCount == 0) obstacleCount++;
- Console.WriteLine($" 障碍物({obstaclePos.X},{obstaclePos.Y})出边数: {edgeCount}");
- }
- Console.WriteLine($"成功设置{obstacleCount}个障碍物网格");
-
- // 设置测试起点和终点:在通道内但靠近障碍物
- var startPos = new GridPosition(49, 25); // 通道左下角
- var endPos = new GridPosition(53, 26); // 通道右上角
-
- Console.WriteLine($"\n开始寻路:从网格({startPos.X},{startPos.Y})到网格({endPos.X},{endPos.Y})");
-
- // 执行A*寻路
- var pathfinder = new PathFinder();
- var path = pathfinder.FindPath(startPos, endPos, grid);
-
- // 验证找到了路径
- Assert.IsNotNull(path, "应该能在通道中找到路径");
- Assert.IsTrue(path.Edges.Count > 0, $"路径应该包含边,实际边数: {path.Edges.Count}");
-
- Console.WriteLine($"✅ 找到路径,包含{path.Edges.Count}条边");
- Console.WriteLine($"路径类型: {path.Type}");
-
- // 关键验证:检查路径上的每个点
- var pathPoints = new List { startPos };
-
- Console.WriteLine("\nA*路径坐标详细分析:");
- Console.WriteLine("起点转换: A*位置 -> 米坐标 -> 网格坐标");
-
- int pointIndex = 0;
- foreach (var edge in path.Edges)
- {
- pointIndex++;
- var endMeterPos = edge.End.Position;
-
- // 关键:将A*返回的米坐标转换回网格坐标
- var calculatedGridX = (int)Math.Floor(endMeterPos.X / 0.5); // cellSize = 0.5米
- var calculatedGridY = (int)Math.Floor(endMeterPos.Y / 0.5);
- var calculatedGridPos = new GridPosition(calculatedGridX, calculatedGridY);
-
- pathPoints.Add(calculatedGridPos);
-
- Console.WriteLine($" 点{pointIndex}: A*米坐标({endMeterPos.X:F2}, {endMeterPos.Y:F2}) -> 计算网格({calculatedGridPos.X}, {calculatedGridPos.Y})");
- }
-
- // 验证所有路径点是否在允许的通道区域内
- Console.WriteLine("\n路径点合法性检查:");
- var invalidPoints = new List();
-
- foreach (var point in pathPoints)
- {
- bool isInChannel = (point.X >= 49 && point.X <= 53) && (point.Y >= 25 && point.Y <= 26);
- bool isObstacle = obstaclePositions.Any(obs => obs.X == point.X && obs.Y == point.Y);
-
- string status;
- if (isObstacle)
- {
- status = "❌障碍物";
- invalidPoints.Add(point);
- }
- else if (isInChannel)
- {
- status = "✅通道内";
- }
- else
- {
- status = "⚠️通道外";
- invalidPoints.Add(point);
- }
-
- Console.WriteLine($" 网格({point.X},{point.Y}) - {status}");
- }
-
- // 最终验证
- if (invalidPoints.Any())
- {
- Console.WriteLine($"\n🔥 发现问题:{invalidPoints.Count}个路径点在非法位置!");
- foreach (var invalid in invalidPoints)
- {
- Console.WriteLine($" 非法点: 网格({invalid.X},{invalid.Y})");
- }
-
- Assert.Fail($"A*路径包含{invalidPoints.Count}个非法位置的点!这证明了A*算法或坐标转换存在问题。");
- }
- else
- {
- Console.WriteLine("\n✅ 所有路径点都在合法的通道区域内");
- Console.WriteLine("✅ A*算法返回的坐标转换正确");
- }
- }
-
- ///
- /// 创建模拟的GridMap,使用实际系统的参数
- ///
- private MockGridMap CreateRealGridMap()
- {
- // 模拟实际系统的GridMap参数
- var origin = new Point2D(0.0, 0.0); // 网格原点
- var cellSize = 0.5; // 网格单元大小:0.5米
- var width = 150; // 网格宽度:150个单元
- var height = 80; // 网格高度:80个单元
- var bounds = new BoundingBox3D(
- new Point3D(0, 0, 0),
- new Point3D(width * cellSize, height * cellSize, 10.0)
- );
-
- Console.WriteLine($"创建GridMap - 原点:({origin.X}, {origin.Y}), 单元大小:{cellSize}m, 尺寸:{width}x{height}");
-
- var gridMap = new MockGridMap(origin, cellSize, width, height, bounds);
-
- // 初始化为可通行网格
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- gridMap.SetCell(new GridPoint2D(x, y), true);
- }
- }
-
- return gridMap;
- }
-
- ///
- /// 从GridMap创建对应的Roy_T.AStar网格
- ///
- private Grid CreateAStarGridFromGridMap(MockGridMap gridMap)
- {
- // 使用真实的单位转换
- double cellSizeInMeters = MockUnitsConverter.ConvertToMeters(gridMap.CellSize);
-
- var gridSize = new GridSize(gridMap.Width, gridMap.Height);
- var cellSize = new Size(
- Distance.FromMeters((float)cellSizeInMeters),
- Distance.FromMeters((float)cellSizeInMeters)
- );
- var velocity = Velocity.FromKilometersPerHour(5.0f);
-
- Console.WriteLine($"创建A*网格 - 网格大小:{gridSize.Columns}x{gridSize.Rows}, 单元大小:{cellSizeInMeters}m");
-
- return Grid.CreateGridWithLateralConnections(gridSize, cellSize, velocity);
- }
-
- ///
- /// 在网格中设置障碍物
- ///
- private void SetupObstaclesInMockGridMap(MockGridMap gridMap, Grid astarGrid)
- {
- // 模拟实际问题场景:在路径中间设置障碍物
- var obstaclePositions = new List();
-
- // 设置从网格(50,25)到(61,25)为障碍物
- for (int x = 50; x <= 61; x++)
- {
- obstaclePositions.Add(new GridPoint2D(x, 25));
- }
-
- Console.WriteLine("设置障碍物位置:");
- foreach (var obstaclePos in obstaclePositions)
- {
- if (gridMap.IsValidGridPosition(obstaclePos))
- {
- // 在MockGridMap中设置为不可通行
- gridMap.SetCell(obstaclePos, false);
-
- // 在A*网格中断开连接
- var astarPos = new GridPosition(obstaclePos.X, obstaclePos.Y);
- astarGrid.DisconnectNode(astarPos);
-
- var worldPos = gridMap.GridToWorld2D(obstaclePos);
- Console.WriteLine($" 障碍物网格({obstaclePos.X},{obstaclePos.Y}) -> 世界坐标({worldPos.X:F2},{worldPos.Y:F2})");
-
- // 验证断开成功
- var node = astarGrid.GetNode(astarPos);
- var edgeCount = node.Outgoing.Count();
- Console.WriteLine($" A*节点出边数: {edgeCount}");
- }
- }
- }
-
- ///
- /// 执行真实的坐标转换和路径查找
- ///
- private PathFindingResult ExecuteRealCoordinatePathfinding(Point3D startWorld, Point3D endWorld, MockGridMap gridMap, Grid astarGrid)
- {
- Console.WriteLine("=== 执行真实坐标转换和A*寻路 ===");
-
- // 步骤1: 世界坐标转换为网格坐标
- var startGrid = gridMap.WorldToGrid(startWorld);
- var endGrid = gridMap.WorldToGrid(endWorld);
-
- Console.WriteLine($"世界坐标转换:");
- Console.WriteLine($" 起点: 世界({startWorld.X}, {startWorld.Y}) -> 网格({startGrid.X}, {startGrid.Y})");
- Console.WriteLine($" 终点: 世界({endWorld.X}, {endWorld.Y}) -> 网格({endGrid.X}, {endGrid.Y})");
-
- // 步骤2: 网格坐标转换为A*位置
- var startPos = new GridPosition(startGrid.X, startGrid.Y);
- var endPos = new GridPosition(endGrid.X, endGrid.Y);
-
- Console.WriteLine($"A*位置设置:");
- Console.WriteLine($" 起点A*位置: ({startPos.X}, {startPos.Y})");
- Console.WriteLine($" 终点A*位置: ({endPos.X}, {endPos.Y})");
-
- // 步骤3: 执行A*算法
- var pathfinder = new PathFinder();
- var astarPath = pathfinder.FindPath(startPos, endPos, astarGrid);
-
- Console.WriteLine($"A*寻路结果: {(astarPath != null ? $"找到路径,包含{astarPath.Edges.Count}条边" : "未找到路径")}");
-
- if (astarPath != null && astarPath.Edges.Count > 0)
- {
- // 步骤4: 转换A*路径回世界坐标
- var worldPath = ConvertAStarPathToWorldCoordinates(astarPath, gridMap);
-
- return new PathFindingResult
- {
- PathPoints = worldPath,
- OriginalEndPoint = endWorld,
- IsSuccessful = true
- };
- }
-
- return new PathFindingResult
- {
- PathPoints = new List(),
- OriginalEndPoint = endWorld,
- IsSuccessful = false
- };
- }
-
- ///
- /// 转换A*路径为世界坐标(模拟AutoPathFinder的逻辑)
- ///
- private List ConvertAStarPathToWorldCoordinates(Path astarPath, MockGridMap gridMap)
- {
- var worldPath = new List();
-
- Console.WriteLine("=== A*路径转换为世界坐标 ===");
-
- // 获取单位转换因子(模拟真实系统)
- double metersToWorldUnit = 1.0 / MockUnitsConverter.ConvertToMeters(1.0);
- Console.WriteLine($"单位转换因子: metersToWorldUnit = {metersToWorldUnit}");
-
- if (astarPath.Edges.Count > 0)
- {
- // 添加起点
- var startNode = astarPath.Edges[0].Start;
- var startWorldPos = new Point3D(
- gridMap.Origin.X + startNode.Position.X * metersToWorldUnit,
- gridMap.Origin.Y + startNode.Position.Y * metersToWorldUnit,
- 0
- );
-
- Console.WriteLine($"起点转换: A*米坐标({startNode.Position.X:F2}, {startNode.Position.Y:F2}) -> 世界坐标({startWorldPos.X:F2}, {startWorldPos.Y:F2})");
- worldPath.Add(startWorldPos);
-
- // 添加路径上的每个点
- foreach (var edge in astarPath.Edges)
- {
- var endWorldPos = new Point3D(
- gridMap.Origin.X + edge.End.Position.X * metersToWorldUnit,
- gridMap.Origin.Y + edge.End.Position.Y * metersToWorldUnit,
- 0
- );
-
- // 转换为网格坐标以便分析
- var endGridPos = gridMap.WorldToGrid(endWorldPos);
-
- Console.WriteLine($"路径点: A*米坐标({edge.End.Position.X:F2}, {edge.End.Position.Y:F2}) -> 世界坐标({endWorldPos.X:F2}, {endWorldPos.Y:F2}) -> 网格({endGridPos.X}, {endGridPos.Y})");
-
- worldPath.Add(endWorldPos);
- }
- }
-
- return worldPath;
- }
-
- ///
- /// 验证真实坐标系统下的路径结果
- ///
- private void ValidateRealCoordinatePathResult(PathFindingResult result, Point3D startWorld, Point3D endWorld, MockGridMap gridMap)
- {
- Console.WriteLine("=== 验证路径结果 ===");
-
- if (!result.IsSuccessful || result.PathPoints.Count == 0)
- {
- Console.WriteLine("❌ 未找到路径 - 这可能是正常的,因为障碍物阻挡了直接路径");
- return;
- }
-
- Console.WriteLine($"✅ 找到路径,包含{result.PathPoints.Count}个点");
-
- // 转换所有路径点为网格坐标以便检查
- var pathGridPositions = new List();
- foreach (var worldPos in result.PathPoints)
- {
- var gridPos = gridMap.WorldToGrid(worldPos);
- pathGridPositions.Add(gridPos);
- }
-
- Console.WriteLine("路径经过的网格坐标:");
- foreach (var gridPos in pathGridPositions)
- {
- var cell = gridMap.GetCell(gridPos);
- var isWalkable = cell.IsWalkable;
- var status = isWalkable ? "✅可通行" : "❌障碍物";
- Console.WriteLine($" 网格({gridPos.X},{gridPos.Y}) - {status}");
- }
-
- // 检查是否有路径穿越障碍物
- var obstacleTraversals = new List();
- foreach (var gridPos in pathGridPositions)
- {
- var cell = gridMap.GetCell(gridPos);
- if (!cell.IsWalkable)
- {
- obstacleTraversals.Add(gridPos);
- }
- }
-
- if (obstacleTraversals.Any())
- {
- Console.WriteLine($"🔥 发现问题:路径穿越了{obstacleTraversals.Count}个障碍物网格!");
- foreach (var obs in obstacleTraversals)
- {
- Console.WriteLine($" 障碍物穿越: 网格({obs.X},{obs.Y})");
- }
-
- Assert.Fail($"真实坐标系统下发现A*路径穿越障碍物问题!穿越了{obstacleTraversals.Count}个障碍物网格。");
- }
- else
- {
- Console.WriteLine("✅ 路径正确避开了所有障碍物");
- }
- }
-
- ///
- /// 模拟路径查找结果类
- ///
- private class PathFindingResult
- {
- public List PathPoints { get; set; } = new List();
- public Point3D OriginalEndPoint { get; set; }
- public bool IsSuccessful { get; set; }
- }
-
- ///
- /// 模拟UnitsConverter类(用于测试)
- ///
- private static class MockUnitsConverter
- {
- ///
- /// 模拟:将距离从文档单位转换为米
- /// 假设文档单位就是米,转换因子为1.0
- ///
- public static double ConvertToMeters(double distance)
- {
- return distance * 1.0; // 假设文档单位就是米
- }
- }
-
- ///
- /// 模拟GridMap类(用于测试)
- ///
- private class MockGridMap
- {
- public Point2D Origin { get; private set; }
- public double CellSize { get; private set; }
- public int Width { get; private set; }
- public int Height { get; private set; }
- public BoundingBox3D Bounds { get; private set; }
-
- private MockGridCell[,] cells;
-
- public MockGridMap(Point2D origin, double cellSize, int width, int height, BoundingBox3D bounds)
- {
- Origin = origin;
- CellSize = cellSize;
- Width = width;
- Height = height;
- Bounds = bounds;
- cells = new MockGridCell[width, height];
-
- // 初始化所有单元格
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- cells[x, y] = new MockGridCell { IsWalkable = false };
- }
- }
- }
-
- ///
- /// 世界坐标转换为网格坐标
- ///
- public GridPoint2D WorldToGrid(Point3D worldPosition)
- {
- double gridX = (worldPosition.X - Origin.X) / CellSize;
- double gridY = (worldPosition.Y - Origin.Y) / CellSize;
- return new GridPoint2D((int)Math.Floor(gridX), (int)Math.Floor(gridY));
- }
-
- ///
- /// 网格坐标转换为世界2D坐标(网格左下角)
- ///
- public Point3D GridToWorld2D(GridPoint2D gridPosition)
- {
- double worldX = Origin.X + gridPosition.X * CellSize;
- double worldY = Origin.Y + gridPosition.Y * CellSize;
- return new Point3D(worldX, worldY, 0);
- }
-
- ///
- /// 检查网格坐标是否有效
- ///
- public bool IsValidGridPosition(GridPoint2D gridPosition)
- {
- return gridPosition.X >= 0 && gridPosition.X < Width &&
- gridPosition.Y >= 0 && gridPosition.Y < Height;
- }
-
- ///
- /// 设置网格单元
- ///
- public void SetCell(GridPoint2D gridPosition, bool isWalkable)
- {
- if (IsValidGridPosition(gridPosition))
- {
- cells[gridPosition.X, gridPosition.Y].IsWalkable = isWalkable;
- }
- }
-
- ///
- /// 获取网格单元
- ///
- public MockGridCell GetCell(GridPoint2D gridPosition)
- {
- if (IsValidGridPosition(gridPosition))
- {
- return cells[gridPosition.X, gridPosition.Y];
- }
- return new MockGridCell { IsWalkable = false };
- }
- }
-
- ///
- /// 模拟GridCell类(用于测试)
- ///
- private class MockGridCell
- {
- public bool IsWalkable { get; set; }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Core/PathDataManager.cs b/src/Core/PathDataManager.cs
index 0c9b30c..e637879 100644
--- a/src/Core/PathDataManager.cs
+++ b/src/Core/PathDataManager.cs
@@ -192,6 +192,14 @@ namespace NavisworksTransport
name = route.Name,
description = route.Description ?? "",
totalLength = Math.Round(route.TotalLength, exportSettings?.Precision ?? 6),
+ vehicleLimits = new
+ {
+ maxLength = Math.Round(route.MaxVehicleLength, exportSettings?.Precision ?? 6),
+ maxWidth = Math.Round(route.MaxVehicleWidth, exportSettings?.Precision ?? 6),
+ maxHeight = Math.Round(route.MaxVehicleHeight, exportSettings?.Precision ?? 6),
+ safetyMargin = Math.Round(route.SafetyMargin, exportSettings?.Precision ?? 6)
+ },
+ gridSize = Math.Round(route.GridSize, exportSettings?.Precision ?? 6),
created = route.CreatedTime.ToString("yyyy-MM-ddTHH:mm:ss"),
points = route.GetSortedPoints().Select(point => new
{
@@ -253,7 +261,7 @@ namespace NavisworksTransport
using (var writer = new StreamWriter(filePath, false, Encoding.UTF8))
{
// 写入CSV表头
- writer.WriteLine("路径名称,路径描述,点编号,点名称,X坐标,Y坐标,Z坐标,点类型,点索引,创建时间,路径总长度,路径创建时间");
+ writer.WriteLine("路径名称,路径描述,点编号,点名称,X坐标,Y坐标,Z坐标,点类型,点索引,创建时间,路径总长度,限长,限宽,限高,安全间隙,网格尺寸,路径创建时间");
// 写入数据行
foreach (var route in routes.Where(r => r.IsValid()))
@@ -273,6 +281,11 @@ namespace NavisworksTransport
point.Index.ToString(),
point.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss"),
route.TotalLength.ToString("F3", CultureInfo.InvariantCulture),
+ route.MaxVehicleLength.ToString("F3", CultureInfo.InvariantCulture),
+ route.MaxVehicleWidth.ToString("F3", CultureInfo.InvariantCulture),
+ route.MaxVehicleHeight.ToString("F3", CultureInfo.InvariantCulture),
+ route.SafetyMargin.ToString("F3", CultureInfo.InvariantCulture),
+ route.GridSize.ToString("F3", CultureInfo.InvariantCulture),
route.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss")
);
writer.WriteLine(row);
@@ -658,6 +671,11 @@ namespace NavisworksTransport
routeElement.SetAttribute("name", route.Name);
routeElement.SetAttribute("description", route.Description ?? "");
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("safetyMargin", route.SafetyMargin.ToString("F3"));
+ routeElement.SetAttribute("gridSize", route.GridSize.ToString("F3"));
routeElement.SetAttribute("created", route.CreatedTime.ToString("yyyy-MM-ddTHH:mm:ss"));
// 添加路径点
@@ -694,7 +712,12 @@ namespace NavisworksTransport
{
Id = routeNode.Attributes?["id"]?.Value ?? Guid.NewGuid().ToString(),
Name = routeNode.Attributes?["name"]?.Value ?? "导入路径",
- Description = routeNode.Attributes?["description"]?.Value ?? ""
+ Description = routeNode.Attributes?["description"]?.Value ?? "",
+ MaxVehicleLength = double.TryParse(routeNode.Attributes?["maxVehicleLength"]?.Value, out double maxLength) ? maxLength : 1.0,
+ MaxVehicleWidth = double.TryParse(routeNode.Attributes?["maxVehicleWidth"]?.Value, out double maxWidth) ? maxWidth : 1.0,
+ MaxVehicleHeight = double.TryParse(routeNode.Attributes?["maxVehicleHeight"]?.Value, out double maxHeight) ? maxHeight : 2.0,
+ SafetyMargin = double.TryParse(routeNode.Attributes?["safetyMargin"]?.Value, out double safetyMargin) ? safetyMargin : 0.5,
+ GridSize = double.TryParse(routeNode.Attributes?["gridSize"]?.Value, out double gridSize) ? gridSize : 0.5
};
// 解析创建时间
@@ -788,6 +811,15 @@ 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 segmentsElement = xmlDoc.CreateElement("Segments", _delmiaNamespace);
pathElement.AppendChild(segmentsElement);
diff --git a/src/Core/PathDatabase.cs b/src/Core/PathDatabase.cs
index 366eda7..ea8dee5 100644
--- a/src/Core/PathDatabase.cs
+++ b/src/Core/PathDatabase.cs
@@ -68,6 +68,11 @@ namespace NavisworksTransport
Name TEXT NOT NULL,
TotalLength REAL,
EstimatedTime REAL,
+ MaxVehicleLength REAL,
+ MaxVehicleWidth REAL,
+ MaxVehicleHeight REAL,
+ SafetyMargin REAL,
+ GridSize REAL,
CreatedTime DATETIME,
LastModified DATETIME
)
@@ -136,8 +141,8 @@ namespace NavisworksTransport
// 保存路径基本信息
var sql = @"
INSERT OR REPLACE INTO PathRoutes
- (Id, Name, TotalLength, EstimatedTime, CreatedTime, LastModified)
- VALUES (@id, @name, @length, @time, @created, @modified)
+ (Id, Name, TotalLength, EstimatedTime, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight, SafetyMargin, GridSize, CreatedTime, LastModified)
+ VALUES (@id, @name, @length, @time, @maxLength, @maxWidth, @maxHeight, @safetyMargin, @gridSize, @created, @modified)
";
using (var cmd = new SQLiteCommand(sql, _connection))
@@ -146,6 +151,11 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@name", route.Name);
cmd.Parameters.AddWithValue("@length", route.TotalLength);
cmd.Parameters.AddWithValue("@time", route.EstimatedTime);
+ cmd.Parameters.AddWithValue("@maxLength", route.MaxVehicleLength);
+ cmd.Parameters.AddWithValue("@maxWidth", route.MaxVehicleWidth);
+ cmd.Parameters.AddWithValue("@maxHeight", route.MaxVehicleHeight);
+ cmd.Parameters.AddWithValue("@safetyMargin", route.SafetyMargin);
+ cmd.Parameters.AddWithValue("@gridSize", route.GridSize);
cmd.Parameters.AddWithValue("@created", route.CreatedTime);
cmd.Parameters.AddWithValue("@modified", DateTime.Now);
cmd.ExecuteNonQuery();
@@ -393,6 +403,11 @@ namespace NavisworksTransport
Name = reader["Name"].ToString(),
TotalLength = Convert.ToDouble(reader["TotalLength"]),
EstimatedTime = Convert.ToDouble(reader["EstimatedTime"]),
+ MaxVehicleLength = reader.IsDBNull(reader.GetOrdinal("MaxVehicleLength")) ? 1.0 : Convert.ToDouble(reader["MaxVehicleLength"]),
+ MaxVehicleWidth = reader.IsDBNull(reader.GetOrdinal("MaxVehicleWidth")) ? 1.0 : Convert.ToDouble(reader["MaxVehicleWidth"]),
+ MaxVehicleHeight = reader.IsDBNull(reader.GetOrdinal("MaxVehicleHeight")) ? 2.0 : Convert.ToDouble(reader["MaxVehicleHeight"]),
+ SafetyMargin = reader.IsDBNull(reader.GetOrdinal("SafetyMargin")) ? 0.5 : Convert.ToDouble(reader["SafetyMargin"]),
+ GridSize = reader.IsDBNull(reader.GetOrdinal("GridSize")) ? 0.5 : Convert.ToDouble(reader["GridSize"]),
CreatedTime = Convert.ToDateTime(reader["CreatedTime"]),
LastModified = Convert.ToDateTime(reader["LastModified"])
};
diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs
index b90f418..197744d 100644
--- a/src/Core/PathPlanningManager.cs
+++ b/src/Core/PathPlanningManager.cs
@@ -1029,9 +1029,11 @@ namespace NavisworksTransport
// 保存GridMap和参数到PathRoute以便后续恢复网格可视化
autoRoute.AssociatedGridMap = gridMap;
autoRoute.GridSize = gridSize;
- autoRoute.VehicleSize = vehicleRadius;
+ // 将vehicleRadius拆分为长宽(暂时使用相同值,后续可以传入更详细的参数)
+ autoRoute.MaxVehicleLength = vehicleRadius * 2; // 车辆半径转换为长度
+ autoRoute.MaxVehicleWidth = vehicleRadius * 2; // 车辆半径转换为宽度
+ autoRoute.MaxVehicleHeight = vehicleHeight;
autoRoute.SafetyMargin = safetyMargin;
- autoRoute.VehicleHeight = vehicleHeight;
LogManager.Info($"已保存GridMap到路径: {routeName}, 网格大小: {gridSize}米");
// 7. 添加到路径集合
diff --git a/src/Core/PathPlanningModels.cs b/src/Core/PathPlanningModels.cs
index 0c2ca47..c9093ef 100644
--- a/src/Core/PathPlanningModels.cs
+++ b/src/Core/PathPlanningModels.cs
@@ -394,20 +394,25 @@ namespace NavisworksTransport
public double GridSize { get; set; } = 0.5;
///
- /// 车辆尺寸(米)
+ /// 最大车辆长度(米) - 路径适用的车辆长度限制
///
- public double VehicleSize { get; set; } = 1.0;
+ public double MaxVehicleLength { get; set; } = 1.0;
+
+ ///
+ /// 最大车辆宽度(米) - 路径适用的车辆宽度限制
+ ///
+ public double MaxVehicleWidth { get; set; } = 1.0;
+
+ ///
+ /// 最大车辆高度(米) - 路径适用的车辆高度限制
+ ///
+ public double MaxVehicleHeight { get; set; } = 2.0;
///
/// 安全间隙(米)
///
public double SafetyMargin { get; set; } = 0.5;
- ///
- /// 车辆高度(米)
- ///
- public double VehicleHeight { get; set; } = 2.0;
-
// 数据库分析相关属性
///
/// 碰撞数量(从数据库加载)
@@ -617,9 +622,10 @@ namespace NavisworksTransport
CompletionPercentage = CompletionPercentage,
AssociatedGridMap = AssociatedGridMap, // 共享引用,不深拷贝
GridSize = GridSize,
- VehicleSize = VehicleSize,
- SafetyMargin = SafetyMargin,
- VehicleHeight = VehicleHeight
+ MaxVehicleLength = MaxVehicleLength,
+ MaxVehicleWidth = MaxVehicleWidth,
+ MaxVehicleHeight = MaxVehicleHeight,
+ SafetyMargin = SafetyMargin
};
// 克隆所有路径点