🪐

【連載⑤】宇宙を目指す小さなIMU試作の記録:PythonでIMUデータをリアルタイムに可視化する

に公開

はじめに

前回までで、MPU6050(加速度・ジャイロ)とTMP102(温度)をArduinoで同時に読み取り、CSV形式での出力ができる状態が整いました。

この回では、そのデータをPythonで読み込み、グラフとして可視化する方法を紹介します。特に今回は、

  • 温度と各軸の加速度・角速度との相関
  • 温度上昇に伴うセンサー出力のドリフト傾向
  • センサーデータの時間変化の視覚化

といった分析を通じて、**「目に見えなかった誤差」を“見える化”**していきます。


なぜPythonで可視化するのか?

  • CSVは人間にとっては「数字のかたまり」に過ぎない
  • 温度とZ軸加速度の相関、回転の偏りなどは、グラフ化することで直感的に理解できる
  • Pythonのpandasmatplotlibなどを使えば、数行のコードで強力な可視化ができる

本記事でできるようになること

  • 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