4、Python日志解析库:使用 pymavlink 库读取 .bin 日志,安装与基础API使用
做无人机飞控这么多年,我处理过最多的数据格式就是 .bin 日志。说实话,刚开始接触 ArduPilot 的二进制日志时,我也挺头疼的——一堆二进制数据,用记事本打开全是乱码。后来发现 pymavlink 这个库,简直就是救星。
这一节,我们就来聊聊怎么用 pymavlink 读取 .bin 日志。我会把安装步骤、基础 API 调用,以及我踩过的坑都讲清楚。
4.1 为什么选择 pymavlink?
pymavlink 是 ArduPilot 官方推荐的 Python 库。它不仅能解析 MAVLink 协议,还能直接读取 .bin 日志文件。说白了,它就是飞控数据的一把万能钥匙。
我个人习惯用 pymavlink 做日志分析,原因有三:
- 原生支持:ArduPilot 的 .bin 日志格式,pymavlink 直接就能读
- 数据完整:不会丢失任何字段,包括一些冷门传感器数据
- 社区活跃:遇到问题,GitHub 上基本都能找到答案
4.2 安装 pymavlink
安装其实很简单,但有几个细节要注意。我刚开始装的时候,就因为在 Windows 上少了 C++ 编译环境,折腾了半天。
4.2.1 基础安装
# 最基础的安装方式
pip install pymavlink
# 如果你需要完整功能(包括图形界面工具)
pip install pymavlink[all]
4.2.2 常见安装问题
嗯,这里要注意。pymavlink 底层依赖一些 C 扩展,编译时容易出问题。我曾经在 Windows 上遇到过这样的报错:
error: Microsoft Visual C++ 14.0 is required...
解决办法其实很简单:
- Windows 用户:先安装 Visual Studio Build Tools,或者直接用 Anaconda 环境
- Linux 用户:先装好 gcc 和 python3-dev
- macOS 用户:确保 Xcode Command Line Tools 已安装
pip install --only-binary :all: pymavlink。
4.3 核心 API 使用
安装好之后,我们来看看怎么用。我会从最基础的读取日志开始,一步步带你上手。
4.3.1 打开日志文件
读取 .bin 日志,核心就一个函数:mavutil.mavlink_connection()。你想想看,这个函数名字起得挺直白——建立 MAVLink 连接。只不过这里的"连接"对象不是飞控,而是日志文件。
from pymavlink import mavutil
# 打开 .bin 日志文件
log_file = "flight_log.bin"
mlog = mavutil.mavlink_connection(log_file)
print(f"日志已打开: {log_file}")
print(f"日志格式版本: {mlog.logformat}")
4.3.2 遍历消息
打开日志后,怎么读取里面的数据?pymavlink 把每条数据都封装成消息对象。我们可以用 recv_msg() 一条一条地读:
# 遍历所有消息
msg_count = 0
while True:
msg = mlog.recv_msg()
if msg is None:
break
msg_count += 1
# 只打印前5条消息的类型
if msg_count <= 5:
print(f"消息类型: {msg.get_type()}, 时间戳: {msg._timestamp}")
print(f"总共读取了 {msg_count} 条消息")
这里有个小技巧:msg.get_type() 返回的是消息类型名称,比如 GPS、IMU、ATT 这些。我刚开始分析日志时,就是靠这个函数先摸清日志里有哪些数据。
4.3.3 按类型筛选消息
实际分析中,我们通常只关心特定类型的数据。比如炸机分析,最常看的就是 GPS 和 IMU 数据。pymavlink 提供了 recv_match() 方法,可以按类型筛选:
# 只读取 GPS 消息
gps_count = 0
while True:
msg = mlog.recv_match(type='GPS', blocking=False)
if msg is None:
break
gps_count += 1
if gps_count <= 3:
print(f"GPS 状态: {msg.Status}, 卫星数: {msg.Satellites}")
print(f"共找到 {gps_count} 条 GPS 消息")
blocking=False 这个参数很关键。如果不加,recv_match() 会一直阻塞等待,直到读到匹配的消息。加了之后,没找到就返回 None,不会卡死。
4.3.4 按时间范围筛选
炸机分析时,我们往往只关心坠机前后几秒的数据。pymavlink 支持按时间戳筛选:
# 设置时间范围(单位:秒)
start_time = 120.0 # 从第120秒开始
end_time = 130.0 # 到第130秒结束
# 先定位到起始时间
mlog = mavutil.mavlink_connection(log_file)
while True:
msg = mlog.recv_msg()
if msg is None:
break
if msg._timestamp >= start_time:
break
# 读取时间范围内的数据
print(f"时间范围: {start_time}s - {end_time}s")
count = 0
while True:
msg = mlog.recv_msg()
if msg is None or msg._timestamp > end_time:
break
count += 1
print(f"该时间段内共有 {count} 条消息")
我记得有一次分析炸机原因,就是靠这个时间筛选功能,精确锁定了坠机前 0.5 秒的 IMU 数据。发现加速度计突然跳变,最终定位到是减震垫脱落导致的。
4.4 实战:快速提取关键数据
光讲 API 太枯燥,我们直接上一个实战例子。假设你拿到一份炸机日志,想快速提取 GPS 坐标和高度变化:
from pymavlink import mavutil
import csv
def extract_gps_data(log_file, output_csv):
"""从 .bin 日志中提取 GPS 数据并保存为 CSV"""
mlog = mavutil.mavlink_connection(log_file)
# 准备 CSV 文件
with open(output_csv, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['时间戳', '纬度', '经度', '高度', '卫星数', '定位状态'])
while True:
msg = mlog.recv_match(type='GPS', blocking=False)
if msg is None:
break
writer.writerow([
msg._timestamp,
msg.Lat,
msg.Lng,
msg.Alt,
msg.Satellites,
msg.Status
])
print(f"GPS 数据已保存到: {output_csv}")
# 使用示例
extract_gps_data("crash_log.bin", "gps_data.csv")
4.5 本章知识体系
为了让你更直观地理解 pymavlink 的使用流程,我画了一张图:
4.6 常见问题与避坑
最后,分享几个我实际工作中遇到的问题:
| 问题 | 现象 | 解决办法 |
|---|---|---|
| 日志版本不兼容 | 读取时抛出 Unknown MAVLink version |
升级 pymavlink 到最新版:pip install --upgrade pymavlink |
| 内存占用过高 | 读取大日志时内存飙升 | 使用迭代器逐条读取,不要一次性加载所有数据 |
| 时间戳异常 | 某些消息的时间戳为 0 | 检查日志是否完整,或者用 msg._timestamp 替代 msg.TimeUS |
| 中文路径报错 | Windows 下日志路径含中文时读取失败 | 将日志文件复制到纯英文路径下再读取 |
好了,pymavlink 的基础用法就讲到这里。记住一个核心原则:先摸清日志里有什么数据,再针对性地提取分析。下一节我们会用这些 API 做实际的炸机数据提取,到时候你就知道这些基础操作有多重要了。
公众号:蓝海资料掘金营,微信deep3321