Kategorien
RnD

AURORA – Abbildung der Mechanik im Kalibrationsmodul

Die Spiegelelemente der Skulptur AURORA werden von Servomotoren bewegt, die 180° Bewegungsfreiheit haben. Die Arme an den Motoren sind aber nur 53mm lang, die Aufnahme an den Spiegeln ist dagegen 80mm vom Angelpunkt entfernt. Während die Motoren also ±90° drehen, neigen sie den Spiegel dabei nur etwa um ±45° je Achse. Bis jetzt wurden die Kalibrationspunkte einfach linear interpoliert, obwohl mit dieser Geometrie keine lineare Abbildung erfolgt.

Die erste Zeichnung der Geometrie zeigt die Stellung an den Endpunkten, vergisst aber, dass damit der Kolben in Mittelposition nicht orthogonal steht. Inzwischen haben wir andere Maße, längere Motorhörner und andere Gelenke, woraus sich eine größere Winkelspanne ergibt und damit wird die Präzision kritischer.

Formel gesucht

Schon vor einigen Jahren hatte ich das Problem einmal skizziert und eine erste Formel dazu.

Die erste Skizze des Problems nennt den Punkt auf demSpiegel noch F. Der Winkel lambda spielt auch in der folgenden Lösung keine Rolle. Die Vektoren X0 und Y0 heißen im Folgenden Ms und Mm, aus sigma wurde rho.

Erst jetzt bin ich dazu gekommen, diese endlich einmal nach dem gesuchten Motorwinkel aufzulösen und in Python korrekt zu plotten.

# Define the variables and function
Ms = glm.vec2(0,12.0) # Centers of the rotations.
Mm = glm.vec2(4.5,0)  # Its German, so [m]otor and [s]piegel: 
rm = 5.3  # Radius of circle m
rs = 8.0  # Radius of circle s
L = 12.0  # length of crank

# Calculation of alpha as a function of rho
def rho_from_alpha(alpha_deg, _L):
	alpha = np.deg2rad(alpha_deg)

	# coordinates of S on the mirror
	Sx = Ms.x + rs * np.cos(alpha)
	Sy = Ms.y + rs * np.sin(alpha)
	
	# angle of the line Mm-S:
	MmSx = Sx-Mm.x
	MmSy = Sy-Mm.y
	rho1 = np.arctan2(MmSx,MmSy)
	
	# distance between motor axis and S on mirror
	MmSls = MmSx**2+MmSy**2
	MmSl = np.sqrt(MmSls)
	
	 # second component of the angle between vertical and crank
	try:
		rho2 = np.arccos((rm**2 + MmSls - _L**2) / (2*rm*MmSl))
	except:
		rho2 = np.nan
	
	rho = np.pi/2 - rho1 - rho2
	return rho

Witzigerweise bin ich auf der Suche nach besserer Formatierung der Gitterlinien in Matplotlib auf eine Seite gestoßen, die genau diese Aufgabe löst und auch noch den englischen Namen dafür benennt: Crank and Rocker Motion.

Da meine Implementation den genauen Punkt, wo das Motorhorn (Rocker) zu stehen kommt, nicht zeichnet, entfällt eine Berechnung, und dieser Punkt kann m.E. auch mit einem atan-Ausdruck weniger berechnet werden.

Jedenfalls steht aktuell aus,

  • die Animation in der oben verlinkten Lösung in meiner Ausgabe als subplot zu sehen,
  • diese beiden zu animieren und
  • im Idealfall einige Parameter interaktiv zu machen.

Erkenntnisse über den Fußweg

Ich habe also zunächst einmal sehen wollen, wie sehr und wohin sich die Kurve bewegt, wenn z.B Unregelmäßigkeiten in der Länge des Kolbens (Crank) auftreten. Und siehe da:
Bei Abweichung um 2mm sieht man sofort, dass es ein Problem mit der Mittelstellung gibt, dass man entweder durch physische Korrektur oder durch weitere Geometriedaten per Motor lösen kann.

Drei Variationen der Kolbenlänge. Man sieht:
· während der Motor beim Einziehen unter -90° noch wirkt, ist beim Ausfahren bei 87° das Maximum erreicht. Geometrisch schlüssig.
· nur bei Kolbenlänge 12.2mm entspricht die Mittelposition des Motors auch der des Spiegels
· die Abbildung ist selbst zwischen ±30° nicht ausreichend linear,
· kann aber mit den richtigen Parametern über eine einfache Arkussinus-funktion super angenähert werden

Dazu interessiert mich nun, ob als physische Korrektur eine Verschiebung der Käfige mit je zwei Motoren die Abbildung wesentlich beeinflusst.

Die Kurven für verschiedene Abstände des Motors von der Befestigung zeigen:
Abweichungen um ±2mm sind praktisch vernachlässigbar.

An dieser Stelle wird wurde es sinnvoll, die Variationen in dem Skript komfortabler zu handhaben, nämlich indem man die Kurvenschar in Schleifen errechnet und zeichnet statt jetzt dreimal manuell.

Approximation

Vermittels der Plots konnte ich wie angekündigt eine Annäherung vornehmen. Sie beinhaltet in ihrer linearen Komponente die Geometrie und ist für große positive Winkel nicht ideal:

Die Kurven für verschiedene Abstände jetzt in dünnen Linien, dafür die Annäherung gestrichelt. Formel in der Legende: ρ=1.05*arcsin(1.37*(α-2°))+3°

Insgesamt haben die Simulationen dieser Abbildung mittels Matplotlib ergeben:

  • Die Käfige sollten so am Rahmen eingestellt werden, dass die Spiegel bei orthogonalem Motorhorn ebenfalls plan stehen. Das ist bei L-2mm der Fall. Alle anderen Abweichungen sind vernachlässigbar.
  • Davon unberührt sind natürlich Abweichungen, die sich durch Kombination der beiden Auslenkungen ergeben. Ich erwarte einen leichten Kisseneffekt.
    Darüberhinaus wird es schwierigere Fehlabbildungen geben durch Schwerkraft, von der insbesondere die vier seitlichen Dreiecke betroffen sind.

Bei neuerlichem Nachdenken wurde mir klar, dass die zuletzt genannten Abweichungen bei Kombination in 2D große Probleme darstellen. Wahrscheinlich muss eine völlig neue Formel für die Spiegelebene aus den drei Eckpunkten gefunden werden, denn die Achse der Drehung ändert sich ja beim Auslenken mit dem anderen Motor. In der Praxis sieht das ganz einfach und funktionierend aus, aber geometrtisch sind es mitnichten unabhängige Achsen zur Auslenkung in x bzw. y.

Kategorien
Allgemein Satori

Bezierkurven in GFA-Basic

Ok, wir haben Ende 2023. Aber immernoch gibt es manchmal Situationen, wo ich weder mit Code noch mit Pseudocode was gut erklären kann. Dann hilft mir oft ein Relikt aus meiner Kindheit namens GFA-Basic. Der Interpreter kümmert sich um die Einrückung und Colorierung während ich tippe. Es gibt kaum Klammern und keine Semicola.
Menschen können das fast vom Blatt lesen, und darum geht es.

Ihr kennt sicher alle diese schönen Kurven, die galant durch eine Reihe von Kontrollpunkten laufen? Nun, gestern hab ich versucht, Emilio zu erklären, wie solche Bezierkurven entstehen:

Durch Interpolation zwischen Punkten, die ihrerseits Interpolationen sind. Vielleicht erkläre ich später noch ein paar Fallstricke zu Anzahl und Reihenfolge dieser Punkte, aber für heute nehmen wir die einfachste Form: Jeder Ankerpunkt, durch den die Kurve läuft, hat zwei spiegelbildliche Kontrollpunkte, durch feine Nadeln zu ihrem Ankerpunkt dargestellt. Deswegen brauchen wir nur einen davon abzuspeichern, der andere leitet sich davon ab.

Der Trick ist nun, dass man die Kurve in Abschnitten zwischen je zwei Ankerpunkten zeichnet. Den ersten Ankerpunkt wird die Kurve in Richtung ausgehender Kontrollpunkt verlassen, den zweiten Ankerpunkt in Richtung eingehender Kontrollpunkt erreichen. Dazu lässt man einen Parameter von 0 nach eins wandern und nutzt ihn insgesamt sechs mal zur Interpolation zwischen je zwei Punkten:

Zunächst errechnet man drei Zwischenpunkte aus vieren, aus denen sich wiederum zwei Zwischenpunkte ergeben, und zwischen diesen liegt der eine gesuchte Punkt, der die eigentliche Kurve zeichnet.

Hier ist der GFA-Basic-Code, um einige zufällige Punkte zu erzeugen und dann eine Bezierkurve durch sie zu zeichnen:


' *** Für Emilio mal eben BezierKurven visualisiert
' *** Remember: We have to calculate 3 levels of interpolation in order to
' ***           get those curves going through control points continuously

' ** INITIALISIERUNG

Type Int2 :
  x As Short
  y As Short
EndType

Local Short maxSegment = 18
Local Short maxContRange = 512
Local Short maxSteps = 120
Global Tang(maxSegment) As Int2
Global Cont(maxSegment) As Int2
Global ext, p As Int2

AutoRedraw = True
OpenW 1, , , 1600, 1280, 0

Global res As Int2 : res.x = _X : res.y = _Y

randomPoints(maxSegment, maxContRange)
DrawBezier(maxSegment, maxSteps)

'Warteschleife
Repeat
  DoEvents
Until ext Or InKey$ = #27
CloseW 1

Sub randomPoints(maxSegment&, maxContRange&)
  ' Generate and draw points
  Local T As Int2, C As Int2, segment
  For segment = 0 To maxSegment - 1

    ' tangent points
    T.x = Rand(res.x * .8) + res.x * .1
    T.y = Rand(res.y * .8) + res.y * .1
    Color RGB(128, 128, 128)
    Box T.x - 2, T.y - 2, T.x + 3, T.y + 3
    ' write to array
    Tang(segment).x = T.x
    Tang(segment).y = T.y

    ' control points
    C.x = Rand(maxContRange) - maxContRange / 2
    C.y = Rand(maxContRange) - maxContRange / 2
    Color RGB(64, 192, 128)
    Box T.x + C.x - 1, T.y + C.y - 1, T.x + C.x + 1, T.y + C.y + 1
    Color RGB(128, 144, 255)
    Box T.x - C.x - 1, T.y - C.y - 1, T.x - C.x + 1, T.y - C.y + 1
    Cont(segment).x = C.x
    Cont(segment).y = C.y

  Next
Return

Sub DrawBezier(maxSegment&, maxSteps&)
  Local Int segment, step, j
  Local Float s
  Local T0 As Int2,  T1 As Int2, C0 As Int2, C1 As Int2
  Local  R As Int2,   G As Int2,  B As Int2
  Local GR As Int2,  RB As Int2
  Local  Z As Int2, old As Int2

  For j = 0 To maxSegment - 1
    T0 = Tang(j)
    C0 = Cont(j)
    ' make control point absolute:
    C0.x += T0.x : C0.y += T0.y

    T1 = Tang((j + 1) % maxSegment)
    C1 = Cont((j + 1) % maxSegment)
    ' mirror control point 1 and make absolute:
    C1.x = T1.x - C1.x : C1.y = T1.y - C1.y

    For step = 0 To maxSteps
      s = step / maxSteps

      ' Interpolation Level 1:
      ' grüne Punkte = Tangentialpunkt 0 bis Kontrollpunkt 0
      interpol(T0, C0, s, G)
      Color RGB(64, 192, 128)
      Pset G.x, G.y

      ' blaue Punkte = Spiegelung von Kontrollpunkt 1 bis Tangentialpunkt 1
      interpol(C1, T1, s, B)
      Color RGB(128, 144, 255)
      Pset B.x, B.y

      ' rote Punkte = Interpolation zwischen Kontrollpunkten
      interpol(C0, C1, s, R)
      Color RGB(255, 192, 184)
      Pset R.x, R.y

      ' Interpolation Level 2:
      ' grün-rote Interpolation
      interpol(G, R, s, GR)
      ' rot-blaue Interpolation
      interpol(R, B, s, RB)

      ' Interpolation Level 3:
      ' schwarZe Punkte = BeZierkurve
      old = Z
      interpol(GR, RB, s, Z)       ' in Z wird das Ergebnis geschrieben

      If step > 0
        Color RGB(0, 0, 0)
        Line old.x, old.y, Z.x, Z.y
      EndIf

    Next
  Next
Return

Sub interpol(ByRef start As Int2, ByRef end As Int2, s, ByRef p As Int2)
  p.x = start.x * (1 - s) + end.x * s
  p.y = start.y * (1 - s) + end.y * s
Return

Sub Win_1_MouseWheel(Buttons&, Delta%, MseX%, MseY%)
  ext = Buttons && 1
Return

Sub Win_1_ReSize
  res.x = _X : res.y = _Y
  Win_1.BorderStyle = 0
Return
Kategorien
Allgemein

TELEGRAM vs. SIGNAL

Anlässlich der präzisen Analyse von Jürgen Schmidt auf heise online

Telegram-Chat: der sichere Datenschutz-Albtraum – eine Analyse und ein Kommentar

habe ich nochmals gecheckt, wie praktikabel es wirklich ist, Telegram in seriös zu nutzen, also nicht völlig unverschlüsselt.

Also. Die für modernes instant messaging essentielle Ende-zu-Ende-Verschlüsselung ist in Telegram

a) optional b) umständlich zu finden c) muss sie für jeden Kontakt einzeln angeschaltet werden und d) nicht aus dem Kontakt selbst aufrufbar, also nicht für die zentrale Funktion unserer Mobilgeräte, das „Teilen“, verfügbar.

Ich bin raus, sorry.

Kategorien
Wo/Men On Bikes

Ein Pils, bitte!

Frühstück in Pilsen. Ist ja klar, was es heute gibt…

Kategorien
Wo/Men On Bikes

Hey Duda! Mach’s gut!

My first Insta-Schnute: Emilio mit zeitgemäßem Gesichtsausdruck beim Gruppenselfie

Wiedermal alles richtig gemacht: Wundervolle Strecken über Stock und Stein und leider auch über zu langsame Feldmäuse führen uns nach Waldenburg (Wałbrzych), wo wir das erstbeste Lokal heimsuchen und sehr glücklich sind über das perfekte Abendessen.

Heute wollen wir das hiesige Schloss ansehen und dann Polen verlassen.

Kategorien
Wo/Men On Bikes

Einfahrt in Breslau

Nach zwei Tagen Kilometerfressen rauschen wir pünktlich zum Abendessen in Breslau ein.

Die ersten Tage mussten wir gegen den Wind antreten, heute sind wir im Regen aufgewacht. Trotzdem haben wir endlich korrekte Reisegeschwindigkeit erreicht und über lange Strecken um die 26km/h geschafft. Wir sind etwas durchfeuchtet und die Zelte klitschnass.

Entsprechend freuen wir uns, dass jetzt zum Abend die Sonne rauskommt und auf eine gescheite Dusche. Mal sehen, was Breslau zu bieten hat.

Kategorien
Wo/Men On Bikes

THEEOOOO! Wir fahr’n nach Łódz!

Alles, was man wissen muss, in Strophenform und – ganz nützlich – deutscher Sprache.

Inzwischen haben wir die Zelte genutzt und obwohl es nur mäßig sonnig ist, wird Baden gerade zum zentralen Bedürfnis.

Łódz wirkt irgendwie größer als Warschau, und auch spannender. Eventuell werden wir noch ein Foto nachreichen, aber zunächst mal die Fakten. Eigentlich wäre unsere Aufgabe, Theo von Stefan zu grüßen und rauszufinden, was schon Otto Waalkes zum Thema fragte.

Kategorien
Wo/Men On Bikes

Kurioses Kleinbahnmuseum

Is it a bus? Is it a trash bin? No! It’s a kurioses Schienengefährt at the Kleinbahnmuseum Sochaczew
And it’s not the only kurioses Schienengefährt!
Caro and Lio wanted to take a ride – but Alas! The kurioses Schienengefährt was in chains 🔗

Kategorien
Wo/Men On Bikes

Einkehren nach Tag eins…

Eine Partie Schach gehört schon dazu im Café Loskot. Man spielt an zehn Tischen – oder sitzt draußen in der Sonne.

Wir sind extrem happy, dass wir den kleinen Abstecher von der Ost-West-Route gemacht haben: In dem kleinen Städtchen Sochaczew betreiben Katarzyna and Radek Pyś-Fabiańczyk ein Café mit veganem Essen und ausgiebiger Spielekultur. Die leckere Mate dazu hatten wir dringend nötig.

Kategorien
Wo/Men On Bikes

Museum Świat Iluzji

Emilio hat schon wieder einen krassen Wachstumsschub – wo soll das noch hinführen??