Ich hatte Filament gewechselt, ganz normal über die Makros UNLOAD_FILAMENT und LOAD_FILAMENT, und danach einen Druck gestartet. Der Drucker hat brav gehomt, Z-Tilt und Bed Mesh durchlaufen – und dann in die Luft gedruckt.
Die Ursache war ein fehlendes Argument in meiner printer.cfg: G92 statt G92 E0. Spannender als die Ursache ist allerdings, warum Klipper sie nicht von selbst repariert: Ich war fest davon ausgegangen, dass ein G28 jeden verschobenen Nullpunkt wieder geraderückt. Das stimmt nicht – zumindest nicht für Z, wenn über die Eddy-Sonde gehomt wird.
Wie es sich zeigte
Angefangen hat es mit einer harmlosen Frage: Warum fährt das Bett nach dem Z-Homing rund 60 mm herunter und gleich wieder hoch? Kurz darauf druckte der Drucker ins Leere.
Der Blick auf die laufende Instanz zeigte zwei Z-Werte, die nicht zusammenpassten. Der Kopf stand zu diesem Zeitpunkt auf der Parkposition X200/Y395:
toolhead.position Z = 102,601 # wo die Maschine tatsächlich ist
gcode_move.gcode_position Z = 15,320 # wo der G-Code glaubt zu sein87 mm Unterschied zwischen dem, was die Maschine tut, und dem, was der G-Code annimmt. Das ist kein Kalibrierfehler mehr, das ist ein anderes Koordinatensystem.
Was ich untersucht habe
Die Formel aus dem Quellcode
Klipper rechnet G-Code-Koordinaten in gcode_move.py um. Die Beziehung ist simpel:
gcode_position = last_position − base_positionSetzt man die beiden Live-Werte ein, ergibt sich ein base_z von 87,28. Irgendetwas hatte also den Z-Ursprung des G-Code-Koordinatensystems um gut 87 mm nach oben verschoben.
Die Probe auf die Zahl
Bevor ich nach dem Schuldigen suchte, wollte ich wissen, ob die 15,32 überhaupt plausibel sind. Sie sind es: 0,32 mm erste Schicht plus die 15 mm, um die END_PRINT den Kopf am Ende anhebt (G1 Z15). Die Zahl passt also zu einem normal durchgelaufenen G-Code – nur eben 87 mm über dem Bett.
Was vor dem Druck passiert ist
Der gcode_store von Klipper hält die zuletzt ausgeführten Befehle vor. Darin stand die Reihenfolge, die alles erklärt hat:
13:47:57 UNLOAD_FILAMENT
13:48:45 LOAD_FILAMENT
13:49:11 SDCARD_PRINT_FILEUnd dasselbe Muster gab es schon am Vormittag. Filamentwechsel, dann Druckstart – zweimal am selben Tag.
Live-Experimente
Statt weiter zu lesen, habe ich auf der Maschine ausprobiert, was die einzelnen Befehle mit dem Koordinatensystem anstellen:
- Ein nacktes
G92setzt die G-Code-Position auf 0/0/0/0 – also X, Y, Z und E. G28 X Ysetztbase_xundbase_yzurück.- Ein volles
G28und sogar ein rohesG28.1 Zlassenbase_zbei 87,294 stehen.
Der letzte Punkt war der eigentliche Schock.
Die Irrwege
„Das ist der G28-Wrapper“
Auf dem Drucker läuft der G28-Wrapper aus dem BTT-Eddy-Paket. Als ich nur die „60 mm runter und wieder hoch“ sah, lag der Verdacht nahe, dass der Wrapper oder eine eingestellte Fahrhöhe dahintersteckt. Die Zahlen passten aber nicht zu dieser Erklärung – danach fand sich der verschobene Nullpunkt.
„Das G92 steckt in der Druckdatei“
Ein G92 mit Z-Wert in der Druckdatei hätte genau diesen Effekt gehabt. Ein grep über die Datei hat das widerlegt: Alle 282 G92-Zeilen darin lauten G92 E0. Der Slicer setzt nur den Extruder zurück, wie er soll.
„Homing heilt den Versatz“
Das war der hartnäckigste Irrtum, weil er so selbstverständlich klingt. Homing heißt doch: Die Maschine sucht ihren Nullpunkt neu. Danach müsste jeder Versatz weg sein.
Er war es nicht. Nach einem vollen G28 stand base_z weiterhin bei 87,294. Die Maschine stand bei Z 97,294, während der G-Code von Z 10 ausging. Homing, Z-Tilt und Bed Mesh liefen alle durch, ohne zu bemerken, dass die Z-Achse des G-Codes 87 mm über dem Bett beginnt.
In meinem Aufbau setzt G28 über die Eddy-Sonde den Z-Ursprung des G-Codes nicht zurück. Ein Versatz in base_z, der vor dem Druckstart entsteht, überlebt den gesamten Startablauf.
Die Ursache
In der printer.cfg endeten beide Filament-Makros auf ein nacktes G92. Gemeint war G92 E0:
[gcode_macro LOAD_FILAMENT]
gcode:
G91
G1 E60 F1800
G1 E60 F180
G92 # gemeint war: G92 E0
[gcode_macro UNLOAD_FILAMENT]
gcode:
G91
G1 E-120 F1800
G92 # gemeint war: G92 E0Ohne Achsangabe macht G92 etwas völlig anderes als mit. In gcode_move.py (in meiner Version Zeile 189/190) steht dafür:
base_position[:4] = last_position[:4]X, Y, Z und E werden also auf die aktuelle Position genullt. Beim Filamentwechsel stand der Kopf bei Z 87,28 – und genau dort lag ab sofort die neue „Null“ für den G-Code.
Dazu kam ein zweites Detail: Beide Makros schalteten mit G91 auf relative Koordinaten und ließen das so stehen.
Die Lösung
Filament-Makros ohne G92
Beide Makros habe ich so umgebaut (Schema, die genauen Zeilen können abweichen), dass sie den G-Code-Zustand sichern, den Extruder relativ fahren und den Zustand danach wiederherstellen – ganz ohne G92. Damit bleibt auch kein G91 mehr hängen.
[gcode_macro LOAD_FILAMENT]
gcode:
SAVE_GCODE_STATE NAME=<name>
M83 # nur der Extruder relativ
G1 E60 F1800
G1 E60 F180
RESTORE_GCODE_STATE NAME=<name>
[gcode_macro UNLOAD_FILAMENT]
gcode:
SAVE_GCODE_STATE NAME=<name>
M83
G1 E-120 F1800
RESTORE_GCODE_STATE NAME=<name>Eine Schutzzeile nach dem Homing
Die Makros zu reparieren behebt diesen einen Auslöser. Es bleibt aber dabei, dass G28 einen verschobenen Z-Ursprung nicht abfängt – egal woher er kommt. Deshalb ruft START_PRINT jetzt direkt nach dem Homing ein neues Makro _Z_FRAME_RESET auf:
[gcode_macro START_PRINT]
gcode:
...
G28
_Z_FRAME_RESET
...Das Makro selbst ist kurz:
[gcode_macro _Z_FRAME_RESET]
description: Setzt gcode-Z hart auf den Homing-Nullpunkt (siehe START_PRINT)
gcode:
G90
G92 Z{printer.toolhead.position.z - printer.gcode_move.homing_origin.z}Es setzt die G-Code-Z-Position auf die echte Werkzeugposition abzüglich eines eventuell gesetzten Z-Offsets (homing_origin.z). Damit ist ein durch ein nacktes G92 verschobenes base_z wieder weg, ohne dass der Z-Offset verloren geht.
Dass das ein eigenes Makro ist und nicht ein paar Zeilen direkt in START_PRINT, hat einen Grund: Klipper rendert ein Makro komplett, bevor die erste Zeile ausgeführt wird. Mein erster Anlauf ist genau daran gescheitert. Mein Schluss daraus: Ein separates Makro wird erst beim Aufruf gerendert – also nach dem Homing.
Vor dem Umbau habe ich die alte Konfiguration als printer.cfg.vor-g92fix gesichert.
Wie ich es geprüft habe
Nach einem Neustart habe ich den Fehler absichtlich wieder herbeigeführt, um zu sehen, ob die Schutzzeile ihn wirklich abfängt:
| Schritt | base_z | Ergebnis |
|---|---|---|
| nach G28 | −0,0044 | gleich homing_z – sauber |
| Sabotage: G92 Z50 | −40,0044 | Versatz reproduziert |
| nach der neuen Schutzzeile | −0,0044 | zurück auf dem Ausgangswert |
| Endprüfung | 0,098320 | gleich homing_origin, Differenz 0 |
Der Sabotagetest ist der wichtigste Teil: Ohne ihn hätte ich nur gezeigt, dass im Normalfall nichts schiefgeht. Mit ihm ist belegt, dass die Schutzzeile einen künstlich verschobenen Nullpunkt tatsächlich zurückholt.
Der erste echte Druckstart am 31.08. setzte die erste Schicht bei Maschinen-Z 0,346 an: G-Code-Z 0,240 plus Offset 0,106. Genau da, wo sie hingehört.
Was offen bleibt
Warum das Z-Homing über die Eddy-Sonde base_z nicht zurücksetzt, habe ich im Quellcode (homing.py und safe_z_home.py) nur angerissen. Für den Drucker spielt das inzwischen keine Rolle mehr, weil _Z_FRAME_RESET den Fall abfängt. Verstanden habe ich den Mechanismus aber noch nicht.
Meine Empfehlung aus diesem Fall: Wer Filament- oder sonstige Hilfsmakros mit G92 hat, sollte nachsehen, ob dort wirklich eine Achse dahintersteht. Und nach einem G28 lohnt ein Blick auf gcode_move.base_position in der laufenden Instanz: Steht dort für Z etwas anderes als der Homing-Offset, druckt der Drucker in einem verschobenen Koordinatensystem.
Drucker: HevORT, Klipper v0.13, BTT Eddy mit G28-Wrapper aus dem BTT-Eddy-Paket.
Die Zahlen stammen aus Messungen an dieser Maschine. Das Verhalten von G28 bezüglich base_z gilt für diesen Aufbau; ob es bei anderen Sonden oder Endschaltern genauso ist, habe ich nicht geprüft.
Anzeige · Affiliate-Link – kaufst du darüber, erhalte ich ggf. eine Provision. Für dich ändert sich am Preis nichts.