【連載⑤】宇宙を目指す小さなIMU試作の記録:PythonでIMUデータをリアルタイムに可視化する
はじめに
前回までで、MPU6050(加速度・ジャイロ)とTMP102(温度)をArduinoで同時に読み取り、CSV形式での出力ができる状態が整いました。
この回では、そのデータをPythonで読み込み、グラフとして可視化する方法を紹介します。特に今回は、
- 温度と各軸の加速度・角速度との相関
- 温度上昇に伴うセンサー出力のドリフト傾向
- センサーデータの時間変化の視覚化
といった分析を通じて、**「目に見えなかった誤差」を“見える化”**していきます。
なぜPythonで可視化するのか?
- CSVは人間にとっては「数字のかたまり」に過ぎない
- 温度とZ軸加速度の相関、回転の偏りなどは、グラフ化することで直感的に理解できる
- Pythonの
pandas
やmatplotlib
などを使えば、数行のコードで強力な可視化ができる
本記事でできるようになること
- Arduinoから出力されたCSV形式のログをPythonで読み込む
- センサー各軸データを折れ線グラフで表示する
- 温度と加速度・ジャイロの**相関グラフ(散布図)**を描画する
- 誤差傾向をつかみ、次の「補正」フェーズにつなげる
この可視化ステップは、PoC(技術実証)の中でも非常に重要なパートです。
「センサーがただ動いている」から一歩進んで、「どう動いているか・何がズレているか」を知るところまで一緒に進んでいきましょう。
使用ライブラリ
今回の可視化では、Pythonでよく使われる以下の2つのライブラリを使用します。
ライブラリ名 | 用途 |
---|---|
pandas |
CSVファイルの読み込み・データ整形・時間データ処理など |
matplotlib |
折れ線グラフや散布図などのデータ可視化ツール |
これらは非常にメジャーなライブラリで、Pythonでデータ分析やIoTログを扱うなら必須とも言えるツールです。
インストール方法(ターミナル・コマンドプロンプトで)
pip3 install pandas matplotlib
pip3 install pandas pyserial
リアルタイムでセンサー値をグラフ表示する
ここからは、ArduinoがCSV形式でシリアル出力しているセンサーデータを、Pythonでリアルタイムに受信してグラフ化する方法を紹介します。
特に以下のような分析を行いたい場面に有効です:
センサーの出力変化の傾向をその場で確認
温度と加速度などの相関関係を可視化
実験やPoC中の動作確認やデバッグ
1. Arduinoからの出力(リアルタイム形式)
Arduino側では以下のような形式でデータがシリアル出力されていることを想定します:
Temp_C,Accel_X[g],Accel_Y[g],Accel_Z[g],Gyro_X[dps],Gyro_Y[dps],Gyro_Z[dps]
29.56,0.01,-0.02,0.97,1.42,0.85,-0.12
29.62,0.01,-0.02,0.98,1.48,0.84,-0.15
29.69,0.02,-0.01,0.98,1.53,0.82,-0.10
このようなヘッダー+CSV形式の出力を Serial.println() によってArduinoが1行ずつ送ってきます。
2. Pythonコード(基本可視化)
以下のコードを使えば、温度・加速度・ジャイロの各軸を時系列でグラフ表示できます。
/dev/tty.usbmodemXXXXX や /dev/tty.usbserial-XXXX のようなものが Arduino です。
COM3 は Windowsのポート名で、
macOSではポート名が /dev/tty.* 形式になります。
import serial
import matplotlib.pyplot as plt
import pandas as pd
import time
from collections import deque
# シリアル接続と初期化
ser = serial.Serial('/dev/tty.usbmodem11301', 9600, timeout=1)
time.sleep(2)
# データ格納と可視化準備
max_len = 50
temp_data = deque([0.0]*max_len, maxlen=max_len)
accel_z_data = deque([0.0]*max_len, maxlen=max_len)
log_rows = []
# グラフ初期化(ツインY軸)
plt.ion()
fig, ax1 = plt.subplots()
# 左Y軸(Accel_Z)
line1, = ax1.plot(range(max_len), list(accel_z_data), label="Accel_Z (g)", color='orange')
ax1.set_ylabel("Accel_Z (g)")
ax1.set_ylim(-2.5, 2.5)
ax1.set_xlabel("Sample Index")
ax1.grid(True)
# 右Y軸(Temp_C)
ax2 = ax1.twinx()
line2, = ax2.plot(range(max_len), list(temp_data), label="Temp_C (°C)", color='blue', linestyle='--')
ax2.set_ylabel("Temperature (°C)")
ax2.set_ylim(20, 40) # 例:TMP102は常温で25〜35℃の範囲を想定
# 凡例
fig.legend(loc="upper right")
ax1.set_title("Real-time IMU Data")
# メインループ
try:
while True:
line = ser.readline().decode('utf-8').strip()
if not line or line.startswith("Temp_C"):
continue
parts = line.split(',')
if len(parts) == 7:
temp = float(parts[0])
accel_z = float(parts[3])
temp_data.append(temp)
accel_z_data.append(accel_z)
line1.set_ydata(accel_z_data)
line1.set_xdata(range(len(accel_z_data)))
line2.set_ydata(temp_data)
line2.set_xdata(range(len(temp_data)))
ax1.relim()
ax1.autoscale_view()
ax2.relim()
ax2.autoscale_view()
plt.pause(0.05)
# ログに保存
log_rows.append(parts)
except KeyboardInterrupt:
print("リアルタイム表示を終了しました。CSVに保存します...")
df = pd.DataFrame(log_rows, columns=[
"Temp_C", "Accel_X[g]", "Accel_Y[g]", "Accel_Z[g]",
"Gyro_X[dps]", "Gyro_Y[dps]", "Gyro_Z[dps]"
])
df.to_csv("imu_log.csv", index=False)
print("imu_log.csv に保存しました。")
ser.close()
結果
CNTL+Cでbashを抜けると、ログファイルが生成されています。
Discussion