Skip to content

Commit b901338

Browse files
mh-dmthinkyhead
andauthored
🐛 Fix planner jerk limits (MarlinFirmware#26529)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
1 parent 75da355 commit b901338

File tree

4 files changed

+79
-129
lines changed

4 files changed

+79
-129
lines changed

Marlin/src/gcode/config/M200-M205.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,11 @@ void GcodeSuite::M205() {
297297
if (parser.seenval('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units();
298298
if (parser.seenval('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units();
299299
#if HAS_JUNCTION_DEVIATION
300-
#if ENABLED(CLASSIC_JERK) && AXIS_COLLISION('J')
301-
#error "Can't set_max_jerk for 'J' axis because 'J' is used for Junction Deviation."
302-
#endif
303300
if (parser.seenval('J')) {
304301
const float junc_dev = parser.value_linear_units();
305302
if (WITHIN(junc_dev, 0.01f, 0.3f)) {
306303
planner.junction_deviation_mm = junc_dev;
307-
TERN_(LIN_ADVANCE, planner.recalculate_max_e_jerk());
304+
TERN_(HAS_LINEAR_E_JERK, planner.recalculate_max_e_jerk());
308305
}
309306
else
310307
SERIAL_ERROR_MSG("?J out of range (0.01 to 0.3)");

Marlin/src/gcode/motion/G4.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ void GcodeSuite::G4() {
3838
SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP);
3939
#endif
4040

41-
if (!ui.has_status()) LCD_MESSAGE(MSG_DWELL);
42-
43-
dwell(dwell_ms);
41+
if (dwell_ms) {
42+
if (!ui.has_status()) LCD_MESSAGE(MSG_DWELL);
43+
dwell(dwell_ms);
44+
}
4445
}

Marlin/src/module/planner.cpp

+73-119
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,7 @@ float Planner::mm_per_step[DISTINCT_AXES]; // (mm) Millimeters per step
153153
#if HAS_LINEAR_E_JERK
154154
float Planner::max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm
155155
#endif
156-
#endif
157-
158-
#if ENABLED(CLASSIC_JERK)
156+
#else // CLASSIC_JERK
159157
TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) Planner::max_jerk;
160158
#endif
161159

@@ -2374,42 +2372,42 @@ bool Planner::_populate_block(
23742372

23752373
// Limit speed on extruders, if any
23762374
#if HAS_EXTRUDERS
2377-
{
2378-
current_speed.e = dist_mm.e * inverse_secs;
2379-
#if HAS_MIXER_SYNC_CHANNEL
2380-
// Move all mixing extruders at the specified rate
2381-
if (mixer.get_current_vtool() == MIXER_AUTORETRACT_TOOL)
2382-
current_speed.e *= MIXING_STEPPERS;
2383-
#endif
2384-
2385-
const feedRate_t cs = ABS(current_speed.e),
2386-
max_fr = settings.max_feedrate_mm_s[E_AXIS_N(extruder)]
2387-
* TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1);
2388-
2389-
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)
2390-
2391-
#if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT)
2392-
const feedRate_t max_vfr = volumetric_extruder_feedrate_limit[extruder]
2393-
* TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1);
2375+
{
2376+
current_speed.e = dist_mm.e * inverse_secs;
2377+
#if HAS_MIXER_SYNC_CHANNEL
2378+
// Move all mixing extruders at the specified rate
2379+
if (mixer.get_current_vtool() == MIXER_AUTORETRACT_TOOL)
2380+
current_speed.e *= MIXING_STEPPERS;
2381+
#endif
23942382

2395-
// TODO: Doesn't work properly for joined segments. Set MIN_STEPS_PER_SEGMENT 1 as workaround.
2383+
const feedRate_t cs = ABS(current_speed.e),
2384+
max_fr = settings.max_feedrate_mm_s[E_AXIS_N(extruder)]
2385+
* TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1);
23962386

2397-
if (block->steps.a || block->steps.b || block->steps.c) {
2387+
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)
23982388

2399-
if (max_vfr > 0 && cs > max_vfr) {
2400-
NOMORE(speed_factor, max_vfr / cs); // respect volumetric extruder limit (if any)
2401-
/* <-- add a slash to enable
2402-
SERIAL_ECHOPGM("volumetric extruder limit enforced: ", (cs * CIRCLE_AREA(filament_size[extruder] * 0.5f)));
2403-
SERIAL_ECHOPGM(" mm^3/s (", cs);
2404-
SERIAL_ECHOPGM(" mm/s) limited to ", (max_vfr * CIRCLE_AREA(filament_size[extruder] * 0.5f)));
2405-
SERIAL_ECHOPGM(" mm^3/s (", max_vfr);
2406-
SERIAL_ECHOLNPGM(" mm/s)");
2407-
//*/
2408-
}
2389+
#if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT)
2390+
const feedRate_t max_vfr = volumetric_extruder_feedrate_limit[extruder]
2391+
* TERN(HAS_MIXER_SYNC_CHANNEL, MIXING_STEPPERS, 1);
2392+
2393+
// TODO: Doesn't work properly for joined segments. Set MIN_STEPS_PER_SEGMENT 1 as workaround.
2394+
2395+
if (block->steps.a || block->steps.b || block->steps.c) {
2396+
2397+
if (max_vfr > 0 && cs > max_vfr) {
2398+
NOMORE(speed_factor, max_vfr / cs); // respect volumetric extruder limit (if any)
2399+
/* <-- add a slash to enable
2400+
SERIAL_ECHOPGM("volumetric extruder limit enforced: ", (cs * CIRCLE_AREA(filament_size[extruder] * 0.5f)));
2401+
SERIAL_ECHOPGM(" mm^3/s (", cs);
2402+
SERIAL_ECHOPGM(" mm/s) limited to ", (max_vfr * CIRCLE_AREA(filament_size[extruder] * 0.5f)));
2403+
SERIAL_ECHOPGM(" mm^3/s (", max_vfr);
2404+
SERIAL_ECHOLNPGM(" mm/s)");
2405+
//*/
24092406
}
2410-
#endif
2411-
}
2412-
#endif
2407+
}
2408+
#endif
2409+
}
2410+
#endif // HAS_EXTRUDERS
24132411

24142412
#ifdef XY_FREQUENCY_LIMIT
24152413

@@ -2492,7 +2490,7 @@ bool Planner::_populate_block(
24922490
*
24932491
* extruder_advance_K[extruder] : There is an advance factor set for this extruder.
24942492
*
2495-
* dist.e > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
2493+
* dist.e > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
24962494
*/
24972495
use_advance_lead = esteps && extruder_advance_K[E_INDEX_N(extruder)] && dist.e > 0;
24982496

@@ -2511,9 +2509,10 @@ bool Planner::_populate_block(
25112509
else {
25122510
// Scale E acceleration so that it will be possible to jump to the advance speed.
25132511
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;
2514-
if (TERN0(LA_DEBUG, accel > max_accel_steps_per_s2))
2515-
SERIAL_ECHOLNPGM("Acceleration limited.");
2516-
NOMORE(accel, max_accel_steps_per_s2);
2512+
if (accel > max_accel_steps_per_s2) {
2513+
accel = max_accel_steps_per_s2;
2514+
if (ENABLED(LA_DEBUG)) SERIAL_ECHOLNPGM("Acceleration limited.");
2515+
}
25172516
}
25182517
}
25192518
#endif
@@ -2764,104 +2763,59 @@ bool Planner::_populate_block(
27642763

27652764
prev_unit_vec = unit_vec;
27662765

2767-
#endif
2768-
2769-
#if ENABLED(CLASSIC_JERK)
2766+
#else // CLASSIC_JERK
27702767

27712768
/**
2772-
* Adapted from Průša MKS firmware
2769+
* Heavily modified. Originally adapted from Průša firmware.
27732770
* https://github.com/prusa3d/Prusa-Firmware
27742771
*/
2775-
// Exit speed limited by a jerk to full halt of a previous last segment
2776-
static float previous_safe_speed;
2777-
2778-
// Start with a safe speed (from which the machine may halt to stop immediately).
2779-
float safe_speed = block->nominal_speed;
2780-
27812772
#ifndef TRAVEL_EXTRA_XYJERK
2782-
#define TRAVEL_EXTRA_XYJERK 0
2773+
#define TRAVEL_EXTRA_XYJERK 0.0f
27832774
#endif
2784-
const float extra_xyjerk = TERN0(HAS_EXTRUDERS, dist.e <= 0) ? TRAVEL_EXTRA_XYJERK : 0;
2785-
2786-
uint8_t limited = 0;
2787-
TERN(HAS_LINEAR_E_JERK, LOOP_NUM_AXES, LOOP_LOGICAL_AXES)(i) {
2788-
const float jerk = ABS(current_speed[i]), // cs : Starting from zero, change in speed for this axis
2789-
maxj = (max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f)); // mj : The max jerk setting for this axis
2790-
if (jerk > maxj) { // cs > mj : New current speed too fast?
2791-
if (limited) { // limited already?
2792-
const float mjerk = block->nominal_speed * maxj; // ns*mj
2793-
if (jerk * safe_speed > mjerk) safe_speed = mjerk / jerk; // ns*mj/cs
2794-
}
2795-
else {
2796-
safe_speed *= maxj / jerk; // Initial limit: ns*mj/cs
2797-
++limited; // Initially limited
2798-
}
2799-
}
2800-
}
2775+
const float extra_xyjerk = TERN0(HAS_EXTRUDERS, dist.e <= 0) ? TRAVEL_EXTRA_XYJERK : 0.0f;
28012776

2802-
float vmax_junction;
2803-
if (moves_queued && !UNEAR_ZERO(previous_nominal_speed)) {
2804-
// Estimate a maximum velocity allowed at a joint of two successive segments.
2805-
// If this maximum velocity allowed is lower than the minimum of the entry / exit safe velocities,
2806-
// then the machine is not coasting anymore and the safe entry / exit velocities shall be used.
2777+
if (!moves_queued || UNEAR_ZERO(previous_nominal_speed)) {
2778+
// Compute "safe" speed, limited by a jerk to/from full halt.
28072779

2808-
// Factor to multiply the previous / current nominal velocities to get componentwise limited velocities.
2809-
float v_factor = 1;
2810-
limited = 0;
2780+
float v_factor = 1.0f;
2781+
LOOP_LOGICAL_AXES(i) {
2782+
const float jerk = ABS(current_speed[i]), // Starting from zero, change in speed for this axis
2783+
maxj = max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f); // The max jerk setting for this axis
2784+
if (jerk * v_factor > maxj) v_factor = maxj / jerk;
2785+
}
2786+
vmax_junction_sqr = sq(block->nominal_speed * v_factor);
2787+
NOLESS(minimum_planner_speed_sqr, vmax_junction_sqr);
2788+
}
2789+
else {
2790+
// Compute the maximum velocity allowed at a joint of two successive segments.
28112791

28122792
// The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
2813-
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
2814-
float smaller_speed_factor = 1.0f;
2793+
float vmax_junction, previous_speed_factor, current_speed_factor;
28152794
if (block->nominal_speed < previous_nominal_speed) {
28162795
vmax_junction = block->nominal_speed;
2817-
smaller_speed_factor = vmax_junction / previous_nominal_speed;
2796+
previous_speed_factor = vmax_junction / previous_nominal_speed;
2797+
current_speed_factor = 1.0f;
28182798
}
2819-
else
2799+
else {
28202800
vmax_junction = previous_nominal_speed;
2801+
previous_speed_factor = 1.0f;
2802+
current_speed_factor = vmax_junction / block->nominal_speed;
2803+
}
28212804

28222805
// Now limit the jerk in all axes.
2823-
TERN(HAS_LINEAR_E_JERK, LOOP_NUM_AXES, LOOP_LOGICAL_AXES)(axis) {
2824-
// Limit an axis. We have to differentiate: coasting, reversal of an axis, full stop.
2825-
float v_exit = previous_speed[axis] * smaller_speed_factor,
2826-
v_entry = current_speed[axis];
2827-
if (limited) {
2828-
v_exit *= v_factor;
2829-
v_entry *= v_factor;
2830-
}
2831-
2832-
// Calculate jerk depending on whether the axis is coasting in the same direction or reversing.
2833-
const float jerk = (v_exit > v_entry)
2834-
? // coasting axis reversal
2835-
( (v_entry > 0 || v_exit < 0) ? (v_exit - v_entry) : _MAX(v_exit, -v_entry) )
2836-
: // v_exit <= v_entry coasting axis reversal
2837-
( (v_entry < 0 || v_exit > 0) ? (v_entry - v_exit) : _MAX(-v_exit, v_entry) );
2838-
2839-
const float maxj = (max_jerk[axis] + (axis == X_AXIS || axis == Y_AXIS ? extra_xyjerk : 0.0f));
2840-
2841-
if (jerk > maxj) {
2842-
v_factor *= maxj / jerk;
2843-
++limited;
2844-
}
2806+
float v_factor = 1.0f;
2807+
LOOP_LOGICAL_AXES(i) {
2808+
// Scale per-axis velocities for the same vmax_junction.
2809+
const float v_exit = previous_speed[i] * previous_speed_factor,
2810+
v_entry = current_speed[i] * current_speed_factor;
2811+
2812+
// Jerk is the per-axis velocity difference.
2813+
const float jerk = ABS(v_exit - v_entry),
2814+
maxj = max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f);
2815+
if (jerk * v_factor > maxj) v_factor = maxj / jerk;
28452816
}
2846-
if (limited) vmax_junction *= v_factor;
2847-
// Now the transition velocity is known, which maximizes the shared exit / entry velocity while
2848-
// respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints.
2849-
const float vmax_junction_threshold = vmax_junction * 0.99f;
2850-
if (previous_safe_speed > vmax_junction_threshold && safe_speed > vmax_junction_threshold)
2851-
vmax_junction = safe_speed;
2817+
vmax_junction_sqr = sq(vmax_junction * v_factor);
28522818
}
2853-
else
2854-
vmax_junction = safe_speed;
2855-
2856-
previous_safe_speed = safe_speed;
2857-
2858-
NOLESS(minimum_planner_speed_sqr, sq(safe_speed));
2859-
2860-
#if HAS_JUNCTION_DEVIATION
2861-
NOMORE(vmax_junction_sqr, sq(vmax_junction)); // Throttle down to max speed
2862-
#else
2863-
vmax_junction_sqr = sq(vmax_junction); // Go up or down to the new speed
2864-
#endif
28652819

28662820
#endif // CLASSIC_JERK
28672821

Marlin/src/module/planner.h

+1-3
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,7 @@ class Planner {
475475
#if HAS_LINEAR_E_JERK
476476
static float max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm
477477
#endif
478-
#endif
479-
480-
#if ENABLED(CLASSIC_JERK)
478+
#else // CLASSIC_JERK
481479
// (mm/s^2) M205 XYZ(E) - The largest speed change requiring no acceleration.
482480
static TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) max_jerk;
483481
#endif

0 commit comments

Comments
 (0)