⚡️ Optimize G2-G3 Arcs (#24366)
This commit is contained in:
parent
af6995845c
commit
5c46ae4f00
|
@ -373,11 +373,12 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NOLESS(segments, 1U); // Must have at least one segment
|
NOLESS(segments, 1U); // Must have at least one segment
|
||||||
const float inv_segments = 1.0f / segments, // Reciprocal to save calculation
|
const float inv_segments = 1.0f / segments; // Reciprocal to save calculation
|
||||||
segment_xyz_mm = SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments; // Length of each segment
|
|
||||||
|
|
||||||
|
// Add hints to help optimize the move
|
||||||
|
PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment
|
||||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||||
const float inv_duration = scaled_fr_mm_s / segment_xyz_mm;
|
hints.inv_duration = scaled_fr_mm_s / hints.millimeters;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
xyze_float_t diff = total * inv_segments;
|
xyze_float_t diff = total * inv_segments;
|
||||||
|
@ -391,13 +392,9 @@
|
||||||
if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) {
|
if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) {
|
||||||
while (--segments) {
|
while (--segments) {
|
||||||
raw += diff;
|
raw += diff;
|
||||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
|
||||||
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, segment_xyz_mm
|
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints);
|
||||||
OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)
|
|
||||||
);
|
|
||||||
return false; // Did not set current from destination
|
return false; // Did not set current from destination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +463,7 @@
|
||||||
TERN_(ENABLE_LEVELING_FADE_HEIGHT, * fade_scaling_factor); // apply fade factor to interpolated height
|
TERN_(ENABLE_LEVELING_FADE_HEIGHT, * fade_scaling_factor); // apply fade factor to interpolated height
|
||||||
|
|
||||||
const float oldz = raw.z; raw.z += z_cxcy;
|
const float oldz = raw.z; raw.z += z_cxcy;
|
||||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) );
|
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
|
||||||
raw.z = oldz;
|
raw.z = oldz;
|
||||||
|
|
||||||
if (segments == 0) // done with last segment
|
if (segments == 0) // done with last segment
|
||||||
|
|
|
@ -172,8 +172,9 @@ Joystick joystick;
|
||||||
current_position += move_dist;
|
current_position += move_dist;
|
||||||
apply_motion_limits(current_position);
|
apply_motion_limits(current_position);
|
||||||
const float length = sqrt(hypot2);
|
const float length = sqrt(hypot2);
|
||||||
|
PlannerHints hints(length);
|
||||||
injecting_now = true;
|
injecting_now = true;
|
||||||
planner.buffer_line(current_position, length / seg_time, active_extruder, length);
|
planner.buffer_line(current_position, length / seg_time, active_extruder, hints);
|
||||||
injecting_now = false;
|
injecting_now = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,9 +191,12 @@ void plan_arc(
|
||||||
const uint16_t segments = nominal_segment_mm > (MAX_ARC_SEGMENT_MM) ? CEIL(flat_mm / (MAX_ARC_SEGMENT_MM)) :
|
const uint16_t segments = nominal_segment_mm > (MAX_ARC_SEGMENT_MM) ? CEIL(flat_mm / (MAX_ARC_SEGMENT_MM)) :
|
||||||
nominal_segment_mm < (MIN_ARC_SEGMENT_MM) ? _MAX(1, FLOOR(flat_mm / (MIN_ARC_SEGMENT_MM))) :
|
nominal_segment_mm < (MIN_ARC_SEGMENT_MM) ? _MAX(1, FLOOR(flat_mm / (MIN_ARC_SEGMENT_MM))) :
|
||||||
nominal_segments;
|
nominal_segments;
|
||||||
|
const float segment_mm = flat_mm / segments;
|
||||||
|
|
||||||
|
// Add hints to help optimize the move
|
||||||
|
PlannerHints hints;
|
||||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||||
const float inv_duration = (scaled_fr_mm_s / flat_mm) * segments;
|
hints.inv_duration = (scaled_fr_mm_s / flat_mm) * segments;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,6 +262,17 @@ void plan_arc(
|
||||||
int8_t arc_recalc_count = N_ARC_CORRECTION;
|
int8_t arc_recalc_count = N_ARC_CORRECTION;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// An arc can always complete within limits from a speed which...
|
||||||
|
// a) is <= any configured maximum speed,
|
||||||
|
// b) does not require centripetal force greater than any configured maximum acceleration,
|
||||||
|
// c) is <= nominal speed,
|
||||||
|
// d) allows the print head to stop in the remining length of the curve within all configured maximum accelerations.
|
||||||
|
// The last has to be calculated every time through the loop.
|
||||||
|
const float limiting_accel = _MIN(planner.settings.max_acceleration_mm_per_s2[axis_p], planner.settings.max_acceleration_mm_per_s2[axis_q]),
|
||||||
|
limiting_speed = _MIN(planner.settings.max_feedrate_mm_s[axis_p], planner.settings.max_acceleration_mm_per_s2[axis_q]),
|
||||||
|
limiting_speed_sqr = _MIN(sq(limiting_speed), limiting_accel * radius, sq(scaled_fr_mm_s));
|
||||||
|
float arc_mm_remaining = flat_mm;
|
||||||
|
|
||||||
for (uint16_t i = 1; i < segments; i++) { // Iterate (segments-1) times
|
for (uint16_t i = 1; i < segments; i++) { // Iterate (segments-1) times
|
||||||
|
|
||||||
thermalManager.task();
|
thermalManager.task();
|
||||||
|
@ -311,8 +325,14 @@ void plan_arc(
|
||||||
planner.apply_leveling(raw);
|
planner.apply_leveling(raw);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, 0 OPTARG(SCARA_FEEDRATE_SCALING, inv_duration)))
|
// calculate safe speed for stopping by the end of the arc
|
||||||
|
arc_mm_remaining -= segment_mm;
|
||||||
|
hints.safe_exit_speed_sqr = _MIN(limiting_speed_sqr, 2 * limiting_accel * arc_mm_remaining);
|
||||||
|
|
||||||
|
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
hints.curve_radius = radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +348,9 @@ void plan_arc(
|
||||||
planner.apply_leveling(raw);
|
planner.apply_leveling(raw);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, 0 OPTARG(SCARA_FEEDRATE_SCALING, inv_duration));
|
hints.curve_radius = 0;
|
||||||
|
hints.safe_exit_speed_sqr = 0.0f;
|
||||||
|
planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints);
|
||||||
|
|
||||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||||
ARC_LIJK_CODE(raw[axis_l] = start_L, raw.i = start_I, raw.j = start_J, raw.k = start_K);
|
ARC_LIJK_CODE(raw[axis_l] = start_L, raw.i = start_I, raw.j = start_J, raw.k = start_K);
|
||||||
|
|
|
@ -1013,19 +1013,18 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
|
||||||
NOLESS(segments, 1U);
|
NOLESS(segments, 1U);
|
||||||
|
|
||||||
// The approximate length of each segment
|
// The approximate length of each segment
|
||||||
const float inv_segments = 1.0f / float(segments),
|
const float inv_segments = 1.0f / float(segments);
|
||||||
cartesian_segment_mm = cartesian_mm * inv_segments;
|
|
||||||
const xyze_float_t segment_distance = diff * inv_segments;
|
const xyze_float_t segment_distance = diff * inv_segments;
|
||||||
|
|
||||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
// Add hints to help optimize the move
|
||||||
const float inv_duration = scaled_fr_mm_s / cartesian_segment_mm;
|
PlannerHints hints(cartesian_mm * inv_segments);
|
||||||
#endif
|
TERN_(SCARA_FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SERIAL_ECHOPGM("mm=", cartesian_mm);
|
SERIAL_ECHOPGM("mm=", cartesian_mm);
|
||||||
SERIAL_ECHOPGM(" seconds=", seconds);
|
SERIAL_ECHOPGM(" seconds=", seconds);
|
||||||
SERIAL_ECHOPGM(" segments=", segments);
|
SERIAL_ECHOPGM(" segments=", segments);
|
||||||
SERIAL_ECHOPGM(" segment_mm=", cartesian_segment_mm);
|
SERIAL_ECHOPGM(" segment_mm=", hints.millimeters);
|
||||||
SERIAL_EOL();
|
SERIAL_EOL();
|
||||||
//*/
|
//*/
|
||||||
|
|
||||||
|
@ -1037,11 +1036,12 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
|
||||||
while (--segments) {
|
while (--segments) {
|
||||||
segment_idle(next_idle_ms);
|
segment_idle(next_idle_ms);
|
||||||
raw += segment_distance;
|
raw += segment_distance;
|
||||||
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration))) break;
|
if (!planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure last segment arrives at target location.
|
// Ensure last segment arrives at target location.
|
||||||
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration));
|
planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints);
|
||||||
|
|
||||||
return false; // caller will update current_position
|
return false; // caller will update current_position
|
||||||
}
|
}
|
||||||
|
@ -1080,17 +1080,16 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
|
||||||
NOLESS(segments, 1U);
|
NOLESS(segments, 1U);
|
||||||
|
|
||||||
// The approximate length of each segment
|
// The approximate length of each segment
|
||||||
const float inv_segments = 1.0f / float(segments),
|
const float inv_segments = 1.0f / float(segments);
|
||||||
cartesian_segment_mm = cartesian_mm * inv_segments;
|
|
||||||
const xyze_float_t segment_distance = diff * inv_segments;
|
const xyze_float_t segment_distance = diff * inv_segments;
|
||||||
|
|
||||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
// Add hints to help optimize the move
|
||||||
const float inv_duration = scaled_fr_mm_s / cartesian_segment_mm;
|
PlannerHints hints(cartesian_mm * inv_segments);
|
||||||
#endif
|
TERN_(SCARA_FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters);
|
||||||
|
|
||||||
//SERIAL_ECHOPGM("mm=", cartesian_mm);
|
//SERIAL_ECHOPGM("mm=", cartesian_mm);
|
||||||
//SERIAL_ECHOLNPGM(" segments=", segments);
|
//SERIAL_ECHOLNPGM(" segments=", segments);
|
||||||
//SERIAL_ECHOLNPGM(" segment_mm=", cartesian_segment_mm);
|
//SERIAL_ECHOLNPGM(" segment_mm=", hints.millimeters);
|
||||||
|
|
||||||
// Get the raw current position as starting point
|
// Get the raw current position as starting point
|
||||||
xyze_pos_t raw = current_position;
|
xyze_pos_t raw = current_position;
|
||||||
|
@ -1100,12 +1099,13 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
|
||||||
while (--segments) {
|
while (--segments) {
|
||||||
segment_idle(next_idle_ms);
|
segment_idle(next_idle_ms);
|
||||||
raw += segment_distance;
|
raw += segment_distance;
|
||||||
if (!planner.buffer_line(raw, fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration))) break;
|
if (!planner.buffer_line(raw, fr_mm_s, active_extruder, hints))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since segment_distance is only approximate,
|
// Since segment_distance is only approximate,
|
||||||
// the final move must be to the exact destination.
|
// the final move must be to the exact destination.
|
||||||
planner.buffer_line(destination, fr_mm_s, active_extruder, cartesian_segment_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration));
|
planner.buffer_line(destination, fr_mm_s, active_extruder, hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // SEGMENT_LEVELED_MOVES && !AUTO_BED_LEVELING_UBL
|
#endif // SEGMENT_LEVELED_MOVES && !AUTO_BED_LEVELING_UBL
|
||||||
|
|
|
@ -843,20 +843,22 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||||
/**
|
/**
|
||||||
* Laser Trapezoid Calculations
|
* Laser Trapezoid Calculations
|
||||||
*
|
*
|
||||||
* Approximate the trapezoid with the laser, incrementing the power every `trap_ramp_entry_incr` steps while accelerating,
|
* Approximate the trapezoid with the laser, incrementing the power every `trap_ramp_entry_incr`
|
||||||
* and decrementing the power every `trap_ramp_exit_decr` while decelerating, to keep power proportional to feedrate.
|
* steps while accelerating, and decrementing the power every `trap_ramp_exit_decr` while decelerating,
|
||||||
* Laser power trap will reduce the initial power to no less than the laser_power_floor value. Based on the number
|
* to keep power proportional to feedrate. Laser power trap will reduce the initial power to no less
|
||||||
* of calculated accel/decel steps the power is distributed over the trapezoid entry- and exit-ramp steps.
|
* than the laser_power_floor value. Based on the number of calculated accel/decel steps the power is
|
||||||
|
* distributed over the trapezoid entry- and exit-ramp steps.
|
||||||
*
|
*
|
||||||
* trap_ramp_active_pwr - The active power is initially set at a reduced level factor of initial power / accel steps and
|
* trap_ramp_active_pwr - The active power is initially set at a reduced level factor of initial
|
||||||
* will be additively incremented using a trap_ramp_entry_incr value for each accel step processed later in the stepper code.
|
* power / accel steps and will be additively incremented using a trap_ramp_entry_incr value for each
|
||||||
* The trap_ramp_exit_decr value is calculated as power / decel steps and is also adjusted to no less than the power floor.
|
* accel step processed later in the stepper code. The trap_ramp_exit_decr value is calculated as
|
||||||
|
* power / decel steps and is also adjusted to no less than the power floor.
|
||||||
*
|
*
|
||||||
* If the power == 0 the inline mode variables need to be set to zero to prevent stepper processing. The method allows
|
* If the power == 0 the inline mode variables need to be set to zero to prevent stepper processing.
|
||||||
* for simpler non-powered moves like G0 or G28.
|
* The method allows for simpler non-powered moves like G0 or G28.
|
||||||
*
|
*
|
||||||
* Laser Trap Power works for all Jerk and Curve modes; however Arc-based moves will have issues since the segments are
|
* Laser Trap Power works for all Jerk and Curve modes; however Arc-based moves will have issues since
|
||||||
* usually too small.
|
* the segments are usually too small.
|
||||||
*/
|
*/
|
||||||
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
|
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
|
||||||
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
|
if (planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled) {
|
||||||
|
@ -937,20 +939,30 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||||
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
|
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
|
||||||
this requirement when encountered by the Planner::release_current_block() routine during a cycle.
|
this requirement when encountered by the Planner::release_current_block() routine during a cycle.
|
||||||
|
|
||||||
NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
|
NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
|
||||||
line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
|
segments (e.g., complex curves) may seem to move slowly. This is because there simply isn't
|
||||||
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and then
|
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and
|
||||||
decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this happens and
|
then decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this
|
||||||
becomes an annoyance, there are a few simple solutions: (1) Maximize the machine acceleration. The planner
|
happens and becomes an annoyance, there are a few simple solutions:
|
||||||
will be able to compute higher velocity profiles within the same combined distance. (2) Maximize line
|
|
||||||
motion(s) distance per block to a desired tolerance. The more combined distance the planner has to use,
|
- Maximize the machine acceleration. The planner will be able to compute higher velocity profiles
|
||||||
the faster it can go. (3) Maximize the planner buffer size. This also will increase the combined distance
|
within the same combined distance.
|
||||||
for the planner to compute over. It also increases the number of computations the planner has to perform
|
|
||||||
to compute an optimal plan, so select carefully.
|
- Maximize line motion(s) distance per block to a desired tolerance. The more combined distance the
|
||||||
|
planner has to use, the faster it can go.
|
||||||
|
|
||||||
|
- Maximize the planner buffer size. This also will increase the combined distance for the planner to
|
||||||
|
compute over. It also increases the number of computations the planner has to perform to compute an
|
||||||
|
optimal plan, so select carefully.
|
||||||
|
|
||||||
|
- Use G2/G3 arcs instead of many short segments. Arcs inform the planner of a safe exit speed at the
|
||||||
|
end of the last segment, which alleviates this problem.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 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
|
||||||
|
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
|
||||||
|
@ -970,9 +982,10 @@ 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 new_entry_speed_sqr = current->flag.nominal_length
|
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))),
|
||||||
|
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 ? next->entry_speed_sqr : sq(float(MINIMUM_PLANNER_SPEED)), current->millimeters));
|
: _MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next_entry_speed_sqr, current->millimeters));
|
||||||
if (current->entry_speed_sqr != new_entry_speed_sqr) {
|
if (current->entry_speed_sqr != new_entry_speed_sqr) {
|
||||||
|
|
||||||
// Need to recalculate the block speed - Mark it now, so the stepper
|
// Need to recalculate the block speed - Mark it now, so the stepper
|
||||||
|
@ -1001,7 +1014,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() {
|
void Planner::reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, 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);
|
||||||
|
|
||||||
|
@ -1025,7 +1038,7 @@ void Planner::reverse_pass() {
|
||||||
|
|
||||||
// Only process movement blocks
|
// Only process movement blocks
|
||||||
if (current->is_move()) {
|
if (current->is_move()) {
|
||||||
reverse_pass_kernel(current, next);
|
reverse_pass_kernel(current, next OPTARG(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr));
|
||||||
next = current;
|
next = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1138,7 +1151,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() {
|
void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, 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;
|
||||||
|
@ -1211,8 +1224,10 @@ void Planner::recalculate_trapezoids() {
|
||||||
block_index = next_block_index(block_index);
|
block_index = next_block_index(block_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
|
// Last/newest block in buffer. Always recalculated.
|
||||||
if (next) {
|
if (block) {
|
||||||
|
// Exit speed is set with MINIMUM_PLANNER_SPEED unless some code higher up knows better.
|
||||||
|
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
|
||||||
|
@ -1225,33 +1240,33 @@ void Planner::recalculate_trapezoids() {
|
||||||
if (!stepper.is_block_busy(block)) {
|
if (!stepper.is_block_busy(block)) {
|
||||||
// Block is not BUSY, we won the race against the Stepper ISR:
|
// Block is not BUSY, we won the race against the Stepper ISR:
|
||||||
|
|
||||||
const float next_nominal_speed = SQRT(next->nominal_speed_sqr),
|
const float current_nominal_speed = SQRT(block->nominal_speed_sqr),
|
||||||
nomr = 1.0f / next_nominal_speed;
|
nomr = 1.0f / current_nominal_speed;
|
||||||
calculate_trapezoid_for_block(next, next_entry_speed * nomr, float(MINIMUM_PLANNER_SPEED) * nomr);
|
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
|
||||||
#if ENABLED(LIN_ADVANCE)
|
#if ENABLED(LIN_ADVANCE)
|
||||||
if (next->use_advance_lead) {
|
if (block->use_advance_lead) {
|
||||||
const float comp = next->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
|
const float comp = block->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
|
||||||
next->max_adv_steps = next_nominal_speed * comp;
|
block->max_adv_steps = current_nominal_speed * comp;
|
||||||
next->final_adv_steps = (MINIMUM_PLANNER_SPEED) * comp;
|
block->final_adv_steps = next_entry_speed * comp;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset next only to ensure its trapezoid is computed - The stepper is free to use
|
// Reset block to ensure its trapezoid is computed - The stepper is free to use
|
||||||
// the block from now on.
|
// the block from now on.
|
||||||
next->flag.recalculate = false;
|
block->flag.recalculate = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Planner::recalculate() {
|
void Planner::recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, 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();
|
reverse_pass(TERN_(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr));
|
||||||
forward_pass();
|
forward_pass();
|
||||||
}
|
}
|
||||||
recalculate_trapezoids();
|
recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, safe_exit_speed_sqr));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1771,22 +1786,21 @@ float Planner::get_axis_position_mm(const AxisEnum axis) {
|
||||||
void Planner::synchronize() { while (busy()) idle(); }
|
void Planner::synchronize() { while (busy()) idle(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Planner::_buffer_steps
|
* @brief Add a new linear movement to the planner queue (in terms of steps).
|
||||||
*
|
*
|
||||||
* Add a new linear movement to the planner queue (in terms of steps).
|
* @param target Target position in steps units
|
||||||
|
* @param target_float Target position in direct (mm, degrees) units.
|
||||||
|
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
|
||||||
|
* @param fr_mm_s (target) speed of the move
|
||||||
|
* @param extruder target extruder
|
||||||
|
* @param hints parameters to aid planner calculations
|
||||||
*
|
*
|
||||||
* target - target position in steps units
|
* @return true if movement was properly queued, false otherwise (if cleaning)
|
||||||
* target_float - target position in direct (mm, degrees) units. optional
|
|
||||||
* fr_mm_s - (target) speed of the move
|
|
||||||
* extruder - target extruder
|
|
||||||
* millimeters - the length of the movement, if known
|
|
||||||
*
|
|
||||||
* Returns true if movement was properly queued, false otherwise (if cleaning)
|
|
||||||
*/
|
*/
|
||||||
bool Planner::_buffer_steps(const xyze_long_t &target
|
bool Planner::_buffer_steps(const xyze_long_t &target
|
||||||
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_float_t millimeters
|
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// Wait for the next available block
|
// Wait for the next available block
|
||||||
|
@ -1802,7 +1816,7 @@ bool Planner::_buffer_steps(const xyze_long_t &target
|
||||||
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, millimeters
|
, fr_mm_s, extruder, hints
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// Movement was not queued, probably because it was too short.
|
// Movement was not queued, probably because it was too short.
|
||||||
|
@ -1824,7 +1838,7 @@ bool Planner::_buffer_steps(const xyze_long_t &target
|
||||||
block_buffer_head = next_buffer_head;
|
block_buffer_head = next_buffer_head;
|
||||||
|
|
||||||
// Recalculate and optimize trapezoidal speed profiles
|
// Recalculate and optimize trapezoidal speed profiles
|
||||||
recalculate();
|
recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, hints.safe_exit_speed_sqr));
|
||||||
|
|
||||||
// Movement successfully queued!
|
// Movement successfully queued!
|
||||||
return true;
|
return true;
|
||||||
|
@ -1842,8 +1856,7 @@ bool Planner::_buffer_steps(const xyze_long_t &target
|
||||||
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
|
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
|
||||||
* @param fr_mm_s (target) speed of the move
|
* @param fr_mm_s (target) speed of the move
|
||||||
* @param extruder target extruder
|
* @param extruder target extruder
|
||||||
* @param millimeters A pre-calculated linear distance for the move, in mm,
|
* @param hints parameters to aid planner calculations
|
||||||
* or 0.0 to have the distance calculated here.
|
|
||||||
*
|
*
|
||||||
* @return true if movement is acceptable, false otherwise
|
* @return true if movement is acceptable, false otherwise
|
||||||
*/
|
*/
|
||||||
|
@ -1852,7 +1865,7 @@ bool Planner::_populate_block(
|
||||||
const abce_long_t &target
|
const abce_long_t &target
|
||||||
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_float_t millimeters/*=0.0*/
|
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
|
||||||
) {
|
) {
|
||||||
int32_t LOGICAL_AXIS_LIST(
|
int32_t LOGICAL_AXIS_LIST(
|
||||||
de = target.e - position.e,
|
de = target.e - position.e,
|
||||||
|
@ -2107,8 +2120,8 @@ bool Planner::_populate_block(
|
||||||
block->millimeters = TERN0(HAS_EXTRUDERS, ABS(steps_dist_mm.e));
|
block->millimeters = TERN0(HAS_EXTRUDERS, ABS(steps_dist_mm.e));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (millimeters)
|
if (hints.millimeters)
|
||||||
block->millimeters = millimeters;
|
block->millimeters = hints.millimeters;
|
||||||
else {
|
else {
|
||||||
block->millimeters = SQRT(
|
block->millimeters = SQRT(
|
||||||
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
|
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
|
||||||
|
@ -2475,23 +2488,15 @@ bool Planner::_populate_block(
|
||||||
if (block->step_event_count <= acceleration_long_cutoff) {
|
if (block->step_event_count <= acceleration_long_cutoff) {
|
||||||
LOGICAL_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
LIMIT_ACCEL_LONG(E_AXIS, E_INDEX_N(extruder)),
|
LIMIT_ACCEL_LONG(E_AXIS, E_INDEX_N(extruder)),
|
||||||
LIMIT_ACCEL_LONG(A_AXIS, 0),
|
LIMIT_ACCEL_LONG(A_AXIS, 0), LIMIT_ACCEL_LONG(B_AXIS, 0), LIMIT_ACCEL_LONG(C_AXIS, 0),
|
||||||
LIMIT_ACCEL_LONG(B_AXIS, 0),
|
LIMIT_ACCEL_LONG(I_AXIS, 0), LIMIT_ACCEL_LONG(J_AXIS, 0), LIMIT_ACCEL_LONG(K_AXIS, 0)
|
||||||
LIMIT_ACCEL_LONG(C_AXIS, 0),
|
|
||||||
LIMIT_ACCEL_LONG(I_AXIS, 0),
|
|
||||||
LIMIT_ACCEL_LONG(J_AXIS, 0),
|
|
||||||
LIMIT_ACCEL_LONG(K_AXIS, 0)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOGICAL_AXIS_CODE(
|
LOGICAL_AXIS_CODE(
|
||||||
LIMIT_ACCEL_FLOAT(E_AXIS, E_INDEX_N(extruder)),
|
LIMIT_ACCEL_FLOAT(E_AXIS, E_INDEX_N(extruder)),
|
||||||
LIMIT_ACCEL_FLOAT(A_AXIS, 0),
|
LIMIT_ACCEL_FLOAT(A_AXIS, 0), LIMIT_ACCEL_FLOAT(B_AXIS, 0), LIMIT_ACCEL_FLOAT(C_AXIS, 0),
|
||||||
LIMIT_ACCEL_FLOAT(B_AXIS, 0),
|
LIMIT_ACCEL_FLOAT(I_AXIS, 0), LIMIT_ACCEL_FLOAT(J_AXIS, 0), LIMIT_ACCEL_FLOAT(K_AXIS, 0)
|
||||||
LIMIT_ACCEL_FLOAT(C_AXIS, 0),
|
|
||||||
LIMIT_ACCEL_FLOAT(I_AXIS, 0),
|
|
||||||
LIMIT_ACCEL_FLOAT(J_AXIS, 0),
|
|
||||||
LIMIT_ACCEL_FLOAT(K_AXIS, 0)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2577,7 +2582,7 @@ bool Planner::_populate_block(
|
||||||
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
|
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
|
||||||
float junction_cos_theta = LOGICAL_AXIS_GANG(
|
float junction_cos_theta = LOGICAL_AXIS_GANG(
|
||||||
+ (-prev_unit_vec.e * unit_vec.e),
|
+ (-prev_unit_vec.e * unit_vec.e),
|
||||||
(-prev_unit_vec.x * unit_vec.x),
|
+ (-prev_unit_vec.x * unit_vec.x),
|
||||||
+ (-prev_unit_vec.y * unit_vec.y),
|
+ (-prev_unit_vec.y * unit_vec.y),
|
||||||
+ (-prev_unit_vec.z * unit_vec.z),
|
+ (-prev_unit_vec.z * unit_vec.z),
|
||||||
+ (-prev_unit_vec.i * unit_vec.i),
|
+ (-prev_unit_vec.i * unit_vec.i),
|
||||||
|
@ -2591,14 +2596,19 @@ bool Planner::_populate_block(
|
||||||
vmax_junction_sqr = sq(float(MINIMUM_PLANNER_SPEED));
|
vmax_junction_sqr = sq(float(MINIMUM_PLANNER_SPEED));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
NOLESS(junction_cos_theta, -0.999999f); // Check for numerical round-off to avoid divide by zero.
|
|
||||||
|
|
||||||
// Convert delta vector to unit vector
|
// Convert delta vector to unit vector
|
||||||
xyze_float_t junction_unit_vec = unit_vec - prev_unit_vec;
|
xyze_float_t junction_unit_vec = unit_vec - prev_unit_vec;
|
||||||
normalize_junction_vector(junction_unit_vec);
|
normalize_junction_vector(junction_unit_vec);
|
||||||
|
|
||||||
const float junction_acceleration = limit_value_by_axis_maximum(block->acceleration, junction_unit_vec),
|
const float junction_acceleration = limit_value_by_axis_maximum(block->acceleration, junction_unit_vec);
|
||||||
sin_theta_d2 = SQRT(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive.
|
|
||||||
|
if (TERN0(HINTS_CURVE_RADIUS, hints.curve_radius)) {
|
||||||
|
TERN_(HINTS_CURVE_RADIUS, vmax_junction_sqr = junction_acceleration * hints.curve_radius);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NOLESS(junction_cos_theta, -0.999999f); // Check for numerical round-off to avoid divide by zero.
|
||||||
|
|
||||||
|
const float sin_theta_d2 = SQRT(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive.
|
||||||
|
|
||||||
vmax_junction_sqr = junction_acceleration * junction_deviation_mm * sin_theta_d2 / (1.0f - sin_theta_d2);
|
vmax_junction_sqr = junction_acceleration * junction_deviation_mm * sin_theta_d2 / (1.0f - sin_theta_d2);
|
||||||
|
|
||||||
|
@ -2690,6 +2700,7 @@ bool Planner::_populate_block(
|
||||||
|
|
||||||
#endif // JD_HANDLE_SMALL_SEGMENTS
|
#endif // JD_HANDLE_SMALL_SEGMENTS
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get the lowest speed
|
// Get the lowest speed
|
||||||
vmax_junction_sqr = _MIN(vmax_junction_sqr, block->nominal_speed_sqr, previous_nominal_speed_sqr);
|
vmax_junction_sqr = _MIN(vmax_junction_sqr, block->nominal_speed_sqr, previous_nominal_speed_sqr);
|
||||||
|
@ -2848,12 +2859,11 @@ bool Planner::_populate_block(
|
||||||
} // _populate_block()
|
} // _populate_block()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Planner::buffer_sync_block
|
* @brief Add a block to the buffer that just updates the position
|
||||||
* Add a block to the buffer that just updates the position
|
|
||||||
* @param sync_flag BLOCK_FLAG_SYNC_FANS & BLOCK_FLAG_LASER_PWR
|
|
||||||
* Supports LASER_SYNCHRONOUS_M106_M107 and LASER_POWER_SYNC power sync block buffer queueing.
|
* Supports LASER_SYNCHRONOUS_M106_M107 and LASER_POWER_SYNC power sync block buffer queueing.
|
||||||
|
*
|
||||||
|
* @param sync_flag The sync flag to set, determining the type of sync the block will do
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void Planner::buffer_sync_block(const BlockFlagBit sync_flag/*=BLOCK_BIT_SYNC_POSITION*/) {
|
void Planner::buffer_sync_block(const BlockFlagBit sync_flag/*=BLOCK_BIT_SYNC_POSITION*/) {
|
||||||
|
|
||||||
// Wait for the next available block
|
// Wait for the next available block
|
||||||
|
@ -2861,14 +2871,13 @@ void Planner::buffer_sync_block(const BlockFlagBit sync_flag/*=BLOCK_BIT_SYNC_PO
|
||||||
block_t * const block = get_next_free_block(next_buffer_head);
|
block_t * const block = get_next_free_block(next_buffer_head);
|
||||||
|
|
||||||
// Clear block
|
// Clear block
|
||||||
memset(block, 0, sizeof(block_t));
|
block->reset();
|
||||||
block->flag.apply(sync_flag);
|
block->flag.apply(sync_flag);
|
||||||
|
|
||||||
block->position = position;
|
block->position = position;
|
||||||
#if ENABLED(BACKLASH_COMPENSATION)
|
#if ENABLED(BACKLASH_COMPENSATION)
|
||||||
LOOP_LINEAR_AXES(axis) block->position[axis] += backlash.get_applied_steps((AxisEnum)axis);
|
LOOP_LINEAR_AXES(axis) block->position[axis] += backlash.get_applied_steps((AxisEnum)axis);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if BOTH(HAS_FAN, LASER_SYNCHRONOUS_M106_M107)
|
#if BOTH(HAS_FAN, LASER_SYNCHRONOUS_M106_M107)
|
||||||
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
|
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
|
||||||
#endif
|
#endif
|
||||||
|
@ -2895,22 +2904,24 @@ void Planner::buffer_sync_block(const BlockFlagBit sync_flag/*=BLOCK_BIT_SYNC_PO
|
||||||
} // buffer_sync_block()
|
} // buffer_sync_block()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Planner::buffer_segment
|
* @brief Add a single linear movement
|
||||||
*
|
*
|
||||||
* Add a new linear movement to the buffer in axis units.
|
* @description Add a new linear movement to the buffer in axis units.
|
||||||
|
* Leveling and kinematics should be applied before calling this.
|
||||||
*
|
*
|
||||||
* Leveling and kinematics should be applied ahead of calling this.
|
* @param abce Target position in mm and/or degrees
|
||||||
|
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
|
||||||
|
* @param fr_mm_s (target) speed of the move
|
||||||
|
* @param extruder optional target extruder (otherwise active_extruder)
|
||||||
|
* @param hints optional parameters to aid planner calculations
|
||||||
*
|
*
|
||||||
* a,b,c,e - target positions in mm and/or degrees
|
* @return false if no segment was queued due to cleaning, cold extrusion, full queue, etc.
|
||||||
* fr_mm_s - (target) speed of the move
|
|
||||||
* extruder - target extruder
|
|
||||||
* millimeters - the length of the movement, if known
|
|
||||||
*
|
|
||||||
* Return 'false' if no segment was queued due to cleaning, cold extrusion, full queue, etc.
|
|
||||||
*/
|
*/
|
||||||
bool Planner::buffer_segment(const abce_pos_t &abce
|
bool Planner::buffer_segment(const abce_pos_t &abce
|
||||||
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
|
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
|
||||||
, const_feedRate_t fr_mm_s, const uint8_t extruder/*=active_extruder*/, const_float_t millimeters/*=0.0*/
|
, const_feedRate_t fr_mm_s
|
||||||
|
, const uint8_t extruder/*=active_extruder*/
|
||||||
|
, const PlannerHints &hints/*=PlannerHints()*/
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// If we are cleaning, do not accept queuing of movements
|
// If we are cleaning, do not accept queuing of movements
|
||||||
|
@ -2998,8 +3009,8 @@ bool Planner::buffer_segment(const abce_pos_t &abce
|
||||||
if (!_buffer_steps(target
|
if (!_buffer_steps(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, millimeters)
|
, fr_mm_s, extruder, hints
|
||||||
) return false;
|
)) return false;
|
||||||
|
|
||||||
stepper.wake_up();
|
stepper.wake_up();
|
||||||
return true;
|
return true;
|
||||||
|
@ -3012,12 +3023,12 @@ bool Planner::buffer_segment(const abce_pos_t &abce
|
||||||
*
|
*
|
||||||
* cart - target position in mm or degrees
|
* cart - target position in mm or degrees
|
||||||
* fr_mm_s - (target) speed of the move (mm/s)
|
* fr_mm_s - (target) speed of the move (mm/s)
|
||||||
* extruder - target extruder
|
* extruder - optional target extruder (otherwise active_extruder)
|
||||||
* millimeters - the length of the movement, if known
|
* hints - optional parameters to aid planner calculations
|
||||||
* inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)
|
|
||||||
*/
|
*/
|
||||||
bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, const uint8_t extruder/*=active_extruder*/, const float millimeters/*=0.0*/
|
bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s
|
||||||
OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration/*=0.0*/)
|
, const uint8_t extruder/*=active_extruder*/
|
||||||
|
, const PlannerHints &hints/*=PlannerHints()*/
|
||||||
) {
|
) {
|
||||||
xyze_pos_t machine = cart;
|
xyze_pos_t machine = cart;
|
||||||
TERN_(HAS_POSITION_MODIFIERS, apply_modifiers(machine));
|
TERN_(HAS_POSITION_MODIFIERS, apply_modifiers(machine));
|
||||||
|
@ -3037,28 +3048,30 @@ bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, cons
|
||||||
);
|
);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const float mm = millimeters ?: (cart_dist_mm.x || cart_dist_mm.y) ? cart_dist_mm.magnitude() : TERN0(HAS_Z_AXIS, ABS(cart_dist_mm.z));
|
|
||||||
|
|
||||||
// Cartesian XYZ to kinematic ABC, stored in global 'delta'
|
// Cartesian XYZ to kinematic ABC, stored in global 'delta'
|
||||||
inverse_kinematics(machine);
|
inverse_kinematics(machine);
|
||||||
|
|
||||||
|
PlannerHints ph = hints;
|
||||||
|
if (!hints.millimeters)
|
||||||
|
ph.millimeters = (cart_dist_mm.x || cart_dist_mm.y) ? cart_dist_mm.magnitude() : TERN0(HAS_Z_AXIS, ABS(cart_dist_mm.z));
|
||||||
|
|
||||||
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||||
// For SCARA scale the feedrate from mm/s to degrees/s
|
// For SCARA scale the feedrate from mm/s to degrees/s
|
||||||
// i.e., Complete the angular vector in the given time.
|
// i.e., Complete the angular vector in the given time.
|
||||||
const float duration_recip = inv_duration ?: fr_mm_s / mm;
|
const float duration_recip = hints.inv_duration ?: fr_mm_s / ph.millimeters;
|
||||||
const xyz_pos_t diff = delta - position_float;
|
const xyz_pos_t diff = delta - position_float;
|
||||||
const feedRate_t feedrate = diff.magnitude() * duration_recip;
|
const feedRate_t feedrate = diff.magnitude() * duration_recip;
|
||||||
#else
|
#else
|
||||||
const feedRate_t feedrate = fr_mm_s;
|
const feedRate_t feedrate = fr_mm_s;
|
||||||
#endif
|
#endif
|
||||||
TERN_(HAS_EXTRUDERS, delta.e = machine.e);
|
TERN_(HAS_EXTRUDERS, delta.e = machine.e);
|
||||||
if (buffer_segment(delta OPTARG(HAS_DIST_MM_ARG, cart_dist_mm), feedrate, extruder, mm)) {
|
if (buffer_segment(delta OPTARG(HAS_DIST_MM_ARG, cart_dist_mm), feedrate, extruder, ph)) {
|
||||||
position_cart = cart;
|
position_cart = cart;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
return buffer_segment(machine, fr_mm_s, extruder, millimeters);
|
return buffer_segment(machine, fr_mm_s, extruder, hints);
|
||||||
#endif
|
#endif
|
||||||
} // buffer_line()
|
} // buffer_line()
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,8 @@ typedef struct PlannerBlock {
|
||||||
block_laser_t laser;
|
block_laser_t laser;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void reset() { memset((char*)this, 0, sizeof(*this)); }
|
||||||
|
|
||||||
} block_t;
|
} block_t;
|
||||||
|
|
||||||
#if ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL, POWER_LOSS_RECOVERY)
|
#if ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL, POWER_LOSS_RECOVERY)
|
||||||
|
@ -348,6 +350,30 @@ typedef struct {
|
||||||
typedef IF<(BLOCK_BUFFER_SIZE > 64), uint16_t, uint8_t>::type last_move_t;
|
typedef IF<(BLOCK_BUFFER_SIZE > 64), uint16_t, uint8_t>::type last_move_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENABLED(ARC_SUPPORT)
|
||||||
|
#define HINTS_CURVE_RADIUS
|
||||||
|
#define HINTS_SAFE_EXIT_SPEED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct PlannerHints {
|
||||||
|
float millimeters = 0.0; // Move Length, if known, else 0.
|
||||||
|
#if ENABLED(SCARA_FEEDRATE_SCALING)
|
||||||
|
float inv_duration = 0.0; // Reciprocal of the move duration, if known
|
||||||
|
#endif
|
||||||
|
#if ENABLED(HINTS_CURVE_RADIUS)
|
||||||
|
float curve_radius = 0.0; // Radius of curvature of the motion path - to calculate cornering speed
|
||||||
|
#else
|
||||||
|
static constexpr float curve_radius = 0.0;
|
||||||
|
#endif
|
||||||
|
#if ENABLED(HINTS_SAFE_EXIT_SPEED)
|
||||||
|
float safe_exit_speed_sqr = 0.0; // Square of the speed considered "safe" at the end of the segment
|
||||||
|
// i.e., at or below the exit speed of the segment that the planner
|
||||||
|
// would calculate if it knew the as-yet-unbuffered path
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PlannerHints(const_float_t mm=0.0f) : millimeters(mm) {}
|
||||||
|
};
|
||||||
|
|
||||||
class Planner {
|
class Planner {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -751,14 +777,14 @@ class Planner {
|
||||||
* target - target position in steps units
|
* target - target position in steps units
|
||||||
* fr_mm_s - (target) speed of the move
|
* fr_mm_s - (target) speed of the move
|
||||||
* extruder - target extruder
|
* extruder - target extruder
|
||||||
* millimeters - the length of the movement, if known
|
* hints - parameters to aid planner calculations
|
||||||
*
|
*
|
||||||
* Returns true if movement was buffered, false otherwise
|
* Returns true if movement was buffered, false otherwise
|
||||||
*/
|
*/
|
||||||
static bool _buffer_steps(const xyze_long_t &target
|
static bool _buffer_steps(const xyze_long_t &target
|
||||||
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_float_t millimeters=0.0
|
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -773,15 +799,14 @@ class Planner {
|
||||||
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
|
* @param cart_dist_mm The pre-calculated move lengths for all axes, in mm
|
||||||
* @param fr_mm_s (target) speed of the move
|
* @param fr_mm_s (target) speed of the move
|
||||||
* @param extruder target extruder
|
* @param extruder target extruder
|
||||||
* @param millimeters A pre-calculated linear distance for the move, in mm,
|
* @param hints parameters to aid planner calculations
|
||||||
* or 0.0 to have the distance calculated here.
|
|
||||||
*
|
*
|
||||||
* @return true if movement is acceptable, false otherwise
|
* @return true if movement is acceptable, false otherwise
|
||||||
*/
|
*/
|
||||||
static bool _populate_block(block_t * const block, const xyze_long_t &target
|
static bool _populate_block(block_t * const block, const xyze_long_t &target
|
||||||
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_float_t millimeters=0.0
|
, feedRate_t fr_mm_s, const uint8_t extruder, const PlannerHints &hints
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -808,12 +833,14 @@ class Planner {
|
||||||
*
|
*
|
||||||
* a,b,c,e - target positions in mm and/or degrees
|
* a,b,c,e - target positions in mm and/or degrees
|
||||||
* fr_mm_s - (target) speed of the move
|
* fr_mm_s - (target) speed of the move
|
||||||
* extruder - target extruder
|
* extruder - optional target extruder (otherwise active_extruder)
|
||||||
* millimeters - the length of the movement, if known
|
* hints - optional parameters to aid planner calculations
|
||||||
*/
|
*/
|
||||||
static bool buffer_segment(const abce_pos_t &abce
|
static bool buffer_segment(const abce_pos_t &abce
|
||||||
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
|
OPTARG(HAS_DIST_MM_ARG, const xyze_float_t &cart_dist_mm)
|
||||||
, const_feedRate_t fr_mm_s, const uint8_t extruder=active_extruder, const_float_t millimeters=0.0
|
, const_feedRate_t fr_mm_s
|
||||||
|
, const uint8_t extruder=active_extruder
|
||||||
|
, const PlannerHints &hints=PlannerHints()
|
||||||
);
|
);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -825,12 +852,12 @@ class Planner {
|
||||||
*
|
*
|
||||||
* cart - target position in mm or degrees
|
* cart - target position in mm or degrees
|
||||||
* fr_mm_s - (target) speed of the move (mm/s)
|
* fr_mm_s - (target) speed of the move (mm/s)
|
||||||
* extruder - target extruder
|
* extruder - optional target extruder (otherwise active_extruder)
|
||||||
* millimeters - the length of the movement, if known
|
* hints - optional parameters to aid planner calculations
|
||||||
* inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)
|
|
||||||
*/
|
*/
|
||||||
static bool buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s, const uint8_t extruder=active_extruder, const float millimeters=0.0
|
static bool buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s
|
||||||
OPTARG(SCARA_FEEDRATE_SCALING, const_float_t inv_duration=0.0)
|
, const uint8_t extruder=active_extruder
|
||||||
|
, const PlannerHints &hints=PlannerHints()
|
||||||
);
|
);
|
||||||
|
|
||||||
#if ENABLED(DIRECT_STEPPING)
|
#if ENABLED(DIRECT_STEPPING)
|
||||||
|
@ -1022,15 +1049,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);
|
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 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();
|
static void reverse_pass(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
|
||||||
static void forward_pass();
|
static void forward_pass();
|
||||||
|
|
||||||
static void recalculate_trapezoids();
|
static void recalculate_trapezoids(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
|
||||||
|
|
||||||
static void recalculate();
|
static void recalculate(TERN_(ARC_SUPPORT, const_float_t safe_exit_speed_sqr));
|
||||||
|
|
||||||
#if HAS_JUNCTION_DEVIATION
|
#if HAS_JUNCTION_DEVIATION
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,9 @@ void cubic_b_spline(
|
||||||
|
|
||||||
millis_t next_idle_ms = millis() + 200UL;
|
millis_t next_idle_ms = millis() + 200UL;
|
||||||
|
|
||||||
|
// Hints to help optimize the move
|
||||||
|
PlannerHints hints;
|
||||||
|
|
||||||
for (float t = 0; t < 1;) {
|
for (float t = 0; t < 1;) {
|
||||||
|
|
||||||
thermalManager.task();
|
thermalManager.task();
|
||||||
|
@ -177,7 +180,7 @@ void cubic_b_spline(
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
step = new_t - t;
|
hints.millimeters = new_t - t;
|
||||||
t = new_t;
|
t = new_t;
|
||||||
|
|
||||||
// Compute and send new position
|
// Compute and send new position
|
||||||
|
@ -200,7 +203,7 @@ void cubic_b_spline(
|
||||||
const xyze_pos_t &pos = bez_target;
|
const xyze_pos_t &pos = bez_target;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!planner.buffer_line(pos, scaled_fr_mm_s, active_extruder, step))
|
if (!planner.buffer_line(pos, scaled_fr_mm_s, active_extruder, hints))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1958,12 +1958,13 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
else if (LA_steps) nextAdvanceISR = 0;
|
else if (LA_steps) nextAdvanceISR = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Adjust Laser Power - Accelerating
|
* Adjust Laser Power - Accelerating
|
||||||
|
*
|
||||||
* isPowered - True when a move is powered.
|
* isPowered - True when a move is powered.
|
||||||
* isEnabled - laser power is active.
|
* isEnabled - laser power is active.
|
||||||
* Laser power variables are calulated and stored in this block by the planner code.
|
|
||||||
*
|
*
|
||||||
|
* Laser power variables are calulated and stored in this block by the planner code.
|
||||||
* trap_ramp_active_pwr - the active power in this block across accel or decel trap steps.
|
* trap_ramp_active_pwr - the active power in this block across accel or decel trap steps.
|
||||||
* trap_ramp_entry_incr - holds the precalculated value to increase the current power per accel step.
|
* trap_ramp_entry_incr - holds the precalculated value to increase the current power per accel step.
|
||||||
*
|
*
|
||||||
|
@ -1988,6 +1989,7 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
uint32_t step_rate;
|
uint32_t step_rate;
|
||||||
|
|
||||||
#if ENABLED(S_CURVE_ACCELERATION)
|
#if ENABLED(S_CURVE_ACCELERATION)
|
||||||
|
|
||||||
// If this is the 1st time we process the 2nd half of the trapezoid...
|
// If this is the 1st time we process the 2nd half of the trapezoid...
|
||||||
if (!bezier_2nd_half) {
|
if (!bezier_2nd_half) {
|
||||||
// Initialize the Bézier speed curve
|
// Initialize the Bézier speed curve
|
||||||
|
@ -2002,6 +2004,7 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
? _eval_bezier_curve(deceleration_time)
|
? _eval_bezier_curve(deceleration_time)
|
||||||
: current_block->final_rate;
|
: current_block->final_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// Using the old trapezoidal control
|
// Using the old trapezoidal control
|
||||||
step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate);
|
step_rate = STEP_MULTIPLY(deceleration_time, current_block->acceleration_rate);
|
||||||
|
@ -2011,9 +2014,8 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
step_rate = current_block->final_rate;
|
step_rate = current_block->final_rate;
|
||||||
#endif
|
|
||||||
|
|
||||||
// step_rate is in steps/second
|
#endif
|
||||||
|
|
||||||
// step_rate to timer interval and steps per stepper isr
|
// step_rate to timer interval and steps per stepper isr
|
||||||
interval = calc_timer_interval(step_rate, &steps_per_isr);
|
interval = calc_timer_interval(step_rate, &steps_per_isr);
|
||||||
|
@ -2065,10 +2067,10 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
interval = ticks_nominal;
|
interval = ticks_nominal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adjust Laser Power - Cruise
|
/**
|
||||||
|
* Adjust Laser Power - Cruise
|
||||||
* power - direct or floor adjusted active laser power.
|
* power - direct or floor adjusted active laser power.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if ENABLED(LASER_POWER_TRAP)
|
#if ENABLED(LASER_POWER_TRAP)
|
||||||
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
|
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS) {
|
||||||
if (step_events_completed + 1 == accelerate_until) {
|
if (step_events_completed + 1 == accelerate_until) {
|
||||||
|
@ -2086,7 +2088,7 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLED(LASER_FEATURE)
|
#if ENABLED(LASER_FEATURE)
|
||||||
/*
|
/**
|
||||||
* CUTTER_MODE_DYNAMIC is experimental and developing.
|
* CUTTER_MODE_DYNAMIC is experimental and developing.
|
||||||
* Super-fast method to dynamically adjust the laser power OCR value based on the input feedrate in mm-per-minute.
|
* Super-fast method to dynamically adjust the laser power OCR value based on the input feedrate in mm-per-minute.
|
||||||
* TODO: Set up Min/Max OCR offsets to allow tuning and scaling of various lasers.
|
* TODO: Set up Min/Max OCR offsets to allow tuning and scaling of various lasers.
|
||||||
|
@ -2103,9 +2105,8 @@ uint32_t Stepper::block_phase_isr() {
|
||||||
}
|
}
|
||||||
else { // !current_block
|
else { // !current_block
|
||||||
#if ENABLED(LASER_FEATURE)
|
#if ENABLED(LASER_FEATURE)
|
||||||
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC) {
|
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC)
|
||||||
cutter.apply_power(0); // No movement in dynamic mode so turn Laser off
|
cutter.apply_power(0); // No movement in dynamic mode so turn Laser off
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue