文章目录
- 一、节点
- 1.节点的概念
- 2.节点的核心代码
- 3.创建节点流程
- 二、话题
- 1.话题的概念
- 2.话题的核心代码
- 3.创建话题流程
- 三、服务
- 1.服务的概念
- 2.服务的核心代码
- 3.创建服务流程
- 总结
一、节点
1.节点的概念
实现机器人的各项功能,如用摄像头获取外界环境信息、用电机驱动轮子前进等等。
特点是:
- 执行某些具体的任务;
- 每一个节点独立运行的可执行文件;
- 节点编程语言可以不同;
- 节点处在的位置可以是位于硬件上,也可以位于云端;
- 节点是唯一命名的,便于查询节点的状态等。
2.节点的核心代码
"""
创建一个HelloWorld节点, 节点功能输出“HelloWorld”日志
节点实现效果:在终端每隔0.5s输出一句“HelloWorld”
"""
class HelloWorldNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
while rclpy.ok(): # ROS2系统是否正常运行
self.get_logger().info("Hello World") # ROS2日志输出
time.sleep(0.5) # 休眠控制循环时间
3.创建节点流程
- 编程接口初始化
- 创建节点并初始化
- 实现节点功能
- 销毁节点并关闭接口
二、话题
1.话题的概念
节点是机器人实现各种功能,但这些功能并不是独立的,通过话题就可以构建节点间传递数据的桥梁。话题通信的特性是单向传输,从一个节点到另一个节点,发布者发送数据,订阅者接收数据。如发布者发布一个话题的名称,将数据发送到话题上,订阅者就可以通过话题查看发布的内容。
话题通信另一个特性就是异步,发布者发出数据后,并不知道订阅者什么时候可以收到,只管发,适合用于一些周期发布的数据,比如传感器的数据,运动控制的指令等等。
如上图所示,创建两个节点分别为发布者和订阅者,发布者发布一个话题的名称,将数据发送到话题上,订阅者就可以通过话题查看发布的内容。
如上所示,一个发布者发布的数据,可以同时被不同的订阅者获得,即一对多模型。
2.话题的核心代码
话题的发布者:
"""
创建一个发布者节点
"""
class PublisherNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.pub = self.create_publisher(String, "chatter", 10) # 创建发布者对象(消息类型、话题名、队列长度)
self.timer = self.create_timer(0.5, self.timer_callback) # 创建一个定时器(单位为秒的周期,定时执行的回调函数)
def timer_callback(self): # 创建定时器周期执行的回调函数
msg = String() # 创建一个String类型的消息对象
msg.data = 'Hello World' # 填充消息对象中的消息数据
self.pub.publish(msg) # 发布话题消息
self.get_logger().info('Publishing: "%s"' % msg.data) # 输出日志信息,提示已经完成话题发布
话题的订阅者:
"""
创建一个订阅者节点
"""
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
3.创建话题流程
发布者端 | 订阅者端 |
1. 编程接口初始化 | 1. 编程接口初始化 |
2. 创建节点并初始化 | 2. 创建节点并初始化 |
3. 创建发布者对象 | 3. 创建订阅者对象 |
4. 创建并填充话题消息 | 4. 回调函数处理话题数据 |
5. 发布话题消息 | 5. 销毁节点并关闭接口 |
6. 销毁节点并关闭接口 |
三、服务
1.服务的概念
ROS另一种常用的通信方法是服务,可以实现你问我答的同步通信效果,适合同步性要求更高的数据,比如获取机器视觉识别到的目标位置。
如上图所示,创建两个节点分别为服务端和客户端,客户端在需要某些数据的时候,针对某个具体的服务,发送请求信息,服务器端收到请求之后,就会进行处理并反馈应答信息,即为一问一答的形式。
如上图所示为一对多通信。不同的客户端可以向同一个服务端发送请求并获得想要的数据。
2.服务的核心代码
创建服务端:
class adderServer(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.srv = self.create_service(AddTwoInts, 'add_two_ints', self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)
def adder_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 # 反馈应答信息
创建客户端:
class adderClient(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.client = self.create_client(AddTwoInts, 'add_two_ints') # 创建服务客户端对象(服务接口类型,服务名)
while not self.client.wait_for_service(timeout_sec=1.0): # 循环等待服务器端成功启动
self.get_logger().info('service not available, waiting again...')
self.request = AddTwoInts.Request() # 创建服务请求的数据对象
def send_request(self): # 创建一个发送服务请求的函数
self.request.a = int(sys.argv[1])
self.request.b = int(sys.argv[2])
self.future = self.client.call_async(self.request) # 异步方式发送服务请求
实现结果:客户端将一组数据两个数发送至服务端,服务端接收后,进行数据处理,将两个数相加,发送至客户端作为应答。
3.创建服务流程
服务端 | 客户端 |
1. 编程接口初始化 | 1. 编程接口初始化 |
2. 创建节点并初始化 | 2. 创建节点并初始化 |
3. 创建服务器端对象 | 3. 创建客户端对象 |
4. 通过回调函数处进行服务 | 4. 创建并发送请求数据 |
5. 向客户端反馈应答结果 | 5. 等待服务器端应答数据 |
6. 销毁节点并关闭接口 | 6. 销毁节点并关闭接口 |
总结
话题和服务是ROS中最为常用的两种数据通信方法,前者适合传感器、控制指令等周期性、单向传输的数据,后者适合一问一答,同步性要求更高的数据。