Pracownia Sygnałów Biologicznych/Zajecia 5 6: Różnice pomiędzy wersjami

Z Brain-wiki
(Nie pokazano 25 pośrednich wersji utworzonych przez tego samego użytkownika)
Linia 2: Linia 2:
  
 
==Wstęp==
 
==Wstęp==
[https://youtu.be/ousflrOzQHc Filmik ilustrujący działanie mięśni]
+
[https://www.youtube.com/watch?v=w_R5t2-C5cA Filmik ilustrujący działanie mięśni]
  
  
Linia 44: Linia 44:
  
 
== Ćwiczenia ==
 
== Ćwiczenia ==
=== Ćwiczenie: 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: detekcja aktywności wybranej jednostki ruchowej===
 
Będziemy analizować sygnał: zdrowy.bin
 
* jako wzorzec poszukiwany przyjmij fragment sygnału pomiędzy: 0.110 do 0.124 s
 
* znormalizuj ten fragment -> wzorzec
 
* przesuwaj ten wzorzec wzdłuż całego sygnału i w każdej pozycji oblicz iloczyn skalarny nakładających się fragmentów -> obejrzyj ten sygnał i porównaj go z oryginałem. Czy na jego podstawie można wykryć wystąpienia struktur podobnych do wzorca?
 
  
 
=== Ć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 =====
 
* przygotuj odpowiedni montaż, aby sygnał można było rejestrować bipolarnie,
 
* przygotuj odpowiedni montaż, aby sygnał można było rejestrować bipolarnie,
* dobierz odpowiednio filtry górnoprzepustowe,
+
* W SVAROGU wczytaj sygnał
* opisz obserwowany sygnał.
+
** Przyjrzyj się przebiegowi i widmu sygnałów
* zarejestruj około 1 min. sygnału spoczynkowego i około 1 min sygnału przy obciążeniu mięśnia (<strong> w roku 2020 analizujemy plik 'spoczynek_wysilek.raw'</strong>):
+
** dobierz odpowiednio filtry górnoprzepustowe
 +
** zaobserwuj czy i kiedy w sygnale pojawiają się artefakty
 +
 
 +
===== Rejestracja 1: napinanie i rozluźnianie ręki =====
 +
Zarejestruj sygnał, w którym naprzemiennie po około 5 s napinasz i rozluźniasz biceps.
 +
 
 +
===== Rejestracja 2: zwiększanie i zmniejszanie obciążenia ręki =====
 +
Rejestrujemy trzy fragmenty sygnału: spoczynek, narastajace obciążenie, zmniejszające się 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
 +
* w ostatniej części stopniowo zmniejszamy obciążenie co około 20 s
 +
 
 +
<!--
 +
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 oba sygnały
+
 
** oraz ich widma mocy.
+
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 różnych obciążeń. Zaobserwuj jakie zmiany następują w widmie wraz ze wzrostem obciążenia.
 +
 
 
<!--
 
<!--
 
* 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 98:
 
-->
 
-->
  
 +
=== Ć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===
 +
Będziemy analizować sygnał: zdrowy.bin
 +
* jako wzorzec poszukiwany przyjmij fragment sygnału pomiędzy: 0.110 do 0.124 s
 +
* znormalizuj ten fragment -> wzorzec
 +
* przesuwaj ten wzorzec wzdłuż całego sygnału i w każdej pozycji oblicz iloczyn skalarny nakładających się fragmentów -> obejrzyj ten sygnał i porównaj go z oryginałem. Czy na jego podstawie można wykryć wystąpienia struktur podobnych do wzorca?
 +
<!--
 
=== Ć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 93: Linia 150:
 
3. Wyznaczy początki wykonywanych przez kciuk ruchów, w oparciu o analizę amplitudy sygnału EMG.<br/>
 
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> &mdash; czas wykonania ruchu kciukiem wyznaczony przy pomocy sygnału z kanału ''trigger'', <math>t_{emg}</math> &mdash; 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> &mdash; czas wykonania ruchu kciukiem wyznaczony przy pomocy sygnału z kanału ''trigger'', <math>t_{emg}</math> &mdash; czas wykonania ruchu kciukiem wyznaczony na podstawie sygnału EMG.<br/>
 +
-->
 +
  
== Dalszy materiał wykonamy jeśli uda się wrócić do zajęć na żywo ==
+
=== Ćwiczenie V: Wykorzystanie pomiaru EMG do sterowania on-line ===
=== Ćwiczenie III: 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.
 
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 obci_cpp_amplifiers.amplifiers import TmsiCppAmplifier
+
 
 +
 
 +
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
amps = TmsiCppAmplifier.get_available_amplifiers('usb')
 
amp = TmsiCppAmplifier(amps[0])
 
  
amp.sampling_rate = 512
+
# 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)
 
def samples_to_microvolts(samples):  # z jednostek wzmacniacza do mikrowoltów
 
    return samples * gains + offsets
 
   
 
 
while True:
 
while True:
     # 16 próbek w pakiecie, nieodebrane próbki się bufurują i można odebrać je później
+
     # 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>
  
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.
+
==== 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)
  
/opt/braintech/bin/python3 example.py
+
while True:
 +
# pobieramy próbki (w mikrowoltach)
 +
    sample, timestamp = inlet.pull_chunk(timeout=1.0, max_samples=10)
 +
    print(sample, timestamp, time.monotonic())
  
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.
+
</source>
  
==== Dodatek ====
+
=== 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 ustalinymi warunkami początkowymi.  Używa się tego w następujący sposób:
 
Do działania on-line przydatne może być filtrowanie sygnału w sposób biegnący. Najlepiej zastosować do tego funkcję lfilter z ustalinymi warunkami początkowymi.  Używa się tego w następujący sposób:
 
<source lang =python>
 
<source lang =python>
Linia 152: Linia 277:
 
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(y)
+
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 bufor buf, oraz przesuwamy jego zawartość, tak aby na końcu móc dadać nową próbkę. Jest to możliwa implementacja kolejki FIFO (first in first out):
+
 
 +
 
 +
 
 +
 
 +
==== 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)
buf = np.zeros(30)
 
 
y = np.zeros(xn.shape)
 
y = np.zeros(xn.shape)
 
for  ind, s  in enumerate(xn):
 
for  ind, s  in enumerate(xn):
  buf[:-1:] = buf[1:]
+
   y_tmp, zi = ss.lfilter(b, a, [s], zi=zi)
  buf[-1] = s
 
   y_tmp, zi = ss.lfilter(b, a, buf, zi=zi)
 
 
   y[ind]=y_tmp[-1]
 
   y[ind]=y_tmp[-1]
  
Linia 176: Linia 304:
  
 
</source>
 
</source>
 +
 +
 +
  
  
Linia 181: Linia 312:
 
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://www.gamesloon.com/free-sports-12/baseball-games-16/winnie-the-pooh-home-run-derby-54790.html
+
https://dino-chrome.com/
  
  
Linia 187: Linia 318:
 
   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 195: Linia 338:
  
 
def click():
 
def click():
 +
#lewy klik myszy
 
mouse.press(Button.left)
 
mouse.press(Button.left)
 
time.sleep(500)
 
time.sleep(500)
Linia 200: Linia 344:
  
 
</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:

Wersja z 09:29, 29 kwi 2022

Pomiar EMG

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.

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. 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
  • przygotuj odpowiedni montaż, aby sygnał można było rejestrować bipolarnie,
  • W SVAROGU wczytaj sygnał
    • Przyjrzyj się przebiegowi i widmu sygnałów
    • dobierz odpowiednio filtry górnoprzepustowe
    • zaobserwuj czy i kiedy w sygnale pojawiają się artefakty
Rejestracja 1: napinanie i rozluźnianie ręki

Zarejestruj sygnał, w którym naprzemiennie po około 5 s napinasz i rozluźniasz biceps.

Rejestracja 2: zwiększanie i zmniejszanie obciążenia ręki

Rejestrujemy trzy fragmenty sygnału: spoczynek, narastajace obciążenie, zmniejszające się 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
  • w ostatniej części stopniowo zmniejszamy obciążenie co około 20 s


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 różnych obciążeń. Zaobserwuj jakie zmiany następują w widmie wraz ze wzrostem obciążenia.


Ć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

  • 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 IV: detekcja aktywności wybranej jednostki ruchowej

Będziemy analizować sygnał: zdrowy.bin

  • jako wzorzec poszukiwany przyjmij fragment sygnału pomiędzy: 0.110 do 0.124 s
  • znormalizuj ten fragment -> wzorzec
  • przesuwaj ten wzorzec wzdłuż całego sygnału i w każdej pozycji oblicz iloczyn skalarny nakładających się fragmentów -> obejrzyj ten sygnał i porównaj go z oryginałem. Czy na jego podstawie można wykryć wystąpienia struktur podobnych do wzorca?


Ć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 ustalinymi 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:

https://dino-chrome.com/


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