scripts: Added shaper tuning parameters to calibrate_shaper script
The added parameters include square_corner_velocity, shaper frequencies to optimize, input shapers to test, input shaper damping ratio and damping ratios to test. All these options can be useful for fine-tuning the input shapers when the default suggestions generated by the tuning script are not optimal. Also the `SHAPER_CALIBRATE` command was modified to pass some of these parameters to the shaper tuning routine. Specifically, square corner velocity and the maximum tested frequency are used to adjust shaper tuning and maximum acceleration recommendations. Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
This commit is contained in:
committed by
KevinOConnor
parent
4f00f21991
commit
72b301a285
@@ -1,6 +1,6 @@
|
||||
# A utility class to test resonances of the printer
|
||||
#
|
||||
# Copyright (C) 2020 Dmitry Butyugin <dmbutyugin@google.com>
|
||||
# Copyright (C) 2020-2024 Dmitry Butyugin <dmbutyugin@google.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, math, os, time
|
||||
@@ -114,6 +114,8 @@ class VibrationPulseTest:
|
||||
if input_shaper is not None:
|
||||
input_shaper.enable_shaping()
|
||||
gcmd.respond_info("Re-enabled [input_shaper]")
|
||||
def get_max_freq(self):
|
||||
return self.freq_end
|
||||
|
||||
class ResonanceTester:
|
||||
def __init__(self, config):
|
||||
@@ -302,8 +304,14 @@ class ResonanceTester:
|
||||
"Calculating the best input shaper parameters for %s axis"
|
||||
% (axis_name,))
|
||||
calibration_data[axis].normalize_to_frequencies()
|
||||
systime = self.printer.get_reactor().monotonic()
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead_info = toolhead.get_status(systime)
|
||||
scv = toolhead_info['square_corner_velocity']
|
||||
best_shaper, all_shapers = helper.find_best_shaper(
|
||||
calibration_data[axis], max_smoothing, gcmd.respond_info)
|
||||
calibration_data[axis], max_smoothing=max_smoothing,
|
||||
scv=scv, max_freq=1.5*self.test.get_max_freq(),
|
||||
logging=gcmd.respond_info)
|
||||
gcmd.respond_info(
|
||||
"Recommended shaper_type_%s = %s, shaper_freq_%s = %.1f Hz"
|
||||
% (axis_name, best_shaper.name,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Automatic calibration of input shapers
|
||||
#
|
||||
# Copyright (C) 2020 Dmitry Butyugin <dmbutyugin@google.com>
|
||||
# Copyright (C) 2020-2024 Dmitry Butyugin <dmbutyugin@google.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import collections, importlib, logging, math, multiprocessing, traceback
|
||||
@@ -227,34 +227,49 @@ class ShaperCalibrate:
|
||||
offset_180 *= inv_D
|
||||
return max(offset_90, offset_180)
|
||||
|
||||
def fit_shaper(self, shaper_cfg, calibration_data, max_smoothing):
|
||||
def fit_shaper(self, shaper_cfg, calibration_data, shaper_freqs,
|
||||
damping_ratio, scv, max_smoothing, test_damping_ratios,
|
||||
max_freq):
|
||||
np = self.numpy
|
||||
|
||||
test_freqs = np.arange(shaper_cfg.min_freq, MAX_SHAPER_FREQ, .2)
|
||||
damping_ratio = damping_ratio or shaper_defs.DEFAULT_DAMPING_RATIO
|
||||
test_damping_ratios = test_damping_ratios or TEST_DAMPING_RATIOS
|
||||
|
||||
if not shaper_freqs:
|
||||
shaper_freqs = (None, None, None)
|
||||
if isinstance(shaper_freqs, tuple):
|
||||
freq_end = shaper_freqs[1] or MAX_SHAPER_FREQ
|
||||
freq_start = min(shaper_freqs[0] or shaper_cfg.min_freq,
|
||||
freq_end - 1e-7)
|
||||
freq_step = shaper_freqs[2] or .2
|
||||
test_freqs = np.arange(freq_start, freq_end, freq_step)
|
||||
else:
|
||||
test_freqs = np.array(shaper_freqs)
|
||||
|
||||
max_freq = max(max_freq or MAX_FREQ, test_freqs.max())
|
||||
|
||||
freq_bins = calibration_data.freq_bins
|
||||
psd = calibration_data.psd_sum[freq_bins <= MAX_FREQ]
|
||||
freq_bins = freq_bins[freq_bins <= MAX_FREQ]
|
||||
psd = calibration_data.psd_sum[freq_bins <= max_freq]
|
||||
freq_bins = freq_bins[freq_bins <= max_freq]
|
||||
|
||||
best_res = None
|
||||
results = []
|
||||
for test_freq in test_freqs[::-1]:
|
||||
shaper_vibrations = 0.
|
||||
shaper_vals = np.zeros(shape=freq_bins.shape)
|
||||
shaper = shaper_cfg.init_func(
|
||||
test_freq, shaper_defs.DEFAULT_DAMPING_RATIO)
|
||||
shaper_smoothing = self._get_shaper_smoothing(shaper)
|
||||
shaper = shaper_cfg.init_func(test_freq, damping_ratio)
|
||||
shaper_smoothing = self._get_shaper_smoothing(shaper, scv=scv)
|
||||
if max_smoothing and shaper_smoothing > max_smoothing and best_res:
|
||||
return best_res
|
||||
# Exact damping ratio of the printer is unknown, pessimizing
|
||||
# remaining vibrations over possible damping values
|
||||
for dr in TEST_DAMPING_RATIOS:
|
||||
for dr in test_damping_ratios:
|
||||
vibrations, vals = self._estimate_remaining_vibrations(
|
||||
shaper, dr, freq_bins, psd)
|
||||
shaper_vals = np.maximum(shaper_vals, vals)
|
||||
if vibrations > shaper_vibrations:
|
||||
shaper_vibrations = vibrations
|
||||
max_accel = self.find_shaper_max_accel(shaper)
|
||||
max_accel = self.find_shaper_max_accel(shaper, scv)
|
||||
# The score trying to minimize vibrations, but also accounting
|
||||
# the growth of smoothing. The formula itself does not have any
|
||||
# special meaning, it simply shows good results on real user data
|
||||
@@ -278,6 +293,8 @@ class ShaperCalibrate:
|
||||
|
||||
def _bisect(self, func):
|
||||
left = right = 1.
|
||||
if not func(1e-9):
|
||||
return 0.
|
||||
while not func(left):
|
||||
right = left
|
||||
left *= .5
|
||||
@@ -292,22 +309,27 @@ class ShaperCalibrate:
|
||||
right = middle
|
||||
return left
|
||||
|
||||
def find_shaper_max_accel(self, shaper):
|
||||
def find_shaper_max_accel(self, shaper, scv):
|
||||
# Just some empirically chosen value which produces good projections
|
||||
# for max_accel without much smoothing
|
||||
TARGET_SMOOTHING = 0.12
|
||||
max_accel = self._bisect(lambda test_accel: self._get_shaper_smoothing(
|
||||
shaper, test_accel) <= TARGET_SMOOTHING)
|
||||
shaper, test_accel, scv) <= TARGET_SMOOTHING)
|
||||
return max_accel
|
||||
|
||||
def find_best_shaper(self, calibration_data, max_smoothing, logger=None):
|
||||
def find_best_shaper(self, calibration_data, shapers=None,
|
||||
damping_ratio=None, scv=None, shaper_freqs=None,
|
||||
max_smoothing=None, test_damping_ratios=None,
|
||||
max_freq=None, logger=None):
|
||||
best_shaper = None
|
||||
all_shapers = []
|
||||
shapers = shapers or AUTOTUNE_SHAPERS
|
||||
for shaper_cfg in shaper_defs.INPUT_SHAPERS:
|
||||
if shaper_cfg.name not in AUTOTUNE_SHAPERS:
|
||||
if shaper_cfg.name not in shapers:
|
||||
continue
|
||||
shaper = self.background_process_exec(self.fit_shaper, (
|
||||
shaper_cfg, calibration_data, max_smoothing))
|
||||
shaper_cfg, calibration_data, shaper_freqs, damping_ratio,
|
||||
scv, max_smoothing, test_damping_ratios, max_freq))
|
||||
if logger is not None:
|
||||
logger("Fitted shaper '%s' frequency = %.1f Hz "
|
||||
"(vibrations = %.1f%%, smoothing ~= %.3f)" % (
|
||||
|
Reference in New Issue
Block a user