ros2学习(02)—发布者与订阅者
Published: 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的接口
- 可以使用
- 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
可以看到,左边发布者在不断的发布消息。右边订阅者不断的收到消息。
Categories: 机器人技术
Comments