====== 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.