ACTIVITE ARDUINO/PYTHON : compteur de défilement d’anneaux pour interféromètre de Michelson, applications

Objectif de cette activité : Comptage automatique de défilements d’anneaux pour un interféromètre de Michelson avec une carte Arduino et un shield LCD DFROBOT. Possibilité de récupérer les courbes avec Python.

Applications : mesure de l’indice de l’air, étude de la dilatation d’un métal, …

Pré-requis :

  • Etre familier avec le langage Arduino et Python (mais pas forcément, on peut utiliser le code sans le comprendre !).
  • Savoir régler un interféromètre en lame d’air pour obtenir des anneaux localisés à l’infini (qui seront projetés sur un écran au niveau du plan focal d’une lentille convergente de +20 cm)

Présentation du compteur

Utilisation des boutons du curseur

LEFT : Pour remettre le compteur à zéro

RIGHT : réglage automatique du seuil ; chariotter  doucement en maintenant ce bouton appuyé

UP/DOWN : réglage manuel du seuil à zéro

Principe

Un photodétecteur (« cible » DIDALAB par exemple) est connecté à la première entrée analogique (entrée A1 car AO est déjà utilisée pour récupérer les valeurs envoyées par les boutons du curseur du shield).

Le réglage du seuil de détection dépend du photodétecteur et de la luminosité. Idéalement, il faudrait qu’il corresponde à la valeur moyenne de la tension mesurée lors de l’alternance des franges ou anneaux.

Pour le régler efficacement, Il suffit de faire défiler les anneaux en maintenant le bouton RIGHT enfoncé, ou le régler manuellement (boutons UP/DOWN en faisant une acquisition avec la carte Sysam pour visualiser le signal.

Il est aussi possible de lancer le script Python Michelson.py (avec un IDLE comme Pyzo par exemple) pour une acquisition en temps réel.

Codes Arduino et Python utilisés

/* 
 * Code pour compteur de défilement d'anneaux avevl'interféromètre de Michelson.
 * Un photodétecteur (« cible » DIDALAB par exemple) est connecté à la première entrée analogique 
 * (entrée A1 car AO est déjà utilisée pour récupérer les valeurs envoyées par les boutons du curseur du shield).

  Le réglage du seuil de détection dépend du photodétecteur et de la luminosité. Idéalement, 
  il faudrait qu’il corresponde à la valeur moyenne de la tension mesurée lors de l’alternance des franges ou anneaux. 
  Pour le régler efficacement, il suffit de faire défiler les anneaux en maintenant le bouton  RIGHT enfoncé, 
  ou faire une acquisition avec la carte Sysam pour visualiser la tension.
.
 */

#include <EEPROM.h>

#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9, 4, 5, 6, 7);
int sensor = A1; // broche pour détection du capteur
int etatSensor ; // état du capteur (haut ou bas)
int valeur_bouton;
int seuil  ;
float seuil_tension ;
int mesure;
int somme_mesure = 0;
float mesure_tension;
float valeur_moyenne =0;
int compt_moy = 0;
bool etat_old= false ; // 
bool  etat_new = false; // les états vont changer à chaque chaque modiication de la valeu lue par le capteur (haut/5V ou bas/0V)
int compt=0; // comptage initialisé à 0
long temps; //mesure du temps pour l'acquisition

void setup() 
{
  
  Serial.begin(9600); // pour le moniteur série
  lcd.begin(16,2);
  int seuil;
  valeur_bouton  = analogRead(A0);
  temps = millis();
}  
 
void loop()
{

valeur_bouton  = analogRead(A0);
//Serial.println(valeur_bouton);

// gestion des boutons du curseur 
//(remise à zéro pour LEFT, réglage du seuil auto avec RIGHT,
//réglage du seui manuel avec UP/DOWN)

if (valeur_bouton ==205 or valeur_bouton ==203) {
  seuil+=1;
  EEPROM.put(0, seuil);
  delay(10);
}
if (valeur_bouton == 405 or valeur_bouton == 402) {
  seuil-=1;
  EEPROM.put(0, seuil);
  delay(10);
}

 EEPROM.get(0,seuil);
 
if (valeur_bouton == 619 or valeur_bouton == 622) {
  lcd.clear();
  compt=0;
  somme_mesure = 0;
  compt_moy = 0;
  delay(100);
}

temps = millis();
mesure = analogRead(sensor);
mesure_tension = mesure*5.0/1023;
seuil_tension = seuil *5.0/1023;

if (valeur_bouton == 0) {
  
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(" Etalonnage  ");
  lcd.setCursor(0,1);
  lcd.print("Chariotter SVP");
 
  compt_moy = compt_moy + 1;
  somme_mesure = somme_mesure + mesure;
  valeur_moyenne = somme_mesure/compt_moy;
  seuil = valeur_moyenne;
  EEPROM.put(0, seuil);
//  Serial.print(compt_moy);
//Serial.print ("\t");
//Serial.println(valeur_moyenne);
  delay(500);
 lcd.clear();
  
}
else{
  somme_mesure = 0;
  compt_moy = 0;
}

lcd.setCursor(0,0);
lcd.print("Comptage  |seuil");
lcd.setCursor(0,1);
lcd.print(compt/2);
lcd.setCursor(10,1);
lcd.print("|");
lcd.setCursor(11,1);
lcd.print(seuil_tension);
lcd.setCursor(15,1);
lcd.print("V");



if (mesure_tension> seuil_tension){
  etat_new = true; 
}
else {
  etat_new =false;
}
if (etat_old != etat_new) {
  etat_old = etat_new;
  compt = compt + 1;
//  Serial.print("  comptage ");
//  Serial.println(compt);
//lcd.clear();


  }
//  
Serial.print(temps);
Serial.print ("\t");
Serial.println(mesure_tension);

//Serial.println(valeur_bouton);

 delay(20);
}

Voici le script Python qu’on peut utiliser une fois le code Arduino ci-dessus téléversé.

Attention : le moniteur série doit être fermé pour que ce script fonctionne !


 #importation des modules
 import serial
 import serial.tools.list_ports # pour la communication avec le port série
 import matplotlib.pyplot as plt  # pour le tracé de graphe
 from matplotlib import animation # pour la figure animée
 import time # gestion du temps
 

 #initialisation des listes
 

 liste_temps=[] # liste pour stocker les valeurs de temps en partant de t=0
 liste_tension = [] # liste pour stocker les valeurs de distance
 

 t_acquisition = 1000
 tensionmax= 5 # en V
 


 #pour le graphe en temps réel
 def animate(i):
     line1 = Data.readline()
     line1 = Data.readline()
     print (line1)
     # on retire les caractères d'espacement en début et fin de chaîne
     listeDonnees = line1.strip()
     # on sépare les informations reçues séparées par les espaces et on stocke ces informations dans une liste pour chacune de lignes
     listeDonnees = line1.split()
     print (listeDonnees)
 


     if len(listeDonnees) == 2 : # parfois des lignes de données vides peuvent être envoyées, il faut les "écarter"
         try :
             tension = float(listeDonnees[1].decode()) # après consulation des données, nous choisissons le 4ème élément de listeDonnees
             temps = (float(listeDonnees[0].decode()))/1000.0 # après consulation des données, nous choisissons le 2ème élément de listeDonnees
 

 

             while temps <= t_acquisition:
 

                 liste_tension.append(tension)
                 print("tension = %f"%(tension)) # affichage de la valeur de la distance
                 liste_temps.append(temps)
                 print("temps mesuré = %f"%(temps), " s") # affichage de la valeur du temps absolu
                 line.set_data(liste_temps,liste_tension)
                 return line,
 

         except:
             pass
 


 # Fonction pour la récupération des données série venant de la carte Arduino
 def recup_port_Arduino() :
     ports = list(serial.tools.list_ports.comports())
     for p in ports:
         if 'Arduino' in p.description :
             mData = serial.Serial(p.device,9600)
     print(mData.is_open) # Affiche et vérifie que le port est ouvert
     print(mData.name) # Affiche le nom du port
     return mData
 

 
 def moyenne(liste):
     somme=0
     for i in range (len (liste)) :
         somme += liste[i]
     return somme/(len(liste))
 

 def comptage (liste):
     moy = moyenne (liste)
     etat_old = False
     etat_new = False
     compt = 0
     for i in range (len(liste)):
         if (liste[i]> moy) :
             etat_new = True
         else :
             etat_new = False
 

         if etat_old != etat_new :
             etat_old = etat_new
             compt = compt + 1
     return compt/2
 


 Data =recup_port_Arduino() #récupération des données
 
 # Création figure
 fig=plt.figure()
 line, = plt.plot([],[])
 plt.xlim(0, t_acquisition)
 plt.ylim(0,tensionmax)
 plt.xlabel('temps en s')
 plt.ylabel('tension')
 plt.grid()
 

 
 #Animation
 ani = animation.FuncAnimation(fig, animate, frames=20000,  interval=20,repeat=False)
 

 plt.show()


 plt.close(fig)
 Data.close() # pour arrêter la lecture des données série
 



 #comptage en parcourant les listes
 compteur = int(comptage(liste_tension))
 
 texte = "Compteur  : " +str(compteur)
 plt.title(texte) # titre du graphique
 plt.plot(liste_temps,liste_tension, color ='r')
 plt.xlabel('temps en s')
 plt.ylabel('tension')
 plt.xlim (min(liste_temps),max(liste_temps))  #limtes pour les axes avec les valeurs extrêmes de I et de U
 plt.ylim(min(liste_tension),max(liste_tension))
 

 plt.show()  #afficher le graphique (ne rien mettre dans la parenthèse)
 

 #Ecriture dans un fichier txt
 lines=['t\ttension\n'] #première ligne du fichier txt
 for i in range (len (liste_tension)):
     line = str(liste_temps[i]) +'\t'+ str(liste_tension[i])+'\n'
     lines.append(line)
 

 fichier = open('U:\Documents\essais Python\Améliorations\Données série Michelson\data.txt', 'w')
 fichier.writelines(lines) #création d'un nouveau fichier texte
 

Astuce : parfois un message d’erreur apparaît (problème de communication avec la carte, données reçues incomplètes …), comment résoudre ce problème ?

La plupart du temps, il suffit de redémarrer le shell (CTRL+K) et relancer ensuite le programme.

Applications

Mesure de l’indice de l’air par interférométrie

Matériel :

  • Interféromètre de Michelson
  • Laser
  • Elargisseur de faisceau (lentille  +5mm)
  • 1 lentille + 20 cm
  • 1 écran
  • Enceinte à dépression de longueur 40 mm NOVA avec système de pompe à vide (à main)

Protocole

  • Insérer la cuve à vide sur pied dans l’un des bras de l’interféromètre.
  • Avec un laser et élargisseur de faisceau, régler l’interféromètre en lame d’air (anneaux)
  • Positionner le détecteur en sortie de Michelson au niveau du plan focal image d’une lentille de focale +20 cm (« cible » avec réglage de gain x20).
  • Relier ce détecteur à l’entrée analogique EA1 du compteur Arduino
  • Faire le vide avec la pompe à main.
  • Maintenir la touche RIGHT du compteur pendant cette opération (réglage automatique du seuil) 
  • Mettre le compteur à zéro (touche LEFT)
  • Lancer l’acquisition (pendant 1 min environ) avec le script Python Michelson.py , puis faire rentrer doucement  l’air à l’aide de la pince en plastique.

Exploitation

En supposant que le vide ait été entièrement  réalisé,   l’indice  n de l’air est donné par la  formule :

  n  =  1   +   Nλ/2L
Avec
N nombre d’anneaux qui défilent,
longueur d’onde du laser λ =  632,8 e-9 m,
longueurde la cuve L = 40 e-3 m

Graphe obtenu avec le script Python

Etude de dilation d’un métal

Matériel :

  • Interféromètre de Michelson
  • Laser
  • Elargisseur de faisceau (lentille  +5mm)
  • 1 lentille + 20 cm
  • Bloc métallique placé de référence
  • Bloc métallique placé préalablement au congélateur
  • Capteur « cible » Didalab (photodétecteur)
  • Compteur Arduino
  • Carte Sysam

Protocole

  • Intercaler le bloc métallique de référence (température ambiante)
  • Avec un laser et élargisseur de faisceau, régler l’interféromètre en lame d’air (anneaux)
  • Positionner le détecteur en sortie de Michelson au niveau du plan focal image d’une lentille de focale +20 cm (« cible » avec réglage de gain x20).
  • Relier ce détecteur à l’entrée analogique EA1 du compteur Arduino
  • Pour régler le seuil, faire défiler les anneaux doucement en maintenant la touche RIGHT enfoncée.
  • Mettre le compteur à zéro (touche LEFT)
  • Insérer le bloc métallique froid (qui était stocké dans le congélateur)
  • Lancer l’acquisition (pendant 1 min environ) avec le script Python Michelson.py

Exploitation

Le coefficient de dilatation isobare est donné par la relation :


α  =    3N λ    
           2l∆T

avec  N nombre d’anneaux qui défilent,
longueur d’onde du laser λ =  632,8 e-9 m,
l longueur du bloc
∆T variation de température du bloc

Lien Github avec les scripts :

https://github.com/jonasforlot/python-arduino/tree/main/compteur_Michelson

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *