之前博客《RO发布者S复习笔记之——系统框架及其编程规范》的(发布者节点和订阅者节点的创建和运行)一节中以及介绍如何创建发布者、订阅者、话题消息的通信。博客《 ROS复习笔记之——创建和运行服务服务器与客户端节点》复习了创建和运行服务服务器与客户端节点。本博文继这个博客,来复习一下创建和运行动作服务器与客户端节点。
动作与话题和服务不同,在异步、双向,以及请求和响应之间需要很长的时间的情况,以及需要中途结果值等需要更复杂的编程的情况中显的格外有用。
首先基于之前创建的功能包my_first_ros_pkg进行修改。由于依赖于m e s s ag e_generation、std_msgs、actionlib_msgs、actionlib和roscpp功能包,因此将这些作为了依赖选项。修改对应的功能包配置文件(package.xml)如下
<buildtool_depend>catkin</buildtool_depend>
<build_depend>roscpp</build_depend>
<build_depend>std_msgs</build_depend>
<build_depend>message_generation</build_depend>
<build_depend>actionlib_msgs</build_depend>
<build_depend>actionlib</build_depend>
<build_export_depend>roscpp</build_export_depend>
<build_export_depend>std_msgs</build_export_depend>
<build_export_depend>message_generation</build_export_depend>
<build_export_depend>actionlib_msgs</build_export_depend>
<build_export_depend>actionlib</build_export_depend>
<exec_depend>roscpp</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>message_generation</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
<exec_depend>actionlib</exec_depend>
然后修改构建配置文件(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
如下图所示
动作文件的内容如下
#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/ros.h> // ROS的基本头文件
#include <actionlib/server/simple_action_server.h> // 动作库头文件
#include <my_first_ros_pkg/FibonacciAction.h> // FibonacciAction动作头文件(生成后自动生成)
class FibonacciAction
{
protected:
// 声明节点句柄
ros::NodeHandle nh_;
// 声明动作服务器
actionlib::SimpleActionServer<my_first_ros_pkg::FibonacciAction> 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/ros.h> // ROS的基本头文件
#include <actionlib/client/simple_action_client.h> // 动作库头文件
#include <actionlib/client/terminal_state.h> // 动作目标状态头文件
#include <my_first_ros_pkg/FibonacciAction.h> // FibonacciAction动作头文件(构建后自动生成)
int main (int argc, char **argv) // 节点主函数
{
ros::init(argc, argv, "action_client"); // 初始化节点名称
// 声明动作客户端(动作名称:ros_tutorial_action)
actionlib::SimpleActionClient<my_first_ros_pkg::FibonacciAction> 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中的<name>标记中描述的功能包名称不同,
#则在构建时会发生错误,因此需要注意。
#############################*****************************************************
## 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
运行后发现报错
原来是没有添加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
)
然后编译就没有问题了。测试结果如下所示
参考资料
http://wiki.ros.org/Books/ROS_Robot_Programming_English