산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Manufacturing Technology >> 제조공정

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

개요

이 튜토리얼에서는 MPU6050 가속도계와 자이로스코프 센서를 아두이노와 함께 사용하는 방법을 배웁니다. 먼저 MPU6050이 어떻게 작동하는지와 데이터를 읽는 방법을 설명하고, 두 가지 실습 예제를 만들겠습니다.

첫 번째 예제에서는 Processing 개발 환경을 사용하여 센서 방향의 3D 시각화를 만들고, 두 번째 예제에서는 간단한 자가 안정화 플랫폼 또는 DIY 짐벌을 만듭니다. MPU6050의 방향과 융합된 가속도계 및 자이로스코프 데이터를 기반으로 플랫폼을 수평으로 유지하는 세 개의 서보를 제어합니다.

작동 원리

MPU6050 IMU는 단일 칩에 3축 가속도계와 3축 자이로스코프가 통합되어 있습니다.

자이로스코프는 X, Y, Z 축을 따라 회전 속도 또는 시간 경과에 따른 각도 위치의 변화율을 측정합니다. MEMS 기술과 코리올리 효과를 사용하여 측정하지만, 자세한 내용은 별도 'MEMS 센서 작동 원리' 튜토리얼을 참조하세요. 자이로스코프 출력은 초당 도(degrees per second) 단위이므로, 각속도를 적분하면 각도 위치를 얻을 수 있습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

반면 MPU6050 가속도계는 이전 영상에서 설명한 ADXL345 가속도계 센서와 같은 방식으로 가속도를 측정합니다. 간단히 말해 3축을 따라 중력 가속도를 측정할 수 있으며, 약간의 삼각함수 수학을 사용하면 센서가 위치한 각도를 계산할 수 있습니다. 가속도계와 자이로스코프 데이터를 융합(결합)하면 센서 방향에 대한 매우 정확한 정보를 얻을 수 있습니다.

MPU6050 IMU는 6개의 출력, 즉 3개의 가속도계 출력과 3개의 자이로스코프 출력 때문에 6축 모션 트래킹 장치 또는 6 DoF(6 자유도) 장치라고도 불립니다.

아두이노와 MPU6050

아두이노를 사용하여 MPU6050 센서를 연결하고 데이터를 읽는 방법을 살펴보겠습니다. 아두이노와 I2C 프로토콜로 통신하므로 전원용 두 선을 포함해 총 네 선만 필요합니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

필요한 부품

  • MPU6050 IMU Amazon / Banggood / AliExpress
  • 아두이노 보드
  • 브레드보드와 점퍼 와이어

MPU6050 아두이노 코드

MPU6050 센서에서 데이터를 읽는 아두이노 코드입니다. 코드 아래에 상세 설명이 있습니다.

/* 
   아두이노와 MPU6050 가속도계 및 자이로스코프 센서 튜토리얼
   by Dejan, https://howtomechatronics.com
*/
#include <Wire.h>
const int MPU = 0x68; // MPU6050 I2C 주소
float AccX, AccY, AccZ;
float GyroX, GyroY, GyroZ;
float accAngleX, accAngleY, gyroAngleX, gyroAngleY, gyroAngleZ;
float roll, pitch, yaw;
float AccErrorX, AccErrorY, GyroErrorX, GyroErrorY, GyroErrorZ;
float elapsedTime, currentTime, previousTime;
int c = 0;

void setup() {
  Serial.begin(19200);
  Wire.begin(); // 통신 초기화
  Wire.beginTransmission(MPU); // MPU6050과 통신 시작
  Wire.write(0x6B); // 레지스터 6B와 통신
  Wire.write(0x00); // 리셋 - 6B 레지스터에 0 쓰기
  Wire.endTransmission(true); // 전송 종료
  
  /* 
  // 가속도계 감도 설정 - 전체 스케일 범위 (기본 +/- 2g)
  Wire.beginTransmission(MPU);
  Wire.write(0x1C); // ACCEL_CONFIG 레지스터 (1C hex)와 통신
  Wire.write(0x10); // 레지스터 비트를 00010000으로 설정 (+/- 8g 전체 스케일)
  Wire.endTransmission(true);
  
  // 자이로 감도 설정 - 전체 스케일 범위 (기본 +/- 250deg/s)
  Wire.beginTransmission(MPU);
  Wire.write(0x1B); // GYRO_CONFIG 레지스터 (1B hex)와 통신
  Wire.write(0x10); // 레지스터 비트를 00010000으로 설정 (1000deg/s 전체 스케일)
  Wire.endTransmission(true);
  delay(20);
  */
  
  // 모듈의 IMU 오차 값이 필요하면 이 함수 호출
  calculate_IMU_error();
  delay(20);
}

void loop() {
  // === 가속도계 데이터 읽기 ===
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); // 레지스터 0x3B(ACCEL_XOUT_H)부터 시작
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // 총 6개 레지스터 읽기, 각 축 값은 2개 레지스터에 저장
  
  // +/- 2g 범위에서 데이터시트에 따라 원시 값을 16384로 나누어야 함
  AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X축 값
  AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y축 값
  AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z축 값
  
  // 가속도계 데이터로부터 롤과 피치 계산
  accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - 0.58; // AccErrorX ~(0.58) calculate_IMU_error() 함수 참조
  accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 1.58; // AccErrorY ~(-1.58)
  
  // === 자이로스코프 데이터 읽기 ===
  // previousTime = currentTime; // 실제 시간 읽기 전 이전 시간 저장
  currentTime = millis(); // 현재 실제 시간 읽기
  elapsedTime = (currentTime - previousTime) / 1000; // 초 단위 얻기 위해 1000으로 나눔
  
  Wire.beginTransmission(MPU);
  Wire.write(0x43); // 자이로 데이터 첫 레지스터 주소 0x43
  Wire.endTransmission(false);
  Wire.requestFrom(MPU, 6, true); // 총 6개 레지스터 읽기 (원문 4개는 오타, 6개가 맞음)
  
  GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // 250deg/s 범위에서 데이터시트에 따라 원시 값을 131.0으로 나눔
  GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
  GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
  
  // 계산된 오차 값으로 출력 보정
  GyroX = GyroX + 0.56; // GyroErrorX ~(-0.56)
  GyroY = GyroY - 2;    // GyroErrorY ~(2)
  GyroZ = GyroZ + 0.79; // GyroErrorZ ~(-0.8)
  
  // 현재 원시 값이 초당 도(deg/s) 단위이므로 초(s)를 곱해 각도(도) 얻기
  gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
  gyroAngleY = gyroAngleY + GyroY * elapsedTime;
  yaw = yaw + GyroZ * elapsedTime;
  
  // 상보 필터 - 가속도계와 자이로 각도 값 결합
  roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
  pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;
  
  // 시리얼 모니터에 값 출력
  Serial.print(roll);
  Serial.print("/");
  Serial.print(pitch);
  Serial.print("/");
  Serial.println(yaw);
}

void calculate_IMU_error() {
  // 이 함수를 setup에서 호출하여 가속도계와 자이로 데이터 오차 계산
  // 여기서 위에서 방정식에 사용된 오차 값들을 시리얼 모니터에 출력
  // IMU를 평평하게 놓아야 올바른 값을 얻을 수 있음
  
  // 가속도계 값 200회 읽기
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0;
    // 모든 읽기 값 합산
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  // 200으로 나누어 오차 값 얻기
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  
  // 자이로 값 200회 읽기
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // 모든 읽기 값 합산
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  // 200으로 나누어 오차 값 얻기
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  
  // 시리얼 모니터에 오차 값 출력
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
}

코드 설명

먼저 I2C 통신용 Wire.h 라이브러리를 포함하고 데이터를 저장하는 데 필요한 변수들을 정의합니다.

setup 섹션에서 와이어 라이브러리를 초기화하고 전원 관리 레지스터를 통해 센서를 리셋합니다. 이를 위해 센서 데이터시트에서 레지스터 주소를 확인할 수 있습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

원한다면 가속도계와 자이로스코프의 전체 스케일 범위를 각각의 구성 레지스터로 선택할 수도 있습니다. 이 예제에서는 가속도계 기본 +/- 2g 범위와 자이로스코프 250도/초 범위를 사용하므로 이 부분은 주석 처리했습니다.

// 가속도계 감도 설정 - 전체 스케일 범위 (기본 +/- 2g)
Wire.beginTransmission(MPU);
Wire.write(0x1C); // ACCEL_CONFIG 레지스터 (1C hex)와 통신
Wire.write(0x10); // 레지스터 비트를 00010000으로 설정 (+/- 8g 전체 스케일)
Wire.endTransmission(true);

// 자이로 감도 설정 - 전체 스케일 범위 (기본 +/- 250deg/s)
Wire.beginTransmission(MPU);
Wire.write(0x1B); // GYRO_CONFIG 레지스터 (1B hex)와 통신
Wire.write(0x10); // 레지스터 비트를 00010000으로 설정 (1000deg/s 전체 스케일)
Wire.endTransmission(true);
delay(20);

loop 섹션에서 가속도계 데이터 읽기로 시작합니다. 각 축 데이터는 2바이트 또는 레지스터에 저장되며, 센서 데이터시트에서 이 레지스터들의 주소를 볼 수 있습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

모두 읽기 위해 첫 번째 레지스터에서 시작하고, requestFrom() 함수를 사용하여 X, Y, Z 축의 모든 6개 레지스터 읽기를 요청합니다. 그런 다음 각 레지스터에서 데이터를 읽고, 출력이 2의 보수 형태이므로 적절히 결합하여 올바른 값을 얻습니다.

// === 가속도계 데이터 읽기 ===
Wire.beginTransmission(MPU);
Wire.write(0x3B); // 레지스터 0x3B(ACCEL_XOUT_H)부터 시작
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // 총 6개 레지스터 읽기, 각 축 값은 2개 레지스터에 저장
// +/- 2g 범위에서 데이터시트에 따라 원시 값을 16384로 나누어야 함
AccX = (Wire.read() << 8 | Wire.read()) / 16384.0; // X축 값
AccY = (Wire.read() << 8 | Wire.read()) / 16384.0; // Y축 값
AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0; // Z축 값

각도 계산에 적합한 -1g ~ +1g 출력 값을 얻기 위해 이전에 선택한 감도로 출력을 나눕니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

마지막으로 이 두 공식으로 가속도계 데이터로부터 롤과 피치 각도를 계산합니다.

// 가속도계 데이터로부터 롤과 피치 계산
accAngleX = (atan(AccY / sqrt(pow(AccX, 2) + pow(AccZ, 2))) * 180 / PI) - 0.58; // AccErrorX ~(0.58)
accAngleY = (atan(-1 * AccX / sqrt(pow(AccY, 2) + pow(AccZ, 2))) * 180 / PI) + 1.58; // AccErrorY ~(-1.58)

같은 방법으로 자이로스코프 데이터를 얻습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

6개 자이로스코프 레지스터를 읽고, 데이터를 적절히 결합한 뒤 이전에 선택한 감도로 나누어 초당 도(deg/s) 단위 출력을 얻습니다.

// === 자이로스코프 데이터 읽기 ===
previousTime = currentTime; // 실제 시간 읽기 전 이전 시간 저장
currentTime = millis(); // 현재 실제 시간 읽기
elapsedTime = (currentTime - previousTime) / 1000; // 초 단위 얻기 위해 1000으로 나눔
Wire.beginTransmission(MPU);
Wire.write(0x43); // 자이로 데이터 첫 레지스터 주소 0x43
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // 총 6개 레지스터 읽기
GyroX = (Wire.read() << 8 | Wire.read()) / 131.0; // 250deg/s 범위에서 데이터시트에 따라 원시 값을 131.0으로 나눔
GyroY = (Wire.read() << 8 | Wire.read()) / 131.0;
GyroZ = (Wire.read() << 8 | Wire.read()) / 131.0;
MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

여기서 계산된 작은 오차 값으로 출력을 보정하는 것을 볼 수 있는데, 이에 대해서는 곧 설명하겠습니다. 출력이 초당 도 단위이므로 이제 시간과 곱해 도 단위 각도를 얻습니다. 시간 값은 millis() 함수를 사용하여 각 읽기 반복 전에 캡처합니다.

// 계산된 오차 값으로 출력 보정
GyroX = GyroX + 0.56; // GyroErrorX ~(-0.56)
GyroY = GyroY - 2;    // GyroErrorY ~(2)
GyroZ = GyroZ + 0.79; // GyroErrorZ ~(-0.8)
// 현재 원시 값이 초당 도(deg/s) 단위이므로 초(s)를 곱해 각도(도) 얻기
gyroAngleX = gyroAngleX + GyroX * elapsedTime; // deg/s * s = deg
gyroAngleY = gyroAngleY + GyroY * elapsedTime;
yaw = yaw + GyroZ * elapsedTime;

마지막으로 상보 필터를 사용하여 가속도계와 자이로스코프 데이터를 융합합니다. 여기서는 자이로스코프 데이터의 96%를 취하는데, 외부 힘의 영향을 받지 않고 매우 정확하기 때문입니다. 자이로스코프의 단점은 드리프트(drift)가 발생하거나 시간이 지남에 따라 출력에 오차가 생긴다는 것입니다. 따라서 장기적으로는 가속도계 데이터(이 경우 4%)를 사용하여 자이로스코프 드리프트 오차를 제거합니다.

// 상보 필터 - 가속도계와 자이로 각도 값 결합
roll = 0.96 * gyroAngleX + 0.04 * accAngleX;
pitch = 0.96 * gyroAngleY + 0.04 * accAngleY;

하지만 가속도계 데이터로는 Yaw를 계산할 수 없으므로 Yaw에는 상보 필터를 적용할 수 없습니다.

결과를 보기 전에 오차 보정 값을 얻는 방법을 빠르게 설명하겠습니다. 센서를 평평하고 정지된 위치에 두고 calculate_IMU_error() 사용자 정의 함수를 호출하면 이 오차들을 계산할 수 있습니다. 모든 출력에 대해 200회 읽기 수행, 합산 후 200으로 나눕니다. 센서를 평평하고 정지된 상태로 잡고 있으므로 예상 출력 값은 0이어야 합니다. 이 계산을 통해 센서가 만드는 평균 오차를 얻을 수 있습니다.

void calculate_IMU_error() {
  // setup 섹션에서 이 함수를 호출하여 가속도계와 자이로 데이터 오차 계산
  // 여기서 위에서 방정식에 사용된 오차 값들을 시리얼 모니터에 출력
  // IMU를 평평하게 놓아야 올바른 값을 얻을 수 있음
  
  // 가속도계 값 200회 읽기
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    AccX = (Wire.read() << 8 | Wire.read()) / 16384.0;
    AccY = (Wire.read() << 8 | Wire.read()) / 16384.0;
    AccZ = (Wire.read() << 8 | Wire.read()) / 16384.0;
    // 모든 읽기 값 합산
    AccErrorX = AccErrorX + ((atan((AccY) / sqrt(pow((AccX), 2) + pow((AccZ), 2))) * 180 / PI));
    AccErrorY = AccErrorY + ((atan(-1 * (AccX) / sqrt(pow((AccY), 2) + pow((AccZ), 2))) * 180 / PI));
    c++;
  }
  // 200으로 나누어 오차 값 얻기
  AccErrorX = AccErrorX / 200;
  AccErrorY = AccErrorY / 200;
  c = 0;
  
  // 자이로 값 200회 읽기
  while (c < 200) {
    Wire.beginTransmission(MPU);
    Wire.write(0x43);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 6, true);
    GyroX = Wire.read() << 8 | Wire.read();
    GyroY = Wire.read() << 8 | Wire.read();
    GyroZ = Wire.read() << 8 | Wire.read();
    // 모든 읽기 값 합산
    GyroErrorX = GyroErrorX + (GyroX / 131.0);
    GyroErrorY = GyroErrorY + (GyroY / 131.0);
    GyroErrorZ = GyroErrorZ + (GyroZ / 131.0);
    c++;
  }
  // 200으로 나누어 오차 값 얻기
  GyroErrorX = GyroErrorX / 200;
  GyroErrorY = GyroErrorY / 200;
  GyroErrorZ = GyroErrorZ / 200;
  
  // 시리얼 모니터에 오차 값 출력
  Serial.print("AccErrorX: ");
  Serial.println(AccErrorX);
  Serial.print("AccErrorY: ");
  Serial.println(AccErrorY);
  Serial.print("GyroErrorX: ");
  Serial.println(GyroErrorX);
  Serial.print("GyroErrorY: ");
  Serial.println(GyroErrorY);
  Serial.print("GyroErrorZ: ");
  Serial.println(GyroErrorZ);
}

시리얼 모니터에 값을 출력하고 나면, 앞서 롤과 피치 계산뿐만 아니라 3개 자이로스코프 출력 모두에 대해 코드에 구현할 수 있습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

마지막으로 Serial.print 함수로 롤, 피치, 요 값을 시리얼 모니터에 출력하여 센서가 제대로 작동하는지 확인합니다.

MPU6050 방향 추적 – 3D 시각화

이제 3D 시각화 예제를 만들기 위해 아두이노가 시리얼 포트를 통해 보내는 데이터를 Processing 개발 환경에서 수신하면 됩니다. 전체 Processing 코드:

/* 
   아두이노와 MPU6050 IMU - 3D 시각화 예제
   by Dejan, https://howtomechatronics.com
*/
import processing.serial.*;
import java.awt.event.KeyEvent;
import java.io.IOException;

Serial myPort;
String data = "";
float roll, pitch, yaw;

void setup() {
  size(2560, 1440, P3D);
  myPort = new Serial(this, "COM7", 19200); // 시리얼 통신 시작
  myPort.bufferUntil('\n');
}

void draw() {
  translate(width/2, height/2, 0);
  background(233);
  textSize(22);
  text("Roll: " + int(roll) + " Pitch: " + int(pitch), -100, 265);
  
  // 객체 회전
  rotateX(radians(-pitch));
  rotateZ(radians(roll));
  rotateY(radians(yaw));
  
  // 3D 객체
  textSize(30);
  fill(0, 76, 153);
  box(386, 40, 200); // 박스 그리기
  textSize(25);
  fill(255, 255, 255);
  text("www.HowToMechatronics.com", -183, 10, 101);
}

// 시리얼 포트에서 데이터 읽기
void serialEvent(Serial myPort) {
  // 시리얼 포트에서 '\n' 문자까지 데이터를 읽어 문자열 변수 "data"에 저장
  data = myPort.readStringUntil('\n');
  // 라인피드 외 바이트를 받은 경우:
  if (data != null) {
    data = trim(data);
    // "/"에서 문자열 분할
    String[] items = split(data, '/');
    if (items.length > 1) {
      //--- 롤, 피치 (도 단위)
      roll = float(items[0]);
      pitch = float(items[1]);
      yaw = float(items[2]);
    }
  }
}

아두이노에서 들어오는 데이터를 읽어 적절한 롤, 피치, 요 변수에 저장합니다. 메인 draw 루프에서 이 값들을 사용하여 3D 객체(이 경우 특정 색상과 텍스트가 있는 간단한 박스)를 회전시킵니다.

스케치를 실행하면 MPU6050 센서가 방향 추적에 얼마나 좋은지 볼 수 있습니다. 3D 객체가 센서의 방향을 꽤 정확하게 추적하며 매우 반응이 빠릅니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

앞서 언급했듯 유일한 단점은 Yaw가 시간이 지남에 따라 드리프트된다는 것입니다. 상보 필터를 Yaw에 사용할 수 없기 때문입니다. 이를 개선하려면 추가 센서가 필요합니다. 보통 자력계(magnetometer)를 사용하여 자이로스코프 Yaw 드리프트에 대한 장기 보정을 합니다. 하지만 MPU6050에는 디지털 모션 프로세서(DMP)라는 기능이 있어 데이터의 온보드 계산에 사용되며 Yaw 드리프트를 제거할 수 있습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

디지털 모션 프로세서 사용 시 같은 3D 예제입니다. 이제 Yaw 드리프트 없이 방향 추적이 얼마나 정확한지 볼 수 있습니다. 온보드 프로세서는 3차원에서 물체의 방향과 회전을 나타내는 데 사용되는 쿼터니언(Quaternions)도 계산하고 출력할 수 있습니다. 이 예제에서는 짐벌 락(Gimbal lock)이 발생하는 오일러 각 대신 쿼터니언을 사용하여 방향을 표현합니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

하지만 이 데이터를 센서에서 얻는 것은 앞서 설명한 것보다 좀 더 복잡합니다. 우선 아두이노 디지털 핀에 추가 와이어를 연결해야 합니다. 그게 MPU6050에서 이 데이터 유형을 읽기 위해 사용되는 인터럽트 핀입니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

코드도 좀 더 복잡하므로 Jeff Rowberg의 i2cdevlib 라이브러리를 사용합니다. 이 라이브러리는 GitHub에서 다운로드할 수 있으며 웹사이트 기사에 링크를 포함하겠습니다.

라이브러리를 설치하면 라이브러리에서 MPU6050_DMP6 예제를 열 수 있습니다. 이 예제는 각 줄에 대한 주석으로 잘 설명되어 있습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

여기서 원하는 출력 종류를 선택할 수 있습니다: 쿼터니언, 오일러 각, 요/피치/롤, 가속도 값, 3D 시각화용 쿼터니언. 이 라이브러리에는 3D 시각화 예제용 Processing 스케치도 포함되어 있습니다. 이전 예제와 같은 박스 모양을 얻도록 약간 수정했습니다. 선택된 "OUTPUT_TEAPOT" 출력으로 작동하는 MPU6050_DMP6 예제용 3D 시각화 Processing 코드:

/* 
   아두이노와 MPU6050 IMU - 3D 시각화 예제
   by Dejan, https://howtomechatronics.com
*/
import processing.serial.*;
import java.awt.event.KeyEvent;
import java.io.IOException;

Serial myPort;
String data = "";
float roll, pitch, yaw;

void setup() {
  size(2560, 1440, P3D);
  myPort = new Serial(this, "COM7", 19200);
  myPort.bufferUntil('\n');
}

void draw() {
  translate(width/2, height/2, 0);
  background(233);
  textSize(22);
  text("Roll: " + int(roll) + " Pitch: " + int(pitch), -100, 265);
  
  // 객체 회전
  rotateX(radians(-pitch));
  rotateZ(radians(roll));
  rotateY(radians(yaw));
  
  // 3D 객체
  textSize(30);
  fill(0, 76, 153);
  box(386, 40, 200);
  textSize(25);
  fill(255, 255, 255);
  text("www.HowToMechatronics.com", -183, 10, 101);
}

// 시리얼 포트에서 데이터 읽기
void serialEvent(Serial myPort) {
  // 시리얼 포트에서 '\n' 문자까지 데이터를 읽어 문자열 변수 "data"에 저장
  data = myPort.readStringUntil('\n');
  // 라인피드 외 바이트를 받은 경우:
  if (data != null) {
    data = trim(data);
    // "/"에서 문자열 분할
    String[] items = split(data, '/');
    if (items.length > 1) {
      //--- 롤, 피치 (도 단위)
      roll = float(items[0]);
      pitch = float(items[1]);
      yaw = float(items[2]);
    }
  }
}

여기서 serialEvent() 함수로 아두이노에서 들어오는 쿼터니언을 수신하고, 메인 draw 루프에서 이를 사용하여 3D 객체를 회전시킵니다. 스케치를 실행하면 쿼터니언이 3차원에서 객체를 회전시키는 데 얼마나 좋은지 볼 수 있습니다.

이 튜토리얼을 과부하시키지 않기 위해 두 번째 예제인 DIY 아두이노 짐벌 또는 자가 안정화 플랫폼은 별도 기사에 실었습니다.

MPU6050 마스터하기: 아두이노용 가속도계 & 자이로스코프 튜토리얼

이 튜토리얼과 관련된 질문은 아래 댓글 섹션에 자유롭게 남겨 주시고, 아두이노 프로젝트 모음도 확인해 보세요.

제조공정

  1. 아두이노 RFID 도어락 만들기 – 단계별 완벽 가이드
  2. 아두이노 우노로 만드는 LCD 애니메이션 & 게임 기초
  3. 아두이노 & MPU6050: SG90 & MG996R 서보 모터 정밀 제어
  4. 파이썬 3로 아두이노 우노 제어하기 – 시리얼 통신 LED 제어 기초
  5. 아두이노 나노와 RDA8057M으로 소형 FM 라디오 만들기
  6. 아두이노 나노와 MPU6050으로 기울기 각도 계산하기 (Visuino 활용)
  7. 아두이노 나노 지문 센서 프로젝트 – 단계별 튜토리얼
  8. 아두이노 UNO로 만드는 미니 피아노 - 단계별 튜토리얼
  9. 라즈베리 파이 3 & 아두이노 노트북 직접 만들기: 단계별 가이드
  10. 아두이노 입문 튜토리얼 01: 첫 프로젝트 시작하기