-
-
Notifications
You must be signed in to change notification settings - Fork 19.5k
Smoother motion by fixing calculating trapezoids and ISR stepping. #27013
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
Changes from 6 commits
549a4f4
1f3f416
b9c8e24
30ece3d
89d7e17
2fb1a2b
81686a3
4b41cd8
2519e73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,10 +58,16 @@ | |
* | ||
* time -----> | ||
* | ||
* The trapezoid is the shape the speed curve over time. It starts at block->initial_rate, accelerates | ||
* first block->accelerate_until step_events_completed, then keeps going at constant speed until | ||
* step_events_completed reaches block->decelerate_after after which it decelerates until the trapezoid generator is reset. | ||
* The slope of acceleration is calculated using v = u + at where t is the accumulated timer values of the steps so far. | ||
* The speed over time graph forms a TRAPEZOID. The slope of acceleration is calculated by | ||
* v = u + t | ||
* where 't' is the accumulated timer values of the steps so far. | ||
* | ||
* The Stepper ISR dynamically executes acceleration, deceleration, and cruising according to the block parameters. | ||
* - Start at block->initial_rate. | ||
* - Accelerate while step_events_completed < block->accelerate_before. | ||
* - Cruise while step_events_completed < block->decelerate_start. | ||
* - Decelerate after that, until all steps are completed. | ||
* - Reset the trapezoid generator. | ||
*/ | ||
|
||
/** | ||
|
@@ -193,6 +199,7 @@ bool Stepper::abort_current_block; | |
; | ||
#endif | ||
|
||
// In timer_ticks | ||
uint32_t Stepper::acceleration_time, Stepper::deceleration_time; | ||
|
||
#if MULTISTEPPING_LIMIT > 1 | ||
|
@@ -224,8 +231,8 @@ xyze_long_t Stepper::delta_error{0}; | |
xyze_long_t Stepper::advance_dividend{0}; | ||
uint32_t Stepper::advance_divisor = 0, | ||
Stepper::step_events_completed = 0, // The number of step events executed in the current block | ||
Stepper::accelerate_until, // The count at which to stop accelerating | ||
Stepper::decelerate_after, // The count at which to start decelerating | ||
Stepper::accelerate_before, // The count at which to start cruising | ||
thinkyhead marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Stepper::decelerate_start, // The count at which to start decelerating | ||
Stepper::step_event_count; // The total event count for the current block | ||
|
||
#if ANY(HAS_MULTI_EXTRUDER, MIXING_EXTRUDER) | ||
|
@@ -2299,7 +2306,7 @@ hal_timer_t Stepper::block_phase_isr() { | |
// Step events not completed yet... | ||
|
||
// Are we in acceleration phase ? | ||
if (step_events_completed <= accelerate_until) { // Calculate new timer value | ||
if (step_events_completed < accelerate_before) { // Calculate new timer value | ||
|
||
#if ENABLED(S_CURVE_ACCELERATION) | ||
// Get the next speed to use (Jerk limited!) | ||
|
@@ -2316,6 +2323,7 @@ hal_timer_t Stepper::block_phase_isr() { | |
// step_rate to timer interval and steps per stepper isr | ||
interval = calc_multistep_timer_interval(acc_step_rate << oversampling_factor); | ||
acceleration_time += interval; | ||
deceleration_time = 0; // Reset since we're doing acceleration first. | ||
thinkyhead marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#if ENABLED(NONLINEAR_EXTRUSION) | ||
calc_nonlinear_e(acc_step_rate << oversampling_factor); | ||
|
@@ -2355,30 +2363,24 @@ hal_timer_t Stepper::block_phase_isr() { | |
#endif | ||
thinkyhead marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
// Are we in Deceleration phase ? | ||
else if (step_events_completed > decelerate_after) { | ||
else if (step_events_completed >= decelerate_start) { | ||
uint32_t step_rate; | ||
|
||
#if ENABLED(S_CURVE_ACCELERATION) | ||
|
||
// If this is the 1st time we process the 2nd half of the trapezoid... | ||
if (!bezier_2nd_half) { | ||
// Initialize the Bézier speed curve | ||
_calc_bezier_curve_coeffs(current_block->cruise_rate, current_block->final_rate, current_block->deceleration_time_inverse); | ||
bezier_2nd_half = true; | ||
// The first point starts at cruise rate. Just save evaluation of the Bézier curve | ||
step_rate = current_block->cruise_rate; | ||
} | ||
else { | ||
// Calculate the next speed to use | ||
step_rate = deceleration_time < current_block->deceleration_time | ||
? _eval_bezier_curve(deceleration_time) | ||
: current_block->final_rate; | ||
} | ||
|
||
// Calculate the next speed to use | ||
step_rate = deceleration_time < current_block->deceleration_time | ||
? _eval_bezier_curve(deceleration_time) | ||
: current_block->final_rate; | ||
#else | ||
// Using the old trapezoidal control | ||
step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate); | ||
if (step_rate < acc_step_rate) { // Still decelerating? | ||
if (step_rate < acc_step_rate) { | ||
step_rate = acc_step_rate - step_rate; | ||
NOLESS(step_rate, current_block->final_rate); | ||
} | ||
|
@@ -2442,6 +2444,9 @@ hal_timer_t Stepper::block_phase_isr() { | |
if (ticks_nominal == 0) { | ||
// step_rate to timer interval and loops for the nominal speed | ||
ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor); | ||
// Prepare for deceleration | ||
IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = current_block->nominal_rate); | ||
deceleration_time = ticks_nominal / 2; | ||
|
||
#if ENABLED(NONLINEAR_EXTRUSION) | ||
calc_nonlinear_e(current_block->nominal_rate << oversampling_factor); | ||
|
@@ -2463,7 +2468,7 @@ hal_timer_t Stepper::block_phase_isr() { | |
*/ | ||
#if ENABLED(LASER_POWER_TRAP) | ||
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) { | ||
if (step_events_completed + 1 == accelerate_until) { | ||
if (step_events_completed + 1 == accelerate_before) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, the new naming did highlight this: I think it should be without the +1 but I'm not sure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll have to analyze the intent of this line and look at its history. Previously it wanted to match Again, we need to look closer at the original intent of the line, because, for one thing, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's see if @descipher has anything to add…. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well it's been quite some time since my head was deep inside this code! "first block->accelerate_until step_events_completed" is the key element, since the first block starts at 0 we will have value 1 after the ISR does the count increment, thus the < accelerate_until naming usage which is not the same meaning as accelerate_before, it only looks that way when viewing it as less than accelerate_until at the moment of comparison. However this comparison moment is the actual accelerate_until PIT and not before. In regards to the +1 == if I recall correctly the reason for + 1 stems from an odd step count and the PIT when the laser would need to be turned off or incr/decr . It worked out that the counts were shifted toward the tail of the trap so the counts were consistently short on the decel side of the trap. This can be viewed by turning on debug where it will give you the actual step counts. It becomes obvious with debug on and the simulator is your friend for that ;) The laser power will be inc'd and dec'd closer to ideal with the +1. Keep in mind its not uniform in a real world print or laser burn, but in tests from a dead start to full stop the ramps should be equal and that's when this shifted count behavior was first observed. If there is a change in the counts/shaping now it should be re-evaluated again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My worry is that with some moves it will never be that The best thing to do would be to test this in the simulator, not with laser enabled, but just with that comparison being done at that same point in the code to see how it matches up and whether we are in fact guaranteed to be able to scrutinize every step index. If we can, then the very last index of acceleration will be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
As a reminder, it is possible to log signals in the simulator and export them to files that can be viewed in PulseView. This allows you to analyze stepper pulses without requiring a physical logic analyzer setup. I find that it provides a much faster feedback loop than working with actual hardware. It won't work for hardware PWM signals like you would have for a laser, but for any software-timed pulses it works well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PR Sim compiling result: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since there are Now to address how this change affects laser: I looked over how Before: I've also looked at the acceleration->cruise transition. I'd say the difference is not significant / not above the noise in the pwm/logic analyzer capture: Last step 75.4 -> 80% versus 76.7% -> 80%.
@descipher You are right about the absolute ideal being equal intervals. And that even with this change the first step is slower than the last step, or stated another way: the first step is at exactly
|
||
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) { | ||
if (current_block->laser.trap_ramp_entry_incr > 0) { | ||
current_block->laser.trap_ramp_active_pwr = current_block->laser.power; | ||
|
@@ -2640,9 +2645,6 @@ hal_timer_t Stepper::block_phase_isr() { | |
); | ||
axis_did_move = didmove; | ||
|
||
// No acceleration / deceleration time elapsed so far | ||
acceleration_time = deceleration_time = 0; | ||
|
||
#if ENABLED(ADAPTIVE_STEP_SMOOTHING) | ||
// Nonlinear Extrusion needs at least 2x oversampling to permit increase of E step rate | ||
// Otherwise assume no axis smoothing (via oversampling) | ||
|
@@ -2696,8 +2698,8 @@ hal_timer_t Stepper::block_phase_isr() { | |
step_events_completed = 0; | ||
|
||
// Compute the acceleration and deceleration points | ||
accelerate_until = current_block->accelerate_until << oversampling_factor; | ||
decelerate_after = current_block->decelerate_after << oversampling_factor; | ||
accelerate_before = current_block->accelerate_before << oversampling_factor; | ||
decelerate_start = current_block->decelerate_start << oversampling_factor; | ||
|
||
TERN_(MIXING_EXTRUDER, mixer.stepper_setup(current_block->b_color)); | ||
|
||
|
@@ -2783,7 +2785,8 @@ hal_timer_t Stepper::block_phase_isr() { | |
|
||
// Calculate the initial timer interval | ||
interval = calc_multistep_timer_interval(current_block->initial_rate << oversampling_factor); | ||
acceleration_time += interval; | ||
// Initialize ac/deceleration time as if half the time passed. | ||
acceleration_time = deceleration_time = interval / 2; | ||
thinkyhead marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
#if ENABLED(NONLINEAR_EXTRUSION) | ||
calc_nonlinear_e(current_block->initial_rate << oversampling_factor); | ||
|
Uh oh!
There was an error while loading. Please reload this page.