Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Input shaping #24797

Merged
merged 11 commits into from
Oct 21, 2022
29 changes: 29 additions & 0 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,35 @@

// @section motion

/**
* Input Shaping -- EXPERIMENTAL
*
* Zero Vibration (ZV) Input Shaping for X and/or Y movements.
*
* This option uses a lot of SRAM for the step buffer, which is proportional
* to the largest step rate possible for any axis. If the build fails due to
* low SRAM the buffer size may be reduced by setting smaller values for
* DEFAULT_AXIS_STEPS_PER_UNIT and/or DEFAULT_MAX_FEEDRATE. Runtime editing
* of max feedrate (M203) or resonant frequency (M593) may result feedrate
* being capped to prevent buffer overruns.
*
* Tune with M593 D<factor> F<frequency>:
*
* D<factor> Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
* F<frequency> Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
* T[map] Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
* X<1> Set the given parameters only for the X axis.
* Y<1> Set the given parameters only for the Y axis.
*/
//#define INPUT_SHAPING
#if ENABLED(INPUT_SHAPING)
#define SHAPING_FREQ_X 40 // (Hz) The dominant resonant frequency of the X axis.
#define SHAPING_FREQ_Y 40 // (Hz) The dominant resonant frequency of the Y axis.
#define SHAPING_ZETA_X 0.3f // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping).
#define SHAPING_ZETA_Y 0.3f // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping).
//#define SHAPING_MENU // Add a menu to the LCD to set shaping parameters.
#endif

#define AXIS_RELATIVE_MODES { false, false, false, false }

// Add a Duplicate option for well-separated conjoined nozzles
Expand Down
83 changes: 83 additions & 0 deletions Marlin/src/gcode/feature/input_shaping/M593.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2022 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/>.
*
*/

#include "../../../inc/MarlinConfig.h"

#if ENABLED(INPUT_SHAPING)

#include "../../gcode.h"
#include "../../../module/stepper.h"

void GcodeSuite::M593_report(const bool forReplay/*=true*/) {
report_heading_etc(forReplay, F("Input Shaping"));
#if HAS_SHAPING_X
SERIAL_ECHO_MSG("M593 X"
" F", stepper.get_shaping_frequency(X_AXIS),
" D", stepper.get_shaping_damping_ratio(X_AXIS)
);
#endif
#if HAS_SHAPING_Y
SERIAL_ECHO_MSG("M593 Y"
" F", stepper.get_shaping_frequency(Y_AXIS),
" D", stepper.get_shaping_damping_ratio(Y_AXIS)
);
#endif
}

/**
* M593: Get or Set Input Shaping Parameters
* D<factor> Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes.
* F<frequency> Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes.
* T[map] Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet)
* X<1> Set the given parameters only for the X axis.
* Y<1> Set the given parameters only for the Y axis.
*/
void GcodeSuite::M593() {
if (!parser.seen_any()) return M593_report();

const bool seen_X = TERN0(HAS_SHAPING_X, parser.seen_test('X')),
seen_Y = TERN0(HAS_SHAPING_Y, parser.seen_test('Y')),
for_X = seen_X || TERN0(HAS_SHAPING_X, (!seen_X && !seen_Y)),
for_Y = seen_Y || TERN0(HAS_SHAPING_Y, (!seen_X && !seen_Y));

if (parser.seen('D')) {
const float zeta = parser.value_float();
if (WITHIN(zeta, 0, 1)) {
if (for_X) stepper.set_shaping_damping_ratio(X_AXIS, zeta);
if (for_Y) stepper.set_shaping_damping_ratio(Y_AXIS, zeta);
}
else
SERIAL_ECHO_MSG("?Zeta (D) value out of range (0-1)");
}

if (parser.seen('F')) {
const float freq = parser.value_float();
if (freq > 0) {
if (for_X) stepper.set_shaping_frequency(X_AXIS, freq);
if (for_Y) stepper.set_shaping_frequency(Y_AXIS, freq);
}
else
SERIAL_ECHO_MSG("?Frequency (F) must be greater than 0");
}
}

#endif
4 changes: 4 additions & 0 deletions Marlin/src/gcode/gcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 575: M575(); break; // M575: Set serial baudrate
#endif

#if ENABLED(INPUT_SHAPING)
case 593: M593(); break; // M593: Set Input Shaping parameters
#endif

#if ENABLED(ADVANCED_PAUSE_FEATURE)
case 600: M600(); break; // M600: Pause for Filament Change
case 603: M603(); break; // M603: Configure Filament Change
Expand Down
6 changes: 6 additions & 0 deletions Marlin/src/gcode/gcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@
* M554 - Get or set IP gateway. (Requires enabled Ethernet port)
* M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160)
* M575 - Change the serial baud rate. (Requires BAUD_RATE_GCODE)
* M593 - Get or set input shaping parameters. (Requires INPUT_SHAPING)
* M600 - Pause for filament change: "M600 X<pos> Y<pos> Z<raise> E<first_retract> L<later_retract>". (Requires ADVANCED_PAUSE_FEATURE)
* M603 - Configure filament change: "M603 T<tool> U<unload_length> L<load_length>". (Requires ADVANCED_PAUSE_FEATURE)
* M605 - Set Dual X-Carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
Expand Down Expand Up @@ -1080,6 +1081,11 @@ class GcodeSuite {
static void M575();
#endif

#if ENABLED(INPUT_SHAPING)
static void M593();
static void M593_report(const bool forReplay=true);
#endif

#if ENABLED(ADVANCED_PAUSE_FEATURE)
static void M600();
static void M603();
Expand Down
14 changes: 14 additions & 0 deletions Marlin/src/inc/Conditionals_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1085,3 +1085,17 @@
#else
#define CALC_FAN_SPEED(f) (f ? map(f, 1, 255, FAN_MIN_PWM, FAN_MAX_PWM) : FAN_OFF_PWM)
#endif

// Input shaping
#if ENABLED(INPUT_SHAPING)
#if !HAS_Y_AXIS
#undef SHAPING_FREQ_Y
#undef SHAPING_BUFFER_Y
#endif
#ifdef SHAPING_FREQ_X
#define HAS_SHAPING_X 1
#endif
#ifdef SHAPING_FREQ_Y
#define HAS_SHAPING_Y 1
#endif
#endif
35 changes: 30 additions & 5 deletions Marlin/src/inc/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -4238,11 +4238,6 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");
#endif
#endif

// Misc. Cleanup
#undef _TEST_PWM
#undef _NUM_AXES_STR
#undef _LOGICAL_AXES_STR

// JTAG support in the HAL
#if ENABLED(DISABLE_DEBUG) && !defined(JTAGSWD_DISABLE)
#error "DISABLE_DEBUG is not supported for the selected MCU/Board."
Expand All @@ -4254,3 +4249,33 @@ static_assert(_PLUS_TEST(4), "HOMING_FEEDRATE_MM_M values must be positive.");
#if ENABLED(XFER_BUILD) && !BOTH(BINARY_FILE_TRANSFER, CUSTOM_FIRMWARE_UPLOAD)
#error "BINARY_FILE_TRANSFER and CUSTOM_FIRMWARE_UPLOAD are required for custom upload."
#endif

// Check requirements for Input Shaping
#if ENABLED(INPUT_SHAPING) && defined(__AVR__)
#if HAS_SHAPING_X
#if F_CPU > 16000000
static_assert((SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (20) for AVR 20MHz.");
#else
static_assert((SHAPING_FREQ_X) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_X is below the minimum (16) for AVR 16MHz.");
#endif
#elif HAS_SHAPING_Y
#if F_CPU > 16000000
static_assert((SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (20) for AVR 20MHz.");
#else
static_assert((SHAPING_FREQ_Y) * 2 * 0x10000 >= (STEPPER_TIMER_RATE), "SHAPING_FREQ_Y is below the minimum (16) for AVR 16MHz.");
#endif
#endif
#endif

#if ENABLED(INPUT_SHAPING)
#if ENABLED(DIRECT_STEPPING)
#error "INPUT_SHAPING cannot currently be used with DIRECT_STEPPING."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically these should be able to coexist, with either the direct stepping blocks being shaped or by having the direct stepping blocks skip the shaper altogether.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. They may even work okay as is. I think direct stepping would probably just not be affected by input shaping. But I don't have the capacity to check that out right now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that Klipper's direct stepping protocol sends basic segments but the shaping happens on the printer's mainboard, so it makes sense to also do that for Direct Stepping. But maybe Klipper does some pre-calculation for the benefit of shaping before it sends those segments over.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we use the same block protocol as Klipper? Or is there a front end out there somewhere for feeding Marlin specifically?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct Stepping G6 specifically requires https://github.com/colinrgodsey/step-daemon

#elif ENABLED(LASER_FEATURE)
#error "INPUT_SHAPING cannot currently be used with LASER_FEATURE."
#endif
#endif

// Misc. Cleanup
#undef _TEST_PWM
#undef _NUM_AXES_STR
#undef _LOGICAL_AXES_STR
5 changes: 5 additions & 0 deletions Marlin/src/lcd/language/language_en.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ namespace Language_en {
LSTR MSG_AMAX_EN = _UxGT("Max * Accel");
LSTR MSG_A_RETRACT = _UxGT("Retract Accel");
LSTR MSG_A_TRAVEL = _UxGT("Travel Accel");
LSTR MSG_INPUT_SHAPING = _UxGT("Input Shaping");
LSTR MSG_SHAPING_X_FREQ = STR_X _UxGT(" frequency");
LSTR MSG_SHAPING_Y_FREQ = STR_Y _UxGT(" frequency");
LSTR MSG_SHAPING_X_ZETA = STR_X _UxGT(" damping");
LSTR MSG_SHAPING_Y_ZETA = STR_Y _UxGT(" damping");
LSTR MSG_XY_FREQUENCY_LIMIT = _UxGT("XY Freq Limit");
LSTR MSG_XY_FREQUENCY_FEEDRATE = _UxGT("Min FR Factor");
LSTR MSG_STEPS_PER_MM = _UxGT("Steps/mm");
Expand Down
41 changes: 39 additions & 2 deletions Marlin/src/lcd/menu/menu_advanced.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "menu_item.h"
#include "../../MarlinCore.h"
#include "../../module/planner.h"
#include "../../module/stepper.h"

#if DISABLED(NO_VOLUMETRICS)
#include "../../gcode/parser.h"
Expand Down Expand Up @@ -80,8 +81,6 @@ void menu_backlash();

#if HAS_MOTOR_CURRENT_PWM

#include "../../module/stepper.h"

void menu_pwm() {
START_MENU();
BACK_ITEM(MSG_ADVANCED_SETTINGS);
Expand Down Expand Up @@ -538,6 +537,39 @@ void menu_backlash();
END_MENU();
}

#if ENABLED(SHAPING_MENU)

void menu_advanced_input_shaping() {
constexpr float min_frequency = TERN(__AVR__, float(STEPPER_TIMER_RATE) / 2 / 0x10000, 1.0f);

START_MENU();
BACK_ITEM(MSG_ADVANCED_SETTINGS);

// M593 F Frequency
#if HAS_SHAPING_X
editable.decimal = stepper.get_shaping_frequency(X_AXIS);
EDIT_ITEM_FAST(float61, MSG_SHAPING_X_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(X_AXIS, editable.decimal); });
#endif
#if HAS_SHAPING_Y
editable.decimal = stepper.get_shaping_frequency(Y_AXIS);
EDIT_ITEM_FAST(float61, MSG_SHAPING_Y_FREQ, &editable.decimal, min_frequency, 200.0f, []{ stepper.set_shaping_frequency(Y_AXIS, editable.decimal); });
#endif

// M593 D Damping ratio
#if HAS_SHAPING_X
editable.decimal = stepper.get_shaping_damping_ratio(X_AXIS);
EDIT_ITEM_FAST(float42_52, MSG_SHAPING_X_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(X_AXIS, editable.decimal); });
#endif
#if HAS_SHAPING_Y
editable.decimal = stepper.get_shaping_damping_ratio(Y_AXIS);
EDIT_ITEM_FAST(float42_52, MSG_SHAPING_Y_ZETA, &editable.decimal, 0.0f, 1.0f, []{ stepper.set_shaping_damping_ratio(Y_AXIS, editable.decimal); });
#endif

END_MENU();
}

#endif

#if HAS_CLASSIC_JERK

void menu_advanced_jerk() {
Expand Down Expand Up @@ -657,6 +689,11 @@ void menu_advanced_settings() {
// M201 - Acceleration items
SUBMENU(MSG_ACCELERATION, menu_advanced_acceleration);

// M593 - Acceleration items
#if ENABLED(SHAPING_MENU)
SUBMENU(MSG_INPUT_SHAPING, menu_advanced_input_shaping);
#endif

#if HAS_CLASSIC_JERK
// M205 - Max Jerk
SUBMENU(MSG_JERK, menu_advanced_jerk);
Expand Down
8 changes: 8 additions & 0 deletions Marlin/src/module/planner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2483,6 +2483,14 @@ bool Planner::_populate_block(

#endif // XY_FREQUENCY_LIMIT

#if ENABLED(INPUT_SHAPING)
const float top_freq = _MIN(float(0x7FFFFFFFL)
OPTARG(HAS_SHAPING_X, stepper.get_shaping_frequency(X_AXIS))
OPTARG(HAS_SHAPING_Y, stepper.get_shaping_frequency(Y_AXIS))),
max_factor = (top_freq * float(shaping_dividends - 3) * 2.0f) / block->nominal_rate;
NOMORE(speed_factor, max_factor);
#endif

// Correct the speed
if (speed_factor < 1.0f) {
current_speed *= speed_factor;
Expand Down
Loading