MEMSセンサー探求: Arduinoプロジェクト向け加速度計、ジャイロスコープ、磁力計
このチュートリアルでは、MEMS加速度計、ジャイロスコープ、磁力計の仕組みと、Arduinoボードでそれらを使用する方法を学びます。また、Processing IDEを使ってセンサーを活用した実践的なアプリケーションも作成します。以下の動画をご覧いただくか、書面によるチュートリアルをお読みください。 MEMS(Micro-Electro-Mechanical Systems:微小電気機械システム)は、0.001mm〜0.1mmのサイズの微小コンポーネントで構成される非常に小さなシステムやデバイスです。これらのコンポーネントはシリコン、ポリマー、金属、セラミックなどで作られ、通常CPU(マイクロコントローラー)と組み合わせてシステムを完成させます。 それでは、これらのMEMSセンサーがどのように動作するか簡単に説明します。 加速度計は静電容量の変化を測定することで加速度を測定します。その微細構造は以下のようなものです。一方向にのみ動くように拘束されたバネに取り付けられた質量と、固定された外側のプレートがあります。特定の方向に加速度が加わると質量が動き、プレートと質量の間の静電容量が変化します。この静電容量の変化を測定・処理し、特定の加速度値に対応付けます。 ジャイロスコープはコリオリ効果を利用して角速度を測定します。ある質量が特定の方向に特定の速度で動いているとき、外部から角速度が加わると(緑の矢印)、力が発生し(青赤の矢印)、質量が垂直方向に変位します。加速度計と同様に、この変位が静電容量の変化を引き起こし、それが測定・処理されて特定の角速度に対応付けられます。 ジャイロスコープの微細構造は以下のようになっています。常に動いている(振動している)質量があり、外部から角速度が加わると質量の柔軟な部分が動いて垂直変位を生じさせます。 磁力計はホール効果または磁気抵抗効果を利用して地球磁場を測定します。実は市販センサーの約90%がホール効果を使用しており、その仕組みは以下の通りです。 写真のような導電プレートがあり、電流を流すと電子はプレートの片側からもう片側へ真っ直ぐ流れます。ここでプレートの近くに磁場を近づけると、真っ直ぐな流れが乱され、電子はプレートの片側に、正孔は反対側に偏向します。つまり、この両側の間にメーターを置くと、磁場の強さと方向に依存した電圧が得られます。 残り約10%のセンサーは磁気抵抗効果を使用します。これらのセンサーは磁場に敏感な材料(通常は鉄(Fe)とニッケル(Ni)で構成)を使用します。これらの材料が磁場にさらされると抵抗が変化します。 では、これらのセンサーをArduinoボードに接続して活用してみましょう。例として、以下のセンサーを搭載したGY-80ブレイクアウトボードを使用します:ADXL345 3軸加速度計、L3G4200D 3軸ジャイロスコープ、MC5883L 3軸磁力計、さらに気圧計と温度計も搭載されていますが、このチュートリアルでは使用しません。 以下のサイトから部品を入手できます: このボードはI2C通信プロトコルを使用しており、わずか2本のワイヤーで全センサーと通信できます。Arduinoとセンサー間の通信を行うには、各センサーの固有デバイスアドレスと、データを取得するための内部レジスタアドレスを知る必要があります。これらのアドレスは各センサーのデータシートから確認できます: I2C通信の仕組みについて詳しくは、別のI2C通信プロトコルチュートリアルをご覧ください。 それでは、センサーからデータを取得するコードを見ていきましょう。加速度計から始め、各コードの前に説明を加え、コード内のコメントにも追加説明を入れています。 まずArduino Wireライブラリをインクルードし、センサーのレジスタアドレスを定義します。setup()ではWireライブラリを初期化し、シリアル通信を開始します(シリアルモニターで結果を表示するため)。また、ここでセンサーをアクティブ化、つまりPower_CTLレジスタに適切なバイトを送信して測定を有効にします。Wire.beginTransmission()関数で通信相手のセンサー(この場合は3軸加速度計)を選択し、Wire.write()関数でどの内部レジスタと通信するかを指定します。その後、測定有効化のための適切なバイトを送信し、Wire.endTransmission()関数で伝送を終了してデータをレジスタに送ります。 loop()では各軸のデータを読み取ります。X軸から始めます。まず、X軸の2つの内部レジスタと通信するよう指定します。次にWire.requestFrom()関数で、2つのレジスタから送信されたデータ(2バイト)を要求します。Wire.available()関数は取得可能なバイト数を返し、要求したバイト数(この場合2バイト)と一致する場合、Wire.read()関数でX軸の2つのレジスタからバイトを読み取ります。 レジスタからの出力データは2の補数形式で、X0が最下位バイト、X1が最上位バイトです。これらのバイトを、X軸の方向に対する地球の加速度(重力)に応じて-1〜+1の浮動小数点値に変換する必要があります。同じ手順を他の2軸でも繰り返し、最後にこれらの値をシリアルモニターに出力します。 ジャイロスコープからデータを取得するコードも前回と似ています。まずレジスタアドレスとデータ用の変数を定義します。setup()では、CTRL_REG1を使ってセンサーをウェイクアップしノーマルモードに設定し、感度も選択します。この例では2000dps感度モードを選択します。 loop()では加速度計と同様にX、Y、Z軸のデータを読み取ります。その後、生データを角度値に変換する必要があります。センサーのデータシートによると、2000dps感度モードでは70 mdps/digitに対応します。つまり、生の出力データに0.07を掛けることで、度/秒単位の角速度が得られます。さらに角速度に時間を掛けると角度値が得られます。そのため、各ループセクションの時間間隔を計算する必要があり、loop()の先頭と末尾でmillis()関数を使用し、その値を「dt」変数に格納します。実行される各ループで角度を計算し、最終的な角度値に加算します。他の2軸も同様に処理し、最後にシリアルモニターに結果を出力します。 ここでも前回と同様の手法を使用します。まずレジスタアドレスを定義し、setup()でセンサーを連続測定モードに設定します。loop()では、前回のセンサーと同じ方法で各軸の生データを取得します。 その後、生データを磁場値またはガウス単位に変換する必要があります。センサーのデータシートによると、デフォルト感度モードは0.92mG/digitです。つまり、生データに0.00092を掛けることで、地球磁場をガウス単位で取得できます。最後にシリアルモニターに値を出力します。 こちらがセンサーを使ったクールなアプリケーション例、MEMSデジタルコンパスです。Processing IDEで作成しました。詳細とこの例のソースコードは以下のリンクからご覧いただけます:MEMSとは?
MEMS加速度計

MEMSジャイロスコープ


MEMS磁力計


ArduinoとMEMSセンサー

ソースコード
Arduino加速度計コード
#include <Wire.h>
//--- 加速度計レジスタアドレス
#define Power_Register 0x2D
#define X_Axis_Register_DATAX0 0x32 // DATAX0内部レジスタの16進アドレス
#define X_Axis_Register_DATAX1 0x33 // DATAX1内部レジスタの16進アドレス
#define Y_Axis_Register_DATAY0 0x34
#define Y_Axis_Register_DATAY1 0x35
#define Z_Axis_Register_DATAZ0 0x36
#define Z_Axis_Register_DATAZ1 0x37
int ADXAddress = 0x53; // デバイスアドレス(8ビット目でモード選択、この場合は読み取り)
int X0,X1,X_out;
int Y0,Y1,Y_out;
int Z1,Z0,Z_out;
float Xa,Ya,Za;
void setup() {
Wire.begin(); // Wireライブラリを初期化
Serial.begin(9600);
delay(100);
Wire.beginTransmission(ADXAddress);
Wire.write(Power_Register); // Power_CTLレジスタ
// 測定を有効化
Wire.write(8); // ビットD3をHighにして測定有効化 (0000 1000)
Wire.endTransmission();
}
void loop() {
// X軸
Wire.beginTransmission(ADXAddress); // センサーへの伝送開始
// 特定のレジスタにデータを要求
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);
Wire.endTransmission(); // 伝送終了し、2つのレジスタからデータを送信
Wire.requestFrom(ADXAddress,2); // 2つのレジスタから送信された2バイトを要求
if(Wire.available()<=2) {
// X0 = Wire.read(); // レジスタからデータを読み取り
X1 = Wire.read();
/* X軸の生データをX軸加速度に変換
* 出力データは2の補数
* X0が最下位バイト
* X1が最上位バイト
*/
X1=X1<<8;
X_out =X0+X1;
Xa=X_out/256.0; // Xa = -1〜+1の出力値、X軸に作用する重力加速度
}
// Y軸
Wire.beginTransmission(ADXAddress);
Wire.write(Y_Axis_Register_DATAY0);
Wire.write(Y_Axis_Register_DATAY1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available()<=2) {
Y0 = Wire.read();
Y1 = Wire.read();
Y1=Y1<<8;
Y_out =Y0+Y1;
Ya=Y_out/256.0;
}
// Z軸
Wire.beginTransmission(ADXAddress);
Wire.write(Z_Axis_Register_DATAZ0);
Wire.write(Z_Axis_Register_DATAZ1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress,2);
if(Wire.available()<=2) {
Z0 = Wire.read();
Z1 = Wire.read();
Z1=Z1<<8;
Z_out =Z0+Z1;
Za=Z_out/256.0;
}
// シリアルモニターにデータを出力
Serial.print("Xa= ");
Serial.print(Xa);
Serial.print(" Ya= ");
Serial.print(Ya);
Serial.print(" Za= ");
Serial.println(Za);
}コード言語: Arduino (arduino)Arduinoジャイロスコープコード
#include <Wire.h>
//--- ジャイロレジスタアドレス
#define Gyro_gX0 0x28
#define Gyro_gX1 0x29
#define Gyro_gY0 0x2A
#define Gyro_gY1 0x2B
#define Gyro_gZ0 0x2C
#define Gyro_gZ1 0x2D
int Gyro = 0x69; // デバイスアドレス(8ビット目でモード選択、この場合は読み取り)
int gX0, gX1, gX_out;
int gY0, gY1, gY_out;
int gZ0, gZ1, gZ_out;
float Xg,Yg,Zg;
float angleX,angleY,angleZ,angleXc,angleYc,angleZc;
unsigned long start, finished, elapsed;
float dt=0.015;
void setup() {
Wire.begin();
Serial.begin(9600);
delay(100);
Wire.beginTransmission(Gyro);
Wire.write(0x20); // CTRL_REG1 - 電源モード
Wire.write(15); // ノーマルモード: 15d - 00001111b
Wire.endTransmission();
Wire.beginTransmission(Gyro);
Wire.write(0x23); // CTRL_REG4 - 感度、スケール選択
Wire.write(48); // 2000dps: 48d - 00110000b
Wire.endTransmission();
}
void loop() {
start=millis();
//---- X軸
Wire.beginTransmission(Gyro); // デバイスへ伝送
Wire.write(Gyro_gX0);
Wire.endTransmission();
Wire.requestFrom(Gyro,1);
if(Wire.available()<=1) {
gX0 = Wire.read();
}
Wire.beginTransmission(Gyro); // デバイスへ伝送
Wire.write(Gyro_gX1);
Wire.endTransmission();
Wire.requestFrom(Gyro,1);
if(Wire.available()<=1) {
gX1 = Wire.read();
}
//---- Y軸
Wire.beginTransmission(Gyro); // デバイスへ伝送
Wire.write(Gyro_gY0);
Wire.endTransmission();
Wire.requestFrom(Gyro,1);
if(Wire.available()<=1) {
gY0 = Wire.read();
}
Wire.beginTransmission(Gyro); // デバイスへ伝送
Wire.write(Gyro_gY1);
Wire.endTransmission();
Wire.requestFrom(Gyro,1);
if(Wire.available()<=1) {
gY1 = Wire.read();
}
//---- Z軸
Wire.beginTransmission(Gyro); // デバイスへ伝送
Wire.write(Gyro_gZ0);
Wire.endTransmission();
Wire.requestFrom(Gyro,1);
if(Wire.available()<=1) {
gZ0 = Wire.read();
}
Wire.beginTransmission(Gyro); // デバイスへ伝送
Wire.write(Gyro_gZ1);
Wire.endTransmission();
Wire.requestFrom(Gyro,1);
if(Wire.available()<=1) {
gZ1 = Wire.read();
}
//---------- X軸
// 生データ
gX1=gX1<<8;
gX_out =gX0+gX1;
// データシートより: 70 mdps/digit
Xg=gX_out*0.07; // 角速度
// 角速度 * dt = 角度
angleXc = Xg*dt;
angleX = angleX + angleXc;
//---------- Y軸
gY1=gY1<<8;
gY_out =gY0+gY1;
Yg=gY_out*0.07;
angleYc = Yg*dt;
angleY = angleY + angleYc;
//---------- Z軸
gZ1=gZ1<<8;
gZ_out =gZ0+gZ1;
Zg=gZ_out*0.07;
angleZc = Zg*dt;
angleZ = angleZ + angleZc;
// シリアルモニターにデータを出力
Serial.print("angleX= ");
Serial.print(angleX);
Serial.print(" angleY= ");
Serial.print(angleY);
Serial.print(" angleZ= ");
Serial.println(angleZ);
delay(10);
// dtの計算
finished=millis();
elapsed=finished-start;
dt=elapsed/1000.0;
start = elapsed = 0;
}コード言語: Arduino (arduino)Arduino磁力計コード
#include <Wire.h> // I2C Arduinoライブラリ
#define Magnetometer_mX0 0x03
#define Magnetometer_mX1 0x04
#define Magnetometer_mZ0 0x05
#define Magnetometer_mZ1 0x06
#define Magnetometer_mY0 0x07
#define Magnetometer_mY1 0x08
int mX0, mX1, mX_out;
int mY0, mY1, mY_out;
int mZ0, mZ1, mZ_out;
float Xm,Ym,Zm;
#define Magnetometer 0x1E // HMC5883のI2C 7ビットアドレス
void setup(){
// シリアルとI2C通信を初期化
Serial.begin(9600);
Wire.begin();
delay(100);
Wire.beginTransmission(Magnetometer);
Wire.write(0x02); // モードレジスタを選択
Wire.write(0x00); // 連続測定モード
Wire.endTransmission();
}
void loop(){
//---- X軸
Wire.beginTransmission(Magnetometer); // デバイスへ伝送
Wire.write(Magnetometer_mX1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1) {
mX0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // デバイスへ伝送
Wire.write(Magnetometer_mX0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1) {
mX1 = Wire.read();
}
//---- Y軸
Wire.beginTransmission(Magnetometer); // デバイスへ伝送
Wire.write(Magnetometer_mY1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1) {
mY0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // デバイスへ伝送
Wire.write(Magnetometer_mY0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1) {
mY1 = Wire.read();
}
//---- Z軸
Wire.beginTransmission(Magnetometer); // デバイスへ伝送
Wire.write(Magnetometer_mZ1);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1) {
mZ0 = Wire.read();
}
Wire.beginTransmission(Magnetometer); // デバイスへ伝送
Wire.write(Magnetometer_mZ0);
Wire.endTransmission();
Wire.requestFrom(Magnetometer,1);
if(Wire.available()<=1) {
mZ1 = Wire.read();
}
//---- X軸
mX1=mX1<<8;
mX_out =mX0+mX1; // 生データ
// データシートより: 0.92 mG/digit
Xm = mX_out*0.00092; // ガウス単位
//* 地球磁場は0.25〜0.65ガウスの範囲なので、これらの値がおおよそ得られるはずです。
//---- Y軸
mY1=mY1<<8;
mY_out =mY0+mY1;
Ym = mY_out*0.00092;
//---- Z軸
mZ1=mZ1<<8;
mZ_out =mZ0+mZ1;
Zm = mZ_out*0.00092;
// 各軸の値を出力
Serial.print("x: ");
Serial.print(Xm);
Serial.print(" y: ");
Serial.print(Ym);
Serial.print(" z: ");
Serial.println(Zm);
delay(50);
}コード言語: Arduino (arduino)
製造プロセス
- Arduino NanoとMPU-6050で作るリアルタイムジャイロスコープゲーム
- NeoPixelリングとArduinoで作るインタラクティブジャイロスコープLEDディスプレイ
- K30 CO2センサで室内空気質をリアルタイム監視する
- 1SheeldとArduinoで盲ろう者のコミュニケーションを支援する
- スマート貯金箱を作ろう:Arduino Nanoでコインアクセプターを制御
- ArduinoでBluetooth経由でLEDを制御 – シンプルDIYガイド
- DFRobot静電容量式指紋センサーをArduino/ESP8266で使う – 登録・認証・削除の完全ガイド
- Arduino MegaとNextion HMIで作る高機能ウェザーステーション
- Arduinoベースのヌンチャク操作6自由度ロボットアームキット
- 磁力計とは? 基礎、原理、実用アプリケーション
- 「信頼性のインターネット」におけるデータ活用:効果的管理のための戦略