package com.algo.service; import com.algo.model.CTUPhysicalConfig; import com.algo.model.Conflict; import com.algo.model.PathCode; import com.algo.model.PlannedPath; import com.algo.util.JsonUtils; import java.util.*; /** * 碰撞检测器(增强版) * 用于检测CTU路径之间的时空冲突,支持物理参数和高精度时间检测 */ public class CollisionDetector { /** * 路径映射表 */ private final Map> pathMapping; /** * 默认最小安全距离 */ private final double defaultMinDistance; /** * 时间精度(毫秒) */ private final long timeResolution = 100; // 100毫秒的时间精度 /** * 空间分辨率(米) */ private final double spaceResolution = 0.1; // 10厘米的空间精度 /** * 构造函数 * * @param pathMapping 路径映射表 * @param defaultMinDistance 默认最小安全距离 */ public CollisionDetector(Map> pathMapping, double defaultMinDistance) { this.pathMapping = pathMapping; this.defaultMinDistance = defaultMinDistance; } /** * 构造函数(使用默认参数) * * @param pathMapping 路径映射表 */ public CollisionDetector(Map> pathMapping) { this(pathMapping, 3.0); } /** * 检测路径冲突(增强版,支持CTU物理参数) * * @param plannedPaths 规划路径列表 * @return 冲突列表 */ public List detectConflicts(List plannedPaths) { List conflicts = new ArrayList<>(); if (plannedPaths == null || plannedPaths.size() < 2) { return conflicts; } System.out.println("开始增强版碰撞检测,路径数量: " + plannedPaths.size()); // 构建高精度时空表 Map> spaceTimeTable = buildEnhancedSpaceTimeTable(plannedPaths); // 检测顶点冲突(同一时间同一位置) List vertexConflicts = detectEnhancedVertexConflicts(spaceTimeTable); conflicts.addAll(vertexConflicts); System.out.println("顶点冲突数量: " + vertexConflicts.size()); // 检测边冲突(CTU交换位置) List edgeConflicts = detectEnhancedEdgeConflicts(spaceTimeTable); conflicts.addAll(edgeConflicts); System.out.println("边冲突数量: " + edgeConflicts.size()); // 检测跟随冲突(CTU距离过近,考虑物理参数) List followingConflicts = detectEnhancedFollowingConflicts(spaceTimeTable); conflicts.addAll(followingConflicts); System.out.println("跟随冲突数量: " + followingConflicts.size()); // 检测物理尺寸冲突 List physicalConflicts = detectPhysicalSizeConflicts(spaceTimeTable); conflicts.addAll(physicalConflicts); System.out.println("物理尺寸冲突数量: " + physicalConflicts.size()); System.out.println("增强版碰撞检测完成,总冲突数量: " + conflicts.size()); return conflicts; } /** * 构建增强的时空表(高精度时间和物理参数) * * @param plannedPaths 规划路径列表 * @return 增强时空表 */ private Map> buildEnhancedSpaceTimeTable(List plannedPaths) { Map> spaceTimeTable = new HashMap<>(); for (PlannedPath path : plannedPaths) { String agvId = path.getAgvId(); List codeList = path.getCodeList(); if (codeList == null || codeList.isEmpty()) { continue; } // 获取CTU的物理配置(这里需要从某个地方获取,可能需要扩展PlannedPath类) CTUPhysicalConfig physicalConfig = getPhysicalConfigForCTU(agvId); long currentTime = System.currentTimeMillis(); // 基准时间 for (int i = 0; i < codeList.size(); i++) { PathCode pathCode = codeList.get(i); String position = pathCode.getCode(); int[] coordinates = JsonUtils.getCoordinate(position, pathMapping); if (coordinates != null) { // 计算精确的到达时间 long arrivalTime = calculateArrivalTime(currentTime, i, codeList, physicalConfig); // 计算停留时间 long departureTime = calculateDepartureTime(arrivalTime, pathCode, physicalConfig); // 创建时间段内的多个时间点 for (long timePoint = arrivalTime; timePoint <= departureTime; timePoint += timeResolution) { EnhancedSpaceTimeNode node = new EnhancedSpaceTimeNode( agvId, position, coordinates, timePoint, arrivalTime, departureTime, physicalConfig ); spaceTimeTable.computeIfAbsent(timePoint, k -> new ArrayList<>()).add(node); } } } } return spaceTimeTable; } /** * 获取CTU的物理配置 * * @param agvId CTU编号 * @return 物理配置 */ private CTUPhysicalConfig getPhysicalConfigForCTU(String agvId) { // 这里应该从配置管理器或数据库获取实际的CTU物理参数 // 暂时返回默认配置 CTUPhysicalConfig config = new CTUPhysicalConfig(); config.setMaxSpeed(2.0); config.setNormalSpeed(1.5); config.setMaxAcceleration(1.0); config.setMaxDeceleration(1.5); config.setTurnTime90(2.0); config.setTurnTime180(4.0); config.setMinSafetyDistance(defaultMinDistance); config.setMinFollowingDistance(2.0); config.setCtuLength(1.2); config.setCtuWidth(0.8); config.setStartupTime(1.0); config.setStopTime(0.5); config.setStandardPointDistance(1.0); return config; } /** * 计算到达时间 * * @param startTime 起始时间 * @param pathIndex 路径索引 * @param codeList 路径代码列表 * @param config 物理配置 * @return 到达时间 */ private long calculateArrivalTime(long startTime, int pathIndex, List codeList, CTUPhysicalConfig config) { if (pathIndex == 0) { return startTime; } long travelTime = 0; for (int i = 1; i <= pathIndex; i++) { PathCode currentCode = codeList.get(i); PathCode previousCode = codeList.get(i - 1); // 计算移动时间 double distance = config.getStandardPointDistance(); double speed = config.getNormalSpeed(); travelTime += (long) ((distance / speed) * 1000); // 转换为毫秒 // 如果有方向变化,增加转向时间 if (!currentCode.getDirection().equals(previousCode.getDirection())) { double turnTime = calculateTurnTime(previousCode.getDirection(), currentCode.getDirection(), config); travelTime += (long) (turnTime * 1000); } // 考虑加速和减速时间 if (i == 1) { travelTime += (long) (config.getStartupTime() * 1000); } } return startTime + travelTime; } /** * 计算离开时间 * * @param arrivalTime 到达时间 * @param pathCode 路径代码 * @param config 物理配置 * @return 离开时间 */ private long calculateDepartureTime(long arrivalTime, PathCode pathCode, CTUPhysicalConfig config) { long stayTime = 500; // 默认停留时间500毫秒 // 根据路径代码类型确定停留时间 if (pathCode.isTargetPoint()) { if ("1".equals(pathCode.getPosType())) { // 取货 stayTime = 3000; // 取货需要3秒 } else if ("2".equals(pathCode.getPosType())) { // 放货 stayTime = 2000; // 放货需要2秒 } } return arrivalTime + stayTime; } /** * 计算转向时间 * * @param fromDirection 起始方向 * @param toDirection 目标方向 * @param config 物理配置 * @return 转向时间(秒) */ private double calculateTurnTime(String fromDirection, String toDirection, CTUPhysicalConfig config) { if (fromDirection.equals(toDirection)) { return 0.0; } // 简化的方向转换计算 // 实际实现中需要根据具体的方向编码计算角度差 int angleDiff = Math.abs(Integer.parseInt(toDirection) - Integer.parseInt(fromDirection)); angleDiff = Math.min(angleDiff, 4 - angleDiff); // 考虑环形 switch (angleDiff) { case 1: return config.getTurnTime90(); case 2: return config.getTurnTime180(); default: return config.getTurnTime90() * angleDiff; } } /** * 检测增强的顶点冲突 * * @param spaceTimeTable 时空表 * @return 顶点冲突列表 */ private List detectEnhancedVertexConflicts(Map> spaceTimeTable) { List conflicts = new ArrayList<>(); for (Map.Entry> entry : spaceTimeTable.entrySet()) { long timePoint = entry.getKey(); List nodes = entry.getValue(); // 按位置分组 Map> positionGroups = new HashMap<>(); for (EnhancedSpaceTimeNode node : nodes) { positionGroups.computeIfAbsent(node.position, k -> new ArrayList<>()).add(node); } // 检查每个位置是否有多个CTU for (Map.Entry> posEntry : positionGroups.entrySet()) { String position = posEntry.getKey(); List ctuNodes = posEntry.getValue(); if (ctuNodes.size() > 1) { // 进一步检查时间重叠 for (int i = 0; i < ctuNodes.size(); i++) { for (int j = i + 1; j < ctuNodes.size(); j++) { EnhancedSpaceTimeNode node1 = ctuNodes.get(i); EnhancedSpaceTimeNode node2 = ctuNodes.get(j); // 检查时间段是否重叠 if (timeRangeOverlap(node1.arrivalTime, node1.departureTime, node2.arrivalTime, node2.departureTime)) { Conflict conflict = new Conflict( "enhanced_vertex", node1.agvId, node2.agvId, (int) (timePoint / 1000), // 转换为秒用于显示 position, position, String.format("CTU %s 和 %s 在时间 %d 占用同一位置 %s(时间重叠)", node1.agvId, node2.agvId, timePoint / 1000, position) ); conflicts.add(conflict); } } } } } } return conflicts; } /** * 检测增强的边冲突 * * @param spaceTimeTable 时空表 * @return 边冲突列表 */ private List detectEnhancedEdgeConflicts(Map> spaceTimeTable) { List conflicts = new ArrayList<>(); if (spaceTimeTable.isEmpty()) { return conflicts; } List sortedTimes = new ArrayList<>(spaceTimeTable.keySet()); Collections.sort(sortedTimes); for (int i = 0; i < sortedTimes.size() - 1; i++) { long currentTime = sortedTimes.get(i); long nextTime = sortedTimes.get(i + 1); // 只检查连续的时间点 if (nextTime - currentTime <= timeResolution * 2) { List currentNodes = spaceTimeTable.get(currentTime); List nextNodes = spaceTimeTable.get(nextTime); conflicts.addAll(detectPositionSwapping(currentNodes, nextNodes, currentTime, nextTime)); } } return conflicts; } /** * 检测位置交换 * * @param currentNodes 当前时间的节点 * @param nextNodes 下一时间的节点 * @param currentTime 当前时间 * @param nextTime 下一时间 * @return 冲突列表 */ private List detectPositionSwapping(List currentNodes, List nextNodes, long currentTime, long nextTime) { List conflicts = new ArrayList<>(); Map currentPositions = new HashMap<>(); Map nextPositions = new HashMap<>(); for (EnhancedSpaceTimeNode node : currentNodes) { currentPositions.put(node.agvId, node.position); } for (EnhancedSpaceTimeNode node : nextNodes) { nextPositions.put(node.agvId, node.position); } for (Map.Entry entry1 : currentPositions.entrySet()) { String ctu1 = entry1.getKey(); String pos1Current = entry1.getValue(); for (Map.Entry entry2 : currentPositions.entrySet()) { String ctu2 = entry2.getKey(); String pos2Current = entry2.getValue(); if (ctu1.compareTo(ctu2) >= 0) { continue; } String pos1Next = nextPositions.get(ctu1); String pos2Next = nextPositions.get(ctu2); if (pos1Next != null && pos2Next != null && pos1Current.equals(pos2Next) && pos2Current.equals(pos1Next)) { Conflict conflict = new Conflict( "enhanced_edge", ctu1, ctu2, (int) (currentTime / 1000), pos1Current, pos2Current, String.format("CTU %s 和 %s 在时间 %d-%d 交换位置 %s<->%s", ctu1, ctu2, currentTime / 1000, nextTime / 1000, pos1Current, pos2Current) ); conflicts.add(conflict); } } } return conflicts; } /** * 检测增强的跟随冲突(考虑CTU物理参数) * * @param spaceTimeTable 时空表 * @return 跟随冲突列表 */ private List detectEnhancedFollowingConflicts(Map> spaceTimeTable) { List conflicts = new ArrayList<>(); for (Map.Entry> entry : spaceTimeTable.entrySet()) { long timePoint = entry.getKey(); List nodes = entry.getValue(); for (int i = 0; i < nodes.size(); i++) { for (int j = i + 1; j < nodes.size(); j++) { EnhancedSpaceTimeNode node1 = nodes.get(i); EnhancedSpaceTimeNode node2 = nodes.get(j); double distance = calculateDistance(node1.coordinates, node2.coordinates); double minSafeDistance = Math.max( node1.physicalConfig.getMinFollowingDistance(), node2.physicalConfig.getMinFollowingDistance() ); if (distance > 0 && distance < minSafeDistance) { Conflict conflict = new Conflict( "enhanced_follow", node1.agvId, node2.agvId, (int) (timePoint / 1000), node1.position, node2.position, String.format("CTU %s 和 %s 在时间 %d 距离过近 (%.2f < %.2f)", node1.agvId, node2.agvId, timePoint / 1000, distance, minSafeDistance) ); conflicts.add(conflict); } } } } return conflicts; } /** * 检测物理尺寸冲突 * * @param spaceTimeTable 时空表 * @return 物理冲突列表 */ private List detectPhysicalSizeConflicts(Map> spaceTimeTable) { List conflicts = new ArrayList<>(); for (Map.Entry> entry : spaceTimeTable.entrySet()) { long timePoint = entry.getKey(); List nodes = entry.getValue(); for (int i = 0; i < nodes.size(); i++) { for (int j = i + 1; j < nodes.size(); j++) { EnhancedSpaceTimeNode node1 = nodes.get(i); EnhancedSpaceTimeNode node2 = nodes.get(j); if (checkPhysicalCollision(node1, node2)) { Conflict conflict = new Conflict( "physical_size", node1.agvId, node2.agvId, (int) (timePoint / 1000), node1.position, node2.position, String.format("CTU %s 和 %s 在时间 %d 发生物理尺寸冲突", node1.agvId, node2.agvId, timePoint / 1000) ); conflicts.add(conflict); } } } } return conflicts; } /** * 检查两个CTU是否发生物理碰撞 * * @param node1 CTU节点1 * @param node2 CTU节点2 * @return 是否碰撞 */ private boolean checkPhysicalCollision(EnhancedSpaceTimeNode node1, EnhancedSpaceTimeNode node2) { double distance = calculateDistance(node1.coordinates, node2.coordinates); // 考虑CTU的物理尺寸 double ctu1Radius = Math.max(node1.physicalConfig.getCtuLength(), node1.physicalConfig.getCtuWidth()) / 2; double ctu2Radius = Math.max(node2.physicalConfig.getCtuLength(), node2.physicalConfig.getCtuWidth()) / 2; double minClearance = ctu1Radius + ctu2Radius + spaceResolution; return distance > 0 && distance < minClearance; } /** * 检查时间范围是否重叠 * * @param start1 开始时间1 * @param end1 结束时间1 * @param start2 开始时间2 * @param end2 结束时间2 * @return 是否重叠 */ private boolean timeRangeOverlap(long start1, long end1, long start2, long end2) { return Math.max(start1, start2) < Math.min(end1, end2); } /** * 计算两个坐标之间的距离 * * @param coord1 坐标1 * @param coord2 坐标2 * @return 距离值 */ private double calculateDistance(int[] coord1, int[] coord2) { if (coord1 == null || coord2 == null || coord1.length != 2 || coord2.length != 2) { return -1; } return Math.sqrt(Math.pow(coord1[0] - coord2[0], 2) + Math.pow(coord1[1] - coord2[1], 2)); } /** * 增强的时空节点内部类 */ private static class EnhancedSpaceTimeNode { final String agvId; final String position; final int[] coordinates; final long timePoint; final long arrivalTime; final long departureTime; final CTUPhysicalConfig physicalConfig; public EnhancedSpaceTimeNode(String agvId, String position, int[] coordinates, long timePoint, long arrivalTime, long departureTime, CTUPhysicalConfig physicalConfig) { this.agvId = agvId; this.position = position; this.coordinates = coordinates; this.timePoint = timePoint; this.arrivalTime = arrivalTime; this.departureTime = departureTime; this.physicalConfig = physicalConfig; } } }