🎨 Clean up FTM, etc.

This commit is contained in:
Scott Lahteine 2025-09-13 23:05:41 -05:00
parent 294903439a
commit 8aac26275e
8 changed files with 65 additions and 65 deletions

View file

@ -1150,16 +1150,18 @@
#if ENABLED(FT_MOTION)
//#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default?
#define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (DISABLED, Z_BASED, MASS_BASED)
#define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV)
#define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis
#define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false)
#define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain. (Acceleration-based scaling factor.)
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
#define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV)
#define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis
#define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis
#define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis
#define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers
#define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis
#define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters

View file

@ -61,13 +61,13 @@ void say_shaping() {
// FT Shaping
#if HAS_X_AXIS
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
SERIAL_ECHOPGM(" with " AXIS_0_NAME);
say_shaper_type(X_AXIS);
}
#endif
#if HAS_Y_AXIS
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
SERIAL_ECHOPGM(" and with " AXIS_1_NAME);
say_shaper_type(Y_AXIS);
}
@ -80,7 +80,7 @@ void say_shaping() {
dynamic = z_based || g_based;
// FT Dynamic Frequency Mode
if (AXIS_HAS_SHAPER(X) || AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) {
#if HAS_DYNAMIC_FREQ
SERIAL_ECHOPGM("Dynamic Frequency Mode ");
switch (ftMotion.cfg.dynFreqMode) {
@ -263,7 +263,7 @@ void GcodeSuite::M493() {
// Dynamic frequency mode parameter.
if (parser.seenval('D')) {
if (AXIS_HAS_SHAPER(X) || AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y)) {
const dynFreqMode_t val = dynFreqMode_t(parser.value_byte());
switch (val) {
#if HAS_DYNAMIC_FREQ_MM
@ -297,7 +297,7 @@ void GcodeSuite::M493() {
// Parse frequency parameter (X axis).
if (parser.seenval('A')) {
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
const float val = parser.value_float();
// TODO: Frequency minimum is dependent on the shaper used; the above check isn't always correct.
if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) {
@ -326,7 +326,7 @@ void GcodeSuite::M493() {
// Parse zeta parameter (X axis).
if (parser.seenval('I')) {
const float val = parser.value_float();
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
if (WITHIN(val, 0.01f, 1.0f)) {
ftMotion.cfg.zeta[0] = val;
flag.update = true;
@ -341,7 +341,7 @@ void GcodeSuite::M493() {
// Parse vtol parameter (X axis).
if (parser.seenval('Q')) {
const float val = parser.value_float();
if (AXIS_HAS_EISHAPER(X)) {
if (AXIS_IS_EISHAPING(X)) {
if (WITHIN(val, 0.00f, 1.0f)) {
ftMotion.cfg.vtol[0] = val;
flag.update = true;
@ -359,7 +359,7 @@ void GcodeSuite::M493() {
// Parse frequency parameter (Y axis).
if (parser.seenval('B')) {
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
const float val = parser.value_float();
if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) {
ftMotion.cfg.baseFreq.y = val;
@ -387,7 +387,7 @@ void GcodeSuite::M493() {
// Parse zeta parameter (Y axis).
if (parser.seenval('J')) {
const float val = parser.value_float();
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
if (WITHIN(val, 0.01f, 1.0f)) {
ftMotion.cfg.zeta[1] = val;
flag.update = true;
@ -402,7 +402,7 @@ void GcodeSuite::M493() {
// Parse vtol parameter (Y axis).
if (parser.seenval('R')) {
const float val = parser.value_float();
if (AXIS_HAS_EISHAPER(Y)) {
if (AXIS_IS_EISHAPING(Y)) {
if (WITHIN(val, 0.00f, 1.0f)) {
ftMotion.cfg.vtol[1] = val;
flag.update = true;

View file

@ -95,15 +95,16 @@ void GcodeSuite::M920() {
void GcodeSuite::M920_report(const bool forReplay/*=true*/) {
TERN_(MARLIN_SMALL_BUILD, return);
report_heading(forReplay, F(STR_HOMING_CURRENT));
auto say_M920 = [](const bool forReplay, int8_t index=-1) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M920");
if (index >= 0) SERIAL_ECHOPGM(" " I_PARAM_STR, index);
};
#if X_SENSORLESS || Y_SENSORLESS || Z_SENSORLESS
report_heading(forReplay, F(STR_HOMING_CURRENT));
auto say_M920 = [](const bool forReplay, int8_t index=-1) {
report_echo_start(forReplay);
SERIAL_ECHOPGM(" M920");
if (index >= 0) SERIAL_ECHOPGM(" " I_PARAM_STR, index);
};
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS || Z3_SENSORLESS || Z4_SENSORLESS
say_M920(forReplay, 0);
#else
@ -123,23 +124,26 @@ void GcodeSuite::M920_report(const bool forReplay/*=true*/) {
TERN_(V_SENSORLESS, SERIAL_ECHOPGM_P(SP_V_STR, homing_current_mA.V));
TERN_(W_SENSORLESS, SERIAL_ECHOPGM_P(SP_W_STR, homing_current_mA.W));
SERIAL_EOL();
#endif
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS
say_M920(forReplay, 1);
TERN_(X2_SENSORLESS, SERIAL_ECHOPGM_P(SP_X_STR, homing_current_mA.X2));
TERN_(Y2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Y_STR, homing_current_mA.Y2));
TERN_(Z2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Z_STR, homing_current_mA.Z2));
SERIAL_EOL();
#endif
#if Z3_SENSORLESS
say_M920(forReplay, 2);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z3);
#endif
#if Z4_SENSORLESS
say_M920(forReplay, 3);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z4);
#endif
#if X2_SENSORLESS || Y2_SENSORLESS || Z2_SENSORLESS
say_M920(forReplay, 1);
TERN_(X2_SENSORLESS, SERIAL_ECHOPGM_P(SP_X_STR, homing_current_mA.X2));
TERN_(Y2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Y_STR, homing_current_mA.Y2));
TERN_(Z2_SENSORLESS, SERIAL_ECHOPGM_P(SP_Z_STR, homing_current_mA.Z2));
SERIAL_EOL();
#endif
#if Z3_SENSORLESS
say_M920(forReplay, 2);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z3);
#endif
#if Z4_SENSORLESS
say_M920(forReplay, 3);
SERIAL_ECHOLNPGM(" Z", homing_current_mA.Z4);
#endif
#endif // X_SENSORLESS || Y_SENSORLESS || Z_SENSORLESS
}
#endif // EDITABLE_HOMING_CURRENT

View file

@ -312,7 +312,7 @@ void menu_move() {
#include "../../module/ft_motion.h"
FSTR_P get_shaper_name(const AxisEnum axis=X_AXIS) {
FSTR_P get_shaper_name(const AxisEnum axis) {
switch (ftMotion.cfg.shaper[axis]) {
default: return nullptr;
case ftMotionShaper_NONE: return GET_TEXT_F(MSG_LCD_OFF);
@ -457,20 +457,20 @@ void menu_move() {
#if HAS_X_AXIS
SUBMENU_N_S(X_AXIS, _shaper_name(X_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_x);
if (AXIS_HAS_SHAPER(X)) {
if (AXIS_IS_SHAPING(X)) {
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq.x, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_ZETA_N, &c.zeta.x, 0.0f, 1.0f, ftMotion.update_shaping_params);
if (AXIS_HAS_EISHAPER(X))
if (AXIS_IS_EISHAPING(X))
EDIT_ITEM_FAST_N(float42_52, X_AXIS, MSG_FTM_VTOL_N, &c.vtol.x, 0.0f, 1.0f, ftMotion.update_shaping_params);
}
#endif
#if HAS_Y_AXIS
SUBMENU_N_S(Y_AXIS, _shaper_name(Y_AXIS), MSG_FTM_CMPN_MODE, menu_ftm_shaper_y);
if (AXIS_HAS_SHAPER(Y)) {
if (AXIS_IS_SHAPING(Y)) {
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_BASE_FREQ_N, &c.baseFreq.y, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2, ftMotion.update_shaping_params);
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_ZETA_N, &c.zeta.y, 0.0f, 1.0f, ftMotion.update_shaping_params);
if (AXIS_HAS_EISHAPER(Y))
if (AXIS_IS_EISHAPING(Y))
EDIT_ITEM_FAST_N(float42_52, Y_AXIS, MSG_FTM_VTOL_N, &c.vtol.y, 0.0f, 1.0f, ftMotion.update_shaping_params);
}
#endif

View file

@ -269,11 +269,8 @@ void FTMotion::loop() {
Ai[2] = Ai[0] * K2;
const float adj = 1.0f / (Ai[0] + Ai[1] + Ai[2]);
for (uint32_t i = 0U; i < 3U; i++) {
Ai[i] *= adj;
}
}
break;
for (uint32_t i = 0; i < 3U; i++) Ai[i] *= adj;
} break;
case ftMotionShaper_2HEI: {
max_i = 3U;
@ -285,11 +282,8 @@ void FTMotion::loop() {
Ai[3] = Ai[0] * K3;
const float adj = 1.0f / (Ai[0] + Ai[1] + Ai[2] + Ai[3]);
for (uint32_t i = 0U; i < 4U; i++) {
Ai[i] *= adj;
}
}
break;
for (uint32_t i = 0; i < 4U; i++) Ai[i] *= adj;
} break;
case ftMotionShaper_3HEI: {
max_i = 4U;
@ -300,11 +294,8 @@ void FTMotion::loop() {
Ai[4] = Ai[0] * K4;
const float adj = 1.0f / (Ai[0] + Ai[1] + Ai[2] + Ai[3] + Ai[4]);
for (uint32_t i = 0U; i < 5U; i++) {
Ai[i] *= adj;
}
}
break;
for (uint32_t i = 0; i < 5U; i++) Ai[i] *= adj;
} break;
case ftMotionShaper_MZV: {
max_i = 2U;
@ -358,13 +349,13 @@ void FTMotion::loop() {
void FTMotion::update_shaping_params() {
#if HAS_X_AXIS
if ((shaping.x.ena = AXIS_HAS_SHAPER(X))) {
if ((shaping.x.ena = AXIS_IS_SHAPING(X))) {
shaping.x.set_axis_shaping_A(cfg.shaper.x, cfg.zeta.x, cfg.vtol.x);
shaping.x.set_axis_shaping_N(cfg.shaper.x, cfg.baseFreq.x, cfg.zeta.x);
}
#endif
#if HAS_Y_AXIS
if ((shaping.y.ena = AXIS_HAS_SHAPER(Y))) {
if ((shaping.y.ena = AXIS_IS_SHAPING(Y))) {
shaping.y.set_axis_shaping_A(cfg.shaper.y, cfg.zeta.y, cfg.vtol.y);
shaping.y.set_axis_shaping_N(cfg.shaper.y, cfg.baseFreq.y, cfg.zeta.y);
}
@ -663,6 +654,7 @@ void FTMotion::generateTrajectoryPointsFromBlock() {
}
#endif
if (++shaping.zi_idx == (FTM_ZMAX)) shaping.zi_idx = 0;
#endif // HAS_FTM_SHAPING
// Filled up the queue with regular and shaped steps

View file

@ -56,12 +56,14 @@ typedef struct FTConfig {
#else
static constexpr dynFreqMode_t dynFreqMode = dynFreqMode_DISABLED;
#endif
#endif // HAS_FTM_SHAPING
#if HAS_EXTRUDERS
bool linearAdvEna = FTM_LINEAR_ADV_DEFAULT_ENA; // Linear advance enable configuration.
float linearAdvK = FTM_LINEAR_ADV_DEFAULT_K; // Linear advance gain.
#endif
} ft_config_t;
class FTMotion {

View file

@ -41,8 +41,8 @@ enum dynFreqMode_t : uint8_t {
dynFreqMode_MASS_BASED = 2
};
#define AXIS_HAS_SHAPER(A) (ftMotion.cfg.shaper[_AXIS(A)] != ftMotionShaper_NONE)
#define AXIS_HAS_EISHAPER(A) WITHIN(ftMotion.cfg.shaper[_AXIS(A)], ftMotionShaper_EI, ftMotionShaper_3HEI)
#define AXIS_IS_SHAPING(A) (ftMotion.cfg.shaper[_AXIS(A)] != ftMotionShaper_NONE)
#define AXIS_IS_EISHAPING(A) WITHIN(ftMotion.cfg.shaper[_AXIS(A)], ftMotionShaper_EI, ftMotionShaper_3HEI)
typedef struct XYZEarray<float, FTM_WINDOW_SIZE> xyze_trajectory_t;
typedef struct XYZEarray<float, FTM_BATCH_SIZE> xyze_trajectoryMod_t;

View file

@ -3007,7 +3007,7 @@ hal_timer_t Stepper::block_phase_isr() {
static int32_t smoothed_vals[SMOOTH_LIN_ADV_EXP_ORDER] = {0};
for (uint8_t i = 0; i < SMOOTH_LIN_ADV_EXP_ORDER; i++) {
// Approximate gaussian smoothing via higher order exponential smoothing
// Approximate Gaussian smoothing via higher order exponential smoothing
smoothed_vals[i] += MULT_Q(30, la_step_rate - smoothed_vals[i], extruder_advance_alpha_q30[E_INDEX_N(active_extruder)]);
la_step_rate = smoothed_vals[i];
}