fix: Laengenbilanz des gefalteten Modus-2-Laufs (U-Form)

Gefaltete VF-Laeufe wurden zu kurz gebaut, weil der Solver Posten dem
falschen Schenkel zurechnete. Vier Aenderungen, alle nach demselben
Leitsatz: die letzten freien Groessen folgen der GEMESSENEN Ist-Lage,
nicht einer vorab verteilten Gesamtplanung.

1. vfl-zsolve-schluss-ziel (neu): endet der Lauf auf einem flachen
   Fueller, werden RICHTUNG und LAENGE gemeinsam gegen den Zielpunkt
   geloest statt nur die Laenge. Geschlossene Loesung ueber
   A = sqrt(|D|^2 - P^2), hz = Winkel(D) - atan2(P, A); C und P sind
   von hz unabhaengig, daher ohne Iteration. Bisher uebernahm der
   Solve die Richtung aus dem gezeichneten Pfad - ein Querversatz der
   realen Eck-Bauteile war damit prinzipiell unerreichbar, weil eine
   Laenge die Kette nur entlang ihrer Achse verschiebt.

2. Eingangs-Hardware bei gefaltetem Lauf: Umlenkstation + Separator
   stehen auf dem Eingangs-Schenkel und verbrauchen die Kletterspanne
   nicht; ihr Laengenanteil wird der Spanne wieder zugeschlagen.
   feste-vf bleibt unveraendert - sein sin3-Hoehenanteil ist real.

3. kletter-ende-fp: endet der Lauf auf einem KLETTERER, gibt es keinen
   Schluss-Fueller, der Motorstation + Separator + ES abzieht - sie
   standen in keinem Budget. Nur bei flacher Zone, Pfadende und
   vorhandenem ES, sonst sind sie bereits anderweitig verbucht.

4. Nachrechner wieder aktiv (8 statt 4 Runden): GF1 steht im Budget,
   aber im Eingangs-Schenkel. Die Kollision mit filler-A-len, wegen
   der er abgeschaltet war, ist entfallen.

3 und 4 wirken NUR gemeinsam - die Betraege heben sich teilweise auf,
eine einzeln eingebaute Korrektur verschiebt den Fehler bloss in die
andere Richtung (GF1 1636.9 - (Motor+Sep+ES) 1235.1 = 401.8 mm).

Gemessen am selben Pfad: 9 Grad 0.56 mm, 12 Grad 0.7 mm quer (vorher
1424 bzw. 934 mm), dZ 0.1 mm. Beide Modus-2-Regressionsketten weiter
OK; TEST_UNIT 904/904 mit 7 neuen file-freien Tests.

doc/todo.md: Punkt "Modus-2-Solver: achsenweise Laengen-Bilanz" ist
damit erledigt. Die Detaildatei bleibt liegen, weil vier Kommentare in
vf_linienzug.lsp auf ihren Pfad verweisen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 11:41:35 +01:00
parent 9fb43b3881
commit 35ebee1e88
6 changed files with 746 additions and 53 deletions
-11
View File
@@ -11,17 +11,6 @@ Stand: 2026-10-26. Wird woechentlich (freitags) mit der Doku geprueft, siehe
## 1. VarioFoerderer / Linienzug
- **Modus-2-Solver: achsenweise Laengen-Bilanz.** Gefaltete VF-Laeufe (U-Form)
werden falsch gebaut (erste GF zu lang, Kletterstrecke zu kurz, Motor/ES
falsch), weil der Solver die festen Fussabdruecke skalar von EINER Spanne
abzieht. **Teilweise erledigt 2026-10-26** (ce4ad0f): AS-Element,
Abschlussbogen+Motorstation und die Eck-Differenzen werden seitdem dem
Segment zugeschlagen, auf dem sie physisch stehen (`vfl-eck-extra` je
Segmentindex, `as-along-fix` auf `vf-start`, `abschluss-fp` auf `vf-ende`) -
das ist die achsenweise Idee fuer diese Posten. SKALAR bleiben `feste-vf`
(Umlenkstation + Separator) und `gf-total` (GF1), und genau die verursachen
den U-Form-Fehler. Als Zwischenloesung zumindest gefaltete Laeufe mit
Meldung abweisen. -> `doc/todo/modus2-achsenweise-bilanz.md`
- **VF-Linienzug ohne GUI/Konsole, Stufe 2** (Funktionstrennung): eigener,
separat abzuarbeitender Auftrag mit Schritt-Tabelle. Schritte 5c, 6, 8, 9,
10 offen (u.a. `vfl-spec-ausfuehren`). Schritt 11 war zurueckgestellt, bis
+233 -8
View File
@@ -1,13 +1,21 @@
# TODO: Modus-2-Solver auf achsenweise Laengen-Bilanz umstellen
# Modus-2-Solver: achsenweise Laengen-Bilanz
Status: **Vorschlag / noch nicht umgesetzt** (2026-09-18). Mit dem Entwickler zu
besprechen, bevor der Solver angefasst wird. Reiner Plan - kein Code geaendert.
Status: **ERLEDIGT 2026-10-29.** Gefaltete Laeufe (U-Form) werden gebaut; am
selben Pfad gemessen 0.56 mm (9 Grad) und 0.7 mm (12 Grad) quer, beide
Regressionsketten weiter OK. Der Abschnitt "Stand 2026-10-29" und der "Nachtrag
2026-10-29" am Ende beschreiben Loesung und Herleitung.
**Pruefung 2026-10-26:** weiterhin OFFEN - der Solver bilanziert noch skalar
(`climber-span`, `hor-pairs` 0/1), auch die Zwischenloesung (gefaltete Laeufe
abweisen) ist nicht eingebaut. Die Zeilennummern unten sind veraltet: Phase B
von `vf-linienzug-modus2` liegt inzwischen bei ca. `vf_linienzug.lsp:11880-12100`
(per Suche nach `climber-span` finden).
Die Datei bleibt BEWUSST liegen, obwohl der Auftrag abgeschlossen ist: vier
Kommentare in `Lisp/vf_linienzug.lsp` verweisen auf diesen Pfad (Suche nach
`modus2-achsenweise-bilanz`). Sie dokumentiert damit nicht mehr einen Plan,
sondern WARUM der Solver so bilanziert, wie er es tut - einschliesslich der
fehlgeschlagenen Versuche, damit sie niemand ein zweites Mal unternimmt.
Alles unterhalb dieser Zeile ist der urspruengliche Plan von 2026-09-18 und
seine Zwischenstaende. Die dort genannten Zeilennummern sind veraltet; Phase B
von `vf-linienzug-modus2` findet man per Suche nach `climber-span`. Die damals
vorgeschlagene Zwischenloesung (gefaltete Laeufe mit Meldung abweisen) wurde
NICHT umgesetzt und ist gegenstandslos - solche Laeufe funktionieren jetzt.
## Kurzfassung
@@ -166,3 +174,220 @@ namentlich (`AS-Fussabdruck ... von Segment N abgezogen`,
`Abschluss-Fussabdruck ...`, `Eck-Korrektur ... auf Segment N`). Eine
FEHLENDE Zeile ist das Warnsignal - so wurde ein 2,4-mm-Rest gefunden. Fuer
die U-Form-Diagnose sind diese Zeilen der schnellste Einstieg.
---
## Stand 2026-10-28: Fueller-Seite geloest, Solver-Seite offen
### Geloest und gemessen
`vfl-m2-eingang-fp`: GF1 + Separator + Umlenkstation + Uebergangsbogen stehen
auf dem Segment `vf-start`. Ist dieses Segment ein FUELLER (der Kletterer
liegt hinter einer Kurve), bekam der Fueller bisher die volle Restlaenge, als
staende dort nichts. Jetzt werden sie abgezogen.
Ist das Eingangs-Segment selbst der Kletterer - der gerade Normalfall -, gibt
die Funktion hart 0.0 zurueck; der funktionierende Fall kann sich also nicht
aendern. Gemessen am U-Form-Testfall: **Querabweichung 8005 -> 1424 mm**,
Segment 1 danach auf 0,2 mm genau belegt.
### Noch offen: der Solver rechnet GF und Stationen gegen die falsche Achse
Rest 1424 mm. Segment 5 (Kletterer) legt 6475,5 mm zurueck, verfuegbar waeren
7899,8 - der Solver reserviert dort Platz fuer Umlenkstation, Separator und
den GF1-Anteil, die auf Segment 1 stehen.
### Fehlgeschlagene Versuche (nicht wiederholen)
Drei Anlaeufe am 2026-10-27/28, jeder an einer anderen Kopplung gescheitert:
1. **`feste-vf` kleiner uebergeben.** Geht nicht: `feste-hz` fliesst in
`vf-winkel-AB` (vf_standard.lsp:241/245) sowohl als `feste*cos3` in die
LAENGE als auch als `feste*sin3` in die HOEHE. Die Hoehe ist real - die
Teile senken die Kette ab, egal auf welchem Schenkel sie stehen. Nur der
Laengenanteil darf verschoben werden.
2. **Spanne vorab korrigieren und iterieren.** Geht nicht direkt:
`vfl-zsolve-winkel-waehlen` enthaelt die WINKELFRAGE an den Nutzer - eine
Iteration wuerde mehrfach fragen. Und die Korrektur ist pro Winkel
verschieden, weil jeder Winkel ein anderes `L_GF` ergibt.
3. **Stiller Nachrechner nach der GF-Verteilungsfrage** (Code vorhanden, per
`(setq nachrechnen-k 4)` deaktiviert). Technisch sauber: kein Dialog,
Fragenfolge und Journal unveraendert, Konvergenz in 4 Runden
(3278 -> 4372 -> 4737 -> 4859 -> 4899 mm). ABER er kollidiert mit
**`filler-A-len`**: dieser Fueller belegt im Kletter-Segment genau den
Platz, den das halbe GF1 dort frei laesst. Nimmt man GF1 aus der
Kletterer-Spanne heraus, wird Fueller-A trotzdem gebaut - zusaetzlich zum
echten GF1 auf demselben Segment. Gemessen: Fueller nach der
Umlenkstation -369 mm (auf 100 geklemmt), Kette 746 mm daneben.
Kleinerer Endfehler, aber entartete Geometrie - deshalb zurueckgenommen.
### Daraus fuer den naechsten Anlauf
Der Nachrechner ist vermutlich der richtige Weg, aber **`filler-A-len` muss
mitbehandelt werden**: er gehoert zur Annahme "GF1 liegt im Kletter-Segment"
und darf bei gefaltetem Lauf nicht mehr entstehen. Wer das angeht, sollte
beide Mechanismen zusammen betrachten, nicht nacheinander.
Testfall vorhanden: U-Form 5855 / 270 / 7806 mm (Kletterer hinter zwei
90-Grad-Kurven), Soll-Querabweichung ~0, Stand heute 1424 mm. Die beiden
geraden Referenzketten in `c:TEST_LINIENZUG_MODUS2` schuetzen den Normalfall.
---
## Stand 2026-10-29: Eingangs-Segment exakt, Kletterer-Segment offen
### Zusaetzlich geloest (gegenueber dem Stand vom 2026-10-28)
**Fueller-A entfaellt bei gefaltetem Lauf**, und `vfl-m2-eingang-fp` zieht
seitdem `br-gf1` ab statt `gf-total`. Beides gehoert zusammen:
`filler-A-len` fuellt den Platz, den ein halbiertes GF1 auf DERSELBEN Achse
frei laesst - er gehoert zur Annahme "GF1 und GF2 liegen auf einer Geraden".
Bei gefaltetem Lauf wurde er auf dem Eingangs-Segment ZUSAETZLICH zu GF1
gebaut; zusammen mit GF2 im Kletterer-Schenkel entstanden damit 1.5 x
gf-total Gefaellestrecke bei einem Budget von gf-total.
Ergebnis, am U-Form-Testfall nachgerechnet (9 Grad, Verteilung 1/2+1/2):
```
Segment 1: AS 425,8 + GF1 1370,3 + Sep 299,6 + Umlenk 499,3
+ Bogen 199,9 + Fueller 3153,7 = 5948,6 mm
Budget: 5855 + 93,8 (Eck-Korrektur) = 5948,8 mm
```
Das Eingangs-Segment ist damit **auf 0,2 mm exakt belegt**, und im Protokoll
steht nur noch EIN Fueller statt zwei.
### Was offen bleibt
Das KLETTERER-Segment. Gemessen im selben Lauf: Segment 5 belegt 6965,8 mm,
verfuegbar waeren 7899,8 - es fehlen **934 mm**, exakt die Endabweichung.
Der Solver bekommt als Spanne 7704,16 mm und verplant darin u.a. GF1
(1370,3) sowie Umlenkstation + Separator (798,9) - Teile, die auf Segment 1
stehen. Umgekehrt enthaelt seine Spanne NICHT den Abschluss-Fussabdruck
(ab_3 + Motorstation, 695 mm), weil dieser nur von FUELLER-Segmenten
abgezogen wird (`fill-len`) und das Kletterer-Segment keines ist.
**Der Restfehler skaliert mit `L_GF`** und damit mit dem gewaehlten Winkel -
unabhaengig bestaetigt durch zwei Laeufe auf demselben Pfad:
| Winkel | L_GF | Querabweichung |
|---|---|---|
| 6 Grad | 1093 | 90 mm |
| 9 Grad | 2744 | 934 mm |
Praktische Folge: Mit flachem Winkel ist der gefaltete Lauf heute brauchbar,
mit steilem nicht.
### Hinweis zur Methodik fuer den naechsten Anlauf
Zwei Laeufe sind nur vergleichbar, wenn **Winkel UND GF-Verteilung gleich**
sind. Mehrere Messreihen dieser Sitzung waren nicht deutbar, weil sich
zwischendurch der Winkel geaendert hatte und der Restfehler mit `L_GF`
skaliert. Vor einer Aenderung also eine feste Kombination festlegen
(Vorschlag: 12 Grad, Verteilung 1/2+1/2) und nur die vergleichen.
---
## Nachtrag 2026-10-29: Loesungsansatz vom Nutzer
Der Abschnitt darueber beschreibt den Stand VOR dem folgenden Umbau; er bleibt
als Herleitung stehen.
Nutzer-Vorgabe: *"nach der zweiten VF-Kurve den passenden Winkel neu aus dem
KS_AUS der Kurve und dem Zielpunkt rechnen"* und *"da GF2 feststeht, steht auch
die fuer Auf/Ab verbleibende Laenge fest - passender Winkel plus eine
ausgleichende GF"*.
Das ist die Umkehrung der bisherigen Logik und trifft den Kern: bisher wurde
ALLES vorab global verplant und der aufgelaufene Fehler landete am Kettenende.
Richtig ist, die letzten freien Groessen aus der GEMESSENEN Ist-Lage zu
bestimmen. Je nachdem, was das letzte Segment ist, sind das verschiedene
Groessen:
| Letztes Segment | feste Groesse | freie Groessen |
|---|---|---|
| flacher Fueller | - | Richtung + Fuellerlaenge |
| Auf/Ab-Koerper (U-Form) | Laenge (aus dem Pfad) | Winkel (diskret) + GF-Ausgleich |
### Umgesetzt
**1. Flacher Fueller als Schlussstueck** - neue Funktion
`vfl-zsolve-schluss-ziel`. Bisher loeste `vfl-zsolve-es-fueller` nur die LAENGE
und uebernahm die Richtung aus dem gezeichneten Pfad. Eine Laenge verschiebt die
Kette aber nur ENTLANG ihrer Achse - steht der reale Kurvenausgang seitlich
NEBEN der gezeichneten Geraden, laeuft die gebaute Achse parallel zur Zielachse
und der Querversatz ist unerreichbar. Gibt man die Richtung mit frei, stehen
zwei Unbekannte zwei Gleichungen (X und Y) gegenueber, geschlossen loesbar:
```
D = Ziel - KS_AUS(letzte Kurve)
A = sqrt(|D|^2 - P^2) ; P = Querversatz des ES-Fussabdrucks
fill = A - C
hz = Winkel(D) - atan2(P, A)
```
`C` und `P` sind von `hz` unabhaengig, weil `vfl-zsolve-es-vektor` Vektor und
Bezugsrahmen um denselben Winkel dreht - deshalb ist keine Iteration noetig.
Scheitert der Solve (Ziel naeher als `P`), bleibt der bisherige Weg. Unit-Tests:
`tu-tests-vfl-schluss` (file-frei ueber `es-block = nil`).
**2. Eingangs-Hardware bei gefaltetem Lauf** - der am 2026-10-28 zurueck-
genommene Spannen-Zuschlag ist wieder da. Begruendung fuer die Ruecknahme war
die Kollision mit `filler-A-len`; dieser Blocker ist entfallen, seit Fueller-A
bei gefaltetem Lauf gar nicht mehr entsteht. Zugeschlagen wird weiterhin NUR
der Laengenanteil (`entry-fixed * cos3`), nie `feste-vf` selbst - dessen
`sin3`-Hoehenanteil ist real.
**3. Abschluss-Hardware auf dem Kletterer** (`kletter-ende-fp`) - endet der Lauf
auf einem FUELLER, zieht `vfl-zsolve-es-fueller` Motorstation + Separator + ES
beim Loesen der Fuellerlaenge selbst ab. Endet er auf einem KLETTERER, gibt es
diesen Fueller nicht: die drei Teile werden dort gebaut, standen aber in keinem
Budget. Abgezogen wird dasselbe wie in dessen Konstante `cc`, OHNE den
ab_3-Bogen (der steckt in diesem Fall schon in `uebergang-fp`).
**4. GF1-Fremdanteil** (Nachrechner wieder aktiv, 8 statt 4 Runden) - `gf-total`
ist ein ERGEBNIS des Winkel-Solves, sein auf dem Eingangs-Schenkel stehender
Teil laesst sich dort noch nicht abziehen (Henne-Ei). Der Nachrechner loest bei
UNVERAENDERTEM Winkel neu und konvergiert mit Faktor ~1/3.
### Der entscheidende Punkt: 3 und 4 wirken nur GEMEINSAM
| Posten | im Budget des Solvers | steht real auf dem Kletterer |
|---|---|---|
| GF1 | ja | nein - liegt im Eingangs-Schenkel |
| Motor + Separator + ES | nein | ja |
Die beiden Betraege heben sich TEILWEISE auf. Wer nur einen davon korrigiert,
verschiebt den Fehler bloss in die andere Richtung - genau das war der
746-mm-Befund vom 2026-10-28, der damals wie eine Verschlechterung aussah.
Gemessen am 9-Grad-Lauf: GF1 1636.9 - (Motor+Sep+ES) 1235.1 = 401.8 mm, exakt
die damalige Restabweichung von 401.7 mm.
### Messergebnis
| Lauf | vorher | nachher |
|---|---|---|
| U-Form 9 Grad | 1424 -> 934 mm | **0.56 mm** |
| U-Form 12 Grad | - | **0.7 mm** |
| Variante A (Vario-Kurve) | OK | OK |
| Variante B (GF-Bogen) | OK | OK |
Zwei verschiedene Winkel auf demselben Pfad - der Restfehler skaliert also
nicht mehr mit `L_GF`, die Bilanz selbst stimmt.
### Regression auf dem Weg dorthin (Lehrstueck)
`kletter-ende-fp` schlug zunaechst auch in Variante B zu und verkuerzte deren
Segment 1 um 798.9 mm. Dort ist `vf-start = vf-ende = 0`: der VF-Lauf besteht
aus EINEM Segment, die Segmente 2..5 sind Back-Lauf. Damit ist der Kletterer
zugleich Laufende, der Abzug griff - obwohl ohne flache Zone die Motorstation
bereits in `feste-vf` steckt und Separator + ES vom Back-Lauf reserviert werden.
Die drei Bedingungen am `if` (`hor-pairs > 0`, `vf-ende = n-1`, `es-vorhanden`)
sind deshalb keine Vorsichtsmassnahme, sondern benennen genau die Faelle, in
denen sonst doppelt gebucht wird.
Auffindbar war die Ursache nur, weil Segment 1 von Variante B bei `hz=0` liegt:
eine Verkuerzung dieses Segments uebersetzt sich 1:1 in `dX`, und `IST - SOLL =
(-799.11 | -104.68)` aus `logs/vfl_modus2.dbg` zeigte die reine X-Verschiebung.