Create pt2.py

This commit is contained in:
infiniteCable
2025-04-11 19:30:56 +02:00
committed by GitHub
parent c4a23447da
commit 1cc80e1a7a

112
common/pt2.py Normal file
View File

@@ -0,0 +1,112 @@
import math
class PT2Filter:
"""
Diskreter PT2-Filter mittels Tustin (bilinear transform)
Übertragungsfunktion (kontinuierlich):
H(s) = (w0^2) / (s^2 + 2*zeta*w0*s + w0^2)
Abtastzeit dt.
"""
def __init__(self, w0: float, zeta: float, dt: float):
"""
w0: Eigenkreisfrequenz [rad/s], bestimmt u.a. die Anstiegszeit
zeta: Dämpfungsgrad (zeta=1 => kritisch gedämpft)
dt: Abtastzeit [s]
"""
self.w0 = w0
self.zeta = zeta
self.dt = dt
self.a1, self.a2, self.b0, self.b1, self.b2 = self._design_pt2(self.w0, self.zeta, self.dt)
self.y1 = 0.0
self.y2 = 0.0
self.u1 = 0.0
self.u2 = 0.0
def _design_pt2(self, w0, zeta, dt):
"""
Erzeuge (a1, a2, b0, b1, b2) aus:
H(s) = w0^2 / [s^2 + 2*zeta*w0 s + w0^2]
via Tustin (s = (2/dt)*(1-z^-1)/(1+z^-1)).
Wir bringen G(z) in die Normalform:
Y(z)/U(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 + a1 z^-1 + a2 z^-2).
=> Zeitbereich: y[k] = -a1*y[k-1] - a2*y[k-2] + b0*u[k] + b1*u[k-1] + b2*u[k-2].
"""
Ts = dt
wd = w0
alpha = 2.0 / Ts
b2_ = w0**2
b1_ = 2.0 * w0**2
b0_ = w0**2
A2_f1 = alpha**2
A1_f1 = -2.0 * alpha**2
A0_f1 = alpha**2
factor2 = 2.0*zeta*wd*alpha
A2_f2 = factor2
A1_f2 = 0.0
A0_f2 = -factor2
A2_f3 = wd**2
A1_f3 = 2.0*(wd**2)
A0_f3 = wd**2
A2 = A2_f1 + A2_f2 + A2_f3
A1 = A1_f1 + A1_f2 + A1_f3
A0 = A0_f1 + A0_f2 + A0_f3
B2 = b2_
B1 = b1_
B0 = b0_
b2d = B2 / A2
b1d = B1 / A2
b0d = B0 / A2
a2d = A0 / A2
a1d = A1 / A2
return (a1d, a2d, b0d, b1d, b2d)
def sync(self, target: float):
steps = compute_saturation_steps(self.w0, self.zeta, self.dt)
for i in range(1, steps + 1):
update(target)
def compute_saturation_steps(self, w0: float, zeta: float, dt: float) -> int:
"""
Berechnet eine Abschätzung der Schritte, bis der Filter (95% des Endwerts) erreicht ist.
Wir nutzen hier die Abschätzung:
T_s = 4 / (zeta * w0)
und setzen N = T_s / dt.
"""
Ts = 4.0 / (zeta * w0)
N = Ts / dt
return math.ceil(N)
def reset(self):
"""
Setzt interne Zustände zurück
"""
self.y1 = 0.0
self.y2 = 0.0
self.u1 = 0.0
self.u2 = 0.0
def update(self, u: float) -> float:
"""
Ein Schritt des Filters mit aktuellem Eingang u.
Gibt Ausgang y zurück.
"""
y = (
- self.a1 * self.y1
- self.a2 * self.y2
+ self.b0 * u
+ self.b1 * self.u1
+ self.b2 * self.u2
)
self.y2 = self.y1
self.y1 = y
self.u2 = self.u1
self.u1 = u
return y