Table des matières Python

Émetteur et récepteur d'impulsions ultrasonores

1. Introduction

Les ultrasons peuvent être utilisés pour la mesure de distances ou le repérage d'objets. L'émetteur est un transducteur piézoélectrique auquel on doit appliquer une perturbation électrique afin de le faire osciller pendant un temps très court à sa fréquence de résonance (par ex. 40 kHz). Le récepteur est aussi un transducteur piézoélectrique, dont la fréquence de résonance (40 kHz) doit être identique à celle de l'émetteur. Le décalage temporel entre l'excitation de l'émetteur et le signal reçu nous informe sur la durée du trajet et donc sur la distance parcoure par l'onde entre l'émetteur et le récepteur.

Ce document décrit la réalisation d'un émetteur piloté par un microcontrôleur Arduino, permettant d'émettre des impulsions à la fréquence de 40 kHz. L'utilisation d'un pont à transistors MOSFET associé à un circuit résonant RLC permet d'atteindre une tension de plusieurs dizaines de volts. Le circuit peut être alimenté par une pile de 9 V, largement suffisante pour un transducteur de faible puissance.

Un circuit de réception sera aussi décrit, comportant un amplificateur de charge et un filtre passe-bande.

2. Circuit d'émetteur

2.a. Principe de fonctionnement

Un pont de transistors (pont en H) alimenté par une source de tension 12 V (valeur minimale) permet de générer des impulsions de période 25 microsecondes (fréquence 40 kHz) et d'amplitude +/- 12 V. La tension U en sortie du pont est appliquée à une bobine en série avec un condensateur. Le transducteur est branché aux bornes du condensateur.

principe-emetteur.svgFigure pleine page

Lorsque les transistors T1 et T4 sont à l'état passant, et T2 et T3 à l'état bloquant, la tension U est égale à +12 V. Lorsque les transistors T1 et T4 sont à l'état bloquant, et T2 et T3 à l'état passant, la tension U est égale à -12 V. On obtient ainsi une variation de tension de forme carrée et d'amplitude 12 V (ou plus si on augmente la tension d'alimentation).

La période des impulsions est 25μs, ce qui correspond à la fréquence de 40 kHz adaptée au transducteur utilisé.

Le circuit résonant est constitué d'une bobine sans noyau (enroulement d'un fil de cuivre sur un tube en PVC) en série avec un condensateur. Sa fréquence de résonance est :

fr=12πLC(1)

Elle doit être proche de 40 kHz. Pour réaliser ce circuit, nous avons utilisé deux condensateurs de 98 nF chacun en série, soit une capacité C=49nF. Nous disposons d'une bobine d'inductance propre L=280μH et de résistance interne R=0,6Ω. La fréquence de résonance est donc 43kHz, ce qui est assez proche de la fréquence souhaitée. Si cela est nécessaire, il est toujours possible de faire la bobine sur mesure pour obtenir exactement la résonance à 40 kHz. Nous avons utilisé des condensateurs de puissance, pouvant supporter une tension de 1000 V, ce qui est très largement suffisant pour cette application. Il faut remarquer que l'emploi d'une bobine à noyau en fer doux ou en ferrite n'est pas conseillé en raison des pertes supérieures qu'il impliquerait et donc d'un moindre facteur de qualité du circuit résonant.

Voyons la tension aux bornes du condensateur en fonction de la fréquence. La fonction de transfert en régime harmonique est :

H(ω)=11+j1Qωω0-ω2ω02(2) ω0=1LC(3) Q=1RLC(4)

Un facteur de qualité élevé est souhaité. La résistance est celle de la bobine. Il est préférable de choisir une capacité faible et une inductance propre élevée. L'inductance est proportionnelle au carré du nombre de spires et la résistance au nombre de spires. Le rapport LR est donc indépendant du nombre de spires pour un fil de section donné. On aura donc intérêt à choisir un condensateur de capacité relativement faible (mais pouvant supporter des tension élevées) et à réaliser l'enroulement du nombre de spires permettant d'obtenir la valeur de L souhaitée, avec un fil de grosse section.

La modélisation de ce circuit n'est pas aisée car la résistance de la bobine effective à 40 kHz est certainement plus grande que la résistance mesurée à l'ohmmètre.

import numpy as np
from matplotlib.pyplot import * 

L=280e-6
C=49e-9
R=1
f0=1/(np.pi*2*np.sqrt(L*C))
Q=1/R*np.sqrt(L/C)
                 
print(f0)
--> 42967.79585181555
print(Q)
--> 75.59289460184544

Voici la tension appliquée au transducteur en fonction de la fréquence :

f = np.linspace(35e3,45e3,1000)
H = 1/(1-f**2/f0**2+1j/Q*f/f0)
U = 24*np.absolute(H)
figure()
plot(f/1e3,U)
plot([40,40],[0,200],"r-")
xlabel("f (kHz)")
ylabel("V (V)")
grid()
                 
courbeResonance.svgFigure pleine page

L'amplitude à la fréquence de 40 kHz est donc d'environ 200 V, mais la valeur réelle est probablement plus faible car la résistance interne de la bobine est certainement plus grande. Si la tension atteinte est trop grande pour le transducteur utilisé, on pourra augmenter un peu la fréquence de résonance en enlevant quelques spires à la bobine.

2.b. Réalisation

Nous utilisons le pont à transistors DMOS L6203. Ce pont peut fonctionner jusqu'à 100 kHz, ce qui est bien pour cette application. Il peut fournir jusqu'à 4 A, ce qui est tout à fait inutile car le transducteur utilisé est de faible puissance.

pont-L6203.svgFigure pleine page

La charge est constituée de la bobine et du condensateur en série, avec le transducteur aux bornes du condensateur. L'alimentation Vs est à 12 V. Le courant consommé est très faible donc une petite alimentation convient. Nous avons fait fonctionné le circuit avec une pile de 9 V associée à un régulateur boost (Pololu) qui nous permet d'obtenir la tension de 12 V (minimum nécessaire au fonctionnement correct du pont). Cette alimentation 12 V peut également alimenter l'Arduino, bien qu'en général celui-ci soit plutôt alimenté par le PC sur lequel il est branché pour sa programmation.

La sortie D6 de l'Arduino permet d'activer le pont (ENABLE). La sortie D5, reliée à l'entrée IN 1 du pont, permet de commander la paire de transistors T1 et T2 : si IN1 est à l'état bas, T2 est fermé et T1 est ouvert. La sortie D2, reliée à l'entrée IN2, permet de commander la paire de transistors T3 et T4 : si IN2 est à l'état bas, T4 est fermé et T3 est ouvert.

Voici le programme Arduino qui permet de piloter le pont. La fonction pulse_train configure le Timer 3 pour qu'il génère un certain nombre d'impulsions avec une période donnée et un rapport cyclique donné. Les sorties OC3A et OC3B sont utilisées pour générer les signaux de commande de IN1 et IN2. Le premier est généré sur la sortie PE3, reliée à la borne D5 de l'Arduino, et le signal complémentaire est généré sur la sortie PE4, reliée à la borne D2. À l'état de repos, on place IN1 et IN2 à l'état bas, ce qui ramène les deux bornes de la charge à la masse.

arduinoGenerateurImpulsionsMEGA.ino
#include "Arduino.h"

#define ENABLE 6
#define IN1 5 // OC3A (timer 3, Output compare A), PE3 (port E sortie 3)
#define IN2 2 // OC3B (timer 3, Output compare A), PE4 (port 3 sortie 4)
 
char clockBits;
volatile uint32_t count;
int32_t max_count;
uint16_t diviseur[6] = {0,1,8,64,256,1024};
uint32_t icr;


void pulse_train(uint32_t period, int32_t nombre, float rapport) { 
  TCCR3A = 0;
  TCCR3A |= (1 << COM3A1) | (1 << COM3A0) | (1 << COM3B1); // OC3A : set on compare match, OC3B : clear on compare match
  TCCR3B = 1 << WGM13; // phase and frequency correct pwm mode, top = ICR3
  icr = (F_CPU/1000000*period/2);
  int d = 1;
  while ((icr>0xFFFF)&&(d<5)) {
     d++;
     icr = (F_CPU/1000000*period/2/diviseur[d]);
   } 
  clockBits = d;
  uint16_t ocra = icr * (1.0-rapport);
  ICR3 = icr;
  OCR3A = ocra;
  OCR3B = ocra;
  TIMSK3 = 1 << TOIE3; // overflow interrupt enable
  sei();
  count = 0;
  max_count = nombre+1;
  TCNT3 = 0; // mise à zéro du compteur
  TCCR3B |= clockBits;
}
 
ISR(TIMER3_OVF_vect) { // Overflow interrupt
   count++;
    if (count==max_count) {
      TCCR3B &= ~0x7;
      TCNT3 = 0;
      TCCR3A = 0;
      PORTE &= ~((1 << PORTE3)|(1 << PORTE4));
   } 
}

void setup() {
  pinMode(ENABLE,OUTPUT);
  digitalWrite(ENABLE,HIGH);
  DDRE |= 1 << PORTE3; // data direction register
  DDRE |= 1 << PORTE4;
  PORTE &= ~((1 << PORTE3)|(1 << PORTE4)); // IN1 = 0, IN2 = 0 état de repos
  
}

void loop() {
  delay(1000);
  pulse_train(25,6,0.5);
}            
                     

3. Circuit récepteur

Le transducteur de réception présente une charge électrique proportionnelle à l'effort mécanique qu'on lui applique. Un intégrateur de courant est donc un bon choix pour obtenir une tension électrique proportionnelle à cet effort.

amplificateur.svgFigure pleine page

Le courant délivré par le transducteur vient charger le condensateur C2 (une très petite partie traverse la résistance). En première approche, si l'ALI est supposé idéal :

i=-dqdt dudt=-1C2i dudt=1C2dqdt

La résistance en parallèle avec C2 permet d'absorber le courant de polarisation de l'ALI et d'éviter ainsi une dérive de l'intégrateur. On a donc finalement :

u(t)=1C2q(t)(5)

La capacité C2 est choisie assez petite pour assurer une amplitude suffisante en sortie (inférieure à la tension d'alimentation).

Il est nécessaire d'ajouter un filtrage passe-haut permettant d'éliminer les ondulations à 50Hz. On opte donc pour le montage suivant, qui a de plus l'avantage de fonctionner avec une alimentation simple :

amplificateur-filtre.svgFigure pleine page

L'ALI utilisé (TLV 2372) peut fournir une tension de sortie qui s'étend jusqu'aux tensions d'alimentation (rail to rail) et sa bande passante à gain unité est 3MHz. Si le circuit est réalisé sur une plaque d'essai, le condensateur C2 peut être omis car la capacité entre les deux bandes de cuivre est largement suffisante.

La fonction de transfert de l'ALI peut être modélisé par la fonction suivante :

Ha=μ1+jffc(6)

Le TLV2372 a une bande passande de 3 MHz à gain unité donc μfc=3MHz . Les tensions dans le circuit (en régime sinusoïdal) sont reliées par :

Vs=-HaV-Ve-Vs=Z1IV--Vs=Z2I

On obtient ainsi la fonction de transfert du filtre :

H=VsVe=-11Ha+Z1Z2(1Ha+1)(7)
R1=1000
C1=100e-9
R2=220e3
C2=1e-12
fc=10
mu=3e6/fc

def transfert(f):
    w =2*np.pi*f
    Z1 = 1j/(C1*w)+R1
    Z2 = 1/(1j*C2*w+1/R2)
    Ha = mu/(1+1j*f/fc)
    H = -1/(1/Ha+Z1/Z2*(1/Ha+1))
    return H
    
f = np.logspace(1,5,1000)
H = transfert(f)
GdB = 20*np.log10(np.absolute(H))

figure()
plot(f,GdB)
xscale('log')
grid()
xlabel("f (Hz)")
ylabel("GdB")
              
ampliampli.pdf
G = np.absolute(transfert(40e3))
             
print(G)
--> 68.7278676291063

4. Tests

L'émetteur et le récepteur sont placés l'un en face de l'autre à une distance de 60cm. Sur la voie 1 de l'oscilloscope (en bleu), on enregistre le signal de commande (IN1). Sur la voie 2 (en rouge), on enregistre le signal en sortie de l'amplificateur de réception, lequel est alimenté par une alimentation de 10 V.

Le signal de commande comporte 5 cycles.

signaux-60cm-LCsignaux-60cm-LC.pdf

Pour cette alimentation de 10 V, on atteint ici le maximum d'amplitude (+/- 5 V).

La tension aux bornes du transducteur ne peut être visualisée directement avec un oscilloscope car aucune des deux bornes n'est à la masse. Une sonde différentielle permet de le faire. Voici cette tension (en rouge) obtenue avec une sonde différentielle qui atténue la tension d'un facteur 100 :

tensionPiezo-6cyclestensionPiezo-6cycles.pdf
print(u2.max())
--> 1.496063

La tension de crête aux bornes du transducteur est 150V. Si cette tension est trop grande pour le transducteur utilisé, on peut l'abaisser en diminuant le nombre de spires de la bobine, ce qui devrait avoir pour effet d'éloigner la fréquence de fonctionnement de la fréquence de résonance. Voici la tension aux bornes du condensateur, en l'absence du transducteur :

tensionCondens-6cyclestensionCondens-6cycles.pdf

La comparaison avec la courbe précédente montre que le transducteur lui-même ne joue qu'un rôle négligeable dans la forme de cette tension.

L'intervalle de temps entre le début du signal de commande et le début du paquet reçu est de 1,80ms. Or le temps de propagation du son dans l'air pour cette distance est 1,74ms. L'écart correspond à une durée comprise entre 2 et 3 cycles de 25μs, sans doute le temps qu'il faut à l'émetteur pour se mettre en vibration de manière significative.

On peut se contenter d'exciter l'émetteur avec une seule impulsion :

signaux-50cm-LC-1cyclesignaux-50cm-LC-1cycle.pdf

L'amplitude du signal reçu est 2 fois plus faible que dans le cas où en excite avec 6 impulsions. Voyons la tension aux bornes du transducteur :

tensionPiezo-1cycletensionPiezo-1cycle.pdf
print(u2.max())
--> 0.6299213

On constate que l'excitation du transducteur s'étend bien au delà de l'impulsion de déclenchement. Pour une impulsion, la tension maximale n'est plus que de 63 V. On peut donc réduire la tension appliquée en diminuant le nombre d'impulsions (le nombre de commutations du pont).

L'hypothèse qu'il s'agirait de l'onde acoustique se propageant dans la table conduit à un temps de propagation (pour une vitesse de l'ordre de 3000m⋅s-1) de 0,1ms, nettement plus grand que le délai observé. Si on soulève l'émetteur et le récepteur en les tenant à la main, cette partie du signal est toujours présente, ce qui montre clairement qu'il n'est pas d'origine acoustique. Si on éloigne les deux transducteurs l'un de l'autre, ce signal est plus faible mais apparaît toujours au même moment. La similitude avec la tension aux bornes du transducteur montre qu'il s'agit très probablement d'une perturbation électromagnétique.

L'amplificateur peut être aussi alimenté par l'Arduino, mais dans ce cas l'amplitude de crête à crête en sortie ne peut dépasser 5 V. Voici les signaux pour une excitation comportant 6 impulsions et une distance de 50 cm entre l'émetteur et le détecteur (avec une amplitude d'excitation de 12 V).

signaux-50cm-sansLC-6cyclessignaux-50cm-sansLC-6cycles.pdf

Voici les signaux pour une excitation de 10 cycles :

signaux-50cm-sansLC-10cyclessignaux-50cm-sansLC-10cycles.pdf

Voici les signaux pour une excitation de 15 cycles :

signaux-50cm-sansLC-15cyclessignaux-50cm-sansLC-15cycles.pdf

Voici les signaux pour une excitation de 20 cycles :

signaux-50cm-sansLC-20cyclessignaux-50cm-sansLC-20cycles.pdf

Nous voyons que le maximum d'amplitude est obtenu lorsque l'excitation comporte environ 15 cycles. Aller au-delà ne fait que prolonger la durée du paquet.

Voyons un exemple de réception d'une onde réfléchie sur un obstacle. Les deux transducteurs sont posés sur le bord d'une table à 15 cm l'un de l'autre et leur face est dirigée vers le sol, situé à 76 cm.

signaux-LC-echosignaux-LC-echo.pdf

L'écho reçu à 4,57ms correspond bien à une distance de propagation d'environ 152 cm à cause de la réflexion sur le sol. Le paquet reçu à 0,55ms vient de la propagation directe entre l'émetteur et le récepteur.

5. Mesure du temps de trajet

Afin de faire une détection du paquet reçu, nous ajoutons un comparateur derrière l'amplificateur :

amplificateur-detecteur.svgFigure pleine page

Le potentiomètre est réglé afin que la tension de l'entrée inverseuse du comparateur soit un peu au-dessus de 2,5V, assez haute pour que seuls les paquets significatifs soient détectés.

Voici le signal en sortie du comparateur (en rouge) lorsque les deux transducteurs sont placés côte à côte à 15cm l'un de l'autre et dirigés vers le sol.

detection-echodetection-echo.pdf

Le programme Arduino présenté ci-dessous permet de mesurer la durée entre le début du signal de commande et le premier front montant complet du second paquet, celui provenant de la réflexion sur le sol. On utilise pour cela l'entrée D48 de l'Arduino MEGA, qui permet d'utiliser le Timer 5 en mode Input Capture, comme expliqué dans Mesures de fréquence et de temps. Le compteur du Timer 5 est déclenché en même temps que celui du Timer 3 qui sert à générer les impulsions. On le programme de manière à déclencher une interruption lorsqu'un front montant est détecté sur l'entrée ICP5, qui est reliée à la borne D48 sur l'Arduino MEGA. Dans le gestionnaire d'interruption (TIMER5_CAPT_vect), on lit la valeur du compteur de Timer 5 et on stoppe le Timer 5. Pour ne pas détecter le premier paquet, on introduit aussi un délai avant de démarrer la détection.

La fonction pulse_train a un quatrième argument, qui précise le délai en microsecondes avant le début de la détection. La fonction loop effectue, toutes les secondes, l'émission d'un signal et la détection du paquet, puis affiche la durée en millisecondes.

arduinoGenerateurImpulsionsMEGA.ino
#include "Arduino.h"

#define ENABLE 6
#define IN1 5 // OC3A (timer 3, Output compare A), PE3 (port E sortie 3)
#define IN2 2 // OC3B (timer 3, Output compare A), PE4 (port 3 sortie 4)
#define DETECT 48 // ICP5
 
char clockBits;
volatile uint32_t count;
int32_t max_count, count_start_detect;
volatile boolean start_detect;
uint16_t diviseur[6] = {0,1,8,64,256,1024};
uint32_t icr;
volatile uint16_t delai;
uint8_t capture_clock = 2;


void pulse_train(uint32_t period, int32_t nombre, float rapport, float delai_detect) { 
  cli();
  TCCR3A = 0;
  TCCR3A |= (1 << COM3A1) | (1 << COM3A0) | (1 << COM3B1); // OC3A : set on compare match, OC3B : clear on compare match
  TCCR3B = 1 << WGM13; // phase and frequency correct pwm mode, top = ICR3
  icr = (F_CPU/1000000*period/2);
  int d = 1;
  while ((icr>0xFFFF)&&(d<5)) {
     d++;
     icr = (F_CPU/1000000*period/2/diviseur[d]);
   } 
  clockBits = d;
  count_start_detect = delai_detect/period;
  uint16_t ocra = icr * (1.0-rapport);
  ICR3 = icr;
  OCR3A = ocra;
  OCR3B = ocra;
  TIMSK3 = 1 << TOIE3; // overflow interrupt enable
  
  count = 0;
  max_count = nombre+1;
  TCNT3 = 0; // mise à zéro du compteur
  // Timer 5 en mode Input Capture
  TCCR5A = 0;
  TCNT5 = 0;
  TCCR5B = (1 << ICES5);
  TIMSK5 = (1 << ICIE5);
  
  // déclenchement des deux Timers
  TCCR3B |= clockBits;
  TCCR5B |= capture_clock; // 2 MHz 
  sei();
  start_detect = false;
  delai = 0;
}

ISR(TIMER3_OVF_vect) { // Overflow interrupt
   count++;
    if (count==max_count) {
      TCCR3A = 0;
      PORTE &= ~((1 << PORTE3)|(1 << PORTE4));
      
   } 
   if (count>=count_start_detect) start_detect = true;
   
}

ISR(TIMER5_CAPT_vect) {
   
   if (start_detect) {
      delai = ICR5; // input capture register
      TCCR5B = 0; // arrêt du Timer  
   }
}   

void setup() {
  Serial.begin(115200);
  pinMode(ENABLE,OUTPUT);
  digitalWrite(ENABLE,HIGH);
  pinMode(DETECT,INPUT);
  DDRE |= 1 << PORTE3; // data direction register
  DDRE |= 1 << PORTE4;
  PORTE &= ~((1 << PORTE3)|(1 << PORTE4)); // IN1 = 0, IN2 = 0 état de repos
  
}

void loop() {
  delay(500);
  pulse_train(25,15,0.5,1500);
  delay(500);
  float duree = delai*6.25e-5*diviseur[capture_clock]; // en ms
  Serial.println(duree,4);
  
}               
                

Voici la sortie de la console pour la situation précédente :

4.5910
4.5910
4.5910
4.5910
4.5910
4.5905
4.5905
4.5905
4.5905
4.5905
4.5910
4.5905
4.5905
                

Le sol se trouve à 76cm des transducteurs. La distance entre chacun d'eux et le point du sol à égale distance est donc 76,4cm. Le temps de parcours du son est donc 4,44ms, plus court de 0,15ms que le temps mesuré. Cet écart vient, d'une part de l'écart déjà remarqué plus haut, d'autre part du délai qu'il faut pour que le signal reçu dépasse le seuil du comparateur. Il faudra donc procéder à un étallonnage précis pour effectuer la compensation de ce décalage. Si on en tient pas compte, l'erreur est d'environ de 2 pour cent.

6. Annexes

6.a. Augmentation du gain de réception

Pour détecter des échos provenant d'obstacles situés à plusieurs mètres, il peut être nécessaire d'augmenter le gain de l'amplificateur. On ajoute pour cela un amplificateur inverseur dont la tension de référence est 2,5V :

amplificateur-detecteur-2.svgFigure pleine page

Le potentiomètre permet d'ajuster le gain de cet amplificateur entre 1 et 5.

6.b. Montage en demi-pont

Lorsqu'on utilise le circuit RLC résonant, il peut être intéressant d'utiliser la moitité du pont en H (c.a.d.) deux transistors, pour alimenter ce circuit.

principe-emetteur-demipont.svgFigure pleine page

Dans ce cas, l'amplitude de la tension appliquée est deux fois moins grande qu'avec le pont complet. Une des bornes du transducteur émetteur est reliée à la masse. Voici la tension à ses bornes pour une excitation à 6 impulsions :

tensionPiezo-6cycles-demiponttensionPiezo-6cycles-demipont.pdf
print(u2.max())
--> 0.9055118

La tension maximale est de 90V, alors qu'elle est de 150V pour le pont complet avec le même nombre d'impulsions.

Creative Commons LicenseTextes et figures sont mis à disposition sous contrat Creative Commons.