傾斜計の校正 (v0.1) > setXAccelOffset()で校正できない

```txt:動作環境
ESP-WROOM-02
MPU-9250 https://strawberry-linux.com/catalog/items?code=12250
ライブラリ https://github.com/jrowberg/i2cdevlib/
```

http://qiita.com/7of9/items/906f92cbb9a4beb97914
に記載のコードで自動校正しようとしたがうまくいかない。

単純にゼロオフセットの時の測定値を出力するコードにした。
割込みを使うように変更もした。

## code v0.1

[v0.1 @ github](https://github.com/yasokada/esp8266_160703_calibMPU9250/releases/tag/v0.1)


```c:esp8266_160703_calibMPU9250.ino
/*
 * v0.1 2016 Jul. 03
 *   - remove Calibration()
 *   - use Interrupt
 *   - import for MPU-9250
 *   - based on the code at
 *   http://www.i2cdevlib.com/forums/topic/96-arduino-sketch-to-automatically-calculate-mpu6050-offsets/
 */

#include "I2Cdev.h"
#include "MPU9150_9Axis_MotionApps41.h"
#include "Wire.h"
#include <ESP8266WiFi.h>
MPU9150 accelgyro;

#define INTERRUPT_PIN 14  // ESP8266

int buffersize = 1000;     //Amount of readings used to average, make it higher to get more precision but sketch will be slower  (default:1000)
int16_t ax, ay, az,gx, gy, gz;

int mean_ax, mean_ay, mean_az;
int mean_gx, mean_gy, mean_gz;
int state=0;
int ax_offset, ay_offset, az_offset;
int gx_offset, gy_offset, gz_offset;

uint16_t packetSize;
uint16_t fifoCount;

void meansensors();
void showData();

volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}

void setup() {
  WiFi.disconnect();
  Wire.begin();
  Wire.setClock(400000L); // 400kHz
  Serial.begin(115200);

  accelgyro.initialize();
  pinMode(INTERRUPT_PIN, INPUT);

  while (Serial.available() && Serial.read()); // empty buffer
  while (!Serial.available()){
    Serial.println(F("Send any character to start sketch.\n"));
    delay(1500);
  }                
  while (Serial.available() && Serial.read()); // empty buffer again

  packetSize = accelgyro.dmpGetFIFOPacketSize();

  Serial.println("\nYour MPU-9250 should be placed in horizontal position, with package letters facing up. \nDon't touch it until you see a finish message.\n");
  delay(3000);
//  // verify connection
//  Serial.println(accelgyro.testConnection() ? "MPU9250 connection successful" : "MPU9250 connection failed");
//  delay(1000);
}

void loop() {
  if (state==0){
    Serial.println("\nReading sensors for first time...");
    meansensors();
    state++;
    delay(1000);
  }

  if (state==1) {
    Serial.println("\nMeasuring...");
    showData(); 
    state++;
    delay(1000);
  }

  if (state==2) {
    meansensors();
    Serial.print("\nSensor readings with zero offsets:\t");
    Serial.print(mean_ax); 
    Serial.print("\t");
    Serial.print(mean_ay); 
    Serial.print("\t");
    Serial.print(mean_az); 
    Serial.print("\t");
    Serial.print(mean_gx); 
    Serial.print("\t");
    Serial.print(mean_gy); 
    Serial.print("\t");
    Serial.println(mean_gz);
    Serial.println("\nData is printed as: acelX acelY acelZ giroX giroY giroZ");
    Serial.println("Check that your sensor readings are close to 0 0 16384 0 0 0");
    state++;
  }
}

///////////////////////////////////   FUNCTIONS   ////////////////////////////////////
void meansensors(){
  long i=0;
  int buff_ax=0;
  int buff_ay=0;
  int buff_az=0;
  int buff_gx=0;
  int buff_gy=0;
  int buff_gz=0;

  while (i < (buffersize + 101) ){
    fifoCount = accelgyro.getFIFOCount();
    if (!mpuInterrupt && fifoCount < packetSize) {
      continue;
    }
    mpuInterrupt = false;
    
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
        
    if ( (i > 100) && i <= (buffersize + 100) ){ // First 100 discarded
      buff_ax = buff_ax + ax;
      buff_ay = buff_ay + ay;
      buff_az = buff_az + az;
      buff_gx = buff_gx + gx;
      buff_gy = buff_gy + gy;
      buff_gz = buff_gz + gz;
    }
    if ( i == (buffersize + 100) ) {
      mean_ax = buff_ax / buffersize;
      mean_ay = buff_ay / buffersize;
      mean_az = buff_az / buffersize;
      mean_gx = buff_gx / buffersize;
      mean_gy = buff_gy / buffersize;
      mean_gz = buff_gz / buffersize;
    }
    i++;
  }
}

void showData()
{
  accelgyro.setXAccelOffset(0);
  accelgyro.setYAccelOffset(0);
  accelgyro.setZAccelOffset(0);
  accelgyro.setXGyroOffset(0);
  accelgyro.setYGyroOffset(0);
  accelgyro.setZGyroOffset(0);

  for(int loop = 0; loop < 10; loop++) {
    meansensors();
    Serial.print(" ,mean_ax:");
    Serial.print(mean_ax);
    Serial.print(" ,mean_ay:");
    Serial.print(mean_ay);
    Serial.print(" ,mean_az:");
    Serial.print(mean_az);
    //
    Serial.print(" ,mean_gx:");
    Serial.print(mean_gx);
    Serial.print(" ,mean_gy:");
    Serial.print(mean_gy);
    Serial.print(" ,mean_gz:");
    Serial.print(mean_gz);
    //
    Serial.println();
  }

  accelgyro.setXAccelOffset(-81/8);
  accelgyro.setYAccelOffset(-264/8);
  accelgyro.setZAccelOffset((16384-16308)/8);
  accelgyro.setXGyroOffset(0);
  accelgyro.setYGyroOffset(0);
  accelgyro.setZGyroOffset(0);

  for(int loop = 0; loop < 10; loop++) {
    meansensors();
    Serial.print(" ,mean_ax:");
    Serial.print(mean_ax);
    Serial.print(" ,mean_ay:");
    Serial.print(mean_ay);
    Serial.print(" ,mean_az:");
    Serial.print(mean_az);
    //
    Serial.print(" ,mean_gx:");
    Serial.print(mean_gx);
    Serial.print(" ,mean_gy:");
    Serial.print(mean_gy);
    Serial.print(" ,mean_gz:");
    Serial.print(mean_gz);
    //
    Serial.println();
  } 
}
```

showData()において、最初の10回はゼロオフセットの値を表示、次の10回は(加速度のみ)オフセット値を適用した値とした。

## 結果

```
Send any character to start sketch.


Your MPU-9250 should be placed in horizontal position, with package letters facing up. 
Don't touch it until you see a finish message.


Reading sensors for first time...

Measuring...
 ,mean_ax:98 ,mean_ay:267 ,mean_az:16311 ,mean_gx:-67 ,mean_gy:56 ,mean_gz:-286
 ,mean_ax:105 ,mean_ay:270 ,mean_az:16318 ,mean_gx:-67 ,mean_gy:57 ,mean_gz:-286
 ,mean_ax:105 ,mean_ay:257 ,mean_az:16319 ,mean_gx:-66 ,mean_gy:56 ,mean_gz:-287
 ,mean_ax:100 ,mean_ay:260 ,mean_az:16321 ,mean_gx:-66 ,mean_gy:58 ,mean_gz:-286
 ,mean_ax:99 ,mean_ay:257 ,mean_az:16314 ,mean_gx:-64 ,mean_gy:57 ,mean_gz:-286
 ,mean_ax:104 ,mean_ay:258 ,mean_az:16318 ,mean_gx:-66 ,mean_gy:56 ,mean_gz:-287
 ,mean_ax:102 ,mean_ay:263 ,mean_az:16317 ,mean_gx:-65 ,mean_gy:58 ,mean_gz:-285
 ,mean_ax:103 ,mean_ay:262 ,mean_az:16324 ,mean_gx:-65 ,mean_gy:56 ,mean_gz:-284
 ,mean_ax:103 ,mean_ay:260 ,mean_az:16315 ,mean_gx:-69 ,mean_gy:58 ,mean_gz:-284
 ,mean_ax:104 ,mean_ay:262 ,mean_az:16318 ,mean_gx:-66 ,mean_gy:56 ,mean_gz:-285
 ,mean_ax:103 ,mean_ay:260 ,mean_az:16307 ,mean_gx:-65 ,mean_gy:-1108 ,mean_gz:-219
 ,mean_ax:101 ,mean_ay:261 ,mean_az:16311 ,mean_gx:-66 ,mean_gy:-1107 ,mean_gz:-222
 ,mean_ax:100 ,mean_ay:262 ,mean_az:16316 ,mean_gx:-66 ,mean_gy:-1106 ,mean_gz:-219
 ,mean_ax:101 ,mean_ay:261 ,mean_az:16313 ,mean_gx:-67 ,mean_gy:-1104 ,mean_gz:-222
 ,mean_ax:100 ,mean_ay:261 ,mean_az:16312 ,mean_gx:-68 ,mean_gy:-1103 ,mean_gz:-220
 ,mean_ax:104 ,mean_ay:260 ,mean_az:16312 ,mean_gx:-66 ,mean_gy:-1103 ,mean_gz:-221
 ,mean_ax:102 ,mean_ay:259 ,mean_az:16312 ,mean_gx:-68 ,mean_gy:-1103 ,mean_gz:-221
 ,mean_ax:101 ,mean_ay:264 ,mean_az:16309 ,mean_gx:-66 ,mean_gy:-1102 ,mean_gz:-220
 ,mean_ax:101 ,mean_ay:266 ,mean_az:16314 ,mean_gx:-66 ,mean_gy:-1101 ,mean_gz:-220
 ,mean_ax:102 ,mean_ay:260 ,mean_az:16318 ,mean_gx:-67 ,mean_gy:-1101 ,mean_gz:-221

Sensor readings with zero offsets:	106	260	16310	-66	-1101	-220

Data is printed as: acelX acelY acelZ giroX giroY giroZ
Check that your sensor readings are close to 0 0 16384 0 0 0
```

最初の10回のmean_axの値と、オフセットを適用した次の10回のmean_axｎ値がほぼ同じ。
代わりにmean_gy, mean_gz(ジャイロのY,Z)の値が変更されている。

setXAccelOffset(),setYAccelOffset(),setZAccelOffset()にバグがあるのだろうか。
MPU-6050でなくMPU-9150で使う場合の使い方が間違っているのかもしれない。

setXGyroOffset(),setYGyroOffset(),setZGyroOffset()の方はきちんと校正できる。

## ライブラリの中身

### setXAccelOffset()とsetXGyroOffset()

setXAccelOffset()やsetXGyroOffset()はic2devライブラリのMPU9150.cppにて宣言されている。

```MPU9150.cpp
int16_t MPU9150::getXAccelOffset() {
    I2Cdev::readBytes(devAddr, MPU9150_RA_XA_OFFS_H, 2, buffer);
    return (((int16_t)buffer[0]) << 8) | buffer[1];
}
void MPU9150::setXAccelOffset(int16_t offset) {
    I2Cdev::writeWord(devAddr, MPU9150_RA_XA_OFFS_H, offset);
}
```

```MPU9150.cpp
int16_t MPU9150::getXGyroOffset() {
    I2Cdev::readBytes(devAddr, MPU9150_RA_XG_OFFS_USRH, 2, buffer);
    return (((int16_t)buffer[0]) << 8) | buffer[1];
}
void MPU9150::setXGyroOffset(int16_t offset) {
    I2Cdev::writeWord(devAddr, MPU9150_RA_XG_OFFS_USRH, offset);
}
```

成功するsetXGyroOffset()と失敗しているgetXAccelOffset()ともにI2Cdev::writeWord()にて書込みしている点に違いはない。


## レジスタの定義

### MPU9150.hの定義

MPU9150.hにおいて以下のようにレジスタ定義がされている。

```
#define MPU9150_RA_XA_OFFS_H        0x06 //[15:0] XA_OFFS
#define MPU9150_RA_XA_OFFS_L_TC     0x07

#define MPU9150_RA_XG_OFFS_USRH     0x13 //[15:0] XG_OFFS_USR
#define MPU9150_RA_XG_OFFS_USRL     0x14
```

そして、某処で見たMPU-9250用のRegister Mapではアドレスは以下となっている。

- 0x77: XA_OFFSET_H [14:7]
- 0x78: XA_OFFSET_L [6:0]
- 0x13: XG_OFFS_USR_H [15:8]
- 0x14: XG_OFFS_USR_L [7:0]

MPU9150.hの定義しているレジスタアドレス(XA_OFFSET_H: 0x06で定義)が実際のRegister Mapの値(0x77など)に一致していないということだろうか。

試しに 上記の0x77などの値をMPU9150_RA_XA_OFFS_Hなどに定義してみたが、その場合は加速度の値が32768や-32768になってしまった。

以下では0x77を使っているようではある。
https://developer.mbed.org/questions/6482/Configuring-MPU9250-magnometer/

### kriswinerさんの定義

https://github.com/kriswiner/MPU-9250/blob/master/STM32F401/MPU9250.h
において以下のように定義はしている。

```c
#define XA_OFFSET_H      0x77
#define XA_OFFSET_L      0x78
#define YA_OFFSET_H      0x7A
#define YA_OFFSET_L      0x7B
#define ZA_OFFSET_H      0x7D
#define ZA_OFFSET_L      0x7E
```

その定義にもとづき読込みをしている。

```c
readBytes(MPU9250_ADDRESS, XA_OFFSET_H, 2, &data[0]); // Read factory accelerometer trim values
accel_bias_reg[0] = (int16_t) ((int16_t)data[0] << 8) | data[1];
```

しかしながら、書込みはしていない。以下のコメントがついている。

```c
// Apparently this is not working for the acceleration biases in the MPU-9250
// Are we handling the temperature correction bit properly?
// Push accelerometer biases to hardware registers
/*  writeByte(MPU9250_ADDRESS, XA_OFFSET_H, data[0]);
  writeByte(MPU9250_ADDRESS, XA_OFFSET_L, data[1]);
  writeByte(MPU9250_ADDRESS, YA_OFFSET_H, data[2]);
  writeByte(MPU9250_ADDRESS, YA_OFFSET_L, data[3]);
  writeByte(MPU9250_ADDRESS, ZA_OFFSET_H, data[4]);
  writeByte(MPU9250_ADDRESS, ZA_OFFSET_L, data[5]);
*/
```

レジスタ書込み失敗することに対して、プログラム自身で引き算をしているようだ (manual subtraction)。

```c
// Output scaled accelerometer biases for manual subtraction in the main program
   dest2[0] = (float)accel_bias[0]/(float)accelsensitivity; 
   dest2[1] = (float)accel_bias[1]/(float)accelsensitivity;
   dest2[2] = (float)accel_bias[2]/(float)accelsensitivity;
```

## 補正方法
(追記 2016/07/05)

以下に記載した。
http://qiita.com/7of9/items/d3c765912b435815cfdf