🖥️ Oscillateur de Wien ★★

Notebook Capytale de cet exercice : 2253-2586522

Pour rappel, un oscillateur de Wien est constitué d’un montage amplificateur non-inverseur et d’un filtre de Wien.

On note

Les fonctions de transfert du filtre de Wien et de l’amplificateur non-inverseur sont respectivement données par :

𝐻ANI(𝑝)=𝑉(𝑝)𝑈(𝑝)=1+𝑅2𝑅11+(1+𝑅2𝑅1)𝜏𝐴0𝑝𝐻Wien(𝑝)=𝑈(𝑝)𝑉(𝑝)=131+13(1𝑅𝐶𝑝+𝑅𝐶𝑝)

On pose 𝑤=d𝑢d𝑡. On cherche à mettre le problème sous la forme d’un problème d’Euler de la forme d𝑌⃗d𝑡=𝐹(𝑡,𝑌⃗) avec 𝑌⃗=(𝑢𝑣𝑤).

1/ Exprimer les dérivées de 𝑢, 𝑣 et 𝑤 uniquement en fonction de 𝑢, 𝑣, 𝑤, 𝑅, 𝐶, 𝑅1, 𝑅2, 𝐴0 et 𝜏.

Coup de pouce 1
Transformer les deux fonctions de transfert en équations différentielles dans le domaine temporel.
Coup de pouce 2
Multiplier par 𝑝 et simplifier les fractions de sorte à ne plus avoir 𝑝 au dénominateur.
Coup de pouce 3
À partir de 𝐻ANI, on peut obtenir d𝑣d𝑡. À partir de 𝐻Wien, on peut obtenir d𝑤d𝑡.
Corrigé
d𝑢d𝑡=𝑤d𝑣d𝑡=𝐴0𝜏𝑢−1(1+𝑅2𝑅1)𝜏𝐴0𝑣d𝑤d𝑡=(−1𝑅2𝐶2+1𝑅𝐶𝐴0𝜏)𝑢−1𝑅𝐶1(1+𝑅2𝑅1)𝜏𝐴0𝑣−3𝑅𝐶𝑤

Avec Python, on représente le vecteur 𝑌⃗ par un array numpy à 3 éléments.

2/ Écrire une fonction Python qui prend en entrée Y et renvoie sa dérivée. On pourra supposer les variables 𝑅, 𝐶, 𝑅1, 𝑅2, 𝐴0 et 𝜏 déjà définies.

import numpy as np
def F(t, Y):
  """
  t : le temps (inutile ici, mais solve_ivp le fournit)
  Y : array numpy à 3 éléments contenant u, v et w
  Renvoie un array numpy contenant la dérivée de Y par rapport au temps.
  """
  ...
Coup de pouce 1
On peut décomposer les éléments de Y par Y[0], Y[1] et Y[2].
Coup de pouce 2
Utiliser les résultats de la question précédente pour écrire la fonction F.
Corrigé
import numpy as np
def F(t, Y):
  u,v,w = Y
  return np.array([
        w,
        A0/tau * u - 1/((1+R2/R1)*tau/A0) * v,
        (-1/(R*C)**2+1/(R*C)*A0/tau) * u - 1/(R*C)/((1+R2/R1)*tau/A0) * v - 3/(R*C) * w
    ])

Dans un premier temps, on utilise la fonction scipy.integrate.solve_ivp qui résout numériquement des équations différentielles mises sous la forme d’un problème d’Euler. Cette fonction utilise des variantes de la méthode d’Euler la rendant plus précise.

La fonction solve_ivp prend en argument

La fonction solve_ivp renvoie un objet. Si on le stocke dans la variable solution,

3/ Définir et affecter les variables 𝑅=1 kΩ, 𝐶=10 nF, 𝑅1, 𝑅2, 𝐴0=100 000 et 𝜏=0,01 s avec des valeurs vraisemblables satisfaisant la condition de démarrage des oscillations.

R = ... # Ohm
C = ... # F
R1 = ... # Ohm
R2 = ... # Ohm
A0 = ...
tau = ... # s
Coup de pouce 1
Dans quelle plage de valeurs sont les résistances utilisées en TP ?
Coup de pouce 2
Quelle est la condition de démarrage des oscillations vue en cours ?
Corrigé

La condition de démarrage vue en cours est 1+𝑅2𝑅1>3, soit 𝑅2>2𝑅1. Il faut la prendre avec une marge : la bande passante finie de l’ALI atténue légèrement le gain à 𝜔0, ce qui relève le seuil (ici à 𝑅2≈2002 Ω).

R = 1000 # Ohm
C = 10E-9 # F
R1 = 1000 # Ohm
R2 = 2100 # Ohm, soit un gain de 3,1 : au-dessus du seuil avec de la marge
A0 = 1E5
tau = 0.01 # s
Vsat = 15 # V, tension de saturation de l'ALI

4/ Définir Y0 avec de très petites valeurs pour 𝑢, 𝑣 et 𝑤 (0,0001 par exemple).

Y0 = ...
Corrigé
Y0 = np.array([0.0001, 0.0001, 0.0001])

5/ Définir tf pour observer une dizaine d’oscillations.

tf = ... # s
Coup de pouce 1
Quelle relation relie la période des oscillations à 𝑅 et 𝐶 lorsque la condition d’existence d’oscillations sinusoïdales est satisfaite ?
Corrigé

𝑇=2𝜋𝜔=2𝜋𝑅𝐶

tf = 10 * 2 * np.pi * R * C

6/ Tracer 𝑢 et 𝑣 en fonction du temps.

import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp

solution = solve_ivp(..., ..., ...)
plt.plot(..., ..., label='u(t)')
plt.plot(..., ..., label='v(t)')
plt.xlabel('Temps (s)')
plt.ylabel('Tension (V)')
plt.legend()
plt.show()
Coup de pouce 1
solution.y[0] correspond à 𝑢 et solution.y[1] à 𝑣.
Corrigé
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp

solution = solve_ivp(F, (0,tf), Y0)
plt.plot(solution.t, solution.y[0], label='u(t)')
plt.plot(solution.t, solution.y[1], label='v(t)')
plt.xlabel('Temps (s)')
plt.ylabel('Tension (V)')
plt.legend()
plt.show()

7/ Vérifier la condition de démarrage des oscillations.

Coup de pouce 1
Changer les valeurs de 𝑅1 et 𝑅2 et lancer la simulation. Vérifier que des oscillations apparaissent seulement si la condition de démarrage des oscillations vue en cours est vérifiée.
Corrigé

Le filtre de Wien atténue d’un facteur 3 à 𝜔0=1/(𝑅𝐶) : les oscillations démarrent si l’amplificateur compense au moins cette atténuation, soit 1+𝑅2/𝑅1>3, c’est-à-dire 𝑅2>2𝑅1=2000 Ω.

En relançant la simulation à 𝑅1 fixé :

  • 𝑅2=1900 Ω (gain 2{,}9) : l’amplitude décroit, l’oscillateur ne démarre pas ;
  • 𝑅2=2100 Ω (gain 3{,}1) : l’amplitude croit exponentiellement.

Le seuil observé est en fait très légèrement supérieur à 2000 Ω : à 𝜔0, la bande passante finie de l’ALI abaisse le gain de 1+𝑅2/𝑅1 à (1+𝑅2/𝑅1)/1+(𝜔0(1+𝑅2/𝑅1)𝜏/𝐴0)2. Avec les valeurs choisies, le gain de 3{,}000 obtenu à 𝑅2=2000 Ω ne suffit pas tout à fait et l’amplitude décroit encore ; il faut 𝑅2≳2002 Ω.

8/ Vérifier la valeur de la période des oscillations.

Coup de pouce 1
Il faut que la condition d’oscillation soit satisfaite.
Coup de pouce 2
Mesurer la période des oscillations sur le graphe. Est-ce compatible avec la pulsation vue en cours ?
Corrigé

Le filtre de Wien n’a un déphasage nul qu’à 𝜔0=1/(𝑅𝐶) : c’est la seule pulsation à laquelle la condition de bouclage peut être satisfaite. La période attendue vaut donc

𝑇=2𝜋𝜔0=2𝜋𝑅𝐶=2𝜋×1000×10⋅10−9=63 µs

soit une fréquence de 16 kHz. On la retrouve sur le graphe en mesurant l’écart entre deux maximums successifs.

Dans la suite, on souhaite se passer de la fonction solve_ivp et implémenter nous-même la méthode d’Euler.

On note 𝑌𝑖=𝑌(𝑖⋅Δ𝑡) où Δ𝑡 est la durée entre deux échantillons (période d’échantillonnage).

9/ Dans le cas général, exprimer 𝑌𝑖+1 en fonction de 𝑌𝑖, de 𝑖, Δ𝑡 et de la fonction 𝐹.

Coup de pouce 1
Utiliser la définition de 𝑌𝑖+1 puis la relation de Taylor à l’ordre 1.
Corrigé
𝑌𝑖+1=𝑌𝑖+Δ𝑡⋅𝐹(𝑖⋅Δ𝑡,𝑌𝑖)

10/ Implémenter la méthode d’Euler pour simuler l’évolution des tensions pour un oscillateur de Wien. Δ𝑡 sera choisi de sorte qu’il y ait environ 200 échantillons par période.

Delta_t = ... # s, environ 200 échantillons par période
N = int(tf / Delta_t) # Nombre d'échantillons
t = np.zeros(N)
Y = np.zeros((N,3))
t[0] = 0
Y[0] = Y0
for i in range(1,N):
    Y[i] = ...
    t[i] = ...
Corrigé

Le schéma d’Euler explicite n’est stable que si Δ𝑡 est petit devant la plus courte constante de temps du système. Ici la plus rapide n’est pas la période d’oscillation mais le pôle de l’ALI, 𝐴0/((1+𝑅2/𝑅1)𝜏) : à 50 échantillons par période la simulation diverge. Il en faut environ 120 au minimum, d’où le choix de 200.

Delta_t = (2 * np.pi * R * C) / 200
N = int(tf / Delta_t)
t = np.zeros(N)
Y = np.zeros((N,3))
t[0] = 0
Y[0] = Y0
for i in range(1,N):
    Y[i] = Y[i-1] + Delta_t * F(t[i-1], Y[i-1])
    t[i] = t[i-1] + Delta_t

11/ Adapter le code précédent pour prendre en compte la saturation de l’ALI.

Vsat = 15 # V, tension de saturation de l'ALI
Corrigé

Il ne suffit pas d’écrêter 𝑣 après chaque pas : l’expression de d𝑤d𝑡 a été obtenue en y substituant d𝑣d𝑡 du régime linéaire, et cette substitution n’est plus valable dès que l’ALI sature. Il faut calculer d𝑣d𝑡 d’abord — nul quand la sortie est bloquée — puis l’injecter dans d𝑤d𝑡.

Il faut aussi simuler plus longtemps : partant de 1⋅10−4 V, il faut une quarantaine de périodes pour atteindre la saturation.

def F_sat(t, Y):
  u,v,w = Y
  dv = A0/tau * u - 1/((1+R2/R1)*tau/A0) * v
  if (v >= Vsat and dv > 0) or (v <= -Vsat and dv < 0):
      dv = 0 # la sortie de l'ALI est bloquée, elle ne varie plus
  return np.array([w, dv, -u/(R*C)**2 - 3/(R*C) * w + dv/(R*C)])

tf = 60 * 2 * np.pi * R * C
Delta_t = (2 * np.pi * R * C) / 200
N = int(tf / Delta_t)
t = np.zeros(N)
Y = np.zeros((N,3))
t[0] = 0
Y[0] = Y0
for i in range(1,N):
    Y[i] = Y[i-1] + Delta_t * F_sat(t[i-1], Y[i-1])
    Y[i][1] = min(max(Y[i][1], -Vsat), Vsat)
    t[i] = t[i-1] + Delta_t

En régime établi, 𝑣 est un signal carré à ±𝑉sat et 𝑢, filtré par le pont de Wien, reste quasi sinusoïdal d’amplitude ≈5,5 V.