이 튜토리얼에서는 MEMS 가속도계, 자이로스코프, 자력계의 작동 원리와 아두이노 보드와의 사용법을 배웁니다. 또한 Processing IDE를 사용하여 센서를 활용한 실용적인 애플리케이션도 만들어 보겠습니다.
MEMS(Micro-Electro-Mechanical Systems)는 0.001mm~0.1mm 크기의 마이크로 구성 요소로 이루어진 매우 작은 시스템 또는 장치입니다. 이 구성 요소들은 실리콘, 폴리머, 금속, 세라믹 등으로 만들어지며, 보통 CPU(마이크로컨트롤러)와 결합되어 시스템을 완성합니다.
이제 각 MEMS 센서가 어떻게 작동하는지 간단히 설명하겠습니다.
MEMS 가속도계
가속도계는 정전 용량의 변화를 측정하여 가속도를 측정합니다. 마이크로 구조는 대략 다음과 같습니다. 한 방향으로만 움직일 수 있도록 스프링에 연결된 질량과 고정된 외부 판이 있습니다. 특정 방향으로 가속도가 가해지면 질량이 움직이고 판과 질량 사이의 정전 용량이 변합니다. 이 정전 용량 변화를 측정하고 처리하여 특정 가속도 값에 대응시킵니다.
MEMS 자이로스코프
자이로스코프는 코리올리 효과(Coriolis Effect)를 사용하여 각속도를 측정합니다. 질량이 특정 속도로 특정 방향으로 움직이고 있을 때 외부 각속도가 가해지면(초록 화살표), 힘(파란/빨간 화살표)이 발생하여 질량이 수직 방향으로 변위됩니다. 가속도계와 유사하게 이 변위로 정전 용량 변화가 발생하며, 이를 측정 및 처리하여 특정 각속도에 대응시킵니다.
자이로스코프의 마이크로 구조는 대략 다음과 같습니다. 지속적으로 움직이는(진동하는) 질량에 외부 각속도가 가해지면 질량의 유연한 부분이 움직여 수직 변위를 만듭니다.
MEMS 자력계
자력계는 홀 효과(Hall Effect) 또는 자기 저항 효과(Magneto Resistive Effect)를 사용하여 지구 자기장을 측정합니다. 시중 센서의 약 90%가 홀 효과를 사용하며, 그 작동 원리는 다음과 같습니다.
사진과 같은 도전성 판에 전류를 흐르게 하면 전자가 판의 한쪽에서 다른 쪽으로 직선으로 흐릅니다. 여기에 자기장을 가까이 가져오면 직선 흐름이 방해받아 전자가 판의 한쪽으로, 양극이 다른 쪽으로 치우칩니다. 이때 양쪽 사이에 미터를 연결하면 자기장의 세기와 방향에 따른 전압을 측정할 수 있습니다.
나머지 10%의 센서는 자기 저항 효과를 사용합니다. 이 센서는 자기장에 민감한 재료(주로 철(Fe)과 니켈(Ni) 조성)를 사용하여, 자기장에 노출되면 저항이 변합니다.
아두이노와 MEMS 센서
이제 이 센서들을 아두이노 보드에 연결하여 활용해 보겠습니다. 예제로 GY-80 브레이크아웃 보드를 사용하며, 이 보드에는 다음 센서들이 탑재되어 있습니다: ADXL345 3축 가속도계, L3G4200D 3축 자이로스코프, MC5883L 3축 자력계, 그리고 기압계와 온도계(이 튜토리얼에서는 사용하지 않음).
필요한 부품
ADXL345 3축 가속도계
MPU6050 6축 자이로스코프 & 가속도계 (2 in 1)
GY-80 9축 자기장 가속도 자이로스코프 (3 in 1)
GY-86 10DOF MS5611 HMC5883L MPU6050 모듈 (3 in 1)
이 보드는 I2C 통신 프로토콜을 사용하여 두 개의 선만으로 모든 센서와 통신할 수 있습니다. 아두이노와 센서 간 통신을 위해 각 센서의 고유 장치 주소와 데이터를 읽기 위한 내부 레지스터 주소를 알아야 합니다. 이 주소들은 각 센서의 데이터시트에서 확인할 수 있습니다:
이제 센서에서 데이터를 가져오는 코드를 살펴보겠습니다. 가속도계부터 시작하며, 각 코드 전에 설명과 코드 내 주석으로 추가 설명을 제공합니다.
아두이노 가속도계 코드
먼저 아두이노 Wire 라이브러리를 포함하고 센서의 레지스터 주소를 정의합니다. 설정 섹션에서 Wire 라이브러리를 초기화하고 결과를 시리얼 모니터에 표시하기 위해 시리얼 통신을 시작합니다. 또한 Power_CTL 레지스터에 적절한 바이트를 전송하여 센서를 활성화(측정 모드)해야 합니다.
#include <Wire.h>
//--- 가속도계 레지스터 주소
#define Power_Register 0x2D
#define X_Axis_Register_DATAX0 0x32
#define X_Axis_Register_DATAX1 0x33
#define Y_Axis_Register_DATAY0 0x34
#define Y_Axis_Register_DATAY1 0x35
#define Z_Axis_Register_DATAZ0 0x36
#define Z_Axis_Register_DATAZ1 0x37
int ADXAddress = 0x53; // 장치 주소 (8비트는 읽기 모드 선택용)
int X0, X1, X_out;
int Y0, Y1, Y_out;
int Z1, Z0, Z_out;
float Xa, Ya, Za;
void setup() {
Wire.begin(); // Wire 라이브러리 초기화
Serial.begin(9600);
delay(100);
Wire.beginTransmission(ADXAddress);
Wire.write(Power_Register); // Power_CTL 레지스터
Wire.write(8); // 비트 D3 높게 설정으로 측정 활성화 (0000 1000)
Wire.endTransmission();
}
void loop() {
// X축
Wire.beginTransmission(ADXAddress);
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2); // 2바이트 요청
if (Wire.available() <= 2) {
X0 = Wire.read();
X1 = Wire.read();
// 원시 데이터를 X축 가속도로 변환 - 출력은 2의 보수
// X0: 최하위 바이트, X1: 최상위 바이트
X1 = X1 << 8;
X_out = X0 + X1;
Xa = X_out / 256.0; // Xa = -1 ~ +1, X축에 작용하는 중력 가속도
}
// Y축 (동일 방식)
Wire.beginTransmission(ADXAddress);
Wire.write(Y_Axis_Register_DATAY0);
Wire.write(Y_Axis_Register_DATAY1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2);
if (Wire.available() <= 2) {
Y0 = Wire.read();
Y1 = Wire.read();
Y1 = Y1 << 8;
Y_out = Y0 + Y1;
Ya = Y_out / 256.0;
}
// Z축 (동일 방식)
Wire.beginTransmission(ADXAddress);
Wire.write(Z_Axis_Register_DATAZ0);
Wire.write(Z_Axis_Register_DATAZ1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2);
if (Wire.available() <= 2) {
Z0 = Wire.read();
Z1 = Wire.read();
Z1 = Z1 << 8;
Z_out = Z0 + Z1;
Za = Z_out / 256.0;
}
// 시리얼 모니터에 출력
Serial.print("Xa= "); Serial.print(Xa);
Serial.print(" Ya= "); Serial.print(Ya);
Serial.print(" Za= "); Serial.println(Za);
}
아두이노 자이로스코프 코드
자이로스코프 데이터 읽기도 가속도계와 유사합니다. 레지스터 주소와 변수를 정의하고, 설정 섹션에서 CTRL_REG1을 사용하여 센서를 깨우고 일반 모드로 설정하며, 감도도 선택합니다. 이 예제에서는 2000dps 감도 모드를 선택합니다.
루프 섹션에서 X, Y, Z축 데이터를 읽고 원시 데이터를 각도 값으로 변환합니다. 데이터시트에 따르면 2000dps 감도 모드는 70 mdps/digit 단위에 해당하므로, 원시 출력 데이터에 0.07을 곱해 초당 도(degrees per second) 단위의 각속도를 구합니다. 각속도에 시간을 곱하면 각도 값이 됩니다. 루프 섹션 상단과 하단에서 millis() 함수를 사용하여 각 루프의 시간 간격(dt)을 계산합니다.