| | |
| | | import com.algo.model.PathCode; |
| | | import com.algo.model.PlannedPath; |
| | | import com.algo.util.JsonUtils; |
| | | import com.algo.util.PathTimeCalculator; |
| | | |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * A*路径规划器实现 |
| | | * 使用3D A*算法进行CTU时空路径规划,支持物理约束 |
| | | */ |
| | | public class AStarPathPlanner implements PathPlanner { |
| | | |
| | |
| | | * 邻接表 |
| | | */ |
| | | private final Map<String, List<Map<String, String>>> adjacencyList; |
| | | |
| | | /** |
| | | * 环境连通性数据 |
| | | */ |
| | | private final JsonUtils.EnvironmentConnectivity environmentConnectivity; |
| | | |
| | | /** |
| | | * 时间精度(毫秒) |
| | |
| | | private final int maxSearchDepth = 15000; |
| | | |
| | | /** |
| | | * 距离缓存 - 优化:缓存距离计算结果 |
| | | * 距离缓存 |
| | | */ |
| | | private final Map<String, Double> distanceCache = new HashMap<>(); |
| | | |
| | |
| | | * 快速路径缓存 |
| | | */ |
| | | private final Map<String, List<String>> fastPathCache = new HashMap<>(); |
| | | |
| | | /** |
| | | * 实际坐标映射 |
| | | */ |
| | | private Map<String, double[]> realCoordinateMapping; |
| | | |
| | | /** |
| | | * 路径时间计算器 |
| | | */ |
| | | private PathTimeCalculator timeCalculator; |
| | | |
| | | /** |
| | | * 构造函数 |
| | |
| | | this.pathMapping = pathMapping; |
| | | this.adjacencyList = new HashMap<>(); |
| | | |
| | | // 直接构建邻接表 |
| | | buildAdjacencyList(); |
| | | // 加载环境连通性数据 |
| | | this.environmentConnectivity = JsonUtils.loadEnvironmentConnectivity("environment.json"); |
| | | |
| | | // 构建环境感知的邻接表 |
| | | buildEnvironmentAwareAdjacencyList(); |
| | | |
| | | // 加载实际坐标映射 |
| | | loadRealCoordinateMapping(); |
| | | |
| | | // 初始化时间计算器 |
| | | this.timeCalculator = new PathTimeCalculator(pathMapping, realCoordinateMapping); |
| | | |
| | | // 预计算常用距离 |
| | | precomputeCommonDistances(); |
| | | |
| | | System.out.println("A*路径规划器初始化完成,邻接表包含 " + adjacencyList.size() + " 个节点"); |
| | | } |
| | | |
| | | /** |
| | | * 加载实际坐标映射 |
| | | */ |
| | | private void loadRealCoordinateMapping() { |
| | | try { |
| | | this.realCoordinateMapping = JsonUtils.loadRealCoordinateMapping("man_code.json"); |
| | | if (realCoordinateMapping == null || realCoordinateMapping.isEmpty()) { |
| | | System.out.println("未能加载实际坐标映射,使用网格坐标"); |
| | | this.realCoordinateMapping = new HashMap<>(); |
| | | } |
| | | } catch (Exception e) { |
| | | System.err.println("加载实际坐标映射失败: " + e.getMessage()); |
| | | this.realCoordinateMapping = new HashMap<>(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | } |
| | | |
| | | // 减少等待选项 - 只在必要时等待(20%概率) |
| | | // 减少等待选项 |
| | | if (Math.random() < 0.2) { |
| | | long waitTime = timeResolution; |
| | | long waitUntilTime = current.timePoint + waitTime; |
| | |
| | | /** |
| | | * 时空启发式函数 |
| | | * |
| | | * @param coord1 当前坐标 |
| | | * @param coord2 目标坐标 |
| | | * @param coord1 当前坐标(网格) |
| | | * @param coord2 目标坐标(网格) |
| | | * @param currentTime 当前时间 |
| | | * @param physicalConfig 物理配置 |
| | | * @return 启发式值 |
| | | */ |
| | | private double spaceTimeHeuristic(int[] coord1, int[] coord2, long currentTime, CTUPhysicalConfig physicalConfig) { |
| | | // 空间距离 |
| | | String pathId1 = findPathIdByCoordinate(coord1); |
| | | String pathId2 = findPathIdByCoordinate(coord2); |
| | | |
| | | if (pathId1 != null && pathId2 != null) { |
| | | double[] realCoord1 = JsonUtils.getRealCoordinate(pathId1, realCoordinateMapping); |
| | | double[] realCoord2 = JsonUtils.getRealCoordinate(pathId2, realCoordinateMapping); |
| | | |
| | | if (realCoord1 != null && realCoord2 != null) { |
| | | // 使用实际距离计算 |
| | | double realDistance = CTUPhysicalConfig.calculateRealDistance(realCoord1, realCoord2); |
| | | |
| | | // 时间成本估计(假设平均速度) |
| | | double timeEstimate = realDistance / physicalConfig.getNormalSpeed(); |
| | | |
| | | // 考虑转向成本 |
| | | double turnPenalty = realDistance > 0.5 ? physicalConfig.getTurnTime90() / 2.0 : 0; |
| | | |
| | | return timeEstimate + turnPenalty; |
| | | } |
| | | } |
| | | |
| | | // 前方案:使用网格坐标 |
| | | double spatialDistance = Math.abs(coord1[0] - coord2[0]) + Math.abs(coord1[1] - coord2[1]); |
| | | |
| | | // 时间成本估计 |
| | | double timeEstimate = spatialDistance * physicalConfig.getStandardPointDistance() / physicalConfig.getNormalSpeed(); |
| | | |
| | | // 考虑转向成本 |
| | | double turnPenalty = spatialDistance > 1 ? physicalConfig.getTurnTime90() : 0; |
| | | |
| | | return timeEstimate + turnPenalty; |
| | | } |
| | | |
| | | /** |
| | | * 根据网格坐标查找路径ID |
| | | */ |
| | | private String findPathIdByCoordinate(int[] coord) { |
| | | if (coord == null) return null; |
| | | |
| | | for (Map.Entry<String, Map<String, Integer>> entry : pathMapping.entrySet()) { |
| | | Map<String, Integer> coordMap = entry.getValue(); |
| | | if (coordMap != null && |
| | | coord[0] == coordMap.getOrDefault("x", -1) && |
| | | coord[1] == coordMap.getOrDefault("y", -1)) { |
| | | return entry.getKey(); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 计算移动时间 |
| | | * 计算精确的移动时间,基于实际坐标和物理参数 |
| | | * |
| | | * @param fromCode 起始代码 |
| | | * @param toCode 目标代码 |
| | |
| | | * @return 移动时间(毫秒) |
| | | */ |
| | | private long calculateTravelTime(String fromCode, String toCode, String direction, CTUPhysicalConfig physicalConfig) { |
| | | // 基本移动时间 |
| | | // 获取实际坐标 |
| | | double[] fromCoord = JsonUtils.getRealCoordinate(fromCode, realCoordinateMapping); |
| | | double[] toCoord = JsonUtils.getRealCoordinate(toCode, realCoordinateMapping); |
| | | |
| | | if (fromCoord == null || toCoord == null) { |
| | | // fallback到网格坐标计算 |
| | | return calculateFallbackTravelTime(fromCode, toCode, physicalConfig); |
| | | } |
| | | |
| | | // 计算实际距离(米) |
| | | double realDistance = CTUPhysicalConfig.calculateRealDistance(fromCoord, toCoord); |
| | | |
| | | if (realDistance == 0.0) { |
| | | return 100; // 最小时间100ms |
| | | } |
| | | |
| | | // 假设起始和结束速度(可以根据上下文进一步优化) |
| | | double startSpeed = 0.0; // 假设从静止开始 |
| | | double endSpeed = 0.0; // 假设结束时停止 |
| | | double targetSpeed = physicalConfig.getNormalSpeed(); |
| | | |
| | | // 计算精确的移动时间 |
| | | double movementTime = physicalConfig.calculatePreciseMovementTime( |
| | | realDistance, startSpeed, endSpeed, targetSpeed |
| | | ); |
| | | |
| | | // 计算转向时间 |
| | | double turnTime = calculateTurnTime(fromCode, toCode, physicalConfig); |
| | | |
| | | return Math.max(100, (long) ((movementTime + turnTime) * 1000)); |
| | | } |
| | | |
| | | /** |
| | | * 计算转向时间 |
| | | */ |
| | | private double calculateTurnTime(String fromCode, String toCode, CTUPhysicalConfig physicalConfig) { |
| | | |
| | | double[] fromCoord = JsonUtils.getRealCoordinate(fromCode, realCoordinateMapping); |
| | | double[] toCoord = JsonUtils.getRealCoordinate(toCode, realCoordinateMapping); |
| | | |
| | | if (fromCoord == null || toCoord == null) { |
| | | return physicalConfig.getTurnTime90() / 4.0; // 平均转向时间 |
| | | } |
| | | |
| | | // 计算方向变化 |
| | | double dx = toCoord[0] - fromCoord[0]; |
| | | double dy = toCoord[1] - fromCoord[1]; |
| | | |
| | | // 直线运动无需转向 |
| | | if (Math.abs(dx) < 1.0 && Math.abs(dy) < 1.0) { |
| | | return 0.0; |
| | | } |
| | | |
| | | return physicalConfig.getTurnTime90() / 2.0; |
| | | } |
| | | |
| | | /** |
| | | * 备用时间计算方法(当无法获取实际坐标时使用) |
| | | */ |
| | | private long calculateFallbackTravelTime(String fromCode, String toCode, CTUPhysicalConfig physicalConfig) { |
| | | double distance = physicalConfig.getStandardPointDistance(); |
| | | double speed = physicalConfig.getNormalSpeed(); |
| | | long moveTime = (long) ((distance / speed) * 1000); |
| | | |
| | | // 转向时间 |
| | | long turnTime = (long) (physicalConfig.getTurnTime90() * 1000 / 4); // 假设平均转向时间 |
| | | |
| | | long turnTime = (long) (physicalConfig.getTurnTime90() * 1000 / 4); |
| | | return moveTime + turnTime; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | PathCode pathCode = new PathCode(node.code, direction); |
| | | |
| | | // 添加时间信息 |
| | | // pathCode.setArrivalTime(node.timePoint); |
| | | |
| | | codeList.add(pathCode); |
| | | } |
| | | |
| | | return new PlannedPath("", "", codeList); |
| | | PlannedPath plannedPath = new PlannedPath("", "", codeList); |
| | | |
| | | // 使用统一的时间计算器计算精确时间 |
| | | long startTime = pathNodes.get(0).timePoint; |
| | | CTUPhysicalConfig defaultConfig = createDefaultPhysicalConfig(); |
| | | timeCalculator.calculatePathTiming(plannedPath, startTime, defaultConfig, 0.0); |
| | | |
| | | return plannedPath; |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | @Override |
| | | public double calculateDistance(String startCode, String endCode) { |
| | | // 优先使用实际坐标 |
| | | double[] startRealCoord = JsonUtils.getRealCoordinate(startCode, realCoordinateMapping); |
| | | double[] endRealCoord = JsonUtils.getRealCoordinate(endCode, realCoordinateMapping); |
| | | |
| | | if (startRealCoord != null && endRealCoord != null) { |
| | | return CTUPhysicalConfig.calculateRealDistance(startRealCoord, endRealCoord); |
| | | } |
| | | |
| | | // 前方案:使用网格坐标 |
| | | int[] startCoord = JsonUtils.getCoordinate(startCode, pathMapping); |
| | | int[] endCoord = JsonUtils.getCoordinate(endCode, pathMapping); |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 构建邻接表 |
| | | * 构建环境感知的邻接表 |
| | | */ |
| | | private void buildAdjacencyList() { |
| | | private void buildEnvironmentAwareAdjacencyList() { |
| | | // 创建坐标到编号的临时映射 |
| | | Map<String, String> tempCoordToCode = new HashMap<>(); |
| | | for (Map.Entry<String, Map<String, Integer>> entry : pathMapping.entrySet()) { |
| | |
| | | int x = coordMap.get("x"); |
| | | int y = coordMap.get("y"); |
| | | |
| | | // 环境感知:检查当前节点是否可通行 |
| | | if (!environmentConnectivity.isTraversable(x, y)) { |
| | | adjacencyList.put(code, new ArrayList<>()); // 不可通行的节点没有邻居 |
| | | continue; |
| | | } |
| | | |
| | | List<Map<String, String>> neighbors = new ArrayList<>(); |
| | | |
| | | // 检查四个方向的邻居 |
| | |
| | | String coordKey = newX + "," + newY; |
| | | |
| | | String neighborCode = tempCoordToCode.get(coordKey); |
| | | if (neighborCode != null) { |
| | | |
| | | // 环境感知:只有当邻居节点也可通行时才添加连接 |
| | | if (neighborCode != null && environmentConnectivity.isTraversable(newX, newY)) { |
| | | Map<String, String> neighbor = new HashMap<>(); |
| | | neighbor.put("code", neighborCode); |
| | | neighbor.put("direction", directionAngles[i]); |
| | |
| | | |
| | | adjacencyList.put(code, neighbors); |
| | | } |
| | | |
| | | System.out.println("环境感知邻接表构建完成,过滤了不可通行的连接"); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | |
| | | /** |
| | | * 快速路径规划 - 先尝试简化空间路径规划 |
| | | * 快速路径规划 |
| | | * 对于近距离路径,直接返回结果避免复杂的时空计算 |
| | | */ |
| | | private PlannedPath tryFastPathPlanning(String startCode, String endCode, List<double[]> constraints) { |
| | |
| | | openSet.offer(startNode); |
| | | gScores.put(startCode, 0.0); |
| | | |
| | | // 简化的约束检查器 |
| | | // 约束检查器 |
| | | FastConstraintChecker constraintChecker = new FastConstraintChecker(constraints); |
| | | |
| | | int searchDepth = 0; |