🐛 Fix planner jerk limits (#26529)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
Mihai 2023-12-14 22:16:15 +02:00 committed by GitHub
parent 75da3555ee
commit b90133813a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 124 deletions

View file

@ -297,14 +297,11 @@ void GcodeSuite::M205() {
if (parser.seenval('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units(); 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 (parser.seenval('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units();
#if HAS_JUNCTION_DEVIATION #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')) { if (parser.seenval('J')) {
const float junc_dev = parser.value_linear_units(); const float junc_dev = parser.value_linear_units();
if (WITHIN(junc_dev, 0.01f, 0.3f)) { if (WITHIN(junc_dev, 0.01f, 0.3f)) {
planner.junction_deviation_mm = junc_dev; 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 else
SERIAL_ERROR_MSG("?J out of range (0.01 to 0.3)"); SERIAL_ERROR_MSG("?J out of range (0.01 to 0.3)");

View file

@ -38,7 +38,8 @@ void GcodeSuite::G4() {
SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP); SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP);
#endif #endif
if (dwell_ms) {
if (!ui.has_status()) LCD_MESSAGE(MSG_DWELL); if (!ui.has_status()) LCD_MESSAGE(MSG_DWELL);
dwell(dwell_ms); dwell(dwell_ms);
}
} }

View file

@ -153,9 +153,7 @@ float Planner::mm_per_step[DISTINCT_AXES]; // (mm) Millimeters per step
#if HAS_LINEAR_E_JERK #if HAS_LINEAR_E_JERK
float Planner::max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm float Planner::max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm
#endif #endif
#endif #else // CLASSIC_JERK
#if ENABLED(CLASSIC_JERK)
TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) Planner::max_jerk; TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) Planner::max_jerk;
#endif #endif
@ -2409,7 +2407,7 @@ bool Planner::_populate_block(
} }
#endif #endif
} }
#endif #endif // HAS_EXTRUDERS
#ifdef XY_FREQUENCY_LIMIT #ifdef XY_FREQUENCY_LIMIT
@ -2511,9 +2509,10 @@ bool Planner::_populate_block(
else { else {
// Scale E acceleration so that it will be possible to jump to the advance speed. // 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; 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)) if (accel > max_accel_steps_per_s2) {
SERIAL_ECHOLNPGM("Acceleration limited."); accel = max_accel_steps_per_s2;
NOMORE(accel, max_accel_steps_per_s2); if (ENABLED(LA_DEBUG)) SERIAL_ECHOLNPGM("Acceleration limited.");
}
} }
} }
#endif #endif
@ -2764,104 +2763,59 @@ bool Planner::_populate_block(
prev_unit_vec = unit_vec; prev_unit_vec = unit_vec;
#endif #else // CLASSIC_JERK
#if ENABLED(CLASSIC_JERK)
/** /**
* Adapted from Průša MKS firmware * Heavily modified. Originally adapted from Průša firmware.
* https://github.com/prusa3d/Prusa-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 #ifndef TRAVEL_EXTRA_XYJERK
#define TRAVEL_EXTRA_XYJERK 0 #define TRAVEL_EXTRA_XYJERK 0.0f
#endif #endif
const float extra_xyjerk = TERN0(HAS_EXTRUDERS, dist.e <= 0) ? TRAVEL_EXTRA_XYJERK : 0; const float extra_xyjerk = TERN0(HAS_EXTRUDERS, dist.e <= 0) ? TRAVEL_EXTRA_XYJERK : 0.0f;
uint8_t limited = 0; if (!moves_queued || UNEAR_ZERO(previous_nominal_speed)) {
TERN(HAS_LINEAR_E_JERK, LOOP_NUM_AXES, LOOP_LOGICAL_AXES)(i) { // Compute "safe" speed, limited by a jerk to/from full halt.
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 float v_factor = 1.0f;
if (jerk > maxj) { // cs > mj : New current speed too fast? LOOP_LOGICAL_AXES(i) {
if (limited) { // limited already? const float jerk = ABS(current_speed[i]), // Starting from zero, change in speed for this axis
const float mjerk = block->nominal_speed * maxj; // ns*mj maxj = max_jerk[i] + (i == X_AXIS || i == Y_AXIS ? extra_xyjerk : 0.0f); // The max jerk setting for this axis
if (jerk * safe_speed > mjerk) safe_speed = mjerk / jerk; // ns*mj/cs 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 { else {
safe_speed *= maxj / jerk; // Initial limit: ns*mj/cs // Compute the maximum velocity allowed at a joint of two successive segments.
++limited; // Initially limited
}
}
}
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.
// Factor to multiply the previous / current nominal velocities to get componentwise limited velocities.
float v_factor = 1;
limited = 0;
// The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum. // 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 vmax_junction, previous_speed_factor, current_speed_factor;
float smaller_speed_factor = 1.0f;
if (block->nominal_speed < previous_nominal_speed) { if (block->nominal_speed < previous_nominal_speed) {
vmax_junction = block->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; 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. // Now limit the jerk in all axes.
TERN(HAS_LINEAR_E_JERK, LOOP_NUM_AXES, LOOP_LOGICAL_AXES)(axis) { float v_factor = 1.0f;
// Limit an axis. We have to differentiate: coasting, reversal of an axis, full stop. LOOP_LOGICAL_AXES(i) {
float v_exit = previous_speed[axis] * smaller_speed_factor, // Scale per-axis velocities for the same vmax_junction.
v_entry = current_speed[axis]; const float v_exit = previous_speed[i] * previous_speed_factor,
if (limited) { v_entry = current_speed[i] * current_speed_factor;
v_exit *= v_factor;
v_entry *= v_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;
} }
vmax_junction_sqr = sq(vmax_junction * 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;
} }
}
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;
}
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 #endif // CLASSIC_JERK

View file

@ -475,9 +475,7 @@ class Planner {
#if HAS_LINEAR_E_JERK #if HAS_LINEAR_E_JERK
static float max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm static float max_e_jerk[DISTINCT_E]; // Calculated from junction_deviation_mm
#endif #endif
#endif #else // CLASSIC_JERK
#if ENABLED(CLASSIC_JERK)
// (mm/s^2) M205 XYZ(E) - The largest speed change requiring no acceleration. // (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; static TERN(HAS_LINEAR_E_JERK, xyz_pos_t, xyze_pos_t) max_jerk;
#endif #endif