아두이노

OpenCV + MediaPipe로 손가락 개수 인식해 아두이노 LED 제어하기 – Computer Vision Gesture Control

게임교수 2026. 8. 21. 21:18
반응형

🌐 Quick Overview for International Readers

Control Arduino/ESP32 LEDs using Python, OpenCV, and MediaPipe Hand Tracking. This tutorial explains how to count extended fingers via webcam and send serial commands to trigger hardware outputs in real-time.

단순히 버튼을 누르거나 웹페이지 스위치로 LED를 제어하는 단계를 넘어, 오늘은 영화 '마이너리티 리포트'처럼 카메라에 내 손가락을 펼치는 것만으로 하드웨어를 제어하는 컴퓨터 비전(Computer Vision) IoT 프로젝트를 진행해보겠습니다.

Google의 MediaPipe 라이브러리와 OpenCV를 활용하면 복잡한 딥러닝 모델 학습 없이도 단 몇 십 줄의 Python 코드로 실시간 손가락 마디(Landmark) 추적 및 개수 인식이 가능합니다. 인식된 손가락 개수를 시리얼 통신(Serial)으로 아두이노/ESP32에 전달해 LED를 켜고 끌 수 있는 시스템을 완성해봅시다.




1. 시스템 구조 및 준비물 (System Architecture)

전체적인 시스템 데이터 흐름은 다음과 같습니다:

[웹캠 영상 입력] ➔ [Python OpenCV / MediaPipe 처리 (손가락 개수 카운트)] ➔ [시리얼 통신 전송] ➔ [아두이노 / ESP32 LED 제어]

  • PC / 노트북 (웹캠 및 Python 실행 환경)
  • 아두이노 Uno 또는 ESP32 1개
  • LED 1개 및 220Ω 저항 1개
  • USB 데이터 케이블

2. 아두이노 소스코드 (Arduino C++ Code)

시리얼 포트로 들어오는 데이터가 '1'이면 LED를 켜고, '0'이면 끄는 기본 수신 코드입니다. 아두이노 IDE에서 먼저 업로드해 둡니다.

const int ledPin = 13; // 아두이노 내장 LED 사용 (또는 GPIO 2)

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
}

void loop() {
  if (Serial.available() > 0) {
    char command = Serial.read();
    
    if (command == '1') {
      digitalWrite(ledPin, HIGH); // 손가락 펴짐 ➔ LED ON
    } else if (command == '0') {
      digitalWrite(ledPin, LOW);  // 주먹 쥠 ➔ LED OFF
    }
  }
}



3. 파이썬 손 인식 및 제어 프로그램 (Python Script)

파이썬 실행 전 필요한 라이브러리를 먼저 터미널(CMD)에서 설치해야 합니다.

pip install opencv-python mediapipe pyserial

아래 코드에서 COM3 부분을 실제 아두이노가 연결된 포트 번호로 수정 후 실행하세요.

import cv2
import mediapipe as mp
import serial
import time

# 아두이노 시리얼 포트 설정 (본인 포트로 변경: 예 'COM3' 또는 '/dev/ttyUSB0')
try:
    py_serial = serial.Serial(port='COM3', baudrate=9600, timeout=1)
    time.sleep(2) # 시리얼 연결 안정화 대기
    print("Arduino Connected!")
except:
    print("Serial Port Connection Failed. Check Port Number.")

# MediaPipe Hand 모듈 초기화
mp_hands = mp.solutions.hands
mp_draw = mp.solutions.drawing_utils
hands = mp_hands.Hands(max_num_hands=1, min_detection_confidence=0.7)

# 손가락 끝 마디 랜드마크 인덱스 (검지, 중지, 약지, 소지)
tip_ids = [8, 12, 16, 20]

cap = cv2.VideoCapture(0) # 웹캠 열기

while cap.isOpened():
    success, img = cap.read()
    if not success:
        continue

    img = cv2.flip(img, 1) # 좌우 반전
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    results = hands.process(img_rgb)

    total_fingers = 0

    if results.multi_hand_landmarks:
        for hand_landmarks in results.multi_hand_landmarks:
            mp_draw.draw_landmarks(img, hand_landmarks, mp_hands.HAND_CONNECTIONS)
            
            lm_list = []
            for id, lm in enumerate(hand_landmarks.landmark):
                h, w, c = img.shape
                cx, cy = int(lm.x * w), int(lm.y * h)
                lm_list.append([id, cx, cy])

            if len(lm_list) != 0:
                fingers = []

                # 엄지 손가락 판별 (X축 기준)
                if lm_list[4][1] > lm_list[3][1]:
                    fingers.append(1)
                else:
                    fingers.append(0)

                # 나머지 4개 손가락 판별 (Y축 기준: 끝마디가 아래마디보다 위에 있는지)
                for id in range(0, 4):
                    if lm_list[tip_ids[id]][2] < lm_list[tip_ids[id] - 2][2]:
                        fingers.append(1)
                    else:
                        fingers.append(0)

                total_fingers = fingers.count(1)

                # 손가락이 1개 이상 펴지면 LED ON ('1'), 주먹을 쥐면 LED OFF ('0')
                if total_fingers > 0:
                    py_serial.write(b'1')
                else:
                    py_serial.write(b'0')

    # 화면에 인식된 손가락 개수 표시
    cv2.putText(img, f'Fingers: {total_fingers}', (30, 70), 
                cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3)

    cv2.imshow("Hand Tracking Controller", img)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
if 'py_serial' in locals():
    py_serial.close()

4. 핵심 구현 원리 (Key Technical Points)

1) MediaPipe의 21개 Hand Landmark 추적
MediaPipe는 손 전체에서 21개 3D 포인트를 실시간으로 추적합니다. 손가락 끝 마디(8, 12, 16, 20번)의 Y 좌표값과 바로 밑 마디(6, 10, 14, 18번)의 Y 좌표를 비교하여 손가락이 올라가 있는지(펴짐) 내려가 있는지(굽힘)를 수식으로 간단하게 판별합니다.

2) Python-Arduino간 PySerial 데이터 전송
py_serial.write(b'1')와 같이 바이트(Byte) 형태로 문자열을 전달합니다. 파이썬 매 프레임마다 연속 전송하여 웹캠에서의 동작 지연 시간(Latency)을 50ms 미만으로 극소화시켰습니다.


5. 트러블슈팅 및 예외 처리 (Troubleshooting)

  • SerialException: PermissionError (포트 열기 실패): 아두이노 IDE의 시리얼 모니터 창이 열려 있으면 파이썬이 포트를 점유하지 못합니다. 아두이노 IDE 시리얼 모니터 창을 닫은 후 파이썬 코드를 실행하세요.
  • 손가락 인식율 저하 (조명/배경 영향): min_detection_confidence=0.7 신뢰도 수치를 0.5로 낮추거나 손을 정면으로 카메라에 비추면 훨씬 안정적으로 인식합니다.
  • 웹캠이 열리지 않을 때: 다른 웹캠 프로그램(Zoom, 팀즈 등)이 카메라를 사용 중인지 확인하고 cv2.VideoCapture(0)의 인덱스를 `1`로 바꿔보세요.

6. 응용 및 발전 아이디어 (Next Projects)

기본 LED 제어를 익히셨다면 다음 단계로 발전시켜 보세요:

  • 손가락 개수별 동작 지정: 1개=LED 1번, 2개=LED 2번, 3개=모터 회전 등 멀티 채널 제어
  • 손바닥 각도/거리 인식: 손을 앞으로 가져가면 서보모터 각도가 조절되는 3D 무선 컨트롤러 제작
  • 스마트홈 비접촉 스위치: 위생이 중요한 의료 환경이나 요리 중 손을 대지 않고 가전기기를 켜는 스위치 구현

다음 글에서는 MediaPipe Pose 모듈을 이용한 "몸짓/포즈 인식으로 스마트 조명 제어하기" 실습을 다뤄보겠습니다. 질문은 댓글로 편하게 남겨주세요!



반응형