ROS复习笔记之——创建和运行动作服务器与客户端节点

之前博客《RO发布者S复习笔记之——系统框架及其编程规范》的(发布者节点和订阅者节点的创建和运行)一节中以及介绍如何创建发布者、订阅者、话题消息的通信。博客《 ROS复习笔记之——创建和运行服务服务器与客户端节点》复习了创建和运行服务服务器与客户端节点。本博文继这个博客,来复习一下创建和运行动作服务器与客户端节点。

动作与话题和服务不同,在异步、双向,以及请求和响应之间需要很长的时间的情况,以及需要中途结果值等需要更复杂的编程的情况中显的格外有用。

 

首先基于之前创建的功能包my_first_ros_pkg进行修改。由于依赖于m e s s ag e_generation、std_msgs、actionlib_msgs、actionlib和roscpp功能包,因此将这些作为了依赖选项。修改对应的功能包配置文件(package.xml)如下

  catkin

  roscpp
  std_msgs
  message_generation
  actionlib_msgs
  actionlib

  roscpp
  std_msgs
  message_generation
  actionlib_msgs
  actionlib


  roscpp
  std_msgs
  message_generation
  actionlib_msgs
  actionlib

然后修改构建配置文件(CMakeLists.txt)如下

首先添加依赖包

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
## 这是进行catkin构建时所需的组件包。
## 依赖包是message_generation、std_msgs和roscpp。如果这些包不存在,在构建过程中会发生错误。
find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
  std_msgs
  actionlib_msgs
  actionlib
)

然后声明动作文件

#添加要使用动作文件选项
## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )
##动作声明
add_action_files(
    FILES            #FILES会引用功能包目录中的srv目录中的*.srv文件
    Fibonacci.action
)

对应地,创建动作消息文件

roscd my_first_ros_pkg
mkdir action
cd action
gedit Fibonacci.action

如下图所示

ROS复习笔记之——创建和运行动作服务器与客户端节点_第1张图片

动作文件的内容如下

#goal definition
int32 order
---
#result definition
int32[] sequence
---
#feedback
int32[] sequence

在动作文件中,三个连字符(---)用作分隔符,第一个是goal消息,第二个是result消息,第三个是feedback消息。goal消息和result消息之间的关系与之前博客提到的srv文件相同,但主要区别在于feedback消息用于指定进程执行过程中的中间值传输。

除了可以在动作文件中找到的目标(goal)、结果(result)和反馈(feedback)之外,动作基本上还使用两个额外的消息:取消(cancel)和状态(status)。取消(cancel)消息使用actionlib_msgs/GoalID,它在动作运行时可以取消动作客户端和单独节点上的动作的执行。状态(status)消息可以根据状态转换(如PENDING、ACTIVE、PREEMPTED和SUCCEEDED12)检查当前动作的状态。

然后回到CMakeLists.txt文件,添加动作服务器节点

##添加名为action_server的动作服务器节点
add_executable(action_server src/action_server.cpp)

然后添加cpp文件

roscd my_first_ros_pkg/src
gedit action_server.cpp

代码如下所示:(这个代码很有参考意义,以类的形式来封装了,但是在这里先不详细的描述了)

#include  // ROS的基本头文件
#include  // 动作库头文件
#include  // FibonacciAction动作头文件(生成后自动生成)


class FibonacciAction
{
    protected:


    // 声明节点句柄
    ros::NodeHandle nh_;


    // 声明动作服务器
    actionlib::SimpleActionServer as_;


    // 用作动作名称
    std::string action_name_;


    // 声明用于发布的反馈及结果
    my_first_ros_pkg::FibonacciFeedback feedback_;
    my_first_ros_pkg::FibonacciResult result_;




    public:


    // 初始化动作服务器(节点句柄、动作名称、动作后台函数)
    FibonacciAction(std::string name) :
    as_(nh_, name, boost::bind(&FibonacciAction::executeCB, this, _1), false),
    action_name_(name)
    {
        as_.start();
    }


    ~FibonacciAction(void)
    {

    }


    // 接收动作目标(goal)消息并执行指定动作(此处为斐波那契数列)的函数。
    void executeCB(const my_first_ros_pkg::FibonacciGoalConstPtr &goal)
    {
        ros::Rate r(1); // 循环周期:1 Hz
        bool success = true; // 用作保存动作的成功或失败的变量
        // 斐波那契数列的初始化设置,也添加了反馈的第一个(0)和第二个消息(1)
        feedback_.sequence.clear();
        feedback_.sequence.push_back(0);
        feedback_.sequence.push_back(1);


        // 将动作名称、目标和斐波那契数列的两个初始值通知给用户
        ROS_INFO("%s: Executing, creating fibonacci sequence of order %i with seeds %i, %i",
        action_name_.c_str(), goal->order, feedback_.sequence[0], feedback_.sequence[1]);


        // 动作细节
        for(int i=1; i<=goal->order; i++)
        {
        // 从动作客户端得知动作取消
            if (as_.isPreemptRequested() || !ros::ok())
            {
                ROS_INFO("%s: Preempted", action_name_.c_str()); // 通知动作取消
                as_.setPreempted(); // 取消动作
                success = false; // 看作动作失败并保存到变量
                break;
            }

            // 除非有动作取消或已达成动作目标
            // 将当前斐波纳契数字加上前一个数字的值保存到反馈值。
            feedback_.sequence.push_back(feedback_.sequence[i] + feedback_.sequence[i-1]);
            as_.publishFeedback(feedback_); // 发布反馈。
            r.sleep(); // 按照上面定义的循环周期调用暂歇函数。
        }

        // 如果达到动作目标值,则将当前斐波那契数列作为结果值传输。
        if(success)
        {
            result_.sequence = feedback_.sequence;
            ROS_INFO("%s: Succeeded", action_name_.c_str());
            as_.setSucceeded(result_);
        }
    }
};


int main(int argc, char** argv) // 节点主函数
{
    ros::init(argc, argv, "action_server"); // 初始化节点名称
    FibonacciAction fibonacci("ros_tutorial_action"); // 声明Fibonacci (动作名: ros_tutorial_action)
    ros::spin(); // 等待动作目标
    return 0;
} 

然后回到CMakeLists.txt文件,添加依赖

add_dependencies(action_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(action_server ${catkin_LIBRARIES})

添加动作客户端节点

add_executable(action_client src/action_client.cpp)

然后添加对应的cpp文件

roscd my_first_ros_pkg/src
gedit action_client.cpp

代码如下

#include  // ROS的基本头文件
#include  // 动作库头文件
#include  // 动作目标状态头文件
#include  // FibonacciAction动作头文件(构建后自动生成)


int main (int argc, char **argv) // 节点主函数
{
    ros::init(argc, argv, "action_client"); // 初始化节点名称

    // 声明动作客户端(动作名称:ros_tutorial_action)
    actionlib::SimpleActionClient ac("ros_tutorial_action",
    true);

    ROS_INFO("Waiting for action server to start.");
    ac.waitForServer(); // 等待动作服务器启动


    ROS_INFO("Action server started, sending goal.");
    my_first_ros_pkg::FibonacciGoal goal; // 声明动作目标
    goal.order = 20; // 指定动作目标(进行20次斐波那契运算)
    ac.sendGoal(goal); // 发送动作目标


    // 设置动作完成时间限制(这里设置为30秒)
    bool finished_before_timeout = ac.waitForResult(ros::Duration(30.0));


    // 在动作完成时限内收到动作结果值时
    if (finished_before_timeout)
    {
        // 获取动作目标状态值并将其显示在屏幕上
        actionlib::SimpleClientGoalState state = ac.getState();
        ROS_INFO("Action finished: %s",state.toString().c_str());
    }
    else
        ROS_INFO("Action did not finish before the time out."); // 超过了动作完成时限的情况
    //exit
    return 0;
}

然后回到CMakeLists.txt文件,添加依赖

add_dependencies(action_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
target_link_libraries(action_client ${catkin_LIBRARIES})

总的CMakeLists.txt文件如下:

(注意,此处的CMakeLists.txt文件结合了之前博文介绍到的注释,至此应该可以比较全面的了解CMakeLists.txt文件的各项了。)

cmake_minimum_required(VERSION 2.8.3)
###***上面一条是指是操作系统中安装的cmake的最低版本。
###***由于它目前被指定为版本2.8.3,所以如果使用低于此版本的cmake,则必须更新版本。
project(my_first_ros_pkg)########project项是功能包的名称
#请注意,如果功能包名称与package.xml中的标记中描述的功能包名称不同,
#则在构建时会发生错误,因此需要注意。


#############################*****************************************************
## 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
## 这是进行catkin构建时所需的组件包。
## 依赖包是message_generation、std_msgs和roscpp。如果这些包不存在,在构建过程中会发生错误。
find_package(catkin REQUIRED COMPONENTS
  message_generation
  roscpp
  std_msgs
  actionlib_msgs
  actionlib
)



#############################*****************************************************
## System dependencies are found with CMake's conventions
##使用ROS以外的功能包时使用的方法。例如,使用Boost时,必须安装system功能包。
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()####在使用Python,也就是使用rospy时的配置选项。



#############################*****************************************************
################################################
## 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
# )
## 消息声明:MsgTutorial.msg
add_message_files(
    FILES###FILES将引用当前功能包目录的msg目录中的*.msg文件,自动生成一个头文件(*.h)
    MsgTutorial.msg
)



#############################*****************************************************
#添加要使用的服务文件的选项
## Generate services in the 'srv' folder
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )
## 服务声明:SrvTutorial.srv
add_service_files(
    FILES ###FILES会引用功能包目录中的srv目录中的*.srv文件
    SrvTutorial.srv
)



#############################*****************************************************
#添加要使用动作文件选项
## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )
##动作声明
add_action_files(
    FILES            #FILES会引用功能包目录中的srv目录中的*.srv文件
    Fibonacci.action
)



#############################*****************************************************
## Generate added messages and services with any dependencies listed here
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )
## 这是设置依赖性消息的选项。
## 如果未安装std_msgs,则在构建过程中会发生错误。
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

##使用dynamic_reconfigure时加载要引用的配置文件的设置
## 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 构建选项
## catkin功能包选项,描述了库、catkin构建依赖项和系统依赖的功能包。
catkin_package(
#  INCLUDE_DIRS include       #表示将使用INCLUDE_DIRS后面的内部目录include的头文件
#  LIBRARIES my_first_ros_pkg             #表示将使用随后而来的功能包的库
#  CATKIN_DEPENDS roscpp std_msgs        #后面指定如roscpp或std_msgs等依赖包
#  DEPENDS system_lib         #一个描述系统依赖包的设置
   LIBRARIES my_first_ros_pkg#表示将使用随后而来的功能包的库
   CATKIN_DEPENDS std_msgs roscpp
)




#############################*****************************************************
###########
## Build ##
###########

## 设置包含目录。
## Specify additional locations of header files
## Your package locations should be listed before other locations
# ${catkin_INCLUDE_DIRS}这意味着将引用每个功能包中的include目录中的头文件。
#当用户想指定一个额外的include目录时,写在${catkin_INCLUDE_DIRS}的下一行即可。
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)



#############################*****************************************************
#声明构建之后需要创建的库
#以下是引用位于my_first_ros_pkg功能包的src目录中的my_first_ros_pkg.cpp文件来创建my_first_ros_pkg库的命令。
## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/my_first_ros_pkg.cpp
# )



#############################*****************************************************
#在构建该库和可执行文件之前,如果有需要预先生成的有依赖性的消息或dynamic_reconfigure,则要先执行
## 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/my_first_ros_pkg_node.cpp)
add_executable(hello_world_node src/hello_world_node.cpp)
## topic_publisher节点的构建选项。
add_executable(topic_publisher src/topic_publisher.cpp)
## topic_subscriber节点的构建选项。
add_executable(topic_subscriber src/topic_subscriber.cpp)
## 这是service_server节点的构建选项。可执行文件
add_executable(service_server src/service_server.cpp)
## 这是service_client节点的构建选项。可执行文件
add_executable(service_client src/service_client.cpp)
##添加名为action_server的动作服务器节点
add_executable(action_server src/action_server.cpp)
##添加名为action_client的动作服务器节点
add_executable(action_client src/action_client.cpp)




#############################*****************************************************
#在构建库和可执行文件之前创建依赖消息和dynamic reconfigure的设置
## 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})
## topic_publisher节点的构建选项。
add_dependencies(topic_publisher ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## topic_subscriber节点的构建选项。
add_dependencies(topic_subscriber ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
## service_server节点的构建选项。附加依赖项。
add_dependencies(service_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
##service_client节点的构建选项。附加依赖项。
add_dependencies(service_client ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
##action_server节点的构建选项。附加依赖项。
add_dependencies(action_server ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})
##action_client节点的构建选项。附加依赖项。
add_dependencies(action_client ${${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}
# )
 target_link_libraries(hello_world_node 
   ${catkin_LIBRARIES}
 )
## topic_publisher节点的构建选项。
target_link_libraries(topic_publisher 
   ${catkin_LIBRARIES}
)
## topic_subscriber节点的构建选项。
target_link_libraries(topic_subscriber 
   ${catkin_LIBRARIES}
)
## service_server节点的构建选项。目标链接库
target_link_libraries(service_server 
    ${catkin_LIBRARIES}
)
## service_client节点的构建选项。目标链接库
target_link_libraries(service_client 
   ${catkin_LIBRARIES}
)
## action_server节点的构建选项。目标链接库
target_link_libraries(action_server 
   ${catkin_LIBRARIES}
)
## action_client节点的构建选项。目标链接库
target_link_libraries(action_client 
   ${catkin_LIBRARIES}
)




#############################*****************************************************
#创建官方发行版ROS功能包时使用的Install项目和用于单元测试的Testing项目。
#############
## 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
# install(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_my_first_ros_pkg.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)

然后编译

cd ~/catkin_ws && catkin_make

运行后发现报错

ROS复习笔记之——创建和运行动作服务器与客户端节点_第2张图片

原来是没有添加actionlib_msgs。修改如下

#############################*****************************************************
## Generate added messages and services with any dependencies listed here
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )
## 这是设置依赖性消息的选项。
## 如果未安装std_msgs,则在构建过程中会发生错误。
generate_messages(
   DEPENDENCIES 
   std_msgs
   actionlib_msgs
)

然后编译就没有问题了。测试结果如下所示

ROS复习笔记之——创建和运行动作服务器与客户端节点_第3张图片

 

 

参考资料

http://wiki.ros.org/Books/ROS_Robot_Programming_English

 

 

 

你可能感兴趣的:(ROS,移动机器人)