机器人路径规划 - A*(星级)

Robotic Path Planning - A* (Star)

我正在用 C++ 为我的主要机器人探索行为实现 A* 路径规划算法。随着机器人的移动,它会将自身周围的环境映射为二维图形。从这张图中,我设置了一个 Vector2D 元组 {x, y},它保存了这个路点的位置,我希望机器人也导航到那里。

我对 A* 做的第一件事是有一个 Node class,它保存有关当前节点的信息;

double f; //  F, final score
double g; // Movement cost
double h; // Hueristic cost (Manhatten)
Node* parent;
Vector2d position;

当 A* 开始时,我将我的起始节点作为我的机器人起始位置(我也将此位置作为 Vector 以方便访问)。然后,我进入一个 while 循环,直到找到最终目标。我在这个循环中做的第一件事是生成八个相邻的节点(左、下、右、上、左上、右上、左下、右下),然后我 return 在一个 OpenList向量。

// Open List是当前要检查的节点 std::vector 个职位;

positions.push_back(Vector2d(current->position.getX() - gridSize, current->position.getY())); // Left of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX() + gridSize, current->position.getY())); // right of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX(), current->position.getY() + gridSize)); // Top of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX(), current->position.getY() - gridSize)); // Bottom of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX() + gridSize,current->position.getY() + gridSize)); // Top Right of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX() - gridSize,current->position.getY() + gridSize)); // Top Left of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX() + gridSize,current->position.getY() - gridSize)); // Bottom Right of my current grid space (parent node)
positions.push_back(Vector2d(current->position.getX() - gridSize,current->position.getY() - gridSize)); // Bottom Left of my current grid space (parent node)

// moving diagnolly has a bigger cost
int movementCost[8] = { 10, 10, 10, 10, 14, 14, 14, 14 };

// loop through all my positions and calculate their g, h and finally, f score.
for (int i = 0; i < positions.size(); i++)
{
    Node* node = new Node(positions[i]);

    node->parent = current;
    node->movementCost = movementCost[i];
    if (!presentInClosedList(node))
    {
        // if the probability value of the current node is less then 0.5 (not an obstacle) then add to the open list, else skip it as an obstacle
        // Set astar grid occupancy
        if (grid->getProbabilityValue(node->position) < 0.51)
        {
            node->g = current->g + movementCost[i];
            node->h = (abs(positions[i].getX() - wantedLocation.getX())) + (abs(positions[i].getY() - wantedLocation.getY()));
            node->f = node->g + node->h;

            openList.push_back(node);
        }
    }
}

这是查看当前节点是否存在于我的 closedList 中的代码

bool 存在 = false;

for (int i = 0; i < closedList.size(); i++)
{
    if (closedList[i]->position == currentNode->position)
    {
        closedList[i]->f = currentNode->f;
        closedList[i]->g = currentNode->g;
        closedList[i]->h = currentNode->h;
        closedList[i]->parent = currentNode->parent;

        exists = true;
        break;
    }
}

return exists;

这 return 是一条 openlist 可能的路线。接下来,我 select 得分最小的 F ,并将其添加到我的 closedList 中。我一直这样做,直到找到最终目标。最后,一旦找到,我就使用 parent 对象返回列表。这是代码的其余部分

    // If my parents location is the same as my wanted location, then we've found our position.
    if (locationFound(current, wantedLocation))
    {
        // Now we need to backtrack from our wantedNode looking at the parents of each node to reconstruct the AStar path
        Node* p = current->parent;
        rollingDist = p->g;

        while (!wantedFound)
        {
            if (p->position == startLocation)
            {
                wantedFound = true;
                wantedNodeFound = true;
                break;
            }

            path.push_back(p);
            p = p->parent;

        }

    }

现在这是我的问题。在每次尝试中,它总能找到想要的位置,但从来没有找到最短路径。见下图一

图一。黄色标记是想要的位置,红色飞镖是我想要的位置的 "Path",最后,"Blue" 标记是 A 星开始的地方。

这是我的问题。我似乎无法重建这条路。

回顾一下评论,有两个重要的问题

  • 曼哈顿距离不允许用于您的移动成本,因为实际的最短路径可以采用曼哈顿距离不会考虑的对角线捷径。
  • 在将新节点添加到Open列表之前,不仅要检查它是否在Closed列表中,还要检查它是否已经在Open列表中。如果它已经在 Open 列表中,则必须比较 G 并选择最小的(连同相应的父指针)。[1]

因为你有 10/14 成本的 octile 运动,你的启发式函数可以是(来自 http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html

function heuristic(node) =
    dx = abs(node.x - goal.x)
    dy = abs(node.y - goal.y)
    return D * (dx + dy) + (D2 - 2 * D) * min(dx, dy)

D = 10,D2 = 14。当然你也可以使用任何其他可接受的公式,但这个公式已经反映了开阔平原上的实际距离,因此不容易改进。

在 Open 列表中查找和更新节点是 A* 中令人讨厌的部分,我相信很多人会假装没有必要,因为这意味着您不能合理地使用任何预定义的优先级队列(他们缺乏有效的查找)。这可以通过手动实现的二进制堆和将坐标映射到堆中相应索引的散列 table 来完成。每当移动节点时,堆都必须更新散列 table。

[1]:wikipedia article 中的相关伪代码片段是:

    tentative_gScore := gScore[current] + dist_between(current, neighbor)
    if neighbor not in openSet  // Discover a new node
        openSet.Add(neighbor)
    else if tentative_gScore >= gScore[neighbor]
        continue        // This is not a better path.

    // This path is the best until now. Record it!
    cameFrom[neighbor] := current
    gScore[neighbor] := tentative_gScore
    fScore[neighbor] := gScore[neighbor] + heuristic_cost_estimate(neighbor, goal)