跳到主要内容

1,话题

话题:故事其实就是讲了一下ROS2中Topic通信方式,Topic通信模型是一种发布订阅模型。

需要满足以下规则:

  • 话题名字是关键,发布订阅接口类型要相同,发布的是字符串,接受也要用字符串来接收;
  • 同一个人(节点)可以订阅多个话题,同时也可以发布多个话题,就像一本书的作者也可以是另外一本书的读者;
  • 同一个小说不能有多个作者(版权问题),但跟小说不一样,同一个话题可以有多个发布者。

1,相关的工具

ros2 run demo_nodes_py listener
ros2 run demo_nodes_cpp talker
rqt_graph

2,ROS2话题相关命令行界面(CLI)工具

ros2也支持很多强大的topic指令。可以使用下面的指令查看。

ros2 topic -h

ros2 topic list 返回系统中当前活动的所有主题的列表

命令

ros2 topic list

ros2 topic list -t 增加消息类型

命令

2ros2 topic list -t

ros2 topic echo 打印实时话题内容

命令

ros2 topic echo /c)hatter

ros2 topic info 查看主题信息

命令

ros2 topic info  /chatter

ros2 interface show 查看消息类型

上面一个指令告诉大家这个消息是std_msgs/msg/String,那String里面有什么呢?

命令

ros2 interface show std_msgs/msg/String

topic pub arg 手动发布命令

关闭发布者,我们受到来发布

命令

ros2 topic pub /chatter std_msgs/msg/String 'data: "123"'

参考链接:Understanding ROS 2 topics — ROS 2 Documentation: Foxy documentation

话题编程

1.发布话题(sexy_girl)

下面这样:

class WriterNode(Node): 

那代码里的WriterNode继承Node之后,会具备什么能力呢?在本节中用到了以下四个能力:

  • 创建一个话题发布者的能力
  • 创建一个定时器的能力
  • 创建一个话题订阅者的能力
  • 获取日志打印器的能力

除了上述四种能力之外,Node节点所具备的详细的能力可以查看APIhttps://docs.ros2.org/foxy/api/rclpy/index.html

编写一个话题发布者一流程:

  1. 导入消息类型
  2. 声明并创建发布者
  3. 编写发布逻辑发布数据

用VsCode打开上一章中town_ws工作空间,并打开li4.py。我们在其中添加代码即可。

添加完成后WriterNode类中代码如下:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
# 1. 导入消息类型
from std_msgs.msg import String

class WriterNode(Node):
"""
创建一个李四节点,并在初始化时输出一个话
"""
def __init__(self,name):
super().__init__(name)
self.get_logger().info("大家好,我是%s,我是一名作家!" % name)
# 2.创建并初始化发布者成员属性pubnovel
self.pub_novel = self.create_publisher(String,"sexy_girl", 10)

#3. 编写发布逻辑
# 创建定时器成员属性timer
self.i = 0 # i 是个计数器,用来算章节编号的
timer_period = 5 #每5s写一章节话
self.timer = self.create_timer(timer_period, self.timer_callback) #启动一个定时装置,每 1 s,调用一次time_callback函数

def timer_callback(self):
"""
定时器回调函数
"""
msg = String()
msg.data = '第%d回:潋滟湖 %d 次偶遇胡艳娘' % (self.i,self.i)
self.pub_novel.publish(msg) #将小说内容发布出去
self.get_logger().info('李四:我发布了艳娘传奇:"%s"' % msg.data) #打印一下发布的数据,供我们看
self.i += 1 #章节编号+1

def main(args=None):
rclpy.init(args = args)
li4_node = WriterNode("li4")
rclpy.spin(li4_node)
rclpy.shutdown()

1.3 代码讲解

1.3.1 创建发布者

self.create_publisher(String,"sexy_girl", 10) 

小鱼这里使用create_publisher方法来创建的发布者,该方法一共有三个参数,第一个是方法类型,第二个是话题名称,第三个是消息队列长度,第一个参数我们这里添了String,需要注意的是,这里的String并非Python自带的字符串类型,我们使用

from std_msgs.msg import String 

std_msgs.msg中导入了String类,那std_msgs是什么呢?

std_msgs是ROS2自带的接口类型,其中规定了我们常用的大多数消息类型,可以使用下面的指令来查看std_msgs中所有的消息类型。

ros2 interface package std_msgs 

比如我们接下来想让李四收钱,我们将消息类型设置为UInt32,无符号整型,毕竟收钱没有收成负数的。

还可以使用 ros2 interface list查看所有ros2自带的消息类型。

需要注意的是,ros2中自带的类型基本上能够满足我们日常做机器人时的使用,但如果ros2中的消息类型不能满足我们的需求时,也可以选择自己定义消息类型。

1.3.2 编写发布逻辑发布数据

使用了一个方法来创建一个定时器

self.create_timer(timer_period, self.timer_callback)

这个定时器的作用就是根据传入的timer_period时间周期,每隔一个timer_period秒,调用一次self.timer_callback函数。

self.timer_callback函数里,我们使用publish方法将数据(小说内容)发送出去。也就是说每1s中发送一次小说内容。

self.write.publish(msg)  #将小说内容发布出去

2.测试是否发布成功

完成上面的代码后,我们就可以编译运行节点了。

在VsCode中可以使用下面的命令打开和拆分终端:

单独编译李家村,可以使用下面的指令来单独编译某一个功能包。

colcon build --packages-select  village_li

运行节点

source install/setup.bash
ros2 run village_li li4_node

订阅收钱话题

毕竟作家还要过生活的,不能给别人免费看,所以作家要建立了一个收钱话题(sexy_girl_money),专门用来收艳娘传奇的稿费。

代码编写

创建话题订阅者的一般流程:

  1. 导入订阅的话题接口类型
  2. 创建订阅回调函数
  3. 声明并创建订阅者
  4. 编写订阅回调处理逻辑

在上一部分代码的基础上添加了创建订阅器的函数:

self.create_subscription(UInt32,"sexy_girl_money",self.recv_money_callback,10) 

这句话的意思是创建订阅者,订阅话题sexy_girl_money,话题类型为UInt32,每次收到钱就去调用self.recv_money_callback函数存起来。

完整代码如下:

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
# 导入话题消息类型
from std_msgs.msg import String,UInt32

class WriterNode(Node):
"""
创建一个李四节点,并在初始化时输出一个话
"""
def __init__(self,name):
super().__init__(name)
self.get_logger().info("大家好,我是%s,我是一名作家!" % name)
# 创建并初始化发布者成员属性pubnovel
self.pub_novel = self.create_publisher(String,"sexy_girl", 10)

# 创建定时器成员属性timer
self.i = 0 # i 是个计数器,用来算章节编号的
timer_period = 5 #每5s写一章节话
self.timer = self.create_timer(timer_period, self.timer_callback) #启动一个定时装置,每 1 s,调用一次time_callback函数

# 账户钱的数量
self.account = 80
# 创建并初始化订阅者成员属性submoney
self.sub_money = self.create_subscription(UInt32,"sexy_girl_money",self.recv_money_callback,10)

def timer_callback(self):
"""
定时器回调函数
"""
msg = String()
msg.data = '第%d回:潋滟湖 %d 次偶遇胡艳娘' % (self.i,self.i)
self.pubnovel.publish(msg) #将小说内容发布出去
self.get_logger().info('李四:我发布了艳娘传奇:"%s"' % msg.data) #打印一下发布的数据,供我们看
self.i += 1 #章节编号+1

def recv_money_callback(self,money):
"""
4. 编写订阅回调处理逻辑
"""
self.account += money.data
self.get_logger().info('李四:我已经收到了%d的稿费' % self.account)

def main(args=None):
"""
ros2运行该节点的入口函数,可配置函数名称
"""
rclpy.init(args=args) # 初始化rclpy
node = WriterNode("li4") # 新建一个节点
rclpy.spin(node) # 保持节点运行,检测是否收到退出指令(Ctrl+C)
rclpy.shutdown() # rcl关闭

4.测试是否订阅成功

再次编译运行李家村。

colcon build --packages-select  village_li
source install/setup.bash
ros2 run village_li li4_node

使用Ctrl+Shift+5切分一个终端出来,然后通过命令行发布话题数据:

ROS2支持下面两种方式,其中方式2最后是有一个空格的,大家注意

ros2 topic pub  /sexy_girl_money std_msgs/msg/UInt32 "{data: 10}"

可以看到,李四已经收到共计50块钱的稿费了。

##参考与致谢