スキップしてメイン コンテンツに移動

MPU‑6050で6軸センサ値を読み取り、OLED SSD1306にリアルタイム表示!

今回は、MPU‑6050(加速度センサ+ジャイロセンサを内蔵した6軸モーションセンサ)を使い、デバイスの動きや傾きを計測して、そのデータをOLEDディスプレイ(SSD1306)にリアルタイム表示します。

MPU‑6050とは?

MPU‑6050は、3軸方向の加速度を測定する加速度センサと、3軸の角速度を測定するジャイロセンサを1つの小型チップに集約した、6軸モーションセンサです。

  • 加速度センサは、デバイスの直線的な動きや傾きを検出します。例えば、スマートフォンの画面が回転する仕組みや、歩数計の動きを感知する役割を担います。

  • ジャイロセンサは、回転の速さや角度の変化を測定します。これにより、デバイスがどの方向にどれだけ回転したかを正確に把握できます。

この2つのセンサを組み合わせることで、デバイスの姿勢や動きを多角的に把握することが可能です。例えば、ロボットやドローンの制御、自作ゲームコントローラの動き検知、モーションキャプチャーなど、幅広い応用が期待できます。

また、MPU‑6050はI²C通信を用いて簡単にマイコンと接続でき、小型で省電力、比較的安価なため、多くの電子工作や組み込みシステムで人気のあるセンサです。

ライブラリのインストール(Thonny)

  1. Pico W を PC に接続し、Thonny を開きます。

  2. メニュー [ツール] → [パッケージを管理..] をクリック。

OLED 用ライブラリ

  • 検索窓に ssd1306 と入力 → micropython-ssd1306 を選び Install
    (詳しい手順や日本語解説は、前回の記事を参考にしてください)

配線図


3.ソースコード

# main.py ── Raspberry Pi Pico W + MPU‑6050  (OLED 表示 + キャリブレーション & フィルタ版)
#   ・起動時に静置キャリブレーション(オフセット補正)
#   ・IIR ローパスフィルタで揺れを低減
#   ・DLPF 10 Hz・±250 °/s に設定
#   ・ジャイロ角速度を OLED にリアルタイム表示
#
# 20250703  sakura lain 用

from machine import Pin, I2C
import time, struct, ssd1306

# ───────────────────────────────
# 定数・設定
# ───────────────────────────────
I2C_ID   = 0                 # I2C0 (SDA=GP16, SCL=GP17)
I2C_FREQ = 400_000           # 400 kHz
MPU_ADDR = 0x68              # AD0=LOW → 0x68

GYRO_SENS     = 131.0        # ±250°/s → 131 LSB/°/s
ALPHA         = 0.2          # IIR フィルタ係数 (0<α≤1)
CAL_SAMPLES   = 200          # キャリブレーション試行回数
CAL_DELAY_MS  = 5            # キャリブレーション間隔

# ───────────────────────────────
# インスタンス生成
# ───────────────────────────────
i2c  = I2C(I2C_ID, sda=Pin(16), scl=Pin(17), freq=I2C_FREQ)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)

# ───────────────────────────────
# MPU‑6050 操作関数
# ───────────────────────────────
def mpu_write(reg: int, data: int) -> None:
    i2c.writeto_mem(MPU_ADDR, reg, bytes([data]))

def mpu_read(reg: int, length: int) -> bytes:
    return i2c.readfrom_mem(MPU_ADDR, reg, length)

def mpu_init() -> None:
    """
    DLPF: 10 Hz / ±250 °/s / ±8 g で初期化
    """
    mpu_write(0x6B, 0x00)   # PWR_MGMT_1: スリープ解除
    mpu_write(0x1B, 0x00)   # GYRO_CONFIG: ±250 °/s
    mpu_write(0x1C, 0x10)   # ACCEL_CONFIG: ±8 g
    mpu_write(0x1A, 0x05)   # CONFIG: DLPF 10 Hz
    time.sleep_ms(100)

def read_raw():
    """0x3B から 14 byte 読み出し → tuple(raw_ax, raw_ay, raw_az, raw_tmp, raw_gx, raw_gy, raw_gz)"""
    return struct.unpack(">hhhhhhh", mpu_read(0x3B, 14))

# ───────────────────────────────
# キャリブレーション(静置でオフセット取得)
# ───────────────────────────────
def calibrate_gyro():
    sum_gx = sum_gy = sum_gz = 0
    for _ in range(CAL_SAMPLES):
        _, _, _, _, gx, gy, gz = read_raw()
        sum_gx += gx
        sum_gy += gy
        sum_gz += gz
        time.sleep_ms(CAL_DELAY_MS)
    return (sum_gx // CAL_SAMPLES,
            sum_gy // CAL_SAMPLES,
            sum_gz // CAL_SAMPLES)

# ───────────────────────────────
# OLED 画面レイアウト
# ───────────────────────────────
def oled_header():
    oled.fill(0)
    oled.text("MPU-6050  Gyro", 0, 0)
    oled.text("X:        dps",  0, 16)
    oled.text("Y:        dps",  0, 32)
    oled.text("Z:        dps",  0, 48)
    oled.show()

def oled_update(x, y, z, old):
    """
    値が変わった軸のみ部分書き換え (x,y,z: float, old: dict)
    """
    def _draw(val, ypos, key):
        if val != old[key]:
            oled.fill_rect(24, ypos, 48, 8, 0)
            oled.text("{:+6.2f}".format(val), 24, ypos)
            old[key] = val
    _draw(x, 16, 'x')
    _draw(y, 32, 'y')
    _draw(z, 48, 'z')
    oled.show()

# ───────────────────────────────
# メイン
# ───────────────────────────────
def main():
    print("Initializing MPU‑6050 …")
    mpu_init()
    print("Calibrating gyro … (keep sensor still)")
    off_gx, off_gy, off_gz = calibrate_gyro()
    print("Offset =", off_gx, off_gy, off_gz)

    oled_header()

    # IIR フィルタ初期値
    filt_x = filt_y = filt_z = 0.0
    prev = {'x': None, 'y': None, 'z': None}

    while True:
        # ── 生データ取得 & オフセット補正 ───────────────────────
        _, _, _, _, raw_gx, raw_gy, raw_gz = read_raw()
        gx = (raw_gx - off_gx) / GYRO_SENS   # °/s
        gy = (raw_gy - off_gy) / GYRO_SENS
        gz = (raw_gz - off_gz) / GYRO_SENS

        # ── IIR ローパスフィルタ ───────────────────────────
        filt_x += ALPHA * (gx - filt_x)
        filt_y += ALPHA * (gy - filt_y)
        filt_z += ALPHA * (gz - filt_z)

        # ── OLED 更新 ────────────────────────────────────
        oled_update(filt_x, filt_y, filt_z, prev)

        # ── デバッグ出力 (USB‑Serial) ─────────────────────
        print("gyr_fx:{:+.2f},gyr_fy:{:+.2f},gyr_fz:{:+.2f}"
              .format(filt_x, filt_y, filt_z))

        time.sleep_ms(50)  # 20 Hz 表示

# スクリプト直実行時のみ
if __name__ == "__main__":
    main()


処理の流れ

  1. ⚙️ I2CとOLEDの初期化

    • I2Cバスを設定(SDA=GP16、SCL=GP17)

    • SSD1306 OLEDディスプレイの初期化

  2. 🔧 MPU-6050の初期設定

    • スリープ解除

    • ジャイロの感度設定(±250°/秒)

    • 加速度の感度設定(±8g)

    • DLPF(デジタルローパスフィルタ)を10Hzに設定

  3. 🧮 静置状態でジャイロのキャリブレーション(オフセット補正)

    • 一定回数(200回)ジャイロ値を読み取り、平均値をオフセットとして保存

    • これにより動作開始時の誤差を低減

  4. 🖥️ OLED画面のレイアウト表示

    • 画面にタイトルと軸ラベル(X, Y, Z)を表示

  5. 🔄 メインループ(約20Hzで繰り返し)

    • MPU-6050から生データ(加速度・ジャイロ)を取得

    • ジャイロデータからキャリブレーションオフセットを引いて補正

    • IIR(単純指数移動平均)ローパスフィルタで揺れを低減

    • フィルタ後の角速度データをOLEDにリアルタイム表示(変化のあった部分のみ更新)

    • USBシリアルに値を出力(デバッグ用)

  6. 一定時間(50ms)スリープしてループ継続

    • 約20Hzの更新速度を実現





コメント

このブログの人気の投稿

Raspberry Pi Pico Wを使ってみよう

   Raspberry Pi Pico W(ラズベリーパイ ピコ ダブリュー) は、英国 Raspberry Pi 財団が 2022 年に発売した Wi-Fi 搭載マイコンボードです。従来の「Raspberry Pi」と聞くと Linux が動くシングルボードコンピュータ(SBC)を思い浮かべがちですが、Pico W は マイクロコントローラ (MCU)に分類され、いわゆる “組み込み開発” を手軽に始められるデバイスです。搭載 MCU はデュアルコア Arm Cortex-M0+(133 MHz 動作)の RP2040 。ここに Infineon 製 CYW43439 チップが追加され、 2.4 GHz IEEE 802.11 b/g/n Wi-Fi(BLE 対応 FW も提供中) が使えるのが最大の特徴です。  開発言語は MicroPython や CircuitPython が真っ先に紹介されることが多いのですが、 公式 Pico SDK を使えば C/C++ でも本格的に開発 できます。SDK は CMake ベースで Windows/macOS/Linux いずれでも利用可能。さらに、Arduino Core RP2040 が整備されたことで Arduino IDE 2.x からも “スケッチ感覚” で書き込みが可能 になりました。したがって、 「まずは Python でサッと試す」 「より高速化や省メモリ化が必要になったら C/C++ へ移行」 といった二段構えの学習ルートが取れるのが魅力です。 メニュー(基礎編) 01. Raspberry Pi Pico Wの開発環境を整える。 02. スイッチで 発光ダイオード(LED) を点灯・消灯させる 03. タイマーで発光ダイオード(LED)を1秒ごとに点滅させる 04. シリアル通信(オウム返し ) 05. シリアル通信(発光ダイオードの点灯・消灯) 06. サーボモーターを動かしてみよう(SG90制御入門) 07. DCモータを動かす(PWM) 08. IRリモートでRGBLED点灯 09. アナログ電圧を測定する(ADCの基本) 10. GPIO割り込み処理 11. リレーを駆動してLEDを制御する 12. DFPlayer で MP3 再生 13. 7 セグ 4 ...

スイッチで発光ダイオード(LED)を点灯・消灯させる

 スイッチを使って発光ダイオード(LED)を制御してみましょう。今回は、スイッチをオンにすると LED が点灯し、オフにすると消灯するように動作させます。もちろん、スイッチと LED を直接接続するのではなく、適切な回路を介して制御します。 1.回路 GPIO16 はデジタル入力として使用し、内部プルアップを有効にします。GPIO15 は出力として設定します。なお、接続には 1kΩ(1キロオーム)の抵抗を使用します。 【ソースコード】 # スイッチで LED を ON / OFF する簡単な例 #   GPIO15 : LED(出力)— LOW で消灯、HIGH で点灯 #   GPIO16 : スイッチ(入力)— 内部プルアップ抵抗を使用 # # ※ 配線例 #   LED のアノード → 1 kΩ 抵抗 → GPIO15 #   LED のカソード → GND #   スイッチ片側   → GPIO16 #   スイッチ反対側 → GND # # ★ 動作 #   スイッチを押す(GPIO16 が LOW)   → LED 点灯 #   スイッチを離す(GPIO16 が HIGH) → LED 消灯 from machine import Pin    # GPIO 制御ライブラリ import utime               # 時間関連(今回は使用しないが拡張用に読み込み) led = Pin ( 15 , Pin.OUT)                 # GPIO15 を出力モードに設定(LED 用) sw   = Pin ( 16 , Pin.IN, Pin.PULL_UP)     # GPIO16 を入力モード+内部プルアップ有効 while True:     if sw. value () == 1 :      # プルアップなので押していないときは 1   ...

シリアル通信(オウム返し)

 Raspberry Pi Pico Wは単体で使うのも便利ですが、パソコンや他のマイコンなど、さまざまな機器と通信できるとさらに活用の幅が広がります。 そこで今回は「シリアル通信」について紹介します。 まずは、パソコンから送られてきたデータをPico Wが受け取り、同じデータをそのままパソコンに送り返す、いわゆる「オウム返し(エコー)」を実装してみましょう。 シリアル通信とは? シリアル通信は、マイコンとパソコンや他の機器がデータを1ビットずつ順番に送受信する通信方式です。Raspberry Pi Pico WではUART(Universal Asynchronous Receiver/Transmitter)というハードウェアを使って実現します。 接続回路  id      TX       RX        baudrate     parity    stopbit  0  GPIO0 GPIO1   9600  なし  1  1  GPIO4 GPIO5   9600  なし  1 今回はUART1(GPIO4:TX、GPIO5:RX)を使用します。 ソースコード例 from machine import UART, Pin # UART1ポートを9600bpsで初期化(ボーレートを指定) sirial = UART ( 1 , 9600 ) # UARTの詳細設定: 9600bps 、データビット8、パリティ無し、ストップビット1で初期化 sirial. init ( 9600 , bits = 8 , parity = None, stop = 1 ) # UARTで文字列を送信する(※MicroPythonではバイト列で送るのが正しいため b '' にしたほうが安全) sirial. write (b 'Hello World \n\r ' )  # 起動時に「Hello World」を送信 # メインループ while True:     # 受信バッファにデータが存在するかチェック     if sirial. any () > 0 : ...