Bug: extruder [re]activation applies the incorrect coordinate system

Hello everyone, I’m playing around with absolute extrusion mode with multiple extruders and found something unexpected.

  1. Start by choosing T0, setting absolute mode, and move to E position 10.
    • The first extruder stepper moves to +10.
  2. Choose T1, set absolute mode, move to E position 10.
    • The second extruder stepper moves to +10.
  3. Choose T0 again, set absolute mode, move to E position 10.
    • The first extruder stepper moves 10 units further to +20, even though it already was at E=10.

It looks unexpected/buggy.

GCODE to reproduce:

G0 E10

G0 E10

G0 E10 ; should not move but it does!

It would be great if someone tried to reproduce this behavior and report back. :slight_smile:


I would recommend to stick to relative extrusion.

You will have way less problems.

Thanks Eddy, I’ve done just that for the moment.

It would be nice, however, to get it working as intended.

Would you happen to know what parts of the code may be causing the issue?

Please share your klippy.log

Hi, sure: klippy.log (32.3 KB)

The procedure was:

  1. Open mainsail.
  2. Reset Klippy.
  3. Run the test GCODE above.
  4. Download the log.

Hi everyone! Were there any leads you could spot in the log?

It would be great to have some insight before yet another spelunking session on klippy’s code. :sweat_smile:

It is strange indeed. Maybe this is intended behavior but would need developer confirmation. See klipper/gcode_move.py at 0b918b357cb0c282d53cbdf59e1931a2054cd60a · Klipper3d/klipper · GitHub

Which indeed would make kind of sense. If you switch extruder and print with T1 then you would not want to return to T0 with some unknown E step offset.

Thanks Sineos. I’m trying to figure out that line too.

It seems erroneous that the extruder base position (its frame of reference) should be overwritten and set to the last position of the same extruder. This makes “absolute” moves relative to the position before the tool-change.

What I just described would happen to T0 after switching to T1 and back, without T1 moving at all.

This behavior is certainly due to a bug, evidenced by this reduced test code:

T0 ; Change to T0, the default (optional).
M82 ; Set absolute coordinates for extrusion moves.
G0 E10 ; Move to E=10.

T1 ; Switch to T1.
T0 ; Switch back to T0.

G0 E10 ; Move to E=10.
M82 ; Set absolute coordinates for extrusion moves (optional).
; T0 should not move because it is already at E=10, but it does!

PS: For anyone else reading, the “base position” is used to calculate the final coordinate of an absolute move, by adding it to the E parameter in the GCODE command (relevant code here).

I’m not sure I understand that part. I expected base coordinates to be known, stored for each extruder, and restored on tool-change.

To which use case could that apply?


Found the commit of the problematic lines after some digging: gcode: Reset extrude_factor and extruder position on a tool change · Klipper3d/klipper@61fbd19 · GitHub

The commit message from May 20, 2018 states:

gcode: Reset extrude_factor and extruder position on a tool change

The extrude_factor and extruder position are specific to the current extruder, so reset them to default values on a Tn command.

It does not look like base position is reset to the default value of 0, as intended by the commit.

The present effect of that line is to instead set the base position to the last position of the activated extruder, which breaks the origin coordinate for absolute extruder moves.

The nearby call to reset_last_position is not obvious either (commit here), but it is responsible for setting last_position[3] to the position of the new extruder, which is then used to overwrite base_position[3] (but only if the printer is ready, otherwise the value from a previous extruder will be used).

I also found other “uses” of base_position[3]. The M221 command seems to rely on manipulating the extruder’s origin somehow, to implement the extrusion factor feature. Commit here.

I’m starting to understand why.

This is now too hairy, perhaps @koconnor will know how to proceed.

1 Like

My reasoning is:

  • You print some Millimeters in green (T0)
  • You switch to red (T1) and print some millimeters
  • Now back to green: You would not want to start with a stored E-steps offset, regardless if you are using absolute or relative extrusion

I’d expect to start with the stored coordinate system and last position of the tool being picked-up.

For an (M82) absolute extrusion move to work predictably across a GCODE program, the coordinate origin of the extruder axis must remain fixed; as for any stepper axis.

In the current implementation, the origin is made relative to the last move (i.e. before the T0/T1/T0 sequence). This seems contradictory, and makes absolute M82 moves work in the way that relative M83 would be expected to.

This is shown clearly by the second GCODE sample above. It achieves relative motion style using only M82 G0 E10 commands between tool-changes, and no motion with M82 G0 E10 + M82 G0 E0, which should be impossible.

I do indeed think that this line is essentially doing a G92 E0 just after switching extruders.

I’m not familiar with tool changing in klipper, so I took your liquid handler for a ride :sweat_smile:
I compared the gcode positions after each instructions in your test gocde test gcode, for these conditions:

  1. Vanillia Klipper,
  2. Klipper with the line commented out,
  3. Same as above but with a G92 E0 executed after each tool change.

test_files.zip (11.7 KB)

  1. and 3. have this final gcode state:
gcode: X:0.000000 Y:0.000000 Z:0.000000 E:20.000000
gcode base: X:0.000000 Y:0.000000 Z:0.000000 E:10.000000

That offset is added by the tool change, or simulated with G92 E0.

And 2., without the offset as @naikymen expects:

gcode: X:0.000000 Y:0.000000 Z:0.000000 E:10.000000
gcode base: X:0.000000 Y:0.000000 Z:0.000000 E:0.000000

I wouldn’t expect this offset when switching tool. It doesn’t restore the original extruder position, but resets it to 0 and adds what was already extruded to the offset.
This is just my observation, there could be implications for relative extrusion or other configurations that I am not familiar with.

1 Like

Thanks a lot Piezo :slight_smile: I just tested your code and reproduced the same result.

Knocking-out line 73 seems like a good quick fix.

The documentation lists the commands that affect the coordinate systems. Activate extruder is not there, but M221 is. I’ve been looking at the definition of M221 (code here) but can’t figure out why it relies on changing the base position.

I can’t tell if removing L73 messes up the expected behavior of M221.

As an alternative, I hoped that using SET_GCODE_OFFSET E=0 after the tool change would remove the base_position position offset.
But it doesn’t. Instead this does something like base_position[3] += offset - self.homing_position[3] which is a no-op.

Implementing M112 seems difficult with absolute extruder positioning, the current E position can’t be simply scaled. That could explain the complexity we are seeing here.

When it comes to old G-Code commands, the goal in Klipper is to support the G-Code commands produced by common 3rd party software (eg, OctoPrint, Printrun, Slic3r, Cura, etc.) in their standard configurations (as mentioned at G-Codes - Klipper documentation ).

FWIW, the old G-Code commands are maddeningly cryptic and I try my best to avoid spending any time trying to decipher them.

To the best of my knowledge, the Klipper code works with the popular slicer programs (which to the best of my knowledge effectively issue a “g92 e0” when changing tools).


Thanks for the input Kevin, I hope the info makes it into the docs.

Do you think disabling the problematic line will break M221? (or if doing so while using M221 will break absolute moves?)


I don’t know. It’s not something I’ve tested.

If you’re concerned about the position being reset between toolhead changes, you could manually save and restore it. The position prior to the toolhead change is in printer.gcode_move.base_position.e and it can be restored with a G92 command (something like G92 E{-old_e_base_position})