Mathematik für Ingenieure mit Python

Mathematische Grundlagen
www.grund-wissen.de/mathematik
WinPython
Alles was man für Mathematik mit Python unter Windows braucht.
winpython.sourceforge.net
Download WinPython
Python-Code
Animation: zahnrad_animation.py
Abbildungen des Tutorials: zahnrad_zahn.py
Zu schwer?
Als Vorbereitung sollte zunächst die Drehung und Verschiebung eines Rechtecks mit komplexen Zahlen ausprobiert werden.

Animierte Zahnräder

animierte Zahnräder

3 Drehungen

Die Zahnrad-Animation wird mit Hilfe von 3 Drehungen erstellt.
  1. Die Eckpunkte eines Zahnradzahns werden in Position gedreht.
  2. Die einzelnen Zahnradzähne eines Zahnrads werden in Position gedreht.
  3. Die Zahnräder werden Bild für Bild der Animation in Position gedreht.

Imports

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import matplotlib.animation as animation

Ein Zahnrad konstruieren

Die Schnittstelle

Wir brauchen als Parameter den Zahnradradius, die Höhe der Zahnradzähne und die Anzahl der Zahnradzähne:
def createGear(r, teethHight, teethCount):

Der Winkelbereich eines Zahnradzahns

    d_phi = 2*np.pi/teethCount
Zahnradzahn Winkel

Die Eckpunkte eines Zahnradzahns

Ein Zahnradzah wird mit 4 Eckpunkten konstruiert. Jeder Eckpunkt wird zunächst in Polarkoordinaten definiert.
    tooth_points_angle = np.array([-1./3,-1./6,1./6,1./3])*d_phi
    tooth_point_r      = np.array([r-teethHight/2., r+teethHight/2., 
                                   r+teethHight/2., r-teethHight/2.])
Die Polarkoordinaten werden in komplexe Zahlen umgerechnet. D.h. die Eckpunkte eines Zahnradzahns werden in Position gedreht (Drehung Nr. 1).
    tooth =  tooth_point_r * np.exp(1j*tooth_points_angle)
Zahnradzahn Eckpunkte

Aus Zahnradzähnen wird ein Zahnrad

Das Zahnrad wird aus einzelnen gedrehten Zahnradzähnen aufgebaut. Jeder Zahnradzahn wird dabei ein Stück weiter gedreht (Drehung Nr.2).
    return np.outer(np.exp(1j*d_phi*np.arange(teethCount)), tooth).flatten()
Zahnradzähne

Die Funktion komplett

def createGear(r, teethHight, teethCount):
    d_phi = 2*np.pi/teethCount
    tooth_points_angle = np.array([-1./3,-1./6,1./6,1./3])*d_phi
    tooth_point_r      = np.array([r-teethHight/2., r+teethHight/2., 
                                   r+teethHight/2., r-teethHight/2.])
    tooth =  tooth_point_r * np.exp(1j*tooth_points_angle)
    return np.outer(np.exp(1j*d_phi*np.arange(teethCount)), tooth).flatten()

2 Zahnräder erzeugen

Wichtig dabei ist, das beide zahnräder das gleiche Verhältnis teethCount/r haben, sonst passen sie nicht zusammen.
g1 = createGear(r=8, teethCount=32, teethHight=1)
g2 = createGear(r=4, teethCount=16, teethHight=1)
Das zweite Zahnrad wird um einen halben Zahn weitergedreht und (später) um d=r1+r2 nach rechts verschoben.
g2 *= np.exp(1j*2*np.pi/32)
d2 = 12 #r1+r2

Die Zahnräder animieren

Schrittweite der Animation

Wir animieren die Zahnräder nur soweit, bis sich die Bewegung wiederholt bzw. bis es so aussieht, als ob sich die Bewegung wiederholt. Mechanisch wiederholt sich die Bewegung, wenn sich das große (d.h. das langsame) Zahnrad um 360° weitergedreht hat. Animationstechnisch wiederholt sich die Bewegung aber schon, wenn sich beide Zahnräder einen Zahn weiterdreht haben. Diese Bewegung teilen wir in 12 Bilder auf. In der Animationstechnik nennt man das Frames.
stepCount=12
Die Schrittweite einer Drehung des großen Zahnrads:
step = 2*np.pi/32/stepCount

Figure und Axes Objekt parametrieren

Die Animation soll im Web als animated gif gezeigt werden. Dieses gif-Bild besteht dann aus 12 Einzelbildern und ist damit auch 12 mal so groß wie ein Einzelbild. Es lohnt sich deshalb jedes Einzelbild nicht zu groß zu machen (3 Zoll x 2 Zoll)
fig = plt.figure(figsize=(3,2))
und überflüssiges wegzulassen (Achsen und weißer Rand):
ax = fig.add_axes([-.05, -.05, 1.1, 1.1])
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.set_aspect("equal")
plt.margins(0,0)
plt.xlim(-9.5,17.6)
plt.ylim(-9.5,9.5)

Die einzelnen Frames erstellen

Die 12 Frames der Animation werden erzeugt.
ims = []
for i in range(stepCount):
Zahnrad 1 wird um den Winkel step gegen den Uhrzeigersinn (mathematisch positiv) gedreht.
    g1 *= np.exp( 1j*step)
Zahnrad 2 dreht sich doppelt so schnell wie Zahnrad 1 in die entgegengesetzte richtung.
    g2 *= np.exp(-2j*step)
Für jeden Frame werden die beiden Zahnräder in ihrer aktuellen Position als gefüllte Polygone erzeugt. Die Matplotlib Klasse Polygon erwartet die n Punkte als n x 2 Float-Array. Mit g1.view(float).reshape(g1.size,2) wird deshalb auf das eindimensionale komplexe Array mit n Elementen eine n x 2 Float-Array-Ansicht erzeugt. Zahnrad 2 wird außerdem noch um d2 verschoben.
    ims.append([ax.add_patch(Polygon(g1.view(float).reshape(g1.size,2),
                                     color=(.1,0,1,.5))),
                ax.add_patch(Polygon(g2.view(float).reshape(g2.size,2)+[d2,0],
                                     color=(1,0,.1,.5)))])

Die Animation ablaufen lassen

Der folgende Befehl lässt die Animation in einem Fenster in einer Endlosschleife ablaufen (bis das Fenster geschlossen wird). Eine Zahnraddrehung soll 2/3 einer Sekunde dauern. Das Intervall in Millisekunden zwischen zwei Frames beträgt deshalb 1000. * 2/3 / stepCount
ani = animation.ArtistAnimation(fig, ims, interval=1000. * 2/3 / stepCount)
Will man die Animation (für die Webseite) abspeichern, dann so. Das funktioniert aber nur wenn die benötigten Codecs installiert sind, deshalb ist der Code erstmal auskommentiert. Unter Matplotlib 1.1 hat das auch noch den Seiteneffekt, dass die einzelnen Frames als png-tmp-Files rausgeschrieben werden. Ab Matplotlib 1.3 ist das aber leider nicht mehr der Fall.
#ani.save('zahnrad_animation.mp4')
Damit das Animationsfenster angezeigt wird braucht es in Matplotlib den show Befehl.
plt.show()

Animated gif erzeugen

Die mit Matplotlib 1.1 erzeugten png-tmp-Files können mit dem Linux Programm convert mit dem folgenden Befehl zu Animated-Gif konvertiert werden.
convert -delay 5.6 -loop 0 *.png zahnrad_animation.gif

Der Code komplett

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
import matplotlib.animation as animation

def createGear(r, teethCount, teethHight):
    d_phi = 2*np.pi/teethCount
    tooth_points_angle = np.array([-1./3,-1./6,1./6,1./3])*d_phi
    tooth_point_r      = np.array([r-teethHight/2., r+teethHight/2., 
                                   r+teethHight/2., r-teethHight/2.])
    tooth =  tooth_point_r * np.exp(1j*tooth_points_angle)
    return np.outer(np.exp(1j*d_phi*np.arange(teethCount)), tooth).flatten()

g1 = createGear(r=8, teethCount=32, teethHight=1)
g2 = createGear(r=4, teethCount=16, teethHight=1)

g2 *= np.exp(1j*2*np.pi/32)
d2 = 12 #r1+r2

stepCount=12
step = 2*np.pi/32/stepCount

fig = plt.figure(figsize=(3,2))
ax = fig.add_axes([-.05, -.05, 1.1, 1.1])
ax.set_xticklabels([])
ax.set_yticklabels([])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.set_aspect("equal")
plt.margins(0,0)
plt.xlim(-9.5,17.6)
plt.ylim(-9.5,9.5)

ims = []
for i in range(stepCount):
    g1 *= np.exp( 1j*step)
    g2 *= np.exp(-2j*step)
    ims.append([ax.add_patch(Polygon(g1.view(float).reshape(g1.size,2),
                                     color=(.1,0,1,.5))),
                ax.add_patch(Polygon(g2.view(float).reshape(g2.size,2)+[d2,0],
                                     color=(1,0,.1,.5)))])


ani = animation.ArtistAnimation(fig, ims, interval=1000. * 2/3 / stepCount)
#ani.save('zahnrad_animation.mp4')
plt.show()