라즈베리파이와 OpenCV로 자동화된 비전 기반 객체 추적 시스템
컴퓨터 비전을 사용해 색상 객체를 자동으로 추적하는 팬/틸트 서보 시스템입니다.
스토리
소개
이전 튜토리얼에서 라즈베리파이 카메라를 위치시키기 위해 팬/틸트 서보를 제어하는 방법을 배웠습니다. 오늘은 카메라가 색상 객체를 자율적으로 추적할 수 있도록 설정을 확장하겠습니다.
이것은 OpenCV와의 첫 실습 경험이며, 이 오픈소스 컴퓨터 비전 라이브러리에 이미 매료되었습니다.
OpenCV는 학술 및 상업용으로 무료이며, C++, C, 파이썬, 자바 인터페이스를 제공하고 Windows, Linux, macOS, iOS, 안드로이드에서 실행됩니다. 이 시리즈에서는 OpenCV가 계산 효율성과 실시간 애플리케이션을 위해 구축되었기 때문에 라즈베리파이(라즈비안)와 파이썬에 초점을 맞춥니다 – 물리 컴퓨팅 프로젝트에 정확히 필요한 것들입니다.
1단계: BOM – 자재 명세서
주요 부품:
- 라즈베리파이 V3 – US$ 32.00
- 5MP 1080p OV5647 미니 카메라 비디오 모듈 – US$ 13.00
- TowerPro SG90 9G 180도 마이크로 서보 (2개) – US$ 4.00
- 미니 팬/틸트 카메라 플랫폼 안티바이브레이션 마운트 (서보 2개 포함) (*) – US$ 8.00
- 적색 LED
- 저항 220Ω
- 저항 1kΩ (2개) – 선택 사항
- 기타: 금속 부품, 밴드 등 (직접 팬/틸트 메커니즘 제작 시)
(*) 서보 포함 완전한 팬/틸트 플랫폼을 구매하거나 직접 제작 가능.
2단계: OpenCV 3 패키지 설치
최신 라즈비안 스트레치 실행 라즈베리파이 V3에서 가장 신뢰할 수 있는 방법은 Adrian Rosebrock의 튜토리얼(Raspbian Stretch: Install OpenCV 3 + Python on your Raspberry Pi)을 따르는 것입니다. OpenCV를 소스부터 빌드하고 전용 파이썬 가상 환경을 설정하는 과정을 안내합니다.
튜토리얼 완료 후 가상 환경 활성화 및 설치 확인:
source ~/.profile
workon cv
python
import cv2
print(cv2.__version__)
버전 3.3.0 이상이 보여야 합니다. 출력은 OpenCV가 cv 가상 환경 내부에서 사용 준비되었음을 확인합니다.
3단계: 카메라 테스트
OpenCV 설치 후 PiCamera 스트리밍이 올바른지 확인. 다음 스크립트 실행 (또는 simpleCamTest.py 다운로드):
import cv2
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
frame = cv2.flip(frame, -1) # 필요시 수직 뒤집기
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('frame', frame)
cv2.imshow('gray', gray)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
q 또는 Ctrl+C로 종료. 성공 시 컬러와 그레이스케일 피드 모두 표시.
4단계: OpenCV로 파이썬에서 컬러 감지
OpenCV는 인간의 색상 인식과 더 잘 맞는 HSV(Hue, Saturation, Value) 공간에서 작동합니다. 특정 색상을 감지하려면 먼저 BGR 값을 결정하고, HSV로 변환한 후 하한/상한 범위를 설정합니다.
예: 노란색 객체 추적. 컬러 피커(예: PowerPoint)로 BGR 값 (71, 234, 213)을 찾았습니다. 아래 스크립트로 HSV 변환 (또는 bgr_hsv_converter.py 다운로드):
import sys, numpy as np, cv2
blue, green, red = int(sys.argv[1]), int(sys.argv[2]), int(sys.argv[3])
color = np.uint8([[[blue, green, red]]])
hsv = cv2.cvtColor(color, cv2.COLOR_BGR2HSV)
hue = hsv[0][0][0]
print('Lower bound:', [hue-10, 100, 100])
print('Upper bound:', [hue+10, 255, 255])
python bgr_hsv_converter.py 71 234 213 실행 시 출력:
Lower bound: [24, 100, 100]
Upper bound: [44, 255, 255]
이 범위로 이미지에서 색상 마스킹:
import cv2, numpy as np
img = cv2.imread('yellow_object.JPG', 1)
img = cv2.resize(img, (0,0), fx=0.2, fy=0.2)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower = np.array([24, 100, 100], dtype=np.uint8)
upper = np.array([44, 255, 255], dtype=np.uint8)
mask = cv2.inRange(hsv, lower, upper)
cv2.imshow('mask', mask)
cv2.imshow('image', img)
while True:
k = cv2.waitKey(0)
if k == 27:
break
cv2.destroyAllWindows()
colorDetection.py로 저장 후 python colorDetection.py 실행 (이미지가 같은 디렉토리에 있어야 함).
5단계: 실시간 객체 이동 추적
마스크를 기반으로 객체의 중심점을 프레임별로 추적할 수 있습니다. 아래 코드는 Adrian Rosebrock의 Ball Tracking 튜토리얼에서 차용했으며 앞서 정의한 HSV 범위를 포함합니다.
먼저 imutils 라이브러리 설치 확인:
pip install imutils
그런 다음 ball_tracking.py 실행:
python ball_tracking.py
스크립트는 프레임을 수직으로 뒤집고(필요시) 마스크를 적용합니다. 객체 감지 시 원과 중심점이 그려집니다.
6단계: GPIO 테스트
이제 파이에 적색 LED를 추가하고 GPIO로 제어 가능한지 확인합니다.
배선: LED 캐소드를 GPIO 21에 연결, 애노드를 220Ω 저항 통해 그라운드에 연결.
cv 환경에 RPi.GPIO가 아직 설치되지 않았다면 설치:
pip install RPi.GPIO
테스트 스크립트 (LED_simple_test.py):
import sys, time, RPi.GPIO as GPIO
led = int(sys.argv[1])
freq = int(sys.argv[2])
GPIO.setmode(GPIO.BCM)
GPIO.setup(led, GPIO.OUT)
GPIO.setwarnings(False)
print(f'Blinking LED on GPIO {led} every {freq}s, 5 times.')
for _ in range(5):
GPIO.output(led, GPIO.LOW)
time.sleep(freq)
GPIO.output(led, GPIO.HIGH)
time.sleep(freq)
GPIO.cleanup()
python LED_simple_test.py 21 1로 실행해 LED를 1초 간격으로 5회 점멸.
7단계: 컬러 인식 및 GPIO 상호작용
추적 로직과 GPIO 제어를 결합해 대상 색상 감지 시 LED가 켜지도록 합니다. 핵심 추가 사항:
import RPi.GPIO as GPIO
led = 21
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(led, GPIO.OUT)
GPIO.output(led, GPIO.LOW)
ledOn = False
감지 루프 내부에서 유효한 반경 확인 후 추가:
if not ledOn:
GPIO.output(led, GPIO.HIGH)
ledOn = True
전체 스크립트: object_detection_LED.py. python object_detection_LED.py로 실행. 객체 색상이 HSV 범위 내에 있을 때만 LED가 켜집니다.
8단계: 팬/틸트 메커니즘
OpenCV와 GPIO가 작동하면 서보를 외부 5V 전원에 연결. 데이터 핀 배선:
- GPIO 17 → 틸트 서보
- GPIO 27 → 팬 서보
파이, 서보, 전원 공급장치 간 그라운드 공유. 선택적으로 파이 GPIO와 서보 데이터 핀 사이에 1kΩ 직렬 저항을 배치해 전류 스파이크로부터 파이 보호.
angleServoCtrl.py로 서보 제어 테스트:
from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
def setServoAngle(servo, angle):
pwm = GPIO.PWM(servo, 50)
pwm.start(8)
duty = angle / 18. + 3.
pwm.ChangeDutyCycle(duty)
sleep(0.3)
pwm.stop()
if __name__ == '__main__':
import sys
servo = int(sys.argv[1])
GPIO.setup(servo, GPIO.OUT)
setServoAngle(servo, int(sys.argv[2]))
GPIO.cleanup()
예: python angleServoCtrl.py 17 45로 틸트 서보를 45° 이동.
9단계: 객체 실시간 위치 찾기
카메라를 타겟 중심으로 맞추려면 먼저 객체의 중심 좌표가 필요합니다. objectDetect_LED.py를 수정해 x와 y 값 출력:
def mapObjectPosition(x, y):
print(f'[INFO] Object center at X={x}, Y={y}')
좌표 범위는 x가 0–500 (좌→우), y가 0–350 (상→하). 객체를 움직이며 실시간 출력 관찰.
10단계: 객체 위치 추적 시스템
"중심" 영역 정의 (예: 220 < x < 280 및 160 < y < 210). 객체가 영역 밖이면 팬/틸트 서보 조정:
def mapServoPosition(x, y):
global panAngle, tiltAngle
if x < 220:
panAngle = min(panAngle + 10, 140)
positionServo(panServo, panAngle)
if x > 280:
panAngle = max(panAngle - 10, 40)
positionServo(panServo, panAngle)
if y < 160:
tiltAngle = min(tiltAngle + 10, 140)
positionServo(tiltServo, tiltAngle)
if y > 210:
tiltAngle = max(tiltAngle - 10, 40)
positionServo(tiltServo, tiltAngle)
헬퍼 positionServo(servo, angle)는 단순히 angleServoCtrl.py 호출:
import os
def positionServo(servo, angle):
os.system(f'python angleServoCtrl.py {servo} {angle}')
print(f'[INFO] Positioning servo GPIO {servo} to {angle}°')
모든 스크립트는 같은 디렉토리에 있으며, 완전 구현은 objectDetectTrack.py에서 다운로드 가능.
11단계: 결론
이 프로젝트는 OpenCV, 라즈베리파이 GPIO, 팬-틸트 서보 마운트를 결합해 자율 색상 객체 추적 시스템을 만드는 방법을 시연합니다. 다양한 색상, 형태, 서보 속도로 실험해 보세요.
전체 코드와 추가 리소스는 GitHub와 MJRoBot.org 블로그에서 확인 가능.
읽어주셔서 감사합니다 – 다음 튜토리얼에서 뵙겠습니다!