Skip to content

Commit 232a104

Browse files
authored
Fix, improve Linear Advance (#24533)
1 parent aba35ec commit 232a104

4 files changed

Lines changed: 305 additions & 351 deletions

File tree

Marlin/src/module/planner.cpp

Lines changed: 110 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
788788
NOLESS(initial_rate, uint32_t(MINIMAL_STEP_RATE));
789789
NOLESS(final_rate, uint32_t(MINIMAL_STEP_RATE));
790790

791-
#if ENABLED(S_CURVE_ACCELERATION)
791+
#if EITHER(S_CURVE_ACCELERATION, LIN_ADVANCE)
792792
// If we have some plateau time, the cruise rate will be the nominal rate
793793
uint32_t cruise_rate = block->nominal_rate;
794794
#endif
@@ -820,7 +820,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
820820
accelerate_steps = _MIN(uint32_t(_MAX(accelerate_steps_float, 0)), block->step_event_count);
821821
decelerate_steps = block->step_event_count - accelerate_steps;
822822

823-
#if ENABLED(S_CURVE_ACCELERATION)
823+
#if EITHER(S_CURVE_ACCELERATION, LIN_ADVANCE)
824824
// We won't reach the cruising rate. Let's calculate the speed we will reach
825825
cruise_rate = final_speed(initial_rate, accel, accelerate_steps);
826826
#endif
@@ -849,6 +849,14 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
849849
#endif
850850
block->final_rate = final_rate;
851851

852+
#if ENABLED(LIN_ADVANCE)
853+
if (block->la_advance_rate) {
854+
const float comp = extruder_advance_K[block->extruder] * block->steps.e / block->step_event_count;
855+
block->max_adv_steps = cruise_rate * comp;
856+
block->final_adv_steps = final_rate * comp;
857+
}
858+
#endif
859+
852860
#if ENABLED(LASER_POWER_TRAP)
853861
/**
854862
* Laser Trapezoid Calculations
@@ -899,75 +907,76 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
899907
#endif // LASER_POWER_TRAP
900908
}
901909

902-
/* PLANNER SPEED DEFINITION
903-
+--------+ <- current->nominal_speed
904-
/ \
905-
current->entry_speed -> + \
906-
| + <- next->entry_speed (aka exit speed)
907-
+-------------+
908-
time -->
909-
910-
Recalculates the motion plan according to the following basic guidelines:
911-
912-
1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
913-
(i.e. current->entry_speed) such that:
914-
a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
915-
neighboring blocks.
916-
b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
917-
with a maximum allowable deceleration over the block travel distance.
918-
c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
919-
2. Go over every block in chronological (forward) order and dial down junction speed values if
920-
a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
921-
acceleration over the block travel distance.
922-
923-
When these stages are complete, the planner will have maximized the velocity profiles throughout the all
924-
of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
925-
other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
926-
are possible. If a new block is added to the buffer, the plan is recomputed according to the said
927-
guidelines for a new optimal plan.
928-
929-
To increase computational efficiency of these guidelines, a set of planner block pointers have been
930-
created to indicate stop-compute points for when the planner guidelines cannot logically make any further
931-
changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
932-
planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
933-
bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
934-
added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
935-
them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
936-
point) are all accelerating, they are all optimal and can not be altered by a new block added to the
937-
planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
938-
junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
939-
used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
940-
recomputed as stated in the general guidelines.
941-
942-
Planner buffer index mapping:
943-
- block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
944-
- block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
945-
the buffer is full or empty. As described for standard ring buffers, this block is always empty.
946-
- block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
947-
streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
948-
planner buffer that don't change with the addition of a new block, as describe above. In addition,
949-
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
950-
this requirement when encountered by the Planner::release_current_block() routine during a cycle.
951-
952-
NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
953-
segments (e.g., complex curves) may seem to move slowly. This is because there simply isn't
954-
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and
955-
then decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this
956-
happens and becomes an annoyance, there are a few simple solutions:
957-
958-
- Maximize the machine acceleration. The planner will be able to compute higher velocity profiles
959-
within the same combined distance.
960-
961-
- Maximize line motion(s) distance per block to a desired tolerance. The more combined distance the
962-
planner has to use, the faster it can go.
963-
964-
- Maximize the planner buffer size. This also will increase the combined distance for the planner to
965-
compute over. It also increases the number of computations the planner has to perform to compute an
966-
optimal plan, so select carefully.
967-
968-
- Use G2/G3 arcs instead of many short segments. Arcs inform the planner of a safe exit speed at the
969-
end of the last segment, which alleviates this problem.
970-
*/
910+
/**
911+
* PLANNER SPEED DEFINITION
912+
* +--------+ <- current->nominal_speed
913+
* / \
914+
* current->entry_speed -> + \
915+
* | + <- next->entry_speed (aka exit speed)
916+
* +-------------+
917+
* time -->
918+
*
919+
* Recalculates the motion plan according to the following basic guidelines:
920+
*
921+
* 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
922+
* (i.e. current->entry_speed) such that:
923+
* a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
924+
* neighboring blocks.
925+
* b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
926+
* with a maximum allowable deceleration over the block travel distance.
927+
* c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
928+
* 2. Go over every block in chronological (forward) order and dial down junction speed values if
929+
* a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
930+
* acceleration over the block travel distance.
931+
*
932+
* When these stages are complete, the planner will have maximized the velocity profiles throughout the all
933+
* of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
934+
* other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
935+
* are possible. If a new block is added to the buffer, the plan is recomputed according to the said
936+
* guidelines for a new optimal plan.
937+
*
938+
* To increase computational efficiency of these guidelines, a set of planner block pointers have been
939+
* created to indicate stop-compute points for when the planner guidelines cannot logically make any further
940+
* changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
941+
* planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
942+
* bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
943+
* added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
944+
* them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
945+
* point) are all accelerating, they are all optimal and can not be altered by a new block added to the
946+
* planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
947+
* junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
948+
* used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
949+
* recomputed as stated in the general guidelines.
950+
*
951+
* Planner buffer index mapping:
952+
* - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
953+
* - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
954+
* the buffer is full or empty. As described for standard ring buffers, this block is always empty.
955+
* - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
956+
* streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
957+
* planner buffer that don't change with the addition of a new block, as describe above. In addition,
958+
* this block can never be less than block_buffer_tail and will always be pushed forward and maintain
959+
* this requirement when encountered by the Planner::release_current_block() routine during a cycle.
960+
*
961+
* NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
962+
* segments (e.g., complex curves) may seem to move slowly. This is because there simply isn't
963+
* enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and
964+
* then decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this
965+
* happens and becomes an annoyance, there are a few simple solutions:
966+
*
967+
* - Maximize the machine acceleration. The planner will be able to compute higher velocity profiles
968+
* within the same combined distance.
969+
*
970+
* - Maximize line motion(s) distance per block to a desired tolerance. The more combined distance the
971+
* planner has to use, the faster it can go.
972+
*
973+
* - Maximize the planner buffer size. This also will increase the combined distance for the planner to
974+
* compute over. It also increases the number of computations the planner has to perform to compute an
975+
* optimal plan, so select carefully.
976+
*
977+
* - Use G2/G3 arcs instead of many short segments. Arcs inform the planner of a safe exit speed at the
978+
* end of the last segment, which alleviates this problem.
979+
*/
971980

972981
// The kernel called by recalculate() when scanning the plan from last to first entry.
973982
void Planner::reverse_pass_kernel(block_t * const current, const block_t * const next
@@ -1211,13 +1220,6 @@ void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t
12111220
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
12121221
const float nomr = 1.0f / block->nominal_speed;
12131222
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
1214-
#if ENABLED(LIN_ADVANCE)
1215-
if (block->use_advance_lead) {
1216-
const float comp = block->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
1217-
block->max_adv_steps = block->nominal_speed * comp;
1218-
block->final_adv_steps = next_entry_speed * comp;
1219-
}
1220-
#endif
12211223
}
12221224

12231225
// Reset current only to ensure next trapezoid is computed - The
@@ -1251,13 +1253,6 @@ void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t
12511253

12521254
const float nomr = 1.0f / block->nominal_speed;
12531255
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
1254-
#if ENABLED(LIN_ADVANCE)
1255-
if (block->use_advance_lead) {
1256-
const float comp = block->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
1257-
block->max_adv_steps = block->nominal_speed * comp;
1258-
block->final_adv_steps = next_entry_speed * comp;
1259-
}
1260-
#endif
12611256
}
12621257

12631258
// Reset block to ensure its trapezoid is computed - The stepper is free to use
@@ -2502,13 +2497,15 @@ bool Planner::_populate_block(
25022497
// Compute and limit the acceleration rate for the trapezoid generator.
25032498
const float steps_per_mm = block->step_event_count * inverse_millimeters;
25042499
uint32_t accel;
2500+
#if ENABLED(LIN_ADVANCE)
2501+
bool use_advance_lead = false;
2502+
#endif
25052503
if (NUM_AXIS_GANG(
25062504
!block->steps.a, && !block->steps.b, && !block->steps.c,
25072505
&& !block->steps.i, && !block->steps.j, && !block->steps.k,
25082506
&& !block->steps.u, && !block->steps.v, && !block->steps.w)
25092507
) { // Is this a retract / recover move?
25102508
accel = CEIL(settings.retract_acceleration * steps_per_mm); // Convert to: acceleration steps/sec^2
2511-
TERN_(LIN_ADVANCE, block->use_advance_lead = false); // No linear advance for simple retract/recover
25122509
}
25132510
else {
25142511
#define LIMIT_ACCEL_LONG(AXIS,INDX) do{ \
@@ -2535,33 +2532,29 @@ bool Planner::_populate_block(
25352532
/**
25362533
* Use LIN_ADVANCE for blocks if all these are true:
25372534
*
2538-
* esteps : This is a print move, because we checked for A, B, C steps before.
2535+
* esteps : This is a print move, because we checked for A, B, C steps before.
25392536
*
2540-
* extruder_advance_K[active_extruder] : There is an advance factor set for this extruder.
2537+
* extruder_advance_K[extruder] : There is an advance factor set for this extruder.
25412538
*
2542-
* de > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
2539+
* de > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
25432540
*/
2544-
block->use_advance_lead = esteps
2545-
&& extruder_advance_K[active_extruder]
2546-
&& de > 0;
2547-
2548-
if (block->use_advance_lead) {
2549-
block->e_D_ratio = (target_float.e - position_float.e) /
2550-
#if IS_KINEMATIC
2551-
block->millimeters
2552-
#else
2541+
use_advance_lead = esteps && extruder_advance_K[extruder] && de > 0;
2542+
2543+
if (use_advance_lead) {
2544+
float e_D_ratio = (target_float.e - position_float.e) /
2545+
TERN(IS_KINEMATIC, block->millimeters,
25532546
SQRT(sq(target_float.x - position_float.x)
25542547
+ sq(target_float.y - position_float.y)
25552548
+ sq(target_float.z - position_float.z))
2556-
#endif
2557-
;
2549+
);
25582550

25592551
// Check for unusual high e_D ratio to detect if a retract move was combined with the last print move due to min. steps per segment. Never execute this with advance!
25602552
// This assumes no one will use a retract length of 0mm < retr_length < ~0.2mm and no one will print 100mm wide lines using 3mm filament or 35mm wide lines using 1.75mm filament.
2561-
if (block->e_D_ratio > 3.0f)
2562-
block->use_advance_lead = false;
2553+
if (e_D_ratio > 3.0f)
2554+
use_advance_lead = false;
25632555
else {
2564-
const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[active_extruder] * block->e_D_ratio) * steps_per_mm;
2556+
// Scale E acceleration so that it will be possible to jump to the advance speed.
2557+
const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[extruder] * e_D_ratio) * steps_per_mm;
25652558
if (TERN0(LA_DEBUG, accel > max_accel_steps_per_s2))
25662559
SERIAL_ECHOLNPGM("Acceleration limited.");
25672560
NOMORE(accel, max_accel_steps_per_s2);
@@ -2593,13 +2586,21 @@ bool Planner::_populate_block(
25932586
block->acceleration_rate = (uint32_t)(accel * (float(1UL << 24) / (STEPPER_TIMER_RATE)));
25942587
#endif
25952588
#if ENABLED(LIN_ADVANCE)
2596-
if (block->use_advance_lead) {
2597-
block->advance_speed = (STEPPER_TIMER_RATE) / (extruder_advance_K[active_extruder] * block->e_D_ratio * block->acceleration * settings.axis_steps_per_mm[E_AXIS_N(extruder)]);
2589+
block->la_advance_rate = 0;
2590+
block->la_scaling = 0;
2591+
2592+
if (use_advance_lead) {
2593+
// the Bresenham algorithm will convert this step rate into extruder steps
2594+
block->la_advance_rate = extruder_advance_K[extruder] * block->acceleration_steps_per_s2;
2595+
2596+
// reduce LA ISR frequency by calling it only often enough to ensure that there will
2597+
// never be more than four extruder steps per call
2598+
for (uint32_t dividend = block->steps.e << 1; dividend <= (block->step_event_count >> 2); dividend <<= 1)
2599+
block->la_scaling++;
2600+
25982601
#if ENABLED(LA_DEBUG)
2599-
if (extruder_advance_K[active_extruder] * block->e_D_ratio * block->acceleration * 2 < block->nominal_speed * block->e_D_ratio)
2600-
SERIAL_ECHOLNPGM("More than 2 steps per eISR loop executed.");
2601-
if (block->advance_speed < 200)
2602-
SERIAL_ECHOLNPGM("eISR running at > 10kHz.");
2602+
if (block->la_advance_rate >> block->la_scaling > 10000)
2603+
SERIAL_ECHOLNPGM("eISR running at > 10kHz: ", block->la_advance_rate);
26032604
#endif
26042605
}
26052606
#endif

Marlin/src/module/planner.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,10 @@ typedef struct PlannerBlock {
239239

240240
// Advance extrusion
241241
#if ENABLED(LIN_ADVANCE)
242-
bool use_advance_lead;
243-
uint16_t advance_speed, // STEP timer value for extruder speed offset ISR
244-
max_adv_steps, // max. advance steps to get cruising speed pressure (not always nominal_speed!)
245-
final_adv_steps; // advance steps due to exit speed
246-
float e_D_ratio;
242+
uint32_t la_advance_rate; // The rate at which steps are added whilst accelerating
243+
uint8_t la_scaling; // Scale ISR frequency down and step frequency up by 2 ^ la_scaling
244+
uint16_t max_adv_steps, // Max advance steps to get cruising speed pressure
245+
final_adv_steps; // Advance steps for exit speed pressure
247246
#endif
248247

249248
uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
@@ -1018,7 +1017,7 @@ class Planner {
10181017
return target_velocity_sqr - 2 * accel * distance;
10191018
}
10201019

1021-
#if ENABLED(S_CURVE_ACCELERATION)
1020+
#if EITHER(S_CURVE_ACCELERATION, LIN_ADVANCE)
10221021
/**
10231022
* Calculate the speed reached given initial speed, acceleration and distance
10241023
*/

0 commit comments

Comments
 (0)