Ros noetic Move_base 监听Move状态 实战使用教程

本文详细介绍了如何从零开始创建一个监听ROS noetic Move_base状态的节点包,针对机器人在遇到临时障碍物时停止运动的问题提出解决方案。通过监听Move_base状态并解析Action状态机,当检测到震荡失败时,采取措施恢复机器人导航。内容包括创建包、编写cpp文件、CMakeLists修改、启动节点和仿真测试,以及源码解读和事后处理方案。
摘要由CSDN通过智能技术生成

前言:

        承接上一篇文章,在上一文中我们了解到move_base有几种监听的状态,我一文章中我将开源全部监听代码,本文将从0开始建立监听包,并覆上全部的工程代码,和仿真实操结果。

        本文,还将解决当临时障碍物与机身相交时,机器人回人为自己被“卡住”,局部规划器规划的速度为0,机器人停止运动;即使更换新的任何目标点,机器人均停止不动;move_base假死状态,在经过恢复行为后无果,不知道新的前进途径的情况。用于恢复状态机。

建立监听节点包:

1) 创建包

cd catkin_ws/src 
catkin_create_pkg listen_move_base roscpp rospy std_msgs

 可见已经生成了节点

2)创建cpp文件:

roscd listen_move_base
cd src 
touch listen_move_base.cpp

3) 输入:

#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 == 0)
	{
		std::cout<<"目标尚未由操作服务器处理,还没有触发"<<std::endl;
	}

	if(msg.status.status == 1)
	{
		std::cout<<"动作服务器当前正在处理目标,正在前进"<<std::endl;
	}

	if(msg.status.status == 2)
	{
		std::cout<<"目标在开始执行后收到一个取消请求,并已完成执行"<<std::endl;
	}

	if(msg.status.status == 3)
	{
		std::cout<<"动作服务器成功实现目标(终端状态)"<<std::endl;
	}

	if(msg.status.status == 4)
	{
		std::cout<<"由于##到某种故障(终端状态)"<<std::endl;
	}

	if(msg.status.status == 5)
	{
		std::cout<<"目标未经处理就被动作服务器拒绝,因为目标无法实现或无效(终端状态)"<<std::endl;
	}

  if(msg.status.status == 6)
	{
		std::cout<<"目标在开始执行后收到一个取消请求,尚未完成执行"<<std::endl;
	}

  if(msg.status.status == 7)
	{
		std::cout<<"目标在开始执行之前收到一个取消请求,但操作服务器尚未确认目标已取消"<<std::endl;
	}

  if(msg.status.status == 8)
	{
		std::cout<<"目标在开始执行之前收到一个取消请求,已成功取消(终端状态)"<<std::endl;
	}

  if(msg.status.status == 9)
	{
		std::cout<<"动作客户端可以确定目标已丢失。这不应该由操作服务器通过有线发送"<<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;
}

4)修改CMakeList.txt

cmake_minimum_required(VERSION 3.0.2)
project(listen_move_base)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend tag for "message_generation"
##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
##     but can be declared for certainty nonetheless:
##     * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )

## Generate services in the 'srv' folder
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

## Generate added messages and services with any dependencies listed here
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
##   * add "dynamic_reconfigure" to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * uncomment the "generate_dynamic_reconfigure_options" section below
##     and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
#   cfg/DynReconf1.cfg
#   cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES listen_move_base
   CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations

include_directories(include ${catkin_INCLUDE_DIRS} )

add_executable(${PROJECT_NAME}_node src/listen_move_base.cpp)

add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

target_link_libraries(${PROJECT_NAME}_node
  ${catkin_LIBRARIES}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/listen_move_base.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/listen_move_base_node.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )

#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_listen_move_base.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

5) 主要修改地方

Build 加入cpp 文件路径和依赖

6) 打开编译依赖文件

7) 顺利通过

8) 编写launch文件启动节点

参考这里考虑到有node的节点,因为c++中区域不明显

listen_move_base_node

<?xml version="1.0"?>

<launch>
    <node pkg="listen_move_base" type="listen_move_base_node" name="get_movebase_status" output="screen">
    </node>
</launch>

9) 启动

10)启动真实的仿真界面

启动Rviz

启动节点

roslaunch listen_move_base listen_move_base.launch 

由此可见监听成功,因为action的整个工作状态是异步通信,必须要等到这个过程完成

就像A和B在丢箱子,必须要等到B拿到箱子后,才会给A讲,我拿到箱子了

至于A怎么反应是A的事情

11)关于收到箱子后的B反馈给A ,A作何反映这一点就比较有意思了,如下:

        这里是A可以执行的一些反馈,从而修正了Move_base的导航假死状态

        当临时障碍物与机身相交时,机器人回人为自己被“卡住”,局部规划器规划的速度为0,机器人停止运动;即使更换新的任何目标点,机器人均停止不动;

12)源码解读

具体步骤:①触发条件的判断

当接收到目标点Goal后,move_base的执行状态会通过MoveBaseActionResult中的status反馈([原创]actionlib中ActionServer和ActionClient的状态机),

其中当执行结果为失败时一般反馈aborted(即status= 4)

造成aborted的原因有很多,其中以全局规划失败

局部规划失败和震荡失败三种情况为主(关于三种情况的介绍和震荡的定义)

以上的这种机器人停止不动会触发震荡失败。

但触发震荡失败检测必须设定oscillation_timeout和oscillation_distance才能开启(oscillation_timeout表示执行修复操作之前,允许的震荡时间是几秒。 值0意味着永不超时)!

move_base.cpp源码中,当出现震荡信号时,status= 4,

且text会被赋值字符串Robot is oscillating. Even after executing recovery behaviors.  

13)解决方案

我们在上诉中导出了状态这里只需要用订阅到的机器人状态即可完成解决

1. Problem

在导航时,遇到一个情况,就是突然很多人出现在机器人附近,它没办法规划轨迹,然后启动move base的terminal直接就crash了,之后的操作就没办法进行了

2. Solution

解决方案有两种:

    事前:在发生crash前处理突发状况
    事后:发生crash后,怎么重新启动move base以及重新发布目标位置

在这里只提供一个事后的处理方案,事前的解决方案比较麻烦,要到算法里头去找,这个就不容易弄了,事后补救的方案还是容易实现。

3.这里我们主打选择事后处理的方式进行处理:
代码解析:
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本身就会一直发布自身状态信息,topic名是move_base/status,信息类型是actionlib_msgs/GoalStatusArray,包含如下信息
    # 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
代码解析:
if( move_base_stamp == move_base_stamp_last) move_base_counting++:如果前后两个时间戳是一样的,累计数就+1
else move_base_counting = 0:时间戳不一样就归0
if (move_base_counting>=100):当到100,如果按10Hz频率运行,就是10s,具体根据节点的频率和需求来算
if (relaunch_status == true) MoveBaseStatus = -1:把result的结果设成-1,即非列表里的数
if (move_base_status==false):这时就是出现crash的情况了,开始事后补救
system("gnome-terminal -e 'bash -c \"roslaunch /opt/ros/melodic/share/move_base/move_base.launch; exec bash\"'"):这句很重要!是重新启动move base的指令,最重要的一点是,把move_base.launch放到/opt/ros/melodic/share/move_base路径下,放在/home下启动不了的,当然,如果有大神知道在/home下启动方法,还望告知,感谢
if (relaunch_status == true):启动一次后,就不启动了,不然就一直重复启动
BasegoalPub.publish(base_goal):下面就是重新发布goal的位置,根据实际情况调整
if (MoveBaseStatus != -1):这一步也很重要,就是说只有当result有变化才跳出现在的crash事后补救状态,不然就一直发布goal,因为有时候发布一次goal会没响应,要多发几次,这个跟topic这种通信方式有关,如果换成action或service就不会有这种Miss的问题
4.源码:
//(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;
      }
    }
 
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忒懂先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值