Skip to content

Improve Pressure Equalizer and fix related bugs #9622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions src/libslic3r/GCode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2860,8 +2860,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
std::vector<ProcessedPoint> new_points{};
if (this->m_config.enable_dynamic_overhang_speeds && !this->on_first_layer() && path.role().is_perimeter()) {
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, m_config.overhang_overlap_levels,
m_config.dynamic_overhang_speeds,
m_config.get_abs_value("external_perimeter_speed"), speed);
m_config.dynamic_overhang_speeds, speed);
variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed; });
}

Expand Down Expand Up @@ -2916,19 +2915,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
+ float_to_string_decimal_point(m_last_height) + "\n";
}

std::string comment;
std::string cooling_marker_setspeed_comments;
if (m_enable_cooling_markers) {
if (path.role().is_bridge())
gcode += ";_BRIDGE_FAN_START\n";
else
comment = ";_EXTRUDE_SET_SPEED";
// NOTE: THIS TAG IS ALSO USED BY PRESSURE EQUALIZER
// so it knows which extrusions are eligible for flow modification
cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED";
if (path.role() == ExtrusionRole::ExternalPerimeter)
comment += ";_EXTERNAL_PERIMETER";
cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER";
}

if (!variable_speed) {
// F is mm per minute.
gcode += m_writer.set_speed(F, "", comment);
gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments);
double path_length = 0.;
std::string comment;
if (m_config.gcode_comments) {
Expand All @@ -2946,13 +2947,13 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
prev = p;
}
} else {
std::string comment;
std::string comment = "";
if (m_config.gcode_comments) {
comment = description;
comment += description;
comment += description_bridge;
}
double last_set_speed = new_points[0].speed * 60.0;
gcode += m_writer.set_speed(last_set_speed, "", comment);
gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments);
Vec2d prev = this->point_to_gcode_quantized(new_points[0].p);
for (size_t i = 1; i < new_points.size(); i++) {
const ProcessedPoint& processed_point = new_points[i];
Expand All @@ -2962,7 +2963,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
prev = p;
double new_speed = processed_point.speed * 60.0;
if (last_set_speed != new_speed) {
gcode += m_writer.set_speed(new_speed, "", comment);
gcode += m_writer.set_speed(new_speed, "", cooling_marker_setspeed_comments);
last_set_speed = new_speed;
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/libslic3r/GCode/ExtrusionProcessor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,14 +260,13 @@ class ExtrusionQualityEstimator
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path,
const ConfigOptionPercents &overlaps,
const ConfigOptionFloatsOrPercents &speeds,
float ext_perimeter_speed,
float original_speed)
{
size_t speed_sections_count = std::min(overlaps.values.size(), speeds.values.size());
std::vector<std::pair<float, float>> speed_sections;
for (size_t i = 0; i < speed_sections_count; i++) {
float distance = path.width * (1.0 - (overlaps.get_at(i) / 100.0));
float speed = speeds.get_at(i).percent ? (ext_perimeter_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
float speed = speeds.get_at(i).percent ? (original_speed * speeds.get_at(i).value / 100.0) : speeds.get_at(i).value;
speed_sections.push_back({distance, speed});
}
std::sort(speed_sections.begin(), speed_sections.end(),
Expand Down
108 changes: 88 additions & 20 deletions src/libslic3r/GCode/PressureEqualizer.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <memory.h>
#include <cstring>
#include <cfloat>
#include <algorithm>

#include "../libslic3r.h"
#include "../PrintConfig.hpp"
Expand All @@ -20,13 +21,19 @@ static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER";

// Maximum segment length to split a long segment if the initial and the final flow rate differ.
// Smaller value means a smoother transition between two different flow rates.
static constexpr float max_segment_length = 5.f;
static constexpr float max_segment_length = 1.f;

// For how many GCode lines back will adjust a flow rate from the latest line.
// Bigger values affect the GCode export speed a lot, and smaller values could
// affect how distant will be propagated a flow rate adjustment.
static constexpr int max_look_back_limit = 128;

// Max non-extruding XY distance (travel move) in mm between two continous extrusions where we pretend
// its all one continous extruded line. Above this distance we assume extruder pressure hits 0
// This exists because often there's tiny travel moves between stuff like infill
// lines where some extruder pressure will remain (so we should equalize between these small travels)
static constexpr long max_ignored_gap_between_extruding_segments = 3;

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.
Expand Down Expand Up @@ -59,8 +66,8 @@ PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig &config) : m_use_
extrusion_rate_slope.positive = m_max_volumetric_extrusion_rate_slope_positive;
}

// Don't regulate the pressure before and after gap-fill and ironing.
for (const GCodeExtrusionRole er : {GCodeExtrusionRole::GapFill, GCodeExtrusionRole::Ironing}) {
// Don't regulate the pressure before and after 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)].positive = 0;
}
Expand Down Expand Up @@ -97,8 +104,71 @@ void PressureEqualizer::process_layer(const std::string &gcode)
}
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 extrudes into segments of continous extrusion and process those
// We skip over large travels, and pretend small ones are part of a continous extrusion segment
long idx_end_current_extrusion = 0;
while (idx_end_current_extrusion < m_gcode_lines.size()) {
// find beginning of next extrusion segment from current pos
const long idx_begin_current_extrusion = find_if(m_gcode_lines.begin() + idx_end_current_extrusion, m_gcode_lines.end(),
[](GCodeLine line) { return line.extruding(); }) - m_gcode_lines.begin();
// (extrusion begin idx = extrusion end idx) here because we start with extrusion length of zero
idx_end_current_extrusion = idx_begin_current_extrusion;

// inner loop extends the extrusion segment over small travel moves
while (idx_end_current_extrusion < m_gcode_lines.size()) {
// find end of the current extrusion segment
const auto just_after_end_extrusion = find_if(m_gcode_lines.begin() + idx_end_current_extrusion, m_gcode_lines.end(),
[](GCodeLine line) { return !line.extruding(); });
idx_end_current_extrusion = std::max<long>(0,(just_after_end_extrusion - m_gcode_lines.begin()) - 1);
const long idx_begin_segment_continuation = advance_segment_beyond_small_gap(idx_end_current_extrusion);
if (idx_begin_segment_continuation > idx_end_current_extrusion) {
// extend the continous line over the small gap
idx_end_current_extrusion = idx_begin_segment_continuation;
continue; // keep going, loop again to find new end of extrusion segment
} else {
// gap to next extrude is too big, stop looking forward. We've found end of this segment
break;
}
}

// 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
for (int i = idx_begin_current_extrusion; i < idx_end_current_extrusion; ++i) {
// feed pressure equalizer past lines, going back to max_look_back_limit (or start of segment)
const auto start_idx = std::max<long>(idx_begin_current_extrusion, i - max_look_back_limit);
adjust_volumetric_rate(start_idx, i);
}
// current extrusion is all done processing so advance beyond it for next loop
idx_end_current_extrusion++;
}
}


long PressureEqualizer::advance_segment_beyond_small_gap(const long idx_orig)
{
// this should only be run on the last extruding line before a gap
assert(m_gcode_lines[idx_cur_pos].extruding());
double distance_traveled = 0.0;
// start at beginning of gap, advance till extrusion found or gap too big
for (auto idx_cur_pos = idx_orig + 1; idx_cur_pos < m_gcode_lines.size(); idx_cur_pos++) {
// started extruding again! return segment extension
if (m_gcode_lines[idx_cur_pos].extruding()) {
return idx_cur_pos;
}

distance_traveled += m_gcode_lines[idx_cur_pos].dist_xy();
// gap too big, dont extend segment
if (distance_traveled > max_ignored_gap_between_extruding_segments) {
return idx_orig;
}
}
// looped until end of layer and couldn't extend extrusion
return idx_orig;
}


LayerResult PressureEqualizer::process_layer(LayerResult &&input)
{
const bool is_first_layer = m_layer_results.empty();
Expand Down Expand Up @@ -390,8 +460,6 @@ bool PressureEqualizer::process_line(const char *line, const char *line_end, GCo

buf.extruder_id = m_current_extruder;
memcpy(buf.pos_end, m_current_pos, sizeof(float)*5);

adjust_volumetric_rate();
#ifdef PRESSURE_EQUALIZER_DEBUG
++line_idx;
#endif
Expand Down Expand Up @@ -506,14 +574,12 @@ void PressureEqualizer::output_gcode_line(const size_t line_idx)
}
}

void PressureEqualizer::adjust_volumetric_rate()
void PressureEqualizer::adjust_volumetric_rate(const size_t fist_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-fist_line_idx < 2)
return;

// Go back from the current circular_buffer_pos and lower the feedtrate to decrease the slope of the extrusion rate changes.
size_t fist_line_idx = size_t(std::max<int>(0, int(m_gcode_lines.size()) - max_look_back_limit));
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.
Expand All @@ -528,8 +594,8 @@ void PressureEqualizer::adjust_volumetric_rate()
for (; !m_gcode_lines[idx_prev].extruding() && idx_prev != fist_line_idx; --idx_prev);
if (!m_gcode_lines[idx_prev].extruding())
break;
// Don't decelerate before ironing and gap-fill.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) {
// Don't decelerate before ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_prev;
continue;
}
Expand All @@ -549,7 +615,8 @@ void PressureEqualizer::adjust_volumetric_rate()
// Limit by the succeeding volumetric flow rate.
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;
} else if (line.volumetric_extrusion_rate_end > rate_end) {
line.volumetric_extrusion_rate_end = rate_end;
Expand All @@ -572,8 +639,8 @@ void PressureEqualizer::adjust_volumetric_rate()
}
}
// 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.
if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill)
// Don't store feed rate for ironing
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_start;
}
}
Expand All @@ -587,8 +654,8 @@ void PressureEqualizer::adjust_volumetric_rate()
for (; !m_gcode_lines[idx_next].extruding() && idx_next != last_line_idx; ++idx_next);
if (!m_gcode_lines[idx_next].extruding())
break;
// Don't accelerate after ironing and gap-fill.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing || m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::GapFill) {
// Don't accelerate after ironing.
if (m_gcode_lines[line_idx].extrusion_role == GCodeExtrusionRole::Ironing) {
line_idx = idx_next;
continue;
}
Expand All @@ -603,7 +670,8 @@ void PressureEqualizer::adjust_volumetric_rate()
continue; // The positive rate is unlimited or the rate for GCodeExtrusionRole iRole is unlimited.

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;
} else if (iRole == size_t(line.extrusion_role) && rate_prec < rate_start)
rate_start = rate_prec;
Expand All @@ -628,8 +696,8 @@ void PressureEqualizer::adjust_volumetric_rate()
}
}
// 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.
if (line.extrusion_role != GCodeExtrusionRole::Ironing && line.extrusion_role != GCodeExtrusionRole::GapFill)
// Don't store feed rate for ironing
if (line.extrusion_role != GCodeExtrusionRole::Ironing)
feedrate_per_extrusion_role[iRole] = line.volumetric_extrusion_rate_end;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/libslic3r/GCode/PressureEqualizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,12 @@ class PressureEqualizer
#endif

bool process_line(const char *line, const char *line_end, GCodeLine &buf);
long advance_segment_beyond_small_gap(long idx_cur_pos);
void output_gcode_line(size_t line_idx);

// 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.
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.
inline void push_to_output(GCodeG1Formatter &formatter);
Expand Down