Pracownia Sygnałów Biologicznych/Zajecia 5 6: Różnice pomiędzy wersjami
m (→Wstęp) |
m |
||
(Nie pokazano 40 pośrednich wersji utworzonych przez tego samego użytkownika) | |||
Linia 1: | Linia 1: | ||
+ | [[Pracownia Sygnałów Bioelektrycznych]]/EMG | ||
+ | |||
<b>Pomiar EMG</b> | <b>Pomiar EMG</b> | ||
==Wstęp== | ==Wstęp== | ||
− | [https:// | + | [https://www.youtube.com/watch?v=w_R5t2-C5cA Filmik ilustrujący działanie mięśni] |
Linia 11: | Linia 13: | ||
[[Plik:Electromyogram.png|250px|thumb|right|<figure id="fig:Electromyogram"></figure> Przykłady elektromiogramów. Panel górny — pacjent zdrowy. Panel środkowy — pacjent ze zmianami w nerwach obwodowych (neuropatia). Panel dolny — pacjent ze zmianami w mięśniach (miopatia).]] | [[Plik:Electromyogram.png|250px|thumb|right|<figure id="fig:Electromyogram"></figure> Przykłady elektromiogramów. Panel górny — pacjent zdrowy. Panel środkowy — pacjent ze zmianami w nerwach obwodowych (neuropatia). Panel dolny — pacjent ze zmianami w mięśniach (miopatia).]] | ||
− | Badanie powierzchniowe EMG wykonuje się z użyciem elektrod samoprzylepnych, umieszczonych na powierzchni skóry. Ocenie podlegają mięśnie położone powierzchownie lub grupy mięśni. | + | |
+ | Badanie powierzchniowe EMG wykonuje się z użyciem elektrod samoprzylepnych, umieszczonych na powierzchni skóry, zwykle elektrody bipolarne są rozmieszczone na linii równoległej do włókien. Ocenie podlegają mięśnie położone powierzchownie lub grupy mięśni. | ||
Obydwie metody mają swoje wady i zalety. Metoda „igłowa” umożliwia rejestrację sygnału EMG z wybranego mięśnia, podczas gdy metoda powierzchniowa rejestruje zbiorczą aktywność wielu jednostek ruchowych. Jednakże, w przeciwieństwie do metody powierzchniowej, metoda igłowa jest badaniem inwazyjnym i czasem bolesnym, które wykonywane jest w ośrodku klinicznym. | Obydwie metody mają swoje wady i zalety. Metoda „igłowa” umożliwia rejestrację sygnału EMG z wybranego mięśnia, podczas gdy metoda powierzchniowa rejestruje zbiorczą aktywność wielu jednostek ruchowych. Jednakże, w przeciwieństwie do metody powierzchniowej, metoda igłowa jest badaniem inwazyjnym i czasem bolesnym, które wykonywane jest w ośrodku klinicznym. | ||
Linia 44: | Linia 47: | ||
== Ćwiczenia == | == Ćwiczenia == | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== Ćwiczenie I: Badanie zależności sygnału EMG od obciążenia === | === Ćwiczenie I: Badanie zależności sygnału EMG od obciążenia === | ||
+ | ====Pomiar==== | ||
* umieść elektrodę GND na wewnętrznej części przedramienia, w połowie jego długości, | * umieść elektrodę GND na wewnętrznej części przedramienia, w połowie jego długości, | ||
* umieść dwie elektrody do rejestracji sygnału EMG na mięśniu dwugłowym ramienia (popularnie zwanym bicepsem). Kable tych elektrod połącz z unipolarnymi wejściami wzmacniacza numer 1 i 2. | * umieść dwie elektrody do rejestracji sygnału EMG na mięśniu dwugłowym ramienia (popularnie zwanym bicepsem). Kable tych elektrod połącz z unipolarnymi wejściami wzmacniacza numer 1 i 2. | ||
* ustaw częstość próbkowania sygnału na 2048 | * ustaw częstość próbkowania sygnału na 2048 | ||
− | + | ===== Obserwacje wstępne ===== | |
− | * dobierz odpowiednio filtry górnoprzepustowe | + | W tej części proszę wykonać wskazane poniżej obserwacje i zarejestrować fragmenty sygnałów, które mogłyby ilustrować ciekawe obserwacje, aby można je było wykorzystać w końcowej prezentacji. |
− | * | + | |
− | * zarejestruj około 1 min. sygnału spoczynkowego i | + | |
+ | * W SVAROGU: | ||
+ | ** badać będziemy mięsień przedramienia | ||
+ | ** przetestuj sygnały rejestrowane w kilku różnych lokalizacjach względem mięśnia | ||
+ | ** Przyjrzyj się przebiegowi i widmu sygnałów | ||
+ | ** dobierz odpowiednio filtry górnoprzepustowe | ||
+ | ** zaobserwuj czy i kiedy w sygnale pojawiają się artefakty | ||
+ | ** porównaj sygnały rejestrowane przez elektrody bipolarne i przez elektrody monopolarne zmontowane bipolarnie | ||
+ | |||
+ | ===== Rejestracja 1: napinanie i rozluźnianie ręki ===== | ||
+ | Zarejestruj sygnał, w którym naprzemiennie po około 5 s napinasz i rozluźniasz biceps lub ściskasz i rozluźniasz pięść (dla badania mięśnia przedramienia). | ||
+ | |||
+ | ===== Rejestracja 2: zwiększanie obciążenia ręki ===== | ||
+ | Rejestrujemy dwa stany: spoczynek, narastajace obciążenie: | ||
+ | * zarejestruj około 1 min. sygnału spoczynkowego | ||
+ | * w dalszej części będziemy stopniowo zwiększać obciążenie: co około 20 s kolejne zwiększenie obciążenia - 5 lub 6 poziomów | ||
+ | |||
+ | ===== Rejestracja 3: stałe obciążenie ręki ===== | ||
+ | Rejestrujemy dwa stany: spoczynek, narastajace obciążenie: | ||
+ | * zarejestruj około 20s. sygnału spoczynkowego | ||
+ | * w dalszej części dokładamy obciążenie (środkowy poziom z poprzedniego ćwiczenia) i rejestrujemy 1 min. sygnału | ||
+ | |||
+ | |||
+ | <!-- | ||
+ | w 2021 AD:"każda grupa robiła eksperyment, w którym mieli na zmianę ruszać lewą bądź prawą reką (~ 5s na ruch) (wszystko było przez psychopy, powinny być tagi w plikach *.tag). Potem tym, co szło szybciej zadałam badanie EMG pod wpływem obciązenia - tutaj mieli sobie zmodyfikowac procedurę poprzednią i np. na nową komendę dokładać książki, każda grupa oceniała sama ile książek udźwignie, więc te dane będą trochę różne. To nie wszystcy dali radę zrobić - Ci co byli w tyle przeszli do badania EMG z twarzy. Tutaj były dwa warunki: bierne patrzenie i naśladowanie - każda grupa miała sobie wybrać po 3 mięśnie w zależności jakie emocje wybrali do eksperymentu (każda grupa wybierała z 7 zdjęć: 6 emocji podst. i 1 neurtr.). " | ||
+ | (<strong> w roku 2020 analizujemy plik 'spoczynek_wysilek.raw'</strong>): | ||
** oblicz ich średnią amplitudę (odchylenie standardowe) | ** oblicz ich średnią amplitudę (odchylenie standardowe) | ||
− | ** wykreśl | + | |
− | ** | + | Do wczytywanie sygnałów w pythonie wykorzystaj biblioteke ReadManager (http://moduly-analizy-danych.braintech.pl) |
+ | |||
+ | Porównaj wyniki detekcji z zapisami tagów w pliku *.tag | ||
+ | --> | ||
+ | |||
+ | ====Analiza==== | ||
+ | * Dalsze analizy robimy w pythonie (notebook). | ||
+ | ** wykreśl sygnał | ||
+ | ** sygnały filtrujemy: 30 Hz high-pass, 500 Hz low-pass and 50 Hz notch, | ||
+ | ** Przedstaw przebieg średniej mocy sygnałów. Aby to zrobić podnieś próbki do kwadratu i uśrednij za pomocą średniej biegnącej o długości 0.1 s (filtrowanie oknem prostokątnym). Porównaj uzyskane przebiegi z przebiegiem sygnałów wejściowych. | ||
+ | ** Zaproponuj algorytm detektora, wykrywającego ruch ręki. | ||
+ | |||
+ | * Zbadaj zależność parametrów sygnału EMG od obciązenia mięśnia: | ||
+ | ** Predstaw wykres średnią amplitudę (odchylenie standardowe) od obciążenia | ||
+ | ** Przedstaw widma dla kolejnych obciążeń (na wspólnym wykresie). Do estymacji wykorzystaj metodę Welcha, dobierając parametry tak aby rodzielczość częstotliwościowa była 1Hz. | ||
+ | ** Przedstaw mapę czas-częstość (rozważ spectrogram i scalogram - czy któraś z technik jest tu lepsza?) dla całego zapisu. Zaobserwuj jakie zmiany następują w widmie wraz ze wzrostem obciążenia. | ||
+ | |||
+ | * Zbadaj zależność parametrów sygnału EMG od zmęczenia mięśnia: | ||
+ | ** Przedstaw mapę czas-częstość dla całego czasu rejestracji. Zaobserwuj jakie zmiany następują w widmie wraz z czasem. | ||
+ | ** Przedstaw ewolucję czasową mediany widma | ||
+ | |||
<!-- | <!-- | ||
* Zbadaj zależność parametrów sygnału EMG od obciązenia mięśnia: | * Zbadaj zależność parametrów sygnału EMG od obciązenia mięśnia: | ||
Linia 76: | Linia 115: | ||
--> | --> | ||
+ | === Ćwiczenie II: Analiza napięcia mięśni twarzy pod wpływem bodźców emocjonalnych === | ||
+ | ====Pomiar==== | ||
+ | * Zamontuj pary elektrod nad wybranymi trzema mięśniami uczestniczącymi w wyrażaniu prezentowanych emocji. Uziemienie umieść na obojczyku. | ||
+ | * Ustaw częstość rejestracji na 2048 Hz | ||
+ | * Uruchom procedurę eksperymentalną w SVAROGU (menu psychopy). | ||
+ | |||
+ | ====Analiza==== | ||
+ | * Analizę oprzemy na art. https://www.researchgate.net/publication/247901198_Dynamic_facial_expressions-_EMG_study | ||
+ | ** sygnały filtrujemy: 30 Hz high-pass, 500 Hz low-pass and 50 Hz notch, | ||
+ | ** podnosimy próbki do kwadratu | ||
+ | ** wygładzamy średnią biegnącą o długości 50 ms. | ||
+ | |||
+ | * Zaczniemy od analizy sygnałów z procedury czynnej (Blok 2). | ||
+ | ** korzystając z tagów wytnij fragmenty sygnału (wygładzonego) od -1s do 1 s po bodźcu wg. typów emocji | ||
+ | ** zastosuj "baseline correction" tzn. odejmij od otrzymanych wycinków średnią z sek. poprzedzającej bodziec | ||
+ | ** nałóż na siebie wycięte fragmenty z każdej pary elektrod osobno w każdej z kategorii emocji. | ||
+ | ** zaobserwuj powtarzalność aktywacji mięśni. Czy występują jakieś wyraźne artefakty? Jeśli tak, to usuń je z zestawu danych i uśrednij pozostałe fragmenty. | ||
+ | |||
+ | * W procedurze biernej powtórz powyższe kroki analizy i porównaj występujące wzorce z procedurą aktywną | ||
+ | |||
+ | === Ćwiczenie III: zapoznanie się z sygnałami rejestrowanymi przez inwazyjne EMG=== | ||
+ | Proszę wczytać i przyjrzeć się sygnałom | ||
+ | [https://drive.google.com/file/d/1AO7w4m2F3nkSzISYXAeDGCpjMIRDx5NS/view?usp=sharing zdrowy.bin], | ||
+ | [https://drive.google.com/file/d/1APY5xDQJrwFuPY_V7Uza9JrKSgew1Yx1/view?usp=sharing miopatia.bin], | ||
+ | [https://drive.google.com/file/d/1ARzqLlYYLmhyefkBsOkoQBx4ToYpQ3yK/view?usp=sharing neuropatia.bin]. Sygnały są zapisane jako dtype='float64' | ||
+ | Częstość próbkowania 4000Hz, amplitudy zapisane są w mV. Dane pochodzą z bazy Physionet: https://physionet.org/content/emgdb/1.0.0/ | ||
+ | |||
+ | Proszę przeczytać informację o tych danych. W raporcie proszę napisać istotne informacje o tych sygnałach i wybrać charakterystyczne fragmenty typowe dla danego stanu klinicznego. | ||
+ | |||
+ | <!-- | ||
+ | === Ćwiczenie IV: detekcja aktywności wybranej jednostki ruchowej=== | ||
+ | Wstępna symulacja: | ||
+ | |||
+ | Proszę wykonać i przmyśleć wynik następującej symulacji: | ||
+ | |||
+ | <source lang = python> | ||
+ | import matplotlib.pylab as plt | ||
+ | import numpy as np | ||
+ | import scipy.signal as ss | ||
+ | |||
+ | x = np.array([0,0, 1, 2, 3, 4, 4, 4, 6, 0], dtype=float) | ||
+ | y = np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 1, 2, 3, 4, 4, 4, 6, 0,0, 0,0, 0,0, 0,0, 0,0], dtype=float) | ||
+ | |||
+ | h = np.flip(x)/np.linalg.norm(x) | ||
+ | z = ss.lfilter(h,[1],y) | ||
+ | |||
+ | wzorzec = x/np.linalg.norm(x) | ||
+ | Nw = len(wzorzec) | ||
+ | zz = np.zeros(y.shape) | ||
+ | for i in range(len(y)-Nw): | ||
+ | zz[i+Nw-1] = np.dot(wzorzec,y[i:i+Nw]) | ||
+ | |||
+ | plt.plot(x) | ||
+ | plt.plot(y) | ||
+ | plt.plot(z) | ||
+ | plt.plot(zz,'.') | ||
+ | plt.legend(('x','y','z','zz')) | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Będziemy analizować sygnał: zdrowy.bin | ||
+ | * jako wzorzec poszukiwany przyjmij fragment sygnału pomiędzy: 0.110 do 0.124 s | ||
+ | * za pomocą analogicznej techniki spróbuj określić wystąpienia struktur podobnych do wzorca. | ||
+ | * wykryj wystąpienia poszczególnych jednostek ruchowych w sygnale z neuropatii. | ||
+ | |||
+ | <!-- | ||
=== Ćwiczenie II: Badnanie przebiegu sygnału EMG względem triggera === | === Ćwiczenie II: Badnanie przebiegu sygnału EMG względem triggera === | ||
'''W roku 2020 skorzystamy z wcześniej zarejestrowanych danych zgodnie z poniższym opisem. Dane znajdują się w pliku KCIUK.raw, są to 3 kanałowe zapisy (dtype ='<f'), próbkowane 2048Hz. Dwa pierwsze kanały to monopolarnie rejestrowane kanały u nasady kciuka, kanał trzeci zawiera sygnał z triggera. | '''W roku 2020 skorzystamy z wcześniej zarejestrowanych danych zgodnie z poniższym opisem. Dane znajdują się w pliku KCIUK.raw, są to 3 kanałowe zapisy (dtype ='<f'), próbkowane 2048Hz. Dwa pierwsze kanały to monopolarnie rejestrowane kanały u nasady kciuka, kanał trzeci zawiera sygnał z triggera. | ||
Linia 91: | Linia 196: | ||
1. Wczyta zarejestrowany sygnał.<br/> | 1. Wczyta zarejestrowany sygnał.<br/> | ||
2. Przefiltruje filtrem górnoprzepustowym sygnał EMG.<br/> | 2. Przefiltruje filtrem górnoprzepustowym sygnał EMG.<br/> | ||
− | 3. Wyznaczy początki wykonywanych przez kciuk ruchów, w oparciu o analizę | + | 3. Wyznaczy początki wykonywanych przez kciuk ruchów, w oparciu o analizę amplitudy sygnału EMG.<br/> |
4. Narysuje histogram różnicy czasu <math>\Delta T = t_p - t_{emg}</math>, gdzie <math>t_p</math> — czas wykonania ruchu kciukiem wyznaczony przy pomocy sygnału z kanału ''trigger'', <math>t_{emg}</math> — czas wykonania ruchu kciukiem wyznaczony na podstawie sygnału EMG.<br/> | 4. Narysuje histogram różnicy czasu <math>\Delta T = t_p - t_{emg}</math>, gdzie <math>t_p</math> — czas wykonania ruchu kciukiem wyznaczony przy pomocy sygnału z kanału ''trigger'', <math>t_{emg}</math> — czas wykonania ruchu kciukiem wyznaczony na podstawie sygnału EMG.<br/> | ||
+ | --> | ||
+ | --> | ||
+ | === Ćwiczenie V: Wykorzystanie pomiaru EMG do sterowania on-line === | ||
+ | Przykładowy fragment kodu <tt>example.py</tt> umożliwiający odbieranie sygnału on line w pythonie przedstawiony jest poniżej. | ||
− | |||
− | |||
− | |||
− | <source lang = python> | + | |
− | from | + | Proszę dodać fragment analizujący sygnał on-line i wykrywający moment napięcia mięśnia. Po wykryciu w najprostszej wersji niech w terminalu pojawia się komunikat o wykryciu kliknięcia. W wersji max proszę zrobić wizualizację napięcia mięśnia albo podpiąć ten sygnał do sterowania jakimś prostym interfejsem. |
+ | ==== Wersja działająca z pythonem z pakietu Svarog-Lab ==== | ||
+ | <source lang =python> | ||
+ | #!/opt/braintech-svarog-lab-python/bin/python3 | ||
+ | |||
+ | # powinno się odpalać pythonem z pakietu Svarog-Lab | ||
+ | # lub python w którym są zainstalowane sterowniki z | ||
+ | # https://braintech.pl/pliki/svarog/svarog-streamer-src/svarog-streamer-src-latest.zip | ||
+ | # zaletą jest dostęp wprost do sterownika wzmacniacza | ||
+ | # /opt/braintech-svarog-lab-python/bin/python3 | ||
+ | |||
+ | |||
+ | # Alternatywne wzmacniacze | ||
+ | # from braintech.drivers.perun32.amplifier import Perun32Amplifier as Amplifier # duży wzmacniacz na 32 kanały na USB | ||
+ | # from braintech.drivers.perun8.amplifiers import PerunCppAmplifier as Amplifier # headset na 8 kanałów | ||
+ | from braintech.drivers.tmsi.amplifiers import TmsiCppAmplifier as Amplifier # wzmacniacze TMSI | ||
+ | |||
import numpy as np | import numpy as np | ||
− | |||
− | |||
− | amp.sampling_rate = | + | # Szukamy wzmacniaczy |
+ | amps = Amplifier.get_available_amplifiers() | ||
+ | if len(amps) < 1: | ||
+ | raise Exception("Amplifier not connected") | ||
+ | amp = Amplifier(amps[0]) | ||
+ | |||
+ | amp.sampling_rate = 1024 # dla TMSI | ||
+ | # amp.sampling_rate = 500 # dla Perun8, Perun32 | ||
+ | |||
+ | |||
+ | |||
+ | def samples_to_microvolts(samples): # amplifier podaje próbki w integerach, wprost z ADC | ||
+ | return samples * gains + offsets | ||
+ | |||
amp.start_sampling() | amp.start_sampling() | ||
gains = np.array(amp.current_description.channel_gains) | gains = np.array(amp.current_description.channel_gains) | ||
offsets = np.array(amp.current_description.channel_offsets) | offsets = np.array(amp.current_description.channel_offsets) | ||
− | |||
− | |||
− | |||
− | |||
while True: | while True: | ||
− | # 16 próbek | + | # pobieramy 16 próbek |
+ | # Proponuje używać IPython dla eksperymentowania | ||
packet = amp.get_samples(16) | packet = amp.get_samples(16) | ||
print(samples_to_microvolts(packet.samples)) | print(samples_to_microvolts(packet.samples)) | ||
print(packet.ts[0]) | print(packet.ts[0]) | ||
print(packet.samples.shape, amp.current_description.channel_names) | print(packet.samples.shape, amp.current_description.channel_names) | ||
+ | |||
</source> | </source> | ||
− | + | ==== Wersja dla protokołu LSL ==== | |
+ | |||
+ | <source lang =python> | ||
+ | # gdy nie ma możliwości używać pythona wbudowanego w svarog-lab możliwe jest używanie streamowania do standardu LSL | ||
+ | |||
+ | # włączenie streamowania do LSL: | ||
+ | # w terminalu: | ||
+ | # svarog_streamer -l | ||
+ | # wypisze listę wzmacniaczy | ||
+ | # szukamy ID odpowiedniego wzmacniacza np: | ||
+ | # * Perun-8 Headset | ||
+ | # id: "Perun8 1" | ||
+ | |||
+ | # odpalamy stream LSL danego wzmacniacza: | ||
+ | # svarog_streamer -a "Perun8 1" -n "nazwa_streamu" | ||
+ | # nazwa streamu jest ważna, ponieważ streamy są widoczne w sieci LAN | ||
+ | # zaleta streamu jest też taka, że można podglądać go w Svarogu jednocześnie z naszym skryptem python | ||
+ | # po odpaleniu streamu można odpalać poniższy skrypt dowolnym pythonem z zainstalowanym numpy oraz pylsl | ||
+ | |||
+ | from pylsl import StreamInlet, resolve_stream | ||
+ | import time | ||
+ | |||
+ | nazwa_streamu = "nazwa_streamu" # należy odpowiednio zmienić na nazwę użytą w svarog_streamer -n | ||
+ | |||
+ | # znajdujemy streamy | ||
+ | print("szukamy streamy LSL") | ||
+ | streams = resolve_stream('type', 'EEG') | ||
+ | |||
+ | selected_stream = None | ||
+ | # wybieramy nasz | ||
+ | for stream in streams: | ||
+ | if stream.name() in nazwa_streamu: | ||
+ | selected_stream = stream | ||
+ | if selected_stream is None: | ||
+ | print("Nie znalesiono streamu", nazwa_streamu, "w liście", [i.name() for i in streams]) | ||
+ | exit() | ||
+ | # używamy streama | ||
+ | inlet = StreamInlet(selected_stream) | ||
− | + | while True: | |
+ | # pobieramy próbki (w mikrowoltach) | ||
+ | sample, timestamp = inlet.pull_chunk(timeout=1.0, max_samples=10) | ||
+ | print(sample, timestamp, time.monotonic()) | ||
− | + | </source> | |
− | + | === Dodatek === | |
− | Do działania on-line przydatne może być filtrowanie sygnału w sposób biegnący. Najlepiej zastosować do tego funkcję lfilter z | + | Do działania on-line przydatne może być filtrowanie sygnału w sposób biegnący. Najlepiej zastosować do tego funkcję lfilter z ustalonymi warunkami początkowymi. Używa się tego w następujący sposób: |
<source lang =python> | <source lang =python> | ||
import scipy.signal as ss | import scipy.signal as ss | ||
Linia 154: | Linia 325: | ||
zi = ss.lfilter_zi(b, a) | zi = ss.lfilter_zi(b, a) | ||
z, _ = ss.lfilter(b, a, xn, zi=zi*xn[0]) | z, _ = ss.lfilter(b, a, xn, zi=zi*xn[0]) | ||
− | plt.plot( | + | plt.plot(xn) |
plt.plot(z) | plt.plot(z) | ||
plt.show() | plt.show() | ||
</source> | </source> | ||
− | Symulacja zastosowania tego sposobu on-line; filtrujemy za każdą iteracją pętli | + | |
+ | |||
+ | |||
+ | |||
+ | ==== Filtrowanie online ==== | ||
+ | |||
+ | Symulacja zastosowania tego sposobu on-line; filtrujemy za każdą iteracją pętli to co przychodzi ze wzmacniacza [s]: | ||
<source lang =python> | <source lang =python> | ||
zi = ss.lfilter_zi(b, a) | zi = ss.lfilter_zi(b, a) | ||
− | |||
y = np.zeros(xn.shape) | y = np.zeros(xn.shape) | ||
for ind, s in enumerate(xn): | for ind, s in enumerate(xn): | ||
− | + | y_tmp, zi = ss.lfilter(b, a, [s], zi=zi) | |
− | |||
− | y_tmp, zi = ss.lfilter(b, a, | ||
y[ind]=y_tmp[-1] | y[ind]=y_tmp[-1] | ||
Linia 178: | Linia 352: | ||
</source> | </source> | ||
+ | |||
+ | |||
+ | |||
Linia 183: | Linia 360: | ||
Dla zabawy detektor napięcia mięśni mógłby generować kliknięcia myszki, aby np. zagrać w: | Dla zabawy detektor napięcia mięśni mógłby generować kliknięcia myszki, aby np. zagrać w: | ||
− | https:// | + | https://dino-chrome.com/ |
Linia 189: | Linia 366: | ||
pip3 install --user pynput | pip3 install --user pynput | ||
− | a potem zastosować kod: | + | a potem zastosować kod ze strony: |
+ | https://pynput.readthedocs.io/en/latest/keyboard.html | ||
<source lang = python> | <source lang = python> | ||
+ | |||
+ | |||
+ | from pynput.keyboard import Key, Controller | ||
+ | |||
+ | keyboard = Controller() | ||
+ | |||
+ | def spacja(): | ||
+ | # Press and release space | ||
+ | keyboard.press(Key.space) | ||
+ | keyboard.release(Key.space) | ||
+ | |||
from pynput.mouse import Button, Controller | from pynput.mouse import Button, Controller | ||
Linia 197: | Linia 386: | ||
def click(): | def click(): | ||
+ | #lewy klik myszy | ||
mouse.press(Button.left) | mouse.press(Button.left) | ||
time.sleep(500) | time.sleep(500) | ||
Linia 202: | Linia 392: | ||
</source> | </source> | ||
+ | |||
+ | ==== wersja dla systemu dostępnego w Labie 4.59 na Ubuntu 18 ==== | ||
+ | <source lang = python> | ||
+ | from obci_cpp_amplifiers.amplifiers import TmsiCppAmplifier | ||
+ | import numpy as np | ||
+ | amps = TmsiCppAmplifier.get_available_amplifiers('usb') | ||
+ | amp = TmsiCppAmplifier(amps[0]) | ||
+ | |||
+ | amp.sampling_rate = 512 | ||
+ | |||
+ | amp.start_sampling() | ||
+ | gains = np.array(amp.current_description.channel_gains) | ||
+ | offsets = np.array(amp.current_description.channel_offsets) | ||
+ | |||
+ | def samples_to_microvolts(samples): # z jednostek wzmacniacza do mikrowoltów | ||
+ | return samples * gains + offsets | ||
+ | |||
+ | while True: | ||
+ | # 16 próbek w pakiecie, nieodebrane próbki się bufurują i można odebrać je później | ||
+ | packet = amp.get_samples(16) | ||
+ | print(samples_to_microvolts(packet.samples)) | ||
+ | print(packet.ts[0]) | ||
+ | print(packet.samples.shape, amp.current_description.channel_names) | ||
+ | |||
+ | </source> | ||
+ | |||
+ | Aby wykonać go w terminalu należy uruchomić polecenie, Uwaga, aby zadziałał trzeba wyłączyć SVAROGa. Proszę przetestować czy po podłączeniu wzmacniacza i uruchomieniu tego skryptu pojawiają się w terminalu wartości próbek. | ||
+ | |||
+ | |||
+ | /opt/braintech/bin/python3 example.py | ||
+ | |||
<!-- | <!-- | ||
Ćwiczenie polega pomiarze czasu reakcji badanej osoby. W tym celu: | Ćwiczenie polega pomiarze czasu reakcji badanej osoby. W tym celu: |
Aktualna wersja na dzień 19:10, 5 maj 2024
Pracownia Sygnałów Bioelektrycznych/EMG
Pomiar EMG
Spis treści
- 1 Wstęp
- 2 Źródła błędu
- 3 Ćwiczenia
- 3.1 Ćwiczenie I: Badanie zależności sygnału EMG od obciążenia
- 3.2 Ćwiczenie II: Analiza napięcia mięśni twarzy pod wpływem bodźców emocjonalnych
- 3.3 Ćwiczenie III: zapoznanie się z sygnałami rejestrowanymi przez inwazyjne EMG
- 3.4 Ćwiczenie V: Wykorzystanie pomiaru EMG do sterowania on-line
- 3.5 Dodatek
Wstęp
Filmik ilustrujący działanie mięśni
Sygnały elektro-fizjologiczne pochodzące z mięśni nazywa się elektromiogramem (EMG).
Elektromiografia jest jednym z podstawowych badań w rozpoznawaniu chorób mięśni i nerwów obwodowych. Ma ona również wiele zastosowań naukowych. Amplituda sygnału EMG wynosi od około kilkudziesięciu μV do 10 mV, zaś pasmo sygnału obejmuje zakres częstości od 2 do 5000 Hz, przy czym największa energia sygnału znajduje się w przedziale od 50 do 150 Hz.
Istnieją dwa sposoby pomiaru sygnałów EMG — badanie igłowe i powierzchniowe. W badaniu igłowym EMG, elektroda igłowa lub igła z dwoma elektrodami wbijana jest w mięsień lub w nerw ruchowy. Następnie obserwuje się aktywność elektryczną mięśni w spoczynku i podczas wysiłku.
Badanie powierzchniowe EMG wykonuje się z użyciem elektrod samoprzylepnych, umieszczonych na powierzchni skóry, zwykle elektrody bipolarne są rozmieszczone na linii równoległej do włókien. Ocenie podlegają mięśnie położone powierzchownie lub grupy mięśni. Obydwie metody mają swoje wady i zalety. Metoda „igłowa” umożliwia rejestrację sygnału EMG z wybranego mięśnia, podczas gdy metoda powierzchniowa rejestruje zbiorczą aktywność wielu jednostek ruchowych. Jednakże, w przeciwieństwie do metody powierzchniowej, metoda igłowa jest badaniem inwazyjnym i czasem bolesnym, które wykonywane jest w ośrodku klinicznym.
Na zajęciach przeprowadzimy powierzchniowy pomiar EMG (w literaturze często takie badanie oznacza się skrótem sEMG, s od ang. surface — powierzchnia). W tym celu umieścimy elektrody na skórze, nad mięśniami, których aktywność chcemy zbadać.
Filmik o zastosowaniach klinicznych EMG
Lektura uzupełniająca: Reaz MBI, Hussain MS and Mohd-Yasin F. Techniques of EMG signal analysis: detection, processing, classification and applications. Biol. Proced. Online 2006; 8(1): 11-35. https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1455479/pdf/bpo_v8_p11_m115.pdf
Źródła błędu
Rejestracja sygnału EMG, podobnie jak pomiar innych sygnały bioelektrycznych podlega pewnym zakłóceniom. Są to przede wszystkim
- artefakty ruchowe,
- artefakty związane z obecnością zewnętrznych pól elektromagnetycznych.
Artefakty ruchowe
W wyniku aktywacji mięśnia, ulega on skróceniu, co powoduje przemieszczanie się względem siebie mięśnia, skóry i elektrody. Przemieszczenie to powoduje zmianę amplitudy mierzonego sygnału (np. elektroda oddala się od mięśnia), zmianę potencjału DC (potencjału powstającego na granicy skóra-elektrolit) oraz rozciągniecie skóry. Jak pamiętamy z pierwszych zajęć pomiędzy naskórkiem a skórą właściwą istnieje różnica potencjałów o amplitudzie dochodzącej do 30 mV. W trakcie rozciągania skóry, wartość to spada do 25 mV. Powstająca zmiana potencjału, o wartości około 5 mV jest w porównaniu z sygnałem EMG bardzo znacząca.
Artefakty sieciowe
Artefakty sieciowe stanowią poważny problem w przypadku rejestracji sygnału EMG. Zwykle bowiem nie obserwujemy tylko zakłócenia o jednej częstości, równej częstości zmian napięcia w gniazdku zasilającym (np. w Polsce jest to 50 Hz), lecz również wyższe harmoniczne tej częstości (w Polsce będzie to 100 Hz, 150 Hz, 200 Hz, itd). Jak można zauważyć, częstości 50 Hz, 100 Hz i 150 Hz znajdują się w paśmie, w którym sygnał EMG ma największą energię. Stosowanie filtrów pasmowo zaporowych w takim przypadku nie jest wskazane, bowiem filtry jak wiemy nie tłumią ściśle określonych częstości tylko pasma o pewnej szerokości (np. 45-55 Hz, 95-105 Hz, itd). W efekcie znaczna część interesującego nas pasma sygnału EMG zostałaby odrzucona. Aby zminimalizować przenikanie od rejestrowanego sygnału EMG sygnału sieciowego o częstości 50 Hz należy zadbać o niską impedancję pomiędzy elektrodą a skóra oraz powinno mierzyć się sygnały różnicowe.
Artefakty sieciowo-ruchowe
Podczas ruchu mięśnia ciało może ulec przemieszczeniu, co spowoduje ruch kabla w przestrzeni pomiędzy elektrodą a wzmacniaczem. W przestrzeni tej istnieje pole elektromagnetyczne, wywołane zasilaniem sieci elektrycznej. Ruch kabla w polu elektromagnetycznym może powodować zaburzenia sygnału o częstości 50 Hz. Ponadto układ elektrody-kable-wzmacniacz, tworzy pewne ramki, które w trakcie ruchu zmieniają kształt a także powierzchnię. Zgodnie z Prawem Indukcji Faraday'a zmiana strumienia magnetycznego powoduje powstawanie siły elektromotorycznej, która również może zakłócać pomiar.
Artefakty ruchowe można w większości wyeliminować przez zastosowanie filtru górnoprzepustowego, którego częstość odcięcia ustawia się w granicy od 10 do 20 Hz. Aby wyeliminować artefakty związane z ruchem kabla w polu elektromagnetycznym można zastosować tzw. elektrody aktywne. W elektrodach tych (miniaturowy) wzmacniacz znajduje się na elektrodzie. Wzmacnianie sygnału na elektrodzie zwiększa względną czułość układu na sygnał mierzony na elektrodzie w stosunku do zaburzenia związanego z ruchem kabla. Innym rozwiązaniem tego problemu jest zastosowanie kabli ekranowanych. Kable TMSI, których używamy na Pracowni są kablami ekranowanymi. W kablach tych, pomiędzy dwiema warstwami izolatora, istnieje dodatkowa osłona z przewodnika podłączona do wzmacniacza. Dzięki temu, zewnętrzne pole elektromagnetyczne nie przenika do środka kabla. Dodatkowo, aby wyeliminować indukowanie się ładunków w wyniku tarcia pomiędzy izolatorami a osłoną, jest ona częściowo pokryta warstwą węgla.
Sygnały do naszych zajęć znajdują się w tym katalogu: https://drive.google.com/drive/folders/18skluB3j2CHMXX8l1UmIvNXR1tZYGU0C?usp=sharing Proszę je pobrać.
Ćwiczenia
Ćwiczenie I: Badanie zależności sygnału EMG od obciążenia
Pomiar
- umieść elektrodę GND na wewnętrznej części przedramienia, w połowie jego długości,
- umieść dwie elektrody do rejestracji sygnału EMG na mięśniu dwugłowym ramienia (popularnie zwanym bicepsem). Kable tych elektrod połącz z unipolarnymi wejściami wzmacniacza numer 1 i 2.
- ustaw częstość próbkowania sygnału na 2048
Obserwacje wstępne
W tej części proszę wykonać wskazane poniżej obserwacje i zarejestrować fragmenty sygnałów, które mogłyby ilustrować ciekawe obserwacje, aby można je było wykorzystać w końcowej prezentacji.
- W SVAROGU:
- badać będziemy mięsień przedramienia
- przetestuj sygnały rejestrowane w kilku różnych lokalizacjach względem mięśnia
- Przyjrzyj się przebiegowi i widmu sygnałów
- dobierz odpowiednio filtry górnoprzepustowe
- zaobserwuj czy i kiedy w sygnale pojawiają się artefakty
- porównaj sygnały rejestrowane przez elektrody bipolarne i przez elektrody monopolarne zmontowane bipolarnie
Rejestracja 1: napinanie i rozluźnianie ręki
Zarejestruj sygnał, w którym naprzemiennie po około 5 s napinasz i rozluźniasz biceps lub ściskasz i rozluźniasz pięść (dla badania mięśnia przedramienia).
Rejestracja 2: zwiększanie obciążenia ręki
Rejestrujemy dwa stany: spoczynek, narastajace obciążenie:
- zarejestruj około 1 min. sygnału spoczynkowego
- w dalszej części będziemy stopniowo zwiększać obciążenie: co około 20 s kolejne zwiększenie obciążenia - 5 lub 6 poziomów
Rejestracja 3: stałe obciążenie ręki
Rejestrujemy dwa stany: spoczynek, narastajace obciążenie:
- zarejestruj około 20s. sygnału spoczynkowego
- w dalszej części dokładamy obciążenie (środkowy poziom z poprzedniego ćwiczenia) i rejestrujemy 1 min. sygnału
Analiza
- Dalsze analizy robimy w pythonie (notebook).
- wykreśl sygnał
- sygnały filtrujemy: 30 Hz high-pass, 500 Hz low-pass and 50 Hz notch,
- Przedstaw przebieg średniej mocy sygnałów. Aby to zrobić podnieś próbki do kwadratu i uśrednij za pomocą średniej biegnącej o długości 0.1 s (filtrowanie oknem prostokątnym). Porównaj uzyskane przebiegi z przebiegiem sygnałów wejściowych.
- Zaproponuj algorytm detektora, wykrywającego ruch ręki.
- Zbadaj zależność parametrów sygnału EMG od obciązenia mięśnia:
- Predstaw wykres średnią amplitudę (odchylenie standardowe) od obciążenia
- Przedstaw widma dla kolejnych obciążeń (na wspólnym wykresie). Do estymacji wykorzystaj metodę Welcha, dobierając parametry tak aby rodzielczość częstotliwościowa była 1Hz.
- Przedstaw mapę czas-częstość (rozważ spectrogram i scalogram - czy któraś z technik jest tu lepsza?) dla całego zapisu. Zaobserwuj jakie zmiany następują w widmie wraz ze wzrostem obciążenia.
- Zbadaj zależność parametrów sygnału EMG od zmęczenia mięśnia:
- Przedstaw mapę czas-częstość dla całego czasu rejestracji. Zaobserwuj jakie zmiany następują w widmie wraz z czasem.
- Przedstaw ewolucję czasową mediany widma
Ćwiczenie II: Analiza napięcia mięśni twarzy pod wpływem bodźców emocjonalnych
Pomiar
- Zamontuj pary elektrod nad wybranymi trzema mięśniami uczestniczącymi w wyrażaniu prezentowanych emocji. Uziemienie umieść na obojczyku.
- Ustaw częstość rejestracji na 2048 Hz
- Uruchom procedurę eksperymentalną w SVAROGU (menu psychopy).
Analiza
- Analizę oprzemy na art. https://www.researchgate.net/publication/247901198_Dynamic_facial_expressions-_EMG_study
- sygnały filtrujemy: 30 Hz high-pass, 500 Hz low-pass and 50 Hz notch,
- podnosimy próbki do kwadratu
- wygładzamy średnią biegnącą o długości 50 ms.
- Zaczniemy od analizy sygnałów z procedury czynnej (Blok 2).
- korzystając z tagów wytnij fragmenty sygnału (wygładzonego) od -1s do 1 s po bodźcu wg. typów emocji
- zastosuj "baseline correction" tzn. odejmij od otrzymanych wycinków średnią z sek. poprzedzającej bodziec
- nałóż na siebie wycięte fragmenty z każdej pary elektrod osobno w każdej z kategorii emocji.
- zaobserwuj powtarzalność aktywacji mięśni. Czy występują jakieś wyraźne artefakty? Jeśli tak, to usuń je z zestawu danych i uśrednij pozostałe fragmenty.
- W procedurze biernej powtórz powyższe kroki analizy i porównaj występujące wzorce z procedurą aktywną
Ćwiczenie III: zapoznanie się z sygnałami rejestrowanymi przez inwazyjne EMG
Proszę wczytać i przyjrzeć się sygnałom zdrowy.bin, miopatia.bin, neuropatia.bin. Sygnały są zapisane jako dtype='float64' Częstość próbkowania 4000Hz, amplitudy zapisane są w mV. Dane pochodzą z bazy Physionet: https://physionet.org/content/emgdb/1.0.0/
Proszę przeczytać informację o tych danych. W raporcie proszę napisać istotne informacje o tych sygnałach i wybrać charakterystyczne fragmenty typowe dla danego stanu klinicznego.
-->
Ćwiczenie V: Wykorzystanie pomiaru EMG do sterowania on-line
Przykładowy fragment kodu example.py umożliwiający odbieranie sygnału on line w pythonie przedstawiony jest poniżej.
Proszę dodać fragment analizujący sygnał on-line i wykrywający moment napięcia mięśnia. Po wykryciu w najprostszej wersji niech w terminalu pojawia się komunikat o wykryciu kliknięcia. W wersji max proszę zrobić wizualizację napięcia mięśnia albo podpiąć ten sygnał do sterowania jakimś prostym interfejsem.
Wersja działająca z pythonem z pakietu Svarog-Lab
#!/opt/braintech-svarog-lab-python/bin/python3
# powinno się odpalać pythonem z pakietu Svarog-Lab
# lub python w którym są zainstalowane sterowniki z
# https://braintech.pl/pliki/svarog/svarog-streamer-src/svarog-streamer-src-latest.zip
# zaletą jest dostęp wprost do sterownika wzmacniacza
# /opt/braintech-svarog-lab-python/bin/python3
# Alternatywne wzmacniacze
# from braintech.drivers.perun32.amplifier import Perun32Amplifier as Amplifier # duży wzmacniacz na 32 kanały na USB
# from braintech.drivers.perun8.amplifiers import PerunCppAmplifier as Amplifier # headset na 8 kanałów
from braintech.drivers.tmsi.amplifiers import TmsiCppAmplifier as Amplifier # wzmacniacze TMSI
import numpy as np
# Szukamy wzmacniaczy
amps = Amplifier.get_available_amplifiers()
if len(amps) < 1:
raise Exception("Amplifier not connected")
amp = Amplifier(amps[0])
amp.sampling_rate = 1024 # dla TMSI
# amp.sampling_rate = 500 # dla Perun8, Perun32
def samples_to_microvolts(samples): # amplifier podaje próbki w integerach, wprost z ADC
return samples * gains + offsets
amp.start_sampling()
gains = np.array(amp.current_description.channel_gains)
offsets = np.array(amp.current_description.channel_offsets)
while True:
# pobieramy 16 próbek
# Proponuje używać IPython dla eksperymentowania
packet = amp.get_samples(16)
print(samples_to_microvolts(packet.samples))
print(packet.ts[0])
print(packet.samples.shape, amp.current_description.channel_names)
Wersja dla protokołu LSL
# gdy nie ma możliwości używać pythona wbudowanego w svarog-lab możliwe jest używanie streamowania do standardu LSL
# włączenie streamowania do LSL:
# w terminalu:
# svarog_streamer -l
# wypisze listę wzmacniaczy
# szukamy ID odpowiedniego wzmacniacza np:
# * Perun-8 Headset
# id: "Perun8 1"
# odpalamy stream LSL danego wzmacniacza:
# svarog_streamer -a "Perun8 1" -n "nazwa_streamu"
# nazwa streamu jest ważna, ponieważ streamy są widoczne w sieci LAN
# zaleta streamu jest też taka, że można podglądać go w Svarogu jednocześnie z naszym skryptem python
# po odpaleniu streamu można odpalać poniższy skrypt dowolnym pythonem z zainstalowanym numpy oraz pylsl
from pylsl import StreamInlet, resolve_stream
import time
nazwa_streamu = "nazwa_streamu" # należy odpowiednio zmienić na nazwę użytą w svarog_streamer -n
# znajdujemy streamy
print("szukamy streamy LSL")
streams = resolve_stream('type', 'EEG')
selected_stream = None
# wybieramy nasz
for stream in streams:
if stream.name() in nazwa_streamu:
selected_stream = stream
if selected_stream is None:
print("Nie znalesiono streamu", nazwa_streamu, "w liście", [i.name() for i in streams])
exit()
# używamy streama
inlet = StreamInlet(selected_stream)
while True:
# pobieramy próbki (w mikrowoltach)
sample, timestamp = inlet.pull_chunk(timeout=1.0, max_samples=10)
print(sample, timestamp, time.monotonic())
Dodatek
Do działania on-line przydatne może być filtrowanie sygnału w sposób biegnący. Najlepiej zastosować do tego funkcję lfilter z ustalonymi warunkami początkowymi. Używa się tego w następujący sposób:
import scipy.signal as ss
import numpy as np
import matplotlib.pyplot as plt
Fs = 256
T = 1
t = np.arange(0,T,1/Fs)
f0 = 10
f1 = 17
f2 = 23
x = (np.sin(2*np.pi*f0*t) +
np.sin(2*np.pi*f1*t ) +
np.cos(2*np.pi*f2*t))
xn = x + np.random.randn(len(t)) * 0.08
b, a = ss.butter(3, 11/(Fs/2))
zi = ss.lfilter_zi(b, a)
z, _ = ss.lfilter(b, a, xn, zi=zi*xn[0])
plt.plot(xn)
plt.plot(z)
plt.show()
Filtrowanie online
Symulacja zastosowania tego sposobu on-line; filtrujemy za każdą iteracją pętli to co przychodzi ze wzmacniacza [s]:
zi = ss.lfilter_zi(b, a)
y = np.zeros(xn.shape)
for ind, s in enumerate(xn):
y_tmp, zi = ss.lfilter(b, a, [s], zi=zi)
y[ind]=y_tmp[-1]
plt.plot(y)
plt.plot(z)
plt.plot(xn)
plt.show()
Dodatek 2
Dla zabawy detektor napięcia mięśni mógłby generować kliknięcia myszki, aby np. zagrać w:
W tym celu trzeba doinstalować bibliotekę pynput:
pip3 install --user pynput
a potem zastosować kod ze strony: https://pynput.readthedocs.io/en/latest/keyboard.html
from pynput.keyboard import Key, Controller
keyboard = Controller()
def spacja():
# Press and release space
keyboard.press(Key.space)
keyboard.release(Key.space)
from pynput.mouse import Button, Controller
mouse = Controller()
def click():
#lewy klik myszy
mouse.press(Button.left)
time.sleep(500)
mouse.release(Button.left)
wersja dla systemu dostępnego w Labie 4.59 na Ubuntu 18
from obci_cpp_amplifiers.amplifiers import TmsiCppAmplifier
import numpy as np
amps = TmsiCppAmplifier.get_available_amplifiers('usb')
amp = TmsiCppAmplifier(amps[0])
amp.sampling_rate = 512
amp.start_sampling()
gains = np.array(amp.current_description.channel_gains)
offsets = np.array(amp.current_description.channel_offsets)
def samples_to_microvolts(samples): # z jednostek wzmacniacza do mikrowoltów
return samples * gains + offsets
while True:
# 16 próbek w pakiecie, nieodebrane próbki się bufurują i można odebrać je później
packet = amp.get_samples(16)
print(samples_to_microvolts(packet.samples))
print(packet.ts[0])
print(packet.samples.shape, amp.current_description.channel_names)
Aby wykonać go w terminalu należy uruchomić polecenie, Uwaga, aby zadziałał trzeba wyłączyć SVAROGa. Proszę przetestować czy po podłączeniu wzmacniacza i uruchomieniu tego skryptu pojawiają się w terminalu wartości próbek.
/opt/braintech/bin/python3 example.py
# -*- coding: utf-8 -*-
#!/usr/bin/env python
# Author:
# Mateusz Kruszyński <mateusz.kruszynski@gmail.com>
#
from Xlib import X, display, Xutil, XK
import Xlib
special_X_keysyms = {
' ' : "space",
'\t' : "Tab",
'\n' : "Return", # for some reason this needs to be cr, not lf
'\r' : "Return",
'\e' : "Escape",
'!' : "exclam",
'#' : "numbersign",
'%' : "percent",
'$' : "dollar",
'&' : "ampersand",
'"' : "quotedbl",
'\'' : "apostrophe",
'(' : "parenleft",
')' : "parenright",
'*' : "asterisk",
'=' : "equal",
'+' : "plus",
',' : "comma",
'-' : "minus",
'.' : "period",
'/' : "slash",
':' : "colon",
';' : "semicolon",
'<' : "less",
'>' : "greater",
'?' : "question",
'@' : "at",
'[' : "bracketleft",
']' : "bracketright",
'\\' : "backslash",
'^' : "asciicircum",
'_' : "underscore",
'`' : "grave",
'{' : "braceleft",
'|' : "bar",
'}' : "braceright",
'~' : "asciitilde"
}
display = display.Display()
window = display.screen().root
def wait(p_keys_list):
"""Block the whole keyboard!!! And wait until some key from p_keys_list
is pressed. By now p_keys_list is a list of strings, so use single
ascii symbols.
There is a way out of this hell - hit 'Escape'.
The function returns hit button`s string representation
Eg. for p_keys_list == ['1','2','3'] the function will hand untill
1,2 or 3 key is preseed or Escape is pressed."""
ds = display
window.grab_keyboard(1, X.GrabModeAsync, X.GrabModeAsync, X.CurrentTime)
while True:
ev = ds.next_event()
if ev.type == X.KeyPress:
keysym = ds.keycode_to_keysym(ev._data['detail'], 0)
keystr = XK.keysym_to_string(keysym)
print("Got keysym/keystr: "+str(keysym)+ ' / '+str(keystr))
if keystr in p_keys_list:
ds.ungrab_keyboard(X.CurrentTime)
ds.flush()
return keystr
elif str(keysym) in p_keys_list:
ds.ungrab_keyboard(X.CurrentTime)
ds.flush()
return keysym
elif keysym == 65307:
ds.ungrab_keyboard(X.CurrentTime)
ds.flush()
return 'Escape'
def char_to_keysym(ch) :
keysym = Xlib.XK.string_to_keysym(ch)
if keysym == 0 :
# Unfortunately, although this works to get the correct keysym
# i.e. keysym for '#' is returned as "numbersign"
# the subsequent display.keysym_to_keycode("numbersign") is 0.
keysym = Xlib.XK.string_to_keysym(special_X_keysyms[ch])
return keysym
def keysym_to_keycode(keysym):
keycode = display.keysym_to_keycode(keysym)
shift_mask = 0
return keycode, shift_mask
def char_to_keycode(ch) :
keysym = char_to_keysym(ch)
keycode = display.keysym_to_keycode(keysym)
if keycode == 0 :
print "Sorry, can't map", ch
if (is_shifted(ch)) :
shift_mask = Xlib.X.ShiftMask
else :
shift_mask = 0
return keycode, shift_mask
def is_shifted(ch) :
if ch.isupper() :
return True
if "~!@#$%^&*()_+{}|:\"<>?".find(ch) >= 0 :
return True
return False
def send_string(str) :
"""I am not working. I dont know why:("""
for ch in str :
#print "sending", ch, "=",
display.keysym_to_keycode(Xlib.XK.string_to_keysym(ch))
keycode, shift_mask = char_to_keycode(ch)
event = Xlib.protocol.event.KeyPress(
time = int(time.time()),
root = display.screen().root,
window = window,
same_screen = 0, child = Xlib.X.NONE,
root_x = 0, root_y = 0, event_x = 0, event_y = 0,
state = shift_mask,
detail = keycode
)
window.send_event(event, propagate = True)
event = Xlib.protocol.event.KeyRelease(
time = int(time.time()),
root = display.screen().root,
window = window,
same_screen = 0, child = Xlib.X.NONE,
root_x = 0, root_y = 0, event_x = 0, event_y = 0,
state = shift_mask,
detail = keycode
)
window.send_event(event, propagate = True)
if __name__ == "__main__":
import sys,time
print(wait(sys.argv[1:]))
#send_string("aBcd")
#time.sleep(10)
"""Używa się tak:
import keystroke
keystroke.wait(['a','b'])
i to wisi dopóki ktoś nie wciśnie a lub b.
Z innymi klawiszam jest tak, że trzeba ich numery podać, np żeby czakać
na SPACE można zrobić
keystroke.wait(['32'])
w razie czego funkcja się 'odwiesza' jak się wciśnie ESC."""