jianghaiyue
2025-11-05 12f30a09bce1c61b2ae90129124cdc467a59b074
algo-zkd/src/main/java/com/algo/service/PathPlanningService.java
@@ -3,6 +3,7 @@
import com.algo.config.EnvDataConfig;
import com.algo.model.*;
import com.algo.util.JsonUtils;
import com.algo.util.PathTimeCalculator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -43,6 +44,11 @@
     * 剩余路径处理
     */
    private RemainingPathProcessor remainingPathProcessor;
    /**
     * 路径时间计算器
     */
    private PathTimeCalculator timeCalculator;
    /**
@@ -73,6 +79,13 @@
        // 初始化剩余路径处理器
        this.remainingPathProcessor = new RemainingPathProcessor(envDataConfig.getPathMapping());
        // 初始化时间计算器
        Map<String, double[]> realCoordinateMapping = JsonUtils.loadRealCoordinateMapping("man_code.json");
        this.timeCalculator = new PathTimeCalculator(envDataConfig.getPathMapping(), realCoordinateMapping);
        // 为碰撞解决器设置时间计算器
        this.collisionResolver.setTimeCalculator(timeCalculator);
    }
    /**
@@ -90,33 +103,70 @@
        this.taskExtractor = new ExecutingTaskExtractor(envDataConfig.getPathMapping(), taskList);
        long startTime = System.currentTimeMillis();
        long unifiedTimestamp = startTime / 1000;
        System.out.println("开始为 " + agvStatusList.size() + " 个CTU规划");
        System.out.println("时间基准: " + unifiedTimestamp + "秒 (" + new Date(startTime) + ")");
        // 统计故障车数量
        long faultyCount = agvStatusList.stream().filter(agv -> agv.getError() == 1).count();
        if (faultyCount > 0) {
            System.out.println("检测到 " + faultyCount + " 个故障车(error),将在时空表中占据位置");
        }
        // 1. 构建现有剩余路径的时空占用表
        System.out.println("步骤1: 构建时空占用表");
        Map<String, String> spaceTimeOccupancyMap = remainingPathProcessor.buildSpaceTimeOccupancyMap(agvStatusList);
        Map<String, String> spaceTimeOccupancyMap = remainingPathProcessor.buildSpaceTimeOccupancyMap(agvStatusList, unifiedTimestamp);
        System.out.println("时空占用表构建完成,包含 " + spaceTimeOccupancyMap.size() + " 个占用点");
        // 2. 分类处理CTU:有剩余路径的和需要新路径的
        // 2. 初始化规划结果集合
        List<PlannedPath> plannedPaths = new ArrayList<>();
        List<ExecutingTask> executingTasks = new ArrayList<>();
        Map<String, String> plannedAgvIds = new HashMap<>(); // 提前初始化标记故障车
        // 3. 分类处理CTU:有剩余路径的和需要新路径的
        List<AGVStatus> ctuWithRemainingPaths = new ArrayList<>();
        List<AGVStatus> ctuNeedingNewPaths = new ArrayList<>();
        List<AGVStatus> unhandledAgvs = new ArrayList<>(); // 未处理的AGV
        for (AGVStatus agv : agvStatusList) {
            // 故障车已在时空占用表中处理,标记为已处理但不规划路径
            if (agv.getError() == 1) {
                plannedAgvIds.put(agv.getAgvId(), "FAULTY");
                continue;
            }
            if (agv.hasRemainingPath()) {
                ctuWithRemainingPaths.add(agv);
            } else if (agv.canAcceptNewTask() || (includeIdleAgv && isAgvIdle(agv))) {
            } else if (agv.canAcceptNewTask()) {
                // 可以接受新任务的AGV(有任务或可以接受新任务)
                ctuNeedingNewPaths.add(agv);
            } else if (includeIdleAgv && isAgvIdle(agv)) {
                // 完全空闲的AGV(无任务,无剩余路径)
                ctuNeedingNewPaths.add(agv);
            } else {
                // 其他情况:不可用、有剩余路径但>1、有任务但不可用等
                unhandledAgvs.add(agv);
            }
        }
        System.out.println("CTU分类完成: 有剩余路径=" + ctuWithRemainingPaths.size() +
                ", 需要新路径=" + ctuNeedingNewPaths.size());
                ", 需要新路径=" + ctuNeedingNewPaths.size() +
                ", 未处理=" + unhandledAgvs.size());
        // 记录未处理的AGV信息(debug)
        if (!unhandledAgvs.isEmpty()) {
            for (AGVStatus agv : unhandledAgvs) {
                System.out.println("  - AGV " + agv.getAgvId() +
                    ": error=" + agv.getError() +
                    ", hasRemainingPath=" + agv.hasRemainingPath() +
                    ", canAcceptNewTask=" + agv.canAcceptNewTask() +
                    ", isIdle=" + isAgvIdle(agv) +
                    ", isAvailable=" + agv.isAvailable());
            }
        }
        // 3. 处理有剩余路径的CTU
        List<PlannedPath> plannedPaths = new ArrayList<>();
        List<ExecutingTask> executingTasks = new ArrayList<>();
        Map<String, String> plannedAgvIds = new HashMap<>();
        // 4. 处理有剩余路径的CTU
        System.out.println("步骤2: 处理有剩余路径的CTU");
        for (AGVStatus agv : ctuWithRemainingPaths) {
@@ -136,12 +186,30 @@
            }
        }
        // 4. 为需要新路径的CTU提取执行中任务
        // 4.5. 检查空闲AGV是否需要让行
        System.out.println("检查空闲AGV是否需要让行");
        List<AGVStatus> yieldingAgvs = identifyYieldingAgvs(agvStatusList, plannedPaths);
        if (!yieldingAgvs.isEmpty()) {
            System.out.println("  发现 " + yieldingAgvs.size() + " 个需要让行的空闲AGV");
            for (AGVStatus yieldAgv : yieldingAgvs) {
                PlannedPath yieldPath = planYieldPath(yieldAgv, plannedPaths, spaceTimeOccupancyMap, constraints, unifiedTimestamp);
                if (yieldPath != null) {
                    plannedPaths.add(yieldPath);
                    plannedAgvIds.put(yieldAgv.getAgvId(), "AVOIDING");
                    System.out.println("  AGV " + yieldAgv.getAgvId() + " 让行路径规划成功,从 " +
                        yieldAgv.getPosition() + " 移开");
                }
            }
        } else {
            System.out.println("  无需让行的AGV");
        }
        // 5. 为需要新路径的CTU提取执行中任务
        System.out.println("步骤3: 提取需要新路径的CTU任务");
        List<ExecutingTask> newTasks = taskExtractor.extractExecutingTasks(ctuNeedingNewPaths);
        System.out.println("提取到 " + newTasks.size() + " 个新任务");
        // 5. 为新任务规划路径(考虑时空约束)
        // 6. 为新任务规划路径(考虑时空约束)
        System.out.println("步骤4: 为新任务规划路径");
        for (ExecutingTask task : newTasks) {
            String agvId = task.getAgvId();
@@ -169,7 +237,7 @@
            // 规划时空安全的路径
            PlannedPath plannedPath = planPathWithSpaceTimeConstraints(
                    currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus
                    currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus, unifiedTimestamp
            );
            if (plannedPath != null) {
@@ -177,7 +245,6 @@
                plannedPath.setAgvId(agvId);
                plannedPath.setSegId(generateSegId(task.getTaskId(), agvId, task.getTaskType()));
                // 增强路径代码信息
                enhancePathCodes(plannedPath, task);
                plannedPaths.add(plannedPath);
@@ -194,17 +261,45 @@
            }
        }
        // 6. 处理空闲CTU
        // 7. 处理空闲CTU
        if (includeIdleAgv) {
            System.out.println("步骤5: 处理空闲CTU");
            // 7.1. 检查空闲AGV是否占用已规划路径,如果占用则生成避让路径
            List<AGVStatus> yieldingIdleAgvs = identifyYieldingAgvs(agvStatusList, plannedPaths);
            List<AGVStatus> yieldingAgvsToHandle = new ArrayList<>();
            for (AGVStatus yieldAgv : yieldingIdleAgvs) {
                if (!plannedAgvIds.containsKey(yieldAgv.getAgvId())) {
                    yieldingAgvsToHandle.add(yieldAgv);
                }
            }
            if (!yieldingAgvsToHandle.isEmpty()) {
                System.out.println( yieldingAgvsToHandle.size() + " 个需要避让的空闲AGV");
                for (AGVStatus yieldAgv : yieldingAgvsToHandle) {
                    PlannedPath yieldPath = planYieldPath(yieldAgv, plannedPaths, spaceTimeOccupancyMap, constraints, unifiedTimestamp);
                    if (yieldPath != null) {
                        plannedPaths.add(yieldPath);
                        plannedAgvIds.put(yieldAgv.getAgvId(), "AVOIDING");
                    }
                }
            } else {
                System.out.println("  无需避让的空闲AGV");
            }
            // 7.2. 然后为剩余的空闲AGV规划待机/充电路径
            List<PlannedPath> idlePaths = planIdleAgvPathsWithConstraints(
                    ctuNeedingNewPaths, plannedAgvIds, constraints, spaceTimeOccupancyMap
                    ctuNeedingNewPaths, plannedAgvIds, constraints, spaceTimeOccupancyMap, unifiedTimestamp
            );
            plannedPaths.addAll(idlePaths);
            System.out.println("空闲CTU路径规划完成,数量: " + idlePaths.size());
            System.out.println("  空闲CTU路径规划,数量: " + idlePaths.size());
        }
        // 7. 最终碰撞检测和解决(针对同时生成的新路径)
        // 7.5. 最终避让检查
        performFinalYieldingCheck(agvStatusList, plannedPaths, plannedAgvIds, spaceTimeOccupancyMap, constraints, unifiedTimestamp);
        // 8. 最终碰撞检测和解决
        System.out.println("步骤6: 最终碰撞检测");
        List<Conflict> conflicts = collisionDetector.detectConflicts(plannedPaths);
@@ -256,17 +351,51 @@
        PlannedPath remainingPath = agv.getRemainingPath();
        List<PathCode> remainingCodes = new ArrayList<>();
        // 从当前位置开始,获取剩余路径
        // 获取剩余路径
        List<PathCode> originalCodes = remainingPath.getCodeList();
        for (int i = agv.getCurrentPathIndex(); i < originalCodes.size(); i++) {
            remainingCodes.add(originalCodes.get(i));
            PathCode originalCode = originalCodes.get(i);
            PathCode newCode = new PathCode(originalCode.getCode(), originalCode.getDirection());
            newCode.setActionType(originalCode.getActionType());
            newCode.setTaskId(originalCode.getTaskId());
            newCode.setPosType(originalCode.getPosType());
            newCode.setLev(originalCode.getLev());
            newCode.setTargetPoint(originalCode.isTargetPoint());
            remainingCodes.add(newCode);
        }
        // 创建新的路径对象
        PlannedPath processedPath = new PlannedPath();
        processedPath.setAgvId(agv.getAgvId());
        processedPath.setCodeList(remainingCodes);
        processedPath.setSegId(agv.getAgvId() + "_REMAINING_" + System.currentTimeMillis());
        // 使用输入中的segId,如果没有则生成新的
        String segId = remainingPath.getSegId();
        if (segId == null || segId.trim().isEmpty()) {
            segId = agv.getAgvId() + "_REMAINING_" + System.currentTimeMillis();
        }
        processedPath.setSegId(segId);
        if (timeCalculator != null && !remainingCodes.isEmpty()) {
            // 获取AGV的下一个路径点到达时间作为起始时间
            long startTime = agv.getNextPointArrivalTime();
            CTUPhysicalConfig config = agv.getPhysicalConfig();
            // 估算当前速度(AGV移动中为正常速度;静止为0)
            double initialSpeed = agv.hasRemainingPath() && agv.getRemainingPathLength() > 0
                                ? config.getNormalSpeed() : 0.0;
            // 计算时间信息
            // arrivalTime, departureTime, cumulativeTime
            timeCalculator.calculatePathTiming(
                processedPath,
                startTime,
                config,
                initialSpeed
            );
        } else {
            System.out.println("  未能为剩余路径设置时间信息 - AGV: " + agv.getAgvId());
        }
        return processedPath;
    }
@@ -314,27 +443,32 @@
     * @param constraints  约束条件
     * @param occupancyMap 时空占用表
     * @param agvStatus    CTU状态
     * @param unifiedTimestamp 统一时间戳(秒)
     * @return 规划的路径
     */
    private PlannedPath planPathWithSpaceTimeConstraints(String startPos, String endPos,
                                                         List<double[]> constraints,
                                                         Map<String, String> occupancyMap,
                                                         AGVStatus agvStatus) {
        // 首先尝试基本路径规划
        PlannedPath basicPath = pathPlanner.planPath(startPos, endPos, constraints);
        if (basicPath == null) {
                                                         AGVStatus agvStatus,
                                                         long unifiedTimestamp) {
        // 带时空占用表的路径规划
        long startTimeMs = unifiedTimestamp * 1000;
        PlannedPath spacetimePath = ((AStarPathPlanner)pathPlanner).planSpaceTimePath(
                startPos,
                endPos,
                constraints,
                occupancyMap,
                agvStatus.getPhysicalConfig(),
                startTimeMs
        );
        if (spacetimePath == null) {
            return null;
        }
        // 找到安全的起始时间
        long safeStartTime = remainingPathProcessor.findSafeStartTime(
                basicPath, occupancyMap, agvStatus.getPhysicalConfig()
        );
        timeCalculator.calculatePathTiming(spacetimePath, startTimeMs, agvStatus.getPhysicalConfig(), 0.0);
        // 设置路径的时间信息
        enhancePathWithTimeInfo(basicPath, safeStartTime, agvStatus.getPhysicalConfig());
        return basicPath;
        return spacetimePath;
    }
    /**
@@ -364,9 +498,12 @@
                // 如果有方向变化,增加转向时间
                PathCode nextCode = codeList.get(i + 1);
                if (!pathCode.getDirection().equals(nextCode.getDirection())) {
                    double turnTime = config.getTurnTime(pathCode.getDirection(), nextCode.getDirection());
                    currentTime += (long) (turnTime * 1000);
                // 添加null检查
                if (pathCode.getDirection() != null && nextCode.getDirection() != null) {
                    if (!pathCode.getDirection().equals(nextCode.getDirection())) {
                        double turnTime = config.getTurnTime(pathCode.getDirection(), nextCode.getDirection());
                        currentTime += (long) (turnTime * 1000);
                    }
                }
            }
        }
@@ -420,12 +557,14 @@
     * @param plannedAgvIds 已规划CTU映射
     * @param constraints   约束条件
     * @param occupancyMap  时空占用表
     * @param unifiedTimestamp 统一时间戳(秒)
     * @return 空闲CTU路径列表
     */
    private List<PlannedPath> planIdleAgvPathsWithConstraints(List<AGVStatus> agvStatusList,
                                                              Map<String, String> plannedAgvIds,
                                                              List<double[]> constraints,
                                                              Map<String, String> occupancyMap) {
                                                              Map<String, String> occupancyMap,
                                                              long unifiedTimestamp) {
        List<PlannedPath> idlePaths = new ArrayList<>();
        for (AGVStatus agvStatus : agvStatusList) {
@@ -443,7 +582,7 @@
                if (targetPos != null && !targetPos.equals(currentPos)) {
                    PlannedPath idlePath = planPathWithSpaceTimeConstraints(
                            currentPos, targetPos, constraints, occupancyMap, agvStatus
                            currentPos, targetPos, constraints, occupancyMap, agvStatus, unifiedTimestamp
                    );
                    if (idlePath != null) {
@@ -597,6 +736,332 @@
    }
    /**
     * 迭代检查所有空闲AGV是否占用已规划路径
     */
    private void performFinalYieldingCheck(List<AGVStatus> agvStatusList,
                                           List<PlannedPath> plannedPaths,
                                           Map<String, String> plannedAgvIds,
                                           Map<String, String> spaceTimeOccupancyMap,
                                           List<double[]> constraints,
                                           long unifiedTimestamp) {
        final int MAX_ITERATIONS = 5; // 最大迭代次数
        int iteration = 0;
        int totalYieldingCount = 0;
        while (iteration < MAX_ITERATIONS) {
            iteration++;
            // 识别需要避让的空闲AGV
            List<AGVStatus> yieldingAgvs = identifyYieldingAgvs(agvStatusList, plannedPaths);
            if (yieldingAgvs.isEmpty()) {
                if (iteration == 1) {
                    System.out.println("  无需避让的AGV");
                } else {
                    System.out.println("  第" + iteration + "轮检查:无新的避让需求");
                }
                break;
            }
            System.out.println("  第" + iteration + "轮检查:" + yieldingAgvs.size() + " 个需要避让的AGV");
            // 规划避让路径
            int successCount = 0;
            for (AGVStatus yieldAgv : yieldingAgvs) {
                if (plannedAgvIds.containsKey(yieldAgv.getAgvId())) {
                    continue;
                }
                PlannedPath yieldPath = planYieldPath(yieldAgv, plannedPaths, spaceTimeOccupancyMap, constraints, unifiedTimestamp);
                if (yieldPath != null) {
                    plannedPaths.add(yieldPath);
                    plannedAgvIds.put(yieldAgv.getAgvId(), "AVOIDING");
                    successCount++;
                    totalYieldingCount++;
                }
            }
            if (successCount == 0) {
                System.out.println("  第" + iteration + "轮检查:无法为需要避让的AGV规划路径,停止迭代");
                break;
            }
        }
        if (iteration >= MAX_ITERATIONS) {
            System.out.println("  达到最大迭代次数(" + MAX_ITERATIONS + "),可能存在复杂的避让场景");
        }
        if (totalYieldingCount > 0) {
            System.out.println("  避让检查完成:共处理 " + totalYieldingCount + " 个避让AGV,迭代 " + iteration + " 轮");
        }
    }
    /**
     * 识别需要让行的AGV
     * 检查空闲AGV是否占用了其他AGV剩余路径上的位置
     *
     * @param agvStatusList 所有AGV状态列表
     * @param plannedPaths  已规划的路径列表(包含剩余路径)
     * @return 需要让行的AGV列表
     */
    private List<AGVStatus> identifyYieldingAgvs(List<AGVStatus> agvStatusList, List<PlannedPath> plannedPaths) {
        List<AGVStatus> yieldingAgvs = new ArrayList<>();
        // 收集所有已规划路径上的位置
        Set<String> occupiedPositions = new HashSet<>();
        for (PlannedPath path : plannedPaths) {
            if (path.getCodeList() != null) {
                for (PathCode code : path.getCodeList()) {
                    if (code.getCode() != null) {
                        occupiedPositions.add(code.getCode());
                    }
                }
            }
        }
        // 检查每个AGV
        for (AGVStatus agv : agvStatusList) {
            // 如果这个AGV已经有剩余路径,跳过(已在任务)
            if (agv.hasRemainingPath()) {
                continue;
            }
            // 检查AGV是否有位置信息
            if (agv.getPosition() == null || agv.getPosition().trim().isEmpty()) {
                continue;
            }
            // 检查AGV是否是空闲状态(status=0)
            // 只要没有剩余路径且有位置,就检查是否需要让行
            int status = agv.getStatus();
            if (status != 0) {
                // status=1或2(问题或忙碌)等其他状态跳过
                continue;
            }
            // 检查该AGV的位置是否在其他AGV的路径上
            String currentPos = agv.getPosition();
            if (occupiedPositions.contains(currentPos)) {
                System.out.println(" CTU " + agv.getAgvId() +
                    " (status=" + status + ") 在位置 " + currentPos + " 占用了其他CTU路径,需要让行");
                yieldingAgvs.add(agv);
            }
        }
        return yieldingAgvs;
    }
    /**
     * 为让行AGV规划避让路径
     *
     * @param yieldAgv         需要让行的AGV
     * @param existingPaths    已存在的路径列表
     * @param occupancyMap     时空占用表
     * @param constraints      路径约束
     * @return 让行路径
     */
    private PlannedPath planYieldPath(AGVStatus yieldAgv, List<PlannedPath> existingPaths,
                                     Map<String, String> occupancyMap, List<double[]> constraints,
                                     long unifiedTimestamp) {
        String currentPos = yieldAgv.getPosition();
        String agvId = yieldAgv.getAgvId();
        // 1. 找到安全的目标位置
        Set<String> blockedPositions = new HashSet<>();
        for (PlannedPath path : existingPaths) {
            if (path.getCodeList() != null) {
                for (PathCode code : path.getCodeList()) {
                    if (code.getCode() != null) {
                        blockedPositions.add(code.getCode());
                    }
                }
            }
        }
        // 2. 寻找合适让行目标位置
        String targetPos = findYieldTargetPosition(currentPos, blockedPositions, yieldAgv);
        if (targetPos == null || targetPos.equals(currentPos)) {
            System.out.println("    AGV " + agvId + " 无法找到合适的让行位置");
            return null;
        }
        // 3. 规划避让路径
        PlannedPath yieldPath = planPathWithSpaceTimeConstraints(
            currentPos, targetPos, constraints, occupancyMap, yieldAgv, unifiedTimestamp
        );
        if (yieldPath != null) {
            yieldPath.setAgvId(agvId);
            yieldPath.setSegId(generateSegId("AVOID", agvId, "avoiding"));
            // 设置路径代码信息
            enhancePathCodesForYielding(yieldPath);
            // 更新占用表
            updateSpaceTimeOccupancyMap(yieldPath, occupancyMap, yieldAgv);
        }
        return yieldPath;
    }
    /**
     * 寻找让行的目标位置
     */
    private String findYieldTargetPosition(String currentPos, Set<String> blockedPositions, AGVStatus agv) {
        // 使用BFS搜索满足安全距离的空闲位置
        final int MAX_SEARCH_DEPTH = 15;
        // 获取安全距离参数(从物理配置中获取,默认3.0)
        CTUPhysicalConfig config = agv.getPhysicalConfig();
        double minSafetyDistance = config != null ? config.getMinSafetyDistance() : 3.0;
        // 将安全距离转换为网格步数(假设每个节点间距为1)
        int safetySteps = (int) Math.ceil(minSafetyDistance);
        Queue<String> queue = new LinkedList<>();
        Map<String, Integer> visited = new HashMap<>(); // 记录位置和距离
        queue.offer(currentPos);
        visited.put(currentPos, 0);
        while (!queue.isEmpty()) {
            String pos = queue.poll();
            int depth = visited.get(pos);
            if (depth >= MAX_SEARCH_DEPTH) {
                break;
            }
            List<Map<String, String>> neighbors = pathPlanner.getNeighbors(pos);
            for (Map<String, String> neighbor : neighbors) {
                String neighborPos = neighbor.get("code");
                if (neighborPos == null || visited.containsKey(neighborPos)) {
                    continue;
                }
                int distanceToPos = depth + 1;
                visited.put(neighborPos, distanceToPos);
                // 检查该位置是否满足要求
                if (!blockedPositions.contains(neighborPos) &&
                    isSafeDistanceFromBlockedPositions(neighborPos, blockedPositions, safetySteps)) {
                    System.out.println("  找到安全避让位置: " + neighborPos);
                    return neighborPos;
                }
                // 加入队列继续搜索
                queue.offer(neighborPos);
            }
        }
        System.out.println(" 未找到满足安全距离的避让位置,降低安全要求重试");
        // 如果找不到满足安全距离的位置,降低要求再次搜索(至少保证2步距离)
        return findYieldTargetPositionWithReducedSafety(currentPos, blockedPositions, 2);
    }
    /**
     * 检查候选位置与所有被占用位置是否满足安全距离
     */
    private boolean isSafeDistanceFromBlockedPositions(String candidatePos,
                                                       Set<String> blockedPositions,
                                                       int minSteps) {
        // 获取候选位置的坐标
        int[] candidateCoord = JsonUtils.getCoordinate(candidatePos, envDataConfig.getPathMapping());
        if (candidateCoord == null) {
            return false;
        }
        // 检查与每个被占用位置的距离
        for (String blockedPos : blockedPositions) {
            int[] blockedCoord = JsonUtils.getCoordinate(blockedPos, envDataConfig.getPathMapping());
            if (blockedCoord == null) {
                continue;
            }
            // 计算曼哈顿距离
            int distance = Math.abs(candidateCoord[0] - blockedCoord[0]) +
                          Math.abs(candidateCoord[1] - blockedCoord[1]);
            if (distance < minSteps) {
                return false;
            }
        }
        return true;
    }
    /**
     * 使用降低的安全要求查找让行位置(备选)
     *
     * @param currentPos 当前位置
     * @param blockedPositions 被占用的位置集合
     * @param minSteps 最小步数要求
     * @return 让行目标位置
     */
    private String findYieldTargetPositionWithReducedSafety(String currentPos,
                                                            Set<String> blockedPositions,
                                                            int minSteps) {
        final int MAX_SEARCH_DEPTH = 10;
        Queue<String> queue = new LinkedList<>();
        Map<String, Integer> visited = new HashMap<>();
        queue.offer(currentPos);
        visited.put(currentPos, 0);
        while (!queue.isEmpty()) {
            String pos = queue.poll();
            int depth = visited.get(pos);
            if (depth >= MAX_SEARCH_DEPTH) {
                break;
            }
            List<Map<String, String>> neighbors = pathPlanner.getNeighbors(pos);
            for (Map<String, String> neighbor : neighbors) {
                String neighborPos = neighbor.get("code");
                if (neighborPos == null || visited.containsKey(neighborPos)) {
                    continue;
                }
                visited.put(neighborPos, depth + 1);
                // 使用降低的安全要求
                if (!blockedPositions.contains(neighborPos) &&
                    isSafeDistanceFromBlockedPositions(neighborPos, blockedPositions, minSteps)) {
                    System.out.println("  找到降级安全避让位置: " + neighborPos +
                                     " (距离=" + (depth + 1) + "步, 最小安全距离=" + minSteps + "步)");
                    return neighborPos;
                }
                queue.offer(neighborPos);
            }
        }
        System.out.println("  无法找到避让位置");
        return null;
    }
    private void enhancePathCodesForYielding(PlannedPath yieldPath) {
        if (yieldPath == null || yieldPath.getCodeList() == null) {
            return;
        }
        List<PathCode> codes = yieldPath.getCodeList();
        for (int i = 0; i < codes.size(); i++) {
            PathCode code = codes.get(i);
            code.setActionType("0");
            code.setPosType(null);
            code.setLev(0);
            code.setTargetPoint(i == codes.size() - 1);
        }
    }
    /**
     * 路径规划结果类
     */
    public static class PathPlanningResult {
@@ -684,7 +1149,8 @@
    private List<PlannedPath> planTasksInParallel(List<ExecutingTask> tasks,
                                                  List<double[]> constraints,
                                                  Map<String, String> spaceTimeOccupancyMap,
                                                  List<AGVStatus> agvStatusList) {
                                                  List<AGVStatus> agvStatusList,
                                                  long unifiedTimestamp) {
        List<PlannedPath> allPaths = Collections.synchronizedList(new ArrayList<>());
@@ -705,7 +1171,7 @@
            for (ExecutingTask task : batch) {
                Future<PlannedPath> future = executorService.submit(() -> {
                    return planSingleTaskPath(task, constraints, spaceTimeOccupancyMap, agvStatusList);
                    return planSingleTaskPath(task, constraints, spaceTimeOccupancyMap, agvStatusList, unifiedTimestamp);
                });
                futures.add(future);
            }
@@ -735,7 +1201,8 @@
    private PlannedPath planSingleTaskPath(ExecutingTask task,
                                           List<double[]> constraints,
                                           Map<String, String> spaceTimeOccupancyMap,
                                           List<AGVStatus> agvStatusList) {
                                           List<AGVStatus> agvStatusList,
                                           long unifiedTimestamp) {
        String agvId = task.getAgvId();
        String currentPos = task.getCurrentPosition();
@@ -751,9 +1218,9 @@
            return null;
        }
        // 规划时空安全的路径
        // 规划时空安全的路径(使用统一时间戳)
        PlannedPath plannedPath = planPathWithSpaceTimeConstraints(
                currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus
                currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus, unifiedTimestamp
        );
        if (plannedPath != null) {