关于 matlab-connect-mavlink
帮助 MATLAB 用户通过 UDP 将 MATLAB 连接到 PX4 或 ArduPilot 飞控的技能,面向地面控制站和 SITL 场景。它规定了 MAVLink 方言、心跳发送与客户端发现的顺序,以避免连接流程中的常见错误。
主要功能
- 01建立 MAVLink UDP 通信
- 02配置 MAVLink 方言与接口
- 03发送地面站心跳
- 04发现并连接飞控客户端
- 05订阅 MAVLink 消息
适用场景
- 01连接 MATLAB 与 PX4 SITL
- 02将 MATLAB 配置为地面控制站
- 03监控已发现飞控的消息主题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ matlab/ matlab-agentic-toolkit --skill matlab-connect-mavlink安装后在 Claude Code 中输入/matlab-connect-mavlink确认已加载。
附带文件manifest.yaml
详细介绍
MAVLink 连接设置
在 MATLAB 与 PX4 或 ArduPilot 飞控之间建立 MAVLink UDP 连接,并让 MATLAB 充当地面控制站(GCS)。此技能编码了正确的协议顺序和心跳模式,而 Agent 经常会弄错这些内容。
适用场景
- 用户想通过 UDP 将 MATLAB 连接到 PX4 或 ArduPilot 自动驾驶仪
- 用户正在 MATLAB 中构建地面控制站
- 用户需要与 SITL 或联网的自动驾驶仪建立 MAVLink 通信
- 用户询问心跳交换或客户端发现
- 用户提到
mavlinkio、mavlinkdialect或sendudpmsg
不适用场景
- 用户想要通过已建立的连接读取/设置参数、上传任务或下载日志——无需此技能也能很好地完成这些操作
- 用户正在使用 Simulink MAVLink 模块(工作流不同)
- 用户只想离线解析
.ulg日志文件——直接使用ulogreader - 用户只询问 MAVLink 消息的序列化/反序列化
工作流
严格按照以下顺序操作。步骤顺序很重要——跳过或调整顺序会导致静默故障。
1. 创建方言
dialect = mavlinkdialect("common.xml", 2);
PX4 和 ArduPilot 均使用 "common.xml"(涵盖所有标准消息)。
只有在需要 ArduPilot 专属扩展消息时,才使用 "ardupilotmega.xml"。
2 指定 MAVLink 协议版本 2。
可用方言:common.xml、ardupilotmega.xml、standard.xml、minimal.xml
2. 创建 MAVLink IO 接口
mavlink = mavlinkio(dialect, 'SystemID', 255, 'ComponentID', 1);
- SystemID 255、ComponentID 1 是标准 GCS 标识
- 不要创建
mavlinksub(mavlink, "HEARTBEAT")来验证连接。mavlinkio内置了心跳订阅器,并会将心跳提供给listClients()。 手动创建 HEARTBEAT 订阅器既多余又浪费资源。
3. 连接 UDP 传输
connect(mavlink, "UDP", LocalPort=14550);
所有传输选项都必须使用名称-值对。切勿使用位置参数。
名称-值选项:LocalPort(默认值为 0)、ConnectionName(默认值为 "Connection#")
4. 构建 GCS 心跳消息
createmsg 的签名:createmsg(dialect, msgName)——方言对象始终是第一个参数,消息名称字符串是第二个参数。切勿将 mavlinkio 对象传给 createmsg。
hbMsg = createmsg(dialect, "HEARTBEAT");
hbMsg.Payload.type(:) = dialect.enum2num("MAV_TYPE", "MAV_TYPE_GCS");
hbMsg.Payload.autopilot(:) = dialect.enum2num("MAV_AUTOPILOT", "MAV_AUTOPILOT_INVALID");
hbMsg.Payload.base_mode(:) = 0;
hbMsg.Payload.custom_mode(:) = 0;
hbMsg.Payload.system_status(:) = 0; % GCS has no vehicle state
关键:始终通过 msg.Payload.fieldname(:) 访问字段——绝不要使用 msg.fieldname(:)。消息结构体包含一个 .Payload 子结构体,其中包含所有协议字段。写入 hbMsg.type(:) = ... 会失败,因为 type 不是顶层字段,而是位于 hbMsg.Payload.type 中。
关键:为负载字段赋值时,始终使用 (:) 索引。 写入 msg.Payload.type = 6(不带 (:))会静默地将线上的类型(uint8)替换为 double,从而导致 MAVLink 数据包损坏。(:) 可以保留原始数据类型。
5. 启动周期性心跳
根据自动驾驶仪是否已在广播,有两种工作流:
工作流 A:自动发现(自动驾驶仪已在广播心跳)
如果已配置 PX4 SITL 广播 mavlink 消息,自动驾驶仪的心跳会自动到达。先轮询 listClients,然后向发现的客户端发送心跳:
% Wait for autopilot to appear
timeout = 10;
tic;
discovered = false;
while toc < timeout
clients = listClients(mavlink);
if height(clients) > 1
discovered = true;
break;
end
pause(0.5);
end
if discovered
% Use SystemID/ComponentID from listClients output
remoteClient = clients(clients.SystemID ~= 255, :); % exclude local GCS
autopilot = mavlinkclient(mavlink, remoteClient.SystemID, remoteClient.ComponentID);
hbTimer = timer('ExecutionMode', 'fixedRate', 'Period', 1, ...
'TimerFcn', @(~,~) sendmsg(mavlink, hbMsg, autopilot));
start(hbTimer);
end
工作流 B:手动发起(自动驾驶仪尚未广播)
如果自动驾驶仪需要先收到 GCS 心跳才会响应,请使用 sendudpmsg 并指定自动驾驶仪的监听端口。对于 PX4 SITL,可在构建日志中查找:
[mavlink] ... on udp port <SITL_PORT> remote port 14550。如果不知道该端口,请向用户询问。
sitlHost = "172.x.x.x"; % IP of SITL instance (use "ip a" in WSL to find it)
sitlPort = 18570; % PX4 SITL listening port (from SITL build log "udp port" line)
hbTimer = timer('ExecutionMode', 'fixedRate', 'Period', 1, ...
'TimerFcn', @(~,~) sendudpmsg(mavlink, hbMsg, sitlHost, sitlPort));
start(hbTimer);
sendudpmsg(io, msg, host, port)会将消息发送到特定 UDP 端点——当客户端尚未发现时使用sendmsg(io, msg, client)会将消息发送到已发现的客户端——在listClients显示自动驾驶仪后使用- MAVLink 标准心跳频率为 1 Hz
- 不要将 14550 用作远程端口——这是 GCS 本地端口。自动驾驶仪的监听端口不同(例如,PX4 SITL 的
-u端口)。
6. 验证客户端发现(仅限工作流 B)
通过 sendudpmsg 启动心跳后,轮询,直到自动驾驶仪响应:
timeout = 10;
tic;
discovered = false;
while toc < timeout
clients = listClients(mavlink);
if height(clients) > 1 % local GCS client is always listed
discovered = true;
break;
end
pause(0.5);
end
if discovered
disp(clients);
else
error("Autopilot not discovered within %d seconds.", timeout);
end
listClients(mavlink) 使用内置心跳订阅器,无需手动使用
mavlinksub。对于工作流 A,发现已在步骤 5 中完成。
7. 完成后清理
stop(hbTimer);
delete(hbTimer);
disconnect(mavlink);
断开连接前,务必停止并删除定时器,以防留下孤立的定时器。
关键函数
| 函数 | 签名 | 用途 |
|---|---|---|
mavlinkdialect | (xmlFile, version) | 解析方言 XML,创建消息定义 |
mavlinkio | (dialect, 'SystemID', N, 'ComponentID', N) | 创建 I/O 接口 |
connect | (io, "UDP", LocalPort=N) | 打开 UDP 传输 |
mavlinkclient | (io, systemID, componentID) | 为远程系统创建客户端句柄 |
createmsg | (dialect, msgType) | 创建消息结构体 — 第一个参数是 dialect,不是 io |
sendudpmsg | (io, msg, remoteHost, remotePort) | 向指定的 UDP 端点发送消息 |
sendmsg | (io, msg) 或 (io, msg, client) | 向所有客户端或已发现的客户端发送消息 |
listClients | (io) | 列出所有已发现的客户端(使用内置订阅器) |
listTopics | (io) | 列出所有已接收的消息主题 |
listConnections | (io) | 列出活动传输连接 |
mavlinksub | (io, topic) 或 (io, client, topic) | 订阅消息 |
latestmsgs | (subscriber, count) | 从订阅器读取最新消息 |
disconnect | (io) | 关闭所有连接 |
模式
连接到 PX4 SITL 的 UDP(自动发现)
将 PX4 SITL 配置为向 GCS 端口 14550 广播(检查构建日志中的
remote port 14550)。自动驾驶仪会自动出现在 listClients 中。
% Setup
dialect = mavlinkdialect("common.xml", 2);
mavlink = mavlinkio(dialect, 'SystemID', 255, 'ComponentID', 1);
connect(mavlink, "UDP", LocalPort=14550);
% Build GCS heartbeat
hbMsg = createmsg(dialect, "HEARTBEAT");
hbMsg.Payload.type(:) = dialect.enum2num("MAV_TYPE", "MAV_TYPE_GCS");
hbMsg.Payload.autopilot(:) = dialect.enum2num("MAV_AUTOPILOT", "MAV_AUTOPILOT_INVALID");
hbMsg.Payload.base_mode(:) = 0;
hbMsg.Payload.custom_mode(:) = 0;
hbMsg.Payload.system_status(:) = 0;
% Wait for autopilot to be discovered
timeout = 10;
tic;
while toc < timeout
clients = listClients(mavlink);
if height(clients) > 1
break;
end
pause(0.5);
end
disp(clients);
% Start GCS heartbeat back to the discovered autopilot
remoteClient = clients(clients.SystemID ~= 255, :);
autopilot = mavlinkclient(mavlink, remoteClient.SystemID, remoteClient.ComponentID);
hbTimer = timer('ExecutionMode', 'fixedRate', 'Period', 1, ...
'TimerFcn', @(~,~) sendmsg(mavlink, hbMsg, autopilot));
start(hbTimer);
% ... perform operations ...
% Clean up
stop(hbTimer);
delete(hbTimer);
disconnect(mavlink);
订阅特定消息
当你需要监控特定消息类型时(超出连接验证范围):
% Subscribe to all messages of a type
sub = mavlinksub(mavlink, "GLOBAL_POSITION_INT");
% Subscribe to messages from a specific discovered client
remoteClient = clients(clients.SystemID ~= 255, :);
autopilot = mavlinkclient(mavlink, remoteClient.SystemID, remoteClient.ComponentID);
sub = mavlinksub(mavlink, autopilot, "ATTITUDE");
% Read the latest message(s)
msgs = latestmsgs(sub, 1);
if ~isempty(msgs)
disp(msgs.Payload);
end
发现后发送命令
通过 listClients 发现客户端后,即可使用 sendmsg 和该客户端发送命令:
% After discovery, send to specific client
clients = listClients(mavlink);
if height(clients) > 1
remoteClient = clients(clients.SystemID ~= 255, :);
autopilot = mavlinkclient(mavlink, remoteClient.SystemID, remoteClient.ComponentID);
% Create command message
cmdMsg = createmsg(dialect, "COMMAND_LONG");
cmdMsg.Payload.target_system(:) = remoteClient.SystemID;
cmdMsg.Payload.target_component(:) = remoteClient.ComponentID;
cmdMsg.Payload.command(:) = 400; % MAV_CMD_COMPONENT_ARM_DISARM
cmdMsg.Payload.param1(:) = 1; % arm
sendmsg(mavlink, cmdMsg, autopilot);
end
带错误处理的心跳定时器
对于健壮的应用程序,可封装定时器回调,防止静默失败:
% Using sendudpmsg (pre-discovery, to known SITL endpoint)
hbTimer = timer('ExecutionMode', 'fixedRate', 'Period', 1, ...
'ErrorFcn', @(~,evt) warning("Heartbeat error: %s", evt.Data.message), ...
'TimerFcn', @(~,~) sendudpmsg(mavlink, hbMsg, sitlHost, sitlPort));
start(hbTimer);
注意事项
- 端口 14550 是 GCS 的本地端口,不是自动驾驶仪的端口。 使用
sendudpmsg时,远程端口必须是自动驾驶仪的监听端口(PX4 SITL-u标志),而不是 14550。如果已通过listClients发现自动驾驶仪,请改用sendmsg(io, msg, client)——它会自动路由。 - 发现客户端之前发送消息,请使用
sendudpmsg,而非sendmsg。 如果尚未发现客户端就调用sendmsg(io, msg, client),会抛出错误。发送心跳和进行任何发现前通信时, 请使用sendudpmsg(io, msg, host, port)。 - 所有消息字段都位于
.Payload下。 应写成msg.Payload.type(:) = ..., 而不是msg.type(:) = ...。顶层消息结构体包含元数据; 协议字段始终位于msg.Payload.fieldname。 - 为负载字段赋值时,始终使用
(:)。msg.Payload.field(:) = value会保留线缆类型(uint8、uint16、int32 等)。如果省略(:),MATLAB 会将字段替换为 double,导致 MAVLink 数据包在线缆上传输时损坏。 此错误是静默的——既不会报错,也不会警告——只会在互操作期间显现。 createmsg(dialect, msgName)——dialect 是第一个参数。 不要将mavlinkio传给createmsg。io 对象负责发送消息;dialect 负责创建消息。connect()只使用名称-值对。 应写成connect(io, "UDP", LocalPort=14550), 而不是connect(io, "udpin", "0.0.0.0", 14550)。- 不要创建
mavlinksub(mavlink, "HEARTBEAT")来验证连接。mavlinkio已内置心跳订阅器,会填充listClients()。手动创建 HEARTBEAT 订阅器是多余的。只有在订阅非心跳消息类型时才使用mavlinksub(例如,"GLOBAL_POSITION_INT"、"ATTITUDE")。 listClients始终包含本地 GCS 客户端。 请检查height(clients) > 1以确认已发现远程系统,而不是检查> 0。mavlinkclient(io, sysID, compID)是构造函数,不是列表函数。 它会创建一个句柄,不会验证客户端是否存在。请使用listClients(io)检查已发现的客户端。- 订阅器的读取方法是
latestmsgs(sub, count)——不是read()、receive()或next()。 - 始终停止并删除计时器。 孤立的计时器在
disconnect后仍会继续运行, 可能导致 MATLAB 不稳定。请使用stop(t); delete(t),或将其包装在onCleanup中。
约定
- GCS 标识:SystemID 255,ComponentID 1
- GCS 本地端口(GCS 绑定的端口):14550
- PX4 SITL 监听端口:不固定(请查看 SITL 构建日志中的
udp port <N>;例如 18570) - ArduPilot SITL 默认发往 GCS 的出站端口:14550
- 自动驾驶仪的典型标识:SystemID 1,ComponentID 1
- 心跳频率:1 Hz(MAVLink 标准)
- 除非需要 ArduPilot 专属扩展,否则请使用
"common.xml"dialect
版权所有 2026 The MathWorks, Inc.