|
|
5 mēneši atpakaļ | |
|---|---|---|
| .. | ||
| README.md | 5 mēneši atpakaļ | |
A* 算法结合了 Dijkstra 算法(保证找到最短路径)和 贪婪最佳优先搜索(优先探索目标方向)的优点
参考代码如下:
template <typename T>
class Node
{
protected:
T x_, y_; // x 轴坐标 y 轴坐标
double g_; // 从 StartNode 到 CurrentNode 的 Cast
double h_; // 从 CurrentNode 到 EndNode 的 欧几里得距离
int id_; // Node 节点的 ID
int pid_; // Node 节点的 父节点
public:
// 提供排序的仿函数
struct compare_cost
{
bool operator()(const Node& n1, const Node& n2) const
{
return (n1.g() + n1.h() > n2.g() + n2.h()) || ((n1.g() + n1.h() == n2.g() + n2.h()) && (n1.h() > n2.h()));
};
};
}
观察 Node 节点的代码,比较一个节点 Cast 的大小是将 g 值 + h 值进行比较,如果相等,则单纯比较 h 值
解析来是 A* 算法的实现
std::priority_queue<Node, std::vector<Node>, Node::compare_cost> open_list; 使用 priority_queue 优先队列对插入的数据进行排序结合 Node::compare_cost 的排序函数,可知 open_list 中 Node 是按 Cast 从小到大进行排序
每次都取出 open_list 中 Cast 最小的 Node,也就是第一个 Node
对 Node 周围的 8 个 Node 进行遍历,将其添加到 open_list 中
添加 Node 之前先判断其在
open_list或者closed_list中是否已经存在,避免重复处理 Node
bool AStarPathPlanner::plan(const Point3d& start, const Point3d& goal, Points3d& path, Points3d& expand)
{
double m_start_x, m_start_y, m_goal_x, m_goal_y;
if ((!validityCheck(start.x(), start.y(), m_start_x, m_start_y)) ||
(!validityCheck(goal.x(), goal.y(), m_goal_x, m_goal_y)))
{
return false;
}
Node start_node(m_start_x, m_start_y);
Node goal_node(m_goal_x, m_goal_y);
start_node.set_id(grid2Index(start_node.x(), start_node.y()));
goal_node.set_id(grid2Index(goal_node.x(), goal_node.y()));
// clear vector
path.clear();
expand.clear();
// open list and closed list
std::priority_queue<Node, std::vector<Node>, Node::compare_cost> open_list;
std::unordered_map<int, Node> closed_list;
open_list.push(start_node);
// main process
while (!open_list.empty())
{
// pop current node from open list
auto current = open_list.top();
open_list.pop();
// current node does not exist in closed list
if (closed_list.find(current.id()) != closed_list.end())
continue;
closed_list.insert(std::make_pair(current.id(), current));
expand.emplace_back(current.x(), current.y());
// goal found
if (current == goal_node)
{
const auto& backtrace = _convertClosedListToPath<Node>(closed_list, start_node, goal_node);
for (auto iter = backtrace.rbegin(); iter != backtrace.rend(); ++iter)
{
// convert to world frame
double wx, wy;
costmap_->mapToWorld(iter->x(), iter->y(), wx, wy);
path.emplace_back(wx, wy);
}
return true;
}
// explore neighbor of current node
for (const auto& motion : motions)
{
// explore a new node
auto node_new = current + motion;
node_new.set_g(current.g() + motion.g());
node_new.set_id(grid2Index(node_new.x(), node_new.y()));
// node_new in closed list
if (closed_list.find(node_new.id()) != closed_list.end())
continue;
node_new.set_pid(current.id());
// next node hit the boundary or obstacle
// prevent planning failed when the current within inflation
if ((node_new.id() < 0) || (node_new.id() >= map_size_) ||
(costmap_->getCharMap()[node_new.id()] >= costmap_2d::LETHAL_OBSTACLE * obstacle_factor_ &&
costmap_->getCharMap()[node_new.id()] >= costmap_->getCharMap()[current.id()]))
continue;
// if using dijkstra implementation, do not consider heuristics cost
if (!is_dijkstra_)
node_new.set_h(std::hypot(node_new.x() - goal_node.x(), node_new.y() - goal_node.y()));
// if using GBFS implementation, only consider heuristics cost
if (is_gbfs_)
node_new.set_g(0.0);
// else, g will be calculate through node_new = current + m
open_list.push(node_new);
}
}
return false;
}
motions 的内容如下,是为了方便遍历 上下左右 + 斜方向 总共 八方向 的 Node 节点而定义的一个数组
const std::vector<Node> motions = {
{ 0, 1, 1.0 }, { 1, 0, 1.0 }, { 0, -1, 1.0 }, { -1, 0, 1.0 },
{ 1, 1, std::sqrt(2) }, { 1, -1, std::sqrt(2) }, { -1, 1, std::sqrt(2) }, { -1, -1, std::sqrt(2) },
};
分别对应 上、右、下、左、右上、右下、左上、左下