Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| kurse:efcomputergrafik:kw4 [2020/01/22 20:07] – [Effektive Beschleunigung und Komponente in Bahnnormalebene] Ivo Blöchliger | kurse:efcomputergrafik:kw4 [2020/02/12 20:06] (current) – [Beschleunigung bei konstantem Geschwindigkeisbetrag 1] Ivo Blöchliger | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Nötige Methode für die Bezier-Klasse ====== | ||
| + | * x(t): Liefert die Position zum Parameter $t$. | ||
| + | * v(t): Liefert die Geschwindigkeit zum Parameter $t$ (" | ||
| + | * len(tmin, tmax): Liefert die Länge der Kurve zwischen den Parametern tmin, tmax (angenähert). | ||
| + | * vnorm(t): Geschwindkeitsvektor mit Länge 1 zum Parameter $t$ (" | ||
| + | * adl(t): Die Ableitung von vnorm nach der **Länge** der Kurve | ||
| + | * | ||
| + | ===== Todo ===== | ||
| + | * forward: Interpolation der verbleibenden Länge, wenn über die Kurve hinaus gegangen wird | ||
| + | * adl(t): | ||
| + | * a(t,v,g): Effektiver Beschleunigungsvektor beim Punkt $p(t)$, wenn der Geschwindigkeitsbetrag v und Inverse Erdbeschleunigung gegeben ist. | ||
| + | * koordsyst(t): | ||
| + | <hidden Python Code> | ||
| + | <code python bezier2.py> | ||
| + | # -*- coding: utf-8 -*- | ||
| + | |||
| + | from vector import Vector | ||
| + | |||
| + | class Bezier: | ||
| + | | ||
| + | def __init__(self, | ||
| + | if len(pts)==4: | ||
| + | self.pts = [p.copy() for p in pts] | ||
| + | if len(pts)==3: | ||
| + | self.pts = [pts[0].copy(), | ||
| + | if len(pts)==2: | ||
| + | self.pts = [pts[0].copy(), | ||
| + | | ||
| + | # Punkt für Parameter t (in [0, | ||
| + | def x(self,t): | ||
| + | return (1.0-t)**3*self.pts[0]+\ | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | # Geschwindigkeit (dx/dt) in t-Parametrierung | ||
| + | def v(self,t): | ||
| + | return -3*(1.0-t)**2*self.pts[0]+\ | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | # Normalisierte Geschwindigkeit | ||
| + | def vnorm(self, | ||
| + | return self.v(t).normalize() | ||
| + | | ||
| + | | ||
| + | def adl(self,t, dt=0.000001): | ||
| + | dl = self.v(t).length*dt # (self.x(t+dt)-self.x(t-dt)).length | ||
| + | return (self.vnorm(t+dt/ | ||
| + | | ||
| + | # Effektive Beschleunigung im Punkt zum Parameter t, mit | ||
| + | # gegebenem Geschwindigkeitsbetrag und Gravitationsvektor | ||
| + | def aeff(self, t, v, g): | ||
| + | return self.adl(t)*v*v-g | ||
| + | | ||
| + | # Orthonnormales Bahn-Koordinatensystem im Punkt zum Parameter t, mit | ||
| + | # gegebenem Geschwindigkeitsbetrag und Gravitationsvektor. | ||
| + | # Resultat (v,a, v cross a) | ||
| + | def koordsyst(self, | ||
| + | x = self.vnorm(t) | ||
| + | # Effektive Beschleunigung | ||
| + | aeff = self.aeff(t, | ||
| + | # Projektion von aeff auf x (d.h. normalisierte Geschwindigkeit) | ||
| + | av = x.dot(aeff)/ | ||
| + | # Effektive Beschleunigung in Normalebene zur Bahn. | ||
| + | # (ist mathematisch rechtwinklig!) | ||
| + | aa = (aeff-av).normalize() | ||
| + | z = x.cross(aa) | ||
| + | return (x,aa,z) | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | |||
| + | # Laenge der Bezier-Kurve | ||
| + | def length(self, | ||
| + | l = 0.0 | ||
| + | # Zeitschritt | ||
| + | dt = (tmax-tmin)/ | ||
| + | for i in range(steps): | ||
| + | # Den i-ten Zeitpunkt von steps Zeitpunkten | ||
| + | ti = (i+0.5)*dt+tmin | ||
| + | l += self.v(ti).length*dt | ||
| + | return l | ||
| + | | ||
| + | # Input: Startzeitpunkt t, Streckenlaenge l | ||
| + | # Output: Endzeitpunkt t oder -Restlaenge | ||
| + | def forward(self, | ||
| + | lastl = l | ||
| + | lastt = startt | ||
| + | while (startt< | ||
| + | dl = self.v(startt).length*dt | ||
| + | lastl = l | ||
| + | lastt = startt | ||
| + | l -= dl | ||
| + | startt += dt | ||
| + | if startt< | ||
| + | return (startt-lastt)*lastl/ | ||
| + | | ||
| + | if l>0: # Es ist noch Laenge uebrig, also auf naechster Kurve | ||
| + | return -(lastl - (self.x(1)-self.x(lastt)).length) | ||
| + | return 1.0 # Sonst genau auf dem Ende der Kurve | ||
| + | | ||
| + | |||
| + | # Zeichnet den Spline | ||
| + | # Achtung: Funktioniert nur, wenn diese Datei ausgeführt wird... | ||
| + | def draw(self, steps=1000): | ||
| + | move(self.pts[0][0], | ||
| + | for i in range(1, | ||
| + | p = self.x(1.0*i/ | ||
| + | lineTo((p[0], | ||
| + | | ||
| + | |||
| + | if __name__== " | ||
| + | from gpanel import * | ||
| + | makeGPanel(Size(600, | ||
| + | window(0, 599, 0, 599) | ||
| + | b = Bezier([Vector([100, | ||
| + | b.draw() | ||
| + | print(b.length(0, | ||
| + | t = 0.0 | ||
| + | while (t> | ||
| + | p = b.x(t) | ||
| + | a = b.adl(t) | ||
| + | pa = p+a*10000 | ||
| + | # move(p.x, p.y) | ||
| + | # circle(5) | ||
| + | line(p.x, | ||
| + | t = b.forward(10, | ||
| + | print(t) | ||
| + | |||
| + | </ | ||
| + | </ | ||
| + | |||
| + | ====== Bahn-Koordinatensystem ====== | ||
| + | ===== Bewegung mit konstantem Geschwindigkeitsbetrag 1 ===== | ||
| + | Kreisbewegung mit Geschwindigkeitsbetrag 1: | ||
| + | |||
| + | \begin{align*} | ||
| + | x(t) & = r \cdot \textrm{e}^{\textrm{i}t \cdot \frac{1}{r}}\\ | ||
| + | v(t) & = x'(t) = \textrm{i}\textrm{e}^{\textrm{i}t \cdot \frac{1}{r}}\\ | ||
| + | a(t) & = v'(t) = -\frac{1}{r} \cdot \textrm{e}^{\textrm{i}t \cdot \frac{1}{r}}\\ | ||
| + | \end{align*} | ||
| + | |||
| + | Beträgt jetzt der Betrag der Geschwindigkeit $\lambda$ anstatt 1, ändern sich die Gleichungen wie folgt: | ||
| + | |||
| + | \begin{align*} | ||
| + | x(t) & = r \cdot \textrm{e}^{\textrm{i} \lambda t \cdot \frac{1}{r}}\\ | ||
| + | v(t) & = x'(t) = \textrm{i}\lambda | ||
| + | a(t) & = v'(t) = -\lambda^2 \cdot \frac{1}{r} \cdot \textrm{e}^{\textrm{i} \lambda t \cdot \frac{1}{r}}\\ | ||
| + | \end{align*} | ||
| + | |||
| + | D.h., wenn der Betrag der Geschwindigkeit von $1$ auf $\lambda$ erhöht wird, wird der Betrag der Beschleunigung mit $\lambda^2$ multipliziert. | ||
| + | |||
| + | ===== Beschleunigung bei konstantem Geschwindigkeisbetrag 1 ===== | ||
| + | Sei $\ell(t)$ die Bogenlänge der Bezierkurve, | ||
| + | $\ell' | ||
| + | |||
| + | Sei $t(\ell)$ die Umkehrfunktion, | ||
| + | (nach der Formel $(f^{-1})' | ||
| + | $$ | ||
| + | \frac{\textrm{d}t(\ell)}{\textrm{d}\ell} = \frac{1}{\ell' | ||
| + | $$ | ||
| + | (D.h. der Parameter $t$ ändert sich mit der Länge indirekt proportional zur $t$-Geschwindigkeit). | ||
| + | |||
| + | Anstatt der " | ||
| + | |||
| + | Es gilt $p_{\ell}(\ell) = p(t(\ell))$ und | ||
| + | \[ | ||
| + | v_n(t(\ell)) := \frac{\textrm{d}p_{\ell}(\ell)}{\textrm{d} \ell} = p' | ||
| + | p' | ||
| + | \] | ||
| + | d.h. man erhält den auf den Betrag 1 normalisierten Geschwindgkeitsvektor (was ja genau der Sinn der $p_{\ell}$ Parametrierung ist). | ||
| + | |||
| + | Die zweite Ableitung von $p_{\ell}$ nach $\ell$ ergibt die Beschleunigung $a_n(\ell)$, | ||
| + | |||
| + | Diese zweite Ableitung berechnen wir nummerisch durch Ableiten der ersten: | ||
| + | \[ | ||
| + | a_n(t(\ell)) := \frac{\mathrm{d}v_n(\ell)}{\mathrm{d}\ell} \approx \frac{v_n(t(\ell)+\Delta t)-v_n(t(\ell)-\Delta t)}{\ell(t+\Delta t)-\ell(t-\Delta t)} \approx \frac{v_n(t(\ell)+\Delta t)-v_n(t(\ell)-\Delta t)}{|p(t+\Delta t)-p(t-\Delta t)|} | ||
| + | \] | ||
| + | |||
| + | Algebraisch erhält man folgendes: | ||
| + | \[ | ||
| + | \frac{\mathrm{d}}{\mathrm{d}\ell} \left(\frac{p' | ||
| + | \frac{p'' | ||
| + | \] | ||
| + | mit $e=\frac{p' | ||
| + | ===== Effektive Beschleunigung und Komponente in Bahnnormalebene ===== | ||
| + | Sei $v_{\text{eff}}(t) \in \mathbb{R}$ der Betrag der effektiven Bahngeschwindigkeit im Punkt zum entsprechenden $t$-Parameter. | ||
| + | Die effektive Beschleunigung ist also | ||
| + | \[ | ||
| + | a_{\text{eff}}(t) = (v_{\text{eff}}(t))^2 \cdot a_n(t) -g | ||
| + | \] | ||
| + | wobei $g$ die Gravitationsbeschleunigung ist. | ||
| + | |||
| + | Diese Beschleunigung zerlegen wir in zwei Komponenten, | ||
| + | |||
| + | $a_t$ ist die Projektion von $a_{\text{eff}}$ auf $v_n$. Es gilt (mit $|v_n|=1$): | ||
| + | \[ | ||
| + | a_t = (\vec a_{\text{eff}} \cdot \vec v_n) \cdot \vec v_n \qquad \text{Skalarprodukt in der Klammer!} | ||
| + | \] | ||
| + | Und damit | ||
| + | \[ | ||
| + | a_r = a_{\text{eff}}-a_t | ||
| + | \] | ||
| + | |||
| + | Damit bilden wir ein Koordinatensystem $K(t)$ mit Ursprung $p(t)$ und Einheitsvektoren | ||
| + | \[ | ||
| + | e_1 = v_n(t), \qquad e_2 = \frac{a_r(t)}{|a_r(t)|}, | ||
| + | \] | ||
| + | |||
| + | ===== Bau der Bahn ===== | ||
| + | In geeigneter Schrittgrösse der Kurve entlang gehen, an jedem Punkt mit $K(t)$ Schienen-Punkte definieren. | ||
| + | Eventuell Stützen definieren. | ||
| + | |||
| + | Blender Code analog zur Generierung der glatten Kurve. | ||
| + | ===== Simulation der Bewegung ===== | ||
| + | Zustand: $t$ (Ort auf der Bahn), $v_{\text{eff}}$ (aktueller Betrag der Geschwindigkeit) | ||
| + | |||
| + | Schritt: Zeit um 1/framerate vorrücken, der Bahn folgen (z.B. um die Strecke, die mit $v_{\text{eff}}$ in dieser Zeit zurückgelegt würde, oder genauere schrittweise Simulation). Aus der Höhendifferenz und eventuell Reibung die neue Geschwindigkeit berechnen. | ||
| + | |||
| + | Kamera entsprechend positionieren und Keyframe setzen: | ||
| + | |||
| + | <code python> | ||
| + | cam = bpy.data.objects[' | ||
| + | frame = 0 | ||
| + | cam.animation_data_clear() | ||
| + | cam.matrix_world = ( (y.x, | ||
| + | cam.keyframe_insert(data_path=" | ||
| + | cam.keyframe_insert(data_path=" | ||
| + | frame+=1 | ||
| + | </ | ||
| + | Siehe auch https:// | ||
| + | |||
| + | D.h. die erste Koordinatenrichtung ist rechts, die zweite oben und die dritte ist entgegen der Blickrichtung. | ||
| + | |||
| + | ===== Blender ===== | ||
| + | Bezier Klasse laden in Blender: | ||
| + | <code python bahn.py> | ||
| + | # Nimmt die Bezierkurven aus myspline und erzeugt | ||
| + | # die Bahn und die Kamera-Animation | ||
| + | |||
| + | # Import in Blender 2.8 (see https:// | ||
| + | Bezier = bpy.data.texts[" | ||
| + | |||
| + | obj = bpy.data.objects[' | ||
| + | |||
| + | # Kurvenpunkte auslesen | ||
| + | mypoints=[] | ||
| + | if obj.type == ' | ||
| + | for subcurve in obj.data.splines: | ||
| + | curvetype = subcurve.type | ||
| + | if curvetype == ' | ||
| + | for bezpoint in subcurve.bezier_points: | ||
| + | mypoints.append(bezpoint.handle_left) | ||
| + | mypoints.append(bezpoint.co) | ||
| + | mypoints.append(bezpoint.handle_right) | ||
| + | | ||
| + | |||
| + | # Sammlung von Bezierkurven erzeugen | ||
| + | mySplines = [] | ||
| + | numpoints = len(mypoints) | ||
| + | totalLength = 0 | ||
| + | for i in range(numpoints// | ||
| + | mySplines.append(Bezier((mypoints[i*3+1], | ||
| + | mypoints[i*3+2], | ||
| + | mypoints[(i*3+3)%numpoints], | ||
| + | mypoints[(i*3+4)%numpoints]))) | ||
| + | totalLength+=mySplines[-1].length() | ||
| + | |||
| + | |||
| + | # Bahn erzeugen | ||
| + | try: | ||
| + | bpy.ops.collection.objects_remove(bpy.data.collections[' | ||
| + | except: | ||
| + | pass | ||
| + | | ||
| + | railsCol = bpy.data.collections.new(' | ||
| + | linksCol = bpy.data.collections.new(' | ||
| + | railsCol.children.link(linksCol) | ||
| + | bpy.context.scene.collection.children.link(railsCol) | ||
| + | |||
| + | abstand = 0.2 # Bahnpunkte | ||
| + | ldone = 0 # Erledigte Bahnstrecke | ||
| + | i=0 # Aktuelle Bezierkurve | ||
| + | t = 0 # Aktuelle t-Parameter | ||
| + | g = Vector(0, | ||
| + | hmax = 40 # Hoehe fuer v=0 | ||
| + | # Bahnpunkte: Ctrl-Links, Knoten, Ctrl-Rechts | ||
| + | railspts=[[], | ||
| + | while(ldone< | ||
| + | dl = abstand; | ||
| + | tnext = -1 | ||
| + | while(tnext< | ||
| + | tnext = mySplines[i].forward(dl, | ||
| + | if (tnext< | ||
| + | i=(i+1)%numSplines | ||
| + | t = 0 | ||
| + | dl=abs(tnext) | ||
| + | else: | ||
| + | ldone+=dl | ||
| + | t = tnext | ||
| + | # Potentielle Energie mgh | ||
| + | ekin = (hmax-mySplines[i].x(t).z)*abs(g.z) | ||
| + | # Ek = 1/2 * m * v^2 | ||
| + | v = (2*ekin)**0.5 | ||
| + | # Koordinatensystem (vorne, oben, rechts) | ||
| + | k = mySplines[i].koordsyst(t, | ||
| + | # Bahnpunkte berechnen | ||
| + | # | ||
| + | # | ||
| + | # | ||
| + | | ||
| + | |||
| + | |||
| + | # Blender-Kurven aus den Bahnpunkten erzeugen | ||
| + | for j in range(3): | ||
| + | curvedata = bpy.data.curves.new(name=" | ||
| + | curvedata.dimensions = ' | ||
| + | objectdata = bpy.data.objects.new(" | ||
| + | objectdata.location = (0,0,0) | ||
| + | objectdata.data.bevel_depth = 0.01 | ||
| + | |||
| + | railsCol.objects.link(objectdata) | ||
| + | |||
| + | polyline = curvedata.splines.new(' | ||
| + | polyline.bezier_points.add(len(railspts[j])-1) | ||
| + | |||
| + | for idx, (h1, knot, h2) in enumerate(railspts[j]): | ||
| + | point = polyline.bezier_points[idx] | ||
| + | point.co = knot | ||
| + | point.handle_left = h1 | ||
| + | point.handle_right = h2 | ||
| + | point.handle_left_type = ' | ||
| + | point.handle_right_type = ' | ||
| + | |||
| + | </ | ||