780. Reaching Points

本文介绍了一种判断从坐标(sx, sy)是否能通过特定运算到达坐标(tx, ty)的算法。该算法通过循环迭代减小目标坐标值,直至满足特定条件,实现了高效判断。适用于竞赛编程和算法优化场景。

idea:
1.从后向前找
2.while (tx > ty) tx -= ty; 替为 % 操作
3.经过循环后,必定只有两种情况才true

  1. sx == tx && sy <= ty && (ty - sy) % sx == 0
  2. sy == ty && sx <= tx && (tx - sx) % sy == 0
public boolean reachingPoints(int sx, int sy, int tx, int ty) {
        while (tx > sx && ty > sy){
            if (tx > ty)    tx %= ty;
            else    ty %= tx;
        }
        return sx == tx && sy <= ty && (ty - sy) % sx == 0 ||
            sy == ty && sx <= tx && (tx - sx) % sy == 0;
            
    }

转载于:https://www.cnblogs.com/whyaza/p/10723267.html

using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.AI; using TMPro; using static ControlTheSceneXF; public class PathManager : MonoBehaviour { [Header("寻路设置")] public GameObject mainArrowPrefab; // 主箭头预制体 public GameObject secondaryArrowPrefab; // 辅助箭头预制体 public float moveSpeed = 3.5f; // 箭头移动速度 public float arrivalThreshold = 1f; // 到达点的阈值距离 public float pathHeight = 0.5f; // 路径离地面的高度 private bool isUpdate = false; [Header("路径查找设置")] public float secondaryPathRange = 2.0f; // 辅助路径查找范围 public int maxJunctionsPerSegment = 3; // 每段路径最多添加的路口点数量 [Header("寻路标签设置")] public string pathPointTag = "PathPoint"; // 路径点标签 public string junctionPointTag = "JunctionPoint"; // 路口点标签 public LayerMask pathPointLayer; // 路径点Layer public bool useTagInsteadOfLayer = true; // 使用标签而不是Layer [Header("路径可视化")] public bool showPathsInGame = true; // 是否显示路径 public Material mainPathMaterial; // 主路径材质 public Material secondaryPathMaterial; // 辅助路径材质 public float pathWidth = 0.2f; // 路径宽度 [Header("显示调试Gizmos")] public bool showDebugGizmos = true; // 显示调试Gizmos public bool logDebugInfo = true; // 打印调试信息 public float navMeshSampleDistance = 5f; // 导航网格采样距离 // 路径数据 private List<Transform> pathPoints = new List<Transform>(); private List<ArrowController> activeArrows = new List<ArrowController>(); private Dictionary<ArrowController, LineRenderer> arrowPathRenderers = new Dictionary<ArrowController, LineRenderer>(); private Dictionary<Vector3, List<Vector3>> junctionConnections = new Dictionary<Vector3, List<Vector3>>(); // 主路径和辅助路径 private List<Vector3> mainPath = new List<Vector3>(); private Dictionary<Vector3, List<List<Vector3>>> secondaryPaths = new Dictionary<Vector3, List<List<Vector3>>>(); private ArrowController mainArrow; void Update() { // 更新所有箭头 foreach (var arrow in activeArrows.ToArray()) { if (arrow != null) { arrow.UpdateMovement(); // 更新路径渲染 if (showPathsInGame && arrowPathRenderers.ContainsKey(arrow)) { UpdatePathRenderer(arrow, arrowPathRenderers[arrow]); } // 检查主箭头是否到达路径点 if (arrow == mainArrow && mainPath.Count > 0) { int currentIndex = arrow.CurrentPathIndex; if (currentIndex >= 0 && currentIndex < mainPath.Count) { Vector3 pathPoint = mainPath[currentIndex]; float distance = Vector3.Distance(arrow.transform.position, pathPoint); // 当接近路径点时检查辅助路径 if (distance < arrivalThreshold * 1.5f) { // 检查并激活辅助路径 CheckAndActivateSecondaryPaths(pathPoint); } } } } else { if (arrowPathRenderers.ContainsKey(arrow)) { Destroy(arrowPathRenderers[arrow].gameObject); arrowPathRenderers.Remove(arrow); } activeArrows.Remove(arrow); } } } /// <summary> /// 更新路径渲染器,逐步绘制路径 /// </summary> void UpdatePathRenderer(ArrowController arrow, LineRenderer lineRenderer) { if (arrow == null || lineRenderer == null) return; // 获取箭头已经经过的路径点 int pointsToShow = Mathf.Min(arrow.CurrentPathIndex + 1, arrow.PathPoints.Count); if (pointsToShow < 2) return; // 创建临时列表存储要显示的点 List<Vector3> points = new List<Vector3>(); points.AddRange(arrow.PathPoints.GetRange(0, pointsToShow)); // 添加当前箭头位置作为最后一个点 points.Add(arrow.transform.position); // 更新LineRenderer lineRenderer.positionCount = points.Count; lineRenderer.SetPositions(points.ToArray()); } /// <summary> /// 收集所有路径点并按时间排序 /// </summary> void CollectPathPoints() { pathPoints.Clear(); List<Transform> allPathPoints = FindAllPathPoints(); if (allPathPoints.Count == 0) { Debug.LogError("没有找到路径点!"); enabled = false; return; } // 按时间排序路径点 pathPoints = allPathPoints .Where(p => p.GetComponent<GjImagePrefab>() != null) .OrderBy(p => p.GetComponent<GjImagePrefab>().GetTimeInMinutes()) .ToList(); } /// <summary> /// 查找场景中所有路径点 /// </summary> List<Transform> FindAllPathPoints() { List<Transform> points = new List<Transform>(); if (useTagInsteadOfLayer) { // 通过标签查找路径点 GameObject[] taggedObjects = GameObject.FindGameObjectsWithTag(pathPointTag); foreach (GameObject obj in taggedObjects) { points.Add(obj.transform); ValidatePointPosition(obj.transform); } // 通过标签查找路口点并构建连接图 GameObject[] junctionObjects = GameObject.FindGameObjectsWithTag(junctionPointTag); BuildJunctionConnectionGraph(junctionObjects); } else { // 通过Layer查找(略) } return points; } /// <summary> /// 构建路口连接图 /// </summary> void BuildJunctionConnectionGraph(GameObject[] junctions) { junctionConnections.Clear(); // 初始化连接图 foreach (var junction in junctions) { Vector3 pos = junction.transform.position; if (!junctionConnections.ContainsKey(pos)) { junctionConnections.Add(pos, new List<Vector3>()); } } // 添加连接 foreach (var junction in junctions) { JunctionPoint junctionComp = junction.GetComponent<JunctionPoint>(); if (junctionComp != null && junctionComp.connectedPoints != null) { Vector3 startPos = junction.transform.position; foreach (var connectedPoint in junctionComp.connectedPoints) { if (connectedPoint != null) { Vector3 endPos = connectedPoint.position; if (!junctionConnections[startPos].Contains(endPos)) { junctionConnections[startPos].Add(endPos); } // 确保双向连接 if (junctionConnections.ContainsKey(endPos) && !junctionConnections[endPos].Contains(startPos)) { junctionConnections[endPos].Add(startPos); } } } } } } /// <summary> /// 验证路径点是否在导航网格上 /// </summary> void ValidatePointPosition(Transform point) { NavMeshHit hit; if (!NavMesh.SamplePosition(point.position, out hit, navMeshSampleDistance, NavMesh.AllAreas)) { Debug.LogWarning($"路径点 '{point.name}' 不在导航网格上! 位置: {point.position}"); if (NavMesh.FindClosestEdge(point.position, out hit, NavMesh.AllAreas)) { point.position = hit.position; Debug.Log($"调整 '{point.name}' 到最近的导航网格点: {hit.position}"); } } } /// <summary> /// 计算主路径 /// </summary> public void CalculateMainPath() { mainPath.Clear(); secondaryPaths.Clear(); if (pathPoints.Count < 2) { Debug.LogWarning("路径点数不足"); return; } // 1. 计算基础路径 - 直接连接所有PathPoint的最短路径 mainPath = CalculatePathThroughAllPoints(pathPoints); if (mainPath == null || mainPath.Count < 2) { Debug.LogError("主路径计算失败!"); return; } // 2. 在关键路口点预计算辅助路径 PrecalculateSecondaryPaths(); } /// <summary> /// 预计算所有可能的分支路径(核心修改) /// </summary> void PrecalculateSecondaryPaths() { secondaryPaths.Clear(); // 遍历主路径上的每一段(从当前点到下一个点) for (int i = 0; i < mainPath.Count - 1; i++) { Vector3 currentPoint = mainPath[i]; Vector3 nextPoint = mainPath[i + 1]; // 计算当前段的直径范围 Vector3 segmentCenter = (currentPoint + nextPoint) / 2f; float segmentDiameter = Vector3.Distance(currentPoint, nextPoint); // 查找范围内的所有路口点 List<Vector3> nearbyJunctions = FindNearbyJunctions(segmentCenter, segmentDiameter * secondaryPathRange); if (logDebugInfo) Debug.Log($"主路径段 {i}: {currentPoint} -> {nextPoint}, 找到{nearbyJunctions.Count}个附近路口点"); List<List<Vector3>> alternatives = new List<List<Vector3>>(); // 尝试为每个路口点创建替代路径 foreach (var junction in nearbyJunctions) { // 跳过主路径点本身 if (junction == currentPoint || junction == nextPoint) continue; // 计算替代路径:当前点 -> 路口点 -> 下一个点 List<Vector3> altPath = CalculateAlternativePath(currentPoint, junction, nextPoint); if (altPath != null && altPath.Count > 1) { alternatives.Add(altPath); if (logDebugInfo) Debug.Log($"创建替代路径: {currentPoint} -> {junction} -> {nextPoint}, 点数: {altPath.Count}"); // 限制每段路径的最大分支数 if (alternatives.Count >= maxJunctionsPerSegment) break; } } if (alternatives.Count > 0) { // 使用当前点作为键存储替代路径 secondaryPaths[currentPoint] = alternatives; if (logDebugInfo) Debug.Log($"为点 {currentPoint} 添加 {alternatives.Count} 条替代路径"); } } } /// <summary> /// 查找范围内的路口点 /// </summary> List<Vector3> FindNearbyJunctions(Vector3 center, float radius) { List<Vector3> nearby = new List<Vector3>(); foreach (var junction in junctionConnections.Keys) { if (Vector3.Distance(junction, center) <= radius) { nearby.Add(junction); } } return nearby; } /// <summary> /// 计算替代路径(当前点 -> 路口点 -> 目标点) /// </summary> List<Vector3> CalculateAlternativePath(Vector3 start, Vector3 junction, Vector3 end) { // 计算三段路径 List<Vector3> pathToJunction = CalculatePath(start, junction); List<Vector3> pathFromJunctionToEnd = CalculatePath(junction, end); if (pathToJunction == null || pathToJunction.Count == 0 || pathFromJunctionToEnd == null || pathFromJunctionToEnd.Count == 0) { return null; } // 合并路径 List<Vector3> fullPath = new List<Vector3>(); fullPath.AddRange(pathToJunction); fullPath.AddRange(pathFromJunctionToEnd.Skip(1)); // 跳过重复点 return fullPath; } /// <summary> /// 检查并激活辅助路径 /// </summary> void CheckAndActivateSecondaryPaths(Vector3 junctionPoint) { if (!secondaryPaths.ContainsKey(junctionPoint)) { if (logDebugInfo) Debug.Log($"点 {junctionPoint} 没有预计算的辅助路径"); return; } // 获取该点的所有辅助路径 List<List<Vector3>> paths = secondaryPaths[junctionPoint]; if (logDebugInfo) Debug.Log($"在点 {junctionPoint} 找到 {paths.Count} 条辅助路径"); foreach (var path in paths) { if (path == null || path.Count < 2) { if (logDebugInfo) Debug.LogWarning("跳过无效的辅助路径"); continue; } // 检查是否已存在相同路径的箭头 bool pathExists = false; foreach (var arrow1 in activeArrows) { if (arrow1 == null || arrow1.IsMainArrow) continue; // 简单比较路径起点和终点 if (Vector3.Distance(arrow1.StartPosition, path[0]) < 0.1f && Vector3.Distance(arrow1.EndPosition, path[path.Count - 1]) < 0.1f) { pathExists = true; break; } } if (pathExists) { if (logDebugInfo) Debug.Log($"辅助路径已存在: {path[0]} -> {path[path.Count - 1]}"); continue; } // 创建辅助箭头 GameObject arrowObj = Instantiate(secondaryArrowPrefab, path[0], Quaternion.identity); ArrowController arrow = arrowObj.GetComponent<ArrowController>(); if (arrow == null) { arrow = arrowObj.AddComponent<ArrowController>(); } arrow.Initialize(this, path, false); // 设置箭头材质 var renderer = arrowObj.GetComponent<MeshRenderer>(); if (renderer != null && secondaryPathMaterial != null) { renderer.material = secondaryPathMaterial; } activeArrows.Add(arrow); // 创建辅助路径渲染器 if (showPathsInGame) { CreatePathRenderer(arrow, path, secondaryPathMaterial); } if (logDebugInfo) Debug.Log($"创建辅助路径箭头: {path[0]} -> {path[path.Count - 1]}, 点数: {path.Count}"); } } /// <summary> /// 计算经过所有路径点的完整路径 /// </summary> List<Vector3> CalculatePathThroughAllPoints(List<Transform> points) { if (points.Count < 2) return null; List<Vector3> fullPath = new List<Vector3>(); for (int i = 0; i < points.Count - 1; i++) { Vector3 start = points[i].position; Vector3 end = points[i + 1].position; // 计算两点之间的路径 List<Vector3> segment = CalculatePath(start, end); if (segment == null || segment.Count == 0) { Debug.LogWarning($"路径段 {i} 计算失败,在 {points[i].name} 和 {points[i + 1].name} 之间"); return null; } // 如果是第一个段,添加全部点 if (i == 0) { fullPath.AddRange(segment); } else { // 移除重复点(上一段的终点) if (fullPath.Count > 0) fullPath.RemoveAt(fullPath.Count - 1); fullPath.AddRange(segment); } } return fullPath; } /// <summary> /// 计算两点之间的导航路径 /// </summary> List<Vector3> CalculatePath(Vector3 startPos, Vector3 endPos) { NavMeshHit hitStart, hitEnd; if (!NavMesh.SamplePosition(startPos, out hitStart, navMeshSampleDistance, NavMesh.AllAreas) || !NavMesh.SamplePosition(endPos, out hitEnd, navMeshSampleDistance, NavMesh.AllAreas)) { Debug.LogError($"点不在导航网格上!起点: {startPos}, 终点: {endPos}"); return null; } NavMeshPath navPath = new NavMeshPath(); if (!NavMesh.CalculatePath(hitStart.position, hitEnd.position, NavMesh.AllAreas, navPath)) { Debug.LogError($"路径计算失败!起点: {startPos}, 终点: {endPos}"); return null; } // 调整路径高度 List<Vector3> adjustedPath = new List<Vector3>(); foreach (Vector3 point in navPath.corners) { adjustedPath.Add(new Vector3(point.x, point.y + pathHeight, point.z)); } return adjustedPath; } /// <summary> /// 创建主箭头 /// </summary> void CreateMainArrow() { if (mainArrowPrefab == null) { Debug.LogError("未设置主箭头预制体!"); return; } // 清除现有箭头 foreach (var arrow in activeArrows) if (arrow != null) Destroy(arrow.gameObject); activeArrows.Clear(); foreach (var renderer1 in arrowPathRenderers.Values) if (renderer1 != null) Destroy(renderer1.gameObject); arrowPathRenderers.Clear(); if (mainPath == null || mainPath.Count == 0) return; // 创建主箭头 GameObject arrowObj = Instantiate(mainArrowPrefab, mainPath[0], Quaternion.identity); mainArrow = arrowObj.GetComponent<ArrowController>(); if (mainArrow == null) { mainArrow = arrowObj.AddComponent<ArrowController>(); } mainArrow.Initialize(this, mainPath, true); // 设置箭头材质 var renderer = arrowObj.GetComponent<MeshRenderer>(); if (renderer != null && mainPathMaterial != null) { renderer.material = mainPathMaterial; } activeArrows.Add(mainArrow); // 创建主路径渲染器 if (showPathsInGame) { CreatePathRenderer(mainArrow, mainPath, mainPathMaterial); } } /// <summary> /// 创建路径渲染器 /// </summary> void CreatePathRenderer(ArrowController arrow, List<Vector3> path, Material material) { if (path == null || path.Count < 2) return; GameObject pathObj = new GameObject("PathRenderer"); pathObj.transform.SetParent(transform); LineRenderer lineRenderer = pathObj.AddComponent<LineRenderer>(); lineRenderer.material = material; lineRenderer.startWidth = pathWidth; lineRenderer.endWidth = pathWidth; lineRenderer.useWorldSpace = true; // 初始只设置第一个点 lineRenderer.positionCount = 1; lineRenderer.SetPosition(0, path[0]); arrowPathRenderers[arrow] = lineRenderer; } /// <summary> /// 手动重新计算路径 /// </summary> public void RecalculatePaths() { isUpdate = true; ClearAllPaths(); CollectPathPoints(); CalculateMainPath(); CreateMainArrow(); } /// <summary> /// 清除所有路径和箭头 /// </summary> public void ClearAllPaths() { isUpdate = false; foreach (var arrow in activeArrows) if (arrow != null) Destroy(arrow.gameObject); activeArrows.Clear(); pathPoints.Clear(); junctionConnections.Clear(); mainPath.Clear(); secondaryPaths.Clear(); } void OnDrawGizmos() { if (!showDebugGizmos) return; // 绘制路径点 Gizmos.color = Color.cyan; foreach (Transform point in pathPoints) { Gizmos.DrawSphere(point.position, 0.5f); Gizmos.DrawWireSphere(point.position, navMeshSampleDistance); } // 绘制路口点 Gizmos.color = Color.magenta; foreach (var junction in junctionConnections.Keys) { Gizmos.DrawSphere(junction, 0.3f); // 绘制路口连接 if (junctionConnections.ContainsKey(junction)) { foreach (var connectedPoint in junctionConnections[junction]) { Gizmos.DrawLine(junction, connectedPoint); } } } // 绘制主路径 if (mainPath.Count > 1) { Gizmos.color = Color.green; for (int i = 1; i < mainPath.Count; i++) { Gizmos.DrawLine(mainPath[i - 1], mainPath[i]); } } // 绘制辅助路径 Gizmos.color = Color.yellow; foreach (var kvp in secondaryPaths) { foreach (var path in kvp.Value) { for (int i = 1; i < path.Count; i++) { Gizmos.DrawLine(path[i - 1], path[i]); } } } } } using System.Collections.Generic; using TMPro; using UnityEngine; public class ArrowController : MonoBehaviour { private List<Vector3> currentPath; private int currentCornerIndex = 0; private float moveSpeed; private float arrivalThreshold; private bool isMainArrow; private PathManager pathManager; private List<Vector3> pathPoints; // Public properties for path checking public bool IsMainArrow => isMainArrow; public Vector3 StartPosition => pathPoints != null && pathPoints.Count > 0 ? pathPoints[0] : Vector3.zero; public Vector3 EndPosition => pathPoints != null && pathPoints.Count > 0 ? pathPoints[pathPoints.Count - 1] : Vector3.zero; public List<Vector3> PathPoints => pathPoints; public int CurrentPathIndex => currentCornerIndex; public void Initialize(PathManager manager, List<Vector3> path, bool isMain) { pathPoints = new List<Vector3>(path); // Store a copy of the path points pathManager = manager; moveSpeed = manager.moveSpeed; arrivalThreshold = manager.arrivalThreshold; isMainArrow = isMain; SetPath(path); } public void SetPath(List<Vector3> path) { currentPath = path; currentCornerIndex = 0; if (currentPath != null && currentPath.Count > 0) transform.position = currentPath[0]; } public void UpdateMovement() { if (currentPath == null || currentPath.Count == 0) return; // Reached end of path if (currentCornerIndex >= currentPath.Count) { // Secondary arrows disappear when reaching the end if (!isMainArrow) { Destroy(gameObject); } else { // Main arrow loops currentCornerIndex = 0; transform.position = currentPath[0]; } return; } Vector3 targetPoint = currentPath[currentCornerIndex]; transform.position = Vector3.MoveTowards(transform.position, targetPoint, moveSpeed * Time.deltaTime); // Update arrow direction if (transform.position != targetPoint) { Vector3 direction = -(targetPoint - transform.position).normalized; if (direction != Vector3.zero) { transform.rotation = Quaternion.LookRotation(direction); } } // Check if reached current waypoint if (Vector3.Distance(transform.position, targetPoint) <= arrivalThreshold) currentCornerIndex++; } } 这两个脚本你给我修改一下,首先是主路径一定要结合navigation,先看从起点到终点的最短路线(这个不需要划线),然后把这条路线左右一定范围内的路口点和途径点用线条连接起来作为主路径线条(划线要)不论是主路径线条还是辅助路径线条都要结合navigation;另外当路径开始规划的时候,辅助路线不能走除了主路径的当前点和下一个点,辅助路径也不能走主路径已经走过的路口点的一定范围,也就是说,除了去到主路径的下一个路口点的周围外,主路径线条的左右一定范围内是没有辅助路径的,你给我修改一下,完整的脚本代码和中文注释给我
最新发布
06-07
lass RRTStar3D: def __init__(self, start, goal, builds, bounds, max_iter=RRT_MAX_ITER, step_size=RRT_STEP, neighbor_radius=RRT_NEIGHBOR_RADIUS): self.start = np.array(start) self.goal = np.array(goal) # Pre-calculate builds with safety height buffer self.builds_with_safety = builds.copy() self.builds_with_safety[:, 4] -= SAFE_HEIGHT # Decrease zmin self.builds_with_safety[:, 5] += SAFE_HEIGHT # Increase zmax # Ensure zmin is not negative if SAFE_HEIGHT is large self.builds_with_safety[:, 4] = np.maximum(0, self.builds_with_safety[:, 4]) self.bounds = np.array(bounds) # Ensure bounds is numpy array self.max_iter = max_iter self.step_size = step_size self.neighbor_radius = neighbor_radius self.nodes = [self.start] self.parent = {tuple(self.start): None} self.cost = {tuple(self.start): 0.0} # Initialize KDTree with the start node self.kdtree = cKDTree(np.array([self.start])) # Ensure it's a 2D array def sample(self): # Biased sampling towards goal occasionally if np.random.rand() < 0.1: # 10% chance to sample goal return self.goal # Sample within bounds return np.random.uniform(self.bounds[:, 0], self.bounds[:, 1]) def nearest(self, q): _, idx = self.kdtree.query(q) # Handle case where KDTree might have only one node initially if isinstance(idx, (int, np.integer)): return self.nodes[idx] else: # Should not happen if tree has >= 1 node, but safety check return self.nodes[0] def steer(self, q_near, q_rand): delta = q_rand - q_near dist = np.linalg.norm(delta) if dist == 0: # Avoid division by zero return q_near ratio = self.step_size / dist if ratio >= 1.0: return q_rand return q_near + delta * ratio def near_neighbors(self, q_new): # Ensure nodes list is not empty before querying if not self.nodes: return [] # Ensure kdtree has points before querying if self.kdtree.n == 0: return [] # Use query_ball_point which is efficient for radius searches indices = self.kdtree.query_ball_point(q_new, self.neighbor_radius) # Filter out the index of q_new itself if it's already in nodes (might happen during rewiring) q_new_tuple = tuple(q_new) neighbors = [] for i in indices: # Check bounds and ensure it's not the node itself if already added # This check might be redundant if q_new isn't added before calling this if i < len(self.nodes): # Ensure index is valid node = self.nodes[i] if tuple(node) != q_new_tuple: neighbors.append(node) return neighbors def plan(self): for i in range(self.max_iter): q_rand = self.sample() # Check if nodes list is empty (shouldn't happen after init) if not self.nodes: print("Warning: Node list empty during planning.") continue # Or handle appropriately q_near = self.nearest(q_rand) q_new = self.steer(q_near, q_rand) # Check collision for the new segment using the pre-calculated safe builds if check_segment_collision(q_near, q_new, self.builds_with_safety) > 0: continue # If collision-free, add the node and update KD-Tree periodically q_new_tuple = tuple(q_new) q_near_tuple = tuple(q_near) # Choose parent with minimum cost among neighbors min_cost = self.cost[q_near_tuple] + np.linalg.norm(q_new - q_near) best_parent_node = q_near neighbors = self.near_neighbors(q_new) # Find neighbors first for q_neighbor in neighbors: q_neighbor_tuple = tuple(q_neighbor) # Check connectivity collision if check_segment_collision(q_neighbor, q_new, self.builds_with_safety) == 0: new_cost = self.cost[q_neighbor_tuple] + np.linalg.norm(q_new - q_neighbor) if new_cost < min_cost: min_cost = new_cost best_parent_node = q_neighbor # Add the new node with the best parent found self.nodes.append(q_new) q_best_parent_tuple = tuple(best_parent_node) self.parent[q_new_tuple] = q_best_parent_tuple self.cost[q_new_tuple] = min_cost # Rebuild KDTree periodically if len(self.nodes) % KD_REBUILD_EVERY == 0 or i == self.max_iter - 1: # Important: Ensure nodes is a list of arrays before creating KDTree if self.nodes: # Check if nodes is not empty self.kdtree = cKDTree(np.array(self.nodes)) # Rewire neighbors to go through q_new if it provides a shorter path for q_neighbor in neighbors: q_neighbor_tuple = tuple(q_neighbor) # Check if rewiring through q_new is shorter and collision-free cost_via_new = min_cost + np.linalg.norm(q_neighbor - q_new) if cost_via_new < self.cost[q_neighbor_tuple]: if check_segment_collision(q_new, q_neighbor, self.builds_with_safety) == 0: self.parent[q_neighbor_tuple] = q_new_tuple self.cost[q_neighbor_tuple] = cost_via_new # Check if goal is reached if np.linalg.norm(q_new - self.goal) < self.step_size: # Check final segment collision if check_segment_collision(q_new, self.goal, self.builds_with_safety) == 0: goal_tuple = tuple(self.goal) self.nodes.append(self.goal) # Add goal node self.parent[goal_tuple] = q_new_tuple self.cost[goal_tuple] = min_cost + np.linalg.norm(self.goal - q_new) print(f"RRT*: Goal reached at iteration {i+1}") # Rebuild KDTree one last time if goal is reached self.kdtree = cKDTree(np.array(self.nodes)) break # Exit planning loop else: # Loop finished without reaching goal condition print(f"RRT*: Max iterations ({self.max_iter}) reached. Connecting nearest node to goal.") # Find node closest to goal among existing nodes if not self.nodes: print("Error: No nodes generated by RRT*.") return None # Or raise error nodes_arr = np.array(self.nodes) distances_to_goal = np.linalg.norm(nodes_arr - self.goal, axis=1) nearest_node_idx = np.argmin(distances_to_goal) q_final_near = self.nodes[nearest_node_idx] q_final_near_tuple = tuple(q_final_near) goal_tuple = tuple(self.goal) # Try connecting nearest found node to goal if check_segment_collision(q_final_near, self.goal, self.builds_with_safety) == 0: self.nodes.append(self.goal) self.parent[goal_tuple] = q_final_near_tuple self.cost[goal_tuple] = self.cost[q_final_near_tuple] + np.linalg.norm(self.goal - q_final_near) print("RRT*: Connected nearest node to goal.") else: print("RRT*: Could not connect nearest node to goal collision-free. Returning path to nearest node.") # Path will be constructed to q_final_near instead of goal goal_tuple = q_final_near_tuple # Target for path reconstruction # Backtrack path from goal (or nearest reachable node) path = [] # Start backtracking from the actual last node added (goal or nearest) curr_tuple = goal_tuple if curr_tuple not in self.parent and curr_tuple != tuple(self.start): print(f"Warning: Target node {curr_tuple} not found in parent dict. Path reconstruction might fail.") # Fallback to the last added node if goal wasn't reachable/added correctly if self.nodes: curr_tuple = tuple(self.nodes[-1]) else: return None # No path possible while curr_tuple is not None: # Ensure the node corresponding to the tuple exists # This requires searching self.nodes, which is inefficient. # A better approach is to store nodes in the dict or use indices. # For now, let's assume tuple keys match numpy arrays. path.append(np.array(curr_tuple)) curr_tuple = self.parent.get(curr_tuple, None) if not path: print("Error: Path reconstruction failed.") return None if tuple(path[-1]) != tuple(self.start): print("Warning: Path does not end at start node.") return np.array(path[::-1]) # Reverse to get start -> goal order # --------------------- Path Cost Function (Use safe builds) --------------------- def path_cost(path_pts, builds_with_safety, drone_speed=DRONE_SPEED, penalty_k=PENALTY_K): total_time = 0.0 total_penalty = 0.0 num_segments = len(path_pts) - 1 if num_segments < 1: return 0.0 # No cost for a single point path # Vectorized calculations where possible p = path_pts[:-1] # Start points of segments q = path_pts[1:] # End points of segments segments = q - p distances = np.linalg.norm(segments, axis=1) # Avoid division by zero for zero-length segments valid_segments = distances > 1e-6 if not np.any(valid_segments): return 0.0 # Path has no length p = p[valid_segments] q = q[valid_segments] segments = segments[valid_segments] distances = distances[valid_segments] dir_unit = segments / distances[:, np.newaxis] # Interpolate wind at midpoints for better average (optional, could use start/end) midpoints = p + segments / 2.0 # try: # wind_vectors = interp(midpoints) # except ValueError as e: # print(f"Interpolation error: {e}") # print(f"Midpoints shape: {midpoints.shape}") # # Handle error, e.g., return a large cost or use zero wind # wind_vectors = np.zeros_like(midpoints) # Calculate ground speed component along each segment # Ensure wind_vectors and dir_unit have compatible shapes for dot product # np.einsum is efficient for row-wise dot products # wind_along_path = np.einsum('ij,ij->i', wind_vectors, dir_unit) ground_speeds = np.maximum(drone_speed , 1e-3) # Avoid zero/negative speed # Calculate time for each segment segment_times = distances / ground_speeds total_time = np.sum(segment_times) # Calculate collision penalty (iterate segments as check_segment_collision is per-segment) for i in range(len(p)): # Pass pre-calculated builds_with_safety penetration = check_segment_collision(p[i], q[i], builds_with_safety) if penetration > 0: total_penalty += penalty_k * penetration**2 # Quadratic penalty return total_time + total_penalty生成注释
05-14
import numpy as np import matplotlib.pyplot as plt from scipy.integrate import cumulative_trapezoid # ==================== 参数设置 ==================== # 细菌生长参数 a, b, c = -0.0011, 0.0545, -0.0602 K = 3e10 N0 = 1 dt = 0.1 t_total = 36 # 压强模型参数 (调整后的值) k_EPS = 2.5e-6 # 增大EPS分泌速率 alpha = 2.5e3 # 增强EPS对水势的影响 beta = -3e5 gamma = 1000 P_crit = 2e5 # ==================== 函数定义 ==================== def temperature(t): x = t return (-8e-12 * x**6 + 7e-9 * x**5 - 2e-6 * x**4 + 0.0003 * x**3 - 0.0171 * x**2 + 0.2602 * x + 16.89) def humidity(t): x = t return (2e-11 * x**6 - 2e-8 * x**5 + 6e-6 * x**4 - 0.0008 * x**3 + 0.0374 * x**2 - 0.1759 * x + 74.181) def r_T(T): return a * T**2 + b * T + c def bacterial_growth(N, t): T = temperature(t) return r_T(T) * N * (1 - N / K) # ==================== 数值求解 ==================== time_points = np.arange(0, t_total + dt, dt) T_values = temperature(time_points) RH_values = humidity(time_points) # 细菌数量求解 N_values = np.zeros_like(time_points) N_values[0] = N0 for i in range(len(time_points) - 1): t_i = time_points[i] N_i = N_values[i] N_p = N_i + dt * bacterial_growth(N_i, t_i) N_c = N_i + 0.5 * dt * (bacterial_growth(N_i, t_i) + bacterial_growth(N_p, t_i + dt)) N_values[i + 1] = N_c # 压强计算 EPS_values = k_EPS * cumulative_trapezoid(N_values, time_points, initial=0) psi_bacteria = alpha * EPS_values + beta psi_leaf = gamma * (RH_values - 100) P_values = psi_bacteria - psi_leaf # 计算破裂时间 burst_idx = np.where(P_values >= P_crit)[0] t_burst = time_points[burst_idx[0]] if len(burst_idx) > 0 else None # ==================== 可视化 ==================== plt.figure(figsize=(12, 6)) # 压强曲线 plt.plot(time_points, P_values / 1e5, 'b-', label='Pressure (×10⁵ Pa)') plt.axhline(P_crit / 1e5, color='r', linestyle='--', label='Critical Pressure') # 标记破裂时间 if t_burst is not None: plt.axvline(t_burst, color='g', linestyle=':', linewidth=2, label=f'Burst Time: {t_burst:.1f} h') plt.scatter(t_burst, P_crit / 1e5, color='k', zorder=5) plt.xlabel('Time (hours)') plt.ylabel('Pressure (×10⁵ Pa)') plt.title('Necrotic Pressure Dynamics with Burst Time Threshold') plt.legend() plt.grid(True) plt.tight_layout() plt.show() # ==================== 结果输出 ==================== print(f"破裂时间 t_burst: {t_burst:.1f} 小时" if t_burst else "未达到临界压强") 帮我运行上述代码,显示出运行结果
05-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值