Skip to content

Commit 2388087

Browse files
DerAndere1LMBernardo
authored andcommitted
πŸ§‘β€πŸ’» Add get_move_distance for rotation/kinematics (MarlinFirmware#25370)
1 parent 2ed2687 commit 2388087

File tree

4 files changed

+139
-73
lines changed

4 files changed

+139
-73
lines changed

β€ŽMarlin/src/module/motion.cpp

+100-3
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,88 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
10591059
thermalManager.task(); // Returns immediately on most calls
10601060
}
10611061

1062+
/**
1063+
* Get distance from displacements along axes and, if required, update move type.
1064+
*/
1065+
float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool &is_cartesian_move)) {
1066+
if (!(NUM_AXIS_GANG(diff.x, || diff.y, /* skip z */, || diff.i, || diff.j, || diff.k, || diff.u, || diff.v, || diff.w)))
1067+
return TERN0(HAS_Z_AXIS, ABS(diff.z));
1068+
1069+
#if ENABLED(ARTICULATED_ROBOT_ARM)
1070+
1071+
// For articulated robots, interpreting feedrate like LinuxCNC would require inverse kinematics. As a workaround, pretend that motors sit on n mutually orthogonal
1072+
// axes and assume that we could think of distance as magnitude of an n-vector in an n-dimensional Euclidian space.
1073+
const float distance_sqr = NUM_AXIS_GANG(
1074+
sq(diff.x), + sq(diff.y), + sq(diff.z),
1075+
+ sq(diff.i), + sq(diff.j), + sq(diff.k),
1076+
+ sq(diff.u), + sq(diff.v), + sq(diff.w)
1077+
);
1078+
1079+
#elif ENABLED(FOAMCUTTER_XYUV)
1080+
1081+
const float distance_sqr = (
1082+
#if HAS_J_AXIS
1083+
_MAX(sq(diff.x) + sq(diff.y), sq(diff.i) + sq(diff.j)) // Special 5 axis kinematics. Return the larger of plane X/Y or I/J
1084+
#else
1085+
sq(diff.x) + sq(diff.y) // Foamcutter with only two axes (XY)
1086+
#endif
1087+
);
1088+
1089+
#else
1090+
1091+
/**
1092+
* Calculate distance for feedrate interpretation in accordance with NIST RS274NGC interpreter - version 3) and its default CANON_XYZ feed reference mode.
1093+
* Assume:
1094+
* - X, Y, Z are the primary linear axes;
1095+
* - U, V, W are secondary linear axes;
1096+
* - A, B, C are rotational axes.
1097+
*
1098+
* Then:
1099+
* - dX, dY, dZ are the displacements of the primary linear axes;
1100+
* - dU, dV, dW are the displacements of linear axes;
1101+
* - dA, dB, dC are the displacements of rotational axes.
1102+
*
1103+
* The time it takes to execute a move command with feedrate F is t = D/F,
1104+
* plus any time for acceleration and deceleration.
1105+
* Here, D is the total distance, calculated as follows:
1106+
*
1107+
* D^2 = dX^2 + dY^2 + dZ^2
1108+
* if D^2 == 0 (none of XYZ move but any secondary linear axes move, whether other axes are moved or not):
1109+
* D^2 = dU^2 + dV^2 + dW^2
1110+
* if D^2 == 0 (only rotational axes are moved):
1111+
* D^2 = dA^2 + dB^2 + dC^2
1112+
*/
1113+
float distance_sqr = XYZ_GANG(sq(diff.x), + sq(diff.y), + sq(diff.z));
1114+
1115+
#if SECONDARY_LINEAR_AXES
1116+
if (UNEAR_ZERO(distance_sqr)) {
1117+
// Move does not involve any primary linear axes (xyz) but might involve secondary linear axes
1118+
distance_sqr = (
1119+
SECONDARY_AXIS_GANG(
1120+
IF_DISABLED(AXIS4_ROTATES, + sq(diff.i)),
1121+
IF_DISABLED(AXIS5_ROTATES, + sq(diff.j)),
1122+
IF_DISABLED(AXIS6_ROTATES, + sq(diff.k)),
1123+
IF_DISABLED(AXIS7_ROTATES, + sq(diff.u)),
1124+
IF_DISABLED(AXIS8_ROTATES, + sq(diff.v)),
1125+
IF_DISABLED(AXIS9_ROTATES, + sq(diff.w))
1126+
)
1127+
);
1128+
}
1129+
#endif
1130+
1131+
#if HAS_ROTATIONAL_AXES
1132+
if (UNEAR_ZERO(distance_sqr)) {
1133+
// Move involves only rotational axes. Calculate angular distance in accordance with LinuxCNC
1134+
is_cartesian_move = false;
1135+
distance_sqr = ROTATIONAL_AXIS_GANG(sq(diff.i), + sq(diff.j), + sq(diff.k), + sq(diff.u), + sq(diff.v), + sq(diff.w));
1136+
}
1137+
#endif
1138+
1139+
#endif
1140+
1141+
return SQRT(distance_sqr);
1142+
}
1143+
10621144
#if IS_KINEMATIC
10631145

10641146
#if IS_SCARA
@@ -1109,7 +1191,10 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
11091191
if (!position_is_reachable(destination)) return true;
11101192

11111193
// Get the linear distance in XYZ
1112-
float cartesian_mm = xyz_float_t(diff).magnitude();
1194+
#if HAS_ROTATIONAL_AXES
1195+
bool cartes_move = true;
1196+
#endif
1197+
float cartesian_mm = get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartes_move));
11131198

11141199
// If the move is very short, check the E move distance
11151200
TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(diff.e));
@@ -1118,7 +1203,13 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
11181203
if (UNEAR_ZERO(cartesian_mm)) return true;
11191204

11201205
// Minimum number of seconds to move the given distance
1121-
const float seconds = cartesian_mm / scaled_fr_mm_s;
1206+
const float seconds = cartesian_mm / (
1207+
#if BOTH(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT)
1208+
cartes_move ? scaled_fr_mm_s : LINEAR_UNIT(scaled_fr_mm_s)
1209+
#else
1210+
scaled_fr_mm_s
1211+
#endif
1212+
);
11221213

11231214
// The number of segments-per-second times the duration
11241215
// gives the number of segments
@@ -1140,6 +1231,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
11401231

11411232
// Add hints to help optimize the move
11421233
PlannerHints hints(cartesian_mm * inv_segments);
1234+
TERN_(HAS_ROTATIONAL_AXES, hints.cartesian_move = cartes_move);
11431235
TERN_(FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters);
11441236

11451237
/*
@@ -1190,9 +1282,13 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
11901282
}
11911283

11921284
// Get the linear distance in XYZ
1285+
#if HAS_ROTATIONAL_AXES
1286+
bool cartes_move = true;
1287+
#endif
1288+
float cartesian_mm = get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartes_move));
1289+
11931290
// If the move is very short, check the E move distance
11941291
// No E move either? Game over.
1195-
float cartesian_mm = diff.magnitude();
11961292
TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(diff.e));
11971293
if (UNEAR_ZERO(cartesian_mm)) return;
11981294

@@ -1207,6 +1303,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
12071303

12081304
// Add hints to help optimize the move
12091305
PlannerHints hints(cartesian_mm * inv_segments);
1306+
TERN_(HAS_ROTATIONAL_AXES, hints.cartesian_move = cartes_move);
12101307
TERN_(FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters);
12111308

12121309
//SERIAL_ECHOPGM("mm=", cartesian_mm);

β€ŽMarlin/src/module/motion.h

+2
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ void report_current_position_projected();
302302
#endif
303303
#endif
304304

305+
float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool &is_cartesian_move));
306+
305307
void get_cartesian_from_steppers();
306308
void set_current_from_steppers_for_axis(const AxisEnum axis);
307309

β€ŽMarlin/src/module/planner.cpp

+32-70
Original file line numberDiff line numberDiff line change
@@ -2130,8 +2130,8 @@ bool Planner::_populate_block(
21302130

21312131
TERN_(LCD_SHOW_E_TOTAL, e_move_accumulator += steps_dist_mm.e);
21322132

2133-
#if BOTH(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT)
2134-
bool cartesian_move = true;
2133+
#if HAS_ROTATIONAL_AXES
2134+
bool cartesian_move = hints.cartesian_move;
21352135
#endif
21362136

21372137
if (true NUM_AXIS_GANG(
@@ -2152,71 +2152,34 @@ bool Planner::_populate_block(
21522152
if (hints.millimeters)
21532153
block->millimeters = hints.millimeters;
21542154
else {
2155-
/**
2156-
* Distance for interpretation of feedrate in accordance with LinuxCNC (the successor of NIST
2157-
* RS274NGC interpreter - version 3) and its default CANON_XYZ feed reference mode.
2158-
* Assume that X, Y, Z are the primary linear axes and U, V, W are secondary linear axes and A, B, C are
2159-
* rotational axes. Then dX, dY, dZ are the displacements of the primary linear axes and dU, dV, dW are the displacements of linear axes and
2160-
* dA, dB, dC are the displacements of rotational axes.
2161-
* The time it takes to execute move command with feedrate F is t = D/F, where D is the total distance, calculated as follows:
2162-
* D^2 = dX^2 + dY^2 + dZ^2
2163-
* if D^2 == 0 (none of XYZ move but any secondary linear axes move, whether other axes are moved or not):
2164-
* D^2 = dU^2 + dV^2 + dW^2
2165-
* if D^2 == 0 (only rotational axes are moved):
2166-
* D^2 = dA^2 + dB^2 + dC^2
2167-
*/
2168-
float distance_sqr = (
2169-
#if ENABLED(ARTICULATED_ROBOT_ARM)
2170-
// For articulated robots, interpreting feedrate like LinuxCNC would require inverse kinematics. As a workaround, pretend that motors sit on n mutually orthogonal
2171-
// axes and assume that we could think of distance as magnitude of an n-vector in an n-dimensional Euclidian space.
2172-
NUM_AXIS_GANG(
2173-
sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z),
2174-
+ sq(steps_dist_mm.i), + sq(steps_dist_mm.j), + sq(steps_dist_mm.k),
2175-
+ sq(steps_dist_mm.u), + sq(steps_dist_mm.v), + sq(steps_dist_mm.w)
2176-
)
2177-
#elif ENABLED(FOAMCUTTER_XYUV)
2178-
#if HAS_J_AXIS
2179-
// Special 5 axis kinematics. Return the largest distance move from either X/Y or I/J plane
2180-
_MAX(sq(steps_dist_mm.x) + sq(steps_dist_mm.y), sq(steps_dist_mm.i) + sq(steps_dist_mm.j))
2181-
#else // Foamcutter with only two axes (XY)
2182-
sq(steps_dist_mm.x) + sq(steps_dist_mm.y)
2183-
#endif
2184-
#elif ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
2185-
XYZ_GANG(sq(steps_dist_mm.head.x), + sq(steps_dist_mm.head.y), + sq(steps_dist_mm.z))
2155+
const xyze_pos_t displacement = LOGICAL_AXIS_ARRAY(
2156+
steps_dist_mm.e,
2157+
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
2158+
steps_dist_mm.head.x,
2159+
steps_dist_mm.head.y,
2160+
steps_dist_mm.z,
21862161
#elif CORE_IS_XZ
2187-
XYZ_GANG(sq(steps_dist_mm.head.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.head.z))
2162+
steps_dist_mm.head.x,
2163+
steps_dist_mm.y,
2164+
steps_dist_mm.head.z,
21882165
#elif CORE_IS_YZ
2189-
XYZ_GANG(sq(steps_dist_mm.x), + sq(steps_dist_mm.head.y), + sq(steps_dist_mm.head.z))
2166+
steps_dist_mm.x,
2167+
steps_dist_mm.head.y,
2168+
steps_dist_mm.head.z,
21902169
#else
2191-
XYZ_GANG(sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z))
2170+
steps_dist_mm.x,
2171+
steps_dist_mm.y,
2172+
steps_dist_mm.z,
21922173
#endif
2174+
steps_dist_mm.i,
2175+
steps_dist_mm.j,
2176+
steps_dist_mm.k,
2177+
steps_dist_mm.u,
2178+
steps_dist_mm.v,
2179+
steps_dist_mm.w
21932180
);
21942181

2195-
#if SECONDARY_LINEAR_AXES && NONE(FOAMCUTTER_XYUV, ARTICULATED_ROBOT_ARM)
2196-
if (UNEAR_ZERO(distance_sqr)) {
2197-
// Move does not involve any primary linear axes (xyz) but might involve secondary linear axes
2198-
distance_sqr = (0.0f
2199-
SECONDARY_AXIS_GANG(
2200-
IF_DISABLED(AXIS4_ROTATES, + sq(steps_dist_mm.i)),
2201-
IF_DISABLED(AXIS5_ROTATES, + sq(steps_dist_mm.j)),
2202-
IF_DISABLED(AXIS6_ROTATES, + sq(steps_dist_mm.k)),
2203-
IF_DISABLED(AXIS7_ROTATES, + sq(steps_dist_mm.u)),
2204-
IF_DISABLED(AXIS8_ROTATES, + sq(steps_dist_mm.v)),
2205-
IF_DISABLED(AXIS9_ROTATES, + sq(steps_dist_mm.w))
2206-
)
2207-
);
2208-
}
2209-
#endif
2210-
2211-
#if HAS_ROTATIONAL_AXES && NONE(FOAMCUTTER_XYUV, ARTICULATED_ROBOT_ARM)
2212-
if (UNEAR_ZERO(distance_sqr)) {
2213-
// Move involves only rotational axes. Calculate angular distance in accordance with LinuxCNC
2214-
TERN_(INCH_MODE_SUPPORT, cartesian_move = false);
2215-
distance_sqr = ROTATIONAL_AXIS_GANG(sq(steps_dist_mm.i), + sq(steps_dist_mm.j), + sq(steps_dist_mm.k), + sq(steps_dist_mm.u), + sq(steps_dist_mm.v), + sq(steps_dist_mm.w));
2216-
}
2217-
#endif
2218-
2219-
block->millimeters = SQRT(distance_sqr);
2182+
block->millimeters = get_move_distance(displacement OPTARG(HAS_ROTATIONAL_AXES, cartesian_move));
22202183
}
22212184

22222185
/**
@@ -2354,12 +2317,13 @@ bool Planner::_populate_block(
23542317
// Calculate inverse time for this move. No divide by zero due to previous checks.
23552318
// Example: At 120mm/s a 60mm move involving XYZ axes takes 0.5s. So this will give 2.0.
23562319
// Example 2: At 120Β°/s a 60Β° move involving only rotational axes takes 0.5s. So this will give 2.0.
2357-
float inverse_secs;
2358-
#if BOTH(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT)
2359-
inverse_secs = inverse_millimeters * (cartesian_move ? fr_mm_s : LINEAR_UNIT(fr_mm_s));
2360-
#else
2361-
inverse_secs = fr_mm_s * inverse_millimeters;
2362-
#endif
2320+
float inverse_secs = inverse_millimeters * (
2321+
#if BOTH(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT)
2322+
cartesian_move ? fr_mm_s : LINEAR_UNIT(fr_mm_s)
2323+
#else
2324+
fr_mm_s
2325+
#endif
2326+
);
23632327

23642328
// Get the number of non busy movements in queue (non busy means that they can be altered)
23652329
const uint8_t moves_queued = nonbusy_movesplanned();
@@ -3157,9 +3121,7 @@ bool Planner::buffer_line(const xyze_pos_t &cart, const_feedRate_t fr_mm_s
31573121

31583122
PlannerHints ph = hints;
31593123
if (!hints.millimeters)
3160-
ph.millimeters = (cart_dist_mm.x || cart_dist_mm.y)
3161-
? xyz_pos_t(cart_dist_mm).magnitude()
3162-
: TERN0(HAS_Z_AXIS, ABS(cart_dist_mm.z));
3124+
ph.millimeters = get_move_distance(xyze_pos_t(cart_dist_mm) OPTARG(HAS_ROTATIONAL_AXES, ph.cartesian_move));
31633125

31643126
#if DISABLED(FEEDRATE_SCALING)
31653127

β€ŽMarlin/src/module/planner.h

+5
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,11 @@ struct PlannerHints {
377377
// would calculate if it knew the as-yet-unbuffered path
378378
#endif
379379

380+
#if HAS_ROTATIONAL_AXES
381+
bool cartesian_move = true; // True if linear motion of the tool centerpoint relative to the workpiece occurs.
382+
// False if no movement of the tool center point relative to the work piece occurs
383+
// (i.e. the tool rotates around the tool centerpoint)
384+
#endif
380385
PlannerHints(const_float_t mm=0.0f) : millimeters(mm) {}
381386
};
382387

0 commit comments

Comments
Β (0)