added axes_map computation

This commit is contained in:
Félix Boisselier
2023-12-01 00:08:32 +01:00
parent 7ba692954f
commit 8721488d8c
13 changed files with 298 additions and 47 deletions

View File

@@ -0,0 +1,60 @@
############################################################
###### AXE_MAP DETECTION AND ACCELEROMETER VALIDATION ######
############################################################
# Written by Frix_x#0161 #
[gcode_macro AXES_MAP_CALIBRATION]
gcode:
{% set z_height = params.Z_HEIGHT|default(20)|int %} # z height to put the toolhead before starting the movements
{% set speed = params.SPEED|default(80)|float * 60 %} # feedrate for the movements
{% set accel = params.ACCEL|default(1500)|int %} # accel value used to move on the pattern
{% set feedrate_travel = params.TRAVEL_SPEED|default(120)|int * 60 %} # travel feedrate between moves
{% set accel_chip = params.ACCEL_CHIP|default("adxl345") %} # ADXL chip name in the config
{% set mid_x = printer.toolhead.axis_maximum.x|float / 2 %}
{% set mid_y = printer.toolhead.axis_maximum.y|float / 2 %}
{% 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 %}
{% if not 'xyz' in printer.toolhead.homed_axes %}
{ action_raise_error("Must Home printer first!") }
{% endif %}
{action_respond_info("")}
{action_respond_info("Starting accelerometer axe_map calibration")}
{action_respond_info("This operation can not be interrupted by normal means. Hit the \"emergency stop\" button to stop it if needed")}
{action_respond_info("")}
SAVE_GCODE_STATE NAME=STATE_AXESMAP_CALIBRATION
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
G1 Z{z_height} F{feedrate_travel / 8}
G1 X{mid_x - 15} Y{mid_y - 15} F{feedrate_travel}
G4 P500
ACCELEROMETER_MEASURE CHIP={accel_chip}
G4 P1000 # This first waiting time is to record the background accelerometer noise before moving
G1 X{mid_x + 15} F{speed}
G4 P1000
G1 Y{mid_y + 15} F{speed}
G4 P1000
G1 Z{z_height + 15} F{speed}
G4 P1000
ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=axemap
RESPOND MSG="Analysis of the movements..."
RUN_SHELL_COMMAND CMD=shaketune PARAMS="AXESMAP {accel}"
# 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_AXESMAP_CALIBRATION

View File

@@ -6,7 +6,6 @@
[gcode_macro AXES_SHAPER_CALIBRATION] [gcode_macro AXES_SHAPER_CALIBRATION]
description: Perform standard axis input shaper tests on one or both XY axes to select the best input shaper filter 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 min_freq = params.FREQ_START|default(5)|float %} {% set min_freq = params.FREQ_START|default(5)|float %}
{% 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 %}
@@ -28,10 +27,8 @@ gcode:
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
{% if verbose %}
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)"
{% endif %}
RUN_SHELL_COMMAND CMD=shaketune PARAMS=SHAPER RUN_SHELL_COMMAND CMD=shaketune PARAMS=SHAPER
{% endif %} {% endif %}
@@ -39,9 +36,7 @@ gcode:
TEST_RESONANCES AXIS=Y OUTPUT=raw_data NAME=y FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec} TEST_RESONANCES AXIS=Y OUTPUT=raw_data NAME=y FREQ_START={min_freq} FREQ_END={max_freq} HZ_PER_SEC={hz_per_sec}
M400 M400
{% if verbose %}
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)"
{% endif %}
RUN_SHELL_COMMAND CMD=shaketune PARAMS=SHAPER RUN_SHELL_COMMAND CMD=shaketune PARAMS=SHAPER
{% endif %} {% endif %}

View File

@@ -6,7 +6,6 @@
[gcode_macro BELTS_SHAPER_CALIBRATION] [gcode_macro BELTS_SHAPER_CALIBRATION]
description: Perform a custom half-axis test to analyze and compare the frequency profiles of individual 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 min_freq = params.FREQ_START|default(5)|float %} {% set min_freq = params.FREQ_START|default(5)|float %}
{% set max_freq = params.FREQ_END|default(133.33)|float %} {% set max_freq = params.FREQ_END|default(133.33)|float %}
{% set hz_per_sec = params.HZ_PER_SEC|default(1)|float %} {% set hz_per_sec = params.HZ_PER_SEC|default(1)|float %}
@@ -17,8 +16,6 @@ gcode:
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 %}
RESPOND MSG="Belts comparative frequency profile generation..." RESPOND MSG="Belts comparative frequency profile generation..."
RESPOND MSG="This may take some time (3-5min)" RESPOND MSG="This may take some time (3-5min)"
{% endif %}
RUN_SHELL_COMMAND CMD=shaketune PARAMS=BELTS RUN_SHELL_COMMAND CMD=shaketune PARAMS=BELTS

View File

@@ -8,7 +8,6 @@ gcode:
{% 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
{% set verbose = params.VERBOSE|default(true) %} # Wether to log the current speed in the console
{% set min_speed = params.MIN_SPEED|default(20)|float * 60 %} # minimum feedrate for the movements {% set min_speed = params.MIN_SPEED|default(20)|float * 60 %} # minimum feedrate for the movements
{% 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
@@ -100,9 +99,7 @@ gcode:
} }
%} %}
#
# STARTING...
#
{% if not 'xyz' in printer.toolhead.homed_axes %} {% if not 'xyz' in printer.toolhead.homed_axes %}
{ action_raise_error("Must Home printer first!") } { action_raise_error("Must Home printer first!") }
{% endif %} {% endif %}
@@ -126,22 +123,19 @@ gcode:
SAVE_GCODE_STATE NAME=STATE_VIBRATIONS_CALIBRATION SAVE_GCODE_STATE NAME=STATE_VIBRATIONS_CALIBRATION
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 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} 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} F{feedrate_travel / 10}
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}
# vibration pattern for each frequency # vibration pattern for each frequency
{% for curr_sample in range(0, nb_samples) %} {% for curr_sample in range(0, nb_samples) %}
{% set curr_speed = min_speed + curr_sample * speed_increment %} {% set curr_speed = min_speed + curr_sample * speed_increment %}
{% if verbose %}
RESPOND MSG="{"Current speed: %.2f mm/s" % (curr_speed / 60)|float}" RESPOND MSG="{"Current speed: %.2f mm/s" % (curr_speed / 60)|float}"
{% endif %}
ACCELEROMETER_MEASURE CHIP={accel_chip} ACCELEROMETER_MEASURE CHIP={accel_chip}
{% if direction == 'E' %} {% if direction == 'E' %}
@@ -152,14 +146,13 @@ gcode:
{% endfor %} {% endfor %}
{% endif %} {% endif %}
ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=sp{("%.2f" % (curr_speed / 60)|float)|replace('.','_')}n1 ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=sp{("%.2f" % (curr_speed / 60)|float)|replace('.','_')}n1
G4 P300 G4 P300
M400 M400
{% endfor %} {% endfor %}
{% if verbose %} RESPOND MSG="Machine and motors vibration graph generation..."
RESPOND MSG="Graphs generation... Please wait a minute or two and look in the configured folder." RESPOND MSG="This may take some time (3-5min)"
{% endif %}
RUN_SHELL_COMMAND CMD=shaketune PARAMS="VIBRATIONS {direction}" RUN_SHELL_COMMAND CMD=shaketune PARAMS="VIBRATIONS {direction}"
# Restore the previous acceleration values # Restore the previous acceleration values

View File

@@ -0,0 +1,171 @@
#!/usr/bin/env python3
######################################
###### AXE_MAP DETECTION SCRIPT ######
######################################
# Written by Frix_x#0161 #
# Be sure to make this script executable using SSH: type 'chmod +x ./analyze_axesmap.py' when in the folder !
#####################################################################
################ !!! DO NOT EDIT BELOW THIS LINE !!! ################
#####################################################################
import optparse
import numpy as np
import locale
from scipy.signal import butter, filtfilt
NUM_POINTS = 500
# 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
######################################################################
def accel_signal_filter(data, cutoff=2, fs=100, order=5):
nyq = 0.5 * fs
normal_cutoff = cutoff / nyq
b, a = butter(order, normal_cutoff, btype='low', analog=False)
filtered_data = filtfilt(b, a, data)
filtered_data -= np.mean(filtered_data)
return filtered_data
def find_first_spike(data):
min_index, max_index = np.argmin(data), np.argmax(data)
return ('-', min_index) if min_index < max_index else ('', max_index)
def get_movement_vector(data, start_idx, end_idx):
if start_idx < end_idx:
vector = []
for i in range(3):
vector.append(np.mean(data[i][start_idx:end_idx], axis=0))
return vector
else:
return np.zeros(3)
def angle_between(v1, v2):
v1_u = v1 / np.linalg.norm(v1)
v2_u = v2 / np.linalg.norm(v2)
return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
def compute_errors(filtered_data, spikes_sorted, accel_value, num_points):
# Get the movement start points in the correct order from the sorted bag of spikes
movement_starts = [spike[0][1] for spike in spikes_sorted]
# Theoretical unit vectors for X, Y, Z printer axes
printer_axes = {
'x': np.array([1, 0, 0]),
'y': np.array([0, 1, 0]),
'z': np.array([0, 0, 1])
}
alignment_errors = {}
sensitivity_errors = {}
for i, axis in enumerate(['x', 'y', 'z']):
movement_start = movement_starts[i]
movement_end = movement_start + num_points
movement_vector = get_movement_vector(filtered_data, movement_start, movement_end)
alignment_errors[axis] = angle_between(movement_vector, printer_axes[axis])
measured_accel_magnitude = np.linalg.norm(movement_vector)
if accel_value != 0:
sensitivity_errors[axis] = abs(measured_accel_magnitude - accel_value) / accel_value * 100
else:
sensitivity_errors[axis] = None
return alignment_errors, sensitivity_errors
######################################################################
# Startup and main routines
######################################################################
def parse_log(logname):
with open(logname) as f:
for header in f:
if not header.startswith('#'):
break
if not header.startswith('freq,psd_x,psd_y,psd_z,psd_xyz'):
# Raw accelerometer data
return np.loadtxt(logname, comments='#', delimiter=',')
# Power spectral density data or shaper calibration data
raise ValueError("File %s does not contain raw accelerometer data and therefore "
"is not supported by this script. Please use the official Klipper "
"calibrate_shaper.py script to process it instead." % (logname,))
def axesmap_calibration(lognames, accel=None):
# Parse the raw data and get them ready for analysis
raw_datas = [parse_log(filename) for filename in lognames]
if len(raw_datas) > 1:
raise ValueError("Analysis of multiple CSV files at once is not possible with this script")
filtered_data = [accel_signal_filter(raw_datas[0][:, i+1]) for i in range(3)]
spikes = [find_first_spike(filtered_data[i]) for i in range(3)]
spikes_sorted = sorted([(spikes[0], 'x'), (spikes[1], 'y'), (spikes[2], 'z')], key=lambda x: x[0][1])
# Using the previous variables to get the axes_map and errors
axes_map = ','.join([f"{spike[0][0]}{spike[1]}" for spike in spikes_sorted])
# alignment_error, sensitivity_error = compute_errors(filtered_data, spikes_sorted, accel, NUM_POINTS)
results = f"Detected axes_map:\n {axes_map}\n"
# TODO: work on this function that is currently not giving good results...
# results += "Accelerometer angle deviation:\n"
# for axis, angle in alignment_error.items():
# angle_degrees = np.degrees(angle) # Convert radians to degrees
# results += f" {axis.upper()} axis: {angle_degrees:.2f} degrees\n"
# results += "Accelerometer sensitivity error:\n"
# for axis, error in sensitivity_error.items():
# results += f" {axis.upper()} axis: {error:.2f}%\n"
return results
def main():
# Parse command-line arguments
usage = "%prog [options] <raw logs>"
opts = optparse.OptionParser(usage)
opts.add_option("-o", "--output", type="string", dest="output",
default=None, help="filename of output graph")
opts.add_option("-a", "--accel", type="string", dest="accel",
default=None, help="acceleration value used to do the movements")
options, args = opts.parse_args()
if len(args) < 1:
opts.error("No CSV file(s) to analyse")
if options.accel is None:
opts.error("You must specify the acceleration value used when generating the CSV file (option -a)")
try:
accel_value = float(options.accel)
except ValueError:
opts.error("Invalid acceleration value. It should be a numeric value.")
results = axesmap_calibration(args, accel_value)
print(results)
if options.output is not None:
with open(options.output, 'w') as f:
f.write(results)
if __name__ == '__main__':
main()

View File

@@ -317,7 +317,7 @@ def parse_log(logname):
return np.loadtxt(logname, comments='#', delimiter=',') return np.loadtxt(logname, comments='#', delimiter=',')
# Power spectral density data or shaper calibration data # Power spectral density data or shaper calibration data
raise ValueError("File %s does not contain raw accelerometer data and therefore " raise ValueError("File %s does not contain raw accelerometer data and therefore "
"is not supported by graph_vibrations.py script. Please use " "is not supported by this script. Please use the official Klipper"
"calibrate_shaper.py script to process it instead." % (logname,)) "calibrate_shaper.py script to process it instead." % (logname,))
@@ -326,7 +326,7 @@ def extract_speed(logname):
speed = re.search('sp(.+?)n', os.path.basename(logname)).group(1).replace('_','.') speed = re.search('sp(.+?)n', os.path.basename(logname)).group(1).replace('_','.')
except AttributeError: except AttributeError:
raise ValueError("File %s does not contain speed in its name and therefore " raise ValueError("File %s does not contain speed in its name and therefore "
"is not supported by graph_vibrations.py script." % (logname,)) "is not supported by this script." % (logname,))
return float(speed) return float(speed)

View File

@@ -30,6 +30,7 @@ STORE_RESULTS = 3
from graph_belts import belts_calibration from graph_belts import belts_calibration
from graph_shaper import shaper_calibration from graph_shaper import shaper_calibration
from graph_vibrations import vibrations_calibration from graph_vibrations import vibrations_calibration
from analyze_axesmap import axesmap_calibration
RESULTS_SUBFOLDERS = ['belts', 'inputshaper', 'vibrations'] RESULTS_SUBFOLDERS = ['belts', 'inputshaper', 'vibrations']
@@ -50,7 +51,7 @@ def is_file_open(filepath):
return False return False
def get_belts_graph(): def create_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 = []
@@ -61,6 +62,7 @@ def get_belts_graph():
if len(globbed_files) < 2: if len(globbed_files) < 2:
print("Not enough CSV files found in the /tmp folder. Two files are required for the belt graphs!") print("Not enough CSV files found in the /tmp folder. Two files are required for the belt graphs!")
sys.exit(1) sys.exit(1)
sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True) sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True)
for filename in sorted_files[:2]: for filename in sorted_files[:2]:
@@ -85,10 +87,11 @@ def get_belts_graph():
fig = belts_calibration(lognames, KLIPPER_FOLDER) fig = belts_calibration(lognames, KLIPPER_FOLDER)
png_filename = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[0], f'belts_{current_date}.png') png_filename = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[0], f'belts_{current_date}.png')
return fig, png_filename fig.savefig(png_filename)
return
def get_shaper_graph(): def create_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 # Get all the files and sort them based on last modified time to select the most recent one
@@ -96,6 +99,7 @@ def get_shaper_graph():
if not globbed_files: if not globbed_files:
print("No CSV files found in the /tmp folder to create the input shaper graphs!") print("No CSV files found in the /tmp folder to create the input shaper graphs!")
sys.exit(1) sys.exit(1)
sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True) sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True)
filename = sorted_files[0] filename = sorted_files[0]
@@ -117,10 +121,11 @@ def get_shaper_graph():
fig = shaper_calibration([new_file], KLIPPER_FOLDER) fig = shaper_calibration([new_file], KLIPPER_FOLDER)
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')
return fig, png_filename fig.savefig(png_filename)
return
def get_vibrations_graph(axis_name): def create_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 = []
@@ -159,7 +164,35 @@ def get_vibrations_graph(axis_name):
tar.add(csv_file, recursive=False) tar.add(csv_file, recursive=False)
os.remove(csv_file) os.remove(csv_file)
return fig, png_filename fig.savefig(png_filename)
return
def find_axesmap(accel):
current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
result_filename = os.path.join(RESULTS_FOLDER, f'axes_map_{current_date}.txt')
lognames = []
globbed_files = glob.glob('/tmp/adxl345-*.csv')
if not globbed_files:
print("No CSV files found in the /tmp folder to analyze and find the axes_map!")
sys.exit(1)
sorted_files = sorted(globbed_files, key=os.path.getmtime, reverse=True)
filename = sorted_files[0]
# Wait for the file handler to be released by Klipper
while is_file_open(filename):
time.sleep(2)
# Analyze the CSV to find the axes_map parameter
lognames.append(filename)
results = axesmap_calibration(lognames, accel)
with open(result_filename, 'w') as f:
f.write(results)
return
# Utility function to get old files based on their modification time # Utility function to get old files based on their modification time
@@ -210,20 +243,21 @@ def main():
os.makedirs(folder) os.makedirs(folder)
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: is_workflow.py [SHAPER|BELTS|VIBRATIONS]") print("Usage: is_workflow.py [BELTS|SHAPER|VIBRATIONS|AXESMAP]")
sys.exit(1) sys.exit(1)
if sys.argv[1].lower() == 'belts': if sys.argv[1].lower() == 'belts':
fig, png_filename = get_belts_graph() create_belts_graph()
elif sys.argv[1].lower() == 'shaper': elif sys.argv[1].lower() == 'shaper':
fig, png_filename = get_shaper_graph() create_shaper_graph()
elif sys.argv[1].lower() == 'vibrations': elif sys.argv[1].lower() == 'vibrations':
fig, png_filename = get_vibrations_graph(axis_name=sys.argv[2]) create_vibrations_graph(axis_name=sys.argv[2])
elif sys.argv[1].lower() == 'axesmap':
find_axesmap(accel=sys.argv[2])
else: else:
print("Usage: is_workflow.py [SHAPER|BELTS|VIBRATIONS]") print("Usage: is_workflow.py [BELTS|SHAPER|VIBRATIONS|AXESMAP]")
sys.exit(1) sys.exit(1)
fig.savefig(png_filename)
clean_files() clean_files()
print(f"Graphs created. You will find the results in {RESULTS_FOLDER}") print(f"Graphs created. You will find the results in {RESULTS_FOLDER}")

View File

@@ -2,3 +2,5 @@
command: ~/printer_data/config/K-ShakeTune/scripts/shaketune.sh command: ~/printer_data/config/K-ShakeTune/scripts/shaketune.sh
timeout: 600.0 timeout: 600.0
verbose: True verbose: True
[respond]

View File

@@ -4,8 +4,9 @@
### Detailed documentation ### Detailed documentation
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. Before running any tests, you should first run the `AXES_MAP_CALIBRATION` macro to detect the and set Klipper's accelerometer axes_map parameter and validate that your accelerometer is working correctly and properly mounted. Then, 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.
Then look at the documentation for each type of graph by clicking on them below to better understand your results and tune your machine!
Finally look at the documentation for each type of graph by clicking on them below tu run the tests and better understand your results to tune your machine!
| [Belts graph](./macros/belts_tuning.md) | [Axis input shaper graphs](./macros/axis_tuning.md) | [Vibrations graph](./macros/vibrations_tuning.md) | | [Belts graph](./macros/belts_tuning.md) | [Axis input shaper graphs](./macros/axis_tuning.md) | [Vibrations graph](./macros/vibrations_tuning.md) |
|:----------------:|:------------:|:---------------------:| |:----------------:|:------------:|:---------------------:|

View File

@@ -11,7 +11,6 @@ Then, call the `AXES_SHAPER_CALIBRATION` macro and look for the graphs in the re
| parameters | default value | description | | parameters | default value | description |
|-----------:|---------------|-------------| |-----------:|---------------|-------------|
|VERBOSE|1|Wether to log things in the console|
|FREQ_START|5|Starting excitation frequency| |FREQ_START|5|Starting excitation frequency|
|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|

View File

@@ -11,7 +11,6 @@ Then, call the `BELTS_SHAPER_CALIBRATION` macro and look for the graphs in the r
| parameters | default value | description | | parameters | default value | description |
|-----------:|---------------|-------------| |-----------:|---------------|-------------|
|VERBOSE|1|Wether to log things in the console|
|FREQ_START|5|Starting excitation frequency| |FREQ_START|5|Starting excitation frequency|
|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|

View File

@@ -16,7 +16,6 @@ Call the `VIBRATIONS_CALIBRATION` macro with the direction and speed range you w
|SIZE|60|size in mm of the area where the movements are done| |SIZE|60|size in mm of the area where the movements are done|
|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|
|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| |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|

View File

@@ -8,4 +8,5 @@ packaging==23.2
Pillow==10.1.0 Pillow==10.1.0
pyparsing==3.1.1 pyparsing==3.1.1
python-dateutil==2.8.2 python-dateutil==2.8.2
scipy==1.11.4
six==1.16.0 six==1.16.0