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

Add 4th order S_CURVE_ACCELERATION with configurable S_CURVE_FACTOR #27101

Open
wants to merge 1 commit into
base: bugfix-2.1.x
Choose a base branch
from

Conversation

mh-dm
Copy link
Contributor

@mh-dm mh-dm commented May 18, 2024

Description

Add configurable 4th order motion support within S_CURVE_ACCELERATION alongside the existing 6th order motion support.

Background

4th? 6th? What? Short explanation: by default (trapezoidal motion), you set an acceleration limit (example 500mm/s^2), and then whenever the gcode/instructions involve a change in speed it is achieved by accelerating (or decelerating) at that set limit for the required amount of time to achieve the desired speed change. The acceleration goes from 0 (at previous speed) ~instantly to 1x of set limit then back to 0 (at newly desired speed). Force is mass times acceleration which means sudden changes in forces whenever changing speed.

s-curve-no
Trapezoidal motion, showing only X-axis. Most speed changes are at 500mm/s^2 acceleration limit.

However, with S_CURVE_ACCELERATION the acceleration will smoothly ramp up from 0, go to roughly 2x of the limit then smoothly ramp down to 0 exactly right to achieve the desired speed change. 2x? Yes. We must temporarily go over 1x to complete the same speed change in the same amount of time but while ramping from 0 acceleration. Overall, the average acceleration remains at the set acceleration limit.

s-curve-6th
S_CURVE_ACCELERATION 6th order, showing reaching roughly 2x of the acceleration limit

It's "6th order" because the motion model is position over time as a 6th order polynomial function.
However, the actual implementation works with velocity (derivative of speed) modeled as a 5th order polynomial.

Details

The problem, I might argue, is that it's "too smooth" for some applications, including 3d printers. S_CURVE_ACCELERATION 6th order spends a lot of time around 0 slowly ramping up then it has to hit high acceleration levels. The solution would be a model with a lower order polynomial. So I did the math for a 4th order polynomial (3rd order in velocity) and this is the result:

s-curve-4th
4th order, with S_CURVE_FACTOR of 0

Note how it only reaches roughly 1.6x of the acceleration limit.

The 4th order motion allows some configuration: the acceleration will start at S_CURVE_FACTOR of the limit, ramp up to above 1x then smoothly back down to S_CURVE_FACTOR of the limit exactly right to achieve the desired speed change.

s-curve-4th-0 25
4th order, with S_CURVE_FACTOR of 0.25

Note how it only reaches roughly 1.4x of the acceleration limit.

Benefits

Less smooth is more smooth™.

For 3d printing in particular, parts are printed line by line with direction changes/corners in between. Cornering is done with "jerk" or junction deviation, both of which involve basically instant speed changes. Basically instant speed change means high acceleration spikes. That doesn't pair that well with a very slowly smoothly ramping S_CURVE_ACCELERATION. It should pair better with a still smooth 4th order motion model that ramps up faster, like starting at a 0.25 S_CURVE_FACTOR. Lower peak acceleration should pair better with low-torque motors used in 3d printers.

TL;DR

Enable S_CURVE_ACCELERATION, uncomment S_CURVE_FACTOR 0.25 and check whether you get less ringing and/or if the printer sounds better/quieter. Then check how much you can increase the acceleration limits before you get too much ringing.

Acceleration only, this will have zero or minimal effect on ringing induced by jerk/junction deviation (but you don't know which type of ringing you have until you test). For the other kind of ringing I think you can try input shaping.

Configurations

This feature is not implemented for AVR. If interested I encourage you to have a go at implementing if you have a logic analyzer, an AVR board and are interested in assembly.

The graphs were obtained with a logic analyzer capturing 15M samples at 12Mhz, directly from the X-axis step/dir pins. (Just FYI my boards are LPC176x). Tiny note that captures were done with PRs #26881 and #27035 already applied. I did that to fix some issues that would pollute the captures a tiny bit.

Test gcode:

G4 P550 ; Delay for capture
G92 X10 Y10
M92 X80 Y80 ; 80 steps per mm
M204 S500
M205 X0.2 Y0.2 ; Low jerk
G0 F1500 X11.5
G0 F1200 X12
G0 F1800 X13.5
G0 F1400 X14.1
G0 F1600 X15.2
G92 Y10 ; sync test
G0 F1800 Y10.1
M204 S250
G0 F2000 X15.33
M204 S350
G0 X15.55
M204 S500
G0 X15.85
M204 S750
G0 X16.5
M204 S420
G0 X16.77
M204 S250
G0 X17
G92 Y10 ; sync test
M204 S500
G0 Y10.1
G0 F600 X17.4
G0 F1800 X18.95
G0 F600 X19.3
G0 F1800 X20.9
G0 F600 X21.3
G0 F1800 X23
G0 F600 X23.4
G0 F1800 X25.2
G0 F1500 X26
G0 F1200 X27
G0 F1800 X24.5
G92 Y10 ; sync test
G0 Y10.1
G0 F6000 X10

Related Issues

@InsanityAutomation
Copy link
Contributor

Nice work again! Configurable bow was the biggest drawback to s-curve in my opinion! One more thing to pull into my test branch! Lol

@cbagwell
Copy link
Contributor

This will be interesting to test on boards with an STM32G0 (such as SKR Mini E3 V3.0 and BTT Manta boards) because they are using the fallback implementation for S_CURVE_ACCELRATION and that section runs in 500 cycles on those boards since it doesn't have the umull instruction. Each multiplication on STM32G0 takes 52 cycles so this PR takes around 100 cycles off that 500.

@vovodroid
Copy link
Contributor

vovodroid commented Jul 16, 2024

But there is a question - does S_CURVE have meaning at all, taking in consideration that jerk, happening just before/after acceleration/deceleration produces infinite (obviously not) acceleration?

@mh-dm
Copy link
Contributor Author

mh-dm commented Aug 7, 2024

@vovodroid Yes S_CURVE still has meaning but in different ways to different people:

  1. if the primary objective is smooth motion you can use S_CURVE and just set jerk low enough. Note that it's not actually infinite acceleration because the jerk (cornering change in speed) happens from one step to the next so over the duration of a step. At 10mm/s cornering speed and 80 steps/mm that's 800 steps/s or 0.00125s for one step. So a 1mm/s jerk then is equivalent to 800mm/s^2 acceleration. People can do the math or lower jerk and test until it works for their application.
  2. if the primary objective is fast motion/printing then S_CURVE can work with jerk motion. A high jerk means a really high momentary acceleration at the corner. Without S_CURVE you also usually have full deceleration on the segment up to the corner then full acceleration after. S_CURVE avoids all (or most with S_CURVE_FACTOR) of that, flattening the max load on the system. In theory this means less ringing than without S_CURVE at high acceleration and jerk. In practice, the improvement I've personally seen is small. However, without more specific testing/calibration it's hard to judge since the improvement is highly sensitive to the machine's resonance characteristics and the acceleration used. (If you want to know why it's highly sensitive, there's this cool explainer but note that it doesn't reference Marlin 'jerk' rather it uses is 'physics jerk', which I think should be referred to as jolt in 3d printing contexts: https://xenovacivus.github.io/s-vs-t-curve/s-vs-t-curve.html by @xenovacivus . It also confusingly changes the actual overall acceleration of the motion when changing the physics jerk but the overall idea is there)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants