mesh_bed_calibration.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. #include "Marlin.h"
  2. #include "Configuration.h"
  3. #include "language_all.h"
  4. #include "mesh_bed_calibration.h"
  5. #include "mesh_bed_leveling.h"
  6. #include "stepper.h"
  7. #include "ultralcd.h"
  8. // #include "qr_solve.h"
  9. extern float home_retract_mm_ext(int axis);
  10. static inline void go_xyz(float x, float y, float z, float fr)
  11. {
  12. plan_buffer_line(x, y, z, current_position[E_AXIS], fr, active_extruder);
  13. st_synchronize();
  14. }
  15. static inline void go_xy(float x, float y, float fr)
  16. {
  17. plan_buffer_line(x, y, current_position[Z_AXIS], current_position[E_AXIS], fr, active_extruder);
  18. st_synchronize();
  19. }
  20. static inline void go_to_current(float fr)
  21. {
  22. plan_buffer_line(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS], fr, active_extruder);
  23. st_synchronize();
  24. }
  25. static inline void update_current_position_xyz()
  26. {
  27. current_position[X_AXIS] = st_get_position_mm(X_AXIS);
  28. current_position[Y_AXIS] = st_get_position_mm(Y_AXIS);
  29. current_position[Z_AXIS] = st_get_position_mm(Z_AXIS);
  30. plan_set_position(current_position[X_AXIS], current_position[Y_AXIS], current_position[Z_AXIS], current_position[E_AXIS]);
  31. }
  32. // At the current position, find the Z stop.
  33. inline void find_bed_induction_sensor_point_z()
  34. {
  35. bool endstops_enabled = enable_endstops(true);
  36. bool endstop_z_enabled = enable_z_endstop(false);
  37. // move down until you find the bed
  38. current_position[Z_AXIS] = -10;
  39. go_to_current(homing_feedrate[Z_AXIS]/60);
  40. // we have to let the planner know where we are right now as it is not where we said to go.
  41. update_current_position_xyz();
  42. // move up the retract distance
  43. current_position[Z_AXIS] += home_retract_mm_ext(Z_AXIS);
  44. go_to_current(homing_feedrate[Z_AXIS]/60);
  45. // move back down slowly to find bed
  46. current_position[Z_AXIS] -= home_retract_mm_ext(Z_AXIS) * 2;
  47. go_to_current(homing_feedrate[Z_AXIS]/(4*60));
  48. // we have to let the planner know where we are right now as it is not where we said to go.
  49. update_current_position_xyz();
  50. enable_endstops(endstops_enabled);
  51. enable_z_endstop(endstop_z_enabled);
  52. }
  53. // Search around the current_position[X,Y],
  54. // look for the induction sensor response.
  55. // Adjust the current_position[X,Y,Z] to the center of the target dot and its response Z coordinate.
  56. #define FIND_BED_INDUCTION_SENSOR_POINT_X_RADIUS (8.f)
  57. #define FIND_BED_INDUCTION_SENSOR_POINT_Y_RADIUS (6.f)
  58. #define FIND_BED_INDUCTION_SENSOR_POINT_XY_STEP (1.f)
  59. #define FIND_BED_INDUCTION_SENSOR_POINT_Z_STEP (0.5f)
  60. inline bool find_bed_induction_sensor_point_xy()
  61. {
  62. float feedrate = homing_feedrate[X_AXIS] / 60.f;
  63. bool found = false;
  64. {
  65. float x0 = current_position[X_AXIS] - FIND_BED_INDUCTION_SENSOR_POINT_X_RADIUS;
  66. float x1 = current_position[X_AXIS] + FIND_BED_INDUCTION_SENSOR_POINT_X_RADIUS;
  67. float y0 = current_position[Y_AXIS] - FIND_BED_INDUCTION_SENSOR_POINT_Y_RADIUS;
  68. float y1 = current_position[Y_AXIS] + FIND_BED_INDUCTION_SENSOR_POINT_Y_RADIUS;
  69. uint8_t nsteps_y;
  70. uint8_t i;
  71. if (x0 < X_MIN_POS)
  72. x0 = X_MIN_POS;
  73. if (x1 > X_MAX_POS)
  74. x1 = X_MAX_POS;
  75. if (y0 < Y_MIN_POS)
  76. y0 = Y_MIN_POS;
  77. if (y1 > Y_MAX_POS)
  78. y1 = Y_MAX_POS;
  79. nsteps_y = int(ceil((y1 - y0) / FIND_BED_INDUCTION_SENSOR_POINT_XY_STEP));
  80. enable_endstops(false);
  81. bool dir_positive = true;
  82. // go_xyz(current_position[X_AXIS], current_position[Y_AXIS], MESH_HOME_Z_SEARCH, homing_feedrate[Z_AXIS]/60);
  83. go_xyz(x0, y0, current_position[Z_AXIS], feedrate);
  84. // Continously lower the Z axis.
  85. endstops_hit_on_purpose();
  86. enable_z_endstop(true);
  87. while (current_position[Z_AXIS] > -10.f) {
  88. // Do nsteps_y zig-zag movements.
  89. current_position[Y_AXIS] = y0;
  90. for (i = 0; i < nsteps_y; current_position[Y_AXIS] += (y1 - y0) / float(nsteps_y - 1), ++ i) {
  91. // Run with a slightly decreasing Z axis, zig-zag movement. Stop at the Z end-stop.
  92. current_position[Z_AXIS] -= FIND_BED_INDUCTION_SENSOR_POINT_Z_STEP / float(nsteps_y);
  93. go_xyz(dir_positive ? x1 : x0, current_position[Y_AXIS], current_position[Z_AXIS], feedrate);
  94. dir_positive = ! dir_positive;
  95. if (endstop_z_hit_on_purpose())
  96. goto endloop;
  97. }
  98. for (i = 0; i < nsteps_y; current_position[Y_AXIS] -= (y1 - y0) / float(nsteps_y - 1), ++ i) {
  99. // Run with a slightly decreasing Z axis, zig-zag movement. Stop at the Z end-stop.
  100. current_position[Z_AXIS] -= FIND_BED_INDUCTION_SENSOR_POINT_Z_STEP / float(nsteps_y);
  101. go_xyz(dir_positive ? x1 : x0, current_position[Y_AXIS], current_position[Z_AXIS], feedrate);
  102. dir_positive = ! dir_positive;
  103. if (endstop_z_hit_on_purpose())
  104. goto endloop;
  105. }
  106. }
  107. endloop:
  108. // SERIAL_ECHOLN("First hit");
  109. // we have to let the planner know where we are right now as it is not where we said to go.
  110. update_current_position_xyz();
  111. // Search in this plane for the first hit. Zig-zag first in X, then in Y axis.
  112. for (int8_t iter = 0; iter < 3; ++ iter) {
  113. if (iter > 0) {
  114. // Slightly lower the Z axis to get a reliable trigger.
  115. current_position[Z_AXIS] -= 0.02f;
  116. go_xyz(current_position[X_AXIS], current_position[Y_AXIS], MESH_HOME_Z_SEARCH, homing_feedrate[Z_AXIS]/60);
  117. }
  118. // Do nsteps_y zig-zag movements.
  119. float a, b;
  120. enable_endstops(false);
  121. enable_z_endstop(false);
  122. current_position[Y_AXIS] = y0;
  123. go_xy(x0, current_position[Y_AXIS], feedrate);
  124. enable_z_endstop(true);
  125. found = false;
  126. for (i = 0, dir_positive = true; i < nsteps_y; current_position[Y_AXIS] += (y1 - y0) / float(nsteps_y - 1), ++ i, dir_positive = ! dir_positive) {
  127. go_xy(dir_positive ? x1 : x0, current_position[Y_AXIS], feedrate);
  128. if (endstop_z_hit_on_purpose()) {
  129. found = true;
  130. break;
  131. }
  132. }
  133. update_current_position_xyz();
  134. if (! found) {
  135. // SERIAL_ECHOLN("Search in Y - not found");
  136. continue;
  137. }
  138. // SERIAL_ECHOLN("Search in Y - found");
  139. a = current_position[Y_AXIS];
  140. enable_z_endstop(false);
  141. current_position[Y_AXIS] = y1;
  142. go_xy(x0, current_position[Y_AXIS], feedrate);
  143. enable_z_endstop(true);
  144. found = false;
  145. for (i = 0, dir_positive = true; i < nsteps_y; current_position[Y_AXIS] -= (y1 - y0) / float(nsteps_y - 1), ++ i, dir_positive = ! dir_positive) {
  146. go_xy(dir_positive ? x1 : x0, current_position[Y_AXIS], feedrate);
  147. if (endstop_z_hit_on_purpose()) {
  148. found = true;
  149. break;
  150. }
  151. }
  152. update_current_position_xyz();
  153. if (! found) {
  154. // SERIAL_ECHOLN("Search in Y2 - not found");
  155. continue;
  156. }
  157. // SERIAL_ECHOLN("Search in Y2 - found");
  158. b = current_position[Y_AXIS];
  159. current_position[Y_AXIS] = 0.5f * (a + b);
  160. // Search in the X direction along a cross.
  161. found = false;
  162. enable_z_endstop(false);
  163. go_xy(x0, current_position[Y_AXIS], feedrate);
  164. enable_z_endstop(true);
  165. go_xy(x1, current_position[Y_AXIS], feedrate);
  166. update_current_position_xyz();
  167. if (! endstop_z_hit_on_purpose()) {
  168. // SERIAL_ECHOLN("Search X span 0 - not found");
  169. continue;
  170. }
  171. // SERIAL_ECHOLN("Search X span 0 - found");
  172. a = current_position[X_AXIS];
  173. enable_z_endstop(false);
  174. go_xy(x1, current_position[Y_AXIS], feedrate);
  175. enable_z_endstop(true);
  176. go_xy(x0, current_position[Y_AXIS], feedrate);
  177. update_current_position_xyz();
  178. if (! endstop_z_hit_on_purpose()) {
  179. // SERIAL_ECHOLN("Search X span 1 - not found");
  180. continue;
  181. }
  182. // SERIAL_ECHOLN("Search X span 1 - found");
  183. b = current_position[X_AXIS];
  184. // Go to the center.
  185. enable_z_endstop(false);
  186. current_position[X_AXIS] = 0.5f * (a + b);
  187. go_xy(current_position[X_AXIS], current_position[Y_AXIS], feedrate);
  188. found = true;
  189. #if 1
  190. // Search in the Y direction along a cross.
  191. found = false;
  192. enable_z_endstop(false);
  193. go_xy(current_position[X_AXIS], y0, feedrate);
  194. enable_z_endstop(true);
  195. go_xy(current_position[X_AXIS], y1, feedrate);
  196. update_current_position_xyz();
  197. if (! endstop_z_hit_on_purpose()) {
  198. // SERIAL_ECHOLN("Search Y2 span 0 - not found");
  199. continue;
  200. }
  201. // SERIAL_ECHOLN("Search Y2 span 0 - found");
  202. a = current_position[Y_AXIS];
  203. enable_z_endstop(false);
  204. go_xy(current_position[X_AXIS], y1, feedrate);
  205. enable_z_endstop(true);
  206. go_xy(current_position[X_AXIS], y0, feedrate);
  207. update_current_position_xyz();
  208. if (! endstop_z_hit_on_purpose()) {
  209. // SERIAL_ECHOLN("Search Y2 span 1 - not found");
  210. continue;
  211. }
  212. // SERIAL_ECHOLN("Search Y2 span 1 - found");
  213. b = current_position[Y_AXIS];
  214. // Go to the center.
  215. enable_z_endstop(false);
  216. current_position[Y_AXIS] = 0.5f * (a + b);
  217. go_xy(current_position[X_AXIS], current_position[Y_AXIS], feedrate);
  218. found = true;
  219. #endif
  220. break;
  221. }
  222. }
  223. enable_z_endstop(false);
  224. return found;
  225. }
  226. // Search around the current_position[X,Y,Z].
  227. // It is expected, that the induction sensor is switched on at the current position.
  228. // Look around this center point by painting a star around the point.
  229. inline bool improve_bed_induction_sensor_point()
  230. {
  231. static const float search_radius = 8.f;
  232. bool endstops_enabled = enable_endstops(false);
  233. bool endstop_z_enabled = enable_z_endstop(false);
  234. bool found = false;
  235. float feedrate = homing_feedrate[X_AXIS] / 60.f;
  236. float center_old_x = current_position[X_AXIS];
  237. float center_old_y = current_position[Y_AXIS];
  238. float center_x = 0.f;
  239. float center_y = 0.f;
  240. for (uint8_t iter = 0; iter < 4; ++ iter) {
  241. switch (iter) {
  242. case 0:
  243. destination[X_AXIS] = center_old_x - search_radius * 0.707;
  244. destination[Y_AXIS] = center_old_y - search_radius * 0.707;
  245. break;
  246. case 1:
  247. destination[X_AXIS] = center_old_x + search_radius * 0.707;
  248. destination[Y_AXIS] = center_old_y + search_radius * 0.707;
  249. break;
  250. case 2:
  251. destination[X_AXIS] = center_old_x + search_radius * 0.707;
  252. destination[Y_AXIS] = center_old_y - search_radius * 0.707;
  253. break;
  254. case 3:
  255. default:
  256. destination[X_AXIS] = center_old_x - search_radius * 0.707;
  257. destination[Y_AXIS] = center_old_y + search_radius * 0.707;
  258. break;
  259. }
  260. // Trim the vector from center_old_[x,y] to destination[x,y] by the bed dimensions.
  261. float vx = destination[X_AXIS] - center_old_x;
  262. float vy = destination[Y_AXIS] - center_old_y;
  263. float l = sqrt(vx*vx+vy*vy);
  264. float t;
  265. if (destination[X_AXIS] < X_MIN_POS) {
  266. // Exiting the bed at xmin.
  267. t = (center_x - X_MIN_POS) / l;
  268. destination[X_AXIS] = X_MIN_POS;
  269. destination[Y_AXIS] = center_old_y + t * vy;
  270. } else if (destination[X_AXIS] > X_MAX_POS) {
  271. // Exiting the bed at xmax.
  272. t = (X_MAX_POS - center_x) / l;
  273. destination[X_AXIS] = X_MAX_POS;
  274. destination[Y_AXIS] = center_old_y + t * vy;
  275. }
  276. if (destination[Y_AXIS] < Y_MIN_POS) {
  277. // Exiting the bed at ymin.
  278. t = (center_y - Y_MIN_POS) / l;
  279. destination[X_AXIS] = center_old_x + t * vx;
  280. destination[Y_AXIS] = Y_MIN_POS;
  281. } else if (destination[Y_AXIS] > Y_MAX_POS) {
  282. // Exiting the bed at xmax.
  283. t = (Y_MAX_POS - center_y) / l;
  284. destination[X_AXIS] = center_old_x + t * vx;
  285. destination[Y_AXIS] = Y_MAX_POS;
  286. }
  287. // Move away from the measurement point.
  288. enable_endstops(false);
  289. go_xy(destination[X_AXIS], destination[Y_AXIS], feedrate);
  290. // Move towards the measurement point, until the induction sensor triggers.
  291. enable_endstops(true);
  292. go_xy(center_old_x, center_old_y, feedrate);
  293. update_current_position_xyz();
  294. center_x += current_position[X_AXIS];
  295. center_y += current_position[Y_AXIS];
  296. }
  297. // Calculate the new center, move to the new center.
  298. center_x /= 4.f;
  299. center_y /= 4.f;
  300. current_position[X_AXIS] = center_x;
  301. current_position[Y_AXIS] = center_y;
  302. enable_endstops(false);
  303. go_xy(current_position[X_AXIS], current_position[Y_AXIS], feedrate);
  304. enable_endstops(endstops_enabled);
  305. enable_z_endstop(endstop_z_enabled);
  306. return found;
  307. }
  308. // Search around the current_position[X,Y,Z].
  309. // It is expected, that the induction sensor is switched on at the current position.
  310. // Look around this center point by painting a star around the point.
  311. #define IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS (8.f)
  312. inline bool improve_bed_induction_sensor_point2(bool lift_z_on_min_y)
  313. {
  314. float center_old_x = current_position[X_AXIS];
  315. float center_old_y = current_position[Y_AXIS];
  316. float a, b;
  317. enable_endstops(false);
  318. {
  319. float x0 = center_old_x - IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS;
  320. float x1 = center_old_x + IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS;
  321. if (x0 < X_MIN_POS)
  322. x0 = X_MIN_POS;
  323. if (x1 > X_MAX_POS)
  324. x1 = X_MAX_POS;
  325. // Search in the X direction along a cross.
  326. enable_z_endstop(false);
  327. go_xy(x0, current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
  328. enable_z_endstop(true);
  329. go_xy(x1, current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
  330. update_current_position_xyz();
  331. if (! endstop_z_hit_on_purpose())
  332. return false;
  333. a = current_position[X_AXIS];
  334. enable_z_endstop(false);
  335. go_xy(x1, current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
  336. enable_z_endstop(true);
  337. go_xy(x0, current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
  338. update_current_position_xyz();
  339. if (! endstop_z_hit_on_purpose())
  340. return false;
  341. b = current_position[X_AXIS];
  342. // Go to the center.
  343. enable_z_endstop(false);
  344. current_position[X_AXIS] = 0.5f * (a + b);
  345. go_xy(current_position[X_AXIS], current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
  346. }
  347. {
  348. float y0 = center_old_y - IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS;
  349. float y1 = center_old_y + IMPROVE_BED_INDUCTION_SENSOR_SEARCH_RADIUS;
  350. if (y0 < Y_MIN_POS)
  351. y0 = Y_MIN_POS;
  352. if (y1 > Y_MAX_POS)
  353. y1 = Y_MAX_POS;
  354. // Search in the Y direction along a cross.
  355. enable_z_endstop(false);
  356. go_xy(current_position[X_AXIS], y0, homing_feedrate[X_AXIS] / 60.f);
  357. if (lift_z_on_min_y) {
  358. // The first row of points are very close to the end stop.
  359. // Lift the sensor to disengage the trigger. This is necessary because of the sensor hysteresis.
  360. go_xyz(current_position[X_AXIS], y0, current_position[Z_AXIS]+5.f, homing_feedrate[Z_AXIS] / 60.f);
  361. // and go back.
  362. go_xyz(current_position[X_AXIS], y0, current_position[Z_AXIS], homing_feedrate[Z_AXIS] / 60.f);
  363. }
  364. if (lift_z_on_min_y && (READ(Z_MIN_PIN) ^ Z_MIN_ENDSTOP_INVERTING) == 1) {
  365. // Already triggering before we started the move.
  366. // Shift the trigger point slightly outwards.
  367. a = current_position[Y_AXIS] - 1.5f;
  368. } else {
  369. enable_z_endstop(true);
  370. go_xy(current_position[X_AXIS], y1, homing_feedrate[X_AXIS] / 60.f);
  371. update_current_position_xyz();
  372. if (! endstop_z_hit_on_purpose())
  373. return false;
  374. a = current_position[Y_AXIS];
  375. }
  376. enable_z_endstop(false);
  377. go_xy(current_position[X_AXIS], y1, homing_feedrate[X_AXIS] / 60.f);
  378. enable_z_endstop(true);
  379. go_xy(current_position[X_AXIS], y0, homing_feedrate[X_AXIS] / 60.f);
  380. update_current_position_xyz();
  381. if (! endstop_z_hit_on_purpose())
  382. return false;
  383. b = current_position[Y_AXIS];
  384. // Go to the center.
  385. enable_z_endstop(false);
  386. current_position[Y_AXIS] = 0.5f * (a + b);
  387. go_xy(current_position[X_AXIS], current_position[Y_AXIS], homing_feedrate[X_AXIS] / 60.f);
  388. }
  389. return true;
  390. }
  391. #define MESH_BED_CALIBRATION_SHOW_LCD
  392. bool find_bed_offset_and_skew()
  393. {
  394. // Reusing the z_values memory for the measurement cache.
  395. // 7x7=49 floats, good for 16 (x,y,z) vectors.
  396. float *pts = &mbl.z_values[0][0];
  397. float *vec_x = pts + 3 * 4;
  398. float *vec_y = vec_x + 3;
  399. float *cntr = vec_y + 3;
  400. memset(pts, 0, sizeof(float) * 7 * 7);
  401. #ifdef MESH_BED_CALIBRATION_SHOW_LCD
  402. lcd_implementation_clear();
  403. lcd_print_at_PGM(0, 0, MSG_FIND_BED_OFFSET_AND_SKEW_LINE1);
  404. #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
  405. // Collect the rear 2x3 points.
  406. current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
  407. for (int k = 0; k < 4; ++ k) {
  408. #ifdef MESH_BED_CALIBRATION_SHOW_LCD
  409. lcd_print_at_PGM(0, 1, MSG_FIND_BED_OFFSET_AND_SKEW_LINE2);
  410. lcd_implementation_print(k+1);
  411. lcd_print_at_PGM(0, 2, MSG_FIND_BED_OFFSET_AND_SKEW_LINE3);
  412. #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
  413. int i, j;
  414. switch (k) {
  415. case 0: i = 1; j = 0; break;
  416. case 1: i = 2; j = 1; break;
  417. case 2: i = 1; j = 2; break;
  418. case 3: i = 0; j = 1; break;
  419. }
  420. float *pt = pts + k * 3;
  421. // Go up to z_initial.
  422. go_to_current(homing_feedrate[Z_AXIS] / 60.f);
  423. // Go to the measurement point position.
  424. mbl.get_meas_xy(i, j, current_position[X_AXIS], current_position[Y_AXIS], true); // use default, uncorrected coordinates
  425. go_to_current(homing_feedrate[X_AXIS] / 60.f);
  426. if (! find_bed_induction_sensor_point_xy())
  427. return false;
  428. find_bed_induction_sensor_point_z();
  429. pt[0] = current_position[X_AXIS];
  430. pt[1] = current_position[Y_AXIS];
  431. pt[2] = current_position[Z_AXIS];
  432. // Start searching for the other points at 3mm above the last point.
  433. current_position[Z_AXIS] += 3.f;
  434. cntr[0] += pt[0];
  435. cntr[1] += pt[1];
  436. cntr[2] += pt[2];
  437. }
  438. // Average the X and Y vectors. They may not be perpendicular, if the printer is built incorrectly.
  439. {
  440. float len;
  441. // Average the center point.
  442. cntr[0] *= 1.f/4.f;
  443. cntr[1] *= 1.f/4.f;
  444. cntr[2] *= 1.f/4.f;
  445. // Average the X vector.
  446. vec_x[0] = (pts[3 * 1 + 0] - pts[3 * 3 + 0]) / 2.f;
  447. vec_x[1] = (pts[3 * 1 + 1] - pts[3 * 3 + 1]) / 2.f;
  448. len = sqrt(vec_x[0]*vec_x[0] + vec_x[1]*vec_x[1]);
  449. if (0) {
  450. // if (len < MEAS_NUM_X_DIST) {
  451. // Scale the vector up to MEAS_NUM_X_DIST lenght.
  452. float factor = MEAS_NUM_X_DIST / len;
  453. vec_x[0] *= factor;
  454. vec_x[0] *= factor;
  455. } else {
  456. // The vector is longer than MEAS_NUM_X_DIST. The X/Y axes are skewed.
  457. // Verify the maximum skew?
  458. }
  459. // Average the Y vector.
  460. vec_y[0] = (pts[3 * 2 + 0] - pts[3 * 0 + 0]) / 2.f;
  461. vec_y[1] = (pts[3 * 2 + 1] - pts[3 * 0 + 1]) / 2.f;
  462. len = sqrt(vec_y[0]*vec_y[0] + vec_y[1]*vec_y[1]);
  463. if (0) {
  464. // if (len < MEAS_NUM_Y_DIST) {
  465. // Scale the vector up to MEAS_NUM_X_DIST lenght.
  466. float factor = MEAS_NUM_Y_DIST / len;
  467. vec_y[1] *= factor;
  468. vec_y[1] *= factor;
  469. } else {
  470. // The vector is longer than MEAS_NUM_X_DIST. The X/Y axes are skewed.
  471. // Verify the maximum skew?
  472. }
  473. // Fearlessly store the calibration values into the eeprom.
  474. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+0), cntr [0]);
  475. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+4), cntr [1]);
  476. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +0), vec_x[0]);
  477. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +4), vec_x[1]);
  478. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +0), vec_y[0]);
  479. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +4), vec_y[1]);
  480. #if 0
  481. SERIAL_ECHOLN("Calibration done.");
  482. SERIAL_ECHO("Center: ");
  483. SERIAL_ECHO(cntr[0]);
  484. SERIAL_ECHO(",");
  485. SERIAL_ECHO(cntr[1]);
  486. SERIAL_ECHO(", x: ");
  487. SERIAL_ECHO(vec_x[0]);
  488. SERIAL_ECHO(",");
  489. SERIAL_ECHO(vec_x[1]);
  490. SERIAL_ECHO(", y: ");
  491. SERIAL_ECHO(vec_y[0]);
  492. SERIAL_ECHO(",");
  493. SERIAL_ECHO(vec_y[1]);
  494. SERIAL_ECHOLN("");
  495. #endif
  496. }
  497. return true;
  498. }
  499. bool improve_bed_offset_and_skew(int8_t method)
  500. {
  501. // Reusing the z_values memory for the measurement cache.
  502. // 7x7=49 floats, good for 16 (x,y,z) vectors.
  503. float *pts = &mbl.z_values[0][0];
  504. float *vec_x = pts + 2 * 9;
  505. float *vec_y = vec_x + 2;
  506. float *cntr = vec_y + 2;
  507. memset(pts, 0, sizeof(float) * 7 * 7);
  508. bool endstops_enabled = enable_endstops(false);
  509. bool endstop_z_enabled = enable_z_endstop(false);
  510. #ifdef MESH_BED_CALIBRATION_SHOW_LCD
  511. lcd_implementation_clear();
  512. lcd_print_at_PGM(0, 0, MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE1);
  513. #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
  514. // Collect a matrix of 9x9 points.
  515. for (int8_t mesh_point = 0; mesh_point < 9; ++ mesh_point) {
  516. int ix = mesh_point % MESH_MEAS_NUM_X_POINTS;
  517. int iy = mesh_point / MESH_MEAS_NUM_X_POINTS;
  518. if (iy & 1) ix = (MESH_MEAS_NUM_X_POINTS - 1) - ix; // Zig zag
  519. // Print the decrasing ID of the measurement point.
  520. #ifdef MESH_BED_CALIBRATION_SHOW_LCD
  521. lcd_print_at_PGM(0, 1, MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE2);
  522. lcd_implementation_print_at(7, 1, mesh_point+1);
  523. lcd_print_at_PGM(0, 2, MSG_IMPROVE_BED_OFFSET_AND_SKEW_LINE3);
  524. #endif /* MESH_BED_CALIBRATION_SHOW_LCD */
  525. // Move up.
  526. current_position[Z_AXIS] = MESH_HOME_Z_SEARCH;
  527. enable_endstops(false);
  528. enable_z_endstop(false);
  529. go_to_current(homing_feedrate[Z_AXIS]/60);
  530. // Go to the measurement point.
  531. // Use the coorrected coordinate, which is a result of find_bed_offset_and_skew().
  532. mbl.get_meas_xy(ix, iy, current_position[X_AXIS], current_position[Y_AXIS], false);
  533. go_to_current(homing_feedrate[X_AXIS]/60);
  534. // Find its Z position by running the normal vertical search.
  535. // delay_keep_alive(3000);
  536. find_bed_induction_sensor_point_z();
  537. // delay_keep_alive(3000);
  538. // Improve the point position by searching its center in a current plane.
  539. int8_t n_errors = 3;
  540. for (int8_t iter = 0; iter < 4; ++ iter) {
  541. bool found = false;
  542. switch (method) {
  543. case 0: found = improve_bed_induction_sensor_point(); break;
  544. case 1: found = improve_bed_induction_sensor_point2(iy == 0); break;
  545. default: break;
  546. }
  547. if (! found) {
  548. if (n_errors -- == 0) {
  549. // Give up.
  550. goto canceled;
  551. } else {
  552. // Try to move the Z axis down a bit to increase a chance of the sensor to trigger.
  553. current_position[Z_AXIS] -= 0.025f;
  554. enable_endstops(false);
  555. enable_z_endstop(false);
  556. go_to_current(homing_feedrate[Z_AXIS]);
  557. }
  558. }
  559. }
  560. // delay_keep_alive(3000);
  561. float *pt = pts + 2 * (ix + iy * 3);
  562. pt[0] = current_position[X_AXIS];
  563. pt[1] = current_position[Y_AXIS];
  564. cntr[0] += pt[0];
  565. cntr[1] += pt[1];
  566. }
  567. // Average the X and Y vectors. They may not be perpendicular, if the printer is built incorrectly.
  568. // Average the center point.
  569. cntr[0] *= 1.f/9.f;
  570. cntr[1] *= 1.f/9.f;
  571. // Average the X vector.
  572. vec_x[0] = (pts[2 * 2 + 0] - pts[2 * 0 + 0] + pts[2 * 5 + 0] - pts[2 * 3 + 0] + pts[2 * 8 + 0] - pts[2 * 6 + 0]) / 6.f;
  573. vec_x[1] = (pts[2 * 2 + 1] - pts[2 * 0 + 1] + pts[2 * 5 + 1] - pts[2 * 3 + 1] + pts[2 * 8 + 1] - pts[2 * 6 + 1]) / 6.f;
  574. // Average the Y vector.
  575. vec_y[0] = (pts[2 * 6 + 0] - pts[2 * 0 + 0] + pts[2 * 7 + 0] - pts[2 * 1 + 0] + pts[2 * 8 + 0] - pts[2 * 2 + 0]) / 6.f;
  576. vec_y[1] = (pts[2 * 6 + 1] - pts[2 * 0 + 1] + pts[2 * 7 + 1] - pts[2 * 1 + 1] + pts[2 * 8 + 1] - pts[2 * 2 + 1]) / 6.f;
  577. #if 1
  578. // Fearlessly store the calibration values into the eeprom.
  579. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+0), cntr [0]);
  580. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_CENTER+4), cntr [1]);
  581. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +0), vec_x[0]);
  582. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_X +4), vec_x[1]);
  583. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +0), vec_y[0]);
  584. eeprom_update_float((float*)(EEPROM_BED_CALIBRATION_VEC_Y +4), vec_y[1]);
  585. #endif
  586. #if 0
  587. // and let us know the result.
  588. SERIAL_ECHOLN("Calibration done.");
  589. SERIAL_ECHO("Center: ");
  590. SERIAL_ECHO(cntr[0]);
  591. SERIAL_ECHO(",");
  592. SERIAL_ECHO(cntr[1]);
  593. SERIAL_ECHO(", x: ");
  594. SERIAL_ECHO(vec_x[0]);
  595. SERIAL_ECHO(",");
  596. SERIAL_ECHO(vec_x[1]);
  597. SERIAL_ECHO(", y: ");
  598. SERIAL_ECHO(vec_y[0]);
  599. SERIAL_ECHO(",");
  600. SERIAL_ECHO(vec_y[1]);
  601. SERIAL_ECHOLN("");
  602. #endif
  603. enable_endstops(endstops_enabled);
  604. enable_z_endstop(endstop_z_enabled);
  605. return true;
  606. canceled:
  607. enable_endstops(endstops_enabled);
  608. enable_z_endstop(endstop_z_enabled);
  609. return false;
  610. }
  611. void reset_bed_offset_and_skew()
  612. {
  613. eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_CENTER+0), 0x0FFFFFFFF);
  614. eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_CENTER+4), 0x0FFFFFFFF);
  615. eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_VEC_X +0), 0x0FFFFFFFF);
  616. eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_VEC_X +4), 0x0FFFFFFFF);
  617. eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_VEC_Y +0), 0x0FFFFFFFF);
  618. eeprom_update_dword((uint32_t*)(EEPROM_BED_CALIBRATION_VEC_Y +4), 0x0FFFFFFFF);
  619. }
  620. static const float[9][2] PROGMEM bed_points = {
  621. };
  622. bool calculate_machine_skew_and_offset_LS(
  623. // Matrix of 9 2D points (18 floats)
  624. float *pts,
  625. // Resulting correction matrix.
  626. float *vec_x,
  627. float *vec_y,
  628. float *cntr,
  629. // Temporary values, 49-18-(2*3)=25 floats
  630. float *temp
  631. {
  632. {
  633. // Create covariance matrix for A, collect the right hand side b.
  634. float A[3][3] = { 0.f };
  635. float b[3] = { 0.f };
  636. float acc;
  637. for (uint8_t r = 0; r < 3; ++ r) {
  638. for (uint8_t c = 0; c < 3; ++ c) {
  639. acc = 0;
  640. for (uint8_t i = 0; i < 9; ++ i) {
  641. float a = (r == 2) ? 1.f : pts[2 * i + r];
  642. float b = (c == 2) ? 1.f : pts[2 * i + c];
  643. acc += a * b;
  644. }
  645. A[r][c] = acc;
  646. }
  647. acc = 0.f;
  648. for (uint8_t i = 0; i < 9; ++ i) {
  649. float a = (r == 2) ? 1.f : pts[2 * i + r];
  650. float b = pgm_read_float(&coeff2[i][0]);
  651. acc += a * b;
  652. }
  653. b[r] = acc;
  654. }
  655. // Solve the linear equation for ax, bx, cx.
  656. float x[3] = { 0.f };
  657. for (uint8_t iter = 0; iter < 100; ++ iter) {
  658. x[0] = (b[0] - A[1] * x[1] - A[2] * x[2]) / A[0];
  659. x[1] = (b[1] - A[0] * x[0] - A[2] * x[2]) / A[1];
  660. x[2] = (b[2] - A[0] * x[0] - A[1] * x[1]) / A[2];
  661. }
  662. // Store the result to the output variables.
  663. vec_x[0] = x[0];
  664. vec_y[0] = x[1];
  665. cntr[0] = x[2];
  666. // Recalculate b for the y values.
  667. for (uint8_t r = 0; r < 3; ++ r) {
  668. acc = 0.f;
  669. for (uint8_t i = 0; i < 9; ++ i) {
  670. float a = (r == 2) ? 1.f : pts[2 * i + r];
  671. float b = pgm_read_float(&coeff2[i][1]);
  672. acc += a * b;
  673. }
  674. b[r] = acc;
  675. }
  676. // Solve the linear equation for ay, by, cy.
  677. x[0] = 0.f, x[1] = 0.f; x[2] = 0.f;
  678. for (uint8_t iter = 0; iter < 100; ++ iter) {
  679. x[0] = (b[0] - A[1] * x[1] - A[2] * x[2]) / A[0];
  680. x[1] = (b[1] - A[0] * x[0] - A[2] * x[2]) / A[1];
  681. x[2] = (b[2] - A[0] * x[0] - A[1] * x[1]) / A[2];
  682. }
  683. // Store the result to the output variables.
  684. vec_x[1] = x[0];
  685. vec_y[1] = x[1];
  686. cntr[1] = x[2];
  687. }
  688. // Normalize the vectors. We expect, that the machine axes may be skewed a bit, but the distances are correct.
  689. // l shall be very close to 1 already.
  690. float l = sqrt(vec_x[0]*vec_x[0] + vec_x[1] * vec_x[1]);
  691. vec_x[0] /= l;
  692. vec_x[1] /= l;
  693. l = sqrt(vec_y[0]*vec_y[0] + vec_y[1] * vec_y[1]);
  694. vec_y[0] /= l;
  695. vec_y[1] /= l;
  696. // Invert the transformation matrix made of vec_x, vec_y and cntr.
  697. }