MQTT简介和如何通过MQTT进行数据传输

一、简介

MQTT 是 Message Queuing Telemetry Transport 的缩写,是一种轻量级的、基于发布/订阅模式的物联网通信协议。它具有以下特点:

  • 简单易用:MQTT 的协议规范很简单,易于学习和使用。
  • 可靠性高:MQTT 使用了 TCP 协议进行传输,具有较高的可靠性。
  • 低延迟:MQTT 使用了发布/订阅模式,可以减少消息传递的延迟。

在机器人应用中,MQTT 可以用于以下场景:

  • 传感器数据上传:使用 MQTT 可以将传感器数据上传到云端或其他机器人系统。
  • 机器人控制:使用 MQTT 可以远程控制机器人。
  • 机器人协作:使用 MQTT 可以实现多个机器人之间的协作。

MQTT 协议由三个主要部分组成:

  • 客户端: MQTT 客户端是发送和接收消息的应用程序。
  • 服务器: MQTT 服务器是处理消息的应用程序。
  • 主题: 主题是消息的路径,用于区分不同类型的消息。

MQTT 协议使用发布/订阅模式进行通信。发布者将消息发布到主题,订阅者订阅主题,并接收发布者发布的消息。
这一点跟ros2里面的topic非常类似

1. MQTT 客户端

MQTT 客户端可以是任何类型的应用程序,包括嵌入式设备、PC 应用程序和 Web 应用程序。MQTT 客户端需要实现 MQTT 协议的三个主要功能:

  • 连接: 客户端连接到 MQTT 服务器。
  • 发布: 客户端发布消息到主题。
  • 订阅: 客户端订阅主题,并接收发布者发布的消息。

2. MQTT 服务器

MQTT 服务器是一个运行 MQTT 协议的应用程序。MQTT 服务器需要实现 MQTT 协议的三个主要功能:

  • 连接: 服务器接受客户端的连接请求。
  • 发布: 服务器将发布者发布的消息传递给订阅者。
  • 订阅: 服务器将订阅者的订阅信息存储起来。

3. MQTT 主题

MQTT 主题是消息的路径,用于区分不同类型的消息。主题的格式为:

1
/topic/[topic_name]

其中,topic_name 是主题名称。主题名称可以包含字母、数字、下划线和点。

4. MQTT 消息

MQTT 消息由两部分组成:

  • 报头: 报头包含消息的标识符、主题、QoS 等信息。
  • 数据: 数据是消息的内容。

QoS 是消息质量等级,用于控制消息的可靠性。QoS 有三个级别:

  • 0: 最多一次传递。
  • 1: 至少一次传递。
  • 2: 只有一次传递。

二、如何部署一个MQTT服务

1. 选择哪一种MQTT实现方案

根据前面的介绍,可以知道,要想使用MQTT,必须要有一个服务端。这个服务端既可以自己部署,也可以使用公有云提供的服务。

我这里目前选择在本地部署一个服务,但是后面为了稳定和网络问题,应该会选择使用阿里云的MQTT服务。

前面提到,MQTT是一种协议,具体的实现有很多种,我这里选择使用Mosquitto,它由Eclipse基金会维护,实现了MQTT协议版本5、3.1和3.1.1。

以下是一些使用Mosquitto的优点:

  1. 轻型: Mosquitto非常小巧,对系统资源要求低,这使得它可以在各种设备上运行,包括嵌入式设备
  2. 跨平台: 无论是Linux、Windows还是MacOS,甚至其他更多的操作系统都可以很好地支持Mosquitt
  3. 稳定性高: Mosquitto已经存在已久,并且被广泛使用在众多生产环境中,其稳定性得到了验证
  4. 活跃的社区: Mosquitto由Eclipse基金会维护,有着积极活跃的开发者和用户社区,这意味着当你遇到问题时,周围有许多人可能可以提供帮助
  5. 针对物联网的优化: MQTT协议本身就是为低功耗、不可靠网络设计的,而Mosquitto作为MQTT的实现,在物联网方案上也表现出色
  6. 强大的安全机制: Mosquitto支持SSL/TLS,还具备ACL(Access Control Lists)功能,以便您管理谁可以发布或订阅特定主题

让我感到意外的是,RabbiMQ竟然也支持MQTT协议,而且配置很简单,只是默认没有开启这个功能,以后可以试一下,互联网行业的开发者应该对RabbitMQ更熟悉一点。

2. 部署和配置过程

  1. 安装
1
sudo apt install mosquitto
  1. 配置
1
2
3
4
5
6
7
# 创建配置文件
vim /etc/mosquitto/mosquitto.conf
# 填充以下内容
# 端口
listener 1883
# 用户名和密码
allow_anonymous true

注意:我在这里为了本地测试方便,允许匿名访问,但是实际开发应用中不应该这么做。

  1. 启动
1
sudo systemctl start mosquitto

到这里,一个简单MQTT服务端就搭建完成了。

使用MQTT进行数据传输

在ros里面,是有专门的工具帮忙做这一步的,但是ros2里面还没有,不过我看论坛上大家更加推荐使用代码的形式做数据传输。

我使用的是python的paho这个包,首先需要安装

1
pip install paho

我这里贴两个代码,分别是publisher和subscriber,也就是发布者和订阅者。

1. publisher

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import time
import paho.mqtt.client as mqtt


class Publisher:
def __init__(self, host="127.0.0.1", port=1883, topic="test_channel"):
self.host = host
self.port = port
self.topic = topic
self.client = mqtt.Client()

self.client.on_connect = self.on_connect
self.client.on_publish = self.on_publish

def on_connect(self, client, userdata, flags, rc):
print("Connected with result code "+str(rc))

def on_publish(self, client, userdata, mid):
print("Message Published ...")

def start(self, msg="Hello MQTT", times=10, delay=1):
self.client.connect(self.host, self.port, 60)
self.client.loop_start()

for i in range(times):
time.sleep(delay)
self.client.publish(self.topic, f"{msg} {i}")


if __name__ == "__main__":
publisher = Publisher()
publisher.start()

2. subscriber

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import paho.mqtt.client as mqtt


class Subscriber:
def __init__(self, host="127.0.0.1", port=1883, topic="test_channel"):
self.host = host
self.port = port
self.topic = topic
self.msg_count = 0
self.client = mqtt.Client()

self.client.on_connect = self.on_connect
self.client.on_message = self.on_message

def on_connect(self, client, userdata, flags, rc):
print("Connected with result code "+str(rc))
self.client.subscribe(self.topic)

def on_message(self, client, userdata, msg):
self.msg_count += 1
print(f"Message {self.msg_count}: {msg.topic} {str(msg.payload)}")

def start(self):
self.client.connect(self.host, self.port, 60)
self.client.loop_forever()


if __name__ == "__main__":
subscriber = Subscriber()
subscriber.start()

可以在跟mosquitto所在的同一台机器上运行上面两个脚本,否则就要修改代码中的host为mosquitto实际的IP地址,还要确保网络没有限制。

测试的时候,要先运行subscriber,然后再运行publisher,否则subscriber很可能接收不到数据。