Skip to content

Commit

Permalink
✨ Polar Kinematics (MarlinFirmware#25214)
Browse files Browse the repository at this point in the history
  • Loading branch information
kadirilkimen authored and Tracy Spiva committed May 25, 2023
1 parent 2310693 commit 1373605
Show file tree
Hide file tree
Showing 32 changed files with 376 additions and 76 deletions.
65 changes: 59 additions & 6 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,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)
Expand Down Expand Up @@ -968,7 +968,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)
Expand Down Expand Up @@ -1003,7 +1003,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)
Expand All @@ -1013,6 +1013,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.
Expand Down Expand Up @@ -1426,13 +1479,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
Expand Down
2 changes: 2 additions & 0 deletions Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Marlin/src/core/language.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@
#define STR_S_SEG_PER_SEC "S<seg-per-sec>"
#define STR_DELTA_SETTINGS "Delta (L<diagonal-rod> R<radius> H<height> S<seg-per-sec> XYZ<tower-angle-trim> ABC<rod-trim>)"
#define STR_SCARA_SETTINGS "SCARA"
#define STR_POLAR_SETTINGS "Polar"
#define STR_POLARGRAPH_SETTINGS "Polargraph"
#define STR_SCARA_P_T_Z "P<theta-psi-offset> T<theta-offset> Z<home-offset>"
#define STR_ENDSTOP_ADJUSTMENT "Endstop adjustment"
Expand Down
32 changes: 15 additions & 17 deletions Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/gcode/calibrate/G33.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/gcode/calibrate/M48.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 19 additions & 0 deletions Marlin/src/gcode/calibrate/M665.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/gcode/host/M114.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions Marlin/src/gcode/host/M360.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/gcode/motion/G0_G1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/gcode/motion/G2_G3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion Marlin/src/inc/Conditionals_LCD.h
Original file line number Diff line number Diff line change
Expand Up @@ -1406,7 +1406,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
Expand Down
14 changes: 8 additions & 6 deletions Marlin/src/inc/Conditionals_post.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,19 +267,19 @@
*/
#if IS_KINEMATIC
#undef LCD_BED_TRAMMING
#undef SLOWDOWN
#endif

/**
* SCARA cannot use SLOWDOWN and requires QUICKHOME
* 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

Expand Down Expand Up @@ -378,7 +378,6 @@
*/
#if ENABLED(DELTA)
#undef Z_SAFE_HOMING
#undef SLOWDOWN
#endif

#ifndef MESH_INSET
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
23 changes: 19 additions & 4 deletions Marlin/src/inc/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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

/**
Expand Down Expand Up @@ -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

/**
Expand Down Expand Up @@ -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.");
Expand Down Expand Up @@ -2004,6 +2017,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)
Expand Down
Loading

0 comments on commit 1373605

Please sign in to comment.