12 Commits

Author SHA1 Message Date
FOG_Yamato
82b91c1b40 Replace max_accel_to_decel with minimum_cruise_ratio (#53) 2024-03-15 19:20:51 +01:00
Félix Boisselier
536c3c0eff improved notes on LIS2DW with a worst case example 2024-03-11 09:07:06 +00:00
Félix Boisselier
312a9c9ffa add note on Klipper version to documentation
Due to some breaking changes in the resonance testing code on the Klipper side, Shake&Tune has been modified to take advantage of this, and thus S&T v2.6+ will only support a Klipper version from Feb 17th 2024. If you are using an older version of Klipper, you must use S&T <=2.5.x
2024-02-20 15:27:09 +01:00
Félix Boisselier
f4e700a1ff fixed typo 2024-02-19 23:26:53 +01:00
Félix Boisselier
80c8da622d added proper use of damping ratio and SCV to compute shaper recommendations 2024-02-19 22:53:47 +01:00
Félix Boisselier
b42e377ac6 clarifying mounting point 2024-02-08 12:50:57 +00:00
Félix Boisselier
7cfd02a7c6 more details about LIS2DW problems 2024-02-08 09:52:31 +00:00
Félix Boisselier
9fa07a12c4 Half-Quadratic Gain method for damping ratio estimation
that should be more precise than the Half-Power method for higher damping ratio values (above 0.05)
2024-01-29 22:58:32 +01:00
shinanca
1a4fea3c8c More precise method for damping ration calculation 2024-01-25 16:00:30 +03:00
Félix Boisselier
eab10ce5da cast accel value to integer 2024-01-14 18:53:31 +01:00
Félix Boisselier
0696a60b7f add security note 2024-01-11 12:15:17 +01:00
Félix Boisselier
ac96cb2eb7 modified moonraker update management section and install 2024-01-09 16:14:45 +00:00
18 changed files with 140 additions and 55 deletions

View File

@@ -16,7 +16,7 @@ gcode:
{% set accel = [accel, printer.configfile.settings.printer.max_accel]|min %} {% set accel = [accel, printer.configfile.settings.printer.max_accel]|min %}
{% set old_accel = printer.toolhead.max_accel %} {% set old_accel = printer.toolhead.max_accel %}
{% set old_accel_to_decel = printer.toolhead.max_accel_to_decel %} {% set old_cruise_ratio = printer.toolhead.minimum_cruise_ratio %}
{% set old_sqv = printer.toolhead.square_corner_velocity %} {% set old_sqv = printer.toolhead.square_corner_velocity %}
@@ -34,7 +34,7 @@ gcode:
G90 G90
# Set the wanted acceleration values (not too high to avoid oscillation, not too low to be able to reach constant speed on each segments) # Set the wanted acceleration values (not too high to avoid oscillation, not too low to be able to reach constant speed on each segments)
SET_VELOCITY_LIMIT ACCEL={accel} ACCEL_TO_DECEL={accel} SQUARE_CORNER_VELOCITY={[(accel / 1000), 5.0]|max} SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY={[(accel / 1000), 5.0]|max}
# Going to the start position # Going to the start position
G1 Z{z_height} F{feedrate_travel / 8} G1 Z{z_height} F{feedrate_travel / 8}
@@ -52,9 +52,9 @@ gcode:
ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=axemap ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=axemap
RESPOND MSG="Analysis of the movements..." RESPOND MSG="Analysis of the movements..."
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type axesmap --accel {accel} --chip_name {accel_chip}" RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type axesmap --accel {accel|int} --chip_name {accel_chip}"
# Restore the previous acceleration values # Restore the previous acceleration values
SET_VELOCITY_LIMIT ACCEL={old_accel} ACCEL_TO_DECEL={old_accel_to_decel} SQUARE_CORNER_VELOCITY={old_sqv} SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_cruise_ratio} SQUARE_CORNER_VELOCITY={old_sqv}
RESTORE_GCODE_STATE NAME=STATE_AXESMAP_CALIBRATION RESTORE_GCODE_STATE NAME=STATE_AXESMAP_CALIBRATION

View File

@@ -10,6 +10,8 @@ gcode:
{% set max_freq = params.FREQ_END|default(133.3)|float %} {% set max_freq = params.FREQ_END|default(133.3)|float %}
{% set hz_per_sec = params.HZ_PER_SEC|default(1)|float %} {% set hz_per_sec = params.HZ_PER_SEC|default(1)|float %}
{% set axis = params.AXIS|default("all")|string|lower %} {% set axis = params.AXIS|default("all")|string|lower %}
{% set scv = params.SCV|default(None) %}
{% set max_sm = params.MAX_SMOOTHING|default(None) %}
{% set keep_results = params.KEEP_N_RESULTS|default(3)|int %} {% set keep_results = params.KEEP_N_RESULTS|default(3)|int %}
{% set keep_csv = params.KEEP_CSV|default(True) %} {% set keep_csv = params.KEEP_CSV|default(True) %}
@@ -25,13 +27,17 @@ gcode:
{ action_raise_error("AXIS selection invalid. Should be either all, x or y!") } { action_raise_error("AXIS selection invalid. Should be either all, x or y!") }
{% endif %} {% endif %}
{% if scv is none %}
{% set scv = printer.toolhead.square_corner_velocity %}
{% endif %}
{% if X %} {% if X %}
TEST_RESONANCES AXIS=X OUTPUT=raw_data NAME=x FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec} TEST_RESONANCES AXIS=X OUTPUT=raw_data NAME=x FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec}
M400 M400
RESPOND MSG="X axis frequency profile generation..." RESPOND MSG="X axis frequency profile generation..."
RESPOND MSG="This may take some time (1-3min)" RESPOND MSG="This may take some time (1-3min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type shaper {% if keep_csv %}--keep_csv{% endif %}" RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type shaper --scv {scv} {% if max_sm is not none %}--max_smoothing {max_sm}{% endif %} {% if keep_csv %}--keep_csv{% endif %}"
{% endif %} {% endif %}
{% if Y %} {% if Y %}
@@ -40,7 +46,7 @@ gcode:
RESPOND MSG="Y axis frequency profile generation..." RESPOND MSG="Y axis frequency profile generation..."
RESPOND MSG="This may take some time (1-3min)" RESPOND MSG="This may take some time (1-3min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type shaper {% if keep_csv %}--keep_csv{% endif %}" RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type shaper --scv {scv} {% if max_sm is not none %}--max_smoothing {max_sm}{% endif %} {% if keep_csv %}--keep_csv{% endif %}"
{% endif %} {% endif %}
M400 M400

View File

@@ -25,7 +25,7 @@ gcode:
{% set accel = [accel, printer.configfile.settings.printer.max_accel]|min %} {% set accel = [accel, printer.configfile.settings.printer.max_accel]|min %}
{% set old_accel = printer.toolhead.max_accel %} {% set old_accel = printer.toolhead.max_accel %}
{% set old_accel_to_decel = printer.toolhead.max_accel_to_decel %} {% set old_cruise_ratio = printer.toolhead.minimum_cruise_ratio %}
{% set old_sqv = printer.toolhead.square_corner_velocity %} {% set old_sqv = printer.toolhead.square_corner_velocity %}
{% set direction_factor = { {% set direction_factor = {
@@ -129,7 +129,7 @@ gcode:
G90 G90
# Set the wanted acceleration values (not too high to avoid oscillation, not too low to be able to reach constant speed on each segments) # Set the wanted acceleration values (not too high to avoid oscillation, not too low to be able to reach constant speed on each segments)
SET_VELOCITY_LIMIT ACCEL={accel} ACCEL_TO_DECEL={accel} SQUARE_CORNER_VELOCITY={[(accel / 1000), 5.0]|max} SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY={[(accel / 1000), 5.0]|max}
# Going to the start position # Going to the start position
G1 Z{z_height} F{feedrate_travel / 10} G1 Z{z_height} F{feedrate_travel / 10}
@@ -156,11 +156,11 @@ gcode:
RESPOND MSG="Machine and motors vibration graph generation..." RESPOND MSG="Machine and motors vibration graph generation..."
RESPOND MSG="This may take some time (3-5min)" RESPOND MSG="This may take some time (3-5min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type vibrations --axis_name {direction} --accel {accel} --chip_name {accel_chip} {% if keep_csv %}--keep_csv{% endif %}" RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type vibrations --axis_name {direction} --accel {accel|int} --chip_name {accel_chip} {% if keep_csv %}--keep_csv{% endif %}"
M400 M400
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}" RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}"
# Restore the previous acceleration values # Restore the previous acceleration values
SET_VELOCITY_LIMIT ACCEL={old_accel} ACCEL_TO_DECEL={old_accel_to_decel} SQUARE_CORNER_VELOCITY={old_sqv} SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_cruise_ratio} SQUARE_CORNER_VELOCITY={old_sqv}
RESTORE_GCODE_STATE NAME=STATE_VIBRATIONS_CALIBRATION RESTORE_GCODE_STATE NAME=STATE_VIBRATIONS_CALIBRATION

View File

@@ -84,7 +84,10 @@ def compute_mechanical_parameters(psd, freqs):
freq_above_half_power = freqs[idx_above - 1] + (half_power - psd[idx_above - 1]) * (freqs[idx_above] - freqs[idx_above - 1]) / (psd[idx_above] - psd[idx_above - 1]) freq_above_half_power = freqs[idx_above - 1] + (half_power - psd[idx_above - 1]) * (freqs[idx_above] - freqs[idx_above - 1]) / (psd[idx_above] - psd[idx_above - 1])
bandwidth = freq_above_half_power - freq_below_half_power bandwidth = freq_above_half_power - freq_below_half_power
zeta = bandwidth / (2 * fr) bw1 = math.pow(bandwidth/fr,2)
bw2 = math.pow(bandwidth/fr,4)
zeta = math.sqrt(0.5-math.sqrt(1/(4+4*bw1-bw2)))
return fr, zeta, max_power_index return fr, zeta, max_power_index

View File

@@ -42,17 +42,22 @@ KLIPPAIN_COLORS = {
# Computation # Computation
###################################################################### ######################################################################
# Find the best shaper parameters using Klipper's official algorithm selection # Find the best shaper parameters using Klipper's official algorithm selection with
def calibrate_shaper(datas, max_smoothing): # a proper precomputed damping ratio (zeta) and using the configured printer SQV value
def calibrate_shaper(datas, max_smoothing, scv, max_freq):
helper = shaper_calibrate.ShaperCalibrate(printer=None) helper = shaper_calibrate.ShaperCalibrate(printer=None)
calibration_data = helper.process_accelerometer_data(datas) calibration_data = helper.process_accelerometer_data(datas)
calibration_data.normalize_to_frequencies() calibration_data.normalize_to_frequencies()
shaper, all_shapers = helper.find_best_shaper(calibration_data, max_smoothing, print_with_c_locale)
fr, zeta, _ = compute_mechanical_parameters(calibration_data.psd_sum, calibration_data.freq_bins) fr, zeta, _ = compute_mechanical_parameters(calibration_data.psd_sum, calibration_data.freq_bins)
print_with_c_locale("Recommended shaper is %s @ %.1f Hz" % (shaper.name, shaper.freq)) shaper, all_shapers = helper.find_best_shaper(
print_with_c_locale("Axis has a main resonant frequency at %.1fHz with an estimated damping ratio of %.3f" % (fr, zeta)) calibration_data, shapers=None, damping_ratio=zeta,
scv=scv, shaper_freqs=None, max_smoothing=max_smoothing,
test_damping_ratios=None, max_freq=max_freq,
logger=print_with_c_locale)
print_with_c_locale("\n-> Recommended shaper is %s @ %.1f Hz (when using a square corner velocity of %.1f and a computed damping ratio of %.3f)" % (shaper.name.upper(), shaper.freq, scv, zeta))
return shaper.name, all_shapers, calibration_data, fr, zeta return shaper.name, all_shapers, calibration_data, fr, zeta
@@ -198,7 +203,7 @@ def plot_spectrogram(ax, t, bins, pdata, peaks, max_freq):
# Startup and main routines # Startup and main routines
###################################################################### ######################################################################
def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max_freq=200.): def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, scv=5. , max_freq=200.):
set_locale() set_locale()
global shaper_calibrate global shaper_calibrate
shaper_calibrate = setup_klipper_import(klipperdir) shaper_calibrate = setup_klipper_import(klipperdir)
@@ -209,7 +214,7 @@ def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max
print_with_c_locale("Warning: incorrect number of .csv files detected. Only the first one will be used!") print_with_c_locale("Warning: incorrect number of .csv files detected. Only the first one will be used!")
# Compute shapers, PSD outputs and spectrogram # Compute shapers, PSD outputs and spectrogram
performance_shaper, shapers, calibration_data, fr, zeta = calibrate_shaper(datas[0], max_smoothing) performance_shaper, shapers, calibration_data, fr, zeta = calibrate_shaper(datas[0], max_smoothing, scv, max_freq)
pdata, bins, t = compute_spectrogram(datas[0]) pdata, bins, t = compute_spectrogram(datas[0])
del datas del datas
@@ -231,7 +236,7 @@ def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max
# Print the peaks info in the console # Print the peaks info in the console
peak_freqs_formated = ["{:.1f}".format(f) for f in peaks_freqs] peak_freqs_formated = ["{:.1f}".format(f) for f in peaks_freqs]
num_peaks_above_effect_threshold = np.sum(calibration_data.psd_sum[peaks] > peaks_threshold[1]) num_peaks_above_effect_threshold = np.sum(calibration_data.psd_sum[peaks] > peaks_threshold[1])
print_with_c_locale("Peaks detected on the graph: %d @ %s Hz (%d above effect threshold)" % (num_peaks, ", ".join(map(str, peak_freqs_formated)), num_peaks_above_effect_threshold)) print_with_c_locale("\nPeaks detected on the graph: %d @ %s Hz (%d above effect threshold)" % (num_peaks, ", ".join(map(str, peak_freqs_formated)), num_peaks_above_effect_threshold))
# Create graph layout # Create graph layout
fig, (ax1, ax2) = plt.subplots(2, 1, gridspec_kw={ fig, (ax1, ax2) = plt.subplots(2, 1, gridspec_kw={
@@ -245,17 +250,23 @@ def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max
}) })
fig.set_size_inches(8.3, 11.6) fig.set_size_inches(8.3, 11.6)
# Add title # Add a title with some test info
title_line1 = "INPUT SHAPER CALIBRATION TOOL" title_line1 = "INPUT SHAPER CALIBRATION TOOL"
fig.text(0.12, 0.965, title_line1, ha='left', va='bottom', fontsize=20, color=KLIPPAIN_COLORS['purple'], weight='bold') fig.text(0.12, 0.965, title_line1, ha='left', va='bottom', fontsize=20, color=KLIPPAIN_COLORS['purple'], weight='bold')
try: try:
filename_parts = (lognames[0].split('/')[-1]).split('_') filename_parts = (lognames[0].split('/')[-1]).split('_')
dt = datetime.strptime(f"{filename_parts[1]} {filename_parts[2]}", "%Y%m%d %H%M%S") dt = datetime.strptime(f"{filename_parts[1]} {filename_parts[2]}", "%Y%m%d %H%M%S")
title_line2 = dt.strftime('%x %X') + ' -- ' + filename_parts[3].upper().split('.')[0] + ' axis' title_line2 = dt.strftime('%x %X') + ' -- ' + filename_parts[3].upper().split('.')[0] + ' axis'
title_line3 = '| Square corner velocity: ' + str(scv) + 'mm/s'
title_line4 = '| Max allowed smoothing: ' + str(max_smoothing)
except: except:
print_with_c_locale("Warning: CSV filename look to be different than expected (%s)" % (lognames[0])) print_with_c_locale("Warning: CSV filename look to be different than expected (%s)" % (lognames[0]))
title_line2 = lognames[0].split('/')[-1] title_line2 = lognames[0].split('/')[-1]
title_line3 = ''
title_line4 = ''
fig.text(0.12, 0.957, title_line2, ha='left', va='top', fontsize=16, color=KLIPPAIN_COLORS['dark_purple']) fig.text(0.12, 0.957, title_line2, ha='left', va='top', fontsize=16, color=KLIPPAIN_COLORS['dark_purple'])
fig.text(0.58, 0.960, title_line3, ha='left', va='top', fontsize=10, color=KLIPPAIN_COLORS['dark_purple'])
fig.text(0.58, 0.946, title_line4, ha='left', va='top', fontsize=10, color=KLIPPAIN_COLORS['dark_purple'])
# Plot the graphs # Plot the graphs
plot_freq_response(ax1, calibration_data, shapers, performance_shaper, peaks, peaks_freqs, peaks_threshold, fr, zeta, max_freq) plot_freq_response(ax1, calibration_data, shapers, performance_shaper, peaks, peaks_freqs, peaks_threshold, fr, zeta, max_freq)
@@ -284,6 +295,8 @@ def main():
help="maximum frequency to graph") help="maximum frequency to graph")
opts.add_option("-s", "--max_smoothing", type="float", default=None, opts.add_option("-s", "--max_smoothing", type="float", default=None,
help="maximum shaper smoothing to allow") help="maximum shaper smoothing to allow")
opts.add_option("--scv", "--square_corner_velocity", type="float",
dest="scv", default=5., help="square corner velocity")
opts.add_option("-k", "--klipper_dir", type="string", dest="klipperdir", opts.add_option("-k", "--klipper_dir", type="string", dest="klipperdir",
default="~/klipper", help="main klipper directory") default="~/klipper", help="main klipper directory")
options, args = opts.parse_args() options, args = opts.parse_args()
@@ -294,7 +307,7 @@ def main():
if options.max_smoothing is not None and options.max_smoothing < 0.05: if options.max_smoothing is not None and options.max_smoothing < 0.05:
opts.error("Too small max_smoothing specified (must be at least 0.05)") opts.error("Too small max_smoothing specified (must be at least 0.05)")
fig = shaper_calibration(args, options.klipperdir, options.max_smoothing, options.max_freq) fig = shaper_calibration(args, options.klipperdir, options.max_smoothing, options.scv, options.max_freq)
fig.savefig(options.output, dpi=150) fig.savefig(options.output, dpi=150)

View File

@@ -93,7 +93,7 @@ def create_belts_graph(keep_csv):
return return
def create_shaper_graph(keep_csv): def create_shaper_graph(keep_csv, max_smoothing, scv):
current_date = datetime.now().strftime('%Y%m%d_%H%M%S') current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
# Get all the files and sort them based on last modified time to select the most recent one # Get all the files and sort them based on last modified time to select the most recent one
@@ -120,7 +120,7 @@ def create_shaper_graph(keep_csv):
time.sleep(2) time.sleep(2)
# Generate the shaper graph and its name # Generate the shaper graph and its name
fig = shaper_calibration([new_file], KLIPPER_FOLDER) fig = shaper_calibration([new_file], KLIPPER_FOLDER, max_smoothing=max_smoothing, scv=scv)
png_filename = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[1], f'resonances_{current_date}_{axis}.png') png_filename = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[1], f'resonances_{current_date}_{axis}.png')
fig.savefig(png_filename, dpi=150) fig.savefig(png_filename, dpi=150)
@@ -263,6 +263,10 @@ def main():
help="number of results to keep in the result folder after each run of the script") help="number of results to keep in the result folder after each run of the script")
opts.add_option("-c", "--keep_csv", action="store_true", default=False, dest="keep_csv", opts.add_option("-c", "--keep_csv", action="store_true", default=False, dest="keep_csv",
help="weither or not to keep the CSV files alongside the PNG graphs image results") help="weither or not to keep the CSV files alongside the PNG graphs image results")
opts.add_option("--scv", "--square_corner_velocity", type="float", dest="scv", default=5.,
help="square corner velocity used to compute max accel for axis shapers graphs")
opts.add_option("--max_smoothing", type="float", dest="max_smoothing", default=None,
help="maximum shaper smoothing to allow")
options, args = opts.parse_args() options, args = opts.parse_args()
if options.type is None: if options.type is None:
@@ -282,7 +286,7 @@ def main():
create_belts_graph(keep_csv=options.keep_csv) create_belts_graph(keep_csv=options.keep_csv)
print(f"Belt graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[0]}") print(f"Belt graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[0]}")
elif graph_mode.lower() == 'shaper': elif graph_mode.lower() == 'shaper':
axis = create_shaper_graph(keep_csv=options.keep_csv) axis = create_shaper_graph(keep_csv=options.keep_csv, max_smoothing=options.max_smoothing, scv=options.scv)
print(f"{axis} input shaper graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[1]}") print(f"{axis} input shaper graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[1]}")
elif graph_mode.lower() == 'vibrations': elif graph_mode.lower() == 'vibrations':
create_vibrations_graph(axis_name=options.axis_name, accel=options.accel_used, chip_name=options.chip_name, keep_csv=options.keep_csv) create_vibrations_graph(axis_name=options.axis_name, accel=options.accel_used, chip_name=options.chip_name, keep_csv=options.keep_csv)

View File

@@ -17,6 +17,10 @@ Check out the **[detailed documentation of the Shake&Tune module here](./docs/RE
|:----------------:|:------------:|:---------------------:| |:----------------:|:------------:|:---------------------:|
| [<img src="./docs/images/belts_example.png">](./docs/macros/belts_tuning.md) | [<img src="./docs/images/axis_example.png">](./docs/macros/axis_tuning.md) | [<img src="./docs/images/vibrations_example.png">](./docs/macros/vibrations_tuning.md) | | [<img src="./docs/images/belts_example.png">](./docs/macros/belts_tuning.md) | [<img src="./docs/images/axis_example.png">](./docs/macros/axis_tuning.md) | [<img src="./docs/images/vibrations_example.png">](./docs/macros/vibrations_tuning.md) |
> **Note**:
>
> Be aware that Shake&Tune uses the [Gcode shell command plugin](https://github.com/dw-0/kiauh/blob/master/docs/gcode_shell_command.md) under the hood to call the Python scripts that generate the graphs. While my scripts should be safe, the Gcode shell command plugin also has great potential for abuse if not used carefully for other purposes, since it opens shell access from Klipper.
## Installation ## Installation
Follow these steps to install the Shake&Tune module in your printer: Follow these steps to install the Shake&Tune module in your printer:
@@ -29,18 +33,10 @@ Follow these steps to install the Shake&Tune module in your printer:
``` ```
[include K-ShakeTune/*.cfg] [include K-ShakeTune/*.cfg]
``` ```
1. Finally, if you want to get automatic updates, add the following to your `moonraker.cfg` file:
```
[update_manager Klippain-ShakeTune]
type: git_repo
path: ~/klippain_shaketune
channel: beta
origin: https://github.com/Frix-x/klippain-shaketune.git
primary_branch: main
managed_services: klipper
install_script: install.sh
```
> **Note**:
>
> Due to some breaking changes in the resonance testing code on the Klipper side, Shake&Tune has been modified to take advantage of this, and thus S&T v2.6+ will only support a Klipper version from Feb 17th 2024. If you are using an older version of Klipper, you must use S&T <=2.5.x
## Usage ## Usage

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

@@ -22,16 +22,20 @@ When tuning Input Shaper, keep the following in mind:
1. Finally, remember why you're running these tests: to get clean prints. Don't become too obsessive over perfect graphs, as the last bits of optimization will probably have the least impact on the printed parts in terms of ringing and ghosting. 1. Finally, remember why you're running these tests: to get clean prints. Don't become too obsessive over perfect graphs, as the last bits of optimization will probably have the least impact on the printed parts in terms of ringing and ghosting.
### Special note on accelerometer mounting point ### Note on accelerometer mounting point
Input Shaping algorithms work by suppressing a single resonant frequency (or a range around a single resonant frequency). When setting the filter, **the primary goal is to target the resonant frequency of the toolhead and belts system** (see the [theory behind it](#theory-behind-it)), as this has the most significant impact on print quality and is the root cause of ringing. Input Shaping algorithms are designed to mitigate resonances by targeting a specific resonant frequency or a range around it. When setting the filter, **the primary goal is to target the resonant frequency of the toolhead and belts system** (see the [theory behind it](#theory-behind-it)), as this has the most significant impact on print quality and is the root cause of ringing.
When setting up Input Shaper, it is important to consider the accelerometer mounting point. There are mainly two possibilities, each with its pros and cons: Choosing the accelerometer's mounting point is important. There are currently three mounting strategies, each offering distinct advantages:
| Directly at the nozzle tip | Near the toolhead's center of gravity | | Mounting Point | Advantages | Considerations |
| --- | --- | | --- | --- | --- |
| This method provides a more accurate and comprehensive measurement of everything in your machine. It captures the main resonant frequency along with other vibrations and movements, such as toolhead wobbling and printer frame movements. This approach is excellent for diagnosing your machine's kinematics and troubleshooting problems. However, it also leads to noisier graphs, making it harder for the algorithm to select the correct filter for input shaping. Graphs may appear worse, but this is due to the different "point of view" of the printer's behavior. | I personally recommend mounting the accelerometer in this way, as it provides a clear view of the main resonant frequency you want to target, allowing for accurate input shaper filter settings. This approach results in cleaner graphs with less visible noise from other subsystem vibrations, making interpretation easier for both automatic algorithms and users. However, this method provides less detail in the graphs and may be slightly less effective for troubleshooting printer problems. | | **Directly at the nozzle tip** | Provides a comprehensive view of all machine vibrations, including the main resonance, but also toolhead wobbling and global frame movements. Ideal for diagnosing kinematic issues and troubleshooting. | Results in noisier data, which may complicate the final Input Shaping filter selection on machines that are not perfect and/or not fully rigid. |
| **Near the toolhead's center of gravity** | Provides a view of mostly only the primary resonant frequencies of the toolhead and belts, allowing precise filter selection for Input Shaping. The data is often cleaner, with only severe mechanical issues or very problematic toolhead wobble visible on the graphs. | May provide less detail on secondary vibrations (which have a fairly minor effect on ringing) and may be less effective in diagnosing unrelated mechanical problems. |
| **Integrated accelerometer on a CANBus Board** | Simple and effective, requires no additional installation and always available. Can help for diagnosing issues like those caused by bowden tubes, umbillical coords and cable chains. If toolhead is very rigid, measurements are close enough to those of the center of gravity. | Not accurate for a detailed analysis or diagnosing mechanical issues due to distance from the nozzle tip and potential noise from attached components. |
A suggested workflow is to first use the nozzle mount to diagnose mechanical issues, such as loose screws or a bad X carriage. Once the mechanics are in good condition, switch to a mounting point closer to the toolhead's center of gravity for setting the input shaper filter settings by using cleaner graphs that highlights the most impactful frequency. While you should usually try to focus on the toolhead/belts mechanical subsystem for resonance mitigation (since it has the most impact on ringing and print quality), you don't want to overlook the importance of nozzle tip measurements for other sources of vibration. Indeed, if resonance analysis results vary a lot between mounting points, reinforcing the toolhead's rigidity to minimize wobbling and vibrations is recommended. Here is a strategy that attempts to methodically address mechanical issues and then allow for the day-to-day selection of input shaping filters as needed:
1. **Diagnosis phase**: Begin with the nozzle tip mount to identify and troubleshoot mechanical issues to ensure the printer components are healthy and the assembly is well done and optimized.
1. **Filter selection phase**: If the graphs are mostly clean, you can transition to a mounting point near the toolhead's center of gravity for cleaner data on the main resonance, facilitating accurate Input Shaping filter settings. You can also consider the CANBus integrated accelerometer for its simplicity, especially if the toolhead is particularly rigid and minimally affected by wobble.
## Theory behind it ## Theory behind it

View File

@@ -15,6 +15,8 @@ Then, call the `AXES_SHAPER_CALIBRATION` macro and look for the graphs in the re
|FREQ_END|133|Maximum excitation frequency| |FREQ_END|133|Maximum excitation frequency|
|HZ_PER_SEC|1|Number of Hz per seconds for the test| |HZ_PER_SEC|1|Number of Hz per seconds for the test|
|AXIS|"all"|Axis you want to test in the list of "all", "X" or "Y"| |AXIS|"all"|Axis you want to test in the list of "all", "X" or "Y"|
|SCV|printer square corner velocity|Square corner velocity you want to use to calculate shaper recommendations. Using higher SCV values usually results in more smoothing and lower maximum accelerations|
|MAX_SMOOTHING|None|Max smoothing allowed when calculating shaper recommendations|
|KEEP_N_RESULTS|3|Total number of results to keep in the result folder after running the test. The older results are automatically cleaned up| |KEEP_N_RESULTS|3|Total number of results to keep in the result folder after running the test. The older results are automatically cleaned up|
|KEEP_CSV|True|Weither or not to keep the CSV data file alonside the PNG graphs| |KEEP_CSV|True|Weither or not to keep the CSV data file alonside the PNG graphs|
@@ -39,13 +41,13 @@ For setting your Input Shaping filters, rely on the auto-computed values display
* `MZV` is usually the top pick for well-adjusted machines. It's a good compromise for low remaining vibrations while still allowing pretty good acceleration values. Keep in mind, `MZV` is only recommended by Klipper on good graphs. * `MZV` is usually the top pick for well-adjusted machines. It's a good compromise for low remaining vibrations while still allowing pretty good acceleration values. Keep in mind, `MZV` is only recommended by Klipper on good graphs.
* `EI` can be used as a fallback for challenging graphs. But first, try to fix your mechanical issues before using it: almost every printer should be able to run `MZV` instead. * `EI` can be used as a fallback for challenging graphs. But first, try to fix your mechanical issues before using it: almost every printer should be able to run `MZV` instead.
* `2HUMP_EI` and `3HUMP_EI` are last-resort choices. Usually, they lead to a high level of smoothing in order to suppress the ringing while also using relatively low acceleration values. If they pop up as suggestions, it's likely your machine has underlying mechanical issues (that lead to pretty bad or "wide" graphs). * `2HUMP_EI` and `3HUMP_EI` are last-resort choices. Usually, they lead to a high level of smoothing in order to suppress the ringing while also using relatively low acceleration values. If they pop up as suggestions, it's likely your machine has underlying mechanical issues (that lead to pretty bad or "wide" graphs).
- **Recommended Acceleration** (`accel<=...`): This isn't a standalone figure. It's essential to also consider the `vibr` and `sm` values as it's a compromise between the three. They will give you the percentage of remaining vibrations and the smoothing after Input Shaping, when using the recommended acceleration. Nothing will prevent you from using higher acceleration values; they are not a limit. However, when doing so, Input Shaping may not be able to suppress all the ringing on your parts. Finally, keep in mind that high acceleration values are not useful at all if there is still a high level of remaining vibrations: you should address any mechanical issues first. - **Recommended Acceleration** (`accel<=...`): This isn't a standalone figure. It's essential to also consider the `vibr` and `sm` values as it's a compromise between the three. They will give you the percentage of remaining vibrations and the smoothing after Input Shaping, when using the recommended acceleration. Nothing will prevent you from using higher acceleration values; they are not a limit. However, in this case, Input Shaping may not be able to suppress all the ringing on your parts, and more smoothing will occur. Finally, keep in mind that high acceleration values are not useful at all if there is still a high level of remaining vibrations: you should address any mechanical issues first.
- **The remaining vibrations** (`vibr`): This directly correlates with ringing. It correspond to the total value of the blue "after shaper" signal. Ideally, you want a filter with minimal or zero vibrations. - **The remaining vibrations** (`vibr`): This directly correlates with ringing. It correspond to the total value of the "after shaper" signal. Ideally, you want a filter with minimal remaining vibrations.
- **Shaper recommendations**: This script will give you some tailored recommendations based on your graphs. Pick the one that suit your needs: - **Shaper recommendations**: This script will give you some tailored recommendations based on your graphs. Pick the one that suit your needs:
* The "performance" shaper is Klipper's original suggestion that is good for high acceleration while also sometimes allowing a little bit of remaining vibrations. Use it if your goal is speed printing and you don't care much about some remaining ringing. * The "performance" shaper is Klipper's original suggestion, which is good for high acceleration, but sometimes allows a little residual vibration while minimizing smoothing. Use it if your goal is speed printing and you don't care much about some remaining ringing.
* The "low vibration" shaper aims for the lowest level of remaining vibration to ensure the best print quality with minimal ringing. This should be the best bet for most users. * The "low vibration" shaper aims for the lowest level of remaining vibration to ensure the best print quality with minimal ringing. This should be the best bet for most users.
* Sometimes, only a single recommendation called "best" shaper is presented. This means that either no suitable "low vibration" shaper was found (due to a high level of vibration or with too much smoothing) or because the "performance" shaper is also the one with the lowest vibration level. * Sometimes only a single recommendation is given as the "best" shaper. This means that either no suitable "low vibration" shaper was found (due to a high level of residual vibration or too much smoothing), or that the "performance" shaper is also the one with the lowest vibration level.
- **Damping Ratio**: Displayed at the end, this estimatation is only reliable when the graph shows a distinct, standalone and clean peak. On a well tuned machine, setting the damping ratio (instead of Klipper's 0.1 default value) can further reduce the ringing at high accelerations and with higher square corner velocities. - **Damping Ratio**: Displayed at the end, this is an estimate based on your data that is used to improve the shaper recommendations for your machine. Defining it in the `[input_shaper]` section (instead of Klipper's default value of 0.1) can further reduce ringing at high accelerations and higher square corner velocities.
Then, add to your configuration: Then, add to your configuration:
``` ```
@@ -146,17 +148,19 @@ The presence of an unbalanced or poorly running fan can be directly observed in
| --- | --- | --- | | --- | --- | --- |
| ![](../images/shaper_graphs/fan_notproblematic.png) | ![](../images/shaper_graphs/fan_maybeproblematic.png) | ![](../images/shaper_graphs/fan_problematic.png) | | ![](../images/shaper_graphs/fan_notproblematic.png) | ![](../images/shaper_graphs/fan_maybeproblematic.png) | ![](../images/shaper_graphs/fan_problematic.png) |
### Noisy accelerometer ### Spectrogram lightshow (LIS2DW)
The integration of LIS2DW as a resonance measuring device in Klipper is starting to be more and more common, particularly due to some manufacturers promoting its superiority over the established ADXL345. A critical analysis of their respective datasheets reveals a nuanced reality: the LIS2DW offers a higher sampling rate, but also it tends to exhibit increased noise levels at comparable sensitivity settings compared to the ADXL345. But, given that LIS2DW chips are also 5-10 times cheaper, it definitely makes sense for mass-producing PCBs. The integration of LIS2DW as a resonance measuring device in Klipper is becoming more and more common, especially because some manufacturers are promoting its superiority over the established ADXL345. It's indeed a new generation chip that should be better to measure traditional "accelerations". However, a detailed comparison of their datasheets and practical measurements paints a more complex picture: the LIS2DW boasts greater sensitivity, but it has a lower sampling rate and produce significant aliasing that results in a "lightshow" effect on the spectrogram, characterized by multiple spurious resonance lines parallel to the main resonance, accompanied by intersecting interference lines that distort the harmonic profile.
In our use case, this chip manifests aliasing in the spectrogram that can be seen as additional 'ghosting' resonance lines parallel to the main resonance diagonal, with some intersecting interference lines that skew across the harmonics. Fortunately, this apparent lightshow do not distort the overall shape of the top graph and both the resonant frequency and damping ratio remain accurately measured as well as the input shaping filters that are also quite similar. This only makes it more challenging to discern fine details that could be masked, and it doesn't help for diagnosing mechanical issues. While in most cases the overall shape of the upper resonance curve, including resonant frequency and damping ratio, should be close to reality with fairly similar input shaping filter recommendations, this aliasing makes it difficult to identify subtle details and complicates the diagnosis of mechanical problems. In particular, it introduces a potential misinterpretation of "[binding](#low-frequency-energy)" due to a global offset of the curve. In the worst cases (see the last example below), the aliasing is too severe and adds too much noise to the graph, making it unusable.
Finally, please note that LIS2DW are known to add a small offset all over the top graph due to this aliasing. So the curve and peaks might be a bit higher, even at very low frequencies: in this case, this is probably not [#low-frequency-energy] but just some noise and it's not a mechanical problem. > **Note**:
>
> It seems that some LIS2DW chips are better than others: in some cases aliasing is not a problem, but it can also be very problematic and lead to bad graphs, as seen in the "Extreme Aliasing" example below.
| LIS2DW measurement | ADXL345 measurement | | ADXL345 measurement | LIS2DW measurement | LIS2DW extreme aliasing |
| --- | --- | | --- | --- | --- |
| ![](../images/shaper_graphs/chipcomp_s2dw.png) | ![](../images/shaper_graphs/chipcomp_adxl.png) | | ![](../images/shaper_graphs/chipcomp_adxl.png) | ![](../images/shaper_graphs/chipcomp_s2dw.png) | ![](../images/shaper_graphs/chipcomp_s2dw_2.png) |
### Crazy graphs and miscs ### Crazy graphs and miscs

View File

@@ -60,7 +60,6 @@ The following graphs show the effect of incorrect or uneven belt tension. Rememb
| The A belt tension is slightly lower than the B belt tension. This can be quickly remedied by tightening the screw only about one-half to one full turn. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | ![](../images/belt_graphs/low_A_tension.png) | | The A belt tension is slightly lower than the B belt tension. This can be quickly remedied by tightening the screw only about one-half to one full turn. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | ![](../images/belt_graphs/low_A_tension.png) |
| B belt tension is significantly lower than the A belt. If you encounter this graph, I recommend going back to the [Voron belt tensioning documentation](https://docs.vorondesign.com/tuning/secondary_printer_tuning.html#belt-tension) for a more solid base. However, you could slightly increase the B tension and decrease the A tension, but exercise caution to avoid diverging from the recommended 110Hz base. | ![](../images/belt_graphs/low_B_tension.png) | | B belt tension is significantly lower than the A belt. If you encounter this graph, I recommend going back to the [Voron belt tensioning documentation](https://docs.vorondesign.com/tuning/secondary_printer_tuning.html#belt-tension) for a more solid base. However, you could slightly increase the B tension and decrease the A tension, but exercise caution to avoid diverging from the recommended 110Hz base. | ![](../images/belt_graphs/low_B_tension.png) |
### Belt path problem ### Belt path problem
If there's an issue within the belt path, aligning and overlaying the curve might be unachievable even with proper belt tension. Begin by verifying that each belt has **the exact same number of teeth**. Then, inspect the belt paths, bearings, any signs of wear (like belt dust), and ensure the belt aligns correctly on all bearing flanges during motion. If there's an issue within the belt path, aligning and overlaying the curve might be unachievable even with proper belt tension. Begin by verifying that each belt has **the exact same number of teeth**. Then, inspect the belt paths, bearings, any signs of wear (like belt dust), and ensure the belt aligns correctly on all bearing flanges during motion.
@@ -70,3 +69,13 @@ If there's an issue within the belt path, aligning and overlaying the curve migh
| On this chart, there are two peaks. The first pair of peaks seems nearly aligned, but the second peak appears solely on the B belt, significantly deviating from the A belt. This suggests an issue with the belt path, likely with the B belt. | ![](../images/belt_graphs/beltpath_problem1.png) | | On this chart, there are two peaks. The first pair of peaks seems nearly aligned, but the second peak appears solely on the B belt, significantly deviating from the A belt. This suggests an issue with the belt path, likely with the B belt. | ![](../images/belt_graphs/beltpath_problem1.png) |
| This chart is quite complex, displaying 3 peaks. While all the pairs seem well-aligned and tension ok, there are more than just two total peaks because `[1]` is split in two smaller peaks. This could be an issue, but it's not certain. It's recommended to generate the [Axis Input Shaper Graphs](./axis_tuning.md) to determine its impact. | ![](../images/belt_graphs/beltpath_problem2.png) | | This chart is quite complex, displaying 3 peaks. While all the pairs seem well-aligned and tension ok, there are more than just two total peaks because `[1]` is split in two smaller peaks. This could be an issue, but it's not certain. It's recommended to generate the [Axis Input Shaper Graphs](./axis_tuning.md) to determine its impact. | ![](../images/belt_graphs/beltpath_problem2.png) |
| This graph might indicate too low belt tension, but also potential binding, friction or something impeding the toolhead's smooth movement. Indeed, the signal strength is considerably low (with a peak around 300k, compared to the typical ~1M) and is primarily filled with noise. Start by going back [here](https://docs.vorondesign.com/tuning/secondary_printer_tuning.html#belt-tension) to establish a robust tension foundation. Next, produce the [Axis Input Shaper Graphs](./axis_tuning.md) to identify any binding and address the issue. | ![](../images/belt_graphs/beltpath_problem3.png) | | This graph might indicate too low belt tension, but also potential binding, friction or something impeding the toolhead's smooth movement. Indeed, the signal strength is considerably low (with a peak around 300k, compared to the typical ~1M) and is primarily filled with noise. Start by going back [here](https://docs.vorondesign.com/tuning/secondary_printer_tuning.html#belt-tension) to establish a robust tension foundation. Next, produce the [Axis Input Shaper Graphs](./axis_tuning.md) to identify any binding and address the issue. | ![](../images/belt_graphs/beltpath_problem3.png) |
### Spectrogram lightshow (LIS2DW)
The integration of LIS2DW as a resonance measuring device in Klipper is becoming more and more common, especially because some manufacturers are promoting its superiority over the established ADXL345. It's indeed a new generation chip that should be better to measure traditional "accelerations". However, a detailed comparison of their datasheets and practical measurements paints a more complex picture: the LIS2DW boasts greater sensitivity, but it has a lower sampling rate and produce significant aliasing that results in a "lightshow" effect on the spectrogram, characterized by multiple spurious resonance lines parallel to the main resonance, accompanied by intersecting interference lines that distort the harmonic profile.
For the belt graph, this can be problematic because it can introduce a lot of noise into the results and make them difficult to interpret, and it will probably tell you that there is a mechanical problem when there isn't.
| ADXL345 measurement | LIS2DW measurement |
| --- | --- |
| ![](../images/belt_graphs/chipcomp_adxl.png) | ![](../images/belt_graphs/chipcomp_s2dw.png) |

View File

@@ -49,3 +49,13 @@ For reference, the default settings used in Klipper are:
#driver_HEND: 0 #driver_HEND: 0
#driver_HSTRT: 5 #driver_HSTRT: 5
``` ```
### Semi-blank spectrogram (LIS2DW)
The integration of LIS2DW as a resonance measuring device in Klipper is becoming more and more common, especially because some manufacturers are promoting its superiority over the established ADXL345. It's indeed a new generation chip that should be better to measure traditional "accelerations". However, a detailed comparison of their datasheets and practical measurements paints a more complex picture: the LIS2DW boasts greater sensitivity, but it has a lower sampling rate and produce significant aliasing.
This lower sampling rate is problematic for the vibration graph because it only records data up to 200 Hz, which is too low to produce an accurate graph. This will be seen as a small low frequency band on the spectrogram with a blank area for higher frequencies and incorrect data printed in the speed profile and motor frequency profile.
| LIS2DW vibration measurement |
| --- |
| ![](../images/vibrations_graphs/sd2w_spectrogram.png) |

View File

@@ -1,6 +1,7 @@
#!/bin/bash #!/bin/bash
USER_CONFIG_PATH="${HOME}/printer_data/config" USER_CONFIG_PATH="${HOME}/printer_data/config"
MOONRAKER_CONFIG="${HOME}/printer_data/config/moonraker.conf"
KLIPPER_PATH="${HOME}/klipper" KLIPPER_PATH="${HOME}/klipper"
K_SHAKETUNE_PATH="${HOME}/klippain_shaketune" K_SHAKETUNE_PATH="${HOME}/klippain_shaketune"
@@ -110,11 +111,24 @@ function link_gcodeshellcommandpy {
fi fi
} }
function add_updater {
update_section=$(grep -c '\[update_manager[a-z ]* Klippain-ShakeTune\]' $MOONRAKER_CONFIG || true)
if [ "$update_section" -eq 0 ]; then
echo -n "[INSTALL] Adding update manager to moonraker.conf..."
cat ${K_SHAKETUNE_PATH}/moonraker.conf >> $MOONRAKER_CONFIG
fi
}
function restart_klipper { function restart_klipper {
echo "[POST-INSTALL] Restarting Klipper..." echo "[POST-INSTALL] Restarting Klipper..."
sudo systemctl restart klipper sudo systemctl restart klipper
} }
function restart_moonraker {
echo "[POST-INSTALL] Restarting Moonraker..."
sudo systemctl restart moonraker
}
printf "\n=============================================\n" printf "\n=============================================\n"
echo "- Klippain Shake&Tune module install script -" echo "- Klippain Shake&Tune module install script -"
@@ -126,5 +140,7 @@ preflight_checks
check_download check_download
setup_venv setup_venv
link_extension link_extension
add_updater
link_gcodeshellcommandpy link_gcodeshellcommandpy
restart_klipper restart_klipper
restart_moonraker

11
moonraker.conf Normal file
View File

@@ -0,0 +1,11 @@
## Klippain Shake&Tune automatic update management
[update_manager Klippain-ShakeTune]
type: git_repo
origin: https://github.com/Frix-x/klippain-shaketune.git
path: ~/klippain_shaketune
virtualenv: ~/klippain_shaketune-env
requirements: requirements.txt
system_dependencies: system-dependencies.json
primary_branch: main
managed_services: klipper

9
system-dependencies.json Normal file
View File

@@ -0,0 +1,9 @@
{
"debian": [
"python3-venv",
"python3-numpy",
"python3-matplotlib",
"libopenblas-dev",
"libatlas-base-dev"
]
}