diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 46d6b96dd1..070d31c6d4 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -376,6 +376,7 @@ const bool Z_MAX_ENDSTOP_INVERTING = true; // set to true to invert the logic of //============================= Bed Auto Leveling =========================== //#define ENABLE_AUTO_BED_LEVELING // Delete the comment to enable (remove // at the start of the line) +#define Z_PROBE_REPEATABILITY_TEST // If not commented out, Z-Probe Repeatability test will be included if Auto Bed Leveling is Enabled. #ifdef ENABLE_AUTO_BED_LEVELING diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index b1c9e4d3ce..aa39e53d89 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -894,7 +894,7 @@ static void set_bed_level_equation_lsq(double *plane_equation_coefficients) current_position[Y_AXIS] = corrected_position.y; current_position[Z_AXIS] = corrected_position.z; - // but the bed at 0 so we don't go below it. + // put the bed at 0 so we don't go below it. current_position[Z_AXIS] = zprobe_zoffset; // in the lsq we reach here after raising the extruder due to the loop structure plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]); @@ -1862,6 +1862,280 @@ void process_commands() } } break; + +// M48 Z-Probe repeatability measurement function. +// +// Usage: M48 +// +// This function assumes the bed has been homed. Specificaly, that a G28 command +// as been issued prior to invoking the M48 Z-Probe repeatability measurement function. +// Any information generated by a prior G29 Bed leveling command will be lost and need to be +// regenerated. +// +// The number of samples will default to 10 if not specified. You can use upper or lower case +// letters for any of the options EXCEPT n. n must be in lower case because Marlin uses a capital +// N for its communication protocol and will get horribly confused if you send it a capital N. +// + +#ifdef ENABLE_AUTO_BED_LEVELING +#ifdef Z_PROBE_REPEATABILITY_TEST + + case 48: // M48 Z-Probe repeatability + { + #if Z_MIN_PIN == -1 + #error "You must have a Z_MIN endstop in order to enable calculation of Z-Probe repeatability." + #endif + + double sum=0.0; + double mean=0.0; + double sigma=0.0; + double sample_set[50]; + int verbose_level=1, n=0, j, n_samples = 10, n_legs=0, engage_probe_for_each_reading=0 ; + double X_current, Y_current, Z_current; + double X_probe_location, Y_probe_location, Z_start_location, ext_position; + + if (code_seen('V') || code_seen('v')) { + verbose_level = code_value(); + if (verbose_level<0 || verbose_level>4 ) { + SERIAL_PROTOCOLPGM("?Verbose Level not plausable.\n"); + goto Sigma_Exit; + } + } + + if (verbose_level > 0) { + SERIAL_PROTOCOLPGM("M48 Z-Probe Repeatability test. Version 2.00\n"); + SERIAL_PROTOCOLPGM("Full support at: http://3dprintboard.com/forum.php\n"); + } + + if (code_seen('n')) { + n_samples = code_value(); + if (n_samples<4 || n_samples>50 ) { + SERIAL_PROTOCOLPGM("?Specified sample size not plausable.\n"); + goto Sigma_Exit; + } + } + + X_current = X_probe_location = st_get_position_mm(X_AXIS); + Y_current = Y_probe_location = st_get_position_mm(Y_AXIS); + Z_current = st_get_position_mm(Z_AXIS); + Z_start_location = st_get_position_mm(Z_AXIS) + Z_RAISE_BEFORE_PROBING; + ext_position = st_get_position_mm(E_AXIS); + + if (code_seen('E') || code_seen('e') ) + engage_probe_for_each_reading++; + + if (code_seen('X') || code_seen('x') ) { + X_probe_location = code_value() - X_PROBE_OFFSET_FROM_EXTRUDER; + if (X_probe_locationX_MAX_POS ) { + SERIAL_PROTOCOLPGM("?Specified X position out of range.\n"); + goto Sigma_Exit; + } + } + + if (code_seen('Y') || code_seen('y') ) { + Y_probe_location = code_value() - Y_PROBE_OFFSET_FROM_EXTRUDER; + if (Y_probe_locationY_MAX_POS ) { + SERIAL_PROTOCOLPGM("?Specified Y position out of range.\n"); + goto Sigma_Exit; + } + } + + if (code_seen('L') || code_seen('l') ) { + n_legs = code_value(); + if ( n_legs==1 ) + n_legs = 2; + if ( n_legs<0 || n_legs>15 ) { + SERIAL_PROTOCOLPGM("?Specified number of legs in movement not plausable.\n"); + goto Sigma_Exit; + } + } + +// +// Do all the preliminary setup work. First raise the probe. +// + + st_synchronize(); + plan_bed_level_matrix.set_to_identity(); + plan_buffer_line( X_current, Y_current, Z_start_location, + ext_position, + homing_feedrate[Z_AXIS]/60, + active_extruder); + st_synchronize(); + +// +// Now get everything to the specified probe point So we can safely do a probe to +// get us close to the bed. If the Z-Axis is far from the bed, we don't want to +// use that as a starting point for each probe. +// + if (verbose_level > 2) + SERIAL_PROTOCOL("Positioning probe for the test.\n"); + + plan_buffer_line( X_probe_location, Y_probe_location, Z_start_location, + ext_position, + homing_feedrate[X_AXIS]/60, + active_extruder); + st_synchronize(); + + current_position[X_AXIS] = X_current = st_get_position_mm(X_AXIS); + current_position[Y_AXIS] = Y_current = st_get_position_mm(Y_AXIS); + current_position[Z_AXIS] = Z_current = st_get_position_mm(Z_AXIS); + current_position[E_AXIS] = ext_position = st_get_position_mm(E_AXIS); + +// +// OK, do the inital probe to get us close to the bed. +// Then retrace the right amount and use that in subsequent probes +// + + engage_z_probe(); + + setup_for_endstop_move(); + run_z_probe(); + + current_position[Z_AXIS] = Z_current = st_get_position_mm(Z_AXIS); + Z_start_location = st_get_position_mm(Z_AXIS) + Z_RAISE_BEFORE_PROBING; + + plan_buffer_line( X_probe_location, Y_probe_location, Z_start_location, + ext_position, + homing_feedrate[X_AXIS]/60, + active_extruder); + st_synchronize(); + current_position[Z_AXIS] = Z_current = st_get_position_mm(Z_AXIS); + + if (engage_probe_for_each_reading) + retract_z_probe(); + + for( n=0; nX_MAX_POS) + X_current = X_MAX_POS; + + if ( Y_currentY_MAX_POS) + Y_current = Y_MAX_POS; + + if (verbose_level>3 ) { + SERIAL_ECHOPAIR("x: ", X_current); + SERIAL_ECHOPAIR("y: ", Y_current); + SERIAL_PROTOCOLLNPGM(""); + } + + do_blocking_move_to( X_current, Y_current, Z_current ); + } + do_blocking_move_to( X_probe_location, Y_probe_location, Z_start_location); // Go back to the probe location + } + + if (engage_probe_for_each_reading) { + engage_z_probe(); + delay(1000); + } + + setup_for_endstop_move(); + run_z_probe(); + + sample_set[n] = current_position[Z_AXIS]; + +// +// Get the current mean for the data points we have so far +// + sum=0.0; + for( j=0; j<=n; j++) { + sum = sum + sample_set[j]; + } + mean = sum / (double (n+1)); +// +// Now, use that mean to calculate the standard deviation for the +// data points we have so far +// + + sum=0.0; + for( j=0; j<=n; j++) { + sum = sum + (sample_set[j]-mean) * (sample_set[j]-mean); + } + sigma = sqrt( sum / (double (n+1)) ); + + if (verbose_level > 1) { + SERIAL_PROTOCOL(n+1); + SERIAL_PROTOCOL(" of "); + SERIAL_PROTOCOL(n_samples); + SERIAL_PROTOCOLPGM(" z: "); + SERIAL_PROTOCOL_F(current_position[Z_AXIS], 6); + } + + if (verbose_level > 2) { + SERIAL_PROTOCOL(" mean: "); + SERIAL_PROTOCOL_F(mean,6); + + SERIAL_PROTOCOL(" sigma: "); + SERIAL_PROTOCOL_F(sigma,6); + } + + if (verbose_level > 0) + SERIAL_PROTOCOLPGM("\n"); + + plan_buffer_line( X_probe_location, Y_probe_location, Z_start_location, + current_position[E_AXIS], homing_feedrate[Z_AXIS]/60, active_extruder); + st_synchronize(); + + if (engage_probe_for_each_reading) { + retract_z_probe(); + delay(1000); + } + } + + retract_z_probe(); + delay(1000); + + clean_up_after_endstop_move(); + +// enable_endstops(true); + + if (verbose_level > 0) { + SERIAL_PROTOCOLPGM("Mean: "); + SERIAL_PROTOCOL_F(mean, 6); + SERIAL_PROTOCOLPGM("\n"); + } + +SERIAL_PROTOCOLPGM("Standard Deviation: "); +SERIAL_PROTOCOL_F(sigma, 6); +SERIAL_PROTOCOLPGM("\n\n"); + +Sigma_Exit: + break; + } +#endif // Z_PROBE_REPEATABILITY_TEST +#endif // ENABLE_AUTO_BED_LEVELING + case 104: // M104 if(setTargetedHotend(104)){ break;