M5StickCで角度

M5StickCで角度を取得し,振動デバイスに出力する。

// M5StickC 初期型(MPU6886) Pitch / Roll 取得デモ
// 50Hz更新,相補フィルタ(α=0.98)
// 画面表示 + シリアルCSV出力(Pitch,Roll)

#include <M5StickC.h>

const float alpha = 0.98f;      // 相補フィルタ係数(0.95〜0.99で調整)
const uint32_t dt_ms = 20;      // サンプリング周期 20ms → 50Hz
const float dt = dt_ms / 1000.0f;

float pitch = 0.0f;             // 融合後の角度[deg]
float roll  = 0.0f;

void setup() {
  M5.begin();
  M5.IMU.Init();                // MPU6886 初期化
  M5.Lcd.setRotation(3);        // 横向き
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("M5StickC IMU");
  M5.Lcd.println("Pitch/Roll");

  Serial.begin(115200);
  while (!Serial) { delay(10); }

  // 初期姿勢を加速度ベースで推定して,初期値に採用
  float ax, ay, az;
  M5.IMU.getAccelData(&ax, &ay, &az);        // [g]
  float pitchAcc = atan2f(-ax, sqrtf(ay*ay + az*az)) * 180.0f / PI;
  float rollAcc  = atan2f( ay, az ) * 180.0f / PI;
  pitch = pitchAcc;
  roll  = rollAcc;

  delay(500);
}

void loop() {
  // センサ取得
  float ax, ay, az;           // 加速度[g]
  float gx, gy, gz;           // 角速度[deg/s]
  M5.IMU.getAccelData(&ax, &ay, &az);
  M5.IMU.getGyroData(&gx, &gy, &gz);

  // 加速度からの姿勢角(重力のみ仮定)
  float pitchAcc = atan2f(-ax, sqrtf(ay*ay + az*az)) * 180.0f / PI;
  float rollAcc  = atan2f( ay, az ) * 180.0f / PI;

  // ジャイロ積分(軸対応:MPU6886はおおむね gx→Roll, gy→Pitch)
  float pitchGyro = pitch + gy * dt;
  float rollGyro  = roll  + gx * dt;

  // 相補フィルタ融合
  pitch = alpha * pitchGyro + (1.0f - alpha) * pitchAcc;
  roll  = alpha * rollGyro  + (1.0f - alpha) * rollAcc;

  // 画面表示
  M5.Lcd.fillRect(0, 40, 160, 40, BLACK);
  M5.Lcd.setCursor(0, 40);
  M5.Lcd.printf("Pitch:%6.2f deg\n", pitch);
  M5.Lcd.printf("Roll :%6.2f deg\n", roll);

  // シリアルCSV(Pitch,Roll)
  Serial.print(pitch, 3);
  Serial.print(",");
  Serial.println(roll, 3);

  delay(dt_ms);
}
// M5StickC 初期型(MPU6886) Pitch / Roll 取得+Pitch>10°でG26出力
// 相補フィルタ(α=0.98) / 50Hz / ヒステリシス: ON 10°, OFF 8°

#include <M5StickC.h>

const int MOTOR_PIN = 26;          // G26 にモータードライバの入力を接続(直結は不可)
const float alpha = 0.98f;         // 相補フィルタ係数
const uint32_t dt_ms = 20;         // 50Hz
const float dt = dt_ms / 1000.0f;

const float PITCH_ON_DEG  = 10.0f; // これを超えたらON
const float PITCH_OFF_DEG = 8.0f;  // これを下回ったらOFF(ヒステリシス)

float pitch = 0.0f;
float roll  = 0.0f;
bool motorOn = false;

void setup() {
  M5.begin();
  M5.IMU.Init();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("M5StickC IMU");
  M5.Lcd.println("Pitch->G26");

  Serial.begin(115200);
  while (!Serial) delay(10);

  // モーターピン初期化
  pinMode(MOTOR_PIN, OUTPUT);
  digitalWrite(MOTOR_PIN, LOW);   // 初期はOFF

  // 初期姿勢(加速度ベース)
  float ax, ay, az;
  M5.IMU.getAccelData(&ax, &ay, &az);
  float pitchAcc = atan2f(-ax, sqrtf(ay*ay + az*az)) * 180.0f / PI;
  float rollAcc  = atan2f( ay, az ) * 180.0f / PI;
  pitch = pitchAcc;
  roll  = rollAcc;

  delay(300);
}

void loop() {
  // センサ取得
  float ax, ay, az;  // [g]
  float gx, gy, gz;  // [deg/s]
  M5.IMU.getAccelData(&ax, &ay, &az);
  M5.IMU.getGyroData(&gx, &gy, &gz);

  // 姿勢角(加速度)
  float pitchAcc = atan2f(-ax, sqrtf(ay*ay + az*az)) * 180.0f / PI;
  float rollAcc  = atan2f( ay, az ) * 180.0f / PI;

  // ジャイロ積分
  float pitchGyro = pitch + gy * dt;  // gy ≈ pitch軸
  float rollGyro  = roll  + gx * dt;  // gx ≈ roll軸

  // 相補フィルタ融合
  pitch = alpha * pitchGyro + (1.0f - alpha) * pitchAcc;
  roll  = alpha * rollGyro  + (1.0f - alpha) * rollAcc;

  // —— Pitchしきい値でG26制御(ヒステリシスあり)——
  if (!motorOn && pitch > PITCH_ON_DEG) {
    motorOn = true;
    digitalWrite(MOTOR_PIN, HIGH);
  } else if (motorOn && pitch < PITCH_OFF_DEG) {
    motorOn = false;
    digitalWrite(MOTOR_PIN, LOW);
  }

  // 表示&ログ
  M5.Lcd.fillRect(0, 40, 160, 50, BLACK);
  M5.Lcd.setCursor(0, 40);
  M5.Lcd.printf("Pitch:%6.2f deg\n", pitch);
  M5.Lcd.printf("Roll :%6.2f deg\n", roll);
  M5.Lcd.printf("MOTOR:%s    \n", motorOn ? "ON " : "OFF");

  Serial.print(pitch, 3);
  Serial.print(",");
  Serial.print(roll, 3);
  Serial.print(",");
  Serial.println(motorOn ? 1 : 0);

  delay(dt_ms);
}
// M5StickC 初期型(MPU6886) Pitch / Roll 取得+角度帯域でG26出力パターン
#include <M5StickC.h>

const int MOTOR_PIN = 26;          // モータードライバ入力(直結不可)
const float alpha = 0.98f;         // 相補フィルタ係数
const uint32_t dt_ms = 20;         // 50Hz
const float dt = dt_ms / 1000.0f;

// 角度しきい値(度):必要に応じて設定してください
float TH_A = 5.0f;
float TH_B = 10.0f;
float TH_C = 15.0f;
float TH_D = 20.0f;

// パターン定義(ONは常に100ms固定)
const uint16_t ON_MS  = 100;
const uint16_t OFF_A  = 500;  // A〜B未満
const uint16_t OFF_B  = 300;  // B〜C未満
const uint16_t OFF_C  = 100;  // C〜D未満

float pitch = 0.0f;
float roll  = 0.0f;

// -------- サブルーチン:角度に応じて出力を駆動(非ブロッキング)--------
enum Band { BAND_OFF, BAND_A, BAND_B, BAND_C, BAND_ON };
Band currentBand = BAND_OFF;
bool phaseOn = false;
unsigned long phaseStartMs = 0;

Band classifyBand(float ang) {
  if (ang < TH_A)          return BAND_OFF;
  else if (ang < TH_B)     return BAND_A;
  else if (ang < TH_C)     return BAND_B;
  else if (ang < TH_D)     return BAND_C;
  else                     return BAND_ON;
}

// 指定した角度に従って G26 を点滅/常時ON/OFF させる
void driveOutputByAngle(float angDeg) {
  Band band = classifyBand(angDeg);
  unsigned long now = millis();

  // 帯域が変わったらフェーズをリセット(各帯域は100ms ON から開始)
  if (band != currentBand) {
    currentBand = band;
    phaseOn = true;                  // まずはON
    phaseStartMs = now;
    if (band == BAND_OFF) {
      digitalWrite(MOTOR_PIN, LOW);
    } else if (band == BAND_ON) {
      digitalWrite(MOTOR_PIN, HIGH);
    } else {
      digitalWrite(MOTOR_PIN, HIGH); // 点滅帯域の初回ON
    }
  }

  // 各帯域の動作
  switch (currentBand) {
    case BAND_OFF:
      // ずっとOFF
      digitalWrite(MOTOR_PIN, LOW);
      break;

    case BAND_ON:
      // ずっとON
      digitalWrite(MOTOR_PIN, HIGH);
      break;

    case BAND_A: {
      // 100ms ON / 500ms OFF
      uint16_t offMs = OFF_A;
      if (phaseOn && (now - phaseStartMs >= ON_MS)) {
        phaseOn = false; phaseStartMs = now; digitalWrite(MOTOR_PIN, LOW);
      } else if (!phaseOn && (now - phaseStartMs >= offMs)) {
        phaseOn = true;  phaseStartMs = now; digitalWrite(MOTOR_PIN, HIGH);
      }
    } break;

    case BAND_B: {
      // 100ms ON / 300ms OFF
      uint16_t offMs = OFF_B;
      if (phaseOn && (now - phaseStartMs >= ON_MS)) {
        phaseOn = false; phaseStartMs = now; digitalWrite(MOTOR_PIN, LOW);
      } else if (!phaseOn && (now - phaseStartMs >= offMs)) {
        phaseOn = true;  phaseStartMs = now; digitalWrite(MOTOR_PIN, HIGH);
      }
    } break;

    case BAND_C: {
      // 100ms ON / 100ms OFF
      uint16_t offMs = OFF_C;
      if (phaseOn && (now - phaseStartMs >= ON_MS)) {
        phaseOn = false; phaseStartMs = now; digitalWrite(MOTOR_PIN, LOW);
      } else if (!phaseOn && (now - phaseStartMs >= offMs)) {
        phaseOn = true;  phaseStartMs = now; digitalWrite(MOTOR_PIN, HIGH);
      }
    } break;
  }
}
// -------- サブルーチンここまで --------

void setup() {
  M5.begin();
  M5.IMU.Init();
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextSize(2);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.println("M5StickC IMU");
  M5.Lcd.println("Angle->G26");

  Serial.begin(115200);
  while (!Serial) delay(10);

  pinMode(MOTOR_PIN, OUTPUT);
  digitalWrite(MOTOR_PIN, LOW);

  // 初期姿勢(加速度ベース)
  float ax, ay, az;
  M5.IMU.getAccelData(&ax, &ay, &az);
  float pitchAcc = atan2f(-ax, sqrtf(ay*ay + az*az)) * 180.0f / PI;
  float rollAcc  = atan2f( ay, az ) * 180.0f / PI;
  pitch = pitchAcc;
  roll  = rollAcc;

  delay(300);
}

void loop() {
  // センサ取得
  float ax, ay, az;  // [g]
  float gx, gy, gz;  // [deg/s]
  M5.IMU.getAccelData(&ax, &ay, &az);
  M5.IMU.getGyroData(&gx, &gy, &gz);

  // 姿勢角(加速度)
  float pitchAcc = atan2f(-ax, sqrtf(ay*ay + az*az)) * 180.0f / PI;
  float rollAcc  = atan2f( ay, az ) * 180.0f / PI;

  // ジャイロ積分
  float pitchGyro = pitch + gy * dt;  // gy ≈ pitch軸
  float rollGyro  = roll  + gx * dt;  // gx ≈ roll軸

  // 相補フィルタ融合
  pitch = alpha * pitchGyro + (1.0f - alpha) * pitchAcc;
  roll  = alpha * rollGyro  + (1.0f - alpha) * rollAcc;

  // —— ここで角度に応じた出力を実行(Pitchを使用)——
  driveOutputByAngle(pitch);
  // driveOutputByAngle(roll);  // Rollで判定したい場合はこちら

  // 画面表示&ログ
  M5.Lcd.fillRect(0, 40, 160, 60, BLACK);
  M5.Lcd.setCursor(0, 40);
  M5.Lcd.printf("Pitch:%6.2f deg\n", pitch);
  M5.Lcd.printf("Roll :%6.2f deg\n", roll);

  Serial.print(pitch, 3);
  Serial.print(",");
  Serial.print(roll, 3);
  Serial.print(",");
  Serial.println((currentBand==BAND_ON) ? 2 : (currentBand==BAND_OFF ? 0 : 1)); // 2=ON連続,1=点滅,0=OFF

  delay(dt_ms);
}

コメントを残す