Gedämpfter Feder-Oszillator analytische & numerische Lösungen in Python
Dieser Beitrag demonstriert zwei Ansätze zur Simulation eines gedämpften Feder-Oszillators: analytische und numerische Lösungen.
Simulationsmethoden
Analytische Lösung
Der gedämpfte harmonische Oszillator folgt der Differentialgleichung:
$$\frac{d^2x}{dt^2} + 2\zeta\omega_0\frac{dx}{dt} + \omega_0^2x = 0$$Für ein untergedämpftes System ($\zeta < 1$) lautet die analytische Lösung:
$$x(t) = e^{-\zeta\omega_0 t}\left[x_0\cos(\omega_d t) + \frac{v_0 + \zeta\omega_0 x_0}{\omega_d}\sin(\omega_d t)\right]$$Wobei:
- $\zeta$ das Dämpfungsverhältnis ist
- $\omega_0$ die natürliche Kreisfrequenz $(2\pi f_0)$ ist
- $\omega_d = \omega_0\sqrt{1-\zeta^2}$ die gedämpfte Frequenz ist
- $x_0$ und $v_0$ die anfängliche Position und Geschwindigkeit sind
Numerische Lösung
Der numerische Ansatz verwendet das Runge-Kutta-Verfahren 4. Ordnung, um das System von Differentialgleichungen erster Ordnung zu integrieren:
$$\frac{dx}{dt} = v$$$$\frac{dv}{dt} = -\omega_0^2x - 2\zeta\omega_0v$$Diese Methode bietet hohe Genauigkeit und ermöglicht den Vergleich mit der analytischen Lösung, um die Korrektheit zu überprüfen.
Quellcode
damped_spring_oscillator.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# SPDX-License-Identifier: CC0 1.0 Universal
import numpy as np
import matplotlib.pyplot as plt
plt.style.use("ggplot")
# Simulationsparameter
fs = 1000 # Abtastrate (Hz)
t_duration = 5 # Dauer (Sekunden)
f_resonant = 4 # Resonanzfrequenz (Hz)
x0 = 1.0 # Anfangsauslenkung
v0 = 0.0 # Anfangsgeschwindigkeit
# Abgeleitete Parameter
omega0 = 2 * np.pi * f_resonant # Natürliche Kreisfrequenz
dt = 1.0 / fs # Zeitschritt
t = np.arange(0, t_duration, dt) # Zeit-Array
n_samples = len(t)
# Dämpfungskoeffizient (du kannst dies anpassen, um die Dämpfung zu steuern)
# zeta < 1: untergedämpft, zeta = 1: kritisch gedämpft, zeta > 1: übergedämpft
zeta = 0.03 # Leichte Dämpfung für oszillatorische Bewegung
gamma = 2 * zeta * omega0 # Dämpfungskoeffizient
# Analytische Lösung für untergedämpften Oszillator
omega_d = omega0 * np.sqrt(1 - zeta**2) # Gedämpfte Frequenz
# Für Anfangsbedingungen x(0) = x0, v(0) = v0
# Lösung: x(t) = e^(-ζω₀t)[x0*cos(ωd*t) + ((v0 + ζω₀x0)/ωd)*sin(ωd*t)]
x_analytical = np.exp(-zeta * omega0 * t) * (
x0 * np.cos(omega_d * t) +
((v0 + zeta * omega0 * x0) / omega_d) * np.sin(omega_d * t)
)
print(f"Natural frequency: {f_resonant:.2f} Hz")
print(f"Damped frequency: {omega_d / (2 * np.pi):.2f} Hz")
print(f"Damping ratio: {zeta:.2f}")
print(f"Damping coefficient gamma: {gamma:.3f}")
print(f"Natural angular frequency ω₀: {omega0:.3f} rad/s")
# Numerische Simulation mit Runge-Kutta-Verfahren
def spring_ode(state, t):
"""
Differentialgleichung für gedämpften harmonischen Oszillator
d²x/dt² + 2ζω₀(dx/dt) + ω₀²x = 0
state = [Position, Geschwindigkeit]
gibt [Geschwindigkeit, Beschleunigung] zurück
"""
x, v = state
# Beschleunigung = -ω₀²x - 2ζω₀v
acceleration = -omega0**2 * x - 2 * zeta * omega0 * v
return np.array([v, acceleration])
# Arrays für numerische Lösung initialisieren
x_numerical = np.zeros(n_samples)
v_numerical = np.zeros(n_samples)
# Anfangsbedingungen
x_numerical[0] = x0
v_numerical[0] = v0
# Runge-Kutta-Integration 4. Ordnung
for i in range(n_samples - 1):
state = np.array([x_numerical[i], v_numerical[i]])
k1 = dt * spring_ode(state, t[i])
k2 = dt * spring_ode(state + k1/2, t[i] + dt/2)
k3 = dt * spring_ode(state + k2/2, t[i] + dt/2)
k4 = dt * spring_ode(state + k3, t[i] + dt)
next_state = state + (k1 + 2*k2 + 2*k3 + k4) / 6
x_numerical[i+1] = next_state[0]
v_numerical[i+1] = next_state[1]
# Ergebnisse darstellen
plt.figure(figsize=(12, 8))
# Positionsdiagramm
plt.subplot(2, 1, 1)
plt.plot(t, x_analytical, 'b-', label='Analytical Solution', linewidth=2)
plt.plot(t[::10], x_numerical[::10], 'ro', markersize=2, label='Numerical Solution', alpha=0.7)
plt.xlabel('Time (s)')
plt.ylabel('Position (m)')
plt.title(f'Dampened Spring Oscillator (f₀={f_resonant}Hz, ζ={zeta}, fs={fs}Hz)')
plt.legend()
plt.grid(True, alpha=0.3)
# Analytische Geschwindigkeit (Ableitung der Position)
v_analytical = np.exp(-zeta * omega0 * t) * (
(-zeta * omega0 * x0 + (v0 + zeta * omega0 * x0) * omega_d / omega_d) * np.cos(omega_d * t) +
(-omega_d * x0 - zeta * omega0 * (v0 + zeta * omega0 * x0) / omega_d) * np.sin(omega_d * t)
)
# Geschwindigkeitsdiagramm
plt.subplot(2, 1, 2)
plt.plot(t, v_analytical, 'g-', label='Analytical Velocity', linewidth=2)
plt.plot(t[::10], v_numerical[::10], 'mo', markersize=2, label='Numerical Velocity', alpha=0.7)
plt.xlabel('Time (s)')
plt.ylabel('Velocity (m/s)')
plt.title('Velocity of the Spring Mass')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Fehler zwischen analytischer und numerischer Lösung berechnen
error = np.abs(x_analytical - x_numerical)
max_error = np.max(error)
rms_error = np.sqrt(np.mean(error**2))
print(f"\nNumerical accuracy:")
print(f"Maximum error: {max_error:.6f} m")
print(f"RMS error: {rms_error:.6f} m")
print(f"Relative RMS error: {rms_error/np.max(np.abs(x_analytical))*100:.4f}%")
plt.savefig("dampened_spring_oscillator.svg", dpi=300, bbox_inches='tight')Wenn dieser Beitrag dir geholfen hat, erwäge, mir einen Kaffee zu spendieren oder per PayPal zu spenden, um die Recherche und Veröffentlichung neuer Beiträge auf TechOverflow zu unterstützen