一文搞定MAVLINK软件协议

2023-12-18 04:58

本文主要是介绍一文搞定MAVLINK软件协议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载:https://mp.weixin.qq.com/s/iGURlSS7V-5iBCEtgpzT7w

一文搞定MAVLINK软件协议

原创 L君 TBUS社区 2019-11-06

本文纯属资深程序猿个人观点,旨在让大家从不同的角度理解MAVLINK协议,文中包含的一些玩笑话,大家不要当真,看着玩玩儿呗~


 

图片

搞开源无人机的朋友最耳熟能详的莫过于它的通讯协议MAVLINK了。

 

Mavlink----一个又好气又好笑的名词,仿佛自带光环,它一出场,就会附带两个小弟:ros、mavros。

图片

网络上mavros免费的课程一大堆,mavlink的倒是少之又少,犹如,未过门的媳妇,见不得人啊图片

 

 

很多人都会形成一种观念,难道是因为mavros要简单些?今天L君要告诉你们,其实直接使用mavlink比使用mavros简单的多啦。

 

对于一个老程序员来说,理解数据结构犹如吃饭,但是理解框架犹如登天!

 

先来好好说说这个大哥和两小弟的关系。

ROS

robot operation system,机器人操作系统。说是操作系统,其实跟硬件毫无关系,完全是用一大堆软件堆砌起来的彻头彻尾的----“骗局”。没有mmu、smp、cache,它唯一的功能就是----通讯。把所有的逻辑性代码转换为通讯数据,比如一个函数调用完成的事情,它会拆分成----publish数据、对端接受数据、执行代码。这里看起来,ros跟mavlink似乎毫无关系,直到说到mavros。

 

MAVROS

一个在ros下的mavlink软件适配包,它的作用其实就是将mavlink协议放到ros框架里去解析,统一使用ros框架,publish、subscribe进行操作。

 

MAVLINK

一种软件协议,说白了,就是一大堆的struct,做过服务器的朋友应该会搞懂,其实就是封包格式啦,只是mavlink由于介质不唯一,所以加了CRC效验而已。

 

 

那么从这里我们已经看出了端倪:学mavros其实是无端的增加了学习成本呀,本来我收发收发数据就完事儿了的东西,我为啥一定要去学一套ros呢?

 

而且就程序员的原则而言,我们学习时,一定要看到本质,用mavros的时候,中间经过了一个黑盒,里面到底干了什么,我们根本不知道呀。

 

L君以前也吃过很多亏,用mavros反跟ardupilot代码,发现,我publish出去的东西,人家ardupilot收到的压根儿就不一样!坐标系从NED变成了ENU,然而heading的坐标系还是NED。有些结构体少了几个参数,有的甚至比mavlink原来的还多了几个。

 

不仅mavros在给你制造着麻烦,ros也时不时给你制造麻烦:

CMakeList怎么改才能用PCL库?

TMD怎么有的包必须用catkin_make_isolated才能编译?

不是说好的catkin_make吗?

为什么我publish出去没效果啊?

catkin_make一分钟,看一次效果3秒。MD怎么用gdb啊?

我好想单步走啊,printf调试起来内心在滴血- -。。。

 

不!!!这简直就是地狱!我要砍掉它,这不是人干的事儿。我忍受着在linux下用记事本写代码的痛苦,你还给我搞这些幺蛾子?

 

算了算了,框架这玩意儿不是我等卑微程序员能玩儿的东西,我们还是不怕苦不怕累的啃啃数据流吧。

 

当我正打算回到小黑屋啃mavlink数据协议的时候,我发现了

图片

 

一个老程序员的直觉,既然mavlink是以modules存在的,那么一定在那神奇的网

图片

好吧好吧,其实我早就知道了,人家mavlink为了用户方便,sdk早就给我们准备好啦,根本不需要我们自己去做解析,我们快速的git clone下来:

图片

然后我们执行mavgenerate.py脚本:

图片

 

这里我们XML选择:message_definitions/v1.0/common.xml(其中还有ardupilotmega.xml表示支持APM固件扩展协议,以及其他无人机固件的协议,common表示通用协议,所有固件都支持。)

 

Out随意选择一个要生成代码的地方

 

语言选择c语言

 

协议簇2.0

 

点击生成:

图片

图片

 

一个通用的mavlink库就生成好啦

图片

我们现在只需要在编程的时候包含这些头文件就可以实现我们的mavlink通讯了。

 

 

现在赶快连上ALICE飞控,打开visual studio:

图片

 

将我们刚才生成的mav_inc文件夹整个丢进新创建的项目:

图片

 

并在解决方案资源管理器中添加mavlink的头文件和一个main.cpp(注意文件夹需要自己添加哦):

图片

 

 

写入如下代码:

#include <math.h>
#include <thread>
#include <mutex>
#include "mav_inc/mavlink_types.h"
#define MAVLINK_USE_CONVENIENCE_FUNCTIONS
#define MAVLINK_SEND_UART_BYTES(a, b, c) send(a, b, c, 0)
mavlink_system_t mavlink_system = { 1,1 };
#include "mav_inc/common/mavlink.h"
int main(void){
        Socket sock;
        sock.Create();
        while (sock.Connect((char*)"127.0.0.1", 1244) != 0) 
       { 
               printf("connect error.waiting for 1 second...\n");
                usleep(1000000);
        } 
        while (1)
        { 
               mavlink_message_t msg; 
               mavlink_status_t status;
                uint8_t buf[1024]; 
               int recv_len = sock.Receive((char*)buf, 1024); 
               for (int i = 0; i < recv_len; i++) 
               { 
                       if (mavlink_parse_char(0, buf[i], &msg, &status))
                        { 
                               switch (msg.msgid)  
                              {  
                              case MAVLINK_MSG_ID_ATTITUDE: 
                                       mavlink_attitude_t attitude; 
                                       mavlink_msg_attitude_decode(&msg, &attitude);
                                        printf("rpy=%f,%f,%f\n", attitude.roll, attitude.pitch, attitude.yaw);  
                                      break; 
                               case MAVLINK_MSG_ID_HEARTBEAT: 
                                       mavlink_heartbeat_t heart; 
                                       mavlink_msg_heartbeat_decode(&msg, &heart);
                                        if (heart.base_mode) 
                                       {  
                                              bool armed = (heart.base_mode & MAV_MODE_FLAG_SAFETY_ARMED); 
                                               bool guided = (heart.base_mode & MAV_MODE_FLAG_GUIDED_ENABLED); 
                                               printf("armed=%d,guided=%d\n", armed, guided); 
                                       }  
                                      break; 
                               default: 
                                       break; 
                               } 
                       }
                } 
       }
         return 0;
}

 

 

编译并执行:

图片

 

可以看到我们已经从mavlink取到当前飞机的姿态信息啦!有着完美的代码变色、自动补全、智能分析排错的visual studio是不是已经让你享受起开发的乐趣了呢?再配合上Alice板卡的wifi网卡功能,你甚至都可以不需要任何辅助,直接实地调试你的程序哟(被逼着插进来的硬核广告图片)。

 

现在我们再回过头来开看看代码。

 

class Socket这个类就不必说了,他是一个tcp协议的公用库类,实现网络通讯功能,不懂的自己百度一下。Alice的ardupilot固件默认使用2号telem作为本地tcp通讯端口,方便开发mavlink外机控制程序。 现在我们先来看main函数里面做了什么,寥寥几行,基本就实现了通讯了:

//实例化网络对象
Socket sock;
//初始化网络对象
sock.Create();
        //循环连接本地ip端口1244,alice开机自启动ardupilot使用tcp协议侦听1244号端口发送mavlink 
         while (sock.Connect((char*)"127.0.0.1", 1244) != 0) 
         {
                //如果连接失败,打印消息,等待1s重试。
                printf("connect error.waiting for 1 second...\n");
                usleep(1000000);
        } 
        while (1)
       { 
              mavlink_message_t msg; 
               mavlink_status_t status; 
               uint8_t buf[1024];//从连接上的tcpsocket收包存到buf中
              int recv_len = sock.Receive((char*)buf, 1024); 
               for (int i = 0; i < recv_len; i++) 
               {
                      //这里其实就是最重要最重要的地方了,mavlink_parse_char函数
                      //第一个参数默认0就行
                      //第二个参数是一个字节,也就是我们收一次包,比如有20个自己,就循环
                      //调用20次mavlink_parse_char,把每个字节依次放入
      //第三个参数,返回的真实mavlink_raw包
                    //第四个参数,返回一些正在解析的状态
                     //如果mavlink_parse_char返回true,表示已经真正收到了一个包,并已
                     //填充到了msg中。但是我们还不能直接使用这个包,应为现在还是raw形式
                     //下面还会将它转换成结构体形式!
                        if (mavlink_parse_char(0, buf[i], &msg, &status)) 
                       {
                                //这个开关语句就是判断收到包的ID号到底是多少啦
                                //raw形式的msg只能取到ID,无法取到内容 
                               switch (msg.msgid) 
                               {
                                case MAVLINK_MSG_ID_ATTITUDE:
                                         //判断完ID以后我们就可以直接开始调用函数解析啦
                                        //这里解析的是attitude那么就调用mavlink_msg_attitude_decode
                                        //如果是local_position就是mavlink_msg_local_position_decode
                                        //具体用什么decode你们可以直接到头文件中去查
                                       //mavlink头文件名几乎都是自注释的
                                        mavlink_attitude_t attitude;
                                        mavlink_msg_attitude_decode(&msg, &attitude);
                                        printf("rpy=%f,%f,%f\n", attitude.roll, attitude.pitch, attitude.yaw);
                                        break;
                                case MAVLINK_MSG_ID_HEARTBEAT:
                                        mavlink_heartbeat_t heart;
                                        //那么这里id是心跳,那么解析函数就是mavlink_msg_heartbeat_decode啦
                                        mavlink_msg_heartbeat_decode(&msg, &heart);
                                        if (heart.base_mode) 
                                       { 
                                               bool armed = (heart.base_mode & MAV_MODE_FLAG_SAFETY_ARMED); 
                                               bool guided = (heart.base_mode & MAV_MODE_FLAG_GUIDED_ENABLED);
                                                printf("armed=%d,guided=%d\n", armed, guided);
                                        }
                                        break; 
                               default: 
                                       break; 
                               } 
                      }
                } 
       }

 

那么,这篇就讲到这里啦,看完全篇,是不是觉得mavlink开发其实是一件非常惬意的事情呢,用对了工具,用对了硬件,才能真正方便我们快速解决问题。

 

本文选自TBUS论坛_L君原创文章

 

这篇关于一文搞定MAVLINK软件协议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/507111

相关文章

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

一文带你迅速搞懂路由器/交换机/光猫三者概念区别

《一文带你迅速搞懂路由器/交换机/光猫三者概念区别》讨论网络设备时,常提及路由器、交换机及光猫等词汇,日常生活、工作中,这些设备至关重要,居家上网、企业内部沟通乃至互联网冲浪皆无法脱离其影响力,本文将... 当谈论网络设备时,我们常常会听到路由器、交换机和光猫这几个名词。它们是构建现代网络基础设施的关键组成

Java对接MQTT协议的完整实现示例代码

《Java对接MQTT协议的完整实现示例代码》MQTT是一个基于客户端-服务器的消息发布/订阅传输协议,MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛,:本文主要介绍Ja... 目录前言前置依赖1. MQTT配置类代码解析1.1 MQTT客户端工厂1.2 MQTT消息订阅适配器1.

Linux中的自定义协议+序列反序列化用法

《Linux中的自定义协议+序列反序列化用法》文章探讨网络程序在应用层的实现,涉及TCP协议的数据传输机制、结构化数据的序列化与反序列化方法,以及通过JSON和自定义协议构建网络计算器的思路,强调分层... 目录一,再次理解协议二,序列化和反序列化三,实现网络计算器3.1 日志文件3.2Socket.hpp

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

一文解密Python进行监控进程的黑科技

《一文解密Python进行监控进程的黑科技》在计算机系统管理和应用性能优化中,监控进程的CPU、内存和IO使用率是非常重要的任务,下面我们就来讲讲如何Python写一个简单使用的监控进程的工具吧... 目录准备工作监控CPU使用率监控内存使用率监控IO使用率小工具代码整合在计算机系统管理和应用性能优化中,监

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

如何在Spring Boot项目中集成MQTT协议

《如何在SpringBoot项目中集成MQTT协议》本文介绍在SpringBoot中集成MQTT的步骤,包括安装Broker、添加EclipsePaho依赖、配置连接参数、实现消息发布订阅、测试接口... 目录1. 准备工作2. 引入依赖3. 配置MQTT连接4. 创建MQTT配置类5. 实现消息发布与订阅