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: 1 040
J’aime ça : J’aime chargement…
Je souhaiterais réaliser votre projet avec arduino , et avec une impression 3d filament PLA.
Programme arduino pas de problème.
Par contre est il possible d’avoir les dimensions des différentes pièces.
En vous remercient
Lucien Verdier
Bonjour,
Les fichiers STL et solidworks sont disponibles en téléchargement dans l’article.
Cordialement,