Logo Studenta

PRACTICA 8

¡Este material tiene más páginas!

Vista previa del material en texto

INSTITUCIÓN: TECNOLÓGICO DE LA LAGUNA 
 
CARRERA: MECATRÓNICA 
 
MATERIA: SENSORES INTELIGENTES 
 
DOCENTE: HERRERA CARRILLO NAZLE EDITH 
 
ALUMNO: RODRÍGUEZ GUERRA EDUARDO ANTONIO 
 
MATRICULA: 19131252 
 
PRÁCTICA 8: DETECCIÓN DE TRES COLORES EN VIDEO 
 
FECHA DE ENTREGA: 20 DE ABRIL 2023 
 
Descripción de la práctica: 
Retomando lo aprendido en la practica 7, elaboraremos un programa en Python capaz de 
reconocer tres colores diferentes y activar tres actuadores de acuerdo a los colores detectados. 
Cabe destacar que los actuadores los activaremos desde el Arduino, gracias a la librería serial que 
ofrece Python. 
En mi caso decidí activar un servo capaz de moverse en ángulos diferentes dependiendo de la 
ubicación el color azul fuerte, también detecta el color rojo, accionando un motor a pasos y por 
último dos leds que se activan solo con la presencia del color amarillo, si esta a la izquierda prende 
el led 1, y si están a la derecha prende el led dos. 
DIAGRAMA ELÉCTRICO DE CONEXIONES: 
Se empleo del puente H (LS298N) en sustitución del driver correspondiente del motor a pasos 
(28BYJ-48), también use un servomotor de 180° y por último se usó de 2 leds. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ANALOG IN
ATMEGA328P-PU
1121
~
~
~ ~
~
~
T
X
R
X
Reset BTN
ON
www.TheEngineeringProjects.com
P
D
0
/R
X
D
0
P
D
1
/T
X
D
1
P
D
2
/I
N
T
0
2
P
D
3
/I
N
T
1
3
P
D
4
/T
0
/X
C
K
4
P
D
5
/T
1
5
P
D
6
/A
IN
0
6
P
D
7
/A
IN
1
7
P
B
0
/I
C
P
1
/C
L
K
O
8
P
B
1
/O
C
1
A
9
P
B
2
/S
S
/O
C
1
B
1
0
P
B
3
/M
O
S
I/
O
C
2
A
1
1
P
B
4
/M
IS
O
1
2
P
B
5
/S
C
K
1
3
A
R
E
F
P
C
5
/A
D
C
5
/S
C
L
A
5
P
C
4
/A
D
C
4
/S
D
A
A
4
P
C
3
/A
D
C
3
A
3
P
C
2
/A
D
C
2
A
2
P
C
1
/A
D
C
1
A
1
P
C
0
/A
D
C
0
A
0
R
E
S
E
T
V
C
C
G
N
D
ARD1
ARDUINO UNO
+88.8
220
35V
RVT
+12V GND +5V
OUT4
OUT3
OUT1
OUT2
ENA IN1 IN2 IN3 IN4 ENB
www.TheEngineeringProjects.com
L1
L298 MOTOR DRIVER
D1
LED-BLUE
D2
LED-RED
B1
5V
+88.8
5V
GND
5
V
G
N
D
GND
G
N
D
5
V
Ya que es la primera vez que usaba un motor a pasos opte por acudir a la datasheet 
correspondiente 
 
Y realizar las conexiones conforme se indica 
 
 
 
 
 
 
 
 
 
Código de Arduino: 
#include <Servo.h> 
Servo servo1; 
//_____________________________________ 
#include <Stepper.h> //libreria a usar 
Stepper motorpasos(2048, 8, 9, 10, 11); 
//______________________________________ 
String entradaSerial = ""; // String para almacenar entrada 
bool entradaCompleta = false; // Indicar si el String está completo 
int pin = 6; // pin de conexión PWM al servo 
//int angulo = 0; // Variable para guardar el angulo que deseamos 
de giro 
int IN1=2; 
int IN2=4; 
void setup() 
{ 
 servo1.attach(pin, 580, 2500); 
 Serial.begin(9600); 
 pinMode(IN1, OUTPUT); //DECLARAMOS COMO SALIDAS LOS PINES QUE HARAN 
 pinMode(IN2, OUTPUT); //girar al motor de directa 
 motorpasos.setSpeed(3); //velocidad motor a pasos, valor maximo 
} 
void loop() 
{ 
 if (entradaCompleta) 
 { 
 if (entradaSerial == "izq1\n") 
 { 
 servo1.write(0); 
 } 
 else if (entradaSerial == "izq2\n") 
 { 
 servo1.write(30); 
 } 
 else if (entradaSerial == "izq3\n") 
 { 
 servo1.write(60); 
 } 
 else if (entradaSerial == "ctr\n") 
 { 
 servo1.write(90); 
 } 
 else if (entradaSerial == "der3\n") 
 { 
 servo1.write(120); 
 } 
 else if (entradaSerial == "der2\n") 
 { 
 servo1.write(150); 
 } 
 else if (entradaSerial == "der1\n") 
 { 
 servo1.write(180); 
 } 
 else if (entradaSerial == "dcder1\n") 
 { 
 digitalWrite(IN1,LOW); 
 //delay(10); 
 digitalWrite(IN2,HIGH); 
 } 
 else if (entradaSerial == "dciz2\n") 
 { 
 digitalWrite(IN2,LOW); 
 //delay(10); 
 digitalWrite(IN1,HIGH); 
 } 
 else if (entradaSerial == "pasos1\n") 
 { 
 motorpasos.step(64); 
 delay(20); 
 } 
 else if (entradaSerial == "pasos2\n") 
 { 
 motorpasos.step(-64); 
 delay(20); 
 
 } 
 else 
 { 
 Serial.println("El dato recibido es inválido!!"); 
 } 
 entradaSerial = ""; 
 entradaCompleta = false; 
 } 
 //digitalWrite(IN2,LOW); //EN CASO DE QUE NO HAYA INSTRUCCIONES 
 //digitalWrite(IN1,LOW); //DETENDRE EL MOTOR DC 
} 
// Función que se activa al recibir algo por 
// el puerto serie, Interrupción del Puerto Serie. 
void serialEvent() 
{ 
 while (Serial.available()) 
 { 
 // Obtener bytes de entrada: 
 char inChar = (char)Serial.read(); 
 // Agregar al String de entrada: 
 entradaSerial += inChar; 
 // Para saber si el string está completo, se detendrá al recibir 
 // el caracter de retorno de línea ENTER \n 
 if (inChar == '\n') 
 { 
 entradaCompleta = true; 
 } 
 } 
} 
Básicamente establecemos conexión con el puerto serial y en caso de recibir alguna de las 
instrucciones correspondientes activar un accionador, ahora bien, el código se ejecuta una y otra 
vez a una muy alta velocidad, por ejemplo, si ponemos un objeto de color amarillo enfrente, el 
led no se quedará activado si no que se activará una y otra vez, pero a una alta velocidad que 
simula quedarse activado, además de que apenas retiremos el objeto el led se apagará. 
Cabe destacar que al seleccionar un motor a pasos presente algunas dificultades, ya que al 
seleccionar que el motor recorriera muchos pasos al registrar la presencia del color rojo, me 
encontré que si bien ejecutaba la instrucción el programa presentaba un delay muy grande, pues, 
el tiempo que demoraba en hacer el recorrido, era un tiempo en que el Arduino no recibía las 
instrucciones por parte de Python. Para solucionar eso fue cuestión de reducir la cantidad de 
pasos recorridos en cada loop del código, y a la hora de poner un objeto de color rojo, el programa 
funcionaba más fluido. 
Por último, hacer mención a que se usaron dos librerías (Servo.h) y (Stepper.h). 
 
 
 
 
 
 
 
 
Código de Python: 
#PRÁCTICA 8 DE SENSORES RECONOCIMIENTO DE 3 COLORES 
#INGRESAMOSLAS TRES LIBERIAS A UTILIZAR 
import cv2 
import numpy as np 
import serial 
#SELECCIONAMOS EL PUERTO COM DONDE SE CONECTE EL ARDUINO 
COM = 'COM5' 
#ESTABLECEMOS LA VELOCIDAD DE COMUNICACION 
BAUD = 9600 
#CREAMOS VARIABLE PARA GUARDAR LOS DATOS DEL PUERTO SERIAL 
ser = serial.Serial(COM, BAUD) 
#con la biblioteca open cv creamos variable para optener prestada la imagen de la camara web, en el caso 
#de mi equipo solo tengo una camara conectada por eso el cero. 
cap = cv2.VideoCapture(0) 
#definimos dos variables por color basadas enLA ESCALA de color HSV 
#azul 
azulBajo = np.array([90, 100, 20], np.uint8) 
azulAlto = np.array([125, 255, 255], np.uint8) 
#amarillo 
amarilloBajo = np.array([25, 100, 20], np.uint8) 
amarilloAlto = np.array([35, 255, 255], np.uint8) 
#rojo 
rojoBajo = np.array([165, 100, 20], np.uint8) 
rojoAlto = np.array([180, 255, 255], np.uint8) 
#Dentro del while tenemos el ciclo que se repetira constantemente 
while True: 
 #obtenemos dos variables, en ret se guarda un true o false indicando si se capturo una imagen 
 #en frame guarda la imagen capturada por la web cam 
 ret, frame = cap.read() 
 #el if ns permitira pocesar la imagen en caso de ser capturda 
 if ret: 
 #reflejamos la imagen para tener una perspectiva directa el movimiento 
 frame = cv2.flip(frame, 1) 
 #convertimos espacio de color BGR Formato de la imagen capturada con web cam a HSV 
 frameHSV = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) 
 #Creamos una mascara con el valor del color bajo y el color alto como los limites superior e inferior 
 mascaraazul = cv2.inRange(frameHSV, azulBajo, azulAlto) 
################################################################ 
 mascaraamarilla = cv2.inRange(frameHSV, amarilloBajo, amarilloAlto)############################################################### 
 mascararoja = cv2.inRange(frameHSV, rojoBajo, rojoAlto) 
############################################################### 
 #en la variable contornos guardamos todos los contornos encontrados en la mascara 
 contornosazul, _ = cv2.findContours(mascaraazul, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 
############################################################### 
 contornosamarillo, _ = cv2.findContours(mascaraamarilla, cv2.RETR_EXTERNAL, 
cv2.CHAIN_APPROX_SIMPLE) 
############################################################### 
 contornosrojo, _ = cv2.findContours(mascararoja, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 
############################################################### 
 #dibujamos el contorno sobre la imagen usando se comando, ademas de asignar un color para el contorno 
en ese caso es un azul fuerte en formato BGR 
 cv2.drawContours(frame, contornosazul, -1, (0, 0, 255), 4) 
############################################################### 
 cv2.drawContours(frame, contornosamarillo, -1, (0, 255, 0), 4) 
############################################################### 
 cv2.drawContours(frame, contornosrojo, -1, (255, 0, 0), 4) 
############################################################### 
 #verificamos los contornos obtenidos de la imagen 
 for c in contornosazul: 
 #revisamos el area de dichos contornos 
 area = cv2.contourArea(c) 
 #en caso de que el area sea mayor a 6000 buscamos un centroide 
 if area > 6000: 
 #calculamos los momentos 
 M = cv2.moments(c) 
 #si el momento m00 es cero, para evitar errores le asignamos un 1 
 if M["m00"] == 0: 
 M["m00"] = 1 
 #con esta divisiones encontramos las coordenadas X Y del centroide 
 x = int(M["m10"] / M["m00"]) 
 y = int(M['m01'] / M['m00']) 
 #dibujamos un circulo de color rojo de radio -7 ademas de rellenarlo con la variable -1 
 cv2.circle(frame, (x, y), 7, (0, 0, 255), -1) 
 #usamos una variable para insertar la tipografia 
 font = cv2.FONT_HERSHEY_SIMPLEX 
 #ingresamos el teto con las direcciones del centroide justo en la cooredenada (x + 10, y) 
 cv2.putText(frame, '{},{}'.format(x, y), (x + 10, y), font, 1.2, (0, 0, 255), 2, cv2.LINE_AA) 
 #creamos un nuevo contorno de forma convexa cerrando tos las aperturas que tenia el contorno 
real a fin de cerrar el contorno y respetar la forma de la imagen de color 
 nuevoContorno = cv2.convexHull(c) 
 #dibujamos el nuevo contorno 
 cv2.drawContours(frame, [nuevoContorno], 0, (255, 0, 0), 3) 
 #con este if dependiendo de la ubicacion del centroide en el eje de las X, el programa enviara un mensaje 
especifico por el serial port a fin de decirle al servo para donde tiene que moverse 
 #por ultimo escribimos la posicion en la consola de spyder 
 if x < 85: 
 print("SERVO izquierda 100%") 
 ser.write(b"izq1\n") 
 elif 85 > x > 170: 
 print("SERVO izquierda 60%") 
 ser.write(b"izq2\n") 
 elif 170 > x > 255: 
 print("SERVO izquierda 30%") 
 ser.write(b"izq3\n") 
 elif 255 < x < 340: 
 print("SERVO al centro") 
 ser.write(b"ctr\n") 
 elif 340 < x < 425: 
 print("SERVO derecha 30%") 
 ser.write(b"der3\n") 
 elif 425 < x < 510: 
 print("SERVO derecha 60%") 
 ser.write(b"der2\n") 
 elif x > 510: 
 print("SERVO derecha 100%") 
 ser.write(b"der1\n") 
############################################################### 
 for c2 in contornosamarillo: 
 #revisamos el area de dichos contornos 
 areaamarilla = cv2.contourArea(c2) 
 #en caso de que el area sea mayor a 6000 buscamos un centroide 
 if areaamarilla > 6000: 
 #calculamos los momentos 
 M2 = cv2.moments(c2) 
 #si el momento m00 es cero, para evitar errores le asignamos un 1 
 if M2["m00"] == 0: 
 M2["m00"] = 1 
 #con esta divisiones encontramos las coordenadas X Y del centroide 
 x2 = int(M2["m10"] / M2["m00"]) 
 y2 = int(M2['m01'] / M2['m00']) 
 #dibujamos un circulo de color rojo de radio -7 ademas de rellenarlo con la variable -1 
 cv2.circle(frame, (x2, y2), 7, (0, 0, 255), -1) 
 #usamos una variable para insertar la tipografia 
 font2 = cv2.FONT_HERSHEY_SIMPLEX 
 #ingresamos el teto con las direcciones del centroide justo en la cooredenada (x + 10, y) 
 cv2.putText(frame, '{},{}'.format(x2, y2), (x2 + 10, y2), font2, 1.2, (0, 255, 0), 2, 
cv2.LINE_AA) 
 #creamos un nuevo contorno de forma convexa cerrando tos las aperturas que tenia el contorno 
real a fin de cerrar el contorno y respetar la forma de la imagen de color 
 nuevoContornoamarillo = cv2.convexHull(c2) 
 #dibujamos el nuevo contorno 
 cv2.drawContours(frame, [nuevoContornoamarillo], 0, (0, 255, 0), 3) 
#con este if dependiendo de la ubicacion del centroide en el eje de las X, el programa enviara un mensaje 
especifico por el serial port a fin de decirle al servo para donde tiene que moverse 
#por ultimo escribimos la posicion en la consola de spyder 
 if x2 < 300: 
 print("MOTOR DC DERECHA") 
 ser.write(b"dcder1\n") 
 elif x2 > 300: 
 print("MOTOR DC IZQUIERDA") 
 #el mensaje que le mandamos al arduino debe ser en este formato 
 ser.write(b"dciz2\n") 
############################################################### 
############################################################### 
 for c3 in contornosrojo: 
 #revisamos el area de dichos contornos 
 arearoja = cv2.contourArea(c3) 
 #en caso de que el area sea mayor a 6000 buscamos un centroide 
 if arearoja > 6000: 
 #calculamos los momentos 
 M3 = cv2.moments(c3) 
 #si el momento m00 es cero, para evitar errores le asignamos un 1 
 if M3["m00"] == 0: 
 M3["m00"] = 1 
 #con esta divisiones encontramos las coordenadas X Y del centroide 
 x3 = int(M3["m10"] / M3["m00"]) 
 y3 = int(M3['m01'] / M3['m00']) 
 #dibujamos un circulo de color rojo de radio -7 ademas de rellenarlo con la variable -1 
 cv2.circle(frame, (x3, y3), 7, (0, 255, 0), -1) 
 #usamos una variable para insertar la tipografia 
 font3 = cv2.FONT_HERSHEY_SIMPLEX 
 #ingresamos el teto con las direcciones del centroide justo en la cooredenada (x + 10, y) 
 cv2.putText(frame, '{},{}'.format(x3, y3), (x3 + 10, y3), font3, 1.2, (0, 255, 0), 2, 
cv2.LINE_AA) 
 #creamos un nuevo contorno de forma convexa cerrando tos las aperturas que tenia el contorno 
real a fin de cerrar el contorno y respetar la forma de la imagen de color 
 nuevoContornorojo = cv2.convexHull(c3) 
 #dibujamos el nuevo contorno 
 cv2.drawContours(frame, [nuevoContornorojo], 0, (255, 0, 0), 3) 
#con este if dependiendo de la ubicaciondel centroide en el eje de las X, el programa enviara un mensaje 
especifico por el serial port a fin de decirle al servo para donde tiene que moverse 
#por ultimo escribimos la posicion en la consola de spyder 
 if x3 < 300: 
 print("PASOS DERECHA") 
 ser.write(b"pasos1\n") 
 elif x3 > 300: 
 print("PASOS IZQUIERDA") 
 ser.write(b"pasos2\n") 
############################################################### 
 #cv2.imshow('mascaraAzul', mascaraazul) 
 cv2.imshow('frame', frame) 
 if cv2.waitKey(1) & 0xFF == ord('s'): 
 ser.close() 
 break 
cap.release() 
cv2.destroyAllWindows() 
 
Todo ese código, lo que hace es emplear de tres librerías (numpy, serial y cv2) para establecer 
una comunicación serial, además de procesar las imágenes obtenidas desde la cámara del equipo 
y plasmarlas al usuario. 
Ya que usamos la comunicación serial del arduino establecemos una velocidad de transmisión de 
9600, y tomamos prestada la cámara de la computadora (0), en caso de que tengamos diferentes 
cámaras solo asignamos un numero deacuerdo a la cámara que deseemos usar. 
De acuerdo con la escala de colores HSV: 
 
Seleccionamos un rango de colores a detectar, indicado los limites superior e inferior. 
Después establecemos un ciclo while que se repetirá una y otra vez hasta resetear el Kernel de 
spyder. 
Dentro de dicho ciclo, primero convertimos la escala de colores de la imagen obtenida en hsv ya 
que las computadoras usan un tipo BGR, después inicializamos una mascara para cada color 
indicando el rango de color. En caso de que detecte alguna superficie con un área mayor a 600 y 
de uno de los colores programados, procederá a dibujar su contorno por encima de la imagen, 
para más tarde calcular el centroide de la imagen, y una vez lo tengamos dibujarlo en la imagen 
plasmada al usuario. 
Ahora bien, dependiendo de la ubicación que tenga dicho centroide en el eje de las x, mandará 
un mensaje diferente, en el caso del servomotor tengo programado que envié 7 mensajes 
diferentes, en caso de que sea el motor a pasos dos posibles respuestas, que activen el giro en 
un sentido u otro. 
Las ultimas partes del código están diseñadas a fin de cerrar el contorno dibujado ya que hay ua 
alta posibilidad de detectar áreas irregulares, entonces rellenaremos los huecos de contorno con 
trazos curvos. Y los centroides dibujados son de diferentes colores. 
EJECUCIÓN DE LA PRÁCTICA: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Para comprender aun mejor el funcionamiento del código en el caso de los bordes del área azul 
decidí dejar el primer borde detectado en color rojo y el dibujado al final con curvas en color azul 
para ser capaces de distinguir las diferencias entre un borde y otro. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Para energizar los actuadores decidí usar una fuente ajena al Arduino para evitar fallas. 
Conclusiones: 
Para comenzar me sorprendió el hecho de que el programa pudiera reconocer los tres colores al 
mismo tiempo, que si bien presentaba un delay menor a la hora de poner en marcha los 
programas. Decidí manipular aun más los pasos que daba en cada instrucción el motor a pasos 
hasta llevarlo a la menor cantidad, donde logre reducir dicho delay considerablemente, algo más 
que me parece trascendental destacar es el hecho de que dependiendo de la cámara que uses 
será la efectividad obtenida con la práctica, pues la cámara de mi laptop detecta bien los colores 
en mi casa, pero en el salón de clases donde hay mucha luz batallaba y tenia que acomodar los 
objetos para poderlos detectar, cosa que no sucedía con las computadoras de dos de mis 
compañeros donde decidí poner en marcha este código y detectando los colores a la primera, 
una de esas computadoras tenía una cámara de alta definición y otro traía su propia cámara. Al 
correr los códigos cuando cargo la laptop veo que presentar menores tiempos de delay al ser 
sometido a los tres colores, en cambio si uso la laptop con poca pila los delays incrementan. 
Por todo lo anterior destaco que para ser funcional este código debe ajustarse conforme la 
computadora donde se ejecute y el entorno en que se ejecute pues quizás si hubiese considerado 
colores más oscuros hubiese logrado una mayor precisión en el salón de clases.

Continuar navegando