본문 바로가기

ESP32

ESP32 및 Python을 사용한 손동작 인식

반응형

 

이 ESP32 프로젝트는 손동작을 사용하여 LED를 제어하는 흥미롭고 인터랙티브한 방법입니다. 특정 손동작을 감지하고 이를 LED를 제어하는 동작으로 변환할 수 있는 시스템을 만들기 위해 ESP32 보드, Python, MediaPipe 및 OpenCV를 사용할 것입니다.

 

 

 

 

손 제스처를 인식하는 데는 MediaPipe를 사용하고, 웹캠에서 실시간 비디오 피드를 캡처하는 데는 OpenCV를 사용합니다. 제스처(예: 손, 주먹, 손가락 움직임)에 따라 명령이 ESP32로 전송되고, ESP32는 연결된 LED를 제어합니다. 이를 통해 LED를 켜고 끄고 밝기를 변경하거나 특정 제스처로 다른 LED를 제어할 수도 있습니다. 제스처 인식, 하드웨어 제어, 파이썬과 마이크로컨트롤러 간의 통신에 대해 실용적이고 재미있게 배울 수 있는 방법입니다. 

 

개요

 

이 프로젝트는 Python, OpenCV, ESP32 마이크로컨트롤러의 조합을 통해 손동작을 사용하여 LED를 제어하는 방법을 보여줍니다. 손 제스처는 웹캠을 통해 인식되고 손 추적 및 제스처 인식을 위해 설계된 라이브러리인 MediaPipe로 처리됩니다. 감지된 각 제스처는 LED를 켜거나 끄는 등의 LED 제어 명령에 매핑됩니다. 이를 통해 간단한 손동작으로 LED를 인터랙티브하게 제어할 수 있습니다.

 

손 제스처에서 생성된 명령은 HTTP 요청을 통해 Wi-Fi를 통해 무선으로 ESP32로 전송됩니다. 서버 역할을 하는 ESP32는 이러한 명령을 수신하고 GPIO 핀에 연결된 LED를 제어합니다. 이 프로젝트는 컴퓨터 비전, 임베디드 시스템 및 네트워킹을 통합하여 제스처 기반 제어 시스템, IoT 및 하드웨어-소프트웨어 통합에 관심이 있는 초보자에게 훌륭한 시작점을 제공합니다. 

 

팔요한 구성품

1. ESP32 Board - 1 ( ESP32 is used as the controller for handling commands sent based on the hand gestures recognized by the camera )

2. LEDs (5 pieces) for each finger (Thumb, Index, Middle, Ring, Pinky)

3. Resistors (Five 220 Ohms for each LEDs )

4. Jumper wires ( Male to Female )

5. Breadboard 

 

회로도 

 

 

ESP32를 사용하여 5개의 LED를 제어하려면 각 LED가 고유한 손 제스처에 해당하는 ESP32의 특정 GPIO 핀에 연결됩니다. 각 LED의 양극 단자(양극)는 LED를 제어할 GPIO 핀 14, 27, 26, 25, 33과 같은 전용 GPIO 핀에 연결됩니다. 이러한 GPIO 핀은 각 LED를 켜는 데 필요한 전압을 제공합니다. 과도한 전류로 인해 LED 또는 ESP32가 손상되는 것을 방지하기 위해 220Ω 또는 330Ω 저항이 각 LED에 직렬로 연결됩니다. 저항은 LED를 통해 흐르는 전류를 제한하여 안전한 작동을 보장하기 때문에 매우 중요합니다. 

 

 

 

 

그런 다음 각 LED의 음극 단자(음극)를 ESP32의 접지(GND)에 연결합니다. 이렇게 하면 각 LED에 대한 완전한 전기 회로가 설정됩니다. GPIO 핀을 HIGH로 설정하면 LED의 양극에 전압이 전송되어 LED가 켜집니다. 반대로 GPIO 핀을 LOW로 설정하면 LED가 꺼집니다. 올바른 연결을 통해 손동작 제어 시스템은 감지된 제스처에 따라 각 LED를 효과적으로 켜거나 끌 수 있으므로 다양한 애플리케이션에 유용하고 인터랙티브하게 설정할 수 있습니다. 

 

 

손 제스처로 LED를 제어하는 ESP32 Arduino 코드

 

 

이제 이 사운드 현지화 프로젝트의 코드 로직과 기능을 분석해 보겠습니다.

 

이 코드는 엄지, 검지, 중지, 약지, 새끼 손가락에 해당하는 5개의 LED를 제어하는 웹 서버로 ESP32를 설정합니다. 각 LED는 특정 GPIO 핀에 연결되며 HTTP 요청을 통해 켜거나 끌 수 있습니다. ESP32는 제공된 자격 증명을 사용하여 WiFi에 연결되며, 일단 연결되면 각 LED를 제어하기 위한 엔드포인트를 제공합니다. 사용자가 LED에 해당하는 URL(예: "/led/thumb/on")에 접속하면 해당 LED가 켜지고 메시지와 함께 응답합니다. 서버는 포트 80에서 작동하며 LED를 제어하기 위한 요청을 처리합니다.

 

이제 사용된 라이브러리부터 코드를 분석해 보겠습니다.

 

사용된 라이브러리 라이브러리

 

라이브러리 관리자에서 사용 가능한 ESPAsyncWebServer

Wire-기본 라이브러리

 

전체 ESP32 코드 

 

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
const char* ssid = "Wifi_name";
const char* password = "Wifi_password";
AsyncWebServer server(80);
// Define GPIO pins for LEDs
const int thumbLedPin = 27; 
const int indexLedPin = 26; 
const int middleLedPin = 25;
const int ringLedPin = 33; 
const int pinkyLedPin = 32;
void setup() {
 Serial.begin(115200);
 pinMode(thumbLedPin, OUTPUT);
 pinMode(indexLedPin, OUTPUT);
 pinMode(middleLedPin, OUTPUT);
 pinMode(ringLedPin, OUTPUT);
 pinMode(pinkyLedPin, OUTPUT);
 digitalWrite(thumbLedPin, LOW);
 digitalWrite(indexLedPin, LOW);
 digitalWrite(middleLedPin, LOW);
 digitalWrite(ringLedPin, LOW);
 digitalWrite(pinkyLedPin, LOW);
 Serial.println("Connecting to WiFi...");
 WiFi.begin(ssid, password);
 int attempts = 0;
 while (WiFi.status() != WL_CONNECTED && attempts < 20) {  // Try for 20 seconds
   delay(1000);
   Serial.print(".");
   attempts++;
 }
 if (WiFi.status() == WL_CONNECTED) {
   Serial.println("\nConnected to WiFi");
   Serial.print("IP Address: ");
   Serial.println(WiFi.localIP());
   // Define HTTP request handlers for each LED
   server.on("/led/thumb/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(thumbLedPin, HIGH);
     request->send(200, "text/plain", "Price - 300");
   });
   server.on("/led/thumb/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(thumbLedPin, LOW);
     request->send(200, "text/plain", "Thumb LED is OFF");
   });
   server.on("/led/index/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(indexLedPin, HIGH);
     request->send(200, "text/plain", "Index finger LED is ON");
   });
   server.on("/led/index/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(indexLedPin, LOW);
     request->send(200, "text/plain", "Index finger LED is OFF");
   });
   server.on("/led/middle/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(middleLedPin, HIGH);
     request->send(200, "text/plain", "Middle finger LED is ON");
   });
   server.on("/led/middle/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(middleLedPin, LOW);
     request->send(200, "text/plain", "Middle finger LED is OFF");
   });
   server.on("/led/ring/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(ringLedPin, HIGH);
     request->send(200, "text/plain", "Ring finger LED is ON");
   });
   server.on("/led/ring/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(ringLedPin, LOW);
     request->send(200, "text/plain", "Ring finger LED is OFF");
   });
   server.on("/led/pinky/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(pinkyLedPin, HIGH);
     request->send(200, "text/plain", "Pinky finger LED is ON");
   });
   server.on("/led/pinky/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(pinkyLedPin, LOW);
     request->send(200, "text/plain", "Pinky finger LED is OFF");
   });
   server.begin();
   Serial.println("Server started");
 } else {
   Serial.println("\nFailed to connect to WiFi");
 }
}
void loop() {
 // Additional code can be added here if needed, but typically not necessary for basic HTTP server operations.
}

 

 

코드 설명 

 

  • 초기화: ESP32는 직렬 통신을 115200 보드로 초기화하고 LED용 GPIO 핀(27, 26, 25, 33, 32)을 설정합니다.
  • LED 설정: 초기에는 GPIO 핀을 LOW로 설정하면 모든 LED가 꺼집니다.
  • WiFi 연결: ESP32는 제공된 SSID와 비밀번호를 사용하여 지정된 WiFi 네트워크에 연결을 시도합니다.
  • WiFi 상태 확인: 연결에 성공하면 IP 주소를 인쇄하고, 실패하면 최대 20초 동안 연결을 시도합니다.
  • 서버 설정: WiFi에 연결되면 ESP32는 포트 80에서 HTTP 서버를 시작합니다.
  • 엔드포인트 정의: 각 LED를 켜거나 끄기 위한 URL(예: "/led/thumb/on")과 함께 각 LED에 대해 HTTP GET 요청 핸들러가 정의됩니다.
  • LED 제어: 특정 엔드포인트에서 요청이 수신되면(예: "/led/thumb/on") 해당 LED가 켜지거나 꺼집니다.
  • 응답: 각 HTTP 요청은 "엄지 손가락 LED가 켜짐" 또는 "가격 - 300"과 같은 확인 메시지와 함께 응답을 보냅니다.
  • 서버 시작: 서버가 수신 요청을 수신 대기하기 시작하고 웹 엔드포인트를 통해 LED 제어 기능을 제공합니다.
  • Loop: 루프() 함수는 비어 있습니다. 서버가 비동기적으로 실행되므로 루프에서 지속적으로 확인할 필요 없이 요청을 처리합니다. 

 

OpenCV와 미디어 파이프를 사용한 파이썬 코드

 

이 코드는 OpenCV와 미디어파이프를 사용하여 웹캠을 통해 손동작을 인식합니다. 각 손가락의 상태(위 또는 아래)를 감지하여 LED를 켜거나 끄는 등 해당 제어 명령을 ESP32로 보냅니다. ESP32의 IP 주소가 설정되면 서버와 통신하여 HTTP 요청을 통해 명령을 주고받습니다. 이 코드는 모든 손가락이 내려왔는지 확인하고 내려진 경우 특수 명령을 보냅니다. 손동작 인식이 화면에 표시되고, ESP32 명령이 실시간으로 표시됩니다.

 

사용한 라이브러리

 

pip install opencv-python

pip install mediapipe

pip install 요청

 

전체 파이썬 코드 

 

import cv2
import mediapipe as mp
import requests
# ESP32 Base URL
ESP32_IP = "http://192.168.137.123"   # Change this to your ESP32 IP address
# Initialize MediaPipe Hands
mp_hands = mp.solutions.hands
hands = mp_hands.Hands()
mp_drawing = mp.solutions.drawing_utils
# Function to send hand gesture commands to ESP32
def control_led(endpoint):
   url = f"{ESP32_IP}/cart/{endpoint}"
   try:
       response = requests.get(url)
       print(f"Sent command: {endpoint}, ESP32 Response: {response.text}")
   except Exception as e:
       print(f"Failed to send command: {endpoint}, Error: {e}")
# Function to fetch commands from ESP32
def fetch_esp32_command():
   try:
       url = f"{ESP32_IP}/command"
       response = requests.get(url)
       return response.text.strip()
   except Exception as e:
       print(f"Error fetching command from ESP32: {e}")
       return None
# Function to detect the state of each finger
def count_fingers(hand_landmarks):
   # Detect finger states (up or down)
   thumb_up = hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP].x < hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_IP].x
   index_up = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP].y < hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_PIP].y
   middle_up = hand_landmarks.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_TIP].y < hand_landmarks.landmark[mp_hands.HandLandmark.MIDDLE_FINGER_PIP].y
   ring_up = hand_landmarks.landmark[mp_hands.HandLandmark.RING_FINGER_TIP].y < hand_landmarks.landmark[mp_hands.HandLandmark.RING_FINGER_PIP].y
   pinky_up = hand_landmarks.landmark[mp_hands.HandLandmark.PINKY_TIP].y < hand_landmarks.landmark[mp_hands.HandLandmark.PINKY_PIP].y
   # Combine finger statuses into a list
   finger_status = [thumb_up, index_up, middle_up, ring_up, pinky_up]
   # Send control commands to ESP32 for each finger
   control_led("add" if thumb_up else "remove")
   control_led("index/on" if index_up else "index/off")
   control_led("middle/on" if middle_up else "middle/off")
   # Check if all fingers are down
   if not any(finger_status):
       print("All fingers are down")  # Message when all fingers are down
       control_led("all/down")  # Example action when all fingers are down
   return finger_status
# Initialize VideoCapture
cap = cv2.VideoCapture(1)
while cap.isOpened():
   ret, frame = cap.read()
   if not ret:
       break
   frame = cv2.flip(frame, 1)
   frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
   # Detect hand landmarks
   results = hands.process(frame_rgb)
   if results.multi_hand_landmarks:
       for hand_landmarks in results.multi_hand_landmarks:
           mp_drawing.draw_landmarks(frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)
           fingers = count_fingers(hand_landmarks)
   # Fetch and display command from ESP32
   esp32_command = fetch_esp32_command()
   if esp32_command:
       cv2.putText(frame, f"ESP32 Command: {esp32_command}", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
   cv2.imshow('Hand Gesture Recognition', frame)
   if cv2.waitKey(5) & 0xFF == 27:  # Exit on pressing 'Esc'
       break
cap.release()
cv2.destroyAllWindows()

 

 

파이썬 코드 설명

 

  • 라이브러리 초기화: 비디오 캡처와 손동작 인식을 처리하기 위해 OpenCV와 MediaPipe를 가져옵니다.
  • ESP32 IP 설정: HTTP 요청을 송수신하는 데 사용되는 ESP32 IP 주소를 정의합니다. 미디어파이프 핸즈 초기화: 비디오 피드에서 손 랜드마크를 감지하고 처리하기 위해 MediaPipe의 손 추적 모델이 초기화됩니다.
  • control_led 함수 정의: 이 함수는 손 제스처에 따라 LED를 제어하기 위해 ESP32에 명령을 보냅니다(예: LED 켜기 또는 끄기).
  • fetch_esp32_command 함수 정의: 이 함수는 특정 URL로 HTTP GET 요청을 전송하여 ESP32에서 명령을 가져옵니다.
  • count_fingers 함수 정의: 이 함수는 손의 랜드마크를 분석하여 각 손가락이 위 또는 아래인지 확인하고 해당 LED 제어 명령을 ESP32로 보냅니다.
  • 비디오 캡처: 이 코드는 비디오 피드(카메라)를 열고 처리할 프레임을 캡처합니다.
  • 손 랜드마크 처리: MediaPipe는 각 프레임을 처리하여 손의 랜드마크를 감지하고 각 손가락의 상태(위 또는 아래)를 식별합니다.
  • ESP32로 명령 보내기: 감지된 손가락 상태를 기반으로 ESP32로 명령을 전송하여 LED를 제어합니다(예: 특정 LED 켜기 또는 끄기).
  • 결과 표시: 손 제스처와 ESP32 명령은 OpenCV를 사용하여 화면에 실시간으로 표시되며, 사용자가 'Esc' 키를 누를 때까지 프로그램이 계속 영상을 처리합니다.
  • 기억해야 할 핵심 사항: "http://192.168.x.x"를 Ardu의 실제 IP 주소로 바꿔야 합니다. 

 

ESP32 손 제스처 프로젝트 - 워킹 데모

 

이 시스템은 손 제스처를 사용하여 LED를 직관적으로 제어할 수 있습니다. 이 프로젝트는 MediaPipe를 사용하여 손가락의 위치를 감지하여 어떤 손가락을 들어 올리는지 식별하고 해당 명령을 ESP32로 보냅니다. 각 손가락(엄지, 검지, 중지, 약지, 새끼손가락)은 특정 LED에 매핑됩니다. 손가락을 올리면 관련 LED가 켜지고, 내리면 LED가 꺼집니다. 이 프로젝트는 이러한 제어 명령을 HTTP 요청을 통해 ESP32로 전송하므로 여러 개의 LED를 실시간으로 쉽게 제어할 수 있습니다. 또한 손가락을 모두 내리면 시스템이 모든 LED를 끄는 등 미리 정의된 동작을 트리거합니다. 이 솔루션은 손동작 인식을 IoT와 통합하여 사용자와 ESP32 하드웨어 간의 원활한 상호 작용을 가능하게 합니다. 

 

 

이 프로젝트의 소스 코드를 찾을 수 있는 GitHub 리포지토리 링크는 다음과 같습니다. 

 

ESP32 전체 코드는 아래와 같습니다.

 

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
const char* ssid = "Wifi_name";
const char* password = "Wifi_password";
AsyncWebServer server(80);
// Define GPIO pins for LEDs
const int thumbLedPin = 27; 
const int indexLedPin = 26; 
const int middleLedPin = 25;
const int ringLedPin = 33; 
const int pinkyLedPin = 32;
void setup() {
 Serial.begin(115200);
 pinMode(thumbLedPin, OUTPUT);
 pinMode(indexLedPin, OUTPUT);
 pinMode(middleLedPin, OUTPUT);
 pinMode(ringLedPin, OUTPUT);
 pinMode(pinkyLedPin, OUTPUT);
 digitalWrite(thumbLedPin, LOW);
 digitalWrite(indexLedPin, LOW);
 digitalWrite(middleLedPin, LOW);
 digitalWrite(ringLedPin, LOW);
 digitalWrite(pinkyLedPin, LOW);
 Serial.println("Connecting to WiFi...");
 WiFi.begin(ssid, password);
 int attempts = 0;
 while (WiFi.status() != WL_CONNECTED && attempts < 20) {  // Try for 20 seconds
   delay(1000);
   Serial.print(".");
   attempts++;
 }
 if (WiFi.status() == WL_CONNECTED) {
   Serial.println("\nConnected to WiFi");
   Serial.print("IP Address: ");
   Serial.println(WiFi.localIP());
   // Define HTTP request handlers for each LED
   server.on("/led/thumb/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(thumbLedPin, HIGH);
     request->send(200, "text/plain", "Price - 300");
   });
   server.on("/led/thumb/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(thumbLedPin, LOW);
     request->send(200, "text/plain", "Thumb LED is OFF");
   });
   server.on("/led/index/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(indexLedPin, HIGH);
     request->send(200, "text/plain", "Index finger LED is ON");
   });
   server.on("/led/index/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(indexLedPin, LOW);
     request->send(200, "text/plain", "Index finger LED is OFF");
   });
   server.on("/led/middle/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(middleLedPin, HIGH);
     request->send(200, "text/plain", "Middle finger LED is ON");
   });
   server.on("/led/middle/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(middleLedPin, LOW);
     request->send(200, "text/plain", "Middle finger LED is OFF");
   });
   server.on("/led/ring/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(ringLedPin, HIGH);
     request->send(200, "text/plain", "Ring finger LED is ON");
   });
   server.on("/led/ring/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(ringLedPin, LOW);
     request->send(200, "text/plain", "Ring finger LED is OFF");
   });
   server.on("/led/pinky/on", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(pinkyLedPin, HIGH);
     request->send(200, "text/plain", "Pinky finger LED is ON");
   });
   server.on("/led/pinky/off", HTTP_GET, [](AsyncWebServerRequest *request){
     digitalWrite(pinkyLedPin, LOW);
     request->send(200, "text/plain", "Pinky finger LED is OFF");
   });
   server.begin();
   Serial.println("Server started");
 } else {
   Serial.println("\nFailed to connect to WiFi");
 }
}
void loop() {
 // Additional code can be added here if needed, but typically not necessary for basic HTTP server operations.
}

 

 

포스팅 참고 문서는 이 링크를 따라가면 만날 수 있습니다.

 

 

반응형

더욱 좋은 정보를 제공하겠습니다.~ ^^