4. MAVLink通信模式:从点对点到心跳机制

通信模式这东西,说白了就是无人机和地面站之间「怎么说话、什么时候说、说错了怎么办」。我做了这么多年嵌入式,见过太多团队把MAVLink当串口随便用,结果飞着飞着丢包、掉线,最后炸机才回头补课。这一章,咱们把五种核心模式掰开揉碎讲清楚。

核心观点: MAVLink的通信模式不是选择题,而是组合题。一个成熟的无人机系统,往往同时使用2-3种模式。

4.1 点对点通信:最基础的对话方式

点对点,就是两个节点之间直接通信。在MAVLink里,每个消息都带着system_id和component_id,接收方一看就知道「这消息是发给我的吗?」。

举个例子,飞控(system_id=1, component_id=1)给GPS模块(system_id=1, component_id=5)发配置指令,这就是典型的点对点。

// 点对点发送示例
mavlink_message_t msg;
uint8_t buf[MAVLINK_MAX_PACKET_LEN];

// 目标:system_id=1, component_id=5
mavlink_msg_command_long_pack(
    1,      // 发送方system_id
    1,      // 发送方component_id
    &msg,
    1,      // 目标system_id
    5,      // 目标component_id
    MAV_CMD_SET_MESSAGE_INTERVAL,
    0, 0, 0, 0, 0, 0, 0
);

uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
// 然后通过串口/UDP发送buf
我的习惯: 在系统设计阶段,我会先画一张「谁跟谁说话」的矩阵图。点对点模式最适合控制类消息——比如解锁、切换模式、参数写入。这些消息一旦发错对象,后果很严重。

4.2 广播通信:一呼百应,但别滥用

广播模式,就是发送方不指定目标,所有在同一个网络里的节点都能收到。MAVLink里,把target_system和target_component设为0,就是广播。

什么时候用广播?我个人经验是:

  • 系统发现阶段:新节点上线,喊一嗓子「我来了」
  • 状态广播:飞控的GPS位置、姿态信息,让所有节点都知道
  • 紧急告警:比如「电池即将耗尽,所有人注意」
避坑指南: 我曾经在一个项目中,把参数写入也用了广播。结果地面站、数传、甚至另一个飞控都收到了,导致参数冲突。广播只适合「只读」或「通知」类消息,千万别用来发控制指令。

4.3 请求-响应模式:一问一答,精准获取

这个模式很直观:A问B要数据,B回复。MAVLink里最常见的例子就是MAV_CMD_REQUEST_MESSAGE和MAV_CMD_REQUEST_DATA_STREAM。

我建议把请求-响应模式用在「按需获取」的场景:

  1. 参数读取:地面站请求某个参数,飞控回复
  2. 航点上传:地面站发一个航点,飞控确认收到
  3. 版本查询:获取飞控固件版本、协议版本
// 请求参数PARAM_ID的典型流程
// 步骤1:地面站发送PARAM_REQUEST_READ
mavlink_msg_param_request_read_pack(
    gcs_sysid, gcs_compid, &msg,
    target_sysid, target_compid,
    param_id,  // 参数名称字符串
    -1         // 索引,-1表示用名称匹配
);

// 步骤2:飞控收到后,回复PARAM_VALUE
// 步骤3:地面站解析PARAM_VALUE,得到参数值
关键点: 请求-响应模式一定要加超时重传。我一般设500ms超时,重试3次。如果3次都失败,就报错给用户,而不是无限等下去。

4.4 心跳机制与系统发现

心跳(HEARTBEAT)是MAVLink里最重要的消息,没有之一。它每隔一定时间(通常是1Hz)发送一次,告诉全网「我还活着」。

心跳消息里包含了:

字段 含义 典型值
type 系统类型 MAV_TYPE_QUADROTOR (2)
autopilot 飞控类型 MAV_AUTOPILOT_PX4 (12)
base_mode 系统状态 0x81 (已上锁、已就绪)
custom_mode 自定义模式 0=手动, 4=自动
system_status 系统状态 MAV_STATE_STANDBY (3)

系统发现的过程是这样的:

  1. 新节点上线,开始发送HEARTBEAT
  2. 地面站收到HEARTBEAT,发现system_id不认识
  3. 地面站发送SYSTEM_TIME或AUTOPILOT_VERSION请求,确认节点能力
  4. 节点回复,地面站将其加入系统列表
  5. 之后地面站持续监控心跳超时(通常3-5秒没收到就判定离线)
我的经验: 心跳间隔不要死板地用1Hz。在数传链路质量差的时候,我会动态提高到0.5Hz,减少带宽占用;在链路好的时候,可以提高到2Hz,让地面站更快感知状态变化。

4.5 消息流控与丢包处理

这是实战中最容易出问题的地方。MAVLink本身没有内置流控和重传机制,全靠应用层自己实现。

流控策略,我常用的有三种:

  • 速率限制:每个消息类型设置最大发送频率。比如GPS位置消息最多10Hz,姿态消息最多50Hz
  • 优先级队列:心跳、紧急告警放高优先级队列;日志、调试信息放低优先级队列
  • 背压机制:当发送缓冲区快满时,自动降低非关键消息的发送频率

丢包处理,我的做法是:

  1. 序列号检测:每个消息都有seq字段,接收方检查是否连续
  2. 选择性重传:只重传丢失的关键消息(如控制指令、参数写入)
  3. 状态同步:对于非关键消息(如位置、姿态),不重传,等下一帧即可
// 丢包检测的简化实现
static uint16_t last_seq = 0;

void on_message_received(mavlink_message_t *msg) {
    uint16_t current_seq = msg->seq;
    
    if (current_seq != (last_seq + 1) && last_seq != 0) {
        uint16_t lost = current_seq - last_seq - 1;
        printf("检测到丢包: %d 个包丢失\n", lost);
        
        // 如果是关键消息,发起重传请求
        if (is_critical_message(msg->msgid)) {
            request_retransmit(msg->msgid, last_seq + 1);
        }
    }
    
    last_seq = current_seq;
}
我曾经踩过的坑: 在一个测绘无人机项目里,我把所有消息都做了重传。结果链路一差,重传风暴直接把带宽占满,心跳都发不出去,地面站判定飞控离线,触发了紧急降落。后来改成「关键消息重传,非关键消息跳过」,问题解决。

4.6 五种模式的协同工作

实际系统中,这五种模式是同时存在的。我画了一张图,帮你理清它们的关系:

MAVLink通信模式协同工作图 飞控 (FCU) 地面站 (GCS) GPS模块 其他节点 点对点(控制指令) 请求-响应(参数) 点对点(配置) 广播(位置数据) 心跳(1Hz) 请求-响应(发现) 广播(状态) 实线:持续通信(心跳、广播) | 虚线:按需通信(请求-响应、点对点控制) 流控作用于所有链路,丢包处理只针对关键消息

你看这张图,飞控同时跟地面站、GPS模块、其他节点通信。每种通信模式用在不同的场景:

  • 心跳:飞控向全网广播「我活着」,所有节点都能收到
  • 点对点:飞控给GPS发配置指令,只发给GPS
  • 广播:GPS把位置数据广播出去,谁需要谁收
  • 请求-响应:地面站问飞控要参数,一问一答
  • 流控与丢包:所有链路上都在运行,保证系统稳定
总结一下: 这五种模式不是孤立的。心跳是系统的「生命线」,点对点和请求-响应是「精准对话」,广播是「信息分发」,流控和丢包处理是「保底机制」。一个成熟的MAVLink系统,一定是这五种模式的有机组合。