레이블이 MPU6050인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MPU6050인 게시물을 표시합니다. 모든 게시물 표시

2014년 3월 10일 월요일

mpu 6050 가속도 센서값 활용 공식 : ROLL,PITCH

앞서...
  • 앞서 아두이노 프로젝트에서 제가 MPU 6050의 값을 처리를 못한 것으로 끝난 것으로 포스팅을 마친것과는 달리 최근 MPU 6050의 값을 이용하여 값을 활용하여 안드로이드에서 보일수 있게 만들었습니다. 정확히 말씀드리자면 Roll,Pitch,Yaw 값중에 Roll,Pitch의 값만 구할수 있었으니 반만 구했다고 할수 있습니다. 먼저 이유를 말씀드리자면 Yaw값을 구하기 위해서는 gyro값을 처리하여 accel값과 같이 처리를 하여야 하는데 제가 만든 작품의 특성상 GYRO값을 누적시킬 만큼의 데이터를 활용하지 못하였고 적분을 하지 못하였기 때문입니다. 

MPU6050으로 얻을 수 있는 값

  • 먼저 MPU 6050의 출력값으로는 우측과 같습니다. 제가 아두이노 센서 관련 포스팅을 할때 많이 보여드린 결과값이죠.
  • accel의 값으로는 x,y,z축을 기준으로 -16384~+16384만큼의 범위를 가집니다. 저의 결과 값은 자동차키트(센서)를 정방향으로 가만히 두었을 때 값이지만 위와 아래의 값이 다르고 z의 값이 유난히 큰것을 볼수 있습니다.
  • 기본적으로 MPU6050에서 값을 파형 형태로 전달하기 때문에 항상 정확히 같은 값을 기대할 수 없으며 accel값은 중력의 영향을 받기 때문에 수직의 값을 측정하는 z의 값이 유난히 크게 나오게 됩니다.





 Roll,Pitch???

  • 공식에 있어서 x y z의 값은 accel의 값이 사용 됩니다. 이 글의 목적인 센서의 값을 이용한 공식을 적어드리면
    • pitch = arctan(x/z)
    • roll = arctan(y/z)
  • 자바 공식으로 적으면

    • float pitch = (float) (Math.atan2(y, z) * 180/PI);
    • float roll = (float) (Math.atan2(x, z) * 180/PI);
  • 이렇게 사용할 수 있습니다. 자바에서 뒤에 180/PI를 붙이는 이유는 atan2의 리턴값이 라디안값으로 리턴되기 때문입니다.
  • Roll,Pitch는 아래의 사진을 보면 이해하기 쉬운데 기울기 값입니다. 즉 값을 이용해서 MPU6050센서의 앞뒤,좌우의 기울기 값을 알수 있습니다. Roll은 센서의 좌우로의 기울기, Pitch는 앞뒤로의 기울기를 알수 있습니다.

  • 오른쪽의 스마트폰에서의 값은 제 자동차 키트에서의 값입니다. 나침판 그림은 각각의 값을 보기 쉽게 표현하기 위한 것입니다. 이런식으로 활용할 수 있습니다.

Yaw??
  • yaw값을 알면 mpu6050센서에서의 방향상태를 x,y,z축으로 알수 있지만 yaw값은 제일 까다로운 값입니다. 제가 전문성이 떨어지기 때문에 간단히 설명을 드리자면 yaw값을 구하기 위해서는 gyro값을 계속 누적시켜 적분을 한 값이 필요로 한데, 이 값은 위에서 적은 거와 같이 센서값은 파형의 형태로 전달되기 때문에 순간순간 미세한 오차가 있으며 계속 누적할 경우 오차가 커지게 됩니다. 이를 위해선 필터의 값이 필요합니다. 저 같은 경우는 아두이노에서 자동차의 컨트롤값의 처리와 센서의 처리에 있어 컨트롤의 값이 실시간으로 이루어져야하고 우선시 되기때문에 센서의 값을 누적시킬수 없기 때문에 구하지 못하였습니다.
참조사이트 입니다.
http://www.geekmomprojects.com/mpu-6050-dmp-data-from-i2cdevlib/

2014년 2월 13일 목요일

자이로센서(MPU 6050) - 블루투스를 이용한 자동차 키트 + 안드로이드 컨트롤러


---------------------------------------------------------------------------------

자동차 키드에서 가장 필요할거 같지만 저는 결국 활용을 못한 센서입니다.
MPU 6050 - 자이로+가속도 센서


MPU 6000,6050 datasheet

가속도계랑 자이로센서, 온도계가 있는데요  SDA (data line) and SCL (clock line)라는 선을 이용합니다. Arduino Uno와 같은 다른 Arduino에서는 따로 구분이 안되어 있어 그냥 사용 하면 되지만 Arduino 2560 Mega에서는 정해진 SCL,SDA선에 연결을 해야 하더군요. 그래서 별거 아닌데 몇 시간을 잡아 먹었습니다. ㅠ 



Arduino 2560 Mega

2560Mega에서는 20,21번에 연결하면 된다는 것을 알수 있습니다.
제가 고생하면서 다른곳에 연결한(고의x ㅠ) 결과, 센서의 결과값들이 상당히 튀면서 값이 들어오는 것을 알 수 있었습니다. 사용하는 방법을 찾아봤었는데 다른 곳에서는 INT를 사용하는 사용하지만, 센서값을 읽어오는 목적에서는 문제가 없습니다.

위의 MPU 6050의 pdf를 읽어보면 3.3v를 사용한다는 것을 알 수 있습니다.

위의 내용를 정리하여 정리하면
VCC는 3.3v, GND는 GND
SCL은 21, SDA는 20번
에 연결을 하면 되겠습니다.



아두이노의 스케치를 살펴보겠습니다.

출처는 아마.. 플레이아두이노 였던것으로 기억합니다.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#define MPU6050_ACCEL_XOUT_H 0x3B // R
#define MPU6050_PWR_MGMT_1 0x6B // R/W
#define MPU6050_PWR_MGMT_2 0x6C // R/W
#define MPU6050_WHO_AM_I 0x75 // R
#define MPU6050_I2C_ADDRESS 0x68
....
typedef union accel_t_gyro_union
{
  struct
  {
    uint8_t x_accel_h;
    uint8_t x_accel_l;
    uint8_t y_accel_h;
    uint8_t y_accel_l;
    uint8_t z_accel_h;
    uint8_t z_accel_l;
    uint8_t t_h;
    uint8_t t_l;
    uint8_t x_gyro_h;
    uint8_t x_gyro_l;
    uint8_t y_gyro_h;
    uint8_t y_gyro_l;
    uint8_t z_gyro_h;
    uint8_t z_gyro_l;
  }
  reg;
  struct
  {
    int x_accel;
    int y_accel;
    int z_accel;
    int temperature;
    int x_gyro;
    int y_gyro;
    int z_gyro;
  }
  value;
};
...

void setup(){
  ...
  error = MPU6050_read (MPU6050_WHO_AM_I, &c, 1);
  Serial.print(F("WHO_AM_I : "));
  Serial.print(c,HEX);
  Serial.print(F(", error = "));
  Serial.println(error,DEC);
  error = MPU6050_read (MPU6050_PWR_MGMT_2, &c, 1);
  Serial.print(F("PWR_MGMT_2 : "));
  Serial.print(c,HEX);
  Serial.print(F(", error = "));
  Serial.println(error,DEC);
  MPU6050_write_reg (MPU6050_PWR_MGMT_1, 0);
  ...
}


void loop(){
........
    int error;
    double dT;
    accel_t_gyro_union accel_t_gyro;
    Serial.println(F(""));
    Serial.println(F("MPU-6050"));
    // Read the raw values.
    // Read 14 bytes at once,
    // containing acceleration, temperature and gyro.
    // With the default settings of the MPU-6050,
    // there is no filter enabled, and the values
    // are not very stable.
    error = MPU6050_read (MPU6050_ACCEL_XOUT_H, (uint8_t *) &accel_t_gyro, sizeof(accel_t_gyro));
    Serial.print(F("Read accel, temp and gyro, error = "));
    Serial.println(error,DEC);
    // Swap all high and low bytes.
    // After this, the registers values are swapped,
    // so the structure name like x_accel_l does no
    // longer contain the lower byte.
    uint8_t swap;
#define SWAP(x,y) swap = x; x = y; y = swap
    SWAP (accel_t_gyro.reg.x_accel_h, accel_t_gyro.reg.x_accel_l);
    SWAP (accel_t_gyro.reg.y_accel_h, accel_t_gyro.reg.y_accel_l);
    SWAP (accel_t_gyro.reg.z_accel_h, accel_t_gyro.reg.z_accel_l);
    SWAP (accel_t_gyro.reg.t_h, accel_t_gyro.reg.t_l);
    SWAP (accel_t_gyro.reg.x_gyro_h, accel_t_gyro.reg.x_gyro_l);
    SWAP (accel_t_gyro.reg.y_gyro_h, accel_t_gyro.reg.y_gyro_l);
    SWAP (accel_t_gyro.reg.z_gyro_h, accel_t_gyro.reg.z_gyro_l);
    dT = ( (double) accel_t_gyro.value.temperature + 12412.0) / 340.0;

...........
}


int MPU6050_read(int start, uint8_t *buffer, int size)
{
  int i, n, error;
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  n = Wire.write(start);
  if (n != 1)
    return (-10);
  n = Wire.endTransmission(false); // hold the I2C-bus
  if (n != 0)
    return (n);
  // Third parameter is true: relase I2C-bus after data is read.
  Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true);
  i = 0;
  while(Wire.available() && i<size)
  {
    buffer[i++]=Wire.read();
  }
  if ( i != size)
    return (-11);
  return (0); // return : no error
}
int MPU6050_write(int start, const uint8_t *pData, int size)
{
  int n, error;
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  n = Wire.write(start); // write the start address
  if (n != 1)
    return (-20);
  n = Wire.write(pData, size); // write data bytes
  if (n != size)
    return (-21);
  error = Wire.endTransmission(true); // release the I2C-bus
  if (error != 0)
    return (error);
  return (0); // return : no error
}
int MPU6050_write_reg(int reg, uint8_t data)
{
  int error;
  error = MPU6050_write(reg, &data, 1);
  return (error);
}
위와 같이 작성한 결과를 통해

accel_t_gyro.value.temperature
accel_t_gyro.value.x_accel
accel_t_gyro.value.y_accel
accel_t_gyro.value.z_accel
accel_t_gyro.value.x_gyro
accel_t_gyro.value.y_gyro
accel_t_gyro.value.z_gyro

위의 이름으로 값을 받을 수 있습니다.

아래 3줄이 MPU 6050으로 부터 얻은 값들입니다.
이것을 활용을 해야하는데 못하겠어요 ㅠ

그리고 실험을 해보면 값들이 흔들리는 걸 볼 수 있는데
(위 그림에서 자동차 키트는 움직이지 않았습니다.)
PWM으로 신호를 받아와서 그렇다고 합니다.