손의 움직임만으로 마우스를 조작할 수 있을까?
IMU 센서로 만드는 에어 마우스 실험!

서문

그림1 마우스 없이 손의 움직임만으로 화면을 조작하는 토니 스타크

영화 '아이언맨'에서 토니 스타크는 손을 휘저으며 가상 디스플레이를 자유자재로 조작합니다. 터치도, 물리적 장치도 없이 제스처만으로 정보를 탐색하는 모습은 정말 놀랍죠. 과연 현실에서도 손의 움직임만으로 이렇게 화면을 자유롭게 조작할 수 있을까요?

현대 기술은 모션 인식 인터페이스의 발전을 통해 우리에게 더욱 편리한 세상을 선물하고 있습니다. 특히, IMU(Inertial Measurement Unit) 센서는 사용자의 손 움직임을 실시간으로 감지하여 입력 신호로 변환하는 핵심 기술입니다. 본 기사에서는 IMU 센서를 활용하여 손의 움직임을 마우스 커서 이동으로 변환하는 '에어 마우스'를 직접 구현해 볼 예정입니다. 손을 기울이면 커서가 움직이고, 특정한 동작을 하면 클릭이 되는 이 흥미로운 실험을 통해 미래 인터페이스 기술의 가능성을 탐구해봅시다!

IMU 센서란 무엇일까?

그림2 IMU 센서의 좌표축

IMU(Inertial Measurement Unit) 센서는 물체의 움직임을 정밀하게 감지하는 관성 측정 장치입니다. IMU 센서는 크게 가속도 센서와 자이로스코프1)로 구성되며, 경우에 따라 지자기 센서2)를 포함하기도 합니다.

가속도 센서는 물체의 속도 변화량을 측정하여 가속도 값을 출력합니다. 이 값을 적분하면 물체의 속도와 위치를 계산할 수 있습니다. 하지만 시간이 지날수록 오차가 누적되는 단점이 있어, GPS와 같은 다른 센서와 함께 사용하여 정확도를 높이는 경우가 많습니다. 자이로스코프는 코리올리 힘3)을 이용하여 물체의 회전 변화량인 각속도를 측정합니다. 이를 시간에 따라 적분하면 물체의 회전 각도를 계산할 수 있습니다. [그림2]에 표시되어 있듯이, 물체가 축을 기준으로 얼마나 회전했는지를 나타내는 용어가 있습니다. x축에 대한 회전 반경을 Roll, y축에 대한 회전 반경을 Pitch, z축에 대한 회전 반경을 Yaw라고 합니다. 이러한 기능 덕분에 IMU 센서는 스마트폰 화면 회전, 드론의 균형 유지, VR 장치의 움직임 추적 등 다양한 분야에서 활용되고 있습니다.

센서로 받은 데이터와 PC는 어떻게 통신할까?

IMU 센서가 손의 움직임을 감지하여 데이터를 생성했다면, 이제 이 데이터를 컴퓨터로 전송해야 합니다. 예를 들어, IMU 센서가 "손이 오른쪽으로 기울었습니다!"라는 신호를 보내면, PC는 "알겠습니다! 마우스 커서를 오른쪽으로 움직이겠습니다."라고 응답해야 합니다. 이러한 데이터 전송을 가능하게 하는 것이 바로 시리얼 통신(Serial Communication)입니다.

그림3 이진 펄스

시리얼 통신은 데이터를 한 번에 한 비트씩 순차적으로 전송하는 방식으로, 마치 한 줄로 서서 차례대로 이동하는 것과 비슷합니다. IMU 센서와 PC가 정보를 주고받는 데 하나의 선만을 이용하기 때문에 간단하면서도 효율적인 방식이죠.

하지만 센서가 감지한 움직임 정보는 그대로 전송될 수 없습니다. 컴퓨터가 이해할 수 있도록 이진 펄스(binary pulse) 형태로 변환되어야 하죠. 시리얼 통신에서는 이진수 1은 논리 HIGH, 이진수 0은 논리 LOW로 표현됩니다. 송신 장치는 이러한 HIGH/LOW 신호를 조합하여 데이터를 전송하고, 수신 장치는 이를 해석합니다. 이때 중요한 요소가 Baud Rate(전송 속도) 입니다. Baud Rate는 초당 몇 개의 비트가 전송되는지를 나타내는 값이며, 송신 장치와 수신 장치의 Baud Rate가 동일해야 원활한 통신이 가능합니다. 이렇게 전달된 데이터는 Python 프로그램에 의해 해석되어 마우스 커서를 이동시키거나 클릭하는 등의 동작으로 변환됩니다.

IMU 센서를 이용해서 에어마우스를 만들어보자!

그림4 준비물 사진

이제, IMU 센서를 이용해서 에어마우스를 만들어 볼 건데요, 준비물은 다음과 같습니다. 왼쪽부터 아두이노 우노(UNO) 보드, 브레드 보드, IMU 센서(WT 901), 점퍼 와이어, 노트북입니다.

먼저, 시리얼 통신을 위해 아두이노 우노(Arduino Uno)와 IMU 센서를 올바르게 연결해야 합니다. IMU 센서의 GND(0V)를 아두이노의 GND와 연결하고, VCC(5V)는 아두이노의 5V 핀에 연결하여 전원을 공급합니다. 데이터 송수신을 위해 IMU 센서의 SCL(클럭 신호 라인)4)을 아두이노의 아날로그 A5 핀에, SDA(데이터 신호 라인)5)는 아두이노의 아날로그 A4 핀에 연결합니다.

IMU 센서가 x축 방향(Pitch)을 기준으로 회전하면 마우스 커서가 좌우로 움직이고, y축 방향(Roll)을 기준으로 회전하면 마우스 커서가 상하로 움직이도록 코드를 구현해봅시다. 여기에 z축 방향으로 손짓을 해주면, 클릭이 되는 기능까지 구현해보도록 합시다.

void setup(){
    Wire.begin();
    Serial.begin(9600);
    delay(100);
    Serial.println("Air Mouse using IMU sensor");

    Int16_t rawRoll = readWT901Register(ROLL_REG);
    Int16_t rawPitch = readWT901Register(PITCH_REG);
    Int16_t rawAccZ = readWT901Register(ACCZ_REG);

    roll_base = rawRoll / 32768.0 * 180.0;
    pitch_base = rawPitch / 32768.0 * 180.0;
    accZ_base = rawAccZ / 32768.0 * 180.0;
    delay(200);}

void loop(){
    float relative_roll = roll - roll_base;
    float relative_pitch = pitch - pitch_base;
    float relative_accZ = accZ - accZ_base;
    Serial.print(relative_roll,2);
    Serial.print(relative_pitch,2)
    Serial.print(relative_yaw,2);
    delay(100);}
그림5 아두이노 코드 setup()과 main() 함수 중 일부분

아두이노 코드는 초기 설정을 수행하는 setup() 함수와 반복적으로 실행되는 loop() 함수로 구성됩니다. setup() 함수에서는 Serial 통신을 시작하는 코드와 함께 IMU 센서의 초기 기준값을 저장하는 변수를 선언해주었습니다. IMU 센서는 데이터를 16비트 정수(-32,768 ~ 32,767)로 제공하는데, 이를 -180° ~ 180° 범위의 회전각으로 변환하기 위해 비율을 곱하여 변환해주었습니다.

이후, loop() 함수에서는 100ms마다 IMU 센서로부터 데이터를 읽고, 변화량을 지속적으로 측정합니다. 센서 데이터가 정상적으로 측정되고 있는지 확인하기 위해 Serial.print()를 사용하여 출력해 보았습니다. 그런데, 코드에는 readWT901Register()라는 함수가 포함되어 있습니다. 이 함수는 어떤 역할을 할까요?

int16_t readWT901Register(uint8_t reg) {
    Wire.beginTransmission(SENSOR_ADDR);
    Wire.write(reg);
    Wire.endTransmission(false);

    Wire.requestFrom((uint8_t)WT901_ADDR, (uint8_t)2);

if (Wire.available() == 2) {
    uint8_t low = Wire.read();
    uint8_t high = Wire.read();
    return (int16_t)((high << 8) | low);
}

return 0;
}
그림6 readWT901Register() 함수 코드 부분

이 함수는 setup() 함수 위에서 미리 정의해주었는데요, IMU 센서로부터 16비트 데이터를 읽어 반환하는 역할을 합니다. IMU 센서는 데이터를 16비트(2byte) 단위로 제공하지만, Serial 통신은 앞서 말했듯이 데이터를 8비트(1바이트)씩 직렬로 전송합니다. 따라서 8비트 데이터를 두 번 나눠 받아 하나의 16비트 값으로 조합해야 하므로, 해당 함수가 필요한 것이랍니다.

MOVE_SENSITIVITY = 0.5
move_x = relative_roll * MOVE_SENSITIVITY
move_y = relative_pitch * MOVE_SENSITIVITY
pyautogui.moveRel(move_x, move_y, duration=0)

CLICK_THRESHOLD = 2.0
if relative_accZ > CLICK_THRESHOLD:
    pyautogui.click()
그림7 파이썬 코드 부분

다음은 Python 프로그램을 이용해 IMU 센서 데이터를 기반으로 마우스를 조작하는 코드입니다. pyautogui는 키보드 입력 및 마우스 조작을 지원하는 Python 라이브러리로, 이를 설치하면 마우스 커서를 제어할 수 있습니다. IMU 센서로 얻은 relative_roll 및 relative_pitch 값에 이동 감도(sensitivity)를 곱한 후, pyautogui.moveRel() 함수를 사용하여 마우스 커서를 해당 각도만큼 이동시킵니다. 또한 Z축 방향 가속도(AccZ)가 일정 기준 이상 증가하면, 현재 마우스 위치에서 pyautogui.click() 함수를 실행하여 마우스 클릭을 수행하도록 구현하였습니다.

IMU 센서를 손등에 부착하고 작성한 Python 코드를 실행시키면, 손의 움직임만으로 마우스 커서를 조작할 수 있습니다! 단순한 클릭 기능을 넘어, 손을 벌려 화면을 확대하거나 특정한 제스처로 페이지를 넘기는 등 다양한 기능도 추가할 수도 있습니다. 이번 실험을 통해 우리는 에어 마우스를 제작하며 VR/AR 인터페이스, 모션 인식 기술과 같은 미래 기술을 직접 경험해볼 수 있었습니다. 여러분도 직접 새로운 아이디어를 실험하고 구현해 보는 건 어떨까요? 작은 아이디어에서 시작된 도전이 언젠가 세상을 변화시키는 놀라운 기술로 발전할 수도 있습니다. 공학자의 꿈을 품고 끊임없이 탐구하고 도전한다면, 미래 기술 혁신의 주인공이 될 수 있을 것입니다!

참고
  • 1) 자이로스코프: 각속도를 측정하여 물체의 회전 방향과 속도를 감지하는 센서.
  • 2) 지자기 센서: 지구의 자기장을 감지하여 방향과 위치를 측정하는 센서.
  • 3) 코리올리 힘: 회전하는 계에서 운동하는 물체에 가해지는 가상의 관성력.
  • 4) 클럭 신호 라인(SCL): 데이터를 동기화하기 위해 일정한 주기로 신호를 생성하는 라인.
  • 5) 데이터 신호 라인(SDA): 아두이노와 PC 간 데이터 전송 및 수신을 담당하는 라인.
그림 출처
지난 기사 보기