Banc de vision piloté

Banc de vision et convoyeur

Fait par :

  • BOUDAOUDI Aissame
  • ANDERHUBER Tom

Objectif

L’objectif de ce projet est de réaliser un banc de vision et convoyeur pour pouvoir scanner des pièces en 3D et repérer les défauts existants. En utilisant un tapis avec un moteur pas à pas, des boutons pour gérer le tapis, des rubans de leds, un capteur de détection de pièces, une caméra et finalement un arduino UNO qui va nous permettre de lier tous ces éléments afin de les faire fonctionner.

Fonctionnement

Une caméra avec leds sur un banc de vision

Nous plaçons une pièce sur le convoyeur, cette pièce est éclairer par des leds afin d’avoir un reflet, une fois que la pièce est détecté par le capteur infrarouge, la caméra de vision prend une photo et analyse la pièce afin de vérifier la pièce et ainsi voir si cette dernière est conforme (présence d’un défaut ou non)

Afin de programmer tout cela nous utiliseront un Arduino UNO, ce dernier vas nous permettre de faire fonctionner les boutons poussoirs, le capteur infrarouge, le moteur du banc de vision et les bandeaux leds

Nous allons également utiliser anaconda afin de réaliser une liaison série qui vas nous permettre de contrôler le programme à distance via un ordinateur.

Nous avons décidé d’utiliser deux boutons et un switch.

Un bouton va gérer la fonction marche/arrêt du tapis, un deuxième bouton est pour l’instant libre, il ne fait rien, on l’a installé pour pouvoir le coder et programmer. Le switch va gérer le sens du tapis, en changeant la position du Switch on change le sens de rotation du tapis.

Partie électrique

Schéma de câblage

(Fichier disponible sur la plateforme ED campus )

  • On peut voir qu’on utilise un différentielle afin de protéger l’installation d’un défaut de tension, et couper la tension en cas de problème.

En ce qui concerne le matériel utilisé :

  • Arduino UNO
  • COnvertisseur 230V en 24V, 12V, et 5V
  • Différentielle merlin gerin multi 9 DPN vigi
  • Carte de commande Micrositch driver
  • Deux bouton, un switch
  • Un capteur de détection de pièces
  • Un Tapis avec moteur pas à pas

Plaque électronique

Partie code

On a fourni la code complet dans un fichier « ino » sur la plateforme EDCampus

Pour la partie code du projet, on a utilisé le logiciel arduino , et le langage C++ pour pouvoir donner des instructions bien précises à l’arduino et pouvoir le piloter aussi à distance en utilisant un ordinateur.

Le point de plus qui change cette année en comparant les deux projets celui de l’année dernière et l’actuel en tout ce qui est code c’est l’utilisation du TIMER, on va utiliser un Timer pour pouvoir gérer le tapis au lieu de le faire rouler avec des pas et des temporisations qui n’est pas pratique.

Bibliothèques

Pour pouvoir gérer les Leds, on utilisera une bibliothèque spécifique aux types des Leds utilisés « Adafruit_Neopixel » qu’on va intégrer dans notre code: #include <Adafruit_Neopixel.h>.

Ensuite On va créer quatre variables qu’on va utiliser pour configurer cette bibliothèque, on a choisi LED1_COUNT, LED1_PIN, LED2_COUNT et LED2_PIN, dont le rôle est de choisir un pin pour chaque ruban de leds et une autre qui va compter les leds de chaque ruban. On choisit après chaque ruban et on affecte ces variables au ruban.

//Bilbliothèque des Leds
Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(LED1_COUNT, LED1_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(LED2_COUNT, LED2_PIN, NEO_GRB + NEO_KHZ800);

Définition des Pins

On a définit les Pins de l’Arduino comme suit :

  • Pin 1 du Switch : 7
  • Pin 2 du Switch : 8
  • Pin du premier bouton : 2
  • Pin du deuxième bouton : 13
  • Pin du timer : 9
  • Pin qui gère le sens du tapis : 5
  • Pin pour alimenter le tapis : 6
  • Pin du premier ruban des leds : 3
  • Pin du deuxième ruban des leds : 4
  • Premier Pin de la caméra : 10
  • Deuxième Pin de la caméra : 11
//Définition des Pins
# define Switch_1 7
# define Switch_2 8
# define Entree_Bouton 2
# define Entree_Bouton2 13
# define OC1a_Timer 9
# define Sens_Tapis 5
# define Alim_Tapis 6

//led pin
#define LED1_PIN  3
#define LED1_COUNT  25
#define LED2_PIN  4
#define LED2_COUNT  25

//camera pins
#define CAM1_PIN 10
#define CAM2_PIN 11

Déclaration des varaiables globales

Pour pouvoir gérer les Leds on a créé les variables suivantes : br1, br2, r1, r2, g1, g2, b1, b2, rg1_1, rg1_2, rg2_1, rg2_2. PB1_state et PB2_state pour savoir si le bouton est appuyé ou relâché.

numChars est une variable qui va nous servir dans la liaison série pour pouvoir envoyer ou recevoir un nombre limité de caractères, et receivedChars est un tableau qui va contenir les caractères envoyés ou reçu. newData est juste un booléen qui est égal soit à 0 ou 1 et qui dépend de la réception du caractère (reçu ou non). Par défaut on lu affecte la valeur 0 ( ou False.)

Ensuite passons aux variables qu’on va utiliser pour gérer nos boutons :

– lastbottonstat est la variable qui va nous informer du dernier événement appliqué sur le bouton

– currentbottonstat est l’état actuel du bouton

–  tapistat est l’état du tapis s’il est en marche ou pas.

//Déclaration variables globales

//vriables leds
int br1, br2, r1, r2, g1, g2, b1, b2, rg1_1, rg1_2, rg2_1, rg2_2;
 
int PB1_state, PB2_state; //variables boutons


const byte numChars = 200;
char receivedChars[numChars];
boolean newData = false;



//variables boutons
bool lastbottonstat=LOW;
bool currentbottonstat=LOW;
bool tapistat=LOW;

On commence notre code finalement en initialiisant la liaison en série, on va choisir une vitesse de 9600 bauds avec un délai de 300 ms, et on va mettre cette inialisation dans la fonction void setup comme suit :

Serial.begin(9600); //Liaison en série
delay(300);

Et juste après, on initialise aussi le TIMER 1 de l’arduino Uno en choisisant une fréquence d’échentillonnage de 16 MHz.

TCCR1B = 0x18; // 0001 1000, Disable Timer Clock 
TCCR1A = 0xA2; // 1010 0010

ICR1 = 1400;
OCR1A = (int) (ICR1 * 0.25);
OCR1B = (int) (ICR1 * 0.50);
TCNT1=0x0;

TCCR1B |= 1; // Prescale=1, Enable Timer Clock

Toujours dans la fonction setup, on passe à l’étape de choix de Pins. Après déclarer et affecter les Pins à des variables précis, on choisit le mode de chaque Pin.

//Choix des Pins
  pinMode(Switch_1,INPUT_PULLUP);
  pinMode(Switch_2,INPUT_PULLUP);
  pinMode(Entree_Bouton,INPUT_PULLUP);
  pinMode(Entree_Bouton2,INPUT_PULLUP);
  pinMode(OC1a_Timer, OUTPUT);  // OC1a
  pinMode(Alim_Tapis,OUTPUT);
  pinMode(Sens_Tapis,OUTPUT);
  digitalWrite(Sens_Tapis,HIGH);
  digitalWrite(Alim_Tapis,LOW);
  digitalWrite(Entree_Bouton,HIGH);
  digitalWrite(Entree_Bouton2,HIGH);
  digitalWrite(Switch_1,HIGH);
  digitalWrite(Switch_2,HIGH);

Ensuite, on configure les leds à une valeur par défaut, pour pouvoir s’activer et afficher au moins une couleur au démarrage et quand on change pas la valeur du degré soir du bleu, rouge ou vert de chaque ruban. les variables sont décrites comme suit:

  • br1/2 est le degré de luminosité des rubans
  • r1/2 est la valeur de la couleur rouge
  • b1/2 est la valeur de la couleur bleu
  • g1/2 est la valeur de la couleur verte
  • rg1-1/2 est le nombre des leds dans chaque ruban
//Configuration des Leds
  br1 = 100;
    r1 = 255;
    g1 = 255;
    b1 = 255;
    rg1_1 = 0;
    rg1_2 = LED1_COUNT-1;
    strip1.begin();
    strip1.show();

    br2 = 100;
    r2 = 255;
    g2 = 255;
    b2 = 255;
    rg2_1 = 0;
    rg2_2 = LED2_COUNT-1;
    strip2.begin();
    strip2.show();


Fonctions utilisés

recvWithStartEndMarkers()

Cette fonction sert à recevoir des caractères en utilisant la liaison en série . Après vérifier qu’il n’y a pas de nouveaux Data qui arrive on recois les caratères et on les place dans notre tableau receivedChar sinon on patiente.

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
 
    while (Serial.available() > 0 && newData == false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

ProcessNewData()

La fonction ProcessNewData sert à lire le caractères reçus. Si la valeur de newData est égale à 1 on lis les caratère reçus, si c’est

  • « Go » = Le tapis marche
  • « NoGo »: Le tapis s’arrête
  • « RightLeft » : le tapis tourne de droite à gauche
  • « LeftRight » : le tapis tourne au sens inverse de gauche à droite
  • « V » en indiquant un chiffre après: La vitesse du tapis change avec la valeur écrite après la chaîne de carctère V
  • « Led1_Go » : Lance le premier ruban de leds
  • « Led2_Go »: lance le deuxième ruban de leds
void ProcessNewData() {
    if (newData == true) {
    
        //Serial.print("The received data is :: ");
        //Serial.println(receivedChars);

        if (strcmp(receivedChars, "Go") == 0 || strcmp(receivedChars, "NoGo") == 0)
        {
            if (strcmp(receivedChars, "Go") == 0) 
              StartStopMotor(HIGH);
            else
              StartStopMotor(LOW);
        }
        if (strcmp(receivedChars, "RightLeft") == 0 || strcmp(receivedChars, "LeftRight") == 0)
        {
            if (strcmp(receivedChars, "RightLeft") == 0) 
              Direction(HIGH);
            else
              Direction(LOW);
        }
        
//##############
      if (strcmp(receivedChars, "V") == 0)
        {
           ICR1=receivedChars[1]-30*1000+receivedChars[2]-30*100+receivedChars[3]-30*10+receivedChars[4]-30;
           
        }
  

//################
        
        if (strcmp(receivedChars, "Vitesse") == 0)
        {
          Vitesse(ICR1);
        }
        else
        {
          char * pch;
          pch = strtok(receivedChars,",");
          while (pch != NULL)
          {
            
            if(strcmp(pch, "R1") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                r1 = atoi(pch);
                //strip1.fill(strip1.Color(r1, g1, b1), rg1_1, rg1_2);
              }
            }
  
            else if(strcmp(pch, "R2") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                r2 = atoi(pch);
                //strip2.fill(strip2.Color(r2, g2, b2), rg2_1, rg2_2);
              }
            }
  
            else if(strcmp(pch, "G1") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                g1 = atoi(pch);
                //strip1.fill(strip1.Color(r1, g1, b1), rg1_1, rg1_2);
              }
            }
            else if(strcmp(pch, "G2") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                g2 = atoi(pch);
                //strip2.fill(strip2.Color(r2, g2, b2), rg2_1, rg2_2);
              }
            }
            else if(strcmp(pch, "B1") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                b1 = atoi(pch);
                //strip1.fill(strip1.Color(r1, g1, b1), rg1_1, rg1_2);
              }
            }
            else if(strcmp(pch, "B2") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                b2 = atoi(pch);
                //strip2.fill(strip2.Color(r2, g2, b2), rg2_1, rg2_2);
              }
            }
            else if(strcmp(pch, "Br1") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                br1 = atoi(pch);
                strip1.setBrightness(br1);
                //strip1.fill(strip1.Color(r1, g1, b1), rg1_1, rg1_2);
              }
            }
            else if(strcmp(pch, "Br2") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                br2 = atoi(pch);
                strip2.setBrightness(br2);
                //strip2.fill(strip2.Color(r2, g2, b2), rg2_1, rg2_2);
              }
            }
            else if(strcmp(pch, "Rg1") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                rg1_1 = atoi(pch);
                pch = strtok (NULL, ",");
                if (pch != NULL)
                {
                  rg1_2 = atoi(pch);
                  strip1.fill(strip1.Color(r1, g1, b1), rg1_1, rg1_2);
                }
              }
            }
            else if(strcmp(pch, "Rg2") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                rg2_1 = atoi(pch);
                pch = strtok (NULL, ",");
                if (pch != NULL)
                {
                  rg2_2 = atoi(pch);
                  strip2.fill(strip2.Color(r2, g2, b2), rg2_1, rg2_2);
                }
              }
            }
            else if(strcmp(pch, "LED1_go") == 0)
            {
              strip1.show();
            }
            else if(strcmp(pch, "LED2_go") == 0)
            {
              strip2.show();
            }
            else if(strcmp(pch, "Sp") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                Sp = atoi(pch);
              }
            }
            else if(strcmp(pch, "Dr") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                Dr = atoi(pch);
              }
            }
            else if(strcmp(pch, "NbSteps") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                NbSteps = atoi(pch);
              }
            }
            else if(strcmp(pch, "Ret") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                Ret = atoi(pch);
              }
            }
            else if(strcmp(pch, "TrigPas1") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                TrigSteps1 = atoi(pch);
              }
            }
            else if(strcmp(pch, "TrigPas2") == 0)
            {
              pch = strtok (NULL, ",");
              if (pch != NULL)
              {
                TrigSteps2 = atoi(pch);



                
              }
            }
          
            pch = strtok (NULL, ",");
          }
        }
        
        newData = false; 
    }
}

StartMotor()

StartMotor() est la fonction principale qui lance et arrête le moteur du tapis. En utilisant les variables « currentbottonstat » et « lastbottonstat » on peut savoir si le bouton a été relâché pour pouvoir arrêter, puis faire fonctionner le tapis.

Si l’état actuel du bouton est différent de l’état précédent on alimente le tapis et on le fait tourner sinon on l’arrête et bien sûr après avoir vérifié si le tapis tourne déjà ou pas.

La variable tapistat permet de vérifier si le tapis tourne ou pas, si tapistat est à la valeur HIGH on arrête le tapis sinon on le fait tourner.

void StartMotor(){
     
    //Utilisation du bouton STOP/Marche
    currentbottonstat = digitalRead(Entree_Bouton);
     if (digitalRead(Entree_Bouton) == HIGH){
        if ( currentbottonstat != lastbottonstat )
          {
                if ( currentbottonstat == HIGH )
                {
       
                    if ( tapistat == LOW ) 
                        {
                        digitalWrite(Alim_Tapis, HIGH);
                        tapistat = HIGH;
                        }
                    else          
                    {
                      digitalWrite(Alim_Tapis, LOW);
                      tapistat = LOW;
                     }
                  }
                  else
                  {
                      if ( tapistat == HIGH ) 
                      {
                        digitalWrite(6, LOW);
                        tapistat = LOW;
                      }
                      else          
                      {
                        digitalWrite(6, HIGH);
                        tapistat = HIGH;
                      }
                   }
            }
       }
        if (digitalRead(Entree_Bouton) == LOW) 
        {  
          if ( currentbottonstat != lastbottonstat )
          {
              if ( currentbottonstat == HIGH )
              {
                  if ( tapistat == LOW ) 
                  {
                      digitalWrite(6, HIGH);
                      tapistat = HIGH;
                  }
                  else 
                  {
                      digitalWrite(6, LOW);
                      tapistat = LOW;
                  }
                }
                else
                {
                    if ( tapistat == HIGH ) 
                    {
                      digitalWrite(6, LOW);
                      tapistat = LOW;
                    }
                    else          
                    {
                      digitalWrite(6, HIGH);
                      tapistat = HIGH;
                    }
                 }
            }
          }
    lastbottonstat = currentbottonstat;
          if (digitalRead(Switch_1) == HIGH)
        {
            digitalWrite(Sens_Tapis, LOW);
        }
        if (digitalRead(Switch_2) == HIGH)
        {
            digitalWrite(Sens_Tapis, HIGH);
        }
}

Direction()

void Direction(bool RightLeft){
  digitalWrite(Sens_Tapis, RightLeft);
  }
 

StartStopMotor()

void StartStopMotor(bool GoNoGo){
  digitalWrite(Alim_Tapis, GoNoGo);
}

Vitesse()

void Vitesse(int vit){
  digitalWrite(ICR1, vit);
}

Fonction Principale

void loop() {
    recvWithStartEndMarkers(); //Données données par l'utilisateur
    ProcessNewData(); 
    
    StartMotor();
}

Contrôle du banc de vision avec du Python

Finalement pour pouvoir contrôler le banc de vision en utilisant un ordinateur, il faut être obligatoirement connecté à l’Arduino en utilisant un câble USB, et on va utiliser dy python et le logiciel Anaconda.

On lance Anaconda, et on a utilisé Spyder comme éditeur de texte pour lancer notre programme. Le programme écrit en python nous permet de gérer les leds et le moteur du tapis en utilisant les commandes qu’on a intégré dans nos fonctions. Le bout de code qui nous permet de faire cela est le suivant :

if serial_arduino is not None:

    serial_arduino.write(('<Br1,255,B1,255,G1,255,R1,255,Rg1,0,12>').encode())
    serial_arduino.write(('<LED1_go>').encode())

    serial_arduino.write(('<Br2,255,B2,255,G2,255,R2,255,Rg2,0,12>').encode())
    serial_arduino.write(('<LED2_go>').encode())


    serial_arduino.write(('<Sp,300>').encode())
    sleep(1)
    serial_arduino.write(('<Dr,0>').encode())
    serial_arduino.write(('<NbSteps,50000,TrigSteps1,500,TrigSteps2,500>').encode())

    #sleep(1)
    #serial_arduino.write(('<Go>').encode())
    #sleep(1)
    #erial_arduino.write(('<NbSteps,1500>').encode())
    #sleep(1)

Caméra

Nous utiliseront une Caméra In-Sight D900 ainsi que le logiciel Insight Explorer afin de traiter les images renvoyées par la caméra, et pour pouvoir lire les défauts détectés.

Partie Structure

On un banc qui a été fait par les étudiants de l’année précédante, avec des plaques en couleur noir ayant pour but d’absorber la lumière, et des barres métalliques pour tenir le matériel qui sera installé dessus. On a aussi une plaque de bois bien rigide, qu’on a utilisé pour poser le tapis dessus, et coller l’alimentation, la carte commande et l’arduino au-dessous.

Carte électronique

Ensuite on a le convoyeur qui lui permet de déplacer les pièces , il s’agit d’un tapis mis en rotation par un moteur .

Et la structure qui vas accueillir ces 2 éléments ainsi que les le supports leds.

Cependant la plaque ne dispose pas d’une protection électrique certifié IP2X. On a protégé la partie alimentation avec un boitier qu’on a installé en utilisant des pièces magnétiques pour pouvoir le détacher facilement, mais il faut l’installer définitivement sans pouvoir le détacher, et protéger la partie Arduino et commande.

Résultats

  • Code Fonctionnel
  • Câblage installation terminé
  • Liaison série réussie

Travail à finir

  • Installer la caméra de vision afin de rendre le système opérationnel
  • Mettre en place une protection IP2X afin de protéger l’installation (un boitier fermé que l’on ne peux ouvrir qu’a l’aide d’un outil )
  • Améliorer le câblage de l’installation

Problèmes rencontrés

  • Problème de code parce que c’est a première fois qu’in utilise ce type de logiciel et on code en langage arduino
  • Problème de liaison série et les commandes à envoyer pour pouvoir le contrôler
  • Problème de protection IP2X , parce qu’il y avait pas assez de matériel et de temps pour pouvoir bien protéger le banc
  • Problème de câblage, parce que on a pas prévu des longs câbles pour les leds
  • Prôblème de gestion de temps et d’organisaton

Codes sources