mardi 25 décembre 2012

Robot suiveur de balles OpenCV (Projet expérimental)

Le projet robot tourelle suiveuse de balles, est sans doutes le premier d'une longue série de petits projets à venir. En effet les possibilités offertes par la librairie OpenCV sont impressionnantes et "simples" à mettre en place. L'idée de ce premier projet à base d'OpenCV est de réussir à déplacer une webcam montée sur un châssis rotatif afin que celle ci fixe une balle et la centre au milieu de l'image. Le châssis est entièrement réalisé en légos et la caméra est une petite webcam générique maintenue en place sur une pièce légo par des visses. La motorisation est un kit power functions contenant deux petits moteurs, le bloc batterie et le récepteur infrarouge légo. La webcam est branchée directement à un PC. La tourelle quand à elle est branchée aussi à l'USB grâce à une interface "maison" USB vers signaux infrarouges légo. Côté commande de la tourelle il suffit d'envoyer des chaines de caractères (correspondant à la rotation moteur voulue) dans le bon périphérique usb.




Partie électronique du projet

Schemas électronique de l'intérface USB vers Signaux infrarouges légoLa partie électronique du projet se résume en quelques composants très basiques ainsi que des concepts précédemment évoqués sur mon blog. En effet la partie électronique nécessaire est l'interface entre l'USB et la commande infrarouge légo. Donc au final c'est un mélange entre le projet Télécommande infrarouge lego power function et librairie arduino (pour toute la partie génération des signaux infrarouges et commande des moteurs) et Mini écran LCD pour serveur (linux) en USB (pour tout ce qui concerne la lecture des chaines de caractères sur le port USB. Le coeur de la partie électronique est un teensy2++ (équivalent de l'arduino). Ce circuit en lui même est on ne peut plus simple. J'ai choisi de conserver le montage du mini écran LCD en USB pour ce projet car cela permet d'avoir un debug supplémentaire sous la main.

Le schéma électronique ne change quasiment pas du schéma du mini écran LCD pour usb. En effet la seule évolution c'est le rajout de la diode infrarouge et de sa résistance pour l'envoi des commandes infrarouges vers le récepteur de Lego.



Le programme informatique de l'adaptateur USB vers commande infrarouge lego

Le programme est très simple. La boucle principale s'occupe de lire les messages séries arrivant par le port USB puis de les retranscrire en signaux légo grâce a la classe Lego détaillée dans l'article télécommande infrarouge lego power function. Les messages USB sont eux générés par un autre programme tournant cette fois ci sur le PC. Dans le programme du teensy (équivalent arduino) vous pouvez voir une méthode permettant d'utiliser l'écran LCD pour afficher ce qui se passe en ce qui concerne la réception des messages en USB. Cette phase est évidement chronophage,  de ce fait si on cherche a optimiser la fluidité de transmission de notre interface il s'agira de commenter tout ce qui commence par lcd dans la fonction sendRc.

#include <liquidcrystal.h>
#include "LegoRC.h"

LegoRC legoRC(2, 50);
LiquidCrystal lcd(24, 25, 8, 9, 27, 0);
String l1="", l2="", tmpl="";
char incomingByte;
int newMSG = 0;
int led = 12;

void setup() {
  pinMode(led, OUTPUT);
  lcd.begin(16, 2);
  Serial.begin(9600);
}


void loop() {
  // Lecture d'une chaine de carracteres
  while (Serial.available()) {
    digitalWrite(led, HIGH);
    incomingByte = Serial.read();  // will not be -1
    tmpl = tmpl + char(incomingByte);
    newMSG = 1;
  }

   digitalWrite(led, LOW);

    if (newMSG) {
      l1 = l2;
      l2 = tmpl.trim();
      String  myString = l2;
      int myStringLength = myString.length()+1;
      char myChar[myStringLength];
      myString.toCharArray(myChar,myStringLength);
      int result = atoi(myChar);
      sendRc(result);
      tmpl = "";      
    }

    newMSG = 0;
}


void sendRc(int i) {
 
 int m1 = i / 100;
 int m2 = i % 100;
 
 lcd.clear();
 lcd.setCursor(0, 0);
 lcd.print(m1);   

 lcd.setCursor(0, 1);
 lcd.print(m2);   
  
 legoRC.sendCommand(4, m1, m2);

}


Le programme informatique côté PC

Côté PC un programme en C permet la détection de ronds a la webcam et indique la correction nécessaire pour amener le rond détecté au centre de l'image de la webcam. La détection de cercles dans l'image se fait grâce a la librairie OpenCV.

La libraire openCV propose la fonction HoughCircles permettant de détecter tous les cercles reconnus à l'image. Pour chaque cercle les coordonnées x et y ainsi que le rayon du cercle détecté sont renvoyés par la fonction. Dans mon code je pars du principe qu'un seul cercle est présent a l'image, si tel n'est pas le cas, alors je prends le cercle qui a été détecté en dernier dans l'image courante. Une fois que nous disposons des coordonnées (x,y) du cercle détecté, nous pouvons donc estimer la direction de la rotation de la tourelle afin que x et y soient le plus proche possible du centre de l'image. Dans le code j'ai implémenté une variable servant d'erreur acceptée afin qu'un déplacement minime du cercle dans la vision de la webcam ne déclenche pas une rotation de la tourelle. Ce procédé me permet d'éviter la réaction de la tourelle au bruit induit par la détection des cercles par OpenCV.

#include "stdio.h"
#include "opencv/cv.h"
#include <math.h>
#include "opencv/highgui.h"

void xl();
void xr();
void yt();
void yb();
void xys();

using namespace cv;

int main() {

    VideoCapture cap(1); // open the default camera
    if(!cap.isOpened())  // check if we succeeded
        return -1;

    Mat edges;
    namedWindow("edges",1);

    int circlex = 0;
    int circley = 0;
    int bbox = 0;

    for(;;)
    {
        Mat frame;
        cap >> frame; // get a new frame from camera
        cvtColor(frame, edges, CV_BGR2GRAY);
        GaussianBlur(edges, edges, Size(9,9), 2, 2);
       
        vector<vec3f> circles;
        HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 2, edges.rows/4, 200, 100 );


       if (circles.size() > 0) {
       
           for (int i = 0; i < circles.size(); i++) {
            circlex = cvRound(circles[i][0]);
            circley = cvRound(circles[i][1]);
            Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
            int radius = cvRound(circles[i][2]);
            circle(frame, center, 3, Scalar(0,255,0), -1, 8, 0 );
            circle( frame, center, radius, Scalar(0,0,255), 3, 8, 0 );
            printf("x %d ; y %d \n", cvRound(circles[i][0]), cvRound(circles[i][1]) );

           }
            bbox = 20;
            if (circlex > (320 + bbox)) {
                xr();
                xys();

            } else if ((circlex < (320 - bbox)) && (circlex != 0)) {
                xl();
                xys();
            }

            if (circley > (240 + bbox)) {
                yb();
                xys();

            } else if ((circley < (240-bbox)) && (circley != 0)) {
                yt();
                xys();
            }
            
        } else {
            circlex = 0;
            circley = 0;
        }

        imshow("edges", frame);
        
        if(waitKey(30) >= 0) break;
    }
 
    return 0;
}

void xl() {
    system("echo \"408\" > /dev/ttyACM0");
}

void xr() {
    system("echo \"1208\" > /dev/ttyACM0");
}

void yt() {
    system("echo \"812\" > /dev/ttyACM0");
}

void yb() {
    system("echo \"804\" > /dev/ttyACM0");
}

void xys() {
    system("echo \"808\" > /dev/ttyACM0");
}


Partie mécanique en légos technics et Lego Power functions

La mécanique de ce robot suiveur de balles est entièrement faite en légos technics. Seule la caméra usb est fixé sur une pièce légo standard avec des vices. Le principe est simple. La caméra peut tourner a droite ou a gauche en actionnant un des deux petits moteurs des power functions. L'autre moteur sert a la commande monter et descendre la caméra. Les deux moteurs sonts reliés a un récepteur infrarouge (première version) Légo. Le récepteur en lui même est évidement connecté a un bloc d'alimentation légo. Le récepteur infrarouge dervé évidement être mis en face de la diode infrarouge qui émet les signaux transmis par le programme PC via le port USB. La webcam utilisée dans ce projet est une simple caméra usb générique acheté une quinzaine d'euros dans une boutique d'informatique. J'ai privilégié ce modèle pour la facilité de fixation.




Fichiers du projet robot suiveur de balles a télécharger

Code source complet arduino de l'adaptateur USB vers commande légo infrarouge
Code source complet C programme de détection opencv et envoi USB (code::blocks)


Toutes les photos du projet robot suiveur de balles

photos représantant le robot suiveur de balles robot tourelle suiveur de balles detection de cercles par artiom fedorov

Aucun commentaire:

Enregistrer un commentaire