Pracownia Sygnałów Biologicznych/Zajecia 7

Z Brain-wiki

Pomiar Elektrookulogramu

Wstęp

Kolejnym narządem, w którym znajdują się generatory czynności bioelektrycznej jest oko. W narządzie tym zachodzą skomplikowane procesy biochemiczne i elektryczne, umożliwiające widzenie. Procesy te zostaną omówione na oddzielnym wykładzie, w tym miejscu zaś wymienimy tylko te zjawiska, które mają wpływ na powstawanie w oku czynności elektrycznej.

  1. Jednym z najważniejszych części oka jest siatkówka, której zadaniem jest odbieranie bodźców świetlnych oraz zamiana ich na sygnały elektryczne, przekazywane dalej do kory wzrokowej. W siatkówce płynie nieustannie prąd, którego natężenie zmienia się wraz z intensywnością padającego na nią światła. Związane z tym sygnały bioelektryczne można rejestrować za pomocą elektrod umieszczonych na powierzchni oka lub nawet za pomocą elektrod umieszczonych na powierzchni skóry wokół oka. Tak zarejestrowany sygnał elektryczny, powstały w oku w trakcie widzenia, nazywamy Elektroretinogramem (ERG). Sygnał ten ma amplitudę od kilku nanowoltów do kilku μV i jest wykorzystywany w diagnostyce wielu chorób siatkówki.
  2. Rogówka (zewnętrzna warstwa oka znajdująca się w jego przedniej części) jest naładowana dodatnio względem siatkówki umiejscowionej po przeciwnej stronie oka. Rogówka wraz z siatkówką tworzą zatem w przybliżeniu układ dipola elektrycznego. W momencie ruchu okiem, dipol ten zmienia orientację w przestrzeni, zaburzając rozkład natężenia pola elektrycznego. Związany z tym sygnał o amplitudzie kilku miliwoltów można zmierzyć za pomocą elektrod umieszczonych na skórze wokół oka. Widoczny jest ona także na elektrodach umieszczonych na powierzchni głowy w trakcie pomiary czynności elektrycznej mózgu. Sygnał ten, czyli elektryczny zapis ruchu gałek ocznych, nazywamy Elektrookulogramem (EOG). Czynność elektryczną związaną z ruchem gałek ocznych obserwuje się również w trakcie mrugania, kiedy to gałki oczne skręcają nieco ku górze (tzw. zjawiska Bella), a także w trakcie badań diagnostycznych zaburzeń snu (w różnych etapach snu występują wolne lub szybkie ruchy gałek ocznych).

Sygnał EOG można też wykorzystać do konstrukcji interfejsów [[1]].

  1. Ruch oka sterowany jest za pomocą mięśni. W trakcie rejestracji Elektrookulogramu widoczne będą również wyładowania elektryczne związane z działaniem mięśni.

Ćwiczenie I

Wykonaj pomiar Elektrokulogramu za pomocą dwóch par elektrod połączonych w montażu dwubiegunowym.

  • Jedną parę elektrod umieść poniżej i powyżej oka, drugą w pobliżu lewej i prawej skroni.
  • Skonfiguruj program do rejestracji i przeglądania mierzonego sygnału w czasie rzeczywistym.
  • Wykonaj ruch oczami w górę i opisz zarejestrowany sygnał.
  • Wykonaj ruch oczami w dół i opisz zarejestrowany sygnał.
  • Wykonaj mrugnięcie i opisz zarejestrowany sygnał.
  • Przeczytaj fragment tekstu, zaobserwuj efekty związane z ruchem sakadowym oka.

Zarejestruj sygnały w wyżej wymienionych sytuacjach. Wyświetl te sygnały w programie napisanym samodzielnie, bez stosowania filtra grórnoprzepustowego.

Ćwiczenie II

Powtórz ćwiczenie I, łącząc elektrody z monopolarnymi wejściami wzmacniacza, elektrodę odniesienia umieść na lewym płatku uszu lub wyrostku sutkowatym.


Ćwiczenie III

Napisz program wyświetlający na macierzy 5x5 następujące sekwencje:

  • kwadrat środkowy - kwadrat górny - kwadrat środkowy
  • kwadrat środkowy - kwadrat prawy - kwadrat środkowy
  • kwadrat środkowy - kwadrat dolny - kwadrat środkowy
  • kwadrat środkowy - kwadrat lewy - kwadrat środkowy

Zapisuj do pliku tekstowego czasy i kody poszczególnych sekwencji.

Wyświetl te sygnały w programie napisanym samodzielnie, bez stosowania filtra grórnoprzepustowego.

Zaprojektuj detektor, wykrywający, która sekwencja została wykonana.


Ćwiczenie IV

Zrealizuj prosty system do rejestracji ruchu gałki ocznej, tzw. Eye tracker. w tym celu:

Napisz moduł:

  • Korzystając detektora sekwencji napisanego w ramach ćwiczenia III napisz moduł wykrywający sekwencje w czasie rzeczywistym. Niech Twój moduł wypisuje wynik detekcji na konsolę. Opis korzystania i pisania modułów do systemu OBCI znajduje się [tutaj]. W dużym skrócie to co należy zrobić to:
  • W katalogu domowym utwórz podkatalog:
~/obci/scenarios
  • w katalogu tym umieść plik:
eog.ini

powinien on zawierać następującą treść:

[peers]
scenario_dir=
;***********************************************
[peers.mx]
path=multiplexer-install/bin/mxcontrol

;***********************************************
[peers.config_server]
path=obci_control/peer/config_server.py

;***********************************************
;***********************************************
[peers.amplifier]
path=drivers/eeg/c_tmsi_amplifier/amplifier_tmsi.py
;ponizsza sciezka pokazuje na plik zaierajacy nasze ustawienia parametrow wzmacniacza
config= ~/obci/scenarios/eog_local_params.ini
[peers.analysis]
path=~/obci/analysis/eog_realtime.py
  • W powyższym pliku zadeklarowaliśmy, że lokalne parametry dla wzmacniacza znajduję się w pliku:
~/obci/scenarios/eog_local_params.ini

zatem musimy ten plik wytworzyć i wypełnić go np. taką treścią:

[local_params]
channel_names=gora;dol;lewa;prawa
active_channels=0;1;2;3
sampling_rate=256
  • W pliku ze scenariuszem zadeklarowaliśmy też, że nasz moduł analizy danych znajduje się w pliku:
~/obci/analysis/eog_realtime.py

tak więc musimy ten plik stworzyć i tam właśnie wpisać algorytm detekcji. Dla zachowania konwencji w tym samym katalogu powinien znajdować się plik na ewentualne parametry dla modułu eog_realtime.py. Musi on się nazywać tak samo, tyle, że ma rozszerzenie .ini. Musimy więc wytworzyć pusty plik:

~/obci/analysis/eog_realtime.ini

Ponieważ nasz algorytm musi on się komunikować z resztą systemu obci trzeba go opakować w poniższy kod-szkielet:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
import random, time, numpy
 
from multiplexer.multiplexer_constants import peers, types
from obci_control.peer.configured_multiplexer_server import ConfiguredMultiplexerServer
from configs import settings, variables_pb2
 
from interfaces import interfaces_logging as logger
#LOGGER = logger.get_logger("sample_analysis", "info")
LOGGER = logger.get_logger("sample_analysis", "debug")
 
class SampleAnalysis(ConfiguredMultiplexerServer):
    """A class responsible for handling signal message and making proper decision.
    The class inherits from generic class for convinience - all technical stuff
    is being done in this super-class"""
    def __init__(self, addresses):
        """Initialization - super() and ready() calls are required..."""
        super(SampleAnalysis, self).__init__(addresses=addresses,
                                          type=peers.ANALYSIS)
        self.ready()
        LOGGER.info("Sample analysis init finished!")
 
    def handle_message(self, mxmsg):
        """The only required function in the class
        that will be fired every time message is received"""
        if mxmsg.type == types.AMPLIFIER_SIGNAL_MESSAGE:
            # Got proper message, let`s unpack it ...
 
            # Messages are transmitted in bunches so lets define SampleVector
            # in order to unpack bunch of Sample messages ...
	    l_vect = variables_pb2.SampleVector()
            l_vect.ParseFromString(mxmsg.message)
 
            # Now we have message unpacked, lets iterate over every sample ...
            for s in l_vect.samples:
 
                # Every sample has two fields:
                # timestamp - system clock time of a moment of Sample`s creation
                # channels - a list of values - one for every channel
                LOGGER.debug("Got sample with timestamp: "+str(s.timestamp))
 
                # One can copy samples to numpy array ...

                a = numpy.array(s.channels) # w tym miejscu mamy w tablicy a "paczke" próbek (domyślnie 4próbki ) ze wszystkich zadeklarowanych kanalow 
#################### TU TRZEBA WPISAC SWOJ KOD BUFOROWANIA i ANALIZY  ##############
                print a #na dobry poczatek wypiszmy probki

####################################################################
 
                # Or just iterate over values ...
                for ch in s.channels:
                    LOGGER.debug(ch)
 
            # Having a new bunch of values one can fire some magic analysis and 
            # generate decision ....
 
            # Below we have quite simple decision-maker - it generates a random
            # decision every ~100 samples-bunch
########## TU NA PODSTAWIE ANALLIZY PODEJMUJEMY DECYZJE I MOZEMY JA PRZEKAZAC DO RESZTY SYSTEMU OBCI ######################
########## W TYM PRZYKLADZIE JEST TO LOSOWA DECYZJA ##############
########## W TYM CWICZENIU WYSTARCZY JESLI WYPISZECIE DECYZJE NA EKRAN ###########
            if random.random() > 0.99:
                # Here we send DECISION message somewhere-to-the-system ...
                # It's up to scenario's configuration how the decision will be used ...
                # Eg. it might be used by LOGIC module to push some button in speller.
                self.conn.send_message(message = str(random.randint(0,7)), 
                                       type = types.DECISION_MESSAGE, 
                                       flush=True)
        else:
            LOGGER.warning("Got unrecognised message type: "+str(mxmsg.type))
 
        # Tell the system 'I`ll not respond to this message, I`m just receiving'
        self.no_response()
 
if __name__ == "__main__":
    # Initialize and run an object in order to have your analysis up and running
    SampleAnalysis(settings.MULTIPLEXER_ADDRESSES).loop()
  • Aby uruchomić nasz scenariusz trzeba wywołać:
obci launch ~/obci/scenarios/eog.ini
  • Zatrzymanie scenariusza robimy tak:
obci kill eog

W tym momencie komunikaty ze wszystkich modułów wypisywane są na jednej konsoli. Przydatne jest korzystanie z obiektu 'LOGGER' zamiast funkcji 'print' - w konsoli mamy informację o źródle komunikatu i jego czasie.

OpenBCI działa tak, że jeśli w jednym module pojawi się błąd, to wszystkie inne moduły są zamykane, stąd komunikat podobny do poniższego sugeruje, że w którymś module wystąpił błąd. W takim wypadku należy przejrzeć konsolę i wyszukać komunikat błędu. Niezbędne jest ustawienie bufora konsoli na 'nieograniczony' wykonując: Edycja->Preferencje profilu->Przewijanie->Nieograniczone .

Przetestuj moduł

  • Umieść elektrody do rejestracji ruchu gałki ocznej jak w ćwiczeniu I i przetestuj działanie modułu. Miłej zabawy :-)