ROS2-python服务service和client节点(实现加法运算)
当节点通过服务进行通讯的时候,发送数据请求的一方我们称之为客户端,接收数据然后相应的一端我们称之为服务器端。请求和响应的数据结构有一个.srv
文件决定。
本例程当中我们做一个加法运算,一个节点发送一个将两个整数相加的请求,另外一个节点对请求进行相应。
第一步:创建包
mkdir -p dev_ws/src
cd ~/dev_ws/src
ros2 pkg create --build-type ament_python py_srvcli --dependencies rclpy example_interfaces
功能包叫py_srvcli,example_interfaces也添加到package.xml里面了。
这个接口并不需要我们自己去写,在以后的应用场景下我们也可以自定义接口。(之后的文章会教大家如何写自定义接口)
这个example_interfaces接口可以按照如下方法查找它的路径
ros2 pkg prefix example_interfaces
cat /opt/ros/<ros2-distro>/share/example_interfaces/srv/AddTwoInts.srv
这时你就可以看到数据结构srv里的内容了
第二步:编写服务service节点
进入dev_ws/src/py_srvcli/py_srvcli,
创建service_member_function.py
文件
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
class MinimalService(Node):
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
def add_two_ints_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()
代码解释:
1、第一行加载了服务的消息类型(也就是接口的路径import它里面的类),然后加载了ROS2的Python客户端和节点的类
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
2、接下来在构造函数里面初始化了节点的名字minimal_service(可以自定义节点名称但保证下面的代码要改对)
服务类型AddTwoInts ,这是example_interfaces
包中定义的服务类型
服务名称add_two_ints,客户端将使用这个名称来找到并调用这个服务
回调函数self.add_two_ints_callback
def __init__(self):
super().__init__('minimal_service')
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.add_two_ints_callback)
3、回调函数将接收到的两个数字相加后返回相应,同时往控制台打印消息。(只有服务端要写回调)
def add_two_ints_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
最后,主函数主类初始化ROS2 Python客户端,创建服务节点,等待处理回调。
def main(args=None):
rclpy.init(args=args)
minimal_service = MinimalService()
rclpy.spin(minimal_service)
rclpy.shutdown()
第三步:编写客户端client节点
进入 dev_ws/src/py_srvcli/py_srvcli,创建client_member_function.py
文件
import sys
from example_interfaces.srv import AddTwoInts
import rclpy
from rclpy.node import Node
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):
self.req.a = int(sys.argv[1])
self.req.b = int(sys.argv[2])
self.future = self.cli.call_async(self.req)
def main(args=None):
rclpy.init(args=args)
minimal_client = MinimalClientAsync()
minimal_client.send_request()
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' %
(minimal_client.req.a, minimal_client.req.b, response.sum))
break
minimal_client.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
代码解释
由于我们需要获得程序输入的参数,我们新加了一行import sys
和服务端类似,首先定义了一个类,然后在构造函数中创建了一个类型名称和服务端一样的节点。
然后white循环一直寻找服务端
之后是发送请求的函数和main函数的定义
在主函数中while循环检测等待服务的相应,同时进行了一个异常处理,如果响应正确打印消息。
添加程序入口
打开setup.py
文件,和服务端一样我们添加客户端的程序入口
entry_points={
'console_scripts': [
'service = py_srvcli.service_member_function:main',
'client = py_srvcli.client_member_function:main',
],
},
编译运行
进入工作空间根目录进行编译
cd ~/dev_ws
colcon build
打开一个新的终端,我们运行service服务端
cd ~/dev_ws
source install/setup.bash
ros2 run py_srvcli service
再打开一个新的终端,我们运行client客户端
cd ~/dev_ws
source install/setup.bash
ros2 run cpp_srvcli client 2 3