Optional homing in LCD Repeatability Test (#19104)

This commit is contained in:
Giuliano Zaro 2020-08-22 04:20:30 +02:00 committed by Scott Lahteine
parent 7852b06038
commit 038945110f
4 changed files with 90 additions and 83 deletions

View File

@ -27,13 +27,10 @@
#include "../gcode.h" #include "../gcode.h"
#include "../../module/motion.h" #include "../../module/motion.h"
#include "../../module/probe.h" #include "../../module/probe.h"
#include "../../lcd/ultralcd.h"
#include "../../feature/bedlevel/bedlevel.h" #include "../../feature/bedlevel/bedlevel.h"
#if HAS_SPI_LCD
#include "../../lcd/ultralcd.h"
#endif
#if HAS_LEVELING #if HAS_LEVELING
#include "../../module/planner.h" #include "../../module/planner.h"
#endif #endif
@ -77,61 +74,85 @@ void GcodeSuite::M48() {
const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE; const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
xy_float_t next_pos = current_position; // Test at the current position by default, overridden by X and Y
const xy_pos_t test_position = {
const xy_pos_t probe_pos = { parser.linearval('X', current_position.x + probe.offset_xy.x), // If no X use the probe's current X position
parser.linearval('X', next_pos.x + probe.offset_xy.x), // If no X use the probe's current X position parser.linearval('Y', current_position.y + probe.offset_xy.y) // If no Y, ditto
parser.linearval('Y', next_pos.y + probe.offset_xy.y) // If no Y, ditto
}; };
if (!probe.can_reach(probe_pos)) { if (!probe.can_reach(test_position)) {
ui.set_status_P(GET_TEXT(MSG_M48_OUT_OF_BOUNDS), 99);
SERIAL_ECHOLNPGM("? (X,Y) out of bounds."); SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
return; return;
} }
// Get the number of leg moves per test-point
bool seen_L = parser.seen('L'); bool seen_L = parser.seen('L');
uint8_t n_legs = seen_L ? parser.value_byte() : 0; uint8_t n_legs = seen_L ? parser.value_byte() : 0;
if (n_legs > 15) { if (n_legs > 15) {
SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15)."); SERIAL_ECHOLNPGM("?Legs of movement implausible (0-15).");
return; return;
} }
if (n_legs == 1) n_legs = 2; if (n_legs == 1) n_legs = 2;
// Schizoid motion as an optional stress-test
const bool schizoid_flag = parser.boolval('S'); const bool schizoid_flag = parser.boolval('S');
if (schizoid_flag && !seen_L) n_legs = 7; if (schizoid_flag && !seen_L) n_legs = 7;
/**
* 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) if (verbose_level > 2)
SERIAL_ECHOLNPGM("Positioning the probe..."); SERIAL_ECHOLNPGM("Positioning the probe...");
// Disable bed level correction in M48 because we want the raw data when we probe // Always disable Bed Level correction before probing...
#if HAS_LEVELING #if HAS_LEVELING
const bool was_enabled = planner.leveling_active; const bool was_enabled = planner.leveling_active;
set_bed_leveling_enabled(false); set_bed_leveling_enabled(false);
#endif #endif
// Work with reasonable feedrates
remember_feedrate_scaling_off(); remember_feedrate_scaling_off();
float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples]; // Working variables
float mean = 0.0, // The average of all points so far, used to calculate deviation
sigma = 0.0, // Standard deviation of all points so far
min = 99999.9, // Smallest value sampled so far
max = -99999.9, // Largest value sampled so far
sample_set[n_samples]; // Storage for sampled values
auto dev_report = [](const bool verbose, const float &mean, const float &sigma, const float &min, const float &max, const bool final=false) {
if (verbose) {
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
if (!final) SERIAL_ECHOPAIR_F(" Sigma: ", sigma, 6);
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
SERIAL_ECHOPAIR_F(" Range: ", max-min, 3);
if (final) SERIAL_EOL();
}
if (final) {
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
SERIAL_EOL();
}
};
// Move to the first point, deploy, and probe // Move to the first point, deploy, and probe
const float t = probe.probe_at_point(probe_pos, raise_after, verbose_level); const float t = probe.probe_at_point(test_position, raise_after, verbose_level);
bool probing_good = !isnan(t); bool probing_good = !isnan(t);
if (probing_good) { if (probing_good) {
randomSeed(millis()); randomSeed(millis());
float sample_sum = 0.0;
LOOP_L_N(n, n_samples) { LOOP_L_N(n, n_samples) {
#if HAS_SPI_LCD #if HAS_SPI_LCD
// Display M48 progress in the status bar // Display M48 progress in the status bar
ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples)); ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
#endif #endif
// When there are "legs" of movement move around the point before probing
if (n_legs) { if (n_legs) {
// Pick a random direction, starting angle, and radius
const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
float angle = random(0, 360); float angle = random(0, 360);
const float radius = random( const float radius = random(
@ -142,48 +163,51 @@ void GcodeSuite::M48() {
int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE)) int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
#endif #endif
); );
if (verbose_level > 3) { if (verbose_level > 3) {
SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:"); SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:");
if (dir > 0) SERIAL_CHAR('C'); if (dir > 0) SERIAL_CHAR('C');
SERIAL_ECHOLNPGM("CW"); SERIAL_ECHOLNPGM("CW");
} }
// Move from leg to leg in rapid succession
LOOP_L_N(l, n_legs - 1) { LOOP_L_N(l, n_legs - 1) {
float delta_angle;
// Move some distance around the perimeter
float delta_angle;
if (schizoid_flag) { if (schizoid_flag) {
// The points of a 5 point star are 72 degrees apart. We need to // The points of a 5 point star are 72 degrees apart.
// skip a point and go to the next one on the star. // Skip a point and go to the next one on the star.
delta_angle = dir * 2.0 * 72.0; delta_angle = dir * 2.0 * 72.0;
} }
else { else {
// If we do this line, we are just trying to move further // Just move further along the perimeter.
// around the circle. delta_angle = dir * (float)random(25, 45);
delta_angle = dir * (float) random(25, 45);
} }
angle += delta_angle; angle += delta_angle;
while (angle > 360.0) angle -= 360.0; // We probably do not need to keep the angle between 0 and 2*PI, but the
// Arduino documentation says the trig functions should not be given values
while (angle < 0.0) angle += 360.0; // outside of this range. It looks like they behave correctly with
// numbers outside of the range, but just to be safe we clamp them.
const xy_pos_t noz_pos = probe_pos - probe.offset_xy; // Trig functions work without clamping, but just to be safe...
next_pos.set(noz_pos.x + cos(RADIANS(angle)) * radius, while (angle > 360.0) angle -= 360.0;
noz_pos.y + sin(RADIANS(angle)) * radius); while (angle < 0.0) angle += 360.0;
#if DISABLED(DELTA) // Choose the next position as an offset to chosen test position
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS); const xy_pos_t noz_pos = test_position - probe.offset_xy;
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS); xy_pos_t next_pos = {
#else noz_pos.x + cos(RADIANS(angle)) * radius,
// If we have gone out too far, we can do a simple fix and scale the numbers noz_pos.y + sin(RADIANS(angle)) * radius
// back in closer to the origin. };
#if ENABLED(DELTA)
// If the probe can't reach the point on a round bed...
// Simply scale the numbers to bring them closer to origin.
while (!probe.can_reach(next_pos)) { while (!probe.can_reach(next_pos)) {
next_pos *= 0.8f; next_pos *= 0.8f;
if (verbose_level > 3) if (verbose_level > 3)
SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y); SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
} }
#else
// For a rectangular bed just keep the probe in bounds
LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
#endif #endif
if (verbose_level > 3) if (verbose_level > 3)
@ -194,45 +218,35 @@ void GcodeSuite::M48() {
} // n_legs } // n_legs
// Probe a single point // Probe a single point
sample_set[n] = probe.probe_at_point(probe_pos, raise_after, 0); const float pz = probe.probe_at_point(test_position, raise_after, 0);
// Break the loop if the probe fails // Break the loop if the probe fails
probing_good = !isnan(sample_set[n]); probing_good = !isnan(pz);
if (!probing_good) break; if (!probing_good) break;
/** // Store the new sample
* Get the current mean for the data points we have so far sample_set[n] = pz;
*/
float sum = 0.0;
LOOP_LE_N(j, n) sum += sample_set[j];
mean = sum / (n + 1);
NOMORE(min, sample_set[n]); // Keep track of the largest and smallest samples
NOLESS(max, sample_set[n]); NOMORE(min, pz);
NOLESS(max, pz);
/** // Get the mean value of all samples thus far
* Now, use that mean to calculate the standard deviation for the sample_sum += pz;
* data points we have so far mean = sample_sum / (n + 1);
*/
sum = 0.0;
LOOP_LE_N(j, n)
sum += sq(sample_set[j] - mean);
sigma = SQRT(sum / (n + 1)); // Calculate the standard deviation so far.
if (verbose_level > 0) { // The value after the last sample will be the final output.
if (verbose_level > 1) { float dev_sum = 0.0;
SERIAL_ECHO(n + 1); LOOP_LE_N(j, n) dev_sum += sq(sample_set[j] - mean);
SERIAL_ECHOPAIR(" of ", int(n_samples)); sigma = SQRT(dev_sum / (n + 1));
SERIAL_ECHOPAIR_F(": z: ", sample_set[n], 3);
if (verbose_level > 2) { if (verbose_level > 1) {
SERIAL_ECHOPAIR_F(" mean: ", mean, 4); SERIAL_ECHO(n + 1);
SERIAL_ECHOPAIR_F(" sigma: ", sigma, 6); SERIAL_ECHOPAIR(" of ", int(n_samples));
SERIAL_ECHOPAIR_F(" min: ", min, 3); SERIAL_ECHOPAIR_F(": z: ", pz, 3);
SERIAL_ECHOPAIR_F(" max: ", max, 3); dev_report(verbose_level > 2, mean, sigma, min, max);
SERIAL_ECHOPAIR_F(" range: ", max-min, 3); SERIAL_EOL();
}
SERIAL_EOL();
}
} }
} // n_samples loop } // n_samples loop
@ -242,16 +256,7 @@ void GcodeSuite::M48() {
if (probing_good) { if (probing_good) {
SERIAL_ECHOLNPGM("Finished!"); SERIAL_ECHOLNPGM("Finished!");
dev_report(verbose_level > 0, mean, sigma, min, max, true);
if (verbose_level > 0) {
SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
SERIAL_ECHOPAIR_F(" Min: ", min, 3);
SERIAL_ECHOPAIR_F(" Max: ", max, 3);
SERIAL_ECHOLNPAIR_F(" Range: ", max-min, 3);
}
SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
SERIAL_EOL();
#if HAS_SPI_LCD #if HAS_SPI_LCD
// Display M48 results in the status bar // Display M48 results in the status bar

View File

@ -124,6 +124,7 @@ namespace Language_en {
PROGMEM Language_Str MSG_USER_MENU = _UxGT("Custom Commands"); PROGMEM Language_Str MSG_USER_MENU = _UxGT("Custom Commands");
PROGMEM Language_Str MSG_M48_TEST = _UxGT("M48 Probe Test"); PROGMEM Language_Str MSG_M48_TEST = _UxGT("M48 Probe Test");
PROGMEM Language_Str MSG_M48_POINT = _UxGT("M48 Point"); PROGMEM Language_Str MSG_M48_POINT = _UxGT("M48 Point");
PROGMEM Language_Str MSG_M48_OUT_OF_BOUNDS = _UxGT("Probe out of bounds");
PROGMEM Language_Str MSG_M48_DEVIATION = _UxGT("Deviation"); PROGMEM Language_Str MSG_M48_DEVIATION = _UxGT("Deviation");
PROGMEM Language_Str MSG_IDEX_MENU = _UxGT("IDEX Mode"); PROGMEM Language_Str MSG_IDEX_MENU = _UxGT("IDEX Mode");
PROGMEM Language_Str MSG_OFFSETS_MENU = _UxGT("Tool Offsets"); PROGMEM Language_Str MSG_OFFSETS_MENU = _UxGT("Tool Offsets");

View File

@ -122,6 +122,7 @@ namespace Language_it {
PROGMEM Language_Str MSG_LCD_TILTING_MESH = _UxGT("Punto inclinaz."); PROGMEM Language_Str MSG_LCD_TILTING_MESH = _UxGT("Punto inclinaz.");
PROGMEM Language_Str MSG_M48_TEST = _UxGT("Test sonda M48"); PROGMEM Language_Str MSG_M48_TEST = _UxGT("Test sonda M48");
PROGMEM Language_Str MSG_M48_POINT = _UxGT("Punto M48"); PROGMEM Language_Str MSG_M48_POINT = _UxGT("Punto M48");
PROGMEM Language_Str MSG_M48_OUT_OF_BOUNDS = _UxGT("Sonda oltre i limiti");
PROGMEM Language_Str MSG_M48_DEVIATION = _UxGT("Deviazione"); PROGMEM Language_Str MSG_M48_DEVIATION = _UxGT("Deviazione");
PROGMEM Language_Str MSG_IDEX_MENU = _UxGT("Modo IDEX"); PROGMEM Language_Str MSG_IDEX_MENU = _UxGT("Modo IDEX");
PROGMEM Language_Str MSG_OFFSETS_MENU = _UxGT("Strumenti Offsets"); PROGMEM Language_Str MSG_OFFSETS_MENU = _UxGT("Strumenti Offsets");

View File

@ -386,7 +386,7 @@ void menu_motion() {
#endif #endif
#if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST) #if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
GCODES_ITEM(MSG_M48_TEST, PSTR("G28\nM48 P10")); GCODES_ITEM(MSG_M48_TEST, PSTR("G28 O\nM48 P10"));
#endif #endif
// //