Polar Kinematics (#25214)

This commit is contained in:
kadir ilkimen
2023-01-11 06:29:38 +02:00
committed by Scott Lahteine
parent 33e5aad364
commit 7717beb793
32 changed files with 376 additions and 76 deletions

View File

@@ -915,7 +915,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 +969,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 +1004,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 +1014,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 +1473,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

View File

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

View File

@@ -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"

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -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")

View File

@@ -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();

View File

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

View File

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

View File

@@ -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)

View File

@@ -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.");
@@ -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)

View File

@@ -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;

View File

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

View File

@@ -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)) };

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

@@ -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,8 @@ 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(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 +936,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 +1071,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 +1125,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 +1138,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 +1205,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);

View File

@@ -32,6 +32,8 @@
#if IS_SCARA
#include "scara.h"
#elif ENABLED(POLAR)
#include "polar.h"
#endif
// Error margin to work around float imprecision

View File

@@ -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)

View File

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

102
Marlin/src/module/polar.cpp Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
*
*/
/**
* 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

36
Marlin/src/module/polar.h Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
*
*/
#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();

View File

@@ -195,12 +195,8 @@ public:
#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

View File

@@ -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)

View File

@@ -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",