话题是节点间传递数据的桥梁,用于连接节点,服务于节点间的信号传递。
- 发布数据的节点为话题的发布者,接受数据的节点为话题的订阅者,是多对多的通讯模型,在多个发布者时需注意订阅者的执行优先级。
- 话题是一个异步通信机制,发布者处并不知道什么时候被订阅者接收,也不需要等到回应,只管发布(因此不适用于参数修改)。
- 发布与接受的数据必须符合统一的数据描述格式,.msg文件将定义话题通信的消息结构
话题编写在节点源码内部,只要节点运行,就会自动按代码内的规则演绎话题。
一个发布者节点示例代码如下:
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # 字符串消息类型
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) # 输出日志信息,提示已经完成话题发布
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = PublisherNode("topic_helloworld_pub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
其中rclpy.spin表示节点循环等待,持续运行,是阻塞的,会进入ros2的主循环持续监听并在满足条件情况下调用回调函数,直到触发退出条件(即按Ctrl+C或调用rclpy.shutdown()),期间进程将停在这一行,无法继续向下执行。与之相对的是rclpy.spin_once,仅执行一次节点任务后就自动退出,在执行完一次前也会持续运行导致阻塞,但一次往往很快就会结束,提供的第二个参数为timeout_sec,表示等待(阻塞)时长,在timeout_sec时间内没有接收到ros队列中的任务则跳过,继续执行后面的其他代码。常用于循环内执行多种操作,间歇性检查ros2节点的工作队列。
一个订阅者节点示例代码如下:
import rclpy # ROS2 Python接口库
from rclpy.node import Node # ROS2 节点类
from std_msgs.msg import String # ROS2标准定义的String消息
class SubscriberNode(Node):
def __init__(self, name):
super().__init__(name) # ROS2节点父类初始化
self.sub = self.create_subscription(\
String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度),同理,未处理数据大于10时会先删掉接收较早的数据
def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
def main(args=None): # ROS2节点主入口main函数
rclpy.init(args=args) # ROS2 Python接口初始化
node = SubscriberNode("topic_helloworld_sub") # 创建ROS2节点对象并进行初始化
rclpy.spin(node) # 循环等待ROS2退出
node.destroy_node() # 销毁节点对象
rclpy.shutdown() # 关闭ROS2 Python接口
同样,依旧需要在setup.py内新增程序入口才能使用。
另:传递图像数据可以通过导入sensor_msgs.msg库的Image类来实现,这是ros2的一个工具包。
需要注意的是,OpenCV的图像格式和ros2中sensor_msgs.msg库的Image的图像格式不同,需要额外引入ros2的库cv-bridge来处理。该库并不默认下载,所以如要安装该库可在命令行执行(仅适用于humble版本):
sudo apt install ros-humble-cv-bridge
执行时:
from cv_bridge import CvBridge
self.cv_bridge = CvBridge()
cv_bridge.cv2_to_imgmsg(frame, 'bgr8'))
.cv_bridge.imgmsg_to_cv2(data, 'bgr8')
即可实现OpenCV与ros2图像格式的相互转化了。
ros2还提供专门的工具包用以适配并使用不同设备的摄像头,即usb-cam。通过apt可以下载它,代码如下:
sudo apt install ros-humble-usb-cam
该工具包控制摄像头录制的内容将发布到名为“image_raw”的话题上。
需要注意的是,电脑出于安全考虑,虚拟机等程序是无法直接唤起摄像头的。总之就挺麻烦的。
在命令行,ros2也有一些管理话题的方法。
列出所有活动话题:
ros2 topic list
查看话题详细信息:
ros2 topic info <topic_name>
监听话题数据:
ros2 topic echo <topic_name>
查看话题发布频率:
ros2 topic hz <topic_name>
手动发布话题消息:
ros2 topic pub <topic_name> <msg_type> <data>
查看话题消息类型:
ros2 topic type <topic_name>
查找特定类型的消息:
ros2 topic find <msg_type>
此外,还可以启用可视化界面观察话题关系。在命令行内输入:
rqt_graph