본문 바로가기

카테고리 없음

(esp32) (GY-521) MPU6050 6축 센서 & 스탭모터 동기화

 

MPU6050 제프 로우버그 씨의 라이브러리를 사용해서, MPU6050_DMP6 코드를 조금 수정해서 별도로 헤더 파일(MPU6050_setup.h)로 빼서 메인 코드를 단순화 함.

그리고 스탭 모터 함수를 직접 작성해서 MyStepper.h 헤드파일로 빼서 메인코드를 단순화 시킴.

 

이 코드에서 핵심은 6축 센서 중 yaw 값을 360도 방식으로 읽어서, 그값만큼 스탭 모터를 회전시킴.

// for esp32

#include "MPU6050_setup.h" // always first in the headers

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);

#include "MyStepper.h"

#define IN1 19
#define IN2 18
#define IN3 5
#define IN4 17

MyStepper myStepper(IN1, IN2, IN3, IN4);

void setup() {
  MPU6050_setup(); // always first in the setup()
  
  lcd.init();
  lcd.backlight();
}

float prev_yaw = 0.0;
unsigned long t = 0;
const unsigned int checkInterval = 30;
const unsigned int ignoreChangeNoise = 80;  // ignored degree change. sudden a lot degree change in a very short time can be a noise.

void loop() {
  mpuDmpGet();

  Serial.print("ypr\t");
  float yaw = ypr[0] * 180 / M_PI; // 라디안을 360도 각도로 변환
  if (yaw < 0) yaw = 360 + yaw; // 마이너스 값이면 360을 더해서 양각으로 만듦
  
  //갑작스런 수치 변화를 노이즈로 간주하고 무시
  if (abs(yaw - prev_yaw) < ignoreChangeNoise) {
    prev_yaw = yaw;  // yaw값이 정상이라면 갱신
  } else if (abs(yaw - prev_yaw - 360) < ignoreChangeNoise) { // 360 라인을 넘어서는 바람에 yaw값이 커진 것이라면; 반시계 방향
    prev_yaw = yaw;  // yaw값이 정상이라면 갱신
  } else if (abs(yaw - prev_yaw + 360) < ignoreChangeNoise){ // 360 라인을 넘어서는 바람에 yaw값이 커진 것이라면; 시계 방향
    prev_yaw = yaw;
  } else {                                    
    yaw = prev_yaw;  // 비정상적인 경우에는 갱신하지 않음, 이전값 고수
  }
  Serial.print(yaw);

  Serial.print("\t");
  float pitch = ypr[1] * 180 / M_PI; // 라디안을 360도 각도로 변환
  if (pitch < 0) pitch = 360.0 + pitch;
  Serial.print(pitch);   
  
  Serial.print("\t");
  float roll = ypr[2] * 180 / M_PI; // 라디안을 360도 각도로 변환
  if (roll < 0) roll = 360.0 + roll;
  Serial.print(roll);  
  Serial.println();

  if (millis() % 1000 == 0) {
      lcd.clear();    // too often clear() makes us read the screen difficult
  }
  
  lcd.setCursor(0, 0);
  lcd.print("yaw/pitch/roll");
  lcd.setCursor(0, 1);
  lcd.print(yaw);
  lcd.setCursor(0, 2);
  lcd.print(pitch);
  lcd.setCursor(0, 3);
  lcd.print(roll);

  if (millis() - t > checkInterval) {
    t = millis();
    myStepper.dstep_coor(round(yaw));
  }
}

 

// MyStepper.h
#define dstep(x)   			dstep_coor(x) // coordinate step
#define dstep_abs(x)		dstep_coor(x)

class MyStepper {
public:
  MyStepper() {
    IN1 = 19;
    IN2 = 18;
    IN3 = 5; 
    IN4 = 17;
    pinMode(IN1, OUTPUT);
    pinMode(IN2, OUTPUT);
    pinMode(IN3, OUTPUT);
    pinMode(IN4, OUTPUT);
  }
  MyStepper(uint8_t IN1, uint8_t IN2, uint8_t IN3, uint8_t IN4) {
    this->IN1 = IN1;
    this->IN2 = IN2;
    this->IN3 = IN3;
    this->IN4 = IN4;
    pinMode(IN1, OUTPUT);
    pinMode(IN2, OUTPUT);
    pinMode(IN3, OUTPUT);
    pinMode(IN4, OUTPUT);
  }

  uint8_t IN1;
  uint8_t IN2;
  uint8_t IN3; 
  uint8_t IN4;
	
  const unsigned int stepsPerRevolution = 512;  // 8 * 512 = 4096
  const float degreePerStep = 360.0 / 512.0; // 1 step 당 이동 각도: 0.703125
  int cur_pos = 0;
  int prev_pos = 0;
  unsigned int speed = 1000; // max: 1000
  unsigned long t = 0;

  bool switching_sequence[8][4] = { {0, 0, 0, 1}, {0, 0, 1, 1}, 
      {0, 0, 1, 0}, {0, 1, 1, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 1} };
  
  void steps_8_clockwise() {
    for (int i = 7; i >= 0; ) {
      if (millis() - t >= round(1000.0/speed)) { // delay(1000/speed);
        t = millis();
        digitalWrite(IN1, switching_sequence[i][0]);
        digitalWrite(IN2, switching_sequence[i][1]);
        digitalWrite(IN3, switching_sequence[i][2]);
        digitalWrite(IN4, switching_sequence[i][3]);
        i--;
      }
    }
  }

  void steps_8_counterclockwise() {
    for (int i = 0; i < 8; ) {
      if (millis() - t >= 1000/speed) {
        t = millis();
        digitalWrite(IN1, switching_sequence[i][0]);
        digitalWrite(IN2, switching_sequence[i][1]);
        digitalWrite(IN3, switching_sequence[i][2]);
        digitalWrite(IN4, switching_sequence[i][3]);
        i++;
      }
    }
  }

  // relative dstep
  void dstep_rel(int target) {
    int steps_ = abs(round(target / degreePerStep));
    if (target > 0) {
      for (int i = 0; i < steps_; i++) {
        steps_8_clockwise();
      }
    } else if (target < 0) {
      for (int i = 0; i < steps_; i++) {
        steps_8_counterclockwise();
      }
    }
    prev_pos = cur_pos;
    cur_pos = cur_pos + target;
    if (cur_pos < 0) {
      cur_pos += 360;
    } else if (cur_pos >= 360) {
      cur_pos = cur_pos % 360;
    }
  }

  // coordinate dstep
  void dstep_coor(int target) {
    if (target < 0 || target >= 360) {   // degree must be greater than 0
      return;
    } else {
      if ( abs(target - cur_pos) > 180 ) {
        if (target - cur_pos > 0) {
          //Serial.println(target - cur_pos - 360);
          dstep_rel(target - cur_pos - 360); // 시계 반대방향 cur 30 -> target 270
        } else {
          //Serial.println(target - cur_pos + 360);
          dstep_rel(target - cur_pos + 360); // 시계 방향 cur 270 -> target 30
        }
      } else {
        //Serial.println(target - cur_pos);
        dstep_rel(target - cur_pos);
      }
    }
  }
};
// MPU6050_setup.h
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"

MPU6050 mpu;

#define INTERRUPT_PIN   23

// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus;  // holds atctual interrupt status byte from MPU
uint8_t devStatus;     // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;   // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;    // count of all bytes currently in FIFO
uint8_t fifoBuffer[64];  // FIFO storage buffer

// orientation/motion vars
Quaternion q;       // [w, x, y, z]    quanternion container
VectorFloat gravity; // [x, y, z]       gravity vector
float equler[3];     // [psi, theta, phi]   Euler angle container
float ypr[3];        // [yaw, pitch, roll]  yaw/pitch/roll container and gravity vector

// INTERRUPT DETECTION ROUTINE
volatile bool mpuInterrupt = false;  // indicates wheter MPU interrupt pin has gone high

void dmpDataReady() {
  mpuInterrupt = true;
}

// INITIAL SETUP
void MPU6050_setup() {
  Wire.begin();
  Wire.setClock(400000);

  Serial.begin(115200);

  Serial.println("Initializing I2C devices...");
  mpu.initialize();
  pinMode(INTERRUPT_PIN, INPUT);

  Serial.println(mpu.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
  Serial.println("Initializing DMP...");
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
//  mpu.setXGyroOffset(220);
//  mpu.setYGyroOffset(76);
//  mpu.setZGyroOffset(-85);
//  mpu.setZAccelOffset(1788);  // 1688 factory default for my test chip

  // make sure it worked (returns 0 if so)
  if (devStatus == 0) {
    // Calibration Time: generate offsets and calibrate our MPU6050
    mpu.CalibrateAccel(6);
    mpu.CalibrateGyro(6);
    mpu.PrintActiveOffsets();
    // turn on the DMP, now that it's ready
    Serial.println("Enabling DMP...");
    mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    Serial.print("Enabling interrupt detection (Arduino external interrupt ");
    //Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
    Serial.println(")...");
    attachInterrupt(INTERRUPT_PIN, dmpDataReady, RISING);
    mpuIntStatus = mpu.getIntStatus();

    // set our DMP ready flag so the main loop() function knows it's okay to use it
    Serial.println("DMP ready! Waiting for first interrupt...");
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  } else {
    // ERROR!
    // 1 = inital memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print("DMP Initailization failed (code ");
    Serial.print(devStatus);
    Serial.println(")");
  }
}

void mpuDmpGet() {
  // if programming failed, don't try to do anything
  if (!dmpReady) return;
  // read a packet from FIFO
  if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Lastest Packet // uint8_t fifoBuffer[64];  // FIFO storage buffer
    // display Euler angles in degress
    mpu.dmpGetQuaternion(&q, fifoBuffer);
    mpu.dmpGetGravity(&gravity, &q);
    mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
  }
}

 

https://www.youtube.com/shorts/tzvcGYZoEp0