Arduino & OpenCV로 실시간 안면 추적 시스템 구축하기
부품 및 소모품
| 부품 |
수량 |
| Arduino UNO |
1 |
| 웹캠 (미니 웹캠 권장) |
1 |
| 마이크로 서보 모터 |
2 |
팬/틸트용 |
| 팬-틸트 키트 |
1 |
직접 제작 또는 3D 프린트 가능 |
| 브레드보드 |
1 |
앱 및 온라인 서비스
- Python 2.7 (또는 3.x 권장)
- OpenCV (cv2)
- pyserial, numpy
- Haarcascade 전면 얼굴 분류기 XML
프로젝트 소개
이전 튜토리얼에서 Arduino-Python 시리얼 통신(LED 제어), OpenCV 색상 추적을 다뤘습니다. 이번에는 Haarcascade 분류기로 얼굴을 검출하고, 팬-틸트 서보로 카메라가 얼굴을 따라가게 만듭니다. 기본적인 Arduino와 Python 지식만 있으면 됩니다.
1단계: 준비물
하드웨어: 위 표 참조. 소프트웨어: Python 2.7/3.x, OpenCV, pyserial, numpy 설치 필요.
2단계: Python 환경 설정
Python 설치 확인
CMD에서 python 입력 시 인터프리터 실행되면 OK. 안 되면 환경 변수 PATH에 Python 경로 추가.
필수 패키지 설치
pip install pyserial
pip install opencv-python
pip install numpy
3단계: Python 스크립트 (face.py)
작업 폴더 생성 → haarcascade_frontalface_default.xml 다운로드하여 폴더에 저장 → 아래 코드를 face.py로 저장.
import numpy as np
import serial
import time
import cv2
# 아두이노 포트 설정 (COM5 → 본인 포트로 변경)
arduino = serial.Serial(port='COM5', baudrate=9600, timeout=.1)
time.sleep(2)
print("아두이노 연결됨")
# Haarcascade 로드
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
# 웹캠 캡처
cap = cv2.VideoCapture(0)
while True:
ret, img = cap.read()
if not ret: break
cv2.resizeWindow('img', 500, 500)
# 화면 중앙 기준선 그리기
cv2.line(img, (500, 250), (0, 250), (0, 255, 0), 1)
cv2.line(img, (250, 0), (250, 500), (0, 255, 0), 1)
cv2.circle(img, (250, 250), 5, (255, 255, 255), -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 얼굴 중심 좌표 계산
center_x = int(x + w/2)
center_y = int(y + h/2)
print(f"Center: ({center_x}, {center_y})")
# 아두이노로 전송: "X{center_x}Y{center_y}Z"
data = f"X{center_x}Y{center_y}Z"
arduino.write(data.encode())
cv2.imshow('img', img)
k = cv2.waitKey(30) & 0xff
if k == 27: break # ESC로 종료
cap.release()
cv2.destroyAllWindows()
4단계: 아두이노 코드 (servo.ino)
#include
Servo servoVer; // 수직(틸트)
Servo servoHor; // 수평(팬)
int x, y;
int prevX = -1, prevY = -1;
void setup() {
Serial.begin(9600);
servoVer.attach(5); // 틸트 서보
servoHor.attach(6); // 팬 서보
servoVer.write(90); // 중앙 초기화
servoHor.write(90);
}
void updateServos() {
if (prevX != x || prevY != y) {
// 화면 좌표(0~600, 0~450) → 서보 각도 매핑
int servoX = map(x, 600, 0, 70, 179); // 팬: 좌우 반전
int servoY = map(y, 450, 0, 179, 95); // 틸트: 상하 반전
servoX = constrain(servoX, 70, 179);
servoY = constrain(servoY, 95, 179);
servoHor.write(servoX);
servoVer.write(servoY);
prevX = x; prevY = y;
}
}
void loop() {
if (Serial.available() > 0) {
if (Serial.read() == 'X') {
x = Serial.parseInt();
if (Serial.read() == 'Y') {
y = Serial.parseInt();
updateServos();
}
}
// 버퍼 비우기
while (Serial.available() > 0) Serial.read();
}
}
5단계: 팬-틸트 메커니즘 조립
시판 키트 사용 권장 (저렴, 조립 용이). 직접 제작 시 목재/아크릴/3D 프린트 가능.
6단계: 배선
- 틸트 서보(수직) → 핀 5
- 팬 서보(수평) → 핀 6
- 서보 VCC → 5V (외부 전원 권장), GND → GND
7단계: 테스트
- 아두이노 스케치 업로드 → IDE 종료 (포트 해제)
- Python IDLE에서
face.py 실행 (F5)
- 웹캠 창 열리고 얼굴 검출 시 서보가 얼굴을 추적
- 카메라를 서보에 장착하면 카메라 자체가 얼굴을 따라감
문제 해결 팁
- 시리얼 포트 권한 문제: Linux/Mac에서
sudo chmod 666 /dev/ttyUSB0
- Haarcascade 파일 경로 확인 (같은 폴더 또는 절대 경로)
- 서보 떨림:
map() 범위 조정, constrain()으로 제한
- 여러 얼굴 검출 시 가장 큰 얼굴만 추적하도록 로직 추가 가능
- Python 3.x 사용 시
print, arduino.write(bytes) 등 문법 수정 필요