4. Python读取MAVLink日志:用pyMavlink打开、遍历、过滤与提取时间戳
上一章我们聊了日志文件长什么样,这一章直接上手——用Python把日志里的数据读出来。
说实话,我见过不少工程师,拿到日志第一件事就是拖进Excel里手动翻。几千几万条消息,翻到眼花。其实用pyMavlink几行代码就能搞定。我自己刚入行时也干过傻事,手动翻了一下午,结果发现看漏了一条关键的错误码……后来就老老实实写脚本了。
4.1 环境准备:装好pyMavlink
先确认你的Python环境里有没有pyMavlink。打开终端,跑一下:
pip install pymavlink
如果已经装过,建议升级到最新版:
pip install --upgrade pymavlink
4.2 打开日志文件:从文件到对象
pyMavlink支持多种日志格式,最常见的是.tlog(Telemetry日志)和.bin(二进制日志)。我们以.tlog为例。
核心代码就两行:
from pymavlink import mavutil
# 打开日志文件
mlog = mavutil.mavlink_connection('flight_log.tlog')
嗯,这里要注意:mavlink_connection这个函数名字有点误导,它不光能连串口、UDP,也能读本地文件。我第一次用的时候还以为只能连硬件,绕了一大圈。
打开成功后,mlog就是一个日志对象。你可以把它想象成一个“消息管道”,里面按时间顺序排着所有MAVLink消息。
4.3 遍历消息:一条一条看
最基础的操作就是遍历。用recv_msg()方法,每次取一条:
while True:
msg = mlog.recv_msg()
if msg is None:
break
# 处理消息
print(msg)
为什么用while True?因为日志读到末尾时,recv_msg()会返回None。这是pyMavlink的设计习惯,跟读取实时数据流保持一致。
我在项目里遇到过一个问题:日志文件很大,几百万条消息,直接遍历会卡很久。后来我加了个计数器,每10万条打印一次进度,心里踏实多了。
count = 0
while True:
msg = mlog.recv_msg()
if msg is None:
break
count += 1
if count % 100000 == 0:
print(f'已处理 {count} 条消息')
4.4 按类型过滤消息:只拿你想要的
日志里消息类型很多——GPS、IMU、姿态、电池、遥控器……全混在一起。你分析问题时通常只关心某几类。
过滤的核心是检查消息的msg_type或name属性:
while True:
msg = mlog.recv_msg()
if msg is None:
break
# 只处理GPS消息
if msg.get_type() == 'GPS_RAW_INT':
lat = msg.lat / 1e7
lon = msg.lon / 1e7
alt = msg.alt / 1e3
print(f'GPS: lat={lat}, lon={lon}, alt={alt}')
常用的消息类型有这些:
| 消息类型 | 含义 | 典型用途 |
|---|---|---|
GPS_RAW_INT |
GPS原始数据 | 定位精度、卫星颗数 |
ATTITUDE |
姿态信息 | 翻滚、俯仰、偏航 |
BATTERY_STATUS |
电池状态 | 电压、电流、剩余电量 |
RC_CHANNELS |
遥控器通道值 | 检查遥控信号是否正常 |
STATUSTEXT |
飞控文本消息 | 错误提示、状态信息 |
HEARTBEAT |
心跳包 | 判断飞控是否在线 |
你想想看,如果日志里有10种消息,你只关心GPS和姿态,过滤后数据量能减少80%以上。分析效率一下子就上来了。
4.5 提取时间戳:给数据打上时间标签
时间戳是分析问题的关键。没有时间,你根本不知道故障发生的先后顺序。
MAVLink消息里有两个时间概念:
- 系统时间(time_boot_ms):飞控启动后的毫秒数。大部分消息都带这个字段。
- 日志记录时间:地面站或记录设备收到消息时打的时间戳。存在
msg._timestamp里。
提取方式如下:
while True:
msg = mlog.recv_msg()
if msg is None:
break
# 系统时间(毫秒)
if hasattr(msg, 'time_boot_ms'):
sys_time_ms = msg.time_boot_ms
sys_time_s = sys_time_ms / 1000.0
# 日志记录时间(Unix时间戳,秒)
log_time = msg._timestamp
print(f'系统时间: {sys_time_s:.2f}s, 日志时间: {log_time}')
time_boot_ms做时间轴。因为它是飞控内部的时间,不受地面站时钟漂移影响。有一次我排查一个GPS丢星问题,用日志时间怎么都对不上,换成系统时间后,发现丢星正好发生在飞控重启后的第47秒——问题一下就定位了。
4.6 完整示例:把GPS和姿态数据导出为CSV
把上面的知识点串起来,写一个实用的脚本:
from pymavlink import mavutil
import csv
mlog = mavutil.mavlink_connection('flight_log.tlog')
with open('extracted_data.csv', 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['time_boot_ms', 'lat', 'lon', 'alt', 'roll', 'pitch', 'yaw'])
while True:
msg = mlog.recv_msg()
if msg is None:
break
msg_type = msg.get_type()
if msg_type == 'GPS_RAW_INT':
# 暂存GPS数据
gps_data = {
'time': msg.time_boot_ms,
'lat': msg.lat / 1e7,
'lon': msg.lon / 1e7,
'alt': msg.alt / 1e3
}
elif msg_type == 'ATTITUDE':
# 如果之前有GPS数据,合并写入
if gps_data and abs(msg.time_boot_ms - gps_data['time']) < 100:
writer.writerow([
msg.time_boot_ms,
gps_data['lat'],
gps_data['lon'],
gps_data['alt'],
msg.roll,
msg.pitch,
msg.yaw
])
这个脚本会把时间对齐的GPS和姿态数据写到CSV里。你拖进Excel就能画图分析了。
4.7 本章知识结构图
下面这张图帮你理清整个流程:
说白了,整个流程就是:打开 → 遍历 → 过滤 → 提取。每一步都不复杂,但组合起来就能高效处理海量日志数据。