Add an option to segment leveled moves

This commit is contained in:
Scott Lahteine 2017-11-29 15:30:42 -06:00
parent ca80564a78
commit ef2531558c
8 changed files with 169 additions and 76 deletions

View file

@ -886,6 +886,11 @@
// The height can be set with M420 Z<height> // The height can be set with M420 Z<height>
#define ENABLE_LEVELING_FADE_HEIGHT #define ENABLE_LEVELING_FADE_HEIGHT
// For Cartesian machines, instead of dividing moves on mesh boundaries,
// split up moves into short segments like a Delta.
#define SEGMENT_LEVELED_MOVES
#define LEVELED_SEGMENT_LENGTH 5.0 // (mm) Length of all segments (except the last one)
/** /**
* Enable the G26 Mesh Validation Pattern tool. * Enable the G26 Mesh Validation Pattern tool.
*/ */

View file

@ -357,7 +357,7 @@ float bilinear_z_offset(const float raw[XYZ]) {
return offset; return offset;
} }
#if !IS_KINEMATIC #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
#define CELL_INDEX(A,V) ((V - bilinear_start[A##_AXIS]) * ABL_BG_FACTOR(A##_AXIS)) #define CELL_INDEX(A,V) ((V - bilinear_start[A##_AXIS]) * ABL_BG_FACTOR(A##_AXIS))
@ -420,6 +420,6 @@ float bilinear_z_offset(const float raw[XYZ]) {
bilinear_line_to_destination(fr_mm_s, x_splits, y_splits); bilinear_line_to_destination(fr_mm_s, x_splits, y_splits);
} }
#endif // !IS_KINEMATIC #endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES
#endif // AUTO_BED_LEVELING_BILINEAR #endif // AUTO_BED_LEVELING_BILINEAR

View file

@ -42,7 +42,7 @@
void bed_level_virt_interpolate(); void bed_level_virt_interpolate();
#endif #endif
#if !IS_KINEMATIC #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
void bilinear_line_to_destination(const float fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF); void bilinear_line_to_destination(const float fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF);
#endif #endif

View file

@ -52,6 +52,8 @@
ZERO(z_values); ZERO(z_values);
} }
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
/** /**
* Prepare a mesh-leveled linear move in a Cartesian setup, * Prepare a mesh-leveled linear move in a Cartesian setup,
* splitting the move where it crosses mesh borders. * splitting the move where it crosses mesh borders.
@ -111,6 +113,8 @@
mesh_line_to_destination(fr_mm_s, x_splits, y_splits); mesh_line_to_destination(fr_mm_s, x_splits, y_splits);
} }
#endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES
void mbl_mesh_report() { void mbl_mesh_report() {
SERIAL_PROTOCOLLNPGM("Num X,Y: " STRINGIFY(GRID_MAX_POINTS_X) "," STRINGIFY(GRID_MAX_POINTS_Y)); SERIAL_PROTOCOLLNPGM("Num X,Y: " STRINGIFY(GRID_MAX_POINTS_X) "," STRINGIFY(GRID_MAX_POINTS_Y));
SERIAL_PROTOCOLPGM("Z offset: "); SERIAL_PROTOCOL_F(mbl.z_offset, 5); SERIAL_PROTOCOLPGM("Z offset: "); SERIAL_PROTOCOL_F(mbl.z_offset, 5);

View file

@ -110,8 +110,9 @@ public:
extern mesh_bed_leveling mbl; extern mesh_bed_leveling mbl;
// Support functions, which may be embedded in the class later // Support functions, which may be embedded in the class later
#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
void mesh_line_to_destination(const float fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF); void mesh_line_to_destination(const float fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF);
#endif
void mbl_mesh_report(); void mbl_mesh_report();

View file

@ -934,7 +934,7 @@
/** /**
* Set granular options based on the specific type of leveling * Set granular options based on the specific type of leveling
*/ */
#define UBL_DELTA (ENABLED(AUTO_BED_LEVELING_UBL) && (ENABLED(DELTA) || ENABLED(UBL_GRANULAR_SEGMENTATION_FOR_CARTESIAN))) #define UBL_DELTA (ENABLED(AUTO_BED_LEVELING_UBL) && (ENABLED(DELTA) || ENABLED(SEGMENT_LEVELED_MOVES)))
#define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT)) #define ABL_PLANAR (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_3POINT))
#define ABL_GRID (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR)) #define ABL_GRID (ENABLED(AUTO_BED_LEVELING_LINEAR) || ENABLED(AUTO_BED_LEVELING_BILINEAR))
#define OLDSCHOOL_ABL (ABL_PLANAR || ABL_GRID) #define OLDSCHOOL_ABL (ABL_PLANAR || ABL_GRID)
@ -949,6 +949,10 @@
#define PROBE_BED_HEIGHT abs(BACK_PROBE_BED_POSITION - (FRONT_PROBE_BED_POSITION)) #define PROBE_BED_HEIGHT abs(BACK_PROBE_BED_POSITION - (FRONT_PROBE_BED_POSITION))
#endif #endif
#if ENABLED(SEGMENT_LEVELED_MOVES) && !defined(LEVELED_SEGMENT_LENGTH)
#define LEVELED_SEGMENT_LENGTH 5
#endif
/** /**
* Bed Probing rectangular bounds * Bed Probing rectangular bounds
* These can be further constrained in code for Delta and SCARA * These can be further constrained in code for Delta and SCARA

View file

@ -223,6 +223,8 @@
#error "ENABLE_MESH_EDIT_GFX_OVERLAY is now MESH_EDIT_GFX_OVERLAY. Please update your configuration." #error "ENABLE_MESH_EDIT_GFX_OVERLAY is now MESH_EDIT_GFX_OVERLAY. Please update your configuration."
#elif defined(BABYSTEP_ZPROBE_GFX_REVERSE) #elif defined(BABYSTEP_ZPROBE_GFX_REVERSE)
#error "BABYSTEP_ZPROBE_GFX_REVERSE is now set by OVERLAY_GFX_REVERSE. Please update your configurations." #error "BABYSTEP_ZPROBE_GFX_REVERSE is now set by OVERLAY_GFX_REVERSE. Please update your configurations."
#elif defined(UBL_GRANULAR_SEGMENTATION_FOR_CARTESIAN)
#error "UBL_GRANULAR_SEGMENTATION_FOR_CARTESIAN is now SEGMENT_LEVELED_MOVES. Please update your configuration."
#endif #endif
/** /**

View file

@ -522,8 +522,11 @@ float soft_endstop_min[XYZ] = { X_MIN_BED, Y_MIN_BED, Z_MIN_POS },
// Get the top feedrate of the move in the XY plane // Get the top feedrate of the move in the XY plane
const float _feedrate_mm_s = MMS_SCALED(feedrate_mm_s); const float _feedrate_mm_s = MMS_SCALED(feedrate_mm_s);
const float xdiff = rtarget[X_AXIS] - current_position[X_AXIS],
ydiff = rtarget[Y_AXIS] - current_position[Y_AXIS];
// If the move is only in Z/E don't split up the move // If the move is only in Z/E don't split up the move
if (rtarget[X_AXIS] == current_position[X_AXIS] && rtarget[Y_AXIS] == current_position[Y_AXIS]) { if (!xdiff && !ydiff) {
planner.buffer_line_kinematic(rtarget, _feedrate_mm_s, active_extruder); planner.buffer_line_kinematic(rtarget, _feedrate_mm_s, active_extruder);
return false; return false;
} }
@ -531,19 +534,15 @@ float soft_endstop_min[XYZ] = { X_MIN_BED, Y_MIN_BED, Z_MIN_POS },
// Fail if attempting move outside printable radius // Fail if attempting move outside printable radius
if (!position_is_reachable(rtarget[X_AXIS], rtarget[Y_AXIS])) return true; if (!position_is_reachable(rtarget[X_AXIS], rtarget[Y_AXIS])) return true;
// Get the cartesian distances moved in XYZE // Remaining cartesian distances
const float difference[XYZE] = { const float zdiff = rtarget[Z_AXIS] - current_position[Z_AXIS],
rtarget[X_AXIS] - current_position[X_AXIS], ediff = rtarget[E_AXIS] - current_position[E_AXIS];
rtarget[Y_AXIS] - current_position[Y_AXIS],
rtarget[Z_AXIS] - current_position[Z_AXIS],
rtarget[E_AXIS] - current_position[E_AXIS]
};
// Get the linear distance in XYZ // Get the linear distance in XYZ
float cartesian_mm = SQRT(sq(difference[X_AXIS]) + sq(difference[Y_AXIS]) + sq(difference[Z_AXIS])); float cartesian_mm = SQRT(sq(xdiff) + sq(ydiff) + sq(zdiff));
// If the move is very short, check the E move distance // If the move is very short, check the E move distance
if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = FABS(difference[E_AXIS]); if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = FABS(ediff);
// No E move either? Game over. // No E move either? Game over.
if (UNEAR_ZERO(cartesian_mm)) return true; if (UNEAR_ZERO(cartesian_mm)) return true;
@ -566,10 +565,10 @@ float soft_endstop_min[XYZ] = { X_MIN_BED, Y_MIN_BED, Z_MIN_POS },
// The approximate length of each segment // The approximate length of each segment
const float inv_segments = 1.0 / float(segments), const float inv_segments = 1.0 / float(segments),
segment_distance[XYZE] = { segment_distance[XYZE] = {
difference[X_AXIS] * inv_segments, xdiff * inv_segments,
difference[Y_AXIS] * inv_segments, ydiff * inv_segments,
difference[Z_AXIS] * inv_segments, zdiff * inv_segments,
difference[E_AXIS] * inv_segments ediff * inv_segments
}; };
// SERIAL_ECHOPAIR("mm=", cartesian_mm); // SERIAL_ECHOPAIR("mm=", cartesian_mm);
@ -644,6 +643,81 @@ float soft_endstop_min[XYZ] = { X_MIN_BED, Y_MIN_BED, Z_MIN_POS },
#else // !IS_KINEMATIC #else // !IS_KINEMATIC
#if ENABLED(SEGMENT_LEVELED_MOVES)
/**
* Prepare a segmented move on a CARTESIAN setup.
*
* This calls planner.buffer_line several times, adding
* small incremental moves. This allows the planner to
* apply more detailed bed leveling to the full move.
*/
inline void segmented_line_to_destination(const float &fr_mm_s, const float segment_size=LEVELED_SEGMENT_LENGTH) {
const float xdiff = destination[X_AXIS] - current_position[X_AXIS],
ydiff = destination[Y_AXIS] - current_position[Y_AXIS];
// If the move is only in Z/E don't split up the move
if (!xdiff && !ydiff) {
planner.buffer_line_kinematic(destination, fr_mm_s, active_extruder);
return;
}
// Remaining cartesian distances
const float zdiff = destination[Z_AXIS] - current_position[Z_AXIS],
ediff = destination[E_AXIS] - current_position[E_AXIS];
// Get the linear distance in XYZ
// If the move is very short, check the E move distance
// No E move either? Game over.
float cartesian_mm = SQRT(sq(xdiff) + sq(ydiff) + sq(zdiff));
if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = FABS(ediff);
if (UNEAR_ZERO(cartesian_mm)) return;
// The length divided by the segment size
// At least one segment is required
uint16_t segments = cartesian_mm / segment_size;
NOLESS(segments, 1);
// The approximate length of each segment
const float inv_segments = 1.0 / float(segments),
segment_distance[XYZE] = {
xdiff * inv_segments,
ydiff * inv_segments,
zdiff * inv_segments,
ediff * inv_segments
};
// SERIAL_ECHOPAIR("mm=", cartesian_mm);
// SERIAL_ECHOLNPAIR(" segments=", segments);
// Drop one segment so the last move is to the exact target.
// If there's only 1 segment, loops will be skipped entirely.
--segments;
// Get the raw current position as starting point
float raw[XYZE];
COPY(raw, current_position);
// Calculate and execute the segments
for (uint16_t s = segments + 1; --s;) {
static millis_t next_idle_ms = millis() + 200UL;
thermalManager.manage_heater(); // This returns immediately if not really needed.
if (ELAPSED(millis(), next_idle_ms)) {
next_idle_ms = millis() + 200UL;
idle();
}
LOOP_XYZE(i) raw[i] += segment_distance[i];
planner.buffer_line_kinematic(raw, fr_mm_s, active_extruder);
}
// Since segment_distance is only approximate,
// the final move must be to the exact destination.
planner.buffer_line_kinematic(destination, fr_mm_s, active_extruder);
}
#endif // SEGMENT_LEVELED_MOVES
/** /**
* Prepare a linear move in a Cartesian setup. * Prepare a linear move in a Cartesian setup.
* *
@ -654,10 +728,13 @@ float soft_endstop_min[XYZ] = { X_MIN_BED, Y_MIN_BED, Z_MIN_POS },
*/ */
inline bool prepare_move_to_destination_cartesian() { inline bool prepare_move_to_destination_cartesian() {
#if HAS_MESH #if HAS_MESH
if (planner.leveling_active) { if (planner.leveling_active && planner.leveling_active_at_z(destination[Z_AXIS])) {
#if ENABLED(AUTO_BED_LEVELING_UBL) #if ENABLED(AUTO_BED_LEVELING_UBL)
ubl.line_to_destination_cartesian(MMS_SCALED(feedrate_mm_s), active_extruder); // UBL's motion routine needs to know about ubl.line_to_destination_cartesian(MMS_SCALED(feedrate_mm_s), active_extruder); // UBL's motion routine needs to know about
return true; // all moves, including Z-only moves. return true; // all moves, including Z-only moves.
#elif ENABLED(SEGMENT_LEVELED_MOVES)
segmented_line_to_destination(MMS_SCALED(feedrate_mm_s));
return false;
#else #else
/** /**
* For MBL and ABL-BILINEAR only segment moves when X or Y are involved. * For MBL and ABL-BILINEAR only segment moves when X or Y are involved.