2019년 4월 26일 금요일

아두이노 우노에서 ADXL335 가속도 센서 사용하기 (Using ADXL335 accelerometer on Arduino Uno)

Original Document: https://lastminuteengineers.com/adxl335-accelerometer-arduino-tutorial/

 

가속도 센서의 동작 원리

가속도 센서의 동작원리를 이해하려면 3차원 큐브 속에 공이 들어 있는걸 상상하면 된다.



만일 이 큐브가 우주에 있어 모든것이 무게가 없는 상태라고 한다면 공은 큐브 가운데에 가만히 떠 있을 것이다. 위의 그림에서 큐브의 각 벽면이 특정 축을 나타낸다고 생각해 보자.

박스가 갑자기 왼쪽으로 가속도 1g의 가속도로 가속되어 움직이면 공은 큐브의 X 벽에 부딛히게 될 것이다. 공이 벽X를 때리는 힘을 측정한다면 X축에 1G의 출력값을 얻을 수 있다.



만일 큐브를 지표면으로 가져온다면 공은 벽 Z로 떨어질것이고 벽Z에 1G의 힘을 가할 것이다.




이 경우 큐브는 움직이지 않았지만 그래도 Z축으로 1G의 값을 얻었다. 이는 중력 가속도가 1G의 힘으로 공을 아래로 당기고 있기 때문이다.

* 가속도 센서는 기울어짐 측정 어플리케이션에서 정적 가속도(static acceleration) 뿐 아니고 모션, 쇼크, 진동에 의한 동적 가속도(dynamic acceleration)도 측정한다.

MEMS 가속도 센서의 동작 원리

MEMS(Micro Electro Mechanical Systems) 가속도 센서는 실리콘 웨이퍼 위에 매주 작은 기계가공된 구조(micro-machined structure)로 이루어져 있다.



이 구조는 폴리실리콘 스프링에 매달려 있다. 특정 축에 가속도가 가해지면 이 구조가 편향될 수 있게 해 준다.

편향으로 인해 고정된 플레이트와 매달려 있는 구조 사이의 정전용량(capacitance)이 바뀌게 된다. 이 정전용량의 변화는 축에 가해진 가속도에 비례한다.

센서는 정전용량의 변화를 처리해 아날로그 출력전압으로 변환한다.

ADXL335 가속도 센서의 하드웨어

모듈의 핵심은 아날로그 디바이스에서 나온 소형, 저전력, 저잡음의 3축 MEMS 가속도 센서인 ADXL335이다. 이 센서는 +-3g 범위 내의 값을 측정할 수 있다. 기울기 감지 어플리케이션에서 중력가속도로 인한 정적 가속도 뿐 아니고 모션, 쇼크, 진동에 의한 동적 가속도도 측정할 수 있다.



이 센서는 1.8~3.6V DC에서 동작하고 약 350uA의 전류를 소비한다. 하지만 모듈에 3.3V 레귤레이터가 들어있기 때문에 아두이노같이 5V를 사용하는 마이크로 컴트롤러에서 사용하는데도 문제가 없다.

빵판에서도 사용하기 쉽게 ADXL335의 모든 핀이 2.54mm 피치의 6핀 헤더로 연결되어 있다. 여기에는 X, Y, Z 3축의 아날로그 출력, 2개의 전원핀, 셀프테스트 핀이 들어 있다.

아날로그 출력은 ratiometic한데 이건 즉 0g는 3.3V 공급 전압의 중간(1.65V)을 출력하고, -3g는 0V, 3g는 3.3V를 출력하고 그 사이 값은 정확하게 비례한 전압이 출력된다.

다음은 ADXL335 가속도 센서 IC의 풀 스펙이다.



더 상세한 내용은 데이터쉬트를 참조하면 된다.


ADXL335 가속도 센서 핀아웃




아두이노 우노와 연결

연결은 매우 쉽다. Vcc핀은 우노의 5V핀에, GND핀은 우노의 그라운드 핀에 연결해 준다. 그리고 X, Y, Z 출력은 각각 우노의 A0, A1, A2에 연결해 주면 된다.

정확한 결과를 위해 아두이노 우노의 아날로그 레퍼런스 (AREF) 전압을 변경해 줄 필요가 있다. 여기서는 우노의 3.3V 핀을 우노의 AREF핀에 연결(아래 그림에서 빨간색 점선 부분)해 주면 된다.

  

예제 코드


const int xInput = A0;
const int yInput = A1;
const int zInput = A2;

// initialize minimum and maximum Raw Ranges for each axis
int RawMin = 0;
int RawMax = 1023;

// Take multiple samples to reduce noise
const int sampleSize = 10;

void setup() 
{
 analogReference(EXTERNAL);
 Serial.begin(115200);
}

void loop() 
{
 //Read raw values
 int xRaw = ReadAxis(xInput);
 int yRaw = ReadAxis(yInput);
 int zRaw = ReadAxis(zInput);

 // Convert raw values to 'milli-Gs"
 long xScaled = map(xRaw, RawMin, RawMax, -3000, 3000);
 long yScaled = map(yRaw, RawMin, RawMax, -3000, 3000);
 long zScaled = map(zRaw, RawMin, RawMax, -3000, 3000);

 // re-scale to fractional Gs
 float xAccel = xScaled / 1000.0;
 float yAccel = yScaled / 1000.0;
 float zAccel = zScaled / 1000.0;

 Serial.print("X, Y, Z  :: ");
 Serial.print(xRaw);
 Serial.print(", ");
 Serial.print(yRaw);
 Serial.print(", ");
 Serial.print(zRaw);
 Serial.print(" :: ");
 Serial.print(xAccel,0);
 Serial.print("G, ");
 Serial.print(yAccel,0);
 Serial.print("G, ");
 Serial.print(zAccel,0);
 Serial.println("G");

 delay(200);
}

// Take samples and return the average
int ReadAxis(int axisPin)
{
 long reading = 0;
 analogRead(axisPin);
 delay(1);
 for (int i = 0; i < sampleSize; i++)
 {
 reading += analogRead(axisPin);
 }
 return reading/sampleSize;
}

위의 코드에서는 가장 먼저 센서의 X,Y, Z 출력이 아두이노 우노의 어느 아날로그 입력에 연결되어 있는가를 선언해 준다.

다음은 아날로그입력의 최소/최대값을 지정해 준다. 아두이노 우노의 경우 10-bit ADC를 사용하므로 0~3.3V의 전압은 0~1023 사이의 값으로 변환된다.

sampleSize 변수는 더 정확한 결과를 얻기 위해 각 출력값에 몇개의 샘플값을 사용하는지를 지정한다.

* 주의 
analogReference(EXTERNAL)을 호출하는걸 빼먹으면 내부적으로 생성된 reference 전압과 AREF핀을 쇼트시키게 되므로 아두이노 우노를 망가트릴 수도 있으므로 가장 먼저 호출하는걸 잊어먹으면 안된다.

analogReference(EXTERNAL);
Serial.begin(115200);

loop() 함수에서 매 200ms마다 아날로그 출력값을 읽는다. analogRead() 함수 대신 ReadAxis() 함수를사용한다. 이 함수에서는 10개의 ADC 변환값을 읽어 평균값을 리턴해준다.

// Read raw values
int xRaw = ReadAxis(xInput);
int yRaw = ReadAxis(yInput);
int zRaw = ReadAxis(zInput);

ADXL335 출력을 가속도로 변환하기

센서의 아날로그 출력값을 실제 가속도로 변환해 줘야 한다. 여기서는 아두이노에 기본으로 들어 있는 map() 함수를 사용한다. map(xRaw, RawMin, RawMax, -3000, 3000); 를 호출하면 RawMin(0)은 -3000으로, RawMax(1023)는 3000으로, 0~1023 사이 값은 -3000~3000 사이에 비례하는 값으로 변환된다.

* 여기서 -3000, 3000은 임의의 값이 아니다. 이 값은 센서에서 측정된 실제 중력 가속도(1/1000 G 단위)를 나타낸다. 

  • 센서의 X축이 0V를 출력하면 (xRaw=0) map() 함수는 -3000을 리턴할 것이고 이는 즉 -3G를 나타낸다
  • 센서의 X축이 3.3V를 출력하면 (xRaw=1023) map() 함수는 3000을 리턴할 것이고 이는 즉 3G를 나타낸다.
  • 센서의 X축이 1.65V를 출력하면 (xRaw=511) map() 함수는 0을 리턴할 것이고 이는 즉 0G (가속도가 없음)을 나타낸다.

아래 그림은 모듈을 각각 다른 방향으로 놓았을 때 가속도 센서의 출력값을 보여준다.


ADXL335 셀프 테스트

ADXL335는 최종 어플리케이션에서 센서의 동작을 확인하기 위한 셀프테스트 기능을 가지고 있다.



모듈에 있는 ST(self-test)핀으로 이 기능을 제어한다.

ST핀이 3.3V에 연결되어 있으면 내부적으로 가속도계에 정전력이 가해진다. 이로 인해 사용자는 가속도 센서가 정상적으로 동작하는지 확인할 수 있다.

이로 인한 출력의 변화는 다음과 같다.

  • X축으로 -1.08g (-325mV)
  • Y축으로 +1.08g (+325mV)
  • Z축으로 +1.83g (+550mV)

정상동작시 ST핀은 오픈시켜 놓거나 GND에 연결해주면 된다.




















댓글 3개:

  1. 아두이노 프로미니로 코딩해봤는데, x,y,z 출력값이 전부 3G로 나오는데 이유가 무엇인가요? 어떻게 해결해야되요?

    답글삭제
  2. 센서 데이터시트에 0g일때 1.5V 이고 0.3V/g 입니다. 그럼 센서 출력전압은 0.6~2.4V(-3 ~3g) 이고요.. 데이터시트에 이렇게 나와있는데 위에 설명과 다르네요.. 좀 이상해서요..

    답글삭제
    답글
    1. 스펙쉬트에 보시면 조건이 Vs가 3V 일때 값입니다. 그리고 아래쪽에 보시면 (Sensitivity is essentially ratiometric to VS.)라는 문구가 들어있구요. 저 모듈 같은 경우 3.3V 레귤레이터가 있어 Vs가 3.3V가 됩니다.

      삭제