Browse Source

MOVE_BACKWARD 支持

master
修宁 6 months ago
parent
commit
fd8de9e205
  1. 1
      servo/src/main/java/com/galaxis/rcs/common/enums/PlanTaskType.java
  2. 10
      servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java
  3. 106
      servo/src/main/java/com/galaxis/rcs/plan/path2/AStarPathPlanner.java
  4. 45
      servo/src/main/java/com/galaxis/rcs/plan/path2/PathUtils.java
  5. 40
      servo/src/main/java/com/galaxis/rcs/plan/path2/PtrPathPlanner.java

1
servo/src/main/java/com/galaxis/rcs/common/enums/PlanTaskType.java

@ -5,6 +5,7 @@ package com.galaxis.rcs.common.enums;
*/ */
public enum PlanTaskType { public enum PlanTaskType {
MOVE, // 移动任务 MOVE, // 移动任务
MOVE_BACKWARD, // 移动任务
ROTATION, // 旋转任务 ROTATION, // 旋转任务
LOAD, // 取货任务 LOAD, // 取货任务
UNLOAD, // 装载任务 UNLOAD, // 装载任务

10
servo/src/main/java/com/galaxis/rcs/plan/PlanTaskSequence.java

@ -63,6 +63,13 @@ public class PlanTaskSequence {
return task; return task;
} }
public RcsTaskPlan addMoveBackward(String wayPointId) {
RcsTaskPlan task = this.createTaskPlanEntity(PlanTaskType.MOVE_BACKWARD.toString());
task.setTargetId(wayPointId);
this.lastWayPointId = wayPointId;
return task;
}
// 添加旋转动作 // 添加旋转动作
public RcsTaskPlan addRotationTo(float rotationAngle) { public RcsTaskPlan addRotationTo(float rotationAngle) {
RcsTaskPlan task = this.createTaskPlanEntity(PlanTaskType.ROTATION.toString()); RcsTaskPlan task = this.createTaskPlanEntity(PlanTaskType.ROTATION.toString());
@ -116,6 +123,9 @@ public class PlanTaskSequence {
case MOVE: case MOVE:
taskStr = "MOVE " + task.getTargetId(); taskStr = "MOVE " + task.getTargetId();
break; break;
case MOVE_BACKWARD:
taskStr = "MOVE_BACKWARD " + task.getTargetId();
break;
case LOAD: case LOAD:
taskStr = "LOAD " + task.getTargetId() + "_" + task.getTargetBay() + "_" + task.getTargetLevel() + "_" + task.getTargetCell(); taskStr = "LOAD " + task.getTargetId() + "_" + task.getTargetBay() + "_" + task.getTargetLevel() + "_" + task.getTargetCell();
break; break;

106
servo/src/main/java/com/galaxis/rcs/plan/path2/AStarPathPlanner.java

@ -8,7 +8,7 @@ import java.util.*;
import static com.galaxis.rcs.plan.path2.PathUtils.*; import static com.galaxis.rcs.plan.path2.PathUtils.*;
public class AStarPathPlanner { public class AStarPathPlanner {
private static final float ROTATION_COST_PER_DEGREE = 0.1f; private static final float ROTATION_COST_PER_DEGREE = 0.01f;
private static final float BLOCKED_COST = 10000f; // 阻塞系数成本 private static final float BLOCKED_COST = 10000f; // 阻塞系数成本
private static final float WEIGHT_FACTOR = 1.5f; // 权重因子 private static final float WEIGHT_FACTOR = 1.5f; // 权重因子
@ -44,8 +44,7 @@ public class AStarPathPlanner {
State current = open.poll(); State current = open.poll();
// 到达目标节点且方向匹配 // 到达目标节点且方向匹配
if (current.node().id().equals(endId) && if (current.node().id().equals(endId) && current.direction() == endDirection) {
current.direction() == endDirection) {
return buildPath(current); return buildPath(current);
} }
@ -54,56 +53,86 @@ public class AStarPathPlanner {
// 检查节点是否被阻塞 // 检查节点是否被阻塞
if (isBlocked(neighbor.id())) continue; if (isBlocked(neighbor.id())) continue;
// 计算移动方向
LCCDirection moveDirection = calculateMoveDirection(current.node(), neighbor); LCCDirection moveDirection = calculateMoveDirection(current.node(), neighbor);
// 前进所需方向 // 前进所需方向
LCCDirection forwardHeading = moveDirection; LCCDirection forwardHeading = moveDirection;
// 后退所需方向 // 后退所需方向
LCCDirection backwardHeading = getOppositeDirection(moveDirection); LCCDirection backwardHeading = getOppositeDirection(moveDirection);
// 尝试前进
// 尝试前进 if (canMoveForward(current.direction(), moveDirection)) {
if (current.direction() == forwardHeading) { float moveCost = calculateMoveCost(current.node(), neighbor, false);
float moveCost = calculateMoveCost(current.node(), neighbor); considerState(current, neighbor, current.direction(),
considerState(current, neighbor, forwardHeading,
moveCost, open, visited, end); moveCost, open, visited, end);
} else if (current.node().rotatable()) {
float rotationCost = calculateRotationCost(
current.direction(), forwardHeading, ROTATION_COST_PER_DEGREE
);
float moveCost = calculateMoveCost(current.node(), neighbor);
float totalCost = rotationCost + moveCost;
considerState(current, neighbor, forwardHeading,
totalCost, open, visited, end);
} }
// 尝试后退 // 尝试后退
if (current.direction() == backwardHeading) { else if (canMoveBackward(current.direction(), moveDirection)) {
float moveCost = calculateMoveCost(current.node(), neighbor); float moveCost = calculateMoveCost(current.node(), neighbor, true);
considerState(current, neighbor, backwardHeading, considerState(current, neighbor, current.direction(),
moveCost, open, visited, end); moveCost, open, visited, end);
} else if (current.node().rotatable()) { }
// 需要旋转
else if (current.node().rotatable()) {
// 计算需要旋转到的方向
LCCDirection requiredDirection = calculateRequiredDirection(moveDirection);
// 考虑旋转后移动
float rotationCost = calculateRotationCost( float rotationCost = calculateRotationCost(
current.direction(), backwardHeading, ROTATION_COST_PER_DEGREE current.direction(), requiredDirection, ROTATION_COST_PER_DEGREE
); );
float moveCost = calculateMoveCost(current.node(), neighbor); float moveCost = calculateMoveCost(current.node(), neighbor, false);
float totalCost = rotationCost + moveCost; float totalCost = rotationCost + moveCost;
considerState(current, neighbor, backwardHeading,
totalCost, open, visited, end); // 创建旋转状态
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.node().rotatable()) {
for (LCCDirection rotation : LCCDirection.values()) { // 只考虑旋转到目标方向(如果可能)
if (rotation == current.direction()) continue; 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( float rotationCost = calculateRotationCost(
current.direction(), rotation, ROTATION_COST_PER_DEGREE current.direction(), requiredDirection, ROTATION_COST_PER_DEGREE
); );
considerState(current, current.node(), rotation, considerState(current, current.node(), requiredDirection,
rotationCost, open, visited, end); 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(); return Collections.emptyList();
} }
@ -166,15 +195,30 @@ public class AStarPathPlanner {
blockedNodes.put(nodeId, blockedFactor); blockedNodes.put(nodeId, blockedFactor);
} }
private float calculateMoveCost(Node from, Node to) { private float calculateMoveCost(Node from, Node to, boolean isBackward) {
float baseCost = graph.distance(from, to); float baseCost = graph.distance(from, to);
float weight = nodeWeights.getOrDefault(to.id(), 1.0f); float weight = nodeWeights.getOrDefault(to.id(), 1.0f);
float blockedFactor = blockedNodes.getOrDefault(to.id(), 0f); float blockedFactor = blockedNodes.getOrDefault(to.id(), 0f);
// 后退移动增加额外成本??
// return baseCost * weight * (1 + blockedFactor) * (isBackward ? 1.2f : 1.0f);
return baseCost * weight * (1 + blockedFactor); return baseCost * weight * (1 + blockedFactor);
} }
private boolean isBlocked(String nodeId) { private boolean isBlocked(String nodeId) {
return blockedNodes.containsKey(nodeId) && blockedNodes.get(nodeId) > 0.8f; 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;
}
} }

45
servo/src/main/java/com/galaxis/rcs/plan/path2/PathUtils.java

@ -10,12 +10,14 @@ public class PathUtils {
*/ */
public static LCCDirection calculateMoveDirection(Node from, Node to) { public static LCCDirection calculateMoveDirection(Node from, Node to) {
float dx = to.x() - from.x(); float dx = to.x() - from.x();
float dz = to.z() - from.z(); float dz = to.z() - from.z(); // 注意:Z轴向下增长
// 考虑左手坐标系:X向右,Z向下
if (Math.abs(dx) > Math.abs(dz)) { if (Math.abs(dx) > Math.abs(dz)) {
return dx > 1 ? LCCDirection.RIGHT : LCCDirection.LEFT; return dx > 0 ? LCCDirection.RIGHT : LCCDirection.LEFT;
} else { } else {
return dz > 1 ? LCCDirection.DOWN : LCCDirection.UP; // 注意:Z向下增长,所以Z值增加表示向下移动
return dz > 0 ? LCCDirection.DOWN : LCCDirection.UP;
} }
} }
@ -55,27 +57,43 @@ public class PathUtils {
/** /**
* 获取最近的旋转点 * 获取最近的旋转点
*/ */
public static Node findNearestRotationNode(NavigationGraph graph, Node from) { public static Node findNearestRotationNode(NavigationGraph graph, Node from, LCCDirection currentDir,
// 使用BFS查找最近的旋转点 LCCDirection requiredDir) {
Queue<Node> queue = new LinkedList<>(); // 如果当前方向已经匹配,不需要旋转
Set<String> visited = new HashSet<>(); 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); queue.add(from);
visited.add(from.id());
Node result = null;
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
Node current = queue.poll(); Node current = queue.poll();
// 找到可旋转点
if (current.rotatable()) { if (current.rotatable()) {
return current; result = current;
break;
} }
// 探索邻居
for (Node neighbor : graph.getNeighbors(current)) { for (Node neighbor : graph.getNeighbors(current)) {
if (!visited.contains(neighbor.id())) { float newDist = distances.get(current) + graph.distance(current, neighbor);
visited.add(neighbor.id());
if (newDist < distances.getOrDefault(neighbor, Float.MAX_VALUE)) {
distances.put(neighbor, newDist);
predecessors.put(neighbor, current);
queue.add(neighbor); queue.add(neighbor);
} }
} }
} }
return null;
return result;
} }
/** /**
@ -98,7 +116,8 @@ public class PathUtils {
float angle2 = getRequiredDirection(to); float angle2 = getRequiredDirection(to);
float diff = Math.abs(angle1 - angle2); float diff = Math.abs(angle1 - angle2);
diff = Math.min(diff, 360 - diff); diff = Math.min(diff, 360 - diff);
return diff * ROTATION_COST_PER_DEGREE; // return diff * ROTATION_COST_PER_DEGREE;
return 0.1f;
} }
/** /**

40
servo/src/main/java/com/galaxis/rcs/plan/path2/PtrPathPlanner.java

@ -38,6 +38,32 @@ public class PtrPathPlanner {
// 规划到取货点路径 // 规划到取货点路径
List<State> toLoadPath = astar.findPath(startNodeId, startDirection, loadNodeDirection.node().id(), loadNodeDirection.direction()); 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()); List<State> toUnloadPath = astar.findPath(loadNodeDirection.node().id(), loadNodeDirection.direction(), unloadNodeDirection.node().id(), unloadNodeDirection.direction());
@ -89,13 +115,23 @@ public class PtrPathPlanner {
// 如果是旋转动作 // 如果是旋转动作
if (current.node().equals(prevState.node())) { if (current.node().equals(prevState.node())) {
float angle = getRequiredDirection(current.direction()); float angle = PathUtils.getRequiredDirection(current.direction());
sequence.addRotationTo(angle); sequence.addRotationTo(angle);
} }
// 移动动作 // 移动动作 - 检查是否需要后退
else { 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()); sequence.addMoveTo(current.node().id());
} }
}
prevState = current; prevState = current;
} }
} }

Loading…
Cancel
Save