mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 13:39:00 +08:00
SPE-2368: Reduce the number of emitted M106 when dynamic fan speed on overhangs is enabled.
This fixed duplicity M106, which doesn't have any effect. Also, it was implemented hysteresis that filters out small back-and-forth changes in fan speeds.
This commit is contained in:
parent
070b2af3fd
commit
7f666265e1
@ -2874,10 +2874,37 @@ std::string GCodeGenerator::change_layer(
|
||||
std::string GCodeGenerator::extrude_smooth_path(
|
||||
const GCode::SmoothPath &smooth_path, const bool is_loop, const std::string_view description, const double speed
|
||||
) {
|
||||
// Extrude along the smooth path.
|
||||
std::string gcode;
|
||||
for (const GCode::SmoothPathElement &el : smooth_path)
|
||||
gcode += this->_extrude(el.path_attributes, el.path, description, speed);
|
||||
|
||||
// Extrude along the smooth path.
|
||||
bool is_bridge_extruded = false;
|
||||
EmitModifiers emit_modifiers = EmitModifiers::create_with_disabled_emits();
|
||||
for (auto el_it = smooth_path.begin(); el_it != smooth_path.end(); ++el_it) {
|
||||
const auto next_el_it = next(el_it);
|
||||
|
||||
// By default, GCodeGenerator::_extrude() emit markers _BRIDGE_FAN_START, _BRIDGE_FAN_END and _RESET_FAN_SPEED for every extrusion.
|
||||
// Together with split extrusions because of different ExtrusionAttributes, this could flood g-code with those markers and then
|
||||
// produce an unnecessary number of duplicity M106.
|
||||
// To prevent this, we control when each marker should be emitted by EmitModifiers, which allows determining when a bridge starts and ends,
|
||||
// even when it is split into several extrusions.
|
||||
if (el_it->path_attributes.role.is_bridge()) {
|
||||
emit_modifiers.emit_bridge_fan_start = !is_bridge_extruded;
|
||||
emit_modifiers.emit_bridge_fan_end = next_el_it == smooth_path.end() || !next_el_it->path_attributes.role.is_bridge();
|
||||
is_bridge_extruded = true;
|
||||
} else if (is_bridge_extruded) {
|
||||
emit_modifiers.emit_bridge_fan_start = false;
|
||||
emit_modifiers.emit_bridge_fan_end = false;
|
||||
is_bridge_extruded = false;
|
||||
}
|
||||
|
||||
// Ensure that just for the last extrusion from the smooth path, the fan speed will be reset back
|
||||
// to the value calculated by the CoolingBuffer.
|
||||
if (next_el_it == smooth_path.end()) {
|
||||
emit_modifiers.emit_fan_speed_reset = true;
|
||||
}
|
||||
|
||||
gcode += this->_extrude(el_it->path_attributes, el_it->path, description, speed, emit_modifiers);
|
||||
}
|
||||
|
||||
// reset acceleration
|
||||
gcode += m_writer.set_print_acceleration(fast_round_up<unsigned int>(m_config.default_acceleration.value));
|
||||
@ -2889,6 +2916,7 @@ std::string GCodeGenerator::extrude_smooth_path(
|
||||
GCode::reverse(reversed_smooth_path);
|
||||
m_wipe.set_path(std::move(reversed_smooth_path));
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
@ -3115,7 +3143,8 @@ std::string GCodeGenerator::_extrude(
|
||||
const ExtrusionAttributes &path_attr,
|
||||
const Geometry::ArcWelder::Path &path,
|
||||
const std::string_view description,
|
||||
double speed)
|
||||
double speed,
|
||||
const EmitModifiers &emit_modifiers)
|
||||
{
|
||||
std::string gcode;
|
||||
const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv;
|
||||
@ -3226,7 +3255,8 @@ std::string GCodeGenerator::_extrude(
|
||||
|
||||
external_perimeter_reference_speed = cap_speed(external_perimeter_reference_speed, m_config, m_writer.extruder()->id(), path_attr);
|
||||
dynamic_print_and_fan_speeds = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(),
|
||||
float(external_perimeter_reference_speed), float(speed));
|
||||
float(external_perimeter_reference_speed), float(speed),
|
||||
m_current_dynamic_fan_speed);
|
||||
}
|
||||
|
||||
if (dynamic_print_and_fan_speeds.print_speed > -1) {
|
||||
@ -3281,19 +3311,29 @@ std::string GCodeGenerator::_extrude(
|
||||
|
||||
std::string cooling_marker_setspeed_comments;
|
||||
if (m_enable_cooling_markers) {
|
||||
if (path_attr.role.is_bridge())
|
||||
if (path_attr.role.is_bridge() && emit_modifiers.emit_bridge_fan_start) {
|
||||
gcode += ";_BRIDGE_FAN_START\n";
|
||||
else
|
||||
} else if (!path_attr.role.is_bridge()) {
|
||||
cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED";
|
||||
if (path_attr.role == ExtrusionRole::ExternalPerimeter)
|
||||
}
|
||||
|
||||
if (path_attr.role == ExtrusionRole::ExternalPerimeter) {
|
||||
cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER";
|
||||
}
|
||||
}
|
||||
|
||||
// F is mm per minute.
|
||||
gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments);
|
||||
|
||||
if (dynamic_print_and_fan_speeds.fan_speed >= 0) {
|
||||
gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_print_and_fan_speeds.fan_speed)) + "\n";
|
||||
const int fan_speed = int(dynamic_print_and_fan_speeds.fan_speed);
|
||||
if (!m_current_dynamic_fan_speed.has_value() || (m_current_dynamic_fan_speed.has_value() && m_current_dynamic_fan_speed != fan_speed)) {
|
||||
m_current_dynamic_fan_speed = fan_speed;
|
||||
gcode += ";_SET_FAN_SPEED" + std::to_string(fan_speed) + "\n";
|
||||
}
|
||||
} else if (m_current_dynamic_fan_speed.has_value() && dynamic_print_and_fan_speeds.fan_speed < 0) {
|
||||
m_current_dynamic_fan_speed.reset();
|
||||
gcode += ";_RESET_FAN_SPEED\n";
|
||||
}
|
||||
|
||||
std::string comment;
|
||||
@ -3345,10 +3385,15 @@ std::string GCodeGenerator::_extrude(
|
||||
}
|
||||
|
||||
if (m_enable_cooling_markers) {
|
||||
gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n";
|
||||
if (path_attr.role.is_bridge() && emit_modifiers.emit_bridge_fan_end) {
|
||||
gcode += ";_BRIDGE_FAN_END\n";
|
||||
} else if (!path_attr.role.is_bridge()) {
|
||||
gcode += ";_EXTRUDE_END\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (dynamic_print_and_fan_speeds.fan_speed >= 0) {
|
||||
if (m_current_dynamic_fan_speed.has_value() && emit_modifiers.emit_fan_speed_reset) {
|
||||
m_current_dynamic_fan_speed.reset();
|
||||
gcode += ";_RESET_FAN_SPEED\n";
|
||||
}
|
||||
|
||||
|
@ -436,6 +436,9 @@ private:
|
||||
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
|
||||
std::unique_ptr<GCode::WipeTowerIntegration> m_wipe_tower;
|
||||
|
||||
// Current fan speed set by dynamic fan speed control.
|
||||
std::optional<float> m_current_dynamic_fan_speed;
|
||||
|
||||
// Heights (print_z) at which the skirt has already been extruded.
|
||||
std::vector<coordf_t> m_skirt_done;
|
||||
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
|
||||
@ -455,8 +458,24 @@ private:
|
||||
// Back-pointer to Print (const).
|
||||
const Print* m_print;
|
||||
|
||||
std::string _extrude(
|
||||
const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, const std::string_view description, double speed = -1);
|
||||
struct EmitModifiers {
|
||||
EmitModifiers(bool emit_fan_speed_reset, bool emit_bridge_fan_start, bool emit_bridge_fan_end)
|
||||
: emit_fan_speed_reset(emit_fan_speed_reset), emit_bridge_fan_start(emit_bridge_fan_start), emit_bridge_fan_end(emit_bridge_fan_end) {}
|
||||
|
||||
EmitModifiers() : EmitModifiers(true, true, true) {};
|
||||
|
||||
static EmitModifiers create_with_disabled_emits() {
|
||||
return {false, false, false};
|
||||
}
|
||||
|
||||
bool emit_fan_speed_reset = true;
|
||||
|
||||
bool emit_bridge_fan_start = true;
|
||||
bool emit_bridge_fan_end = true;
|
||||
};
|
||||
|
||||
std::string _extrude(const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, std::string_view description, double speed, const EmitModifiers &emit_modifiers = EmitModifiers());
|
||||
|
||||
void print_machine_envelope(GCodeOutputStream &file, const Print &print);
|
||||
void _print_first_layer_chamber_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, int temp, bool wait, bool accurate);
|
||||
void _print_first_layer_bed_temperature(GCodeOutputStream &file, const Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
|
@ -95,6 +95,7 @@ struct CoolingLine
|
||||
TYPE_ADJUSTABLE_EMPTY = 1 << 16,
|
||||
// Custom fan speed (introduced for overhang fan speed)
|
||||
TYPE_SET_FAN_SPEED = 1 << 17,
|
||||
// Reset fan speed back to speed calculate by the CoolingBuffer.
|
||||
TYPE_RESET_FAN_SPEED = 1 << 18,
|
||||
};
|
||||
|
||||
@ -571,19 +572,20 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
||||
fast_float::from_chars(sline.data() + (has_S ? pos_S : pos_P) + 1, sline.data() + sline.size(), line.time);
|
||||
if (has_P)
|
||||
line.time *= 0.001f;
|
||||
} else
|
||||
} else {
|
||||
line.time = 0;
|
||||
line.time_max = line.time;
|
||||
}
|
||||
}
|
||||
|
||||
if (boost::contains(sline, ";_SET_FAN_SPEED")) {
|
||||
line.time_max = line.time;
|
||||
} else if (boost::contains(sline, ";_SET_FAN_SPEED")) {
|
||||
auto speed_start = sline.find_last_of('D');
|
||||
int speed = 0;
|
||||
for (char num : sline.substr(speed_start + 1)) {
|
||||
speed = speed * 10 + (num - '0');
|
||||
}
|
||||
line.type |= CoolingLine::TYPE_SET_FAN_SPEED;
|
||||
|
||||
line.fan_speed = speed;
|
||||
line.type |= CoolingLine::TYPE_SET_FAN_SPEED;
|
||||
} else if (boost::contains(sline, ";_RESET_FAN_SPEED")) {
|
||||
line.type |= CoolingLine::TYPE_RESET_FAN_SPEED;
|
||||
}
|
||||
@ -820,14 +822,22 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
new_gcode.reserve(gcode.size() * 2);
|
||||
bool bridge_fan_control = false;
|
||||
int bridge_fan_speed = 0;
|
||||
auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &bridge_fan_control, &bridge_fan_speed]() {
|
||||
auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, &bridge_fan_control, &bridge_fan_speed](const int requested_fan_speed = -1) {
|
||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
|
||||
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
|
||||
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
|
||||
std::pair<int, int> custom_fan_speed_limits{fan_speed_new, 100 };
|
||||
int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers);
|
||||
const int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
|
||||
// Is the fan speed ramp enabled?
|
||||
int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
|
||||
const int full_fan_speed_layer = EXTRUDER_CONFIG(full_fan_speed_layer);
|
||||
int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers);
|
||||
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
|
||||
|
||||
struct FanSpeedRange
|
||||
{
|
||||
int min_speed;
|
||||
int max_speed;
|
||||
};
|
||||
|
||||
FanSpeedRange requested_fan_speed_limits{fan_speed_new, 100};
|
||||
|
||||
if (disable_fan_first_layers <= 0 && full_fan_speed_layer > 0) {
|
||||
// When ramping up fan speed from disable_fan_first_layers to full_fan_speed_layer, force disable_fan_first_layers above zero,
|
||||
// so there will be a zero fan speed at least at the 1st layer.
|
||||
@ -840,43 +850,52 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
if (EXTRUDER_CONFIG(cooling)) {
|
||||
if (layer_time < slowdown_below_layer_time) {
|
||||
// Layer time very short. Enable the fan to a full throttle.
|
||||
fan_speed_new = max_fan_speed;
|
||||
custom_fan_speed_limits.first = fan_speed_new;
|
||||
fan_speed_new = max_fan_speed;
|
||||
requested_fan_speed_limits.min_speed = max_fan_speed;
|
||||
} else if (layer_time < fan_below_layer_time) {
|
||||
// Layer time quite short. Enable the fan proportionally according to the current layer time.
|
||||
assert(layer_time >= slowdown_below_layer_time);
|
||||
double t = (layer_time - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time);
|
||||
fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5);
|
||||
custom_fan_speed_limits.first = fan_speed_new;
|
||||
const double t = (layer_time - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time);
|
||||
|
||||
fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5);
|
||||
requested_fan_speed_limits.min_speed = fan_speed_new;
|
||||
}
|
||||
}
|
||||
bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed);
|
||||
|
||||
bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed);
|
||||
if (int(layer_id) >= disable_fan_first_layers && int(layer_id) + 1 < full_fan_speed_layer) {
|
||||
// Ramp up the fan speed from disable_fan_first_layers to full_fan_speed_layer.
|
||||
float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers);
|
||||
fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 100);
|
||||
bridge_fan_speed = std::clamp(int(float(bridge_fan_speed) * factor + 0.5f), 0, 100);
|
||||
custom_fan_speed_limits.second = fan_speed_new;
|
||||
const float factor = float(int(layer_id + 1) - disable_fan_first_layers) / float(full_fan_speed_layer - disable_fan_first_layers);
|
||||
|
||||
fan_speed_new = std::clamp(int(float(fan_speed_new) * factor + 0.5f), 0, 100);
|
||||
bridge_fan_speed = std::clamp(int(float(bridge_fan_speed) * factor + 0.5f), 0, 100);
|
||||
requested_fan_speed_limits.max_speed = fan_speed_new;
|
||||
}
|
||||
|
||||
#undef EXTRUDER_CONFIG
|
||||
bridge_fan_control = bridge_fan_speed > fan_speed_new;
|
||||
} else { // fan disabled
|
||||
bridge_fan_control = false;
|
||||
bridge_fan_speed = 0;
|
||||
fan_speed_new = 0;
|
||||
custom_fan_speed_limits.second = 0;
|
||||
bridge_fan_control = false;
|
||||
bridge_fan_speed = 0;
|
||||
fan_speed_new = 0;
|
||||
requested_fan_speed_limits.max_speed = 0;
|
||||
}
|
||||
|
||||
requested_fan_speed_limits.min_speed = std::min(requested_fan_speed_limits.min_speed, requested_fan_speed_limits.max_speed);
|
||||
if (requested_fan_speed >= 0) {
|
||||
fan_speed_new = std::clamp(requested_fan_speed, requested_fan_speed_limits.min_speed, requested_fan_speed_limits.max_speed);
|
||||
}
|
||||
|
||||
if (fan_speed_new != m_fan_speed) {
|
||||
m_fan_speed = fan_speed_new;
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed);
|
||||
}
|
||||
custom_fan_speed_limits.first = std::min(custom_fan_speed_limits.first, custom_fan_speed_limits.second);
|
||||
return custom_fan_speed_limits;
|
||||
};
|
||||
|
||||
const char *pos = gcode.c_str();
|
||||
int current_feedrate = 0;
|
||||
std::pair<int,int> fan_speed_limits = change_extruder_set_fan();
|
||||
|
||||
change_extruder_set_fan();
|
||||
for (const CoolingLine *line : lines) {
|
||||
const char *line_start = gcode.c_str() + line->line_start;
|
||||
const char *line_end = gcode.c_str() + line->line_end;
|
||||
@ -887,17 +906,13 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
auto res = std::from_chars(line_start + m_toolchange_prefix.size(), line_end, new_extruder);
|
||||
if (res.ec != std::errc::invalid_argument && new_extruder != m_current_extruder) {
|
||||
m_current_extruder = new_extruder;
|
||||
fan_speed_limits = change_extruder_set_fan();
|
||||
change_extruder_set_fan();
|
||||
}
|
||||
new_gcode.append(line_start, line_end - line_start);
|
||||
} else if (line->type & CoolingLine::TYPE_SET_FAN_SPEED) {
|
||||
int new_speed = std::clamp(line->fan_speed, fan_speed_limits.first, fan_speed_limits.second);
|
||||
if (m_fan_speed != new_speed) {
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, new_speed);
|
||||
m_fan_speed = new_speed;
|
||||
}
|
||||
change_extruder_set_fan(line->fan_speed);
|
||||
} else if (line->type & CoolingLine::TYPE_RESET_FAN_SPEED){
|
||||
fan_speed_limits = change_extruder_set_fan();
|
||||
change_extruder_set_fan();
|
||||
} else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) {
|
||||
if (bridge_fan_control)
|
||||
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, bridge_fan_speed);
|
||||
|
@ -215,7 +215,8 @@ OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes,
|
||||
const FullPrintConfig &config,
|
||||
const size_t extruder_id,
|
||||
const float external_perimeter_reference_speed,
|
||||
const float default_speed)
|
||||
const float default_speed,
|
||||
const std::optional<float> ¤t_fan_speed)
|
||||
{
|
||||
assert(attributes.overhang_attributes.has_value());
|
||||
|
||||
@ -249,6 +250,9 @@ OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes,
|
||||
|
||||
if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) {
|
||||
overhang_speeds.fan_speed = -1;
|
||||
} else if (current_fan_speed.has_value() && (fan_speed < *current_fan_speed) && (*current_fan_speed - fan_speed) <= MIN_FAN_SPEED_NEGATIVE_CHANGE_TO_EMIT) {
|
||||
// Always allow the fan speed to be increased without any hysteresis, but the speed will be decreased only when it exceeds a limit for minimum change.
|
||||
overhang_speeds.fan_speed = *current_fan_speed;
|
||||
}
|
||||
|
||||
return overhang_speeds;
|
||||
|
@ -44,6 +44,10 @@ class Linef;
|
||||
|
||||
namespace Slic3r::ExtrusionProcessor {
|
||||
|
||||
// Minimum decrease of the fan speed in percents that will be emitted into g-code.
|
||||
// Decreases below this limit will be omitted to not overflow the g-code with fan speed changes.
|
||||
const constexpr float MIN_FAN_SPEED_NEGATIVE_CHANGE_TO_EMIT = 3.f;
|
||||
|
||||
struct ExtendedPoint
|
||||
{
|
||||
Vec2d position;
|
||||
@ -276,7 +280,8 @@ OverhangSpeeds calculate_overhang_speed(const ExtrusionAttributes &attributes,
|
||||
const FullPrintConfig &config,
|
||||
size_t extruder_id,
|
||||
float external_perimeter_reference_speed,
|
||||
float default_speed);
|
||||
float default_speed,
|
||||
const std::optional<float> ¤t_fan_speed);
|
||||
|
||||
} // namespace Slic3r::ExtrusionProcessor
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user