package com.zy.acs.manager.core.service.astart; import com.zy.acs.common.utils.RedisSupport; import com.zy.acs.framework.common.Cools; import com.zy.acs.manager.common.utils.MapDataUtils; import com.zy.acs.manager.core.service.LaneService; import com.zy.acs.manager.core.service.astart.domain.AStarNavigateNode; import com.zy.acs.manager.core.service.astart.domain.DynamicNode; import com.zy.acs.manager.core.utils.RouteGenerator; import com.zy.acs.manager.manager.entity.Segment; import com.zy.acs.manager.manager.service.JamService; import com.zy.acs.manager.system.service.ConfigService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; /** * Created by vincent on 6/12/2024 */ @Slf4j @Service public class AStarNavigateService { private final RedisSupport redis = RedisSupport.defaultRedisSupport; public static final boolean OPEN_TURN_COST_WEIGHT = Boolean.TRUE; public static final int WEIGHT_CALC_FACTOR = 1; @Autowired private MapDataDispatcher mapDataDispatcher; @Autowired private JamService jamService; @Autowired private LaneService laneService; @Autowired private ConfigService configService; public synchronized AStarNavigateNode execute(String agvNo, AStarNavigateNode start, AStarNavigateNode end , Boolean lock, List blackList, Segment segment) { if (start.getX() == end.getX() && start.getY() == end.getY()) { return end; } Integer maxAgvCountInLane = configService.getVal("maxAgvCountInLane", Integer.class); PriorityQueue openQueue = new PriorityQueue<>(); Set existNodes = new HashSet<>(); Map bestGMap = new HashMap<>(); start.setG(0); start.setH(calcNodeCost(start, end)); start.setF(start.getG() + start.getH()); String startKey = start.getX() + "_" + start.getY(); bestGMap.put(startKey, start.getG()); openQueue.add(start); existNodes.add(start); int[][] mapMatrix = mapDataDispatcher.getMapMatrix(null, null); String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(null); DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(null); String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(null); long getNeighborNodesTime = 0; int getNeighborNodesCount = 0; while (!openQueue.isEmpty()) { // 取优先队列顶部元素并且把这个元素从Open表中删除,取F值最小的节点 AStarNavigateNode currentNode = openQueue.poll(); // 终点 if (currentNode.getX() == end.getX() && currentNode.getY() == end.getY()) { // System.out.println("getNeighborNodes spend time: " + getNeighborNodesTime +", count: " + getNeighborNodesCount); return currentNode; } long currentTime = System.currentTimeMillis(); List neighbourNodes = this.getNeighborNodes(currentNode, mapMatrix, existNodes); getNeighborNodesTime += System.currentTimeMillis() - currentTime; getNeighborNodesCount ++; for (AStarNavigateNode node : neighbourNodes) { node.setCodeData(codeMatrix[node.getX()][node.getY()]); boolean isEndNode = node.getX() == end.getX() && node.getY() == end.getY(); int weight = 0; if (!Cools.isEmpty(blackList) && blackList.contains(node.getCodeData())) { continue; } // 特殊情况,当blackList有且只有一个元素且为startNode时 // 说明blackList已经知道当前导航起始点和目标点为相邻节点 // 但是当前blackList的任务是不让系统走相邻的最短路径,所以才会有下面的判断和continue if (blackList.size() == 1 && blackList.get(0).equals(start.getCodeData())) { if (isEndNode && currentNode.getCodeData().equals(start.getCodeData())) { continue; } } // 节点被占用 DynamicNode dynamicNode = dynamicMatrix[node.getX()][node.getY()]; String vehicle = dynamicNode.getVehicle(); assert !vehicle.equals(DynamicNodeType.BLOCK.val); if (!vehicle.equals(DynamicNodeType.ACCESS.val)) { if (!vehicle.equals(agvNo)) { if (lock) { continue; } // 如果存在车辆,则增加权重 2 或者 3,因为拐点会增加权重 1 // vehicle已经为当前segment做过了避让,且避让任务已完成,则权重值增加 if (null != segment) { if (!Cools.isEmpty(jamService.getJamFromSegmentByAvo(segment, vehicle))) { weight += (WEIGHT_CALC_FACTOR * 3); } else { weight += (WEIGHT_CALC_FACTOR * 2); } } } } // 避障波 String waveNode = waveMatrix[node.getX()][node.getY()]; assert !waveNode.equals(WaveNodeType.DISABLE.val); if (!waveNode.equals(WaveNodeType.ENABLE.val)) { List waveNodeList = MapDataUtils.parseWaveNode(waveNode); List otherWaveList = MapDataUtils.hasOtherWave(waveNodeList, agvNo); if (!Cools.isEmpty(otherWaveList)) { if (lock) { continue; } } } // 单巷道车辆容载数量 List laneCodeIdxList = laneService.getLaneCodeIdxList(node.getCodeData()); if (!Cools.isEmpty(laneCodeIdxList)) { Set lanVehicleSet = new HashSet<>(); for (int[] codeMatrixIdx : laneCodeIdxList) { DynamicNode laneDynamicNode = dynamicMatrix[codeMatrixIdx[0]][codeMatrixIdx[1]]; String laneVehicle = laneDynamicNode.getVehicle(); assert !laneVehicle.equals(DynamicNodeType.BLOCK.val); if (!laneVehicle.equals(DynamicNodeType.ACCESS.val)) { if (!laneVehicle.equals(agvNo)) { lanVehicleSet.add(laneVehicle); } } } if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { continue; } } if (OPEN_TURN_COST_WEIGHT) { if (this.isTurning(currentNode, node)) { weight += WEIGHT_CALC_FACTOR; node.setTurnCount(currentNode.getTurnCount() + 1); } else { // 方向没变 node.setTurnCount(currentNode.getTurnCount()); } } //进行计算对 G, F, H 等值 node.setWeight(weight); node.setLastDistance(calcNodeCost(currentNode, node)); node.initNode(currentNode, end); node.setH(calcNodeCost(node, end)); node.setF(node.getG() + node.getH()); String key = node.getX() + "_" + node.getY(); Integer recordedG = bestGMap.get(key); if (recordedG == null || node.getG() <= recordedG) { bestGMap.put(key, node.getG()); openQueue.add(node); } // openQueue.add(node); // existNodes.add(node); } } // System.out.println("getNeighborNodes spend time: " + getNeighborNodesTime +", count: " + getNeighborNodesCount); return null; } // right left up down private final static int[][] DIRECTIONS = {{0,1},{0,-1},{-1,0},{1,0}}; // 获取四周节点 private List getNeighborNodes(AStarNavigateNode currentNode, int[][] mapMatrix, Set existNodes) { int x = currentNode.getX(); int y = currentNode.getY(); AStarNavigateNode parent = currentNode.getParent(); // List neighbourNodes = new CopyOnWriteArrayList<>(); List neighbourNodes = new ArrayList<>(); List possibleNodes = new ArrayList<>(); for (int[] d: DIRECTIONS) { int nx = x + d[0]; int ny = y + d[1]; // 如果父节点不为空,并且 (nx,ny) 等于父节点坐标,则跳过 if (parent != null && nx == parent.getX() && ny == parent.getY()) { continue; } possibleNodes.add(new AStarNavigateNode(nx, ny)); } // possibleNodes.parallelStream() // .map(extendNode -> extendNeighborNodes(currentNode, extendNode, mapMatrix, existNodes, null, null)) // .filter(Objects::nonNull) // .forEach(neighbourNodes::add); for (AStarNavigateNode pn : possibleNodes) { AStarNavigateNode next = extendNeighborNodes(currentNode, pn, mapMatrix, existNodes, null, null); if (next != null) { neighbourNodes.add(next); } } return neighbourNodes; } private AStarNavigateNode extendNeighborNodes(AStarNavigateNode currentNode, AStarNavigateNode extendNode, int[][] mapMatrix, Set existNodes, Integer dx, Integer dy) { AStarNavigateNode nextNode; if (null == dx || null == dy) { dx = extendNode.getX() - currentNode.getX(); dy = extendNode.getY() - currentNode.getY(); nextNode = extendNode; } else { nextNode = new AStarNavigateNode(extendNode.getX() + dx, extendNode.getY() + dy); } int x = nextNode.getX(); int y = nextNode.getY(); // 数组越界 if (x < 0 || x >= mapMatrix.length || y < 0 || y >= mapMatrix[0].length) { return null; } if (mapMatrix[x][y] == MapNodeType.DISABLE.val) { return extendNeighborNodes(currentNode, nextNode, mapMatrix, existNodes, dx, dy); } assert mapMatrix[x][y] == MapNodeType.ENABLE.val; // if (existNodes.contains(nextNode)) { // return null; // } // 判断通过性 String routeCdaKey = RouteGenerator.generateRouteCdaKey(new int[]{currentNode.getX(), currentNode.getY()}, new int[]{nextNode.getX(), nextNode.getY()}); if (!mapDataDispatcher.validRouteCdaKey(routeCdaKey)) { return null; } return nextNode; } //------------------A*启发函数------------------// //计算通过现在的结点的位置和最终结点的位置计算H值(曼哈顿法:坐标分别取差值相加) private int calcNodeCost(AStarNavigateNode node1, AStarNavigateNode node2) { return Math.abs(node2.getX() - node1.getX()) + Math.abs(node2.getY() - node1.getY()); } // 转弯判断:只允许“垂直或水平”运动 private boolean isTurning(AStarNavigateNode currNode, AStarNavigateNode nextNode) { // 第一个点 if (currNode.getParent() == null) { return false; } AStarNavigateNode parent = currNode.getParent(); // 如果下一点(nextNode)与 parent 在同一行或同一列 => 没有转弯 // 注意,这实际上等同于“(curr->next) 的方向 == (parent->curr) 的方向”。 boolean sameRowOrCol = (nextNode.getX() == parent.getX()) || (nextNode.getY() == parent.getY()); return !sameRowOrCol; } // 转弯判断:如果这两个向量相同(例如都等于 (0,1)),说明方向相同;否则说明转弯。 // private boolean isTurning(AStarNavigateNode currNode, AStarNavigateNode nextNode) { // // 如果 currNode 没有父节点,说明是起点,不算转弯 // if (currNode.getParent() == null) { // return false; // } // // 取出坐标 // AStarNavigateNode parent = currNode.getParent(); // int px = currNode.getX() - parent.getX(); // parent -> curr 的x偏移 // int py = currNode.getY() - parent.getY(); // parent -> curr 的y偏移 // // int nx = nextNode.getX() - currNode.getX(); // curr -> next 的x偏移 // int ny = nextNode.getY() - currNode.getY(); // curr -> next 的y偏移 // // // 如果 (px, py) 与 (nx, ny) 不一样,就说明转弯 // return (px != nx) || (py != ny); // } }