观前提醒:本期主要内容为ROS中MQTT通信节点的编程,和ROS部分底层通信机制的浅析

一、复习一下:ROS通信机制&MQTT通信异同点

ROS通信机制概述

ROS中的主要通信机制有以下几种:

  1. 话题 (Topics)
    • 发布/订阅模型(Publish/Subscribe):这种通信方式中,节点可以发布消息到一个主题,或者订阅一个主题以接收消息。这种方式是匿名的,发布者和订阅者不需要知道彼此的存在。
    • 消息(Messages):在主题上发送的数据结构,被定义为简单的数据类型(如整数、浮点数、字符串等)的组合,也可以是更复杂的嵌套结构。
  2. 服务 (Services)
    • 请求/响应模型(Request/Response):服务是一种同步通信方式,一个节点可以向另一个节点发出请求,并等待响应。服务定义了请求和响应的消息类型。
    • 服务客户端和服务服务器(Service Client/Service Server):节点可以作为服务服务器来提供服务,或者作为服务客户端来调用服务。
  3. 动作 (Actions)
    • 目标/结果模型(Goal/Result):用于处理可能需要较长时间才能完成的任务。客户端发送一个目标给动作服务器,并且可以选择接收反馈、取消目标或者等待最终结果。
    • 动作客户端和动作服务器(Action Client/Action Server):动作客户端可以请求动作服务器执行某项任务,并根据需要获取反馈信息或结果。
  4. 参数服务器 (Parameter Server)
    • 参数存储:节点可以存储和检索全局参数。这个服务器用于存储配置参数,可以被任何节点访问。

那么这些通信方式是如何实现的呢?

  1. 话题 (Topics)
    • 主题通信是基于发布/订阅模式实现的。当一个节点(Node)发布一个消息到某个主题时,ROS中间件会将这个消息传递给订阅了该主题的所有节点。
    • Publisher:节点通过一个发布者(Publisher)对象将消息发布到一个特定的主题。
    • Subscriber:节点通过一个订阅者(Subscriber)对象从一个特定的主题接收消息。
    • Master:ROS master提供名称解析服务,让发布者和订阅者能够找到对方。
    • roscore:在ROS中启动时首先运行的是roscore,它启动了一个Master,以及参数服务器(Parameter Server)和rosout日志节点。

底层实现是通过TCP/IP(默认)或者UDP(通过UDP ROS数据包协议,UDPROS)进行通信。当一个节点向ROS Master注册自己为某个主题的发布者或订阅者时,Master会帮助发布者和订阅者相互发现,并建立一个直接的通信链接。

  1. 服务 (Services)
    • 服务是基于请求/响应模式实现的。一个节点作为服务器提供服务,另一个节点作为客户端可以调用这个服务并等待响应。
    • Service Server:提供特定服务的节点,它注册到ROS master,等待服务请求。
    • Service Client:需要服务的节点,它会询问ROS master服务器的地址,并直接向服务器发送服务请求,并等待回应。

服务通信通常也是通过TCP/IP进行的,确保了通信的可靠性,因为服务调用通常需要得到确定的响应。

  1. 动作 (Actions)
    • 动作通信是一个更复杂的通信机制,用于处理可能需要长时间执行的任务。它在服务通信的基础上增加了中间反馈的可能性。
    • Action Server:执行长时间运行的任务,并提供中间反馈以及最终结果。
    • Action Client:发送任务请求给动作服务器,可以选择接收反馈、取消任务或等待结果。

动作通信内部实际上使用了多个主题来实现其通信模型,包括用于发送目标、接收反馈和结果的主题。

  1. 参数服务器 (Parameter Server)
    • 参数服务器是ROS中用于存储参数值的中心化服务,节点可以读写这些参数。
    • Parameter Server:作为存储全局参数的服务运行,可以在roscore启动时一起启动。
    • Nodes:各个节点可以通过ROS提供的API来获取和设置参数服务器中的参数值。

参数服务器的操作通常通过XML-RPC实现,一个简单的远程过程调用协议,运行在HTTP之上。

可以看到,大多数通信方式的实现依赖TCP通信,那么ROS中的TCP通信是如何实现的呢?

ROS中进行TCP依赖于TCPROS协议,TCPROS是ROS用于节点间通信的主要协议

TCPROS:

当ROS节点需要通过主题通信或服务通信进行交互时,TCPROS被用作默认的传输协议。以下是TCPROS工作的基本步骤:

  • 节点注册:当节点启动并创建一个发布者或订阅者时,它会在ROS Master上注册自己,声明它将发布或订阅的主题名称和消息类型。
  • 连接建立:订阅者节点会要求ROS Master提供所有发布给特定主题的节点的信息。使用这些信息,订阅者节点直接与发布者节点建立TCP连接。
  • 消息传输:一旦TCP连接建立,发布者节点就可以开始将消息流式传输给订阅者节点。每个消息都保证会按顺序且完整地到达订阅者节点。
  • 服务调用:在服务通信中,服务客户端节点会直接与服务服务器节点建立一个TCP连接,通过该连接发送一个服务请求,并等待服务器处理并返回响应。

TCPROS的特点

  • 可靠性:由于TCPROS基于TCP,它保证了消息的可靠传递。如果数据包在传输过程中丢失或损坏,TCP协议会自动重传这些数据包。
  • 连接导向:TCPROS需要在通信的节点之间建立一个持久的连接。这个连接会一直保持,直到主题的发布或订阅结束,或服务调用完成。
  • 流控制和拥塞控制:TCPROS继承了TCP的流控制功能,可以根据网络的状况动态地调整数据的发送速率,以避免网络拥塞。
  • 消息序列化:在ROS中,消息在通过TCPROS发送前需要序列化。序列化后的消息是一个字节流,这个字节流包括了消息的长度和消息本身,以确保接收方可以正确地反序列化并解析消息。

ROS中的通信与MQTT通信异同点:

相同点

  • 发布/订阅模型:ROS和MQTT都使用了发布/订阅(pub/sub)的消息通信模型。在这种模型中,发布者(publisher)发送消息,而订阅者(subscriber)接收消息,发布者和订阅者之间不需要彼此了解。
  • 消息传递:两者都是通过发送和接收消息来实现通信的,而不是使用传统的客户端-服务器模型。
  • 解耦:在ROS和MQTT的通信中,组件间是解耦的。发送者和接收者不需要直接的网络连接,发布者不需要知道谁是订阅者,反之亦然。

区别

  • 设计目的:
    • ROS:专为机器人应用设计,提供了一套完整的工具和库来帮助开发者构建复杂的机器人应用程序。
    • MQTT:是一个轻量级的消息协议,设计用于物联网(IoT)应用中各种设备的通信,尤其是在网络带宽有限或不可靠的环境中。
  • 协议层级:
    • ROS:运行在应用层,使用TCP(默认)或UDP进行节点间的通信,并且提供了一系列的工具和库来帮助开发。
    • MQTT:是一个基于TCP/IP的应用层协议,通常运行在较低的带宽、高延迟或不可靠的网络上。
  • 网络模式:
    • ROS:主要用于局域网(LAN)内部通信,通常用于单个机器人系统或机器人之间的通信。
    • MQTT:设计用于广域网(WAN),可以在互联网上进行通信,适用于远程监控和控制。
  • 可靠性:
    • ROS:依赖于TCPROS,提供了可靠的消息传输。但是,它不原生支持消息的持久化或保证消息传递。
    • MQTT:提供了不同级别的服务质量(QoS)保证。例如,QoS1保证至少一次消息传递,QoS2保证消息仅传递一次。
  • 中间件:
    • ROS:使用ROS Master作为节点发现的中间件,但每个节点之间的通信是直接进行的。
    • MQTT:使用中央代理(broker)来处理所有消息的传递。所有的通信都通过这个中央代理进行。

ROS中建立MQTT通信有什么优点或者应用场景:

  • 进行跨网络通信,MQTT是设计用于跨网络边界进行通信的,这意味着通过MQTT,ROS系统可以轻松地与互联网上的其他系统或设备进行通信。这对于需要远程监控或控制机器人的应用场景非常有用。
  • 并且在实际应用中,结合MQTT与ROS可以在确保消息传递可靠性的同时,扩展ROS的通信能力到互联网层面,实现更广泛的设备互联和数据交换。这种结合特别适合于需要远程操作、云服务集成或大规模设备管理的机器人应用。

二、ROS MQTT通信节点

步骤一:创建py文件

在文件中编写以下代码:

#!/usr/bin/env python

import rospy
import paho.mqtt.client as mqtt
from std_msgs.msg import String

# MQTT设置
MQTT_BROKER_ADDRESS = "broker.hivemq.com"  # 替换为你的MQTT服务器地址
MQTT_BROKER_PORT = 1883  # 替换为你的MQTT服务器端口
MQTT_TOPIC_SUBSCRIBE = "mqtt_topic_subscribe"  # 你想订阅的MQTT话题
MQTT_TOPIC_PUBLISH = "mqtt_topic_publish"  # 你想发布的MQTT话题

# ROS设置
ROS_TOPIC_SUBSCRIBE = "ros_topic_subscribe"  # 你想订阅的ROS话题
ROS_TOPIC_PUBLISH = "ros_topic_publish"  # 你想发布的ROS话题

# MQTT客户端的回调函数
def on_connect(client, userdata, flags, rc):
    rospy.loginfo("Connected to MQTT Broker with result code " + str(rc))
    client.subscribe(MQTT_TOPIC_SUBSCRIBE)

def on_message(client, userdata, msg):
    rospy.loginfo("MQTT Message received: " + msg.topic + " " + str(msg.payload))
    # 将收到的MQTT消息发布到ROS
    ros_publisher.publish(str(msg.payload))

# ROS订阅者的回调函数
def ros_subscriber_callback(data):
    rospy.loginfo("ROS Message received: " + data.data)
    # 将收到的ROS消息发布到MQTT
    mqtt_client.publish(MQTT_TOPIC_PUBLISH, data.data)

# 初始化ROS节点
rospy.init_node('mqtt_bridge_node', anonymous=True)

# 初始化ROS发布者和订阅者
ros_publisher = rospy.Publisher(ROS_TOPIC_PUBLISH, String, queue_size=10)
ros_subscriber = rospy.Subscriber(ROS_TOPIC_SUBSCRIBE, String, ros_subscriber_callback)

# 初始化MQTT客户端
mqtt_client = mqtt.Client()
mqtt_client.on_connect = on_connect
mqtt_client.on_message = on_message

# 连接MQTT服务器
mqtt_client.connect(MQTT_BROKER_ADDRESS, MQTT_BROKER_PORT, 60)

# 开始MQTT客户端循环
mqtt_client.loop_start()

try:
    # ROS主循环
    rospy.spin()
except KeyboardInterrupt:
    print("Shutting down")

# 停止MQTT客户端循环
mqtt_client.loop_stop()

这段代码定义了一个名为mqtt_bridge_node的ROS节点,它同时是MQTT客户端。它订阅了ROS话题ros_topic_subscribe和MQTT话题mqtt_topic_subscribe,同时发布消息到ROS话题ros_topic_publish和MQTT话题mqtt_topic_publish。

我们首先发布ros话题内容

在终端看眼看到接受到的消息,并进行MQTT转发操作

在MQTT.fx上可以看到MQTT话题发布情况