From 829176839c778647607e16626100dd15d510514b Mon Sep 17 00:00:00 2001 From: kadir ilkimen Date: Wed, 11 Jan 2023 06:29:38 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Polar=20Kinematics=20(#25214,=20#25?= =?UTF-8?q?303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Dan Royer <1464454+i-make-robots@users.noreply.github.com> --- Marlin/Configuration.h | 70 ++++++++++-- Marlin/src/MarlinCore.cpp | 2 + Marlin/src/core/language.h | 1 + .../src/feature/bedlevel/ubl/ubl_motion.cpp | 32 +++--- Marlin/src/gcode/calibrate/G33.cpp | 4 +- Marlin/src/gcode/calibrate/M48.cpp | 4 +- Marlin/src/gcode/calibrate/M665.cpp | 19 ++++ Marlin/src/gcode/gcode.h | 2 +- Marlin/src/gcode/host/M114.cpp | 2 +- Marlin/src/gcode/host/M360.cpp | 1 + Marlin/src/gcode/motion/G0_G1.cpp | 2 +- Marlin/src/gcode/motion/G2_G3.cpp | 2 +- Marlin/src/inc/Conditionals_LCD.h | 2 +- Marlin/src/inc/Conditionals_post.h | 14 +-- Marlin/src/inc/SanityCheck.h | 23 +++- Marlin/src/lcd/e3v2/proui/dwinui.cpp | 4 +- Marlin/src/lcd/extui/ui_api.cpp | 2 +- Marlin/src/lcd/language/language_en.h | 2 + Marlin/src/lcd/menu/menu_delta_calibrate.cpp | 2 +- Marlin/src/lcd/menu/menu_motion.cpp | 9 +- Marlin/src/lcd/tft/ui_1024x600.cpp | 2 +- Marlin/src/lcd/tft/ui_320x240.cpp | 2 +- Marlin/src/lcd/tft/ui_480x320.cpp | 2 +- Marlin/src/module/delta.cpp | 2 +- Marlin/src/module/motion.cpp | 40 +++++-- Marlin/src/module/motion.h | 2 + Marlin/src/module/planner.cpp | 61 ++++++++++- Marlin/src/module/planner.h | 10 +- Marlin/src/module/polar.cpp | 102 ++++++++++++++++++ Marlin/src/module/polar.h | 36 +++++++ Marlin/src/module/probe.h | 6 +- Marlin/src/module/stepper.cpp | 2 +- .../sublime/MarlinFirmware.sublime-project | 2 +- 33 files changed, 390 insertions(+), 78 deletions(-) create mode 100644 Marlin/src/module/polar.cpp create mode 100644 Marlin/src/module/polar.h diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index ce6a70a3aaa42..678db51fc0287 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -878,8 +878,9 @@ // Enable for Polargraph Kinematics //#define POLARGRAPH #if ENABLED(POLARGRAPH) - #define POLARGRAPH_MAX_BELT_LEN 1035.0 - #define DEFAULT_SEGMENTS_PER_SECOND 5 + #define POLARGRAPH_MAX_BELT_LEN 1035.0 // (mm) Belt length at full extension. Override with M665 H. + #define DEFAULT_SEGMENTS_PER_SECOND 5 // Move segmentation based on duration + #define PEN_UP_DOWN_MENU // Add "Pen Up" and "Pen Down" to the MarlinUI menu #endif // @section delta @@ -915,7 +916,7 @@ #endif // Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers). - #define DELTA_PRINTABLE_RADIUS 140.0 // (mm) + #define PRINTABLE_RADIUS 140.0 // (mm) // Maximum reachable area #define DELTA_MAX_RADIUS 140.0 // (mm) @@ -969,7 +970,7 @@ #if ENABLED(MORGAN_SCARA) //#define DEBUG_SCARA_KINEMATICS - #define SCARA_FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly + #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly // Radius around the center where the arm cannot reach #define MIDDLE_DEAD_ZONE_R 0 // (mm) @@ -1004,7 +1005,7 @@ #define TPARA_OFFSET_Y 0 // (mm) #define TPARA_OFFSET_Z 0 // (mm) - #define SCARA_FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly + #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly // Radius around the center where the arm cannot reach #define MIDDLE_DEAD_ZONE_R 0 // (mm) @@ -1014,6 +1015,59 @@ #define PSI_HOMING_OFFSET 0 #endif +// @section polar + +/** + * POLAR Kinematics + * developed by Kadir ilkimen for PolarBear CNC and babyBear + * https://github.com/kadirilkimen/Polar-Bear-Cnc-Machine + * https://github.com/kadirilkimen/babyBear-3D-printer + * + * A polar machine can have different configurations. + * This kinematics is only compatible with the following configuration: + * X : Independent linear + * Y or B : Polar + * Z : Independent linear + * + * For example, PolarBear has CoreXZ plus Polar Y or B. + * + * Motion problem for Polar axis near center / origin: + * + * 3D printing: + * Movements very close to the center of the polar axis take more time than others. + * This brief delay results in more material deposition due to the pressure in the nozzle. + * + * Current Kinematics and feedrate scaling deals with this by making the movement as fast + * as possible. It works for slow movements but doesn't work well with fast ones. A more + * complicated extrusion compensation must be implemented. + * + * Ideally, it should estimate that a long rotation near the center is ahead and will cause + * unwanted deposition. Therefore it can compensate the extrusion beforehand. + * + * Laser cutting: + * Same thing would be a problem for laser engraving too. As it spends time rotating at the + * center point, more likely it will burn more material than it should. Therefore similar + * compensation would be implemented for laser-cutting operations. + * + * Milling: + * This shouldn't be a problem for cutting/milling operations. + */ +//#define POLAR +#if ENABLED(POLAR) + #define DEFAULT_SEGMENTS_PER_SECOND 180 // If movement is choppy try lowering this value + #define PRINTABLE_RADIUS 82.0f // (mm) Maximum travel of X axis + + // Movements fall inside POLAR_FAST_RADIUS are assigned the highest possible feedrate + // to compensate unwanted deposition related to the near-origin motion problem. + #define POLAR_FAST_RADIUS 3.0f // (mm) + + // Radius which is unreachable by the tool. + // Needed if the tool is not perfectly aligned to the center of the polar axis. + #define POLAR_CENTER_OFFSET 0.0f // (mm) + + #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly +#endif + // @section machine // Articulated robot (arm). Joints are directly mapped to axes with no kinematics. @@ -1420,13 +1474,13 @@ // 2 or 3 sets of coordinates for deploying and retracting the spring loaded touch probe on G29, // if servo actuated touch probe is not defined. Uncomment as appropriate for your printer/probe. - #define Z_PROBE_ALLEN_KEY_DEPLOY_1 { 30.0, DELTA_PRINTABLE_RADIUS, 100.0 } + #define Z_PROBE_ALLEN_KEY_DEPLOY_1 { 30.0, PRINTABLE_RADIUS, 100.0 } #define Z_PROBE_ALLEN_KEY_DEPLOY_1_FEEDRATE XY_PROBE_FEEDRATE - #define Z_PROBE_ALLEN_KEY_DEPLOY_2 { 0.0, DELTA_PRINTABLE_RADIUS, 100.0 } + #define Z_PROBE_ALLEN_KEY_DEPLOY_2 { 0.0, PRINTABLE_RADIUS, 100.0 } #define Z_PROBE_ALLEN_KEY_DEPLOY_2_FEEDRATE (XY_PROBE_FEEDRATE)/10 - #define Z_PROBE_ALLEN_KEY_DEPLOY_3 { 0.0, (DELTA_PRINTABLE_RADIUS) * 0.75, 100.0 } + #define Z_PROBE_ALLEN_KEY_DEPLOY_3 { 0.0, (PRINTABLE_RADIUS) * 0.75, 100.0 } #define Z_PROBE_ALLEN_KEY_DEPLOY_3_FEEDRATE XY_PROBE_FEEDRATE #define Z_PROBE_ALLEN_KEY_STOW_1 { -64.0, 56.0, 23.0 } // Move the probe into position diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 676d230d35ea1..ed892ac8132a4 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -168,6 +168,8 @@ #include "module/polargraph.h" #elif IS_SCARA #include "module/scara.h" +#elif ENABLED(POLAR) + #include "module/polar.h" #endif #if HAS_LEVELING diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 545f9df6410bb..2d0b23a52998b 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -279,6 +279,7 @@ #define STR_S_SEG_PER_SEC "S" #define STR_DELTA_SETTINGS "Delta (L R H S XYZ ABC)" #define STR_SCARA_SETTINGS "SCARA" +#define STR_POLAR_SETTINGS "Polar" #define STR_POLARGRAPH_SETTINGS "Polargraph" #define STR_SCARA_P_T_Z "P T Z" #define STR_ENDSTOP_ADJUSTMENT "Endstop adjustment" diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp index 96c30a0efd895..1a2b6eb23abd7 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp @@ -334,16 +334,14 @@ #else // UBL_SEGMENTED #if IS_SCARA - #define DELTA_SEGMENT_MIN_LENGTH 0.25 // SCARA minimum segment size is 0.25mm - #elif ENABLED(DELTA) - #define DELTA_SEGMENT_MIN_LENGTH 0.10 // mm (still subject to DEFAULT_SEGMENTS_PER_SECOND) - #elif ENABLED(POLARGRAPH) - #define DELTA_SEGMENT_MIN_LENGTH 0.10 // mm (still subject to DEFAULT_SEGMENTS_PER_SECOND) + #define SEGMENT_MIN_LENGTH 0.25 // SCARA minimum segment size is 0.25mm + #elif IS_KINEMATIC + #define SEGMENT_MIN_LENGTH 0.10 // (mm) Still subject to DEFAULT_SEGMENTS_PER_SECOND #else // CARTESIAN #ifdef LEVELED_SEGMENT_LENGTH - #define DELTA_SEGMENT_MIN_LENGTH LEVELED_SEGMENT_LENGTH + #define SEGMENT_MIN_LENGTH LEVELED_SEGMENT_LENGTH #else - #define DELTA_SEGMENT_MIN_LENGTH 1.00 // mm (similar to G2/G3 arc segmentation) + #define SEGMENT_MIN_LENGTH 1.00 // (mm) Similar to G2/G3 arc segmentation #endif #endif @@ -361,23 +359,23 @@ const xyze_pos_t total = destination - current_position; const float cart_xy_mm_2 = HYPOT2(total.x, total.y), - cart_xy_mm = SQRT(cart_xy_mm_2); // Total XY distance + cart_xy_mm = SQRT(cart_xy_mm_2); // Total XY distance #if IS_KINEMATIC - const float seconds = cart_xy_mm / scaled_fr_mm_s; // Duration of XY move at requested rate - uint16_t segments = LROUND(segments_per_second * seconds), // Preferred number of segments for distance @ feedrate - seglimit = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length - NOMORE(segments, seglimit); // Limit to minimum segment length (fewer segments) + const float seconds = cart_xy_mm / scaled_fr_mm_s; // Duration of XY move at requested rate + uint16_t segments = LROUND(segments_per_second * seconds), // Preferred number of segments for distance @ feedrate + seglimit = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length + NOMORE(segments, seglimit); // Limit to minimum segment length (fewer segments) #else - uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(DELTA_SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length + uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length #endif - NOLESS(segments, 1U); // Must have at least one segment - const float inv_segments = 1.0f / segments; // Reciprocal to save calculation + NOLESS(segments, 1U); // Must have at least one segment + const float inv_segments = 1.0f / segments; // Reciprocal to save calculation // Add hints to help optimize the move - PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment - #if ENABLED(SCARA_FEEDRATE_SCALING) + PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment + #if ENABLED(FEEDRATE_SCALING) hints.inv_duration = scaled_fr_mm_s / hints.millimeters; #endif diff --git a/Marlin/src/gcode/calibrate/G33.cpp b/Marlin/src/gcode/calibrate/G33.cpp index 656c23cb78ff0..836d5c867b6b3 100644 --- a/Marlin/src/gcode/calibrate/G33.cpp +++ b/Marlin/src/gcode/calibrate/G33.cpp @@ -407,12 +407,12 @@ void GcodeSuite::G33() { towers_set = !parser.seen_test('T'); // The calibration radius is set to a calculated value - float dcr = probe_at_offset ? DELTA_PRINTABLE_RADIUS : DELTA_PRINTABLE_RADIUS - PROBING_MARGIN; + float dcr = probe_at_offset ? PRINTABLE_RADIUS : PRINTABLE_RADIUS - PROBING_MARGIN; #if HAS_PROBE_XY_OFFSET const float total_offset = HYPOT(probe.offset_xy.x, probe.offset_xy.y); dcr -= probe_at_offset ? _MAX(total_offset, PROBING_MARGIN) : total_offset; #endif - NOMORE(dcr, DELTA_PRINTABLE_RADIUS); + NOMORE(dcr, PRINTABLE_RADIUS); if (parser.seenval('R')) dcr -= _MAX(parser.value_float(), 0.0f); TERN_(HAS_DELTA_SENSORLESS_PROBING, dcr *= sensorless_radius_factor); diff --git a/Marlin/src/gcode/calibrate/M48.cpp b/Marlin/src/gcode/calibrate/M48.cpp index bfb3b640078b1..ff14175c71d7a 100644 --- a/Marlin/src/gcode/calibrate/M48.cpp +++ b/Marlin/src/gcode/calibrate/M48.cpp @@ -162,8 +162,8 @@ void GcodeSuite::M48() { float angle = random(0, 360); const float radius = random( #if ENABLED(DELTA) - int(0.1250000000 * (DELTA_PRINTABLE_RADIUS)), - int(0.3333333333 * (DELTA_PRINTABLE_RADIUS)) + int(0.1250000000 * (PRINTABLE_RADIUS)), + int(0.3333333333 * (PRINTABLE_RADIUS)) #else int(5), int(0.125 * _MIN(X_BED_SIZE, Y_BED_SIZE)) #endif diff --git a/Marlin/src/gcode/calibrate/M665.cpp b/Marlin/src/gcode/calibrate/M665.cpp index a8e02831e2a88..22ad80425ac76 100644 --- a/Marlin/src/gcode/calibrate/M665.cpp +++ b/Marlin/src/gcode/calibrate/M665.cpp @@ -181,6 +181,25 @@ ); } +#elif ENABLED(POLAR) + + #include "../../module/polar.h" + + /** + * M665: Set POLAR settings + * Parameters: + * S[segments] - Segments-per-second + */ + void GcodeSuite::M665() { + if (!parser.seen_any()) return M665_report(); + if (parser.seenval('S')) segments_per_second = parser.value_float(); + } + + void GcodeSuite::M665_report(const bool forReplay/*=true*/) { + report_heading_etc(forReplay, F(STR_POLAR_SETTINGS)); + SERIAL_ECHOLNPGM_P(PSTR(" M665 S"), segments_per_second); + } + #endif #endif // IS_KINEMATIC diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index b50d8363a4cf5..9d5621f0c3dd1 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -335,7 +335,7 @@ #include "../feature/encoder_i2c.h" #endif -#if IS_SCARA || defined(G0_FEEDRATE) +#if EITHER(IS_SCARA, POLAR) || defined(G0_FEEDRATE) #define HAS_FAST_MOVES 1 #endif diff --git a/Marlin/src/gcode/host/M114.cpp b/Marlin/src/gcode/host/M114.cpp index 60359eeecfaeb..e6e83ba6e5981 100644 --- a/Marlin/src/gcode/host/M114.cpp +++ b/Marlin/src/gcode/host/M114.cpp @@ -71,7 +71,7 @@ #if IS_KINEMATIC // Kinematics applied to the leveled position - SERIAL_ECHOPGM(TERN(IS_SCARA, "ScaraK: ", "DeltaK: ")); + SERIAL_ECHOPGM(TERN(POLAR, "Polar", TERN(IS_SCARA, "Scara", "Delta")) "K: " ); inverse_kinematics(leveled); // writes delta[] report_linear_axis_pos(delta); #endif diff --git a/Marlin/src/gcode/host/M360.cpp b/Marlin/src/gcode/host/M360.cpp index b3a95a35aaece..d8e166dad8e0c 100644 --- a/Marlin/src/gcode/host/M360.cpp +++ b/Marlin/src/gcode/host/M360.cpp @@ -161,6 +161,7 @@ void GcodeSuite::M360() { SERIAL_ECHOLNPGM( TERN_(DELTA, "Delta") TERN_(IS_SCARA, "SCARA") + TERN_(POLAR, "Polar") TERN_(IS_CORE, "Core") TERN_(MARKFORGED_XY, "MarkForgedXY") TERN_(MARKFORGED_YX, "MarkForgedYX") diff --git a/Marlin/src/gcode/motion/G0_G1.cpp b/Marlin/src/gcode/motion/G0_G1.cpp index cee2f050807e3..b10f1ac920d6a 100644 --- a/Marlin/src/gcode/motion/G0_G1.cpp +++ b/Marlin/src/gcode/motion/G0_G1.cpp @@ -106,7 +106,7 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) { #endif // FWRETRACT - #if IS_SCARA + #if EITHER(IS_SCARA, POLAR) fast_move ? prepare_fast_move_to_destination() : prepare_line_to_destination(); #else prepare_line_to_destination(); diff --git a/Marlin/src/gcode/motion/G2_G3.cpp b/Marlin/src/gcode/motion/G2_G3.cpp index 1a0a9c8cccf21..2634117cd9c0a 100644 --- a/Marlin/src/gcode/motion/G2_G3.cpp +++ b/Marlin/src/gcode/motion/G2_G3.cpp @@ -218,7 +218,7 @@ void plan_arc( // Add hints to help optimize the move PlannerHints hints; - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if ENABLED(FEEDRATE_SCALING) hints.inv_duration = (scaled_fr_mm_s / flat_mm) * segments; #endif diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h index 79568fb625640..88d7c91c6ef34 100644 --- a/Marlin/src/inc/Conditionals_LCD.h +++ b/Marlin/src/inc/Conditionals_LCD.h @@ -1407,7 +1407,7 @@ #if ANY(MORGAN_SCARA, MP_SCARA, AXEL_TPARA) #define IS_SCARA 1 #define IS_KINEMATIC 1 -#elif EITHER(DELTA, POLARGRAPH) +#elif ANY(DELTA, POLARGRAPH, POLAR) #define IS_KINEMATIC 1 #else #define IS_CARTESIAN 1 diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index b9bcb4d0d2867..8fe9a408f22dc 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -267,6 +267,7 @@ */ #if IS_KINEMATIC #undef LCD_BED_TRAMMING + #undef SLOWDOWN #endif /** @@ -274,12 +275,11 @@ * Printable radius assumes joints can fully extend */ #if IS_SCARA - #undef SLOWDOWN #if ENABLED(AXEL_TPARA) - #define SCARA_PRINTABLE_RADIUS (TPARA_LINKAGE_1 + TPARA_LINKAGE_2) + #define PRINTABLE_RADIUS (TPARA_LINKAGE_1 + TPARA_LINKAGE_2) #else #define QUICK_HOME - #define SCARA_PRINTABLE_RADIUS (SCARA_LINKAGE_1 + SCARA_LINKAGE_2) + #define PRINTABLE_RADIUS (SCARA_LINKAGE_1 + SCARA_LINKAGE_2) #endif #endif @@ -378,7 +378,6 @@ */ #if ENABLED(DELTA) #undef Z_SAFE_HOMING - #undef SLOWDOWN #endif #ifndef MESH_INSET @@ -3083,7 +3082,10 @@ /** * Only constrain Z on DELTA / SCARA machines */ -#if IS_KINEMATIC +#if ENABLED(POLAR) + #undef MIN_SOFTWARE_ENDSTOP_Y + #undef MAX_SOFTWARE_ENDSTOP_Y +#elif IS_KINEMATIC #undef MIN_SOFTWARE_ENDSTOP_X #undef MIN_SOFTWARE_ENDSTOP_Y #undef MAX_SOFTWARE_ENDSTOP_X @@ -3154,7 +3156,7 @@ #if EITHER(MESH_BED_LEVELING, AUTO_BED_LEVELING_UBL) #if IS_KINEMATIC // Probing points may be verified at compile time within the radius - // using static_assert(HYPOT2(X2-X1,Y2-Y1)<=sq(DELTA_PRINTABLE_RADIUS),"bad probe point!") + // using static_assert(HYPOT2(X2-X1,Y2-Y1)<=sq(PRINTABLE_RADIUS),"bad probe point!") // so that may be added to SanityCheck.h in the future. #define _MESH_MIN_X (X_MIN_BED + MESH_INSET) #define _MESH_MIN_Y (Y_MIN_BED + MESH_INSET) diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index a7a1d2c9ab5a6..2a209f3b8af71 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -658,6 +658,12 @@ #error "(POLAR|DELTA|SCARA|TPARA)_SEGMENTS_PER_SECOND is now DEFAULT_SEGMENTS_PER_SECOND." #elif ANY(DGUS_LCD_UI_ORIGIN, DGUS_LCD_UI_FYSETC, DGUS_LCD_UI_HIPRECY, DGUS_LCD_UI_MKS, DGUS_LCD_UI_RELOADED) && !defined(DGUS_LCD_UI) #error "DGUS_LCD_UI_[TYPE] is now set using DGUS_LCD_UI TYPE." +#elif defined(DELTA_PRINTABLE_RADIUS) + #error "DELTA_PRINTABLE_RADIUS is now PRINTABLE_RADIUS." +#elif defined(SCARA_PRINTABLE_RADIUS) + #error "SCARA_PRINTABLE_RADIUS is now PRINTABLE_RADIUS." +#elif defined(SCARA_FEEDRATE_SCALING) + #error "SCARA_FEEDRATE_SCALING is now FEEDRATE_SCALING." #endif // L64xx stepper drivers have been removed @@ -1371,6 +1377,13 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #endif #endif +/** + * POLAR warnings + */ +#if BOTH(POLAR, S_CURVE_ACCELERATION) + #warning "POLAR Kinematics may not work well with S_CURVE_ACCELERATION." +#endif + /** * Special tool-changing options */ @@ -1666,8 +1679,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS /** * Allow only one kinematic type to be defined */ -#if MANY(DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV) - #error "Please enable only one of DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, or FOAMCUTTER_XYUV." +#if MANY(DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV, POLAR) + #error "Please enable only one of DELTA, MORGAN_SCARA, MP_SCARA, AXEL_TPARA, COREXY, COREXZ, COREYZ, COREYX, COREZX, COREZY, MARKFORGED_XY, MARKFORGED_YX, ARTICULATED_ROBOT_ARM, FOAMCUTTER_XYUV, or POLAR." #endif /** @@ -1695,7 +1708,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS * Junction deviation is incompatible with kinematic systems. */ #if HAS_JUNCTION_DEVIATION && IS_KINEMATIC - #error "CLASSIC_JERK is required for DELTA and SCARA." + #error "CLASSIC_JERK is required for DELTA, SCARA, and POLAR." #endif /** @@ -1913,7 +1926,7 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS static_assert(PROBING_MARGIN_RIGHT >= 0, "PROBING_MARGIN_RIGHT must be >= 0."); #endif - #define _MARGIN(A) TERN(IS_SCARA, SCARA_PRINTABLE_RADIUS, TERN(DELTA, DELTA_PRINTABLE_RADIUS, ((A##_BED_SIZE) / 2))) + #define _MARGIN(A) TERN(IS_KINEMATIC, PRINTABLE_RADIUS, ((A##_BED_SIZE) / 2)) static_assert(PROBING_MARGIN < _MARGIN(X), "PROBING_MARGIN is too large."); static_assert(PROBING_MARGIN_BACK < _MARGIN(Y), "PROBING_MARGIN_BACK is too large."); static_assert(PROBING_MARGIN_FRONT < _MARGIN(Y), "PROBING_MARGIN_FRONT is too large."); @@ -2006,6 +2019,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #if IS_SCARA #error "AUTO_BED_LEVELING_UBL does not yet support SCARA printers." + #elif ENABLED(POLAR) + #error "AUTO_BED_LEVELING_UBL does not yet support POLAR printers." #elif DISABLED(EEPROM_SETTINGS) #error "AUTO_BED_LEVELING_UBL requires EEPROM_SETTINGS." #elif !WITHIN(GRID_MAX_POINTS_X, 3, 15) || !WITHIN(GRID_MAX_POINTS_Y, 3, 15) diff --git a/Marlin/src/lcd/e3v2/proui/dwinui.cpp b/Marlin/src/lcd/e3v2/proui/dwinui.cpp index afb18f24dbc90..f950d7451c450 100644 --- a/Marlin/src/lcd/e3v2/proui/dwinui.cpp +++ b/Marlin/src/lcd/e3v2/proui/dwinui.cpp @@ -281,7 +281,7 @@ void DWINUI::Draw_FillCircle(uint16_t bcolor, uint16_t x,uint16_t y,uint8_t r) { // color2 : End color uint16_t DWINUI::ColorInt(int16_t val, int16_t minv, int16_t maxv, uint16_t color1, uint16_t color2) { uint8_t B, G, R; - const float n = (float)(val - minv) / (maxv - minv); + const float n = float(val - minv) / (maxv - minv); R = (1 - n) * GetRColor(color1) + n * GetRColor(color2); G = (1 - n) * GetGColor(color1) + n * GetGColor(color2); B = (1 - n) * GetBColor(color1) + n * GetBColor(color2); @@ -296,7 +296,7 @@ uint16_t DWINUI::RainbowInt(int16_t val, int16_t minv, int16_t maxv) { uint8_t B, G, R; const uint8_t maxB = 28, maxR = 28, maxG = 38; const int16_t limv = _MAX(abs(minv), abs(maxv)); - float n = minv >= 0 ? (float)(val - minv) / (maxv - minv) : (float)val / limv; + float n = minv >= 0 ? float(val - minv) / (maxv - minv) : (float)val / limv; LIMIT(n, -1, 1); if (n < 0) { R = 0; diff --git a/Marlin/src/lcd/extui/ui_api.cpp b/Marlin/src/lcd/extui/ui_api.cpp index 4422e8115d8a7..da04c9bc8b5f6 100644 --- a/Marlin/src/lcd/extui/ui_api.cpp +++ b/Marlin/src/lcd/extui/ui_api.cpp @@ -333,7 +333,7 @@ namespace ExtUI { // This assumes the center is 0,0 #if ENABLED(DELTA) if (axis != Z) { - max = SQRT(sq(float(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y - axis])); // (Y - axis) == the other axis + max = SQRT(sq(float(PRINTABLE_RADIUS)) - sq(current_position[Y - axis])); // (Y - axis) == the other axis min = -max; } #endif diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 898aa331855ac..bfa13a1dce216 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -589,6 +589,8 @@ namespace Language_en { LSTR MSG_TOUCHMI_SAVE = _UxGT("Save"); LSTR MSG_MANUAL_DEPLOY_TOUCHMI = _UxGT("Deploy TouchMI"); LSTR MSG_MANUAL_DEPLOY = _UxGT("Deploy Z-Probe"); + LSTR MSG_MANUAL_PENUP = _UxGT("Pen up"); + LSTR MSG_MANUAL_PENDOWN = _UxGT("Pen down"); LSTR MSG_MANUAL_STOW = _UxGT("Stow Z-Probe"); LSTR MSG_HOME_FIRST = _UxGT("Home %s First"); LSTR MSG_ZPROBE_SETTINGS = _UxGT("Probe Settings"); diff --git a/Marlin/src/lcd/menu/menu_delta_calibrate.cpp b/Marlin/src/lcd/menu/menu_delta_calibrate.cpp index ae935e53c446b..94663744ea23f 100644 --- a/Marlin/src/lcd/menu/menu_delta_calibrate.cpp +++ b/Marlin/src/lcd/menu/menu_delta_calibrate.cpp @@ -92,7 +92,7 @@ void _man_probe_pt(const xy_pos_t &xy) { } void _goto_tower_a(const_float_t a) { - float dcr = DELTA_PRINTABLE_RADIUS - PROBING_MARGIN; + float dcr = PRINTABLE_RADIUS - PROBING_MARGIN; TERN_(HAS_PROBE_XY_OFFSET, dcr -= HYPOT(probe.offset_xy.x, probe.offset_xy.y)); TERN_(HAS_DELTA_SENSORLESS_PROBING, dcr *= sensorless_radius_factor); xy_pos_t tower_vec = { cos(RADIANS(a)), sin(RADIANS(a)) }; diff --git a/Marlin/src/lcd/menu/menu_motion.cpp b/Marlin/src/lcd/menu/menu_motion.cpp index b25dd73f26e7d..d6ac26434429f 100644 --- a/Marlin/src/lcd/menu/menu_motion.cpp +++ b/Marlin/src/lcd/menu/menu_motion.cpp @@ -68,7 +68,7 @@ void lcd_move_axis(const AxisEnum axis) { // This assumes the center is 0,0 #if ENABLED(DELTA) if (axis != Z_AXIS) { - max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis + max = SQRT(sq(float(PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis min = -max; } #endif @@ -370,6 +370,13 @@ void menu_motion() { #endif // + // Pen up/down menu + // + #if ENABLED(PEN_UP_DOWN_MENU) + GCODES_ITEM(MSG_MANUAL_PENUP, F("M280 P0 S90")); + GCODES_ITEM(MSG_MANUAL_PENDOWN, F("M280 P0 S50")); + #endif + // Probe Offset Wizard // #if ENABLED(PROBE_OFFSET_WIZARD) diff --git a/Marlin/src/lcd/tft/ui_1024x600.cpp b/Marlin/src/lcd/tft/ui_1024x600.cpp index 585aef05694e2..6511757d7dfaa 100644 --- a/Marlin/src/lcd/tft/ui_1024x600.cpp +++ b/Marlin/src/lcd/tft/ui_1024x600.cpp @@ -734,7 +734,7 @@ static void moveAxis(const AxisEnum axis, const int8_t direction) { // This assumes the center is 0,0 #if ENABLED(DELTA) if (axis != Z_AXIS && axis != E_AXIS) { - max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis + max = SQRT(sq(float(PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis min = -max; } #endif diff --git a/Marlin/src/lcd/tft/ui_320x240.cpp b/Marlin/src/lcd/tft/ui_320x240.cpp index a7c49241b473f..85ed59b2cce99 100644 --- a/Marlin/src/lcd/tft/ui_320x240.cpp +++ b/Marlin/src/lcd/tft/ui_320x240.cpp @@ -714,7 +714,7 @@ static void moveAxis(const AxisEnum axis, const int8_t direction) { // This assumes the center is 0,0 #if ENABLED(DELTA) if (axis != Z_AXIS && axis != E_AXIS) { - max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis + max = SQRT(sq(float(PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis min = -max; } #endif diff --git a/Marlin/src/lcd/tft/ui_480x320.cpp b/Marlin/src/lcd/tft/ui_480x320.cpp index c5e03566a9b1b..e8d51d89a10fb 100644 --- a/Marlin/src/lcd/tft/ui_480x320.cpp +++ b/Marlin/src/lcd/tft/ui_480x320.cpp @@ -715,7 +715,7 @@ static void moveAxis(const AxisEnum axis, const int8_t direction) { // This assumes the center is 0,0 #if ENABLED(DELTA) if (axis != Z_AXIS && axis != E_AXIS) { - max = SQRT(sq((float)(DELTA_PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis + max = SQRT(sq(float(PRINTABLE_RADIUS)) - sq(current_position[Y_AXIS - axis])); // (Y_AXIS - axis) == the other axis min = -max; } #endif diff --git a/Marlin/src/module/delta.cpp b/Marlin/src/module/delta.cpp index ce2a6f4adad06..58dc13972cb25 100644 --- a/Marlin/src/module/delta.cpp +++ b/Marlin/src/module/delta.cpp @@ -132,7 +132,7 @@ float delta_safe_distance_from_top() { xyz_pos_t cartesian{0}; inverse_kinematics(cartesian); const float centered_extent = delta.a; - cartesian.y = DELTA_PRINTABLE_RADIUS; + cartesian.y = PRINTABLE_RADIUS; inverse_kinematics(cartesian); return ABS(centered_extent - delta.a); } diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 8df39dae4b65a..a4e7b103260db 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -38,6 +38,10 @@ #include "../lcd/marlinui.h" #endif +#if ENABLED(POLAR) + #include "polar.h" +#endif + #if HAS_BED_PROBE #include "probe.h" #endif @@ -145,11 +149,14 @@ xyz_pos_t cartes; #if HAS_SOFTWARE_ENDSTOPS float delta_max_radius, delta_max_radius_2; #elif IS_SCARA - constexpr float delta_max_radius = SCARA_PRINTABLE_RADIUS, - delta_max_radius_2 = sq(SCARA_PRINTABLE_RADIUS); + constexpr float delta_max_radius = PRINTABLE_RADIUS, + delta_max_radius_2 = sq(PRINTABLE_RADIUS); + #elif ENABLED(POLAR) + constexpr float delta_max_radius = PRINTABLE_RADIUS, + delta_max_radius_2 = sq(PRINTABLE_RADIUS); #else // DELTA - constexpr float delta_max_radius = DELTA_PRINTABLE_RADIUS, - delta_max_radius_2 = sq(DELTA_PRINTABLE_RADIUS); + constexpr float delta_max_radius = PRINTABLE_RADIUS, + delta_max_radius_2 = sq(PRINTABLE_RADIUS); #endif #endif @@ -183,6 +190,7 @@ xyz_pos_t cartes; inline void report_more_positions() { stepper.report_positions(); TERN_(IS_SCARA, scara_report_positions()); + TERN_(POLAR, polar_report_positions()); } // Report the logical position for a given machine position @@ -277,8 +285,7 @@ void report_current_position_projected() { #endif ); - stepper.report_positions(); - TERN_(IS_SCARA, scara_report_positions()); + report_more_positions(); report_current_grblstate_moving(); } @@ -308,7 +315,7 @@ void report_current_position_projected() { #if ENABLED(DELTA) - can_reach = HYPOT2(rx, ry) <= sq(DELTA_PRINTABLE_RADIUS - inset + fslop); + can_reach = HYPOT2(rx, ry) <= sq(PRINTABLE_RADIUS - inset + fslop); #elif ENABLED(AXEL_TPARA) @@ -343,6 +350,8 @@ void report_current_position_projected() { && b < polargraph_max_belt_len + 1 ); + #elif ENABLED(POLAR) + can_reach = HYPOT(rx, ry) <= PRINTABLE_RADIUS; #endif return can_reach; @@ -426,6 +435,9 @@ void get_cartesian_from_steppers() { OPTARG(AXEL_TPARA, planner.get_axis_position_degrees(C_AXIS)) ); cartes.z = planner.get_axis_position_mm(Z_AXIS); + #elif ENABLED(POLAR) + forward_kinematics(planner.get_axis_position_mm(X_AXIS), planner.get_axis_position_degrees(B_AXIS)); + cartes.z = planner.get_axis_position_mm(Z_AXIS); #else NUM_AXIS_CODE( cartes.x = planner.get_axis_position_mm(X_AXIS), @@ -914,6 +926,10 @@ void restore_feedrate_and_scaling() { #if BOTH(HAS_HOTEND_OFFSET, DELTA) // The effector center position will be the target minus the hotend offset. const xy_pos_t offs = hotend_offset[active_extruder]; + #elif ENABLED(POLARGRAPH) + // POLARGRAPH uses draw_area_* below... + #elif ENABLED(POLAR) + // For now, we don't limit POLAR #else // SCARA needs to consider the angle of the arm through the entire move, so for now use no tool offset. constexpr xy_pos_t offs{0}; @@ -922,6 +938,8 @@ void restore_feedrate_and_scaling() { #if ENABLED(POLARGRAPH) LIMIT(target.x, draw_area_min.x, draw_area_max.x); LIMIT(target.y, draw_area_min.y, draw_area_max.y); + #elif ENABLED(POLAR) + // Motion limits are as same as cartesian limits. #else if (TERN1(IS_SCARA, axis_was_homed(X_AXIS) && axis_was_homed(Y_AXIS))) { const float dist_2 = HYPOT2(target.x - offs.x, target.y - offs.y); @@ -1055,6 +1073,8 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) { * and compare the difference. */ #define SCARA_MIN_SEGMENT_LENGTH 0.5f + #elif ENABLED(POLAR) + #define POLAR_MIN_SEGMENT_LENGTH 0.5f #endif /** @@ -1107,6 +1127,8 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) { // For SCARA enforce a minimum segment size #if IS_SCARA NOMORE(segments, cartesian_mm * RECIPROCAL(SCARA_MIN_SEGMENT_LENGTH)); + #elif ENABLED(POLAR) + NOMORE(segments, cartesian_mm * RECIPROCAL(POLAR_MIN_SEGMENT_LENGTH)); #endif // At least one segment is required @@ -1118,7 +1140,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) { // Add hints to help optimize the move PlannerHints hints(cartesian_mm * inv_segments); - TERN_(SCARA_FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters); + TERN_(FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters); /* SERIAL_ECHOPGM("mm=", cartesian_mm); @@ -1185,7 +1207,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) { // Add hints to help optimize the move PlannerHints hints(cartesian_mm * inv_segments); - TERN_(SCARA_FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters); + TERN_(FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters); //SERIAL_ECHOPGM("mm=", cartesian_mm); //SERIAL_ECHOLNPGM(" segments=", segments); diff --git a/Marlin/src/module/motion.h b/Marlin/src/module/motion.h index ce9094765765d..8950cf6f228d2 100644 --- a/Marlin/src/module/motion.h +++ b/Marlin/src/module/motion.h @@ -32,6 +32,8 @@ #if IS_SCARA #include "scara.h" +#elif ENABLED(POLAR) + #include "polar.h" #endif // Error margin to work around float imprecision diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 867b6a98a8763..aceaf8a253a5f 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -3161,24 +3161,75 @@ bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s ? xyz_pos_t(cart_dist_mm).magnitude() : TERN0(HAS_Z_AXIS, ABS(cart_dist_mm.z)); - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if DISABLED(FEEDRATE_SCALING) + + const feedRate_t feedrate = fr_mm_s; + + #elif IS_SCARA + // For SCARA scale the feedrate from mm/s to degrees/s // i.e., Complete the angular vector in the given time. const float duration_recip = hints.inv_duration ?: fr_mm_s / ph.millimeters; const xyz_pos_t diff = delta - position_float; const feedRate_t feedrate = diff.magnitude() * duration_recip; - #else - const feedRate_t feedrate = fr_mm_s; - #endif + + #elif ENABLED(POLAR) + + /** + * Motion problem for Polar axis near center / origin: + * + * 3D printing: + * Movements very close to the center of the polar axis take more time than others. + * This brief delay results in more material deposition due to the pressure in the nozzle. + * + * Current Kinematics and feedrate scaling deals with this by making the movement as fast + * as possible. It works for slow movements but doesn't work well with fast ones. A more + * complicated extrusion compensation must be implemented. + * + * Ideally, it should estimate that a long rotation near the center is ahead and will cause + * unwanted deposition. Therefore it can compensate the extrusion beforehand. + * + * Laser cutting: + * Same thing would be a problem for laser engraving too. As it spends time rotating at the + * center point, more likely it will burn more material than it should. Therefore similar + * compensation would be implemented for laser-cutting operations. + * + * Milling: + * This shouldn't be a problem for cutting/milling operations. + */ + feedRate_t calculated_feedrate = fr_mm_s; + const xyz_pos_t diff = delta - position_float; + if (!NEAR_ZERO(diff.b)) { + if (delta.a <= POLAR_FAST_RADIUS ) + calculated_feedrate = settings.max_feedrate_mm_s[Y_AXIS]; + else { + // Normalized vector of movement + const float diffBLength = ABS((2.0f * PI * diff.a) * (diff.b / 360.0f)), + diffTheta = DEGREES(ATAN2(diff.a, diffBLength)), + normalizedTheta = 1.0f - (ABS(diffTheta > 90.0f ? 180.0f - diffTheta : diffTheta) / 90.0f); + + // Normalized position along the radius + const float radiusRatio = PRINTABLE_RADIUS/delta.a; + calculated_feedrate += (fr_mm_s * radiusRatio * normalizedTheta); + } + } + const feedRate_t feedrate = calculated_feedrate; + + #endif // POLAR && FEEDRATE_SCALING + TERN_(HAS_EXTRUDERS, delta.e = machine.e); if (buffer_segment(delta OPTARG(HAS_DIST_MM_ARG, cart_dist_mm), feedrate, extruder, ph)) { position_cart = cart; return true; } return false; - #else + + #else // !IS_KINEMATIC + return buffer_segment(machine, fr_mm_s, extruder, hints); + #endif + } // buffer_line() #if ENABLED(DIRECT_STEPPING) diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index 04cde2381da2d..7cc8bc08e4ebe 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -50,6 +50,8 @@ #include "delta.h" #elif ENABLED(POLARGRAPH) #include "polargraph.h" +#elif ENABLED(POLAR) + #include "polar.h" #endif #if ABL_PLANAR @@ -291,7 +293,7 @@ typedef struct PlannerBlock { } block_t; -#if ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL, POWER_LOSS_RECOVERY) +#if ANY(LIN_ADVANCE, FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL, POWER_LOSS_RECOVERY) #define HAS_POSITION_FLOAT 1 #endif @@ -361,7 +363,7 @@ typedef struct { struct PlannerHints { float millimeters = 0.0; // Move Length, if known, else 0. - #if ENABLED(SCARA_FEEDRATE_SCALING) + #if ENABLED(FEEDRATE_SCALING) float inv_duration = 0.0; // Reciprocal of the move duration, if known #endif #if ENABLED(HINTS_CURVE_RADIUS) @@ -913,8 +915,8 @@ class Planner { return out; } - // SCARA AB axes are in degrees, not mm - #if IS_SCARA + // SCARA AB and Polar YB axes are in degrees, not mm + #if EITHER(IS_SCARA, POLAR) FORCE_INLINE static float get_axis_position_degrees(const AxisEnum axis) { return get_axis_position_mm(axis); } #endif diff --git a/Marlin/src/module/polar.cpp b/Marlin/src/module/polar.cpp new file mode 100644 index 0000000000000..4fbd33cccf53b --- /dev/null +++ b/Marlin/src/module/polar.cpp @@ -0,0 +1,102 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * POLAR Kinematics + * developed by Kadir ilkimen for PolarBear CNC and babyBear + * https://github.com/kadirilkimen/Polar-Bear-Cnc-Machine + * https://github.com/kadirilkimen/babyBear-3D-printer + * + * A polar machine can have different configurations. + * This kinematics is only compatible with the following configuration: + * X : Independent linear + * Y or B : Polar + * Z : Independent linear + * + * For example, PolarBear has CoreXZ plus Polar Y or B. + */ + +#include "../inc/MarlinConfigPre.h" + +#if ENABLED(POLAR) + +#include "polar.h" +#include "motion.h" +#include "planner.h" + +#include "../inc/MarlinConfig.h" + +float segments_per_second = DEFAULT_SEGMENTS_PER_SECOND, + polar_center_offset = POLAR_CENTER_OFFSET; + +float absoluteAngle(float a) { + if (a < 0.0) while (a < 0.0) a += 360.0; + else if (a > 360.0) while (a > 360.0) a -= 360.0; + return a; +} + +void forward_kinematics(const_float_t r, const_float_t theta) { + const float absTheta = absoluteAngle(theta); + float radius = r; + if (polar_center_offset > 0.0) radius = SQRT( ABS( sq(r) - sq(-polar_center_offset) ) ); + cartes.x = cos(RADIANS(absTheta))*radius; + cartes.y = sin(RADIANS(absTheta))*radius; +} + +void inverse_kinematics(const xyz_pos_t &raw) { + const float x = raw.x, y = raw.y, + rawRadius = HYPOT(x,y), + posTheta = DEGREES(ATAN2(y, x)); + + static float current_polar_theta = 0; + + float r = rawRadius, + theta = absoluteAngle(posTheta), + currentAbsTheta = absoluteAngle(current_polar_theta); + + if (polar_center_offset > 0.0) { + const float offsetRadius = SQRT(ABS(sq(r) - sq(polar_center_offset))); + float offsetTheta = absoluteAngle(DEGREES(ATAN2(polar_center_offset, offsetRadius))); + theta = absoluteAngle(offsetTheta + theta); + } + + const float deltaTheta = theta - currentAbsTheta; + if (ABS(deltaTheta) <= 180) + theta = current_polar_theta + deltaTheta; + else { + if (currentAbsTheta > 180) theta = current_polar_theta + 360 + deltaTheta; + else theta = current_polar_theta - (360 - deltaTheta); + } + + current_polar_theta = theta; + + delta.set(r, theta, raw.z); +} + +void polar_report_positions() { + SERIAL_ECHOLNPGM("X: ", planner.get_axis_position_mm(X_AXIS), + " POLAR Theta:", planner.get_axis_position_degrees(B_AXIS), + " Z: ", planner.get_axis_position_mm(Z_AXIS) + ); +} + +#endif diff --git a/Marlin/src/module/polar.h b/Marlin/src/module/polar.h new file mode 100644 index 0000000000000..79fbd4194d531 --- /dev/null +++ b/Marlin/src/module/polar.h @@ -0,0 +1,36 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +/** + * polar.h - POLAR-specific functions + */ + +#include "../core/types.h" + +extern float segments_per_second; + +float absoluteAngle(float a); +void forward_kinematics(const_float_t r, const_float_t theta); + +void inverse_kinematics(const xyz_pos_t &raw); +void polar_report_positions(); diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index 73b8af000c3c7..9b28551322499 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -193,12 +193,8 @@ class Probe { #if HAS_BED_PROBE || HAS_LEVELING #if IS_KINEMATIC - static constexpr float printable_radius = ( - TERN_(DELTA, DELTA_PRINTABLE_RADIUS) - TERN_(IS_SCARA, SCARA_PRINTABLE_RADIUS) - ); static constexpr float probe_radius(const xy_pos_t &probe_offset_xy=offset_xy) { - return printable_radius - _MAX(PROBING_MARGIN, HYPOT(probe_offset_xy.x, probe_offset_xy.y)); + return float(PRINTABLE_RADIUS) - _MAX(PROBING_MARGIN, HYPOT(probe_offset_xy.x, probe_offset_xy.y)); } #endif diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 70a1e25ceca35..9c3d459634e36 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -3212,7 +3212,7 @@ int32_t Stepper::triggered_position(const AxisEnum axis) { #if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX, IS_SCARA, DELTA) #define SAYS_A 1 #endif -#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY, MARKFORGED_YX, IS_SCARA, DELTA) +#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY, MARKFORGED_YX, IS_SCARA, DELTA, POLAR) #define SAYS_B 1 #endif #if ANY(CORE_IS_XZ, CORE_IS_YZ, DELTA) diff --git a/buildroot/share/sublime/MarlinFirmware.sublime-project b/buildroot/share/sublime/MarlinFirmware.sublime-project index e0cf953fa8135..11808dd45df05 100644 --- a/buildroot/share/sublime/MarlinFirmware.sublime-project +++ b/buildroot/share/sublime/MarlinFirmware.sublime-project @@ -11,7 +11,7 @@ ".vscode" ], "binary_file_patterns": - [ "*.psd", "*.png", "*.jpg", "*.jpeg", "*.bdf", "*.patch", "avrdude_5.*", "*.svg", "*.bin", "*.woff" ], + [ "*.psd", "*.png", "*.jpg", "*.jpeg", "*.bdf", "*.patch", "avrdude_5.*", "*.svg", "*.bin", "*.woff", "*.otf" ], "file_exclude_patterns": [ "Marlin/platformio.ini",