工業製造
産業用モノのインターネット | 工業材料 | 機器のメンテナンスと修理 | 産業プログラミング |
home  MfgRobots >> 工業製造 >  >> Manufacturing Technology >> 製造プロセス

NeoPixelリングとArduinoで作るインタラクティブジャイロスコープLEDディスプレイ

部品と材料

部品名 数量 備考
SparkFun Arduino Pro Mini 328 - 5V/16MHz 1 小型マイコンボード
Adafruit NeoPixel Ring: WS2812 5050 RGB LED (24個) 1 フルカラーLEDリング
SparkFun IMU Breakout - MPU-9250 1 9軸 IMU (加速度/ジャイロ/磁気) ※コードは MPU6050 用
ブレッドボード (汎用) 1
オス-オス ジャンパーワイヤー 1式
オス-メス ジャンパーワイヤー 1式
単4電池ボックス (4本用) + 電池 1 電源用

プロジェクト概要

このチュートリアルでは、MPU6050 ジャイロスコープ、NeoPixel リング、Arduino を使って、傾き角度に対応して LED が光るデバイスを作ります。

シンプルで楽しいプロジェクトで、ブレッドボード上に組み立てます。手順に従えば冒頭の動画のようなデバイスが完成します。ジャイロスコープと NeoPixel リングの使い方を学ぶのに最適なチュートリアルです。

以前のチュートリアルでシンプルな LED を使ったものに反響があったため、今回は NeoPixel リングに置き換えて作り直しました。Adafruit ライブラリで簡単に制御でき、見た目もずっとスペクタクルです。これらのパーツが手元に眠っているなら、有効活用する絶好の機会です。ステップバイステップで製作方法を解説し、最後には仕組みも説明します。

ステップ 1: 必要なもの

NeoPixelリングとArduinoで作るインタラクティブジャイロスコープLEDディスプレイ

パーツ

  1. Arduino Pro Mini 328P
  2. ブレッドボード
  3. MPU6050 ジャイロスコープ (MPU-9250 でも動作しますが、コードは MPU6050 前提)
  4. 24 LED NeoPixel リング
  5. 単4電池ボックス (4本用) + 電池4本
  6. U字型ジャンパーケーブル (任意)。ブレッドボード上で見栄えが良く、LED が見やすくなります。eBay で 140 本セット約 4 ドル。なければデュポンコードで代用可能。

工具:

  1. USB-シリアル FTDI アダプター FT232RL (Arduino Pro Mini 書き込み用)
  2. Arduino IDE

スキル:

  1. はんだ付け
  2. 基本的な Arduino プログラミング

ステップ 2: 組み立て

NeoPixelリングとArduinoで作るインタラクティブジャイロスコープLEDディスプレイ
NeoPixelリングとArduinoで作るインタラクティブジャイロスコープLEDディスプレイ

Fritzing 回路図 (fzz 形式) と実装写真を掲載しています。接続の可視化にご活用ください。

  1. NeoPixel リング裏面に 3 本のオスピンを半田付け (写真参照)
    • VCC (正極)
    • GND (負極)
    • DATA IN (データ入力)
  2. 電池ボックスをブレッドボード接続可能にする
    簡単なのは、電池ボックスの端子にオスデュポンコードを 2 本半田付けすること。
  3. ブレッドボード上に配線
    • NeoPixel リング、マイクロコントローラー、ジャイロスコープを画像のように配置
    • すべての GND 配線: マイコン、NeoPixel、ジャイロ
    • すべての VCC 配線: マイコン、NeoPixel、ジャイロ
    • データ配線:
      • SDA, SCL: マイコン ↔ ジャイロ (I2C)
      • D6: マイコン → NeoPixel リング DATA IN
    • 通電前にすべての接続をダブルチェック
    • (任意) ガムテープで電池ボックスをブレッドボード裏に固定すると持ち運びしやすくなる

ステップ 3: コードとキャリブレーション

必要なライブラリのインストール

  1. Adafruit NeoPixel ライブラリ - NeoPixel 制御用
  2. MPU6050 ライブラリ - ジャイロスコープ用 (I2Cdevlib に含まれる MPU6050_6Axis_MotionApps20.h を使用)

これらは重い処理を肩代わりしてくれる優秀なライブラリです。NeoPixel 詳細は こちら。

ライブラリとサンプルコード

以下のコードをコピーするか、GitHub からダウンロードしてください。

#include "I2Cdev.h"
#include <Adafruit_NeoPixel.h>
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

#define NEOPIXED_CONTROL_PIN 6
#define NUM_LEDS 24

const int MAX_ANGLE = 45;      // 最大傾き角度 (度)
const int LED_OFFSET = 12;     // LED 位置オフセット (キャリブレーション用)

MPU6050 mpu;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEOPIXED_CONTROL_PIN, NEO_RGB + NEO_KHZ800);

unsigned long lastPrintTime = 0;
bool initialization = false;   // DMP 初期化成功フラグ
uint8_t mpuIntStatus;          // MPU 割り込みステータス
uint8_t devStatus;             // デバイス操作ステータス
uint16_t packetSize;           // DMP パケットサイズ (デフォルト 42 バイト)
uint16_t fifoCount;            // FIFO バイト数
uint8_t fifoBuffer[64];        // FIFO ストレージバッファ

Quaternion q;                  // [w, x, y, z] クォータニオン
VectorFloat gravity;           // [x, y, z] 重力ベクトル
float ypr[3];                  // [yaw, pitch, roll] ヨー/ピッチ/ロール

volatile bool mpuInterrupt = false; // MPU 割り込みフラグ

void setup() {
    Serial.begin(9600);
    Serial.println("Program started");
    initialization = initializeGyroscope();
    strip.begin();
}

void loop() {
    if (!initialization) return;
    
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();
    fifoCount = mpu.getFIFOCount();
    
    if (hasFifoOverflown(mpuIntStatus, fifoCount)) {
        mpu.resetFIFO();
        return;
    }
    
    if (mpuIntStatus & 0x02) {
        while (fifoCount < packetSize) {
            fifoCount = mpu.getFIFOCount();
        }
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        fifoCount -= packetSize;
        
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        mpu.dmpGetGravity(&gravity, &q);
        mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
        
        // ラジアン → 度変換して LED 更新
        redrawLeds(ypr[0] * 180/M_PI, ypr[1] * 180/M_PI, ypr[2] * 180/M_PI);
    }
}

boolean hasFifoOverflown(int mpuIntStatus, int fifoCount) {
    return (mpuIntStatus & 0x10) || (fifoCount == 1024);
}

void redrawLeds(int x, int y, int z) {
    // 角度を ±MAX_ANGLE にクランプ
    x = constrain(x, -MAX_ANGLE, MAX_ANGLE);
    y = constrain(y, -MAX_ANGLE, MAX_ANGLE);
    
    // Y-Z 平面での傾き方向に基づき、4象限に分けて LED 制御
    if (y < 0 && z > 0) {       // 第1象限
        lightLeds(y, z, 0, 5, 0, 89);
    } else if (y < 0 && z < 0) { // 第2象限
        lightLeds(y, z, 6, 12, 89, 0);
    } else if (y > 0 && z < 0) { // 第3象限
        lightLeds(y, z, 13, 19, 0, 89);
    } else if (y > 0 && z > 0) { // 第4象限
        lightLeds(y, z, 20, 24, 89, 0);
    }
}

void lightLeds(int x, int y, int fromLedPosition, int toLedPosition, int fromAngle, int toAngle) {
    // 2軸の傾きから角度を計算 (アークタンジェント)
    double angle = (atan((double)abs(x) / (double)abs(y)) * 4068) / 71;
    
    // 角度を LED 位置にマッピング
    int ledNr = map(angle, fromAngle, toAngle, fromLedPosition, toLedPosition);
    
    printDebug(y, z, ledNr, angle);
    
    uint32_t color;
    for (int i = 0; i < NUM_LEDS; i++) {
        color = strip.Color(0, 0, 0); // 消灯
        if (i == ledNr) {
            color = strip.Color(0, 180, 0);      // 当該 LED: 明るい緑
        } else if (i == ledNr - 1) {
            color = strip.Color(0, 5, 0);        // 1つ前の LED: 暗い緑 (フェード効果)
        }
        strip.setPixelColor(normalizeLedPosition(i), color);
    }
    strip.show();
}

int normalizeLedPosition(int position) {
    // LED_OFFSET を考慮してリング位置を正規化
    if (NUM_LEDS > position + LED_OFFSET) {
        return position + LED_OFFSET;
    }
    return position + LED_OFFSET - NUM_LEDS;
}

void printDebug(int y, int z, int lightLed, int angle) {
    if (millis() - lastPrintTime < 500) return;
    Serial.print("a="); Serial.print(angle); Serial.print("; ");
    Serial.print("ll="); Serial.print(lightLed); Serial.print("; ");
    Serial.print("y="); Serial.print(y); Serial.print("; ");
    Serial.print("z="); Serial.print(z); Serial.println("; ");
    lastPrintTime = millis();
}

bool initializeGyroscope() {
    Wire.begin();
    TWBR = 24; // I2C クロック 400kHz
    
    mpu.initialize();
    Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
    
    Serial.println(F("Initializing DMP..."));
    devStatus = mpu.dmpInitialize();
    
    // 要キャリブレーション: ご自身のセンサーに合わせてオフセット調整
    mpu.setXGyroOffset(220);
    mpu.setYGyroOffset(76);
    mpu.setZGyroOffset(-85);
    mpu.setZAccelOffset(1788);
    
    if (devStatus != 0) {
        Serial.print(F("DMP Initialization failed (code "));
        Serial.print(devStatus);
        Serial.println(F(")"));
        return false;
    }
    
    mpu.setDMPEnabled(true);
    Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
    attachInterrupt(0, dmpDataReady, RISING);
    
    mpuIntStatus = mpu.getIntStatus();
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
    
    packetSize = mp.dmpGetFIFOPacketSize();
    return true;
}

void dmpDataReady() {
    mpuInterrupt = true;
}

コードのアップロード

FTDI アダプターを使って Arduino Pro Mini にコードをアップロードします。

電源接続 (電池)

電池ボックスを接続して電源を入れます。

キャリブレーション

最も重要なのは LED_OFFSET 定数の調整です。サンプルでは 12 ですが、電源投入後に基準方向 (水平) で LED が点灯するよう 0〜23 の範囲で調整してください。NeoPixel リングの取り付け角度とジャイロスコープの向きを合わせるためのものです。

仕組みの詳細は次のステップを参照してください。

ステップ 4: 仕組みの解説 (任意)

NeoPixelリングとArduinoで作るインタラクティブジャイロスコープLEDディスプレイ

MPU6050 ジャイロスコープについて

MPU6050 は MEMS (Microelectromechanical systems) ジャイロスコープです。各種 MEMS ジャイロには何らかの振動子があり、加速度と方向変化を検出します。運動保存の法則により、振動物体は同じ平面で振動し続けようとし、振動面のズレから方向変化を導出できます。

ジャイロ内部のマイクロコントローラーが高度な数学演算でロール、ピッチ、ヨーを算出します。しかし生データにはノイズとドリフトが含まれるため、外部ライブラリ (DMP: Digital Motion Processor) を使ってクリーンなデータを取得しています。

NeoPixel について

NeoPixel は個別にアドレス指定可能な RGB LED で、バンドやリング状にチェーン接続されています。5V 動作で独自の駆動回路を内蔵しているため、電源とデータライン 1 本だけで制御可能です。通信はクロックとデータを含む単一データラインで行われます (詳細は こちら)。Adafruit ライブラリで簡単に操作できます。

コードの流れ

loop() 関数内で MPU6050_6Axis_MotionApps20 ライブラリを呼び出し、ジャイロスコープから新しいデータが得られると redrawLeds(x, y, z) を呼び出します。3つの引数はヨー、ピッチ、ロール (度) です。

redrawLeds() 内部:

lightLeds() 内部:

数学的背景

添付画像に LED リングと、角度決定に使用した三角関数 (アークタンジェント) の関係を図解しています。

ソースコード

GitHub リポジトリ (完全版コード)

回路図

sketch_gMlSrk3rCX.fzz (Fritzing 形式)

製造プロセス

  1. Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
  2. Adafruit 60 LED NeoPixelリングとArduinoで作るスタイリッシュな壁掛け時計
  3. K30 CO2センサで室内空気質をリアルタイム監視する
  4. 1SheeldとArduinoで盲ろう者のコミュニケーションを支援する
  5. DIY Arduino USBゲームコントローラー - 高性能ゲームパッドを自作
  6. スマート貯金箱を作ろう:Arduino Nanoでコインアクセプターを制御
  7. Pixie: Arduino Pro Miniで作るNeoPixel腕時計 - アナログとデジタル融合のDIY光る時計
  8. ArduinoでBluetooth経由でLEDを制御 – シンプルDIYガイド
  9. DFRobot静電容量式指紋センサーをArduino/ESP8266で使う – 登録・認証・削除の完全ガイド
  10. Arduino Dueでマスターする音声認識と音声合成
  11. Arduino UNOとEthernetシールドで太陽放射を測定する自作ピラノメーター