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

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. xy_float_t next_pos = current_position;
  65. const xy_pos_t probe_pos = {
  66. parser.linearval('X', next_pos.x + probe_offset.x),
  67. parser.linearval('Y', next_pos.y + probe_offset.y)
  68. };
  69. if (!position_is_reachable_by_probe(probe_pos)) {
  70. SERIAL_ECHOLNPGM("? (X,Y) out of bounds.");
  71. return;
  72. }
  73. bool seen_L = parser.seen('L');
  74. uint8_t n_legs = seen_L ? parser.value_byte() : 0;
  75. if (n_legs > 15) {
  76. SERIAL_ECHOLNPGM("?Number of legs in movement not plausible (0-15).");
  77. return;
  78. }
  79. if (n_legs == 1) n_legs = 2;
  80. const bool schizoid_flag = parser.boolval('S');
  81. if (schizoid_flag && !seen_L) n_legs = 7;
  82. /**
  83. * Now get everything to the specified probe point So we can safely do a
  84. * probe to get us close to the bed. If the Z-Axis is far from the bed,
  85. * we don't want to use that as a starting point for each probe.
  86. */
  87. if (verbose_level > 2)
  88. SERIAL_ECHOLNPGM("Positioning the probe...");
  89. // Disable bed level correction in M48 because we want the raw data when we probe
  90. #if HAS_LEVELING
  91. const bool was_enabled = planner.leveling_active;
  92. set_bed_leveling_enabled(false);
  93. #endif
  94. remember_feedrate_scaling_off();
  95. float mean = 0.0, sigma = 0.0, min = 99999.9, max = -99999.9, sample_set[n_samples];
  96. // Move to the first point, deploy, and probe
  97. const float t = probe_at_point(probe_pos, raise_after, verbose_level);
  98. bool probing_good = !isnan(t);
  99. if (probing_good) {
  100. randomSeed(millis());
  101. for (uint8_t n = 0; n < n_samples; n++) {
  102. #if HAS_SPI_LCD
  103. // Display M48 progress in the status bar
  104. ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples));
  105. #endif
  106. if (n_legs) {
  107. const int dir = (random(0, 10) > 5.0) ? -1 : 1; // clockwise or counter clockwise
  108. float angle = random(0, 360);
  109. const float radius = random(
  110. #if ENABLED(DELTA)
  111. int(0.1250000000 * (DELTA_PRINTABLE_RADIUS)),
  112. int(0.3333333333 * (DELTA_PRINTABLE_RADIUS))
  113. #else
  114. int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE))
  115. #endif
  116. );
  117. if (verbose_level > 3) {
  118. SERIAL_ECHOPAIR("Start radius:", radius, " angle:", angle, " dir:");
  119. if (dir > 0) SERIAL_CHAR('C');
  120. SERIAL_ECHOLNPGM("CW");
  121. }
  122. for (uint8_t l = 0; l < n_legs - 1; l++) {
  123. float delta_angle;
  124. if (schizoid_flag) {
  125. // The points of a 5 point star are 72 degrees apart. We need to
  126. // skip a point and go to the next one on the star.
  127. delta_angle = dir * 2.0 * 72.0;
  128. }
  129. else {
  130. // If we do this line, we are just trying to move further
  131. // around the circle.
  132. delta_angle = dir * (float) random(25, 45);
  133. }
  134. angle += delta_angle;
  135. while (angle > 360.0) angle -= 360.0; // We probably do not need to keep the angle between 0 and 2*PI, but the
  136. // Arduino documentation says the trig functions should not be given values
  137. while (angle < 0.0) angle += 360.0; // outside of this range. It looks like they behave correctly with
  138. // numbers outside of the range, but just to be safe we clamp them.
  139. next_pos.set(probe_pos.x - probe_offset.x + cos(RADIANS(angle)) * radius,
  140. probe_pos.y - probe_offset.y + sin(RADIANS(angle)) * radius);
  141. #if DISABLED(DELTA)
  142. LIMIT(next_pos.x, X_MIN_POS, X_MAX_POS);
  143. LIMIT(next_pos.y, Y_MIN_POS, Y_MAX_POS);
  144. #else
  145. // If we have gone out too far, we can do a simple fix and scale the numbers
  146. // back in closer to the origin.
  147. while (!position_is_reachable_by_probe(next_pos)) {
  148. next_pos *= 0.8f;
  149. if (verbose_level > 3)
  150. SERIAL_ECHOLNPAIR("Moving inward: X", next_pos.x, " Y", next_pos.y);
  151. }
  152. #endif
  153. if (verbose_level > 3)
  154. SERIAL_ECHOLNPAIR("Going to: X", next_pos.x, " Y", next_pos.y);
  155. do_blocking_move_to_xy(next_pos);
  156. } // n_legs loop
  157. } // n_legs
  158. // Probe a single point
  159. sample_set[n] = probe_at_point(probe_pos, 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(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str));
  211. #endif
  212. }
  213. restore_feedrate_and_scaling();
  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