ROS2—服务(Service)

 服务是基于客户端/服务器模型的通信机制,服务器端只在接收到客户端请求时才会提供反馈数据。  

接下来还是以海龟仿真为例,看看里边有哪些服务的应用呢?  

1.启动海龟仿真器

还是用这两个命令来启动海龟仿真器:

ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key

2.查看服务列表

在小海龟背后,有哪些服务呢?可以使用“ros2 service list”命令来查看一下:  

  在列表中可以看到每个节点都有6个以“parameters”结尾的服务,在ROS2中,几乎每一个节点都会有这几个服务,主要是用来设置一些参数的,算是节点的默认服务配置,这里我们暂时忽略这些默认的服务,主要看其他几个海龟仿真器提供的功能性质的服务。  

3.ros2服务类型

类似话题中的消息,服务中完成通信的数据也是有数据结构的,只不过是有请求和应答两部分的数据组成的。我们可以通过以下命令来查看某一个服务的数据结构:

ros2 service type <service_name>

  以/clear服务为例,可以这样来试试:

ros2 service type /clear

  看到的服务数据结构是这样的:

  Empty类型表示服务请求部分的数据是没有的,发送请求的时候不需要任何数据。  

3.1 ros2 service list -t

  类似查看所有节点的话题消息类型,也可以用类似的方式查看所有服务的数据类型,需要在list命令后边加上“--show-types”配置,或者简写为“-t”。  

 

4.查找提供某类型数据的所有服务

ROS2还允许查看所有提供同样数据类型的的服务,可以使用如下命令:

ros2 service find <type_name>

  比如可以查找所有提供std_srvs/srv/Empty数据类型的服务:

ros2 service find std_srvs/srv/Empty

  可以看到:

 

5.查看服务数据类型的具体结构

现在我们已经可以看到某一个服务的数据类型了,那这种数据类型具体的数据结构是什么样的呢?可以用这个命令:

ros2 interface show <type_name>.srv

  比如:

ros2 interface show std_srvs/srv/Empty.srv

  终端中会显示:

  这里的“---”在服务的数据结构中是用来分割请求和应答两个部分的数据,这里只所以只有“---”,是应为Empty的请求和应答都不需要任何数据描述,类似一个出发信号。   我们来看另外一个服务的数据类型Spawn,之前已经根据list命令看到该服务的数据类型是 turtlesim/srv/Spawn:

ros2 interface show turtlesim/srv/Spawn.srv

  终端中可以看到:

  这里可以看到,在Spawn服务的请求部分,由必须的x、y、theta和可选的name组成,应答数据则是name这个字符串。  

6.通过终端发送服务请求

我们已经了解了如何查看服务的数据类型和结构,接下来通过终端尝试发送一个服务请求。

ros2 service call <service_name> <service_type> <arguments>

  <arguments>部分是可选的,比如Empty类型的服务就没有任何参数:

ros2 service call /clear std_srvs/srv/Empty

  这个服务只是出发一个信号,让海龟仿真器清除海龟后边的运动轨迹,不需要传输什么数据:  

  我们再来试试Spawn服务,这个服务可以产生一只新的海龟,<arguments>部分就需要输入上边看到的请求数据了:

ros2 service call /spawn turtlesim/srv/Spawn "{x: 2, y: 2, theta: 0.2, name: ''}"

  终端中可以看到:

  海龟仿真器中立刻就可以看到一只新的海龟啦:  

  请求数据中的x、y、theta表示海龟的横纵坐标和旋转角度,name表示新生海龟的名字。   好啦,这就是ROS2中的服务概念啦,简而言之,客户端发送请求,服务端完成处理后反馈应答,通信只会交互一次数据,不像话题是周期发送数据的。  

### ROS2 Service 使用教程 #### 创建并配置功能包 为了创建一个新的ROS2服务,首先需要建立一个新功能包,并在此过程中添加必要的依赖项。这包括`rclpy`作为Python客户端库和支持特定消息类型的接口包,如`example_interfaces`[^1]。 ```bash ros2 pkg create --build-type ament_python py_service_example cd py_service_example/ echo "find_package(rclpy REQUIRED)" >> CMakeLists.txt echo "find_package(example_interfaces REQUIRED)" >> CMakeLists.txt ``` 编辑 `package.xml` 文件来声明这些依赖关系: ```xml <depend>rclpy</depend> <depend>example_interfaces</depend> ``` #### 实现服务端逻辑 接下来,在Python脚本中实现服务器部分的业务逻辑。下面是一个简单的例子,它实现了两个整数相加的服务请求处理程序[^2]。 ```python import rclpy from rclpy.node import Node from example_interfaces.srv import AddTwoInts class MinimalService(Node): def __init__(self): super().__init__('minimal_service') self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_callback) def add_callback(self, request, response): response.sum = request.a + request.b self.get_logger().info('Incoming request\na: %d b: %d' % (request.a, request.b)) return response def main(args=None): rclpy.init(args=args) minimal_service = MinimalService() rclpy.spin(minimal_service) rclpy.shutdown() if __name__ == '__main__': main() ``` 这段代码展示了如何设置一个名为`minimal_service`的服务节点,该节点监听来自其他节点发出的消息,并执行相应的操作——在这个案例里即计算两个输入参数之和。 #### 客户端调用方式 对于想要发送请求给上述服务的应用来说,则可以按照以下模式构建对应的客户端实例[^3]: ```python import sys import rclpy from rclpy.node import Node from example_interfaces.srv import AddTwoInts class MinimalClientAsync(Node): def __init__(self): super().__init__('minimal_client_async') self.cli = self.create_client(AddTwoInts, 'add_two_ints') while not self.cli.wait_for_service(timeout_sec=1.0): self.get_logger().info('service not available, waiting again...') self.req = AddTwoInts.Request() def send_request(self, a, b): self.req.a = int(a) self.req.b = int(b) self.future = self.cli.call_async(self.req) def main(): rclpy.init() minimal_client = MinimalClientAsync() minimal_client.send_request(sys.argv[1], sys.argv[2]) while rclpy.ok(): rclpy.spin_once(minimal_client) if minimal_client.future.done(): try: response = minimal_client.future.result() except Exception as e: minimal_client.get_logger().info( 'Service call failed %r' % (e,)) else: minimal_client.get_logger().info( 'Result of add_two_ints: for %d + %d = %d' % (int(sys.argv[1]), int(sys.argv[2]), response.sum)) break minimal_client.destroy_node() rclpy.shutdown() if __name__ == '__main__': main() ``` 这个示例说明了怎样发起一次远程过程调用来获取由另一台机器上的进程所提供的资源或完成的任务的结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值