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!



