ros2学习(02)—发布者与订阅者

Published: less than 1 minute read, with 185 words. Post views:

ros2学习日志(2)—发布者与订阅者

2.0 写在前面

看了很多教程,主要是[鱼香ros]: (http://fishros.com/#/fish_home)

和[古月居]: (https://www.guyuehome.com/),决定自己练习一番,顺便记录一下,以备后续查阅。

2.1 创建发布者

我们创建了一个god节点,这次继续创建一个发布者,来发布一个掷骰子的结果。代码如下:

import rclpy #导入ros的客户端库,必备步骤
from rclpy.node import Node #rclpy.node是ros客户端库自带的Node类,用于创建节点
import random #导入random库产生随机数,来模拟掷骰子的结果
from std_msgs.msg import UInt32 #插入std_msgs中的UInt32数据类型

class GodNode(Node):  #采用面向对象(OOP)的方式建立node; 定义了一个GodNode的类,继承rclpy.node中的Node;
    def __init__(self,name):  #定义初始化方法;或者叫构造函数。。(构造函数是个人的理解,源自C++)
        super().__init__(name) #调用父类初始化,需要传入参数name,通常name是实例化的Node的名称,即节点名字;比如本例子中节点名字是 God
        self.get_logger().info("GodNode初始化成功!")  #get_logger().info是rclpy.node中的方法;
        #在这里意思是,当这个GodNode节点实例化的时候,输出一句日志;

        self.diceGame=self.create_publisher(UInt32,"diceGame",10) #利用create_publisher命令创建一个发布者
        	#第一个参数是发布内容的类型,这里发布的是一个骰子数,范围1~6,可以选择 Uint32;
        	#注意UInt32不是python自带的数据类型,而是ros2中std_msgs.msg中的数据类型
        	#第二个参数是内容发布到的一个Topic的名字,即内容发布到diceGame这个Topic上,
        	#第三个参数10是消息队列长度
        timer_period = 2  #2s的定时间隔
        self.timer = self.create_timer(timer_period, self.diceGame_callback)  #启动一个定时器,每2s调用一次diceGame_callback函数

    def diceGame_callback(self): #定义回调函数
        diceNum=UInt32() #声明一个diceNum,继承std_msgs中的UInt32类,python中一切皆对象啊
        diceNum.data=random.randint(1,6) #调用random库中的randint函数,返回1~6之间的一个随机int数,即产生1,2,3,4,5,6中随机一个数字
        self.diceGame.publish(diceNum) #将骰子的数字发布出去
        self.get_logger().info("成功掷了一次骰子") #打印调用回调函数之后的日志
        
def main(args=None):  #main函数,程序执行的主入口
    rclpy.init(args=args)  # 初始化客户端库,必备步骤
    node=GodNode("God")  # 新建节点对象,必备步骤;传入God,将GodNode实例化
    rclpy.spin(node)   # spin循环节点,保持节点运行,检测是否收到退出指令(Ctrl+C),必备步骤
    rclpy.shutdown()  # 关闭客户端库,必备步骤

代码解析补充:

  • random库是python自带的产生随机数的库;
  • ros2中传递数据需要使用ros2自带的interface库(接口库),这里使用的是UInt32,无符号整型
    • 可以使用ros2 interface list|grep std_msgs命令查看std_msgs包含的接口定义类型,基本包含常用的数据类型
    • 每一个数据类型都是一个类,即UInt32是一个类,因此diceNum=UInt32()是将UInt32这个类给实例化
    • UInt32的用法跟C/C++不一样,在这里其定义是类,取其数值需要用.data的方法,即diceNum.data
    • grep std_msgs意思是筛选结果中包含std_msgs的接口
    • image-20220406154619700
  • create_publisher是ros2中创建发布者的函数
  • 定时器的创建类似单片机中的定时中断函数。不过ros2可能是多线程,可能不中断主程序执行。这句话瞎猜的。

2.2 创建订阅者

创建了发布者之后,我们可以创建在终端手动订阅,看发布的消息是不是自己想要的,调试时很方便。

这里,我们创建一个订阅者,来获取发布者的消息。

在src的路径下,我们重新创建一个package,名为player,并创建一个player的节点,用来获取god/diceGame发布的消息。

cd src
ros2 pkg create player --build-type ament_python --dependencies rclpy
cd player/player/
touch player.py

订阅者代码如下

import rclpy #导入ros的客户端库,必备步骤
from rclpy.node import Node #rclpy.node是ros客户端库自带的Node类,用于创建节点
from std_msgs.msg import UInt32 #ros2的std_msgs数据接口

class PlayerNode(Node):  #采用面向对象(OOP)的方式建立node; 定义了一个PlayerNode的类,继承rclpy.node中的Node;
    def __init__(self,name):  #定义初始化方法;或者叫构造函数。。(构造函数是个人的理解,源自C++)
        super().__init__(name) #调用父类初始化,需要传入参数name,通常name是实例化的Node的名称,即节点名字;比如本例子中节点名字是 God
        self.get_logger().info("PlayerNode初始化成功!")  #get_logger().info是rclpy.node中的方法;
        #在这里意思是,当这个PlayerNode节点实例化的时候,输出一句日志;
        self.getDiceNum=self.create_subscription(UInt32,"diceGame",self.getDiceNum_callback,10)
        #create_subscription是创建订阅者的命令
        #第一个参数是数据类型
        #第二个是Topic(话题)名字,需要与发布者发布的Topic名称完全一致!!
        #第三个参数是回调函数,函数里面一般写收到消息后需要执行的操作!本例子中,收到消息后,回调getDiceNum_calback函数,将收到的消息显示出来
        #第四个参数是消息队列长度
        
    def getDiceNum_callback(self,diceNum): #订阅者 收到消息后的回调函数 
        self.get_logger().info("掷骰子游戏开始!数字是:%d" % diceNum.data) #将收到的骰子数值显示出来
        #注意diceNum是一个UInt32的类,需要用.data来取到其数值。跟C/C++ uint32用法不一样!

def main(args=None):  #main函数,程序执行的主入口
    rclpy.init(args=args)  # 初始化客户端库,必备步骤
    node=PlayerNode("player")  # 新建节点对象,必备步骤;传入player,将playerNode实例化
    rclpy.spin(node)   # spin循环节点,保持节点运行,检测是否收到退出指令(Ctrl+C),必备步骤
    rclpy.shutdown()  # 关闭客户端库,必备步骤

然后依次:

colcon build
source install/setup.bash

接下来打开一个终端,运行发布者节点:ros2 run god god1

打开另一个终端,先source一下,然后运行订阅者节点:ros2 run player player

可以看到,左边发布者在不断的发布消息。右边订阅者不断的收到消息。

image-20220408140547900


Tags: ,

Categories:

Comments