9軸センサMPU-9250を使ってみる(その1)

#はじめに
9軸センサMPU-9250(GROVE-IMU 9DOFv2.0)を使用し、加速度、角速度、地磁気データを取得してみます。また、データのばらつきや、データ取得にかかる時間など基礎的な評価を行います。

#用意するもの
###ハードウェア
必要なハードウェアは下記の通りです。

| No. | 項目 | 型式 | 備考 |
|:-:|:-:|:-:|:-:|
|1|マイコン|Arduino Uno|-|
|2|9軸センサ|[GROVE-IMU 9DOF v2.0](https://www.switch-science.com/catalog/2746/)|MPU-9250(3軸加速度+3軸角速度+3軸地磁気)|
|3|Groveシールド|[GROVE-ベースシールド](https://www.switch-science.com/catalog/1293/)|-|
|4|Groveケーブル|[GROVE - 4ピンケーブル 50cm](https://www.switch-science.com/catalog/797/)|-|

外観はこんな感じです。
![IMG_1611.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626968/c40e986d-1298-db4e-5db7-88d23f64e1e5.jpeg)



###ソフトウェア
　[seeed社のページ](https://wiki.seeedstudio.com/Grove-IMU_9DOF_v2.0/)を参考にして9軸センサを使用するためのライブラリをインストールします。ライブラリをインストールすると４つのサンプルソフト「IMU_9DOF_Demo_Compass_Calibrated」、「MPU6050_DMP6」、「MPU6050_raw」、「MPU9150_raw」を参照できるようになります。

#プログラム
　プログラムはサンプルの「MPU9150_raw.ino」をベースにしました。センサの設定値読み出しなどを追加しています。プログラムを以下に示します。


```ruby:sample.ino

// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#include "Wire.h"

// I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files
// for both classes must be in the include path of your project
#include "I2Cdev.h"
#include "MPU6050.h"

// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 accelgyro;

int16_t ax, ay, az;
int16_t gx, gy, gz;
int16_t mx, my, mz;

#define LED_PIN 13
bool blinkState = false;


//index用
unsigned long indexNum = 0;
//処理時間測定用
unsigned long startTime;
unsigned long endTime;

void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    Wire.begin();

    // initialize serial communication
    // (38400 chosen because it works as well at 8MHz as it does at 16MHz, but
    // it's really up to you depending on your project)
    Serial.begin(38400);

    // initialize device
    Serial.println("Initializing I2C devices...");
    accelgyro.initialize();

    // verify connection
    Serial.println("Testing device connections...");
    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

    // configure Arduino LED for
    pinMode(LED_PIN, OUTPUT);


    
    Serial.println("--------low-pass filter--------");
    Serial.print("Get digital low-pass filter configuration:");
    Serial.println(accelgyro.getDLPFMode());
    
    Serial.println("--------Accelerometer--------");
    Serial.print("Get full-scale accelerometer range:");
    Serial.println(accelgyro.getFullScaleAccelRange());
    Serial.print("Get the high-pass filter configuration:");
    Serial.println(accelgyro.getDHPFMode());

    Serial.println("--------Gyroscope--------");
    Serial.print("Get gyroscope output rate divider:");
    Serial.println(accelgyro.getRate());
    Serial.print("Get full-scale gyroscope range:");
    Serial.println(accelgyro.getFullScaleGyroRange());

    Serial.println("--------I2C--------");
    Serial.print("Get I2C master clock speed:");
    Serial.println(accelgyro.getMasterClockSpeed());

    
    //readyを入力したらスタート
    Serial.println("");
    Serial.println(F("If you are ready ,please sent a command data 'ready' to start sample and calibrate."));
    while (!Serial.find("ready"));
    Serial.println("Start");
    
}

void loop() {
  
    startTime = micros();//データ取得前の時刻を記録
    // read raw accel/gyro measurements from device
    accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);
    endTime = micros();//データ取得後の時刻を記録
    
    // these methods (and a few others) are also available
    //accelgyro.getAcceleration(&ax, &ay, &az);
    //accelgyro.getRotation(&gx, &gy, &gz);

    // display tab-separated accel/gyro x/y/z values
    Serial.print(indexNum); Serial.print(",");
    Serial.print(ax); Serial.print(",");
    Serial.print(ay); Serial.print(",");
    Serial.print(az); Serial.print(",");
    Serial.print(gx); Serial.print(",");
    Serial.print(gy); Serial.print(",");
    Serial.print(gz); Serial.print(",");
    Serial.print(mx); Serial.print(",");
    Serial.print(my); Serial.print(",");
    Serial.print(mz); Serial.print(",");
    Serial.println(micros() - startTime);

    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);

    indexNum++;
}

```

　プログラムをArduinoに書き込んだら、Arduinno IDEのシリアルモニタを立ち上げます。シリアルモニタにはMPU9150の設定情報が表示されます。設定情報を確認したらシリアルモニタ上部にあるデータ送信欄に"ready"と入力し、データ受信を開始します。受信したデータはテイストエディタなどに貼り付けデータファイルとして保存します。



#予備実験

###ばらつき評価に用いるデータ点数
　データのばらつき(標準偏差)を測定するにあたり、サンプル数がどれくらい必要か予備実験を行いました。参考にした「トランジスタ技術,20191年7月号P44,加速度センサとジャイロセンサのばらつき測定」では2000サンプルのデータからばらつきを算出しています。2000サンプルで十分か？多すぎか？確認するために、サンプル数と標準偏差の関係をみてみました。結果を以下に示します。グラフは横軸がサンプル数、縦軸が標準偏差です。グラフより標準偏差を算出するには数百サンプルでは不十分で数千サンプル必要であることがわかります。以降の評価は2000サンプルで行います。(※データ処理はmatlabを使用しました)

![samp_vs_sigma.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626968/47464569-694a-a445-4c55-107148a1f1cd.png)

###データ取得にかかる時間
　加速度、角速度、地磁気のデータはプログラム中の「accelgyro.getMotion9(&ax, &ay, &az, &gx, &gy, &gz, &mx, &my, &mz);」で取得しています。この行の実行時間を測定したところ、平均で25.166msでした。(思ったより時間がかかっている)

#測定結果
###加速度、角速度、地磁気データ
加速度、角速度、地磁気の測定データを以下に示します。加速度Z軸の出力がほぼ1Gであることが確認できます。

![samp_vs_agmdata.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626968/d8476c63-8ea0-99de-f8e5-5248468a9612.png)

###データのばらつき
加速度、角速度、地磁気測定データ(2000点)のヒストグラムを以下に示します。
![data_vs_frequency.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626968/6fb02f26-d1f6-4ef2-a429-92d2f1ccd815.png)

具体的な平均値($\mu$)、標準偏差($\sigma$)の値は以下の通りです。

|項目||X|Y|Z|
|:-:|:-:|:-:|:-:|:-:|
|加速度|平均$\mu$       |0.021G|0.032G|1.060G|
|     |標準偏差$\sigma$ |0.002G|0.002G|0.003G|
|角速度|平均$\mu$       |0.418deg/s|0.742deg/s|0.531deg/s|
|     |標準偏差$\sigma$ |0.141deg/s|0.129deg/s|0.122deg/s|
|地磁気|平均$\mu$       |88.612$\mu$T|36.909$\mu$T|-56.490$\mu$T|
|     |標準偏差$\sigma$ |1.81$\mu$T|1.83$\mu$T|1.75$\mu$T|


#今後
　今回の結果をフィルタの設計などに生かしていきたい！

#免責注意事項
この記事に従って発生した如何なる損害も筆者は負うことができません。