Skip to content

Commit

Permalink
Merge pull request #39 from Frix-x/develop
Browse files Browse the repository at this point in the history
v.2.5.0
  • Loading branch information
Frix-x committed Jan 9, 2024
2 parents fe0fa18 + 3d07904 commit 84c406b
Show file tree
Hide file tree
Showing 19 changed files with 645 additions and 565 deletions.
2 changes: 1 addition & 1 deletion K-ShakeTune/K-SnT_axes_map.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ gcode:
ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=axemap

RESPOND MSG="Analysis of the movements..."
RUN_SHELL_COMMAND CMD=shaketune PARAMS="AXESMAP {accel}"
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type axesmap --accel {accel} --chip_name {accel_chip}"

# Restore the previous acceleration values
SET_VELOCITY_LIMIT ACCEL={old_accel} ACCEL_TO_DECEL={old_accel_to_decel} SQUARE_CORNER_VELOCITY={old_sqv}
Expand Down
9 changes: 7 additions & 2 deletions K-ShakeTune/K-SnT_axis.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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 keep_results = params.KEEP_N_RESULTS|default(3)|int %}
{% set keep_csv = params.KEEP_CSV|default(True) %}

{% set X, Y = False, False %}

Expand All @@ -29,7 +31,7 @@ gcode:

RESPOND MSG="X axis frequency profile generation..."
RESPOND MSG="This may take some time (1-3min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS=SHAPER
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type shaper {% if keep_csv %}--keep_csv{% endif %}"
{% endif %}

{% if Y %}
Expand All @@ -38,5 +40,8 @@ gcode:

RESPOND MSG="Y axis frequency profile generation..."
RESPOND MSG="This may take some time (1-3min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS=SHAPER
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type shaper {% if keep_csv %}--keep_csv{% endif %}"
{% endif %}

M400
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}"
6 changes: 5 additions & 1 deletion K-ShakeTune/K-SnT_belts.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ gcode:
{% set min_freq = params.FREQ_START|default(5)|float %}
{% set max_freq = params.FREQ_END|default(133.33)|float %}
{% set hz_per_sec = params.HZ_PER_SEC|default(1)|float %}
{% set keep_results = params.KEEP_N_RESULTS|default(3)|int %}
{% set keep_csv = params.KEEP_CSV|default(True) %}

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
Expand All @@ -18,4 +20,6 @@ gcode:

RESPOND MSG="Belts comparative frequency profile generation..."
RESPOND MSG="This may take some time (3-5min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS=BELTS
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type belts {% if keep_csv %}--keep_csv{% endif %}"
M400
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}"
7 changes: 6 additions & 1 deletion K-ShakeTune/K-SnT_vibrations.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ gcode:
{% 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 keep_results = params.KEEP_N_RESULTS|default(3)|int %}
{% set keep_csv = params.KEEP_CSV|default(True) %}

{% set mid_x = printer.toolhead.axis_maximum.x|float / 2 %}
{% set mid_y = printer.toolhead.axis_maximum.y|float / 2 %}
{% set nb_samples = ((max_speed - min_speed) / speed_increment + 1) | int %}
Expand Down Expand Up @@ -153,7 +156,9 @@ gcode:

RESPOND MSG="Machine and motors vibration graph generation..."
RESPOND MSG="This may take some time (3-5min)"
RUN_SHELL_COMMAND CMD=shaketune PARAMS="VIBRATIONS {direction}"
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type vibrations --axis_name {direction} --accel {accel} --chip_name {accel_chip} {% if keep_csv %}--keep_csv{% endif %}"
M400
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}"

# Restore the previous acceleration values
SET_VELOCITY_LIMIT ACCEL={old_accel} ACCEL_TO_DECEL={old_accel_to_decel} SQUARE_CORNER_VELOCITY={old_sqv}
Expand Down
20 changes: 2 additions & 18 deletions K-ShakeTune/scripts/analyze_axesmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,13 @@

import optparse
import numpy as np
import locale
from locale_utils import print_with_c_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
######################################################################
Expand Down Expand Up @@ -160,7 +144,7 @@ def main():
opts.error("Invalid acceleration value. It should be a numeric value.")

results = axesmap_calibration(args, accel_value)
print(results)
print_with_c_locale(results)

if options.output is not None:
with open(options.output, 'w') as f:
Expand Down
121 changes: 121 additions & 0 deletions K-ShakeTune/scripts/common_func.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/usr/bin/env python3

# Common functions for the Shake&Tune package
# Written by Frix_x#0161 #

import math
import os, sys
from importlib import import_module
from pathlib import Path
import numpy as np
from scipy.signal import spectrogram
from git import GitCommandError, Repo


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 Shake&Tune. Please use the official Klipper "
"script to process it instead." % (logname,))


def setup_klipper_import(kdir):
kdir = os.path.expanduser(kdir)
sys.path.append(os.path.join(kdir, 'klippy'))
return import_module('.shaper_calibrate', 'extras')


# This is used to print the current S&T version on top of the png graph file
def get_git_version():
try:
# Get the absolute path of the script, resolving any symlinks
# Then get 2 times to parent dir to be at the git root folder
script_path = Path(__file__).resolve()
repo_path = script_path.parents[2]
repo = Repo(repo_path)

try:
version = repo.git.describe('--tags')
except GitCommandError:
# If no tag is found, use the simplified commit SHA instead
version = repo.head.commit.hexsha[:7]
return version

except Exception as e:
return None


# This is Klipper's spectrogram generation function adapted to use Scipy
def compute_spectrogram(data):
N = data.shape[0]
Fs = N / (data[-1, 0] - data[0, 0])
# Round up to a power of 2 for faster FFT
M = 1 << int(.5 * Fs - 1).bit_length()
window = np.kaiser(M, 6.)

def _specgram(x):
return spectrogram(x, fs=Fs, window=window, nperseg=M, noverlap=M//2,
detrend='constant', scaling='density', mode='psd')

d = {'x': data[:, 1], 'y': data[:, 2], 'z': data[:, 3]}
f, t, pdata = _specgram(d['x'])
for axis in 'yz':
pdata += _specgram(d[axis])[2]
return pdata, t, f


# Compute natural resonant frequency and damping ratio by using the half power bandwidth method with interpolated frequencies
def compute_mechanical_parameters(psd, freqs):
max_power_index = np.argmax(psd)
fr = freqs[max_power_index]
max_power = psd[max_power_index]

half_power = max_power / math.sqrt(2)
idx_below = np.where(psd[:max_power_index] <= half_power)[0][-1]
idx_above = np.where(psd[max_power_index:] <= half_power)[0][0] + max_power_index
freq_below_half_power = freqs[idx_below] + (half_power - psd[idx_below]) * (freqs[idx_below + 1] - freqs[idx_below]) / (psd[idx_below + 1] - psd[idx_below])
freq_above_half_power = freqs[idx_above - 1] + (half_power - psd[idx_above - 1]) * (freqs[idx_above] - freqs[idx_above - 1]) / (psd[idx_above] - psd[idx_above - 1])

bandwidth = freq_above_half_power - freq_below_half_power
zeta = bandwidth / (2 * fr)

return fr, zeta, max_power_index

# This find all the peaks in a curve by looking at when the derivative term goes from positive to negative
# Then only the peaks found above a threshold are kept to avoid capturing peaks in the low amplitude noise of a signal
def detect_peaks(data, indices, detection_threshold, relative_height_threshold=None, window_size=5, vicinity=3):
# Smooth the curve using a moving average to avoid catching peaks everywhere in noisy signals
kernel = np.ones(window_size) / window_size
smoothed_data = np.convolve(data, kernel, mode='valid')
mean_pad = [np.mean(data[:window_size])] * (window_size // 2)
smoothed_data = np.concatenate((mean_pad, smoothed_data))

# Find peaks on the smoothed curve
smoothed_peaks = np.where((smoothed_data[:-2] < smoothed_data[1:-1]) & (smoothed_data[1:-1] > smoothed_data[2:]))[0] + 1
smoothed_peaks = smoothed_peaks[smoothed_data[smoothed_peaks] > detection_threshold]

# Additional validation for peaks based on relative height
valid_peaks = smoothed_peaks
if relative_height_threshold is not None:
valid_peaks = []
for peak in smoothed_peaks:
peak_height = smoothed_data[peak] - np.min(smoothed_data[max(0, peak-vicinity):min(len(smoothed_data), peak+vicinity+1)])
if peak_height > relative_height_threshold * smoothed_data[peak]:
valid_peaks.append(peak)

# Refine peak positions on the original curve
refined_peaks = []
for peak in valid_peaks:
local_max = peak + np.argmax(data[max(0, peak-vicinity):min(len(data), peak+vicinity+1)]) - vicinity
refined_peaks.append(local_max)

num_peaks = len(refined_peaks)

return num_peaks, np.array(refined_peaks), indices[refined_peaks]

0 comments on commit 84c406b

Please sign in to comment.