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:
- pip install numpy – Documentação
- pip install opencv-python – Documentação
- pip install mediapipe – Documentação
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:
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
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:
- HandTrackingModule.py – Ficheiro responsavel por criar o objeto de mediapipe responsavel por identificar a posição da mão e os seus landamarks.
- 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!