Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af9fee36ed | |||
| 6b2cedfa28 | |||
| 04ff95921e | |||
| 871dd72a88 | |||
|
|
66f5e32e4c | ||
|
|
c12653e1f7 | ||
|
|
8cf81bcb44 | ||
|
|
92a651b6a6 | ||
|
|
6712506862 | ||
|
|
6e49c2c607 | ||
|
|
4a99e95882 | ||
|
|
f5a74c29e1 | ||
|
|
f87713eacd | ||
|
|
f045b8a49e | ||
|
|
37d0e39d84 | ||
|
|
50ed13ca59 | ||
|
|
90ed7aca3c |
69
.github/workflows/test.yml
vendored
Normal file
69
.github/workflows/test.yml
vendored
Normal 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
|
||||||
|
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ Follow these steps to install Shake&Tune on your printer:
|
|||||||
1. Be sure to have a working accelerometer on your machine and a `[resonance_tester]` section defined. You can follow the official [Measuring Resonances Klipper documentation](https://www.klipper3d.org/Measuring_Resonances.html) to configure it.
|
1. Be sure to have a working accelerometer on your machine and a `[resonance_tester]` section defined. You can follow the official [Measuring Resonances Klipper documentation](https://www.klipper3d.org/Measuring_Resonances.html) to configure it.
|
||||||
1. Install Shake&Tune by running over SSH on your printer:
|
1. Install Shake&Tune by running over SSH on your printer:
|
||||||
```bash
|
```bash
|
||||||
wget -O - https://raw.githubusercontent.com/Frix-x/klippain-shaketune/main/install.sh | bash
|
wget -O - https://cloud.reijii.org/gitea/reijii/klippain-shaketune-telegramm/raw/branch/main/install.sh | bash
|
||||||
```
|
```
|
||||||
1. Then, append the following to your `printer.cfg` file and restart Klipper:
|
1. Then, append the following to your `printer.cfg` file and restart Klipper:
|
||||||
```
|
```
|
||||||
|
|||||||
34
ci/smoke-test/klipper-smoketest.kconfig
Normal file
34
ci/smoke-test/klipper-smoketest.kconfig
Normal 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
|
||||||
9
ci/smoke-test/klippy-tests/simple.cfg
Normal file
9
ci/smoke-test/klippy-tests/simple.cfg
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[mcu]
|
||||||
|
serial: /tmp/klipper_host_mcu
|
||||||
|
|
||||||
|
[printer]
|
||||||
|
kinematics: none
|
||||||
|
max_velocity: 300
|
||||||
|
max_accel: 300
|
||||||
|
|
||||||
|
[shaketune]
|
||||||
4
ci/smoke-test/klippy-tests/simple.test
Normal file
4
ci/smoke-test/klippy-tests/simple.test
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
DICTIONARY linux_basic.dict
|
||||||
|
CONFIG simple.cfg
|
||||||
|
|
||||||
|
G4 P1000
|
||||||
@@ -39,9 +39,9 @@ Aside from the actual belt tension, the resonant frequency/amplitude of the curv
|
|||||||
|
|
||||||
The Cross-Belts plot is an innovative cool way to compare the frequency profiles of the belts at every frequency point. In this plot, each point marks the amplitude response of each belt at different frequencies, connected point by point to trace the frequency spectrum. Ideally, these points should align on the diagonal center line, indicating that both belts have matching energy response values at each frequency.
|
The Cross-Belts plot is an innovative cool way to compare the frequency profiles of the belts at every frequency point. In this plot, each point marks the amplitude response of each belt at different frequencies, connected point by point to trace the frequency spectrum. Ideally, these points should align on the diagonal center line, indicating that both belts have matching energy response values at each frequency.
|
||||||
|
|
||||||
The good zone, wider at the bottom (low-amplitude regions where the deviation doesn't matter much) and narrower at the top right (high-energy region where the main peaks lie), represents acceptable deviations. So **you want all points to be close to the ideal center line and as many as possible within the green zone**, as this means that the bands are well tuned and behave similarly.
|
The good zone, wider at the bottom (low-amplitude regions where the deviation doesn't matter much) and narrower at the top right (high-energy region where the main peaks lie), represents acceptable deviations. So **you want all points to be close to the ideal center line and as many as possible within the green zone**, as this means that the belts are well tuned and behave similarly.
|
||||||
|
|
||||||
Paired peaks of exactly the same frequency will be on the same point (labeled α1/α2, β1/β2, ...) and the distance from the center line will show the difference in energy. For paired peaks that also have a frequency delta between them, they are displayed as two points (labeled α1 and α2, ...) and the additional distance between them along the plotted line represents their frequency delta.
|
Paired peaks at the same frequency will be on the same point (labeled α1/α2, β1/β2, ...) and the distance from the center line will show the difference in energy. For paired peaks that also have a frequency delta between them, they are displayed as two points (labeled α1 and α2, ...) and the additional distance between them along the plotted line represents their frequency delta.
|
||||||
|
|
||||||
### Estimated similarity and mechanical issues indicator
|
### Estimated similarity and mechanical issues indicator
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function check_download {
|
|||||||
|
|
||||||
if [ ! -d "${K_SHAKETUNE_PATH}" ]; then
|
if [ ! -d "${K_SHAKETUNE_PATH}" ]; then
|
||||||
echo "[DOWNLOAD] Downloading Klippain Shake&Tune module repository..."
|
echo "[DOWNLOAD] Downloading Klippain Shake&Tune module repository..."
|
||||||
if git -C $shaketunedirname clone https://github.com/Frix-x/klippain-shaketune.git $shaketunebasename; then
|
if git -C $shaketunedirname clone https://cloud.reijii.org/gitea/reijii/klippain-shaketune-telegramm.git $shaketunebasename; then
|
||||||
chmod +x ${K_SHAKETUNE_PATH}/install.sh
|
chmod +x ${K_SHAKETUNE_PATH}/install.sh
|
||||||
printf "[DOWNLOAD] Download complete!\n\n"
|
printf "[DOWNLOAD] Download complete!\n\n"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
## Klippain Shake&Tune automatic update management
|
## Klippain Shake&Tune automatic update management
|
||||||
[update_manager Klippain-ShakeTune]
|
[update_manager Klippain-ShakeTune]
|
||||||
type: git_repo
|
type: git_repo
|
||||||
origin: https://github.com/Frix-x/klippain-shaketune.git
|
origin: https://cloud.reijii.org/gitea/reijii/klippain-shaketune-telegramm.git
|
||||||
path: ~/klippain_shaketune
|
path: ~/klippain_shaketune
|
||||||
virtualenv: ~/klippy-env
|
virtualenv: ~/klippy-env
|
||||||
requirements: requirements.txt
|
requirements: requirements.txt
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 = []
|
||||||
|
|||||||
@@ -37,15 +37,21 @@ 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']
|
||||||
old_mcr = toolhead_info['minimum_cruise_ratio']
|
|
||||||
old_sqv = toolhead_info['square_corner_velocity']
|
old_sqv = toolhead_info['square_corner_velocity']
|
||||||
|
|
||||||
# set the wanted acceleration values
|
# set the wanted acceleration values
|
||||||
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY=5.0')
|
if 'minimum_cruise_ratio' in toolhead_info:
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
|
||||||
|
old_mcr = None
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} SQUARE_CORNER_VELOCITY=5.0')
|
||||||
|
|
||||||
# Deactivate input shaper if it is active to get raw movements
|
# Deactivate input shaper if it is active to get raw movements
|
||||||
input_shaper = printer.lookup_object('input_shaper', None)
|
input_shaper = printer.lookup_object('input_shaper', None)
|
||||||
@@ -89,9 +95,13 @@ def axes_map_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
|
|||||||
input_shaper.enable_shaping()
|
input_shaper.enable_shaping()
|
||||||
|
|
||||||
# Restore the previous acceleration values
|
# Restore the previous acceleration values
|
||||||
gcode.run_script_from_command(
|
if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
|
||||||
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
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} SQUARE_CORNER_VELOCITY={old_sqv}')
|
||||||
|
|
||||||
toolhead.wait_moves()
|
toolhead.wait_moves()
|
||||||
|
|
||||||
# Run post-processing
|
# Run post-processing
|
||||||
|
|||||||
@@ -76,8 +76,12 @@ def axes_shaper_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
|
|||||||
# set the needed acceleration values for the test
|
# set the needed acceleration values for the test
|
||||||
toolhead_info = toolhead.get_status(systime)
|
toolhead_info = toolhead.get_status(systime)
|
||||||
old_accel = toolhead_info['max_accel']
|
old_accel = toolhead_info['max_accel']
|
||||||
old_mcr = toolhead_info['minimum_cruise_ratio']
|
if 'minimum_cruise_ratio' in toolhead_info: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
|
||||||
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel} MINIMUM_CRUISE_RATIO=0')
|
old_mcr = toolhead_info['minimum_cruise_ratio']
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel} MINIMUM_CRUISE_RATIO=0')
|
||||||
|
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
|
||||||
|
old_mcr = None
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel}')
|
||||||
|
|
||||||
# Deactivate input shaper if it is active to get raw movements
|
# Deactivate input shaper if it is active to get raw movements
|
||||||
input_shaper = printer.lookup_object('input_shaper', None)
|
input_shaper = printer.lookup_object('input_shaper', None)
|
||||||
@@ -95,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()
|
||||||
@@ -117,4 +121,7 @@ def axes_shaper_calibration(gcmd, config, st_process: ShakeTuneProcess) -> None:
|
|||||||
input_shaper.enable_shaping()
|
input_shaper.enable_shaping()
|
||||||
|
|
||||||
# Restore the previous acceleration values
|
# Restore the previous acceleration values
|
||||||
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_mcr}')
|
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}')
|
||||||
|
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel}')
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -89,8 +89,12 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None:
|
|||||||
# set the needed acceleration values for the test
|
# set the needed acceleration values for the test
|
||||||
toolhead_info = toolhead.get_status(systime)
|
toolhead_info = toolhead.get_status(systime)
|
||||||
old_accel = toolhead_info['max_accel']
|
old_accel = toolhead_info['max_accel']
|
||||||
old_mcr = toolhead_info['minimum_cruise_ratio']
|
if 'minimum_cruise_ratio' in toolhead_info: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
|
||||||
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel} MINIMUM_CRUISE_RATIO=0')
|
old_mcr = toolhead_info['minimum_cruise_ratio']
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel} MINIMUM_CRUISE_RATIO=0')
|
||||||
|
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
|
||||||
|
old_mcr = None
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={max_accel}')
|
||||||
|
|
||||||
# Deactivate input shaper if it is active to get raw movements
|
# Deactivate input shaper if it is active to get raw movements
|
||||||
input_shaper = printer.lookup_object('input_shaper', None)
|
input_shaper = printer.lookup_object('input_shaper', None)
|
||||||
@@ -112,7 +116,10 @@ def compare_belts_responses(gcmd, config, st_process: ShakeTuneProcess) -> None:
|
|||||||
input_shaper.enable_shaping()
|
input_shaper.enable_shaping()
|
||||||
|
|
||||||
# Restore the previous acceleration values
|
# Restore the previous acceleration values
|
||||||
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} MINIMUM_CRUISE_RATIO={old_mcr}')
|
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}')
|
||||||
|
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel}')
|
||||||
|
|
||||||
# Run post-processing
|
# Run post-processing
|
||||||
ConsoleOutput.print('Belts comparative frequency profile generation...')
|
ConsoleOutput.print('Belts comparative frequency profile generation...')
|
||||||
|
|||||||
@@ -59,11 +59,17 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
|
|||||||
|
|
||||||
toolhead_info = toolhead.get_status(systime)
|
toolhead_info = toolhead.get_status(systime)
|
||||||
old_accel = toolhead_info['max_accel']
|
old_accel = toolhead_info['max_accel']
|
||||||
old_mcr = toolhead_info['minimum_cruise_ratio']
|
|
||||||
old_sqv = toolhead_info['square_corner_velocity']
|
old_sqv = toolhead_info['square_corner_velocity']
|
||||||
|
|
||||||
# set the wanted acceleration values
|
# set the wanted acceleration values
|
||||||
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} MINIMUM_CRUISE_RATIO=0 SQUARE_CORNER_VELOCITY=5.0')
|
if 'minimum_cruise_ratio' in toolhead_info: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
else: # minimum_cruise_ratio not found: Klipper < v0.12.0-239
|
||||||
|
old_mcr = None
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={accel} SQUARE_CORNER_VELOCITY=5.0')
|
||||||
|
|
||||||
kin_info = toolhead.kin.get_status(systime)
|
kin_info = toolhead.kin.get_status(systime)
|
||||||
mid_x = (kin_info['axis_minimum'].x + kin_info['axis_maximum'].x) / 2
|
mid_x = (kin_info['axis_minimum'].x + kin_info['axis_maximum'].x) / 2
|
||||||
@@ -91,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):
|
||||||
@@ -133,10 +139,13 @@ def create_vibrations_profile(gcmd, config, st_process: ShakeTuneProcess) -> Non
|
|||||||
|
|
||||||
accelerometer.wait_for_file_writes()
|
accelerometer.wait_for_file_writes()
|
||||||
|
|
||||||
# Restore the previous acceleration values
|
# Restore the previous acceleration values
|
||||||
gcode.run_script_from_command(
|
if old_mcr is not None: # minimum_cruise_ratio found: Klipper >= v0.12.0-239
|
||||||
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
|
||||||
|
gcode.run_script_from_command(f'SET_VELOCITY_LIMIT ACCEL={old_accel} SQUARE_CORNER_VELOCITY={old_sqv}')
|
||||||
toolhead.wait_moves()
|
toolhead.wait_moves()
|
||||||
|
|
||||||
# Run post-processing
|
# Run post-processing
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import matplotlib.font_manager
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import matplotlib.ticker
|
import matplotlib.ticker
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy.stats import pearsonr
|
||||||
|
|
||||||
matplotlib.use('Agg')
|
matplotlib.use('Agg')
|
||||||
|
|
||||||
@@ -210,8 +211,8 @@ def plot_compare_frequency(
|
|||||||
ax: plt.Axes, signal1: SignalData, signal2: SignalData, signal1_belt: str, signal2_belt: str, max_freq: float
|
ax: plt.Axes, signal1: SignalData, signal2: SignalData, signal1_belt: str, signal2_belt: str, max_freq: float
|
||||||
) -> None:
|
) -> None:
|
||||||
# Plot the two belts PSD signals
|
# Plot the two belts PSD signals
|
||||||
ax.plot(signal1.freqs, signal1.psd, label='Belt ' + signal1_belt, color=KLIPPAIN_COLORS['purple'])
|
ax.plot(signal1.freqs, signal1.psd, label='Belt ' + signal1_belt, color=KLIPPAIN_COLORS['orange'])
|
||||||
ax.plot(signal2.freqs, signal2.psd, label='Belt ' + signal2_belt, color=KLIPPAIN_COLORS['orange'])
|
ax.plot(signal2.freqs, signal2.psd, label='Belt ' + signal2_belt, color=KLIPPAIN_COLORS['purple'])
|
||||||
|
|
||||||
psd_highest_max = max(signal1.psd.max(), signal2.psd.max())
|
psd_highest_max = max(signal1.psd.max(), signal2.psd.max())
|
||||||
|
|
||||||
@@ -343,14 +344,12 @@ def plot_versus_belts(
|
|||||||
common_freqs: np.ndarray,
|
common_freqs: np.ndarray,
|
||||||
signal1: SignalData,
|
signal1: SignalData,
|
||||||
signal2: SignalData,
|
signal2: SignalData,
|
||||||
interp_psd1: np.ndarray,
|
|
||||||
interp_psd2: np.ndarray,
|
|
||||||
signal1_belt: str,
|
signal1_belt: str,
|
||||||
signal2_belt: str,
|
signal2_belt: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
ax.set_title('Cross-belts comparison plot', fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
ax.set_title('Cross-belts comparison plot', fontsize=14, color=KLIPPAIN_COLORS['dark_orange'], weight='bold')
|
||||||
|
|
||||||
max_psd = max(np.max(interp_psd1), np.max(interp_psd2))
|
max_psd = max(np.max(signal1.psd), np.max(signal2.psd))
|
||||||
ideal_line = np.linspace(0, max_psd * 1.1, 500)
|
ideal_line = np.linspace(0, max_psd * 1.1, 500)
|
||||||
green_boundary = ideal_line + (0.35 * max_psd * np.exp(-ideal_line / (0.6 * max_psd)))
|
green_boundary = ideal_line + (0.35 * max_psd * np.exp(-ideal_line / (0.6 * max_psd)))
|
||||||
ax.fill_betweenx(ideal_line, ideal_line, green_boundary, color='green', alpha=0.15)
|
ax.fill_betweenx(ideal_line, ideal_line, green_boundary, color='green', alpha=0.15)
|
||||||
@@ -364,8 +363,8 @@ def plot_versus_belts(
|
|||||||
linewidth=2,
|
linewidth=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
ax.plot(interp_psd1, interp_psd2, color='dimgrey', marker='o', markersize=1.5)
|
ax.plot(signal1.psd, signal2.psd, color='dimgrey', marker='o', markersize=1.5)
|
||||||
ax.fill_betweenx(interp_psd2, interp_psd1, color=KLIPPAIN_COLORS['red_pink'], alpha=0.1)
|
ax.fill_betweenx(signal2.psd, signal1.psd, color=KLIPPAIN_COLORS['red_pink'], alpha=0.1)
|
||||||
|
|
||||||
paired_peak_count = 0
|
paired_peak_count = 0
|
||||||
unpaired_peak_count = 0
|
unpaired_peak_count = 0
|
||||||
@@ -374,31 +373,27 @@ def plot_versus_belts(
|
|||||||
label = ALPHABET[paired_peak_count]
|
label = ALPHABET[paired_peak_count]
|
||||||
freq1 = signal1.freqs[peak1[0]]
|
freq1 = signal1.freqs[peak1[0]]
|
||||||
freq2 = signal2.freqs[peak2[0]]
|
freq2 = signal2.freqs[peak2[0]]
|
||||||
nearest_idx1 = np.argmin(np.abs(common_freqs - freq1))
|
|
||||||
nearest_idx2 = np.argmin(np.abs(common_freqs - freq2))
|
|
||||||
|
|
||||||
if nearest_idx1 == nearest_idx2:
|
if abs(freq1 - freq2) < 1:
|
||||||
psd1_peak_value = interp_psd1[nearest_idx1]
|
ax.plot(signal1.psd[peak1[0]], signal2.psd[peak2[0]], marker='o', color='black', markersize=7)
|
||||||
psd2_peak_value = interp_psd2[nearest_idx1]
|
|
||||||
ax.plot(psd1_peak_value, psd2_peak_value, marker='o', color='black', markersize=7)
|
|
||||||
ax.annotate(
|
ax.annotate(
|
||||||
f'{label}1/{label}2',
|
f'{label}1/{label}2',
|
||||||
(psd1_peak_value, psd2_peak_value),
|
(signal1.psd[peak1[0]], signal2.psd[peak2[0]]),
|
||||||
textcoords='offset points',
|
textcoords='offset points',
|
||||||
xytext=(-7, 7),
|
xytext=(-7, 7),
|
||||||
fontsize=13,
|
fontsize=13,
|
||||||
color='black',
|
color='black',
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
psd1_peak_value = interp_psd1[nearest_idx1]
|
ax.plot(
|
||||||
psd1_on_peak = interp_psd1[nearest_idx2]
|
signal1.psd[peak2[0]], signal2.psd[peak2[0]], marker='o', color=KLIPPAIN_COLORS['orange'], markersize=7
|
||||||
psd2_peak_value = interp_psd2[nearest_idx2]
|
)
|
||||||
psd2_on_peak = interp_psd2[nearest_idx1]
|
ax.plot(
|
||||||
ax.plot(psd1_on_peak, psd2_peak_value, marker='o', color=KLIPPAIN_COLORS['orange'], markersize=7)
|
signal1.psd[peak1[0]], signal2.psd[peak1[0]], marker='o', color=KLIPPAIN_COLORS['purple'], markersize=7
|
||||||
ax.plot(psd1_peak_value, psd2_on_peak, marker='o', color=KLIPPAIN_COLORS['purple'], markersize=7)
|
)
|
||||||
ax.annotate(
|
ax.annotate(
|
||||||
f'{label}1',
|
f'{label}1',
|
||||||
(psd1_peak_value, psd2_on_peak),
|
(signal1.psd[peak1[0]], signal2.psd[peak1[0]]),
|
||||||
textcoords='offset points',
|
textcoords='offset points',
|
||||||
xytext=(0, 7),
|
xytext=(0, 7),
|
||||||
fontsize=13,
|
fontsize=13,
|
||||||
@@ -406,7 +401,7 @@ def plot_versus_belts(
|
|||||||
)
|
)
|
||||||
ax.annotate(
|
ax.annotate(
|
||||||
f'{label}2',
|
f'{label}2',
|
||||||
(psd1_on_peak, psd2_peak_value),
|
(signal1.psd[peak2[0]], signal2.psd[peak2[0]]),
|
||||||
textcoords='offset points',
|
textcoords='offset points',
|
||||||
xytext=(0, 7),
|
xytext=(0, 7),
|
||||||
fontsize=13,
|
fontsize=13,
|
||||||
@@ -415,16 +410,12 @@ def plot_versus_belts(
|
|||||||
paired_peak_count += 1
|
paired_peak_count += 1
|
||||||
|
|
||||||
for _, peak_index in enumerate(signal1.unpaired_peaks):
|
for _, peak_index in enumerate(signal1.unpaired_peaks):
|
||||||
freq1 = signal1.freqs[peak_index]
|
ax.plot(
|
||||||
freq2 = signal2.freqs[peak_index]
|
signal1.psd[peak_index], signal2.psd[peak_index], marker='o', color=KLIPPAIN_COLORS['purple'], markersize=7
|
||||||
nearest_idx1 = np.argmin(np.abs(common_freqs - freq1))
|
)
|
||||||
nearest_idx2 = np.argmin(np.abs(common_freqs - freq2))
|
|
||||||
psd1_peak_value = interp_psd1[nearest_idx1]
|
|
||||||
psd2_peak_value = interp_psd2[nearest_idx1]
|
|
||||||
ax.plot(psd1_peak_value, psd2_peak_value, marker='o', color=KLIPPAIN_COLORS['purple'], markersize=7)
|
|
||||||
ax.annotate(
|
ax.annotate(
|
||||||
str(unpaired_peak_count + 1),
|
str(unpaired_peak_count + 1),
|
||||||
(psd1_peak_value, psd2_peak_value),
|
(signal1.psd[peak_index], signal2.psd[peak_index]),
|
||||||
textcoords='offset points',
|
textcoords='offset points',
|
||||||
fontsize=13,
|
fontsize=13,
|
||||||
weight='bold',
|
weight='bold',
|
||||||
@@ -434,16 +425,12 @@ def plot_versus_belts(
|
|||||||
unpaired_peak_count += 1
|
unpaired_peak_count += 1
|
||||||
|
|
||||||
for _, peak_index in enumerate(signal2.unpaired_peaks):
|
for _, peak_index in enumerate(signal2.unpaired_peaks):
|
||||||
freq1 = signal1.freqs[peak_index]
|
ax.plot(
|
||||||
freq2 = signal2.freqs[peak_index]
|
signal1.psd[peak_index], signal2.psd[peak_index], marker='o', color=KLIPPAIN_COLORS['orange'], markersize=7
|
||||||
nearest_idx1 = np.argmin(np.abs(common_freqs - freq1))
|
)
|
||||||
nearest_idx2 = np.argmin(np.abs(common_freqs - freq2))
|
|
||||||
psd1_peak_value = interp_psd1[nearest_idx1]
|
|
||||||
psd2_peak_value = interp_psd2[nearest_idx1]
|
|
||||||
ax.plot(psd1_peak_value, psd2_peak_value, marker='o', color=KLIPPAIN_COLORS['orange'], markersize=7)
|
|
||||||
ax.annotate(
|
ax.annotate(
|
||||||
str(unpaired_peak_count + 1),
|
str(unpaired_peak_count + 1),
|
||||||
(psd1_peak_value, psd2_peak_value),
|
(signal1.psd[peak_index], signal2.psd[peak_index]),
|
||||||
textcoords='offset points',
|
textcoords='offset points',
|
||||||
fontsize=13,
|
fontsize=13,
|
||||||
weight='bold',
|
weight='bold',
|
||||||
@@ -476,16 +463,21 @@ def plot_versus_belts(
|
|||||||
|
|
||||||
|
|
||||||
# Original Klipper function to get the PSD data of a raw accelerometer signal
|
# Original Klipper function to get the PSD data of a raw accelerometer signal
|
||||||
def compute_signal_data(data: np.ndarray, max_freq: float) -> SignalData:
|
def compute_signal_data(data: np.ndarray, common_freqs: np.ndarray, max_freq: float) -> SignalData:
|
||||||
helper = shaper_calibrate.ShaperCalibrate(printer=None)
|
helper = shaper_calibrate.ShaperCalibrate(printer=None)
|
||||||
calibration_data = helper.process_accelerometer_data(data)
|
calibration_data = helper.process_accelerometer_data(data)
|
||||||
|
|
||||||
freqs = calibration_data.freq_bins[calibration_data.freq_bins <= max_freq]
|
freqs = calibration_data.freq_bins[calibration_data.freq_bins <= max_freq]
|
||||||
psd = calibration_data.get_psd('all')[calibration_data.freq_bins <= max_freq]
|
psd = calibration_data.get_psd('all')[calibration_data.freq_bins <= max_freq]
|
||||||
|
|
||||||
_, peaks, _ = detect_peaks(psd, freqs, PEAKS_DETECTION_THRESHOLD * psd.max())
|
# Re-interpolate the PSD signal to a common frequency range to be able to plot them one against the other
|
||||||
|
interp_psd = np.interp(common_freqs, freqs, psd)
|
||||||
|
|
||||||
return SignalData(freqs=freqs, psd=psd, peaks=peaks)
|
_, peaks, _ = detect_peaks(
|
||||||
|
interp_psd, common_freqs, PEAKS_DETECTION_THRESHOLD * interp_psd.max(), window_size=20, vicinity=15
|
||||||
|
)
|
||||||
|
|
||||||
|
return SignalData(freqs=common_freqs, psd=interp_psd, peaks=peaks)
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
@@ -517,8 +509,9 @@ def belts_calibration(
|
|||||||
signal2_belt += belt_info.get(signal2_belt, '')
|
signal2_belt += belt_info.get(signal2_belt, '')
|
||||||
|
|
||||||
# Compute calibration data for the two datasets with automatic peaks detection
|
# Compute calibration data for the two datasets with automatic peaks detection
|
||||||
signal1 = compute_signal_data(datas[0], max_freq)
|
common_freqs = np.linspace(0, max_freq, 500)
|
||||||
signal2 = compute_signal_data(datas[1], max_freq)
|
signal1 = compute_signal_data(datas[0], common_freqs, max_freq)
|
||||||
|
signal2 = compute_signal_data(datas[1], common_freqs, max_freq)
|
||||||
del datas
|
del datas
|
||||||
|
|
||||||
# Pair the peaks across the two datasets
|
# Pair the peaks across the two datasets
|
||||||
@@ -526,18 +519,13 @@ def belts_calibration(
|
|||||||
signal1 = signal1._replace(paired_peaks=pairing_result.paired_peaks, unpaired_peaks=pairing_result.unpaired_peaks1)
|
signal1 = signal1._replace(paired_peaks=pairing_result.paired_peaks, unpaired_peaks=pairing_result.unpaired_peaks1)
|
||||||
signal2 = signal2._replace(paired_peaks=pairing_result.paired_peaks, unpaired_peaks=pairing_result.unpaired_peaks2)
|
signal2 = signal2._replace(paired_peaks=pairing_result.paired_peaks, unpaired_peaks=pairing_result.unpaired_peaks2)
|
||||||
|
|
||||||
# Re-interpolate the PSD signals to a common frequency range to be able to plot them one against the other point by point
|
# R² proved to be pretty instable to compute the similarity between the two belts
|
||||||
common_freqs = np.linspace(0, max_freq, 500)
|
# So now, we use the Pearson correlation coefficient to compute the similarity
|
||||||
interp_psd1 = np.interp(common_freqs, signal1.freqs, signal1.psd)
|
correlation, _ = pearsonr(signal1.psd, signal2.psd)
|
||||||
interp_psd2 = np.interp(common_freqs, signal2.freqs, signal2.psd)
|
similarity_factor = correlation * 100
|
||||||
|
similarity_factor = np.clip(similarity_factor, 0, 100)
|
||||||
# Calculating R^2 to y=x line to compute the similarity between the two belts
|
|
||||||
ss_res = np.sum((interp_psd2 - interp_psd1) ** 2)
|
|
||||||
ss_tot = np.sum((interp_psd2 - np.mean(interp_psd2)) ** 2)
|
|
||||||
similarity_factor = (1 - (ss_res / ss_tot)) * 100
|
|
||||||
ConsoleOutput.print(f'Belts estimated similarity: {similarity_factor:.1f}%')
|
ConsoleOutput.print(f'Belts estimated similarity: {similarity_factor:.1f}%')
|
||||||
|
|
||||||
# mhi = compute_mhi(similarity_factor, num_peaks, num_unpaired_peaks)
|
|
||||||
mhi = compute_mhi(similarity_factor, signal1, signal2)
|
mhi = compute_mhi(similarity_factor, signal1, signal2)
|
||||||
ConsoleOutput.print(f'[experimental] Mechanical health: {mhi}')
|
ConsoleOutput.print(f'[experimental] Mechanical health: {mhi}')
|
||||||
|
|
||||||
@@ -582,11 +570,11 @@ def belts_calibration(
|
|||||||
|
|
||||||
# Add the accel_per_hz value to the title
|
# Add the accel_per_hz value to the title
|
||||||
title_line5 = f'| Accel per Hz used: {accel_per_hz} mm/s²/Hz'
|
title_line5 = f'| Accel per Hz used: {accel_per_hz} mm/s²/Hz'
|
||||||
fig.text(0.55, 0.915, title_line5, ha='left', va='top', fontsize=14, color=KLIPPAIN_COLORS['dark_purple'])
|
fig.text(0.551, 0.915, title_line5, ha='left', va='top', fontsize=10, color=KLIPPAIN_COLORS['dark_purple'])
|
||||||
|
|
||||||
# Plot the graphs
|
# Plot the graphs
|
||||||
plot_compare_frequency(ax1, signal1, signal2, signal1_belt, signal2_belt, max_freq)
|
plot_compare_frequency(ax1, signal1, signal2, signal1_belt, signal2_belt, max_freq)
|
||||||
plot_versus_belts(ax3, common_freqs, signal1, signal2, interp_psd1, interp_psd2, signal1_belt, signal2_belt)
|
plot_versus_belts(ax3, common_freqs, signal1, signal2, signal1_belt, signal2_belt)
|
||||||
|
|
||||||
# Adding a small Klippain logo to the top left corner of the figure
|
# Adding a small Klippain logo to the top left corner of the figure
|
||||||
ax_logo = fig.add_axes([0.001, 0.894, 0.105, 0.105], anchor='NW')
|
ax_logo = fig.add_axes([0.001, 0.894, 0.105, 0.105], anchor='NW')
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user