【问题标题】:A* pathfinding with Multiple Agents多代理的 A* 寻路
【发布时间】:2015-03-06 00:29:40
【问题描述】:

我目前一直在使用 A* 算法学习和编程寻路(在 Java 中)。我遇到的一个问题是,当多个实体试图寻路时,它们都改变了previousNode(计算Node 来自的Node),弄乱了算法,最终导致Node A将指向Node BNode B 将指向Node A

如何将算法更改为任一

  • 不要使用这个遍布所有 A * 算法的previousNode 系统(我已经看到了)
  • 更改此系统以同时使用

我试图避免让一个实体完成寻路,然后告诉下一个实体进行寻路,依此类推。就像在 Java 中做 wait() - notify() 对一样。

public Path findPath(int startX, int startY, int goalX, int goalY) { 
  //Path is basically just a class that contains an ArrayList, 
  //containing Nodes,  which contains the steps to reach a goal.
    if(map.getNode(goalX, goalY).isObstacle()) {
        return null;
    }

    map.getNode(startX, startY).setDistanceFromStart(0);
    closedList.clear();
    openList.clear(); //A List with added getFirst() - gets the first Node in the list
    openList.add(map.getNode(startX, startY));

    while(openList.size() != 0) {
        //Node contains a List that has all of the Nodes around this node, a 
        //F, G, and H value, and its row(y) and column(x)
        Node current = openList.getFirst(); 

        if(current.getX() == goalX && current.getY() == goalY)  {
            return backtrackPath(current);
        }

        openList.remove(current);
        closedList.add(current);

        for(Node neighbor : current.getNeighborList()) {
            boolean neighborIsBetter;

            //If I've already searched this neighbor/node, don't check it
            if(closedList.contains(neighbor)) {
                continue;
            }

            if(!neighbor.isObstacle()) {
                float neighborDistanceFromStart = (current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor));

                if(!openList.contains(neighbor)) {
                    openList.add(neighbor);
                    neighborIsBetter = true;
                } else if(neighborDistanceFromStart < current.getDistanceFromStart()) {
                    neighborIsBetter = true;
                } else {
                    neighborIsBetter = false;
                }

                if(neighborIsBetter) {
                    neighbor.setPreviousNode(current);
                    neighbor.setDistanceFromStart(neighborDistanceFromStart);
                    neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY));
                }
            }
        }
    }

    return null;
}

public Path backtrackPath(Node fromNode) {
    Path path = new Path();
    while(fromNode.getPreviousNode() != null) {
        path.prependWaypoint(fromNode);
        fromNode = fromNode.getPreviousNode();
    }

    return path;
}

我具体说的是(findPath()内)

if(neighborIsBetter) {
    neighbor.setPreviousNode(current); //previousNode is a value in the Node class that points to the Node that it came from
    neighbor.setDistanceFromStart(neighborDistanceFromStart);
    neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY));
}

【问题讨论】:

  • 如果没有代码示例,我们不知道您在说什么。为什么您的寻路代理会更改您的数据,而不是使用他们自己的副本或参考资料?
  • 前一个节点是搜索状态的一部分。如果您想进行多个独立搜索,您将需要多个搜索状态,包括标记打开、关闭、上一个节点以及每个节点距起点的当前距离。
  • @rpattiso 你能详细说明一下吗?我不太明白你的意思。
  • 从您的节点对象中删除任何 A* 数据,不存储与图表中开始或上一个节点的距离(我认为这也是一种更简洁的设计)。而是将该数据存储在 A* 对象中。有多个 Astar 对象,每个代理一个。这样一来,它们共享图形结构而不是 A* 特定数据,并且它们不会相互干扰。

标签: java algorithm


【解决方案1】:

如果不以某种方式存储给定路径的反向指针,我认为您无法执行 A*(或任何寻路算法)。因此,您有两个选择

  1. 要求每个代理(我假设是线程)创建自己的图表副本以进行处理。这样一来,每个 A* 调​​用都不会相互干扰,因为它们正在处理不同图表上同一节点的字段。
  2. 更改您的 A* 代码以能够处理多个并发调用。

选项 1 是不言自明的,可能是更好的选择。如果这仅适合您,您可能应该只使用那个(而不是尝试使 A* 在单个图上完全并发)。这将需要添加map 作为输入参数(并要求并发调用应该使用不同的映射实例,要么抛出异常,要么具有未指定的行为,如果没有发生)。此外,您应该在每次调用中将closedListopenList 实例化为新的数据结构,而不是共享一个列表。

如果这不符合您的喜好 - 您真的想将多调用用法完全封装到方法本身中,我认为您可以做到这一点的最简单方法是需要一个 id 的附加参数 - 一些独特的字符串保证不会与另一个并发调用的id 相同。所以 A* 的标题现在看起来像:

public Path findPath(final String ID, int startX, int startY, int goalX, int goalY) { 

从那里,将Node 中每个可设置寻路字段的所有实现更改为HashMap,并以id 作为键。从您的代码中,我猜您的 Node 类看起来像这样:

public class Node{
    //Fields used by the A* call - no problem here
    private boolean obstacle;

    //Fields *edited* by the A* call
    private float distanceFromStart;
    private Node previous;
    private int heuristic;

    //other fields and stuff

    public boolean isObstacle(){
        return obstacle;
    }

    public float getDistanceFromStart(){
        return distanceFromStart;
    }

    public void setDistanceFromStart(float f){
        distanceFromStart = f;
    }

    public Node getPrevious(){
        return previous;
    }

    public void setPrevious(Node p){
        previous = p;
    }

    public int getHeuristic(){
        return heuristic;
    }

    public void setHeuristic(int h){
        heuristic = h;
    }
}

我们可以通过id编辑edited字段以存储许多值,例如:

public class Node{
    //Fields used by the A* call - no problem here
    private boolean obstacle;

    //Fields *edited* by the A* call
    private HashMap<String,Float> distanceFromStart;
    private HashMap<String,Node> previous;
    private HashMap<String,Integer> heuristic;

    //other fields and stuff

    public boolean isObstacle(){
        return obstacle;
    }

    public float getDistanceFromStart(String id){
        return distanceFromStart.get(id);
    }

    public void setDistanceFromStart(String id, float f){
        distanceFromStart.put(id, f);
    }

    public Node getPrevious(String id){
        return previous.get(id);
    }

    public void setPrevious(String id, Node p){
        previous.put(id,p);
    }

    public int getHeuristic(String id){
        return heuristic.get(id);
    }

    public void setHeuristic(String id,int h){
        heuristic.put(id,h);
    }
}

从那里,只需编辑您的 A* 方法,以便在调用时将方法调用中的 id 提供给 getter 和 setter。只要两个并发的方法调用没有相同的id 值,它们就不会相互干扰。要使其正常工作,请记住三件事:

  1. 确保每个可编辑字段都得到这种处理。如果你忘记一个,它就不会起作用。不可编辑的字段(不会作为运行 A* 的副产品而更改的字段)可以保持单数。
  2. 如果您使用上述方法,您可能应该在清理阶段添加一个从图中删除给定 ID 的所有信息的步骤,否则节点的哈希图会随着每次调用而变大。

无论您选择哪种并发方法,您仍然应该创建openListclosedList 新的本地 实例。制作openListclosedList 共享实例没有任何好处,而且只会出现错误。

List<Node> closedList = new LinkedList<Node>();
List<Node> openList = new LinkedList<Node>();
//Don't have to clear them anymore - they're new lists
openList.add(map.getNode(startX, startY));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多