Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: dual_carriage #4508

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 172 additions & 6 deletions config/sample-idex.cfg
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# This file contains a configuration snippet for a dual extruder
# This file contains configuration snippets for a dual extruder
# printer using dual carriages (an "IDEX" printer).

# The most common IDEX setup is to have 2 X carraiges, but 2 Y
# carraiges is also possible on a cartesian printer. You will need
# to update the macros and stepper definitions accordingly to do this.

# See docs/Config_Reference.md for a description of parameters.
# See docs/Dual_Carriage.md for further details on certain modules in
# this document.

# Definition for the primary carriage (holding the primary extruder)
[stepper_x]
Expand Down Expand Up @@ -44,13 +50,30 @@ gcode:

# Activate the primary extruder
[gcode_macro T0]
variable_offset_applied: 0
gcode:
PARK_{printer.toolhead.extruder}
{% set svv = printer.save_variables.variables %}
charlespick marked this conversation as resolved.
Show resolved Hide resolved
{% if "x" in printer.toolhead.homed_axes %} # this check ensures compatibility with Cura
PARK_{printer.toolhead.extruder}
{% endif %}
{% set fan_speed = printer["gcode_macro M106"].swap_speed %}
{% if fan_speed != -1 %}
SET_FAN_SPEED FAN=fan_extruder SPEED={fan_speed}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder what is supposed to happen to the fan speed of the other extruder? Would it make sense to really swap their speeds, so that the fan of the parked extruder get the fan speed of the previously parked extruder?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, using CURA, if you tell it you have separate tools it will set the fan speed of each manually and the variable_swap_speed will stay -1 (Because P is set) and let CURA control everything.

If using PrusaSlicer, it won't emit the P{n} so it expects the active tool's part fan to be controlled.

Currently having the part fan of the parked extruder run will cool somewhere on the bed that isn't where printing is happening. In theory this unnecessary draft could cause part warping so I set it to turn off entirely to minimize risks.
This is based on the assumption that the heatsink fan is adequate to not cause the tool to melt.

{% else %}
# Update core Klipper's fan speed to the fan speed of the active toolhead
# Only do this if you have a sacrificial [fan] section
M106.1 S{printer["fan_generic fan_extruder"].speed * 255}
{% endif %}
ACTIVATE_EXTRUDER EXTRUDER=extruder
SET_DUAL_CARRIAGE CARRIAGE=0
SET_GCODE_OFFSET Y=0
{% if printer["gcode_macro T0"].offset_applied == 1 %}
SET_GCODE_OFFSET X_ADJUST={ -(svv.xoffset) } Y_ADJUST={ -(svv.yoffset) }
SET_GCODE_OFFSET Z_ADJUST={ -(svv.zoffset) } MOVE=1
charlespick marked this conversation as resolved.
Show resolved Hide resolved
SET_GCODE_VARIABLE MACRO=T0 VARIABLE=offset_applied VALUE=0
{% endif %}
# SET_INPUT_SHAPER if nessesary, reset the input shaper after using the second extruder

# Definition for the secondary carriage and extruder1
# Definition for the secondary carriage (holding the secondary extruder)
[dual_carriage]
axis: x
step_pin: ar16
Expand All @@ -63,6 +86,7 @@ position_endstop: 200
position_max: 200
homing_speed: 50

# The definition for the secondary extruder
[extruder1]
step_pin: ar36
dir_pin: ar34
Expand Down Expand Up @@ -90,7 +114,149 @@ gcode:

[gcode_macro T1]
gcode:
PARK_{printer.toolhead.extruder}
{% set svv = printer.save_variables.variables %}
{% if "x" in printer.toolhead.homed_axes %} # this check ensures compatibility with Cura
PARK_{printer.toolhead.extruder}
{% endif %}
{% set fan_speed = printer["gcode_macro M106"].swap_speed %}
{% if fan_speed != -1 %}
SET_FAN_SPEED FAN=fan_extruder1 SPEED={fan_speed}
{% else %}
# Update core Klipper's fan speed to the fan speed of the active toolhead
# Only do this if you have a sacrificial [fan] section
M106.1 S{printer["fan_generic fan_extruder1"].speed * 255}
{% endif %}
ACTIVATE_EXTRUDER EXTRUDER=extruder1
SET_DUAL_CARRIAGE CARRIAGE=1
SET_GCODE_OFFSET Y=15
{% if printer["gcode_macro T0"].offset_applied == 0 %}
SET_GCODE_OFFSET X_ADJUST={ svv.xoffset } Y_ADJUST={ svv.yoffset }
SET_GCODE_OFFSET Z_ADJUST={ svv.zoffset } MOVE=1
SET_GCODE_VARIABLE MACRO=T0 VARIABLE=offset_applied VALUE=1
{% endif %}
# SET_INPUT_SHAPER if nessesary, update input shaping for the second carraige

# YOU MUST CALIBRATE YOUR ROTATION_DISTANCE BEFORE THIS IS USEFUL!!!
# Configure this to draw 2 line segments perpendicular to the dual_carraige axis
# that meet in the middle of the bed. Both lines should have the same coordinate
# on the axis of the dual_carraige. If they don't print inline, adjust your endstop
# settings.
[gcode_macro set_separation]
gcode:
{% set svv = printer.save_variables.variables %}

{% set oldX = svv.xoffset|float %}
{% set oldY = svv.yoffset|float %}
{% set oldZ = svv.zoffset|float %}

{% if params.X is defined %}
SAVE_VARIABLE VARIABLE=xoffset VALUE={ params.X|float }
{% endif %}

{% if params.Y is defined %}
SAVE_VARIABLE VARIABLE=yoffset VALUE={ params.Y|float }
{% endif %}

{% if params.Z is defined %}
SAVE_VARIABLE VARIABLE=zoffset VALUE={ params.Z|float }
{% endif %}

{% if params.X_ADJUST is defined %}
{% set newX = params.X_ADJUST|float + oldX %}
SAVE_VARIABLE VARIABLE=xoffset VALUE={ newX|float }
{% endif %}

{% if params.Y_ADJUST is defined %}
{% set newY = params.Y_ADJUST|float + oldY %}
SAVE_VARIABLE VARIABLE=yoffset VALUE={ newY|float }
{% endif %}

{% if params.Z_ADJUST is defined %}
{% set newZ = params.Z_ADJUST|float + oldZ %}
SAVE_VARIABLE VARIABLE=yoffset VALUE={ newZ|float }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yoffset should probably be zoffset = )

{% endif %}

[gcode_macro calibrate_separation]
gcode:
G28
G90
M83
T0 ; test T0
G1 X120 Y150 Z.2 F4800
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way the tuning guide goes is that the Z height for the hotends is calibrated (step 4) after this macro is used (step 2). And the step 4 says "To print with low layer thicknesses, you'll probably find that the error between your nozzles in the Z axis is more than your layer height itself." I'm thinking if the layer height .2 is too small at this point?

G1 Y75 E10
charlespick marked this conversation as resolved.
Show resolved Hide resolved
T1 ; test T1
G1 X120 Y0 Z.2
G1 Y75 E10

# The following is for if your carraiges have their own part cooling fans.
# Since this macro overrides M106, you cannot also have a [fan] section defined.
# If your printer only has one part cooling fan, don't copy this section
[fan_generic part_fan_0]
pin: PE5

[fan_generic part_fan_1]
pin: PD13

# For completeness, you can add a [fan] section with an unused pin
[fan]
pin: rpi:gpio20

[gcode_macro M106]
# Only rename_existing if you have a sacrificial [fan] section
rename_existing: M106.1
# The variable that controls fan speed swopping if not specifying P parameter
# -1 means the control is disabled, a value of 0-1 is the requested fan speed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be just me, I got confused a bit at first by a description of P parameter. Maybe you could say that P is the index of a fan (0-1)? *And -1 is the active extruder fan.

# Access via {printer["gcode_macro M106"].swap_speed}
variable_swap_speed: -1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this would be more like 'active_fan_speed' ? Because the active fan gets this speed. But I do not insist on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like that name convention. Ultimately @charlespick will have to alter this PR to have that change, so it's up to him.

gcode:
{% set s = [[params.S|default(255)|int, 255]|min, 0]|max %}
{% set p = params.P|default(-1)|int %}
{% set speed = s / 255 %}

# Set speed to -1 by default
SET_GCODE_VARIABLE MACRO=M106 VARIABLE=swap_speed VALUE=-1

{% if p == -1 %}
# Set current active extruder fan
{% if speed == 0 %}
# Always turn off al fans if S0 is specified without a specific fan
SET_FAN_SPEED FAN=fan_extruder SPEED=0
SET_FAN_SPEED FAN=fan_extruder1 SPEED=0
{% else %}
# Opt into fan speed swop control
SET_GCODE_VARIABLE MACRO=M106 VARIABLE=swap_speed VALUE={speed}
SET_FAN_SPEED FAN=fan_{printer.toolhead.extruder} SPEED={speed}
{% endif %}
{% else %}
# Set specified active extruder fan
{% if p == 0 %}
SET_FAN_SPEED FAN=fan_extruder SPEED={speed}
{% else %}
SET_FAN_SPEED FAN=fan_extruder1 SPEED={speed}
{% endif %}
{% endif %}

# Update core Klipper's fan speed
# Only do this if you have a sacrificial [fan] section
M106.1 S{s}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grigorig so, this is only useful for reporting?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are talking of the unused fan pin, then it also allows you to alter the fan speed for the active tool from the display menu(or a webUI).

Most of the benefit is seeing the fan speed correctly reflect on the 12864 display for me though.


[gcode_macro M107]
rename_existing: M107.1
gcode:
{% set p = params.P|default(-1)|int %}
M106 S0 P{p}

[gcode_macro M107]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is duplicate?

rename_existing: M107.1
gcode:
{% set p = params.P|default(-1)|int %}
M106 S0 P{p}

[save_variables]
filename: ~/klipper_config/variables.klip
# this is used for saving and restoring the idex offsets
# create the file and insert the folloring content (without the #) to start
#
# [Variables]
# xoffset = 0.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't try it myself, but would it be possible to use |default(0.0) at the place of their use? Or is defined. So that the user does not have to manually add this section to the save variable file. But maybe it doesn't work that way.

# yoffset = 0.0
# zoffset = 0.0
10 changes: 5 additions & 5 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1781,11 +1781,11 @@ for an example configuration.

### [dual_carriage]

Support for cartesian printers with dual carriages on a single
axis. The active carriage is set via the SET_DUAL_CARRIAGE extended
g-code command. The "SET_DUAL_CARRIAGE CARRIAGE=1" command will
activate the carriage defined in this section (CARRIAGE=0 will return
activation to the primary carriage). Dual carriage support is
Support for cartesian, hybrid-corexy, and hybrid-corexz printers with
dual carriages on a single axis. The active carriage is set via the
SET_DUAL_CARRIAGE extended g-code command. The "SET_DUAL_CARRIAGE CARRIAGE=1"
command will activate the carriage defined in this section (CARRIAGE=0
will return activation to the primary carriage). Dual carriage support is
typically combined with extra extruders - the SET_DUAL_CARRIAGE
command is often called at the same time as the ACTIVATE_EXTRUDER
command. Be sure to park the carriages during deactivation.
Expand Down
135 changes: 135 additions & 0 deletions docs/Dual_Carriage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
This document provides **general** information on configuring and
calibrating a standard IDEX printer. Klipper has support for very
complex IDEX configurations, with multiple hotends and extruders on a
single carriage, independent or grouped cooling fans, and much more,
which are not covered in this document.

# General configuration
Start with studying, and possibly using parts of the [example config](../config/sample-idex.cfg)
for IDEX printers. It has some distinct items that you may or may not
wish to use.
* Definition for `dual_carriage` on the X-axis, which you can change to
be on the Y axis if that is how your printer is configured.
* Sample macros for parking and switching toolheads. You should check
these to make sure they make sense for your setup and update the
position values with those that match your printer
* Fan configurations mentioned later.
* A macro for calibrating carriage separation described later in this
document.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add a warning that sensorless homing is not consistent enough for use on the dual carriage axis, and a standard limit switch (microswitch or optical) should be used instead.
(My custom build was problematic in this way, made calibration essentially pointless)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you tell me more? I was actually wondering earlier if senseless homing would be possible. For example you home the left extruder and then you home the right extruder into the left extruder. This would solve the endstop_phase issue I'm investigating

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried sensorless homing on my custom printer, and I just couldn't get any consistency better than ~0.2mm. which is extremely noticeable on the alignment prints. I'd get it lined up, then on the next home it would not line up anymore.

In this nice doc: https://duet3d.dozuki.com/Wiki/Stall_detection_and_sensorless_homing#Section_Limitations_of_stall_detection
Note the +-2 full step accuracy. Multiply this variance by 2 (one for each side) and you see.

Part of the inaccuracy (as I understand it) is that the EM feedback changes depending on motor coil heat. So cold motors, once heated up, seems to home less sensitively.

I suppose some motors/setup might achieve acceptable accuracy, but my repurposed A8 motors have a very high inductance, and is more prone to variations. (as I understand it)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh have them all home against each other? Hmm, could work...
But I have started crimping wires for microswitch endstops, so will probably use those with phase assistance.

# Tuning
If you want to use both extruders to print different features of a
single object, Klipper will need to know the positions of the extruders
relative to each other.

Because Klipper configures this by the endstop position, you will
need to calibrate your [rotation distance](Rotation_Distance.md) first.

For the finest accuracy, you should consider calibrating the homing
[endstop phase](endstop_phase.md) of your dual_carraige axis motors.

## Step 1 (~5mm)
Start by literally eyeballing it. Put a piece of tape on your bed or
use a line or other graphic already on your bed as a reference point.

Move the first carriage to this point and note it's position. Then,
park carriage 1, and move carriage 2 to the same physical position.
Note it's position (where Klipper "thinks" it is) and calculate the
difference.

In a standard setup where the carriages home to the sides with the
primary on the left, you move the logical coordinate system for the
secondary extruder to the right by lowering it's `position_endstop`
value.

Run `SET_SEPARATION Y={your calculated value}` to set the Y offset.

Once you are within a few mm of accuracy, proceed to step 2

## Step 2 (~1mm)
Configure the `calibrate_separation` macro in [the sample config](../config/sample-idex.cfg)
for your bed size and dual_carriage axis, and add heating commands to
it for the filament you'll be using.

Load filament in both extruders, and run the macro.

The test with the standard axis configuration should look as follows:

![measuring print](img/separation-lines.png)

Note the lines should be colinear. If they are not, adjust your
`position_endstop` setting as noted in step 1. Once they are visually
aligned, proceed to step 3.

At this point you should re-tune your mins and maxes for each axis when
the other is parked by issuing slow, small movements until they
collide.

## Step 3 (~.1 - .025mm)
While the measuring process of this step accounts for symmetric
horizontal expansion, you will likely achieve better results by
calibrating the flow, temperature, and pressure advance for the
filament you'll be using first. You'll also do better with a filament
that is known to have better dimensional stability. Look for filaments
that can be printed without a heated bed.

Use a slicer to generate g-code for the multi-part print found in
[docs/prints/calibrate_idex.stl](prints/calibrate_idex.stl).
Align the long direction of the print perpendicular to the axis of your
`dual_carriage`. Configure your slicer to print the upper part using
the right extruder. I highly recommend using identical filaments printed with
the same temperature, etc. Use a rather coarse layer height since you
have not yet calibrated your Z offset. Print the object.

Use calipers or better, a micrometer to measure the 2 measureing points
(refer to the image below). Complete the following table:

| Part | Measurement | Calculation |
|:--:|:--:|:--:|
| Front | (mm) | 20 - (mm) |
| Back | (mm) | (mm) - 20 |
| Offset | | ^ Average ^ |

![measuring print](img/separation-block.png)

You should be left with the adjustment that should be made to compensate
for any alignment error. Save this to the printer by running:
```
SET_SEPARATION X_ADJUST={your calculated value}
```

Repeat this step if nessesary until you are happy with the alignment. Rotate
the object 90 degrees and repeat with the Y axis.

## Step 4 (required for high quality printing)
To print with low layer thicknesses, you'll probably find that the error
between your nozzles in the Z axis is more than your layer height itself.

Start with a piece of paper and switch between the extruders until the pressure
feels roughly the same on both extruders. Make sure you're testing the exact
same point on your bed.

Prepare to print a square about 10x10x5mm. Slice the object twice with the only
difference being which extruder is used. MAKE SURE IT IS IN THE SAME PLACE ON YOUR BED.

Use calipers or better, a micrometer to measure the height of the two samples and
adjust the Z offset as nessesary.

# Next steps
Configure a homing override to make sure that the inactive carriage is
fully parked in all scenarios.

If you want to use [input shaping](Resonance_compensation.md),
calibrate the input shaper for each carriage separately and add g-code
to your `T0` and `T1` etc commands to configure the input shaper when
the active carriage changes.

For support with Fluidd, SuperSlicer, consider overriding the `ACTIVATE_EXTRUDER`
command to run T0, T1.

If your printer has multiple part cooling fans, create `[fan_generic]` sections
for each of them. For support with Klipper's default LCD resdout, consider using the
`[fan]` section and macro elements in the sample. Otherwise, write your own M106
and M107 macros.

Print a few objects to ensure everything is working as expected!
2 changes: 2 additions & 0 deletions docs/Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ communication with the Klipper developers.
perfectly square.
- [PWM tools](Using_PWM_Tools.md): Guide on how to use PWM controlled
tools such as lasers or spindles.
- [IDEX printers](Dual_Carriage.md): Guide on setting up and calibrating
an IDEX printer.

## Developer Documentation

Expand Down
Binary file added docs/img/separation-block.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/separation-lines.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/prints/calibrate_idex.stl
Binary file not shown.