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

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