MPU-6050 가속도/자이로 센서 제어 (아두이노)

in #kr-arduino6 years ago (edited)

MPU-6050 가속도/자이로 센서 제어 (아두이노)



오늘은 MPU-6050 가속도/자이로 센서를 이용하여 실험하는 시간을 갖도록 하겠습니다. 자이로 센서를 사용하려면 오일러의 공식을 알아야 하고 오일러의 공식 이해하셔야 하고 각속도와 오일러의 변화율 공식과 오일러의 각을 이해하기 위해서 피치, 롤, 요와 같은 용어적 의미와 공식을 알아야 합니다. 그리고 회전각을 구할 때 대표적인 2개의 필터가 있는데 상보필터와 칼만필터 중 하나 정도는 공식을 알아야 합니다. 이처럼 수학적인 공식을 알아야 제대로 사용할 수 있습니다. 나중에 깊게 공부할 때는 모두 알아야 할 내용인데 입문자에게는 버거운 내용입니다. 하지만 이 모든것을 몰라도 사용할 수 있습니다. MPU-6050라이브러리르 사용하면 쉽게 회전각을 구할 수 있습니다. 바로 라이브러리를 사용하는 것보다 먼저 간단히 어떤 값들이 MPU-6050 모듈을 통해 측정되는지 살펴봐야 겠지요. 우선, 간단히 MPU-6050에 대해서 살펴보고 실험해보도록 하죠.

1. MPU-6050 가속도/자이로 센서


MPU-6050 모듈은 가속도/자이로를 측정할 수 있는 센서입니다. 가속도는 지구 중력을 기준으로 x, y, z 축의 가속도 크기를 구할 수 있으면 자이로(각속도)는 시간당 x, y, z 축의 회전속도 속도를 구할 수 있습니다.

MPU-6050 모듈에서 측정된 값은 바로 회전각으로 이해하기 힘듭니다. 그래서 계산이 필요하는 데 오일러의 각 공식을 알아야 합니다. 아래 링크된 위키백과사전에서 한번 읽어주시기 바랍니다.

3차원 회전 좌표계로 X축 회전을 롤, Y축 회전을 피치, Z축 회전을 요라고 합니다. 롤,피치,요에 대한 계산 공식이 따로 있습니다.

상보필터과 칼만필터는 회전각을 구할때 필요한 필터입니다.

아래는 상보필터의 공식입니다.

칼만필터은 아래 위키백과에 가셔서 공식을 살펴보시기 바랍니다.
https://ko.wikipedia.org/wiki/%EC%B9%BC%EB%A7%8C_%ED%95%84%ED%84%B0

대표적으로 이 두개의 필터로 회전각을 구하게 되는데 나중에 실험에서는 상보필터로 된 라이브러리를 이용할 예정입니다.

아래 MPU-6050 데이터 시트에 가셔서 MPU-6050에 대해서 알아두시기 바랍니다.
https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Datasheet1.pdf

2. MPU-6050 가속도/자이로 센서 구조


MPU-6050 모듈은 가속도 (x,y,z)와 자이로(각속도) (x,y,z)의 값과 온도 값을 얻을 수 있습니다.

MPU-6050 모듈은 I2C 통신으로 14개의 레지지스터 값을 아두이노로 보내고 데이터는 16bit로 구성된 총 7개의 데이터를 얻게 됩니다.

MPU-6050 모듈의 핀에 대해서 위표를 잘 살펴보시고 인터럽트핀은 아두이노우노의 경우는 인터럽트 핀이 2번이기에 사용하실 경우 2번 핀에 연결하시면 되고 사용하는 아두이노보드에 따라 해당 I2C핀과 인터럽트 핀에 맞게 연결하시면 됩니다.

3. MPU-6050 가속도/자이로 회로도


  • 준비물 : MPU-6050 모듈, 아두이노우노
  • 내용 : I2C 통신을 위해 A4(SDA), A5(SCL)핀에 연결하시오

I2C핀만 주의해서 연결하시면 됩니다.

4. 코딩



우선 기본 라이브러리 없이 순수 MPU-6050 모듈에서 측정되는 값이 어떤 값들이 출력되는지 살펴보도록 하겟습니다.

위 링크된 MPU-6050소스에 대해 간단히 살펴보겠습니다.

[소스]
// By Arduino User JohnChi

#include<Wire.h>
const int MPU_addr=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}
void loop(){
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53);  //equation for temperature in degrees C from datasheet
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.println(GyZ);
  delay(333);
}

MPU-6050의 I2C 주소는 '0x6B'입니다.

int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

16bit int형 자료형으로 가속도 3개, 온도 1개, 자이로 3개의 변수를 선언합니다.

  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

beginTransmission()함수로 '0x6B'주소로 I2C 슬레이브 디바이스로 전송을 시작합니다. 그리고 버스가 연결되면 '0x6B' 보내고 다시 '0'을 보내고 나서 endTransmission(true)함수로 버스를 해제하는 메세지를 보냅니다. 초기화 수행합니다.

Wire.beginTransmission(MPU_addr); //MPU-6050 '0x6B'주소 시작
Wire.write(0x3B); // 0x3B 읽을 rigister주소
Wire.endTransmission(false) //버스를 연결 활성화
Wire.requestFrom(MPU_addr,14,true) //데이터 요청하는데 14 레지스터 값을 요청

이렇게 해서, Wire.read()함수를 이용해서 실제로 MPU-6050에서 값을 읽게 됩니다. 0x3B~0x48의 레지스터값을 읽어와서 가속도, 온도, 자이로 값을 만들어 내게 됩니다.

Wire.read()<<8|Wire.read();

'0x3B' 레지스터 값을 왼쪽으로 8bit 이동시키고 '0x3C' 레지스터 값을 비트OR 연산을 수행합니다. 이 식을 통해서 2개의 레지스터 값을 합치게 됩니다.

예를 들어 임의의 값 A, B가 있을때 위 식으로 계산한다면

더 쉽게 살펴보면은, 비트OR 연산은 둘중 하나가 1이면 결과가 1이 나오는 연산입니다.

이렇게 14개의 레지스트값을 2개씩 이 수식을 통해서 가속도(x,y,z), 온도, 자이로(x,y,z)값을 순차적으로 Wire.read()함수를 14번 읽어서 연산을 수행하여 값을 얻게 됩니다. 그 값을 시리얼모니터로 출력하기 때문에 순수 MPU-6050 모듈에서 추출한 값입니다.
[결과]

5. 결과


실제 영상으로 살펴보면 MPU-6050 가속도/자이로센서를 움직일 때마다 가속도 AcX, AcY, AcZ 값과 자이로 GyX, GyY, GyZ 값이 나옵니다. 추가로 Tmp(온도)값은 온도를 계산식이 간단해서 값으로 나오고 가속도와 자이로는 우리가 각도로 느끼기에는 다소 어려운 수치로 출력이 이뤄집니다. 이 값으로 나중에 계산해서 회전각을 구하게 되는데 우선 어떤 값들이 찍히는지는 알아야 계산을 할 수 있겠죠. 나중에 위 링크된 곳에 가셔서 한번 공식에 대해서 자세히 배워보시기 바랍니다.

마무리


오늘 실험은 실제 회전각을 공식의 의해 구한 값이 아니라 MPU-6050의 값을 읽어온 값을 시리얼 모니터로 출력만 해서 뭔가 와닿지 않을 것 같네요. 다음 post는 MPU-6050 라이브러리를 이용해서 좀 더 쉽게 접근하고 실제 X, Y, Z 회전각을 구한 값을 통해서 간단히 실험해보면 자이로센서가 좀 더 친근하게 다가 올 듯 싶네요.

오늘은 좀 어렵더라도 대충 이런식으로 MPU-6050 모듈의 값을 읽는구나 정도로 이해하시고 넘어가시면 되겠습니다.

Sort:  

자이로 센서가 드디어 나왔네요. 드론같은데 들어 간다고 하던데요.

드론에 꼭 필요한 부품이지요. 로봇관련 부품에 쓰이기도 하고요. 수평잡는곳이나 사용범위는 많고요.
일상에서 이 부품이 쓰인 곳은 가장 가까운 스마트폰이겠지요.
스마트폰을 세로로 보다가 회전 시켜서 가로로 하면 화면이 자동으로 가로로 바뀔 때 있죠 이 센서의 의해 화면 전환이 된다고 생각하시면 돼요.

넵, 오늘은 어려운 것 맞죠? 좀 어려웠어요 ㅠㅠ

저도 어렵네요. 그래서 최대한 관련 자료들을 링크 위주로 설명했네요.
수학을 손 놓으지가 오래 되어서 이 주제에 대해서 post 하기가 망설이다가 2주 고민하다가 한번은 거쳐야 할 것 같아서 post 했네요
고급 수학이라서 아마 입문자는 오일러각 구하는 것 부터 멘붕이 오실 듯 싶네요.
오늘 내용은 그냥 이런게 있다는 정도로 소개하고 내일 실험 post에서는 라이브러리를 이용하기 때문에 아마도 쉬울 듯 싶네요
방금 막 실험이 끝났는데 processing으로 상자를 만들어 놓고 MPU-6050 모듈을 움직이면 움직이는 각도로 상자가 x,y,z 축으로 회전 시키는 실험으로 만들었네요.
내일은 오늘 보다는 좀 더 쉬운 내용일 듯 싶네요.

짱짱맨 호출에 출동했습니다!!

짱짱맨! 최고! ^^

예전에 세미나 갔었을때도 음음... 아...음 이런 소리만 했었는데
지금도 동일하네요 음....음음 아... ㅜㅜ

그때 세미나 때 기억나는 것은
칼만필터를 정교하게 구현한 모듈은 가격이 매우 비쌈 정도 였네요 -_-;

늘 좋은 글 감사합니다.

저도 mpu-6050 좀 사용하기가 불편해요.
기존의 라이브러리의 예제만 잘 해독해서 필요한 함수들만 잘 분해하셔서 사용하시면 어렵지 않을꺼에요.
그리고 칼만필터를 정교하게 구현한 것이 아니라 그 모듈이 비싼 모듈로 정교한 값을 추출할 수 있고 그 값을 칼만필터를 활용한 것이라고 생각하시면 아마 될 듯요.

Coin Marketplace

STEEM 0.26
TRX 0.11
JST 0.032
BTC 64555.14
ETH 3086.03
USDT 1.00
SBD 3.85