From ac6afa4fce048e5df76b158f60e89293a7a344b7 Mon Sep 17 00:00:00 2001 From: Mihai <299015+mh-dm@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:16:15 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20planner=20jerk=20limits=20?= =?UTF-8?q?(#26529)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Scott Lahteine --- Marlin/src/gcode/config/M200-M205.cpp | 5 +- Marlin/src/gcode/motion/G4.cpp | 7 +- Marlin/src/module/planner.cpp | 192 ++++++++++---------------- Marlin/src/module/planner.h | 4 +- 4 files changed, 79 insertions(+), 129 deletions(-) diff --git a/Marlin/src/gcode/config/M200-M205.cpp b/Marlin/src/gcode/config/M200-M205.cpp index 7cdf4026b8ef6..899f12099bbd8 100644 --- a/Marlin/src/gcode/config/M200-M205.cpp +++ b/Marlin/src/gcode/config/M200-M205.cpp @@ -297,14 +297,11 @@ void GcodeSuite::M205() { if (parser.seenval('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units(); if (parser.seenval('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units(); #if HAS_JUNCTION_DEVIATION - #if ENABLED(CLASSIC_JERK) && AXIS_COLLISION('J') - #error "Can't set_max_jerk for 'J' axis because 'J' is used for Junction Deviation." - #endif if (parser.seenval('J')) { const float junc_dev = parser.value_linear_units(); if (WITHIN(junc_dev, 0.01f, 0.3f)) { planner.junction_deviation_mm = junc_dev; - TERN_(LIN_ADVANCE, planner.recalculate_max_e_jerk()); + TERN_(HAS_LINEAR_E_JERK, planner.recalculate_max_e_jerk()); } else SERIAL_ERROR_MSG("?J out of range (0.01 to 0.3)"); diff --git a/Marlin/src/gcode/motion/G4.cpp b/Marlin/src/gcode/motion/G4.cpp index ebaa6aabc0625..3244c4ea4d514 100644 --- a/Marlin/src/gcode/motion/G4.cpp +++ b/Marlin/src/gcode/motion/G4.cpp @@ -38,7 +38,8 @@ void GcodeSuite::G4() { SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP); #endif - if (!ui.has_status()) LCD_MESSAGE(MSG_DWELL); - - dwell(dwell_ms); + if (dwell_ms) { + if (!ui.has_status()) LCD_MESSAGE(MSG_DWELL); + dwell(dwell_ms); + } } diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 61dff55caf944..64c47301f58a6 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -153,9 +153,7 @@ float Planner::mm_per_step[DISTINCT_AXES]; // (mm) Millimeters per step #if HAS_LINEAR_E_JERK float Planner::max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm #endif -#endif - -#if ENABLED(CLASSIC_JERK) +#else // CLASSIC_JERK TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) Planner::max_jerk; #endif @@ -2374,42 +2372,42 @@ bool Planner::_populate_block( // Limit speed on extruders, if any #if HAS_EXTRUDERS - { - current_speed.e = dist_mm.e * inverse_secs; - #if HAS_MIXER_SYNC_CHANNEL - // Move all mixing extruders at the specified rate - if (mixer.get_current_vtool() == MIXER_AUTORETRACT_TOOL) - current_speed.e *= MIXING_STEPPERS; - #endif - - const feedRate_t cs = ABS(current_speed.e), - max_fr = settings.max_feedrate_mm_s[E_AXIS_N(extruder)] - * TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1); - - if (cs > max_fr) NOMORE(speed_factor, max_fr / cs); //respect max feedrate on any movement (doesn't matter if E axes only or not) - - #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) - const feedRate_t max_vfr = volumetric_extruder_feedrate_limit[extruder] - * TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1); + { + current_speed.e = dist_mm.e * inverse_secs; + #if HAS_MIXER_SYNC_CHANNEL + // Move all mixing extruders at the specified rate + if (mixer.get_current_vtool() == MIXER_AUTORETRACT_TOOL) + current_speed.e *= MIXING_STEPPERS; + #endif - // TODO: Doesn't work properly for joined segments. Set MIN_STEPS_PER_SEGMENT 1 as workaround. + const feedRate_t cs = ABS(current_speed.e), + max_fr = settings.max_feedrate_mm_s[E_AXIS_N(extruder)] + * TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1); - if (block->steps.a || block->steps.b || block->steps.c) { + if (cs > max_fr) NOMORE(speed_factor, max_fr / cs); //respect max feedrate on any movement (doesn't matter if E axes only or not) - if (max_vfr > 0 && cs > max_vfr) { - NOMORE(speed_factor, max_vfr / cs); // respect volumetric extruder limit (if any) - /* <-- add a slash to enable - SERIAL_ECHOPGM("volumetric extruder limit enforced: ", (cs * CIRCLE_AREA(filament_size[extruder] * 0.5f))); - SERIAL_ECHOPGM(" mm^3/s (", cs); - SERIAL_ECHOPGM(" mm/s) limited to ", (max_vfr * CIRCLE_AREA(filament_size[extruder] * 0.5f))); - SERIAL_ECHOPGM(" mm^3/s (", max_vfr); - SERIAL_ECHOLNPGM(" mm/s)"); - //*/ - } + #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) + const feedRate_t max_vfr = volumetric_extruder_feedrate_limit[extruder] + * TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1); + + // TODO: Doesn't work properly for joined segments. Set MIN_STEPS_PER_SEGMENT 1 as workaround. + + if (block->steps.a || block->steps.b || block->steps.c) { + + if (max_vfr > 0 && cs > max_vfr) { + NOMORE(speed_factor, max_vfr / cs); // respect volumetric extruder limit (if any) + /* <-- add a slash to enable + SERIAL_ECHOPGM("volumetric extruder limit enforced: ", (cs * CIRCLE_AREA(filament_size[extruder] * 0.5f))); + SERIAL_ECHOPGM(" mm^3/s (", cs); + SERIAL_ECHOPGM(" mm/s) limited to ", (max_vfr * CIRCLE_AREA(filament_size[extruder] * 0.5f))); + SERIAL_ECHOPGM(" mm^3/s (", max_vfr); + SERIAL_ECHOLNPGM(" mm/s)"); + //*/ } - #endif - } - #endif + } + #endif + } + #endif // HAS_EXTRUDERS #ifdef XY_FREQUENCY_LIMIT @@ -2492,7 +2490,7 @@ bool Planner::_populate_block( * * extruder_advance_K[extruder] : There is an advance factor set for this extruder. * - * dist.e > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves) + * dist.e > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves) */ use_advance_lead = esteps && extruder_advance_K[E_INDEX_N(extruder)] && dist.e > 0; @@ -2511,9 +2509,10 @@ bool Planner::_populate_block( else { // Scale E acceleration so that it will be possible to jump to the advance speed. const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[E_INDEX_N(extruder)] * e_D_ratio) * steps_per_mm; - if (TERN0(LA_DEBUG, accel > max_accel_steps_per_s2)) - SERIAL_ECHOLNPGM("Acceleration limited."); - NOMORE(accel, max_accel_steps_per_s2); + if (accel > max_accel_steps_per_s2) { + accel = max_accel_steps_per_s2; + if (ENABLED(LA_DEBUG)) SERIAL_ECHOLNPGM("Acceleration limited."); + } } } #endif @@ -2764,104 +2763,59 @@ bool Planner::_populate_block( prev_unit_vec = unit_vec; - #endif - - #if ENABLED(CLASSIC_JERK) + #else // CLASSIC_JERK /** - * Adapted from Průša MKS firmware + * Heavily modified. Originally adapted from Průša firmware. * https://github.com/prusa3d/Prusa-Firmware */ - // Exit speed limited by a jerk to full halt of a previous last segment - static float previous_safe_speed; - - // Start with a safe speed (from which the machine may halt to stop immediately). - float safe_speed = block->nominal_speed; - #ifndef TRAVEL_EXTRA_XYJERK - #define TRAVEL_EXTRA_XYJERK 0 + #define TRAVEL_EXTRA_XYJERK 0.0f #endif - const float extra_xyjerk = TERN0(HAS_EXTRUDERS, dist.e <= 0) ? TRAVEL_EXTRA_XYJERK : 0; - - uint8_t limited = 0; - TERN(HAS_LINEAR_E_JERK, LOOP_NUM_AXES, LOOP_LOGICAL_AXES)(i) { - const float jerk = ABS(current_speed[i]), // cs : Starting from zero, change in speed for this axis - maxj = (max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f)); // mj : The max jerk setting for this axis - if (jerk > maxj) { // cs > mj : New current speed too fast? - if (limited) { // limited already? - const float mjerk = block->nominal_speed * maxj; // ns*mj - if (jerk * safe_speed > mjerk) safe_speed = mjerk / jerk; // ns*mj/cs - } - else { - safe_speed *= maxj / jerk; // Initial limit: ns*mj/cs - ++limited; // Initially limited - } - } - } + const float extra_xyjerk = TERN0(HAS_EXTRUDERS, dist.e <= 0) ? TRAVEL_EXTRA_XYJERK : 0.0f; - float vmax_junction; - if (moves_queued && !UNEAR_ZERO(previous_nominal_speed)) { - // Estimate a maximum velocity allowed at a joint of two successive segments. - // If this maximum velocity allowed is lower than the minimum of the entry / exit safe velocities, - // then the machine is not coasting anymore and the safe entry / exit velocities shall be used. + if (!moves_queued || UNEAR_ZERO(previous_nominal_speed)) { + // Compute "safe" speed, limited by a jerk to/from full halt. - // Factor to multiply the previous / current nominal velocities to get componentwise limited velocities. - float v_factor = 1; - limited = 0; + float v_factor = 1.0f; + LOOP_LOGICAL_AXES(i) { + const float jerk = ABS(current_speed[i]), // Starting from zero, change in speed for this axis + maxj = max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f); // The max jerk setting for this axis + if (jerk * v_factor > maxj) v_factor = maxj / jerk; + } + vmax_junction_sqr = sq(block->nominal_speed * v_factor); + NOLESS(minimum_planner_speed_sqr, vmax_junction_sqr); + } + else { + // Compute the maximum velocity allowed at a joint of two successive segments. // The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum. - // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting. - float smaller_speed_factor = 1.0f; + float vmax_junction, previous_speed_factor, current_speed_factor; if (block->nominal_speed < previous_nominal_speed) { vmax_junction = block->nominal_speed; - smaller_speed_factor = vmax_junction / previous_nominal_speed; + previous_speed_factor = vmax_junction / previous_nominal_speed; + current_speed_factor = 1.0f; } - else + else { vmax_junction = previous_nominal_speed; + previous_speed_factor = 1.0f; + current_speed_factor = vmax_junction / block->nominal_speed; + } // Now limit the jerk in all axes. - TERN(HAS_LINEAR_E_JERK, LOOP_NUM_AXES, LOOP_LOGICAL_AXES)(axis) { - // Limit an axis. We have to differentiate: coasting, reversal of an axis, full stop. - float v_exit = previous_speed[axis] * smaller_speed_factor, - v_entry = current_speed[axis]; - if (limited) { - v_exit *= v_factor; - v_entry *= v_factor; - } - - // Calculate jerk depending on whether the axis is coasting in the same direction or reversing. - const float jerk = (v_exit > v_entry) - ? // coasting axis reversal - ( (v_entry > 0 || v_exit < 0) ? (v_exit - v_entry) : _MAX(v_exit, -v_entry) ) - : // v_exit <= v_entry coasting axis reversal - ( (v_entry < 0 || v_exit > 0) ? (v_entry - v_exit) : _MAX(-v_exit, v_entry) ); - - const float maxj = (max_jerk[axis] + (axis == X_AXIS || axis == Y_AXIS ? extra_xyjerk : 0.0f)); - - if (jerk > maxj) { - v_factor *= maxj / jerk; - ++limited; - } + float v_factor = 1.0f; + LOOP_LOGICAL_AXES(i) { + // Scale per-axis velocities for the same vmax_junction. + const float v_exit = previous_speed[i] * previous_speed_factor, + v_entry = current_speed[i] * current_speed_factor; + + // Jerk is the per-axis velocity difference. + const float jerk = ABS(v_exit - v_entry), + maxj = max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f); + if (jerk * v_factor > maxj) v_factor = maxj / jerk; } - if (limited) vmax_junction *= v_factor; - // Now the transition velocity is known, which maximizes the shared exit / entry velocity while - // respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints. - const float vmax_junction_threshold = vmax_junction * 0.99f; - if (previous_safe_speed > vmax_junction_threshold && safe_speed > vmax_junction_threshold) - vmax_junction = safe_speed; + vmax_junction_sqr = sq(vmax_junction * v_factor); } - else - vmax_junction = safe_speed; - - previous_safe_speed = safe_speed; - - NOLESS(minimum_planner_speed_sqr, sq(safe_speed)); - - #if HAS_JUNCTION_DEVIATION - NOMORE(vmax_junction_sqr, sq(vmax_junction)); // Throttle down to max speed - #else - vmax_junction_sqr = sq(vmax_junction); // Go up or down to the new speed - #endif #endif // CLASSIC_JERK diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index e746071de9b3c..726ccb4d3f814 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -475,9 +475,7 @@ class Planner { #if HAS_LINEAR_E_JERK static float max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm #endif - #endif - - #if ENABLED(CLASSIC_JERK) + #else // CLASSIC_JERK // (mm/s^2) M205 XYZ(E) - The largest speed change requiring no acceleration. static TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) max_jerk; #endif