Über Ycebear

Mein Bild
This is my blog for QUANTITATIVE METHODS in physics, medicine, finance and economy: I mainly use the Python-universe. -***- ***- Dies ist mein Blog für QUANTITATIVE METHODEN in Physik, Medizin, Finance und Oekonomie. Dazu verwende ich meistens Python mit den vielen Zusatzpakten.

Sonntag, 31. Juli 2016

Rowing: Ruderbewegung in Python

psc_Ruder_B 2016-08-12-Copy1

Darstellung der Bewegung eines Ruders

Grafische Darstellung der Ruder

Die folgenden Funktionen werden zur grafischen Darstellung des Ruders benötigt. Die wichtigste davon ist plotRuder, welche die Punkte miteinander verbindet, die das Ruder definieren. Die Funktion setGrafikrahmen sieht etwas sperrig aus. Sie dient nur dazu, dass das Programm den Grafikrahmen etwas weiter aussen setzt als gerade auf den äussersten Punkten, die dargestellt werden sollen. initGrafik hat die Aufgabe ein Grafikfenster zu öffnen.

In [101]:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

def initGrafik():
    fig,ax = plt.subplots()
    ax.axis('equal')
    return ax

def setGrafikrahmen(x,y):
    # das folgende soll den Grafikrahmen etwas grösser machen als das Grafikobjekt
    xmin = min(x); xmax = max(x); Lx = xmax - xmin; Mx = 0.5*(xmax + xmin)
    ymin = min(y); ymax = max(y); Ly = ymax - ymin; My = 0.5*(ymax + ymin)
    Ls = 1.2
    xmin = Mx - 0.5*Ls*Lx; xmax = Mx + 0.5*Ls*Lx;
    ymin = My - 0.5*Ls*Ly; ymax = My + 0.5*Ls*Ly;
    return [xmin, xmax, ymin, ymax]

def plotRuder(x,y,colr,ax):
    markers_on = [0, 1, 2]
    ax.plot(x, y, '-o', color=colr, markevery=markers_on,linewidth=2.5)
    #ax.axis(setGrafikrahmen(x,y))
    plt.xlabel('x-Richtung [m]')
    plt.ylabel('y-Richtung [m]')
    return

font = {'family': 'serif',
        'color':  'darkred',
        'weight': 'normal',
        'size': 16,
        }

def getCl():  # Farbreihe
    cl =  ['g', 'c', 'b' , 'm', 'r', 'y', 'grey','k',
           'g', 'c', 'b' , 'm', 'r', 'y', 'grey','k']
    return cl

def getC2():  # Farbreihe
    c2 =  ['g', 'c', 'b' , 'm', 'r', 'y', 'grey','k',
           'g', 'c', 'b' , 'm', 'r', 'y', 'grey','k']
    return c2

Mathematische Darstellung der Ruder

Wir stellen die Ruder als Vektoren dar. Ebenso ihr Aufhänge- und Drehpunkt, der deutsch Dolle genannt wird und englisch Pin. Als x-Richtung wählen wir die Fahrtrichtung des Bootes. Die y-Richtung wählen wir in Fahrrichtung links, rechtwinklig dazu. Den Teil des Ruders von der Dolle zum Ruderblatt bezeichenen wir in diesem Programmcode als äusseren Hebel Ha und den Teil von der Dolle zum Rudergriff als inneren Hebel Hi.

An einem Boot können sowohl die Position der Dolle als auch die Länge des äusseren und des inneren Hebels variiert werden. Sie sind deshalb in diesem Code Input-Grössen.

Die Dolle definieren wir mit dem Ortsvektor $\vec{P_{pin}}$. Für den äusseren Hebel $\vec{r_{a}}$ verwenden wir einen Richtungsvektor der Länge 1, den wir mit der Hebellänge Ha multplizieren. Der innere Hebel $\vec{r_{a}}$ verläuft in die entgegengesetzte Richtung mit der Länge Hi. Wenn wir diese beiden Vektoren zum Ortsvektor der Dolle addieren erhalten wir die Ortsvektoren für das Ruderblatt $\vec{R_{a}}$ und den Griff $\vec{R_{i}}$.

Wir beginnen in der Funktion setRuder die mathematische Darstellung in der Position, in welcher das Ruder rechtwinklig zum Boot genau in der y-Richtung verläuft.

$$ \vec{e_{y}}= \begin{bmatrix} 0 \\ 1 \end{bmatrix}$$$$ \vec{r_{a}}= H_a \cdot \vec{e_{y}} $$$$ \vec{r_{i}}= -H_i \cdot \vec{e_{y}} $$$$\vec{R_{a}} = \vec{P_{pin}} + \vec{r_{a}} $$$$\vec{R_{i}} = \vec{P_{pin}} + \vec{r_{i}} $$

Für ausschliesslich grafische Zwecke generieren wir in der Funktion setOar ein Ruderblatt in Form einer Ellipse. Damit erhält man die nachfolgende Grafik des Ruders.

In [102]:
def setOar(): # das Ruderblatt wird als halbe Ellipse in y-Richtung definiert
    ox = 0.2; oy = 0.4                                     # Längen in x- und in y-Richtung
    φ = np.linspace(-0.5*np.pi, 0.5*np.pi, 10)             # φ durchläuft Halbkreis
    oar = np.array([ox * np.cos(φ), oy + oy * np.sin(φ),]) # Ellipse
    return oar

def setRuder():
    Ha = 3.0                                # äussere Hebellänge
    Hi = 1.0                                # innere Hebellänge
    raV   =     Ha * np.array([0, 1])       # Vektor Ruder aussen = Ha*(0,1)
    riV   = -Hi/Ha * raV                    # Vektor Ruder innen = -ha*(0,1)

    PpinV = np.array([0, 2])                # Ortsvektor der Dolle (engl.: Pin)

    RaV = PpinV + raV                       # OrtsVektor Ruder aussen
    RiV = PpinV + riV                       # OrtsVektor Ruder innen

    oar = setOar()                          # ein Ruderblatt generieren
    oar[0] = oar[0] + RaV[0]                # Ruderblatt an RaV Ruderende verschieben
    oar[1] = oar[1] + RaV[1]

    Ruder = np.vstack((RiV,PpinV,RaV)).T       # alle Punkte des Ruders zusammenfügen
    Ruder = np.concatenate((Ruder,oar),axis=1) # Ruderblatt anfügen
    return Ruder,PpinV



# ----- main program ---------------------------------------------------
R,Ppin = setRuder()
Rx = R[0]                           # die x-Komponenten des Ruders
Ry = R[1]                           # die y-Komponenten des Ruders

ax = initGrafik()
plotRuder(Rx,Ry,"green",ax)
ax.axis([-1, 1, 0.5, 6.2])
plt.text(-0.45, 4.0, r'$\vec{r_{a}}$',  fontdict=font)
plt.text( 0.20, 1.8, r'$\vec{P_{Pin}}$',fontdict=font)
plt.text(-0.45, 1.3, r'$\vec{r_{i}}$',  fontdict=font)
Out[102]:
<matplotlib.text.Text at 0x201c351fdd8>

Drehung der Ruder

Für die Drehung der Ruder kann die Drehmatrix verwendet werden: $$\vec{c} = \mathbf{D}(\theta) \cdot \vec{b} $$

$$\begin{bmatrix} c_x \\ c_y \end{bmatrix} = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix} \begin{bmatrix} b_x \\ b_y \end{bmatrix}$$

Um eine Drehung mit der Matrix durchzuführen, muss sich der Drehpunkt im Nullpunkt befinden. Ist die Dolle nicht im Nullpunkt, so können temporär die Dolle und die zu drehenden Vektoren durch eine Verschiebung um den Ortsvektor -$\vec{P_{pin}}$ in diese Position gebracht werden.

Mit der Funktion D im Code kann die Drehung vollzogen werden. Programmtechnisch enthält das Ruder mehrere Vektoren, die in einer Matrix zusammengfasst sind, nämlich für den Griff, für die Dolle, für das äussere Ruderende sowie mehrere Punkt für das Ruderblatt. Die Funktion V ist dazu da, eine Vektorverschiebung durchzuführen.

In [105]:
def D(b,φ):    # Drehmatrix
    c = np.array([np.cos(φ)*b[0] - np.sin(φ)*b[1],
                  np.sin(φ)*b[0] + np.cos(φ)*b[1],])
    return c

def V(a,v):    # Vektorverchiebung
    b = np.zeros_like(a)
    b[0] = a[0] + v[0]        
    b[1] = a[1] + v[1]
    return b

# ----- main program ---------------------------------------------------
R0 = V(R,-Ppin)   # Dolle in den Nullpunkt verschieben
φ = 0.5*np.pi     # Initalwinkel
xmin= 1.0e10
xmax = -xmin

ax1 = initGrafik()
col = getCl()

R1 = D(R0,-0.35*np.pi); plotRuder(R1[0],R1[1],col[0],ax1)
xmin = min(np.amin(R1[0]),xmin)
xmax = max(np.amax(R1[0]),xmax)
for j in [1,2,3,4,5,6,7]:
     = 0.1*np.pi
    φ  = φ +  
    R1 = D(R1,); 
    plotRuder(R1[0],R1[1],col[j],ax1)
    xmin = min(np.amin(R1[0]),xmin)
    xmax = max(np.amax(R1[0]),xmax)

print('Der grösste und kleinets x-Wert: ','xmin =',xmin,'xmax =',xmax,)
dx = np.array([xmin, xmax])
dy = np.array([4.1, 4.1])
ax1.plot(dx, dy, '-o')
Der grösste und kleinets x-Wert:  xmin = -3.38582479192 xmax = 3.39538586374
Out[105]:
[<matplotlib.lines.Line2D at 0x201c3110c18>]

Kinematik und Hebelwirkung

Bei einem Fahrrad kann die abgewickelte Länge einer Pedalumdrehung gemessen werden und ebenso die Fortbewegung als Folge der Radumdrehung. Wir stellen nun die analoge Überlegung für den Rudervorgang mit dem Boot an. Wir nehmen hypothetisch an, das Boot sei auf Rädern und das Ruder tauche in ein hypothetisches Medium, das in x-Richtung ein Festkörper sei, und in y-Richtung eine Flüssigkeit, um die kreisförmige Ruderbewegung zu ermöglichen. Das Boot wird durch das (in erster Näherung) starre Rudgestänge forwärts geschoben. Wie beim Fahhrad kann nun die zurück gelegte Disntanz gut gemessen,resp. berechnet werden:

Von der kreisförmigen Ruderbewegung trägt einzig die Komponente in der x-Richtung etwas zur Fortbewegung des Bootes bei. Für einen Ruderschlag kann diese Distanz abschliessend berechnet werden. Sie ist abhängig vom Winkel, der überstrichen wird, und von der Länge des äusseren Ruderhebels. Mit dem Computerprogramm können der minimale und maximale x-Punkt je aus den Daten herausgelesen werden, woraus sich die zurückgelegte Distanz ergibt. In der Grafik ist sie mit einem Liniensegment über den Ruderpositionen eingetragen.

Die so gefundene Fortbewegungsdistanz pro Ruderschlag ist kinematisch die maximal mögliche Distanz, die pro Ruderschlag zurückgelegt werden kann. Ist das Boot im Wasser, hat man nicht mehr die idealen Voraussetzungen, wie mit der hypothtischen Festkörperumgebung. Die maximal mögliche Distanz verringert sich, da das Ruder im Wasser nicht alle kinetische Energie auf die Bootsfortbewegung übertragen kann.

Erste Folgerungen

Daraus ergeben sich eine Reihe von Folgerungen:

  • beschränkt man sich auf die Kinematik, welche die geometrischen Zusammenhänge der Bewegung analysiert, so sind möglichst lange äussere Hebelarme von Vorteil sowie möglichst grosse Ruderblätter auch, um dem "Festkörperideal" nahe zu kommen.
  • die Physik berücksichtigt zusätzlich die Kräfte, und es ist heraus zu finden, in welcher Kombination der Krafteinsatz effizient ist.
  • nicht alle Optimierungsvorschläge aus Kinematik und Physik können vom Athleten leicht umgesetzt werden. Die Physiologie berücksichtigt die Möglichkeiten des menschlichen Körpers, sowohl bezüglich der Körperbewegungen als auch bezüglich der Energieabgabe.

Im Folgenden wird es darum gehen, diese drei Aspekte auf einem möglichst je ähnlichen Stand zu berücksichtigen und Optimierungen für den Rudervorgang zu finden.

Konsequenzen des Ruders auf die Kräfte

Kraftverlauf am Innenhebel

Das Ruder dreht sich um eine Drehachse und führt daher immer kreisförmige Bewegungen aus. Die Last am Ruderblatt im Wasser fällt durch den Wasserwiderstand in der Richtung der Blattnormalen an, welche die kreisförmige Ruderbewegung mitmacht. Auf der Kraftseite am Rudergriff stammen die Käfte für einen Ruderschlag aus den Bewegungen der Beine, des Rückens und der Arme des Athleten. Alle drei Teilbewegungen verlaufen im Boot in erster Linie entlang der x-Achse und machen die kreisförmige Bewegung des Rudergriffes nicht mit.

Wir stellen nun die Situation mit dem inneren Hebel auf der Griffseite dar.

In [5]:
def setRuder_2():
    Ha = 3.0                                # äussere Hebellänge
    Hi = 1.0                                # innere Hebellänge
    raV   =     Ha * np.array([0, 1])       # Vektor Ruder aussen = Ha*(0,1)
    riV   = -Hi/Ha * raV                    # Vektor Ruder innen = -ha*(0,1)
    PpinV = np.array([0, 0])                # Ortsvektor der Dolle (engl.: Pin)
    RiV = PpinV + riV                       # OrtsVektor Ruder innen
    Ruder = np.vstack((RiV,PpinV)).T        # Griff und Dolle des Ruders zusammenfügen    
    return Ruder,PpinV

Wir nehmen hypothetisch an, dass über den ganzen Ruderschlag der Athlet eine Kraft der Grösse 1 aufbringt, die genau in die x-Richtung am Innenhebel zieht. Diese Zugkraft wird in zwei Kraftvektoren zerlegt, der eine Vektor ist normal zum Innenhebel und der andere parallel dazu. Diese Parallelkomponente leistet keinen Beitrag, das Ruder zu drehen und ist für die Vorwärtsbewegung verloren.

In [6]:
def getVdecomposition(p): 
    f  = np.array([1, 0]) # Kraftvektor des Ruderers
    fn = np.linalg.norm(f)# Betrag von f
    rr = p[:,0] - p[:,1]  # Richtungsvektor Innenhebel
    rr = rr/np.linalg.norm(rr)       # Betrag von rr
    n = np.array([-rr[1], rr[0]])    # Normalvektor zu rr    
    α = np.arccos(np.dot(-f/fn, rr)) # Winkel zur x-Achse
    
    rr = rr*fn*np.cos(α)  # Kraft parallel zum Hebel
    n =  n*fn*np.sin(α)   # Kraft normal zum Hebel
    return f,n,-rr

In der folgenden Grafik stellt der blaue Vektor die eingesetzte Kraft des Athleten dar, die in diesem Beispiel immer gleich gross bleibt. Dieser Vektor ist in Komponenten aufgeteilt. Der rote Normalvektor zum Innenhebel ist der Beitrag, der das Ruder dreht. Der schwarze Vektor parallel zur Hebelachse steht für die Vorwärtsbewegung nicht zur Verfügung.

In [136]:
# ----- main program ---------------------------------------------------

R,Ppin = setRuder_2()

R2 = V(R,-Ppin)   # Dolle in den Nullpunkt verschieben
φ = 0.5*np.pi     # Initalwinkel

ax2 = initGrafik()
co2 = getC2()

φ_ = np.array([])
f_ = np.array([])
n_ = np.array([])

for j in [0,1,2,3,4,5,6,7,8,9]:
    if j==0 :  = -0.4*np.pi
    else:  = 0.08*np.pi   
    φ  = φ +  
    R2 = D(R2,);  
    ax2.plot(R2[0], R2[1], '-o', color=co2[j],linewidth=1.0)
    
    f,n,rr = getVdecomposition(R2)
    ri = np.array([R2[0,0], R2[1,0]]) 
    plt.quiver(ri[0],ri[1], f[0],f[1],  color='b', angles='xy', scale_units='xy',scale=4)
    plt.quiver(ri[0],ri[1], n[0],n[1],  color='r', angles='xy', scale_units='xy',scale=4)
    plt.quiver(ri[0],ri[1], rr[0],rr[1],color='k', angles='xy', scale_units='xy',scale=4)
    φ_ = np.append(φ_,[φ])
    f_ = np.append(f_,[np.linalg.norm(f)])
    n_ = np.append(n_,[np.linalg.norm(n)])
ax2.plot( [0,0], [0,0], '-bo',linewidth=1.0,label="Krafteinsatz des Athleten (in x-Richtung)")
ax2.plot( [0,0], [0,0], '-ro',linewidth=1.0,label="Normalkraft, nutzbar zur Vorwärtsbewegung ")
ax2.plot( [0,0], [0,0], '-ko',linewidth=1.0,label="Kraft tangential zum Ruder ")
plt.legend(bbox_to_anchor=(0, 1.05), loc=3,ncol=1, borderaxespad=0.0)
Out[136]:
<matplotlib.legend.Legend at 0x201c4a5d2b0>
In [13]:
def grd(β): return β*180.0/np.pi  # Winkelumrechnung von rad --> grad
def rad(β): return β*np.pi/180    # Winkelumrechnung von grad --> rad
In [117]:
fig,ax3 = plt.subplots()
ax3.plot( grd(φ_), (f_), '-bo',linewidth=1.0,label="Krafteinsatz des Athleten (in x-Richtung)")
ax3.plot( grd(φ_), (n_), '-ro',linewidth=1.0,label="Normalkraft, nutzbar zur Vorwärtsbewegung ")

fxx = n_*np.cos(φ_)
#ax3.plot( grd(φ_), (fxx), '-ro',linewidth=1.0,label="xKomponente Aussenhebel im Wasser ")

plt.xlabel('Ruderwinkel (grd)')
plt.ylabel('Kraft')
plt.legend(bbox_to_anchor=(0, 1.05), loc=3,ncol=1, borderaxespad=0.0)

ax3.axis([0, 180, 0, 1.2])
ax3.grid(True)

Kraftverlauf am Aussenhebel

Der Kraftverlauf am Innenhebel ist die Eingangsgrösse für den Kraftverlauf am Aussenhebel. Die Normalkraft am Innenhebel fällt auch am Aussenhebel als Normalkraft an, die als Normale zum Ruderblatt Wasserwiderstand erzeugt. Der Wasserwiderstand ist der "Haltepunkt", der das Ruderblatt "im Wasser fixiert" und damit die Vorwärtsbewegung des Bootes ermöglicht. Nun ist diese "Fixierung" natürlich nicht perfekt. Nimmt man hypothetisch an, die Fixierung des Ruderblattes wäre perfekt, so kann man geometrisch die Weglänge berechnen, um die das Boot mit einem Ruderschlag maximal nach vorwärts geschoben werden kann. Von der Kreisbewegung, die das Ruderblatt beschreibt, ist es wieder nur die x-Komponente, die zur Vorwärtsbewegung des Bootes beiträgt.

Die Normalkraft am Ruderblatt ist vom Betrag her gleich gross wie die Normalkraft am Griff, modifiziert durch die Längenverhältnisse von Innen- zu Aussenhebel gemäss dem Hebelgesetz. Davon ist es wiederum nur die x-Komponente, welche die wirksame Antriebskraft ist, die das Boot antreibt.

$$ \vec{F_{a}^{n} } \cdot H_a= \vec{F_{i}^{n}} \cdot H_i $$$$ \vec{F_{a}^{n} } = \vec{F_{i}^{n}} \cdot H_i/H_a $$
$$ \vec{F_{a}^{x} } = \vec{F_{a}^{n}} \cdot sin(\phi+ \frac{\pi}{2}) $$
In [137]:
def getVdecomposition_2(p): 
    ex  = np.array([1, 0]) # Einheitsvektor in x-Richtung
    ey  = np.array([0, 1]) # Einheitsvektor in y-Richtung
    
    f  = 1*np.array([1, 0]) # Kraftvektor des Ruderers
    fn = np.linalg.norm(f)# Betrag von f
    
    rr = (p[:,2] - p[:,1])           # Richtungsvektor Aussenhebel
    rr = rr/np.linalg.norm(rr)       # rr-Einheitsvektor
    
    n = np.array([-rr[1], rr[0]])    # Normalvektor zu rr 
    n =  n*fn                        # Kraft normal zum Hebel
    fx = np.array([np.dot(ex, n), 0])  # Kraft in x-Richtung
    
    return n,fx


ax5 = initGrafik()
R3 = D(R0,-0.5*np.pi);
for ix in [0,1,2,3,4,5,6,7]:
    a_phi = φ_[ix]
    a_fn =  n_[ix]
    R3 = D(R3,0.1*np.pi);
    plotRuder(R3[0],R3[1],col[ix],ax5) 

    n,fx = getVdecomposition_2(R3)
    ri = np.array([R3[0,7], R3[1,7]]) 
    plt.quiver(ri[0],ri[1],fx[0],fx[1],color='b', angles='xy', scale_units='xy',scale=1)
    plt.quiver(ri[0],ri[1], n[0],n[1], color='r', angles='xy', scale_units='xy',scale=1)

#ax5.axis([-2, 4,-3, 1])