机器人路径规划 - A*(星级) [英] Robotic Path Planning - A* (Star)
问题描述
我正在用 C++ 为我的主要机器人探索行为实现 A* 路径规划算法.当机器人移动时,它会将自身周围的环境映射为 2D 图形.从这张图中,我设置了一个 Vector2D 元组 {x, y}
,它保存了这个航点的位置,我也希望机器人在那里导航.
I'm implementing A* path planning algorithm for my main robots exploration behavior in C++. As the robot moves, it maps the environment around itself as a 2D graph. From this graph, I have set a Vector2D Tuple {x, y}
which holds the location of this waypoint, where I want the robot to navigate too.
我对 A* 做的第一件事是有一个 Node
类,它保存有关当前节点的信息;
The first thing I do with A* is to have a Node
class, which holds information about the current node;
double f; // F, final score
double g; // Movement cost
double h; // Hueristic cost (Manhatten)
Node* parent;
Vector2d position;
当 A* 开始时,我将我的起始节点作为我的机器人起始位置(为了方便访问,我也将此位置作为 Vector 持有).然后,我进入一个 while 循环,直到找到最终目标.我在这个循环中做的第一件事是生成八个相邻的节点(左、下、右、上、左上、右上、左下、右下),然后我在 OpenList<中返回它/代码> 向量.
As A* starts, I have my starting node as my Robots starting position (I also hold this position as a Vector for easy access). Then, I enter a while loop until the end goal is found. The first thing I do in this loop is to generate eight adjacent Nodes (Left, Bottom, Right, Top, Top-left, Top-Right, Bottom-Left, Bottom Right), I then return this in a OpenList
vector.
//打开列表是当前要检查的节点std::vector 位置;
// Open List is current nodes to check std::vector positions;
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中的代码
This is the code to see if the current node is present in my closedList
bool 存在 = false;
bool exists = 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;
这将返回可能路由的openlist
.接下来,我选择 F
分数最小的那个,并将其添加到我的 closedList
.我一直在做这个程序,直到找到最终目标.最后,一旦找到,我就使用 parent
对象返回列表.这是其余的代码
This returns an openlist
of possible routes. Next, I select the one with the smallest F
score, and add this to my closedList
. I keep doing this routine until the end goal has been found. Finally, once found I go back down the list using the parent
objects. Here is the rest of the code
// 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;
}
}
现在这是我的问题.在每次尝试时,它总是会找到想要的位置,但永远不会找到最短路径.见下图一.
Now this is my issue. On every attempt it always finds the wanted location, but never the shortest path. See figure one below.
图一.其中黄色标记是通缉地点,红色飞镖是我通缉地点的路径",最后,蓝色"标记是A星开始的地方.
这是我的问题.我似乎无法重建这条路.
This is my issue. I can't seem to reconstruct this path.
推荐答案
回顾一下评论,有两个重要的问题
To recap the comments, there are two important problems
- 曼哈顿距离不可用于您的移动成本,因为实际最短路径可能采用曼哈顿距离不会考虑的对角线捷径.
- 在向Open链表添加新节点之前,不仅要检查它是否在Closed链表中,还要检查它是否已经在Open链表中.如果它已经在 Open 列表中,则必须比较 G 并且必须选择最小的(连同相应的父指针).[1]
由于您有 10/14 成本的八角运动,您的启发式函数可能是(来自 http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html)
Since you have octile movement with 10/14 costs, your heuristic function could be (from 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.当然你也可以使用任何其他可接受的公式,但这个公式已经反映了开阔平原上的实际距离,因此不能轻易改进.
With D = 10, D2 = 14. Of course you can also use anything else admissible but this formula already reflects the actual distance on an open plain so it can't easily be improved.
在 Open 列表中查找和更新节点是 A* 的一个烦人的部分,我相信很多人都想假装没有必要,因为这意味着您不能合理地使用任何预定义的优先级队列(他们缺乏有效的查找).这可以通过手动实现的二进制堆和将坐标映射到堆中相应索引的哈希表来完成.每当节点移动时,堆都必须更新哈希表.
Finding and updating nodes in the Open list is an annoying part of A* that I'm sure many people would like to pretend isn't necessary, since it means you can't reasonably use any pre-defined priority queue (they lack efficient lookup). It can be done by having a manually implemented binary heap and a hash table that maps coordinates to their corresponding indexes in the heap. The hash table has to be updated by the heap whenever a node is moved.
[1]:来自维基百科文章的相关伪代码片段是:
[1]: the relevant snippet of pseudo code from the wikipedia article is:
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)
这篇关于机器人路径规划 - A*(星级)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!