在ROS入门五中,我们创建了一个消息文件和一个服务文件。在我们编写的消息发布节点和订阅节点中,使用了创建的消息文件,但还没有测试,在本篇中我们将测试这两个节点。此外,我们创建的服务文件还没有使用,本篇中我们会编写‘服务节点’和‘请求服务节点’(也可以叫做服务节点和客户节点)来使用这个服务文件。
1、消息发布节点和订阅节点的测试
在ROS入门五中,我们编写了消息发布器和订阅器,现在我们来测试一下它们是否可以进行通信。由于上一篇写得太长(累人)了,所以把测试放在这一篇里。
在测试之前要进行编译,我们先要在CMakeList文件中加入编译相关的信息。打开终端(ros安装在root下的话,打开新终端均要进入sudo模式),运行roscore:
roscore
打开新的终端,输入:
roscd beginner_tutorialsgedit CMakeLists.txt
此时打开了我们需要编辑的文件,找到如下位置(其实未必要在这个位置,只是为了符合文件的注释信息,它提示加在哪,我们就加在哪):
############# Build #############
确保下面包含以下代码,如果没有则添加上:
include_directories( include ${catkin_INCLUDE_DIRS})add_executable(talker src/talker.cpp)target_link_libraries(talker ${catkin_LIBRARIES})add_dependencies(talker beginner_tutorials_generate_messages_cpp)add_executable(listener src/listener.cpp)target_link_libraries(listener ${catkin_LIBRARIES})add_dependencies(listener beginner_tutorials_generate_messages_cpp)
add_executable和target_link_libraries会生成可执行文件talker 和 listener, 默认存储到devel space目录下,具体是在~/catkin_ws/devel/lib/beginner_tutorials中。add_dependencies为可执行文件添加对生成的消息文件的依赖,确保自定义消息的头文件在被使用之前已经被生成。因为 catkin 把所有的 package 并行编译,所以如果你要使用其他 catkin 工作空间中其他 package 的消息,你同样也需要添加对他们各自生成的消息文件的依赖。
保存后退出。现在进行编译,输入:
cd ~/catkin_wscatkin_makesource ./devel/setup.bash
现在可以运行talker节点和listener节点了,终端输入:
rosrun beginner_tutorials talker
新的终端输入:
rosrun beginner_tutorials listener
可以看到输出是这样的:
这说明我们编写的节点通信成功。
2、服务节点和请求服务节点的编写
在ROS入门四中,我们学习过节点之间通信的两种方式。其一是话题,节点之间通过话题发布或订阅消息进行交流,这一种交流方式我们已经在刚才实现了。其二是服务,服务允许节点发送请求(request)并获得一个响应(response),现在我们来实现这种通信方式。
之前的终端要保留roscore终端,其余可以关闭。打开终端(注意sudo模式),输入:
roscd beginner_tutorials
(1)服务节点
在 src 里创建 add_two_ints_server.cpp 文件,终端输入:
touch src/add_two_ints_server.cpp
编辑此文件,终端输入:
gedit src/add_two_ints_server.cpp
将如下代码粘贴到文件内,保存后退出:
#include "ros/ros.h"#include "beginner_tutorials/AddTwoInts.h"//beginner_tutorials/AddTwoInts.h是由编译系统自动创建的AddTwoInts.srv文件生成的对应该srv文件的头文件。bool add(beginner_tutorials::AddTwoInts::Request &req, beginner_tutorials::AddTwoInts::Response &res)//函数的参数的数据类型都定义在AddTwoInts.srv文件内部,函数返回一个bool值//函数的功能是两个int值求和,int值从Request里面获取,而返回数据装入Response内。//我们上次创建的srv文有两个Request参数(a和b)和一个Response参数(sum),见ROS入门四//Request参数在此函数中引用为req,Response参数引用为res//由于是引用调用参数,在这里改变了形式参数值后,实际参数的值也会改变//如果你不知道我在讲什么,你可能得去复习C++{ res.sum = req.a + req.b; ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b); ROS_INFO("sending back response: [%ld]", (long int)res.sum); return true;}//进行求和操作,Response的sum等于Request的a和b之和//然后使用ROS_INFO显示Request和Response的信息//完成计算后返回true值int main(int argc, char **argv){ ros::init(argc, argv, "add_two_ints_server"); //节点的名称为add_two_ints_server ros::NodeHandle n; //节点的句柄为n ros::ServiceServer service = n.advertiseService("add_two_ints", add); //用ros::ServiceServer声明一个服务器,其名叫service //通过节点的advertiseService()函数创建这个服务器 //这个服务器提供一个名叫“add_two_ints”的服务,调用‘add’函数实现这个服务 //advertiseService()通过master广播这个服务,使其他节点知道可以调用这个服务 ROS_INFO("Ready to add two ints."); //显示服务节点准备就绪 ros::spin(); return 0;}
在ROS入门五中已经非常详细地讲解了消息发布节点和订阅节点的代码,如果大家仔细跟下来,这里的代码类比一下即可很容易地理解,关键的信息已经写在注释中。下面编写请求服务节点。
(2)请求服务节点(客户节点)
在 src 里创建 add_two_ints_client.cpp 文件,终端输入:
touch src/add_two_ints_client.cpp
编辑此文件,终端输入:
gedit src/add_two_ints_client.cpp
将如下代码粘贴到文件内,保存后退出:
#include "ros/ros.h"#include "beginner_tutorials/AddTwoInts.h"#include int main(int argc, char **argv){r_tutorials::AddTwoInts srv; ros::init(argc,argv,"add_two_ints_client");//节点的名称为add_two_ints_client if (argc !=3) { ROS_INFO("usage:add_two_ints_client X Y"); return 1; } //我们要实现的功能是客户节点输入2个参数,服务节点返回它们的和 //如果输入的参数不是2个就显示重新输入,并return 1,不执行后面的代码 //至于为什么是“argc !=3”,在ROS入门五中详细解释了, //argv第一个位置argv[0]存储程序名,后面才存储参数,argc是argv的长度,故argc=参数个数+1 ros::NodeHandle n; //节点句柄 ros::ServiceClient client = n.serviceClient<:addtwoints>("add_two_ints"); //用节点的serviceClient函数创建一个名为client的客户端 //客户端输入的参数要符合AddTwoInts.srv文件的规定 //客户端调用名为“add_two_ints”的服务 beginner_tutorials::AddTwoInts srv; //实例化一个由ROS编译系统自动生成的service类 //一个service类包含两个成员request和response //同时也包括两个类定义Request和Response。给其request成员赋值: srv.request.a = atoll(argv[1]);//atoll将字符串内容转为long int型整数 srv.request.b = atoll(argv[2]); if (client.call(srv)) //调用service,调用的时候占用进程阻止其他代码的执行,一旦调用完成,将返回调用结果 //调用成功,call()函数将返回true,srv.response里面的值将是合法的值 //调用失败,call()函数将返回false,srv.response里面的值将是非法的 { ROS_INFO("Sum: %ld", (long int)srv.response.sum); } else { ROS_ERROR("Failed to call service add_two_ints"); return 1; } return 0;}
3、服务节点和客户节点的测试
在测试之前要进行编译,我们先要在CMakeList文件中加入编译相关的信息。终端输入:
roscd beginner_tutorialsgedit CMakeLists.txt
此时打开了我们需要编辑的文件,确保下面包含以下代码,如果没有则添加上:
add_executable(add_two_ints_server src/add_two_ints_server.cpp)target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})add_dependencies(add_two_ints_server beginner_tutorials_gencpp)add_executable(add_two_ints_client src/add_two_ints_client.cpp)target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
add_executable和target_link_libraries会生成可执行文件add_two_ints_server 和 add_two_ints_client,默认存储到devel目录下~/catkin_ws/devel/lib/beginner_tutorials中。add_dependencies为可执行文件添加对生成的消息文件的依赖。
保存后退出。现在进行编译,输入:
cd ~/catkin_wscatkin_makesource ./devel/setup.bash
现在可以运行服务节点和客户节点了,终端输入:
rosrun beginner_tutorials add_two_ints_server
可以看到:
[ INFO] [1598254022.527098164]: Ready to add two ints.
新的终端输入:
rosrun beginner_tutorials add_two_ints_client 500 21
可以看到,服务节点和客户节点的输出分别为:
[ INFO] [1598254022.527098164]: Ready to add two ints.[ INFO] [1598254120.630279318]: request: x=500, y=21[ INFO] [1598254120.630301636]: sending back response: [521]
[ INFO] [1598254120.630445027]: Sum: 521
这儿的整个流程是:服务节点广播服务,客户节点输入request参数到中间文件,并请求服务,服务节点从中间文件接收两个参数,求出其和,返回response参数到中间文件,客户节点从中间文件处获得response参数。
以上就是今天的内容啦,按照ROS入门三所学的计算机图级的内容和ROS入门四的安排,接下来我们会学习有关bags等的知识。
如果有疑问或建议,欢迎私信我,小出和你一起加油啦~
作者:小出
图源网络
成长路上,小出陪你
让我们一起,见证你的努力