Bootstrap

Ros noetic Move_base 相关状态位置的获取 实战使用教程

前言:

         有一段时间没有更新,这篇文章是为了后续MPC路径跟踪算法开设的帖子用于更新我自己的思路,由于MPC算法,要镶嵌到整个导航任务中去,就绕不开这个move_base包中相关的参数设置和其中相关状态位置的获取和解读等等。

        因为最近遇到小车在其他的环境中有些时候,不需要自己导航,需要按照自己的航向走直线,按照自己的路径进行任务,不需要自己在路线中进行决策,于是传统的A start 和 DWA 和 TEB 算法实现的过程中则不能够满足当前的需求。

        本文章主要介绍move_base的一些基本操作。

move_base节点:

        它的功能主要是把机器人从当前位置移动到目标位置。其本质上是一个SimpleActionServer的实现,即所谓的action(ROS三大通讯方式之一)的服务器,它使用geometry_msgs/PoseStamped格式的消息,因此我们可以使用SimpleActionClient向move_base发送目标点。

       同时move_base提供了一个话题move_base/goal作为导航堆栈的输入,我们可以简单地将目标点发布至这个话题即可触发整个move_base的工作链,指的是手动发送座标点,和四元函数

     查看topic列表会发现,move_base和其他action一样也提供了五个话题用于获取过程数据以及发送取消目标和获取结果。

move_base/goal (move_base_msgs/MoveBaseActionGoal)

move_base/cancel (actionlib_msgs/GoalID)

move_base/feedback (move_base_msgs/MoveBaseActionFeedback)

move_base/status (actionlib_msgs/GoalStatusArray)

move_base/result (move_base_msgs/MoveBaseActionResult)

     使用RViz的2D Nav Goal工具发布一个目标位姿,其本质就是向move_base/goal主题发布了一条消息,下面我们监听move_base的这几个话题来看看在发布一个目标后发生了什么。

    使用这种方式进行导航时,机器人会在规划生成后开始运动,如果你只是想查看规划结果又不想移动机器人,那么可以使用move_base提供的/make_plan这个服务接口,它可以只生成规划但不移动机器人。

    现在我们知道,当move_base收到一个目标姿势, 它会链接到 global planner(全局规划器), local planner(局部规划器), recovery behaviors(恢复行为)以及costmaps(代价地图)等组件,并 生成一个消息类型为 geometry_msgs/Twist的速度命令输出 , 将其发送到 /cmd_vel 主题来移动机器人。

全局规划器:

        全局规划器就是在运动之前,根据接收到的目标位姿基于global_costmap提供的全局地图调用对应的路径规划算法生成一条从当前位置到目标位置的路径,这里的global_costmap就是根据之前加载的map.pgm数据而生成的,也就是说它在规划的过程中并未获取当前传感器的数据,这和局部规划器是有区别的。

       我们可以用第一节中使用的现实世界中导航的例子来说,这个全局规划器就相当于根据预先采集的地图数据,计算一条从当前位置到目标位置的可行(或最优)路径,但它不会考虑路况,也不会帮你避开拥堵路段,因为它的数据来源只有地图,并没有实时路况数据。

    当前ROS中提供了三个全局规划器的插件:navfn,carrot_planner以及global_planner

       其中navfn是最常见的也是默认的全局规划器,使用的是Dijkstra's算法来计算初始位置和目标位置之间的最短路径。carrot_planner的适用性不强,一般在某些特定的场景较为有效(比如让机器人移动到离障碍物尽可能近的场景)。

      global_planner可以说是navfn的升级版本,虽然navfn内置有Dijkstra's和A*的两种算法的实现,但是在早期版本中A*算法的实现有些bug未修复,故认为navfn使用的是Dijkstra's算法,而在之后版本的升级中为了兼容老版本,所以保留了navfn但也推出了global_planner,global_planner既提供了Dijkstra's和A*算法的实现,还支持自定义的全局规划器插件,可以说比navfn更为灵活。

实战:

好的我们再了解了大概的原理后发现

首先启动仿真节点打开gazebo的世界

 查看所有的话题列表:

rostopic list -v

move_base判定是否完成:

1)使用命令
rostopic list | grep move_base

发现话题 /move_base/result ,显然是反馈move_base包执行结果的话题。

2)使用命令rostopic echo /move_base/result


这时候在rviz指定一个goal,仔细观察输出信息,等待机器人运动到点,可以看到status为3时,text为Goal reached,此时move_base正是完成状态。

 text:机器人在摆动。即使在执行恢复行为之后。可能出现了一些问题此时机器人没有移动

这里的status:4

这里是当我们成功发送了节点,status则变化为了1,小车开始运动

 当小车到达了目标后,status则转变为3

 

 这里已经到达了坐标原点并完成了导航,可见已经完成

关于状态123456789的详细情况如下所示:

    # uint8 PENDING         = 0   # The goal has yet to be processed by the action server
    # uint8 ACTIVE          = 1   # The goal is currently being processed by the action server
    # uint8 PREEMPTED       = 2   # The goal received a cancel request after it started executing
    #                             #   and has since completed its execution (Terminal State)
    # uint8 SUCCEEDED       = 3   # The goal was achieved successfully by the action server (Terminal State)
    # uint8 ABORTED         = 4   # The goal was aborted during execution by the action server due
    #                             #    to some failure (Terminal State)
    # uint8 REJECTED        = 5   # The goal was rejected by the action server without being processed,
    #                             #    because the goal was unattainable or invalid (Terminal State)
    # uint8 PREEMPTING      = 6   # The goal received a cancel request after it started executing
    #                             #    and has not yet completed execution
    # uint8 RECALLING       = 7   # The goal received a cancel request before it started executing,
    #                             #    but the action server has not yet confirmed that the goal is canceled
    # uint8 RECALLED        = 8   # The goal received a cancel request before it started executing
    #                             #    and was successfully cancelled (Terminal State)
    # uint8 LOST            = 9   # An action client can determine that a goal is LOST. This should not be
    #                             #    sent over the wire by an action server

move base本身就会一直发布自身状态信息

topic名是move_base/status

信息类型是actionlib_msgs/GoalStatusArray,包含如下信息:

std_msgs/Header header
  uint32 seq
  time stamp
  string frame_id
actionlib_msgs/GoalStatus[] status_list
  uint8 PENDING=0
  uint8 ACTIVE=1
  uint8 PREEMPTED=2
  uint8 SUCCEEDED=3
  uint8 ABORTED=4
  uint8 REJECTED=5
  uint8 PREEMPTING=6
  uint8 RECALLING=7
  uint8 RECALLED=8
  uint8 LOST=9
  actionlib_msgs/GoalID goal_id
    time stamp
    string id
  uint8 status
  string text

问题修复:

       当前小车卡死,或者上一次导航已经完成,我想要再次开启导航或者一般这种情况小车到这里的时候一般就不会再移动了,所以我们可以更具当前监听到的结果反馈的状态正对返回的结果作出一些自创的运动方式,从而去恢复当前小车卡死的状态,

当然处理的方式有很多,可以再次发布航点,当我目前的恢复行为都失效后,则可以使用当前的方式,或在重启来将其恢复。

(1)这里我们使用一个C++的程序来监听Ros中move_base的运动状态
#include "ros/ros.h"
#include <move_base_msgs/MoveBaseAction.h>
#include <move_base_msgs/MoveBaseGoal.h>
 
  //获取到达目标的状态
void status_callback(const move_base_msgs::MoveBaseActionResult& msg)
{	
	if(msg.status.status == 3)
	{
		std::cout<<"the goal was achieved successfully!"<<std::endl;
	}
}
 
int main(int argc, char **argv)
{
    ros::init(argc, argv, "get_movebase_status");
    ros::NodeHandle n;
   
    // 订阅话题
	ros::Subscriber goal_sub =n.subscribe("move_base/result", 10, status_callback);
	
    ros::spin(); 
    return 0;
}

修复并产生响应的C++文件

(2) 根据move base的status和result做处理

在了解了状态和结果后,在这里我选用的是status里的stamp做判断,思路就是如果隔5s或10s没有接收到新的stamp,就判断move base已经crash了,这时就用重启指令重新启动move base并重新发布目标位置。具体代码如下:

//(1) 监听move base状态
#include <move_base_msgs/MoveBaseAction.h>
#include <move_base_msgs/MoveBaseGoal.h> 
#include <actionlib_msgs/GoalStatusArray.h>

  ros::Subscriber goal_sub =node.subscribe("move_base/status", 10, status_callback); //监听move base 状态
  ros::Subscriber BaseStatusSub = node.subscribe("/move_base/result",10,  BaseStatusSubCallback); //监听move base运行的结果
  BasegoalPub = node.advertise<geometry_msgs::PoseStamped>("/move_base_simple/goal", 1);//发布目标位置


ros::Time move_base_stamp;          
ros::Time move_base_stamp_last; 
int MoveBaseStatus = 0;
bool move_base_status = true;
bool relaunch_status = true;
int move_status = 0;

void status_callback(const actionlib_msgs::GoalStatusArray& msg)
{	
  move_base_stamp = msg.header.stamp;
  if(!msg.status_list.empty())
  	move_status = msg.status_list.back().status;

  // std::cout<<"move base stamp:"<< msg.header.stamp <<std::endl;
}

void BaseStatusSubCallback(const move_base_msgs::MoveBaseActionResult& msg)
{
  MoveBaseStatus=msg.status.status;
}

    if( move_base_stamp == move_base_stamp_last) move_base_counting++;
    else move_base_counting = 0;

    if (move_base_counting>=100)
    {
      move_base_status = false;
      move_base_counting = 0;
      if (relaunch_status == true) MoveBaseStatus = -1;
    }
    move_base_stamp_last = move_base_stamp;

	if (move_base_status==false)
    {
      if (relaunch_status == true)
      {
        ROS_INFO("Move base got killed!!!!!!!!!           Try to relaunch move_base");
        system("gnome-terminal -e 'bash -c \"roslaunch /opt/ros/melodic/share/move_base/move_base.launch; exec bash\"'");
        ros::Duration(5.0).sleep();
        relaunch_status = false;
      }
			
      base_goal.header.seq = 1;
      base_goal.header.stamp =ros::Time::now();
      base_goal.header.frame_id = "map";
      base_goal.pose.position.x = 0.0;
      base_goal.pose.position.y = 0.0;
      base_goal.pose.position.z = 0.0;
      base_goal.pose.orientation.x = 0.0;
      base_goal.pose.orientation.y = 0.0;
      base_goal.pose.orientation.z = 0.0;
      base_goal.pose.orientation.w = 1.0;

      BasegoalPub.publish(base_goal);
      ros::Duration(2.0).sleep();
      ROS_INFO("Publish move base goal!");
      if (MoveBaseStatus != -1)//这一步也很重要,就是说只有当result有变化才跳出现在的crash事后补救状态,不然就一直发布goal,因为有时候发布一次goal会没响应,要多发几次,这个跟topic这种通信方式有关,如果换成action或service就不会有这种Miss的问题
      {
        ROS_INFO("Recover success!");
        move_base_status = true;
        relaunch_status = true;
      }
    }


move_base_stamp: 记录当前时间
move_base_stamp_last:记录上一个时间
MoveBaseStatus = 0:记录当前result的结果
move_base_status = true: 用来判断move base是否crash,true是正常,false就是出问题了
relaunch_status = true: 启动一次move base,避免重复启动

move_base取消导航目标:

按前面的搜索方法,找到/move_base/cancel话题,并查看其话题的消息类型

topic type:actionlib_msgs/GoalID
rostopic pub /move_base/cancel (后续tab)


即发空即可取消
 

;