Merge pull request #136 from Frix-x/develop

v4.0.2
This commit is contained in:
Félix Boisselier
2024-06-27 22:33:20 +02:00
committed by GitHub
12 changed files with 201 additions and 41 deletions

69
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Smoke Tests
on:
workflow_dispatch:
push:
jobs:
klippy_testing:
name: Klippy Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
klipper_repo:
- klipper3d/klipper
- DangerKlippers/danger-klipper
steps:
- name: Checkout shaketune
uses: actions/checkout@v4
with:
path: shaketune
- name: Checkout Klipper
uses: actions/checkout@v4
with:
path: klipper
repository: ${{ matrix.klipper_repo }}
ref: master
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential
- name: Build klipper dict
run: |
pushd klipper
cp ../shaketune/ci/smoke-test/klipper-smoketest.kconfig .config
make olddefconfig
make out/compile_time_request.o
popd
- name: Setup klippy env
run: |
python3 -m venv --prompt klippy klippy-env
./klippy-env/bin/python -m pip install -r klipper/scripts/klippy-requirements.txt
./klippy-env/bin/python -m pip install -r shaketune/requirements.txt
- name: Install shaketune
run: |
ln -s $PWD/shaketune/shaketune $PWD/klipper/klippy/extras/shaketune
- name: Klipper import test
run: |
./klippy-env/bin/python klipper/klippy/klippy.py --import-test
- name: Klipper integrated test
run: |
pushd klipper
mkdir ../dicts
cp ../klipper/out/klipper.dict ../dicts/linux_basic.dict
../klippy-env/bin/python scripts/test_klippy.py -d ../dicts ../shaketune/ci/smoke-test/klippy-tests/simple.test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
cache: 'pip'
- name: install ruff
run: |
pip install ruff
- name: run ruff tests
run: |
ruff check

View File

@@ -0,0 +1,34 @@
CONFIG_LOW_LEVEL_OPTIONS=y
# CONFIG_MACH_AVR is not set
# CONFIG_MACH_ATSAM is not set
# CONFIG_MACH_ATSAMD is not set
# CONFIG_MACH_LPC176X is not set
# CONFIG_MACH_STM32 is not set
# CONFIG_MACH_HC32F460 is not set
# CONFIG_MACH_RP2040 is not set
# CONFIG_MACH_PRU is not set
# CONFIG_MACH_AR100 is not set
CONFIG_MACH_LINUX=y
# CONFIG_MACH_SIMU is not set
CONFIG_BOARD_DIRECTORY="linux"
CONFIG_CLOCK_FREQ=50000000
CONFIG_LINUX_SELECT=y
CONFIG_USB_VENDOR_ID=0x1d50
CONFIG_USB_DEVICE_ID=0x614e
CONFIG_USB_SERIAL_NUMBER="12345"
CONFIG_WANT_GPIO_BITBANGING=y
CONFIG_WANT_DISPLAYS=y
CONFIG_WANT_SENSORS=y
CONFIG_WANT_LIS2DW=y
CONFIG_WANT_LDC1612=y
CONFIG_WANT_SOFTWARE_I2C=y
CONFIG_WANT_SOFTWARE_SPI=y
CONFIG_NEED_SENSOR_BULK=y
CONFIG_CANBUS_FREQUENCY=1000000
CONFIG_INITIAL_PINS=""
CONFIG_HAVE_GPIO=y
CONFIG_HAVE_GPIO_ADC=y
CONFIG_HAVE_GPIO_SPI=y
CONFIG_HAVE_GPIO_I2C=y
CONFIG_HAVE_GPIO_HARD_PWM=y
CONFIG_INLINE_STEPPER_HACK=y

View File

@@ -0,0 +1,9 @@
[mcu]
serial: /tmp/klipper_host_mcu
[printer]
kinematics: none
max_velocity: 300
max_accel: 300
[shaketune]

View File

@@ -0,0 +1,4 @@
DICTIONARY linux_basic.dict
CONFIG simple.cfg
G4 P1000

View File

@@ -1,5 +1,5 @@
[project] [project]
name = "Shake&Tune" name = "shake_n_tune"
description = "Klipper streamlined input shaper workflow and calibration tools" description = "Klipper streamlined input shaper workflow and calibration tools"
readme = "README.md" readme = "README.md"
requires-python = ">= 3.9" requires-python = ">= 3.9"

View File

@@ -13,10 +13,13 @@ import os
import time import time
from multiprocessing import Process, Queue from multiprocessing import Process, Queue
FILE_WRITE_TIMEOUT = 10 # seconds
class Accelerometer: class Accelerometer:
def __init__(self, klipper_accelerometer): def __init__(self, reactor, klipper_accelerometer):
self._k_accelerometer = klipper_accelerometer self._k_accelerometer = klipper_accelerometer
self._reactor = reactor
self._bg_client = None self._bg_client = None
self._write_queue = Queue() self._write_queue = Queue()
@@ -70,16 +73,35 @@ class Accelerometer:
os.nice(20) os.nice(20)
except Exception: except Exception:
pass pass
with open(filename, 'w') as f: with open(filename, 'w') as f:
f.write('#time,accel_x,accel_y,accel_z\n') f.write('#time,accel_x,accel_y,accel_z\n')
samples = bg_client.samples or bg_client.get_samples() samples = bg_client.samples or bg_client.get_samples()
for t, accel_x, accel_y, accel_z in samples: for t, accel_x, accel_y, accel_z in samples:
f.write(f'{t:.6f},{accel_x:.6f},{accel_y:.6f},{accel_z:.6f}\n') f.write(f'{t:.6f},{accel_x:.6f},{accel_y:.6f},{accel_z:.6f}\n')
self._write_queue.get() self._write_queue.get()
def wait_for_file_writes(self): def wait_for_file_writes(self):
while not self._write_queue.empty(): while not self._write_queue.empty():
time.sleep(0.1) eventtime = self._reactor.monotonic()
self._reactor.pause(eventtime + 0.1)
for proc in self._write_processes: for proc in self._write_processes:
proc.join() if proc is None:
continue
eventtime = self._reactor.monotonic()
endtime = eventtime + FILE_WRITE_TIMEOUT
complete = False
while eventtime < endtime:
eventtime = self._reactor.pause(eventtime + 0.05)
if not proc.is_alive():
complete = True
break
if not complete:
raise TimeoutError(
'Shake&Tune was not able to write the accelerometer data into the CSV file. '
'This might be due to a slow SD card or a busy or full filesystem.'
)
self._write_processes = [] self._write_processes = []

View File

@@ -37,7 +37,7 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
raise gcmd.error( 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")!' f'The parameter axes_map is already set in your {accel_chip} configuration! Please remove it (or set it to "x,y,z")!'
) )
accelerometer = Accelerometer(k_accelerometer) accelerometer = Accelerometer(printer.get_reactor(), k_accelerometer)
toolhead_info = toolhead.get_status(systime) toolhead_info = toolhead.get_status(systime)
old_accel = toolhead_info['max_accel'] old_accel = toolhead_info['max_accel']
@@ -46,7 +46,9 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
# set the wanted acceleration values # set the wanted acceleration values
if 'minimum_cruise_ratio' in toolhead_info: if 'minimum_cruise_ratio' in toolhead_info:
old_mcr = toolhead_info['minimum_cruise_ratio'] # minimum_cruise_ratio found: Klipper >= v0.12.0-239 old_mcr = toolhead_info['minimum_cruise_ratio'] # minimum_cruise_ratio found: Klipper >= v0.12.0-239
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY=5.0') gcode.run_script_from_command(
f'SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY=5.0'
)
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239 else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
old_mcr = None old_mcr = None
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} SQUARE_CORNER_VELOCITY=5.0') gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} SQUARE_CORNER_VELOCITY=5.0')
@@ -94,7 +96,9 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
# Restore the previous acceleration values # Restore the previous acceleration values
if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239 if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_mcr} SQUARE_CORNER_VELOCITY={old_sqv}') gcode.run_script_from_command(
f'SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_mcr} SQUARE_CORNER_VELOCITY={old_sqv}'
)
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239 else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} SQUARE_CORNER_VELOCITY={old_sqv}') gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} SQUARE_CORNER_VELOCITY={old_sqv}')

View File

@@ -99,7 +99,7 @@ def axes_shaper_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
accel_chip = Accelerometer.find_axis_accelerometer(printer, config['axis']) accel_chip = Accelerometer.find_axis_accelerometer(printer, config['axis'])
if accel_chip is None: if accel_chip is None:
raise gcmd.error('No suitable accelerometer found for measurement!') raise gcmd.error('No suitable accelerometer found for measurement!')
accelerometer = Accelerometer(printer.lookup_object(accel_chip)) accelerometer = Accelerometer(printer.get_reactor(), printer.lookup_object(accel_chip))
# Then do the actual measurements # Then do the actual measurements
accelerometer.start_measurement() accelerometer.start_measurement()

View File

@@ -60,7 +60,7 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None:
raise gcmd.error( raise gcmd.error(
'No suitable accelerometer found for measurement! Multi-accelerometer configurations are not supported for this macro.' '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.get_reactor(), printer.lookup_object(accel_chip))
# Move to the starting point # Move to the starting point
test_points = res_tester.test.get_start_test_points() test_points = res_tester.test.get_start_test_points()

View File

@@ -64,7 +64,9 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
# set the wanted acceleration values # set the wanted acceleration values
if 'minimum_cruise_ratio' in toolhead_info: # minimum_cruise_ratio found: Klipper >= v0.12.0-239 if 'minimum_cruise_ratio' in toolhead_info: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
old_mcr = toolhead_info['minimum_cruise_ratio'] old_mcr = toolhead_info['minimum_cruise_ratio']
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY=5.0') gcode.run_script_from_command(
f'SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY=5.0'
)
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239 else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
old_mcr = None old_mcr = None
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} SQUARE_CORNER_VELOCITY=5.0') gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} SQUARE_CORNER_VELOCITY=5.0')
@@ -95,7 +97,7 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
if k_accelerometer is None: if k_accelerometer is None:
raise gcmd.error(f'Accelerometer [{current_accel_chip}] not found!') raise gcmd.error(f'Accelerometer [{current_accel_chip}] not found!')
ConsoleOutput.print(f'Accelerometer chip used for this angle: [{current_accel_chip}]') ConsoleOutput.print(f'Accelerometer chip used for this angle: [{current_accel_chip}]')
accelerometer = Accelerometer(k_accelerometer) accelerometer = Accelerometer(printer.get_reactor(), k_accelerometer)
# Sweep the speed range to record the vibrations at different speeds # Sweep the speed range to record the vibrations at different speeds
for curr_speed_sample in range(nb_speed_samples): for curr_speed_sample in range(nb_speed_samples):
@@ -139,7 +141,9 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
# Restore the previous acceleration values # Restore the previous acceleration values
if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239 if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_mcr} SQUARE_CORNER_VELOCITY={old_sqv}') gcode.run_script_from_command(
f'SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_mcr} SQUARE_CORNER_VELOCITY={old_sqv}'
)
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239 else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} SQUARE_CORNER_VELOCITY={old_sqv}') gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} SQUARE_CORNER_VELOCITY={old_sqv}')
toolhead.wait_moves() toolhead.wait_moves()

View File

@@ -41,7 +41,7 @@ def excitate_axis_at_freq(gcmd, config, st_process: ShakeTuneProcess) -> None:
k_accelerometer = printer.lookup_object(accel_chip, None) k_accelerometer = printer.lookup_object(accel_chip, None)
if k_accelerometer is None: if k_accelerometer is None:
raise gcmd.error(f'Accelerometer chip [{accel_chip}] was not found!') raise gcmd.error(f'Accelerometer chip [{accel_chip}] was not found!')
accelerometer = Accelerometer(k_accelerometer) accelerometer = Accelerometer(printer.get_reactor(), k_accelerometer)
ConsoleOutput.print(f'Excitating {axis.upper()} axis at {freq}Hz for {duration} seconds') ConsoleOutput.print(f'Excitating {axis.upper()} axis at {freq}Hz for {duration} seconds')

View File

@@ -29,6 +29,8 @@ from .helpers.console_output import ConsoleOutput
from .shaketune_config import ShakeTuneConfig from .shaketune_config import ShakeTuneConfig
from .shaketune_process import ShakeTuneProcess from .shaketune_process import ShakeTuneProcess
IN_DANGER = False
class ShakeTune: class ShakeTune:
def __init__(self, config) -> None: def __init__(self, config) -> None:
@@ -51,21 +53,31 @@ class ShakeTune:
self._config = ShakeTuneConfig(result_folder_path, keep_n_results, keep_csv, dpi) self._config = ShakeTuneConfig(result_folder_path, keep_n_results, keep_csv, dpi)
ConsoleOutput.register_output_callback(gcode.respond_info) ConsoleOutput.register_output_callback(gcode.respond_info)
commands = [ # Register Shake&Tune's measurement commands
measurement_commands = [
( (
'EXCITATE_AXIS_AT_FREQ', 'EXCITATE_AXIS_AT_FREQ',
self.cmd_EXCITATE_AXIS_AT_FREQ, self.cmd_EXCITATE_AXIS_AT_FREQ,
'Maintain a specified excitation frequency for a period of time to diagnose and locate a source of vibration', (
'Maintain a specified excitation frequency for a period '
'of time to diagnose and locate a source of vibrations'
),
), ),
( (
'AXES_MAP_CALIBRATION', 'AXES_MAP_CALIBRATION',
self.cmd_AXES_MAP_CALIBRATION, self.cmd_AXES_MAP_CALIBRATION,
'Perform a set of movements to measure the orientation of the accelerometer and help you set the best axes_map configuration for your printer', (
'Perform a set of movements to measure the orientation of the accelerometer '
'and help you set the best axes_map configuration for your printer'
),
), ),
( (
'COMPARE_BELTS_RESPONSES', 'COMPARE_BELTS_RESPONSES',
self.cmd_COMPARE_BELTS_RESPONSES, self.cmd_COMPARE_BELTS_RESPONSES,
'Perform a custom half-axis test to analyze and compare the frequency profiles of individual belts on CoreXY printers', (
'Perform a custom half-axis test to analyze and compare the '
'frequency profiles of individual belts on CoreXY or CoreXZ printers'
),
), ),
( (
'AXES_SHAPER_CALIBRATION', 'AXES_SHAPER_CALIBRATION',
@@ -75,12 +87,14 @@ class ShakeTune:
( (
'CREATE_VIBRATIONS_PROFILE', 'CREATE_VIBRATIONS_PROFILE',
self.cmd_CREATE_VIBRATIONS_PROFILE, self.cmd_CREATE_VIBRATIONS_PROFILE,
'Perform a set of movements to measure the orientation of the accelerometer and help you set the best axes_map configuration for your printer', (
'Run a series of motions to find speed/angle ranges where the printer could be '
'exposed to VFAs to optimize your slicer speed profiles and TMC driver parameters'
),
), ),
] ]
command_descriptions = {name: desc for name, _, desc in commands} command_descriptions = {name: desc for name, _, desc in measurement_commands}
for name, command, description in measurement_commands:
for name, command, description in commands:
gcode.register_command(f'_{name}' if show_macros else name, command, desc=description) gcode.register_command(f'_{name}' if show_macros else name, command, desc=description)
# Load the dummy macros with their description in order to show them in the web interfaces # Load the dummy macros with their description in order to show them in the web interfaces