上篇我们以小乌龟为实例,介绍ROS服务基本概念和相关指令用法,本篇介绍如何自定义服务类型以及实现服务。
一. 创建服务类型
roscd beginner_tutorials/
mkdir srv
cd srv
touch AddTwoInts.srv
将以下内容复制进去:
int32 a
int32 b
---
int32 sum
“---”上方表示 request数据类型,两个加数,下面是 response类型,表示加数之和。
至此我们创建了一个自定义服务类型。
文件链接:
ahuer2435/ros_programgithub.com和消息类型一样,服务类型最终也是要编译为.h头文件,才能被程序使用,所以以下步骤与
ROS 消息mp.weixin.qq.com篇中消息配置非常相似。
二. 配置package.xml
在 package.xml文件中添加如下三行,使用任何自定义消息,都必须使用这三行。
<build_depend>message_generation</build_depend>
<build_export_depend>message_runtime</build_export_depend>
<exec_depend>message_runtime</exec_depend>
文件链接:
https://github.com/ahuer2435/ros_program/blob/master/catkin_ws/src/beginner_tutorials/package.xmlgithub.com这一步在
ROS 消息mp.weixin.qq.com篇已经做过,这里不必再做,但是如果是在其他包中,新建服务类型,必须要做这一步。
三. 配置CMakeLists.txt
将 message_generation加入到 find_package(), find_package()要与 package.xml文件中的 build_depend一致,表示编译时依赖的包。
将 AddTwoInts.srv加入 add_service_files(),表示要编译的服务类型文件。
放开 generate_messages(),表示生成消息头文件。
将 message_runtime加入 catkin_package()表示导出运行时依赖。
文件链接:
https://github.com/ahuer2435/ros_program/blob/master/catkin_ws/src/beginner_tutorials/CMakeLists.txtgithub.com除了第2步以外,其他与
ROS 消息mp.weixin.qq.com篇保持一致,第二步添加要编译的服务类型文件。至此我们完成服务类型配置。
四. 验证服务类型
rossrv show beginner_tutorials/AddTwoInts
输出如下:
int32 a
int32 b
int32 sum
表明服务类型创建成功。
五. 编译服务类型
catkinmake
编译完之后,会生成三个头文件,如下:
devel/include/beginnertutorials/AddTwoInts.h
devel/include/beginnertutorials/AddTwoIntsRequest.h
devel/include/beginnertutorials/AddTwoIntsResponse.h
六. 使用服务类型
和话题类似,ROS服务也有两部分构成:client和 server, client发送请求, server提供服务,响应请求。
1. 创建server节点
cp listenermsg.cpp listenersrv.cpp
做如下修改:第2行,引入服务类型头文件。第16行,修改节点名。第20行,定义一个服务端,使用 ServiceServer类,定义服务者 service,通过句柄 NodeHandle的 advertiseService方法初始化,此方法需要指令所提供的服务名字和服务回调函数,前者相当于订阅者的话题名,后者相当于订阅者的回调函数。第5行到第11行,定义回调函数,注意其参数类型需要为服务类型,有两项,前者是请求类型,对应第5步中的 AddTwoIntsRequest.h头文件,后者是服务类型,对应 AddTwoIntsResponse.h,
至于头文件内容不需要关心,直接引用其中数据即可。修改后源码如下:
#include "ros/ros.h" //ros.h 包含ros程序常用的头文件。
#include "beginner_tutorials/AddTwoInts.h" //引用服务类型头文件
//回调函数定义,参数类型需要与所服务类型保持一致
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res)
{
res.sum = req.a + req.b; //更新响应值。
ROS_INFO("request: x=%d, y=%d", req.a, req.b);
ROS_INFO("sending back response: [%d]", res.sum);
return true;
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener_srv"); //初始化ros,并命令节点名。
ros::NodeHandle n; //初始化节点,调用ros api接口句柄。
//定义一个服务者,服务名字:add_two_ints,服务回调函数:add
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ros::spin(); //不返回函数,监听服务节点中的回调队列,并执行回调函数。
return 0; //正常运行时,不会执行到。
}
可参考链接:
https://github.com/ahuer2435/ros_program/blob/master/catkin_ws/src/beginner_tutorials/src/listener/listener_srv.cppgithub.com2. 创建client节点
cp talkermsg.cpp talkersrv.cpp
做如下修改:第2行,引入服务类型头文件。第6行,修改节点名。第9行,定义一个客户端,使用 ServiceClient类定义客户端 client,通过句柄 NodeHandle的 serviceClient()函数初始化。serviceClient()是一个模板函数,模板参数为服务类型,函数参数为服务名,这两个都需要与服务端保持一致。第12行,定义一个服务类型变量。第14和15行,更新变量。第16行,发送请求,并判断请求响应结果,如果正常影响,则打印响应值,否则报错。修改之后源码如下:
#include "ros/ros.h" //ros.h 包含ros程序常用的头文件。
#include "beginner_tutorials/AddTwoInts.h" //引用服务类型头文件
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker_srv"); //初始化ros,并命令节点名。
ros::NodeHandle n; //初始化节点,调用ros api接口句柄。
//定义一个客户端
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
ros::Rate loop_rate(1); //设置发布频率
int count = 0; //发布请求计数
beginner_tutorials::AddTwoInts srv; //定义服务变量
while (ros::ok()){ //检测节点是否正常运行
srv.request.a = count; //更新服务变量值。
srv.request.b = count+1;
if (client.call(srv)) { //发送请求
ROS_INFO("Sum: %d", srv.response.sum); //收到请求响应。
}
else {
ROS_ERROR("Failed to call service add_two_ints"); //请求响应失败
return 1;
}
ros::spinOnce(); //处理回调函数,会返回,这里也可以不加,因为此节点没有回调函数。
loop_rate.sleep(); //与Line 10配套使用,用于控制发布频率。
++count; //消息计数。
}
return 0;
}
可参考链接:
https://github.com/ahuer2435/ros_program/blob/master/catkin_ws/src/beginner_tutorials/src/talker/talker_msg.cppgithub.com3. 添加到CMakeLists.txt
addexecutable(listenersrv src/listener/listenersrv.cpp)
targetlinklibraries(listenersrv ${catkinLIBRARIES})
adddependencies(listenersrv ${${PROJECTNAME}EXPORTEDTARGETS} ${catkinEXPORTEDTARGETS})
addexecutable(talkersrv src/talker/talkersrv.cpp)
targetlinklibraries(talkersrv ${catkinLIBRARIES})
adddependencies(talkersrv ${${PROJECTNAME}EXPORTEDTARGETS} ${catkinEXPORTEDTARGETS})
添加编译新创建的两个节点项。
文件链接:
https://github.com/ahuer2435/ros_program/blob/master/catkin_ws/src/beginner_tutorials/CMakeLists.txtgithub.com4. 编译运行
catkin_make
roscore
rosrun beginnertutorials listenersrv
rosrun beginnertutoria talker_srv
运行结果如下:
七. 小结
ROS服务与话题比较类似,可以对应ROS 消息篇对比学习,其中步骤也比较相似。ROS服务与话题的主要区别是,消息发送者发送数据后,需要对方做出回应,在实际项目开发过程中,这两者都是比较常用的通信方式。下一篇介绍ROS Parameter。