Klipper: Upcoming change: junction_deviation to be replaced with square_corner_velocity

Created on 9 Jul 2018  路  12Comments  路  Source: KevinOConnor/klipper

This is a notice that I plan to make a non-backwards compatible change to Klipper. I'm planning to replace the junction_deviation config parameter (and associated SET_VELOCITY_LIMIT parameter) with a square_corner_velocity parameter.

The junction_deviation parameter controls the cornering speed between two moves. There is some discussion of this in the Kinematics document: https://github.com/KevinOConnor/klipper/blob/master/docs/Kinematics.md . This upcoming change will leave the fundamental cornering algorithm unchanged. However, instead of specifying junction_deviation, users will be able to customize the cornering speeds by setting square_corner_velocity (which is the maximum speed the toolhead may travel through a 90掳 corner); Klipper will internally calculate the equivalent junction_deviation from that given speed.

The reason I plan to make this change is that I've found the junction_deviation to have several short-comings:

  1. It is effectively a "magic number" as there is little intuition that users can apply to pick a good value.
  2. The junction_deviation is combined with max_accel to determine actual cornering speeds, and this is not obvious. For example, a junction_deviation of .02 on a printer with max_accel=3000 would allow a 90掳 corner speed as high as ~12mm/s, but a junction_deviation=.02 and max_accel=4000 would allow a 90掳 corner speed of ~14mm/s. I fear users looking to tune max_accel may instead find artifacts from the inadvertent increase in cornering speed.
  3. The junction_deviation parameter has a non-linear impact to cornering speed and that is not obvious. For example, with junction_deviation=.02 and max_accel=3000, the max 90掳 corner speed is ~12mm/s, while junction_deviation=.01 and max_accel=3000 has 90掳 corner speed of ~8.5mm/s, while junction_deviation=.04 and max_accel=3000 has 90掳 corner speed of ~17mm/s. That is, doubling/halving the junction_deviation does not double/halve the cornering speeds, and I fear most users will find that surprising.
  4. I fear the default junction_deviation is too high to be a good default. I plan to replace the default junction_deviation=.02 with a default square_corner_velocity=5mm/s. In my tests, a small cornering velocity significantly reduces print times, while higher cornering velocities provide only a minor reduction in overall print time. In contrast, I fear higher cornering velocities are much more likely to result in print artifacts - a good default should balance the two. For example, tests with one particular print I had handy showed a total print time of 1:44:00 with square_corner_velocity=0, 1:25:00 with square_corner_velocity=5, 1:22:30 with square_corner_velocity=10, and 1:19:30 with square_corner_velocity=20.

There are some disadvantages to making this change:

  1. It will break existing configurations that have set an explicit junction_deviation (and SET_VELOCITY_LIMIT scripts that have set an explicit junction_deviation).
  2. Klipper will deviate from Grbl and Smoothieware which otherwise use the same junction deviation algorithm.

Given the advantages, however, my plan is to go forward with the change. My rough plan is to apply the change in about a week (approx July 15th). The code is available for early testing on the work-square-corner-velocity-20180708 branch:
cd ~/klipper ; git fetch ; git checkout origin/work-square-corner-velocity-20180708 ; sudo service klipper restart

For those who have customized junction_deviation and wish to find an equivalent square_corner_velocity, the following formula may be used:
square_corner_velocity = sqrt(junction_deviation * max_accel * (sqrt(2) + 1))

-Kevin

Most helpful comment

FYI, I have gone ahead and committed the square_corner_velocity change.

I have also taken this opportunity to make another non-backwards compatible change to the config files. The motor_off_timeout has been moved to a new [idle_timeout] config section.

All 12 comments

Suggestion: to avoid breakage and allow making this change sooner, make the first pass to deprecate the old config command (and remove it from documentation), but not remove it entirely. Basically, transparently initialize the square corner velocity using the above equation when the junction_deviation setting is present - but add a warning to the serial output on start (maybe include the calculated value). That way nothing breaks immediately on update

I always thought Marlin's jerk and grbl's junction_deviation are kind of non-scientific solutions. My general feeling is that it should be sufficinet to define acceleration properly.
In fact I am printing with junction_deviation=0.

I never didn't try to solve this dilemma with scientific reasoniing. But I think jerk/junction_deviation violate acceleration curves and as such they cannot be correct.
If they work, I guess you can generally use higher acceleration parameters instead.

Did anyone think more deeply about this?

I don't think jerk is quite the same as what's being described here, but in my mind I think of acceleration as a limitation of the stepper. Specifically, acceleration is a factor of the motor's maximum torque. It takes more torque to change/increase speed than it does to maintain speed.
The reasoning behind jerk is that the motor is capable of going from no speed to some value instantly without exceeding its torque limit. Beyond that first 'jump', it must obey the acceleration limit to avoid stalling. Sure, you could just accelerate constantly to the same top speed, but if you picture speed over time during a move as a trapezoid, and the area under that trapezoid as distance traveled - jerk allows you to cut off the trailing edges of the shape, so that the same movement takes less time.

but in my mind I think of acceleration as a limitation of the stepper
Specifically, acceleration is a factor of the motor's maximum torque

yes, that's the point

The reasoning behind jerk is that the motor is capable of going from no speed to some value instantly without exceeding its torque limit

while I know this reasoning, it doesn't reflect my own observations, nor my physical understanding.

Apart from that I find marlin's "jerk" (relating to the first step) much more clear and logical than the junction_deviation. The "smoothing" doesn't make much sense for me.

jerk allows you to cut off the trailing edges of the shape, so that the same movement takes less time

exactly this is my problem. The trapezoid (or whatever optimized curve you choose) is calculated with the maximum(!) possible acceleration (= torque * constant). If it would be possible to go higher I would choose a higher acceleration instead, because it also helps for the rest of the movement.

Additionally, I would strictly separate the first step at speed zero from a direction change, because in the former case you have static friction, which is usually much higher than dynamic friction. So, I think starting from speed zero you have to accelerate more smoothly than in all other situations. This might also be a reason to partly ignore max_accel at junctions (because max_accel may be choosen too low because of the static friction).

As a conclusion, I think we would probably need something like max_accel_from_zero which would allow to choose a much higher max_accel for the dynamic case.
Then junction_deviation would not be necessary at all (and it would probably fail, because maximum torque would be exceeded if applied).

Apart from that, I think stepping should be considered more closely.
If the motor would follow a demanded step immediately (in zero time!) there would be an infinite acceleration.
Instead (I think) a step needs some time (mass, torque, acceleration, ...) and you cannot demand another step before the rotor has moved (near enough) to the new position. If you exceed this and ask for a next step too early you don't necessarily skip a step, but instead the real position of the toolhead lags behind the demanded position. This results in an inaccurate path, because the real X and Y positions are not in proper sync (in non-trivial cases where X and Y distances or accelerations are different).
The first step is even more problematic because of the static friction, which may have very huge variations (think of a random dust particle just in front of a wheel vs. no dust).

In general, I think all skipping I ever observed was at the beginning of a move (static friction too high?) or at direction changes (junction_deviation adds acceleration and exceeds limits?).
I think this supports my theses above.

to add some numbers and observations:

  • with my corexy (Tronxy X5S heavily modified to be more sturdy) I currently use an acceleration of 1000 mm/s^2 (and max_accel_to_decel=99999 for the record).
  • I could easily use 3000 or more, but there are some cases where I get skipping at certain points for certain parts
  • I still couldn't find a reason, at least the respective points/parts don't look very demanding

However

  • using s-curve acceleration increases the count of skips for the same max_accel, so it doesn't improve anything for me. I think, the s-curve algorithm exceeds max_accel, so if max_accel is already near the limit, the s-curve would exceed it (though I did not test this with the latest sources, I used the 2018-04-16-bezier_velocity_ramp.patch from bmc0 on top of cruwaller's fork)
  • setting junction_deviation to zero allows to use something like max_accel=1500 which is not 100% safe but with only a few skips. I think about 1200 can be used 100% safe. This suggests that junction_deviation also exceeds the limit
  • skips seem to happen only at corners. But back and forth for infill (where the speed was 20% faster) doesn't produce skips (though not verified scientifically)

If I could unambiguously reproduce the skips, I would be a step further...but unfortunately I couldn't yet (though I could invest more time)

While an interesting discussion, we're way off track with regards to klipper. Take a minute to read Kevin's Kinematics doc linked in the first post. The parameters we're discussing have nothing to do with jerk per-se.

The smoothing is a means of allowing moves to be blended together.
If I were to say
G0 X50 Y0
G0 X50 Y50
No smoothing would result in
acceleration, cruise, deceleration, stop at 50,0
acceleration, cruise, deceleration, stop at 50,50

Smoothing allows the system to recognize the two subsequent moves and combine them, without fully decelerating to a stop in the middle. The degree to which it slows down at the junction between the two moves is what this setting impacts (or at least that is my understanding)

On Tue, Jul 10, 2018 at 05:35:57AM -0700, Charlie wrote:

Suggestion: to avoid breakage and allow making this change sooner, make the first pass to deprecate the old config command (and remove it from documentation), but not remove it entirely. Basically, transparently initialize the square corner velocity using the above equation when the junction_deviation setting is present - but add a warning to the serial output on start (maybe include the calculated value). That way nothing breaks immediately on update

That is possible and I did consider doing that. However, I think it
would just delay any pain. My general understanding is that
junction_deviation is left at the default by most users, so I'm not
expecting significant problems with the change.

On Tue, Jul 10, 2018 at 07:19:29AM -0700, Harald wrote:

In fact I am printing with junction_deviation=0.

It will remain possible to disable junction deviation by setting
square_corner_velocity=0

[...]

I always thought Marlin's jerk and grbl's junction_deviation are kind of non-scientific solutions. My general feeling is that it should be sufficinet to define acceleration properly.

I'd prefer to keep this issue focused on the upcoming change and not
on the merits of look-ahead. (The sole reason for look-ahead is to
enable non-zero cornering velocities.) I did my best to describe the
rational behind look-ahead in the kinematics document (though,
look-ahead is certainly not a unique feature of Klipper):

https://github.com/KevinOConnor/klipper/blob/master/docs/Kinematics.md

-Kevin

I'd prefer to keep this issue focused on the upcoming change and not on the merits of look-ahead

I already thought you would say that, but my logic was that dropping the current junction algorithm would render the change unnecessary.

The current junction algoritm seems to be a compromise with the computational efficiency:

In technical terms, Simen鈥檚 solution is a linear fit to a nonlinear acceleration problem, but is quite computationally efficient.

I looked a bit deeper into the formulas and the code and found this:

The formulas on https://onehossshay.wordpress.com/2011/09/24/improving_grbl_cornering_algorithm/ contain a delta:
image

Looking at the code in toolhead.py the delta seems to be exactly the junction_deviation parameter:

R = self.toolhead.junction_deviation * sin_theta_d2 / (1. - sin_theta_d2)

delta (green) is the deviation from the desired path:
image

So junction_deviation is simply a distance and it's representing the maximum deviation of the resulting path from the desired path.
The default 0.02 mm as a maximum error of the path would make sense.

I think, this is very easy to understand from a user's point of view.

As a result I am now convinced that the junction_deviation algorithm and the parameter itself makes sense.
The value should be near to the slicing precision used for the model.

Not sure if my previous comment was clear enough:
Now I think replacing junction_deviation by square_corner_velocity would not help users.
I think junction_deviation is much easier to understand than square_corner_velocity.

On Tue, Jul 10, 2018 at 06:37:48PM -0700, Harald wrote:

So junction_deviation is simply a distance and it's representing the maximum deviation of the path (as the name suggests).
The default 0.02 mm as a maximum error of the path would make sense.

The junction deviation algorithm does not set a distance that the tool
path may deviate. Klipper follows the g-code movement commands it
receives and commands the toolhead to follow the exact tool path
requested. (As does both Smoothieware and Grbl.)

The junction deviation algorithm is used to determine a cornering
velocity. A velocity that the toolhead needs to slow down to (instead
of zero) during the junction between two moves. It results in an
instantaneous velocity change of the toolhead during that junction.

I think, this is very easy to understand from a user's point of view.

Yes, and I hope you'll agree that since it doesn't actually do that,
it is in fact confusing.

-Kevin

FYI, I have gone ahead and committed the square_corner_velocity change.

I have also taken this opportunity to make another non-backwards compatible change to the config files. The motor_off_timeout has been moved to a new [idle_timeout] config section.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

krpepe picture krpepe  路  5Comments

sapell picture sapell  路  3Comments

leungtech picture leungtech  路  4Comments

speendo picture speendo  路  3Comments

TronskiFPV picture TronskiFPV  路  5Comments