4. 可视化基础:用 Matplotlib 绘制单变量时序图

做飞控调优,说白了就是跟数据打交道。

你想想看,飞机在天上飞了十分钟,日志文件里攒了几十万行数据。Roll 角、Pitch 角、Yaw 角、电机转速、GPS 经纬度……全是数字。光盯着数字看,眼睛看花了也看不出门道。

这时候就需要可视化。把数字变成曲线图,一眼就能看出问题在哪。

我个人习惯,拿到一份新日志,第一件事就是画 Roll 角的时序图。为什么?因为 Roll 角最能反映飞控的横滚响应好不好,有没有超调,有没有震荡。画出来一看,心里就有底了。

4.1 横纵坐标到底在说什么?

很多初学者觉得坐标轴很简单,不就是 X 轴和 Y 轴吗?嗯,确实简单,但理解不到位,后面分析就容易跑偏。

  • 横轴(X 轴):时间。单位通常是秒(s)或者毫秒(ms)。注意,飞控日志里的时间戳有两种——系统启动后的相对时间,和 GPS 的绝对时间。我一般用相对时间,方便看飞行阶段。
  • 纵轴(Y 轴):变量值。比如 Roll 角,单位是度(°)。正负号代表方向——正的是右倾,负的是左倾。

举个例子。你画出来的 Roll 角曲线,如果横轴是 0 到 300 秒,纵轴是 -30 到 +30 度。那你就知道:这架飞机在 5 分钟里,最大横滚角度没超过 30 度,说明飞得还算平稳。

一个小技巧:画图之前,先看一眼数据的范围。如果 Roll 角突然跳到 180 度,那大概率是传感器数据异常,不是飞机真的翻了。我在项目里遇到过这种情况,当时吓一跳,后来发现是 IMU 的 SPI 通信丢了一帧。

4.2 第一张图:Roll 角时序图

好,我们直接上代码。用 Matplotlib 画一张 Roll 角随时间变化的图。

import matplotlib.pyplot as plt
import pandas as pd

# 假设你已经把日志解析成了 DataFrame
# df 里有 'timestamp' 和 'roll' 两列
df = pd.read_csv('flight_log.csv')

plt.figure(figsize=(12, 5))
plt.plot(df['timestamp'], df['roll'], color='#3498db', linewidth=1.5)

plt.xlabel('Time (s)')
plt.ylabel('Roll Angle (deg)')
plt.title('Roll Angle vs Time - Flight Log Analysis')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

这段代码很简单。但有几个细节我想强调一下:

  • figsize=(12, 5):宽高比。飞控时序图我习惯用宽屏,因为时间跨度长,窄了看不清细节。
  • linewidth=1.5:线宽。太细了看不清,太粗了遮挡数据点。1.5 刚刚好。
  • grid(True, alpha=0.3):网格线。透明度调低一点,别喧宾夺主。
注意:如果你的日志里时间戳是毫秒级的,记得先转成秒。不然横轴数字会很大,看着别扭。我曾经犯过这个错,画出来的图横轴从 0 到 600000,完全没法看。

4.3 图形标注:让图表会说话

光画一条线不够。你得告诉看图的人——这里发生了什么。

我常用的标注方式有三种:

标注类型 用途 代码示例
垂直参考线 标记关键时间点,比如起飞、降落、切模式 plt.axvline(x=120, color='red', linestyle='--')
水平参考线 标记目标值或阈值,比如期望 Roll 角 0 度 plt.axhline(y=0, color='gray', linestyle=':')
文本注释 在图上直接写说明,比如“此处有超调” plt.text(125, 15, 'Overshoot', fontsize=10)

举个例子。假设你在 120 秒时切了增稳模式,Roll 角出现了一个小尖峰。你可以这样标注:

plt.axvline(x=120, color='#e74c3c', linestyle='--', linewidth=1, label='Mode Switch')
plt.text(122, 10, 'Mode switch to Stabilize', fontsize=9, color='#e74c3c')
plt.legend()

这样一看图,就知道那个尖峰是因为切模式引起的,不是飞控参数有问题。

核心原则:标注是为了让看图的人一眼就能抓住重点。不要标太多,否则图会变成花脸。我一般一张图不超过 5 个标注。

4.4 保存图片:别让分析成果白费

画完图,你得保存下来。不然下次还得重新跑代码。

保存图片用 plt.savefig()。我建议用 PNG 格式,兼顾清晰度和文件大小。

plt.savefig('roll_angle_analysis.png', dpi=200, bbox_inches='tight')

几个参数说明:

  • dpi=200:分辨率。200 够清晰,又不至于文件太大。如果你要打印或者放报告里,可以调到 300。
  • bbox_inches='tight':自动裁剪空白边。不然保存出来的图四周会有一圈白边,看着不专业。
我的习惯:文件名里带上日期和飞行架次。比如 2024-03-15_flight03_roll.png。这样找图的时候不会乱。我曾经把所有图都存成 plot.png,结果第二天就覆盖了,后悔莫及。

4.5 本章知识体系

下面这张图帮你梳理一下本章的核心逻辑:

单变量时序图绘制流程 原始日志数据 提取 timestamp + roll plt.plot() 绘制曲线 添加标注 + 网格 plt.savefig() 保存 关键点 • 横轴:时间 • 纵轴:Roll 角 • 标注不超过 5 个 • 保存用 dpi=200

4.6 避坑指南

最后,分享几个我踩过的坑:

  • 时间戳不连续:飞控日志有时候会丢帧。画出来的图会出现断点。解决办法是用 plt.plot() 的 marker 参数把数据点标出来,比如 marker='o', markersize=2。这样能看出哪些地方数据缺失了。
  • 纵轴范围不合适:如果 Roll 角大部分时间在 -5 到 +5 度之间,但有一次跳到了 30 度,Matplotlib 会自动把纵轴拉到 -30 到 +30。这时候小波动就看不清楚了。我一般用 plt.ylim(-10, 10) 手动固定范围,把异常值单独分析。
  • 中文字体乱码:Matplotlib 默认不支持中文。如果你要在图上写中文标注,记得先设置字体:plt.rcParams['font.sans-serif'] = ['SimHei']。不然出来的图全是方框。

嗯,这一章的内容就这些。画好一张 Roll 角时序图,是后面所有分析的基础。下一张图,我们会加上期望值和实际值的对比,看看飞控到底跟不跟得上指令。