fixed most of the bugs now as a Klipper plugin
This commit is contained in:
@@ -14,6 +14,14 @@ from scipy.signal import spectrogram
|
||||
|
||||
from .console_output import ConsoleOutput
|
||||
|
||||
# Constant used to define the standard axis direction and names
|
||||
AXIS_CONFIG = [
|
||||
{'axis': 'x', 'direction': (1, 0, 0), 'label': 'axis_X'},
|
||||
{'axis': 'y', 'direction': (0, 1, 0), 'label': 'axis_Y'},
|
||||
{'axis': 'a', 'direction': (1, -1, 0), 'label': 'belt_A'},
|
||||
{'axis': 'b', 'direction': (1, 1, 0), 'label': 'belt_B'},
|
||||
]
|
||||
|
||||
|
||||
def parse_log(logname):
|
||||
try:
|
||||
|
||||
@@ -5,20 +5,3 @@ from .axes_map import axes_map_calibration as axes_map_calibration
|
||||
from .belts_comparison import compare_belts_responses as compare_belts_responses
|
||||
from .static_freq import excitate_axis_at_freq as excitate_axis_at_freq
|
||||
from .vibrations_profile import create_vibrations_profile as create_vibrations_profile
|
||||
|
||||
AXIS_CONFIG = [
|
||||
{'axis': 'x', 'direction': (1, 0, 0), 'label': 'axis_X'},
|
||||
{'axis': 'y', 'direction': (0, 1, 0), 'label': 'axis_Y'},
|
||||
{'axis': 'a', 'direction': (1, -1, 0), 'label': 'belt_A'},
|
||||
{'axis': 'b', 'direction': (1, 1, 0), 'label': 'belt_B'},
|
||||
]
|
||||
|
||||
# graph_creators = {
|
||||
# 'axesmap': (AxesMapFinder, lambda gc: gc.configure(options.accel_used, options.chip_name)),
|
||||
# 'belts': (BeltsGraphCreator, None),
|
||||
# 'shaper': (ShaperGraphCreator, lambda gc: gc.configure(options.scv, options.max_smoothing)),
|
||||
# 'vibrations': (
|
||||
# VibrationsGraphCreator,
|
||||
# lambda gc: gc.configure(options.kinematics, options.accel_used, options.chip_name, options.metadata),
|
||||
# ),
|
||||
# }
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
from ..helpers.common_func import AXIS_CONFIG
|
||||
from ..helpers.console_output import ConsoleOutput
|
||||
from ..shaketune_thread import ShakeTuneThread
|
||||
from . import AXIS_CONFIG
|
||||
from .accelerometer import Accelerometer
|
||||
from .resonance_test import vibrate_axis
|
||||
|
||||
|
||||
def axes_shaper_calibration(gcmd, gcode, printer, st_thread: ShakeTuneThread) -> None:
|
||||
def axes_shaper_calibration(gcmd, config, st_thread: ShakeTuneThread) -> None:
|
||||
min_freq = gcmd.get_float('FREQ_START', default=5, minval=1)
|
||||
max_freq = gcmd.get_float('FREQ_END', default=133.33, minval=1)
|
||||
hz_per_sec = gcmd.get_float('HZ_PER_SEC', default=1, minval=1)
|
||||
@@ -21,9 +21,11 @@ def axes_shaper_calibration(gcmd, gcode, printer, st_thread: ShakeTuneThread) ->
|
||||
feedrate_travel = gcmd.get_float('TRAVEL_SPEED', default=120.0, minval=20.0)
|
||||
z_height = gcmd.get_float('Z_HEIGHT', default=None, minval=1)
|
||||
|
||||
systime = printer.get_reactor().monotonic()
|
||||
printer = config.get_printer()
|
||||
gcode = printer.lookup_object('gcode')
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
res_tester = printer.lookup_object('resonance_tester')
|
||||
systime = printer.get_reactor().monotonic()
|
||||
|
||||
if scv is None:
|
||||
toolhead_info = toolhead.get_status(systime)
|
||||
@@ -92,7 +94,7 @@ def axes_shaper_calibration(gcmd, gcode, printer, st_thread: ShakeTuneThread) ->
|
||||
accelerometer.stop_measurement(config['label'], append_time=True)
|
||||
|
||||
# And finally generate the graph for each measured axis
|
||||
ConsoleOutput.print(f'{config['axis'].upper()} axis frequency profile generation...')
|
||||
ConsoleOutput.print(f'{config["axis"].upper()} axis frequency profile generation...')
|
||||
ConsoleOutput.print('This may take some time (1-3min)')
|
||||
st_thread.run()
|
||||
|
||||
|
||||
@@ -6,13 +6,18 @@ from ..shaketune_thread import ShakeTuneThread
|
||||
from .accelerometer import Accelerometer
|
||||
|
||||
|
||||
def axes_map_calibration(gcmd, gcode, printer, st_thread: ShakeTuneThread) -> None:
|
||||
def axes_map_calibration(gcmd, config, st_thread: ShakeTuneThread) -> None:
|
||||
z_height = gcmd.get_float('Z_HEIGHT', default=20.0)
|
||||
speed = gcmd.get_float('SPEED', default=80.0, minval=20.0)
|
||||
accel = gcmd.get_int('ACCEL', default=1500, minval=100)
|
||||
feedrate_travel = gcmd.get_float('TRAVEL_SPEED', default=120.0, minval=20.0)
|
||||
accel_chip = gcmd.get('ACCEL_CHIP', default=None)
|
||||
|
||||
printer = config.get_printer()
|
||||
gcode = printer.lookup_object('gcode')
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
systime = printer.get_reactor().monotonic()
|
||||
|
||||
if accel_chip is None:
|
||||
accel_chip = Accelerometer.find_axis_accelerometer(printer, 'xy')
|
||||
if accel_chip is None:
|
||||
@@ -21,8 +26,6 @@ def axes_map_calibration(gcmd, gcode, printer, st_thread: ShakeTuneThread) -> No
|
||||
)
|
||||
accelerometer = Accelerometer(printer.lookup_object(accel_chip))
|
||||
|
||||
systime = printer.get_reactor().monotonic()
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
toolhead_info = toolhead.get_status(systime)
|
||||
old_accel = toolhead_info['max_accel']
|
||||
old_mcr = toolhead_info['minimum_cruise_ratio']
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
from ..helpers.common_func import AXIS_CONFIG
|
||||
from ..helpers.console_output import ConsoleOutput
|
||||
from ..shaketune_thread import ShakeTuneThread
|
||||
from . import AXIS_CONFIG
|
||||
from .accelerometer import Accelerometer
|
||||
from .resonance_test import vibrate_axis
|
||||
|
||||
|
||||
def compare_belts_responses(gcmd, gcode, printer, st_thread: ShakeTuneThread) -> None:
|
||||
def compare_belts_responses(gcmd, config, st_thread: ShakeTuneThread) -> None:
|
||||
min_freq = gcmd.get_float('FREQ_START', default=5.0, minval=1)
|
||||
max_freq = gcmd.get_float('FREQ_END', default=133.33, minval=1)
|
||||
hz_per_sec = gcmd.get_float('HZ_PER_SEC', default=1.0, minval=1)
|
||||
@@ -16,9 +16,11 @@ def compare_belts_responses(gcmd, gcode, printer, st_thread: ShakeTuneThread) ->
|
||||
feedrate_travel = gcmd.get_float('TRAVEL_SPEED', default=120.0, minval=20.0)
|
||||
z_height = gcmd.get_float('Z_HEIGHT', default=None, minval=1)
|
||||
|
||||
systime = printer.get_reactor().monotonic()
|
||||
printer = config.get_printer()
|
||||
gcode = printer.lookup_object('gcode')
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
res_tester = printer.lookup_object('resonance_tester')
|
||||
systime = printer.get_reactor().monotonic()
|
||||
|
||||
accel_chip = Accelerometer.find_axis_accelerometer(printer, 'xy')
|
||||
if accel_chip is None:
|
||||
@@ -68,7 +70,7 @@ def compare_belts_responses(gcmd, gcode, printer, st_thread: ShakeTuneThread) ->
|
||||
input_shaper = None
|
||||
|
||||
# Filter axis configurations to get the A and B axis only
|
||||
filtered_config = [a for a in AXIS_CONFIG if a['axis'] in ('x', 'y')]
|
||||
filtered_config = [a for a in AXIS_CONFIG if a['axis'] in ('a', 'b')]
|
||||
for config in filtered_config:
|
||||
accelerometer.start_measurement()
|
||||
vibrate_axis(toolhead, gcode, config['direction'], min_freq, max_freq, hz_per_sec, accel_per_hz)
|
||||
|
||||
8
shaketune/measurement/macros.cfg
Normal file
8
shaketune/measurement/macros.cfg
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
# [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
|
||||
@@ -4,8 +4,7 @@
|
||||
# from the Klipper configuration and the TMC registers
|
||||
# Written by Frix_x#0161 #
|
||||
|
||||
import re
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
TRINAMIC_DRIVERS = ['tmc2130', 'tmc2208', 'tmc2209', 'tmc2240', 'tmc2660', 'tmc5160']
|
||||
MOTORS = ['stepper_x', 'stepper_y', 'stepper_x1', 'stepper_y1', 'stepper_z', 'stepper_z1', 'stepper_z2', 'stepper_z3']
|
||||
@@ -17,25 +16,20 @@ class Motor:
|
||||
self.name: str = name
|
||||
self._registers: Dict[str, Dict[str, Any]] = {}
|
||||
self._config: Dict[str, Any] = {}
|
||||
self._driver: Tuple[str, Any] = ('', None)
|
||||
|
||||
def set_driver(self, driver_name: str, tmc_object: Any) -> None:
|
||||
self._driver = (driver_name, tmc_object)
|
||||
|
||||
def get_driver(self) -> Tuple[str, Any]:
|
||||
return self._driver
|
||||
|
||||
def set_register(self, register: str, value_dict: dict) -> None:
|
||||
# First we filter out entries with a value of 0 to avoid having too much uneeded data
|
||||
value_dict = {k: v for k, v in value_dict.items() if v != 0}
|
||||
|
||||
# Special parsing for CHOPCONF to extract meaningful values
|
||||
if register == 'CHOPCONF':
|
||||
# Add intpol=0 if missing from the register dump
|
||||
if 'intpol=' not in value_dict:
|
||||
# Add intpol=0 if missing from the register dump to force printing it as it's important
|
||||
if 'intpol' not in value_dict:
|
||||
value_dict['intpol'] = '0'
|
||||
# Simplify the microstep resolution format
|
||||
# Remove the microsteps entry as the format here is not easy to read and
|
||||
# it's already read in the correct format directly from the Klipper config
|
||||
if 'mres' in value_dict:
|
||||
mres_match = re.search(r'(\d+)usteps', value_dict['mres'])
|
||||
if mres_match:
|
||||
value_dict['mres'] = mres_match.group(1)
|
||||
del value_dict['mres']
|
||||
|
||||
# Special parsing for CHOPCONF to avoid pwm_ before each values
|
||||
if register == 'PWMCONF':
|
||||
@@ -46,7 +40,7 @@ class Motor:
|
||||
new_value_dict[key] = val
|
||||
value_dict = new_value_dict
|
||||
|
||||
# Then fill the registers while merging all the thresholds into the same THRS virtual register
|
||||
# Then gets merged all the thresholds into the same THRS virtual register
|
||||
if register in ['TPWMTHRS', 'TCOOLTHRS']:
|
||||
existing_thrs = self._registers.get('THRS', {})
|
||||
merged_values = {**existing_thrs, **value_dict}
|
||||
@@ -110,35 +104,42 @@ class Motor:
|
||||
|
||||
|
||||
class MotorsConfigParser:
|
||||
def __init__(self, printer, motors: List[str] = MOTORS, drivers: List[str] = TRINAMIC_DRIVERS):
|
||||
def __init__(self, config, motors: List[str] = MOTORS, drivers: List[str] = TRINAMIC_DRIVERS):
|
||||
self._printer = config.get_printer()
|
||||
|
||||
self._motors: List[Motor] = []
|
||||
self._printer = printer
|
||||
|
||||
for motor_name in motors:
|
||||
for driver in drivers:
|
||||
tmc_object = printer.lookup_object(f'{driver} {motor_name}', None)
|
||||
tmc_object = self._printer.lookup_object(f'{driver} {motor_name}', None)
|
||||
if tmc_object is None:
|
||||
continue
|
||||
motor = self._create_motor(motor_name, driver, tmc_object)
|
||||
self._motors.append(motor)
|
||||
|
||||
pconfig = self._printer.lookup_object('configfile')
|
||||
self.kinematics = pconfig.status_raw_config['printer']['kinematics']
|
||||
|
||||
# Create a Motor object with the given name, driver and TMC object
|
||||
# and fill it with the relevant configuration and registers
|
||||
def _create_motor(self, motor_name: str, driver: str, tmc_object: Any) -> Motor:
|
||||
motor = Motor(motor_name)
|
||||
motor.set_driver(driver.upper(), tmc_object)
|
||||
motor.set_config('tmc', driver)
|
||||
self._parse_klipper_config(motor, tmc_object)
|
||||
self._parse_tmc_registers(motor, tmc_object)
|
||||
return motor
|
||||
|
||||
def _parse_klipper_config(self, motor: Motor, tmc: Any) -> None:
|
||||
def _parse_klipper_config(self, motor: Motor, tmc_object: Any) -> None:
|
||||
# The TMCCommandHelper isn't a direct member of the TMC object... but we can still get it this way
|
||||
tmc_cmdhelper = tmc.get_status.__self__
|
||||
tmc_cmdhelper = tmc_object.get_status.__self__
|
||||
|
||||
motor_currents = tmc_cmdhelper.current_helper.get_current()
|
||||
motor.set_config('run_current', motor_currents[0])
|
||||
motor.set_config('hold_current', motor_currents[1])
|
||||
|
||||
pconfig = self._printer.lookup_object('configfile')
|
||||
motor.set_config('microsteps', int(pconfig.status_raw_config[motor.name]['microsteps']))
|
||||
|
||||
autotune_object = self._printer.lookup_object(f'autotune_tmc {motor.name}', None)
|
||||
if autotune_object is not None:
|
||||
motor.set_config('autotune_enabled', True)
|
||||
@@ -147,24 +148,21 @@ class MotorsConfigParser:
|
||||
else:
|
||||
motor.set_config('autotune_enabled', False)
|
||||
|
||||
def _parse_tmc_registers(self, motor: Motor, tmc: Any) -> None:
|
||||
def _parse_tmc_registers(self, motor: Motor, tmc_object: Any) -> None:
|
||||
# The TMCCommandHelper isn't a direct member of the TMC object... but we can still get it this way
|
||||
tmc_cmdhelper = tmc.get_status.__self__
|
||||
tmc_cmdhelper = tmc_object.get_status.__self__
|
||||
|
||||
for register in RELEVANT_TMC_REGISTERS:
|
||||
# value = tmc_cmdhelper.read_register(register)
|
||||
# motor.set_register(register, value)
|
||||
|
||||
val = tmc_cmdhelper.fields.registers.get(register)
|
||||
if (val is not None) and (register not in tmc_cmdhelper.read_registers):
|
||||
# write-only register
|
||||
fields_string = self._extract_register_values(register, val)
|
||||
fields_string = self._extract_register_values(tmc_cmdhelper, register, val)
|
||||
elif register in tmc_cmdhelper.read_registers:
|
||||
# readable register
|
||||
val = tmc_cmdhelper.mcu_tmc.get_register(register)
|
||||
if tmc_cmdhelper.read_translate is not None:
|
||||
register, val = tmc_cmdhelper.read_translate(register, val)
|
||||
fields_string = self._extract_register_values(register, val)
|
||||
fields_string = self._extract_register_values(tmc_cmdhelper, register, val)
|
||||
|
||||
motor.set_register(register, fields_string)
|
||||
|
||||
@@ -173,7 +171,7 @@ class MotorsConfigParser:
|
||||
reg_fields = tmc_cmdhelper.fields.all_fields.get(register, {})
|
||||
reg_fields = sorted([(mask, name) for name, mask in reg_fields.items()])
|
||||
fields = {}
|
||||
for mask, field_name in reg_fields:
|
||||
for _, field_name in reg_fields:
|
||||
field_value = tmc_cmdhelper.fields.get_field(field_name, val, register)
|
||||
fields[field_name] = field_value
|
||||
return fields
|
||||
@@ -181,7 +179,7 @@ class MotorsConfigParser:
|
||||
# Find and return the motor by its name
|
||||
def get_motor(self, motor_name: str) -> Optional[Motor]:
|
||||
for motor in self._motors:
|
||||
if motor._name == motor_name:
|
||||
if motor.name == motor_name:
|
||||
return motor
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from ..helpers.common_func import AXIS_CONFIG
|
||||
from ..helpers.console_output import ConsoleOutput
|
||||
from . import AXIS_CONFIG
|
||||
from .resonance_test import vibrate_axis
|
||||
|
||||
|
||||
def excitate_axis_at_freq(gcmd, gcode, printer) -> None:
|
||||
def excitate_axis_at_freq(gcmd, config) -> None:
|
||||
freq = gcmd.get_int('FREQUENCY', default=25, minval=1)
|
||||
duration = gcmd.get_int('DURATION', default=10, minval=1)
|
||||
accel_per_hz = gcmd.get_float('ACCEL_PER_HZ', default=None)
|
||||
@@ -19,9 +19,11 @@ def excitate_axis_at_freq(gcmd, gcode, printer) -> None:
|
||||
|
||||
ConsoleOutput.print(f'Excitating {axis.upper()} axis at {freq}Hz for {duration} seconds')
|
||||
|
||||
systime = printer.get_reactor().monotonic()
|
||||
printer = config.get_printer()
|
||||
gcode = printer.lookup_object('gcode')
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
res_tester = printer.lookup_object('resonance_tester')
|
||||
systime = printer.get_reactor().monotonic()
|
||||
|
||||
if accel_per_hz is None:
|
||||
accel_per_hz = res_tester.test.accel_per_hz
|
||||
|
||||
@@ -11,7 +11,7 @@ from .motorsconfigparser import MotorsConfigParser
|
||||
MIN_SPEED = 2 # mm/s
|
||||
|
||||
|
||||
def create_vibrations_profile(gcmd, gcode, printer, st_thread: ShakeTuneThread) -> None:
|
||||
def create_vibrations_profile(gcmd, config, st_thread: ShakeTuneThread) -> None:
|
||||
size = gcmd.get_float('SIZE', default=100.0, minval=50.0)
|
||||
z_height = gcmd.get_float('Z_HEIGHT', default=20.0)
|
||||
max_speed = gcmd.get_float('MAX_SPEED', default=200.0, minval=10.0)
|
||||
@@ -23,29 +23,30 @@ def create_vibrations_profile(gcmd, gcode, printer, st_thread: ShakeTuneThread)
|
||||
if (size / (max_speed / 60)) < 0.25:
|
||||
gcmd.error('The size of the movement is too small for the given speed! Increase SIZE or decrease MAX_SPEED!')
|
||||
|
||||
# Check that input shaper is already configured
|
||||
printer = config.get_printer()
|
||||
gcode = printer.lookup_object('gcode')
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
input_shaper = printer.lookup_object('input_shaper', None)
|
||||
systime = printer.get_reactor().monotonic()
|
||||
|
||||
# Check that input shaper is already configured
|
||||
if input_shaper is None:
|
||||
gcmd.error('Input shaper is not configured! Please run the shaper calibration macro first.')
|
||||
|
||||
# TODO: Add the kinematics check to define the main_angles
|
||||
# but this needs to retrieve it from the printer configuration
|
||||
# {% if kinematics == "cartesian" %}
|
||||
# # Cartesian motors are on X and Y axis directly
|
||||
# RESPOND MSG="Cartesian kinematics mode"
|
||||
# {% set main_angles = [0, 90] %}
|
||||
# {% elif kinematics == "corexy" %}
|
||||
# # CoreXY motors are on A and B axis (45 and 135 degrees)
|
||||
# RESPOND MSG="CoreXY kinematics mode"
|
||||
# {% set main_angles = [45, 135] %}
|
||||
# {% else %}
|
||||
# { action_raise_error("Only Cartesian and CoreXY kinematics are supported at the moment for the vibrations measurement tool!") }
|
||||
# {% endif %}
|
||||
kinematics = 'cartesian'
|
||||
motors_config_parser = MotorsConfigParser(config, motors=['stepper_x', 'stepper_y'])
|
||||
|
||||
if motors_config_parser.kinematics == 'cartesian' or motors_config_parser.kinematics == 'corexz':
|
||||
# Cartesian motors are on X and Y axis directly, same for CoreXZ
|
||||
main_angles = [0, 90]
|
||||
elif motors_config_parser.kinematics == 'corexy':
|
||||
# CoreXY motors are on A and B axis (45 and 135 degrees)
|
||||
main_angles = [45, 135]
|
||||
else:
|
||||
gcmd.error(
|
||||
'Only Cartesian and CoreXY kinematics are supported at the moment for the vibrations measurement tool!'
|
||||
)
|
||||
ConsoleOutput.print(f'{motors_config_parser.kinematics.upper()} kinematics mode')
|
||||
|
||||
systime = printer.get_reactor().monotonic()
|
||||
toolhead = printer.lookup_object('toolhead')
|
||||
toolhead_info = toolhead.get_status(systime)
|
||||
old_accel = toolhead_info['max_accel']
|
||||
old_mcr = toolhead_info['minimum_cruise_ratio']
|
||||
@@ -66,6 +67,7 @@ def create_vibrations_profile(gcmd, gcode, printer, st_thread: ShakeTuneThread)
|
||||
|
||||
nb_speed_samples = int((max_speed - MIN_SPEED) / speed_increment + 1)
|
||||
for curr_angle in main_angles:
|
||||
ConsoleOutput.print(f'-> Measuring angle: {curr_angle} degrees...')
|
||||
radian_angle = math.radians(curr_angle)
|
||||
|
||||
# Find the best accelerometer chip for the current angle if not specified
|
||||
@@ -86,6 +88,7 @@ def create_vibrations_profile(gcmd, gcode, printer, st_thread: ShakeTuneThread)
|
||||
# Sweep the speed range to record the vibrations at different speeds
|
||||
for curr_speed_sample in range(nb_speed_samples):
|
||||
curr_speed = MIN_SPEED + curr_speed_sample * speed_increment
|
||||
ConsoleOutput.print(f'Current speed: {curr_speed} mm/s')
|
||||
|
||||
# Reduce the segments length for the lower speed range (0-100mm/s). The minimum length is 1/3 of the SIZE and is gradually increased
|
||||
# to the nominal SIZE at 100mm/s. No further size changes are made above this speed. The goal is to ensure that the print head moves
|
||||
@@ -126,12 +129,9 @@ def create_vibrations_profile(gcmd, gcode, printer, st_thread: ShakeTuneThread)
|
||||
)
|
||||
toolhead.wait_moves()
|
||||
|
||||
# Get the motors and TMC configurations from Klipper
|
||||
motors_config_parser = MotorsConfigParser(printer, motors=['stepper_x', 'stepper_y'])
|
||||
|
||||
# Run post-processing
|
||||
ConsoleOutput.print('Machine vibrations profile generation...')
|
||||
ConsoleOutput.print('This may take some time (5-8min)')
|
||||
creator = st_thread.get_graph_creator()
|
||||
creator.configure(kinematics, accel, motors_config_parser)
|
||||
creator.configure(motors_config_parser.kinematics, accel, motors_config_parser)
|
||||
st_thread.run()
|
||||
|
||||
@@ -109,7 +109,8 @@ def axesmap_calibration(lognames, accel=None):
|
||||
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'
|
||||
results = f'Be aware that this macro is experimental and has been known to sometimes produce incorrect results. Use it with caution and always check the results!\n'
|
||||
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"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import abc
|
||||
import re
|
||||
import shutil
|
||||
import tarfile
|
||||
from datetime import datetime
|
||||
@@ -189,6 +190,7 @@ class VibrationsGraphCreator(GraphCreator):
|
||||
with tarfile.open(tar_path, 'w:gz') as tar:
|
||||
for csv_file in lognames:
|
||||
tar.add(csv_file, arcname=csv_file.name, recursive=False)
|
||||
csv_file.unlink()
|
||||
|
||||
def create_graph(self) -> None:
|
||||
if not self._accel or not self._kinematics:
|
||||
@@ -197,7 +199,7 @@ class VibrationsGraphCreator(GraphCreator):
|
||||
lognames = self._move_and_prepare_files(
|
||||
glob_pattern='shaketune-vib_*.csv',
|
||||
min_files_required=None,
|
||||
custom_name_func=lambda f: f.name,
|
||||
custom_name_func=lambda f: re.search(r'shaketune-vib_(.*?)_\d{8}_\d{6}', f.name).group(1),
|
||||
)
|
||||
fig = vibrations_profile(
|
||||
lognames=[str(path) for path in lognames],
|
||||
|
||||
@@ -564,23 +564,23 @@ def plot_motor_config_txt(fig, motors, differences):
|
||||
motor_details = [(motors[0], 'X motor'), (motors[1], 'Y motor')]
|
||||
|
||||
distance = 0.12
|
||||
if motors[0].get_property('autotune_enabled'):
|
||||
distance = 0.24
|
||||
if motors[0].get_config('autotune_enabled'):
|
||||
distance = 0.27
|
||||
config_blocks = [
|
||||
f"| {lbl}: {mot.get_property('motor').upper()} on {mot.get_property('tmc').upper()} @ {mot.get_property('voltage')}V {mot.get_property('run_current')}A"
|
||||
f"| {lbl}: {mot.get_config('motor').upper()} on {mot.get_config('tmc').upper()} @ {mot.get_config('voltage'):0.1f}V {mot.get_config('run_current'):0.2f}A - {mot.get_config('microsteps')}usteps"
|
||||
for mot, lbl in motor_details
|
||||
]
|
||||
config_blocks.append('| TMC Autotune enabled')
|
||||
else:
|
||||
config_blocks = [
|
||||
f"| {lbl}: {mot.get_property('tmc').upper()} @ {mot.get_property('run_current')}A"
|
||||
f"| {lbl}: {mot.get_config('tmc').upper()} @ {mot.get_config('run_current'):0.2f}A - {mot.get_config('microsteps')}usteps"
|
||||
for mot, lbl in motor_details
|
||||
]
|
||||
config_blocks.append('| TMC Autotune not detected')
|
||||
|
||||
for idx, block in enumerate(config_blocks):
|
||||
fig.text(
|
||||
0.40, 0.990 - 0.015 * idx, block, ha='left', va='top', fontsize=10, color=KLIPPAIN_COLORS['dark_purple']
|
||||
0.41, 0.990 - 0.015 * idx, block, ha='left', va='top', fontsize=10, color=KLIPPAIN_COLORS['dark_purple']
|
||||
)
|
||||
|
||||
tmc_registers = motors[0].get_registers()
|
||||
@@ -589,7 +589,7 @@ def plot_motor_config_txt(fig, motors, differences):
|
||||
settings_str = ' '.join(f'{k}={v}' for k, v in settings.items())
|
||||
tmc_block = f'| {register.upper()}: {settings_str}'
|
||||
fig.text(
|
||||
0.40 + distance,
|
||||
0.41 + distance,
|
||||
0.990 - 0.015 * idx,
|
||||
tmc_block,
|
||||
ha='left',
|
||||
@@ -601,7 +601,7 @@ def plot_motor_config_txt(fig, motors, differences):
|
||||
if differences is not None:
|
||||
differences_text = f'| Y motor diff: {differences}'
|
||||
fig.text(
|
||||
0.40 + distance,
|
||||
0.41 + distance,
|
||||
0.990 - 0.015 * (idx + 1),
|
||||
differences_text,
|
||||
ha='left',
|
||||
|
||||
@@ -18,8 +18,9 @@ from .shaketune_thread import ShakeTuneThread
|
||||
|
||||
class ShakeTune:
|
||||
def __init__(self, config) -> None:
|
||||
self._pconfig = config
|
||||
self._printer = config.get_printer()
|
||||
self._gcode = self._printer.lookup_object('gcode')
|
||||
gcode = self._printer.lookup_object('gcode')
|
||||
|
||||
res_tester = self._printer.lookup_object('resonance_tester')
|
||||
if res_tester is None:
|
||||
@@ -34,29 +35,29 @@ class ShakeTune:
|
||||
dpi = config.getint('dpi', default=150, minval=100, maxval=500)
|
||||
|
||||
self._config = ShakeTuneConfig(result_folder_path, keep_n_results, keep_csv, dpi)
|
||||
ConsoleOutput.register_output_callback(self._gcode.respond_info)
|
||||
ConsoleOutput.register_output_callback(gcode.respond_info)
|
||||
|
||||
self._gcode.register_command(
|
||||
gcode.register_command(
|
||||
'EXCITATE_AXIS_AT_FREQ',
|
||||
self.cmd_EXCITATE_AXIS_AT_FREQ,
|
||||
desc=self.cmd_EXCITATE_AXIS_AT_FREQ_help,
|
||||
)
|
||||
self._gcode.register_command(
|
||||
gcode.register_command(
|
||||
'AXES_MAP_CALIBRATION',
|
||||
self.cmd_AXES_MAP_CALIBRATION,
|
||||
desc=self.cmd_AXES_MAP_CALIBRATION_help,
|
||||
)
|
||||
self._gcode.register_command(
|
||||
gcode.register_command(
|
||||
'COMPARE_BELTS_RESPONSES',
|
||||
self.cmd_COMPARE_BELTS_RESPONSES,
|
||||
desc=self.cmd_COMPARE_BELTS_RESPONSES_help,
|
||||
)
|
||||
self._gcode.register_command(
|
||||
gcode.register_command(
|
||||
'AXES_SHAPER_CALIBRATION',
|
||||
self.cmd_AXES_SHAPER_CALIBRATION,
|
||||
desc=self.cmd_AXES_SHAPER_CALIBRATION_help,
|
||||
)
|
||||
self._gcode.register_command(
|
||||
gcode.register_command(
|
||||
'CREATE_VIBRATIONS_PROFILE',
|
||||
self.cmd_CREATE_VIBRATIONS_PROFILE,
|
||||
desc=self.cmd_CREATE_VIBRATIONS_PROFILE_help,
|
||||
@@ -68,7 +69,7 @@ class ShakeTune:
|
||||
|
||||
def cmd_EXCITATE_AXIS_AT_FREQ(self, gcmd) -> None:
|
||||
ConsoleOutput.print(f'Shake&Tune version: {ShakeTuneConfig.get_git_version()}')
|
||||
excitate_axis_at_freq(gcmd, self._gcode, self._printer)
|
||||
excitate_axis_at_freq(gcmd, self._pconfig)
|
||||
|
||||
cmd_AXES_MAP_CALIBRATION_help = 'Perform a set of movements to measure the orientation of the accelerometer and help you set the best axes_map configuration for your printer'
|
||||
|
||||
@@ -76,7 +77,7 @@ class ShakeTune:
|
||||
ConsoleOutput.print(f'Shake&Tune version: {ShakeTuneConfig.get_git_version()}')
|
||||
axes_map_finder = AxesMapFinder(self._config)
|
||||
st_thread = ShakeTuneThread(self._config, axes_map_finder, self._printer.get_reactor(), self.timeout)
|
||||
axes_map_calibration(gcmd, self._gcode, self._printer, st_thread)
|
||||
axes_map_calibration(gcmd, self._pconfig, st_thread)
|
||||
|
||||
cmd_COMPARE_BELTS_RESPONSES_help = 'Perform a custom half-axis test to analyze and compare the frequency profiles of individual belts on CoreXY printers'
|
||||
|
||||
@@ -84,7 +85,7 @@ class ShakeTune:
|
||||
ConsoleOutput.print(f'Shake&Tune version: {ShakeTuneConfig.get_git_version()}')
|
||||
belt_graph_creator = BeltsGraphCreator(self._config)
|
||||
st_thread = ShakeTuneThread(self._config, belt_graph_creator, self._printer.get_reactor(), self.timeout)
|
||||
compare_belts_responses(gcmd, self._gcode, self._printer, st_thread)
|
||||
compare_belts_responses(gcmd, self._pconfig, st_thread)
|
||||
|
||||
cmd_AXES_SHAPER_CALIBRATION_help = (
|
||||
'Perform standard axis input shaper tests on one or both XY axes to select the best input shaper filter'
|
||||
@@ -94,7 +95,7 @@ class ShakeTune:
|
||||
ConsoleOutput.print(f'Shake&Tune version: {ShakeTuneConfig.get_git_version()}')
|
||||
shaper_graph_creator = ShaperGraphCreator(self._config)
|
||||
st_thread = ShakeTuneThread(self._config, shaper_graph_creator, self._printer.get_reactor(), self.timeout)
|
||||
axes_shaper_calibration(gcmd, self._gcode, self._printer, st_thread)
|
||||
axes_shaper_calibration(gcmd, self._pconfig, st_thread)
|
||||
|
||||
cmd_CREATE_VIBRATIONS_PROFILE_help = 'Perform a set of movements to measure the orientation of the accelerometer and help you set the best axes_map configuration for your printer'
|
||||
|
||||
@@ -102,4 +103,4 @@ class ShakeTune:
|
||||
ConsoleOutput.print(f'Shake&Tune version: {ShakeTuneConfig.get_git_version()}')
|
||||
vibration_profile_creator = VibrationsGraphCreator(self._config)
|
||||
st_thread = ShakeTuneThread(self._config, vibration_profile_creator, self._printer.get_reactor(), self.timeout)
|
||||
create_vibrations_profile(gcmd, self._gcode, self._printer, st_thread)
|
||||
create_vibrations_profile(gcmd, self._pconfig, st_thread)
|
||||
|
||||
Reference in New Issue
Block a user