Robot convivial de suivi des personnes omnidirectionnel
Composants et fournitures
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 3 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 10 | ||||
| × | 1 | ||||
| × | 1 |
Outils et machines nécessaires
| ||||
|
Applications et services en ligne
|
À propos de ce projet
Soumission initiale
Ma première soumission au concours "Hackster Terminate the competition" était de créer un robot qui recherchait des humains, mais contrairement à ceux de l'univers Terminator, il ne tuait pas des gens, mais utilisait ses pouvoirs pour de bon.
Ce robot omnidirectionnel vous trouvera, vous détectera, vous repérera et vous complimentera !
Une exigence que je me suis spécifiée est que je m'assurerais que cela pourrait être fait sans avoir à construire l'électronique vous-même. Chaque pièce à l'intérieur de ce robot peut être achetée sur eBay, connectée à l'aide de didacticiels disponibles gratuitement et elle devrait fonctionner. Le boîtier sera alors imprimable en 3D, vous pourrez donc soit l'imprimer vous-même, soit le faire fabriquer sur des hubs 3D. Je pense que ce projet répond à cet objectif et je vais maintenant vous guider à travers les étapes de construction de votre propre Robot Hunter Flatterer
Comment tout fonctionne !
Commençons donc par un schéma du système qui montre toutes les parties du robot et comment elles s'assemblent. Nous ferons référence à cela tout au long de la construction et le vérifierons au fur et à mesure.
Ne soyez pas trop intimidé par ce schéma si vous n'avez jamais utilisé de moteurs auparavant. Cela se résume essentiellement à quatre parties principales :
- Le côté gauche traite de la prise de la batterie et s'assure que les tensions sont correctes pour le système.
- Le petit malin se fait sur le ci20
- L'Arduino est utilisé pour dire aux moteurs quoi faire
Les quatre sections suivantes refléteront cette liste et vous guideront dans la configuration de chaque partie.
Faire face à l'électricité
Alors vous avez une batterie, et ensuite ?
Le schéma ci-dessus montre comment connecter tous les composants pour l'alimentation, si vous suivez le câblage dans le schéma, c'est la représentation réelle.
Connecter l'Arduino au L298N
Je pourrais expliquer cela, mais il vaut mieux suivre le tutoriel que j'ai fait :http://www.instructables.com/id/Arduino-Modules-L298N-Dual-H-Bridge-Motor-Controll.
Alors maintenant que vous avez tout câblé, vous avez des lumières bleues qui clignotent partout et aucune fumée bleue, donc vous êtes prêt à partir. Commençons à faire fonctionner ce mauvais garçon.
Imprimez vous-même une coque en 3D
Quand j'ai conçu cela, j'ai décidé que je voulais que deux choses se produisent, ça a l'air cool et ça montre le Ci20 aussi bien que possible. Je veux dire, c'est le but de la compétition, n'est-ce pas ?
J'ai conçu ce robot pour qu'il soit entièrement imprimable en 3D et les dessins sont tous disponibles ci-dessous. Fondamentalement, à ce stade, imprimez vous-même le haut, le bas et les roues. Si vous n'avez pas accès à une imprimante 3D, vous pouvez consulter www.3dhubs.com pour demander à quelqu'un de l'imprimer pour vous ! Vous pouvez consulter les conceptions ci-dessous
Voici un joli rendu que j'ai fait de la conception pour m'assurer que le ci20 allait s'adapter et être à la place d'honneur
Une fois que vous avez tout imprimé, vous pouvez coller toutes les pièces comme ci-dessus (ou utiliser du ruban adhésif si vous ne vous sentez pas à l'aise. Vous aurez besoin d'un boulon M4 pour maintenir les moteurs.
Faire de la magie du CI20
Le CI20 est le patron, le cerveau de l'opération. Sans cela, le robot pataugera. Alors qu'est-ce que ça fait ?
Eh bien, le ci20 va utiliser OpenCV pour détecter votre visage, puis envoyer les commandes appropriées en série à l'Arduino pour que les moteurs aillent dans la bonne direction.
Étape 1 :Installez OpenCV
Comme je l'ai dit tout au long de ce guide, je n'essayais pas de réinventer le panier à pommes. J'essaie de construire quelque chose que vous pouvez faire à la maison et étendre avec une relative facilité. Alors pour installer OpenCV allez et suivez ce tutoriel :
Il vous explique pas à pas comment faire fonctionner OpenCV.
Étape 2 :Exécutez le code
Le code Face Tracking devra être compilé et exécuté, exécutez cette commande sur le fichier sur le Ci20.
g++ -I/usr/local/include/opencv -I/usr/local/include/opencv2 -L/usr/local/lib/ -g -o binary main.cpp -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml -lopencv_video -lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_contrib -lopencv_legacy -lopencv_stitching
Le fichier binaire à créer :FaceTracking :main.cpp> Fichier source :FaceTracking.cpp
Facile non ?
Ci20 et magie Arduino
Après avoir flashé l'Arduino avec le code dans les pièces jointes ci-dessous. Branchez l'Arduino sur le port USB du CI20 et exécutez votre FaceTracker récemment créé. Vous verrez apparaître un flux de caméra (Oh oui, branchez la webcam) et si vous collez votre visage au milieu, les roues du robot devraient commencer à le faire avancer !
Travail terminé ?
Maintenant, en raison de contraintes de temps, je n'ai pas réussi à faire autre chose, alors voici un défi pour tous ceux qui sont allés jusqu'ici. Il y a deux choses que vous pouvez ajouter pour rendre ce bot super génial :
Ajoutez un ensemble de haut-parleurs et des fichiers son afin que lorsque le visage est suffisamment grand sur l'écran, il diffuse un fichier son à la personne
- Si le robot n'a pas détecté de visage en 1 à 2 minutes, faites-le tourner sur place. Vous pouvez le faire en envoyant la commande "R XX" à l'Arduino. Remplacez XX par la durée pendant laquelle vous souhaitez faire demi-tour.
Code
- Arduino Sketch - Principal
- Arduino Sketch - MotorControl
- FaceTracking C++ pour Ci20
Arduino Sketch - PrincipalArduino
Ce code Arduino reçoit la commande d'entraînement du Ci20 via série et garantit que les moteurs sont entraînés correctement pour aller dans la bonne direction.J'ai initialement écrit ceci pour un Raspberry Pi mais cela fonctionne aussi avec le Ci20
// Motor 1int dir1PinA =3;int dir2PinA =2;int speedPinA =9; // Doit être une broche PWM pour pouvoir contrôler la vitesse du moteur// Motor 2int dir1PinB =4;int dir2PinB =5;int speedPinB =8; // Doit être une broche PWM pour pouvoir contrôler la vitesse du moteur// Moteur 3int dir1PinC =6;int dir2PinC =7;int speedPinC =10; // Doit être une broche PWM pour pouvoir contrôler la vitesse du moteurint x =0;int y =0;int dominantUltrasonic =0;bool moveMotor =false;int startTime =0;void setup() { Serial.begin(9600); pinMode(dir1PinA,OUTPUT); pinMode(dir2PinA,OUTPUT); pinMode(speedPinA,OUTPUT); pinMode(dir1PinB,OUTPUT); pinMode(dir2PinB,OUTPUT); pinMode(speedPinB,OUTPUT); pinMode(dir1PinC,OUTPUT); pinMode(dir2PinC,OUTPUT); pinMode(speedPinC,OUTPUT); startTime =millis();}String rpiString;void loop() { readDataFromRPi(); // Ceci teste si nous avons reçu une valeur du RPi dans la dernière seconde. // Il agit effectivement comme un tampon afin qu'il ne s'arrête pas et ne démarre pas car la commande de déplacement n'est pas continue // Il permet également l'arrêt si rien ne vient du RPi // int elapsedTime =millis() - startTime; // if (elapsedTime> 1000)// {// x =0;// y =0;// dominantUltrasonic =0;// moveMotor =false;// startTime =millis();// // } // Envoyez les X &Y aux moteurs //if(moveMotor ==true &&(x !=0 &&y !=0)) //{ //Serial.println("MovingMotor"); // driveInDirection(x,y); //} //if(x ==0 &&y ==0) //{ //Serial.println("ZeroMMotor"); // driveInDirection(x,y); //} // Cela pourrait simplement être laissé par les ultrasons - hésitant à supprimer delay(30);}void readDataFromRPi(){ // Lire à partir de Rpi while (Serial.available()) { delay(3); //délai pour permettre au tampon de se remplir if (Serial.available()>0) { char c =Serial.read(); // obtient un octet du tampon série rpiString +=c; //rend la chaîne readString if(c =='n') { break; } } } // ENDWHILE // Si quelque chose a été lu depuis le RPi, placez-le dans x,y &domniantUltrasonic if (rpiString.length()>0) { Serial.println(rpiString); // voir ce qui a été reçu String isRotate =getValue(rpiString, ' ', 0); Chaîne xval =getValue(rpiString, ' ', 1); Chaîne yval =getValue(rpiString, ' ', 2); x =xval.toInt(); y =yval.toInt(); startTime =millis(); if (isRotate =="r") { rotate(x); } else { driveInDirection(x,y); } rpiString=""; } //ENDIF} String getValue(String data, char separator, int index){ int found =0; int strIndex[] ={0, -1 } ; int maxIndex =data.length()-1; for(int i=0; i<=maxIndex &&found<=index; i++){ if(data.charAt(i)==separator || i==maxIndex){ found++; strIndex[0] =strIndex[1]+1; strIndex[1] =(i ==maxIndex) ? i+1 :je; } } return found>index ? data.substring(strIndex[0], strIndex[1]) :"";}
Arduino Sketch - MotorControlArduino
Cela va avec le fichier ino principal et doit être inclus dans le même fichier de projet/* * Code de contrôle du moteur * * Cette classe inclura le code pour faire aller le robot dans n'importe quelle direction * et tourner autour du point central. */void driveInDirection(float newX, float newY){ delay(20); flottant x =nouveauX ; float y =newY; flottant thêta =atan2(y,x); float mag =sqrt((x*x) + (y*y)); float vx =mag * cos (thêta); float vy =mag * sin(theta); flottant w1 =-vx; float w2 =0.5 * vx - sqrt(3)/2 * vy; float w3 =0.5 * vx + sqrt(3)/2 * vy; // Récupère la plus grande valeur w float wSet[] ={w1, w2, w3}; float la plus grandeValue =0,0 ; for (int i =0; i <3; i++) { if(abs(wSet[i])> plus grandeValue) { plus grande =abs(wSet[i]); } } float speedCoef =(float)147.0 /largeValue; w1 =w1 * speedCoef ; w2 =w2 * speedCoef ; w3 =w3 * speedCoef ; if (x ==0 &&y ==0) { w1 =0; w2 =0 ; w3 =0 ; } Serial.println(w1) ; Serial.println(w2); Serial.println(w3) ; w1 =contraindre (w1, -150, 150); w2 =contraindre (w2, -150, 150); w3 =contraindre (w3, -150, 150); booléen w1_ccw =w1 <0 ? vrai faux; booléen w2_ccw =w2 <0 ? vrai faux; booléen w3_ccw =w3 <0 ? vrai faux; octet w1_speed =(octet) map(abs(w1), 0, 150, 0, 255); octet w2_speed =(octet) map(abs(w2), 0, 150, 0, 255); octet w3_speed =(octet) map(abs(w3), 0, 150, 0, 255); printMotorSpeed (w1_speed, 1); printMotorSpeed (w2_speed, 2); printMotorSpeed (w3_speed, 3); analogWrite(speedPinA, w1_speed);//Définit la variable de vitesse via PWM analogWrite(speedPinB, w2_speed); analogWrite(speedPinC, w3_speed);//Définit la variable de vitesse via PWM digitalWrite(dir1PinA, !w1_ccw); digitalWrite(dir2PinA, w1_ccw); digitalWrite(dir1PinB, !w2_ccw); digitalWrite(dir2PinB, w2_ccw); digitalWrite(dir1PinC, w3_ccw); digitalWrite(dir2PinC, !w3_ccw);}void rotate(float milliseconds){ float w1 =255; flottant w2 =255 ; flottant w3 =255 ; booléen w1_ccw =w1 <0 ? vrai faux; booléen w2_ccw =w2 <0 ? vrai faux; booléen w3_ccw =w3 <0 ? vrai faux; octet w1_speed =(octet) map(abs(w1), 0, 150, 0, 255); octet w2_speed =(octet) map(abs(w2), 0, 150, 0, 255); octet w3_speed =(octet) map(abs(w3), 0, 150, 0, 255); printMotorSpeed (w1_speed, 1); printMotorSpeed (w2_speed, 2); printMotorSpeed (w3_speed, 3); analogWrite(speedPinA, w1_speed);//Définit la variable de vitesse via PWM analogWrite(speedPinB, w2_speed); analogWrite(speedPinC, w3_speed);//Définit la variable de vitesse via PWM digitalWrite(dir1PinA, !w1_ccw); digitalWrite(dir2PinA, w1_ccw); digitalWrite(dir1PinB, !w2_ccw); digitalWrite(dir2PinB, w2_ccw); digitalWrite(dir1PinC, w3_ccw); digitalWrite(dir2PinC, !w3_ccw); délai (millisecondes); analogWrite(speedPinA, 0);//Définit la variable de vitesse via PWM analogWrite(speedPinB, 0); analogWrite(speedPinC, 0);//Définit la variable de vitesse via PWM }void printMotorSpeed(byte motorSpeed, int motor){ Serial.print("Motor"); Serial.print(moteur); Serial.print(":"); Serial.println(motorSpeed); }
FaceTracking C++ pour Ci20C/C++
Suivez le guide dans Story#include "opencv2/objdetect/objdetect.hpp"#include "opencv2/highgui/highgui.hpp"#include "opencv2/imgproc/imgproc.hpp"#include#include en utilisant l'espace de noms std;en utilisant l'espace de noms cv;CascadeClassifier face_cascade, eyes_cascade;String window_name ="Face Detection";#include #include #include #include #include #include #include #include int sendSerial(char* message){int fd =open("/ dev/ttyUSB0", O_RDWR);if (fd ==-1){ perror("dev/ttyUSB0"); return 1;}struct termios tios;tcgetattr(fd, &tios);tios.c_iflag =IGNBRK | IGNPAR;tios.c_oflag =0;tios.c_lflag =0;cfsetspeed(&tios, B96000);tcsetattr(fd, TCSAFLUSH,&tios);usleep(1000);//char msg[] ="50 50";write(fd , message, strlen(message));return 0;}/** * Détecte les visages et dessine une ellipse autour d'eux */void detectFaces(Mat frame) { std::vector faces; Tapis frame_gray; // Convertir en échelle de gris cvtColor(frame, frame_gray, COLOR_BGR2GRAY); // Égaliser l'histogramme equalizeHist(frame_gray, frame_gray); // Détecte les visages face_cascade.detectMultiScale(frame_gray, faces, 1.1, 3, 0|CASCADE_SCALE_IMAGE, Size(30,30)); // Itérer sur toutes les faces for(size_t i =0; i yeux ; // Essayez de détecter les yeux, à l'intérieur de chaque visage // eyes_cascade.detectMultiScale(face, eyes, 1.1, 2, // 0 |CASCADE_SCALE_IMAGE, Size(50, 50) ); // if(eyes.size()> 0) // Dessine une ellipse autour du visage ellipse(frame, center, Size(faces[i].width/2, faces[i].height/2), 0, 0, 360 , Scalaire( 255, 0, 255 ), 4, 8, 0 ); if(center.x> frame.cols/3 &¢er.x =0) // pause break; } renvoie 0 ;}
Pièces et boîtiers personnalisés
C'est la base du robot omnidirectionnel, conçu par moi pour s'adapter à toutes les pièces et avoir un couvercle attachéC'est le couvercle qui montre le Ci20 dans toute sa splendeur.Roues omnidirectionnelles
C'est la conception originale des roues omnidirectionnelles. J'en ai utilisé la moitié avec les jantes, puis je l'ai édité pour inclure le fichier CAO de l'arbre du moteur sur thingverse.com Une roue remixée du lien thingverseSchémas
Voir une meilleure description dans la section histoireProcessus de fabrication