Skip to content

Commit

Permalink
offload accelerometer finish in its own process
Browse files Browse the repository at this point in the history
  • Loading branch information
Frix-x committed Sep 1, 2024
1 parent fe78456 commit 9836ee5
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 34 deletions.
10 changes: 4 additions & 6 deletions shaketune/commands/axes_map_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# and performs post-processing to analyze the collected data.


from ..helpers.accelerometer import Accelerometer, MeasurementsManager
from ..helpers.accelerometer import Accelerometer
from ..helpers.console_output import ConsoleOutput
from ..shaketune_process import ShakeTuneProcess

Expand Down Expand Up @@ -37,7 +37,7 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
raise gcmd.error(
f'The parameter axes_map is already set in your {accel_chip} configuration! Please remove it (or set it to "x,y,z") to be able to use this macro!'
)
accelerometer = Accelerometer(k_accelerometer)
accelerometer = Accelerometer(k_accelerometer, printer.get_reactor())

toolhead_info = toolhead.get_status(systime)
old_accel = toolhead_info['max_accel']
Expand Down Expand Up @@ -69,14 +69,12 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
toolhead.move([mid_x - SEGMENT_LENGTH / 2, mid_y - SEGMENT_LENGTH / 2, z_height, E], feedrate_travel)
toolhead.dwell(0.5)

measurements_manager = MeasurementsManager()

# Start the measurements and do the movements (+X, +Y and then +Z)
accelerometer.start_recording(measurements_manager, name='axesmap_X', append_time=True)
accelerometer.start_recording(None, name='axesmap_X', append_time=True)
toolhead.dwell(0.5)
toolhead.move([mid_x + SEGMENT_LENGTH / 2, mid_y - SEGMENT_LENGTH / 2, z_height, E], speed)
toolhead.dwell(0.5)
accelerometer.stop_recording()
measurements_manager = accelerometer.stop_recording()
toolhead.dwell(0.5)
accelerometer.start_recording(measurements_manager, name='axesmap_Y', append_time=True)
toolhead.dwell(0.5)
Expand Down
12 changes: 4 additions & 8 deletions shaketune/commands/axes_shaper_calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# and generates graphs for each axis to analyze the collected data.


from ..helpers.accelerometer import Accelerometer, MeasurementsManager
from ..helpers.accelerometer import Accelerometer
from ..helpers.common_func import AXIS_CONFIG
from ..helpers.console_output import ConsoleOutput
from ..helpers.resonance_test import vibrate_axis
Expand Down Expand Up @@ -90,26 +90,22 @@ def axes_shaper_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
else:
input_shaper = None

measurements_manager = MeasurementsManager()

# Filter axis configurations based on user input, assuming 'axis_input' can be 'x', 'y', 'all' (that means 'x' and 'y')
filtered_config = [
a for a in AXIS_CONFIG if a['axis'] == axis_input or (axis_input == 'all' and a['axis'] in ('x', 'y'))
]
for config in filtered_config:
measurements_manager.clear_measurements() # Clear the measurements in each iteration of the loop

# First we need to find the accelerometer chip suited for the axis
accel_chip = Accelerometer.find_axis_accelerometer(printer, config['axis'])
if accel_chip is None:
raise gcmd.error('No suitable accelerometer found for measurement!')
accelerometer = Accelerometer(printer.lookup_object(accel_chip))
accelerometer = Accelerometer(printer.lookup_object(accel_chip), printer.get_reactor())

# Then do the actual measurements
ConsoleOutput.print(f'Measuring {config["label"]}...')
accelerometer.start_recording(measurements_manager, name=config['label'], append_time=True)
accelerometer.start_recording(None, name=config['label'], append_time=True)
vibrate_axis(toolhead, gcode, config['direction'], min_freq, max_freq, hz_per_sec, accel_per_hz)
accelerometer.stop_recording()
measurements_manager = accelerometer.stop_recording()
toolhead.dwell(0.5)
toolhead.wait_moves()

Expand Down
4 changes: 2 additions & 2 deletions shaketune/commands/compare_belts_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None:
raise gcmd.error(
'No suitable accelerometer found for measurement! Multi-accelerometer configurations are not supported for this macro.'
)
accelerometer = Accelerometer(printer.lookup_object(accel_chip))
accelerometer = Accelerometer(printer.lookup_object(accel_chip), printer.get_reactor())

# Move to the starting point
test_points = res_tester.test.get_start_test_points()
Expand Down Expand Up @@ -103,7 +103,7 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None:
else:
input_shaper = None

measurements_manager = MeasurementsManager()
measurements_manager = MeasurementsManager(printer.get_reactor())

# Run the test for each axis
for config in filtered_config:
Expand Down
4 changes: 2 additions & 2 deletions shaketune/commands/create_vibrations_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
toolhead.move([mid_x - 15, mid_y - 15, z_height, E], feedrate_travel)
toolhead.dwell(0.5)

measurements_manager = MeasurementsManager()
measurements_manager = MeasurementsManager(printer.get_reactor())

nb_speed_samples = int((max_speed - MIN_SPEED) / speed_increment + 1)
for curr_angle in main_angles:
Expand All @@ -99,7 +99,7 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
if k_accelerometer is None:
raise gcmd.error(f'Accelerometer [{current_accel_chip}] not found!')
ConsoleOutput.print(f'Accelerometer chip used for this angle: [{current_accel_chip}]')
accelerometer = Accelerometer(k_accelerometer)
accelerometer = Accelerometer(k_accelerometer, printer.get_reactor())

# Sweep the speed range to record the vibrations at different speeds
for curr_speed_sample in range(nb_speed_samples):
Expand Down
9 changes: 4 additions & 5 deletions shaketune/commands/excitate_axis_at_freq.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# and optionally creates a graph of the vibration data collected by the accelerometer.


from ..helpers.accelerometer import Accelerometer, MeasurementsManager
from ..helpers.accelerometer import Accelerometer
from ..helpers.common_func import AXIS_CONFIG
from ..helpers.console_output import ConsoleOutput
from ..helpers.resonance_test import vibrate_axis_at_static_freq
Expand Down Expand Up @@ -41,8 +41,7 @@ def excitate_axis_at_freq(gcmd, config, st_process: ShakeTuneProcess) -> None:
k_accelerometer = printer.lookup_object(accel_chip, None)
if k_accelerometer is None:
raise gcmd.error(f'Accelerometer chip [{accel_chip}] was not found!')
accelerometer = Accelerometer(k_accelerometer)
measurements_manager = MeasurementsManager()
accelerometer = Accelerometer(k_accelerometer, printer.get_reactor())

ConsoleOutput.print(f'Excitating {axis.upper()} axis at {freq}Hz for {duration} seconds')

Expand Down Expand Up @@ -88,7 +87,7 @@ def excitate_axis_at_freq(gcmd, config, st_process: ShakeTuneProcess) -> None:

# If the user want to create a graph, we start accelerometer recording
if create_graph:
accelerometer.start_recording(measurements_manager, name=f'staticfreq_{axis.upper()}', append_time=True)
accelerometer.start_recording(None, name=f'staticfreq_{axis.upper()}', append_time=True)

toolhead.dwell(0.5)
vibrate_axis_at_static_freq(toolhead, gcode, axis_config['direction'], freq, duration, accel_per_hz)
Expand All @@ -100,7 +99,7 @@ def excitate_axis_at_freq(gcmd, config, st_process: ShakeTuneProcess) -> None:

# If the user wanted to create a graph, we stop the recording and generate it
if create_graph:
accelerometer.stop_recording()
measurements_manager = accelerometer.stop_recording()
toolhead.dwell(0.5)

creator = st_process.get_graph_creator()
Expand Down
50 changes: 39 additions & 11 deletions shaketune/helpers/accelerometer.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class Measurement(TypedDict):


class MeasurementsManager:
def __init__(self):
def __init__(self, klipper_reactor):
self._reactor = klipper_reactor
self.measurements: List[Measurement] = []
self._write_process = None

Expand Down Expand Up @@ -124,16 +125,16 @@ def load_from_csvs(self, klipper_CSVs: List[Path]) -> List[Measurement]:

return self.measurements

def wait_for_file_writes(self, k_reactor, timeout: int = 20):
def wait_for_file_writes(self, timeout: int = 20):
if self._write_process is None:
return # No file write is pending

eventtime = k_reactor.monotonic()
eventtime = self._reactor.monotonic()
endtime = eventtime + timeout
complete = False

while eventtime < endtime:
eventtime = k_reactor.pause(eventtime + 0.05)
eventtime = self._reactor.pause(eventtime + 0.05)
if not self._write_process.is_alive():
complete = True
break
Expand All @@ -148,8 +149,9 @@ def wait_for_file_writes(self, k_reactor, timeout: int = 20):


class Accelerometer:
def __init__(self, klipper_accelerometer):
def __init__(self, klipper_accelerometer, klipper_reactor):
self._k_accelerometer = klipper_accelerometer
self._reactor = klipper_reactor
self._bg_client = None
self._measurements_manager: MeasurementsManager = None

Expand All @@ -163,7 +165,12 @@ def find_axis_accelerometer(printer, axis: str = 'xy'):
return chip_name
return None

def start_recording(self, measurements_manager: MeasurementsManager, name: str = None, append_time: bool = True):
def start_recording(
self, measurements_manager: MeasurementsManager = None, name: str = None, append_time: bool = True
):
if measurements_manager is None:
measurements_manager = MeasurementsManager(self._reactor)

if self._bg_client is None:
self._bg_client = self._k_accelerometer.start_internal_client()

Expand All @@ -181,18 +188,39 @@ def start_recording(self, measurements_manager: MeasurementsManager, name: str =
else:
raise ValueError('Recording already started!')

def stop_recording(self) -> MeasurementsManager:
def stop_recording(self, timeout: int = 20) -> MeasurementsManager:
if self._bg_client is None:
ConsoleOutput.print('Warning: no recording to stop!')
return None

# Move sample retrieval and processing to a sub-process to reduce Klipper's main process load
offload_process = Process(target=self._retrieve_and_process_samples)
offload_process.start()

eventtime = self._reactor.monotonic()
endtime = eventtime + timeout
complete = False
while eventtime < endtime:
eventtime = self._reactor.pause(eventtime + 0.05)
if not offload_process.is_alive():
complete = True
break

if not complete:
raise TimeoutError(
'Shake&Tune was unable to retrieve the accelerometer '
'data and process it within the specified timeout!'
)

m_manager = self._measurements_manager
self._measurements_manager = None

return m_manager

def _retrieve_and_process_samples(self):
bg_client = self._bg_client
self._bg_client = None
bg_client.finish_measurements()

samples = bg_client.samples or bg_client.get_samples()
self._measurements_manager.append_samples_to_last_measurement(samples)
m_manager = self._measurements_manager
self._measurements_manager = None

return m_manager

0 comments on commit 9836ee5

Please sign in to comment.