My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

M48.cpp 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #include "../../inc/MarlinConfig.h"
  23. #if ENABLED(Z_MIN_PROBE_REPEATABILITY_TEST)
  24. #include "../gcode.h"
  25. #include "../../module/motion.h"
  26. #include "../../module/probe.h"
  27. #include "../../lcd/marlinui.h"
  28. #include "../../feature/bedlevel/bedlevel.h"
  29. #if HAS_LEVELING
  30. #include "../../module/planner.h"
  31. #endif
  32. #if HAS_PTC
  33. #include "../../feature/probe_temp_comp.h"
  34. #endif
  35. /**
  36. * M48: Z probe repeatability measurement function.
  37. *
  38. * Usage:
  39. * M48 <P#> <X#> <Y#> <V#> <E> <L#> <S> <C#>
  40. * P = Number of sampled points (4-50, default 10)
  41. * X = Sample X position
  42. * Y = Sample Y position
  43. * V = Verbose level (0-4, default=1)
  44. * E = Engage Z probe for each reading
  45. * L = Number of legs of movement before probe
  46. * S = Schizoid (Or Star if you prefer)
  47. * C = Enable probe temperature compensation (0 or 1, default 1)
  48. *
  49. * This function requires the machine to be homed before invocation.
  50. */
  51. void GcodeSuite::M48() {
  52. if (homing_needed_error()) return;
  53. const int8_t verbose_level = parser.byteval('V', 1);
  54. if (!WITHIN(verbose_level, 0, 4)) {
  55. SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-4).");
  56. return;
  57. }
  58. if (verbose_level > 0)
  59. SERIAL_ECHOLNPGM("M48 Z-Probe Repeatability Test");
  60. const int8_t n_samples = parser.byteval('P', 10);
  61. if (!WITHIN(n_samples, 4, 50)) {
  62. SERIAL_ECHOLNPGM("?Sample size not plausible (4-50).");
  63. return;
  64. }
  65. const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
  66. // Test at the current position by default, overridden by X and Y
  67. const xy_pos_t test_position = {
  68. parser.linearval('X', current_position.x + probe.offset_xy.x), // If no X use the probe's current X position
  69. parser.linearval('Y', current_position.y + probe.offset_xy.y) // If no Y, ditto
  70. };
  71. if (!probe.can_reach(test_position)) {
  72. ui.set_status(GET_TEXT_F(MSG_M48_OUT_OF_BOUNDS), 99);
  73. SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
  74. return;
  75. }
  76. // Get the number of leg moves per test-point
  77. bool seen_L = parser.seen('L');
  78. uint8_t n_legs = seen_L ? parser.value_byte() : 0;
  79. if (n_legs > 15) {
  80. SERIAL_ECHOLNPGM("?Legs of movement implausible (0-15).");
  81. return;
  82. }
  83. if (n_legs == 1) n_legs = 2;
  84. // Schizoid motion as an optional stress-test
  85. const bool schizoid_flag = parser.boolval('S');
  86. if (schizoid_flag && !seen_L) n_legs = 7;
  87. if (verbose_level > 2)
  88. SERIAL_ECHOLNPGM("Positioning the probe...");
  89. // Always disable Bed Level correction before probing...
  90. #if HAS_LEVELING
  91. const bool was_enabled = planner.leveling_active;
  92. set_bed_leveling_enabled(false);
  93. #endif
  94. TERN_(HAS_PTC, ptc.set_enabled(!parser.seen('C') || parser.value_bool()));
  95. // Work with reasonable feedrates
  96. remember_feedrate_scaling_off();
  97. // Working variables
  98. float mean = 0.0, // The average of all points so far, used to calculate deviation
  99. sigma = 0.0, // Standard deviation of all points so far
  100. min = 99999.9, // Smallest value sampled so far
  101. max = -99999.9, // Largest value sampled so far
  102. sample_set[n_samples]; // Storage for sampled values
  103. auto dev_report = [](const bool verbose, const_float_t mean, const_float_t sigma, const_float_t min, const_float_t max, const bool final=false) {
  104. if (verbose) {
  105. SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
  106. if (!final) SERIAL_ECHOPAIR_F(" Sigma: ", sigma, 6);
  107. SERIAL_ECHOPAIR_F(" Min: ", min, 3);
  108. SERIAL_ECHOPAIR_F(" Max: ", max, 3);
  109. SERIAL_ECHOPAIR_F(" Range: ", max-min, 3);
  110. if (final) SERIAL_EOL();
  111. }
  112. if (final) {
  113. SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
  114. SERIAL_EOL();
  115. }
  116. };
  117. // Move to the first point, deploy, and probe
  118. const float t = probe.probe_at_point(test_position, raise_after, verbose_level);
  119. bool probing_good = !isnan(t);
  120. if (probing_good) {
  121. randomSeed(millis());
  122. float sample_sum = 0.0;
  123. LOOP_L_N(n, n_samples) {
  124. #if HAS_STATUS_MESSAGE
  125. // Display M48 progress in the status bar
  126. ui.status_printf(0, F(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
  127. #endif
  128. // When there are "legs" of movement move around the point before probing
  129. if (n_legs) {
  130. // Pick a random direction, starting angle, and radius
  131. const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
  132. float angle = random(0, 360);
  133. const float radius = random(
  134. #if ENABLED(DELTA)
  135. int(0.1250000000 * (DELTA_PRINTABLE_RADIUS)),
  136. int(0.3333333333 * (DELTA_PRINTABLE_RADIUS))
  137. #else
  138. int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
  139. #endif
  140. );
  141. if (verbose_level > 3) {
  142. SERIAL_ECHOPGM("Start radius:", radius, " angle:", angle, " dir:");
  143. if (dir > 0) SERIAL_CHAR('C');
  144. SERIAL_ECHOLNPGM("CW");
  145. }
  146. // Move from leg to leg in rapid succession
  147. LOOP_L_N(l, n_legs - 1) {
  148. // Move some distance around the perimeter
  149. float delta_angle;
  150. if (schizoid_flag) {
  151. // The points of a 5 point star are 72 degrees apart.
  152. // Skip a point and go to the next one on the star.
  153. delta_angle = dir * 2.0 * 72.0;
  154. }
  155. else {
  156. // Just move further along the perimeter.
  157. delta_angle = dir * (float)random(25, 45);
  158. }
  159. angle += delta_angle;
  160. // Trig functions work without clamping, but just to be safe...
  161. while (angle > 360.0) angle -= 360.0;
  162. while (angle < 0.0) angle += 360.0;
  163. // Choose the next position as an offset to chosen test position
  164. const xy_pos_t noz_pos = test_position - probe.offset_xy;
  165. xy_pos_t next_pos = {
  166. noz_pos.x + float(cos(RADIANS(angle))) * radius,
  167. noz_pos.y + float(sin(RADIANS(angle))) * radius
  168. };
  169. #if ENABLED(DELTA)
  170. // If the probe can't reach the point on a round bed...
  171. // Simply scale the numbers to bring them closer to origin.
  172. while (!probe.can_reach(next_pos)) {
  173. next_pos *= 0.8f;
  174. if (verbose_level > 3)
  175. SERIAL_ECHOLNPGM_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
  176. }
  177. #elif HAS_ENDSTOPS
  178. // For a rectangular bed just keep the probe in bounds
  179. LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
  180. LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
  181. #endif
  182. if (verbose_level > 3)
  183. SERIAL_ECHOLNPGM_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
  184. do_blocking_move_to_xy(next_pos);
  185. } // n_legs loop
  186. } // n_legs
  187. // Probe a single point
  188. const float pz = probe.probe_at_point(test_position, raise_after, 0);
  189. // Break the loop if the probe fails
  190. probing_good = !isnan(pz);
  191. if (!probing_good) break;
  192. // Store the new sample
  193. sample_set[n] = pz;
  194. // Keep track of the largest and smallest samples
  195. NOMORE(min, pz);
  196. NOLESS(max, pz);
  197. // Get the mean value of all samples thus far
  198. sample_sum += pz;
  199. mean = sample_sum / (n + 1);
  200. // Calculate the standard deviation so far.
  201. // The value after the last sample will be the final output.
  202. float dev_sum = 0.0;
  203. LOOP_LE_N(j, n) dev_sum += sq(sample_set[j] - mean);
  204. sigma = SQRT(dev_sum / (n + 1));
  205. if (verbose_level > 1) {
  206. SERIAL_ECHO(n + 1);
  207. SERIAL_ECHOPGM(" of ", n_samples);
  208. SERIAL_ECHOPAIR_F(": z: ", pz, 3);
  209. SERIAL_CHAR(' ');
  210. dev_report(verbose_level > 2, mean, sigma, min, max);
  211. SERIAL_EOL();
  212. }
  213. } // n_samples loop
  214. }
  215. probe.stow();
  216. if (probing_good) {
  217. SERIAL_ECHOLNPGM("Finished!");
  218. dev_report(verbose_level > 0, mean, sigma, min, max, true);
  219. #if HAS_STATUS_MESSAGE
  220. // Display M48 results in the status bar
  221. char sigma_str[8];
  222. ui.status_printf(0, F(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str));
  223. #endif
  224. }
  225. restore_feedrate_and_scaling();
  226. // Re-enable bed level correction if it had been on
  227. TERN_(HAS_LEVELING, set_bed_leveling_enabled(was_enabled));
  228. // Re-enable probe temperature correction
  229. TERN_(HAS_PTC, ptc.set_enabled(true));
  230. report_current_position();
  231. }
  232. #endif // Z_MIN_PROBE_REPEATABILITY_TEST