Skip to content

Commit

Permalink
idex_modes: COPY and MIRROR mode implementation (Klipper3d#6297)
Browse files Browse the repository at this point in the history
COPY and MIRROR mode implementation

Correctly apply input shaper params to new dual_carriage

Added SAVE_/RESTORE_IDEX_STATE commands

Documentation updates for the new IDEX modes

Signed-off-by: Dmitry Butyugin <[email protected]>
  • Loading branch information
dmbutyugin authored Aug 1, 2023
1 parent ea33071 commit 36be1cf
Show file tree
Hide file tree
Showing 14 changed files with 458 additions and 250 deletions.
36 changes: 36 additions & 0 deletions config/sample-idex.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ endstop_pin: ^ar2
position_endstop: 200
position_max: 200
homing_speed: 50
# A minimum distance between the carriages to enforce
safe_distance: 40

[extruder1]
step_pin: ar36
Expand Down Expand Up @@ -94,3 +96,37 @@ gcode:
ACTIVATE_EXTRUDER EXTRUDER=extruder1
SET_DUAL_CARRIAGE CARRIAGE=1
SET_GCODE_OFFSET Y=15

# A helper script to activate copy mode
[gcode_macro ACTIVATE_COPY_MODE]
gcode:
SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY
G1 X0
ACTIVATE_EXTRUDER EXTRUDER=extruder
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY
G1 X100
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY
SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder

# A helper script to activate mirror mode
[gcode_macro ACTIVATE_MIRROR_MODE]
gcode:
SET_DUAL_CARRIAGE CARRIAGE=0 MODE=PRIMARY
G1 X0
ACTIVATE_EXTRUDER EXTRUDER=extruder
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=PRIMARY
G1 X200
SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR
SYNC_EXTRUDER_MOTION EXTRUDER=extruder1 MOTION_QUEUE=extruder

## An optional input shaper support
#[input_shaper]
## The section is intentionally empty
#
#[delayed_gcode init_shaper]
#initial_duration: 0.1
#gcode:
# SET_DUAL_CARRIAGE CARRIAGE=1
# SET_INPUT_SHAPER SHAPER_TYPE_X=<dual_carriage_shaper> SHAPER_FREQ_X=<dual_carriage_freq> SHAPER_TYPE_Y=<y_shaper> SHAPER_FREQ_Y=<y_freq>
# SET_DUAL_CARRIAGE CARRIAGE=0
# SET_INPUT_SHAPER SHAPER_TYPE_X=<primary_carriage_shaper> SHAPER_FREQ_X=<primary_carriage_freq> SHAPER_TYPE_Y=<y_shaper> SHAPER_FREQ_Y=<y_freq>
5 changes: 5 additions & 0 deletions docs/Config_Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ All dates in this document are approximate.

## Changes

20230729: The exported status for `dual_carriage` is changed. Instead of
exporting `mode` and `active_carriage`, the individual modes for each
carriage are exported as `printer.dual_carriage.carriage_0` and
`printer.dual_carriage.carriage_1`.

20230619: The `relative_reference_index` option has been deprecated
and superceded by the `zero_reference_position` option. Refer to the
[Bed Mesh Documentation](./Bed_Mesh.md#the-deprecated-relative_reference_index)
Expand Down
35 changes: 27 additions & 8 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2007,14 +2007,24 @@ 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
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.
Support for cartesian and hybrid_corexy/z printers with dual carriages
on a single axis. The carriage mode can be set via the
SET_DUAL_CARRIAGE extended g-code command. For example,
"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.
Additionally, one could use "SET_DUAL_CARRIAGE CARRIAGE=1 MODE=COPY" or
"SET_DUAL_CARRIAGE CARRIAGE=1 MODE=MIRROR" commands to activate either copying
or mirroring mode of the dual carriage, in which case it will follow the
motion of the carriage 0 accordingly. These commands can be used to print
two parts simultaneously - either two identical parts (in COPY mode) or
mirrored parts (in MIRROR mode). Note that COPY and MIRROR modes also require
appropriate configuration of the extruder on the dual carriage, which can
typically be achieved with
"SYNC_EXTRUDER_MOTION MOTION_QUEUE=extruder EXTRUDER=<dual_carriage_extruder>"
or a similar command.

See [sample-idex.cfg](../config/sample-idex.cfg) for an example
configuration.
Expand All @@ -2024,6 +2034,15 @@ configuration.
axis:
# The axis this extra carriage is on (either x or y). This parameter
# must be provided.
#safe_distance:
# The minimum distance (in mm) to enforce between the dual and the primary
# carriages. If a G-Code command is executed that will bring the carriages
# closer than the specified limit, such a command will be rejected with an
# error. If safe_distance is not provided, it will be inferred from
# position_min and position_max for the dual and primary carriages. If set
# to 0 (or safe_distance is unset and position_min and position_max are
# identical for the primary and dual carraiges), the carriages proximity
# checks will be disabled.
#step_pin:
#dir_pin:
#enable_pin:
Expand Down
30 changes: 27 additions & 3 deletions docs/G-Codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,9 +310,33 @@ The following command is available when the
enabled.

#### SET_DUAL_CARRIAGE
`SET_DUAL_CARRIAGE CARRIAGE=[0|1]`: This command will set the active
carriage. It is typically invoked from the activate_gcode and
deactivate_gcode fields in a multiple extruder configuration.
`SET_DUAL_CARRIAGE CARRIAGE=[0|1] [MODE=[PRIMARY|COPY|MIRROR]]`:
This command will change the mode of the specified carriage.
If no `MODE` is provided it defaults to `PRIMARY`. Setting the mode
to `PRIMARY` deactivates the other carriage and makes the specified
carriage execute subsequent G-Code commands as-is. `COPY` and `MIRROR`
modes are supported only for `CARRIAGE=1`. When set to either of these
modes, carriage 1 will then track the subsequent moves of the carriage 0
and either copy relative movements of it (in `COPY` mode) or execute them
in the opposite (mirror) direction (in `MIRROR` mode).

#### SAVE_DUAL_CARRIAGE_STATE
`SAVE_DUAL_CARRIAGE_STATE [NAME=<state_name>]`: Save the current positions
of the dual carriages and their modes. Saving and restoring DUAL_CARRIAGE
state can be useful in scripts and macros, as well as in homing routine
overrides. If NAME is provided it allows one to name the saved state
to the given string. If NAME is not provided it defaults to "default".

#### RESTORE_DUAL_CARRIAGE_STATE
`RESTORE_DUAL_CARRIAGE_STATE [NAME=<state_name>] [MOVE=[0|1] [MOVE_SPEED=<speed>]]`:
Restore the previously saved positions of the dual carriages and their modes,
unless "MOVE=0" is specified, in which case only the saved modes will be
restored, but not the positions of the carriages. If positions are being
restored and "MOVE_SPEED" is specified, then the toolhead moves will be
performed with the given speed (in mm/s); otherwise the toolhead move will
use the rail homing speed. Note that the carriages restore their positions
only over their own axis, which may be necessary to correctly restore COPY
and MIRROR mode of the dual carraige.

### [endstop_phase]

Expand Down
36 changes: 26 additions & 10 deletions docs/Resonance_Compensation.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,18 +418,34 @@ if necessary.

### Is dual carriage setup supported with input shapers?

There is no dedicated support for dual carriages with input shapers, but it does
not mean this setup will not work. One should run the tuning twice for each
of the carriages, and calculate the ringing frequencies for X and Y axes for
each of the carriages independently. Then put the values for carriage 0 into
[input_shaper] section, and change the values on the fly when changing
carriages, e.g. as a part of some macro:
Yes. In this case, one should measure the resonances twice for each carriage.
For example, if the second (dual) carriage is installed on X axis, it is
possible to set different input shapers for X axis for the primary and dual
carriages. However, the input shaper for Y axis should be the same for both
carriages (as ultimately this axis is driven by one or more stepper motors each
commanded to perform exactly the same steps). One possibility to configure
the input shaper for such setups is to keep `[input_shaper]` section empty and
additionally define a `[delayed_gcode]` section in the `printer.cfg` as follows:
```
SET_DUAL_CARRIAGE CARRIAGE=1
SET_INPUT_SHAPER SHAPER_FREQ_X=... SHAPER_FREQ_Y=...
[input_shaper]
# Intentionally empty
[delayed_gcode init_shaper]
initial_duration: 0.1
gcode:
SET_DUAL_CARRIAGE CARRIAGE=1
SET_INPUT_SHAPER SHAPER_TYPE_X=<dual_carriage_shaper> SHAPER_FREQ_X=<dual_carriage_freq> SHAPER_TYPE_Y=<y_shaper> SHAPER_FREQ_Y=<y_freq>
SET_DUAL_CARRIAGE CARRIAGE=0
SET_INPUT_SHAPER SHAPER_TYPE_X=<primary_carriage_shaper> SHAPER_FREQ_X=<primary_carriage_freq> SHAPER_TYPE_Y=<y_shaper> SHAPER_FREQ_Y=<y_freq>
```

And similarly when switching back to carriage 0.
Note that `SHAPER_TYPE_Y` and `SHAPER_FREQ_Y` should be the same in both
commands. It is also possible to put a similar snippet into the start g-code
in the slicer, however then the shaper will not be enabled until any print
is started.

Note that the input shaper only needs to be configured once. Subsequent changes
of the carriages or their modes via `SET_DUAL_CARRIAGE` command will preserve
the configured input shaper parameters.

### Does input_shaper affect print time?

Expand Down
9 changes: 5 additions & 4 deletions docs/Status_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,11 @@ The following information is available in the `toolhead` object

The following information is available in
[dual_carriage](Config_Reference.md#dual_carriage)
on a hybrid_corexy or hybrid_corexz robot
- `mode`: The current mode. Possible values are: "FULL_CONTROL"
- `active_carriage`: The current active carriage.
Possible values are: "CARRIAGE_0", "CARRIAGE_1"
on a cartesian, hybrid_corexy or hybrid_corexz robot
- `carriage_0`: The mode of the carriage 0. Possible values are:
"INACTIVE" and "PRIMARY".
- `carriage_1`: The mode of the carriage 1. Possible values are:
"INACTIVE", "PRIMARY", "COPY", and "MIRROR".

## virtual_sdcard

Expand Down
13 changes: 10 additions & 3 deletions klippy/chelper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
'pollreactor.c', 'msgblock.c', 'trdispatch.c',
'kin_cartesian.c', 'kin_corexy.c', 'kin_corexz.c', 'kin_delta.c',
'kin_deltesian.c', 'kin_polar.c', 'kin_rotary_delta.c', 'kin_winch.c',
'kin_extruder.c', 'kin_shaper.c',
'kin_extruder.c', 'kin_shaper.c', 'kin_idex.c',
]
DEST_LIB = "c_helper.so"
OTHER_FILES = [
Expand Down Expand Up @@ -101,7 +101,6 @@

defs_kin_cartesian = """
struct stepper_kinematics *cartesian_stepper_alloc(char axis);
struct stepper_kinematics *cartesian_reverse_stepper_alloc(char axis);
"""

defs_kin_corexy = """
Expand Down Expand Up @@ -153,6 +152,14 @@
struct stepper_kinematics * input_shaper_alloc(void);
"""

defs_kin_idex = """
void dual_carriage_set_sk(struct stepper_kinematics *sk
, struct stepper_kinematics *orig_sk);
int dual_carriage_set_transform(struct stepper_kinematics *sk
, char axis, double scale, double offs);
struct stepper_kinematics * dual_carriage_alloc(void);
"""

defs_serialqueue = """
#define MESSAGE_MAX 64
struct pull_queue_message {
Expand Down Expand Up @@ -211,7 +218,7 @@
defs_itersolve, defs_trapq, defs_trdispatch,
defs_kin_cartesian, defs_kin_corexy, defs_kin_corexz, defs_kin_delta,
defs_kin_deltesian, defs_kin_polar, defs_kin_rotary_delta, defs_kin_winch,
defs_kin_extruder, defs_kin_shaper,
defs_kin_extruder, defs_kin_shaper, defs_kin_idex,
]

# Update filenames to an absolute path
Expand Down
39 changes: 0 additions & 39 deletions klippy/chelper/kin_cartesian.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,42 +49,3 @@ cartesian_stepper_alloc(char axis)
}
return sk;
}

static double
cart_reverse_stepper_x_calc_position(struct stepper_kinematics *sk
, struct move *m, double move_time)
{
return -move_get_coord(m, move_time).x;
}

static double
cart_reverse_stepper_y_calc_position(struct stepper_kinematics *sk
, struct move *m, double move_time)
{
return -move_get_coord(m, move_time).y;
}

static double
cart_reverse_stepper_z_calc_position(struct stepper_kinematics *sk
, struct move *m, double move_time)
{
return -move_get_coord(m, move_time).z;
}

struct stepper_kinematics * __visible
cartesian_reverse_stepper_alloc(char axis)
{
struct stepper_kinematics *sk = malloc(sizeof(*sk));
memset(sk, 0, sizeof(*sk));
if (axis == 'x') {
sk->calc_position_cb = cart_reverse_stepper_x_calc_position;
sk->active_flags = AF_X;
} else if (axis == 'y') {
sk->calc_position_cb = cart_reverse_stepper_y_calc_position;
sk->active_flags = AF_Y;
} else if (axis == 'z') {
sk->calc_position_cb = cart_reverse_stepper_z_calc_position;
sk->active_flags = AF_Z;
}
return sk;
}
81 changes: 81 additions & 0 deletions klippy/chelper/kin_idex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Idex dual carriage kinematics
//
// Copyright (C) 2023 Dmitry Butyugin <[email protected]>
//
// This file may be distributed under the terms of the GNU GPLv3 license.

#include <stddef.h> // offsetof
#include <stdlib.h> // malloc
#include <string.h> // memset
#include "compiler.h" // __visible
#include "itersolve.h" // struct stepper_kinematics
#include "trapq.h" // struct move

#define DUMMY_T 500.0

struct dual_carriage_stepper {
struct stepper_kinematics sk;
struct stepper_kinematics *orig_sk;
struct move m;
double x_scale, x_offs, y_scale, y_offs;
};

double
dual_carriage_calc_position(struct stepper_kinematics *sk, struct move *m
, double move_time)
{
struct dual_carriage_stepper *dc = container_of(
sk, struct dual_carriage_stepper, sk);
struct coord pos = move_get_coord(m, move_time);
dc->m.start_pos.x = pos.x * dc->x_scale + dc->x_offs;
dc->m.start_pos.y = pos.y * dc->y_scale + dc->y_offs;
dc->m.start_pos.z = pos.z;
return dc->orig_sk->calc_position_cb(dc->orig_sk, &dc->m, DUMMY_T);
}

void __visible
dual_carriage_set_sk(struct stepper_kinematics *sk
, struct stepper_kinematics *orig_sk)
{
struct dual_carriage_stepper *dc = container_of(
sk, struct dual_carriage_stepper, sk);
dc->sk.calc_position_cb = dual_carriage_calc_position;
dc->sk.active_flags = orig_sk->active_flags;
dc->orig_sk = orig_sk;
}

int __visible
dual_carriage_set_transform(struct stepper_kinematics *sk, char axis
, double scale, double offs)
{
struct dual_carriage_stepper *dc = container_of(
sk, struct dual_carriage_stepper, sk);
if (axis == 'x') {
dc->x_scale = scale;
dc->x_offs = offs;
if (!scale)
dc->sk.active_flags &= ~AF_X;
else if (scale && dc->orig_sk->active_flags & AF_X)
dc->sk.active_flags |= AF_X;
return 0;
}
if (axis == 'y') {
dc->y_scale = scale;
dc->y_offs = offs;
if (!scale)
dc->sk.active_flags &= ~AF_Y;
else if (scale && dc->orig_sk->active_flags & AF_Y)
dc->sk.active_flags |= AF_Y;
return 0;
}
return -1;
}

struct stepper_kinematics * __visible
dual_carriage_alloc(void)
{
struct dual_carriage_stepper *dc = malloc(sizeof(*dc));
memset(dc, 0, sizeof(*dc));
dc->m.move_t = 2. * DUMMY_T;
return &dc->sk;
}
8 changes: 4 additions & 4 deletions klippy/chelper/kin_shaper.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis
struct input_shaper *is = container_of(sk, struct input_shaper, sk);
struct shaper_pulses *sp = axis == 'x' ? &is->sx : &is->sy;
int status = 0;
if (is->orig_sk->active_flags & (axis == 'x' ? AF_X : AF_Y))
// Ignore input shaper update if the axis is not active
if (is->orig_sk->active_flags & (axis == 'x' ? AF_X : AF_Y)) {
status = init_shaper(n, a, t, sp);
else
sp->num_pulses = 0;
shaper_note_generation_time(is);
shaper_note_generation_time(is);
}
return status;
}

Expand Down
Loading

0 comments on commit 36be1cf

Please sign in to comment.