21 changed files with 2105 additions and 179 deletions
File diff suppressed because it is too large
@ -1,8 +0,0 @@ |
|||||
package com.galaxis.rcs.plan.path2; |
|
||||
|
|
||||
import java.util.ArrayList; |
|
||||
import java.util.HashMap; |
|
||||
import java.util.List; |
|
||||
|
|
||||
public class AGVPathPlanner { |
|
||||
} |
|
||||
@ -0,0 +1,220 @@ |
|||||
|
package com.galaxis.rcs.plan.path2; |
||||
|
|
||||
|
import com.galaxis.rcs.common.enums.LCCDirection; |
||||
|
import com.google.common.collect.Maps; |
||||
|
|
||||
|
import java.util.*; |
||||
|
|
||||
|
import static com.galaxis.rcs.plan.path2.PathUtils.*; |
||||
|
|
||||
|
public class AStarPathPlanner { |
||||
|
private static final float ROTATION_COST_PER_DEGREE = 0.01f; |
||||
|
private static final float BLOCKED_COST = 10000f; // 阻塞系数成本
|
||||
|
private static final float WEIGHT_FACTOR = 1.5f; // 权重因子
|
||||
|
|
||||
|
private final NavigationGraph graph; |
||||
|
private final Map<String, Float> nodeWeights = Maps.newConcurrentMap(); |
||||
|
private final Map<String, Float> blockedNodes = Maps.newConcurrentMap(); |
||||
|
|
||||
|
public AStarPathPlanner(NavigationGraph graph) { |
||||
|
this.graph = graph; |
||||
|
} |
||||
|
|
||||
|
// 路径规划状态
|
||||
|
public List<State> findPath(String startId, LCCDirection startDirection, String endId, LCCDirection endDirection) { |
||||
|
Node start = graph.getNode(startId); |
||||
|
Node end = graph.getNode(endId); |
||||
|
if (start == null) { |
||||
|
throw new RuntimeException("Start node not found: " + startId); |
||||
|
} |
||||
|
if (end == null) { |
||||
|
throw new RuntimeException("End node not found: " + endId); |
||||
|
} |
||||
|
|
||||
|
// 使用复合键避免状态重复
|
||||
|
Map<String, State> visited = new HashMap<>(); |
||||
|
PriorityQueue<State> open = new PriorityQueue<>(); |
||||
|
|
||||
|
// 初始状态
|
||||
|
State initialState = new State(start, startDirection, 0, heuristic(start, end), null); |
||||
|
open.add(initialState); |
||||
|
visited.put(stateKey(start.id(), startDirection), initialState); |
||||
|
|
||||
|
while (!open.isEmpty()) { |
||||
|
State current = open.poll(); |
||||
|
|
||||
|
// 到达目标节点且方向匹配
|
||||
|
if (current.node().id().equals(endId) && current.direction() == endDirection) { |
||||
|
return buildPath(current); |
||||
|
} |
||||
|
|
||||
|
// 处理邻边移动
|
||||
|
for (Node neighbor : graph.getNeighbors(current.node())) { |
||||
|
// 检查节点是否被阻塞
|
||||
|
if (isBlocked(neighbor.id())) continue; |
||||
|
|
||||
|
// 计算移动方向
|
||||
|
LCCDirection moveDirection = calculateMoveDirection(current.node(), neighbor); |
||||
|
|
||||
|
// 尝试前进
|
||||
|
if (canMoveForward(current.direction(), moveDirection)) { |
||||
|
float moveCost = calculateMoveCost(current.node(), neighbor, false); |
||||
|
considerState(current, neighbor, current.direction(), |
||||
|
moveCost, open, visited, end); |
||||
|
} |
||||
|
// 尝试后退
|
||||
|
else if (canMoveBackward(current.direction(), moveDirection)) { |
||||
|
float moveCost = calculateMoveCost(current.node(), neighbor, true); |
||||
|
considerState(current, neighbor, current.direction(), |
||||
|
moveCost, open, visited, end); |
||||
|
} |
||||
|
// 需要旋转
|
||||
|
else if (current.node().rotatable()) { |
||||
|
// 计算需要旋转到的方向
|
||||
|
LCCDirection requiredDirection = calculateRequiredDirection(moveDirection); |
||||
|
|
||||
|
// 考虑旋转后移动
|
||||
|
float rotationCost = calculateRotationCost( |
||||
|
current.direction(), requiredDirection, ROTATION_COST_PER_DEGREE |
||||
|
); |
||||
|
float moveCost = calculateMoveCost(current.node(), neighbor, false); |
||||
|
float totalCost = rotationCost + moveCost; |
||||
|
|
||||
|
// 创建旋转状态
|
||||
|
State rotatedState = new State( |
||||
|
current.node(), requiredDirection, |
||||
|
current.g() + rotationCost, |
||||
|
heuristic(current.node(), end), |
||||
|
current |
||||
|
); |
||||
|
|
||||
|
// 考虑旋转后的移动
|
||||
|
considerState(rotatedState, neighbor, requiredDirection, |
||||
|
moveCost, open, visited, end); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 处理原地旋转 - 只考虑目标方向
|
||||
|
if (current.node().rotatable()) { |
||||
|
// 只考虑旋转到目标方向(如果可能)
|
||||
|
if (current.direction() != endDirection) { |
||||
|
float rotationCost = calculateRotationCost( |
||||
|
current.direction(), endDirection, ROTATION_COST_PER_DEGREE |
||||
|
); |
||||
|
considerState(current, current.node(), endDirection, |
||||
|
rotationCost, open, visited, end); |
||||
|
} |
||||
|
|
||||
|
// 另外考虑旋转到移动所需的方向
|
||||
|
for (Node neighbor : graph.getNeighbors(current.node())) { |
||||
|
LCCDirection moveDirection = calculateMoveDirection(current.node(), neighbor); |
||||
|
LCCDirection requiredDirection = calculateRequiredDirection(moveDirection); |
||||
|
|
||||
|
if (requiredDirection != current.direction()) { |
||||
|
float rotationCost = calculateRotationCost( |
||||
|
current.direction(), requiredDirection, ROTATION_COST_PER_DEGREE |
||||
|
); |
||||
|
considerState(current, current.node(), requiredDirection, |
||||
|
rotationCost, open, visited, end); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// for (LCCDirection rotation : LCCDirection.values()) {
|
||||
|
// if (rotation == current.direction()) continue;
|
||||
|
//
|
||||
|
// float rotationCost = calculateRotationCost(
|
||||
|
// current.direction(), rotation, ROTATION_COST_PER_DEGREE
|
||||
|
// );
|
||||
|
// considerState(current, current.node(), rotation,
|
||||
|
// rotationCost, open, visited, end);
|
||||
|
// }
|
||||
|
} |
||||
|
} |
||||
|
return Collections.emptyList(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 考虑新的状态并更新开放列表和访问记录 |
||||
|
* |
||||
|
* @param current 当前状态 |
||||
|
* @param nextNode 下一个节点 |
||||
|
* @param nextDirection 下一个方向 |
||||
|
* @param cost 移动成本 |
||||
|
* @param open 开放列表 |
||||
|
* @param visited 访问记录 |
||||
|
* @param end 目标节点 |
||||
|
*/ |
||||
|
private void considerState(State current, Node nextNode, LCCDirection nextDirection, |
||||
|
float cost, PriorityQueue<State> open, |
||||
|
Map<String, State> visited, Node end) { |
||||
|
String key = stateKey(nextNode.id(), nextDirection); |
||||
|
float newG = current.g() + cost; |
||||
|
|
||||
|
if (!visited.containsKey(key) || visited.get(key).g() > newG) { |
||||
|
float h = heuristic(nextNode, end); |
||||
|
State newState = new State(nextNode, nextDirection, newG, h, current); |
||||
|
open.add(newState); |
||||
|
visited.put(key, newState); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private List<State> buildPath(State state) { |
||||
|
LinkedList<State> path = new LinkedList<>(); |
||||
|
while (state != null) { |
||||
|
path.addFirst(state); |
||||
|
state = state.parent(); |
||||
|
} |
||||
|
return path; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 启发式函数,计算两个节点之间的距离 |
||||
|
*/ |
||||
|
private float heuristic(Node a, Node b) { |
||||
|
return graph.distance(a, b); |
||||
|
// 使用曼哈顿距离??
|
||||
|
// return Math.abs(a.x() - b.x()) + Math.abs(a.z() - b.z());
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 生成状态的唯一键 |
||||
|
*/ |
||||
|
private String stateKey(String nodeId, LCCDirection direction) { |
||||
|
return nodeId + "|" + direction; |
||||
|
} |
||||
|
|
||||
|
public void setNodeWeight(String nodeId, float weight) { |
||||
|
nodeWeights.put(nodeId, weight); |
||||
|
} |
||||
|
|
||||
|
public void setBlocked(String nodeId, float blockedFactor) { |
||||
|
blockedNodes.put(nodeId, blockedFactor); |
||||
|
} |
||||
|
|
||||
|
private float calculateMoveCost(Node from, Node to, boolean isBackward) { |
||||
|
float baseCost = graph.distance(from, to); |
||||
|
float weight = nodeWeights.getOrDefault(to.id(), 1.0f); |
||||
|
float blockedFactor = blockedNodes.getOrDefault(to.id(), 0f); |
||||
|
|
||||
|
// 后退移动增加额外成本??
|
||||
|
// return baseCost * weight * (1 + blockedFactor) * (isBackward ? 1.2f : 1.0f);
|
||||
|
return baseCost * weight * (1 + blockedFactor); |
||||
|
} |
||||
|
|
||||
|
private boolean isBlocked(String nodeId) { |
||||
|
return blockedNodes.containsKey(nodeId) && blockedNodes.get(nodeId) > 0.8f; |
||||
|
} |
||||
|
|
||||
|
private boolean canMoveForward(LCCDirection currentDir, LCCDirection moveDir) { |
||||
|
return currentDir == moveDir; |
||||
|
} |
||||
|
|
||||
|
private boolean canMoveBackward(LCCDirection currentDir, LCCDirection moveDir) { |
||||
|
return currentDir == getOppositeDirection(moveDir); |
||||
|
} |
||||
|
|
||||
|
private LCCDirection calculateRequiredDirection(LCCDirection moveDirection) { |
||||
|
// 侧插式AGV只能前进或后退,不能横移
|
||||
|
return moveDirection; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,168 @@ |
|||||
|
package com.galaxis.rcs.plan.path2; |
||||
|
|
||||
|
import com.galaxis.rcs.common.enums.LCCDirection; |
||||
|
|
||||
|
import java.util.*; |
||||
|
|
||||
|
public class PathUtils { |
||||
|
/** |
||||
|
* 计算移动方向 |
||||
|
*/ |
||||
|
public static LCCDirection calculateMoveDirection(Node from, Node to) { |
||||
|
float dx = to.x() - from.x(); |
||||
|
float dz = to.z() - from.z(); // 注意:Z轴向下增长
|
||||
|
|
||||
|
// 考虑左手坐标系:X向右,Z向下
|
||||
|
if (Math.abs(dx) > Math.abs(dz)) { |
||||
|
return dx > 0.5 ? LCCDirection.RIGHT : LCCDirection.LEFT; |
||||
|
} else { |
||||
|
// 注意:Z向下增长,所以Z值增加表示向下移动
|
||||
|
return dz > 0.5 ? LCCDirection.DOWN : LCCDirection.UP; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 检查方向是否有效(侧插式专用) |
||||
|
*/ |
||||
|
public static boolean isValidForSideLoader(Node node, int bay, LCCDirection direction) { |
||||
|
Optional<StoreLink> link = node.storeLinks().stream() |
||||
|
.filter(l -> l.bay() == bay) |
||||
|
.findFirst(); |
||||
|
|
||||
|
if (link.isEmpty()) return false; |
||||
|
|
||||
|
LCCDirection requiredDirection = convertForSideLoader(link.get().direction()); |
||||
|
return direction == requiredDirection; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 转换货位方向到AGV所需方向(侧插式专用) |
||||
|
*/ |
||||
|
public static LCCDirection convertForSideLoader(LCCDirection storeDirection) { |
||||
|
/* |
||||
|
* 侧插式AGV方向规则: |
||||
|
* - 货位在上方 → 车头向右(货叉朝左) |
||||
|
* - 货位在下方 → 车头向左(货叉朝左) |
||||
|
* - 货位在左方 → 车头向上(货叉朝左) |
||||
|
* - 货位在右方 → 车头向下(货叉朝左) |
||||
|
*/ |
||||
|
return switch (storeDirection) { |
||||
|
case UP -> LCCDirection.RIGHT; |
||||
|
case DOWN -> LCCDirection.LEFT; |
||||
|
case LEFT -> LCCDirection.UP; |
||||
|
case RIGHT -> LCCDirection.DOWN; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取最近的旋转点 |
||||
|
*/ |
||||
|
public static Node findNearestRotationNode(NavigationGraph graph, Node from, LCCDirection currentDir, |
||||
|
LCCDirection requiredDir) { |
||||
|
// 如果当前方向已经匹配,不需要旋转
|
||||
|
if (currentDir == requiredDir) return from; |
||||
|
|
||||
|
// 使用Dijkstra算法查找最近的旋转点
|
||||
|
Map<Node, Float> distances = new HashMap<>(); |
||||
|
Map<Node, Node> predecessors = new HashMap<>(); |
||||
|
PriorityQueue<Node> queue = new PriorityQueue<>(Comparator.comparingDouble(distances::get)); |
||||
|
|
||||
|
distances.put(from, 0f); |
||||
|
queue.add(from); |
||||
|
|
||||
|
Node result = null; |
||||
|
|
||||
|
while (!queue.isEmpty()) { |
||||
|
Node current = queue.poll(); |
||||
|
|
||||
|
// 找到可旋转点
|
||||
|
if (current.rotatable()) { |
||||
|
result = current; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// 探索邻居
|
||||
|
for (Node neighbor : graph.getNeighbors(current)) { |
||||
|
float newDist = distances.get(current) + graph.distance(current, neighbor); |
||||
|
|
||||
|
if (newDist < distances.getOrDefault(neighbor, Float.MAX_VALUE)) { |
||||
|
distances.put(neighbor, newDist); |
||||
|
predecessors.put(neighbor, current); |
||||
|
queue.add(neighbor); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取相反方向 |
||||
|
*/ |
||||
|
public static LCCDirection getOppositeDirection(LCCDirection dir) { |
||||
|
return switch (dir) { |
||||
|
case UP -> LCCDirection.DOWN; |
||||
|
case DOWN -> LCCDirection.UP; |
||||
|
case LEFT -> LCCDirection.RIGHT; |
||||
|
case RIGHT -> LCCDirection.LEFT; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 计算旋转代价 |
||||
|
*/ |
||||
|
public static float calculateRotationCost(LCCDirection from, LCCDirection to, float ROTATION_COST_PER_DEGREE) { |
||||
|
float angle1 = getRequiredDirection(from); |
||||
|
float angle2 = getRequiredDirection(to); |
||||
|
float diff = Math.abs(angle1 - angle2); |
||||
|
diff = Math.min(diff, 360 - diff); |
||||
|
// return diff * ROTATION_COST_PER_DEGREE;
|
||||
|
return 0.1f; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取所需方向的角度 |
||||
|
*/ |
||||
|
public static float getRequiredDirection(LCCDirection direction) { |
||||
|
return switch (direction) { |
||||
|
case UP -> 90f; |
||||
|
case DOWN -> 270f; |
||||
|
case LEFT -> 180f; |
||||
|
case RIGHT -> 0f; |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 转换货位方向到AGV所需方向 |
||||
|
*/ |
||||
|
public static LCCDirection convertStoreDirection(LCCDirection storeDirection) { |
||||
|
// 转换规则: 货位在路径点的方位 -> AGV所需方向
|
||||
|
return switch (storeDirection) { |
||||
|
case UP -> LCCDirection.RIGHT; // 货位在上方 → 车头向右
|
||||
|
case DOWN -> LCCDirection.LEFT; // 货位在下方 → 车头向左
|
||||
|
case LEFT -> LCCDirection.UP; // 货位在左方 → 车头向上
|
||||
|
case RIGHT -> LCCDirection.DOWN; // 货位在右方 → 车头向下
|
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将角度转换为 LCCDirection |
||||
|
*/ |
||||
|
public static LCCDirection convertAngleToDirection(float angle) { |
||||
|
// 标准化角度
|
||||
|
angle = ((angle % 360f) + 360f) % 360f; |
||||
|
|
||||
|
if (angle >= 315f || angle < 45f) return LCCDirection.RIGHT; |
||||
|
if (angle >= 45f && angle < 135f) return LCCDirection.UP; |
||||
|
if (angle >= 135f && angle < 225f) return LCCDirection.LEFT; |
||||
|
return LCCDirection.DOWN; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 计算方向角度差 |
||||
|
*/ |
||||
|
public static float angleDifference(float angle1, float angle2) { |
||||
|
float diff = Math.abs(angle1 - angle2) % 360; |
||||
|
return diff > 180 ? 360 - diff : diff; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,142 @@ |
|||||
|
package com.galaxis.rcs.plan.path2; |
||||
|
|
||||
|
import com.galaxis.rcs.common.enums.LCCDirection; |
||||
|
import com.galaxis.rcs.plan.PlanTaskSequence; |
||||
|
import com.galaxis.rcs.plan.task.CarryTask; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
import static com.galaxis.rcs.plan.path2.PathUtils.convertStoreDirection; |
||||
|
import static com.galaxis.rcs.plan.path2.PathUtils.getRequiredDirection; |
||||
|
|
||||
|
public class PtrPathPlanner { |
||||
|
private final NavigationGraph graph; |
||||
|
private final AStarPathPlanner astar; |
||||
|
|
||||
|
public PtrPathPlanner(NavigationGraph graph) { |
||||
|
this.graph = graph; |
||||
|
this.astar = new AStarPathPlanner(graph); |
||||
|
} |
||||
|
|
||||
|
public void planCarryTask(PlanTaskSequence plan, String startNodeId, LCCDirection startDirection, CarryTask task) { |
||||
|
// 取货点
|
||||
|
String loadRackId = task.from().rackId(); |
||||
|
int pickupBay = task.from().bay(); |
||||
|
NodeDirection loadNodeDirection = findNodeForStore(loadRackId, pickupBay); |
||||
|
if (loadNodeDirection == null) { |
||||
|
throw new RuntimeException("Pickup node not found for rackId=" + loadRackId + ", bay=" + pickupBay); |
||||
|
} |
||||
|
|
||||
|
// 放货点
|
||||
|
String unloadRackId = task.to().rackId(); |
||||
|
int unloadBay = task.to().bay(); |
||||
|
NodeDirection unloadNodeDirection = findNodeForStore(unloadRackId, unloadBay); |
||||
|
if (unloadNodeDirection == null) { |
||||
|
throw new RuntimeException("Drop node not found for rackId=" + unloadRackId + ", bay=" + unloadBay); |
||||
|
} |
||||
|
|
||||
|
// 规划到取货点路径
|
||||
|
List<State> toLoadPath = astar.findPath(startNodeId, startDirection, loadNodeDirection.node().id(), loadNodeDirection.direction()); |
||||
|
|
||||
|
// 检查方向是否匹配,如果不匹配则插入旋转点
|
||||
|
if (!toLoadPath.isEmpty()) { |
||||
|
State lastState = toLoadPath.get(toLoadPath.size() - 1); |
||||
|
if (lastState.direction() != loadNodeDirection.direction()) { |
||||
|
Node rotationNode = PathUtils.findNearestRotationNode( |
||||
|
graph, lastState.node(), lastState.direction(), loadNodeDirection.direction() |
||||
|
); |
||||
|
|
||||
|
if (rotationNode != null) { |
||||
|
// 插入旋转路径
|
||||
|
List<State> toRotation = astar.findPath( |
||||
|
lastState.node().id(), lastState.direction(), |
||||
|
rotationNode.id(), loadNodeDirection.direction() |
||||
|
); |
||||
|
toLoadPath.addAll(toRotation); |
||||
|
|
||||
|
// 从旋转点到目标点
|
||||
|
List<State> fromRotation = astar.findPath( |
||||
|
rotationNode.id(), loadNodeDirection.direction(), |
||||
|
loadNodeDirection.node().id(), loadNodeDirection.direction() |
||||
|
); |
||||
|
toLoadPath.addAll(fromRotation); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 规划到放货点路径
|
||||
|
List<State> toUnloadPath = astar.findPath(loadNodeDirection.node().id(), loadNodeDirection.direction(), unloadNodeDirection.node().id(), unloadNodeDirection.direction()); |
||||
|
|
||||
|
// 生成指令序列
|
||||
|
generateMoves(plan, toLoadPath); |
||||
|
plan.addLoad(task.lpn(), loadRackId, pickupBay, task.from().level(), task.from().cell()); |
||||
|
|
||||
|
generateMoves(plan, toUnloadPath); |
||||
|
plan.addUnload(unloadRackId, task.to().level(), task.to().bay(), task.to().cell()); |
||||
|
|
||||
|
plan.addFinish(); |
||||
|
} |
||||
|
|
||||
|
private NodeDirection findNodeForStore(String storeId, int bay) { |
||||
|
List<Node> nodes = this.graph.getNodesForStore(storeId); |
||||
|
for (Node node : nodes) { |
||||
|
for (StoreLink link : node.storeLinks()) { |
||||
|
if (link.storeId().equals(storeId) && link.bay() == bay) { |
||||
|
LCCDirection agvDirection = convertStoreDirection(link.direction()); |
||||
|
return new NodeDirection(node, agvDirection); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取指定节点和货架的所需朝向 |
||||
|
*/ |
||||
|
private LCCDirection getRequiredHeading(Node node, int bay) { |
||||
|
return node.storeLinks().stream() |
||||
|
.filter(link -> link.bay() == bay) |
||||
|
.findFirst() |
||||
|
.map(link -> link.direction()) |
||||
|
.orElseThrow(() -> new RuntimeException("Not found storeLink in id=" + node.id() + ", bay=" + bay)); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据A*状态,生成移动指令序列 |
||||
|
*/ |
||||
|
private void generateMoves(PlanTaskSequence sequence, List<State> path) { |
||||
|
if (path.isEmpty()) return; |
||||
|
|
||||
|
// 第一个状态是起点,跳过
|
||||
|
State prevState = path.get(0); |
||||
|
|
||||
|
for (int i = 1; i < path.size(); i++) { |
||||
|
State current = path.get(i); |
||||
|
|
||||
|
// 如果是旋转动作
|
||||
|
if (current.node().equals(prevState.node())) { |
||||
|
float angle = PathUtils.getRequiredDirection(current.direction()); |
||||
|
sequence.addRotationTo(angle); |
||||
|
} |
||||
|
// 移动动作 - 检查是否需要后退
|
||||
|
else { |
||||
|
// 检查移动方向
|
||||
|
LCCDirection moveDir = PathUtils.calculateMoveDirection(prevState.node(), current.node()); |
||||
|
|
||||
|
// 如果移动方向与车头方向相反,需要后退
|
||||
|
boolean isBackward = (current.direction() == PathUtils.getOppositeDirection(moveDir)); |
||||
|
|
||||
|
if (isBackward) { |
||||
|
sequence.addMoveBackward(current.node().id()); |
||||
|
} else { |
||||
|
sequence.addMoveTo(current.node().id()); |
||||
|
} |
||||
|
} |
||||
|
prevState = current; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 辅助记录类
|
||||
|
private record NodeDirection(Node node, LCCDirection direction) { |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
package com.galaxis.rcs.plan.path2; |
||||
|
|
||||
|
import com.galaxis.rcs.common.enums.LCCDirection; |
||||
|
|
||||
|
/** |
||||
|
* A* 路径规划状态 |
||||
|
*/ |
||||
|
public record State(Node node, |
||||
|
LCCDirection direction, |
||||
|
float g, |
||||
|
float h, |
||||
|
State parent) |
||||
|
implements Comparable<State> { |
||||
|
|
||||
|
@Override |
||||
|
public int compareTo(State other) { |
||||
|
return Float.compare(g + h, other.g + other.h); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
package com.galaxis.rcs.plan.task; |
||||
|
|
||||
|
import com.galaxis.rcs.common.entity.StoreLocation; |
||||
|
|
||||
|
/** |
||||
|
* 搬运任务 |
||||
|
* <pre> |
||||
|
* { |
||||
|
* type: 'carry', // 任务类型
|
||||
|
* agv: 'cl2', // 车号
|
||||
|
* lpn: 'pallet1124', // 托盘ID,用于校验,规划器不用管
|
||||
|
* priority: 1, // 优先级,用于排车,规划器不用管
|
||||
|
* // 搬运源货位
|
||||
|
* from: { |
||||
|
* item: 'rack1',// 货架编号
|
||||
|
* bay: 0, // 货架列
|
||||
|
* level: 1, // 货架层(用于机械臂,规划器不用管)
|
||||
|
* cell: 0 // 货架格(用于机械臂,规划器不用管)
|
||||
|
* }, |
||||
|
* // 目标货位
|
||||
|
* to: { |
||||
|
* item: '54' // 地堆货位号
|
||||
|
* } |
||||
|
* } |
||||
|
* </pre> |
||||
|
*/ |
||||
|
public record CarryTask( |
||||
|
String agv, |
||||
|
String lpn, |
||||
|
int priority, |
||||
|
StoreLocation from, |
||||
|
StoreLocation to |
||||
|
) { |
||||
|
} |
||||
Loading…
Reference in new issue