Skip to content

Représentations des Systèmes Continus ​

Ce tutorial présente les différentes représentations d'un système continu sous scipy.signal et montre comment passer de l'une à l'autre. L'objectif est de comprendre que ces représentations décrivent le même système, mais sous des formes différentes.

Les trois représentations ​

Représentation BA (numérateur/dénominateur) ​

La représentation BA exprime la fonction de transfert sous forme polynomiale :

H(s)=b2s2+b1s+b0a2s2+a1s+a0

Sous scipy.signal, cette représentation est stockée dans deux tableaux avec les puissances décroissantes :

  • b = [b_2, b_1, b_0] (coefficients du numérateur)
  • a = [a_2, a_1, a_0] (coefficients du dénominateur)

Représentation ZPK (zéros, pôles, gain) ​

La représentation ZPK exprime la fonction de transfert sous forme factorisée :

H(s)=k(s−z1)(s−z2)(s−p1)(s−p2)

Sous scipy.signal, cette représentation est stockée dans :

  • z = [z_1, z_2] (zéros)
  • p = [p_1, p_2] (pôles)
  • k (gain)

Représentation SOS (sections d'ordre 2) ​

La représentation SOS exprime la fonction de transfert comme une cascade de cellules d'ordre 2. Pour un système d'ordre 2, il n'y a qu'une seule section.

Attention à la convention

Contrairement à la représentation BA, la représentation SOS utilise les puissances croissantes :

H(s)=b0+b1s+b2s2a0+a1s+a2s2

Chaque ligne de la matrice SOS contient : [b_0, b_1, b_2, a_0, a_1, a_2]

Cette incohérence est historique : la convention BA vient de l'écriture mathématique classique des polynômes (puissances décroissantes), tandis que la convention SOS vient du traitement du signal numérique où les filtres IIR s'expriment avec des retards z−1 (puissances croissantes).

Exemple de référence ​

Dans ce tutorial, nous utiliserons un filtre passe-bas d'ordre 2 avec ω0=1000 rad/s et m=0.5. La fonction de transfert est :

H(s)=ω02s2+2mω0s+ω02=106s2+1000s+106

Code d'initialisation ​

python
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt

# Paramètres du filtre
omega_0 = 1000
m = 0.5

# Coefficients BA (puissances décroissantes)
b = [omega_0**2]                         # [b_0]
a = [1, 2*m*omega_0, omega_0**2]         # [a_2, a_1, a_0]

print("Représentation BA:")
print("b =", b)
print("a =", a)

Conversion BA vers ZPK ​

Méthode manuelle ​

Pour convertir de BA vers ZPK, il faut :

  1. Trouver les racines du numérateur (zéros)
  2. Trouver les racines du dénominateur (pôles)
  3. Calculer le gain k=bN/aN (rapport des coefficients de plus haut degré)
python
# Conversion manuelle BA -> ZPK
z = np.roots(b)  # Zéros = racines du numérateur
p = np.roots(a)  # Pôles = racines du dénominateur
k = b[0] / a[0]  # Gain = rapport des coefficients dominants

print("Représentation ZPK:")
print("z =", z)
print("p =", p)
print("k =", k)

Résultat attendu ​

Représentation ZPK:
z = []
p = [-500.+866.02540378j -500.-866.02540378j]
k = 1000000.0

Fonction scipy équivalente ​

python
z, p, k = signal.tf2zpk(b, a)

Conversion ZPK vers BA ​

Méthode manuelle ​

Pour convertir de ZPK vers BA, il faut :

  1. Construire le polynôme du numérateur à partir des zéros
  2. Construire le polynôme du dénominateur à partir des pôles
  3. Multiplier le numérateur par le gain k
python
# Conversion manuelle ZPK -> BA
b_rebuilt = k * np.atleast_1d(np.poly(z))  # Numérateur = k × polynôme des zéros
a_rebuilt = np.poly(p)                      # Dénominateur = polynôme des pôles

# Conversion en réels (les coefficients sont réels pour un système physique)
b_rebuilt = np.real(b_rebuilt)
a_rebuilt = np.real(a_rebuilt)

print("Représentation BA reconstruite:")
print("b =", b_rebuilt)
print("a =", a_rebuilt)

Fonction scipy équivalente ​

python
b, a = signal.zpk2tf(z, p, k)

Conversion BA vers SOS ​

Méthode manuelle ​

Pour convertir de BA vers SOS, il faut inverser l'ordre des coefficients (passer des puissances décroissantes aux puissances croissantes) :

python
# Conversion manuelle BA -> SOS
# BA : [b_2, b_1, b_0] -> SOS : [b_0, b_1, b_2]
b_sos = list(reversed(b))
a_sos = list(reversed(a))

# Compléter à 3 coefficients si nécessaire
while len(b_sos) < 3:
    b_sos.append(0)
while len(a_sos) < 3:
    a_sos.append(0)

# Construction de la matrice SOS (une seule ligne pour un ordre 2)
sos_manual = np.array([b_sos + a_sos])

print("Représentation SOS:")
print(sos_manual)

Résultat attendu ​

Représentation SOS:
[[1.e+06 0.e+00 0.e+00 1.e+06 1.e+03 1.e+00]]

Fonction scipy équivalente ​

python
sos = signal.tf2sos(b, a)

Vérification des conversions ​

Pour vérifier que les différentes représentations décrivent bien le même système, nous pouvons comparer leurs réponses fréquentielles.

Code ​

python
# Calcul des réponses fréquentielles
w = np.logspace(1, 5, 500)

# Réponse BA
_, H_ba = signal.freqs(b, a, w)

# Réponse ZPK
sys_zpk = signal.ZerosPolesGain(z, p, k)
_, H_zpk = signal.freqresp(sys_zpk, w)

# Réponse SOS (en inversant l'ordre pour freqs)
b_sos_row = sos_manual[0, :3]
a_sos_row = sos_manual[0, 3:]
_, H_sos = signal.freqs(b_sos_row[::-1], a_sos_row[::-1], w)

# Tracé
fig, axes = plt.subplots(2, 1, figsize=(8, 6))

axes[0].semilogx(w, 20*np.log10(np.abs(H_ba)), label='BA')
axes[0].semilogx(w, 20*np.log10(np.abs(H_zpk)), '--', label='ZPK')
axes[0].semilogx(w, 20*np.log10(np.abs(H_sos)), ':', label='SOS')
axes[0].set_ylabel('Module (dB)')
axes[0].legend()
axes[0].grid(True)

axes[1].semilogx(w, np.angle(H_ba, deg=True), label='BA')
axes[1].semilogx(w, np.angle(H_zpk, deg=True), '--', label='ZPK')
axes[1].semilogx(w, np.angle(H_sos, deg=True), ':', label='SOS')
axes[1].set_xlabel('Pulsation (rad/s)')
axes[1].set_ylabel('Phase (°)')
axes[1].legend()
axes[1].grid(True)

plt.tight_layout()
plt.savefig("06_fig1.png", dpi=150)
plt.show()

Résultat ​

Les trois courbes doivent être parfaitement superposées, confirmant que les représentations BA, ZPK et SOS décrivent bien le même système.

Récapitulatif ​

Conventions scipy ​

ReprésentationConventionExemple ordre 2
BAPuissances décroissantesb = [b_2, b_1, b_0]
SOSPuissances croissantessos = [b_0, b_1, b_2, a_0, a_1, a_2]

Fonctions de conversion scipy ​

ConversionFonction scipy
BA → ZPKsignal.tf2zpk(b, a)
ZPK → BAsignal.zpk2tf(z, p, k)
BA → SOSsignal.tf2sos(b, a)
SOS → BAsignal.sos2tf(sos)
ZPK → SOSsignal.zpk2sos(z, p, k)
SOS → ZPKsignal.sos2zpk(sos)