====== std.astro.attitude — Lagefehler, Drehraten, Schwenkzeiten ====== ''import std.astro.attitude;'' Die Lageseite von ''std.astro'': wie weit ist die Ist-Lage vom Ziel entfernt, wie schnell wird gedreht, wie lange dauert ein Schwenk. Lagen sind [[lyx_-_programmiersprache:units:quat|Quaternionen]], Winkel in Radiant, Raten in rad/s. → [[lyx_-_programmiersprache:units:astro|std.astro (Übersicht)]] · [[lyx_-_programmiersprache:units:astro:guidance|std.astro.guidance]] · [[lyx_-_programmiersprache:units:quat|std.quat]] · [[lyx_-_programmiersprache:guides:kurs-im-raum|Guide: Kurs und Lage im 3D-Raum]] ---- ===== 1. Gültigkeit ===== ^ Funktion ^ Rückgabe ^ Bedeutung ^ | ''AAttThreshold()'' | ''f64'' | 1e−12 — unterhalb gilt eine Drehung als „keine" | | ''AAttValid(q, eps)'' | ''bool'' | ist ''q'' ein Einheitsquaternion (bis auf ''eps'')? | | ''AAttIsError(q)'' | ''bool'' | ist ''q'' der Fehlerwert? | Eine Lage, die keine Einheitslänge hat, ist keine Drehung — sie skaliert zugleich. Nach jeder Verkettung normalisieren; ''AAttPropagate'' tut das selbst. ===== 2. Lagefehler ===== ^ Funktion ^ Rückgabe ^ Bedeutung ^ | ''AAttError(qIst, qSoll)'' | ''Quat'' | Fehlerquaternion — die Drehung, die von ''qIst'' nach ''qSoll'' führt | | ''AAttErrorAngle(qIst, qSoll)'' | ''f64'' [rad] | ihr Betrag: wie weit ist es noch? | | ''AAttErrorAxis(qIst, qSoll)'' | ''AVec3'' | die Drehachse als Einheitsvektor | | ''AAttErrorVector(qIst, qSoll)'' | ''AVec3'' | Achse **mal** Winkel — die Form, die ein Regler direkt als Stellgröße nimmt | Der **Fehlervektor** ist die praktisch wichtigste der vier: sein Betrag ist der Winkel, seine Richtung die Achse, und ein PD-Regler kann ihn unmittelbar mit einem Beiwert multiplizieren. Bei verschwindendem Fehler liefert ''AAttErrorAxis'' den Nullvektor statt einer beliebigen Richtung. ===== 3. Drehraten ===== ^ Funktion ^ Rückgabe ^ Bedeutung ^ | ''AAngularVelocity(qVon, qNach, dt)'' | ''AVec3'' [rad/s] | **mittlere** Winkelgeschwindigkeit, die ''qVon'' in ''dt'' nach ''qNach'' bringt | | ''AAttPropagate(q, w, dt)'' | ''Quat'' | eine Lage um ''w·dt'' weiterdrehen, **normalisiert** | | ''AAttRateWithin(w, wMax)'' | ''bool'' | hält die Rate die Grenze ein? | | ''AAttClampRate(w, wMax)'' | ''AVec3'' | Rate auf ''wMax'' begrenzen, **Richtung erhalten** | > **Zwei Feinheiten, die der Quelltext ausdrücklich benennt:** > > * ''AAngularVelocity'' liefert die **mittlere** Rate — aus zwei Lagen lässt sich der Verlauf dazwischen nicht bestimmen. Wer eine Momentanrate braucht, muss messen. > * ''AAttClampRate'' skaliert den ganzen Vektor. **Komponentenweise zu klemmen wäre falsch**: das dreht die Achse und damit das Ziel. ===== 4. Schwenkmanöver (Slew) ===== Ein Schwenk mit begrenzter Rate und begrenzter Beschleunigung hat zwei Formen: **dreieckig** (beschleunigen, sofort bremsen — ''wMax'' wird nie erreicht) und **trapezförmig** (beschleunigen, halten, bremsen). ^ Funktion ^ Rückgabe ^ Bedeutung ^ | ''ASlewTime(winkel, wMax, aMax)'' | ''f64'' [s] | Dauer; wählt selbst zwischen beiden Formen | | ''ASlewTriangleLimit(wMax, aMax)'' | ''f64'' [rad] | bis zu diesem Winkel bleibt es dreieckig: ''wMax²/aMax'' | | ''ASlewPeakRate(winkel, wMax, aMax)'' | ''f64'' [rad/s] | höchste erreichte Rate — im dreieckigen Fall **kleiner** als ''wMax'' | > **''ASlewPeakRate'' ist nicht einfach ''wMax''.** Bei einem kurzen Schwenk wird die Höchstrate nie erreicht; wer trotzdem ''wMax'' meldet, überschätzt die Belastung der Aktorik. Im Beispiel unten: 5 Grad Schwenk erreichen 0.707 statt 1.0 Grad/s. > > Die beiden Zeitformeln treffen sich an der Dreiecksgrenze exakt — das ist die schärfste Probe der Umsetzung, denn ein Fehler in einem der Zweige bricht die Stetigkeit genau dort. ===== 5. Beispiel ===== import std.astro.state; import std.astro.attitude; import std.quat; import std.math; import std.io; fn zeig(n: pchar, v: f64, d: int64): void { Print(n); PrintLn(FloatToStr(v, d)); } fn BI(b: bool): int64 { if (b) { return 1; } return 0; } fn main(): int64 { var rad: f64 := Pi() / 180.0; var ist: Quat := QuatIdentity(); var soll: Quat := QuatFromAxisAngle(0.0, 0.0, 1.0, 30.0 * rad); zeig("Fehlerwinkel Grad : ", AAttErrorAngle(ist, soll) / rad, 2); var achse: AVec3 := AAttErrorAxis(ist, soll); Print("Fehlerachse z : "); PrintLn(FloatToStr(achse.z, 3)); var fv: AVec3 := AAttErrorVector(ist, soll); zeig("Fehlervektor |.| : ", AMagnitude(fv) / rad, 2); Print("Lage gueltig : "); PrintLn(IntToStr(BI(AAttValid(ist, 0.000001)))); // Rate aus zwei Lagen, 10 s auseinander — und wieder zurueck var w: AVec3 := AAngularVelocity(ist, soll, 10.0); zeig("Rate Grad/s : ", AMagnitude(w) / rad, 3); var neu: Quat := AAttPropagate(ist, w, 10.0); zeig("Restfehler Grad : ", AAttErrorAngle(neu, soll) / rad, 6); // Schwenk: 30 Grad bei 1 Grad/s und 0.1 Grad/s^2 zeig("Slewzeit s : ", ASlewTime(30.0*rad, 1.0*rad, 0.1*rad), 2); zeig("Dreieck bis Grad : ", ASlewTriangleLimit(1.0*rad, 0.1*rad) / rad, 2); zeig("Spitzenrate Grad/s : ", ASlewPeakRate(30.0*rad, 1.0*rad, 0.1*rad) / rad, 3); zeig("Slew 5 Grad s : ", ASlewTime(5.0*rad, 1.0*rad, 0.1*rad), 2); zeig("Spitzenrate dabei : ", ASlewPeakRate(5.0*rad, 1.0*rad, 0.1*rad) / rad, 3); var schnell: AVec3 := AVec(0.0, 0.0, 3.0 * rad); Print("Rate im Rahmen? : "); PrintLn(IntToStr(BI(AAttRateWithin(schnell, 1.0*rad)))); zeig("geklemmt Grad/s : ", AMagnitude(AAttClampRate(schnell, 1.0*rad)) / rad, 3); return 0; } Fehlerwinkel Grad : 30.00 Fehlerachse z : 1.000 Fehlervektor |.| : 30.00 Lage gueltig : 1 Rate Grad/s : 3.000 Restfehler Grad : 0.000000 Slewzeit s : 40.00 Dreieck bis Grad : 10.00 Spitzenrate Grad/s : 1.000 Slew 5 Grad s : 14.14 Spitzenrate dabei : 0.707 Rate im Rahmen? : 0 geklemmt Grad/s : 1.000 Die Zeile **''Restfehler 0.000000''** ist die Probe: die aus zwei Lagen gerechnete Rate, über dieselbe Zeitspanne wieder aufintegriert, trifft das Ziel exakt. Rate und Propagation sind also zueinander konsistent. Der Schwenkvergleich zeigt die zwei Formen: **30 Grad** liegen über der Dreiecksgrenze von 10 Grad, also trapezförmig — 40 s bei voller Rate. **5 Grad** bleiben darunter: 14.14 s, und die Rate erreicht nur 0.707 Grad/s. ---- Letzte Aktualisierung: 2026-09-01 — Seite neu angelegt aus ''std/astro/attitude.lyx'' (Commit 32353cc, #1900, 83 Prüfungen); Beispiel mit ''lyxc 1.1.16A'' gebaut und ausgeführt.