A follow-up from the thread in the PR regarding inverted hybrid corexy kinematics. As per discussion there I went ahead and implemented a new generic_cartesian
kinematics class (code available here). This new kinematics class allows one to define in a pretty flexible manner an arbitrary Cartesian-style kinematics. In principle, the regular cartesian, corexy, hybrid_corexy can be defined that way, but more importantly, previously unsupported kinematics such as inverted hybrid_corexy can now be supported.
Edit: I updated the first post in the thread to reflect the currently implemented proposal. The original proposal can be read here:
Original proposal
The configuration can go like this:
[printer]
kinematics: generic_cartesian
...
[rail_x]
position_endstop: 0
position_max: 300
homing_speed: 50
endstop_pin: ...
[rail_y]
position_endstop: 0
position_max: 200
homing_speed: 50
endstop_pin: ...
[rail_z]
position_endstop: 200
position_max: 200
endstop_pin: ...
[kinematic_stepper b]
kinematics: x+y
step_pin: ...
dir_pin: ...
enable_pin: !...
microsteps: 16
rotation_distance: 40
[kinematic_stepper y]
kinematics: y
step_pin: ...
dir_pin: ...
enable_pin: !...
microsteps: 16
rotation_distance: 40
[kinematic_stepper z]
kinematics: z
...
[kinematic_stepper z1]
kinematics: z
...
Basically, you should specify kinematics: generic_cartesian
in [printer]
section, define each of [rail_x]
, [rail_y]
and [rail_z]
(some of the configuration options were moved there from the regular [stepper_?]
section (which is not used in this kinematics), and define some number of[kinematic_stepper ...]
sections, one per physical stepper. kinematic_stepper
names can be pretty much arbitrary. Besides the regular configuration options related to stepper pins and rotation_distance/microsteps, you must define an appropriate kinematics
option, which can be a simple expression containing some of x
, y
or z
axes joined with ā+ā or ā-ā into an expression (in principle, floating point coefficients are also supported, e.g. kinematics: y+0.5*z
, but Iām not sure if thereās any practical use for that). You must, of course, define the steppers such that the motion of all printer axes is possible (and Klipper will validate that and give you an error if thatās not the case).
IDEX configurations are also supported. For that, you define an extra ārailā as
[dual_carriage]
axis: x # IDEX axis, can be x or y
safe_distance: 60 # can also be omitted
position_endstop: 300
position_max: 300
homing_speed: 50
endstop_pin: ...
Then, for the steppers that are corresponding to rail_x
(dual_carriage is on the x axis) and dual_carriage
you must provide carriage: ...
option, e.g.
[kinematic_stepper b]
kinematics: x+y
carriage: rail_x
step_pin: ...
...
[kinematic_stepper a]
kinematics: x-y
carriage: dual_carriage
step_pin: ...
...
At least one stepper for each of the two carriages must be defined, but in principle you can define more than 1 stepper for carriage: rail_x
and carriage: dual_carriage
(and they donāt have to share the same kinematics), which can be useful for some AWD IDEX setups.
Now, thereās one more point that gets tricky: homing and endstops. Homing is done as usual per axis and towards the configured position_endstop
for each rail. Normally, you can define an endstop_pin
for each [rail_?]
. However, you may want to have an endstop per stepper instead. This can be useful, for example, on a multi-stepper Z axis, or when Y axis is driven by 2 stepper motors on each side of the Y beam. In such cases, you can omit endstop_pin
in the corresponding rail_?
section and specify it in all relevant kinematic_stepper
sections instead. But for that to work, Klipper must know which rail the stepper belongs to. If the stepper kinematics is obvious (specifies only a single axis, e.g. kinematics: z
), then nothing extra needs to be done. Otherwise, you have to provide carriage
option. E.g. consider the following kinematics configuration with the correct declaration of endstop pins (this is just for illustration assuming Y axis is driven by two steppers with corexy-style kinematics mapping, I do not know any real printer that would use it):
[kinematic_stepper x]
kinematics: x
# carriage mapping is obvious from kinematics
endstop_pin: ...
[kinematic_stepper a]
kinematics: x-y
# carriage mapping is not obvious and must be explicitly set for endstop_pin to work
carriage: rail_y
endstop_pin: ...
[kinematic_stepper b]
kinematics: x+y
# carriage mapping is not obvious and must be explicitly set for endstop_pin to work
carriage: rail_y
endstop_pin: ...
One small gotcha in the current implementation is that if you have a rail with multiple steppers and endstops, and there are other steppers kinematically connected with that rail, then when homing, the steppers that are mapped to that rail will stop each on its own endstops, however the other kinematically connected steppers will stop on the first endstop declared for that rail. I believe the currently supported kinematics in Klipper would exhibit the same behavior, but since they are not much flexible, the odds of running into the printer setup that requires some different behavior are next to none. Iām also not sure whether this can be a problem for generic_cartesian
kinematics. But if there are good suggestions on how to conveniently express in the configuration endstop_pin(s) for the pairs rail
/kinematic_stepper
, I can consider that.
For a more complete and elaborate config, you can check this test configuration.
Now, the current proposal for configuration goes like this:
[printer]
kinematics: generic_cartesian
...
[carriage x]
position_endstop: 0
position_max: 300
homing_speed: 50
endstop_pin: ...
[carriage y]
position_endstop: 0
position_max: 200
homing_speed: 50
endstop_pin: ...
[carriage z]
position_endstop: 200
position_max: 200
endstop_pin: ...
[stepper stepper_b]
kinematics: x+y
step_pin: ...
dir_pin: ...
enable_pin: !...
microsteps: 16
rotation_distance: 40
[stepper stepper_y]
kinematics: y
step_pin: ...
dir_pin: ...
enable_pin: !...
microsteps: 16
rotation_distance: 40
[stepper stepper_z]
kinematics: z
...
Basically, you should specify kinematics: generic_cartesian
in [printer]
section, define each of [carriage x]
, [carriage y]
, and [carriage z]
(some of the configuration options were moved there from the regular [stepper_?]
section (which is not used in this kinematics), and define some number of [stepper ...]
sections, one per physical stepper. stepper
names can be pretty much arbitrary. Besides the regular configuration options related to stepper pins and rotation_distance/microsteps, you must define an appropriate kinematics
option, which can be a simple expression containing some of the carriages names (e.g. x
, y
or z
) joined with ā+ā or ā-ā into an expression (in principle, floating point coefficients are also supported, e.g. kinematics: y+0.5*z
, but Iām not sure if thereās any practical use for that). You must, of course, define the steppers such that the motion of all printer axes is possible (and Klipper will validate that and give you an error if thatās not the case).
Then, if a user wants to have an additional endstop on the axis, they will need to define, e.g.
[extra_carriage z1]
primary_carriage: z
endstop_pin: ...
[stepper stepper_z1]
kinematics: z1
...
Note that, unlike main 3 carriages, the names for extra_carriage
are not forced (they only must be distinct from other carriages). And each of the defined extra_carriage
must be referenced by at least one stepper
in the kinematics
.
IDEX configurations are also supported. They can be defined as follows using a dual_carriage
:
[carriage x]
position_endstop: 0
position_max: 300
endstop_pin: ...
...
[dual_carriage u]
primary_carriage: x # defines the corresponding IDEX axis and primary carriage
position_endstop: 300
position_max: 300
endstop_pin: ...
...
[stepper a]
kinematics: x-y
...
[stepper b]
kinematic: u+y
...
and, if needed (e.g. for AWD setups), extra steppers can be defined as, e.g.
[stepper c]
kinematics: x+y
...
[stepper d]
kinematic: u-y
...
In principle, additional extra_carriage
(s) can be defined for both primary (carriage x
in this case) and dual carriages with corresponding steppers, but I donāt think thereās going to be a need for that.
Also worth noting that IDEX code has been reworked to support CoreXYUV to a degree. It can be defined as
CoreXYUV
[carriage x]
...
[carriage y]
...
[dual_carriage u]
primary_carriage: x
...
[dual_carriage v]
primary_carriage: y
...
[stepper a]
kinematics: x+y
...
[stepper b]
kinematics: u-v
...
[stepper c]
kinematics: x-y
...
[stepper d]
kinematics: u+v
...
The more complete example config can be found here.
From the API perspective, if two dual_carriage
(s) are defined, SET_DUAL_CARRIAGE
command will support a new parameter AXIS
as SET_DUAL_CARRIAGE AXIS=<axis> CARRIAGE=[0|1] [MODE=<mode>]
. AXIS
can be omitted if MODE=PRIMARY
(which is also the default if MODE
is not specified). And then for gcode templates dual_carriage
module will now export not a single mode value per carriage, but an array of length 2 - a carriage mode per axis, which can be access as, e.g. {% if printer.dual_carriage.carriage_1[0] == 'INACTIVE' %}
in a macro (this is for X axis == 0).
So, If you are interested in this and want Klipper to get the support of generic cartesian kinematics, please give the code a try and share your feedback. As usual with any new configuration and kinematics: please exercise an extreme caution when doing initial testing and be ready to issue an emergency shutdown if something goes wrong. I did test the code on my IDEX printer, but it has simple cartesian kinematics at the end of the day. So while the code should work, I could not test everything, and bugs are possible. Separately, Iām also open to the suggestions on how to change the configuration. And once this discussion converges to something, Iāll prepare a proper documentation for this kinematics.