Skip to content

Commit

Permalink
Feature/spherical coordinates quaternion (#14)
Browse files Browse the repository at this point in the history
* chore: configure pre-commit

* chore: update credits

* feat: quaternions as spherical coords

* chore(pyinstaller): bump version number

* chore: pre-commit all files
  • Loading branch information
pauloesteban committed Feb 20, 2024
1 parent 96851a9 commit 2cbeda9
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 40 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
draft: false
prerelease: false
generate_release_notes: false

build:
name: Build Release
needs: create_release
Expand All @@ -44,7 +44,7 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: "3.11"

- name: Install dependencies
run: |
pip install -U wheel pip setuptools
Expand Down
17 changes: 17 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.13
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
args: ['--maxkb=2000']
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-ast
- id: check-json
- id: check-toml
- id: check-yaml
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ signtool sign /tr http://timestamp.sectigo.com /td sha256 /a sensor-tile-osc\dis

Developed by Paulo Chiliguano, Travis West and KA HO Wong.

Conducted by Roberto Alonso Trillo @ HKBU Department of Music.
Conducted by Dr Roberto Alonso Trillo @ HKBU Academy of Music.
2 changes: 1 addition & 1 deletion calibrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def __init__(self):

# NOTE: the gyro is not yet calibrated in pyMIMU
self.gyro_transform = self.DEG_RAD_RATIO * np.identity(3)

self.magn_transform = np.identity(3)

def calibrate(self, vector_measurement, matrix_transform, vector_bias):
Expand Down
9 changes: 5 additions & 4 deletions fusion_filter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@


# Modified by Paulo Chiliguano and Travis West
# Modified by Paulo Chiliguano
# Developed by Travis West
# Directed by Dr Roberto Alonso Trillo
# Department of Music - Hong Kong Baptist University
# 2022
# HKBU Academy of Music
# 2024

"""Fusion filter
"""
Expand Down Expand Up @@ -60,7 +61,7 @@ def __init__(self):
self.anm1 = np.zeros(3)
self.aa = np.zeros(3)
self.gyro_bias = np.zeros(3)


def set_gyro_bias_alpha(self, v):
self.galpha = v
Expand Down
9 changes: 5 additions & 4 deletions gesture_model.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@


# Modified by Paulo Chiliguano and Travis West
# Modified by Paulo Chiliguano
# Developed by Travis West
# Directed by Dr Roberto Alonso Trillo
# Department of Music - Hong Kong Baptist University
# 2022
# HKBU Academy of Music
# 2024

"""Gesture model
"""
Expand Down Expand Up @@ -42,4 +43,4 @@ def tick(self, accl: List[float], gyro: List[float], magn: List[float]):
self.roll = self.RAD_DEG_RATIO * np.arctan2(self.matrix[2,1], self.matrix[2,2])
self.movement_acceleration = self.fusion_filter.ma_sensor
self.acceleration_derivative = self.fusion_filter.da
self.movement_velocity = self.movement_velocity * 0.9999 + self.movement_acceleration
self.movement_velocity = self.movement_velocity * 0.9999 + self.movement_acceleration
41 changes: 22 additions & 19 deletions metabow_bridge.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#!/usr/bin/python

# Modified by Paulo Chiliguano (@pauloesteban) and KA HO Wong
# Developed by Paulo Chiliguano and KA HO Wong
# Directed by Dr Roberto Alonso Trillo
# Department of Music - Hong Kong Baptist University
# 2023
# HKBU Academy of Music
# 2024

import asyncio
import csv
Expand All @@ -21,7 +21,7 @@

from pythonosc import udp_client
from gesture_model import GestureModel
from utils import bytearray_to_fusion_data, log_file_path
from utils import bytearray_to_fusion_data, log_file_path, pyquaternion_as_spherical_coords


class FusionThread(threading.Thread):
Expand Down Expand Up @@ -97,7 +97,7 @@ def __init__(self, loop):
'accl_roll'
]


def _instantiate_scanner(self):
try:
self.scanner = BleakScanner(self.device_detected)
Expand All @@ -122,15 +122,15 @@ def on_exit(self):
def create_ports_frame(self, container):
label_frame = ttk.Labelframe(container, text='UDP Ports', relief=tk.RIDGE)
label_frame.grid(row=0, column=0, sticky=tk.W)
title = ttk.Label(label_frame, text=f"Device Port:")
title = ttk.Label(label_frame, text="Device Port:")
title.grid(row=0, column=0, sticky=tk.W)
self.port0_spinbox = ttk.Spinbox(label_frame,
from_=1024,
to=49151,
textvariable=self.port0,
width=10)
self.port0_spinbox.grid(row=0, column=1, sticky=tk.W)
title1 = ttk.Label(label_frame, text=f"Mirror Port:")
title1 = ttk.Label(label_frame, text="Mirror Port:")
title1.grid(row=1, column=0, sticky=tk.W)
self.port1_spinbox = ttk.Spinbox(label_frame,
from_=1024,
Expand Down Expand Up @@ -159,14 +159,14 @@ def create_monitoring_frame(self, container):
self.monitoring_label.state(['disabled'])

return label_frame


def items_selected(self, event):
selected_ix = self.devices_listbox.curselection()
selected_devices_keys = [self.devices_listbox.get(i) for i in selected_ix]
self.selected_devices = [self.AM1V330_devices[i] for i in selected_devices_keys]


def create_scanner_frame(self, container):
label_frame = ttk.Labelframe(container, text='Devices', relief=tk.RIDGE)
label_frame.grid(row=0, column=0, sticky=tk.W)
Expand Down Expand Up @@ -214,7 +214,7 @@ def create_scanner_frame(self, container):

return label_frame


async def device_detected(self, device, _):
if device.name == self.device_name:
self.AM1V330_devices[device.address] = device # bleak.backends.device.BLEDevice
Expand All @@ -229,7 +229,7 @@ async def start_scan(self):
self.stop_scan_button.state(['!disabled'])
self.connect_button.state(['disabled'])
self.refresh_listbox = True


async def stop_scan(self):
self.start_scan_button.state(['!disabled'])
Expand All @@ -255,7 +255,7 @@ async def connect(self):
self._create_csv_file()
await asyncio.gather(*(self.notify(i, device) for i, device in enumerate(self.selected_devices)))


async def notify(self, i, device):
async with BleakClient(device) as client:
if self.option_address.get() == "0":
Expand Down Expand Up @@ -309,7 +309,7 @@ def notification_handler(self, device_number: int | str, sender: int, data: byte
else:
self._send_messages(device_number, self.udp_client, self.model)
write_thread.join()


def _write_log(self, timestamp, device_number, fusion_data, model):
data = [device_number,
Expand All @@ -322,16 +322,19 @@ def _write_log(self, timestamp, device_number, fusion_data, model):
model.skewness,
model.tilt,
model.roll]

with open(self.log_name, 'a', encoding='UTF8') as f:
writer = csv.writer(f)
writer.writerow(data)


def _send_messages(self, device_number, udp_client, model):
address_quaternion = f"/{device_number}/quaternion"
quaternion = model.quaternion.elements.tolist()
elements = model.quaternion.elements
quaternion = elements.tolist()
udp_client.send_message(address_quaternion, quaternion)
spherical_coords = pyquaternion_as_spherical_coords(elements)
udp_client.send_message(f"/{device_number}/spherical_coords", spherical_coords.tolist())
address_sensor_frame = f"/{device_number}/motion_acceleration/sensor_frame"
movement_accl = model.movement_acceleration.tolist()
udp_client.send_message(address_sensor_frame, movement_accl)
Expand Down Expand Up @@ -359,14 +362,14 @@ def populate_devices(self):
for k, v in self.AM1V330_devices.items():
self.devices_listbox.insert('end', k)


def _create_csv_file(self):
self.log_name = log_file_path()
with open(self.log_name, 'w', encoding='UTF8') as f:
writer = csv.writer(f)
writer.writerow(self.csv_header)


async def show(self):
while not self.is_destroyed:
if self.refresh_listbox:
Expand All @@ -387,4 +390,4 @@ async def metabow(self):


if __name__ == '__main__':
asyncio.run(App().metabow())
asyncio.run(App().metabow())
2 changes: 1 addition & 1 deletion metabow_bridge.spec
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ app = BUNDLE(
name='bridge.app',
icon=None,
bundle_identifier='com.metabow.bridge',
version='1.2.0',
version='1.4.0',
info_plist={
'NSBluetoothAlwaysUsageDescription': 'This app uses Bluetooth.'
}
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
numpy<1.26,>=1.23.2
numpy<1.26.5,>=1.26
numpy-quaternion>=2023.0.2
pyquaternion<1.0.0
python-osc<1.9.0
bleak<0.21,>=0.15.1
20 changes: 13 additions & 7 deletions utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Created by Paulo Chiliguano (@pauloesteban)
# Developed by Paulo Chiliguano
# Directed by Dr Roberto Alonso Trillo
# Department of Music - Hong Kong Baptist University
# 2022
# 2024


import quaternion
from datetime import datetime
from os import makedirs
from os.path import expandvars, isdir, join
Expand Down Expand Up @@ -118,10 +119,15 @@ def log_file_path() -> str:
else:
homefolder = expandvars(r"$HOME")

dir = join(homefolder, "Desktop", "Metabow Logs")
_create_folder_in_desktop(dir)
log_dir = join(homefolder, "Desktop", "Metabow Logs")

_create_folder_in_desktop(log_dir)
now = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = join(dir, f"mb_{now}.csv")
filename = join(log_dir, f"mb_{now}.csv")

return filename


return filename
def pyquaternion_as_spherical_coords(elements):
elems = quaternion.from_float_array(elements)
return quaternion.as_spherical_coords(elems)

0 comments on commit 2cbeda9

Please sign in to comment.