Controlar Relé através de Visão Computacional

Neste artigo iremos desenvolver um programa capaz de localizar a nossa mão no espaço e identificar a posição em que se encontra (aberta ou fechada) abre ou fecha um relé. Para seguir este tutorial iremos necessitar dos seguintes artigos:

Imagem Produto Comprar
 

 

 

NodeMcu Esp8266

 


 

 

 

Relé 5v 1 Canal

 


 

 

 

Cabos Jumper Femêa-Femêa

 


Preparar ambiente

Para elaborar este protejo é necessário ter instalado na sua máquina:

  • Python 3.7 ou 3.8 – Link Download;
  • IDE a sua escolha – Recomendamos o VSCode
  • Instalar Bibliotecas NumPy, OpenCV2 e Mediapipe – Abrir a sua linha de comandos e entrar os seguintes comandos:
Sobre o MediaPipe

O mediapipe é um FramwWork de Machine Learning fornecido pelo Google treinado para identificação de rostos, mãos, corpo, identificação de objetos, etc… de forma fácil e precisa. Neste tutorial iremos utilizar o modelo de rastreamento de mãos.

Mediapipe Hands

Este modelo é capaz de identificar 20 pontos no chave nas mãos no espaço, este pontos são denominados por LandMarks e são os seguintes:

Hand Land Marks – hlm

Este modelo irá retomar um array de duas dimensões constituído pelas coordenadas de cada Land Mark (X,Y) no seguinte formato:

[[0, X, Y], [1, X, Y], [2, X, Y], [3, X, Y], [4, X, Y], [5, X, Y]…. [20, X, Y]]

Como vai funcionar

Sendo que através deste array de landmark points conseguimos identificar a posição de cada ponto no espaço, podendo assim inferir se a mão se encontra aberta ou fechada, por exemplo, se o Ponto 8 tiver a posição em Y inferior a posição Y de 6 podemos assumir que o dedo se encontra fechado, caso seja maior o dedo encontra-se para cima. Aplicamos a mesma lógica aos outros 4 dedos para verificar se a mão se encontra aberta ou fechada.

Dependendo da posição da mão enviaremos um Socket especifico para o nosso NodeMCU que irá interpretar o seu conteúdo e ligar ou desligar o GPIO definido..

Esquema de Montagem
Esquema de Montagem
Código NodeMCU
#include "ESP8266WiFi.h"

const char* ssid = "SSID"; //Substituir por SSID
const char* password = "WIFI"; //Substituir por Passowrd

WiFiServer wifiServer(9999); //Mesma porta que o IP em RUN.PY

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);

delay(1000);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting..");
}

Serial.print("Connected to WiFi. IP:");
Serial.println(WiFi.localIP());

wifiServer.begin();
}

void loop() {
WiFiClient client = wifiServer.available();

if (client) {
while (client.connected()) {
while (client.available() > 0) {
char c = client.read();
processReceivedValue(c);
Serial.write(c);
}

delay(10);
}

client.stop();
Serial.println("Client disconnected");
}
}

//Descodificar array para string
void processReceivedValue(char command) {
if (command == '1') {
digitalWrite(LED_BUILTIN, HIGH);
}

else if (command == '0') {
digitalWrite(LED_BUILTIN, LOW);
}
return;
}

Neste código deverá substituir o SSID e Password pelos dados da sua rede, de seguida execute envie este código e abra o monitor de serie de forma a guardar o endereço IP fornecido

Estrutura de Ambiente

De forma a evitarmos ficar com um código longo e confuso iremos dividir o projeto em 2 ficheiros:

  1. HandTrackingModule.py – Ficheiro responsavel por criar o objeto de mediapipe responsavel por identificar a posição da mão e os seus landamarks.
  2. run.py – Programa principal que irá interpretar os dados fornecidos pelo HandTrackingModule.py e comunicar com o nosso NodeMCU.

Observação: O ficheiro HandTrackingModule.py foi elaborado de forma generica com o preposito de poder ser aproveitado para os seus futuros projetos.

O nosso ambiente deverá ter a seguinte estrutura:

  • Ficheiro: run.py
  • Pasta: handController
    • Ficheiro: HandTrackingModule.py
Ficheiro HandTrackingModule.py
import cv2
import mediapipe as mp
import time

class handDetector():
def __init__(self, mode = False, maxHands = 2, detectionCon = 0.5, trackConf = 0.5):
self.mode = mode
self.maxHands = maxHands
self.detectionCon = detectionCon
self.trackConf = trackConf

self.mpHands = mp.solutions.hands
self.hands = self.mpHands.Hands(self.mode, self.maxHands, self.detectionCon, self.trackConf)
self.mpDraw = mp.solutions.drawing_utils

def findHands(self, img, draw = True):
imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
self.results = self.hands.process(imgRGB)

if self.results.multi_hand_landmarks:
for handLms in self.results.multi_hand_landmarks:
if draw:
self.mpDraw.draw_landmarks(img, handLms, self.mpHands.HAND_CONNECTIONS)
return img

def findPosition(self, img, handNo = 0, draw = True, size = 15):
lmList = []
if self.results.multi_hand_landmarks:
myHand = self.results.multi_hand_landmarks[handNo]
for id, lm in enumerate(myHand.landmark):
h, w, c = img.shape
cx, cy = int(lm.x * w), int(lm.y * h)
lmList.append([id, cx, cy])

if draw:
cv2.circle(img, (cx, cy), size, (255, 0, 255), cv2.FILLED)

return lmList

def main():
cap = cv2.VideoCapture(0)
detector = handDetector()

while True:
sucess, img = cap.read() 
img = detector.findHands(img)
lmList = detector.findPosition(img)

if len(lmList) != 0:
print(lmList[4])

if __name__ == "__main__":
main()
Ficheiro run.py
import cv2
import socket
from time import sleep
from handController import HandTrackingModule as htm

def sendSocket(status):
HOST = "" # Ip nodemcu
PORT = 9999 # Porta usada, deve ser a mesma indicada no nodemcu

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))

if status == True:
s.sendall(b'1')
sleep(0.2)

if status == False:
s.sendall(b'0')
sleep(0.2)

def startHandTracking():

wCam, hCam = 640, 480
cap = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
detector = htm.handDetector(detectionCon=0.75)

status = False

while True:
success, img = cap.read()
img = detector.findHands(img, draw = False)

tipsIDs = [8, 12, 16, 20]
firstTime = True
lmList = detector.findPosition(img, draw = False)
if len(lmList) != 0:
fingers = []

for id in range(0, 4):
if lmList[tipsIDs[id]][2] < lmList[tipsIDs[id] - 2][2]:
fingers.append(1)
else:
fingers.append(0)

totalFingers = fingers.count(1)

if totalFingers == 4 and status == True:
print("Enviar Request de Desligar")
sendSocket(status)
status = False
elif totalFingers == 0 and status == False:
print("Enviar Request para Ligar")
sendSocket(status)
status = True
else:
pass

cv2.imshow("Image", img)
cv2.waitKey(1)

def main():
startHandTracking()

if __name__ == "__main__":
main()

Caso use uma webcam externa deverá substituir cap = cv2.VideoCapture(0) por cap = cv2.VideoCapture(1)

 

 
Para mais projetos, percorram o nosso blog, onde podem encontrar vários artigos interessantes relacionados com eletrónica, robótica e muito mais! Visitem também o nosso site, onde encontram tudo para eletrónica e robótica!