My Marlin configs for Fabrikator Mini and CTC i3 Pro B
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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(AUTO_BED_LEVELING_BILINEAR)
  24. #include "../bedlevel.h"
  25. #include "../../../module/motion.h"
  26. #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
  27. #include "../../../core/debug_out.h"
  28. #if ENABLED(EXTENSIBLE_UI)
  29. #include "../../../lcd/extui/ui_api.h"
  30. #endif
  31. xy_pos_t bilinear_grid_spacing, bilinear_start;
  32. xy_float_t bilinear_grid_factor;
  33. bed_mesh_t z_values;
  34. /**
  35. * Extrapolate a single point from its neighbors
  36. */
  37. static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) {
  38. if (!isnan(z_values[x][y])) return;
  39. if (DEBUGGING(LEVELING)) {
  40. DEBUG_ECHOPGM("Extrapolate [");
  41. if (x < 10) DEBUG_CHAR(' ');
  42. DEBUG_ECHO(x);
  43. DEBUG_CHAR(xdir ? (xdir > 0 ? '+' : '-') : ' ');
  44. DEBUG_CHAR(' ');
  45. if (y < 10) DEBUG_CHAR(' ');
  46. DEBUG_ECHO(y);
  47. DEBUG_CHAR(ydir ? (ydir > 0 ? '+' : '-') : ' ');
  48. DEBUG_ECHOLNPGM("]");
  49. }
  50. // Get X neighbors, Y neighbors, and XY neighbors
  51. const uint8_t x1 = x + xdir, y1 = y + ydir, x2 = x1 + xdir, y2 = y1 + ydir;
  52. float a1 = z_values[x1][y ], a2 = z_values[x2][y ],
  53. b1 = z_values[x ][y1], b2 = z_values[x ][y2],
  54. c1 = z_values[x1][y1], c2 = z_values[x2][y2];
  55. // Treat far unprobed points as zero, near as equal to far
  56. if (isnan(a2)) a2 = 0.0;
  57. if (isnan(a1)) a1 = a2;
  58. if (isnan(b2)) b2 = 0.0;
  59. if (isnan(b1)) b1 = b2;
  60. if (isnan(c2)) c2 = 0.0;
  61. if (isnan(c1)) c1 = c2;
  62. const float a = 2 * a1 - a2, b = 2 * b1 - b2, c = 2 * c1 - c2;
  63. // Take the average instead of the median
  64. z_values[x][y] = (a + b + c) / 3.0;
  65. TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
  66. // Median is robust (ignores outliers).
  67. // z_values[x][y] = (a < b) ? ((b < c) ? b : (c < a) ? a : c)
  68. // : ((c < b) ? b : (a < c) ? a : c);
  69. }
  70. //Enable this if your SCARA uses 180° of total area
  71. //#define EXTRAPOLATE_FROM_EDGE
  72. #if ENABLED(EXTRAPOLATE_FROM_EDGE)
  73. #if (GRID_MAX_POINTS_X) < (GRID_MAX_POINTS_Y)
  74. #define HALF_IN_X
  75. #elif (GRID_MAX_POINTS_Y) < (GRID_MAX_POINTS_X)
  76. #define HALF_IN_Y
  77. #endif
  78. #endif
  79. /**
  80. * Fill in the unprobed points (corners of circular print surface)
  81. * using linear extrapolation, away from the center.
  82. */
  83. void extrapolate_unprobed_bed_level() {
  84. #ifdef HALF_IN_X
  85. constexpr uint8_t ctrx2 = 0, xend = GRID_MAX_POINTS_X - 1;
  86. #else
  87. constexpr uint8_t ctrx1 = (GRID_MAX_CELLS_X) / 2, // left-of-center
  88. ctrx2 = (GRID_MAX_POINTS_X) / 2, // right-of-center
  89. xend = ctrx1;
  90. #endif
  91. #ifdef HALF_IN_Y
  92. constexpr uint8_t ctry2 = 0, yend = GRID_MAX_POINTS_Y - 1;
  93. #else
  94. constexpr uint8_t ctry1 = (GRID_MAX_CELLS_Y) / 2, // top-of-center
  95. ctry2 = (GRID_MAX_POINTS_Y) / 2, // bottom-of-center
  96. yend = ctry1;
  97. #endif
  98. LOOP_LE_N(xo, xend)
  99. LOOP_LE_N(yo, yend) {
  100. uint8_t x2 = ctrx2 + xo, y2 = ctry2 + yo;
  101. #ifndef HALF_IN_X
  102. const uint8_t x1 = ctrx1 - xo;
  103. #endif
  104. #ifndef HALF_IN_Y
  105. const uint8_t y1 = ctry1 - yo;
  106. #ifndef HALF_IN_X
  107. extrapolate_one_point(x1, y1, +1, +1); // left-below + +
  108. #endif
  109. extrapolate_one_point(x2, y1, -1, +1); // right-below - +
  110. #endif
  111. #ifndef HALF_IN_X
  112. extrapolate_one_point(x1, y2, +1, -1); // left-above + -
  113. #endif
  114. extrapolate_one_point(x2, y2, -1, -1); // right-above - -
  115. }
  116. }
  117. void print_bilinear_leveling_grid() {
  118. SERIAL_ECHOLNPGM("Bilinear Leveling Grid:");
  119. print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 3,
  120. [](const uint8_t ix, const uint8_t iy) { return z_values[ix][iy]; }
  121. );
  122. }
  123. #if ENABLED(ABL_BILINEAR_SUBDIVISION)
  124. #define ABL_GRID_POINTS_VIRT_X GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1
  125. #define ABL_GRID_POINTS_VIRT_Y GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1
  126. #define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2)
  127. #define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2)
  128. float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y];
  129. xy_pos_t bilinear_grid_spacing_virt;
  130. xy_float_t bilinear_grid_factor_virt;
  131. void print_bilinear_leveling_grid_virt() {
  132. SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:");
  133. print_2d_array(ABL_GRID_POINTS_VIRT_X, ABL_GRID_POINTS_VIRT_Y, 5,
  134. [](const uint8_t ix, const uint8_t iy) { return z_values_virt[ix][iy]; }
  135. );
  136. }
  137. #define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I))
  138. float bed_level_virt_coord(const uint8_t x, const uint8_t y) {
  139. uint8_t ep = 0, ip = 1;
  140. if (x > (GRID_MAX_POINTS_X) + 1 || y > (GRID_MAX_POINTS_Y) + 1) {
  141. // The requested point requires extrapolating two points beyond the mesh.
  142. // These values are only requested for the edges of the mesh, which are always an actual mesh point,
  143. // and do not require interpolation. When interpolation is not needed, this "Mesh + 2" point is
  144. // cancelled out in bed_level_virt_cmr and does not impact the result. Return 0.0 rather than
  145. // making this function more complex by extrapolating two points.
  146. return 0.0;
  147. }
  148. if (!x || x == ABL_TEMP_POINTS_X - 1) {
  149. if (x) {
  150. ep = (GRID_MAX_POINTS_X) - 1;
  151. ip = GRID_MAX_CELLS_X - 1;
  152. }
  153. if (WITHIN(y, 1, ABL_TEMP_POINTS_Y - 2))
  154. return LINEAR_EXTRAPOLATION(
  155. z_values[ep][y - 1],
  156. z_values[ip][y - 1]
  157. );
  158. else
  159. return LINEAR_EXTRAPOLATION(
  160. bed_level_virt_coord(ep + 1, y),
  161. bed_level_virt_coord(ip + 1, y)
  162. );
  163. }
  164. if (!y || y == ABL_TEMP_POINTS_Y - 1) {
  165. if (y) {
  166. ep = (GRID_MAX_POINTS_Y) - 1;
  167. ip = GRID_MAX_CELLS_Y - 1;
  168. }
  169. if (WITHIN(x, 1, ABL_TEMP_POINTS_X - 2))
  170. return LINEAR_EXTRAPOLATION(
  171. z_values[x - 1][ep],
  172. z_values[x - 1][ip]
  173. );
  174. else
  175. return LINEAR_EXTRAPOLATION(
  176. bed_level_virt_coord(x, ep + 1),
  177. bed_level_virt_coord(x, ip + 1)
  178. );
  179. }
  180. return z_values[x - 1][y - 1];
  181. }
  182. static float bed_level_virt_cmr(const float p[4], const uint8_t i, const float t) {
  183. return (
  184. p[i-1] * -t * sq(1 - t)
  185. + p[i] * (2 - 5 * sq(t) + 3 * t * sq(t))
  186. + p[i+1] * t * (1 + 4 * t - 3 * sq(t))
  187. - p[i+2] * sq(t) * (1 - t)
  188. ) * 0.5f;
  189. }
  190. static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) {
  191. float row[4], column[4];
  192. LOOP_L_N(i, 4) {
  193. LOOP_L_N(j, 4) {
  194. column[j] = bed_level_virt_coord(i + x - 1, j + y - 1);
  195. }
  196. row[i] = bed_level_virt_cmr(column, 1, ty);
  197. }
  198. return bed_level_virt_cmr(row, 1, tx);
  199. }
  200. void bed_level_virt_interpolate() {
  201. bilinear_grid_spacing_virt = bilinear_grid_spacing / (BILINEAR_SUBDIVISIONS);
  202. bilinear_grid_factor_virt = bilinear_grid_spacing_virt.reciprocal();
  203. LOOP_L_N(y, GRID_MAX_POINTS_Y)
  204. LOOP_L_N(x, GRID_MAX_POINTS_X)
  205. LOOP_L_N(ty, BILINEAR_SUBDIVISIONS)
  206. LOOP_L_N(tx, BILINEAR_SUBDIVISIONS) {
  207. if ((ty && y == (GRID_MAX_POINTS_Y) - 1) || (tx && x == (GRID_MAX_POINTS_X) - 1))
  208. continue;
  209. z_values_virt[x * (BILINEAR_SUBDIVISIONS) + tx][y * (BILINEAR_SUBDIVISIONS) + ty] =
  210. bed_level_virt_2cmr(
  211. x + 1,
  212. y + 1,
  213. (float)tx / (BILINEAR_SUBDIVISIONS),
  214. (float)ty / (BILINEAR_SUBDIVISIONS)
  215. );
  216. }
  217. }
  218. #endif // ABL_BILINEAR_SUBDIVISION
  219. // Refresh after other values have been updated
  220. void refresh_bed_level() {
  221. bilinear_grid_factor = bilinear_grid_spacing.reciprocal();
  222. TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate());
  223. }
  224. #if ENABLED(ABL_BILINEAR_SUBDIVISION)
  225. #define ABL_BG_SPACING(A) bilinear_grid_spacing_virt.A
  226. #define ABL_BG_FACTOR(A) bilinear_grid_factor_virt.A
  227. #define ABL_BG_POINTS_X ABL_GRID_POINTS_VIRT_X
  228. #define ABL_BG_POINTS_Y ABL_GRID_POINTS_VIRT_Y
  229. #define ABL_BG_GRID(X,Y) z_values_virt[X][Y]
  230. #else
  231. #define ABL_BG_SPACING(A) bilinear_grid_spacing.A
  232. #define ABL_BG_FACTOR(A) bilinear_grid_factor.A
  233. #define ABL_BG_POINTS_X GRID_MAX_POINTS_X
  234. #define ABL_BG_POINTS_Y GRID_MAX_POINTS_Y
  235. #define ABL_BG_GRID(X,Y) z_values[X][Y]
  236. #endif
  237. // Get the Z adjustment for non-linear bed leveling
  238. float bilinear_z_offset(const xy_pos_t &raw) {
  239. static float z1, d2, z3, d4, L, D;
  240. static xy_pos_t prev { -999.999, -999.999 }, ratio;
  241. // Whole units for the grid line indices. Constrained within bounds.
  242. static xy_int8_t thisg, nextg, lastg { -99, -99 };
  243. // XY relative to the probed area
  244. xy_pos_t rel = raw - bilinear_start.asFloat();
  245. #if ENABLED(EXTRAPOLATE_BEYOND_GRID)
  246. #define FAR_EDGE_OR_BOX 2 // Keep using the last grid box
  247. #else
  248. #define FAR_EDGE_OR_BOX 1 // Just use the grid far edge
  249. #endif
  250. if (prev.x != rel.x) {
  251. prev.x = rel.x;
  252. ratio.x = rel.x * ABL_BG_FACTOR(x);
  253. const float gx = constrain(FLOOR(ratio.x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX));
  254. ratio.x -= gx; // Subtract whole to get the ratio within the grid box
  255. #if DISABLED(EXTRAPOLATE_BEYOND_GRID)
  256. // Beyond the grid maintain height at grid edges
  257. NOLESS(ratio.x, 0); // Never <0 (>1 is ok when nextg.x==thisg.x)
  258. #endif
  259. thisg.x = gx;
  260. nextg.x = _MIN(thisg.x + 1, ABL_BG_POINTS_X - 1);
  261. }
  262. if (prev.y != rel.y || lastg.x != thisg.x) {
  263. if (prev.y != rel.y) {
  264. prev.y = rel.y;
  265. ratio.y = rel.y * ABL_BG_FACTOR(y);
  266. const float gy = constrain(FLOOR(ratio.y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX));
  267. ratio.y -= gy;
  268. #if DISABLED(EXTRAPOLATE_BEYOND_GRID)
  269. // Beyond the grid maintain height at grid edges
  270. NOLESS(ratio.y, 0); // Never < 0.0. (> 1.0 is ok when nextg.y==thisg.y.)
  271. #endif
  272. thisg.y = gy;
  273. nextg.y = _MIN(thisg.y + 1, ABL_BG_POINTS_Y - 1);
  274. }
  275. if (lastg != thisg) {
  276. lastg = thisg;
  277. // Z at the box corners
  278. z1 = ABL_BG_GRID(thisg.x, thisg.y); // left-front
  279. d2 = ABL_BG_GRID(thisg.x, nextg.y) - z1; // left-back (delta)
  280. z3 = ABL_BG_GRID(nextg.x, thisg.y); // right-front
  281. d4 = ABL_BG_GRID(nextg.x, nextg.y) - z3; // right-back (delta)
  282. }
  283. // Bilinear interpolate. Needed since rel.y or thisg.x has changed.
  284. L = z1 + d2 * ratio.y; // Linear interp. LF -> LB
  285. const float R = z3 + d4 * ratio.y; // Linear interp. RF -> RB
  286. D = R - L;
  287. }
  288. const float offset = L + ratio.x * D; // the offset almost always changes
  289. /*
  290. static float last_offset = 0;
  291. if (ABS(last_offset - offset) > 0.2) {
  292. SERIAL_ECHOLNPGM("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x);
  293. SERIAL_ECHOLNPGM(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y);
  294. SERIAL_ECHOLNPGM(" ratio.x=", ratio.x, " ratio.y=", ratio.y);
  295. SERIAL_ECHOLNPGM(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4);
  296. SERIAL_ECHOLNPGM(" L=", L, " R=", R, " offset=", offset);
  297. }
  298. last_offset = offset;
  299. //*/
  300. return offset;
  301. }
  302. #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES)
  303. #define CELL_INDEX(A,V) ((V - bilinear_start.A) * ABL_BG_FACTOR(A))
  304. /**
  305. * Prepare a bilinear-leveled linear move on Cartesian,
  306. * splitting the move where it crosses grid borders.
  307. */
  308. void bilinear_line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
  309. // Get current and destination cells for this line
  310. xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) },
  311. c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) };
  312. LIMIT(c1.x, 0, ABL_BG_POINTS_X - 2);
  313. LIMIT(c1.y, 0, ABL_BG_POINTS_Y - 2);
  314. LIMIT(c2.x, 0, ABL_BG_POINTS_X - 2);
  315. LIMIT(c2.y, 0, ABL_BG_POINTS_Y - 2);
  316. // Start and end in the same cell? No split needed.
  317. if (c1 == c2) {
  318. current_position = destination;
  319. line_to_current_position(scaled_fr_mm_s);
  320. return;
  321. }
  322. #define LINE_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist)
  323. float normalized_dist;
  324. xyze_pos_t end;
  325. const xy_int8_t gc { _MAX(c1.x, c2.x), _MAX(c1.y, c2.y) };
  326. // Crosses on the X and not already split on this X?
  327. // The x_splits flags are insurance against rounding errors.
  328. if (c2.x != c1.x && TEST(x_splits, gc.x)) {
  329. // Split on the X grid line
  330. CBI(x_splits, gc.x);
  331. end = destination;
  332. destination.x = bilinear_start.x + ABL_BG_SPACING(x) * gc.x;
  333. normalized_dist = (destination.x - current_position.x) / (end.x - current_position.x);
  334. destination.y = LINE_SEGMENT_END(y);
  335. }
  336. // Crosses on the Y and not already split on this Y?
  337. else if (c2.y != c1.y && TEST(y_splits, gc.y)) {
  338. // Split on the Y grid line
  339. CBI(y_splits, gc.y);
  340. end = destination;
  341. destination.y = bilinear_start.y + ABL_BG_SPACING(y) * gc.y;
  342. normalized_dist = (destination.y - current_position.y) / (end.y - current_position.y);
  343. destination.x = LINE_SEGMENT_END(x);
  344. }
  345. else {
  346. // Must already have been split on these border(s)
  347. // This should be a rare case.
  348. current_position = destination;
  349. line_to_current_position(scaled_fr_mm_s);
  350. return;
  351. }
  352. destination.z = LINE_SEGMENT_END(z);
  353. destination.e = LINE_SEGMENT_END(e);
  354. // Do the split and look for more borders
  355. bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
  356. // Restore destination from stack
  357. destination = end;
  358. bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
  359. }
  360. #endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES
  361. #endif // AUTO_BED_LEVELING_BILINEAR