SPE-2151: Fix dynamic overhang speed doesn't respect pressure equalizer limits. Don't apply pressure equalizer after long travels.

This commit comes from https://github.com/prusa3d/PrusaSlicer/pull/9622 with just minor refactoring.
This commit is contained in:
MGunlogson 2024-02-16 18:01:45 +01:00 committed by Lukas Matena
parent 122d876bca
commit 3e98afa71d
2 changed files with 111 additions and 34 deletions

View File

@ -6,6 +6,7 @@
#include <memory.h> #include <memory.h>
#include <cstring> #include <cstring>
#include <cfloat> #include <cfloat>
#include <algorithm>
#include "../libslic3r.h" #include "../libslic3r.h"
#include "../PrintConfig.hpp" #include "../PrintConfig.hpp"
@ -32,6 +33,12 @@ static constexpr float max_segment_length = 5.f;
// affect how distant will be propagated a flow rate adjustment. // affect how distant will be propagated a flow rate adjustment.
static constexpr int max_look_back_limit = 128; static constexpr int max_look_back_limit = 128;
// Max non-extruding XY distance (travel move) in mm between two continuous extrusions where we pretend
// it's all one continuous extrusion line. Above this distance, we assume extruder pressure hits 0
// This exists because often there are tiny travel moves between stuff like infill.
// Lines where some extruder pressure will remain (so we should equalize between these small travels).
static constexpr double max_ignored_gap_between_extruding_segments = 3.;
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value) PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_relative_e_distances(config.use_relative_e_distances.value)
{ {
// Preallocate some data, so that output_buffer.data() will return an empty string. // Preallocate some data, so that output_buffer.data() will return an empty string.
@ -64,8 +71,8 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive; extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
} }
// Don't regulate the pressure before and after gap-fill and ironing. // Don't regulate the pressure before and after ironing.
for (const GCodeExtrusionRole er : {GCodeExtrusionRole::GapFill, GCodeExtrusionRole::Ironing}) { for (const GCodeExtrusionRole er : {GCodeExtrusionRole::Ironing}) {
m_max_volumetric_extrusion_rate_slopes[size_t(er)].negative = 0; m_max_volumetric_extrusion_rate_slopes[size_t(er)].negative = 0;
m_max_volumetric_extrusion_rate_slopes[size_t(er)].positive = 0; m_max_volumetric_extrusion_rate_slopes[size_t(er)].positive = 0;
} }
@ -102,6 +109,73 @@ void PressureEqualizer::process_layer(const std::string &gcode)
} }
assert(!this->opened_extrude_set_speed_block); assert(!this->opened_extrude_set_speed_block);
} }
// At this point, we have an entire layer of gcode lines loaded into m_gcode_lines.
// Now, we will split the mix of travels and extrusions into segments of continuous extrusions and process them.
// We skip over large travels, and pretend that small ones are part of a continuous extrusion segment.
for (auto current_extrusion_end_it = m_gcode_lines.cbegin(); current_extrusion_end_it != m_gcode_lines.cend();) {
// Find beginning of next extrusion segment from current position.
const auto current_extrusion_begin_it = std::find_if(current_extrusion_end_it, m_gcode_lines.cend(), [](const GCodeLine &line) {
return line.extruding();
});
// We start with extrusion length of zero.
current_extrusion_end_it = current_extrusion_begin_it;
// Inner loop extends the extrusion segment over small travel moves.
while (current_extrusion_end_it != m_gcode_lines.cend()) {
// Find the end of the current extrusion segment.
const auto travel_begin_it = std::find_if(std::next(current_extrusion_end_it), m_gcode_lines.cend(), [](const GCodeLine &line) {
return !line.extruding();
});
current_extrusion_end_it = std::prev(travel_begin_it);
const auto next_extrusion_segment_it = advance_segment_beyond_small_gap(current_extrusion_end_it);
if (std::distance(current_extrusion_end_it, next_extrusion_segment_it) > 0) {
// Extend the continuous line over the small gap.
current_extrusion_end_it = next_extrusion_segment_it;
continue; // Keep going, loop again to find the new end of extrusion segment.
} else {
break; // Gap to next extrude is too big, stop looking forward. We've found the end of this segment.
}
}
// Now, run the pressure equalizer across the segment like a streamroller.
// It operates on a sliding window that moves forward across gcode line by line.
const std::ptrdiff_t current_extrusion_begin_idx = std::distance(m_gcode_lines.cbegin(), current_extrusion_begin_it);
for (auto current_line_it = current_extrusion_begin_it; current_line_it != current_extrusion_end_it; ++current_line_it) {
const std::ptrdiff_t current_line_idx = std::distance(m_gcode_lines.cbegin(), current_line_it);
// Feed pressure equalizer past lines, going back to max_look_back_limit (or start of segment).
const size_t start_idx = size_t(std::max<std::ptrdiff_t>(current_extrusion_begin_idx, current_line_idx - max_look_back_limit));
adjust_volumetric_rate(start_idx, size_t(current_line_idx));
}
// Current extrusion is all done processing so advance beyond it for the next loop.
if (current_extrusion_end_it != m_gcode_lines.cend())
++current_extrusion_end_it;
}
}
PressureEqualizer::GCodeLinesConstIt PressureEqualizer::advance_segment_beyond_small_gap(const GCodeLinesConstIt &last_extruding_line_it) const {
// This should only be run on the last extruding line before a gap.
assert(last_extruding_line_it != m_gcode_lines.cend() && last_extruding_line_it->extruding());
double travel_distance = 0.;
// Start at the beginning of a gap, advance till extrusion found or gap too big.
for (auto current_line_it = std::next(last_extruding_line_it); current_line_it != m_gcode_lines.cend(); ++current_line_it) {
// Started extruding again! Return segment extension.
if (current_line_it->extruding())
return current_line_it;
travel_distance += current_line_it->dist_xy();
// Gap too big, don't extend segment.
if (travel_distance > max_ignored_gap_between_extruding_segments)
return last_extruding_line_it;
}
// Looped until the end of the layer and couldn't extend extrusion.
return last_extruding_line_it;
} }
LayerResult PressureEqualizer::process_layer(LayerResult &&input) LayerResult PressureEqualizer::process_layer(LayerResult &&input)
@ -147,8 +221,8 @@ static inline bool is_ws_or_eol(const char c) { return is_ws(c) || is_eol(c); }
// Eat whitespaces. // Eat whitespaces.
static void eatws(const char *&line) static void eatws(const char *&line)
{ {
while (is_ws(*line)) while (is_ws(*line))
++ line; ++line;
} }
// Parse an int starting at the current position of a line. // Parse an int starting at the current position of a line.
@ -216,7 +290,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
buf.volumetric_extrusion_rate_end = 0.f; buf.volumetric_extrusion_rate_end = 0.f;
buf.max_volumetric_extrusion_rate_slope_positive = 0.f; buf.max_volumetric_extrusion_rate_slope_positive = 0.f;
buf.max_volumetric_extrusion_rate_slope_negative = 0.f; buf.max_volumetric_extrusion_rate_slope_negative = 0.f;
buf.extrusion_role = m_current_extrusion_role; buf.extrusion_role = m_current_extrusion_role;
std::string str_line(line, line_end); std::string str_line(line, line_end);
const bool found_extrude_set_speed_tag = boost::contains(str_line, EXTRUDE_SET_SPEED_TAG); const bool found_extrude_set_speed_tag = boost::contains(str_line, EXTRUDE_SET_SPEED_TAG);
@ -323,7 +397,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
memcpy(m_current_pos, new_pos, sizeof(float) * 5); memcpy(m_current_pos, new_pos, sizeof(float) * 5);
break; break;
} }
case 92: case 92:
{ {
// G92 : Set Position // G92 : Set Position
// Set a logical coordinate position to a new value without actually moving the machine motors. // Set a logical coordinate position to a new value without actually moving the machine motors.
@ -360,7 +434,7 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
break; break;
default: default:
// Ignore the rest. // Ignore the rest.
break; break;
} }
break; break;
} }
@ -395,8 +469,6 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo
buf.extruder_id = m_current_extruder; buf.extruder_id = m_current_extruder;
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5); memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);
adjust_volumetric_rate();
#ifdef PRESSURE_EQUALIZER_DEBUG #ifdef PRESSURE_EQUALIZER_DEBUG
++line_idx; ++line_idx;
#endif #endif
@ -489,13 +561,13 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
for (size_t j = 0; j < 4; ++ j) { for (size_t j = 0; j < 4; ++ j) {
line.pos_end[j] = pos_start[j] + (pos_end[j] - pos_start[j]) * t; line.pos_end[j] = pos_start[j] + (pos_end[j] - pos_start[j]) * t;
line.pos_provided[j] = true; line.pos_provided[j] = true;
} }
// Interpolate the feed rate at the center of the segment. // Interpolate the feed rate at the center of the segment.
push_line_to_output(line_idx, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment); push_line_to_output(line_idx, pos_start[4] + (pos_end[4] - pos_start[4]) * (float(i) - 0.5f) / float(nSegments), comment);
comment = nullptr; comment = nullptr;
memcpy(line.pos_start, line.pos_end, sizeof(float)*5); memcpy(line.pos_start, line.pos_end, sizeof(float)*5);
} }
if (l_steady > 0.f && accelerating) { if (l_steady > 0.f && accelerating) {
for (int i = 0; i < 4; ++ i) { for (int i = 0; i < 4; ++ i) {
line.pos_end[i] = pos_end2[i]; line.pos_end[i] = pos_end2[i];
line.pos_provided[i] = true; line.pos_provided[i] = true;
@ -511,16 +583,14 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
} }
} }
void PressureEqualizer::adjust_volumetric_rate() void PressureEqualizer::adjust_volumetric_rate(const size_t first_line_idx, const size_t last_line_idx)
{ {
if (m_gcode_lines.size() < 2) // Don't bother adjusting volumetric rate if there's no gcode to adjust.
if (last_line_idx <= first_line_idx || last_line_idx - first_line_idx < 2)
return; return;
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes. size_t line_idx = last_line_idx;
size_t fist_line_idx = size_t(std::max<int>(0, int(m_gcode_lines.size()) - max_look_back_limit)); if (line_idx == first_line_idx || !m_gcode_lines[line_idx].extruding())
const size_t last_line_idx = m_gcode_lines.size() - 1;
size_t line_idx = last_line_idx;
if (line_idx == fist_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;
@ -528,13 +598,13 @@ void PressureEqualizer::adjust_volumetric_rate()
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 and gap-fill. // Don't decelerate before ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) { if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_prev; line_idx = idx_prev;
continue; continue;
} }
@ -554,7 +624,8 @@ void PressureEqualizer::adjust_volumetric_rate()
// Limit by the succeeding volumetric flow rate. // Limit by the succeeding volumetric flow rate.
rate_end = rate_succ; rate_end = rate_succ;
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter || line.extrusion_role == GCodeExtrusionRole::GapFill || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) { // Don't alter the flow rate for these extrusion types.
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
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) {
line.volumetric_extrusion_rate_end = rate_end; line.volumetric_extrusion_rate_end = rate_end;
@ -576,9 +647,9 @@ void PressureEqualizer::adjust_volumetric_rate()
line.modified = true; line.modified = true;
} }
} }
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_start : rate_start;
// Don't store feed rate for ironing and gap-fill. // Don't store feed rate for ironing.
if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill) if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start; feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
} }
} }
@ -592,8 +663,8 @@ void PressureEqualizer::adjust_volumetric_rate()
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next); for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
if (!m_gcode_lines[idx_next].extruding()) if (!m_gcode_lines[idx_next].extruding())
break; break;
// Don't accelerate after ironing and gap-fill. // Don't accelerate after ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) { if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_next; line_idx = idx_next;
continue; continue;
} }
@ -608,7 +679,8 @@ void PressureEqualizer::adjust_volumetric_rate()
continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited. continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.
float rate_start = feedrate_per_extrusion_role[iRole]; float rate_start = feedrate_per_extrusion_role[iRole];
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::ExternalPerimeter || line.extrusion_role == GCodeExtrusionRole::GapFill || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) { // Don't alter the flow rate for these extrusion types.
if (!line.adjustable_flow || line.extrusion_role == GCodeExtrusionRole::BridgeInfill || line.extrusion_role == GCodeExtrusionRole::Ironing) {
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)
rate_start = rate_prec; rate_start = rate_prec;
@ -632,9 +704,9 @@ void PressureEqualizer::adjust_volumetric_rate()
line.modified = true; line.modified = true;
} }
} }
// feedrate_per_extrusion_role[iRole] = (iRole == line.extrusion_role) ? line.volumetric_extrusion_rate_end : rate_end;
// Don't store feed rate for ironing and gap-fill. // Don't store feed rate for ironing
if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill) if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end; feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
} }
} }

View File

@ -174,6 +174,9 @@ private:
bool extrude_end_tag = false; bool extrude_end_tag = false;
}; };
using GCodeLines = std::vector<GCodeLine>;
using GCodeLinesConstIt = GCodeLines::const_iterator;
// Output buffer will only grow. It will not be reallocated over and over. // Output buffer will only grow. It will not be reallocated over and over.
std::vector<char> output_buffer; std::vector<char> output_buffer;
size_t output_buffer_length; size_t output_buffer_length;
@ -187,9 +190,11 @@ private:
bool process_line(const char *line, const char *line_end, GCodeLine &buf); bool process_line(const char *line, const char *line_end, GCodeLine &buf);
void output_gcode_line(size_t line_idx); void output_gcode_line(size_t line_idx);
GCodeLinesConstIt advance_segment_beyond_small_gap(const GCodeLinesConstIt &last_extruding_line_it) const;
// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes. // Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
// Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes. // Then go forward and adjust the feedrate to decrease the slope of the extrusion rate changes.
void adjust_volumetric_rate(); void adjust_volumetric_rate(size_t first_line_idx, size_t last_line_idx);
// Push the text to the end of the output_buffer. // Push the text to the end of the output_buffer.
inline void push_to_output(GCodeG1Formatter &formatter); inline void push_to_output(GCodeG1Formatter &formatter);
@ -206,4 +211,4 @@ public:
} // namespace Slic3r } // namespace Slic3r
#endif /* slic3r_GCode_PressureEqualizer_hpp_ */ #endif /* slic3r_GCode_PressureEqualizer_hpp_ */