# 体素网格 3D 路径规划方案 ## 1. 背景与动机 ### 1.1 当前方案的局限性 当前 NavisworksTransport 插件采用 **2.5D 网格 + 高度层** 的路径规划方案: **数据结构**: ```csharp public class GridCell { public List HeightLayers { get; set; } // 多个高度层 // (X, Y) 网格坐标 + 离散的 Z 高度层 } ``` **局限性**: 1. **高度层离散化** - 只能表示有限数量的通行高度 - 相邻高度层之间的连接需要特殊处理 - 难以精确表示连续变化的高度 2. **复杂空间的表达受限** - 管道、桥梁下方的多层通道难以准确建模 - 斜坡、楼梯的连续高度变化需要大量高度层 - 悬空结构(如天桥)的下方空间处理复杂 3. **路径规划的限制** - 垂直方向的移动需要显式的"层间连接" - 真正的 3D 路径(如无人机、爬楼机器人)无法支持 - 算法复杂度高(需要处理层间跳转逻辑) 4. **可扩展性问题** - 增加高度层数会显著增加内存消耗 - 多层网格的生成和维护成本高 - 难以支持动态障碍物的实时更新 ### 1.2 体素网格的优势 采用 **真正的 3D 体素网格**(Voxel Grid)可以解决上述问题: **统一的 3D 表示**: ```csharp public class VoxelGrid { private VoxelCell[,,] cells; // 统一的 3D 数组 (X, Y, Z) } ``` **优势**: 1. ✅ **连续的 3D 空间**:任意高度都可以表示 2. ✅ **简化的路径算法**:标准 3D A* 算法,无需特殊层间逻辑 3. ✅ **精确的障碍物表示**:管道、横梁等任意 3D 形状 4. ✅ **更好的可视化**:直观的 3D 体素显示 5. ✅ **支持真 3D 路径**:无人机、飞行器、爬楼设备 --- ## 2. 技术调研:C# 体素网格库 ### 2.1 候选库对比 经过深入的 GitHub 和 NuGet 调研,找到以下候选库: | 库名 | Stars | 许可证 | 平台 | 活跃度 | 适用性 | |------|-------|--------|------|--------|--------| | **geometry4Sharp** | 273 | Boost | .NET Std 2.0 | 中等 | ⭐⭐⭐⭐⭐ | | geometry3Sharp (原版) | 1400+ | Boost | .NET Std 2.0 | 低 | ⭐⭐⭐⭐ | | Unity PathFinding3D | ~100 | MIT | Unity | 高 | ⭐⭐ (Unity 依赖) | | UnityOctree | ~900 | MIT | Unity | 中 | ⭐⭐ (Unity 依赖) | | 自行实现 | N/A | N/A | 任意 | N/A | ⭐⭐⭐ (工作量大) | ### 2.2 推荐方案:geometry4Sharp **GitHub**: **NuGet**: `geometry4Sharp` 1.0.0 #### 基本信息 - **许可证**: Boost Software License(商业友好,无需开源衍生作品) - **维护者**: New Wheel Technology - **代码来源**: Fork from geometry3Sharp,底层算法来自 WildMagic5/GTEngine (David Eberly) - **支持平台**: .NET Standard 2.0, .NET 6.0, .NET Framework 4.8 - **语言**: 纯 C#,无 C++ 互操作 #### 核心功能模块 1.体素化与距离场 ```csharp // 核心类 MeshSignedDistanceGrid // 网格签名距离场 MeshScalarSamplingGrid // 标量采样网格 Bitmap3 // 3D 位图(密集体素) DSparseGrid3 // 稀疏 3D 网格(按需分配) BiGrid3 // 两层稀疏网格(优化大规模场景) ``` **功能说明**: - **MeshSignedDistanceGrid**: - 从三角网格生成签名距离场 (SDF) - 使用快速行进法 (Fast Marching Method) - 支持窄带构建(仅计算障碍物附近区域,节省内存) - 基于 Christopher Batty 的 SDFGen C++ 代码移植 - **Bitmap3**: - 密集 3D 布尔数组 - 高效的内存布局 - 适合小到中等规模网格(< 1000³) - **DSparseGrid3**: - 稀疏 3D 网格,按需分配内存 - 适合大规模场景(仅存储非空体素) - 基于字典或哈希表实现 2.空间查询与距离计算 ```csharp // 距离查询 bool IsInside(Point3d point) // 点包含测试 double WindingNumber(Point3d point) // 绕组数(判断内外) double FastWindingNumber(Point3d point) // 快速绕组数 double Distance(Point3d point) // 到最近表面的距离 // 空间索引 DMeshAABBTree3 // 三角网格 AABB 树(加速相交测试) PointHashGrid3d // 3D 点哈希网格 PointAABBTree3 // 点云 AABB 树 ``` 3.网格操作与处理 ```csharp // 等值面提取 MarchingCubes // 从体素生成三角网格 MarchingCubesPro // 延续法行进立方体(更高效) // 网格简化与重新网格化 Remesher // 边分裂/翻转/坍缩网格 Reducer // 基于 QEM 的网格简化 // 几何计算 DMesh3 // 动态三角网格类 BoundsUtil // 包围盒计算 MeshMeasurements // 体积、质心、惯性张量 ``` 4.数学与几何工具 ```csharp // 向量数学(完整的 struct 实现) Vector2d/3d/4d, Vector2f/3f/4f Matrix2d/3d, Matrix2f/3f Quaterniond/f Frame3f // 位置+方向表示 // 几何形状 AxisAlignedBox3d/3f // 轴对齐包围盒 Box3d/3f // 有向包围盒 Triangle3d/3f, Segment3d/3f, Ray3d/3f, Plane3d/3f // 距离与相交查询 DistPoint3Triangle3, DistLine3Triangle3 IntrRay3Triangle3, IntrTriangle3Triangle3 ``` #### 为什么选择 geometry4Sharp? ✅ **功能完整**: - 提供完整的网格→体素→距离场工具链 - 丰富的空间查询和距离计算 - 优化的空间数据结构(AABB 树、哈希网格) ✅ **代码质量高**: - 源自 David Eberly 的 WildMagic5/GTEngine(几何计算领域的经典库) - 经过多年实战验证 - 纯 C# 实现,类型安全 ✅ **商业友好**: - Boost License 允许商业使用 - 无需开源衍生代码 - 无专利限制 ✅ **平台兼容**: - 支持 .NET Framework 4.8(Navisworks 2026 要求) - 无 Unity 依赖,纯 .NET 库 - 可与 Navisworks API 无缝集成 ✅ **性能优化**: - 多线程支持(Marching Cubes 等) - 内存高效(稀疏网格、窄带 SDF) - 空间索引加速查询 ⚠️ **不足之处**: - 不包含路径规划算法(需结合 Roy-T.AStar) - 文档以 README 为主,缺少专门的 API 文档网站 - 活跃度中等(原项目 2019 年后维护减少) #### 与 Roy-T.AStar 的集成策略 geometry4Sharp 提供**空间表示**,Roy-T.AStar 提供**路径算法**: ```graph ┌────────────────────────────────────────┐ │ Navisworks BIM 模型 │ │ (三角网格、包围盒) │ └──────────────┬─────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ geometry4Sharp │ │ - MeshSignedDistanceGrid (体素化) │ │ - Distance Field (距离计算) │ │ - IsInside (可通行性判断) │ └──────────────┬─────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ VoxelGrid 适配层 │ │ - 转换为 Roy-T.AStar Grid │ │ - 或构建自定义 3D Graph │ └──────────────┬─────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ Roy-T.AStar │ │ - A* 路径规划算法 │ │ - PathFinder.FindPath() │ └──────────────┬─────────────────────────┘ │ ▼ ┌────────────────────────────────────────┐ │ PathOptimizer │ │ - 路径平滑、简化 │ └────────────────────────────────────────┘ ``` --- ## 3. 架构设计 ### 3.1 整体架构 ```graph ┌──────────────────────────────────────────────────────────┐ │ Navisworks API │ │ (ModelItem, BoundingBox, Geometry) │ └────────────────────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ NavisworksGeometryExtractor │ │ - ExtractTriangleMesh(): DMesh3 │ │ - ExtractBoundingBoxes(): List │ └────────────────────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ VoxelGridGenerator (新) │ │ - GenerateFromBIM() │ │ ├── MeshSignedDistanceGrid (geometry4Sharp) │ │ ├── 体素标记 (Obstacle/Free/Door/...) │ │ └── 生成 VoxelGrid │ └────────────────────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ VoxelGrid (新) │ │ - cells: VoxelCell[X, Y, Z] │ │ - IsPassable(x, y, z): bool │ │ - GetDistance(x, y, z): double │ │ - GetNeighbors(x, y, z): List<(int,int,int)> │ └────────────────────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ VoxelPathFinder (新,替代 AutoPathFinder) │ │ - BuildGraph3D(): 构建 3D 节点图 │ │ - FindPath(): 使用 Roy-T.AStar 进行 3D A* 搜索 │ │ - ConvertToWorldPath(): 体素坐标 → 世界坐标 │ └────────────────────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ PathOptimizer │ │ - SimplifyPath() (保留,适配 3D 路径) │ └────────────────────────┬─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────────┐ │ LogisticsAnimationManager │ │ - AnimatePath() (无需修改) │ └──────────────────────────────────────────────────────────┘ ``` ### 3.2 核心类设计 #### 3.2.1 VoxelCell - 体素单元 ```csharp /// /// 3D 体素单元,表示空间中的一个立方体区域 /// public class VoxelCell { /// /// 体素类型(障碍物、通道、门、楼梯等) /// public LogisticsElementType Type { get; set; } /// /// 是否可通行 /// public bool IsPassable { get; set; } /// /// 到最近障碍物表面的距离(来自 SDF) /// public double Distance { get; set; } /// /// 速度限制(米/秒),用于门、楼梯等特殊区域 /// public double SpeedLimit { get; set; } /// /// 关联的 Navisworks 模型元素(可选) /// public ModelItem SourceItem { get; set; } /// /// 通行成本(基于距离、类型、速度限制计算) /// public double Cost { get; set; } } ``` #### 3.2.2 VoxelGrid - 3D 体素网格 ```csharp /// /// 3D 体素网格,表示整个路径规划空间 /// public class VoxelGrid { #region 字段 /// /// 体素数组 [X, Y, Z] /// private VoxelCell[,,] cells; /// /// 世界坐标原点(网格左下后角) /// public Point3D Origin { get; private set; } /// /// 体素大小(米) /// public double VoxelSize { get; private set; } /// /// 网格尺寸(体素数量) /// public int SizeX { get; private set; } public int SizeY { get; private set; } public int SizeZ { get; private set; } #endregion #region 构造函数 public VoxelGrid(Point3D origin, double voxelSize, int sizeX, int sizeY, int sizeZ) { Origin = origin; VoxelSize = voxelSize; SizeX = sizeX; SizeY = sizeY; SizeZ = sizeZ; cells = new VoxelCell[sizeX, sizeY, sizeZ]; // 初始化所有体素为空 for (int x = 0; x < sizeX; x++) { for (int y = 0; y < sizeY; y++) { for (int z = 0; z < sizeZ; z++) { cells[x, y, z] = new VoxelCell { IsPassable = true, Distance = double.MaxValue, Type = LogisticsElementType.通道 }; } } } } #endregion #region 坐标转换 /// /// 世界坐标 → 体素索引 /// public (int x, int y, int z) WorldToVoxel(Point3D worldPos) { int x = (int)Math.Floor((worldPos.X - Origin.X) / VoxelSize); int y = (int)Math.Floor((worldPos.Y - Origin.Y) / VoxelSize); int z = (int)Math.Floor((worldPos.Z - Origin.Z) / VoxelSize); return (x, y, z); } /// /// 体素索引 → 世界坐标(体素中心) /// public Point3D VoxelToWorld(int x, int y, int z) { double worldX = Origin.X + (x + 0.5) * VoxelSize; double worldY = Origin.Y + (y + 0.5) * VoxelSize; double worldZ = Origin.Z + (z + 0.5) * VoxelSize; return new Point3D(worldX, worldY, worldZ); } #endregion #region 体素访问 /// /// 检查索引是否在网格范围内 /// public bool IsValidIndex(int x, int y, int z) { return x >= 0 && x < SizeX && y >= 0 && y < SizeY && z >= 0 && z < SizeZ; } /// /// 获取体素(安全访问) /// public VoxelCell GetVoxel(int x, int y, int z) { if (!IsValidIndex(x, y, z)) return null; return cells[x, y, z]; } /// /// 设置体素 /// public void SetVoxel(int x, int y, int z, VoxelCell voxel) { if (IsValidIndex(x, y, z)) cells[x, y, z] = voxel; } /// /// 检查体素是否可通行 /// public bool IsPassable(int x, int y, int z) { var voxel = GetVoxel(x, y, z); return voxel != null && voxel.IsPassable; } #endregion #region 邻居查询 /// /// 获取 26 邻域体素(6 面 + 12 边 + 8 角) /// public List<(int x, int y, int z)> GetNeighbors26(int x, int y, int z) { var neighbors = new List<(int, int, int)>(); for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { if (dx == 0 && dy == 0 && dz == 0) continue; // 跳过自己 int nx = x + dx; int ny = y + dy; int nz = z + dz; if (IsValidIndex(nx, ny, nz) && IsPassable(nx, ny, nz)) { neighbors.Add((nx, ny, nz)); } } } } return neighbors; } /// /// 获取 6 邻域体素(仅面相邻) /// public List<(int x, int y, int z)> GetNeighbors6(int x, int y, int z) { var neighbors = new List<(int, int, int)>(); var directions = new[] { (1, 0, 0), (-1, 0, 0), // X 轴 (0, 1, 0), (0, -1, 0), // Y 轴 (0, 0, 1), (0, 0, -1) // Z 轴 }; foreach (var (dx, dy, dz) in directions) { int nx = x + dx; int ny = y + dy; int nz = z + dz; if (IsValidIndex(nx, ny, nz) && IsPassable(nx, ny, nz)) { neighbors.Add((nx, ny, nz)); } } return neighbors; } #endregion #region 统计信息 /// /// 统计可通行体素数量 /// public int CountPassableVoxels() { int count = 0; for (int x = 0; x < SizeX; x++) { for (int y = 0; y < SizeY; y++) { for (int z = 0; z < SizeZ; z++) { if (cells[x, y, z].IsPassable) count++; } } } return count; } #endregion } ``` #### 3.2.3 VoxelGridGenerator - 体素网格生成器 ```csharp /// /// 从 Navisworks BIM 模型生成体素网格 /// public class VoxelGridGenerator { #region 配置参数 private readonly double voxelSize; // 体素大小(米) private readonly double vehicleRadius; // 车辆半径(米) private readonly double vehicleHeight; // 车辆高度(米) private readonly double safetyMargin; // 安全间隙(米) #endregion #region 构造函数 public VoxelGridGenerator( double voxelSizeMeters, double vehicleRadiusMeters, double vehicleHeightMeters, double safetyMarginMeters) { this.voxelSize = voxelSizeMeters; this.vehicleRadius = vehicleRadiusMeters; this.vehicleHeight = vehicleHeightMeters; this.safetyMargin = safetyMarginMeters; } #endregion #region 主生成方法 /// /// 从 BIM 模型生成体素网格 /// public VoxelGrid GenerateFromBIM( BoundingBox3D worldBounds, IEnumerable obstacles, IEnumerable channels, IEnumerable doors) { LogManager.Info("[体素网格生成] 开始生成..."); var sw = Stopwatch.StartNew(); // 1. 创建空网格 VoxelGrid grid = CreateEmptyGrid(worldBounds); LogManager.Info($"[体素网格生成] 网格尺寸: {grid.SizeX} × {grid.SizeY} × {grid.SizeZ}"); // 2. 使用 geometry4Sharp 生成距离场 MeshSignedDistanceGrid sdf = GenerateDistanceField(obstacles); LogManager.Info($"[体素网格生成] 距离场计算完成"); // 3. 标记体素类型 MarkVoxels(grid, sdf); LogManager.Info($"[体素网格生成] 体素标记完成"); // 4. 处理特殊元素(门、通道等) ProcessSpecialElements(grid, doors, channels); sw.Stop(); LogManager.Info($"[体素网格生成] 完成,耗时: {sw.ElapsedMilliseconds}ms"); LogManager.Info($"[体素网格生成] 可通行体素: {grid.CountPassableVoxels()}"); return grid; } #endregion #region 辅助方法 /// /// 创建空的体素网格 /// private VoxelGrid CreateEmptyGrid(BoundingBox3D bounds) { Point3D origin = bounds.Min; int sizeX = (int)Math.Ceiling((bounds.Max.X - bounds.Min.X) / voxelSize); int sizeY = (int)Math.Ceiling((bounds.Max.Y - bounds.Min.Y) / voxelSize); int sizeZ = (int)Math.Ceiling((bounds.Max.Z - bounds.Min.Z) / voxelSize); return new VoxelGrid(origin, voxelSize, sizeX, sizeY, sizeZ); } /// /// 使用 geometry4Sharp 生成距离场 /// private MeshSignedDistanceGrid GenerateDistanceField(IEnumerable obstacles) { // 1. 提取障碍物三角网格 DMesh3 obstacleMesh = ExtractTriangleMesh(obstacles); // 2. 计算网格包围盒 AxisAlignedBox3d meshBounds = obstacleMesh.GetBounds(); // 3. 创建距离场网格 int numCells = (int)Math.Ceiling(meshBounds.MaxDim / voxelSize); MeshSignedDistanceGrid sdf = new MeshSignedDistanceGrid( obstacleMesh, cellSize: voxelSize, numCells: numCells ); // 4. 计算距离场(可能耗时) sdf.Compute(); return sdf; } /// /// 从 Navisworks ModelItem 提取三角网格 /// private DMesh3 ExtractTriangleMesh(IEnumerable items) { DMesh3 mesh = new DMesh3(); foreach (var item in items) { // 使用 Navisworks API 获取几何体 // 转换为 geometry4Sharp 的 DMesh3 格式 // 这部分需要实现 Navisworks Geometry → DMesh3 的转换 // 伪代码示例: // var navisGeometry = item.Geometry; // foreach (var triangle in navisGeometry.Triangles) // { // var v1 = mesh.AppendVertex(triangle.V1); // var v2 = mesh.AppendVertex(triangle.V2); // var v3 = mesh.AppendVertex(triangle.V3); // mesh.AppendTriangle(v1, v2, v3); // } } return mesh; } /// /// 根据距离场标记体素 /// private void MarkVoxels(VoxelGrid grid, MeshSignedDistanceGrid sdf) { double minPassableDistance = vehicleRadius + safetyMargin; for (int x = 0; x < grid.SizeX; x++) { for (int y = 0; y < grid.SizeY; y++) { for (int z = 0; z < grid.SizeZ; z++) { // 体素中心的世界坐标 Point3D worldPos = grid.VoxelToWorld(x, y, z); // 查询距离场 Vector3d point = new Vector3d(worldPos.X, worldPos.Y, worldPos.Z); double distance = sdf[point]; // 判断可通行性 var voxel = grid.GetVoxel(x, y, z); voxel.Distance = distance; voxel.IsPassable = distance >= minPassableDistance; voxel.Type = voxel.IsPassable ? LogisticsElementType.通道 : LogisticsElementType.障碍物; } } } } /// /// 处理特殊元素(门、楼梯等) /// private void ProcessSpecialElements( VoxelGrid grid, IEnumerable doors, IEnumerable channels) { // 处理门元素 foreach (var door in doors) { BoundingBox3D doorBBox = door.BoundingBox(); MarkRegion(grid, doorBBox, LogisticsElementType.门, speedLimit: 0.5, isPassable: true); } // 处理通道元素 foreach (var channel in channels) { BoundingBox3D channelBBox = channel.BoundingBox(); MarkRegion(grid, channelBBox, LogisticsElementType.通道, speedLimit: 1.0, isPassable: true); } } /// /// 标记指定区域的体素 /// private void MarkRegion( VoxelGrid grid, BoundingBox3D bounds, LogisticsElementType type, double speedLimit, bool isPassable) { var (minX, minY, minZ) = grid.WorldToVoxel(bounds.Min); var (maxX, maxY, maxZ) = grid.WorldToVoxel(bounds.Max); for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { var voxel = grid.GetVoxel(x, y, z); if (voxel != null) { voxel.Type = type; voxel.IsPassable = isPassable; voxel.SpeedLimit = speedLimit; } } } } } #endregion } ``` #### 3.2.4 VoxelPathFinder - 体素路径规划器 ```csharp /// /// 基于体素网格的 3D 路径规划 /// public class VoxelPathFinder { private readonly VoxelGrid voxelGrid; public VoxelPathFinder(VoxelGrid voxelGrid) { this.voxelGrid = voxelGrid; } #region 路径规划 /// /// 查找 3D 路径 /// public List FindPath(Point3D start, Point3D end) { LogManager.Info($"[体素路径规划] 开始: {start} → {end}"); var sw = Stopwatch.StartNew(); // 1. 世界坐标 → 体素索引 var startVoxel = voxelGrid.WorldToVoxel(start); var endVoxel = voxelGrid.WorldToVoxel(end); // 2. 检查起终点是否可通行 if (!voxelGrid.IsPassable(startVoxel.x, startVoxel.y, startVoxel.z)) { LogManager.Error($"[体素路径规划] 起点不可通行: {startVoxel}"); return null; } if (!voxelGrid.IsPassable(endVoxel.x, endVoxel.y, endVoxel.z)) { LogManager.Error($"[体素路径规划] 终点不可通行: {endVoxel}"); return null; } // 3. 构建 3D 图(如果使用 Roy-T.AStar 的 Graph 模式) // 或者直接使用自定义 A* 实现 // 方案 A:使用 Roy-T.AStar Grid 模式(需扩展到 3D) // 方案 B:自定义 3D A* 实现 var voxelPath = FindPathAStar(startVoxel, endVoxel); if (voxelPath == null || voxelPath.Count == 0) { LogManager.Error($"[体素路径规划] 未找到路径"); return null; } // 4. 体素坐标 → 世界坐标 var worldPath = ConvertToWorldPath(voxelPath); sw.Stop(); LogManager.Info($"[体素路径规划] 完成,耗时: {sw.ElapsedMilliseconds}ms,路径点: {worldPath.Count}"); return worldPath; } #endregion #region A* 实现 /// /// 3D A* 路径搜索(简化实现) /// private List<(int x, int y, int z)> FindPathAStar( (int x, int y, int z) start, (int x, int y, int z) goal) { // 使用优先队列实现 A* var openSet = new PriorityQueue<(int, int, int), double>(); var cameFrom = new Dictionary<(int, int, int), (int, int, int)>(); var gScore = new Dictionary<(int, int, int), double>(); var fScore = new Dictionary<(int, int, int), double>(); openSet.Enqueue(start, 0); gScore[start] = 0; fScore[start] = Heuristic(start, goal); while (openSet.Count > 0) { var current = openSet.Dequeue(); // 找到目标 if (current.Equals(goal)) { return ReconstructPath(cameFrom, current); } // 遍历邻居(26 邻域) foreach (var neighbor in voxelGrid.GetNeighbors26(current.x, current.y, current.z)) { double tentativeGScore = gScore[current] + MoveCost(current, neighbor); if (!gScore.ContainsKey(neighbor) || tentativeGScore < gScore[neighbor]) { cameFrom[neighbor] = current; gScore[neighbor] = tentativeGScore; fScore[neighbor] = gScore[neighbor] + Heuristic(neighbor, goal); if (!openSet.Contains(neighbor)) { openSet.Enqueue(neighbor, fScore[neighbor]); } } } } return null; // 未找到路径 } /// /// A* 启发式函数(欧几里得距离) /// private double Heuristic((int x, int y, int z) a, (int x, int y, int z) b) { int dx = a.x - b.x; int dy = a.y - b.y; int dz = a.z - b.z; return Math.Sqrt(dx * dx + dy * dy + dz * dz) * voxelGrid.VoxelSize; } /// /// 移动成本(考虑距离、类型、速度限制) /// private double MoveCost((int x, int y, int z) from, (int x, int y, int z) to) { // 欧几里得距离 int dx = to.x - from.x; int dy = to.y - from.y; int dz = to.z - from.z; double distance = Math.Sqrt(dx * dx + dy * dy + dz * dz) * voxelGrid.VoxelSize; // 考虑目标体素的速度限制 var voxel = voxelGrid.GetVoxel(to.x, to.y, to.z); double speedFactor = 1.0; if (voxel != null && voxel.SpeedLimit > 0) { speedFactor = 1.0 / voxel.SpeedLimit; // 速度越慢,成本越高 } return distance * speedFactor; } /// /// 重建路径 /// private List<(int x, int y, int z)> ReconstructPath( Dictionary<(int, int, int), (int, int, int)> cameFrom, (int x, int y, int z) current) { var path = new List<(int, int, int)> { current }; while (cameFrom.ContainsKey(current)) { current = cameFrom[current]; path.Insert(0, current); } return path; } #endregion #region 路径转换 /// /// 体素路径 → 世界坐标路径 /// private List ConvertToWorldPath(List<(int x, int y, int z)> voxelPath) { var worldPath = new List(); foreach (var (x, y, z) in voxelPath) { Point3D worldPos = voxelGrid.VoxelToWorld(x, y, z); worldPath.Add(worldPos); } return worldPath; } #endregion } ``` --- ## 4. 实施计划 ### 4.1 阶段划分 #### 阶段 1:环境搭建与原型验证(3-5 天) **任务**: 1. **安装 geometry4Sharp** - NuGet 添加 `geometry4Sharp` 1.0.0 - 验证与 .NET Framework 4.8 兼容性 - 运行示例代码测试 2. **创建简单原型** - 从 Navisworks 提取简单场景(如单个房间) - 生成体素网格(使用 `Bitmap3` 或 `DSparseGrid3`) - 可视化体素网格(在 Navisworks 中显示为小方块) 3. **距离场测试** - 提取障碍物网格转为 `DMesh3` - 使用 `MeshSignedDistanceGrid` 计算 SDF - 验证距离场的正确性 **验收标准**: - ✅ geometry4Sharp 成功集成 - ✅ 能从 Navisworks 模型生成简单体素网格 - ✅ 能在 Navisworks 3D 视图中可视化体素 --- #### 阶段 2:核心功能开发(7-10 天) **任务**: 1. **实现 VoxelGrid 类** - 完整的体素网格数据结构 - 坐标转换方法 - 邻居查询(6 邻域、26 邻域) - 统计和查询方法 2. **实现 VoxelGridGenerator** - Navisworks Geometry → geometry4Sharp DMesh3 转换 - 距离场计算集成 - 体素标记逻辑(障碍物、通道、门等) - 特殊元素处理(门、楼梯) 3. **实现 VoxelPathFinder** - 3D A* 算法实现 - 或集成 Roy-T.AStar 的 Graph 模式 - 路径坐标转换 - 成本计算(距离 + 速度限制) 4. **集成到现有系统** - 替换 `GridMapGenerator` 为 `VoxelGridGenerator` - 替换 `AutoPathFinder` 为 `VoxelPathFinder` - 保持 `PathOptimizer` 不变(适配 3D 路径) **验收标准**: - ✅ 能从复杂 BIM 模型生成体素网格 - ✅ 能找到 3D 路径(包括垂直移动) - ✅ 路径避开障碍物并穿过门/通道 - ✅ 性能可接受(中等规模场景 < 5 秒) --- #### 阶段 3:优化与测试(5-7 天) **任务**: 1. **性能优化** - 使用 `BiGrid3` 稀疏网格减少内存 - 窄带 SDF(仅计算障碍物附近区域) - A*算法优化(JPS-3D、Theta* 等) - 多线程并行化(网格生成、路径规划) 2. **内存优化** - 大规模场景的内存占用评估 - 分块生成和加载 - 按需计算距离场 3. **测试场景** - 简单场景(单层房间) - 中等场景(多层建筑) - 复杂场景(大型工厂、管道密集) - 边界测试(超大规模、极小体素) 4. **可视化增强** - 体素网格的 3D 渲染(彩色方块) - 路径的 3D 动画展示 - 距离场的热图可视化 **验收标准**: - ✅ 大规模场景(> 100m³)可在合理时间内生成(< 30 秒) - ✅ 内存占用可控(< 2GB) - ✅ 所有测试场景路径规划成功 - ✅ 可视化效果直观 --- #### 阶段 4:文档与集成(2-3 天) **任务**: 1. **代码文档** - XML 注释完善 - 关键类和方法的使用说明 - 示例代码 2. **用户文档** - 体素网格参数配置说明 - 与 2.5D 方案的对比 - 性能调优建议 3. **配置文件更新** - 添加体素网格相关配置项 - 向后兼容 2.5D 模式(可选) 4. **单元测试** - VoxelGrid 测试 - VoxelPathFinder 测试 - 性能基准测试 **验收标准**: - ✅ 完整的代码和用户文档 - ✅ 配置文件支持体素网格参数 - ✅ 单元测试覆盖率 > 70% --- ### 4.2 时间与资源估算 | 阶段 | 任务 | 工作量 | 依赖 | |------|------|--------|------| | 阶段 1 | 环境搭建与原型 | 3-5 天 | geometry4Sharp | | 阶段 2 | 核心功能开发 | 7-10 天 | 阶段 1 | | 阶段 3 | 优化与测试 | 5-7 天 | 阶段 2 | | 阶段 4 | 文档与集成 | 2-3 天 | 阶段 3 | | **总计** | | **17-25 天** | | **资源需求**: - 1 名全职开发人员 - Navisworks 2026 开发环境 - 测试 BIM 模型(不同规模和复杂度) - 性能测试硬件(推荐 16GB+ RAM) --- ## 5. 风险与缓解 ### 5.1 技术风险 #### 风险 1:geometry4Sharp 维护状况 **描述**:geometry4Sharp 原项目活跃度中等,可能缺少最新功能或 bug 修复。 **影响**:可能遇到无法解决的 bug 或性能问题。 **缓解措施**: - ✅ geometry4Sharp 是 fork,源代码可访问,可自行修复 - ✅ 代码基于成熟的 WildMagic5/GTEngine,质量高 - ✅ Boost License 允许自由修改 - ✅ 如需要可 fork 自己维护版本 --- #### 风险 2:Navisworks Geometry 提取复杂性 **描述**:Navisworks API 的几何体提取可能复杂,转换为 DMesh3 可能困难。 **影响**:体素化可能不准确,或性能不佳。 **缓解措施**: - ✅ 可使用包围盒代替精确网格(降低精度但简化实现) - ✅ 分阶段实现:先包围盒体素化,再精确网格体素化 - ✅ 参考现有 GeometryExtractor 代码 --- #### 风险 3:内存占用 **描述**:大规模场景的体素网格可能占用大量内存(如 1000³ 体素 = 10亿个单元格)。 **影响**:内存不足导致程序崩溃。 **缓解措施**: - ✅ 使用 `DSparseGrid3` 稀疏网格(仅存储非空体素) - ✅ 窄带 SDF(仅计算障碍物附近区域) - ✅ 分块加载和生成 - ✅ 限制体素网格的最大尺寸 --- #### 风险 4:路径规划性能 **描述**:3D A* 搜索空间比 2D 大得多(N³ vs N²),可能导致性能下降。 **影响**:路径规划耗时过长(> 5 秒),用户体验差。 **缓解措施**: - ✅ 使用更高效的算法(JPS-3D、Theta*、Lazy Theta*) - ✅ 启发式函数优化(加权 A*) - ✅ 多线程并行搜索(多个起终点) - ✅ 路径缓存(重复路径查询) - ✅ 使用更大的体素(降低分辨率) --- ### 5.2 项目风险 #### 风险 5:时间超期 **描述**:实施过程中遇到意外困难,导致时间超出预期。 **影响**:延迟其他功能开发,影响项目进度。 **缓解措施**: - ✅ 分阶段实施,每个阶段有明确的验收标准 - ✅ 阶段 1 完成后评估,决定是否继续 - ✅ 保留 2.5D 方案作为备选(双模式切换) --- #### 风险 6:与现有代码冲突 **描述**:新体素系统与现有代码不兼容,需要大量重构。 **影响**:工作量增加,可能引入新 bug。 **缓解措施**: - ✅ 新旧系统并存(配置开关) - ✅ 保持接口一致性(PathFinder、PathOptimizer) - ✅ 充分测试确保向后兼容 --- ## 6. 配置文件扩展 ### 6.1 新增配置项 在 `config.toml` 中添加体素网格相关配置: ```toml [path_planning] # 路径规划模式:grid_2d5(当前 2.5D 网格)、voxel_3d(新体素网格) mode = "grid_2d5" # 默认保持向后兼容 [voxel_grid] # 体素大小(米),越小越精确但内存和计算量越大 voxel_size_meters = 0.5 # 是否使用稀疏网格(推荐大规模场景) use_sparse_grid = true # 是否使用窄带距离场(仅计算障碍物附近区域) use_narrow_band = true narrow_band_width_meters = 2.0 # 窄带宽度(米) # 邻域类型:6(面相邻)、26(面+边+角相邻) neighbor_type = 26 # 是否可视化体素网格 visualize_voxels = false # 是否可视化距离场(热图) visualize_distance_field = false [path_planning_3d] # 3D A* 算法类型:astar、jps3d、theta_star algorithm_type = "astar" # 启发式权重(> 1.0 加速但可能不是最优路径) heuristic_weight = 1.0 # 是否允许对角线移动 allow_diagonal = true # 是否允许垂直移动 allow_vertical = true # 垂直移动的额外成本系数(> 1.0 表示更倾向于水平移动) vertical_cost_factor = 1.5 ``` --- ## 7. 性能预期 ### 7.1 性能对比(预估) 基于类似规模的 3D 体素路径规划系统经验: | 指标 | 2.5D 网格(当前) | 3D 体素网格(预期) | 说明 | |------|-------------------|---------------------|------| | **网格生成** | 1-3 秒 | 3-10 秒 | 体素化和 SDF 计算耗时 | | **路径规划** | 50-200 ms | 100-500 ms | 3D 搜索空间更大 | | **内存占用** | 50-200 MB | 100-500 MB | 体素数据 + SDF | | **路径精度** | 中等 | 高 | 真 3D 路径更精确 | | **复杂场景支持** | 有限 | 优秀 | 管道、桥梁等复杂结构 | **场景假设**: - 建筑规模:100m × 100m × 20m - 体素大小:0.5m - 体素数量:200 × 200 × 40 = 160 万个 - 稀疏网格实际占用:20-30% = 32-48 万个 ### 7.2 优化目标 **短期目标**(阶段 2 完成后): - ✅ 网格生成 < 15 秒(中等规模场景) - ✅ 路径规划 < 1 秒(单次查询) - ✅ 内存占用 < 500 MB **长期目标**(阶段 3 优化后): - ✅ 网格生成 < 10 秒 - ✅ 路径规划 < 500 ms - ✅ 内存占用 < 300 MB --- ## 8. 替代方案评估 ### 8.1 方案对比 | 方案 | 优点 | 缺点 | 推荐度 | |------|------|------|--------| | **geometry4Sharp** | 功能完整、代码质量高、商业友好 | 活跃度中等、文档一般 | ⭐⭐⭐⭐⭐ | | **自行实现** | 完全控制、轻量级 | 工作量大、需实现 SDF | ⭐⭐⭐ | | **Unity 相关库** | 活跃、文档好 | Unity 依赖、难以剥离 | ⭐⭐ | | **保持 2.5D** | 无开发成本、稳定 | 功能受限、无法真 3D | ⭐⭐ | ### 8.2 决策建议 **推荐方案**:**geometry4Sharp** **理由**: 1. ✅ 功能完整,提供完整的体素化和距离场工具链 2. ✅ 代码质量高,源自经典几何库 WildMagic5/GTEngine 3. ✅ 商业友好的 Boost License 4. ✅ 纯 C#,与 Navisworks 完美兼容 5. ✅ 即使维护减少,代码仍可用且可自行修改 **备选方案**:如果 geometry4Sharp 不满足需求,可考虑: - 轻量级自行实现(基于简单的 3D 数组 + 基础距离计算) - Fork geometry4Sharp 自行维护 --- ## 9. 总结与建议 ### 9.1 核心结论 1. **体素网格是 3D 路径规划的理想方案** - 统一的 3D 空间表示 - 简化的路径算法 - 更精确的障碍物表示 2. **geometry4Sharp 是最佳的 C# 体素库** - 功能完整、代码质量高 - 商业友好、平台兼容 - 可满足项目需求 3. **实施是可行的** - 清晰的技术路线 - 合理的时间估算(17-25 天) - 风险可控 ### 9.2 建议行动 **立即行动**: 1. ✅ 安装 geometry4Sharp NuGet 包 2. ✅ 创建原型验证可行性(阶段 1) 3. ✅ 评估性能和内存占用 **后续行动**: - 根据原型结果决定是否全面实施 - 如果可行,按阶段 2-4 计划推进 - 保持 2.5D 方案作为备选(双模式) ### 9.3 成功标准 项目成功的标志: - ✅ 能处理真正的 3D 路径(管道下方、桥梁下方、多层重叠) - ✅ 路径精度优于 2.5D 方案 - ✅ 性能可接受(网格生成 < 15 秒,路径规划 < 1 秒) - ✅ 内存占用可控(< 500 MB) - ✅ 代码质量高、可维护性好 --- ## 10. 参考资料 ### 10.1 geometry4Sharp 资源 - **GitHub**: - **NuGet**: - **原版文档**: (geometry3Sharp) - **原作者**: Ryan Schmidt (@rms80) - **Fork 维护者**: New Wheel Technology ### 10.2 理论基础 - **Signed Distance Field (SDF)**: - SDFGen (Christopher Batty, Robert Bridson) - "Signed Distance Field Tutorial" - gradientspace.com - **Marching Cubes**: - "Marching Cubes: A High Resolution 3D Surface Construction Algorithm" (Lorensen & Cline, 1987) - **3D Pathfinding**: - "3D Flight Navigation Using Sparse Voxel Octrees" (Daniel Brewer, Game AI Pro 3) - "Pathfinding in 3D Space: A*, Theta*, Lazy Theta*" (ASCANE) ### 10.3 相关项目 - **WildMagic5**: (David Eberly) - **Roy-T.AStar**: - **Unity PathFinding3D**: - **Nav3D (UE5)**: --- **文档版本**: v1.0 **创建日期**: 2025-10-12 **最后更新**: 2025-10-12 **作者**: NavisworksTransport 开发团队