4、MAVLink命令行与调试控制台:MAVLink Inspector使用、调试消息(NAMED_VALUE_FLOAT)、参数读写(PARAM_REQUEST_READ/PARAM_SET)
做无人机开发,最怕什么?
不是炸机,是炸了之后不知道为啥炸的。我见过太多人,飞控日志导出来一看,全是乱码,或者关键数据根本没记录。说白了,你连飞控在“想”什么都不知道,怎么调参?怎么修Bug?
这一章,我们就来解决这个问题。我会带你用好MAVLink Inspector这个“听诊器”,再教你如何用NAMED_VALUE_FLOAT塞自定义调试数据,最后把参数读写玩明白。嗯,这三板斧砍下去,你基本就能跟飞控“对话”了。
4.1 MAVLink Inspector:飞控的“黑匣子”实时解码器
MAVLink Inspector 是 QGroundControl 里一个被很多人忽略的神器。它不像主界面那样显示地图和姿态,而是把飞控发出的每一条 MAVLink 消息都列出来,实时刷新。
我个人习惯,在调参或者试飞的时候,一定会把 Inspector 窗口打开,放在副屏上。为什么?因为很多问题,在姿态仪表盘上看不出来,但在消息流里一目了然。
4.1.1 如何打开 Inspector
- 打开 QGroundControl,连接飞控(USB 或数传)。
- 点击左上角“应用设置”图标(齿轮)。
- 在左侧菜单选择“MAVLink Inspector”。
- 你会看到一个实时滚动的消息列表,按消息 ID 分组。
SCALED_IMU2 里能看到第二套 IMU 的原始数据。我曾经靠这个发现一颗 IMU 的 Z 轴加速度计漂移严重,提前换掉了传感器,避免了一次空中险情。
4.1.2 常用调试消息一览
Inspector 里消息很多,但别慌。你主要关注这几类:
| 消息类型 | 消息 ID | 用途 |
|---|---|---|
| 系统状态 | HEARTBEAT, SYS_STATUS |
飞控是否 alive、电池电压、错误计数 |
| 姿态与位置 | ATTITUDE, GLOBAL_POSITION_INT |
欧拉角、经纬高、地速 |
| 传感器原始值 | SCALED_IMU, RAW_IMU |
加速度、角速度、磁场 |
| 自定义调试 | NAMED_VALUE_FLOAT |
开发者自己塞进去的变量(重点!) |
| 参数相关 | PARAM_VALUE, PARAM_REQUEST_READ |
参数读写响应 |
4.2 NAMED_VALUE_FLOAT:往飞控里“塞”自定义调试数据
这是 ArduPilot 给开发者留的一个“后门”。你可以在飞控固件代码里,用 gcs().send_named_float() 把任意一个 float 变量发出来,名字自己起。然后 Inspector 里就能看到。
说白了,这就是一个自定义的调试通道。我当年做自定义避障算法时,全靠这个把内部的距离值和置信度实时发到地面站看。
4.2.1 在 ArduPilot 固件中发送
假设你写了一个新的避障逻辑,想看看 obstacle_distance 这个变量到底算得对不对。在代码里加一行:
// 在 Copter 或 Rover 的某个循环中
gcs().send_named_float("ObsDist", obstacle_distance);
gcs().send_named_float("ConfLvl", confidence_level);
编译烧录后,打开 Inspector,找到 NAMED_VALUE_FLOAT 消息,展开就能看到:
- name: "ObsDist"
- value: 1.57 (单位米)
- name: "ConfLvl"
- value: 0.92
4.2.2 在 Mission Planner 里查看
除了 QGC 的 Inspector,Mission Planner 的“飞行数据”界面也有一个“MAVLink Inspector”标签页,效果一样。我个人更喜欢在 Mission Planner 里看,因为它支持把 NAMED_VALUE_FLOAT 的数据直接画成曲线图。
操作步骤:
- 连接飞控后,点击“飞行数据”。
- 在右侧图表区域右键,选择“添加曲线”。
- 在“MAVLink 消息”下拉框里选
NAMED_VALUE_FLOAT。 - 在“字段”里选你定义的名字,比如
ObsDist。
这样你就能实时看到数据波形了。嗯,比看数字直观多了。
4.3 参数读写:PARAM_REQUEST_READ 与 PARAM_SET
调参是无人机开发的日常。你总不能每次改个 PID 都重新编译固件吧?MAVLink 提供了标准的参数读写协议,地面站和飞控通过 PARAM_REQUEST_READ、PARAM_SET 和 PARAM_VALUE 这三条消息完成交互。
4.3.1 参数读取流程
地面站想读一个参数,比如 RTL_ALT(返航高度):
- 地面站发送
PARAM_REQUEST_READ,里面填上参数 ID 字符串 "RTL_ALT"。 - 飞控收到后,查找参数表,找到对应的值。
- 飞控回复一条
PARAM_VALUE消息,里面包含参数 ID、值、参数类型(比如 MAV_PARAM_TYPE_REAL32)。
你想想看,这个过程其实就是一个简单的“请求-响应”模式。但要注意,参数 ID 是大小写敏感的,而且必须完全匹配。
4.3.2 参数写入流程
想改参数?用 PARAM_SET:
- 地面站发送
PARAM_SET,带上参数 ID 和新的值。 - 飞控收到后,校验参数是否可写(有些参数是只读的),然后写入存储。
- 飞控回复一条
PARAM_VALUE作为确认,里面是写入后的值。
PARAM_VALUE 里的值是否和你设置的一致。我曾经遇到过串口干扰,地面站发了 PARAM_SET 但飞控没收到,结果参数没改成功,飞起来还是旧参数,差点出事。所以,读写校验是必须的。
4.3.3 批量读取:PARAM_REQUEST_LIST
如果你需要读取飞控的所有参数(比如做参数备份),可以用 PARAM_REQUEST_LIST。飞控收到后会遍历整个参数表,逐条发送 PARAM_VALUE。参数多的时候(ArduPilot 有上千个参数),这个过程可能会持续几十秒。
我个人习惯,在 Mission Planner 里做一次“全部参数读取”,然后导出为 .param 文件。改乱了直接一键恢复,省心。
4.3.4 参数类型对照表
MAVLink 里参数类型用枚举 MAV_PARAM_TYPE 表示,常见的有:
| 枚举值 | 类型 | C 语言对应 | 说明 |
|---|---|---|---|
| 1 | MAV_PARAM_TYPE_UINT8 | uint8_t | 0~255 |
| 2 | MAV_PARAM_TYPE_INT8 | int8_t | -128~127 |
| 3 | MAV_PARAM_TYPE_UINT16 | uint16_t | 0~65535 |
| 4 | MAV_PARAM_TYPE_INT16 | int16_t | -32768~32767 |
| 5 | MAV_PARAM_TYPE_UINT32 | uint32_t | 0~4294967295 |
| 6 | MAV_PARAM_TYPE_INT32 | int32_t | 带符号 32 位 |
| 7 | MAV_PARAM_TYPE_REAL32 | float | 单精度浮点(最常用) |
| 9 | MAV_PARAM_TYPE_REAL64 | double | 双精度浮点(少用) |
4.4 实战:用 Python 脚本读写参数
光说不练假把式。下面我用 pymavlink 写一个简单的脚本,演示如何读取和设置 RTL_ALT 参数。
#!/usr/bin/env python3
"""
演示:通过 MAVLink 读取和设置参数 RTL_ALT
"""
from pymavlink import mavutil
import time
# 连接飞控(假设串口是 /dev/ttyUSB0,波特率 115200)
master = mavutil.mavlink_connection('/dev/ttyUSB0', baud=115200)
master.wait_heartbeat()
print("飞控已连接")
# ---- 读取参数 ----
master.mav.param_request_read_send(
master.target_system,
master.target_component,
b'RTL_ALT', # 参数 ID,必须是 bytes
-1 # 索引,-1 表示用 ID 匹配
)
# 等待飞控回复 PARAM_VALUE
msg = master.recv_match(type='PARAM_VALUE', blocking=True, timeout=5)
if msg:
print(f"参数 {msg.param_id} 的值为: {msg.param_value} (单位: 厘米)")
else:
print("读取超时")
# ---- 设置参数 ----
new_value = 1500.0 # 设为 15 米(单位厘米)
master.mav.param_set_send(
master.target_system,
master.target_component,
b'RTL_ALT',
new_value,
mavutil.mavlink.MAV_PARAM_TYPE_REAL32
)
# 等待飞控确认
msg = master.recv_match(type='PARAM_VALUE', blocking=True, timeout=5)
if msg:
print(f"参数 {msg.param_id} 已设置为: {msg.param_value} (单位: 厘米)")
else:
print("写入超时,请检查连接")
master.close()
param_request_read_send 要求参数 ID 是 bytes 类型,长度不足 16 字节会自动补 \0。另外,RTL_ALT 的单位是厘米,不是米!设成 1500 就是 15 米,别搞错了。
4.5 本章小结
这一章我们干了三件事:
- 学会了用 MAVLink Inspector 实时监控飞控消息,这是调试的“眼睛”。
- 掌握了
NAMED_VALUE_FLOAT的用法,可以往飞控里塞自定义调试数据,这是调试的“手”。 - 搞懂了参数读写的底层协议,能通过脚本自动化调参,这是调试的“工具”。