M5Stack のIMUでProcessingのモデルをぐりぐりしてみた。

f:id:manpukukoji:20200928124118j:plain
スイッチサイエンスのサイトにM5Stackの3DモデルをM5の動きに合わせて動かすサイトがあって、
pages.switch-science.com

面白いなぁ~と思い、自分もグリグリやってみたいなぁと思ったんですが、あいにく、IMUのチップがサイトのものと違っていました。
自分のは、IMU6886+BMM150のようで、サイトのスケッチそのままでは、動かないみたいです。
幸い、BMM150のサンプルプログラムも出ていたので、これと組み合わせて同じことをやってみました。
なんとか、同じようにできることはできたのですが、どうも、ジャイロの値が安定しない。
キャリブレーションもやってみたのだけど、いまいち。
何か、プログラムが悪いのかなぁ~? あるいは、こんなもん? またまた、はずれセンサーを引いちゃった?
まぁ、今後の参考までに、とりあえず、スケッチを置いておこう。
M5側のスケッチを置き換えるだけで、後は、Processingなどの部分は、上記のサイトと全く同じでグリグリできるようになると思います。
ちなみに、表示部分は、自分のM5のTFTがある日突然オシャカになってしまったので、ちゃんと確認できてませんので、なんか、変かも…。

#define M5STACK_MPU6886 

#include <Arduino.h>
#include <Wire.h>
#include "Preferences.h"
#include "M5Stack.h"
#include "math.h"
#include "bmm150.h"
#include "bmm150_defs.h"
#include <MadgwickAHRS.h>
#include "BluetoothSerial.h"

Preferences prefs;
BluetoothSerial SerialBT;
Madgwick filter;
unsigned long microsPerReading, microsPrevious;

struct bmm150_dev dev;
bmm150_mag_data mag_offset;
bmm150_mag_data mag_max;
bmm150_mag_data mag_min;
TFT_eSprite img = TFT_eSprite(&M5.Lcd);

float accX = 0.0F, accY = 0.0F, accZ = 0.0F;
float gyroX = 0.0F, gyroY = 0.0F, gyroZ = 0.0F;
float pitch = 0.0F, roll = 0.0F, yaw = 0.0F;

float accOffset[3];
float gyroOffset[3];

#define INTERVAL  5
float rolls[INTERVAL], pitchs[INTERVAL], yaws[INTERVAL];
int ma_idx = 0; // moving average index


int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *read_data, uint16_t len)
{
    if(M5.I2C.readBytes(dev_id, reg_addr, len, read_data))
    {
        return BMM150_OK;
    }
    else
    {
        return BMM150_E_DEV_NOT_FOUND;
    }
}

int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *read_data, uint16_t len)
{
    if(M5.I2C.writeBytes(dev_id, reg_addr, read_data, len))
    {
        return BMM150_OK;
    }
    else
    {
        return BMM150_E_DEV_NOT_FOUND;
    }
}

int8_t bmm150_initialization()
{
    int8_t rslt = BMM150_OK;

    /* Sensor interface over SPI with native chip select line */
    dev.dev_id = 0x10;
    dev.intf = BMM150_I2C_INTF;
    dev.read = i2c_read;
    dev.write = i2c_write;
    dev.delay_ms = delay;

    /* make sure max < mag data first  */
    mag_max.x = -2000;
    mag_max.y = -2000;
    mag_max.z = -2000;

    /* make sure min > mag data first  */
    mag_min.x = 2000;
    mag_min.y = 2000;
    mag_min.z = 2000;

    rslt = bmm150_init(&dev);
    dev.settings.pwr_mode = BMM150_NORMAL_MODE;
    rslt |= bmm150_set_op_mode(&dev);
    dev.settings.preset_mode = BMM150_PRESETMODE_ENHANCED;
    rslt |= bmm150_set_presetmode(&dev);
    return rslt;
}

void bmm150_offset_save()
{
    prefs.begin("bmm150", false);
    prefs.putBytes("offset", (uint8_t *)&mag_offset, sizeof(bmm150_mag_data));
    prefs.end();
}

void bmm150_offset_load()
{
    if(prefs.begin("bmm150", true))
    {
        prefs.getBytes("offset", (uint8_t *)&mag_offset, sizeof(bmm150_mag_data));
        prefs.end();
        Serial.printf("bmm150 load offset finish.... \r\n");
    }
    else
    {
        Serial.printf("bmm150 load offset failed.... \r\n");
    }
}

void setup() 
{
    M5.begin(true, false, true, false);
    M5.Power.begin();
    
    M5.IMU.Init();
    Serial.begin(115200);
    Wire.begin(21, 22, 400000);
    SerialBT.begin("M5Stack");

    filter.begin(10); // 10Hz filterを初期化する
    microsPerReading = 1000000 / 10;
    microsPrevious = micros();
    
    img.setColorDepth(1);
    img.setTextColor(TFT_WHITE);
    img.createSprite(320, 240);
    img.setBitmapColor(TFT_WHITE, 0);

    if(bmm150_initialization() != BMM150_OK)
    {
        img.fillSprite(0);
        img.drawCentreString("BMM150 init failed", 160, 110, 4);
        img.pushSprite(0, 0);
        for(;;)
        {
            delay(100);
        }
    }

    bmm150_offset_load();

    for (int i = 0; i<INTERVAL; i++) {
      rolls[i] = 0;
      pitchs[i] = 0;
      yaws[i] = 0;
    }

}

void bmm150_calibrate(uint32_t calibrate_time)
{

        img.fillSprite(0);
        img.drawCentreString("Flip + rotate core calibration", 160, 110, 4);
        img.pushSprite(0, 0);

    uint32_t calibrate_timeout = 0;

    calibrate_timeout = millis() + calibrate_time;
    Serial.printf("Go calibrate, use %d ms \r\n", calibrate_time);
    Serial.printf("running ...");

    while (calibrate_timeout > millis())
    {
        bmm150_read_mag_data(&dev);
        if(dev.data.x)
        {
            mag_min.x = (dev.data.x < mag_min.x) ? dev.data.x : mag_min.x;
            mag_max.x = (dev.data.x > mag_max.x) ? dev.data.x : mag_max.x;
        }

        if(dev.data.y)
        {
            mag_max.y = (dev.data.y > mag_max.y) ? dev.data.y : mag_max.y;
            mag_min.y = (dev.data.y < mag_min.y) ? dev.data.y : mag_min.y;
        }

        if(dev.data.z)
        {
            mag_min.z = (dev.data.z < mag_min.z) ? dev.data.z : mag_min.z;
            mag_max.z = (dev.data.z > mag_max.z) ? dev.data.z : mag_max.z;
        }
        delay(100);
    }

    mag_offset.x = (mag_max.x + mag_min.x) / 2;
    mag_offset.y = (mag_max.y + mag_min.y) / 2;
    mag_offset.z = (mag_max.z + mag_min.z) / 2;
    bmm150_offset_save();

    Serial.printf("\n calibrate finish ... \r\n");
    Serial.printf("mag_max.x: %.2f x_min: %.2f \t", mag_max.x, mag_min.x);
    Serial.printf("y_max: %.2f y_min: %.2f \t", mag_max.y, mag_min.y);
    Serial.printf("z_max: %.2f z_min: %.2f \r\n", mag_max.z, mag_min.z);
}

void calibrate6886(){
  M5.Lcd.println("calibrate6886");
  float gyroSum[3];
  float accSum[3];
  int counter = 500;
  for(int i = 0; i < counter; i++){
    M5.IMU.getGyroData(&gyroX,&gyroY,&gyroZ);
    M5.IMU.getAccelData(&accX,&accY,&accZ);
    gyroSum[0] += gyroX;
    gyroSum[1] += gyroY;
    gyroSum[2] += gyroZ;
    accSum[0] += accX;
    accSum[1] += accY;
    accSum[2] += accZ;
    delay(2);
  }
  gyroOffset[0] = gyroSum[0]/counter;
  gyroOffset[1] = gyroSum[1]/counter;
  gyroOffset[2] = gyroSum[2]/counter;
  accOffset[0] = accSum[0]/counter;
  accOffset[1] = accSum[1]/counter;
  accOffset[2] = (accSum[2]/counter);
}



void loop() 
{
  if (micros() - microsPrevious >= microsPerReading) {
// ----------------- Accl / Gyro -------------------
    M5.IMU.getGyroData(&gyroX,&gyroY,&gyroZ);
    M5.IMU.getAccelData(&accX,&accY,&accZ);
    //M5.IMU.getAhrsData(&pitch,&roll,&yaw);

    gyroX -= gyroOffset[0];
    gyroY -= gyroOffset[1];
    gyroZ -= gyroOffset[2];
  
    accX -= accOffset[0];
    accY -= accOffset[1];
    accZ -= accOffset[2]-1.0;
  
    filter.updateIMU(gyroX, gyroY, gyroZ, accX, accY, accZ );
        rolls[ma_idx % INTERVAL] = filter.getRoll();
        pitchs[ma_idx % INTERVAL] = filter.getPitch();
        yaws[ma_idx % INTERVAL] = filter.getYaw();
        ma_idx++;
        if((ma_idx % INTERVAL) == 0) ma_idx = 0;

        float roll = 0.0;
        float pitch = 0.0;
        float yaw = 0.0;
        for (int i = 0; i < INTERVAL; i++) {
            roll += rolls[i];
            pitch += pitchs[i];
            yaw += yaws[i];
        }
        roll /= INTERVAL;
        pitch /= INTERVAL;
        yaw /= INTERVAL;

// ----------------- magnet ------------------------  
      char text_string[100];
      M5.update();
      bmm150_read_mag_data(&dev);
      float head_dir = atan2(dev.data.x -  mag_offset.x, dev.data.y - mag_offset.y) * 180.0 / M_PI;

      
        img.fillSprite(0);
    
        sprintf(text_string, "Gyro: %.2f  %.2f  %.2f", gyroX, gyroY, gyroZ);
        img.drawString(text_string, 10, 20, 4);
        sprintf(text_string, "Accel: %.2f  %.2f  %.2f", accX, accY, accZ);
        img.drawString(text_string, 10, 50, 4);
        sprintf(text_string, "P/R/Y: %.2f  %.2f  %.2f",pitch, roll, yaw);
        img.drawString(text_string, 10, 80, 4); 
        Serial.printf("%6.2f, %6.2f, %6.2f\r\n", roll, pitch, yaw);
        SerialBT.printf("%6.2f, %6.2f, %6.2f\r\n", roll, pitch, yaw);
        sprintf(text_string, "HEAD Angle: %.2f", head_dir);
        img.drawString(text_string, 10, 110, 4);
    
        img.drawCentreString("Press BtnA enter calibrate", 160, 150, 4);
        img.pushSprite(0, 0);
      
      if(M5.BtnA.wasPressed())
      {
          img.fillSprite(0);
          img.drawCentreString("Keep Stable - Gyro Accel calibration", 160, 110, 4);
          img.pushSprite(0, 0);
          calibrate6886();
          bmm150_calibrate(10000);
      }

      microsPrevious = microsPrevious + microsPerReading;
  }  
}