用A*算法求自主导航的最短路径

A*算法是基于图搜索的一种较快捷的寻路算法,下面是它的基本思路。
一.节点的数据结构。在每一个路径节点或潜在的节点(即地图的栅格)中,具有的数据如下:
1.最基本的坐标值,在这里我们选择用栅格二维数组的下标而不是笛卡尔坐标x、y。
2.父节点,它意味着路径的拓展过程中本节点是从其父节点延伸过来的。
3.在每一轮迭代查找时,该路径点的优劣,它包括F,G,H三个数值,G指的是从起点移动到当前栅格的移动代价,要注意的是必须是从它的父节点延伸过来的移动代价,也就是说在某一时刻,该节点的G值是唯一确定的;H值指的是从该节点到终点的移动代价,我们用曼哈顿方法来估算这个代价;而F值是G与H之和,代表经过该节点到终点的总代价。
二.设置一个openlist,它用来搜索下一次迭代时潜在的路径点目标,由于在栅格地图中的路径点是在栅格中紧挨着依次摸索,不会出现跳跃的情况,因此这个openlist是每个路径点周围的八个栅格组成的,当然这样肯定会有重复,我们接下来阐述重复部分的处理方法;另外,我们设置一个closelist,这是在计算路径时已被我们确定的栅格,它们在下一次寻找中不会被我们考虑。因此,在每一次迭代中,我们把当前点的周围进行探索,将是障碍物的栅格、已经在closelist里的栅格忽略掉,其他的纳入openlist中。
三.当计算开始,首先将起点分别放入openlist与closelist,计算起点周围的节点,纳入openlist,它们的父节点是起点,而此时起点在openlist中已经无作用,将其删去。遍历openlist并选择F值最小的节点作为当前节点,放入closelist中,同时在openlist里去掉该节点。
四.第三点和第四点的行动将会反复执行。参照第二点的规则,遍历openlist并选择F值最小的节点作为当前节点,放入closelist中,同时在openlist里去掉该节点。在每一次执行后,查看该节点的相邻节点,若不在openlist与closelist中,就放进openlist,并且把父节点设为当前节点。再查看已在openlist中的相邻节点,如果经过当前节点到这些节点的G值小于它们的原有G值,那么更新这些节点的G值并将它们的父节点设为当前节点。
五.经过反复迭代,如果把终点纳入到openlist,则计算结束。我们从终点开始依次查找父节点,直至起点,这样便可得到路径。

在实际应用中,我们将ros中的占用栅格地图做成二维数组,这样便可以定位每一个节点的坐标下标位置。至于用什么数据结构能最快地确定某节点在openlist中,我们可以采用map来查找,也就是说,openlist和closelist都是map。

具体代码如下:


namespace astar 
{

using namespace std;
struct PointCell
{
    int x, y;
    /* 由于stl中没有关于结构体大小的定义,我们在这里重写红黑树排序的规则 */
    bool operator < (const PointCell& another) const
    {
        /* 把x作为主键 */
        if(x < another.x)
        {
          return true;
        }
        else if(x == another.x)
        {
          return y < another.y;
        }
        return false;
    }
};

struct TNode
{
    PointCell cell;
    TNode* father;
    int F, G, H;
};



class Astar
{
public:
    Astar();
    ~Astar();
private:
    bool makePlan(const geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& plan);
    void publishPlan(const std::vector<geometry_msgs::PoseStamped>& path, double r, double g, double b, double a);
    bool currentSearch();
    void checkNeighborCell(TNode* node, PointCell neighbor, int deltaX, int deltaY);
    void updateGCost(TNode* node, TNode* neighbor, int deltaX, int deltaY);
    bool goalReached();

    int costGFunction(TNode* current, int deltaX, int deltaY)
    {
        if(deltaX != 0 && deltaY != 0)
            return current->father->G + 14;
        else
            return current->father->G + 10;
    }

    int costHFunction(TNode* current)
    {
        return abs(current->cell.x - goal_node_->cell.x) + abs(current->cell.y - goal_node_->cell.y);
    }

    TNode* root_node_, *current_node_, *goal_node_;

    ros::Publisher plan_pub_;
    ros::Subscriber pose_sub_, goal_sub_, map_sub_;
    void initialPoseCB(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg);
    void goalCB(const geometry_msgs::PoseStamped::ConstPtr &msg);
    void mapCB(const nav_msgs::OccupancyGrid::ConstPtr &msg);
    pair<PointCell, TNode*> temp_node_;
    std::map<PointCell, TNode*> open_list_, close_list_;
    std::map<PointCell, TNode*>::iterator it_;
    PointCell start_, goal_;
    double map_origin_x_, map_origin_y_;
    double map_resolution_;
    int map_size_x_, map_size_y_;
    std::vector<int> map_info_;
    geometry_msgs::PoseStamped goal_pose_;
    std::vector<geometry_msgs::PoseStamped> plan_;
};

} /* end of namespace */

主程序如下:


namespace astar
{

Astar::Astar()
{
    ros::NodeHandle nh;
    plan_pub_ = nh.advertise<nav_msgs::Path>("astar_path", 1);
    pose_sub_ = nh.subscribe<geometry_msgs::PoseWithCovarianceStamped>("initialpose", 1, boost::bind(&Astar::initialPoseCB, this, _1));
    map_sub_ = nh.subscribe<nav_msgs::OccupancyGrid>("map", 1, boost::bind(&Astar::mapCB, this, _1));
    goal_sub_ = nh.subscribe<geometry_msgs::PoseStamped>("move_base_simple/goal", 1, boost::bind(&Astar::goalCB, this, _1));
    root_node_ = new TNode;
    root_node_->father = NULL;
    current_node_ = new TNode;
    open_list_.clear();
    close_list_.clear();
}

Astar::~Astar()
{
    for(it_ = open_list_.begin(); it_ != open_list_.end(); it_++)
    {
        delete it_->second;
    }
    for(it_ = close_list_.begin(); it_ != close_list_.end(); it_++)
    {
        delete it_->second;
    }
}

bool Astar::makePlan(const geometry_msgs::PoseStamped& goal, std::vector<geometry_msgs::PoseStamped>& plan)
{
    if(currentSearch())
    {
        ROS_INFO("Plan found.");
        std::vector<geometry_msgs::PoseStamped> temp_plan;
        geometry_msgs::PoseStamped pose;
        pose.header.frame_id = "map";
        pose.pose.position.x = goal_node_->cell.x * map_resolution_ + map_origin_x_;
        pose.pose.position.y = goal_node_->cell.y * map_resolution_ + map_origin_y_;
        pose.pose.orientation.w = 1;
        temp_plan.push_back(pose);
        TNode* father = goal_node_->father;
        while(father != NULL)
        {
            pose.header.frame_id = "map";
            pose.pose.position.x = father->cell.x * map_resolution_ + map_origin_x_;
            pose.pose.position.y = father->cell.y * map_resolution_ + map_origin_y_;
            pose.pose.orientation.w = 1;
            temp_plan.push_back(pose);
            father = father->father;
        }
        plan.clear();
        for(int i = temp_plan.size() - 1; i > -1; i--)
        {
            plan.push_back(temp_plan[i]);
        }

        publishPlan(plan, 0.0, 1.0, 0.0, 0.0);
        return true;
    }
    return false;
}

void Astar::publishPlan(const std::vector<geometry_msgs::PoseStamped>& path, double r, double g, double b, double a)
{
    //create a message for the plan
    nav_msgs::Path gui_path;
    gui_path.poses.resize(path.size());

    if(!path.empty())
    {
        gui_path.header.frame_id = path[0].header.frame_id;
        gui_path.header.stamp = path[0].header.stamp;
    }

    // Extract the plan in world co-ordinates, we assume the path is all in the same frame
    for(unsigned int i=0; i < path.size(); i++){
        gui_path.poses[i] = path[i];
    }

    plan_pub_.publish(gui_path);
}

bool Astar::goalReached()
{
    it_ = open_list_.find(goal_);
    if(it_ == open_list_.end())
        return false;
    else
        return true;
}

bool Astar::currentSearch()
{
    /* 停止条件是openlist为空,或者将终点纳入到openlist中 */
    while(open_list_.size() != 0)
    {
        /* 遍历openlist,找寻F值最小的节点,选择它为当前节点 */
        int min_cost = INT_MAX;
        for(it_ = open_list_.begin(); it_ != open_list_.end(); it_++)
        {
            if(it_->second->F < min_cost)
            {
                current_node_ = it_->second;
            }
        }
        /* 把当前节点放入closelist */
        temp_node_.first = current_node_->cell;
        temp_node_.second = current_node_;
        close_list_.insert(temp_node_);

        /* 将当前节点从openlist中剔除 */
        open_list_.erase(current_node_->cell);

        /* 检查当前节点的相邻节点 */
        PointCell up, up_right, right, down_right, down, down_left, left, up_left;

        /* up */
        checkNeighborCell(current_node_, up, 0, 1);
        /* upright */
        checkNeighborCell(current_node_, up_right, 1, 1);
        /* right */
        checkNeighborCell(current_node_, right, 1, 0);
        /* downright */
        checkNeighborCell(current_node_, down_right, 1, -1);
        /* down */
        checkNeighborCell(current_node_, down, 0, -1);
        /* downleft */
        checkNeighborCell(current_node_, down_left, -1, -1);
        /* left */
        checkNeighborCell(current_node_, left, -1, 0);
        /* upleft */
        checkNeighborCell(current_node_, up_left, -1, 1);

        if(goalReached())
        {
            return true;
        }
    }
    return false;
}

void Astar::checkNeighborCell(TNode *node, PointCell neighbor, int deltaX, int deltaY)
{
    /* 如果这个邻居节点已在openlist当中,那检查经过本节点会不会有更好的路径代价 */
    it_ = open_list_.find(neighbor);
    if(it_ == open_list_.end())
    {
        /* 如果这个邻居节点也不在closelist中 */
        it_ = close_list_.find(neighbor);
        if(it_ == close_list_.end())
        {
            /* 如果它也不是障碍物,那么就将其放入openlist */
            if(map_info_[neighbor.y * map_size_x_ + neighbor.x] == 0)
            {
                TNode* newNode;
                newNode = new TNode;
                neighbor.x = node->cell.x + deltaX;
                neighbor.y = node->cell.y + deltaY;
                newNode->cell = neighbor;
                newNode->father = node;
                temp_node_.first = neighbor;
                temp_node_.second = newNode;
                open_list_.insert(temp_node_);
            }
        }
    }
    else
    {
        updateGCost(node, open_list_.find(neighbor)->second, deltaX, deltaY);
    }
}

void Astar::updateGCost(TNode* node, TNode* neighbor, int deltaX, int deltaY)
{
    int newCost = node->G + costGFunction(node, deltaX, deltaY);
    if(neighbor->G > newCost)
    {
        neighbor->G = newCost;
        neighbor->father = node;
    }
}

void Astar::initialPoseCB(const geometry_msgs::PoseWithCovarianceStamped::ConstPtr &msg)
{
    ROS_INFO("start received.");
    start_.x = (msg->pose.pose.position.x - map_origin_x_) / map_resolution_;
    start_.y = (msg->pose.pose.position.y - map_origin_y_) / map_resolution_;
    root_node_->cell = start_;
    /* The first time for searching neighbors */
    temp_node_.first = start_;
    temp_node_.second = root_node_;
    open_list_.insert(temp_node_);
    current_node_ = root_node_;
}

void Astar::mapCB(const nav_msgs::OccupancyGrid::ConstPtr &msg)
{
    ROS_INFO("map received.");
    map_resolution_ = msg->info.resolution;
    map_size_x_ = msg->info.width;
    map_size_y_ = msg->info.height;
    map_origin_x_ = msg->info.origin.position.x;
    map_origin_y_ = msg->info.origin.position.y;
    map_info_.clear();
    for(int i = 0; i < msg->data.size(); i++)
    {
        if(msg->data[i] == 0)
        {
            map_info_.push_back(0);
        }
        else
        {
            map_info_.push_back(1);
        }
    }
}

void Astar::goalCB(const geometry_msgs::PoseStamped::ConstPtr &msg)
{
    ROS_INFO("goal received.");
    goal_.x = (msg->pose.position.x - map_origin_x_) / map_resolution_;
    goal_.y = (msg->pose.position.y - map_origin_y_) / map_resolution_;
    goal_node_->cell = goal_;
    goal_pose_.pose = msg->pose;
    makePlan(goal_pose_, plan_);
}

} /* end of namespace */

int main(int argc, char **argv)
{
    ros::init(argc, argv, "astar");
    astar::Astar astar_test;
    ros::spin();
}


 这样可能还有一点点毛病,明天完善一下~

当然,map中的key一定要有序,这个我在一开始写opertator<的时候绕了很多弯路,这样就直接导致了map在find的时候找到错误的TNode,浪费了很多时间.另外就是,我原来为了省事,用遍历来找F值最小的TNode,其实用优先级队列实现就行了.

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值