🔥 Automatic minimum planner junction speed (#26198)

This commit is contained in:
tombrazier 2023-10-27 23:06:04 +01:00 committed by GitHub
parent aa0671fb32
commit c666b492c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 32 deletions

View file

@ -1257,11 +1257,6 @@
#define XY_FREQUENCY_MIN_PERCENT 5 // (percent) Minimum FR percentage to apply. Set with M201 G<min%>. #define XY_FREQUENCY_MIN_PERCENT 5 // (percent) Minimum FR percentage to apply. Set with M201 G<min%>.
#endif #endif
// Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end
// of the buffer and all stops. This should not be much greater than zero and should only be changed
// if unwanted behavior is observed on a user's machine when running at very slow speeds.
#define MINIMUM_PLANNER_SPEED 0.05 // (mm/s)
// //
// Backlash Compensation // Backlash Compensation
// Adds extra movement to axes on direction-changes to account for backlash. // Adds extra movement to axes on direction-changes to account for backlash.
@ -4238,6 +4233,7 @@
// row. By default idle() is profiled so this shows how "idle" the processor is. // row. By default idle() is profiled so this shows how "idle" the processor is.
// See class CodeProfiler. // See class CodeProfiler.
//#define MAX7219_DEBUG_MULTISTEPPING 6 // Show multi-stepping 1 to 128 on this LED matrix row. //#define MAX7219_DEBUG_MULTISTEPPING 6 // Show multi-stepping 1 to 128 on this LED matrix row.
//#define MAX7219_DEBUG_SLOWDOWN 6 // Count (mod 16) how many times SLOWDOWN has reduced print speed.
#endif #endif
/** /**

View file

@ -735,6 +735,15 @@ void Max7219::idle_tasks() {
} }
#endif #endif
#ifdef MAX7219_DEBUG_SLOWDOWN
static uint8_t last_slowdown_count = 0;
const uint8_t slowdown_count = Planner::slowdown_count;
if (slowdown_count != last_slowdown_count) {
mark16(MAX7219_DEBUG_SLOWDOWN, last_slowdown_count, slowdown_count, &row_change_mask);
last_slowdown_count = slowdown_count;
}
#endif
// batch line updates // batch line updates
suspended--; suspended--;
if (!suspended) if (!suspended)

View file

@ -3463,8 +3463,8 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
#error "CNC_COORDINATE_SYSTEMS is incompatible with NO_WORKSPACE_OFFSETS." #error "CNC_COORDINATE_SYSTEMS is incompatible with NO_WORKSPACE_OFFSETS."
#endif #endif
#if !BLOCK_BUFFER_SIZE || !IS_POWER_OF_2(BLOCK_BUFFER_SIZE) #if !BLOCK_BUFFER_SIZE
#error "BLOCK_BUFFER_SIZE must be a power of 2." #error "BLOCK_BUFFER_SIZE must be non-zero."
#elif BLOCK_BUFFER_SIZE > 64 #elif BLOCK_BUFFER_SIZE > 64
#error "A very large BLOCK_BUFFER_SIZE is not needed and takes longer to drain the buffer on pause / cancel." #error "A very large BLOCK_BUFFER_SIZE is not needed and takes longer to drain the buffer on pause / cancel."
#endif #endif

View file

@ -188,6 +188,10 @@ float Planner::mm_per_step[DISTINCT_AXES]; // (mm) Millimeters per step
Planner::volumetric_extruder_feedrate_limit[EXTRUDERS]; // pre calculated extruder feedrate limit based on volumetric_extruder_limit; pre-calculated to reduce computation in the planner Planner::volumetric_extruder_feedrate_limit[EXTRUDERS]; // pre calculated extruder feedrate limit based on volumetric_extruder_limit; pre-calculated to reduce computation in the planner
#endif #endif
#ifdef MAX7219_DEBUG_SLOWDOWN
uint8_t Planner::slowdown_count = 0;
#endif
#if HAS_LEVELING #if HAS_LEVELING
bool Planner::leveling_active = false; // Flag that auto bed leveling is enabled bool Planner::leveling_active = false; // Flag that auto bed leveling is enabled
#if ABL_PLANAR #if ABL_PLANAR
@ -985,9 +989,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
*/ */
// The kernel called by recalculate() when scanning the plan from last to first entry. // The kernel called by recalculate() when scanning the plan from last to first entry.
void Planner::reverse_pass_kernel(block_t * const current, const block_t * const next void Planner::reverse_pass_kernel(block_t * const current, const block_t * const next, const_float_t safe_exit_speed_sqr) {
OPTARG(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)
) {
if (current) { if (current) {
// If entry speed is already at the maximum entry speed, and there was no change of speed // If entry speed is already at the maximum entry speed, and there was no change of speed
// in the next block, there is no need to recheck. Block is cruising and there is no need to // in the next block, there is no need to recheck. Block is cruising and there is no need to
@ -1007,7 +1009,7 @@ void Planner::reverse_pass_kernel(block_t * const current, const block_t * const
// the reverse and forward planners, the corresponding block junction speed will always be at the // the reverse and forward planners, the corresponding block junction speed will always be at the
// the maximum junction speed and may always be ignored for any speed reduction checks. // the maximum junction speed and may always be ignored for any speed reduction checks.
const float next_entry_speed_sqr = next ? next->entry_speed_sqr : _MAX(TERN0(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr), sq(float(MINIMUM_PLANNER_SPEED))), const float next_entry_speed_sqr = next ? next->entry_speed_sqr : safe_exit_speed_sqr,
new_entry_speed_sqr = current->flag.nominal_length new_entry_speed_sqr = current->flag.nominal_length
? max_entry_speed_sqr ? max_entry_speed_sqr
: _MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next_entry_speed_sqr, current->millimeters)); : _MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next_entry_speed_sqr, current->millimeters));
@ -1039,7 +1041,7 @@ void Planner::reverse_pass_kernel(block_t * const current, const block_t * const
* recalculate() needs to go over the current plan twice. * recalculate() needs to go over the current plan twice.
* Once in reverse and once forward. This implements the reverse pass. * Once in reverse and once forward. This implements the reverse pass.
*/ */
void Planner::reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)) { void Planner::reverse_pass(const_float_t safe_exit_speed_sqr) {
// Initialize block index to the last block in the planner buffer. // Initialize block index to the last block in the planner buffer.
uint8_t block_index = prev_block_index(block_buffer_head); uint8_t block_index = prev_block_index(block_buffer_head);
@ -1063,7 +1065,7 @@ void Planner::reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_
// Only process movement blocks // Only process movement blocks
if (current->is_move()) { if (current->is_move()) {
reverse_pass_kernel(current, next OPTARG(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr)); reverse_pass_kernel(current, next, safe_exit_speed_sqr);
next = current; next = current;
} }
@ -1176,7 +1178,7 @@ void Planner::forward_pass() {
* according to the entry_factor for each junction. Must be called by * according to the entry_factor for each junction. Must be called by
* recalculate() after updating the blocks. * recalculate() after updating the blocks.
*/ */
void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)) { void Planner::recalculate_trapezoids(const_float_t safe_exit_speed_sqr) {
// The tail may be changed by the ISR so get a local copy. // The tail may be changed by the ISR so get a local copy.
uint8_t block_index = block_buffer_tail, uint8_t block_index = block_buffer_tail,
head_block_index = block_buffer_head; head_block_index = block_buffer_head;
@ -1243,8 +1245,7 @@ void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t
// Last/newest block in buffer. Always recalculated. // Last/newest block in buffer. Always recalculated.
if (block) { if (block) {
// Exit speed is set with MINIMUM_PLANNER_SPEED unless some code higher up knows better. next_entry_speed = SQRT(safe_exit_speed_sqr);
next_entry_speed = _MAX(TERN0(HINTS_SAFE_EXIT_SPEED, SQRT(safe_exit_speed_sqr)), float(MINIMUM_PLANNER_SPEED));
// Mark the next(last) block as RECALCULATE, to prevent the Stepper ISR running it. // Mark the next(last) block as RECALCULATE, to prevent the Stepper ISR running it.
// As the last block is always recalculated here, there is a chance the block isn't // As the last block is always recalculated here, there is a chance the block isn't
@ -1267,15 +1268,15 @@ void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t
} }
} }
void Planner::recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_speed_sqr)) { void Planner::recalculate(const_float_t safe_exit_speed_sqr) {
// Initialize block index to the last block in the planner buffer. // Initialize block index to the last block in the planner buffer.
const uint8_t block_index = prev_block_index(block_buffer_head); const uint8_t block_index = prev_block_index(block_buffer_head);
// If there is just one block, no planning can be done. Avoid it! // If there is just one block, no planning can be done. Avoid it!
if (block_index != block_buffer_planned) { if (block_index != block_buffer_planned) {
reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr)); reverse_pass(safe_exit_speed_sqr);
forward_pass(); forward_pass();
} }
recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr)); recalculate_trapezoids(safe_exit_speed_sqr);
} }
/** /**
@ -1829,10 +1830,12 @@ bool Planner::_buffer_steps(const xyze_long_t &target
if (cleaning_buffer_counter) return false; if (cleaning_buffer_counter) return false;
// Fill the block with the specified movement // Fill the block with the specified movement
float minimum_planner_speed_sqr;
if (!_populate_block(block, target if (!_populate_block(block, target
OPTARG(HAS_POSITION_FLOAT, target_float) OPTARG(HAS_POSITION_FLOAT, target_float)
OPTARG(HAS_DIST_MM_ARG, cart_dist_mm) OPTARG(HAS_DIST_MM_ARG, cart_dist_mm)
, fr_mm_s, extruder, hints , fr_mm_s, extruder, hints
, minimum_planner_speed_sqr
) )
) { ) {
// Movement was not queued, probably because it was too short. // Movement was not queued, probably because it was too short.
@ -1853,8 +1856,14 @@ bool Planner::_buffer_steps(const xyze_long_t &target
// Move buffer head // Move buffer head
block_buffer_head = next_buffer_head; block_buffer_head = next_buffer_head;
// find a speed from which the new block can stop safely
const float safe_exit_speed_sqr = _MAX(
TERN0(HINTS_SAFE_EXIT_SPEED, hints.safe_exit_speed_sqr),
minimum_planner_speed_sqr
);
// Recalculate and optimize trapezoidal speed profiles // Recalculate and optimize trapezoidal speed profiles
recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, hints.safe_exit_speed_sqr)); recalculate(safe_exit_speed_sqr);
// Movement successfully queued! // Movement successfully queued!
return true; return true;
@ -1882,6 +1891,7 @@ bool Planner::_populate_block(
OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float) OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float)
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm) OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints , feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
, float &minimum_planner_speed_sqr
) { ) {
xyze_long_t dist = target - position; xyze_long_t dist = target - position;
@ -2316,6 +2326,9 @@ bool Planner::_populate_block(
#define SLOWDOWN_DIVISOR 2 #define SLOWDOWN_DIVISOR 2
#endif #endif
if (WITHIN(moves_queued, 2, (BLOCK_BUFFER_SIZE) / (SLOWDOWN_DIVISOR) - 1)) { if (WITHIN(moves_queued, 2, (BLOCK_BUFFER_SIZE) / (SLOWDOWN_DIVISOR) - 1)) {
#ifdef MAX7219_DEBUG_SLOWDOWN
slowdown_count = (slowdown_count + 1) & 0x0F;
#endif
const int32_t time_diff = settings.min_segment_time_us - segment_time_us; const int32_t time_diff = settings.min_segment_time_us - segment_time_us;
if (time_diff > 0) { if (time_diff > 0) {
// Buffer is draining so add extra time. The amount of time added increases if the buffer is still emptied more. // Buffer is draining so add extra time. The amount of time added increases if the buffer is still emptied more.
@ -2548,6 +2561,10 @@ bool Planner::_populate_block(
} }
#endif #endif
// The minimum possible speed is the average speed for
// the first / last step at current acceleration limit
minimum_planner_speed_sqr = 0.5f * block->acceleration / steps_per_mm;
float vmax_junction_sqr; // Initial limit on the segment entry velocity (mm/s)^2 float vmax_junction_sqr; // Initial limit on the segment entry velocity (mm/s)^2
#if HAS_JUNCTION_DEVIATION #if HAS_JUNCTION_DEVIATION
@ -2630,7 +2647,7 @@ bool Planner::_populate_block(
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta). // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
if (junction_cos_theta > 0.999999f) { if (junction_cos_theta > 0.999999f) {
// For a 0 degree acute junction, just set minimum junction speed. // For a 0 degree acute junction, just set minimum junction speed.
vmax_junction_sqr = sq(float(MINIMUM_PLANNER_SPEED)); vmax_junction_sqr = minimum_planner_speed_sqr;
} }
else { else {
// Convert delta vector to unit vector // Convert delta vector to unit vector
@ -2838,6 +2855,8 @@ bool Planner::_populate_block(
previous_safe_speed = safe_speed; previous_safe_speed = safe_speed;
NOLESS(minimum_planner_speed_sqr, sq(safe_speed));
#if HAS_JUNCTION_DEVIATION #if HAS_JUNCTION_DEVIATION
NOMORE(vmax_junction_sqr, sq(vmax_junction)); // Throttle down to max speed NOMORE(vmax_junction_sqr, sq(vmax_junction)); // Throttle down to max speed
#else #else
@ -2849,11 +2868,11 @@ bool Planner::_populate_block(
// Max entry speed of this block equals the max exit speed of the previous block. // Max entry speed of this block equals the max exit speed of the previous block.
block->max_entry_speed_sqr = vmax_junction_sqr; block->max_entry_speed_sqr = vmax_junction_sqr;
// Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. // Initialize block entry speed. Compute based on deceleration to sqrt(minimum_planner_speed_sqr).
const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, sq(float(MINIMUM_PLANNER_SPEED)), block->millimeters); const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, minimum_planner_speed_sqr, block->millimeters);
// Start with the minimum allowed speed // Start with the minimum allowed speed
block->entry_speed_sqr = sq(float(MINIMUM_PLANNER_SPEED)); block->entry_speed_sqr = minimum_planner_speed_sqr;
// Initialize planner efficiency flags // Initialize planner efficiency flags
// Set flag if block will always reach maximum junction speed regardless of entry/exit speeds. // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.

View file

@ -310,6 +310,14 @@ typedef struct PlannerBlock {
#define HAS_POSITION_FLOAT 1 #define HAS_POSITION_FLOAT 1
#endif #endif
constexpr uint8_t block_dec_mod(const uint8_t v1, const uint8_t v2) {
return v1 >= v2 ? v1 - v2 : v1 - v2 + BLOCK_BUFFER_SIZE;
}
constexpr uint8_t block_inc_mod(const uint8_t v1, const uint8_t v2) {
return v1 + v2 < BLOCK_BUFFER_SIZE ? v1 + v2 : v1 + v2 - BLOCK_BUFFER_SIZE;
}
#if IS_POWER_OF_2(BLOCK_BUFFER_SIZE) #if IS_POWER_OF_2(BLOCK_BUFFER_SIZE)
#define BLOCK_MOD(n) ((n)&((BLOCK_BUFFER_SIZE)-1)) #define BLOCK_MOD(n) ((n)&((BLOCK_BUFFER_SIZE)-1))
#else #else
@ -546,6 +554,11 @@ class Planner {
*/ */
static uint32_t acceleration_long_cutoff; static uint32_t acceleration_long_cutoff;
#ifdef MAX7219_DEBUG_SLOWDOWN
friend class Max7219;
static uint8_t slowdown_count;
#endif
#if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
static float last_fade_z; static float last_fade_z;
#endif #endif
@ -768,10 +781,10 @@ class Planner {
#endif // HAS_POSITION_MODIFIERS #endif // HAS_POSITION_MODIFIERS
// Number of moves currently in the planner including the busy block, if any // Number of moves currently in the planner including the busy block, if any
FORCE_INLINE static uint8_t movesplanned() { return BLOCK_MOD(block_buffer_head - block_buffer_tail); } FORCE_INLINE static uint8_t movesplanned() { return block_dec_mod(block_buffer_head, block_buffer_tail); }
// Number of nonbusy moves currently in the planner // Number of nonbusy moves currently in the planner
FORCE_INLINE static uint8_t nonbusy_movesplanned() { return BLOCK_MOD(block_buffer_head - block_buffer_nonbusy); } FORCE_INLINE static uint8_t nonbusy_movesplanned() { return block_dec_mod(block_buffer_head, block_buffer_nonbusy); }
// Remove all blocks from the buffer // Remove all blocks from the buffer
FORCE_INLINE static void clear_block_buffer() { block_buffer_nonbusy = block_buffer_planned = block_buffer_head = block_buffer_tail = 0; } FORCE_INLINE static void clear_block_buffer() { block_buffer_nonbusy = block_buffer_planned = block_buffer_head = block_buffer_tail = 0; }
@ -837,6 +850,7 @@ class Planner {
OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float) OPTARG(HAS_POSITION_FLOAT, const xyze_pos_t &target_float)
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm) OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints , feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
, float &minimum_planner_speed_sqr
); );
/** /**
@ -1029,8 +1043,8 @@ class Planner {
/** /**
* Get the index of the next / previous block in the ring buffer * Get the index of the next / previous block in the ring buffer
*/ */
static constexpr uint8_t next_block_index(const uint8_t block_index) { return BLOCK_MOD(block_index + 1); } static constexpr uint8_t next_block_index(const uint8_t block_index) { return block_inc_mod(block_index, 1); }
static constexpr uint8_t prev_block_index(const uint8_t block_index) { return BLOCK_MOD(block_index - 1); } static constexpr uint8_t prev_block_index(const uint8_t block_index) { return block_dec_mod(block_index, 1); }
/** /**
* Calculate the maximum allowable speed squared at this point, in order * Calculate the maximum allowable speed squared at this point, in order
@ -1052,15 +1066,15 @@ class Planner {
static void calculate_trapezoid_for_block(block_t * const block, const_float_t entry_factor, const_float_t exit_factor); static void calculate_trapezoid_for_block(block_t * const block, const_float_t entry_factor, const_float_t exit_factor);
static void reverse_pass_kernel(block_t * const current, const block_t * const next OPTARG(ARC_SUPPORT, const_float_t safe_exit_speed_sqr)); static void reverse_pass_kernel(block_t * const current, const block_t * const next, const_float_t safe_exit_speed_sqr);
static void forward_pass_kernel(const block_t * const previous, block_t * const current, uint8_t block_index); static void forward_pass_kernel(const block_t * const previous, block_t * const current, uint8_t block_index);
static void reverse_pass(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr)); static void reverse_pass(const_float_t safe_exit_speed_sqr);
static void forward_pass(); static void forward_pass();
static void recalculate_trapezoids(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr)); static void recalculate_trapezoids(const_float_t safe_exit_speed_sqr);
static void recalculate(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr)); static void recalculate(const_float_t safe_exit_speed_sqr);
#if HAS_JUNCTION_DEVIATION #if HAS_JUNCTION_DEVIATION