From 80c8da622de262fd3f1ab6cac3c8caf42ffb6680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Boisselier?= Date: Mon, 19 Feb 2024 22:53:47 +0100 Subject: [PATCH] added proper use of damping ratio and SCV to compute shaper recommendations --- K-ShakeTune/K-SnT_axis.cfg | 10 +++++++-- K-ShakeTune/scripts/graph_shaper.py | 33 ++++++++++++++++++++--------- K-ShakeTune/scripts/is_workflow.py | 10 ++++++--- docs/macros/axis_tuning.md | 12 ++++++----- 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/K-ShakeTune/K-SnT_axis.cfg b/K-ShakeTune/K-SnT_axis.cfg index 27ccdec..cc8149e 100644 --- a/K-ShakeTune/K-SnT_axis.cfg +++ b/K-ShakeTune/K-SnT_axis.cfg @@ -10,6 +10,8 @@ gcode: {% set max_freq = params.FREQ_END|default(133.3)|float %} {% set hz_per_sec = params.HZ_PER_SEC|default(1)|float %} {% 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_csv = params.KEEP_CSV|default(True) %} @@ -25,13 +27,17 @@ gcode: { action_raise_error("AXIS selection invalid. Should be either all, x or y!") } {% endif %} + {% if scv is None %} + {% set scv = printer.toolhead.square_corner_velocity %} + {% endif %} + {% 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} M400 RESPOND MSG="X axis frequency profile generation..." 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 %} {% if Y %} @@ -40,7 +46,7 @@ gcode: RESPOND MSG="Y axis frequency profile generation..." 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 %} M400 diff --git a/K-ShakeTune/scripts/graph_shaper.py b/K-ShakeTune/scripts/graph_shaper.py index b253144..274ec34 100755 --- a/K-ShakeTune/scripts/graph_shaper.py +++ b/K-ShakeTune/scripts/graph_shaper.py @@ -42,17 +42,22 @@ # Computation ###################################################################### -# Find the best shaper parameters using Klipper's official algorithm selection -def calibrate_shaper(datas, max_smoothing): +# Find the best shaper parameters using Klipper's official algorithm selection with +# 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) calibration_data = helper.process_accelerometer_data(datas) 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) - print_with_c_locale("Recommended shaper is %s @ %.1f Hz" % (shaper.name, shaper.freq)) - print_with_c_locale("Axis has a main resonant frequency at %.1fHz with an estimated damping ratio of %.3f" % (fr, zeta)) + shaper, all_shapers = helper.find_best_shaper( + 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 @@ -198,7 +203,7 @@ def plot_spectrogram(ax, t, bins, pdata, peaks, max_freq): # 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() global shaper_calibrate 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!") # 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]) del datas @@ -231,7 +236,7 @@ def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max # Print the peaks info in the console 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]) - 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 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) - # Add title + # Add a title with some test info 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') try: filename_parts = (lognames[0].split('/')[-1]).split('_') 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_line3 = '| Square corner velocity: ' + str(scv) + 'mm/s' + title_line4 = '| Max allowed smoothing: ' + str(max_smoothing) except: print_with_c_locale("Warning: CSV filename look to be different than expected (%s)" % (lognames[0])) 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.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_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") opts.add_option("-s", "--max_smoothing", type="float", default=None, 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", default="~/klipper", help="main klipper directory") options, args = opts.parse_args() @@ -294,7 +307,7 @@ def main(): 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)") - 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) diff --git a/K-ShakeTune/scripts/is_workflow.py b/K-ShakeTune/scripts/is_workflow.py index dde04f6..f072c63 100755 --- a/K-ShakeTune/scripts/is_workflow.py +++ b/K-ShakeTune/scripts/is_workflow.py @@ -93,7 +93,7 @@ def create_belts_graph(keep_csv): 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') # 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) # 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') 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") 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") + 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() if options.type is None: @@ -282,7 +286,7 @@ def main(): create_belts_graph(keep_csv=options.keep_csv) print(f"Belt graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[0]}") 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]}") 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) diff --git a/docs/macros/axis_tuning.md b/docs/macros/axis_tuning.md index 39cfb56..394f2ab 100644 --- a/docs/macros/axis_tuning.md +++ b/docs/macros/axis_tuning.md @@ -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| |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"| +|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_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. * `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). - - **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. - - **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. + - **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 "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: - * 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. - * 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. - - **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. + * 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 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: ```