ardupilot开发 --- MQTT 篇

在这里插入图片描述
               原图:ardupilot-onboardComputer-4Glink-console.drawio

创作不易,白嫖党请点赞、收藏、关注

你说在一起要算命

前言

  • 为什么在ardupilot开发过程中要用到MQTT ?
    客户要求向他们的指挥中心平台推送视频流和飞控数据,即要将图数传数据推送给客户的指挥中心。
    方案1:frp方案搭建后,指挥中心可以通过mavsdk凭借mavlink协议连接到飞控,但是客户不愿使用mavsdk。
    方案2:在公网服务器搭建mqtt服务,飞机的机载计算机使用mavsdk获取到飞控数据,然后解析数据并封装成mqtt消息格式,最后使用mqtt客户端发布封装后的数据到mqtt服务器,客户的指挥中心就可以直接订阅这些mqtt消息就完事了。mqtt生态成熟,客户比较中意这个方案。

  • 文心一言:推荐几个好用的开源MQTT服务器

    • Eclipse Mosquitto
    • EMQ X Broker
    • HiveMQ
    • VerneMQ
    • NanoMQ
    • SMQTTX
  • 本文将是对EMQX学习的记录

  • EMQX有开源免费版付费版,区别如下:
    在这里插入图片描述
    功能对比:
    在这里插入图片描述

  • EMQ X Broker 简述
    特点:EMQ X Broker是一个高度可伸缩的分布式MQTT服务器,具有高性能和稳定性。它支持MQTT、MQTT-SN、CoAP、LwM2M等多种协议,内置了强大的规则引擎,支持多种认证和加密方式。
    优势:高性能、高可用、可扩展,支持集群部署和水平扩展,能够处理大量的连接和消息。同时,EMQ X提供了丰富的插件机制,方便用户进行定制开发。
    应用场景:适用于物联网、实时通信、消息推送等需要高性能和可扩展性的场景。

  • 文档
    EMQX Document

  • MQTT协议教程
    https://www.emqx.com/zh/mqtt-guide

  • MQTT专业术语
    https://www.emqx.com/zh/mqtt-guide

  • MQTT客户端示例
    MQTT 编程
    SDK和代码示例
    MQTT 客户端编程
    客户端SDK
    网页版客户端

  • 与MQTT对标的协议是HTTP

  • 物联网协议:MQTT or HTTP ?

认识MQTT

本节将对MQTT协议进行简单学习。详细教程

  • MQTT
    Message Queuing Telemetry Transport,是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通信。
  • 几个概念
    • MQTT 客户端
      提供数据、订阅数据的终端。运行客户端库的app。

    • MQTT Broker(MQTT服务器)
      建立连接、处理发布和订阅。
      为断连的客户端存储消息。

    • 发布-订阅模式
      发布者和订阅者间无需建立直接连接,通过 MQTT Broker 来路由和分发。

      在这里插入图片描述

    • 主题
      主题用于区分层级,类似于 URL 路径;
      主题支持通配符:+#

    • QoS
      QoS即服务质量,用于定义消息的可靠性。
      QoS 0:消息最多传送一次。如果当前客户端不可用,它将丢失这条消息。
      QoS 1:消息至少传送一次。
      QoS 2:消息只传送一次。

  • 工作流程
    客户端使用 TCP/IP 协议与 Broker 建立连接;
    客户端A订阅主题消息;
    客户端B发布主题消息;
    Broker 接收B发来的主题消息,并转发给订阅该主题的客户端A;
  • 创建连接
  • 订阅主题
  • 发布消息
  • 保留消息
    当 MQTT 客户端向服务器发布消息时,可以设置保留消息标志。保留消息存储在消息服务器上,后续订阅该主题的客户端仍然可以收到该消息。
  • Clean Session
    MQTT 客户端通常只能在在线状态下接收其它客户端发布的消息。如果客户端离线后重新上线,它将无法收到离线期间的消息。但是,如果客户端连接时设置 Clean Session 为 false,并且使用相同的客户端 ID 再次上线,那么消息服务器将为客户端缓存一定数量的离线消息,并在它重新上线时发送给它。
  • 遗嘱消息
    MQTT 客户端在向服务器发起 CONNECT 请求时,可以选择是否发送遗嘱消息标志,并指定遗嘱消息的主题和有效载荷。如果 MQTT 客户端异常离线(在断开连接前没有向服务器发送 DISCONNECT 消息),MQTT 服务器会发布遗嘱消息。
  • 进阶
    在这里插入图片描述
  • docker部署
  • EMQX Dashboard 管理控制台
    设备连接与相关指标监控管理
    http://localhost:18083/
    如: http://47.113.112.74:18083/#/dashboard
    默认用户名及密码:adminpublic
  • 密码认证登录
    • (1)在 EMQX Dashboard 管理控制台中开启密码认证、添加账户和密码密码(或ID和密码的认证方式)
      在这里插入图片描述
    • (2)在客户端连接到 EMQX 服务器时,填写的账号密码与在 EMQX Dashboard配置的相符合的才给连接
      在这里插入图片描述
  • python客户端
import paho.mqtt.client as mqtt 
# MQTT服务器地址  
MQTT_BROKER = "47.113.xx.xx"  
MQTT_PORT = 1883  
SUB_TOPIC = "cmd"
PUB_TOPIC = "copter"
MQTT_USERNAME = "shd01"  # 用户名
MQTT_PASSWORD = "shd01"  # 密码
MQTT_CLIENT_ID = "shd01" # ID
# 当连接建立后调用的回调函数  
def on_connect(client, userdata, flags, rc):  
    print("Connected mqtt server:" + MQTT_BROKER + " with result code " + str(rc))
    # 订阅主题  
    # 注意:某些MQTT服务器可能要求你在连接时提供用户名和密码后才能订阅主题  
    print('subscribe topic: '+SUB_TOPIC)
    client.subscribe(SUB_TOPIC)  

# 当接收到订阅的消息时调用的回调函数  
def on_message(client, userdata, msg):  
    print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")  
  
# 初始化MQTT客户端  
client = mqtt.Client(client_id=MQTT_CLIENT_ID)
# 绑定连接回调函数  
client.on_connect = on_connect  
# 绑定消息回调函数  
client.on_message = on_message  
# 设置用户名和密码  
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)  
# 连接到MQTT服务器  
client.connect(MQTT_BROKER, MQTT_PORT, 60)  
# 发布消息
client.publish('copter',payload='Hello copter',qos=0)
# 开始客户端循环,以处理网络事件
client.loop_start() 
print('mqtt client loop_start...')
try:  
    # 执行业务代码 
    print("执行业务代码...")
    import time
    time.sleep(500)
except Exception as e: 
    print(f"发生了一个错误: {e}")
finally:  # 无论是否发生异常,都会执行的代码块
    # 停止客户端循环
    client.loop_stop()
    # 断开与MQTT服务器的连接
    client.disconnect()
    print('finished...')
  • 集成mavsdk
    要求:Python 3.6+
    安装:
pip3 install mavsdk
pip3 install aioconsole
  • windows测试脚本
#!/usr/bin/env python3

import asyncio
from mavsdk import System
import subprocess  
import os  
import signal  
import time  
from datetime import datetime 

# 杀死exe进程
def kill_process_by_name(process_name):  
    try:  
        # 使用taskkill命令结束名为process_name的进程,/F表示强制结束  
        # 注意:taskkill可能需要管理员权限来结束某些进程  
        subprocess.run(['taskkill', '/F', '/IM', process_name], check=True)  
        print(f"进程 {process_name} 已被成功结束。")  
    except subprocess.CalledProcessError as e:  
        print(f"无法结束进程 {process_name}。错误:{e}")

async def print_battery(drone):
    async for battery in drone.telemetry.battery():
        print(f"Battery: {battery.remaining_percent}")


async def print_gps_info(drone):
    async for gps_info in drone.telemetry.gps_info():
        print(f"GPS info: {gps_info}")


async def print_in_air(drone):
    async for in_air in drone.telemetry.in_air():
        print(f"In air: {in_air}")


async def print_position(drone):
    async for position in drone.telemetry.position():
        print(position)


# EXE程序的路径  
exe_path = 'C:\\Users\\Administrator\\Desktop\\MQTT-WS\\mavsdk_server_win32  -p 50051'  
system_address="udp://:14540"

async def run():
    # 使用Popen启动EXE  
    try:
        # 不管3721先结束mavsdk_server_win32再重新运行
        kill_process_by_name('mavsdk_server_win32.exe')

        filename = 'mavlog.txt'  
        f=open(filename, 'a')
        f.write(str(datetime.now())+'\n')
        # shell=True在Windows上是必需的,如果你需要运行一个.exe文件  
        # 注意:出于安全考虑,避免在不受信任的输入上使用shell=True
        process = subprocess.Popen(exe_path, shell=True, stdout=f, stderr=f)  # stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL表示不输出log
    
        time.sleep(2)

        # Init the drone
        drone = System(mavsdk_server_address='localhost', port=50051)
        print("drone.connect wait...")
        await drone.connect(system_address)
        print(f"connecting: {system_address}")

        # Start the tasks
        asyncio.ensure_future(print_battery(drone))
        asyncio.ensure_future(print_gps_info(drone))
        asyncio.ensure_future(print_in_air(drone))
        asyncio.ensure_future(print_position(drone))

        while True:
            await asyncio.sleep(1)
 
    except Exception as e: 
        print(f"发生了一个错误: {e}")
        f.close()
        kill_process_by_name('mavsdk_server_win32.exe')

    finally:  
        # 尝试杀死进程
        kill_process_by_name('mavsdk_server_win32.exe')
        print("end")
    

if __name__ == "__main__":
    # Start the main function
    asyncio.run(run())

参考文献

【1】文心一言
【2】https://www.emqx.com/zh

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值