Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e9269ba3 | ||
|
|
8e304a71ca | ||
|
|
5d54db0ca0 | ||
|
|
d52680738f | ||
|
|
f95c55230b | ||
|
|
0f7fa66af4 | ||
|
|
da10593ca7 | ||
|
|
060a800cc3 | ||
|
|
7c76be5077 | ||
|
|
a4c2ead732 | ||
|
|
6e884528c0 | ||
|
|
17ccddfa0f | ||
|
|
83f517758a | ||
|
|
c156459420 | ||
|
|
5366ad0581 | ||
|
|
77bfc7ca42 | ||
|
|
ce0330a9d1 | ||
|
|
358773ddef | ||
|
|
d0930261f7 | ||
|
|
a03a3c2e4b | ||
|
|
c102d4145c |
@@ -2,9 +2,10 @@
|
|||||||
###### STANDARD INPUT_SHAPER CALIBRATIONS ######
|
###### STANDARD INPUT_SHAPER CALIBRATIONS ######
|
||||||
################################################
|
################################################
|
||||||
# Written by Frix_x#0161 #
|
# Written by Frix_x#0161 #
|
||||||
# @version: 1.4
|
# @version: 1.5
|
||||||
|
|
||||||
# CHANGELOG:
|
# CHANGELOG:
|
||||||
|
# v1.5: modified EXCITATE_AXIS_AT_FREQ to allow A or B belt testing
|
||||||
# v1.4: added possibility to only run one axis at a time for the axes shaper calibration
|
# v1.4: added possibility to only run one axis at a time for the axes shaper calibration
|
||||||
# v1.3: added possibility to override the default parameters
|
# v1.3: added possibility to override the default parameters
|
||||||
# v1.2: added EXCITATE_AXIS_AT_FREQ to hold a specific excitating frequency on an axis and diagnose mechanical problems
|
# v1.2: added EXCITATE_AXIS_AT_FREQ to hold a specific excitating frequency on an axis and diagnose mechanical problems
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[gcode_macro AXES_SHAPER_CALIBRATION]
|
[gcode_macro AXES_SHAPER_CALIBRATION]
|
||||||
description: Run standard input shaper test for all axes
|
description: Perform standard axis input shaper tests on one or both XY axes to select the best input shaper filter
|
||||||
gcode:
|
gcode:
|
||||||
{% set verbose = params.VERBOSE|default(true) %}
|
{% set verbose = params.VERBOSE|default(true) %}
|
||||||
{% set min_freq = params.FREQ_START|default(5)|float %}
|
{% set min_freq = params.FREQ_START|default(5)|float %}
|
||||||
@@ -51,7 +52,8 @@ gcode:
|
|||||||
M400
|
M400
|
||||||
|
|
||||||
{% if verbose %}
|
{% if verbose %}
|
||||||
RESPOND MSG="X axis shaper graphs generation..."
|
RESPOND MSG="X axis frequency profile generation..."
|
||||||
|
RESPOND MSG="This may take some time (1-3min)"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
RUN_SHELL_COMMAND CMD=plot_graph PARAMS=SHAPER
|
RUN_SHELL_COMMAND CMD=plot_graph PARAMS=SHAPER
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -61,14 +63,15 @@ gcode:
|
|||||||
M400
|
M400
|
||||||
|
|
||||||
{% if verbose %}
|
{% if verbose %}
|
||||||
RESPOND MSG="Y axis shaper graphs generation..."
|
RESPOND MSG="Y axis frequency profile generation..."
|
||||||
|
RESPOND MSG="This may take some time (1-3min)"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
RUN_SHELL_COMMAND CMD=plot_graph PARAMS=SHAPER
|
RUN_SHELL_COMMAND CMD=plot_graph PARAMS=SHAPER
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
[gcode_macro BELTS_SHAPER_CALIBRATION]
|
[gcode_macro BELTS_SHAPER_CALIBRATION]
|
||||||
description: Run custom demi-axe test to analyze belts on CoreXY printers
|
description: Perform a custom half-axis test to analyze and compare the frequency profiles of individual belts on CoreXY printers
|
||||||
gcode:
|
gcode:
|
||||||
{% set verbose = params.VERBOSE|default(true) %}
|
{% set verbose = params.VERBOSE|default(true) %}
|
||||||
{% set min_freq = params.FREQ_START|default(5)|float %}
|
{% set min_freq = params.FREQ_START|default(5)|float %}
|
||||||
@@ -77,21 +80,33 @@ gcode:
|
|||||||
|
|
||||||
TEST_RESONANCES AXIS=1,1 OUTPUT=raw_data NAME=b FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec}
|
TEST_RESONANCES AXIS=1,1 OUTPUT=raw_data NAME=b FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec}
|
||||||
M400
|
M400
|
||||||
|
|
||||||
TEST_RESONANCES AXIS=1,-1 OUTPUT=raw_data NAME=a FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec}
|
TEST_RESONANCES AXIS=1,-1 OUTPUT=raw_data NAME=a FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec}
|
||||||
M400
|
M400
|
||||||
|
|
||||||
{% if verbose %}
|
{% if verbose %}
|
||||||
RESPOND MSG="Belts graphs generation..."
|
RESPOND MSG="Belts comparative frequency profile generation..."
|
||||||
|
RESPOND MSG="This may take some time (3-5min)"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
RUN_SHELL_COMMAND CMD=plot_graph PARAMS=BELTS
|
RUN_SHELL_COMMAND CMD=plot_graph PARAMS=BELTS
|
||||||
|
|
||||||
|
|
||||||
[gcode_macro EXCITATE_AXIS_AT_FREQ]
|
[gcode_macro EXCITATE_AXIS_AT_FREQ]
|
||||||
description: Maintain a specified input shaper excitating frequency for some time to diagnose vibrations
|
description: Maintain a specified input shaper excitation frequency for a period of time to diagnose and locate a source of vibration
|
||||||
gcode:
|
gcode:
|
||||||
{% set FREQUENCY = params.FREQUENCY|default(25)|int %}
|
{% set frequency = params.FREQUENCY|default(25)|int %}
|
||||||
{% set TIME = params.TIME|default(10)|int %}
|
{% set time = params.TIME|default(10)|int %}
|
||||||
{% set AXIS = params.AXIS|default("x")|string|lower %}
|
{% set axis = params.AXIS|default("x")|string|lower %}
|
||||||
|
|
||||||
TEST_RESONANCES OUTPUT=raw_data AXIS={AXIS} FREQ_START={FREQUENCY-1} FREQ_END={FREQUENCY+1} HZ_PER_SEC={1/(TIME/3)}
|
{% if axis not in ["x", "y", "a", "b"] %}
|
||||||
|
{ action_raise_error("AXIS selection invalid. Should be either x, y, a or b!") }
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if axis == "a" %}
|
||||||
|
{% set axis = "1,-1" %}
|
||||||
|
{% elif axis == "b" %}
|
||||||
|
{% set axis = "1,1" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
TEST_RESONANCES OUTPUT=raw_data AXIS={axis} FREQ_START={frequency-1} FREQ_END={frequency+1} HZ_PER_SEC={1/(time/3)}
|
||||||
M400
|
M400
|
||||||
|
|||||||
@@ -2,40 +2,17 @@
|
|||||||
###### VIBRATIONS AND SPEED OPTIMIZATIONS ######
|
###### VIBRATIONS AND SPEED OPTIMIZATIONS ######
|
||||||
################################################
|
################################################
|
||||||
# Written by Frix_x#0161 #
|
# Written by Frix_x#0161 #
|
||||||
# @version: 2.1
|
# @version: 2.2
|
||||||
|
|
||||||
# CHANGELOG:
|
# CHANGELOG:
|
||||||
|
# v2.2: allow custom accel values and set sane defaults (3k) to avoid using very high accels that produce bad results
|
||||||
# v2.1: allow decimal entries for speed and increment and added the E axis as an option to be neasured
|
# v2.1: allow decimal entries for speed and increment and added the E axis as an option to be neasured
|
||||||
# v2.0: added the possibility to measure mutliple axis
|
# v2.0: added the possibility to measure mutliple axis
|
||||||
# v1.0: first speed and vibrations optimization macro
|
# v1.0: first speed and vibrations optimization macro
|
||||||
|
|
||||||
|
|
||||||
### What is it ? ###
|
|
||||||
# This macro helps you to identify the speed settings that exacerbate the vibrations of the machine (ie. where the frame resonate badly).
|
|
||||||
# It also helps to find the clean speed ranges where the machine is silent.
|
|
||||||
# I had some strong vibrations at very specific speeds on my machine (52mm/s for example) and I wanted to find all these problematic speeds
|
|
||||||
# to avoid them in my slicer profile and finally get the silent machine I was dreaming!
|
|
||||||
|
|
||||||
# It works by moving the toolhead at different speed settings while recording the vibrations using the ADXL chip. Then the macro call a custom script
|
|
||||||
# to compute and find the best speed settings. The results can be found in your config folder using Fluidd/Mainsail file manager.
|
|
||||||
|
|
||||||
# The goal is to make it easy to set, share and use it.
|
|
||||||
|
|
||||||
# This macro is parametric and most of the values can be adjusted with their respective input parameters.
|
|
||||||
# It can be called without any parameters - in which case the default values would be used - or with any combination of parameters as desired.
|
|
||||||
|
|
||||||
# Usage:
|
|
||||||
# 1. DO YOUR INPUT SHAPER CALIBRATION FIRST !!! This macro should not be used before as it would be useless and the results invalid.
|
|
||||||
# 2. Call the VIBRATIONS_CALIBRATION macro with the speed range you want to measure (default 20 to 200mm/s with 2mm/s increment).
|
|
||||||
# Be carefull about the Z_HEIGHT variable that default to 20mm -> if your ADXL is under the nozzle, increase it to avoid a crash of the ADXL on the bed of the machine.
|
|
||||||
# 3. Wait for it to finish all the measurement and compute the graph. Then look at it in the results folder.
|
|
||||||
|
|
||||||
|
|
||||||
[gcode_macro VIBRATIONS_CALIBRATION]
|
[gcode_macro VIBRATIONS_CALIBRATION]
|
||||||
gcode:
|
gcode:
|
||||||
#
|
|
||||||
# PARAMETERS
|
|
||||||
#
|
|
||||||
{% set size = params.SIZE|default(60)|int %} # size of the area where the movements are done
|
{% set size = params.SIZE|default(60)|int %} # size of the area where the movements are done
|
||||||
{% set direction = params.DIRECTION|default('XY') %} # can be set to either XY, AB, ABXY, A, B, X, Y, Z
|
{% set direction = params.DIRECTION|default('XY') %} # can be set to either XY, AB, ABXY, A, B, X, Y, Z
|
||||||
{% set z_height = params.Z_HEIGHT|default(20)|int %} # z height to put the toolhead before starting the movements
|
{% set z_height = params.Z_HEIGHT|default(20)|int %} # z height to put the toolhead before starting the movements
|
||||||
@@ -45,16 +22,18 @@ gcode:
|
|||||||
{% set max_speed = params.MAX_SPEED|default(200)|float * 60 %} # maximum feedrate for the movements
|
{% set max_speed = params.MAX_SPEED|default(200)|float * 60 %} # maximum feedrate for the movements
|
||||||
{% set speed_increment = params.SPEED_INCREMENT|default(2)|float * 60 %} # feedrate increment between each move
|
{% set speed_increment = params.SPEED_INCREMENT|default(2)|float * 60 %} # feedrate increment between each move
|
||||||
{% set feedrate_travel = params.TRAVEL_SPEED|default(200)|int * 60 %} # travel feedrate between moves
|
{% set feedrate_travel = params.TRAVEL_SPEED|default(200)|int * 60 %} # travel feedrate between moves
|
||||||
|
{% set accel = params.ACCEL|default(3000)|int %} # accel value used to move on the pattern
|
||||||
{% set accel_chip = params.ACCEL_CHIP|default("adxl345") %} # ADXL chip name in the config
|
{% set accel_chip = params.ACCEL_CHIP|default("adxl345") %} # ADXL chip name in the config
|
||||||
|
|
||||||
#
|
|
||||||
# COMPUTED VALUES
|
|
||||||
#
|
|
||||||
{% set mid_x = printer.toolhead.axis_maximum.x|float / 2 %}
|
{% set mid_x = printer.toolhead.axis_maximum.x|float / 2 %}
|
||||||
{% set mid_y = printer.toolhead.axis_maximum.y|float / 2 %}
|
{% set mid_y = printer.toolhead.axis_maximum.y|float / 2 %}
|
||||||
{% set nb_samples = ((max_speed - min_speed) / speed_increment + 1) | int %}
|
{% set nb_samples = ((max_speed - min_speed) / speed_increment + 1) | int %}
|
||||||
|
|
||||||
|
{% set accel = [accel, printer.configfile.settings.printer.max_accel]|min %}
|
||||||
|
{% set old_accel = printer.toolhead.max_accel %}
|
||||||
|
{% set old_accel_to_decel = printer.toolhead.max_accel_to_decel %}
|
||||||
|
{% set old_sqv = printer.toolhead.square_corner_velocity %}
|
||||||
|
|
||||||
{% set direction_factor = {
|
{% set direction_factor = {
|
||||||
'XY' : {
|
'XY' : {
|
||||||
'start' : {'x': -0.5, 'y': -0.5 },
|
'start' : {'x': -0.5, 'y': -0.5 },
|
||||||
@@ -158,6 +137,9 @@ gcode:
|
|||||||
M83
|
M83
|
||||||
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_VELOCITY_LIMIT ACCEL={accel} ACCEL_TO_DECEL={accel} SQUARE_CORNER_VELOCITY={[(accel / 1000), 5.0]|max}
|
||||||
|
|
||||||
# Going to the start position
|
# Going to the start position
|
||||||
G1 Z{z_height}
|
G1 Z{z_height}
|
||||||
G1 X{mid_x + (size * direction_factor[direction].start.x) } Y{mid_y + (size * direction_factor[direction].start.y)} F{feedrate_travel}
|
G1 X{mid_x + (size * direction_factor[direction].start.x) } Y{mid_y + (size * direction_factor[direction].start.y)} F{feedrate_travel}
|
||||||
@@ -188,4 +170,7 @@ gcode:
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
RUN_SHELL_COMMAND CMD=plot_graph PARAMS="VIBRATIONS {direction}"
|
RUN_SHELL_COMMAND CMD=plot_graph PARAMS="VIBRATIONS {direction}"
|
||||||
|
|
||||||
|
# Restore the previous acceleration values
|
||||||
|
SET_VELOCITY_LIMIT ACCEL={old_accel} ACCEL_TO_DECEL={old_accel_to_decel} SQUARE_CORNER_VELOCITY={old_sqv}
|
||||||
|
|
||||||
RESTORE_GCODE_STATE NAME=STATE_VIBRATIONS_CALIBRATION
|
RESTORE_GCODE_STATE NAME=STATE_VIBRATIONS_CALIBRATION
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
######## CoreXY BELTS CALIBRATION SCRIPT ########
|
######## CoreXY BELTS CALIBRATION SCRIPT ########
|
||||||
#################################################
|
#################################################
|
||||||
# Written by Frix_x#0161 #
|
# Written by Frix_x#0161 #
|
||||||
# @version: 2.0
|
# @version: 2.1
|
||||||
|
|
||||||
# CHANGELOG:
|
# CHANGELOG:
|
||||||
|
# v2.1: replaced the TwoSlopNorm by a custom made norm to allow the script to work on older versions of matplotlib
|
||||||
# v2.0: updated the script to align it to the new K-Shake&Tune module
|
# v2.0: updated the script to align it to the new K-Shake&Tune module
|
||||||
# v1.0: first version of this tool for enhanced vizualisation of belt graphs
|
# v1.0: first version of this tool for enhanced vizualisation of belt graphs
|
||||||
|
|
||||||
@@ -28,10 +29,6 @@ import locale
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
matplotlib.use('Agg')
|
matplotlib.use('Agg')
|
||||||
try:
|
|
||||||
locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())
|
|
||||||
except locale.Error:
|
|
||||||
locale.setlocale(locale.LC_TIME, 'C')
|
|
||||||
|
|
||||||
|
|
||||||
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # For paired peaks names
|
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" # For paired peaks names
|
||||||
@@ -54,6 +51,22 @@ KLIPPAIN_COLORS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Set the best locale for time and date formating (generation of the titles)
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())
|
||||||
|
except locale.Error:
|
||||||
|
locale.setlocale(locale.LC_TIME, 'C')
|
||||||
|
|
||||||
|
# Override the built-in print function to avoid problem in Klipper due to locale settings
|
||||||
|
original_print = print
|
||||||
|
def print_with_c_locale(*args, **kwargs):
|
||||||
|
original_locale = locale.setlocale(locale.LC_ALL, None)
|
||||||
|
locale.setlocale(locale.LC_ALL, 'C')
|
||||||
|
original_print(*args, **kwargs)
|
||||||
|
locale.setlocale(locale.LC_ALL, original_locale)
|
||||||
|
print = print_with_c_locale
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Computation of the PSD graph
|
# Computation of the PSD graph
|
||||||
######################################################################
|
######################################################################
|
||||||
@@ -330,15 +343,15 @@ def compute_mhi(combined_data, similarity_coefficient, num_unpaired_peaks):
|
|||||||
def mhi_lut(mhi):
|
def mhi_lut(mhi):
|
||||||
if 0 <= mhi <= 30:
|
if 0 <= mhi <= 30:
|
||||||
return "Excellent mechanical health"
|
return "Excellent mechanical health"
|
||||||
elif 31 <= mhi <= 45:
|
elif 30 < mhi <= 45:
|
||||||
return "Good mechanical health"
|
return "Good mechanical health"
|
||||||
elif 46 <= mhi <= 55:
|
elif 45 < mhi <= 55:
|
||||||
return "Acceptable mechanical health"
|
return "Acceptable mechanical health"
|
||||||
elif 56 <= mhi <= 70:
|
elif 55 < mhi <= 70:
|
||||||
return "Potential signs of a mechanical issue"
|
return "Potential signs of a mechanical issue"
|
||||||
elif 71 <= mhi <= 85:
|
elif 70 < mhi <= 85:
|
||||||
return "Likely a mechanical issue"
|
return "Likely a mechanical issue"
|
||||||
elif 86 <= mhi <= 100:
|
elif 85 < mhi <= 100:
|
||||||
return "Mechanical issue detected"
|
return "Mechanical issue detected"
|
||||||
|
|
||||||
|
|
||||||
@@ -461,9 +474,13 @@ def plot_difference_spectrogram(ax, data1, data2, signal1, signal2, similarity_f
|
|||||||
ax.set_title(f"Differential Spectrogram", fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
ax.set_title(f"Differential Spectrogram", fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||||
ax.plot([], [], ' ', label=f'{textual_mhi} (experimental)')
|
ax.plot([], [], ' ', label=f'{textual_mhi} (experimental)')
|
||||||
|
|
||||||
# Draw the differential spectrogram with a specific norm to get light grey zero values and red for max values (vmin to vcenter is not used)
|
# Draw the differential spectrogram with a specific custom norm to get white or light orange zero values and red for max values
|
||||||
norm = matplotlib.colors.TwoSlopeNorm(vcenter=np.min(combined_data), vmax=np.max(combined_data))
|
colors = ['white', 'bisque', 'red', 'black']
|
||||||
ax.pcolormesh(bins, t, combined_data.T, cmap='RdBu_r', norm=norm, shading='gouraud')
|
n_bins = [0, 0.12, 0.9, 1] # These values where found experimentaly to get a good higlhlighting of the differences only
|
||||||
|
cm = matplotlib.colors.LinearSegmentedColormap.from_list('WhiteToRed', list(zip(n_bins, colors)))
|
||||||
|
norm = matplotlib.colors.Normalize(vmin=np.min(combined_data), vmax=np.max(combined_data))
|
||||||
|
ax.pcolormesh(bins, t, combined_data.T, cmap=cm, norm=norm, shading='gouraud')
|
||||||
|
|
||||||
ax.set_xlabel('Frequency (hz)')
|
ax.set_xlabel('Frequency (hz)')
|
||||||
ax.set_xlim([0., max_freq])
|
ax.set_xlim([0., max_freq])
|
||||||
ax.set_ylabel('Time (s)')
|
ax.set_ylabel('Time (s)')
|
||||||
|
|||||||
@@ -35,15 +35,12 @@ import locale
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
matplotlib.use('Agg')
|
matplotlib.use('Agg')
|
||||||
try:
|
|
||||||
locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())
|
|
||||||
except locale.Error:
|
|
||||||
locale.setlocale(locale.LC_TIME, 'C')
|
|
||||||
|
|
||||||
|
|
||||||
PEAKS_DETECTION_THRESHOLD = 0.05
|
PEAKS_DETECTION_THRESHOLD = 0.05
|
||||||
PEAKS_EFFECT_THRESHOLD = 0.12
|
PEAKS_EFFECT_THRESHOLD = 0.12
|
||||||
SPECTROGRAM_LOW_PERCENTILE_FILTER = 5
|
SPECTROGRAM_LOW_PERCENTILE_FILTER = 5
|
||||||
|
MAX_SMOOTHING = 0.1
|
||||||
|
|
||||||
KLIPPAIN_COLORS = {
|
KLIPPAIN_COLORS = {
|
||||||
"purple": "#70088C",
|
"purple": "#70088C",
|
||||||
@@ -52,6 +49,22 @@ KLIPPAIN_COLORS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Set the best locale for time and date formating (generation of the titles)
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())
|
||||||
|
except locale.Error:
|
||||||
|
locale.setlocale(locale.LC_TIME, 'C')
|
||||||
|
|
||||||
|
# Override the built-in print function to avoid problem in Klipper due to locale settings
|
||||||
|
original_print = print
|
||||||
|
def print_with_c_locale(*args, **kwargs):
|
||||||
|
original_locale = locale.setlocale(locale.LC_ALL, None)
|
||||||
|
locale.setlocale(locale.LC_ALL, 'C')
|
||||||
|
original_print(*args, **kwargs)
|
||||||
|
locale.setlocale(locale.LC_ALL, original_locale)
|
||||||
|
print = print_with_c_locale
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Computation
|
# Computation
|
||||||
######################################################################
|
######################################################################
|
||||||
@@ -73,7 +86,7 @@ def calibrate_shaper_with_damping(datas, max_smoothing):
|
|||||||
fr, zeta = compute_damping_ratio(psd, freqs)
|
fr, zeta = compute_damping_ratio(psd, freqs)
|
||||||
|
|
||||||
print("Recommended shaper is %s @ %.1f Hz" % (shaper.name, shaper.freq))
|
print("Recommended shaper is %s @ %.1f Hz" % (shaper.name, shaper.freq))
|
||||||
print("Axis has a resonant frequency ω0=%.1fHz with an estimated damping ratio ζ=%.3f" % (fr, zeta))
|
print("Axis has a main resonant frequency at %.1fHz with an estimated damping ratio of %.3f" % (fr, zeta))
|
||||||
|
|
||||||
return shaper.name, all_shapers, calibration_data, fr, zeta
|
return shaper.name, all_shapers, calibration_data, fr, zeta
|
||||||
|
|
||||||
@@ -151,7 +164,7 @@ def detect_peaks(psd, freqs, window_size=5, vicinity=3):
|
|||||||
# Graphing
|
# Graphing
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
def plot_freq_response_with_damping(ax, calibration_data, shapers, selected_shaper, fr, zeta, max_freq):
|
def plot_freq_response_with_damping(ax, calibration_data, shapers, performance_shaper, fr, zeta, max_freq):
|
||||||
freqs = calibration_data.freq_bins
|
freqs = calibration_data.freq_bins
|
||||||
psd = calibration_data.psd_sum[freqs <= max_freq]
|
psd = calibration_data.psd_sum[freqs <= max_freq]
|
||||||
px = calibration_data.psd_x[freqs <= max_freq]
|
px = calibration_data.psd_x[freqs <= max_freq]
|
||||||
@@ -181,30 +194,50 @@ def plot_freq_response_with_damping(ax, calibration_data, shapers, selected_shap
|
|||||||
ax2 = ax.twinx()
|
ax2 = ax.twinx()
|
||||||
ax2.yaxis.set_visible(False)
|
ax2.yaxis.set_visible(False)
|
||||||
|
|
||||||
best_shaper_vals = None
|
lowvib_shaper_vibrs = float('inf')
|
||||||
no_vibr_shaper = None
|
lowvib_shaper = None
|
||||||
no_vibr_shaper_freq = None
|
lowvib_shaper_freq = None
|
||||||
no_vibr_shaper_accel = 0
|
lowvib_shaper_accel = 0
|
||||||
|
|
||||||
# Draw the shappers curves and add their specific parameters in the legend
|
# Draw the shappers curves and add their specific parameters in the legend
|
||||||
# This adds also a way to find the best shaper with 0% of vibrations (to be printed in the legend later)
|
# This adds also a way to find the best shaper with a low level of vibrations (with a resonable level of smoothing)
|
||||||
for shaper in shapers:
|
for shaper in shapers:
|
||||||
shaper_max_accel = round(shaper.max_accel / 100.) * 100.
|
shaper_max_accel = round(shaper.max_accel / 100.) * 100.
|
||||||
label = "%s (%.1f Hz, vibr=%.1f%%, sm~=%.2f, accel<=%.f)" % (
|
label = "%s (%.1f Hz, vibr=%.1f%%, sm~=%.2f, accel<=%.f)" % (
|
||||||
shaper.name.upper(), shaper.freq,
|
shaper.name.upper(), shaper.freq,
|
||||||
shaper.vibrs * 100., shaper.smoothing,
|
shaper.vibrs * 100., shaper.smoothing,
|
||||||
shaper_max_accel)
|
shaper_max_accel)
|
||||||
linestyle = 'dotted'
|
ax2.plot(freqs, shaper.vals, label=label, linestyle='dotted')
|
||||||
if shaper.name == selected_shaper:
|
|
||||||
linestyle = 'dashdot'
|
# Get the performance shaper
|
||||||
selected_shaper_freq = shaper.freq
|
if shaper.name == performance_shaper:
|
||||||
best_shaper_vals = shaper.vals
|
performance_shaper_freq = shaper.freq
|
||||||
if (shaper.vibrs * 100 == 0.) and (shaper_max_accel > no_vibr_shaper_accel):
|
performance_shaper_vibr = shaper.vibrs * 100.
|
||||||
no_vibr_shaper_accel = shaper_max_accel
|
performance_shaper_vals = shaper.vals
|
||||||
no_vibr_shaper = shaper.name
|
|
||||||
no_vibr_shaper_freq = shaper.freq
|
# Get the low vibration shaper
|
||||||
ax2.plot(freqs, shaper.vals, label=label, linestyle=linestyle)
|
if (shaper.vibrs * 100 < lowvib_shaper_vibrs or (shaper.vibrs * 100 == lowvib_shaper_vibrs and shaper_max_accel > lowvib_shaper_accel)) and shaper.smoothing < MAX_SMOOTHING:
|
||||||
ax.plot(freqs, psd * best_shaper_vals, label='With %s applied' % (selected_shaper.upper()), color='cyan')
|
lowvib_shaper_accel = shaper_max_accel
|
||||||
|
lowvib_shaper = shaper.name
|
||||||
|
lowvib_shaper_freq = shaper.freq
|
||||||
|
lowvib_shaper_vibrs = shaper.vibrs * 100
|
||||||
|
lowvib_shaper_vals = shaper.vals
|
||||||
|
|
||||||
|
# User recommendations are added to the legend: one is Klipper's original suggestion that is usually good for performances
|
||||||
|
# and the other one is the custom "low vibration" recommendation that looks for a suitable shaper that doesn't have excessive
|
||||||
|
# smoothing and that have a lower vibration level. If both recommendation are the same shaper, or if no suitable "low
|
||||||
|
# vibration" shaper is found, then only a single line as the "best shaper" recommendation is added to the legend
|
||||||
|
if lowvib_shaper != None and lowvib_shaper != performance_shaper and lowvib_shaper_vibrs <= performance_shaper_vibr:
|
||||||
|
ax2.plot([], [], ' ', label="Recommended performance shaper: %s @ %.1f Hz" % (performance_shaper.upper(), performance_shaper_freq))
|
||||||
|
ax.plot(freqs, psd * performance_shaper_vals, label='With %s applied' % (performance_shaper.upper()), color='cyan')
|
||||||
|
ax2.plot([], [], ' ', label="Recommended low vibrations shaper: %s @ %.1f Hz" % (lowvib_shaper.upper(), lowvib_shaper_freq))
|
||||||
|
ax.plot(freqs, psd * lowvib_shaper_vals, label='With %s applied' % (lowvib_shaper.upper()), color='lime')
|
||||||
|
else:
|
||||||
|
ax2.plot([], [], ' ', label="Recommended best shaper: %s @ %.1f Hz" % (performance_shaper.upper(), performance_shaper_freq))
|
||||||
|
ax.plot(freqs, psd * performance_shaper_vals, label='With %s applied' % (performance_shaper.upper()), color='cyan')
|
||||||
|
|
||||||
|
# And the estimated damping ratio is finally added at the end of the legend
|
||||||
|
ax2.plot([], [], ' ', label="Estimated damping ratio (ζ): %.3f" % (zeta))
|
||||||
|
|
||||||
# Draw the detected peaks and name them
|
# Draw the detected peaks and name them
|
||||||
# This also draw the detection threshold and warning threshold (aka "effect zone")
|
# This also draw the detection threshold and warning threshold (aka "effect zone")
|
||||||
@@ -228,10 +261,6 @@ def plot_freq_response_with_damping(ax, calibration_data, shapers, selected_shap
|
|||||||
ax.fill_between(freqs, 0, peaks_warning_threshold, color='green', alpha=0.15, label='Relax Region')
|
ax.fill_between(freqs, 0, peaks_warning_threshold, color='green', alpha=0.15, label='Relax Region')
|
||||||
ax.fill_between(freqs, peaks_warning_threshold, peaks_effect_threshold, color='orange', alpha=0.2, label='Warning Region')
|
ax.fill_between(freqs, peaks_warning_threshold, peaks_effect_threshold, color='orange', alpha=0.2, label='Warning Region')
|
||||||
|
|
||||||
# Final user recommendations added to the legend with an added 0% vibration shaper and the estimated damping ratio over stock Klipper's algorithms
|
|
||||||
ax2.plot([], [], ' ', label="Recommended shaper: %s @ %.1f Hz" % (selected_shaper.upper(), selected_shaper_freq))
|
|
||||||
ax2.plot([], [], ' ', label="Recommended low vibrations shaper: %s @ %.1f Hz" % (no_vibr_shaper.upper(), no_vibr_shaper_freq))
|
|
||||||
ax2.plot([], [], ' ', label="Estimated damping ratio (ζ): %.3f" % (zeta))
|
|
||||||
|
|
||||||
# Add the main resonant frequency and damping ratio of the axis to the graph title
|
# Add the main resonant frequency and damping ratio of the axis to the graph title
|
||||||
ax.set_title("Axis Frequency Profile (ω0=%.1fHz, ζ=%.3f)" % (fr, zeta), fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
ax.set_title("Axis Frequency Profile (ω0=%.1fHz, ζ=%.3f)" % (fr, zeta), fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||||
@@ -303,7 +332,7 @@ def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max
|
|||||||
datas = [parse_log(fn) for fn in lognames]
|
datas = [parse_log(fn) for fn in lognames]
|
||||||
|
|
||||||
# Calibrate shaper and generate outputs
|
# Calibrate shaper and generate outputs
|
||||||
selected_shaper, shapers, calibration_data, fr, zeta = calibrate_shaper_with_damping(datas, max_smoothing)
|
performance_shaper, shapers, calibration_data, fr, zeta = calibrate_shaper_with_damping(datas, max_smoothing)
|
||||||
|
|
||||||
fig = matplotlib.pyplot.figure()
|
fig = matplotlib.pyplot.figure()
|
||||||
gs = matplotlib.gridspec.GridSpec(2, 1, height_ratios=[4, 3])
|
gs = matplotlib.gridspec.GridSpec(2, 1, height_ratios=[4, 3])
|
||||||
@@ -323,7 +352,7 @@ def shaper_calibration(lognames, klipperdir="~/klipper", max_smoothing=None, max
|
|||||||
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'])
|
||||||
|
|
||||||
# Plot the graphs
|
# Plot the graphs
|
||||||
peaks = plot_freq_response_with_damping(ax1, calibration_data, shapers, selected_shaper, fr, zeta, max_freq)
|
peaks = plot_freq_response_with_damping(ax1, calibration_data, shapers, performance_shaper, fr, zeta, max_freq)
|
||||||
plot_spectrogram(ax2, datas[0], peaks, max_freq)
|
plot_spectrogram(ax2, datas[0], peaks, max_freq)
|
||||||
|
|
||||||
fig.set_size_inches(8.3, 11.6)
|
fig.set_size_inches(8.3, 11.6)
|
||||||
|
|||||||
@@ -29,10 +29,6 @@ import locale
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
matplotlib.use('Agg')
|
matplotlib.use('Agg')
|
||||||
try:
|
|
||||||
locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())
|
|
||||||
except locale.Error:
|
|
||||||
locale.setlocale(locale.LC_TIME, 'C')
|
|
||||||
|
|
||||||
|
|
||||||
PEAKS_DETECTION_THRESHOLD = 0.05
|
PEAKS_DETECTION_THRESHOLD = 0.05
|
||||||
@@ -46,6 +42,22 @@ KLIPPAIN_COLORS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Set the best locale for time and date formating (generation of the titles)
|
||||||
|
try:
|
||||||
|
locale.setlocale(locale.LC_TIME, locale.getdefaultlocale())
|
||||||
|
except locale.Error:
|
||||||
|
locale.setlocale(locale.LC_TIME, 'C')
|
||||||
|
|
||||||
|
# Override the built-in print function to avoid problem in Klipper due to locale settings
|
||||||
|
original_print = print
|
||||||
|
def print_with_c_locale(*args, **kwargs):
|
||||||
|
original_locale = locale.setlocale(locale.LC_ALL, None)
|
||||||
|
locale.setlocale(locale.LC_ALL, 'C')
|
||||||
|
original_print(*args, **kwargs)
|
||||||
|
locale.setlocale(locale.LC_ALL, original_locale)
|
||||||
|
print = print_with_c_locale
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Computation
|
# Computation
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|||||||
@@ -3,9 +3,10 @@
|
|||||||
###### INPUT SHAPER KLIPPAIN WORKFLOW ######
|
###### INPUT SHAPER KLIPPAIN WORKFLOW ######
|
||||||
############################################
|
############################################
|
||||||
# Written by Frix_x#0161 #
|
# Written by Frix_x#0161 #
|
||||||
# @version: 2.0
|
# @version: 2.1
|
||||||
|
|
||||||
# CHANGELOG:
|
# CHANGELOG:
|
||||||
|
# v2.1: added more filesystem sync and file handler checks to avoid using corrupted CSV files by going to fast
|
||||||
# v2.0: new version of this as a Python script (to replace the old bash script) and implement the newer and improved shaper plotting scripts
|
# v2.0: new version of this as a Python script (to replace the old bash script) and implement the newer and improved shaper plotting scripts
|
||||||
# v1.7: updated the handling of shaper files to account for the new analysis scripts as we are now using raw data directly
|
# v1.7: updated the handling of shaper files to account for the new analysis scripts as we are now using raw data directly
|
||||||
# v1.6: - updated the handling of shaper graph files to be able to optionnaly account for added positions in the filenames and remove them
|
# v1.6: - updated the handling of shaper graph files to be able to optionnaly account for added positions in the filenames and remove them
|
||||||
@@ -25,7 +26,6 @@
|
|||||||
# VIBRATIONS - To generate vibration diagram after calling the custom (Frix_x#0161) VIBRATIONS_CALIBRATION macro
|
# VIBRATIONS - To generate vibration diagram after calling the custom (Frix_x#0161) VIBRATIONS_CALIBRATION macro
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import glob
|
import glob
|
||||||
@@ -55,6 +55,10 @@ def is_file_open(filepath):
|
|||||||
if os.path.samefile(fd, filepath):
|
if os.path.samefile(fd, filepath):
|
||||||
return True
|
return True
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
# Klipper has already released the CSV file
|
||||||
|
pass
|
||||||
|
except PermissionError:
|
||||||
|
# Unable to check for this particular process due to permissions
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -63,18 +67,32 @@ def get_belts_graph():
|
|||||||
current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
|
current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
lognames = []
|
lognames = []
|
||||||
|
|
||||||
for filename in glob.glob('/tmp/raw_data_axis*.csv'):
|
globbed_files = glob.glob('/tmp/raw_data_axis*.csv')
|
||||||
|
if not globbed_files:
|
||||||
|
print("No CSV files found in the /tmp folder to create the belt graphs!")
|
||||||
|
sys.exit(1)
|
||||||
|
if len(globbed_files) < 2:
|
||||||
|
print("Not enough CSV files found in the /tmp folder. Two files are required for the belt graphs!")
|
||||||
|
sys.exit(1)
|
||||||
|
sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True)
|
||||||
|
|
||||||
|
for filename in sorted_files[:2]:
|
||||||
# Wait for the file handler to be released by Klipper
|
# Wait for the file handler to be released by Klipper
|
||||||
while is_file_open(filename):
|
while is_file_open(filename):
|
||||||
time.sleep(3)
|
time.sleep(2)
|
||||||
|
|
||||||
# Extract the tested belt from the filename and rename/move the CSV file to the result folder
|
# Extract the tested belt from the filename and rename/move the CSV file to the result folder
|
||||||
belt = os.path.basename(filename).split('_')[3].split('.')[0].upper()
|
belt = os.path.basename(filename).split('_')[3].split('.')[0].upper()
|
||||||
new_file = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[0], f'belt_{current_date}_{belt}.csv')
|
new_file = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[0], f'belt_{current_date}_{belt}.csv')
|
||||||
shutil.move(filename, new_file)
|
shutil.move(filename, new_file)
|
||||||
|
os.sync() # Sync filesystem to avoid problems
|
||||||
|
|
||||||
# Save the file path for later
|
# Save the file path for later
|
||||||
lognames.append(new_file)
|
lognames.append(new_file)
|
||||||
|
|
||||||
|
# Wait for the file handler to be released by the move command
|
||||||
|
while is_file_open(new_file):
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
# Generate the belts graph and its name
|
# Generate the belts graph and its name
|
||||||
fig = belts_calibration(lognames, KLIPPER_FOLDER)
|
fig = belts_calibration(lognames, KLIPPER_FOLDER)
|
||||||
@@ -86,22 +104,27 @@ def get_belts_graph():
|
|||||||
def get_shaper_graph():
|
def get_shaper_graph():
|
||||||
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
|
||||||
globbed_files = glob.glob('/tmp/raw_data*.csv')
|
globbed_files = glob.glob('/tmp/raw_data*.csv')
|
||||||
if len(globbed_files) > 1:
|
if not globbed_files:
|
||||||
print("There is more than 1 measurement.csv found in the /tmp folder. Unable to plot the shaper graphs!")
|
print("No CSV files found in the /tmp folder to create the input shaper graphs!")
|
||||||
print("Please clean the files in the /tmp folder and start again.")
|
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True)
|
||||||
filename = globbed_files[0]
|
filename = sorted_files[0]
|
||||||
|
|
||||||
# Wait for the file handler to be released by Klipper
|
# Wait for the file handler to be released by Klipper
|
||||||
while is_file_open(filename):
|
while is_file_open(filename):
|
||||||
time.sleep(3)
|
time.sleep(2)
|
||||||
|
|
||||||
# Extract the tested axis from the filename and rename/move the CSV file to the result folder
|
# Extract the tested axis from the filename and rename/move the CSV file to the result folder
|
||||||
axis = os.path.basename(filename).split('_')[3].split('.')[0].upper()
|
axis = os.path.basename(filename).split('_')[3].split('.')[0].upper()
|
||||||
new_file = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[1], f'resonances_{current_date}_{axis}.csv')
|
new_file = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[1], f'resonances_{current_date}_{axis}.csv')
|
||||||
shutil.move(filename, new_file)
|
shutil.move(filename, new_file)
|
||||||
|
os.sync() # Sync filesystem to avoid problems
|
||||||
|
|
||||||
|
# Wait for the file handler to be released by the move command
|
||||||
|
while is_file_open(new_file):
|
||||||
|
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)
|
||||||
@@ -114,10 +137,18 @@ def get_vibrations_graph(axis_name):
|
|||||||
current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
|
current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
lognames = []
|
lognames = []
|
||||||
|
|
||||||
for filename in glob.glob('/tmp/adxl345-*.csv'):
|
globbed_files = glob.glob('/tmp/adxl345-*.csv')
|
||||||
|
if not globbed_files:
|
||||||
|
print("No CSV files found in the /tmp folder to create the vibration graphs!")
|
||||||
|
sys.exit(1)
|
||||||
|
if len(globbed_files) < 3:
|
||||||
|
print("Not enough CSV files found in the /tmp folder. At least 3 files are required for the vibration graphs!")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for filename in globbed_files:
|
||||||
# Wait for the file handler to be released by Klipper
|
# Wait for the file handler to be released by Klipper
|
||||||
while is_file_open(filename):
|
while is_file_open(filename):
|
||||||
time.sleep(3)
|
time.sleep(2)
|
||||||
|
|
||||||
# Cleanup of the filename and moving it in the result folder
|
# Cleanup of the filename and moving it in the result folder
|
||||||
cleanfilename = os.path.basename(filename).replace('adxl345', f'vibr_{current_date}')
|
cleanfilename = os.path.basename(filename).replace('adxl345', f'vibr_{current_date}')
|
||||||
@@ -129,6 +160,7 @@ def get_vibrations_graph(axis_name):
|
|||||||
|
|
||||||
# Sync filesystem to avoid problems as there is a lot of file copied
|
# Sync filesystem to avoid problems as there is a lot of file copied
|
||||||
os.sync()
|
os.sync()
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
# Generate the vibration graph and its name
|
# Generate the vibration graph and its name
|
||||||
fig = vibrations_calibration(lognames, KLIPPER_FOLDER, axis_name)
|
fig = vibrations_calibration(lognames, KLIPPER_FOLDER, axis_name)
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ It operates in two steps:
|
|||||||
2. Relocates the graphs and associated CSV files to your Klipper config folder for easy access via Mainsail/Fluidd to eliminate the need for SSH.
|
2. Relocates the graphs and associated CSV files to your Klipper config folder for easy access via Mainsail/Fluidd to eliminate the need for SSH.
|
||||||
3. Manages the folder by retaining only the most recent results (default setting of keeping the latest three sets).
|
3. Manages the folder by retaining only the most recent results (default setting of keeping the latest three sets).
|
||||||
|
|
||||||
The [detailed documentation is here](./docs/README.md).
|
Check out the **[detailed documentation of the Shake&Tune module here](./docs/README.md)**. You can also look at the documentation for each type of graph by directly clicking on them below to better understand your results and tune your machine!
|
||||||
|
|
||||||
| Belts graphs | Axis graphs | Vibrations measurement |
|
| [Belts graph](./docs/macros/belts_tuning.md) | [Axis input shaper graphs](./docs/macros/axis_tuning.md) | [Vibrations graph](./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) |
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
# Klippain Shake&Tune module documentation
|
# Klippain Shake&Tune module documentation
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### Detailed documentation
|
### Detailed documentation
|
||||||
|
|
||||||
1. [Input Shaping and tuning generalities](./is_tuning_generalities.md)
|
First, check out **[input shaping and tuning generalities](./is_tuning_generalities.md)** to understand how it all works and what to look for when taking these measurements.
|
||||||
1. [Belt graphs](./macros/belts_tuning.md)
|
Then look at the documentation for each type of graph by clicking on them below to better understand your results and tune your machine!
|
||||||
1. [Axis Input Shaper graphs](./macros/axis_tuning.md)
|
|
||||||
1. [Klippain vibrations graphs](./macros/vibrations_tuning.md)
|
| [Belts graph](./macros/belts_tuning.md) | [Axis input shaper graphs](./macros/axis_tuning.md) | [Vibrations graph](./macros/vibrations_tuning.md) |
|
||||||
|
|:----------------:|:------------:|:---------------------:|
|
||||||
|
| [<img src="./images/belts_example.png">](./macros/belts_tuning.md) | [<img src="./images/axis_example.png">](./macros/axis_tuning.md) | [<img src="./images/vibrations_example.png">](./macros/vibrations_tuning.md) |
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Complementary ressources
|
### Complementary ressources
|
||||||
|
|
||||||
|
|||||||
BIN
docs/banner_long.png
Normal file
BIN
docs/banner_long.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 740 KiB |
@@ -40,10 +40,10 @@ For setting your Input Shaping filters, rely on the auto-computed values display
|
|||||||
* `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, 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.
|
- **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.
|
||||||
- **Shaper recommendations**: This script will give you two recommandation. 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 first is Klipper's original suggestion, for best performance and acceleration on your machine while also allowing a little bit of remaining vibrations.
|
* 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 second aims for no remaining vibration to ensure the best print quality.
|
* 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 final line provides the estimated damping ratio for the axis. This value is generated automatically and is only accurate if the graph displays a clear and well detached single peak.
|
* 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.
|
- **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.
|
||||||
|
|
||||||
Then, add to your configuration:
|
Then, add to your configuration:
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Call the `VIBRATIONS_CALIBRATION` macro with the direction and speed range you w
|
|||||||
|DIRECTION|"XY"|direction vector where you want to do the measurements. Can be set to either "XY", "AB", "ABXY", "A", "B", "X", "Y", "Z", "E"|
|
|DIRECTION|"XY"|direction vector where you want to do the measurements. Can be set to either "XY", "AB", "ABXY", "A", "B", "X", "Y", "Z", "E"|
|
||||||
|Z_HEIGHT|20|z height to put the toolhead before starting the movements. Be careful, if your ADXL is under the nozzle, increase it to avoid a crash of the ADXL on the bed of the machine|
|
|Z_HEIGHT|20|z height to put the toolhead before starting the movements. Be careful, if your ADXL is under the nozzle, increase it to avoid a crash of the ADXL on the bed of the machine|
|
||||||
|VERBOSE|1|Wether to log the current speed in the console|
|
|VERBOSE|1|Wether to log the current speed in the console|
|
||||||
|
|ACCEL|3000 (or max printer accel)|accel in mm/s^2 used for all the moves. Try to keep it relatively low to avoid bad oscillations that affect the measurements, but but high enough to reach constant speed for >~70% of the segments|
|
||||||
|MIN_SPEED|20|minimum speed of the toolhead in mm/s for the movements|
|
|MIN_SPEED|20|minimum speed of the toolhead in mm/s for the movements|
|
||||||
|MAX_SPEED|200|maximum speed of the toolhead in mm/s for the movements|
|
|MAX_SPEED|200|maximum speed of the toolhead in mm/s for the movements|
|
||||||
|SPEED_INCREMENT|2|speed increments of the toolhead in mm/s between every movements|
|
|SPEED_INCREMENT|2|speed increments of the toolhead in mm/s between every movements|
|
||||||
|
|||||||
Reference in New Issue
Block a user