mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-20 11:53:48 +00:00
Fix extrusion rate smoothing unnecessarily slowing down the entire line (#11249)
* Fix typo & code style * Fix calculation of the min acc time * Avoid slowing down the entire line if both end of a single line has been slowed down
This commit is contained in:
@@ -491,21 +491,143 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
|
|||||||
// Orca:
|
// Orca:
|
||||||
// Calculate the absolute difference in volumetric extrusion rate between the start and end point of the line.
|
// Calculate the absolute difference in volumetric extrusion rate between the start and end point of the line.
|
||||||
// Quantize it to 1mm3/min (0.016mm3/sec).
|
// Quantize it to 1mm3/min (0.016mm3/sec).
|
||||||
int delta_volumetric_rate = std::round(fabs(line.volumetric_extrusion_rate_end - line.volumetric_extrusion_rate_start));
|
int delta_volumetric_rate = std::round(std::max({
|
||||||
|
fabs(line.volumetric_extrusion_rate_end - line.volumetric_extrusion_rate_start),
|
||||||
|
// For line with accel-then-decel, we also calc the max difference to the peak
|
||||||
|
fabs(line.volumetric_extrusion_rate - line.volumetric_extrusion_rate_start),
|
||||||
|
fabs(line.volumetric_extrusion_rate - line.volumetric_extrusion_rate_end),
|
||||||
|
}));
|
||||||
|
|
||||||
// Emit the line with lowered extrusion rates.
|
// Emit the line with lowered extrusion rates.
|
||||||
// Orca:
|
// Orca:
|
||||||
// First, check if the change in volumetric extrusion rate is trivial (less than 10mm3/min -> 0.16mm3/sec (5mm/sec speed for a 0.25 mm nozzle).
|
// First, check if the change in volumetric extrusion rate is trivial (less than 10mm3/min -> 0.16mm3/sec (5mm/sec speed for a 0.25 mm nozzle).
|
||||||
// Or if the line size is equal in length with the smallest segment.
|
// Or if the line size is equal in length with the smallest segment.
|
||||||
// If so, then emit the line as a single extrusion, i.e. dont split into segments.
|
// If so, then emit the line as a single extrusion, i.e. dont split into segments.
|
||||||
if ( nSegments == 1 || delta_volumetric_rate < 10) {
|
constexpr int NON_TRIVIAL_RATE_DELTA = 10;
|
||||||
|
if (nSegments == 1 || delta_volumetric_rate < NON_TRIVIAL_RATE_DELTA) {
|
||||||
push_line_to_output(line_idx, line.feedrate() * line.volumetric_correction_avg(), comment);
|
push_line_to_output(line_idx, line.feedrate() * line.volumetric_correction_avg(), comment);
|
||||||
} else // The line needs to be split the line into segments and apply extrusion rate smoothing
|
} else // The line needs to be split the line into segments and apply extrusion rate smoothing
|
||||||
{
|
{
|
||||||
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
const float original_feedrate = line.feedrate();
|
||||||
// Update the initial and final feed rate values.
|
// Update the initial and final feed rate values.
|
||||||
line.pos_start[4] = line.volumetric_extrusion_rate_start * line.pos_end[4] / line.volumetric_extrusion_rate;
|
line.pos_start[4] = line.volumetric_extrusion_rate_start * line.pos_end[4] / line.volumetric_extrusion_rate;
|
||||||
line.pos_end [4] = line.volumetric_extrusion_rate_end * line.pos_end[4] / line.volumetric_extrusion_rate;
|
line.pos_end [4] = line.volumetric_extrusion_rate_end * line.pos_end[4] / line.volumetric_extrusion_rate;
|
||||||
|
|
||||||
|
// Handle special case where both start & end extrusion rates are smaller than the original extrusion rate,
|
||||||
|
// which means we need to do an accel-then-decel movements to achieve potentially max print speed
|
||||||
|
if (line.volumetric_extrusion_rate > line.volumetric_extrusion_rate_start &&
|
||||||
|
line.volumetric_extrusion_rate > line.volumetric_extrusion_rate_end) {
|
||||||
|
// total extrusion amount of the original line
|
||||||
|
const double original_extrusion = (double) l * line.volumetric_extrusion_rate / original_feedrate;
|
||||||
|
|
||||||
|
// make sure there is a steady segment that's no shorter than `m_max_segment_length`
|
||||||
|
const double min_steady_extrusion = original_extrusion * m_max_segment_length / l;
|
||||||
|
const double max_sloped_extrusion = original_extrusion - min_steady_extrusion;
|
||||||
|
assert(max_sloped_extrusion > 0);
|
||||||
|
|
||||||
|
// Calculate the maximum possible peak extrusion rate
|
||||||
|
// amount of extrusion if accelerate from volumetric_extrusion_rate_start to volumetric_extrusion_rate then
|
||||||
|
// decelerate from volumetric_extrusion_rate to volumetric_extrusion_rate_end with max slope rate
|
||||||
|
const auto pow2 = [](const double x) { return x * x; };
|
||||||
|
const double e_2 = pow2(line.volumetric_extrusion_rate);
|
||||||
|
const double e0_2 = pow2(line.volumetric_extrusion_rate_start);
|
||||||
|
const double e1_2 = pow2(line.volumetric_extrusion_rate_end);
|
||||||
|
const double sp = line.max_volumetric_extrusion_rate_slope_positive;
|
||||||
|
const double sn = line.max_volumetric_extrusion_rate_slope_negative;
|
||||||
|
const double sloped_extrusion = (e_2 - e0_2) / 2 / sp + (e_2 - e1_2) / 2 / sn;
|
||||||
|
|
||||||
|
double target_max_extrusion_rate = line.volumetric_extrusion_rate;
|
||||||
|
if (sloped_extrusion > max_sloped_extrusion) {
|
||||||
|
// We don't have enough time to accel to max possible extrusion rate
|
||||||
|
// now we calculate the actual possible value
|
||||||
|
target_max_extrusion_rate = std::sqrt((2 * max_sloped_extrusion * sp * sn + sn * e0_2 + sp * e1_2) / (sp + sn));
|
||||||
|
}
|
||||||
|
assert(target_max_extrusion_rate > line.volumetric_extrusion_rate_start);
|
||||||
|
assert(target_max_extrusion_rate > line.volumetric_extrusion_rate_end);
|
||||||
|
assert(target_max_extrusion_rate >= line.volumetric_extrusion_rate);
|
||||||
|
|
||||||
|
// if the extrusion rate change is trivial, then ignore this algorithm and use the single sloped version instead
|
||||||
|
delta_volumetric_rate = std::round(std::min({ // important! it's MIN here not max!
|
||||||
|
fabs(target_max_extrusion_rate - line.volumetric_extrusion_rate_start),
|
||||||
|
fabs(target_max_extrusion_rate - line.volumetric_extrusion_rate_end),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (delta_volumetric_rate >= NON_TRIVIAL_RATE_DELTA) {
|
||||||
|
// we then have the target max feedrate when we reach target_max_extrusion_rate
|
||||||
|
const double target_max_feedrate = original_feedrate * target_max_extrusion_rate / line.volumetric_extrusion_rate;
|
||||||
|
assert(target_max_feedrate <= original_feedrate);
|
||||||
|
|
||||||
|
// calculate the accel and deccel time & length
|
||||||
|
const double t_acc = (target_max_extrusion_rate - line.volumetric_extrusion_rate_start) / sp;
|
||||||
|
const double l_acc = t_acc * (target_max_feedrate + line.pos_start[4]) / 2;
|
||||||
|
const double t_dec = (target_max_extrusion_rate - line.volumetric_extrusion_rate_end) / sn;
|
||||||
|
const double l_dec = t_dec * (target_max_feedrate + line.pos_end[4]) / 2;
|
||||||
|
|
||||||
|
float pos_end_bak[5];
|
||||||
|
memcpy(pos_end_bak, line.pos_end, sizeof(float) * 5); // backup the final pos
|
||||||
|
float pos_start[5];
|
||||||
|
float pos_end[5];
|
||||||
|
memcpy(pos_start, line.pos_start, sizeof(float) * 5);
|
||||||
|
memcpy(pos_end, line.pos_end, sizeof(float) * 5);
|
||||||
|
|
||||||
|
// calculate the end pos of the accel slope
|
||||||
|
float t = l_acc / l;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
pos_end[i] = pos_start[i] + (pos_end_bak[i] - pos_start[i]) * t;
|
||||||
|
line.pos_provided[i] = true;
|
||||||
|
}
|
||||||
|
// emit accel slope in nSegments
|
||||||
|
nSegments = size_t(ceil(l_acc / m_max_segment_length));
|
||||||
|
assert(nSegments > 0);
|
||||||
|
for (size_t i = 1; i <= nSegments; ++i) {
|
||||||
|
t = float(i) / float(nSegments);
|
||||||
|
for (size_t j = 0; j < 4; ++j) {
|
||||||
|
line.pos_end[j] = pos_start[j] + (pos_end[j] - pos_start[j]) * t;
|
||||||
|
line.pos_provided[j] = true;
|
||||||
|
}
|
||||||
|
// Interpolate the feed rate at the center of the segment.
|
||||||
|
push_line_to_output(line_idx, pos_start[4] + (target_max_feedrate - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
|
||||||
|
comment = nullptr;
|
||||||
|
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the end pos of the steady segment
|
||||||
|
t = (l - l_dec) / l;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
line.pos_end[i] = pos_start[i] + (pos_end_bak[i] - pos_start[i]) * t;
|
||||||
|
}
|
||||||
|
// emit the steady feed rate segment
|
||||||
|
push_line_to_output(line_idx, target_max_feedrate, nullptr);
|
||||||
|
memcpy(line.pos_start, line.pos_end, sizeof(float) * 5);
|
||||||
|
|
||||||
|
// calculate the start pos of the decl slope
|
||||||
|
memcpy(pos_start, line.pos_end, sizeof(float) * 5);
|
||||||
|
line.pos_start[4] = target_max_feedrate;
|
||||||
|
pos_start[4] = target_max_feedrate;
|
||||||
|
// emit deccel slope in nSegments
|
||||||
|
nSegments = size_t(ceil(l_dec / m_max_segment_length));
|
||||||
|
assert(nSegments > 0);
|
||||||
|
for (size_t i = 1; i <= nSegments; ++ i) {
|
||||||
|
t = float(i) / float(nSegments);
|
||||||
|
for (size_t j = 0; j < 4; ++ j) {
|
||||||
|
line.pos_end[j] = pos_start[j] + (pos_end_bak[j] - pos_start[j]) * t;
|
||||||
|
}
|
||||||
|
// Interpolate the feed rate at the center of the segment.
|
||||||
|
push_line_to_output(line_idx, pos_start[4] + (pos_end_bak[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), nullptr);
|
||||||
|
memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// finish the movement by moving to end pos
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
line.pos_end[i] = pos_end_bak[i];
|
||||||
|
}
|
||||||
|
push_line_to_output(line_idx, pos_end_bak[4], nullptr);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accelerating = line.volumetric_extrusion_rate_start < line.volumetric_extrusion_rate_end;
|
||||||
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
|
float feed_avg = 0.5f * (line.pos_start[4] + line.pos_end[4]);
|
||||||
// Limiting volumetric extrusion rate slope for this segment.
|
// Limiting volumetric extrusion rate slope for this segment.
|
||||||
float max_volumetric_extrusion_rate_slope = accelerating ? line.max_volumetric_extrusion_rate_slope_positive :
|
float max_volumetric_extrusion_rate_slope = accelerating ? line.max_volumetric_extrusion_rate_slope_positive :
|
||||||
@@ -515,7 +637,7 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
|
|||||||
float t_total = line.dist_xyz() / feed_avg;
|
float t_total = line.dist_xyz() / feed_avg;
|
||||||
// Time of the acceleration / deceleration part of the segment, if accelerating / decelerating
|
// Time of the acceleration / deceleration part of the segment, if accelerating / decelerating
|
||||||
// with the maximum volumetric extrusion rate slope.
|
// with the maximum volumetric extrusion rate slope.
|
||||||
float t_acc = 0.5f * (line.volumetric_extrusion_rate_start + line.volumetric_extrusion_rate_end) / max_volumetric_extrusion_rate_slope;
|
float t_acc = std::fabs(line.volumetric_extrusion_rate_start - line.volumetric_extrusion_rate_end) / max_volumetric_extrusion_rate_slope;
|
||||||
float l_acc = l;
|
float l_acc = l;
|
||||||
float l_steady = 0.f;
|
float l_steady = 0.f;
|
||||||
if (t_acc < t_total) {
|
if (t_acc < t_total) {
|
||||||
@@ -590,30 +712,32 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const size_t last_line_idx)
|
void PressureEqualizer::adjust_volumetric_rate(const size_t first_line_idx, const size_t last_line_idx)
|
||||||
{
|
{
|
||||||
// don't bother adjusting volumetric rate if there's no gcode to adjust
|
// don't bother adjusting volumetric rate if there's no gcode to adjust
|
||||||
if (last_line_idx-fist_line_idx < 2)
|
if (last_line_idx - first_line_idx < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t line_idx = last_line_idx;
|
size_t line_idx = last_line_idx;
|
||||||
if (line_idx == fist_line_idx || !m_gcode_lines[line_idx].extruding())
|
if (line_idx == first_line_idx || !m_gcode_lines[line_idx].extruding())
|
||||||
// Nothing to do, the last move is not extruding.
|
// Nothing to do, the last move is not extruding.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::array<float, size_t(ExtrusionRole::erCount)> feedrate_per_extrusion_role{};
|
std::array<float, size_t(ExtrusionRole::erCount)> feedrate_per_extrusion_role{};
|
||||||
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
|
feedrate_per_extrusion_role.fill(std::numeric_limits<float>::max());
|
||||||
feedrate_per_extrusion_role[int(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
feedrate_per_extrusion_role[int(m_gcode_lines[line_idx].extrusion_role)] = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
||||||
|
|
||||||
while (line_idx != fist_line_idx) {
|
while (line_idx != first_line_idx) {
|
||||||
size_t idx_prev = line_idx - 1;
|
size_t idx_prev = line_idx - 1;
|
||||||
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
|
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != first_line_idx; --idx_prev);
|
||||||
if (!m_gcode_lines[idx_prev].extruding())
|
if (!m_gcode_lines[idx_prev].extruding())
|
||||||
break;
|
break;
|
||||||
// Don't decelerate before ironing.
|
// Don't decelerate before ironing.
|
||||||
if (m_gcode_lines[line_idx].extrusion_role == ExtrusionRole::erIroning) { line_idx = idx_prev;
|
if (m_gcode_lines[line_idx].extrusion_role == ExtrusionRole::erIroning) {
|
||||||
|
line_idx = idx_prev;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Volumetric extrusion rate at the start of the succeding segment.
|
// Volumetric extrusion rate at the start of the succeeding segment.
|
||||||
float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
float rate_succ = m_gcode_lines[line_idx].volumetric_extrusion_rate_start;
|
||||||
// What is the gradient of the extrusion rate between idx_prev and idx?
|
// What is the gradient of the extrusion rate between idx_prev and idx?
|
||||||
line_idx = idx_prev;
|
line_idx = idx_prev;
|
||||||
@@ -630,8 +754,8 @@ void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const
|
|||||||
rate_end = rate_succ;
|
rate_end = rate_succ;
|
||||||
|
|
||||||
// don't alter the flow rate for these extrusion types
|
// don't alter the flow rate for these extrusion types
|
||||||
// Orca: Limit ERS to external perimeters and overhangs if option selected by user
|
|
||||||
if (!line.adjustable_flow || line.extrusion_role == ExtrusionRole::erBridgeInfill || line.extrusion_role == ExtrusionRole::erIroning ||
|
if (!line.adjustable_flow || line.extrusion_role == ExtrusionRole::erBridgeInfill || line.extrusion_role == ExtrusionRole::erIroning ||
|
||||||
|
// Orca: Limit ERS to external perimeters and overhangs if option selected by user
|
||||||
(m_extrusion_rate_smoothing_external_perimeter_only && line.extrusion_role != ExtrusionRole::erOverhangPerimeter && line.extrusion_role != ExtrusionRole::erExternalPerimeter)) {
|
(m_extrusion_rate_smoothing_external_perimeter_only && line.extrusion_role != ExtrusionRole::erOverhangPerimeter && line.extrusion_role != ExtrusionRole::erExternalPerimeter)) {
|
||||||
rate_end = line.volumetric_extrusion_rate_end;
|
rate_end = line.volumetric_extrusion_rate_end;
|
||||||
} else if (line.volumetric_extrusion_rate_end > rate_end) {
|
} else if (line.volumetric_extrusion_rate_end > rate_end) {
|
||||||
@@ -687,8 +811,8 @@ void PressureEqualizer::adjust_volumetric_rate(const size_t fist_line_idx, const
|
|||||||
|
|
||||||
float rate_start = feedrate_per_extrusion_role[iRole];
|
float rate_start = feedrate_per_extrusion_role[iRole];
|
||||||
// don't alter the flow rate for these extrusion types
|
// don't alter the flow rate for these extrusion types
|
||||||
// Orca: Limit ERS to external perimeters and overhangs if option selected by user
|
|
||||||
if (!line.adjustable_flow || line.extrusion_role == ExtrusionRole::erBridgeInfill || line.extrusion_role == ExtrusionRole::erIroning ||
|
if (!line.adjustable_flow || line.extrusion_role == ExtrusionRole::erBridgeInfill || line.extrusion_role == ExtrusionRole::erIroning ||
|
||||||
|
// Orca: Limit ERS to external perimeters and overhangs if option selected by user
|
||||||
(m_extrusion_rate_smoothing_external_perimeter_only && line.extrusion_role != ExtrusionRole::erOverhangPerimeter && line.extrusion_role != ExtrusionRole::erExternalPerimeter)) {
|
(m_extrusion_rate_smoothing_external_perimeter_only && line.extrusion_role != ExtrusionRole::erOverhangPerimeter && line.extrusion_role != ExtrusionRole::erExternalPerimeter)) {
|
||||||
rate_start = line.volumetric_extrusion_rate_start;
|
rate_start = line.volumetric_extrusion_rate_start;
|
||||||
} else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start)
|
} else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start)
|
||||||
|
|||||||
@@ -136,7 +136,6 @@ private:
|
|||||||
assert(avg_correction <= 1.00000001f);
|
assert(avg_correction <= 1.00000001f);
|
||||||
return avg_correction;
|
return avg_correction;
|
||||||
}
|
}
|
||||||
float time_corrected() const { return time() * volumetric_correction_avg(); }
|
|
||||||
|
|
||||||
GCodeLineType type;
|
GCodeLineType type;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user