Implemented dynamic fan control. Cooling buffer does not yet relfect that

This commit is contained in:
Pavel Mikus 2023-02-22 18:59:54 +01:00 committed by Pavel Mikuš
parent 278f633a3d
commit 10d04529d6
8 changed files with 151 additions and 76 deletions

View File

@ -2868,19 +2868,37 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
);
}
bool variable_speed = false;
bool variable_speed_or_fan_speed = false;
std::vector<ProcessedPoint> new_points{};
if (this->m_config.enable_dynamic_overhang_speeds && !this->on_first_layer() && path.role().is_perimeter()) {
std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_with_speeds{
{0, m_config.overhang_speed_0}, {20, m_config.overhang_speed_1}, {40, m_config.overhang_speed_2},
{60, m_config.overhang_speed_3}, {80, m_config.overhang_speed_4},
};
if ((this->m_config.enable_dynamic_overhang_speeds || this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) &&
!this->on_first_layer() && path.role().is_perimeter()) {
std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}};
if (this->m_config.enable_dynamic_overhang_speeds) {
std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_with_speeds = {{0, m_config.overhang_speed_0},
{25, m_config.overhang_speed_1},
{50, m_config.overhang_speed_2},
{75, m_config.overhang_speed_3},
{100,
ConfigOptionFloatOrPercent{speed, false}}};
}
std::vector<std::pair<int, ConfigOptionInts>> overhang_w_fan_speeds = {{100, ConfigOptionInts{0}}};
if (this->m_config.enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) {
std::vector<std::pair<int, ConfigOptionInts>> overhang_w_fan_speeds = {{0, m_config.overhang_fan_speed_0},
{25, m_config.overhang_fan_speed_1},
{50, m_config.overhang_fan_speed_2},
{75, m_config.overhang_fan_speed_3},
{100, ConfigOptionInts{0}}};
}
double external_perim_reference_speed = std::min(m_config.get_abs_value("external_perimeter_speed"),
std::min(EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm,
m_config.max_volumetric_speed.value / path.mm3_per_mm));
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, external_perim_reference_speed,
new_points = m_extrusion_quality_estimator.estimate_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds,
m_writer.extruder()->id(), external_perim_reference_speed,
speed);
variable_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed; });
variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(),
[speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; });
}
double F = speed * 60; // convert mm/sec to mm/min
@ -2944,7 +2962,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER";
}
if (!variable_speed) {
if (!variable_speed_or_fan_speed) {
// F is mm per minute.
gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments);
double path_length = 0.;
@ -2969,20 +2987,26 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de
marked_comment = description;
marked_comment += description_bridge;
}
double last_set_speed = new_points[0].speed * 60.0;
double last_set_speed = new_points[0].speed * 60.0;
double last_set_fan_speed = new_points[0].fan_speed;
gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments);
gcode += m_writer.set_fan(last_set_fan_speed);
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];
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
const double line_length = (p - prev).norm();
const ProcessedPoint &processed_point = new_points[i];
Vec2d p = this->point_to_gcode_quantized(processed_point.p);
const double line_length = (p - prev).norm();
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, marked_comment);
prev = p;
prev = p;
double new_speed = processed_point.speed * 60.0;
if (last_set_speed != new_speed) {
gcode += m_writer.set_speed(new_speed, "", cooling_marker_setspeed_comments);
last_set_speed = new_speed;
}
if (last_set_fan_speed != processed_point.fan_speed) {
gcode += m_writer.set_fan(processed_point.fan_speed);
last_set_fan_speed = processed_point.fan_speed;
}
}
}

View File

@ -238,6 +238,7 @@ struct ProcessedPoint
{
Point p;
float speed = 1.0f;
int fan_speed = 0;
};
class ExtrusionQualityEstimator
@ -259,31 +260,25 @@ public:
std::vector<ProcessedPoint> estimate_extrusion_quality(const ExtrusionPath &path,
const std::vector<std::pair<int, ConfigOptionFloatOrPercent>> overhangs_w_speeds,
float ext_perimeter_speed,
float original_speed)
const std::vector<std::pair<int, ConfigOptionInts>> overhangs_w_fan_speeds,
size_t extruder_id,
float ext_perimeter_speed,
float original_speed)
{
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
std::vector<std::pair<float, float>> speed_sections;
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
std::map<float, float> speed_sections;
for (size_t i = 0; i < overhangs_w_speeds.size(); i++) {
float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0));
float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
overhangs_w_speeds[i].second.value;
speed_sections.push_back({distance, speed});
float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0));
float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
overhangs_w_speeds[i].second.value;
speed_sections[distance] = speed;
}
std::sort(speed_sections.begin(), speed_sections.end(), [](const std::pair<float, float> &a, const std::pair<float, float> &b) {
if (a.first == b.first) {
return a.second > b.second;
}
return a.first < b.first;
});
std::pair<float, float> last_section{INFINITY, 0};
for (auto &section : speed_sections) {
if (section.first == last_section.first) {
section.second = last_section.second;
} else {
last_section = section;
}
std::map<float, float> fan_speed_sections;
for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) {
float distance = path.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0));
float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id);
fan_speed_sections[distance] = fan_speed;
}
std::vector<ExtendedPoint> extended_points =
@ -295,28 +290,26 @@ public:
const ExtendedPoint &curr = extended_points[i];
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
auto calculate_speed = [&speed_sections, &original_speed](float distance) {
float final_speed;
if (distance <= speed_sections.front().first) {
final_speed = original_speed;
} else if (distance >= speed_sections.back().first) {
final_speed = speed_sections.back().second;
} else {
size_t section_idx = 0;
while (distance > speed_sections[section_idx + 1].first) {
section_idx++;
}
float t = (distance - speed_sections[section_idx].first) /
(speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
t = std::clamp(t, 0.0f, 1.0f);
final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
auto interpolate_speed = [](const std::map<float, float> &values, float distance) {
auto upper_dist = values.lower_bound(distance);
if (upper_dist == values.end()) {
return values.rend()->second;
}
return final_speed;
if (upper_dist == values.begin()) {
return upper_dist->second;
}
auto lower_dist = --upper_dist;
float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first);
return (1.0f - t) * lower_dist->second + t * upper_dist->second;
};
float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance));
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance),
interpolate_speed(speed_sections, next.distance));
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance),
interpolate_speed(fan_speed_sections, next.distance));
processed_points.push_back({scaled(curr.position), extrusion_speed});
processed_points.push_back({scaled(curr.position), extrusion_speed, int(fan_speed)});
}
return processed_points;
}

View File

@ -440,7 +440,7 @@ static std::vector<std::string> s_Preset_print_options {
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"enable_dynamic_overhang_speeds", "overhang_speed_0", "overhang_speed_1", "overhang_speed_2", "overhang_speed_3", "overhang_speed_4",
"enable_dynamic_overhang_speeds", "overhang_speed_0", "overhang_speed_1", "overhang_speed_2", "overhang_speed_3",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
"external_perimeter_acceleration", "top_solid_infill_acceleration", "solid_infill_acceleration",
@ -473,7 +473,8 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
"temperature", "idle_temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
"max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
"start_filament_gcode", "end_filament_gcode",
"start_filament_gcode", "end_filament_gcode", "enable_dynamic_fan_speeds",
"overhang_fan_speed_0", "overhang_fan_speed_1", "overhang_fan_speed_2", "overhang_fan_speed_3",
// Retract overrides
"filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",

View File

@ -66,6 +66,11 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"between_objects_gcode",
"bridge_acceleration",
"bridge_fan_speed",
"enable_dynamic_fan_speeds",
"overhang_fan_speed_0",
"overhang_fan_speed_1",
"overhang_fan_speed_2",
"overhang_fan_speed_3",
"colorprint_heights",
"cooling",
"default_acceleration",

View File

@ -535,7 +535,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Enable dynamic overhang speeds");
def->category = L("Speed");
def->tooltip = L("This setting enables dynamic speed control on overhangs.");
def->mode = comAdvanced;
def->mode = comExpert;
def->set_default_value(new ConfigOptionBool(false));
auto overhang_speed_setting_description = L("Overhang size is expressed as a percentage of overlap of the extrusion with the previous layer: "
@ -549,44 +549,82 @@ void PrintConfigDef::init_fff_params()
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
def = this->add("overhang_speed_1", coFloatOrPercent);
def->label = L("speed for 20\% overlap");
def->label = L("speed for 25\% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(20, false));
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
def = this->add("overhang_speed_2", coFloatOrPercent);
def->label = L("speed for 40\% overlap");
def->label = L("speed for 50\% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(25, false));
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloatOrPercent(20, false));
def = this->add("overhang_speed_3", coFloatOrPercent);
def->label = L("speed for 60\% overlap");
def->label = L("speed for 75\% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloatOrPercent(25, false));
def = this->add("overhang_speed_4", coFloatOrPercent);
def->label = L("speed for 80\% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(25, false));
def = this->add("enable_dynamic_fan_speeds", coBools);
def->label = L("Enable dynamic fan speeds");
def->tooltip = L("This setting enables dynamic fan speed control on overhangs.");
def->mode = comExpert;
def->set_default_value(new ConfigOptionBools{false});
auto fan_speed_setting_description = L(
"Overhang size is expressed as a percentage of overlap of the extrusion with the previous layer: "
"100% would be full overlap (no overhang), while 0% represents full overhang (floating extrusion, bridge). "
"Fan speeds for overhang sizes in between are calculated via linear interpolation. ");
def = this->add("overhang_fan_speed_0", coInts);
def->label = L("speed for 0\% overlap (bridge)");
def->tooltip = fan_speed_setting_description;
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comExpert;
def->set_default_value(new ConfigOptionInts{100});
def = this->add("overhang_fan_speed_1", coInts);
def->label = L("speed for 25\% overlap");
def->tooltip = fan_speed_setting_description;
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comExpert;
def->set_default_value(new ConfigOptionInts{75});
def = this->add("overhang_fan_speed_2", coInts);
def->label = L("speed for 50\% overlap");
def->tooltip = fan_speed_setting_description;
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comExpert;
def->set_default_value(new ConfigOptionInts{50});
def = this->add("overhang_fan_speed_3", coInts);
def->label = L("speed for 75\% overlap");
def->tooltip = fan_speed_setting_description;
def->sidetext = L("%");
def->min = 0;
def->max = 100;
def->mode = comExpert;
def->set_default_value(new ConfigOptionInts{25});
def = this->add("brim_width", coFloat);
def->label = L("Brim width");

View File

@ -577,7 +577,6 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloatOrPercent, overhang_speed_1))
((ConfigOptionFloatOrPercent, overhang_speed_2))
((ConfigOptionFloatOrPercent, overhang_speed_3))
((ConfigOptionFloatOrPercent, overhang_speed_4))
((ConfigOptionBool, external_perimeters_first))
((ConfigOptionBool, extra_perimeters))
((ConfigOptionBool, extra_perimeters_on_overhangs))
@ -750,6 +749,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionInts, bed_temperature))
((ConfigOptionFloat, bridge_acceleration))
((ConfigOptionInts, bridge_fan_speed))
((ConfigOptionBools, enable_dynamic_fan_speeds))
((ConfigOptionInts, overhang_fan_speed_0))
((ConfigOptionInts, overhang_fan_speed_1))
((ConfigOptionInts, overhang_fan_speed_2))
((ConfigOptionInts, overhang_fan_speed_3))
((ConfigOptionBool, complete_objects))
((ConfigOptionFloats, colorprint_heights))
((ConfigOptionBools, cooling))

View File

@ -769,7 +769,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "overhang_speed_1"
|| opt_key == "overhang_speed_2"
|| opt_key == "overhang_speed_3"
|| opt_key == "overhang_speed_4"
|| opt_key == "external_perimeter_speed"
|| opt_key == "infill_speed"
|| opt_key == "perimeter_speed"

View File

@ -1554,7 +1554,6 @@ void TabPrint::build()
optgroup->append_single_option_line("overhang_speed_1");
optgroup->append_single_option_line("overhang_speed_2");
optgroup->append_single_option_line("overhang_speed_3");
optgroup->append_single_option_line("overhang_speed_4");
optgroup = page->new_optgroup(L("Speed for non-print moves"));
optgroup->append_single_option_line("travel_speed");
@ -1988,6 +1987,13 @@ void TabFilament::build()
optgroup->append_single_option_line("disable_fan_first_layers", category_path + "fan-settings");
optgroup->append_single_option_line("full_fan_speed_layer", category_path + "fan-settings");
optgroup = page->new_optgroup(L("Dynamic fan speeds"), 25);
optgroup->append_single_option_line("enable_dynamic_fan_speeds", category_path + "dynamic-fan-speeds");
optgroup->append_single_option_line("overhang_fan_speed_0", category_path + "dynamic-fan-speeds");
optgroup->append_single_option_line("overhang_fan_speed_1", category_path + "dynamic-fan-speeds");
optgroup->append_single_option_line("overhang_fan_speed_2", category_path + "dynamic-fan-speeds");
optgroup->append_single_option_line("overhang_fan_speed_3", category_path + "dynamic-fan-speeds");
optgroup = page->new_optgroup(L("Cooling thresholds"), 25);
optgroup->append_single_option_line("fan_below_layer_time", category_path + "cooling-thresholds");
optgroup->append_single_option_line("slowdown_below_layer_time", category_path + "cooling-thresholds");
@ -2140,6 +2146,11 @@ void TabFilament::toggle_options()
for (auto el : { "min_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer" })
toggle_option(el, fan_always_on);
bool dynamic_fan_speeds = m_config->opt_bool("enable_dynamic_fan_speeds", 0);
for (int i = 0; i < 4; i++) {
toggle_option("overhang_fan_speed_"+std::to_string(i),dynamic_fan_speeds);
}
}
if (m_active_page->title() == "Filament Overrides")