ROS教程(十三):编写简单的服务器和客户端

编写简单的服务器和客户端 (C++)

Description:  本教程介绍如何用 C++ 编写服务器 Service 和客户端 Client 节点。

Tutorial Level:  BEGINNER

Next Tutorial:   测试简单的Service和Client  

编写Service节点

这里,我们将创建一个简单的service节点("add_two_ints_server"),该节点将接收到两个整形数字,并返回它们的和。

进入先前你在catkin workspace教程中所创建的beginner_tutorials包所在的目录:

cd ~/catkin_ws/src/beginner_tutorials

请确保已经按照creating the AddTwoInts.srv教程的步骤创建了本教程所需要的srv(确保选择了对应的编译系统“catkin”和“rosbuild”)。

代码

在beginner_tutorials包中创建src/add_two_ints_server.cpp文件,并复制粘贴下面的代码:

切换行号显示
   1 #include "ros/ros.h"
   2 #include "beginner_tutorials/AddTwoInts.h"
   3 
   4 bool add(beginner_tutorials::AddTwoInts::Request  &req,
   5          beginner_tutorials::AddTwoInts::Response &res)
   6 {
   7   res.sum = req.a + req.b;
   8   ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
   9   ROS_INFO("sending back response: [%ld]", (long int)res.sum);
  10   return true;
  11 }
  12 
  13 int main(int argc, char **argv)
  14 {
  15   ros::init(argc, argv, "add_two_ints_server");
  16   ros::NodeHandle n;
  17 
  18   ros::ServiceServer service = n.advertiseService("add_two_ints", add);
  19   ROS_INFO("Ready to add two ints.");
  20   ros::spin();
  21 
  22   return 0;
  23 }

代码解释

现在,让我们来逐步分析代码。

切换行号显示
   1 #include "ros/ros.h"
   2 #include "beginner_tutorials/AddTwoInts.h"
   3 

beginner_tutorials/AddTwoInts.h是由编译系统自动根据我们先前创建的srv文件生成的对应该srv文件的头文件。

切换行号显示
   4 bool add(beginner_tutorials::AddTwoInts::Request  &req,
   5          beginner_tutorials::AddTwoInts::Response &res)

这个函数提供两个int值求和的服务,int值从request里面获取,而返回数据装入response内,这些数据类型都定义在srv文件内部,函数返回一个boolean值。

切换行号显示
   6 {
   7   res.sum = req.a + req.b;
   8   ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
   9   ROS_INFO("sending back response: [%ld]", (long int)res.sum);
  10   return true;
  11 }

现在,两个int值已经相加,并存入了response。然后一些关于request和response的信息被记录下来。最后,service完成计算后返回true值。

切换行号显示
  18   ros::ServiceServer service = n.advertiseService("add_two_ints", add);

这里,service已经建立起来,并在ROS内发布出来。

编写Client节点

代码

在beginner_tutorials包中创建src/add_two_ints_client.cpp文件,并复制粘贴下面的代码:

切换行号显示
   1 #include "ros/ros.h"
   2 #include "beginner_tutorials/AddTwoInts.h"
   3 #include <cstdlib>
   4 
   5 int main(int argc, char **argv)
   6 {
   7   ros::init(argc, argv, "add_two_ints_client");
   8   if (argc != 3)
   9   {
  10     ROS_INFO("usage: add_two_ints_client X Y");
  11     return 1;
  12   }
  13 
  14   ros::NodeHandle n;
  15   ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");
  16   beginner_tutorials::AddTwoInts srv;
  17   srv.request.a = atoll(argv[1]);
  18   srv.request.b = atoll(argv[2]);
  19   if (client.call(srv))
  20   {
  21     ROS_INFO("Sum: %ld", (long int)srv.response.sum);
  22   }
  23   else
  24   {
  25     ROS_ERROR("Failed to call service add_two_ints");
  26     return 1;
  27   }
  28 
  29   return 0;
  30 }

代码解释

现在,让我们来逐步分析代码。

切换行号显示
  15   ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");

这段代码为add_two_ints service创建一个client。ros::ServiceClient 对象待会用来调用service。

切换行号显示
  16   beginner_tutorials::AddTwoInts srv;
  17   srv.request.a = atoll(argv[1]);
  18   srv.request.b = atoll(argv[2]);

这里,我们实例化一个由ROS编译系统自动生成的service类,并给其request成员赋值。一个service类包含两个成员requestresponse。同时也包括两个类定义RequestResponse

切换行号显示
  19   if (client.call(srv))

这段代码是在调用service。由于service的调用是模态过程(调用的时候占用进程阻止其他代码的执行),一旦调用完成,将返回调用结果。如果service调用成功,call()函数将返回true,srv.response里面的值将是合法的值。如果调用失败,call()函数将返回false,srv.response里面的值将是非法的。

编译节点

再来编辑一下beginner_tutorials里面的CMakeLists.txt,文件位于~/catkin_ws/src/beginner_tutorials/CMakeLists.txt,并将下面的代码添加在文件末尾:

https://raw.github.com/ros/catkin_tutorials/master/create_package_srvclient/catkin_ws/src/beginner_tutorials/CMakeLists.txt

切换行号显示
  27 add_executable(add_two_ints_server src/add_two_ints_server.cpp)
  28 target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
  29 add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
  30 
  31 add_executable(add_two_ints_client src/add_two_ints_client.cpp)
  32 target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
  33 add_dependencies(add_two_ints_client beginner_tutorials_gencpp)

这段代码将生成两个可执行程序"add_two_ints_server"和"add_two_ints_client",这两个可执行程序默认被放在你的devel space下的包目录下,默认为~/catkin_ws/devel/lib/share/<package name>。你可以直接调用可执行程序,或者使用rosrun命令去调用它们。它们不会被装在<prefix>/bin目录下,因为当你在你的系统里安装这个包的时候,这样做会污染PATH变量。如果你希望在安装的时候你的可执行程序在PATH变量里面,你需要设置一下install target,请参考:catkin/CMakeLists.txt

关于CMakeLists.txt文件更详细的描述请参考:catkin/CMakeLists.txt

现在运行catkin_make命令:

# In your catkin workspace
cd ~/catkin_ws
catkin_make

如果你的编译过程因为某些原因而失败:

现在你已经学会如何编写简单的服务器 Service 和客户端 Client,开始测试简单的Service和Client吧。



编写简单的Service和Client (Python)

Description:  本教程介绍如何用Python编写Service和Client节点。

Tutorial Level:  BEGINNER

Next Tutorial:   测试简单的Service和Client  

Writing a Service Node

Here we'll create the service ("add_two_ints_server") node which will receive two ints and return the sum.

Change directory into the beginner_tutorials package, you created in the earlier tutorial, creating a package:

$ roscd beginner_tutorials

Please make sure you have followed the directions in the previous tutorial for creating the service needed in this tutorial, creating the AddTwoInts.srv (be sure to choose the right version of build tool you're using at the top of wiki page in the link).

The Code

Create the scripts/add_two_ints_server.py file within the beginner_tutorials package and paste the following inside it:

切换行号显示
   1 #!/usr/bin/env python
   2 
   3 from beginner_tutorials.srv import *
   4 import rospy
   5 
   6 def handle_add_two_ints(req):
   7     print "Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b))
   8     return AddTwoIntsResponse(req.a + req.b)
   9 
  10 def add_two_ints_server():
  11     rospy.init_node('add_two_ints_server')
  12     s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
  13     print "Ready to add two ints."
  14     rospy.spin()
  15 
  16 if __name__ == "__main__":
  17     add_two_ints_server()

Don't forget to make the node executable:

  • chmod +x scripts/add_two_ints_server.py

The Code Explained

Now, let's break the code down.

There's very little to writing a service using rospy. We declare our node using init_node() and then declare our service:

切换行号显示
  12     s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)

This declares a new service named add_two_ints with the AddTwoInts service type. All requests are passed to handle_add_two_ints function. handle_add_two_ints is called with instances of AddTwoIntsRequest and returns instances of AddTwoIntsResponse.

Just like with the subscriber example, rospy.spin() keeps your code from exiting until the service is shutdown.

Writing the Client Node

The Code

Create the scripts/add_two_ints_client.py file within the beginner_tutorials package and paste the following inside it:

切换行号显示
   1 #!/usr/bin/env python
   2 
   3 import sys
   4 import rospy
   5 from beginner_tutorials.srv import *
   6 
   7 def add_two_ints_client(x, y):
   8     rospy.wait_for_service('add_two_ints')
   9     try:
  10         add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
  11         resp1 = add_two_ints(x, y)
  12         return resp1.sum
  13     except rospy.ServiceException, e:
  14         print "Service call failed: %s"%e
  15 
  16 def usage():
  17     return "%s [x y]"%sys.argv[0]
  18 
  19 if __name__ == "__main__":
  20     if len(sys.argv) == 3:
  21         x = int(sys.argv[1])
  22         y = int(sys.argv[2])
  23     else:
  24         print usage()
  25         sys.exit(1)
  26     print "Requesting %s+%s"%(x, y)
  27     print "%s + %s = %s"%(x, y, add_two_ints_client(x, y))

Don't forget to make the node executable:

$ chmod +x scripts/add_two_ints_client.py

The Code Explained

Now, let's break the code down.

The client code for calling services is also simple. For clients you don't have to call init_node(). We first call:

Error: No code_block found This is a convenience method that blocks until the service named add_two_ints is available. Next we create a handle for calling the service:

Error: No code_block found We can use this handle just like a normal function and call it:

Error: No code_block found Because we've declared the type of the service to be AddTwoInts, it does the work of generating the AddTwoIntsRequest object for you (you're free to pass in your own instead). The return value is an AddTwoIntsResponseobject. If the call fails, a rospy.ServiceException may be thrown, so you should setup the appropriate try/except block.

Building your nodes

We use CMake as our build system and, yes, you have to use it even for Python nodes. This is to make sure that the autogenerated Python code for messages and services is created.

Go to your catkin workspace and run catkin_make.

# In your catkin workspace
$ cd ~/catkin_ws
$ catkin_make

Try it out!

In a new terminal, run

$ cd ~/catkin_ws
$ . devel/setup.bash
$ rosrun beginner_tutorials add_two_ints_server.py

In a new terminal, run

$ cd ~/catkin_ws
$ . devel/setup.bash
$ rosrun beginner_tutorials add_two_ints_client.py
  • And you will see the usage information printed, similar to
    /home/user/catkin_ws/src/beginner_tutorials/scripts/add_two_ints_client.py [x y]

Then run

$ rosrun beginner_tutorials add_two_ints_client.py 4 5
  • And you will get
    Requesting 4+5
    4 + 5 = 9
    And the server will print out
    Returning [4 + 5 = 9]

现在你已经学会如何编写简单的Service和Client,开始测试简单的Service和Client吧。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值