mavlink(一)帧格式和特性
1. 概述
1.1. 定义
MavLink通讯协议是一个为微型飞行器设计的非常轻巧的、只由头文件构成的信息编组库。它可以通过串口、网口等,非常高效地封装C结构数据,并将这些数据包发送至地面控制站。该协议被PX4, PIXHAWK, APM和Parrot AR.Drone平台所广泛测试并在以上的项目中作为MCU/IMU间以及Linux进程和地面站链路通信间的主干通信协议。
1.2. 优势
1.2.1. 开源
MAVLink基于LGPL开源协议而来,所以作为商业公司可以免费使用,并且相对于GPL要求使用开发的商业软件也必须开源,LGPL并无此要求,所以作为商业公司可以放心使用;
1.2.2. 支持不同的传输层和传输媒介
它支持不同类型的传输层和介质。可以通过WiFi、以太网或串口遥测低频宽通道。
1.2.3. 高可靠性
MAVLink设定了心跳包机制,可用于检测无人设备与地面站之间连通性的检测。众所周知,UDP是一种数据报协议,不需要客户端和服务器之间的连接,也没有机制可以确保可靠地传递消息,但是可以为实时性和丢失提供轻量级的替代方案。该机制使得使用UDP通信方式成为可能。
自2009年以来,MAVLink一直被用于在各种不同且具有挑战性的通信信道上的许多不同车辆,地面站(和其他节点)之间进行通信。它提供了检测数据包丢失,损坏和数据包身份验证的方法。
1.2.4. 支持多种编程语言
可在多种MCU(如:STM32、Atmega、ARM7)和操作系统(如:Windows,Linux,MacOS,Android和iOS)上运行。
1.2.5. 支持网络上最多255个并发系统
2. 版本说明
MAVLink v2.0:当前/推荐的主要版本。 2017年初被主要用户采用。
MAVLink v1.0: 2013年前后广泛采用。 仍被许多传统的外围设备使用。
MAVLink 2.0 C/C++ 和 Python 库向后兼容的 MAVLink 1.0 (支持这两个协议)。
不同mavLink的版本的起始字节不同
MAVLink 1: 0xFE
MAVLink 2: 0xFD
3. 帧格式
3.1. Mavlink v1
字节索引 | C版本 | 内容 | 值 | 说明 |
---|---|---|---|---|
0 | uint8_t magic | 数据包启动标记 | 0xFE | 特定于协议的启动(stx)标记,用于指示新数据包的开始。任何不识别协议版本的系统都将跳过数据包 |
1 | uint8_t len | 载荷长度 | 0 - 255 | 指示以下 payload 部分的长度 (为特定消息固定) |
2 | uint8_t seq | 数据包序列号 | 0 - 255 | 用于检测数据包丢失。 组件为发送的每封消息递增值。 |
3 | uint8_t sysid | 系统 ID | 1 - 255 | 发送消息的系统(vehicle)的 ID。用于区分网络上的系统。请注意,广播地址 0 可能不会在此字段中使用,因为它是无效的源地址。 |
4 | uint8_t compid | 组件ID | 1 - 255 | 发送消息的组件 ID。用于区分系统中的组件(例如自动驾驶仪和摄像头)。在 MAV_COMPONENT 中使用适当的值。请注意,广播地址 MAV_COMP_ID_ALL 可能不会在此字段中使用,因为它是无效的源地址 |
5 | uint8_t msgid | 消息 ID | 0 - 255 | 有效载荷中的 message type 的 id。 用于将数据解码出消息对象 |
n-byte payload | uint8_t payload[max 255] | 有效载荷 | 有效载荷数据,消息内容取决于msgid | |
(n+6) to (n+7) | uint16_t checksum | 校验和 | CRC-16/MCRF4XX计算,不包括magic字段,包括CRC_EXTERN |
- 对于没有有效负载的确认数据包,最小数据包长度为 8 个字节;
- 完整有效负载的最大数据包长度为 263 字节;
3.2. Mavlink v2
字节索引 | C版本 | 内容 | 值 | 说明 |
---|---|---|---|---|
0 | uint8_t magic | 数据包启动标记 | 0xFD | 特定于协议的启动(stx)标记,用于指示新数据包的开始。任何不识别协议版本的系统都将跳过数据包 |
1 | uint8_t len | 载荷长度 | 0 - 255 | 指示以下 payload 部分的长度 (为特定消息固定) |
2 | uint8_t incompat_flags | 不兼容标志 | 必须理解为 MAVLink 兼容性的标志 (如果不理解标志, 则实现丢弃数据包) | |
3 | uint8_t compat_flags | 兼容性标志 | 如果不识别, 则可以忽略的标志 (即使不识别标志, 实现仍然可以处理数据包) | |
4 | uint8_t seq | 数据包序列号 | 0 - 255 | 用于检测数据包丢失。 组件为发送的每封消息递增值 |
5 | uint8_t sysid | 系统 ID | 1 - 255 | 发送消息的系统(vehicle)的 ID。用于区分网络上的系统。请注意,广播地址 0 可能不会在此字段中使用,因为它是无效的源地址 |
6 | uint8_t compid | 组件ID | 1 - 255 | 发送消息的组件 ID。用于区分系统中的组件(例如自动驾驶仪和摄像头)。在 MAV_COMPONENT 中使用适当的值。请注意,广播地址 MAV_COMP_ID_ALL 可能不会在此字段中使用,因为它是无效的源地址 |
7-9 | uint32_t msgid:24 | 消息 ID (低、中、高字节) | 0 - 16777215 | 有效载荷中的 message type 的 id。 用于将数据解码出消息对象 |
n-byte payload | uint8_t payload[max 255] | 有效载荷 | 有效载荷数据,消息内容取决于msgid | |
(n+10) to (n+11) | uint16_t checksum | 校验和 | CRC-16/MCRF4XX计算,不包括magic字段,包括CRC_EXTERN | |
(n+12) to (n+25) | uint8_t signature[13] | 数据签名 | (可选)签名以确保链接是防篡改的 |
- 对于没有有效负载的确认数据包,最小数据包长度为 12 个字节;
- 对于使用整个有效负载的签名消息,最大数据包长度为 280 字节;
3.3. 字段细节
3.3.1. incompat_flags
该标志仅在mavlink v2版本中使用。通过该标志,表示MAVLink 库必须支持某些功能或属性才能处理数据包。该标志必须被识别,且该标志影响数据包的格式/排序。
如果 incompat_flags 字段中无法识别, 则 MAVLink 必须丢弃。
目前该字段支持的包括
标记 | C标志 | 特性 |
---|---|---|
0x01 | MAVLINK_IFLAG_SIGNED | 数据包 signed (签名已追加到数据包中) |
3.3.2. compat_flags
该标志仅在mavlink v2版本中使用。即使该标记不被识别,也应该正常处理该包;
该标记可被任意mavlink库使用者扩展使用,如将该标记视为优先级别设置。
3.3.3. CRC_EXTRA
CRC_EXTRA不在数据帧中,但mavlink使用CRC_EXTRA来参与校验和的计算。在计算校验和过程中,CRC_EXTRA被添加到有效载荷末尾,作为payload的一部分(仅在计算中使用,但不传输到链路中),并参与校验和的计算。 接收器可以使用它来确认它与有效负载消息格式/定义兼容。
如果消息规范不兼容 (例如 c 库 mavlink_parse_char() 给出状态 MAVLINK_FRAMING_BAD_CRC), 则 MAVLink 库应在解码过程中通知错误的 crc。
这个CRC_EXTRA是由生成mavlink代码的xml文件生成的,加入这个额外的东西是为了当飞行器和地面站使用不同版本的mavlink协议时,双方计算得到的校验码会不同,这样不同版本间的mavlink协议就不会在一起正常工作,避免了由于不同版本间通讯时带来的重大潜在问题。
3.3.4. checkSum
校验和,两字节,采用CRC-16/MCRF4XX计算,校验计算的时候不但要算进去收到的1-n+6范围内的数据,还要加上一个CRC_EXTRA。
3.3.5. signature
消息签名,该字段仅在mavlink v2版本中使用。使用该字段,MAVLink 系统可以验证消息是否来自受信任的源。
该字段是可选字段,对于已签名的数据包, incompat_flag 字段的最低位置位, 并在数据包中附加另外13个字节的 "签名" 数据。 签名的数据包格式如下:
签字的13字节为:
数据 | 描述 |
---|---|
linkID (8 bits) | 发送数据包的链接ID。通常与channel相同 |
timestamp (48 bits) | 2015年1月1日GMT时间以来的10个微秒时间戳。每个消息链接必须单步增加。请注意,如果数据包平均每秒100,000多个数据包,那么时间戳可能早于实际时间 |
signature (48 bits) | 基于完整的数据包、时间戳和秘密密钥,数据包有48位签名 |
linkId:
8位链接ID,确保了签名系统对多链接 MAVLink 系统的支持。 每个协议实现方都应该指定一个链接ID,指定它启用的 MAVLink 通信渠道,并将此ID置于链接ID字段中。 链接 ID 特别重要,因为不同链接(如WiFi,加上遥控无线电广播)之间可能存在巨大的潜在差异。
signature:
48位(6byte) 签名是 完整包的SHA-256 哈希值的前48位(不包括签名字段,但包括时间戳)。 密钥是 MAVLink 通道(即自动驾驶仪、地面站或 MAVLink API)两个终端储存的二进制数据的32字节。
这如下所示, 其中 + 表示串联, sha256_48() 是一个 sha256 实现, 它返回正常 sha256 输出的前 48位:
signature = sha256_48(secret_key + header + payload + CRC + link-ID + timestamp)
Timestamp:
时间戳是48位,从2015年1月1日起,单位为10微秒。 对于1970/1/1以来可用的系统(unexpoch),可以使用1420070400 秒的偏移值参与计算(即2015/01/01到1970/01/01差了1420070400秒)。
生成的所有时间戳必须至少比同一会话中同一链接 (SystemID、组件 id、LinkID) 元组发送的上一个时间戳多1。 如果以每秒超过 100,000 个数据包的速率突发数据包,则时间戳可能会早于 GMT 时间。
MAVLink 启用的设备可能不知道当前的 GMT 时间,例如,如果没有可靠的时钟源,或者,如果它刚刚启动,并且尚未从GPS 或其他系统中获得时间。
系统应当执行以下规则,以获得可靠的时间戳:
- 当前时间戳应定期储存在持久性储存中(最好至少每分钟一次);
- 启动时使用的时间戳应该是系统时钟和存储时间戳的最大值;
- 如果该系统没有一个 RTC 机制,则应当更新其在全球定位系统锁定时的时间戳;应该使用全球定位系统和存储时间戳的最大时间戳;
- 从特定链接发送的每个消息中,时间戳应增加一个;
- 当正确签名的信息被解码时,时间戳比当前时间戳大时,时间戳应替换。时间戳绝对不能来自错误签名的包(即使它们已经被 accepted);
- 收到的信息上的时间戳,应当与前一个收到的同链接(linkID,srcSystem,Srcontents) 的时间戳进行对比,如果时间戳较小,则该消息被否决;
- 如果先前没有收到具有给定 (linkID,srcSystem,SrcComponent) 的消息,则如果它比当前时间戳晚不超过 600 万(一分钟),则应接受该时间戳;
对于在持久存储中存储时间戳的设备, 实现方可以通过存储两个时间戳值来防止抢占条件。 在写入时, 应更新两个值中较小的值。 在读取时, 应使用两个值中较大的值。
3.3.5.1. 接受签名包
当签名的数据包到达时, 如果出现以下情况, 则应将其丢弃:
- 时间戳比来自同一逻辑流的上一个数据包小,即时间更早, 其中逻辑流被定义为具有相同 (SystemID、ComponentID、LinkID) 元组的 MAVLink 数据包的序列;
- 计算的48位签名与数据包中包含的签名不匹配;
- 时间戳在本地系统的时间戳后面超过 1分钟 (6, 000, 000);
3.3.5.2. 接受未签名包
MAVLink 库应该提供一种机制, 允许系统有条件地接受未签名数据包。
接受这些数据包的规则将是实现方指定的, 可以基于参数设置、传输类型、消息类型、兼容性标志和非兼容性标记等的组合。
所有不符合系统特定的未签名数据包接受规则的数据包都必须被拒绝 (不利于鉴权验证)。
关于何时接受未签名数据包的一些建议:
- 接受基于系统特定参数的所有未签名数据包;
- 如果连接是通过 "安全通道" (例如本地 usb 电缆或本地有线以太网电缆), 则接受所有未签名的数据包;
- RADIO_STATUS 数据包总是在不签名的情况下被接受 (以便使遥测数传的工作更轻松);
- 在 "无签名模式" (可能由启动时按下的硬件按钮触发) 时接受所有未签名的数据包;
- 接受所有未签名的数据包,直到收到签名的数据包(无条件),然后转到上面更受限制的签名规则;
3.3.5.3. 接受签名不正确的数据包
MAVLink 库应该提供一种机制, 允许系统有条件地接受签名不正确的数据包。
此功能可能有助于查找带有损坏的密钥的失联设备 (gcs 可以选择仍然显示位置信息, 尽管理想情况下使用不同的 "不受信任" 图标)。
接受错误签名数据包的系统应该提供一个非常明显的指示,表明连接是不安全/不安全的。格式错误的签名数据包表示配置错误、传输故障、协议故障或恶意操纵。
3.3.5.4. 密钥管理
密钥是32字节的二进制数据, 用于创建可由密钥的其他持有者验证的消息签名。 密钥应在网络中的一个系统 (通常是 GCS) 上创建, 并通过安全通道共享到其他受信任的设备。 系统必须具有共享密钥才能进行通信。
密钥应存储在持久存储设备中, 并且不得通过任何可公开访问的通信协议公开。 特别是, 密钥不得在可以用于公共日志分析的 MAVLink 参数、MAVLink 日志文件或数据闪存日志文件中公开。
生成密钥的方法取决于实现。 例如, 它可以通过以下方式生成:
- 用户输入的字符串, 然后通过 sha-256 运行。
- 随机密钥生成器。
密钥可以使用 SETUP_SIGNING 消息共享到其他设备。 该消息只能通过安全链接 (如 USB 或有线以太网) 发送, 作为直接消息发送到每个连接的 system_id/component_id。 必须设置接收系统来处理消息, 并将接收到的密钥存储到相应的永久存储中。
可以使用相同的安全方法来设置和重置系统的密钥(重置密钥不一定比首先设置它“更安全”)。
不应广播 SETUP_SIGNING 消息, 接收到的 SETUP_SIGNING 消息不得自动转发到其他活动的 MAVLink 设备/流通道。 这是为了避免通过安全链接 (如 usb) 收到的密钥通过不安全的链接 (例如 wifi) 自动转发到另一个系统的情况。
我们建议 GCS 实现生成密钥, 并通过安全链接 (例如 USB) 与连接的系统共享密钥。 可以将接收系统配置为忽略安全通道上的消息签名 (即接受所有签名、非签名或错误签名的数据包), 以便可以重置已丢失或损坏的密钥。
3.4. 字段重排序
MAVLink 链路上的数据包格式是专为资源受限优化过的,所以其中数据域的次序与 XML 规则中的次序不一致。 链路数据发生器根据信息的长度进行排序,最长的数据(uint64_t) 在前,然后逐渐到最短的数据。 它采用稳定的排序算法进行排序,这可以确保那些不用参与排序的数据域仍保留在同样的相对位置上。 这也可以避免编解码时的对齐问题,使打包/解包算法更高效。
消息有效载荷字段重新排序以便传输,如下:
- Fields are sorted according to their native data size:
(u)int64_t, double (8 bytes)
(u)int32_t, float (4)
(u)int16_t (2)
(u)int8_t, char (1) - 如果两个字段的长度相同, 则它们的顺序将保留为数据字段大小排序之前的顺序;
- 根据它们使用的数据类型处理数组,而不是根据总数组大小处理;
- 已传输的报文与 construction 相同,因此代表重新排序的字段;
- CRC_EXTERA 字段在重新排序后计算,这可以确保字段中的错误可以被 CRC错误检查 发现。提供的 Python, C 和 C# 参考执行测试,以便正确地重新排序,这只是习惯执行的关切。
上述重新排序的唯一例外是 MAVLink 2 扩展字段( MAVLink 2 extension)。 扩展字段以XML定义的顺序发送,不包括在CRC_EXTERA 计算中。 这允许新的扩展字段在消息结束后附加,但不打破二进制兼容性。
3.5. 空字节有效载荷截断(MAVLink 2)
使用mavlink2版本,发送方必须在序列化后,发送数据之前,截断有效载荷的末尾的零值数据;而mavlink 1版本,无论末尾数据如何,都照常发送。
接收带有零填充尾随字节的(不兼容的)MAVLink 2 消息的实现方,必须仍然支持消息的解码(如果它是有效的),并提供路由/转发消息的方法。消息可以完全不变地转发(即,不截断末尾零值数据,使用原始 CRC),或者转发实现可以截断末尾零值并重新计算 CRC。
实际受影响的字段/保存的字节 取决于消息及其内容(MAVLink 字段重新排序意味着我们只能说任何截断的字段通常是具有最小数据大小的字段或扩展字段)。
有效载荷的第一个字节永远不会被截断,即使是零值;
该协议仅截断序列化消息有效负载末尾的空字节;有效载荷正文中的任何空字节/空字段不受影响。
3.6. 路由
一个 MAVLINK 网络由多种系统组成(无人机、地面站、天线追踪器等),这些系统可能由一个或多个组件(自动驾驶仪、相机、服务器等)组成。
每个系统都有一个网络独有的 系统 id,每个组件都有一个系统独有的 组件 id 可用于地址/路由:
- 系统id 具有1-255之间的值。
- 默认自动驾驶系统 id通常是 1。 用户应该在有新的自动驾驶仪加入网络的时候分配新的自增的系统id;
- GCS 系统和开发者API 通常在数值范围顶部使用ID,以减少ID冲突(例如:255)。 它们的系统ID经常可用于允许多GCS系统;
- 组件 id 按类型和数字,从 MAV_COMPONENT 分配 ;
消息可用于所有系统、特定系统、系统中的所有组件或系统内的特定组件。 该协议定义了两个 8 位字段,可以(可选地)在消息载荷中指定,以指示消息应该发送/路由到哪里。如果 id 被省略或设置为零,则该消息被视为广播(适用于所有系统/组件)。 - target_system:执行命令的系统;
- target_component:执行命令的组件 (需要 target_system);
MAVLink 组件应处理具有匹配系统/组件 id 和广播消息的消息。他们应该将用于其他(或所有)接收方的消息路由或重新发送到其他活动通道(MAVLink 系统间可以通过不同的传输连接,由路由消息的 MAVLink 系统连接)。广播消息被转发到所有没有收到消息的通道。如果系统先前已经在该通道上收到来自目标的消息,则在新通道上重新发送已寻址消息(如果接收方不知道发送源,或在原始/传入通道上,则不会重新发送消息)。
转发的消息不得由转发系统更改/重新包装(原始消息传递到新链接)。
系统必须按照路由规则转发消息,即使它们无法处理 (例如无法验证的签名验证消息) 。没有被库支持 / 理解的消息应当转发,它们有可能是广播消息(在这种情况下无法读取目标系统/组件id)。
3.6.1. 路由细节
系统/组件如果具备下列条件,应在本地处理信息:
- 这是一个广播消息(target_system 字段忽略或零);
- target_system 与自身系统 id匹配并且 target_component 是广播字段(该字段为0或者省略);
- target_system 与其系统ID匹配,并拥有 target_component对应的组件;
- target_system 匹配其系统id,组件未知 (没有在链路网络上接收到具有该target_system/target_component的任何消息);
如果以下任一条件存在, 系统应将消息转发到另一个链接:
- 这是一个广播消息(target_system 字段忽略或零);
- target_system 与系统id和不符,系统知道目标系统的链接信息(即它先前从链接网络上接收到target_system的一个消息);
- target_system 与其系统 id 匹配, 并具有 target_component 字段, 并且系统在链接网络上收到过来自 target_system/target_component 组合的消息;
非广播消息只能发送 (或转发) 到已知的目标 (即系统必须于之前就收到来自目标系统/组件的消息)。
系统还应检查 SYSTEM_TIME 消息中的 time_boot_ms 的值是否减少, 因为这表明系统已重新启动过。 在这种情况下, 应该清除存储的路由信息 (并可能在重新启动后执行其他有用的操作-例如重新提取参数和位置等)。
3.6.2. C库支持
为 MAVLink v1 c 库生成的代码对于路由或使用 target_system 和 target_component 没有特定的支持。 若要提取这些字段, 需要使用为读取有效负载字段提供的常规方法, 并在字段名称上匹配。
C 库的 MAVLink v2 生成器已更新, 可以更轻松地从有效负载中获取目标系统和组件 id (分配这些 id 时)。 具体来说,mavlink_msg_entry_t 结构包含标志,告诉你消息是否包含目标系统/组件信息(FLAG_HAVE_TARGET_SYSTEM、FLAG_HAVE_TARGET_COMPONENT)和可用于获取这些 id 的有效负载的偏移量(分别为 target_system_ofs 和 target_component_ofs)。 MAVLink 提供的接口 const mavlink_msg_entry_t* mavlink_get_msg_entry(uint32_t msgid) 可用于从消息 id 获取此结构。
3.7. v1和v2版本的区别
- magic字段不同,v1版本为0xFE,v2版本为0xFD;
- v2版本增加了incompat_flags和compat_flags字段;
- v2版本将msgId扩充为3字节,同时msgId的可用范围也扩大到0~16777215。v1版本为1字节,范围为0-255;
- v2版本在帧末增加了13字节的,可选的signature字段,增加了消息签名的验证机制;
4. 参考链接
本文来自博客园,作者:流翎,转载请注明原文链接:https://www.cnblogs.com/hjx168/p/17706765.html