mirror of
https://github.com/Klipper3d/klipper.git
synced 2026-01-05 06:17:48 -07:00
scripts: Updated graph_shaper.py script
The change removes the shapers defined there in favor of the standard ones and makes the script a lot more configurable via command-line arguments. Signed-off-by: Dmitry Butyugin <dmbutyugin@google.com>
This commit is contained in:
parent
c803249467
commit
d825d43108
1 changed files with 77 additions and 116 deletions
|
|
@ -5,20 +5,15 @@
|
|||
# Copyright (C) 2020 Dmitry Butyugin <dmbutyugin@google.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import optparse, math
|
||||
import importlib, math, optparse, os, sys
|
||||
import matplotlib
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'..', 'klippy'))
|
||||
shaper_defs = importlib.import_module('.shaper_defs', 'extras')
|
||||
|
||||
# A set of damping ratios to calculate shaper response for
|
||||
DAMPING_RATIOS=[0.05, 0.1, 0.2]
|
||||
|
||||
# Parameters of the input shaper
|
||||
SHAPER_FREQ=50.0
|
||||
SHAPER_DAMPING_RATIO=0.1
|
||||
|
||||
# Simulate input shaping of step function for these true resonance frequency
|
||||
# and damping ratio
|
||||
STEP_SIMULATION_RESONANCE_FREQ=60.
|
||||
STEP_SIMULATION_DAMPING_RATIO=0.15
|
||||
DEFAULT_DAMPING_RATIOS=[0.075, 0.1, 0.15]
|
||||
|
||||
# If set, defines which range of frequencies to plot shaper frequency response
|
||||
PLOT_FREQ_RANGE = [] # If empty, will be automatically determined
|
||||
|
|
@ -30,86 +25,8 @@ PLOT_FREQ_STEP = .01
|
|||
# Input shapers
|
||||
######################################################################
|
||||
|
||||
def get_zv_shaper():
|
||||
df = math.sqrt(1. - SHAPER_DAMPING_RATIO**2)
|
||||
K = math.exp(-SHAPER_DAMPING_RATIO * math.pi / df)
|
||||
t_d = 1. / (SHAPER_FREQ * df)
|
||||
A = [1., K]
|
||||
T = [0., .5*t_d]
|
||||
return (A, T, "ZV")
|
||||
|
||||
def get_zvd_shaper():
|
||||
df = math.sqrt(1. - SHAPER_DAMPING_RATIO**2)
|
||||
K = math.exp(-SHAPER_DAMPING_RATIO * math.pi / df)
|
||||
t_d = 1. / (SHAPER_FREQ * df)
|
||||
A = [1., 2.*K, K**2]
|
||||
T = [0., .5*t_d, t_d]
|
||||
return (A, T, "ZVD")
|
||||
|
||||
def get_mzv_shaper():
|
||||
df = math.sqrt(1. - SHAPER_DAMPING_RATIO**2)
|
||||
K = math.exp(-.75 * SHAPER_DAMPING_RATIO * math.pi / df)
|
||||
t_d = 1. / (SHAPER_FREQ * df)
|
||||
|
||||
a1 = 1. - 1. / math.sqrt(2.)
|
||||
a2 = (math.sqrt(2.) - 1.) * K
|
||||
a3 = a1 * K * K
|
||||
|
||||
A = [a1, a2, a3]
|
||||
T = [0., .375*t_d, .75*t_d]
|
||||
return (A, T, "MZV")
|
||||
|
||||
def get_ei_shaper():
|
||||
v_tol = 0.05 # vibration tolerance
|
||||
df = math.sqrt(1. - SHAPER_DAMPING_RATIO**2)
|
||||
K = math.exp(-SHAPER_DAMPING_RATIO * math.pi / df)
|
||||
t_d = 1. / (SHAPER_FREQ * df)
|
||||
|
||||
a1 = .25 * (1. + v_tol)
|
||||
a2 = .5 * (1. - v_tol) * K
|
||||
a3 = a1 * K * K
|
||||
|
||||
A = [a1, a2, a3]
|
||||
T = [0., .5*t_d, t_d]
|
||||
return (A, T, "EI")
|
||||
|
||||
def get_2hump_ei_shaper():
|
||||
v_tol = 0.05 # vibration tolerance
|
||||
df = math.sqrt(1. - SHAPER_DAMPING_RATIO**2)
|
||||
K = math.exp(-SHAPER_DAMPING_RATIO * math.pi / df)
|
||||
t_d = 1. / (SHAPER_FREQ * df)
|
||||
|
||||
V2 = v_tol**2
|
||||
X = pow(V2 * (math.sqrt(1. - V2) + 1.), 1./3.)
|
||||
a1 = (3.*X*X + 2.*X + 3.*V2) / (16.*X)
|
||||
a2 = (.5 - a1) * K
|
||||
a3 = a2 * K
|
||||
a4 = a1 * K * K * K
|
||||
|
||||
A = [a1, a2, a3, a4]
|
||||
T = [0., .5*t_d, t_d, 1.5*t_d]
|
||||
return (A, T, "2-hump EI")
|
||||
|
||||
def get_3hump_ei_shaper():
|
||||
v_tol = 0.05 # vibration tolerance
|
||||
df = math.sqrt(1. - SHAPER_DAMPING_RATIO**2)
|
||||
K = math.exp(-SHAPER_DAMPING_RATIO * math.pi / df)
|
||||
t_d = 1. / (SHAPER_FREQ * df)
|
||||
|
||||
K2 = K*K
|
||||
a1 = 0.0625 * (1. + 3. * v_tol + 2. * math.sqrt(2. * (v_tol + 1.) * v_tol))
|
||||
a2 = 0.25 * (1. - v_tol) * K
|
||||
a3 = (0.5 * (1. + v_tol) - 2. * a1) * K2
|
||||
a4 = a2 * K2
|
||||
a5 = a1 * K2 * K2
|
||||
|
||||
A = [a1, a2, a3, a4, a5]
|
||||
T = [0., .5*t_d, t_d, 1.5*t_d, 2.*t_d]
|
||||
return (A, T, "3-hump EI")
|
||||
|
||||
|
||||
def estimate_shaper(shaper, freq, damping_ratio):
|
||||
A, T, _ = shaper
|
||||
A, T = shaper
|
||||
n = len(T)
|
||||
inv_D = 1. / sum(A)
|
||||
omega = 2. * math.pi * freq
|
||||
|
|
@ -123,14 +40,18 @@ def estimate_shaper(shaper, freq, damping_ratio):
|
|||
return math.sqrt(S*S + C*C) * inv_D
|
||||
|
||||
def shift_pulses(shaper):
|
||||
A, T, name = shaper
|
||||
A, T = shaper
|
||||
n = len(T)
|
||||
ts = sum([A[i] * T[i] for i in range(n)]) / sum(A)
|
||||
for i in range(n):
|
||||
T[i] -= ts
|
||||
|
||||
# Shaper selection
|
||||
get_shaper = get_ei_shaper
|
||||
def get_shaper(shaper_name, shaper_freq, damping_ratio):
|
||||
for s in shaper_defs.INPUT_SHAPERS:
|
||||
if shaper_name.lower() == s.name:
|
||||
return s.init_func(shaper_freq, damping_ratio)
|
||||
return shaper_defs.get_none_shaper()
|
||||
|
||||
|
||||
######################################################################
|
||||
|
|
@ -148,44 +69,46 @@ def bisect(func, left, right):
|
|||
right = mid
|
||||
return .5 * (left + right)
|
||||
|
||||
def find_shaper_plot_range(shaper, vib_tol):
|
||||
def find_shaper_plot_range(shaper, shaper_freq, test_damping_ratios, vib_tol):
|
||||
def eval_shaper(freq):
|
||||
return estimate_shaper(shaper, freq, DAMPING_RATIOS[0]) - vib_tol
|
||||
return estimate_shaper(shaper, freq, test_damping_ratios[0]) - vib_tol
|
||||
if not PLOT_FREQ_RANGE:
|
||||
left = bisect(eval_shaper, 0., SHAPER_FREQ)
|
||||
right = bisect(eval_shaper, SHAPER_FREQ, 2.4 * SHAPER_FREQ)
|
||||
left = bisect(eval_shaper, 0., shaper_freq)
|
||||
right = bisect(eval_shaper, shaper_freq, 2.4 * shaper_freq)
|
||||
else:
|
||||
left, right = PLOT_FREQ_RANGE
|
||||
return (left, right)
|
||||
|
||||
def gen_shaper_response(shaper):
|
||||
def gen_shaper_response(shaper, shaper_freq, test_damping_ratios):
|
||||
# Calculate shaper vibration response on a range of frequencies
|
||||
response = []
|
||||
freqs = []
|
||||
freq, freq_end = find_shaper_plot_range(shaper, vib_tol=0.25)
|
||||
freq, freq_end = find_shaper_plot_range(shaper, shaper_freq,
|
||||
test_damping_ratios, vib_tol=0.25)
|
||||
while freq <= freq_end:
|
||||
vals = []
|
||||
for damping_ratio in DAMPING_RATIOS:
|
||||
for damping_ratio in test_damping_ratios:
|
||||
vals.append(estimate_shaper(shaper, freq, damping_ratio))
|
||||
response.append(vals)
|
||||
freqs.append(freq)
|
||||
freq += PLOT_FREQ_STEP
|
||||
legend = ['damping ratio = %.3f' % d_r for d_r in DAMPING_RATIOS]
|
||||
legend = ['damping ratio = %.3f' % d_r for d_r in test_damping_ratios]
|
||||
return freqs, response, legend
|
||||
|
||||
def gen_shaped_step_function(shaper):
|
||||
def gen_shaped_step_function(shaper, shaper_freq,
|
||||
system_freq, system_damping_ratio):
|
||||
# Calculate shaping of a step function
|
||||
A, T, _ = shaper
|
||||
A, T = shaper
|
||||
inv_D = 1. / sum(A)
|
||||
n = len(T)
|
||||
|
||||
omega = 2. * math.pi * STEP_SIMULATION_RESONANCE_FREQ
|
||||
damping = STEP_SIMULATION_DAMPING_RATIO * omega
|
||||
omega_d = omega * math.sqrt(1. - STEP_SIMULATION_DAMPING_RATIO**2)
|
||||
phase = math.acos(STEP_SIMULATION_DAMPING_RATIO)
|
||||
omega = 2. * math.pi * system_freq
|
||||
damping = system_damping_ratio * omega
|
||||
omega_d = omega * math.sqrt(1. - system_damping_ratio**2)
|
||||
phase = math.acos(system_damping_ratio)
|
||||
|
||||
t_start = T[0] - .5 / SHAPER_FREQ
|
||||
t_end = T[-1] + 1.5 / STEP_SIMULATION_RESONANCE_FREQ
|
||||
t_start = T[0] - .5 / shaper_freq
|
||||
t_end = T[-1] + 1.5 / system_freq
|
||||
result = []
|
||||
time = []
|
||||
t = t_start
|
||||
|
|
@ -214,20 +137,24 @@ def gen_shaped_step_function(shaper):
|
|||
|
||||
result.append(val)
|
||||
time.append(t)
|
||||
t += .01 / SHAPER_FREQ
|
||||
t += .01 / shaper_freq
|
||||
legend = ['step', 'shaper commanded', 'system response']
|
||||
return time, result, legend
|
||||
|
||||
|
||||
def plot_shaper(shaper):
|
||||
def plot_shaper(shaper_name, shaper_freq, damping_ratio, test_damping_ratios,
|
||||
system_freq, system_damping_ratio):
|
||||
shaper = get_shaper(shaper_name, shaper_freq, damping_ratio)
|
||||
shift_pulses(shaper)
|
||||
freqs, response, response_legend = gen_shaper_response(shaper)
|
||||
time, step_vals, step_legend = gen_shaped_step_function(shaper)
|
||||
freqs, response, response_legend = gen_shaper_response(
|
||||
shaper, shaper_freq, test_damping_ratios)
|
||||
time, step_vals, step_legend = gen_shaped_step_function(
|
||||
shaper, shaper_freq, system_freq, system_damping_ratio)
|
||||
|
||||
fig, (ax1, ax2) = matplotlib.pyplot.subplots(nrows=2, figsize=(10,9))
|
||||
ax1.set_title("Vibration response simulation for shaper '%s',\n"
|
||||
"shaper_freq=%.1f Hz, damping_ratio=%.3f"
|
||||
% (shaper[-1], SHAPER_FREQ, SHAPER_DAMPING_RATIO))
|
||||
% (shaper_name, shaper_freq, damping_ratio))
|
||||
ax1.plot(freqs, response)
|
||||
ax1.set_ylim(bottom=0.)
|
||||
fontP = matplotlib.font_manager.FontProperties()
|
||||
|
|
@ -241,8 +168,7 @@ def plot_shaper(shaper):
|
|||
ax1.grid(which='minor', color='lightgrey')
|
||||
|
||||
ax2.set_title("Unit step input, resonance frequency=%.1f Hz, "
|
||||
"damping ratio=%.3f" % (STEP_SIMULATION_RESONANCE_FREQ,
|
||||
STEP_SIMULATION_DAMPING_RATIO))
|
||||
"damping ratio=%.3f" % (system_freq, system_damping_ratio))
|
||||
ax2.plot(time, step_vals)
|
||||
ax2.legend(step_legend, loc='best', prop=fontP)
|
||||
ax2.set_xlabel('Time, sec')
|
||||
|
|
@ -264,13 +190,48 @@ def main():
|
|||
opts = optparse.OptionParser(usage)
|
||||
opts.add_option("-o", "--output", type="string", dest="output",
|
||||
default=None, help="filename of output graph")
|
||||
opts.add_option("--shaper", type="string", dest="shaper", default="mzv",
|
||||
help="a shaper to plot")
|
||||
opts.add_option("--shaper_freq", type="float", dest="shaper_freq",
|
||||
default=50.0, help="shaper frequency")
|
||||
opts.add_option("--damping_ratio", type="float", dest="damping_ratio",
|
||||
default=shaper_defs.DEFAULT_DAMPING_RATIO,
|
||||
help="shaper damping_ratio parameter")
|
||||
opts.add_option("--test_damping_ratios", type="string",
|
||||
dest="test_damping_ratios",
|
||||
default=",".join(["%.3f" % dr
|
||||
for dr in DEFAULT_DAMPING_RATIOS]),
|
||||
help="a comma-separated list of damping ratios to test " +
|
||||
"input shaper for")
|
||||
opts.add_option("--system_freq", type="float", dest="system_freq",
|
||||
default=60.0,
|
||||
help="natural frequency of a system for step simulation")
|
||||
opts.add_option("--system_damping_ratio", type="float",
|
||||
dest="system_damping_ratio", default=0.15,
|
||||
help="damping_ratio of a system for step simulation")
|
||||
options, args = opts.parse_args()
|
||||
if len(args) != 0:
|
||||
opts.error("Incorrect number of arguments")
|
||||
|
||||
if options.shaper.lower() not in [
|
||||
s.name for s in shaper_defs.INPUT_SHAPERS]:
|
||||
opts.error("Invalid --shaper=%s specified" % options.shaper)
|
||||
|
||||
if options.test_damping_ratios:
|
||||
try:
|
||||
test_damping_ratios = [float(s) for s in
|
||||
options.test_damping_ratios.split(',')]
|
||||
except ValueError:
|
||||
opts.error("invalid floating point value in " +
|
||||
"--test_damping_ratios param")
|
||||
else:
|
||||
test_damping_ratios = None
|
||||
|
||||
# Draw graph
|
||||
setup_matplotlib(options.output is not None)
|
||||
fig = plot_shaper(get_shaper())
|
||||
fig = plot_shaper(options.shaper, options.shaper_freq,
|
||||
options.damping_ratio, test_damping_ratios,
|
||||
options.system_freq, options.system_damping_ratio)
|
||||
|
||||
# Show graph
|
||||
if options.output is None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue