using System; using System.Threading; using MultiWheelC.TrajectoryPlanning.Utils; namespace MultiWheelC.TrajectoryPlanning.Mapping; /// 以 m 表示的障碍物净距离保守下界。 internal sealed class ObstacleDistanceField { private readonly double[] _conservativeDistances; private ObstacleDistanceField(double[] conservativeDistances) { _conservativeDistances = conservativeDistances; } /// /// 从占据栅格创建距离场。 /// /// 参数:occupied 为行主序占据数组;rows、cols 为其尺寸;resolutionMeters 为格边长,单位 m。 /// 返回:每个格到最近障碍物的保守净距离下界,单位 m;全空地图中的每项为正无穷。 /// public static ObstacleDistanceField Create(byte[] occupied, int rows, int cols, double resolutionMeters) { if (!TryCreate(occupied, rows, cols, resolutionMeters, PlanningOperationBudget.Unlimited(CancellationToken.None), out ObstacleDistanceField field, out _)) throw new InvalidOperationException("Unbounded distance-field creation unexpectedly stopped."); return field; } /// 使用共享预算创建距离场;停止时不返回部分距离数据。 internal static bool TryCreate(byte[] occupied, int rows, int cols, double resolutionMeters, PlanningOperationBudget budget, out ObstacleDistanceField field, out PlanningOperationStopReason stopReason) { if (occupied == null) throw new ArgumentNullException(nameof(occupied)); if (budget == null) throw new ArgumentNullException(nameof(budget)); field = null; stopReason = budget.GetStopReason(); if (stopReason != PlanningOperationStopReason.None) return false; bool hasObstacle = false; int workItemCount = 0; for (int i = 0; i < occupied.Length; i++) { stopReason = budget.CheckEvery(ref workItemCount); if (stopReason != PlanningOperationStopReason.None) return false; if (occupied[i] != 0) { hasObstacle = true; break; } } var distances = new double[occupied.Length]; if (!hasObstacle) { for (int i = 0; i < distances.Length; i++) { stopReason = budget.CheckEvery(ref workItemCount); if (stopReason != PlanningOperationStopReason.None) return false; distances[i] = double.PositiveInfinity; } field = new ObstacleDistanceField(distances); stopReason = PlanningOperationStopReason.None; return true; } if (!EuclideanDistanceTransform.TryComputeSquaredDistances(occupied, rows, cols, budget, out double[] squared, out stopReason)) return false; double conservativeOffset = Math.Sqrt(2d) * resolutionMeters; for (int i = 0; i < distances.Length; i++) { stopReason = budget.CheckEvery(ref workItemCount); if (stopReason != PlanningOperationStopReason.None) return false; distances[i] = Math.Max(0d, Math.Sqrt(squared[i]) * resolutionMeters - conservativeOffset); } field = new ObstacleDistanceField(distances); stopReason = PlanningOperationStopReason.None; return true; } internal double[] CopyDistances() { return (double[])_conservativeDistances.Clone(); } }