Independant acceleration limits for X and Y axes


I’ve been using different accelerations and velocities limits for X and Y on my E3Pro for the past two years. Now that the code has been tested a bit by the community, I decided to share it here.


Here is the code:
To download: click “Raw” then “Save As” the page. It is implemented as a kinematic implementation file. This way, you can simply put the file under klippy/kinematics/[1] and change your kinematics to kinematics: limited_cartesian. In order to activate the changes, you must restart the klipper process: sudo systemctl restart klipper or reboot. *RESTART gcodes are not enough.

The syntax for the axis limits are the same that the ones for Z, max_x_accel, max_x_velocity, etc.
Note that X and Y always indicate the cartesian axes even on CoreXY: perpendicular and parallel to the gantry, respectively.

Important notes:

  • The resonance tester for shaper calibration expects being able to raise the acceleration limit. It does this using M204, which means that moves for the vibration test continue to be limited by the axes limitations of these kinematics modules. This lowers the resonance amplitudes at high frequencies. This patch fixes this issue.
  • Square corner velocity will be reduced on axis-limited moves by a factor of √(limited accel / commanded accel). This can be fixed using this patch.
  • See the quality warning at the bottom of this post.

The work-peraxis branch contains the kinematics/ files and both patches mentioned above.

Cartesian printers

For optimal speedups, the primary max_accel and max_velocity should be set equal to or higher than the result of the square root of the sum of their axis configuration values, √(x² + y²). A new gcode command SET_KINEMATICS_LIMIT has been introduced, which displays these values and the angle at which they are achieved. If not set accordingly, the acceleration will be capped at the maximums when they are reached. The next figure demonstrate how max_accel abd `max_[xy]_accel`` interact with each other.


If the slicer lowers the acceleration by utilizing the M204 gcode command to adjust max_accel, the circle in the above figure may no longer intersect with the horizontal and vertical axis limits, resulting in the same acceleration being applied in all directions. This typically leads to printed features having constant acceleration, which is beneficial for maintaining extrusion consistency.

However, if you have previously fine-tuned the axis accelerations to achieve similar resonance amplitudes and smoothing in X and Y, you may want to preserve that X/Y ratio at all times by enabling scale_xy_accel: true. This will adjust the X and Y acceleration limits by scaling M204's acceleration / √(ax² + ay²).

For example, if you have max_x_accel: 12000 and max_y_accel: 9000 in your config, limited_cartesian will limit the acceleration to max possible acceleration = 15k on 37° diagonals.
When the slicer sets acceleration of 5000 mm/s² (M204 S5000), effective max_x_accel and max_y_accel values will be the third of their config values.


The corexy version is tested as much as the cartesian version, but it should work in principle.

Unlike Cartesian printers which may have varying motor specifications for the X and Y axes, CoreXY machines use the same steppers for both axes and thus do not have any velocity restrictions per axis. However, it is still beneficial to have a lower acceleration for the Y axis as it moves the heavier gantry. Acceleration and velocity for diagonal movements are reduced to align with the available torque and speed of the recruited stepper(s).

This time, SET_KINEMATICS_LIMIT reports the minimum acceleration and its angle. The maximum, usually aligned with the X axis, is set by max_x_accel.


klipper_estimator should support the limited_cartesian settings out of the box, with the exception of scale_xy_accel.

Quality warning

Note that there are print quality concerns with anisotropic accelerations and velocities. Extruder pressure and lengths of accelerating segments will depends on the orientation. In theory this could lead to dimensional discrepancies. This was the main argument for rejecting previous similar PRs.

Thus, user reports of any kind are welcome!

  1. This is intended to help people with little knowledge of git. More advanced users may cherry-pick the commit or merge the branch and set the correct pull policy to follow upstream. ↩︎


Interesting, thanks for sharing.

Does this also allow for higher speed and acceleration depending on the movement angle of the toolhead on CoreXY printers? I.e., when the toolhead moves at 90 degrees, both motors are running, when the toolhead moves at 45 degrees, only one motor is running. Thus, 90 degrees movements should allow higher acceleration and speed, or?


This plot shows the speed of the motors (yellow and blue lines) as a distance from the center:

At 45°, only the motor A is running at twice the toolhead velocity. At 90, A and B are moving their belts at the speed of the toolhead. Assuming that the enclosing circle represents the limit of the motors, the printer is only reaching for their max velocity on the 45° diagonals.

The thick purple line represents the toolhead velocity when hitting max speed. It is drawn as a circle because the velocity is the same in every direction. Now if we apply the limitations of limited_corexy, we instead get:

The max velocity is more limited at 45° than at 90°. But now, when the toolhead reaches maximum velocity, there is always at least one motor working at it’s maximum rate.

Cartesian kinematics are the same story, but rotated by 45° and with the square being circumscribed instead of inscribed.

1 Like

Awesome, thanks for the explanation :slight_smile:

I tested a little and it looks like it does not correctly compute CoreXY for a non-square bed.

For example:
diagonal one motor move gives me MAX 16k acceleration and 500mm, so I stick with 12kA as a stable limit.

Before testing your code, I test perpendicular motion (I mean both motors work simultaneously), and get 40kA limits ~ (just to know)

But really working is:

// x,y max_accels: [14500.0, 21000.0, 250.0]
// Per axis accelerations limits are independent of current acceleration.
// Minimum XY acceleration of 11932 mm/s^2 reached on 35 degrees diagonals.

And error happens at degree calculation IMHO, the degree will always be 45°, even with bed like mine 316x348

Anyway thanks, awesome job!

1 Like

Thank you for testing the CoreXY module! Right know it needs more testing so your input is appreciated.

The bed size should not matter at all in the computations of acceleration. Maybe we don’t use the same notion of diagonals. I don’t mean bed diagonals. It’s always 45° or, more often, the angle where the acceleration is minimal, reported by SET_KINEMATICS_LIMIT. Angle are measured from the X axis (anticlockwise but this doesn’t matters thank to the symmetries).

I think that may be the issue: the perpendicular limits that you have measured are the ones that should be set as scale_x/y_accel or with SET_KINEMATICS_LIMIT. Here X and Y indicate the cartesian axes aligned perpendicular and parallel to the gantry. As I don’t have a CoreXY, I forgot that the stepper are called stepper_x/y rather than stepper_a/b, which might be a source of confusion.

Usually, the movements aligned with Y axis need to move a bigger mass and thus have less available acceleration compared to movements parallel to the gantry.
This leads to a graph of acceleration like this one:

The thick purple line is the acceleration limit applied by limited_corexy. When moving from a pure X move (horizontal 180°->0°) toward a move with a Y component (toward the vertical), the acceleration diminished for two reason: the load is applied more to one stepper instead of being shared by both, and the moving mass increases.

I recommend measuring the max acceleration in the other direction. I guess that it would be Y, as it’s likely that max_x_accel: 40000 given that 40k >> 16k. Then set both values with X/Y_ACCEL or as scale_x/y_accel in the config.

For example this config:

max_x_accel: 40000
max_y_accel: 15800

would limit the accelerations on the cartesian X/Y axes as specified, to 16K on the 45° diagonals, and to 14.7k on 68° diagonals (where minimum occurs).

All of it is based on the theoretical model of masses moved by a ideal CoreXY and has not really been tested in practice. It’s possible that other factors (friction, rattling, etc) affect the limits for some moves.

1 Like

Oh, got it

You compute the angle based on acceleration, but acceleration limit is always one motor.

I can’t set 40kA on one axis, because on 45° moves that will make one motor to lose steps.
Because as i already said, one motor can handle only up to 16k at 500 toolhead (700mm/s motor) (in my case), and 12-13k is safe limit for my system.

So, feel like i must set both axis to same value to make it work as expected
(same about speed limits, one motor is always a limit and with some value on some shifted degree are difficult to understand for control limits)
(It also will be cool to see current limits for 45° for both speed/acceleration)

(you can take a look at VZBot youtube channel, he is currently most loud influencer in core XY speed limits :smiley: )

About x/y, it doesn’t matter of course but in my case Y is the head and X is the portal :slight_smile:

I seen you has been updated your code, will grab new one :smiley:

I don’t understand your reasoning. You are using kinematics: limited_corexy and not limited_cartesian, right?
The code is supposed to reduce the acceleration on diagonals, taking into account the fact more load is put on a single motor. The config parameters are given for the case where both motor work together while moving either the toolhead or gantry+toolhead. Then it is adapted for different intermediary angles.

Measuring the acceleration of a single motor is the way to go with the original klipper limiting. But for applying higher acceleration when both motors are working together, like this code does, the measurement at 45° is not enough to take into account the change of moving mass: the toolhead alone can be accelerated more than toolhead+gantry. That is why the maximum acceleration are specified in non rotated cartesian space and the value at 45° is derived from it.

I attempted to explain the derivation of the formulas here. Note that the acceleration limit is first defined in terms of available motor torque (or rather, forces on the belts) and masses of toolhead and gantry+toolhead. It is only in the last part that the ratios of max available force over mass is substituted with the max acceleration on one axis.

then something like this should get you close to the limit you measured:

max_x_accel: 15800
max_y_accel: 40000

45° moves will be limited to 16K.

Yes, it’s mostly about how the reference acceleration for scale_xy_accel is computed. Previously it was the max_accel value from the config, now it’s the maximum acceleration reachable by the kinematics’ limit (hypot(x, y) for cartesian, max(x, y) for corexy)

1 Like

Right, i use limited CoreXY, but you are expecting that mass will be a limit, but with such numbers like 700 mm/s, 16kA & etc, it slighter more problems to have like back emf on motors and loose of torque.
And loose of torque and back EMF are not linear and depends on much more external parameters.

So as example i have 4mH 0.9 motors, and they works good at my current 26.5V and 1.1A, i just can’t get more from them and my drivers.
(Of course with 0.9 motors i will have a higher acceleration limits in theory because of low inductance).

And yep, i understood your confusion,
but on my most heavy axis (X) i can get 40kA, and on other >40kA, and just not test lighter axis farther because of it is already too much and create problem at 45°.

I just cant get above 16000 * sqrt(2) = 22620 A per axis any way, because if any other value i will have an overload at 45° with current motors and masses.

So i stick with 18384 (13000 at 45°) at and it will work.

I read your docs and slightly understood mathematical side of it in “vacuum”.
(sorry, i really mean that you are cool, but in real world that will slightly more complex things

But in practice i always must test 45° setup, and have some safe indent from limits to have it works reliable.

And it is confusing, but not surprise after some testing that at degree != 45° we will see different performance, because both motors works together.

With macros from VZBot which has error and not handle correctly non square bed (that the reason why i initially think that you compute something from bed size and not check sources, sorry)

Test has running at 47.1°, 42.9°. Because of size of bed 316x340.
And i can reach 19kA which from my feelings has a bad fit in your model. And at same time i can reach 40kA at 90°.

Thanks! example of my back emf

And another one for 90° (so 500mm/s)
(can’t add more images because of limits)
Approximate peak back EMF due to rotation per motor: 18.1 V at 500.0 mm/s
Approximate peak back EMF due to inductance per motor: 48.9 V at 500.0 mm/s

FYI, if both X and Y acceleration limits are set to 18384, the acceleration will be limited to 6499 12K at 45°: accel / (2 * sqrt(2)) accel / sqrt(2).

1 Like

// x,y max_accels: [18384.0, 18384.0, 250.0]
// Per axis accelerations limits are independent of current acceleration.
// Minimum XY acceleration of 12999 mm/s^2 reached on 45 degrees diagonals.

Some one are wrong, and i think you are just tired of me :smiley:

Any way, your code works, can we expect from you in near future, that you will trying to merge it in main klipper branch?

Ah right, I used the wrong value for cos(45), 0.7 not 1.41. My bad it’s late here :sweat_smile:

I still don’t understand why you don’t want higher acceleration for your Y accel, where only the toolhead is accelerated. I can add the display of the accel limit at 45° to check against empirical values: sqrt(2) * a_x * a_y / (a_x + a_y).

I recommend to stay under velocity of the pull out in the torque curve, with a safe margin if you want to get close to the acceleration limits. Again limited_corexy will ensure that the velocity of the belts never exceed max_velocity, by apllying a slow down of 70% at 45°. Actually, I think this means that you can use the max velocity from the cartesian mode of the EMI calculator.
Unlike acceleration, the velocity reachable on each axis, is not influenced by the moving mass, so there is no settings per axis.

Kevin expressed some concerns about non uniform acceleration when similar changes were proposed. Someone will have to do a lot of testing, taking pictures and measuring improvements in print time. I think my time is better spent making this changes more accessible to other than doing all of it alone.

1 Like

Just for the history: benchy boat race 13 min - YouTube
So the CoreXY code works any way :slight_smile:

1 Like

Great work! Is there a reason it’s not submitted as PR/upstreamed?

@Piezo we would be very grateful if you open a PR into klipper with limited_cartesian, it is very useful!

Hi I am a new and non clever (coding) person!

I have been using this feature for a few months now. I assume its working (on a bed slinger with very heavy large bed) - is it ‘normal’ that the acceleration shown in Fluidd / Mainsail is still at 1500? If i run SET_KINEMATICS_LIMIT it shows the current x-y limits - so i can assume its operational… but the 1500 on the gui keeps throwing me :slight_smile:


To print at the X/Y limits you set, you might have to increase that acceleration.

The acceleration setting displayed in the klipper frontends can be understood as a “requested acceleration”. It’s initialized with the config’s max_accel value, and can be set by the slicer via M204 S[accel] or any macro that calls SET_VELOCITY_LIMIT.

My code will reduce that requested acceleration if the acceleration needed on one axis is greater than the corresponding max_[x/y/z]_accel settings.
Here’s a quick diagram to explain this:

Vectors represent move accelerations and orientation: the arrow length is the acceleration and its orientation is identical to the move orientation in the X/Y plane.
Original klipper always apply an acceleration on the red dashed circle: all moves are treated the same no matter their orientation. The bright red vector is an example of this.
Now, the two limits I added are the horizontal and vertical red lines. The bright green vector is an example of the move being limited by the Y axis acceleration limit.

If max_accel (or the value set via the slicer, or frontend) is lower than the diagonal length of the recangle max_x_accel x max_y_accel, you’ll get this rounded clipping of the allowed acceleration zone. Meaning that diagonal moves will run at max_accel rather than one or the other axis limit.

You can avoid this by setting your max_accel to the maximum diagonal acceleration value reported by SET_KINEMATICS_LIMIT or compute it yourself as sqrt(a_x**2 + a_y**2).
This is not done automatically, because limiting diagonal acceleration can be useful.

However if you or the slicer set a lower acceleration during printing, you’ll get that clipping again. Arguably, this could be the desired behavior: high acceleration travel moves are limited to the axis limits, while lower acceleration extrusion moves won’t be affected by their orientation, therefore avoiding fears of inconsistent extrusion from variable acceleration. (in the figure, extrusion moves will be on a smaller dashed circle that doesn’t contact the axis limits).

The above is true only when scale_xy_accel is disabled (the default). When scaling is enabled, the x/y acceleration limits will scale with max_accel when it is modified. In the figure, as the acceleration is reduced, the thick dark red limit gets closer to the origin, meaning that the allowed acceleration area is shrunken, but its shape is preserved.
This more advanced mode of operation will produce variable accelerations even at extrusion acceleration, but can help reducing the ringing on the print caused by a heavy bed.

Thank you for your interest and feedback. In the past there were PRs submited with similar changes. They were rejected because of lack of evidence that orientation dependent accelerations don’t harm extrusion and print quality.

Very cool, this is just what I need, however I seem to be having issues installing it. I downloaded the file and dropped in in ‘klipper/klippy/kinematics’, and then added limited cartesian as my printer in printer.cfg, however klipper throws an error when I add ‘max_x_accel’ and ‘max_y_accel’ under the [printer] tab. I am running two instances and a modified fork of klipper, and can supply any other needed information. Any advice on what to do?

Edit: Fixed

I’ve just added instructions to restart the klipper service after installation.
Was that the issue you were encountering?

For some reason I had the [printer] section twice in my printer.cfg file. How I never noticed this before, I don’t know, and how it even got like that is beyond me. Anyway one of them was set to limited_cartesian and one just cartesian, so klipper was probably just taking the latter, which was further down in the config file, and threw an error