ENH: wipe tower support nozzle change

jira: none
Change-Id: I398a508cbc8d02644b60e504405392254329ef10
(cherry picked from commit 8b2a94ed5f8b757ccc4919b9182f3d8edaf767fd)
This commit is contained in:
zhimin.zeng
2024-07-30 19:25:45 +08:00
committed by Noisyfox
parent 4a031af091
commit 955bb4dea8
9 changed files with 299 additions and 22 deletions

View File

@@ -593,6 +593,7 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
result.extrusions = std::move(writer.extrusions());
result.wipe_path = std::move(writer.wipe_path());
result.is_finish_first = is_finish;
result.nozzle_change_result = m_nozzle_change_result;
// BBS
result.purge_volume = purge_volume;
return result;
@@ -621,7 +622,8 @@ WipeTower::WipeTower(const PrintConfig& config, int plate_idx, Vec3d plate_origi
m_current_tool(initial_tool),
//wipe_volumes(flush_matrix)
m_wipe_volume(prime_volume),
m_enable_timelapse_print(config.timelapse_type.value == TimelapseType::tlSmooth)
m_enable_timelapse_print(config.timelapse_type.value == TimelapseType::tlSmooth),
m_nozzle_change_length(config.extruder_change_length.get_at(0))
{
// Read absolute value of first layer speed, if given as percentage,
// it is taken over following default. Speeds from config are not
@@ -736,14 +738,49 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
return std::vector<ToolChangeResult>();
}
Vec2f WipeTower::get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length)
{
const float &xl = cleaning_box.ld.x();
const float &xr = cleaning_box.rd.x();
int line_count = wipe_length / (xr - xl);
float dy = m_layer_info->extra_spacing * m_perimeter_width;
float y_offset = float(line_count) * dy;
const Vec2f pos_offset = Vec2f(0.f, m_depth_traversed);
Vec2f res;
int index = m_cur_layer_id % 4;
switch (index % 4) {
case 0:
res = cleaning_box.ld + pos_offset;
break;
case 1:
res = cleaning_box.rd + pos_offset + Vec2f(0, y_offset);
break;
case 2:
res = cleaning_box.rd + pos_offset;
break;
case 3:
res = cleaning_box.ld + pos_offset + Vec2f(0, y_offset);
break;
default: break;
}
return res;
}
WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_perimeter, bool first_toolchange_to_nonsoluble)
{
m_nozzle_change_result.gcode.clear();
if (!m_filament_map.empty() && tool < m_filament_map.size() && m_filament_map[m_current_tool] != m_filament_map[tool]) {
m_nozzle_change_result = nozzle_change(m_current_tool, tool);
}
size_t old_tool = m_current_tool;
float wipe_depth = 0.f;
float wipe_length = 0.f;
float purge_volume = 0.f;
float nozzle_change_depth = 0.f;
// Finds this toolchange info
if (tool != (unsigned int)(-1))
{
@@ -752,6 +789,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
wipe_length = b.wipe_length;
wipe_depth = b.required_depth;
purge_volume = b.purge_volume;
nozzle_change_depth = b.nozzle_change_depth;
break;
}
}
@@ -782,7 +820,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
writer.speed_override_backup();
writer.speed_override(100);
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
Vec2f initial_position = get_next_pos(cleaning_box, wipe_length);
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
// Increase the extruder driver current to allow fast ramming.
@@ -799,9 +837,17 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
toolchange_Load(writer, cleaning_box);
// BBS
//writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
// ensure travel to initial_positoin
{
writer.travel(initial_position - Vec2f(0.5, 0.5));
writer.travel(initial_position);
}
toolchange_Wipe(writer, cleaning_box, wipe_length); // Wipe the newly loaded filament until the end of the assigned wipe area.
if (extrude_perimeter) {
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED) ? m_layer_info->toolchanges_depth() - m_layer_info->depth : 0.f),
m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED) ? m_layer_info->toolchanges_depth() - m_layer_info->depth : 0.f), m_wipe_tower_width,
m_layer_info->depth + m_perimeter_width);
// align the perimeter
Vec2f pos = initial_position;
@@ -836,7 +882,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
} else
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].nozzle_temperature);
m_depth_traversed += wipe_depth;
m_depth_traversed += (wipe_depth - nozzle_change_depth);
//BBS
//if (m_set_extruder_trimpot)
@@ -856,6 +902,114 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
return construct_tcr(writer, false, old_tool, false, purge_volume);
}
WipeTower::NozzleChangeResult WipeTower::nozzle_change(int old_filament_id, int new_filament_id)
{
float wipe_depth = 0.f;
float wipe_length = 0.f;
float purge_volume = 0.f;
int nozzle_change_line_count = 0;
// Finds this toolchange info
if (new_filament_id != (unsigned int) (-1)) {
for (const auto &b : m_layer_info->tool_changes)
if (b.new_tool == new_filament_id) {
wipe_length = b.wipe_length;
wipe_depth = b.required_depth;
purge_volume = b.purge_volume;
nozzle_change_line_count = (b.nozzle_change_depth + WT_EPSILON) / m_perimeter_width;
break;
}
} else {
// Otherwise we are going to Unload only. And m_layer_info would be invalid.
}
float nozzle_change_speed = 60.0f * m_filpar[m_current_tool].max_e_speed / m_extrusion_flow;
WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
.set_initial_tool(m_current_tool)
.set_extrusion_flow(m_extrusion_flow)
.set_y_shift(m_y_shift + (new_filament_id != (unsigned int) (-1) && (m_current_shape == SHAPE_REVERSED) ? m_layer_info->depth - m_layer_info->toolchanges_depth() : 0.f))
.append("; Nozzle change start\n");
writer.speed_override_backup();
writer.speed_override(100);
box_coordinates cleaning_box(Vec2f(m_perimeter_width, m_perimeter_width),
m_wipe_tower_width - 2 * m_perimeter_width,
(new_filament_id != (unsigned int) (-1) ? wipe_depth + m_depth_traversed - m_perimeter_width : m_wipe_tower_depth - m_perimeter_width));
Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed);
writer.travel(initial_position, 30000);
writer.append("G1 Z" + std::to_string(m_z_pos) + "\n");
writer.append("G1 E2 F1800\n");
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
const float &xl = cleaning_box.ld.x();
const float &xr = cleaning_box.rd.x();
float dy = m_layer_info->extra_spacing * m_perimeter_width;
const float target_speed = 4800.f;
float wipe_speed = std::max(target_speed, nozzle_change_speed);
float start_y = writer.y();
m_left_to_right = true;
bool need_change_flow = false;
// now the wiping itself:
for (int i = 0; true; ++i) {
if (m_left_to_right)
writer.extrude(xr + 0.25f * m_perimeter_width, writer.y(), wipe_speed);
else
writer.extrude(xl - 0.25f * m_perimeter_width, writer.y(), wipe_speed);
if (writer.y() - float(EPSILON) > cleaning_box.lu.y())
break; // in case next line would not fit
if (i == nozzle_change_line_count - 1)
break;
// stepping to the next line:
writer.extrude(writer.x(), writer.y() + dy);
m_left_to_right = !m_left_to_right;
}
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
m_depth_traversed += (nozzle_change_line_count) * dy;
auto float_to_string_with_precision = [](float value, int precision) {
std::ostringstream out;
out << std::fixed << std::setprecision(precision) << value;
return out.str();
};
float wipe_distance = 2;
Vec2f wipe_pos = writer.pos();
if (m_left_to_right) {
wipe_pos.x() -= wipe_distance;
}
else {
wipe_pos.x() += wipe_distance;
}
writer.append("; WIPE_START\n");
writer.extrude_explicit(wipe_pos, -2);
writer.append("; WIPE_END\n");
std::string lift_gcode = "G2 Z" + float_to_string_with_precision(m_z_pos + 0.4, 3) + " I0.86 J0.86 P1 F10000\n";
writer.append(lift_gcode);
writer.append("; Nozzle change end\n");
NozzleChangeResult result;
result.start_pos = writer.start_pos_rotated();
result.end_pos = writer.pos();
result.gcode = std::move(writer.gcode());
return result;
}
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
void WipeTower::toolchange_Unload(
@@ -1101,7 +1255,8 @@ void WipeTower::toolchange_Wipe(
}
#endif
m_left_to_right = true;
m_left_to_right = ((m_cur_layer_id + 3) % 4 >= 2);
bool is_from_up = (m_cur_layer_id % 2 == 1);
// BBS: do not need to move dy
#if 0
@@ -1137,9 +1292,12 @@ void WipeTower::toolchange_Wipe(
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(m_layer_height) + "\n");
}
if (writer.y() - float(EPSILON) > cleaning_box.lu.y())
if (!is_from_up && (writer.y() - float(EPSILON) > cleaning_box.lu.y()))
break; // in case next line would not fit
if (is_from_up && (writer.y() + float(EPSILON) < cleaning_box.ld.y()))
break;
x_to_wipe -= (xr - xl);
if (x_to_wipe < WT_EPSILON) {
// BBS: Delete some unnecessary travel
@@ -1147,7 +1305,11 @@ void WipeTower::toolchange_Wipe(
break;
}
// stepping to the next line:
writer.extrude(writer.x(), writer.y() + dy);
if (is_from_up)
writer.extrude(writer.x(), writer.y() - dy);
else
writer.extrude(writer.x(), writer.y() + dy);
m_left_to_right = !m_left_to_right;
}
@@ -1325,7 +1487,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
box_coordinates box = wt_box;
for (size_t i = 0; i < loops_num; ++i) {
box.expand(spacing);
writer.rectangle(box);
writer.rectangle(box, feedrate);
}
if (first_layer) {
@@ -1405,7 +1567,17 @@ void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned in
depth += std::ceil(length_to_extrude / width) * m_perimeter_width;
//depth *= m_extra_spacing;
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, 0.f, 0.f, wipe_volume, length_to_extrude, purge_volume));
float nozzle_change_depth = 0;
if (!m_filament_map.empty() && m_filament_map[old_tool] != m_filament_map[new_tool]) {
double e_flow = extrusion_flow(0.2);
double length = m_nozzle_change_length / e_flow;
int nozzle_change_line_count = length / (m_wipe_tower_width - m_perimeter_width) + 1;
nozzle_change_depth = nozzle_change_line_count * m_perimeter_width;
depth += nozzle_change_depth;
}
WipeTowerInfo::ToolChange tool_change = WipeTowerInfo::ToolChange(old_tool, new_tool, depth, 0.f, 0.f, wipe_volume, length_to_extrude, purge_volume);
tool_change.nozzle_change_depth = nozzle_change_depth;
m_plan.back().tool_changes.push_back(tool_change);
#endif
}
@@ -1473,6 +1645,12 @@ void WipeTower::plan_tower()
x_to_wipe_new = std::max(x_to_wipe_new, x_to_wipe);
int line_count = std::ceil((x_to_wipe_new - WT_EPSILON) / line_len);
{ // nozzle change length
int nozzle_change_line_count = (toolchange.nozzle_change_depth + WT_EPSILON) / m_perimeter_width;
line_count += nozzle_change_line_count;
}
toolchange.required_depth = line_count * m_perimeter_width;
toolchange.wipe_volume = x_to_wipe_new / x_to_wipe * toolchange.wipe_volume;
toolchange.wipe_length = x_to_wipe_new;
@@ -1496,7 +1674,7 @@ void WipeTower::plan_tower()
float max_depth_for_all = 0;
for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index)
{
float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
if (m_enable_timelapse_print && this_layer_depth < EPSILON)
this_layer_depth = min_wipe_tower_depth;
@@ -1621,6 +1799,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
m_old_temperature = -1; // reset last temperature written in the gcode
int index = 0;
std::vector<WipeTower::ToolChangeResult> layer_result;
int index = 0;
for (auto layer : m_plan)
{
m_cur_layer_id = index++;

View File

@@ -17,7 +17,6 @@ class PrintConfig;
enum GCodeFlavor : unsigned char;
class WipeTower
{
public:
@@ -38,6 +37,16 @@ public:
unsigned int tool;
};
struct NozzleChangeResult
{
std::string gcode;
Vec2f start_pos;
Vec2f end_pos;
std::vector<Vec2f> wipe_path;
};
struct ToolChangeResult
{
// Print heigh of this tool change.
@@ -81,6 +90,8 @@ public:
// executing the gcode finish_layer_tcr.
bool is_finish_first = false;
NozzleChangeResult nozzle_change_result;
// Sum the total length of the extrusion.
float total_extrusion_length_in_plane() {
float e_length = 0.f;
@@ -194,7 +205,7 @@ public:
m_num_tool_changes = 0;
} else
++ m_num_layer_changes;
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
m_extrusion_flow = extrusion_flow(layer_height);
@@ -213,7 +224,7 @@ public:
// Returns gcode to prime the nozzles at the front edge of the print bed.
std::vector<ToolChangeResult> prime(
// print_z of the first layer.
float initial_layer_print_height,
float initial_layer_print_height,
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
@@ -225,6 +236,8 @@ public:
// BBS
ToolChangeResult tool_change(size_t new_tool, bool extrude_perimeter = false, bool first_toolchange_to_nonsoluble = false);
NozzleChangeResult nozzle_change(int old_filament_id, int new_filament_id);
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
ToolChangeResult finish_layer(bool extruder_perimeter = true, bool extruder_fill = true);
@@ -248,6 +261,8 @@ public:
std::vector<float> get_used_filament() const { return m_used_filament_length; }
int get_number_of_toolchanges() const { return m_num_tool_changes; }
void set_filament_map(const std::vector<int> &filament_map) { m_filament_map = filament_map; }
struct FilamentParameters {
std::string material = "PLA";
bool is_soluble = false;
@@ -304,7 +319,12 @@ private:
float m_travel_speed = 0.f;
float m_first_layer_speed = 0.f;
size_t m_first_layer_idx = size_t(-1);
size_t m_cur_layer_id;
double m_nozzle_change_length = 10;
size_t m_cur_layer_id;
NozzleChangeResult m_nozzle_change_result;
std::vector<int> m_filament_map;
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
@@ -362,6 +382,8 @@ private:
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
Vec2f get_next_pos(const WipeTower::box_coordinates &cleaning_box, float wipe_length);
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
@@ -379,6 +401,7 @@ private:
float first_wipe_line;
float wipe_volume;
float wipe_length;
float nozzle_change_depth{0};
// BBS
float purge_volume;
ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f, float wl = 0, float pv = 0)
@@ -411,7 +434,7 @@ private:
void toolchange_Unload(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
const box_coordinates &cleaning_box,
const std::string& current_material,
const int new_temperature);
@@ -419,11 +442,11 @@ private:
WipeTowerWriter &writer,
const size_t new_tool,
const std::string& new_material);
void toolchange_Load(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box);
void toolchange_Wipe(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
@@ -435,4 +458,4 @@ private:
} // namespace Slic3r
#endif // WipeTowerPrusaMM_hpp_
#endif // WipeTowerPrusaMM_hpp_