Skip to content

Commit

Permalink
🚀 ZV Input Shaping (MarlinFirmware#24797)
Browse files Browse the repository at this point in the history
  • Loading branch information
tombrazier authored and thinkyhead committed Dec 16, 2022
1 parent 5765449 commit c1f0f26
Show file tree
Hide file tree
Showing 15 changed files with 657 additions and 42 deletions.
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."
#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

0 comments on commit c1f0f26

Please sign in to comment.