MAVLink 消息传送
MAVLink 是一个非常轻量级的消息传输协议,专为无人机生态系统设计。
PX4 的用途 MAVLink 与地面站和 MAVLink SDK(如 QGroundControl 和 MAVSDK同时也是连接飞行控制器以外的无人机组件的集成机制:配套计算机、支持 MAVLink 的相机等。
本主题简要介绍了 MAVLink 的基本概念,如消息、命令和微服务。它还提供了如何添加 PX4 支持的教程说明:
- MAVLink 信息流
- 处理收到的 MAVLink 信息并写入 uORB 主题。
信息
本专题不包括 指挥部 处理和发送,或如何实现自己的微服务。
MAVLink 概览
MAVLink 是一种轻量级协议,旨在通过不可靠的低带宽无线电链路高效发送信息。
信息 是 MAVLink 中最简单、最基本的定义,由一个名称(如 态度)、id 和包含相关数据的字段。它们特意采用轻量级设计,大小有限,没有重发和确认的语义。独立报文通常用于流式传输遥测或状态信息,以及发送不需要确认的命令,例如以高速率发送的设定点命令。
命令协议 是一种更高级别的协议,用于发送可能需要确认的命令。具体命令定义为 MAV_CMD 枚举,例如起飞指令 mav_cmd_nav_takeoff并包含最多 7 个数字参数值。协议发送命令时,会将参数值打包到一个 COMMAND_INT
或 命令长
消息,并等待以 命令返回
.如果没有收到确认,命令将自动重新发送。请注意 MAV_CMD 定义也用于定义任务行动,而且并非所有定义都支持在 PX4 命令/任务中使用。
微服务 是建立在 MAVLink 信息之上的其他高级协议。这些协议用于通信无法在单条信息中发送的信息,并提供可靠通信等功能。上文所述的命令协议就是此类服务之一。其他协议包括 文件传输协议, 摄像机协议 和 任务规程.
MAVLink 信息、命令和枚举在 XML 定义文件.MAVLink 工具链包含代码生成器,可根据这些定义创建特定于编程语言的库,用于发送和接收消息。请注意,大多数生成的库并不创建用于实现微服务的代码。
MAVLink 项目使用以下定义文件(请注意,更高级别的文件为 包括 下面文件的定义):
- development.xml - 建议纳入标准的定义。定义移至
common.xml
如果测试后被接受。 - common.xml - 一个定义库,可满足许多常见的无人机使用情况。许多飞行算法池、地面站和 MAVLink 外围设备都支持这些定义。使用这些定义的飞行算法池更有可能实现互操作。
- 标准.xml - 这些定义实际上是标准定义。它们存在于绝大多数飞行算法池中,并以相同的方式实现。
- minimal.xml - 最小 MAVLink 实现所需的定义。
该项目还主办了 方言 XML 定义,其中包含针对飞行算法池或其他利益相关方的 MAVLink 定义。
该协议依赖于通信的每一端对所发送信息的共享定义。这意味着,为了进行通信,通信的两端都必须使用由相同 XML 定义生成的库。
PX4 和 MAVLink
PX4 发布版本 common.xml
默认情况下,MAVLink 定义与 MAVLink 地面站、库和外部组件(如 MAVLink 摄像机)的兼容性最强。在 主要
分支,这些分支从 development.xml
关于大屿山国际旅游岛,以及 common.xml
为其他董事会服务。
信息
要成为 PX4 版本的一部分,您使用的任何 MAVLink 定义都必须在 common.xml
(或包含的文件,如 标准.xml
和 minimal.xml
).在开发过程中,您可以使用 development.xml
.您需要与 MAVLink 团队 来定义和贡献这些定义。
PX4 包括 mavlink/mavlink repo 下的一个子模块 /src/modules/mavlink.其中包含 XML 定义文件,文件格式为 /mavlink/messages/1.0/.
构建工具链会在构建时生成 MAVLink 2 C 头文件。生成头文件的 XML 文件可在 PX4 kconfig 电路板配置 在每个板块的基础上,使用变量 config_mavlink_dialect
:
- 代表 SITL
config_mavlink_dialect
设置为发展
于 boards/px4/sitl/default.px4board.您可以将其更改为任何其他定义文件,但该文件必须包括common.xml
. - 其他电路板
config_mavlink_dialect
默认情况下没有设置,PX4 会在common.xml
(这些都是内置于 mavlink 模块 默认情况下 - 搜索menuconfig MAVLINK_DIALECT
于 src/modules/mavlink/Kconfig).
文件会生成到构建目录中: /build/<build target>/mavlink/
.
自定义 MAVLink 信息
自定义 MAVLink 信息是 PX4 默认定义中没有的信息。
信息
如果使用自定义定义,则需要在 PX4、地面站以及与之通信的任何其他 SDK 中维护该定义。一般来说,您应尽可能使用(或添加到)标准定义,以减轻维护负担。
自定义定义可添加到与标准 XML 定义相同目录下的新方言文件中。例如,创建 PX4-Autopilot/src/modules/mavlink/mavlink/message_definitions/v1.0/custom_messages.xml
并设置 config_mavlink_dialect
为 SITL 创建新文件。该方言文件应包括 development.xml
这样,所有标准定义也都包含在内。
对于初始原型,或者如果您打算将信息作为标准信息,您也可以将信息添加到 common.xml
(或 development.xml
).这简化了构建过程,因为您不需要修改已构建的方言。
MAVLink 开发人员指南解释了如何在 如何定义 MAVLink 消息和枚举.
您可以通过检查在构建目录 (/build/<build target>/mavlink/
).如果您的邮件未编译,它们可能格式不正确,或使用了冲突的 ID。请查看构建日志了解相关信息。
建立信息后,您就可以按照以下各节所述,流式传输、接收或以其他方式使用信息。
信息
MAVLink 开发人员指南 有更多关于使用 MAVLink 工具链的信息。
MAVLink 信息流
MAVLink 报文使用流类进行流式传输。 MavlinkStream
已添加到 PX4 数据流列表中。PX4 可以从生成的 MAVLink 消息定义中获取所需的信息。它还有一个 发送()
方法,每次需要发送信息时都会调用该方法--重载该方法可将信息从 uORB 订阅复制到要发送的 MAVLink 信息对象。
本教程演示了如何将 uORB 信息流作为 MAVLink 信息,适用于标准信息和自定义信息。
先决条件
一般来说,您已经拥有 uORB 消息,该消息包含您希望流式传输的信息,以及您希望与之一起流式传输的 MAVLink 消息的定义。
在本例中,我们将假设您想要将(现有的)"...... 电池状态 uORB信息转换为新的MAVLink电池状态信息,我们将其命名为 电池状态演示
.
复制 电池状态演示
的信息部分。 development.xml
在您的 PX4 源代码中,其位置为 \src\modules\mavlink\mavlink\message_definitions\v1.0\development.xml
.
<;信息 本我="11514"; 名字="BATTERY_STATUS_DEMO";>;
<;描述简单的电池演示。描述>;
<;领域 类型="uint8_t"; 名字="id"; 实例="true";电池 ID</领域>;
<;领域 类型="int16_t"; 名字="温度"; 单位="cdegC"; 无效="INT16_MAX";>整个电池组(非内部电子元件)的温度。未提供 INT16_MAX 字段。领域>;
<;领域 类型="uint8_t"; 名字="剩余百分比"; 单位="%"; 无效="UINT8_MAX";>剩余电池电量。值:[0-100],UINT8_MAX:未提供字段。领域>;
</信息>;
信息
请注意,这是尚未实施的 battery_status_v2 随机选择的未使用的 11514
.在这里,我们将信息放在 development.xml
,这对于测试和如果该报文打算最终成为标准报文集的一部分是没有问题的,但您也可以将 自定义消息 在自己的方言文件中。
为 SITL 生成 PX4,并确认相关信息已在 /build/px4_sitl_default/mavlink/common/mavlink_msg_battery_status_demo.h
.
因为 电池状态
您不需要做任何事情来创建或构建它。
定义流媒体类
首先创建一个名为 BATTERY_STATUS_DEMO.hpp
内的流媒体类(以要流媒体的信息命名)。 /src/modules/mavlink/streams 目录。
在文件顶部添加 uORB 信息的标头(所需的 MAVLink 标头应已可用):
#include <uORB/topics/battery_status.h>;
信息
uORB 主题头文件是在构建时根据 CamelCase uORB 文件名生成的。
然后将下面的流媒体类定义复制到文件中:
类 MavlinkStream 电池状态演示 : 公 MavlinkStream
{
公众:
天电 MavlinkStream *新实例(Mavlink *多点连接)
{
返回 新 MavlinkStream 电池状态演示(Mavlink);
}
缢 烧焦 *获取名称() 缢
{
返回 MavlinkStream 电池状态演示::获取静态名称();
}
天电 缢 烧焦 *获取静态名称()
{
返回 "BATTERY_STATUS_DEMO";;
}
天电 uint16_t get_id_static()
{
返回 mavlink_msg_id_battery_status_demo;
}
uint16_t 获取()
{
返回 get_id_static();
}
无符号 get_size()
{
返回 mavlink_msg_id_battery_status_demo_len + mavlink_num_non_payload_bytes;
}
私人
//订阅 uORB 电池状态实例数组
uORB订阅多数组<;电池状态 电池状态::max_instances>; _battery_status_subs{ORB_ID电池状态};
由于电池有多个实例,因此需要 // SubscriptionMultiArray 订阅。
// uORB::Subscription 用于订阅单实例主题
/* 不允许在顶部复制该类 */
MavlinkStream 电池状态演示(MavlinkStream 电池状态演示 及样品;);
MavlinkStream 电池状态演示及样品; 操作员 = (缢 MavlinkStream 电池状态演示 及样品;);
受保护:
不含糊 MavlinkStream 电池状态演示(Mavlink *多点连接) : MavlinkStream(MAVLINK)
{}
bool 发送() 否决
{
bool 最新的 = 错误;
// 循环_battery_status_subs(订阅 BatteryStatus 实例数组)
对于 (汽车 及样品;battery_sub : _battery_status_subs) {
// battery_status_s 是一个结构体,用于保存电池对象主题
battery_status_s 电池状态;
// 更新电池状态,仅在状态发生变化时才发布
如果 (battery_sub.更新(及样品;battery_status)) {
// mavlink_battery_status_demo_t 是 MAVLink 消息对象
mavlink_battery_status_demo_t bat_msg{};
bat_msg.id = battery_status.id - 1;
bat_msg.battery_remaining = (battery_status.connected) ? 圆形(电池电量 * 100.f) : -1;
// 检查温度是否有效
如果 (电池电量 &&; PX4_ISFINITE(battery_status.temperature)) {
bat_msg.temperature = 电池状态.温度 * 100.f;
} 不然 {
bat_msg.temperature = INT16_MAX;
}
/发送信息
mavlink_msg_battery_status_demo_send_struct(_mavlink->;获取频道(), 及样品;bat_msg);
最新的 = 真;
}
}
返回 已更新;
}
};
大多数流媒体类都非常相似(见 /src/modules/mavlink/streams):
流媒体类派生自
MavlinkStream
并使用以下模式命名MavlinkStream<CamelCaseMessageName>;
.公
定义是"近似模板",允许 PX4 获取类的实例 (new_instance()
) ,然后用它从 MAVLink 头信息中获取信息的名称、ID 和大小 (get_name()
,get_name_static()
,get_id_static()
,get_id()
,get_size()
).对于您自己的流媒体类,只需复制并修改这些值,使其与 MAVLink 信息的值相匹配即可。私人
定义订阅需要发布的 uORB 主题。在这种情况下,uORB 主题有多个实例:每个电池一个。我们使用uORB::SubscriptionMultiArray
来获取电池状态订阅数组。在这里,我们还定义了构造函数,以防止定义被复制。
受保护的
重要的工作就在这里进行!在这里,我们覆盖
发送()
方法,将订阅的 uORB 主题中的值复制到 MAVLink 信息的适当字段中,然后发送信息。在这个例子中,我们有一个 uORB 实例数组
电池状态子系统
(因为我们有多个电池)。我们遍历数组并使用更新()
来检查相关的电池实例是否发生了变化(并使用当前数据更新结构)。这样我们就可以发送 MAVLink 消息 只是 如果相关的电池 uORB 主题已更改:cpp// 保存当前主题数据的结构。 battery_status_s 电池状态; // update() 填充 battery_status,如果状态发生变化,则返回 true 如果 (battery_sub.更新(及样品;battery_status)) { // 使用 battery_status 填充信息并发送 }
如果想发送 MAVLink 消息,无论数据是否发生变化,我们都可以使用
复制()
如图所示:cppbattery_status_s 电池状态; 电池子系统抄袭(及样品;电池状态);
信息
对于单实例主题,如 载具状态 我们会这样订阅:
cpp// 创建订阅 _vehicle_status_sub uORB::Subscription _vehicle_status_sub{ORB_ID(载具状态)};
我们还可以通过更新或复制以同样的方式使用由此产生的订阅。
cppvehicle_status_s vehicle_status{}; // vehicle_status_s 是 uORB 主题的定义 如果 (_vehicle_status_sub.更新(及样品;vehicle_status)) { // 使用已更新的载具状态。 }
接下来,我们在 mavlink_messages.cpp.将下面一行添加到文件中包含所有其他数据流的部分:
#include "streams/BATTERY_STATUS_DEMO.hpp";
最后将流类附加到 流列表
在 mavlink_messages.cpp
StreamListItem *流列表[] = {
...
#if 轮廓分明的(电池状态_演示_HPP)
创建流列表项目<;MavlinkStream 电池状态演示>;(),
#endif // battery_status_demo_hpp
...
}
该课程现在可以流式传输,但默认情况下不会流式传输。我们将在接下来的章节中介绍这一点。
默认流媒体
默认情况下(作为构建的一部分)流式传输信息的最简单方法是将它们添加到 mavlink_main.cpp 在相应的信息组中。
如果在文件中搜索,你会发现在开关语句中定义的信息组:
mavlink_mode_normal
:流向全球监控系统。mavlink_mode_onboard
:通过快速链接(如以太网)将数据流传输至配套计算机mavlink_mode_onboard_low_bandwidth
:流式传输到配套计算机,以便重新路由到流量较小的链路,如全球监控系统。万向节模式
:流向云台mavlink_mode_extvision
:流向外部视觉系统mavlink_mode_extvisionmin
:通过较慢的链路流向外部视觉系统mavlink_mode_osd
:流向 OSD,如 FPV 耳机。mavlink_mode_custom
:默认情况下无数据流。使用 MAVLink 配置流媒体时使用。mavlink_mode_magic
:相同mavlink_mode_custom
mavlink_mode_config
:通过 USB 传输流媒体,传输速率高于mavlink_mode_normal
.mavlink_mode_minimal
:流式传输一组最少的信息。通常用于较差的遥测链路。mavlink_mode_iridium
:流媒体传输至铱卫星电话
通常情况下,您会在 GCS 上进行测试,因此您只需将信息添加到 mavlink_mode_normal
使用 configure_stream_local()
方法。例如,将 CA_TRAJECTORY 以 5 Hz 的速度串流:
个案 mavlink_mode_config: // USB
// 注意:需要低延迟的数据流优先
...
配置本地流("BATTERY_STATUS_DEMO";, 5.0f);
...
也可以通过调用 多点连接 模块与 溪流
参数中的 启动脚本.例如,您可以将以下一行添加到 /ROMFS/px4fmu_common/init.d-posix/px4-rc.mavlink 为了流 电池状态演示
在 UDP 端口 14556
(-r
配置流传输速率和 -u
确定 UDP 端口 14556 上的 MAVLink 通道)。
多点连接 溪流 -r 50 -s 电池状态演示 -u 14556
按需流播
有些信息只有在特定硬件连接时或其他情况下才需要一次。为了避免不需要的信息堵塞通信链路,默认情况下,即使是在低速率下,也不能对所有信息进行流式处理。
如果需要,GCS 或其他 MAVLink API 可通过以下方式请求以特定速率流式传输特定信息 mav_cmd_set_message_interval.使用 mav_cmd_request_message.
接收 MAVLink 信息
本节介绍如何通过 MAVLink 接收信息并将其发布到 uORB。
它假定我们正在接收 电池状态演示
消息,我们希望更新(现有的) 电池状态 uORB 信息 包含的信息。这就是为支持 MAVLink 电池与 PX4 集成而提供的实现方式。
中添加要发布到的 uORB 主题的标题。 mavlink_receiver.h:
#include <uORB/topics/battery_status.h>;
为处理传入 MAVLink 消息的函数添加函数签名。 MavlinkReceiver
类中 mavlink_receiver.h
空白 处理电池状态演示信息(mavlink_message_t *信息);
通常情况下,您会为 uORB 主题添加一个 uORB 发布器,以便在 MavlinkReceiver
类中 mavlink_receiver.h.在这种情况下 电池状态 uORB 主题已经存在:
uORB出版物<;电池状态>; _battery_pub{ORB_ID(battery_status)} ;
这将为单个 uORB 主题实例创建一个出版物,默认情况下该实例将是 第一次 实例
信息
这种实现方式不适用于多电池系统,因为可能会有多个电池向主题的第一个实例发布数据,而且没有办法区分它们。为了支持多电池,我们需要使用 多种出版物
并将 MAVLink 消息实例 ID 映射到特定的 uORB 主题实例。
实施 处理电池状态演示信息
在 mavlink_receiver.cpp.
空白
MavlinkReceiver::处理电池状态演示信息(mavlink_message_t *msg)
{
如果 ((msg->sysid != mavlink_system.sysid) || (msg->compid == mavlink_system.compid)) {
// 忽略来自其他系统或自动驾驶仪本身的电池状态信息
返回;
}
// 外部电池测量
mavlink_battery_status_t 电池_mavlink;
mavlink_msg_battery_status_decode(msg、 及样品;battery_mavlink);
battery_status_s battery_status{};
电池状态时间戳 = hrt_absolute_time();
battery_status.remaining = (浮动)battery_mavlink.battery_remaining / 100.0f;
电池状态.温度 = (浮动)battery_mavlink.temperature;
battery_status.connected = 真;
_battery_pub.发布(电池状态);
}
信息
以上我们只写入主题中定义的电池字段。实际上,您需要用有效或无效值更新所有字段:为简洁起见,我们减少了这部分内容。
最后确保在 MavlinkReceiver::handle_message()
MavlinkReceiver::处理信息(mavlink_message_t *msg)
{
开关 (msg->msgid) {
...
个案 mavlink_msg_id_battery_status_demo:
处理电池状态演示信息(毫秒);
断裂;
...
}
}
创建自定义 MAVLink 信息的替代方案
有时需要定制 MAVLink 信息,其内容尚未完全定义。
例如,当使用 MAVLink 连接 PX4 和嵌入式设备时,自动驾驶仪和设备之间交换的信息可能会经过多次反复,才能稳定下来。在这种情况下,重新生成 MAVLink 标头并确保两台设备使用相同版本的协议既耗时又容易出错。
另一个临时解决方案是重新利用调试信息。而不是创建一个自定义的 MAVLink 消息 CA_TRAJECTORY
您可以发送信息 DEBUG_VECT
用字符串键 CA_TRAJ
中的数据 x
, y
和 z
领域。参见 本教程 调试信息的使用示例。
信息
该解决方案通过网络发送字符串并涉及字符串比较,因此效率不高。它只能用于开发!
测试
作为第一步,在调试过程中,您通常只想确认您创建的任何信息是否按照您的预期发送/接收。
您应该首先使用 uorb top [<信息名称>]
命令来实时验证信息是否已发布以及发布率(请参阅 uORB 消息传送).这种方法也可用于测试发布 uORB 主题的传入信息(对于其他信息,可使用 printf
并在 SITL 中进行测试)。
您可以使用多种方法查看 MAVLink 流量:
创建一个 Wireshark MAVLink 插件 为您的方言。这样,您就可以检查 IP 接口上的 MAVLink 流量,例如在 QGroundControl 或 MAVSDK 以及 PX4 的真实或模拟版本。
TIP
生成 wireshark 插件并在 Wireshark 中检查流量,比用方言重建 QGroundControl 并使用 MAVLink Inspector 要容易得多。
记录 uORB 主题 与您的 MAVLink 信息相关联。
在 QGroundControl 中查看收到的信息 MAVLink 检查员.您需要使用自定义信息定义重建 QGroundControl、 如下所述
使用外壳设置流速率
在测试中,有时需要在运行时提高单个主题的流速率(例如在 QGC 中进行检查)。这可以通过调用 多点连接 模块通过 QGC MAVLink 控制台 (或其他外壳):
多点连接 溪流 -u <;港 号码r>; -s <;多点连接 主题 命名e>; -r <;大鼠e>;
您可以使用 mavlink 状态
将输出(其中包括) 传输协议:UDP(<端口号>)。
.例如
多点连接 溪流 -u 14556 -s CA_TRAJECTORY -r 300
更新地面站
最终,您需要通过提供相应的地面站或 MAVSDK 实现来使用新的 MAVLink 接口。
这里需要记住的重要一点是,MAVLink 要求您使用根据相同定义(XML 文件)构建的库版本。因此,如果您在 PX4 中创建了自定义信息,除非您使用相同的定义构建 QGC 或 MAVSDK,否则将无法使用该信息。
更新 QGroundControl
您需要 构建 QGroundControl 包括一个预建的 C 库,其中包含您自定义的信息。
QGC 使用一个预建的 C 库,该库必须位于 /qgroundcontrol/libs/mavlink/include/mavlink 在 QGC 源中。
默认情况下,它作为子模块被预先包含在 https://github.com/mavlink/c_library_v2 但你可以 生成自己的 MAVLink 库.
QGC 默认使用 all.xml 方言,其中包括 common.xml.您可以在任一文件或自己的方言中加入信息。但是,如果您使用自己的方言,那么它应该包括 ArduPilotMega.xml(否则它将错过所有现有信息),而且您需要在 MAVLINK_CONF
运行时 qmake.
更新 MAVSDK
请参阅 MAVSDK 文档,了解如何使用 MAVLink 标头和方言.