Mavlink协议理解
mavlink协议官网地址: http://mavlink.io/zh/
mavlink协议介绍http://qgroundcontrol.org/mavlink/start
之前看了mavlink协议,网上关于mavlink的资料不多。本文大概总结了下对mavlink协议的理解。以下如不说明都是说mavlink v1.0版本。
MavLink啥也不是就是一条消息。MavLink也可以用于地面机器人,所以微型飞行器链路(Micro Aerial Vehicle Link,也就是MavLink) 并不是一个十分贴切的名字。之所以采取这种方式命名呢,是因为它起始于直升机(如果我没猜错的话)。
先简单介绍下mavlink协议。Mavlink协议最早由 苏黎世联邦理工学院 计算机视觉与几何实验组 的 Lorenz Meier 于2009年发布,并遵循LGPL开源协议。Mavlink协议是在串口通讯基础上的一种更高层的开源通讯协议,主要应用在微型飞行器(micro aerial vehicle)的通讯上。Mavlink是为小型飞行器和地面站(或者其他飞行器)通讯时常常用到的那些数据制定一种发送和接收的规则并加入了校验 (checksum)功能。
【1】下面开始说介绍mavlink所发送的数据结构。Mavlink传输时的基本单位是消息帧。
如图所示,每个消息帧都是上述的结构,除了灰色外,其他的格子都代表了一个字节的数据。
注意:校验码由crc16算法得到,算法从消息包的1~n+6字节(不包含STX),还要额外加上个MAVLINK_CRC_EXTRA进行计算得到一个16位的校验码。每个消息的头文件里都包含MAVLINK_CRC_EXTRA,这个 MAVLINK_CRC_EXTRA是由生成mavlink代码的xml文件生成的,加入这个额外的东西是为了当飞行器和地面站使用不同版本的mavlink协议时,双方计算得到的校验码会不同,这样不同版本间的mavlink协议就不会在一起正常工作,避免了由于不同版本间通讯时带来的重大潜在问题。
【2】下面主要介绍mavlink里消息的种类和如何看懂开始时提到的那个官方的mavlink消息介绍。
(一)中已经提到了在mavlink消息帧里最重要的两个东西,一个是msgid;一个是payload,前者是payload中内容的编号,后者则存放了消息。消息有许多种类型,在官网的网页中中以蓝色的“#”加数字的方式来表示消息的编号如 “#0”(这样的表示方法应该是为了方便在网页中查找相应编号消息的定义)。在官网介绍网页里往下拉,大概拉到二分之一的位置处,开始出现“MAVLink Messages”的介绍,往下看是各种消息的数据组成说明。
下面将以几个消息为例,讲解mavlink消息。
先以 #0 消息为例,这个消息叫心跳包(heartbeat)。它一般用来表明发出该消息的设备是活跃的,飞行器和地面站都会发出这个信号(一般以1Hz发送),地面站和飞行器会根据是否及时收到了心跳包来判断是否和飞行器或地面站失去了联系。
从图上可以看出,心跳包由6个数据组成,第一个参数是占一个字节的飞行器类型数据(type),这个数据表示了当前发消息的是什么飞行器,比如四旋翼,固定翼等等。type的取值如何与飞行器类 型对应,这要在官方的mavlink消息介绍网页上找,位于网页开始出的数据枚举中。如下图所示:
这里只是一部分的类型,第一个是通用 飞行器,对应的type数值是0;第二个是固定翼类型,对应的数值是1;第三个对应的是四旋翼,对应的数值是2.这个飞行器类型,其实对于发心跳包的地面 站来说可能没什么意义(不同飞控对该消息的处理方法不同,至少刷了PX4固件的Pixhawk飞控对地面站发来的心跳包里的这个参数并不关心,如无特殊说 明,之后所说的Pixhawk飞控都是指刷PX4固件的飞控),对于飞行器端来说代表了当前飞行器的类型,地面站可以根据这个参数来判断飞行器的类型并作 出其他的反应。
第二个参数是自驾仪(即通常所说的飞控)类型,比如apm,ppz,Pixhawk等飞控,具体定义查找和之前查找飞行器类型时的方法一样。同样的,对于发送心跳包的飞行器来说代表了自己的飞控类性,对地面站发出的心跳包来说意义不大。
第四个参数是用户自定义模式(custom mode),大概说一下Pixhawk的用户模式。以多轴为例。它分为主模式(main mode)和子模式(sub mode),两种模式组合在一起成为最终的模式,主模式分为3种,手动(manual),辅助(assist),自动(auto)。手动模式类似apm的姿态模式。在辅助模式中,又分为高度控制模式(altctl)和位置控制模式(posctl)两个子模式,高度控制模式就类似apm的定高模式,油门对应到飞行器高度控制上。位置模式控制飞行器相对地面的速度,油门和高度控制模式一样,yaw轴控制和手动模式一样。自动模式里又分为3个子模式,任务模式(mission),留待模式(loiter),返航模式(return), 任务模式就是执行设定好的航点任务,留待模式就是gps悬停模式,返航模式就是直线返回home点并自动降落。在apm里这个参数貌似是没有用的,注意这 个数据占了4个字节,在Pixhawk中,前两个字节(低位)是保留的,没有用,第三个字节是主模式,第四个字节是子模式。普通用户请无视,开发者请注意:官网给出的通过程序设置模式的代码是错误的。如图,最后一行代码有误,应该为:
uint32_t custom_mode = (main_mode<< 16) | (sub_mode << 24);
第六个是mavlink版本(mavlink version),现在是“3”版本。
其余的消息也是类似的结构,各个数据的定义可以查看mavlink官方网页的说明,这些说明一般在网页的前面部分。具体说明以飞控为准,mavlink仅提供基本的定义。
有几个相对特殊和容易混淆的消息再特别说明下:
#76消 息(command long),该消息是发送长命令,一般是地面站发送给飞控命令用的。该消息组成如下图。目标系统(命令的接收方,就是目标系统编号sysid),目标单元 (命令的接收单元,就是目标单元编号compid)。command数据是这条命令的编号,用于区别不同的命令。confirmation数据,笔者还不 是很明白,大概是是否需要收到命令后回复确认信号的意思。接下去有七个参数,这些参数是执行这条命令所需要告诉飞控的,许多命令都用不到七个参数,多余的 参数清0就可以了。
现在应该对介绍mavlink官网的布局有所了解了吧。网页前面主要讲了各类数据的取值和含义,比如飞控类型(mav_autopilot),飞行器类型 (mav_type)等,其中mav_cmd是比较特殊和重要的一种数据。网页的后半部分主要讲了mavlink消息的种类和数据组成,这里会用到各种数 据,具体数据定义的可以回到前半部分去找。但是mavlink是个通用的通讯协议,不同的飞控支对mavlink支持方式不一样,一般都只支持一部分mavlink消息,还会自己扩展一些mavlink协议所没有定义的消息(pixhawk和apm都是如此),具体都以飞控代码为准。
下面以发送心跳包 (heartbeat)为例,说明下如何使用mavlink头文件来发送心跳包。首先打开common文件夹中的 mavlink_msg_heartbeat.h 头文件。这个头文件可以分为两部分,一部分用来打包、发送heartbeat消息,另一部分用来接收到heartbeat消息时解码消息。 heartbeat.h定义了heartbeat消息对应的数据类型:
typedef struct __mavlink_heartbeat_t { uint32_t custom_mode; ///< A bitfield for use for autopilot-specific flags. uint8_t type; ///< Type of the MAV (quadrotor, helicopter, etc., up to 15 types, defined in MAV_TYPE ENUM) uint8_t autopilot; ///< Autopilot type / class. defined in MAV_AUTOPILOT ENUM uint8_t base_mode; ///< System mode bitfield, see MAV_MODE_FLAG ENUM in mavlink/include/mavlink_types.h uint8_t system_status; ///< System status flag, see MAV_STATE ENUM uint8_t mavlink_version; ///< MAVLink version, not writable by user, gets added by protocol because of magic data type: uint8_t_mavlink_version } mavlink_heartbeat_t;
如果mavlink的发送方式可以使用(串口发送,函数接口也兼容),则可以调用 【1】
static inline void mavlink_msg_heartbeat_send(mavlink_channel_t chan, uint8_t type, uint8_t autopilot, uint8_t base_mode, uint32_t custom_mode, uint8_t system_status)
其中的chan是channel的缩写,用于选择发送的串口或者usb口。type就是飞行器类型,其余参数不明的可以看看本博客的上面文章。
该函数功能是将传入的各个参数按照对应的格式放到heartbeat消息包中(即打包)
这个函数内部有一句预处理:
#if MAVLINK_CRC_EXTRA
是说是否使用额外的crc校验字符(默认使用),详情请看第一篇博客中对于两个校验字节的说明。
函数中会调用函数【2】
_mav_finalize_message_chan_send(chan, MAVLINK_MSG_ID_HEARTBEAT, buf, MAVLINK_MSG_ID_HEARTBEAT_LEN, MAVLINK_MSG_ID_HEARTBEAT_CRC); MAVLINK_MSG_ID_HEARTBEAT//这个是心跳包消息对应的编号 这里=0 MAVLINK_MSG_ID_HEARTBEAT_LEN//这个是心跳包的长度 注意这个长度仅仅是payload的长度,不包括帧的头尾。 MAVLINK_MSG_ID_HEARTBEAT_CRC//这个是heartbeat消息对应的额外的crc校验码 这里=50
如果只是想将对应的心跳包参数按照心跳包的格式存放好,则可以只调用
static inline uint16_t mavlink_msg_heartbeat_pack(uint8_t system_id, uint8_t component_id, mavlink_message_t* msg, uint8_t type, uint8_t autopilot, uint8_t base_mode, uint32_t custom_mode, uint8_t system_status)
MAVLINK_HELPER uint8_t mavlink_parse_char(uint8_t chan, uint8_t c, mavlink_message_t* r_message, mavlink_status_t* r_mavlink_status)
它会将收到的字符一个个进行解码,会检验收到的校验码是否正确;有效载荷的长度小于最大长度并且和该消息的长度一致。如果一切顺利,将会得到解码到的消息,放在解码得到的消息帧类型中
typedef struct __mavlink_message { uint16_t checksum; ///< sent at end of packet uint8_t magic; ///< protocol magic marker uint8_t len; ///< Length of payload uint8_t seq; ///< Sequence of packet uint8_t sysid; ///< ID of message sender system/aircraft uint8_t compid; ///< ID of the message sender component uint8_t msgid; ///< ID of message in payload uint64_t payload64[(MAVLINK_MAX_PAYLOAD_LEN+MAVLINK_NUM_CHECKSUM_BYTES+7)/8]; }