Description :
Yeux animatroniques pilotés à l’aide d’une PI caméra et Open CV pour le suivi de 2 couleurs (vert et jaune).
Un microcontrôleur Arduino est chargé d’orienter les servos grâce à la réception de coordonnées envoyées grâce à un code python.
La conception des yeux avec la caméra embarquée a été faite de façon à ce que chaque assemblage dispose d’un écrou et d’une vis afin de rendre le montage fiable et fluide dans le temps. Chaque pièce a été faite pour qu’elle soit facilement imprimable sur une imprimante résine voir filament (test pas encore effectué). Tous les fichiers sont disponibles ci-dessous en téléchargement.
Vidéo de démonstration :
VIDEO
Composants nécessaires :
Arduino Nano
Carte d’extension pour module de contrôle Nano
1 PCA9685
Des fils de connexion
1 alimentation 5v
4 vis M3x16
8 vis M3x8
4 vis M3x10
16 écrous hexagonal M3
14 Vis tête ronde auto taraudeuse M1.7X6
1 imprimante 3d résine ( imprimante filament test pas encore effectué)
1 Raspberry PI 3
1 Camera PI v2.1
1 LED RGB
3 résistances 220Ω
8 Servos Tower pro 9g sg90 (attention à la sortie des fils voir ci-dessous)
Fichiers SolidWorks et STL :
Bibliothèque nécessaire :
#include « Adafruit_PWMServoDriver.h »
https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
Logiciels nécessaires pour le Raspberry :
Python 2.7
OpenCV
pyserial
NumPy
Schéma de câblage :
Attention aux câblages des servos et à leur nom.
Par exemple le servo 2 est branché sur la sortie 3 du PCA9685.
Résumé :
Servo 0 –> Pin 0
Servo 1 –> Pin 7
Servo 2 –> Pin 3
Servo 3 –> Pin 4
Servo 4 –> Pin 1
Servo 5 –> Pin 2
Servo 6 –> Pin 5
Servo 7 –> Pin 6
Codes pour positionner les servos avant assemblage final :
#include "Wire.h" #include "Adafruit_PWMServoDriver.h" Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40); #define MIN_PULSE_WIDTH 600 #define MAX_PULSE_WIDTH 2600 #define FREQUENCY 50 int analogPin = A0; int valeur = 0; void setup() { Serial.begin(9600); pwm.begin(); pwm.setPWMFreq(FREQUENCY); } int pulseWidth(int angle) { int pulse_wide, analog_value; pulse_wide = map(angle, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); analog_value = int(float(pulse_wide) / 1000000 * FREQUENCY * 4096); return analog_value; } void loop() { valeur = analogRead(analogPin); Serial.println(valeur); int xd = map(valeur, 0, 1023,180, 110); int xg = map(valeur, 0, 1023,0, 70); int yd = map(valeur, 0, 1023,0, 70); int yg = map(valeur, 0, 1023,180, 110); pwm.setPWM(0, 0, pulseWidth(90)); //Servo 0 pwm.setPWM(7, 0, pulseWidth(90)); //Servo 1 pwm.setPWM(3, 0, pulseWidth(180)); //Servo 2 pwm.setPWM(4, 0, pulseWidth(0)); //Servo 3 pwm.setPWM(1, 0, pulseWidth(0)); //Servo 4 pwm.setPWM(2, 0, pulseWidth(180)); //Servo 5 pwm.setPWM(5, 0, pulseWidth(0)); //Servo 6 pwm.setPWM(6, 0, pulseWidth(180)); //Servo 7 }
Vous devez obtenir cette position une fois l’assemblage fini.
Codes Arduino pour le suivi de couleur :
int pinservo[]={ 0, 7, 3, 4, 1, 2, 5, 6}; float poservo[]={95, 95, 140, 40, 0, 180, 0, 180}; float poservoinit[]={95, 95, 140, 40, 30, 135, 45, 140}; float servomin[]={55, 55, 100, 0, 0, 110, 0, 110}; float servomax[]={135, 135, 180, 80, 70, 180, 70, 180}; #include "Wire.h" #include "Adafruit_PWMServoDriver.h" Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40); const unsigned int MAX_MESSAGE_LENGTH = 12; #define MIN_PULSE_WIDTH 600 #define MAX_PULSE_WIDTH 2600 #define FREQUENCY 50 const int rouge = 6; const int vert = 5; const int bleue = 3; float x; float y; int c; long temps; int start = 0; long tempspaupiere; void setup() { Serial.begin(115200); pinMode(rouge, OUTPUT); pinMode(vert, OUTPUT); pinMode(bleue, OUTPUT); analogWrite(rouge, 10); pwm.begin(); pwm.setPWMFreq(FREQUENCY); initservo(); delay(2000); digitalWrite(rouge, LOW); temps = millis(); tempspaupiere = millis(); } int pulseWidth(int angle) { int pulse_wide, analog_value; pulse_wide = map(angle, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); analog_value = int(float(pulse_wide) / 1000000 * FREQUENCY * 4096); return analog_value; } void loop() { if (Serial.available() > 0) { start = 1 ; temps = millis(); static char message[MAX_MESSAGE_LENGTH]; static unsigned int message_pos = 0; char inByte = Serial.read(); if(inByte == 'C'){ inByte = Serial.read(); while (Serial.available() > 0 && inByte != 'C'){ message[message_pos] = inByte; message_pos++; inByte = Serial.read(); delay(3); } message[message_pos] = ' '; char * morceau[4]; char * p; int n = -1; p = strtok(message, ","); while (p != NULL) { morceau[++n] = p; p = strtok(NULL, ","); } x = (atoi(morceau[0]))/4; y = (atoi(morceau[1]))/2;; c = atoi(morceau[2]); message_pos = 0; } } if(start == 0){ paupiere(); } else{ if(c == 1){ analogWrite(vert, 10); analogWrite(rouge, 10); digitalWrite(bleue, LOW); } if(c == 2){ analogWrite(vert, 10); digitalWrite(rouge, LOW); digitalWrite(bleue, LOW); } while(Serial.available() == 0 && (millis() - temps) < 1000) { if(x > 90) { float posx = map(x, 90, 160, 1, 180); poservo[1] = poservo[1]+(posx/1000); poservo[0] = poservo[0]+(posx/1000); } if(x < 70) { float posx = map(x, 0, 70, 180, 1); poservo[1] = poservo[1]-(posx/1000); poservo[0] = poservo[0]-(posx/1000); } if(y > 130) { float posy = map(y, 130, 240, 1, 200); poservo[3] = poservo[3]+(posy/1000); poservo[2] = poservo[2]-(posy/1000); } if(y < 110) { float posy = map(y, 0, 110, 200, 1); poservo[3] = poservo[3]-(posy/1000); poservo[2] = poservo[2]+(posy/1000); } for(int i= 0; i <= 3; i++) { if (poservo[i] >= servomax[i]) { poservo[i] = servomax[i]; } } for(int i= 0; i <= 3; i++){ if (poservo[i] <= servomin[i]) { poservo[i] = servomin[i]; } } posservo(); paupiere(); } while(Serial.available() == 0) { analogWrite(rouge, 10); digitalWrite(vert, LOW); digitalWrite(bleue, LOW); for(int i= 0; i <= 3; i++) { if(poservo[i] < poservoinit[i]-1){ poservo[i] = poservo[i]+0.1; } } for(int i= 0; i <= 3; i++) { if(poservo[i] > poservoinit[i]+1){ poservo[i]= poservo[i]-0.1; } } posservo(); if(start == 1 && poservo[1] > poservoinit[1]-1 && poservo[0] > poservoinit[0]-1 && poservo[1] < poservoinit[1]+1 && poservo[1] < poservoinit[1]+1 && poservo[3] > poservoinit[3]-1 && poservo[2] > poservoinit[2]-1 && poservo[3] < poservoinit[3]+1 && poservo[2] < poservoinit[2]+1){ start = 0; } } } } void posservo(){ for(int i= 0; i <= 3; i++){ if(poservo[i] < servomax[i] && poservo[i] > servomin[i]){ pwm.setPWM(pinservo[i], 0, pulseWidth(poservo[i])); } } paupiere(); } void initservo(){ for(int i= 0; i <= 7; i++){ pwm.setPWM(pinservo[i], 0, pulseWidth(poservoinit[i])); } } void paupiere(){ if(((millis() - tempspaupiere) > 3500 && start == 0) || (x < 90 && x > 70 && y < 130 && y > 110 && (millis() - tempspaupiere) > 3500)){ for(int i= 4; i <= 7; i++){ pwm.setPWM(pinservo[i], 0, pulseWidth(poservo[i])); } delay(200); for(int i= 4; i <= 7; i++){ pwm.setPWM(pinservo[i], 0, pulseWidth(poservoinit[i])); } tempspaupiere = millis(); } }
Codes Python pour le suivi de couleur :
import cv2 import numpy as np import picamera import picamera.array import time import sys import serial lo_jaune=np.array([20, 100, 100]) hi_jaune=np.array([30, 255, 255]) color_info_jaune=(30, 255, 255) lo_vert=np.array([40, 100, 100]) hi_vert=np.array([70, 255, 255]) color_info_vert=(88, 255, 51) WIDTH=640 HEIGHT=480 arduino = serial.Serial('/dev/ttyUSB0', 115200) time.sleep(2) print("Connected to Arduino...") with picamera.PiCamera() as camera: with picamera.array.PiRGBArray(camera) as stream: camera.resolution=(WIDTH, HEIGHT) camera.rotation = 180 while True: camera.capture(stream, 'bgr', use_video_port=True) image=cv2.cvtColor(stream.array, cv2.COLOR_BGR2HSV) mask_jaune=cv2.inRange(image, lo_jaune, hi_jaune) mask_vert=cv2.inRange(image, lo_vert, hi_vert) image_jaune=cv2.blur(image, (7, 7)) image_vert=cv2.blur(image, (7, 7)) mask_jaune=cv2.erode(mask_jaune, None, iterations=4) mask_vert=cv2.erode(mask_vert, None, iterations=4) mask_jaune=cv2.dilate(mask_jaune, None, iterations=4) mask_vert=cv2.dilate(mask_vert, None, iterations=4) image_jaune=cv2.bitwise_and(stream.array, stream.array, mask=mask_jaune) image_vert=cv2.bitwise_and(stream.array, stream.array, mask=mask_vert) element_jaune=cv2.findContours(mask_jaune, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2] elements_vert=cv2.findContours(mask_vert, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2] if len(element_jaune) > 0: c=max(element_jaune, key=cv2.contourArea) ((x, y), rayon)=cv2.minEnclosingCircle(c) if rayon>10: cv2.circle(image_jaune, (int(x), int(y)), int(rayon), color_info_jaune, 2) cv2.circle(stream.array, (int(x), int(y)), 5, color_info_jaune, 10) cv2.line(stream.array, (int(x), int(y)), (int(x)+150, int(y)), color_info_jaune, 2) cv2.putText(stream.array, "jaune", (int(x)+10, int(y) -10), cv2.FONT_HERSHEY_DUPLEX, 1, color_info_jaune, 1, cv2.LINE_AA) print ('X :' +str(x)) print ('Y :'+str(y)) xx = int(x) yy = int(y) cc = 1 print (xx) print (yy) data = "C{0:d},{1:d},{2:d} ".format(xx, yy, cc) print ("output = '" +data+ "'") arduino.write(data.encode()) time.sleep(0.04) if len(elements_vert) > 0: c=max(elements_vert, key=cv2.contourArea) ((x, y), rayon)=cv2.minEnclosingCircle(c) if rayon>10: cv2.circle(image_vert, (int(x), int(y)), int(rayon), color_info_vert, 2) cv2.circle(stream.array, (int(x), int(y)), 5, color_info_vert, 10) cv2.line(stream.array, (int(x), int(y)), (int(x)+150, int(y)), color_info_vert, 2) cv2.putText(stream.array, "vert", (int(x)+10, int(y) -10), cv2.FONT_HERSHEY_DUPLEX, 1, color_info_vert, 1, cv2.LINE_AA) print ('X :' +str(x)) print ('Y :'+str(y)) xx = int(x) yy = int(y) cc = 2 print (xx) print (yy) data = "C{0:d},{1:d},{2:d} ".format(xx, yy, cc) print ("output = '" +data+ "'") arduino.write(data.encode()) time.sleep(0.04) cv2.line(stream.array,(640,240),(0,240),(0,255,0),1) cv2.line(stream.array,(320,0),(320,480),(0,255,0),1) cv2.imshow('Camera', stream.array) #cv2.waitKey(10) #cv2.imshow('image_jaune', image_jaune) #cv2.imshow('image_vert', image_vert) #cv2.imshow('Mask_jaune', mask_jaune) #cv2.imshow('Mask_vert', mask_vert) if cv2.waitKey(1)&0xFF==ord('q'): break stream.seek(0) stream.truncate() cv2.destroyAllWindows()
Photos :
Nombre de vues: 323
WordPress: J’aime chargement…