cleaning up and automating the vibrations measurement
This commit is contained in:
@@ -1,166 +0,0 @@
|
||||
#######################################
|
||||
###### SPEED VIBRATIONS ANALYSIS ######
|
||||
#######################################
|
||||
# Written by Frix_x#0161 #
|
||||
|
||||
[gcode_macro SPEED_VIBRATIONS_PROFILE]
|
||||
gcode:
|
||||
{% 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 z_height = params.Z_HEIGHT|default(20)|int %} # z height to put the toolhead before starting 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 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 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 %}
|
||||
|
||||
{% 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 = {
|
||||
'XY' : {
|
||||
'start' : {'x': -0.5, 'y': -0.5 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': 0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'1' : {'x': 0.5, 'y': 0.5, 'z': 0.0 },
|
||||
'2' : {'x': -0.5, 'y': 0.5, 'z': 0.0 },
|
||||
'3' : {'x': -0.5, 'y': -0.5, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'AB' : {
|
||||
'start' : {'x': 0.0, 'y': 0.0 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': 0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'1' : {'x': -0.5, 'y': 0.5, 'z': 0.0 },
|
||||
'2' : {'x': 0.0, 'y': 0.0, 'z': 0.0 },
|
||||
'3' : {'x': 0.5, 'y': 0.5, 'z': 0.0 },
|
||||
'4' : {'x': -0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'5' : {'x': 0.0, 'y': 0.0, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'ABXY' : {
|
||||
'start' : {'x': -0.5, 'y': 0.5 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': -0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'1' : {'x': 0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'2' : {'x': -0.5, 'y': 0.5, 'z': 0.0 },
|
||||
'3' : {'x': 0.5, 'y': 0.5, 'z': 0.0 },
|
||||
'4' : {'x': -0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'5' : {'x': -0.5, 'y': 0.5, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'B' : {
|
||||
'start' : {'x': 0.5, 'y': 0.5 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': -0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'1' : {'x': 0.5, 'y': 0.5, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'A' : {
|
||||
'start' : {'x': -0.5, 'y': 0.5 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': 0.5, 'y': -0.5, 'z': 0.0 },
|
||||
'1' : {'x': -0.5, 'y': 0.5, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'X' : {
|
||||
'start' : {'x': -0.5, 'y': 0.0 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': 0.5, 'y': 0.0, 'z': 0.0 },
|
||||
'1' : {'x': -0.5, 'y': 0.0, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'Y' : {
|
||||
'start' : {'x': 0.0, 'y': 0.5 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': 0.0, 'y': -0.5, 'z': 0.0 },
|
||||
'1' : {'x': 0.0, 'y': 0.5, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'Z' : {
|
||||
'start' : {'x': 0.0, 'y': 0.0 },
|
||||
'move_factors' : {
|
||||
'0' : {'x': 0.0, 'y': 0.0, 'z': 1.0 },
|
||||
'1' : {'x': 0.0, 'y': 0.0, 'z': 0.0 }
|
||||
}
|
||||
},
|
||||
'E' : {
|
||||
'start' : {'x': 0.0, 'y': 0.0 },
|
||||
'move_factor' : 0.05
|
||||
}
|
||||
}
|
||||
%}
|
||||
|
||||
|
||||
{% if not 'xyz' in printer.toolhead.homed_axes %}
|
||||
{ action_raise_error("Must Home printer first!") }
|
||||
{% endif %}
|
||||
|
||||
{% if params.SPEED_INCREMENT|default(2)|float * 100 != (params.SPEED_INCREMENT|default(2)|float * 100)|int %}
|
||||
{ action_raise_error("Only 2 decimal digits are allowed for SPEED_INCREMENT") }
|
||||
{% endif %}
|
||||
|
||||
{% if (size / (max_speed / 60)) < 0.25 and direction != 'E' %}
|
||||
{ action_raise_error("SIZE is too small for this MAX_SPEED. Increase SIZE or decrease MAX_SPEED!") }
|
||||
{% endif %}
|
||||
|
||||
{% if not (direction in direction_factor) %}
|
||||
{ action_raise_error("DIRECTION is not valid. Only XY, AB, ABXY, A, B, X, Y, Z or E is allowed!") }
|
||||
{% endif %}
|
||||
|
||||
{action_respond_info("")}
|
||||
{action_respond_info("Starting machine speed vibrations profile measurement")}
|
||||
{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_SPEED_VIBRATIONS_PROFILE
|
||||
|
||||
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 / 10}
|
||||
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
|
||||
{% for curr_sample in range(0, nb_samples) %}
|
||||
{% set curr_speed = min_speed + curr_sample * speed_increment %}
|
||||
RESPOND MSG="{"Current speed: %.2f mm/s" % (curr_speed / 60)|float}"
|
||||
|
||||
ACCELEROMETER_MEASURE CHIP={accel_chip}
|
||||
{% if direction == 'E' %}
|
||||
G0 E{curr_speed*direction_factor[direction].move_factor} F{curr_speed}
|
||||
{% else %}
|
||||
{% for key, factor in direction_factor[direction].move_factors|dictsort %}
|
||||
G1 X{mid_x + (size * factor.x) } Y{mid_y + (size * factor.y)} Z{z_height + (size * factor.z)} F{curr_speed}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
ACCELEROMETER_MEASURE CHIP={accel_chip} NAME=sp{("%.2f" % (curr_speed / 60)|float)|replace('.','_')}n1
|
||||
G4 P300
|
||||
|
||||
M400
|
||||
{% endfor %}
|
||||
|
||||
RESPOND MSG="Machine speed vibrations profile generation..."
|
||||
RESPOND MSG="This may take some time (3-5min)"
|
||||
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type speed_vibrations --axis_name {direction} --accel {accel|int} --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}
|
||||
|
||||
RESTORE_GCODE_STATE NAME=STATE_SPEED_VIBRATIONS_PROFILE
|
||||
@@ -1,9 +1,9 @@
|
||||
#############################################
|
||||
###### DIRECTIONAL VIBRATIONS ANALYSIS ######
|
||||
#############################################
|
||||
#########################################
|
||||
###### MACHINE VIBRATIONS ANALYSIS ######
|
||||
#########################################
|
||||
# Written by Frix_x#0161 #
|
||||
|
||||
[gcode_macro DIRECTIONAL_VIBRATIONS_PROFILE]
|
||||
[gcode_macro CREATE_VIBRATIONS_PROFILE]
|
||||
gcode:
|
||||
{% set size = params.SIZE|default(100)|int %} # size of the circle where the angled lines are done
|
||||
{% set z_height = params.Z_HEIGHT|default(20)|int %} # z height to put the toolhead before starting the movements
|
||||
@@ -44,11 +44,11 @@ gcode:
|
||||
{% endif %}
|
||||
|
||||
{action_respond_info("")}
|
||||
{action_respond_info("Starting machine directional vibrations profile measurement")}
|
||||
{action_respond_info("Starting machine vibrations profile measurement")}
|
||||
{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_DIRECTIONAL_VIBRATIONS_PROFILE
|
||||
SAVE_GCODE_STATE NAME=CREATE_VIBRATIONS_PROFILE
|
||||
|
||||
G90
|
||||
|
||||
@@ -158,13 +158,13 @@ gcode:
|
||||
{% endfor %}
|
||||
|
||||
|
||||
RESPOND MSG="Machine directional vibrations profile generation..."
|
||||
RESPOND MSG="Machine vibrations profile generation..."
|
||||
RESPOND MSG="This may take some time (3-5min)"
|
||||
# RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type dir_vibrations --accel {accel|int} --chip_name {accel_chip} {% if keep_csv %}--keep_csv{% endif %}"
|
||||
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type vibrations --accel {accel|int} --kinematics {kinematics} --chip_name {accel_chip} {% if keep_csv %}--keep_csv{% endif %}"
|
||||
M400
|
||||
# RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}"
|
||||
RUN_SHELL_COMMAND CMD=shaketune PARAMS="--type clean --keep_results {keep_results}"
|
||||
|
||||
# Restore the previous acceleration values
|
||||
SET_VELOCITY_LIMIT ACCEL={old_accel} ACCEL_TO_DECEL={old_accel_to_decel} SQUARE_CORNER_VELOCITY={old_sqv}
|
||||
|
||||
RESTORE_GCODE_STATE NAME=STATE_DIRECTIONAL_VIBRATIONS_PROFILE
|
||||
RESTORE_GCODE_STATE NAME=CREATE_VIBRATIONS_PROFILE
|
||||
@@ -1,426 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
##################################################
|
||||
###### SPEED AND VIBRATIONS PLOTTING SCRIPT ######
|
||||
##################################################
|
||||
# Written by Frix_x#0161 #
|
||||
|
||||
# Be sure to make this script executable using SSH: type 'chmod +x ./graph_speed_vibrations.py' when in the folder !
|
||||
|
||||
#####################################################################
|
||||
################ !!! DO NOT EDIT BELOW THIS LINE !!! ################
|
||||
#####################################################################
|
||||
|
||||
import optparse, matplotlib, re, os, operator
|
||||
from datetime import datetime
|
||||
from collections import OrderedDict
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.font_manager, matplotlib.ticker, matplotlib.gridspec
|
||||
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from locale_utils import set_locale, print_with_c_locale
|
||||
from common_func import compute_mechanical_parameters, detect_peaks, get_git_version, parse_log, setup_klipper_import, identify_low_energy_zones
|
||||
|
||||
|
||||
PEAKS_DETECTION_THRESHOLD = 0.05
|
||||
PEAKS_RELATIVE_HEIGHT_THRESHOLD = 0.04
|
||||
VALLEY_DETECTION_THRESHOLD = 0.1 # Lower is more sensitive
|
||||
|
||||
KLIPPAIN_COLORS = {
|
||||
"purple": "#70088C",
|
||||
"orange": "#FF8D32",
|
||||
"dark_purple": "#150140",
|
||||
"dark_orange": "#F24130",
|
||||
"red_pink": "#F2055C"
|
||||
}
|
||||
|
||||
|
||||
######################################################################
|
||||
# Computation
|
||||
######################################################################
|
||||
|
||||
# Call to the official Klipper input shaper object to do the PSD computation
|
||||
def calc_freq_response(data):
|
||||
helper = shaper_calibrate.ShaperCalibrate(printer=None)
|
||||
return helper.process_accelerometer_data(data)
|
||||
|
||||
|
||||
def compute_vibration_spectrogram(datas, group, max_freq):
|
||||
psd_list = []
|
||||
first_freqs = None
|
||||
signal_axes = ['x', 'y', 'z', 'all']
|
||||
|
||||
for i in range(0, len(datas), group):
|
||||
# Round up to the nearest power of 2 for faster FFT
|
||||
N = datas[i].shape[0]
|
||||
T = datas[i][-1,0] - datas[i][0,0]
|
||||
M = 1 << int((N/T) * 0.5 - 1).bit_length()
|
||||
if N <= M:
|
||||
# If there is not enough lines in the array to be able to round up to the
|
||||
# nearest power of 2, we need to pad some zeros at the end of the array to
|
||||
# avoid entering a blocking state from Klipper shaper_calibrate.py
|
||||
datas[i] = np.pad(datas[i], [(0, (M-N)+1), (0, 0)], mode='constant', constant_values=0)
|
||||
|
||||
freqrsp = calc_freq_response(datas[i])
|
||||
for n in range(group - 1):
|
||||
data = datas[i + n + 1]
|
||||
|
||||
# Round up to the nearest power of 2 for faster FFT
|
||||
N = data.shape[0]
|
||||
T = data[-1,0] - data[0,0]
|
||||
M = 1 << int((N/T) * 0.5 - 1).bit_length()
|
||||
if N <= M:
|
||||
# If there is not enough lines in the array to be able to round up to the
|
||||
# nearest power of 2, we need to pad some zeros at the end of the array to
|
||||
# avoid entering a blocking state from Klipper shaper_calibrate.py
|
||||
data = np.pad(data, [(0, (M-N)+1), (0, 0)], mode='constant', constant_values=0)
|
||||
|
||||
freqrsp.add_data(calc_freq_response(data))
|
||||
|
||||
if not psd_list:
|
||||
# First group, just put it in the result list
|
||||
first_freqs = freqrsp.freq_bins
|
||||
psd = freqrsp.psd_sum[first_freqs <= max_freq]
|
||||
px = freqrsp.psd_x[first_freqs <= max_freq]
|
||||
py = freqrsp.psd_y[first_freqs <= max_freq]
|
||||
pz = freqrsp.psd_z[first_freqs <= max_freq]
|
||||
psd_list.append([psd, px, py, pz])
|
||||
else:
|
||||
# Not the first group, we need to interpolate every new signals
|
||||
# to the first one to equalize the frequency_bins between them
|
||||
signal_normalized = dict()
|
||||
freqs = freqrsp.freq_bins
|
||||
for axe in signal_axes:
|
||||
signal = freqrsp.get_psd(axe)
|
||||
signal_normalized[axe] = np.interp(first_freqs, freqs, signal)
|
||||
|
||||
# Remove data above max_freq on all axes and add to the result list
|
||||
psd = signal_normalized['all'][first_freqs <= max_freq]
|
||||
px = signal_normalized['x'][first_freqs <= max_freq]
|
||||
py = signal_normalized['y'][first_freqs <= max_freq]
|
||||
pz = signal_normalized['z'][first_freqs <= max_freq]
|
||||
psd_list.append([psd, px, py, pz])
|
||||
|
||||
return np.array(first_freqs[first_freqs <= max_freq]), np.array(psd_list)
|
||||
|
||||
|
||||
def compute_speed_profile(speeds, freqs, psd_list):
|
||||
# Preallocate arrays as psd_list is known and consistent
|
||||
pwrtot_sum = np.zeros(len(psd_list))
|
||||
pwrtot_x = np.zeros(len(psd_list))
|
||||
pwrtot_y = np.zeros(len(psd_list))
|
||||
pwrtot_z = np.zeros(len(psd_list))
|
||||
|
||||
for i, psd in enumerate(psd_list):
|
||||
pwrtot_sum[i] = np.trapz(psd[0], freqs)
|
||||
pwrtot_x[i] = np.trapz(psd[1], freqs)
|
||||
pwrtot_y[i] = np.trapz(psd[2], freqs)
|
||||
pwrtot_z[i] = np.trapz(psd[3], freqs)
|
||||
|
||||
# Resample the signals to get a better detection of the valleys of low energy
|
||||
# and avoid getting limited by the speed increment defined by the user
|
||||
resampled_speeds, resampled_power_sum = resample_signal(speeds, pwrtot_sum)
|
||||
_, resampled_pwrtot_x = resample_signal(speeds, pwrtot_x)
|
||||
_, resampled_pwrtot_y = resample_signal(speeds, pwrtot_y)
|
||||
_, resampled_pwrtot_z = resample_signal(speeds, pwrtot_z)
|
||||
|
||||
return resampled_speeds, [resampled_power_sum, resampled_pwrtot_x, resampled_pwrtot_y, resampled_pwrtot_z]
|
||||
|
||||
|
||||
def compute_motor_profile(power_spectral_densities):
|
||||
# Sum the PSD across all speeds for each frequency of the spectrogram. Basically this
|
||||
# is equivalent to sum up all the spectrogram column by column to plot the total on the right
|
||||
motor_total_vibration = np.sum([psd[0] for psd in power_spectral_densities], axis=0)
|
||||
|
||||
# Then a very little smoothing of the signal is applied to avoid too much noise and sharp peaks on it and simplify
|
||||
# the resonance frequency and damping ratio estimation later on. Also, too much smoothing is bad and would alter the results
|
||||
smoothed_motor_total_vibration = np.convolve(motor_total_vibration, np.ones(10)/10, mode='same')
|
||||
|
||||
return smoothed_motor_total_vibration
|
||||
|
||||
|
||||
# Resample the signal to achieve denser data points in order to get more precise valley placing and
|
||||
# avoid having to use the original sampling of the signal (that is equal to the speed increment used for the test)
|
||||
def resample_signal(speeds, power_total, new_spacing=0.1):
|
||||
new_speeds = np.arange(speeds[0], speeds[-1] + new_spacing, new_spacing)
|
||||
new_power_total = np.interp(new_speeds, speeds, power_total)
|
||||
return np.array(new_speeds), np.array(new_power_total)
|
||||
|
||||
|
||||
######################################################################
|
||||
# Graphing
|
||||
######################################################################
|
||||
|
||||
def plot_speed_profile(ax, speeds, power_total, num_peaks, peaks, low_energy_zones):
|
||||
# For this function, we have two array for the speeds. Indeed, since the power total sum was resampled to better detect
|
||||
# the valleys of low energy later on, we also need the resampled speed array to plot it. For the rest
|
||||
ax.set_title("Machine speed profile", fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||
ax.set_xlabel('Speed (mm/s)')
|
||||
ax.set_ylabel('Energy')
|
||||
|
||||
ax2 = ax.twinx()
|
||||
ax2.yaxis.set_visible(False)
|
||||
|
||||
max_y = power_total[0].max() + power_total[0].max() * 0.05
|
||||
ax.set_xlim([speeds.min(), speeds.max()])
|
||||
ax.set_ylim([0, max_y])
|
||||
ax2.set_ylim([0, max_y])
|
||||
|
||||
ax.plot(speeds, power_total[0], label="X+Y+Z", color='purple', zorder=5)
|
||||
ax.plot(speeds, power_total[1], label="X", color='red')
|
||||
ax.plot(speeds, power_total[2], label="Y", color='green')
|
||||
ax.plot(speeds, power_total[3], label="Z", color='blue')
|
||||
|
||||
if peaks.size:
|
||||
ax.plot(speeds[peaks], power_total[0][peaks], "x", color='black', markersize=8)
|
||||
for idx, peak in enumerate(peaks):
|
||||
fontcolor = 'red'
|
||||
fontweight = 'bold'
|
||||
ax.annotate(f"{idx+1}", (speeds[peak], power_total[0][peak]),
|
||||
textcoords="offset points", xytext=(8, 5),
|
||||
ha='left', fontsize=13, color=fontcolor, weight=fontweight)
|
||||
ax2.plot([], [], ' ', label=f'Number of peaks: {num_peaks}')
|
||||
else:
|
||||
ax2.plot([], [], ' ', label=f'No peaks detected')
|
||||
|
||||
for idx, (start, end, energy) in enumerate(low_energy_zones):
|
||||
ax.axvline(speeds[start], color='red', linestyle='dotted', linewidth=1.5)
|
||||
ax.axvline(speeds[end], color='red', linestyle='dotted', linewidth=1.5)
|
||||
ax2.fill_between(speeds[start:end], 0, power_total[0][start:end], color='green', alpha=0.2, label=f'Zone {idx+1}: {speeds[start]:.1f} to {speeds[end]:.1f} mm/s (mean energy: {energy:.2f}%)')
|
||||
|
||||
ax.xaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
|
||||
ax.yaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
|
||||
ax.grid(which='major', color='grey')
|
||||
ax.grid(which='minor', color='lightgrey')
|
||||
fontP = matplotlib.font_manager.FontProperties()
|
||||
fontP.set_size('small')
|
||||
ax.legend(loc='upper left', prop=fontP)
|
||||
ax2.legend(loc='upper right', prop=fontP)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def plot_vibration_spectrogram(ax, speeds, freqs, power_spectral_densities, peaks, fr, max_freq):
|
||||
# Prepare the spectrum data
|
||||
spectrum = np.empty([len(freqs), len(speeds)])
|
||||
for i in range(len(speeds)):
|
||||
for j in range(len(freqs)):
|
||||
spectrum[j, i] = power_spectral_densities[i][0][j]
|
||||
|
||||
ax.set_title("Speed vibrations spectrogram", fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||
# ax.pcolormesh(speeds, freqs, spectrum, norm=matplotlib.colors.LogNorm(),
|
||||
# cmap='inferno', shading='gouraud')
|
||||
|
||||
ax.imshow(spectrum, norm=matplotlib.colors.LogNorm(), cmap='inferno',
|
||||
aspect='auto', extent=[speeds[0], speeds[-1], freqs[0], freqs[-1]],
|
||||
origin='lower', interpolation='antialiased')
|
||||
|
||||
# Add peaks lines in the spectrogram to get hint from peaks found in the first graph
|
||||
if peaks is not None:
|
||||
for idx, peak in enumerate(peaks):
|
||||
ax.axvline(peak, color='cyan', linestyle='dotted', linewidth=0.75)
|
||||
ax.annotate(f"Peak {idx+1}", (peak, freqs[-1]*0.9),
|
||||
textcoords="data", color='cyan', rotation=90, fontsize=10,
|
||||
verticalalignment='top', horizontalalignment='right')
|
||||
|
||||
# Add motor resonance line
|
||||
if fr is not None and fr > 25:
|
||||
ax.axhline(fr, color='cyan', linestyle='dotted', linewidth=1)
|
||||
ax.annotate(f"Motor resonance", (speeds[-1]*0.95, fr+2),
|
||||
textcoords="data", color='cyan', fontsize=10,
|
||||
verticalalignment='bottom', horizontalalignment='right')
|
||||
|
||||
ax.set_ylim([0., max_freq])
|
||||
ax.set_ylabel('Frequency (hz)')
|
||||
ax.set_xlabel('Speed (mm/s)')
|
||||
|
||||
return
|
||||
|
||||
|
||||
def plot_motor_profile(ax, freqs, motor_vibration_power, motor_fr, motor_zeta, motor_max_power_index):
|
||||
ax.set_title("Motors frequency profile", fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||
ax.set_xlabel('Energy')
|
||||
ax.set_ylabel('Frequency (hz)')
|
||||
|
||||
ax2 = ax.twinx()
|
||||
ax2.yaxis.set_visible(False)
|
||||
|
||||
ax.set_ylim([freqs.min(), freqs.max()])
|
||||
ax.set_xlim([0, motor_vibration_power.max() + motor_vibration_power.max() * 0.1])
|
||||
|
||||
# Plot the profile curve
|
||||
ax.plot(motor_vibration_power, freqs, color=KLIPPAIN_COLORS['orange'])
|
||||
|
||||
# Tag the resonance peak
|
||||
ax.plot(motor_vibration_power[motor_max_power_index], freqs[motor_max_power_index], "x", color='black', markersize=8)
|
||||
fontcolor = KLIPPAIN_COLORS['purple']
|
||||
fontweight = 'bold'
|
||||
ax.annotate(f"R", (motor_vibration_power[motor_max_power_index], freqs[motor_max_power_index]),
|
||||
textcoords="offset points", xytext=(8, 8),
|
||||
ha='right', fontsize=13, color=fontcolor, weight=fontweight)
|
||||
|
||||
# Add the legend
|
||||
ax2.plot([], [], ' ', label="Motor resonant frequency (ω0): %.1fHz" % (motor_fr))
|
||||
ax2.plot([], [], ' ', label="Motor damping ratio (ζ): %.3f" % (motor_zeta))
|
||||
|
||||
ax.xaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
|
||||
ax.yaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
|
||||
ax.grid(which='major', color='grey')
|
||||
ax.grid(which='minor', color='lightgrey')
|
||||
fontP = matplotlib.font_manager.FontProperties()
|
||||
fontP.set_size('small')
|
||||
ax2.legend(loc='upper right', prop=fontP)
|
||||
|
||||
return
|
||||
|
||||
|
||||
######################################################################
|
||||
# Startup and main routines
|
||||
######################################################################
|
||||
|
||||
def extract_speed(logname):
|
||||
try:
|
||||
speed = re.search('sp(.+?)n', os.path.basename(logname)).group(1).replace('_','.')
|
||||
except AttributeError:
|
||||
raise ValueError("File %s does not contain speed in its name and therefore "
|
||||
"is not supported by this script." % (logname,))
|
||||
return float(speed)
|
||||
|
||||
|
||||
def sort_and_slice(raw_speeds, raw_datas, remove):
|
||||
# Sort to get the speeds and their datas aligned and in ascending order
|
||||
raw_speeds, raw_datas = zip(*sorted(zip(raw_speeds, raw_datas), key=operator.itemgetter(0)))
|
||||
|
||||
# Optionally remove the beginning and end of each data file to get only
|
||||
# the constant speed part of the segments and remove the start/stop phase
|
||||
sliced_datas = []
|
||||
for data in raw_datas:
|
||||
sliced = round((len(data) * remove / 100) / 2)
|
||||
sliced_datas.append(data[sliced:len(data)-sliced])
|
||||
|
||||
return raw_speeds, sliced_datas
|
||||
|
||||
|
||||
def speed_vibrations_profile(lognames, klipperdir="~/klipper", axisname=None, accel=None, max_freq=1000., remove=0):
|
||||
set_locale()
|
||||
global shaper_calibrate
|
||||
shaper_calibrate = setup_klipper_import(klipperdir)
|
||||
|
||||
# Parse the raw data and get them ready for analysis
|
||||
raw_datas = [parse_log(filename) for filename in lognames]
|
||||
raw_speeds = [extract_speed(filename) for filename in lognames]
|
||||
speeds, datas = sort_and_slice(raw_speeds, raw_datas, remove)
|
||||
del raw_datas, raw_speeds
|
||||
|
||||
# As we assume that we have the same number of file for each speed increment, we can group
|
||||
# the PSD results by this number (to combine all the segments of the pattern at a constant speed)
|
||||
group_by = speeds.count(speeds[0])
|
||||
|
||||
# Remove speeds duplicates and graph the processed datas
|
||||
speeds = list(OrderedDict((x, True) for x in speeds).keys())
|
||||
|
||||
# Compute speed profile, vibration spectrogram and motor resonance profile
|
||||
freqs, psd = compute_vibration_spectrogram(datas, group_by, max_freq)
|
||||
upsampled_speeds, speeds_powers = compute_speed_profile(speeds, freqs, psd)
|
||||
motor_vibration_power = compute_motor_profile(psd)
|
||||
|
||||
# Peak detection and low energy valleys (good speeds) identification between the peaks
|
||||
num_peaks, vibration_peaks, peaks_speeds = detect_peaks(
|
||||
speeds_powers[0], upsampled_speeds,
|
||||
PEAKS_DETECTION_THRESHOLD * speeds_powers[0].max(),
|
||||
PEAKS_RELATIVE_HEIGHT_THRESHOLD, 10, 10
|
||||
)
|
||||
low_energy_zones = identify_low_energy_zones(speeds_powers[0], VALLEY_DETECTION_THRESHOLD)
|
||||
|
||||
# Print the vibration peaks info in the console
|
||||
formated_peaks_speeds = ["{:.1f}".format(pspeed) for pspeed in peaks_speeds]
|
||||
print_with_c_locale("Vibrations peaks detected: %d @ %s mm/s (avoid setting a speed near these values in your slicer print profile)" % (num_peaks, ", ".join(map(str, formated_peaks_speeds))))
|
||||
|
||||
# Motor resonance estimation
|
||||
motor_fr, motor_zeta, motor_max_power_index = compute_mechanical_parameters(motor_vibration_power, freqs)
|
||||
if motor_fr > 25:
|
||||
print_with_c_locale("Motors have a main resonant frequency at %.1fHz with an estimated damping ratio of %.3f" % (motor_fr, motor_zeta))
|
||||
else:
|
||||
print_with_c_locale("The detected resonance frequency of the motors is too low (%.1fHz). This is probably due to the test run with too high acceleration!" % motor_fr)
|
||||
print_with_c_locale("Try lowering the ACCEL value before restarting the macro to ensure that only constant speeds are recorded and that the dynamic behavior in the corners is not impacting the measurements.")
|
||||
|
||||
# Create graph layout
|
||||
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, gridspec_kw={
|
||||
'height_ratios':[4, 3],
|
||||
'width_ratios':[5, 3],
|
||||
'bottom':0.050,
|
||||
'top':0.890,
|
||||
'left':0.057,
|
||||
'right':0.985,
|
||||
'hspace':0.166,
|
||||
'wspace':0.138
|
||||
})
|
||||
ax2.remove() # top right graph is not used and left blank for now...
|
||||
fig.set_size_inches(14, 11.6)
|
||||
|
||||
# Add title
|
||||
title_line1 = "SPEED VIBRATIONS ANALYSIS"
|
||||
fig.text(0.075, 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].split('-')[0]}", "%Y%m%d %H%M%S")
|
||||
title_line2 = dt.strftime('%x %X')
|
||||
if axisname is not None:
|
||||
title_line2 += ' -- ' + str(axisname).upper() + ' axis'
|
||||
if accel is not None:
|
||||
title_line2 += ' at ' + str(accel) + ' mm/s²'
|
||||
except:
|
||||
print_with_c_locale("Warning: CSV filename look to be different than expected (%s)" % (lognames[0]))
|
||||
title_line2 = lognames[0].split('/')[-1]
|
||||
fig.text(0.075, 0.957, title_line2, ha='left', va='top', fontsize=16, color=KLIPPAIN_COLORS['dark_purple'])
|
||||
|
||||
# Plot the graphs
|
||||
plot_speed_profile(ax1, upsampled_speeds, speeds_powers, num_peaks, vibration_peaks, low_energy_zones)
|
||||
plot_motor_profile(ax4, freqs, motor_vibration_power, motor_fr, motor_zeta, motor_max_power_index)
|
||||
plot_vibration_spectrogram(ax3, speeds, freqs, psd, peaks_speeds, motor_fr, max_freq)
|
||||
|
||||
# Adding a small Klippain logo to the top left corner of the figure
|
||||
ax_logo = fig.add_axes([0.001, 0.924, 0.075, 0.075], anchor='NW')
|
||||
ax_logo.imshow(plt.imread(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'klippain.png')))
|
||||
ax_logo.axis('off')
|
||||
|
||||
# Adding Shake&Tune version in the top right corner
|
||||
st_version = get_git_version()
|
||||
if st_version is not None:
|
||||
fig.text(0.995, 0.985, st_version, ha='right', va='bottom', fontsize=8, color=KLIPPAIN_COLORS['purple'])
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
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", "--axis", type="string", dest="axisname",
|
||||
default=None, help="axis name to be printed on the graph")
|
||||
opts.add_option("-c", "--accel", type="int", dest="accel",
|
||||
default=None, help="accel value to be printed on the graph")
|
||||
opts.add_option("-f", "--max_freq", type="float", default=1000.,
|
||||
help="maximum frequency to graph")
|
||||
opts.add_option("-r", "--remove", type="int", default=0,
|
||||
help="percentage of data removed at start/end of each CSV files")
|
||||
opts.add_option("-k", "--klipper_dir", type="string", dest="klipperdir",
|
||||
default="~/klipper", help="main klipper directory")
|
||||
options, args = opts.parse_args()
|
||||
if len(args) < 1:
|
||||
opts.error("No CSV file(s) to analyse")
|
||||
if options.output is None:
|
||||
opts.error("You must specify an output file.png to use the script (option -o)")
|
||||
if options.remove > 50 or options.remove < 0:
|
||||
opts.error("You must specify a correct percentage (option -r) in the 0-50 range")
|
||||
|
||||
fig = speed_vibrations_profile(args, options.klipperdir, options.axisname, options.accel, options.max_freq, options.remove)
|
||||
fig.savefig(options.output, dpi=150)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -255,7 +255,7 @@ def plot_speed_profile(ax, all_speeds, sp_min_energy, sp_max_energy, sp_avg_ener
|
||||
ax2.plot(all_speeds[peaks], sp_energy_variance[peaks], "x", color='black', markersize=8, zorder=10)
|
||||
for idx, peak in enumerate(peaks):
|
||||
ax2.annotate(f"{idx+1}", (all_speeds[peak], sp_energy_variance[peak]),
|
||||
textcoords="offset points", xytext=(8, 5), fontweight='bold',
|
||||
textcoords="offset points", xytext=(5, 5), fontweight='bold',
|
||||
ha='left', fontsize=13, color=KLIPPAIN_COLORS['red_pink'], zorder=10)
|
||||
|
||||
for idx, (start, end, _) in enumerate(low_energy_zones):
|
||||
@@ -275,7 +275,7 @@ def plot_speed_profile(ax, all_speeds, sp_min_energy, sp_max_energy, sp_avg_ener
|
||||
|
||||
return
|
||||
|
||||
def plot_motor_profiles(ax, freqs, main_angles, motor_profiles, global_motor_profile):
|
||||
def plot_motor_profiles(ax, freqs, main_angles, motor_profiles, global_motor_profile, max_freq):
|
||||
ax.set_title("Motor frequency profile", fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||
ax.set_ylabel('Energy')
|
||||
ax.set_xlabel('Frequency (Hz)')
|
||||
@@ -291,7 +291,7 @@ def plot_motor_profiles(ax, freqs, main_angles, motor_profiles, global_motor_pro
|
||||
max_value = profile_max
|
||||
ax.plot(freqs, motor_profiles[angle], linestyle='--', label=f'{angle} deg', zorder=2)
|
||||
|
||||
ax.set_xlim([0, 400])
|
||||
ax.set_xlim([0, max_freq])
|
||||
ax.set_ylim([0, max_value * 1.1])
|
||||
|
||||
# Then add the motor resonance peak to the graph and print some infos about it
|
||||
@@ -302,15 +302,15 @@ def plot_motor_profiles(ax, freqs, main_angles, motor_profiles, global_motor_pro
|
||||
print_with_c_locale("The detected resonance frequency of the motors is too low (%.1fHz). This is probably due to the test run with too high acceleration!" % motor_fr)
|
||||
print_with_c_locale("Try lowering the ACCEL value before restarting the macro to ensure that only constant speeds are recorded and that the dynamic behavior of the machine is not impacting the measurements.")
|
||||
|
||||
ax.plot(freqs[motor_res_idx], global_motor_profile[motor_res_idx], "x", color='black', markersize=8)
|
||||
ax.plot(freqs[motor_res_idx], global_motor_profile[motor_res_idx], "x", color='black', markersize=10)
|
||||
ax.annotate(f"R", (freqs[motor_res_idx], global_motor_profile[motor_res_idx]),
|
||||
textcoords="offset points", xytext=(10, 5),
|
||||
ha='right', fontsize=13, color=KLIPPAIN_COLORS['purple'], weight='bold')
|
||||
textcoords="offset points", xytext=(15, 5),
|
||||
ha='right', fontsize=14, color=KLIPPAIN_COLORS['red_pink'], weight='bold')
|
||||
|
||||
legend_texts = ["Motor resonant frequency (ω0): %.1fHz" % (motor_fr),
|
||||
"Motor damping ratio (ζ): %.3f" % (motor_zeta)]
|
||||
legend_texts = ["Resonant frequency (ω0): %.1fHz" % (motor_fr),
|
||||
"Damping ratio (ζ): %.3f" % (motor_zeta)]
|
||||
for i, text in enumerate(legend_texts):
|
||||
ax.text(0.90 + i*0.05, 0.98, text, transform=ax.transAxes, color=KLIPPAIN_COLORS['red_pink'], fontsize=12,
|
||||
ax.text(0.90 + i*0.05, 0.85, text, transform=ax.transAxes, color=KLIPPAIN_COLORS['red_pink'], fontsize=12,
|
||||
fontweight='bold', verticalalignment='top', rotation='vertical', zorder=10)
|
||||
|
||||
ax.xaxis.set_minor_locator(matplotlib.ticker.AutoMinorLocator())
|
||||
@@ -320,7 +320,7 @@ def plot_motor_profiles(ax, freqs, main_angles, motor_profiles, global_motor_pro
|
||||
|
||||
fontP = matplotlib.font_manager.FontProperties()
|
||||
fontP.set_size('small')
|
||||
ax.legend(loc='upper left', prop=fontP)
|
||||
ax.legend(loc='upper right', prop=fontP)
|
||||
|
||||
return
|
||||
|
||||
@@ -382,7 +382,7 @@ def extract_angle_and_speed(logname):
|
||||
return float(angle), float(speed)
|
||||
|
||||
|
||||
def dir_vibrations_profile(lognames, klipperdir="~/klipper", kinematics="cartesian", accel=None, max_freq=1000.):
|
||||
def vibrations_profile(lognames, klipperdir="~/klipper", kinematics="cartesian", accel=None, max_freq=1000.):
|
||||
set_locale()
|
||||
global shaper_calibrate
|
||||
shaper_calibrate = setup_klipper_import(klipperdir)
|
||||
@@ -484,7 +484,7 @@ def dir_vibrations_profile(lognames, klipperdir="~/klipper", kinematics="cartesi
|
||||
if accel is not None:
|
||||
title_line2 += ' at ' + str(accel) + ' mm/s²'
|
||||
except:
|
||||
print_with_c_locale("Warning: CSV filename look to be different than expected (%s)" % (lognames[0]))
|
||||
print_with_c_locale("Warning: CSV filenames appear to be different than expected (%s)" % (lognames[0]))
|
||||
title_line2 = lognames[0].split('/')[-1]
|
||||
fig.text(0.060, 0.957, title_line2, ha='left', va='top', fontsize=16, color=KLIPPAIN_COLORS['dark_purple'])
|
||||
|
||||
@@ -492,7 +492,7 @@ def dir_vibrations_profile(lognames, klipperdir="~/klipper", kinematics="cartesi
|
||||
plot_angle_profile_polar(ax3, all_angles, all_angles_energy, good_angles, symmetry_factor)
|
||||
plot_vibration_spectrogram_polar(ax4, all_angles, all_speeds, spectrogram_data)
|
||||
|
||||
plot_motor_profiles(ax1, target_freqs, main_angles, motor_profiles, global_motor_profile)
|
||||
plot_motor_profiles(ax1, target_freqs, main_angles, motor_profiles, global_motor_profile, max_freq)
|
||||
plot_angle_profile(ax6, all_angles, all_angles_energy, good_angles)
|
||||
plot_speed_profile(ax2, all_speeds, sp_min_energy, sp_max_energy, sp_avg_energy, sp_energy_variance, num_peaks, vibration_peaks, good_speeds)
|
||||
|
||||
@@ -533,7 +533,7 @@ def main():
|
||||
if options.kinematics not in ["cartesian", "corexy"]:
|
||||
opts.error("Only Cartesian and CoreXY kinematics are supported by this tool at the moment!")
|
||||
|
||||
fig = dir_vibrations_profile(args, options.klipperdir, options.kinematics, options.accel, options.max_freq)
|
||||
fig = vibrations_profile(args, options.klipperdir, options.kinematics, options.accel, options.max_freq)
|
||||
fig.savefig(options.output, dpi=150)
|
||||
|
||||
|
||||
@@ -25,10 +25,10 @@ KLIPPER_FOLDER = os.path.expanduser('~/klipper')
|
||||
|
||||
from graph_belts import belts_calibration
|
||||
from graph_shaper import shaper_calibration
|
||||
from graph_speed_vibrations import speed_vibrations_profile
|
||||
from graph_vibrations import vibrations_profile
|
||||
from analyze_axesmap import axesmap_calibration
|
||||
|
||||
RESULTS_SUBFOLDERS = ['belts', 'inputshaper', 'speed_vibrations', 'dir_vibrations']
|
||||
RESULTS_SUBFOLDERS = ['belts', 'inputshaper', 'vibrations']
|
||||
|
||||
|
||||
def is_file_open(filepath):
|
||||
@@ -132,7 +132,7 @@ def create_shaper_graph(keep_csv, max_smoothing, scv):
|
||||
return axis
|
||||
|
||||
|
||||
def create_speed_vibrations_graph(axis_name, accel, chip_name, keep_csv):
|
||||
def create_vibrations_graph(accel, kinematics, chip_name, keep_csv):
|
||||
current_date = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
lognames = []
|
||||
|
||||
@@ -162,15 +162,15 @@ def create_speed_vibrations_graph(axis_name, accel, chip_name, keep_csv):
|
||||
time.sleep(5)
|
||||
|
||||
# Generate the vibration graph and its name
|
||||
fig = speed_vibrations_profile(lognames, KLIPPER_FOLDER, axis_name, accel)
|
||||
png_filename = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[2], f'vibrations_{current_date}_{axis_name}.png')
|
||||
fig = vibrations_profile(lognames, KLIPPER_FOLDER, kinematics, accel)
|
||||
png_filename = os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[2], f'vibrations_{current_date}.png')
|
||||
fig.savefig(png_filename, dpi=150)
|
||||
|
||||
# Archive all the csv files in a tarball in case the user want to keep them
|
||||
if keep_csv:
|
||||
with tarfile.open(os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[2], f'vibrations_{current_date}_{axis_name}.tar.gz'), 'w:gz') as tar:
|
||||
with tarfile.open(os.path.join(RESULTS_FOLDER, RESULTS_SUBFOLDERS[2], f'vibrations_{current_date}.tar.gz'), 'w:gz') as tar:
|
||||
for csv_file in lognames:
|
||||
tar.add(csv_file, recursive=False)
|
||||
tar.add(csv_file, arcname=os.path.basename(csv_file), recursive=False)
|
||||
|
||||
# Remove the remaining CSV files not needed anymore (tarball is safe if it was created)
|
||||
for csv_file in lognames:
|
||||
@@ -267,14 +267,19 @@ def main():
|
||||
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")
|
||||
opts.add_option("-m", "--kinematics", type="string", dest="kinematics",
|
||||
default="cartesian", help="machine kinematics configuration used for the vibrations graphs")
|
||||
options, args = opts.parse_args()
|
||||
|
||||
if options.type is None:
|
||||
opts.error("You must specify the type of output graph you want to produce (option -t)")
|
||||
elif options.type.lower() is None or options.type.lower() not in ['belts', 'shaper', 'speed_vibrations', 'axesmap', 'clean']:
|
||||
opts.error("Type of output graph need to be in the list of 'belts', 'shaper', 'speed_vibrations', 'axesmap' or 'clean'")
|
||||
elif options.type.lower() is None or options.type.lower() not in ['belts', 'shaper', 'vibrations', 'axesmap', 'clean']:
|
||||
opts.error("Type of output graph need to be in the list of 'belts', 'shaper', 'vibrations', 'axesmap' or 'clean'")
|
||||
else:
|
||||
graph_mode = options.type
|
||||
|
||||
if graph_mode.lower() == "vibrations" and options.kinematics not in ["cartesian", "corexy"]:
|
||||
opts.error("Only Cartesian and CoreXY kinematics are supported by this tool at the moment!")
|
||||
|
||||
# Check if results folders are there or create them before doing anything else
|
||||
for result_subfolder in RESULTS_SUBFOLDERS:
|
||||
@@ -288,9 +293,9 @@ def main():
|
||||
elif graph_mode.lower() == 'shaper':
|
||||
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() == 'speed_vibrations':
|
||||
create_speed_vibrations_graph(axis_name=options.axis_name, accel=options.accel_used, chip_name=options.chip_name, keep_csv=options.keep_csv)
|
||||
print(f"{options.axis_name} vibration graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[2]}")
|
||||
elif graph_mode.lower() == 'vibrations':
|
||||
create_vibrations_graph(accel=options.accel_used, kinematics=options.kinematics, chip_name=options.chip_name, keep_csv=options.keep_csv)
|
||||
print(f"Vibrations graph created. You will find the results in {RESULTS_FOLDER}/{RESULTS_SUBFOLDERS[2]}")
|
||||
elif graph_mode.lower() == 'axesmap':
|
||||
print(f"WARNING: AXES_MAP_CALIBRATION is currently very experimental and may produce incorrect results... Please validate the output!")
|
||||
find_axesmap(accel=options.accel_used, chip_name=options.chip_name)
|
||||
@@ -298,6 +303,9 @@ def main():
|
||||
print(f"Cleaning output folder to keep only the last {options.keep_results} results...")
|
||||
clean_files(keep_results=options.keep_results)
|
||||
|
||||
if options.keep_csv is False and graph_mode.lower() != 'clean':
|
||||
print(f"Deleting raw CSV files... If you want to keep them, use the --keep_csv option!")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user