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 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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 <http://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 "../../feature/bedlevel/bedlevel.h"
  28. #if HAS_SPI_LCD
  29. #include "../../lcd/ultralcd.h"
  30. #endif
  31. #if HAS_LEVELING
  32. #include "../../module/planner.h"
  33. #endif
  34. /**
  35. * M48: Z probe repeatability measurement function.
  36. *
  37. * Usage:
  38. * M48 <P#> <X#> <Y#> <V#> <E> <L#> <S>
  39. * P = Number of sampled points (4-50, default 10)
  40. * X = Sample X position
  41. * Y = Sample Y position
  42. * V = Verbose level (0-4, default=1)
  43. * E = Engage Z probe for each reading
  44. * L = Number of legs of movement before probe
  45. * S = Schizoid (Or Star if you prefer)
  46. *
  47. * This function requires the machine to be homed before invocation.
  48. */
  49. extern const char SP_Y_STR[];
  50. void GcodeSuite::M48() {
  51. if (axis_unhomed_error()) return;
  52. const int8_t verbose_level = parser.byteval('V', 1);
  53. if (!WITHIN(verbose_level, 0, 4)) {
  54. SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-4).");
  55. return;
  56. }
  57. if (verbose_level > 0)
  58. SERIAL_ECHOLNPGM("M48 Z-Probe Repeatability Test");
  59. const int8_t n_samples = parser.byteval('P', 10);
  60. if (!WITHIN(n_samples, 4, 50)) {
  61. SERIAL_ECHOLNPGM("?Sample size not plausible (4-50).");
  62. return;
  63. }
  64. const ProbePtRaise raise_after = parser.boolval('E') ? PROBE_PT_STOW : PROBE_PT_RAISE;
  65. xy_float_t next_pos = current_position;
  66. const xy_pos_t probe_pos = {
  67. parser.linearval('X', next_pos.x + probe.offset_xy.x), // If no X use the probe's current X position
  68. parser.linearval('Y', next_pos.y + probe.offset_xy.y) // If no Y, ditto
  69. };
  70. if (!probe.can_reach(probe_pos)) {
  71. SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
  72. return;
  73. }
  74. bool seen_L = parser.seen('L');
  75. uint8_t n_legs = seen_L ? parser.value_byte() : 0;
  76. if (n_legs > 15) {
  77. SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15).");
  78. return;
  79. }
  80. if (n_legs == 1) n_legs = 2;
  81. const bool schizoid_flag = parser.boolval('S');
  82. if (schizoid_flag && !seen_L) n_legs = 7;
  83. /**
  84. * Now get everything to the specified probe point So we can safely do a
  85. * probe to get us close to the bed. If the Z-Axis is far from the bed,
  86. * we don't want to use that as a starting point for each probe.
  87. */
  88. if (verbose_level > 2)
  89. SERIAL_ECHOLNPGM("Positioning the probe...");
  90. // Disable bed level correction in M48 because we want the raw data when we probe
  91. #if HAS_LEVELING
  92. const bool was_enabled = planner.leveling_active;
  93. set_bed_leveling_enabled(false);
  94. #endif
  95. remember_feedrate_scaling_off();
  96. float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
  97. // Move to the first point, deploy, and probe
  98. const float t = probe.probe_at_point(probe_pos, raise_after, verbose_level);
  99. bool probing_good = !isnan(t);
  100. if (probing_good) {
  101. randomSeed(millis());
  102. LOOP_L_N(n, n_samples) {
  103. #if HAS_SPI_LCD
  104. // Display M48 progress in the status bar
  105. ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
  106. #endif
  107. if (n_legs) {
  108. const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
  109. float angle = random(0, 360);
  110. const float radius = random(
  111. #if ENABLED(DELTA)
  112. int(0.1250000000 * (DELTA_PRINTABLE_RADIUS)),
  113. int(0.3333333333 * (DELTA_PRINTABLE_RADIUS))
  114. #else
  115. int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
  116. #endif
  117. );
  118. if (verbose_level > 3) {
  119. SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:");
  120. if (dir > 0) SERIAL_CHAR('C');
  121. SERIAL_ECHOLNPGM("CW");
  122. }
  123. LOOP_L_N(l, n_legs - 1) {
  124. float delta_angle;
  125. if (schizoid_flag) {
  126. // The points of a 5 point star are 72 degrees apart. We need to
  127. // skip a point and go to the next one on the star.
  128. delta_angle = dir * 2.0 * 72.0;
  129. }
  130. else {
  131. // If we do this line, we are just trying to move further
  132. // around the circle.
  133. delta_angle = dir * (float) random(25, 45);
  134. }
  135. angle += delta_angle;
  136. while (angle > 360.0) angle -= 360.0; // We probably do not need to keep the angle between 0 and 2*PI, but the
  137. // Arduino documentation says the trig functions should not be given values
  138. while (angle < 0.0) angle += 360.0; // outside of this range. It looks like they behave correctly with
  139. // numbers outside of the range, but just to be safe we clamp them.
  140. const xy_pos_t noz_pos = probe_pos - probe.offset_xy;
  141. next_pos.set(noz_pos.x + cos(RADIANS(angle)) * radius,
  142. noz_pos.y + sin(RADIANS(angle)) * radius);
  143. #if DISABLED(DELTA)
  144. LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
  145. LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
  146. #else
  147. // If we have gone out too far, we can do a simple fix and scale the numbers
  148. // back in closer to the origin.
  149. while (!probe.can_reach(next_pos)) {
  150. next_pos *= 0.8f;
  151. if (verbose_level > 3)
  152. SERIAL_ECHOLNPAIR_P(PSTR("Moving inward: X"), next_pos.x, SP_Y_STR, next_pos.y);
  153. }
  154. #endif
  155. if (verbose_level > 3)
  156. SERIAL_ECHOLNPAIR_P(PSTR("Going to: X"), next_pos.x, SP_Y_STR, next_pos.y);
  157. do_blocking_move_to_xy(next_pos);
  158. } // n_legs loop
  159. } // n_legs
  160. // Probe a single point
  161. sample_set[n] = probe.probe_at_point(probe_pos, raise_after, 0);
  162. // Break the loop if the probe fails
  163. probing_good = !isnan(sample_set[n]);
  164. if (!probing_good) break;
  165. /**
  166. * Get the current mean for the data points we have so far
  167. */
  168. float sum = 0.0;
  169. LOOP_LE_N(j, n) sum += sample_set[j];
  170. mean = sum / (n + 1);
  171. NOMORE(min, sample_set[n]);
  172. NOLESS(max, sample_set[n]);
  173. /**
  174. * Now, use that mean to calculate the standard deviation for the
  175. * data points we have so far
  176. */
  177. sum = 0.0;
  178. LOOP_LE_N(j, n)
  179. sum += sq(sample_set[j] - mean);
  180. sigma = SQRT(sum / (n + 1));
  181. if (verbose_level > 0) {
  182. if (verbose_level > 1) {
  183. SERIAL_ECHO(n + 1);
  184. SERIAL_ECHOPAIR(" of ", int(n_samples));
  185. SERIAL_ECHOPAIR_F(": z: ", sample_set[n], 3);
  186. if (verbose_level > 2) {
  187. SERIAL_ECHOPAIR_F(" mean: ", mean, 4);
  188. SERIAL_ECHOPAIR_F(" sigma: ", sigma, 6);
  189. SERIAL_ECHOPAIR_F(" min: ", min, 3);
  190. SERIAL_ECHOPAIR_F(" max: ", max, 3);
  191. SERIAL_ECHOPAIR_F(" range: ", max-min, 3);
  192. }
  193. SERIAL_EOL();
  194. }
  195. }
  196. } // n_samples loop
  197. }
  198. probe.stow();
  199. if (probing_good) {
  200. SERIAL_ECHOLNPGM("Finished!");
  201. if (verbose_level > 0) {
  202. SERIAL_ECHOPAIR_F("Mean: ", mean, 6);
  203. SERIAL_ECHOPAIR_F(" Min: ", min, 3);
  204. SERIAL_ECHOPAIR_F(" Max: ", max, 3);
  205. SERIAL_ECHOLNPAIR_F(" Range: ", max-min, 3);
  206. }
  207. SERIAL_ECHOLNPAIR_F("Standard Deviation: ", sigma, 6);
  208. SERIAL_EOL();
  209. #if HAS_SPI_LCD
  210. // Display M48 results in the status bar
  211. char sigma_str[8];
  212. ui.status_printf_P(0, PSTR(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str));
  213. #endif
  214. }
  215. restore_feedrate_and_scaling();
  216. // Re-enable bed level correction if it had been on
  217. #if HAS_LEVELING
  218. set_bed_leveling_enabled(was_enabled);
  219. #endif
  220. report_current_position();
  221. }
  222. #endif // Z_MIN_PROBE_REPEATABILITY_TEST