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 :
On pose . On cherche à mettre le problème sous la forme d’un problème d’Euler de la forme avec .
1/ Exprimer les dérivées de , et uniquement en fonction de , , , , , , , et .
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 , , , , 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.
"""
...Y par Y[0], Y[1] et Y[2].F.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
F définie précédemmentLa fonction solve_ivp renvoie un objet. Si on le stocke dans la variable solution,
solution.t contient les tempssolution.y contient les valeurs successives de à ces temps3/ Définir et affecter les variables , , , , et avec des valeurs vraisemblables satisfaisant la condition de démarrage des oscillations.
R = ... # Ohm
C = ... # F
R1 = ... # Ohm
R2 = ... # Ohm
A0 = ...
tau = ... # sLa condition de démarrage vue en cours est , soit . Il faut la prendre avec une marge : la bande passante finie de l’ALI atténue légèrement le gain à , ce qui relève le seuil (ici à ).
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'ALI4/ Définir Y0 avec de très petites valeurs pour , et ( par exemple).
Y0 = ...Y0 = np.array([0.0001, 0.0001, 0.0001])5/ Définir tf pour observer une dizaine d’oscillations.
tf = ... # stf = 10 * 2 * np.pi * R * C6/ 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()solution.y[0] correspond à et solution.y[1] à .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.
Le filtre de Wien atténue d’un facteur à : les oscillations démarrent si l’amplificateur compense au moins cette atténuation, soit , c’est-à-dire .
En relançant la simulation à fixé :
Le seuil observé est en fait très légèrement supérieur à : à , la bande passante finie de l’ALI abaisse le gain de à . Avec les valeurs choisies, le gain de obtenu à ne suffit pas tout à fait et l’amplitude décroit encore ; il faut .
8/ Vérifier la valeur de la période des oscillations.
Le filtre de Wien n’a un déphasage nul qu’à : c’est la seule pulsation à laquelle la condition de bouclage peut être satisfaite. La période attendue vaut donc
soit une fréquence de . 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 en fonction de , de , et de la fonction .
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 é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] = ...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, : à échantillons par période la simulation diverge. Il en faut environ au minimum, d’où le choix de .
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_t11/ Adapter le code précédent pour prendre en compte la saturation de l’ALI.
Vsat = 15 # V, tension de saturation de l'ALIIl ne suffit pas d’écrêter après chaque pas : l’expression de a été obtenue en y substituant du régime linéaire, et cette substitution n’est plus valable dès que l’ALI sature. Il faut calculer d’abord — nul quand la sortie est bloquée — puis l’injecter dans .
Il faut aussi simuler plus longtemps : partant de , 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_tEn régime établi, est un signal carré à et , filtré par le pont de Wien, reste quasi sinusoïdal d’amplitude .