반응형

이번엔 이전 글에서 집중도를 구했던 방법으로 실제 게임을 하며 집중도를 구해볼 것이다!

 

참고로 이번엔 코드는 제공 안할 것이다.

그렇기에 해보고 싶다면 이전에 한 집중도 코드와 결합해서 해보면 될 것 같다.

 

시작

Brainflow에서는 Github를 돌아다니다 보면 예시로 실행할 수 있는 파일들을 찾을 수 있다.

그 중에서는 실시간 시각화 파일들도 있는데.. 바로 아래꺼다!

https://github.com/brainflow-dev/brainflow/blob/master/python_package/examples/plot_real_time/plot_real_time.py

 

brainflow/python_package/examples/plot_real_time/plot_real_time.py at master · brainflow-dev/brainflow

BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of data from biosensors - brainflow-dev/brainflow

github.com

 

https://github.com/brainflow-dev/brainflow/blob/master/python_package/examples/plot_real_time/plot_real_time_min.py

 

brainflow/python_package/examples/plot_real_time/plot_real_time_min.py at master · brainflow-dev/brainflow

BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of data from biosensors - brainflow-dev/brainflow

github.com

 

위에 링크에 들어가면 나오는 파일을 실행한 결과가 아래 사진이다.

뇌파가 많아보이는건 보드가 SYNTHETIC_BOARD 이걸로 설정되어서 인데.. 그냥 데모 보드라고 생각하면 될 것 같다.

그래서 저걸 실행하면 EEG 기기를 연결하지 않아도 예시로 저렇게 볼 수 있는데, 물론 연결하면 보드에 있는 정보들로 표시가 된다.

 

아무튼 이번에는 저 그래프에 추가적으로 집중도 그래프를 추가하고 게임을 하며 측정해볼 것이다!

 

코드 문제점

그런데 실행해보기 전 한가지 문제가 있긴 하다.

저 코드는 PyQT가 옛날 버전이라서 최신 버전에서는 실행이 안된다. 그래서 최신 버전으로 바꿔야 하는데..

그냥 

  • QtGui > QtWidgets
  • GraphicsWindow > GraphicsLayoutWidget
  • pg.GraphicsLayoutWidget(title='BrainFlow Plot', size=(800, 600), show=True)

현재 코드에다가 > 되어있는건 그렇게 바꾸면 되고 GraphicsLayoutWidget 함수 뒤에 show=True만 추가하면 제대로 작동한다.

현재는 내가 따로 코드를 수정해서 커밋 요청을 해놓았다. 문제가 없다면 아마 수정되어서 업데이트가 될 것이다.

https://github.com/brainflow-dev/brainflow/pull/756

 

Fix PyQT Error by cheongpark · Pull Request #756 · brainflow-dev/brainflow

Fixed PyQt and PyQtGraph compatibility issues: QtGui.QApplication -> QtWidgets.QApplication Error: AttributeError: module 'pyqtgraph.Qt.QtGui' has no attribute 'QApplication' F...

github.com

 

암튼 그렇다. (글을 쓰던 중 업데이트가 되었다. 그러니 직접 가서 다운받아서 수정해서 쓰면 될 것 같다.

 

본론

일단 실행하면 어떻게 뜨는지 본다면.. 이런식으로 뜬다!

https://github.com/brainflow-dev/brainflow/blob/master/python_package/examples/plot_real_time/plot_real_time.py

 

brainflow/python_package/examples/plot_real_time/plot_real_time.py at master · brainflow-dev/brainflow

BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of data from biosensors - brainflow-dev/brainflow

github.com

그냥 이거를 수정한 파일이다. 이 코드로 선정한건 그냥 정보가 많고 예뻐보여서..

수정한건 드라큘라 테마로 바꾼 것, 그래프 2개 추가한 것 정도가 끝이다.

 

그리고 참고로 이 코드에서는 Muse 2 기기를 연결하는 방법은 2가지 정도 있다.

연결할 때는 실행할 때 인수 값을 추가해서 연결할 수도 있고.. 나 같은 경우에는 코드에 이전 글에 있는 것 처럼 MUSE 2와 시리얼 코드를 넣어서 연결되도록 했다. 저 코드의 메인 함수로 가보면 알 수 있을 것이다.

 

그리고 저 아래에 Attention과 Mindfulness는 이전 글에서 설명한 대로의 코드로 계산을 하고 그래프에 추가되도록 했다.

조금만 해보면 간단해서 딱히 이곳에 코드를 넣지는 않았다.

(참고로 저거 Attention의 범위는 딱히 지정하지는 못했다. 지금 와서 생각해보면 2~3이 적당했을 텐데.. 암튼 영상은 미리 찍었기 때문에.. 4로 되어있다는 점..)

 

설치해야할 라이브러리는 아래다! (PyQT6도 실행 가능)

pip install pyqtgraph PyQt5 brainflow

 

암튼 저걸 실행한 후 머리에 착용하고 게임을 해봤다!

 

게임 영상

DJMAX RESPECT V

https://youtu.be/5GIg4USKRSc

https://youtu.be/MI4KGSuK3YY

 

Beat Saber

https://youtu.be/l68sUCtNqFc

https://youtu.be/GU2_KSvK4Ps

https://youtu.be/Z0MRK22TLKE

https://youtu.be/WJNhG9nXO-g

 

결론

일단 DJMAX와 Beat Saber를 선택한 이유는 리듬게임이며 집중도가 높은 게임이기 때문에 선택했다. BeatSaber의 경우 VR를 착용하고 하기 때문에 몰입이 될 수 있어서 집중도를 측정하기에 더 좋았다.

참고로 VR의 경우 VIVE PRO 1을 착용하고 했었다.

 

일단 영상 찍고 확인을 해보니.. 의외로 집중도가 낮게 나온다..

내가 이전 부터 이 기기를 테스트 하면서도 집중도가 확실히 낮게 나오기는 했었는데.. (ADHD인가..?)

 

DJMAX에서는 짧은 노트의 경우 집중도가 의외로 낮게 나온다. 그에 반면 긴 노트에서는 집중도가 높게 나왔다..

나도 게임을 클리어 하면서 짧은 노트들은 어느정도 칠 수 있어서 그나마 쉽게 넘어가는 반면 긴 노트의 경우 긴 노트를 눌르며 다른 노트들도 쳐야하다 보니 집중을 많이 하게 되는 것 같은데.. 심지어 지금도 그렇고 이때도 그렇고 장갑을 끼고 있다보니.. 더 키보드 치는 느낌이 잘 안들어서 저렇게 된 것 같다.

뭐 그래도 대부분 높게 나오기는 했다.

(장갑 끼는건 그냥 키보드 더러워질까봐.. 다한증..)


Beat Saber에서는 이상하게 전체적으로 낮게 나왔다..

이건 잘 모르겠다.. 내가 게임을 잘 못하는게 이것 때문인건가.. 하는 생각도 들긴 하는데..

일단 의심되는건.. 내 방이 덥다. 그래서 땀이 많이 나기도 하는데 그것 때문에 전극에 수분 때문에 전기가 잘 통해서 전극이 잘 요동치고 있다는 것을 알 수 있다. 이것 때문일 가능성도 있고.. 또 손을 엄청 움직이고 하기에 그 움직임도 같이 반영됬을 것으로 추정된다.. 또한 VR과 Muse 2를 같이 착용하고 측정하기 때문에 Muse 2가 제대로 측정이 안되었거나 VIVE PRO의 안면 폼이 방해했을 가능성이 있을 수 있다. 그리고 운동을 안해서인지.. FitBeat 하면서 다리를 굽혔다 피니깐.. 이후에 몇시간 동안 일어나거나 앉을려 하면 힘이 빠져서 움직이기도 어려울 정도였다.

그런데 그걸 제외해서라도 집중도가 낮은건 좀 이상하긴 하다.. 아니면 그냥 내가 게임에 집중을 잘 안했던 것일 수도 있고..

 

그리고 위에서도 말했지만 공통적으로 한가지 문제라면 Muse 2가 민감한건지 뭔지 자세를 움직인다거나 키보드를 친다거나 손을 많이 움직인다거나 이를 쎄게 닫는 경우 뇌파가 심하게 요동친다.. 이것 때문에 뇌파가 제대로 측정이 안되기도 한다. 그래도 모든 전극이 같이 반영되기 때문에 아마도 그에 따라서 바뀌니깐 딱히 상관은 없을 수도 있을 것 같다.

 

아무튼 재밌긴 했다~

반응형
반응형

이번껀 전문가가 아니기에 틀린 정보가 많을 수 있습니다.

이번에도 집중도 구하기다!

다만 이번엔 인공지능을 곁들인..

 

내가 이전 부터 인공지능이라고 말하기는 했지만.. 이번껀 머신러닝에 가깝다고 볼 수 있다.

보통 인공지능 하면 딥러닝을 생각하겠지만.. 여기서는 머신러닝이다.

뭐 인공지능 안에 머신러닝이 포함되어있으니 상관은 없겠지만..

 

이번에 적을 내용은 이전 글 보다는? 적을테고 쉬울 것이다.

대부분이 이미 만들어진 것을 쓰기 때문에..

하지만 자세하게 들어가면 역시 어렵긴 하지만.. 최대한 내가 이해한 배경으로 설명해보겠다..!

틀린 정보가 있을 수도 있다.. 작년인 대학교 1학년 때 했던 작업들이고.. 2~4개월 밖에 안했었기 때문에..

 

그리고.. 28일이 아닌 25 ~ 26일에 기기를 반납해야해서 서둘러야 한다..!!!

 

암튼 먼저 코드부터!

 

Brainflow ML로 집중도 구하는 코드 공유!

from time import sleep
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
from brainflow.data_filter import DataFilter
from brainflow.ml_model import MLModel, BrainFlowModelParams, BrainFlowMetrics, BrainFlowClassifiers

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

use_data_seconds = 5 # n초 동안 데이터 수집 
sampling_rate = BoardShim.get_sampling_rate(board_id)
eeg_channels = BoardShim.get_eeg_channels(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안의 데이터를 가져올지

# 머신러닝 모델 준비
mindfulness_params = BrainFlowModelParams(BrainFlowMetrics.MINDFULNESS, BrainFlowClassifiers.DEFAULT_CLASSIFIER)
mindfulness = MLModel(mindfulness_params)
mindfulness.prepare()

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# n초 + 1초 동안 데이터 수집
sleep(use_data_seconds + 1)

# 수집한 데이터를 변수에 저장
data = board_shim.get_current_board_data(num_points)

# 예측에 필요한 데이터 추출
bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, True)
feature_vector = bands[0]

# 집중도 예측
print(f"Mindfulness: {mindfulness.predict(feature_vector)[0]}")

# 모델 해제
mindfulness.release()

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

이번엔 아마도 저번보다는 쉽지 않을까 한다.. 코드적인걸로는..

이전 글에 대부분의 내용들을 넣어놓은거라서..

지금 새벽 4시 40분인데 다 쓰고 나면 짧겠지..? 라 생각 중..

 

참고로 이전 글에서 말했던 것 처럼 Mindfulness를 썼던게 Brainflow에서 Mindfulness로 썼어서 그걸로 쓴거다..

암튼 그렇다..

 

코드 설명 및 이해하기

설명할 수록 점점 코드 설명할게 적어져서 좋긴 하다.

전 글에서 한가지 안쓴게 있다면 필요한 라이브러리를 불러오는 것에 대해 안적었는데.. 뭐 간단한거니 여기서도 안적겠다!

여기서 적는건 이전에 설명하지 않았던 코드들만

 

mindfulness_params = BrainFlowModelParams(BrainFlowMetrics.MINDFULNESS, BrainFlowClassifiers.DEFAULT_CLASSIFIER)
mindfulness = MLModel(mindfulness_params)
mindfulness.prepare()

여기는 모델을 정의하는 부분이다. 어떤 모델을 쓸지 어떤 분류기를 쓸지 정할 수 있다.

코드에서는 모델과 분류기를 정하고 정한 파라미터로 모델을 할당해서 모델을 쓸 수 있도록 활성화 시키는 코드이다.

 

일단 Brainflow에선 기본적으로 어떤 모델과 분류기가 가능한지 아는 것이 좋다.

그러기 위해선 BrainFlowMetrics, BrainFlowClassifiers 정의 코드를 살펴봐야 한다.. 코드를 보면

# 코드 설명을 하기 위한 코드
class BrainFlowMetrics(enum.IntEnum):
    """Enum to store all supported metrics"""

    MINDFULNESS = 0  #:
    RESTFULNESS = 1  #:
    USER_DEFINED = 2  #:


class BrainFlowClassifiers(enum.IntEnum):
    """Enum to store all supported classifiers"""

    DEFAULT_CLASSIFIER = 0  #:
    DYN_LIB_CLASSIFIER = 1  #:
    ONNX_CLASSIFIER = 2  #:

이런식으로 정의가 되어있다. 

참고로 BrainFlowMetrics는 모델이 어떤 역할을 할지 선택하는 것이고, BrainFlowClassifiers는 그 역할을 계산할 수 있는 분류기를 선택할 수 있도록 하는 것이다.

 

각각에 대해서 설명하자면

BrainFlowMetrics 

  • MINDFULNESS : 마음챙김, 집중도와 비슷할 순 있지만 좀 다른 것, 현재 자연스럽게 어느정도 집중 했는지
  • RESTFULNESS : 휴식상태, 어느정도 휴식 상태인지
  • USER_DEFINED : 사용자가 직접 정의

BrainFlowClassifiers

  • DEFAULT_CLASSIFIER : 기본 분류기 (MINDFULNESS, RESTFULNESS 사용할 때)
  • DYN_LIB_CLASSIFIER : .dll, .so 같은 C/C++로 만든 알고리즘을 적용하고 싶을 때
  • ONNX_CLASSIFIER : ONNX 모델을 사용할 때 (USER_DEFINED 사용할 때)

여기에서 예시로 들 것은 Mindfulness이다. 설명을 적어놓기는 했지만 사실 나도 잘은 이해 못했다. 솔직히 저 설명으론 부족해서 추가로 말하자면, 내가 어느 순간에 공부나 무언가를 뚫어져라 보면서 갑자기 집중하는 것이 아닌.. 자연스럽게 무언가의 변화 등으로 자연스럽게 그것에 관심을 기울이는 것을 말하는 것 같다.

예를 들면 이전에서 한 Attention은 한 곳을 뚫어져라 바라볼 때 지표가 올라가고

Mindfulness는 밥 먹다가 수저가 바닥으로 떨어지며 그 소리에 기울인다 생각하면 된다.

수업 자료를 만들 때는 Mindfulness로 집중도를 구해서 게임할 수 있도록 했었는데.. 이젠 수정해야할 것 같다.. 하하하..

만약 이전 글에서 말한 수식으로 모델을 만든다면 집중하거나 안하는 데이터를 모두 수집한 후 수식으로 계산시키고 정규화 시킨 후에 학습시켜서 사용자 정의 모델로 등록하면 될 것 같긴 하다. (Brainflow에 등록 요청해볼까 생각중..)

 

기본 분류기는 아마도 Mindfulness나 Restfulness를 계산하는데만 쓰는 것 같다. 이에 대한건 predict 부분에서 더 자세하게 얘기할 것이다.

ONNX 분류기는 User_Defined로 선택했을 때 쓸 수 있는 것 같은데, 직접 Python으로 모델을 학습해서 만든 모델을 적용할 수 있도록 해주는 것이다. 이 분류기를 제작하는건 내가 반납 전에 쓸 분량을 다 쓰고 시간이 남는다면 써보고.. 안되면 아래에 링크한 튜토리얼을 보고 하면 될 것 같다.

 

추가로 여기서 한가지 생각이 들 것이다. 모델을 직접 정의할 수 있으면 BrainFlowMetrics는 왜 있는걸까? 라는 생각을 할 수 있다. 나도 제대로는 모르겠지만.. 내가 생각하기에는 Mindfulness랑 Restfulness를 쓸 수 있도록 하기 위해서 인 것 같다. 오픈소스다 보니 코드를 직접 수정할 수도 있는데 거기에서 추가적인 모델을 넣을 수 있도록 하거나 나중에 추가하기 위해서 저렇게 구분한 것 같다고 생각이 든다.

 

아무튼 여기에서는 Mindfulness를 계산하고 이 때문에 Default Classifier를 사용하게 된다.

 

# 예측에 필요한 데이터 추출
bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, True)
feature_vector = bands[0]

# 집중도 예측
print(f"Mindfulness: {mindfulness.predict(feature_vector)[0]}")

위에 2줄은 이전에 설명했기 때문에 패스하고

 

여기에서 볼 것은 저 아래 출력문이다.. 이전에 할당한 Mindfulness 모델에 수집한 데이터의 평균 파워를 넣어 예측하게 하는 코드이다.

즉 이전 글에서 설명한 평균의 Delta, Theta, Alpha, Beta, Gamma 값을 넣어 예측시키는 코드라고 보면 된다.

 

그런데 또 의문이 들것이다. 왜 예측한 것의 0번 인덱스 값을 가져오라고 하는지.. 일단 확인해보니 0번 인덱스 외의 값은 없다. 그런데 리스트로 반환하는 이유가 뭔지 생각해보니.. 아마도 다른 사용자 정의 분류기를 사용하게 되면 여러 값을 리턴해야하는 경우가 있을테니 그것 때문인 듯 하다.

아무튼 리스트로 반환되기 때문에 [값] 이런식으로 출력되지 않게 하기 위해 0 인덱스로 접근해서 데이터를 가져와 출력하게 되어있다.

 

참고로 찾으면서 방금 발견한게 있는데.. 수업 자료를 만들 때는 저거를 1초나 2초로 했었다..

그런데 갑자기 최소 몇초를 해야하는 생각이 들어서 더 찾아봤는데.. 당시에는 못 찾은 주석이 있었다..

# recommended window size for eeg metric calculation is at least 4 seconds, bigger is better

음.. EEG로 어떤 것을 계산해야한다면 4초 이상의 데이터는 필요하다고.. 한다.. 클 수록 좋다고 하고.. 흐음.. 

수업 자료를 고쳐서 다시 교수님께 드려야 할 것 같다는 생각이 든다.. 하하;;

 

그럼 Mindfulness의 계산 방법에 대해 궁금한점이 생길 것이다.

위에서 말했듯이 머신러닝이다. 하지만 더 자세히 보기 위해 코드로 보겠다!

# 코드 설명을 하기 위한 코드
# src/ml/generated/mindfulness_model.cpp

const double mindfulness_coefficients[5] = {-1.4060899708538128,2.597693987367105,-30.96470526503066,12.04593986553724,45.773017975354556};
double mindfulness_intercept = 0.000000;
# 코드 설명을 하기 위한 코드
# src/ml/mindfulness_classifier.cpp

int MindfulnessClassifier::predict (double *data, int data_len, double *output, int *output_len)
{
    if ((data_len < 5) || (data == NULL) || (output == NULL))
    {
        safe_logger (spdlog::level::err,
            "Incorrect arguments. Null pointers or invalid feature vector size.");
        return (int)BrainFlowExitCodes::INVALID_ARGUMENTS_ERROR;
    }
    double value = 0.0;
    for (int i = 0; i < std::min (data_len, 5); i++)
    {
        value += mindfulness_coefficients[i] * data[i];
    }
    double mindfulness = 1.0 / (1.0 + exp (-1.0 * (mindfulness_intercept + value)));
    *output = mindfulness;
    *output_len = 1;
    return (int)BrainFlowExitCodes::STATUS_OK;
}

이게 Mindfulness를 계산하는 코드이다. Python이 아닌 이유는 Brainflow는 C++로 만든걸 Python에 이식한 것이기 때문이다.. 저건 메인 C++에 있는 코드..

 

하나씩 보자면 일단 mindfulness_coefficients, mindfulness_intercept 이거는 인공지능을 어느정도 했었다면 알 수 있다.

가중치와 편향 값이다. mindfulness_coefficients가 가중치 값, mindfulness_intercept 편향 값..

5개인 이유는 입력 값인 Delta, Theta, Alpha, Beta, Gamma를 입력 값으로 계산하기 때문..

 

그러면 예측 코드를 하나씩 봐보자!

아래에서 설명할껀 퍼셉트론 대로 계산한다고 생각하면 된다.

퍼셉트론은 입력 값과 가중치 값을 각각 곱하고 만나는 노드에서 모두 더해 편향 값을 더한 후 활성 함수를 거치는거라 생각하면 된다. (말로는 이해하기 어렵고 모르면 퍼셉트론에 대해서 공부하고 오면 될 것 같다.)

# 코드 설명을 하기 위한 코드
double value = 0.0;
for (int i = 0; i < std::min (data_len, 5); i++)
{
    value += mindfulness_coefficients[i] * data[i];
}

 

 

그 중 위 코드는 입력값과 가중치를 각각 곱한 후 모두 더하는 코드라 보면 된다.

# 코드 설명을 하기 위한 코드
double mindfulness = 1.0 / (1.0 + exp (-1.0 * (mindfulness_intercept + value)));

이건 위에서 곱하고 더한 값인 value에 편향 값을 더하고 활성화 함수를 거치는 것이다. 

그리고 활성화 함수는 시그모이드 함수를 사용하는데 f(x) = 1 / (1 + e^(-x)) 이런 수식이다. 솔직히 몰라도 된다.

 

그리고 실제 계산 과정으로 예를 보여주겠다!

먼저 계산하기 위해서 입력 값을 정의하면..

  • Delta : 0.53311590
  • Theta : 0.30351431
  • Alpha : 0.07848158
  • Beta : 0.06465923
  • Gamma : 0.02022897

이렇다. 이건 실제로 내가 방금 착용해서 얻은 값이다. 참고로 미리 내부에서 계산된 것은 0.33481021 이 값이다.

 

아무튼 계산해보면

일단 처음에 입력 값과 가중치를 곱한다.

  • Value0 : Delta(0.53311590) * Weight0(-1.4060899708538128) = -0.7496089202927042
  • Value1 : Theta(0.30351431) * Weight1(2.597693987367105) = 0.7884372981668756
  • Value2 : Alpha(0.07848158) * Weight2(-30.96470526503066) = -2.430158993433925
  • Value3 : Beta(0.06465923) * Weight3(12.04593986553724) = 0.7788811963319415
  • Value4 : Gamma(0.02022897) * Weight4(45.773017975354556) = 0.925941007432908

그 후에는 곱해진 모두를 더한다.

  • Value : Value0(-0.7496089202927042) + Value1(0.7884372981668756) + Value2(-2.430158993433925) + Value3(0.7788811963319415) + Value4(0.925941007432908) = -0.6865084117949039

그 후 편향 값을 더한다.

  • Value : Value(-0.6865084117949039) + Bias(0.000000) = -0.6865084117949039

참고로 편향 값이 0이기에 값의 변화는 없다.

그 후 활성화 함수를 거친다.

  • Result : 1.0 / (1.0 + math.exp(-1.0 * Value)) = 0.3348102440015052

이런 과정으로 계산되게 된다. 결론적으로 내부적으로 계산된 값인 0.33481021과 비교하면 반올림되서 잘린거 빼고는 결과값의 차이는 없다. 그렇다는건 위 계산 과정이 맞다는 것이 되니 저걸 보면 된다!

 

아무튼 계산 과정이 이렇다. 어떻게 보면 간단할 수도 있고 복잡할 수도 있다.

참고로 DNN(깊은 신경망)은 저런 계산하는 것이 겁나게 많은 것 뿐이다.

 

당시 수업 자료 만들 때의 PPT에 내가 만들어둔 사진이다. 아래 사진을 보면 이해가 더 쉬울지도 몰라서 올려놓겠다.

지금 봤는데.. 가중치 값이 업데이트 되었나보다.. 사진에 있는건 대략 5개월 전의 가중치 값..

 

참고로 재미있는점이 한가지 있는데.. Restfulness를 계산하는 과정이다..

예전에도 찾았었던 것 같은데.. 지금봐도 어이가 없다.

# 코드 설명을 하기 위한 코드
# src/ml/inc/restfulness_classifier.h

#pragma once

#include "brainflow_constants.h"
#include "mindfulness_classifier.h"


class RestfulnessClassifier : public MindfulnessClassifier
{
public:
    RestfulnessClassifier (struct BrainFlowModelParams params) : MindfulnessClassifier (params)
    {
    }

    int predict (double *data, int data_len, double *output, int *output_len)
    {
        int res = MindfulnessClassifier::predict (data, data_len, output, output_len);
        if (res != (int)BrainFlowExitCodes::STATUS_OK)
        {
            return res;
        }
        *output = 1.0 - (*output);
        return res;
    }
};

안믿을 것 같아서 Restfulness의 전체 코드를 가져왔다. 

해석해보면 MindfulnessClassifier를 할당 받아서 MindfulnessClassifier에 있는 예측하는 코드로 예측시킨 후

그 값을 (1.0 - 값) 이걸로 계산한다는 것이다.

즉 (1.0 - Mindfulness 예측 값)이다. 이 말은 Restfulness는 Mindfulness의 반대 값이라고 생각하면 될 것 같다..

그래서 더 해보면 1이 나오는걸 알 수 있다.

 

암튼 저런 계산 과정으로 마음챙김도가 나오는 것이다.

저런 마음 챙김 모델도 학습하는 방법도 나와있는데 그건 맨 아래 링크에 달아두었다.

 

mindfulness.release()

위에서 할당한 모델을 해제하는 것이다.

 

암튼 이제 설명을 다 했으니 코드를 실행해봐야 한다. (위에서 이미 실행한 결과는 무시하시길..)

 

코드 실행

[2025-02-21 09:47:34.717] [board_logger] [info] incoming json: {
    "file": "",
    "file_anc": "",
    "file_aux": "",
    "ip_address": "",
    "ip_address_anc": "",
    "ip_address_aux": "",
    "ip_port": 0,
    "ip_port_anc": 0,
    "ip_port_aux": 0,
    "ip_protocol": 0,
    "mac_address": "",
    "master_board": -100,
    "other_info": "",
    "serial_number": "Muse-0465",
    "serial_port": "",
    "timeout": 0
}
[2025-02-21 09:47:34.717] [board_logger] [info] Use timeout for discovery: 6
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:33 in initialize_winrt: CoGetApartmentType: cotype=-1, qualifier=0, result=800401F0
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:41 in initialize_winrt: RoInitialize: result=0
[2025-02-21 09:47:34.764] [board_logger] [info] found 1 BLE adapter(s)
[2025-02-21 09:47:34.958] [board_logger] [info] Found Muse device
[2025-02-21 09:47:36.400] [board_logger] [info] Connected to Muse Device
[2025-02-21 09:47:37.405] [board_logger] [info] found control characteristic
Mindfulness: 0.97519466

이런식으로 뜬다. 로그 정보들은 이전에도 설명한 것과 같이 똑같으니 무시하고

 

"Mindfulness: 0.97519466" 값만 보면 된다.

다만 전에 이 집중도하고 전 글의 집중도하고 다르긴 하지만 비슷하기 때문에 자세하게 측정결과는 작성 안할 것이다.

어차피.. 내가 설명한 여기에서의 Mindfulness 집중도는.. 나도 잘 만들어내기가 어렵다..

 

참고로 Restfulness는 Mindfulness를 전부 Restfulness로 바꾸면 된다.

 

그럼으로 이번 글은 여기에서 끝내는걸로..

 

추가로 이전 글에서도 올린 것 처럼 실시간으로 돌릴 수 있는 코드!

import keyboard
from time import sleep
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
from brainflow.data_filter import DataFilter
from brainflow.ml_model import MLModel, BrainFlowModelParams, BrainFlowMetrics, BrainFlowClassifiers

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

use_data_seconds = 5 # n초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
eeg_channels = BoardShim.get_eeg_channels(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안의 데이터를 가져올지

# 머신러닝 모델 준비
mindfulness_params = BrainFlowModelParams(BrainFlowMetrics.MINDFULNESS, BrainFlowClassifiers.DEFAULT_CLASSIFIER)
mindfulness = MLModel(mindfulness_params)
mindfulness.prepare()

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# n초 + 1초 동안 데이터 수집 - 안하면 오류남
sleep(use_data_seconds + 1)

while True:
    # 나가기 조건
    if keyboard.is_pressed('q'):
        break
   
    # 수집한 데이터를 변수에 저장
    data = board_shim.get_current_board_data(num_points)
    
    # 예측에 필요한 데이터 추출
    bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, True)
    feature_vector = bands[0]

    # 집중도 예측
    print(f"Mindfulness: {mindfulness.predict(feature_vector)[0]}")

# 모델 해제
mindfulness.release()

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

 

이제 올릴려 한 것들은 다 올렸고.. 추가로 나머지는 인공지능을 만들어서 적용해보거나 게임을 하면서 집중도는 어떤지 측정해서 올려볼려고 한다.

 

암튼 짧게 끝냈다! 다행히 이번엔 쓰는 시간이 4시간 정도 걸렸다.

 

위에서 언급한 내용들의 관련 링크

Tensorflow로 모델을 제작하여 적용

https://brainflow.org/2022-09-08-onnx-tf/

 

BrainFlow with onnxruntime (TensorFlow edition)

ONNX Inference ONNX is an open format built to represent machine learning models. ONNX defines a common set of operators - the building blocks of machine learning and deep learning models - and a common file format to enable AI developers to use models wit

brainflow.org

Scikit Learn으로 모델을 제작하여 적용

https://brainflow.org/2022-06-09-onnx/

 

BrainFlow with onnxruntime

ONNX Inference ONNX is an open format built to represent machine learning models. ONNX defines a common set of operators - the building blocks of machine learning and deep learning models - and a common file format to enable AI developers to use models wit

brainflow.org

BrainFlow 학습 코드

https://github.com/brainflow-dev/brainflow/blob/master/src/ml/train/train_classifiers.py

 

brainflow/src/ml/train/train_classifiers.py at master · brainflow-dev/brainflow

BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of data from biosensors - brainflow-dev/brainflow

github.com

반응형
반응형

이번껀 전문가가 아니기에 틀린 정보가 많을 수 있습니다.

이번엔 집중도 구하기!

 

이번엔 아마도 그나마 짧지 않을까 한다..(글 쓰기 전이라 추측)

그리고 조금 문제가 생겼다.. 당시에는 EEG를 알아내고 brainflow를 C++로도 사용하고 Python으로도 사용해서 집중도나 이런 거 구하는 거를 했었는데.. 학교를 다니면서 기숙사에서 작업하다 보니 정신이 별로 없던 것 같다.. 2~4주 안에 이 모든 걸 다 했으니..

그렇다 보니 지금 다시 확인해 봤는데 코드가 좀 엉망이다.. 잘못된 정보들이 대부분일 수 있다.

최대한으로 찾아서 해보고 있지만 코드적인 실수가 어디선가 있을 수도 있긴 하다..

참고로 대학교 수업 자료로 쓴 것이기에.. 잘못 정보를 전달했다는 걸 지금 느꼈다..

솔직히 어디서부터 써야 될지 감이 안 잡히긴 하는데.. 어떻게든 적어보겠다..

 

아무튼 시작!

 

집중도 구하는 코드 먼저!

from time import sleep
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
from brainflow.data_filter import DataFilter, FilterTypes

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

use_data_seconds = 1 # n초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
eeg_channels = BoardShim.get_eeg_channels(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안의 데이터를 가져올지

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# n초 + 1초 동안 데이터 수집
sleep(use_data_seconds + 1)

# 수집한 데이터를 변수에 저장
data = board_shim.get_current_board_data(num_points)

# 데이터 처리
for count, channel in enumerate(eeg_channels):
    DataFilter.perform_bandpass(data[channel], sampling_rate, 2.0, 45.0, 4, FilterTypes.BESSEL_ZERO_PHASE, 0) # 분석할 파형 밖의 범위 제거
    DataFilter.perform_bandstop(data[channel], sampling_rate, 48.0, 52.0, 4, FilterTypes.BUTTERWORTH_ZERO_PHASE, 0) # 50Hz의 주변 전기 노이즈 제거
    DataFilter.perform_bandstop(data[channel], sampling_rate, 58.0, 62.0, 4, FilterTypes.BUTTERWORTH_ZERO_PHASE, 0) # 60Hz의 주변 전기 노이즈 제거

# 예측에 필요한 데이터 추출
bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, False)
feature_vector = bands[0]

# 집중도 예측
print(f"Attention: {feature_vector[3] / feature_vector[2]}") # Beta / Alpha

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

이번 코드는 이것이다.

최대한 이해하기 쉽게 하기 위해 이전 코드에서 데이터 처리 부분만 추가해서 작동하도록 했다.

그렇다 보니 코드 설명에선 이전에 한 부분은 제외하고 하겠다..

참고로 저번에 데이터를 확인했기 때문에 여기에선 집중도만 구할 것이기에 데이터를 저장하는 코드는 넣지 않았다.

넣고 싶으면 이전 코드에서 가져와서 넣길..

 

그리고 이번에는 n초로 주석이 되어있다. 이유라면 집중도를 측정하기 때문에 1초 정도로는 부족한 것 같아서 5초로 했다. 하지만 저건 변경할 수도 있으니 n초로 한 것

 

코드 설명 전 이해할 것

뇌파로 분석을 할 때 보통 사용되는 특정 주파수의 파형이 있다. (바로 아래 사진)

(바로 아래 사진 말고 그 사진 아래 있는 사진은 한국어로 되어있으니 그걸 먼저 보는 것을 추천합니다.)

https://choosemuse.com/blogs/news/a-deep-dive-into-brainwaves-brainwave-frequencies-explained-2

그 파형은 Delta(델타), Theta(세타), Alpha(알파), Beta(베타), Gamma(감마)인데 위의 Muse 공식 사이트 안에 설명되어 있는 내용 중 사진에서 가져온 걸 보면 된다.

수집된 데이터에서 FFT(고속 푸리에 변환) 방법으로 저렇게 특정 파형을 뽑아낼 수 있다.

각 파형은 각각의 특색이 있다. 그 특색은 사진의 오른쪽에 적혀있다. 그냥 현재 뇌의 상태를 알 수 있는 파형이라 생각하면 된다. 

뭐 잠든 건지 깨어있는지 집중도가 강한지 등을 알 수 있게 된다.

 

아무튼 저런 파형은 적혀있는 대로 Delta(0.5, 4.0), Theta(4.0, 8.0), Alpha(8.0, 13.0), Beta(13.0, 32.0), Gamma(32.0, 100.0) 이런 범위로 되어있다. 저 범위에 있는 걸 추출하면 현재 뇌 상태를 알 수 있다는 의미? 가 된다.

 

참고로 아래 링크로 가면 자세히 설명되어 있다. 그걸 보는 걸 추천한다..

https://choosemuse.com/blogs/news/a-deep-dive-into-brainwaves-brainwave-frequencies-explained-2

 

Understanding Brainwaves and Frequencies: A Comprehensive Guide | Muse™ EEG-Powered Meditation & Sleep Headband

Uncover the science behind brainwave frequencies, explore types of brainwaves, and harness their potential for heightened perception, creativity, and problem-solving. Discover innovative tools like the Muse 2 Headband and the Muse S Headband that revolutio

choosemuse.com

https://choosemuse.com/pages/muse-research

 

Research | Muse™ EEG-Powered Meditation & Sleep Headband

Buy Muse: the brain sensing headband in USD and receive free and fast US delivery with a money back guarantee. Worldwide Shipping available. Muse is the world's most popular consumer EEG device providing real-time neurofeedback to learn, track and evolve y

choosemuse.com

https://www.etri.re.kr/webzine/20170630/sub04.html

 

Technology

Technology

www.etri.re.kr

 

참고로 바로 위인 etri.re.kr 링크에 있는 내용 중에 한국어로 된 사진 있는데..

https://www.etri.re.kr/webzine/20170630/sub04.html

이걸 보면 될 것 같긴 하다.

머.. 파형은 잘은 모르겠지만 개발하는 곳마다 다 다르게 적혀있다..

어디서는 0.5에서 델타가 시작하고 어디에서는 3.5에서 시작하고 어디에서는 2로 시작하고 그런다..

암튼 저 범위는 무시하고 그냥 각각 파형에 따라 그 파형이 무슨 역할을 하는지만 이해하면 될 것 같다.

 

참고로 아래에서 말하겠지만 집중도 구할 때 사용하는 파형은 베타파알파파이다. 

 

코드 설명 및 이해하기

use_data_seconds = 1 # n초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
eeg_channels = BoardShim.get_eeg_channels(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안의 데이터를 가져올지

이전 글에서 설명한 코드에 추가적으로 eeg_channels에 대한게 있어서 그것만 추가로 적어보겠다.

변수에 적힌 말 그대로 그냥 EEG 채널을 출력하는 것이다. 

{'eeg_channels': [1, 2, 3, 4], 'eeg_names': 'TP9,AF7,AF8,TP10', 'marker_channel': 7, 'name': 'Muse2', 'num_rows': 8, 'other_channels': [5], 'package_num_channel': 0, 'sampling_rate': 256, 'timestamp_channel': 6}

이전 글에서 언급했던 Muse 2 기기에 대한 정보값에서 'eeg_channels' 저거 [1, 2, 3, 4] 저거를 반환하는 함수가 get_eeg_channels() 이다.

다른 eeg_names나 이런 거 가져오는 함수들도 있다.

 

암튼 eeg_channels에는 [1, 2, 3, 4] 값이 저장되어 있다.

 

for count, channel in enumerate(eeg_channels):
    DataFilter.perform_bandpass(data[channel], sampling_rate, 2.0, 45.0, 4, FilterTypes.BESSEL_ZERO_PHASE, 0) # 분석할 파형 밖의 범위 제거
    DataFilter.perform_bandstop(data[channel], sampling_rate, 48.0, 52.0, 4, FilterTypes.BUTTERWORTH_ZERO_PHASE, 0) # 50Hz의 주변 전기 노이즈 제거
    DataFilter.perform_bandstop(data[channel], sampling_rate, 58.0, 62.0, 4, FilterTypes.BUTTERWORTH_ZERO_PHASE, 0) # 60Hz의 주변 전기 노이즈 제거

솔직히 제일 자신 없는 부분이다..

당시에도 어떻게든 찾아서 수업 자료로 만든 적도 있는데.. 결국엔 교수님이 나도 제대로 이해하지 못하고 쓰고 학생들도 이해 못 할 것 같아서 제외된 설명이다..

원래는 Detrend라는 함수도 사용했는데 굳이 중요할 것 같진 않아서 제외했다. 수업 자료에도 제외했었는데.. 넣는 게 나았을지도..?

그리고 당시에는 저거 데이터 처리 코드를 가져올 때 딱히 이해는 안 하고 가져와야 될 것 같아서 문서에서 찾아서 가져왔던 것.. 수업 자료(PPT) 만들게 되면서 그나마 이해했던 것..

암튼 여기가 좀 길 듯하다..

 

일단 저 코드에서는 Muse 2 기기에서 수집된 데이터들로 집중도를 구하기 전 전처리를 하는 단계이다.

for 문은 각 전극에 해당하는 채널 번호를 가져오게 하는 건데 data에는 eeg 전극 채널 외에 다른 채널들도 있기에 그것 중 전극으로 수집한 값이 들은 채널만 가져올 수 있도록 하는 역할이다.

 

그리고 이제 for문 안에 있는 걸 설명해야 하는데.. 제일 자신 없다.. 최대한 당시에 이해한 내용으로 설명해 보겠다..

일단 여기에서 사용하는 데이터 처리 함수는 perform_bandpass와 perform_bandstop이 있다.

함수에 들어갈 파라미터는 대충 이렇다.

DataFilter.perform_band[pass|stop](<적용할 데이터>, <샘플링 속도(256)>, <범위 시작 값>, <범위 끝 값>, <오더>, <적용할 필터 방식>, 0)

[pass|stop] <- 이건 pass와 stop 둘 다 된다는 의미

오더는 세기를 생각하면 될 것 같다. 이후에 나옴

필터는 이후에 설명..

 

그리고 각각의 역할은 이렇다.

BandPass

  • 필터에 따라 특정 주파수 범위 사이의 신호를 통과 시킴
  • 필터에 따라 특정 주파수 범위 밖의 나머지 신호를 감소 시킴

BandStop

  • 필터에 따라 특정 주파수 범위 사이의 신호를 감소 시킴
  • 필터에 따라 특정 주파수 범위 밖의 나머지 신호를 통과 시킴

당시에 수업 자료(PPT)에 적은걸 그대로 적은 거긴 한데.. 솔직히 내가 적어도 이해가 안 된다.

간단하게 말하면 BandPass는 범위 안의 데이터는 놔두고 나머지 신호는 감소시킨다. BandStop은 그냥 반대로 생각하면 된다.

 

암튼 이제 필터에 대해서 볼 건데 필터의 종류는 대충 이렇다. (CHEBYSHEV_TYPE_1 이것도 있긴 한데 처음 보는 거라서 패스!)

  • BUTTERWORTH
  • BESSEL
  • BUTTERWORTH_ZERO_PHASE
  • BESSEL_ZERO_PHASE

이제 각각을 설명해 볼 건데 ZERO_PHASE는 위상 왜곡을 최소화하는 거라는데 필터 먼저 설명하고 따로 설명하겠다.

Butterworth : 신호를 부드럽게 처리하며 범위 밖 주파수를 제거 (여기서는 BandStop에  사용)

Bessel : 파형의 원래 모양을 유지하며 신호 왜곡이 적음 (여기서는 BandPass에  사용)

최대한 간단하게 적었다.. 수업 자료(PPT)에 적은 것 보다도 더 간단하게..

솔직히 간단하게 적었다 하더라도 아무리 봐도 이해하기는 어려울 거다.. 그렇기에 시각화! (시각화 Python 코드 비밀~)

솔직히 부드러워 보이는 건 Bessel 같아 보이긴 하는데 뭐.. 설명으론 저렇다. 암튼 저런 차이가 있는데.. 나는 뭐 시각화한 걸 봐도 잘은 모르겠다.

 

그냥 코드에서 사용하는 것하고 같이 말해보면 주석에 적힌 것처럼

분석할 파형 밖의 범위 제거하는 건 파형 원래의 모양을 유지하는 것이 좋으니 BandPass - Bessel 사용

50/60Hz 주변의 전기 노이즈 제거는 노이즈 같은 이상한 신호를 제거해야 하니 부드럽게 처리해야 해서 BandStop - Butterworth 사용

이라고 생각하면 편할 것 같다. (갑자기 이해되면서 생각난 거라 적어본 거)

 

이제 필터 바로 뒤에 붙는 Zero Phase에 대해 설명할 건데.. 이건 그냥 간단하다.

위상 왜곡을 최소화하는 것이다. 그냥 데이터의 왜곡을 적게 만드는 것이다.

또한 Zero Phase를 사용 안 하면 데이터 신호가 지연된 것처럼 보인다.

그냥 시각화해서 보자! (시각화 Python 코드 비밀~)

여기에서 분홍색이 원본, 초록 파랑이 Zero Phase 적용 안 한 거 보라 빨강이 Zero Phase 적용한 거인데

BandPass - line이라고 되어있는 게.. 그냥 그래프 2개 똑같은 거 그린 후에 그림판으로 잘 보이는 기준점을 선정해서 선으로 그려 수정한 거다. 잘 보이게 하려고 2개 넣은 거

암튼 그림을 보면 알 수 있듯이.. 

확대해서 보면 알 수 있다.

빨간색과 보라색은 Zero Phase를 적용한 건데 원본 파형과 기준선의 위치 차이가 없다고 봐도 될 정도이다.

그에 반면 Zero Phase를 적용 안 한 초록색과 파란색은 뒤로 밀려난 것을 확인할 수 있다. 조금 밀려난 건 회색, 좀 더 밀려난 건 검은색으로 표시했다. (참고로 그릴 때 그림판으로 확대해서 맞춰서 그린 거라 내가 봐도 잘 그린 듯)

추가적으로 Butterworth가 더 많이 지연된 것처럼 보이기도 한다.

 

암튼.. 이걸로 끝이 아니라 하나 더..! 오더가 있다.

위에 설명하는데 힘을 다 빼버려서 오더는 적당히 설명하겠다. 어차피 이건 이미지로 보는 게 제일 이해가 쉬운 거라..

그냥 Order은 필터의 강도라고 생각하면 된다. 

Order은 숫자가 커질수록 필터의 세기가 강해진다고 생각하면 된다. 아래 시각화를 보면 이해가 될지도? (시각화 Python 코드 비밀~)

강도가 세질수록 좀 더 가파르게 변한다고 생각하면 된다. 그리고 원본은 표시를 안 해놓긴 했지만 그냥 직선이다.

여기에서는 필터에 따라 직선이 휘어진 것으로 생각하면 된다.

참고로 Order은 1 ~ 8까지만 설정 가능하다.

 

마지막으로 각 데이터 처리 코드를 지나면 어떻게 변하는지도 시각화해 봤다.

 

아무튼 이걸로 데이터 처리 부분 설명은 끝! 휴..

 

bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, False)
feature_vector = bands[0]

코드에 대해서 설명하기 전에 말할 것이 있는데..

수업 자료 만들 때 잘못 썼던 부분이 여기에 하나 있다..

지금은 코드를 정상적으로 고쳤는데.. 저기 뒤에 get_avg_band_powers 함수의 뒤에 False인 부분을 처음에는 True로 했었다. True로 하게 되면 저게 Apply Filter인데 바로 위에 데이터 처리 코드 부분을 수행한 뒤에 데이터를 반환해 준다..

즉 지금은 False로 해서 위에서 전처리한 게 소용이 있는데.. True로 하면 전처리를 하고 또 전처리를 하게 되어서 좀 별로다.. 암튼 그럼..

 

이 코드는 수집된 데이터에서 Delta(델타), Theta(세타), Alpha(알파), Beta(베타), Gamma(감마) 데이터(band_powers)를 뽑아서 그중 평균값만 뽑아 feature_vector에 저장하는 코드이다. (위에 파형에 대한 설명을 적어놓았으니 델타 세타파 저런 게 뭔지 모르겠다면 위에 적은 걸 보시길..)

 

그리고 feature_vector에는 bands[0]을 저장하는데 위에 적혀있는 것처럼 평균값만 뽑는다.

그럼 bands에 저장되어 있는 다른 건 무엇이냐.. 하면.. get_avg_band_powers가 뭘 리턴하는지 보면 된다.

# 코드 설명을 하기 위한 코드
def get_avg_band_powers(cls, data, channels: List, sampling_rate: int, apply_filter: bool) -> Tuple:
    """calculate avg and stddev of BandPowers across all channels, bands are 1-4,4-8,8-13,13-30,30-50

    :param data: 2d array for calculation
    :type data: NDArray[Shape["*, *"], Float64]
    :param channels: channels - rows of data array which should be used for calculation
    :type channels: List
    :param sampling_rate: sampling rate
    :type sampling_rate: int
    :param apply_filter: apply bandpass and bandstop filtrers or not
    :type apply_filter: bool
    :return: avg and stddev arrays for bandpowers
    :rtype: tuple
    """

    bands = [(2.0, 4.0), (4.0, 8.0), (8.0, 13.0), (13.0, 30.0), (30.0, 45.0)]
    return cls.get_custom_band_powers(data, bands, channels, sampling_rate, apply_filter)

이게 그 함수의 코드이다. 

아래 bands에 정의되어 있는 건 Delta, Theta, Alpha, Beta, Gamma 파형의 범위 값이다. 위에서 말했듯이 여기에서 사용하는 파형의 범위도 약간 다르다.

 

아무튼 코드 보단 주석을 보면 되는데..

주석을 보면 return이 뭐를 반환하는지 적혀있다.

avg는 평균값을 의미하고 stddev는 표준편차를 의미한다. 배열로 2가지 값들을 반환하기 때문에 0번 인덱스인 평균값만 불러오도록 코드를 제작한 것이다.

참고로 분석에는 보통 평균값을 이용하고 데이터의 안전성이나 변동성을 확인할 때는 표준편차를 이용한다 한다.

그리고 리턴되는건 Delta, Theta, Alpha, Beta, Gamma 각각의 평균 값과 표준편차 값이다. 즉 입력된 모든 데이터의 5개 뇌파가 나오는게 아니라 모든 전극 뇌파에서 Delta, Theta, Alpha, Beta, Gamma를 추출하고 그것으로 평균과 표준편차 값을 구해서 반환한 것이다. 그래서 리턴되는 것에서 평균과 표준편차 각각은 5개의 실수가 리스트로 담겨서 리턴된다.

 

추가적으로 원하는 범위를 사용하여 추출하고 싶으면 저기 return 부분에 적혀있는 것처럼 get_custom_band_powers 이 함수에 원하는 범위를 담은 리스트를 넘겨서 받아내면 된다. (난 잘 모르기에 brainflow에서 사용하는 걸 사용)

 

추가적으로 위에 왜 함수 사용할 때 False로 해놓았냐에 대한 설명이 있는데.. 그걸 좀 더 자세하게 설명해 보면..

저 apply_filter가 어디에서 사용되는지 따라가 봤다. 따라가 봤는데 DataHandlerDLL의 함수에 사용된다고 한다.. DLL를 본 순간.. 이 brainflow는 C++로 제작된 거니깐 C++ 코드에 있겠다 생각하고 C++ 코드를 찾아보았다..

그리고 찾아냈는데.. 아래 C++ 코드가 있었다.

# 코드 설명을 하기 위한 코드
if (apply_filters)
{
    exit_codes[i] = detrend (thread_data, cols, (int)DetrendOperations::CONSTANT);
    if (exit_codes[i] == (int)BrainFlowExitCodes::STATUS_OK)
    {
        exit_codes[i] = perform_bandstop (thread_data, cols, sampling_rate, 48.0, 52.0, 4,
            (int)FilterTypes::BUTTERWORTH_ZERO_PHASE, 0.0);
    }
    if (exit_codes[i] == (int)BrainFlowExitCodes::STATUS_OK)
    {
        exit_codes[i] = perform_bandstop (thread_data, cols, sampling_rate, 58.0, 62.0, 4,
            (int)FilterTypes::BUTTERWORTH_ZERO_PHASE, 0.0);
    }
    if (exit_codes[i] == (int)BrainFlowExitCodes::STATUS_OK)
    {
        exit_codes[i] = perform_bandpass (thread_data, cols, sampling_rate, 2.0, 45.0, 4,
            (int)FilterTypes::BUTTERWORTH_ZERO_PHASE, 0.0);
    }
}

봤을 때 바로 알 수 있을 것이다.. 이 코드 설명 바로 위 코드 설명에 있는 코드하고 닮았다..

즉 apply_filter를 켜게 되면 위에서 했던 데이터 처리 코드가 작동한다는 것..

그 때문에 apply_filter를 켜면 데이터 처리를 했는데 또 처리를 하게 된다는 의미.. 

 

뭐 그래도 약간은 달라서 괜찮을지도..?

(근데 함수와 괄호 사이가 왜 띄어져 있는 거지? brainflow 코드 살펴보면서 제일 신경 쓰이는 것..)

 

print(f"Attention: {feature_vector[3] / feature_vector[2]}") # Beta / Alpha

이제 여기에서 집중도를 구하게 된다..

원래는 저걸 Attention이라 안 적고 Mindfulness 라 적었는데.. 이유는 다음 글에서 설명할 인공지능으로 집중도 구하는 것과 관련되어 있는데.. 거기에서는 Mindfulness를 사용한다.. 그런데 저 Beta / Alpha 이게.. 집중도를 구하는데 Mindfulness 보단 Attention으로 번역하는 게 더 맞아서 수정했다. 그리고 찾아보니 다음 글에서 할 Mindfulness는 현재의 집중도와 약간 다르긴 하다.. 뭐 딱히 중요하진 않으니 넘어가도 괜찮지 않을까라는.. 생가..ㄱ

 

암튼 저거 위에서 가져온 feature_vector 변수에는 어쨌든 평균값의 델타 베타 이런 값들이 가져와지게 되는데

거기에서 순서대로 델타부터 해서 해보면..

  • Delta(델타) : feature_vector[0]
  • Theta(세타) : feature_vector[1]
  • Alpha(알파) : feature_vector[2]
  • Beta(베타) : feature_vector[3]
  • Gamma(감마) : feature_vector[3]

이렇게 인덱싱할 수 있다고 생각하면 된다.

그렇기에 3번과 2번을 가져와서 나누는 거니깐 암튼 Beta / Alpha를 한다는 소리..

이걸 하면 집중도가 구해진다는 의미가 된다.

 

저 Beta / Alpha 수식은 보통적으로 집중도를 구할 때 많이 사용되는 수식 같다. 솔직히 나는 저게 집중도를 구할 수 있는 건지는 잘 모르겠다. 뭐 그래도 아래에 Beta / Alpha가 언급된 논문 2개를 걸어놓겠다.. (참고로 영어라서 거의 보진 않았다..)

https://www.mdpi.com/2076-3425/13/1/57

https://typeset.io/questions/what-is-the-relationship-between-eeg-beta-alpha-index-and-8f18vjplgq

 

좀 더 편한 코드

from time import sleep
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
from brainflow.data_filter import DataFilter

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

use_data_seconds = 5 # n초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
eeg_channels = BoardShim.get_eeg_channels(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안의 데이터를 가져올지

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# n초 + 1초 동안 데이터 수집
sleep(use_data_seconds + 1)

# 수집한 데이터를 변수에 저장
data = board_shim.get_current_board_data(num_points)

# 예측에 필요한 데이터 추출
bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, True)
feature_vector = bands[0]

# 집중도 예측
print(f"Attention: {feature_vector[3] / feature_vector[2]}") # Beta / Alpha

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

이 코드는 위에서 말했듯이 get_avg_band_powers에서 자동으로 필터 적용해 주는 apply_filter를 켜서 데이터 전처리를 따로 안 하고 되도록 하는 코드이다.

위에 코드는 잊어버리고 이거 사용하면 될 듯하다..

 

암튼 바로 실행해 보자! 이번엔 귀찮긴 하지만 착용하고 측정했다!

그리고 위에서도 말했듯이 이번에는 1초가 아니라 5초 측정이다. 

1초로도 되기는 하는데.. 조금 부족해 보여서 5초로 했고.. 좀 더 길게 할수록 정확도는 높아질 것 같다 생각은 들긴 한다..

 

코드 실행!

[2025-02-19 07:46:39.169] [board_logger] [info] incoming json: {
    "file": "",
    "file_anc": "",
    "file_aux": "",
    "ip_address": "",
    "ip_address_anc": "",
    "ip_address_aux": "",
    "ip_port": 0,
    "ip_port_anc": 0,
    "ip_port_aux": 0,
    "ip_protocol": 0,
    "mac_address": "",
    "master_board": -100,
    "other_info": "",
    "serial_number": "Muse-0465",
    "serial_port": "",
    "timeout": 0
}
[2025-02-19 07:46:39.170] [board_logger] [info] Use timeout for discovery: 6
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:33 in initialize_winrt: CoGetApartmentType: cotype=-1, qualifier=0, result=800401F0
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:41 in initialize_winrt: RoInitialize: result=0
[2025-02-19 07:46:39.203] [board_logger] [info] found 1 BLE adapter(s)
[2025-02-19 07:46:39.409] [board_logger] [info] Found Muse device
[2025-02-19 07:46:41.070] [board_logger] [info] Connected to Muse Device
[2025-02-19 07:46:42.072] [board_logger] [info] found control characteristic
Attention: 0.5993206298011964

다른 정보 출력들은 이전 글에서 말했으니 무시하고 Attention만 봐보자!

 

처음 실행하고 코드의 한 함수를 보면서 집중했을 때 (글 쓰느라 머리 엄청 아픈 상태였음 글 쓰는데 6시간쯤 됐을 때) 

Attention: 1.172955634748009

 

값이 작아지나 머리 흔들거나 집중 안 했을 때 (딴 데 보거나 머리 흔듦.. 집중해서 머리 아픈 상태라 높게 나올까 봐..)

Attention: 0.5993206298011964

 

도대체 값이 어디까지가 최대인거지? 집중 한번 해볼까? 하다가 한곳에 집중하지 코드를 훑어보고 있었을 때

Attention: 0.7456045268742625

 

어라? 왜 낮은 거지.. 하고 함수 안을 살펴보고 있었을 때 (함수 들어갈 때 화면이 바뀌면서 집중력 더 떨어짐)

Attention: 0.45490847948134255

 

그냥 한곳에 집중해 보자 하고 한 함수만 가까이서 집중하며 보고 있었을 때

Attention: 1.2981097661167502

 

측정하면서 어떤 상황인지 함께 적어봤다..

확실히 그 상황마다의 집중도를 확실하게 캐치하는 것 같다. 정규화는 되어있지 않아서 확인하기는 좀 힘들긴 하지만..

저게 0 ~ 1의 값이 아닌 이유는 뭐 당연하지만 값의 범위가 달라서이다.. 저거를 0 ~ 1로 맞추는 방법이 있긴 하겠지만.. 뭐 있겠지만 생각한다. 참고로 뭐 max 값 min 값 해서 범위 찾아서 그걸로 0 ~ 1로 맞추는 걸 생각해 보긴 했는데 어차피 측정할 때 데이터를 이상하게 하는 방법도 있고 개인마다 다 차이 나서 맞추기가 어려울 것 같긴 하다. 뭐 찾아보면 어딘가에는 0 ~ 1로 맞추는 게 있긴 하겠다만.. 어차피 다음 글에서 인공지능으로 하는 게 0 ~ 1 값이다.

 

추가로 이전에 말했던 것 같은데 왼쪽은 근육이 발달하지 않은 건지 잘 안되지만 오른쪽은 뭔가 귀를 위로 올린다 생각하며 힘준 상태로 이를 물어버리면 오른쪽 귀 쪽이 뭔가 위로 올라가며 힘을 주는 듯한 느낌이 난다.

이 상태로 측정해 버리면 베타 값인가? 그게 파워가 확 높아져버림..

 

그렇게 되면 집중도를 조작할 수 있다. 심각하게..

 

그 상태로 측정하게 되면 이렇게 나온다..

Attention: 4.007621255928729

저거 힘주는 건 기숙사 방에서 해보다가 어쩌다가 집중하려고 힘줬더니만 진짜 돼버려서 알게 된 것..

 

참고로 기기를 벗은 상태로 측정하면 -1000 같은 결측값과 이상한 값들 때문에 이렇게 나온다.

Attention: 19.663765197669104

 

암튼 이상하게 나온다. 이래서 0 ~ 1 값으로 맞추기는 좀 어려울 것 같긴 하다.. 뭐 극단적으로 지금 생각나는 방법으로는 범위를 -1000 ~ 1000으로 해서 하면 될 것 같긴 한데, 이러면 집중을 해도 값이 작게 나올 테니 패스

 

그리고 추가로 실시간으로 측정하는 걸 원한다면.. 아래 코드로 해보는 걸 추천한다.

from time import sleep
import keyboard
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds
from brainflow.data_filter import DataFilter

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

use_data_seconds = 5 # n초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
eeg_channels = BoardShim.get_eeg_channels(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안의 데이터를 가져올지

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# n초 + 1초 동안 데이터 수집 - 안하면 오류남
sleep(use_data_seconds + 1)

while True:
    # 나가기 조건
    if keyboard.is_pressed('q'):
        break
        
    # 수집한 데이터를 변수에 저장
    data = board_shim.get_current_board_data(num_points)
    
    # 예측에 필요한 데이터 추출
    bands = DataFilter.get_avg_band_powers(data, eeg_channels, sampling_rate, True)
    feature_vector = bands[0]

    # 집중도 예측
    print(f"Attention: {feature_vector[3] / feature_vector[2]}") # Beta / Alpha

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

실행하려면 pip install keyboard로 keyboard 라이브러리 설치하고 실행할 수 있다.

간단하게 설명하면 처음에 데이터 수집을 위해 수집 초 + 1초를 기다린 후

while로 반복하면서 반복될 때마다 최근의 5 * 256 데이터를 가져오고 그걸로 집중도를 계산해서 바로 출력하는 코드이다. 그리고 키보드로 q를 누르면 종료된다.

이 코드의 함정이라면 집중을 한 것을 보고 싶다면 집중한 후에 바로 보는 것은 하기 힘들고 집중하고 설정한 초가 지난 후에 봐야 집중도를 잴 수 있다는 점이다. (ㅋㅋ)

뭐 저 초를 1초로 바꿔도 되기는 하지만.. 정확도는 낮아지니 추천은 안 하지만.. 수업 자료(PPT)에서는 1초로 했었기 때문에.. 해봐도 문제 될 건 아마 없지 않을까.. 하는..

 

 

암튼 이걸로 Beta / Alpha 수식으로 집중도를 계산해 보는 건 여기에서 끝!

이거 새벽 2시에 작성하기 시작했는데.. 벌써 8시네.. 허허.. 심지어 더 길어 보이네..

 

암튼 끝!

반응형
반응형

이번엔 Muse 2를 연결해 볼 것이다!

 

우선 설명하기 전에 빠르게 시도해 볼 수 있는 코드를 올리겠다!

아래 코드는 Brainflow로 Muse 2를 연결하고 1초간 데이터를 수집하는 코드이다.

import pandas as pd
from time import sleep
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

use_data_seconds = 1 # 1초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안 몇개의 데이터를 가져올지

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# 2초 동안 데이터 수집
sleep(use_data_seconds + 1)

# 수집한 데이터를 변수에 저장
data = board_shim.get_current_board_data(num_points)

# 저장된 데이터를 알기 위한 출력 코드
print(board_shim.get_board_descr(board_id))

# CSV로 저장
df = pd.DataFrame(data).T
df.to_csv(f"data.csv", index=False)

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

 

참고로 당시에 Brainflow나 Muse 2에 대한 정보들이 엄청 적어서.. 코드를 작성하는데 조금 어려움은 있었다..

그래도 현재 올린 코드는 내가 몇번이고 찾아보면서 그나마 다듬어서 제일 쉬운 데이터 수집 코드를 만들었다.

솔직히 변수 명은 딱히 생각 안나서.. 그냥 이해하기 쉬운 걸로 쓴 거다..

 

Muse 2 시리얼 코드 연결

암튼 코드를 설명하기 전에 Muse 2를 연결할 때는 시리얼 코드가 필요하여 그것부터 설명해 보면..

일단 Brainflow에서 Muse 2 기기를 인식하는 방식이 블루투스 연결 장치가 주변 블루투스 기기를 찾고 블루투스 이름으로 Muse 2 기기를 연결하게 되는 방식이다.

 

그런데 나의 경우 Muse 2 기기가 주변에 여러 개 있는 경우 특정 하나만 연결되도록 해야 했었다. 왜냐하면 나중에 수업 때 사용하거나 2개 이상 연결해서 프로젝트를 진행했어야 했기 때문에;;

그렇기에 특정할 수 있는 방법은 시리얼 넘버를 사용하는 것이다.

혹시라도 전체 시리얼 넘버가 공개되면 문제가 있을 수 있어서 가리고 올렸다. 

하나는 머리에 착용했을 때 왼쪽 귀에 해당하는 곳에 적힌 시리얼 넘버와 박스 상자 위에 적힌 시리얼 넘버이다.

기기에서는 상단에 있는 MUSE-0465를 확인하면 되고 박스에서는 S/N : ____-____-0465를 확인하면 된다.. 그냥 숫자 4자리 확인하면 된다.

 

암튼 저 4자리 숫자가 기기를 특정할 수 있도록 하는 것인데.. 저것만 알아두면 된다..!

 

암튼 이제부턴 코드 설명!

코드 설명

import pandas as pd
from time import sleep
from brainflow.board_shim import BoardShim, BrainFlowInputParams, BoardIds

먼저 필요한 라이브러리를 불러온다..!

여기에서 sleep 함수는 몇 초 동안 데이터를 수집하는지에서 대기하도록 하려고 쓰는 것이고.. pandas는 데이터를 저장하기 위한 것, 나머지는 Brainflow 라이브러리들이다.

참고로 pandas는 같이 설치가 안되기 때문에 pip install pandas로 설치하면 된다.

 

board_id = BoardIds.MUSE_2_BOARD
params = BrainFlowInputParams()
params.serial_number = "Muse-0465" # Muse 2의 고유 시리얼 넘버
board_shim = BoardShim(board_id, params)

여기가 가장 중요하다고 생각되는 부분이다.

Muse 2의 기기를 선택시키고 시리얼 넘버를 정하는 부분이다.

 

먼저 board_id이다. board_id는 BoardIds라는 클래스에서 특정 기기의 정보들을 작성하여 연결을 돕거나 하는 그런 클래스라고 생각하면 되는데.. 미리 어떤 기기를 연결할지 선택을 해야 한다.

나의 경우 Muse 2를 연결하기 때문에 MUSE_2_BOARD를 선택한다.

(참고로 다른 기기들의 경우는 https://github.com/brainflow-dev/brainflow/blob/41a1d4acff178b8afff301c842a686e6d5217cab/python_package/brainflow/board_shim.py#L19-L82 여기를 확인하면 된다. 다만 특정 버전의 줄을 선택한 것이기에 Brainflow가 업데이트되거나 코드가 업데이트되면서 바뀔 수 있으니 최신 코드를 확인해 보면 좋다.)

 

다만 Muse 2 기기를 선택할 때 주의할 점이 있는데..

위에 참고 사항으로 올려둔 링크를 타고 가보면 Muse 2가 2개일 것이다.. (참고로 Muse S나 Muse 2016도 있는데 그건 다른 기기;;)

MUSE_2_BLED_BOARD = 22  #:
MUSE_2_BOARD = 38  #:

왜 2개이냐 하면.. 이전 글에서 말했던 BLED112 동글 때문이다.

MUSE_2_BLED_BOARD는 BLED112 동글로 연결할 때 필요한 번호이다. 이젠 딱히 쓰이지는 않겠지만.. 만약 동글로 연결하게 된다면 저걸 선택하면 된다..

하지만 나는 일반 블루투스 연결 장치로 연결할 것이기 때문에 MUSE_2_BOARD로 선택한 것이다..!

 

BrainFlowInputParams는 EEG 기기의 추가 정보들을 지정해서 연결할 수 있도록 해주는 클래스이다.

여기에서는 시리얼 넘버만 사용해서 연결하지만 다른 EEG 기기들을 위해 IP나 Mac Address 등 다른 지정 방법들도 있다.

아무튼 저 클래스를 받아서 시리얼 넘버를 지정하게 되는데..

위에서 말했듯이 나의 경우 Muse-0465가 시리얼 번호이기 때문에 Muse-0465 시리얼 번호를 사용하면 된다.

그렇기 때문에 꼭 자신의 시리얼 번호를 확인 후 작성해서 연결하면 될 것 같다..

그리고 그냥 기기를 한 개만 사용하거나 굳이 시리얼 넘버를 지정하지 않아도 되는 경우라면 그냥 저 serial_number를 설정하는 부분은 주석 처리 하면 된다.

따로 지정하지 않으면 그냥 블루투스 장치 검색할 때 처음으로 뜨는 Muse 2 기기를 연결하게 된다..

 

use_data_seconds = 1 # 1초 동안 데이터 수집
sampling_rate = BoardShim.get_sampling_rate(board_id)
num_points = use_data_seconds * sampling_rate # 몇 초 동안 몇개의 데이터를 가져올지

여기서는 몇 초간의 데이터를 가져올지 설정하는 부분이다.

 

나는 테스트로 간단하게 작동하는지만 보는 것이기 때문에 1초를 사용한다.

그리고 sampling_rate를 기기 정보에서 가져온다. 그냥 sampling_rate = 256으로 작성해도 되긴 한다.

어쨌든 기기들마다 차이가 있으므로 Brainflow에서 미리 정의한 sampling_rate 정보를 가져온 것..

참고로 더 많은 추가 정보들을 가져올 수도 있다.

 

그리고 num_points인데.. 이거는 나중에 몇 개의 데이터를 가져올지 사용할 때 쓴다.

나는 1초 동안의 데이터를 가져오고 이 기기의 초당 데이터 개수는 256이기 때문에 256개를 가져오도록 한다. 만약 2초라면 512개를 가져오도록 하면 된다. 마찬가지로 그냥 num_points = 256으로 작성해도 된다.

 

# Muse 2 연결
board_shim.prepare_session()

# Muse 2 데이터 수집 시작
board_shim.start_stream()

# 2초 동안 데이터 수집
sleep(use_data_seconds + 1)

# 수집한 데이터를 변수에 저장
data = board_shim.get_current_board_data(num_points)

이번엔 연결하고 데이터를 수집하고 데이터를 가져오는 부분이다.

 

주석에 적힌 대로 이해하면 된다.

연결하고 수집을 시작하도록 명령한다.

 

그 후 2초 동안 데이터를 수집한다. 이렇게 하는 이유는 아래에서도 얘기할 텐데 Muse 2 기기는 256hz라고 해도 연구용으로 나온 기기는 아니다 보니.. 초당 256개의 데이터가 정확히 가져와지지는 않는 것 같다.

그렇기 때문에 2초를 수집해서 1초를 수집해서 조금 부족한걸 나머지 1초에서 가져오도록 하기 위해서 저렇게 한 것이다.

뭐 나는 간단하게 하려고 저렇게 했지만 초마다 정확하게 데이터를 가져와야 한다면 초를 판단해서 데이터가 256개가 아니라도 가져오도록 해야 할 것이다.. 하지만 귀찮고 어렵기 때문에 여기에서는 패스..

 

그 후 board_shim의 get_current_board_data로 이전에 지정한 가져올 데이터 개수대로 데이터를 가져온다.

즉 2초간 측정한 데이터에서 1초 분량인 256개의 데이터를 가져온다는 것이다.

참고로 get_current_board_data는 이름에서 짐작할 수 있듯이 최근의 특정 개수의 데이터를 가져온다는 것이다.

만약 지금까지 측정한 전체 데이터를 가져오도록 할 것이라면 get_board_data를 사용하면 된다.

 

# 저장된 데이터를 알기 위한 출력 코드
print(board_shim.get_board_descr(board_id))

# CSV로 저장
df = pd.DataFrame(data).T
df.to_csv(f"data.csv", index=False)

# Muse 2 데이터 수집 정지
board_shim.stop_stream()

# Muse 2 연결 해제
board_shim.release_session()

이번 건 데이터를 csv로 저장하고 데이터 수집 종료 후 Muse 2 기기의 연결을 해제시킨다.

 

먼저 print(board_shim.get_board_descr(board_id)) 이 코드는 csv로 저장하는 것을 설명한 뒤 설명할 것이다. (쓰고 있을 때 생각해 보니.. 그냥 csv 아래에 적어도 되긴 할 텐데라고 생각하긴 했는데.. 귀찮다..

 

먼저 csv로 저장하는 것을 보면

그냥 알 수 있듯이 데이터를 DataFrame에 등록시키고 data.csv로 저장하는 것이다.

 

DataFrame에 등록할 때 T는 그냥 행열 바꾸는 것이다. 저거 안 하면 csv로 열었을 때 읽기 좀 불편하다..

그리고 index를 없애는 건 굳이 필요 없어서.. 더 복잡하게 보일 것 같으니..

 

그리고 데이터 수집을 종료하고 연결을 해제시키는데 만약에 연결 해제 코드를 안 쓰고 실행해 버려서 코드가 끝나면 Muse 2 기기는 연결된 상태로 코드가 끝나게 될 수 있다.. 이럴 경우 다시 연결할 때 이미 컴퓨터에 연결되어 있기 때문에 다시 연결이 안 된다. 그래서 다시 연결하려면 Muse 2 기기의 전원 버튼을 꾹 눌러서 전원을 강제 종료 시키고 다시 켜서 연결시키도록 해야 한다. 귀찮게 다시 연결하도록 할 것이 아니라면 종료 코드는 써주는 게 좋다.

참고로 이미 연결이 잘 되어있는지 확인하려면 Muse 2의 LED 인디케이터를 확인하면 된다. 연결이 되어있을 땐 전체에 불이 들어와 있고 연결이 아직 안 되었을 땐 왔다 갔다 led가 빛난다.

 

암튼 이제 print(board_shim.get_board_descr(board_id)) 에 대해서 설명할 텐데 이건 수집한 데이터의 칼럼 이름이다.

즉 데이터에서 뭐가 뭐인지 설명하도록 되어있다는 것이다.

저 코드를 실행해 보면 뭔가 뜨는데 그건 아래와 같다.

{'eeg_channels': [1, 2, 3, 4], 'eeg_names': 'TP9,AF7,AF8,TP10', 'marker_channel': 7, 'name': 'Muse2', 'num_rows': 8, 'other_channels': [5], 'package_num_channel': 0, 'sampling_rate': 256, 'timestamp_channel': 6}

데이터에서 어떤 게 무엇인지 설명하고 있다.

 

이것에 대한 자세한 건 실행한 후 알아야 하기 때문에 이후에 다시 설명할 것이다.

암튼 여기에선 코드 설명이 끝났다..! 이제 실행해 보는 것이다!

 

코드 실행해 보기

이제 코드를 실행해 보자!

내가 만들었던 가상환경으로 설정하고 Muse 2 기기의 전원 버튼을 눌러 전원을 켜고 코드를 실행하면 아래처럼 나올 것이다!

[2025-02-16 19:00:33.345] [board_logger] [info] incoming json: {
    "file": "",
    "file_anc": "",
    "file_aux": "",
    "ip_address": "",
    "ip_address_anc": "",
    "ip_address_aux": "",
    "ip_port": 0,
    "ip_port_anc": 0,
    "ip_port_aux": 0,
    "ip_protocol": 0,
    "mac_address": "",
    "master_board": -100,
    "other_info": "",
    "serial_number": "Muse-0465",
    "serial_port": "",
    "timeout": 0
}
[2025-02-16 19:00:33.346] [board_logger] [info] Use timeout for discovery: 6
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:33 in initialize_winrt: CoGetApartmentType: cotype=-1, qualifier=0, result=800401F0
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:41 in initialize_winrt: RoInitialize: result=0
[2025-02-16 19:00:33.379] [board_logger] [info] found 1 BLE adapter(s)
[2025-02-16 19:00:33.459] [board_logger] [info] Found Muse device
[2025-02-16 19:00:36.094] [board_logger] [info] Connected to Muse Device
[2025-02-16 19:00:37.103] [board_logger] [info] found control characteristic
{'eeg_channels': [1, 2, 3, 4], 'eeg_names': 'TP9,AF7,AF8,TP10', 'marker_channel': 7, 'name': 'Muse2', 'num_rows': 8, 'other_channels': [5], 'package_num_channel': 0, 'sampling_rate': 256, 'timestamp_channel': 6}

물론 어느 정도 차이는 있을 수 있다.

 

로그에서 뜨는 건 어떤 걸로 연결할지에 대한 것들이다. 그냥 코드만 틀린 게 없다면 Found Muse Device와 Connected to Muse Device가 뜨는지 확인하면 된다.

[2025-02-16 19:00:33.459] [board_logger] [info] Found Muse device
[2025-02-16 19:00:36.094] [board_logger] [info] Connected to Muse Device

이게 제대로 떴다면 연결이 성공적으로 되었다는 것이다..!

 

실행할 때 Muse 2의 인디케이터는 왔다 갔다 불빛이 나오다가 어느 순간 연결이 되면서 불빛이 모두 켜진다.

그 후 다시 연결이 해제되어 다시 왔다 갔다 불빛이 나오게 되는 것을 볼 수 있을 것이다.

 

참고로 인디케이터가 대충 어떻게 뜨나면..

아래 영상은 연결 중이거나 연결이 아직 안 되었다는 표시다.

아래 영상은 연결이 되었다는 표시다.

어쨌든 이런 식으로 확인이 가능하다..

만약 코드는 끝났는데 다시 왔다 갔다 불빛으로 바뀌지 않는다면 코드를 확인하거나 코드가 갑자기 중단되지는 않았는지 확인하는 것이 좋다.

 

참고로 전원을 켜지 않았다 던 지 블루투스 연결 장치가 제대로 작동하지 않다던지, 연결이 안 되었을 경우에는 아래처럼 뜬다. 

[2025-02-17 03:37:41.517] [board_logger] [info] incoming json: {
    "file": "",
    "file_anc": "",
    "file_aux": "",
    "ip_address": "",
    "ip_address_anc": "",
    "ip_address_aux": "",
    "ip_port": 0,
    "ip_port_anc": 0,
    "ip_port_aux": 0,
    "ip_protocol": 0,
    "mac_address": "",
    "master_board": -100,
    "other_info": "",
    "serial_number": "Muse-0465",
    "serial_port": "",
    "timeout": 0
}
[2025-02-17 03:37:41.518] [board_logger] [info] Use timeout for discovery: 6
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:33 in initialize_winrt: CoGetApartmentType: cotype=-1, qualifier=0, result=800401F0
[INFO] SimpleBLE: D:\a\brainflow\brainflow\third_party\SimpleBLE\simpleble\src\backends\windows\Utils.cpp:41 in initialize_winrt: RoInitialize: result=0
[2025-02-17 03:37:41.552] [board_logger] [info] found 1 BLE adapter(s)
[2025-02-17 03:37:47.581] [board_logger] [error] Failed to find Muse Device
Traceback (most recent call last):
  File "D:\개발\AI\블로그\MUSE 2\blog\1_main.py", line 15, in <module>
    board_shim.prepare_session()
  File "C:\Users\____\anaconda3\envs\blogEEG\lib\site-packages\brainflow\board_shim.py", line 1191, in prepare_session
    raise BrainFlowError('unable to prepare streaming session', res)
brainflow.exit_codes.BrainFlowError: BOARD_NOT_READY_ERROR:7 unable to prepare streaming session

이렇게 뜨면 제대로 연결이 되지 않았다는 거니깐 기기나 연결 장치를 확인해 보면 좋을 것 같다.. (뭐.. 그래도 안되면.. 컴퓨터를 바꿔보는 것도.. 추천..)

 

그러면 이제 한번 저장된 csv 파일을 분석..? 해보자..!

 

수집된 데이터 확인!

0 1 2 3 4 5 6 7
16 -1000 -987.3046875 -945.8007813 709.4726563 0 1739688027.40830 0
16 -910.6445313 -856.9335938 -998.5351563 -795.8984375 0 1739688027.41217 0
16 -623.046875 -791.9921875 -981.9335938 -1000 0 1739688027.41605 0
16 -866.2109375 -925.2929688 -933.59375 202.6367188 0 1739688027.41992 0
17 -1000 -1000 -892.578125 999.5117188 0 1739688027.42472 0
17 -780.2734375 -899.4140625 -963.3789063 -389.6484375 0 1739688027.42953 0
17 -482.421875 -773.4375 -1000 -1000 0 1739688027.43433 0
17 -552.734375 -866.2109375 -917.96875 -216.3085938 0 1739688027.43913 0
17 -760.2539063 -991.6992188 -841.796875 999.5117188 0 1739688027.44394 0
17 -636.2304688 -936.5234375 -862.3046875 242.1875 0 1739688027.44874 0
17 321.2890625 -769.0429688 -763.671875 -1000 0 1739688027.45354 0
17 -59.5703125 -805.6640625 -835.4492188 -407.7148438 0 1739688027.45835 0
17 -1000 -979.9804688 -931.1523438 999.5117188 0 1739688027.46315 0
17 -635.2539063 -963.8671875 -836.9140625 511.71875 0 1739688027.46795 0
17 -486.8164063 -818.8476563 -887.2070313 -1000 0 1739688027.47275 0
17 -990.7226563 -813.4765625 -1000 -891.1132813 0 1739688027.47756 0
18 -979.9804688 -954.1015625 -948.7304688 675.78125 0 1739688027.48136 0
18 -724.1210938 -985.3515625 -816.40625 956.5429688 0 1739688027.48517 0
18 -869.140625 -876.4648438 -904.296875 -541.9921875 0 1739688027.48897 0

이렇게 측정되었다!

 

이제 위에서 말했던.. print(board_shim.get_board_descr(board_id)) 이 코드로 출력된 것과 같이 확인해 보면 된다.

{'eeg_channels': [1, 2, 3, 4], 'eeg_names': 'TP9,AF7,AF8,TP10', 'marker_channel': 7, 'name': 'Muse2', 'num_rows': 8, 'other_channels': [5], 'package_num_channel': 0, 'sampling_rate': 256, 'timestamp_channel': 6}

이제 이렇게 보면 출력했던 것이 무엇인지 짐작이 갈 것이다.

출력된 것에서 숫자들은 칼럼이다. 'eeg_channels': [1, 2, 3, 4]는 1, 2, 3, 4 컬럼이 eeg 채널이라는 것을 의미한다.

 

그러면 저기에서 설명한 것을 토대로 csv를 수정해 보면!

package_num_channel TP9 AF7 AF8 TP10 other_channels timestamp_channel marker_channel
16 -1000 -987.3046875 -945.8007813 709.4726563 0 1739688027.40830 0
16 -910.6445313 -856.9335938 -998.5351563 -795.8984375 0 1739688027.41217 0
16 -623.046875 -791.9921875 -981.9335938 -1000 0 1739688027.41605 0
16 -866.2109375 -925.2929688 -933.59375 202.6367188 0 1739688027.41992 0
17 -1000 -1000 -892.578125 999.5117188 0 1739688027.42472 0
17 -780.2734375 -899.4140625 -963.3789063 -389.6484375 0 1739688027.42953 0
17 -482.421875 -773.4375 -1000 -1000 0 1739688027.43433 0
17 -552.734375 -866.2109375 -917.96875 -216.3085938 0 1739688027.43913 0
17 -760.2539063 -991.6992188 -841.796875 999.5117188 0 1739688027.44394 0
17 -636.2304688 -936.5234375 -862.3046875 242.1875 0 1739688027.44874 0
17 321.2890625 -769.0429688 -763.671875 -1000 0 1739688027.45354 0
17 -59.5703125 -805.6640625 -835.4492188 -407.7148438 0 1739688027.45835 0
17 -1000 -979.9804688 -931.1523438 999.5117188 0 1739688027.46315 0
17 -635.2539063 -963.8671875 -836.9140625 511.71875 0 1739688027.46795 0
17 -486.8164063 -818.8476563 -887.2070313 -1000 0 1739688027.47275 0
17 -990.7226563 -813.4765625 -1000 -891.1132813 0 1739688027.47756 0
18 -979.9804688 -954.1015625 -948.7304688 675.78125 0 1739688027.48136 0
18 -724.1210938 -985.3515625 -816.40625 956.5429688 0 1739688027.48517 0
18 -869.140625 -876.4648438 -904.296875 -541.9921875 0 1739688027.48897 0

이렇게 할 수 있다!

표 맞추기가.. 컴퓨터로 보면 그나마 보이기는 하는데.. 모바일에서는 잘 안 보일 수 있으니.. 웬만하면 데스크톱으로 보는 것을 추천!

 

아무튼 이제 표를 보면 대충 이해가 될 것이다. 참고로 'eeg_channels': [1, 2, 3, 4], 'eeg_names': 'TP9, AF7, AF8, TP10' 이것이 1, 2, 3, 4로 정의되어 있지만 각각은 eeg_names도 의미하기 때문에 저걸로 대입한 것이다.

 

이제 이름과 매칭해서 보면 이해하기 쉬울 것이다. 

  • package_num_channel : 지금 보고 이해했는데.. 패킷 번호 같다. 데이터 받아올 때 번호 같은 거?
  • TP9, AF7, AF8, TP10 : 전극에서 측정된 데이터.. (뇌파)
  • other_channels : 잘은 모르겠지만.. 다른 기기에서 쓰일지도..
  • timestamp_channel : 유닉스 시간으로 몇 초에 측정된 데이터인지
  • marker_channel : 이것도 잘은 모르겠다. 다른 기기에서 쓰일지도..

아무튼 내가 이해한 걸로 써보면 대충 이렇다. package_num_channel은 당시에는 딱히 필요 없는 것 같아서 뭔지 신경 쓰진 않았는데.. 패킷 번호인 것 같다.

 

아무튼 지금은 봐서도 뭐가 뭐인지 모르겠다.

나중에 이걸로 처리를 해보면 알 수 있게 되겠지만.. 일단 이렇다.

 

참고로 데이터에서 -1000으로 보이는 건 결측값이다. 즉 측정이 되지 않거나 이상하게 된 것..

왜냐하면 내가 빠르게 실행하려고 머리에 착용하지 않고 실행해서 측정한 것이기 때문..

 

그러면 한번 측정된 것을 그래프로 봐보자!!

 

그래프로 보기

이것이 그래프로 표현했을 때 이다!

색은 흰 배경에 할라 했는데.. 잘 안 보여서 내가 제일 좋아하는 드라큘라 테마로 설정했다.

참고로 이건 위에 예시로 올린 데이터는 아니다. 새로 측정한 것이기 때문에..

 

아무튼 저걸 보면 그래프가 들쑥날쑥하다.. 이유는 당연하게도 아까 말했던 것처럼 착용하지 않고 측정해서 그렇다.

 

그러면 한번 직접 써서 측정해 보겠다!

쓰는 방법은.. 전극은 이마 쪽으로 하고 귀에 거는 형태로 쓰면 된다.

어딘가 쇼핑몰에 있던 사진

위 사진처럼 써서 측정하면 된다.

쓸 때 팁은 최대한 길게 뽑은 뒤에 귀에 걸고 앞부분을 이마에 최대한 밀착하게 누르면서 다시 길게 뽑은 걸 줄이면 된다.

참고로 이마를 까고 해야 해서.. 좀 쓴 모습을 들키기는 싫다;;

 

아무튼 착용하고 측정해 보았다!

이제 이것을 보면 알 수 있다! 이제 결측값이 없기 때문에 제대로 데이터가 어떻게 분포하고 있는지 알 수 있다!

아무튼 데이터가 이런 식으로 보이고 한다!

그래프 그리는 건 귀찮기 때문에 ChatGPT 한테 부탁했다! 그렇기에 그래프 그리는 코드는 딱히 공유 안 하겠다!

 

암튼 각각의 뇌 부위가 어떤 값인지 알 수가 있는데.. 각각이 다르게 측정된다는 게 신기하긴 하다..

이것을 이용해서 이후에는 델타 베타 알파 이런 값으로 바꾸고 집중도나 이런 것도 측정해 보면 더 신기해진다!!

 

암튼 이로써 코드를 작성해서 Brainflow랑 Muse 2를 연결해 보고 데이터 저장은 끝났다!

거의 5시간 이상 째 쓰고 있는데.. 하아 역시 힘들긴 하다.. 그래도 글 연습 하는 겸 쓰는 것이기도 하니 재밌을지도..?

 

그리고 아래는 위에 코드 설명에서 왜 1초를 더 추가 측정하는지에 대한 것이다.

 

데이터를 1초 더 측정하는 이유

위에서 말했던 2초를 측정하는 이유와 이전 글의 Muse 2에 대한 정보를 말할 때도 한번 말했던 것이다.

 

Muse 2의 기기 정보를 보면 초당 256개의 데이터가 측정된다고 작성이 되어있는데..

실제로 측정을 해보면 초당 256개가 모이지 않는 것을 발견할 수 있다.. 아닐 수도 있지만;;

 

아무튼 이런 이유를 생각해 보면 블루투스로 연결하다 보니 발생하는 것 같다.

관련 글은 Muse 2016에 대한 얘기지만 Muse 2와도 관련이 있는 것 같기 때문에 같이 첨부!

아래 사이트에 적힌 말처럼 블루투스로 데이터를 보내면서 손실들이 발생하는 것이 아닐까 생각된다..

https://mind-monitor.com/forums/viewtopic.php?t=1447

 

Muse 2016 Sampling Rate - Mind Monitor

Mindman Posts: 2 Joined: Sun Jul 12, 2020 12:50 am Post by Mindman » Sun Jul 12, 2020 12:54 am I've been doing some data analysis on the mind monitor data from a 2016 Muse. The lengths of my sessions are 25 minutes and the 2016 Muse sampling rate is suppo

mind-monitor.com

 

참고로 데이터를 딱 1초만 수집되게 해서 착용하고 측정해 보면..

빨간색이 5번째 그래프에서 진해지는건 겹친 것인건 비밀!

이런 식으로 측정되는 것을 볼 수 있다.. (참고로 착용하고 측정한 건데 착용하지 않아도 빨간색으로 측정이 안된 건 동일하다.)

빨간색이 결측값에 해당한다. 전극이 닿지 않아 측정되지 않은 값이 아닌, 이건 데이터가 없어서 빨간색으로 뜨는 것이다.

0 값에 위치하게 한건 그냥 보기 쉽게 하려고이다.

 

아무튼 딱 1초만 측정하게 되면 연결되는 시간이나 BLE로 보내다 보니 값이 조금씩 비어서 저렇게 나오는 것 같다.

아무튼 그런 이유로 1초를 추가로 더 측정하는 것이다.

 

아무튼 이번 글은 여기에서 끝!

(다음은 뭔 글로 써야 하지.. 얼마나 더 길까..)

 

자랑

추가로 하나 자랑하자면..

{'eeg_channels': [1, 2, 3, 4], 'eeg_names': 'TP9,AF7,AF8,TP10', 'marker_channel': 7, 'name': 'Muse2', 'num_rows': 8, 'other_channels': [5], 'package_num_channel': 0, 'sampling_rate': 256, 'timestamp_channel': 6}

여기에서 원래 EEG 이름이 'eeg_names': 'TP9, Fp1, Fp2, TP10' 이걸로 설정되어 있었다..

저 설정은 잘못된 정보이다.. Muse 2를 찾아봐도 알 수 있듯이 Fp1, Fp2에 전극이 있지가 않다는 걸 알 수 있다.

그래서 좀 더 찾아보니.. 코드 다른 곳에선 AF7, AF8이 정확하게 적혀있는데.. 데이터를 출력하는 코드에서만 잘못되어 있었다..

 

그래서 내가 직접 코드를 수정해서 맞는 전극 위치로 수정한 것이 AF7, AF8이다.. 처음으로 대형 프로젝트에서 나도 코드 수정하는데 기여했다! 굉장히 뿌듯!

아무튼.. 당시에는 연구과제를 하며.. 나중에도 쓰겠지만.. 수업 자료를 만들고 있었다. 그때 이상하다는 것을 발견하고 수정 요청을 했는데.. 버전은 한 달 뒤에 바뀌어서 바로 수정은 되지 않았지만.. 아무튼 이것이 내 자랑!

https://github.com/brainflow-dev/brainflow/pull/744

 

Fix EEG names for Muse 2, Muse S, Muse 2016 by cheongpark · Pull Request #744 · brainflow-dev/brainflow

Description of Changes I noticed that the eeg_names for the Muse 2 I am using were incorrect. Upon checking, it seems that the names have changed. Original: TP9, Fp1, Fp2, TP10 Updated: TP9, AF7, A...

github.com

이것이 그때 수정했었을 때 수정 요청 날린 거!

 

https://github.com/brainflow-dev/brainflow/pull/745

 

Fix UTF-8 encoding problem by cheongpark · Pull Request #745 · brainflow-dev/brainflow

Traceback (most recent call last): File "C:\Users\cheongpark\brainflow\tools\build.py", line 309, in <module> main() File "C:\Users\cheongpark\brainflow\tools\build.py", l...

github.com

추가로 이건 이 글과는 상관없지만.. C++에서 빌드할 때 한국어나 일본어등 유니코드를 사용하는 윈도우 컴퓨터에서 빌드할 시 생기는 오류를 해결하기 위한 간단한 코드 한 줄 추가한 커밋이다.. 당시 올릴 때 이게 맞나? 올려도 되나? 여러 검증을 하면서 떨리기는 했었는데.. 올리고 나니.. 굉장히 뿌듯했다..!!

 

이거 작성한 2일 후에 생각나서 추가로 작성하는 거지만..

Muse 2는 이렇게 생겼다.

참고로 저거 길이 조정 가능한데 저건 내가 착용할 때 맞춰놓은 길이이고.. (좀 더 길게 할 수 있다.)

길이를 제일 줄이면 이 정도.. (포장은 저 줄여진 상태로 온다.)

다이소에서 산 책상 패드.. 다한증인 나에겐 최고의 패드.. 참고로 키보드는 큰맘 먹고 지른 AK74

 

반응형
반응형

이전 글에 이어서 작성하는 글이다.

 

이번엔 Brainflow를 Python에 설치하는 방법에 대한 얘기다!

일단 Brainflow는 Python, C++, Java, C#, Julia, Matlab, R, Typescript, Rust 언어를 지원하고 있는데..

여기에서 내가 연구원에서 할 때 쓴 언어가 C++과 Python이여서.. C++은 저번에 썼고.. 이번엔 Python을 써볼 것이다..!

 

C++ 설치 방법은 아래를 참고하면 될 것 같다.

https://cheongpark.tistory.com/66

 

BrainFlow C++ 설치방법 (Muse 2, Muse S 지원 설치 | CMake 사용)

학생 연구원에서 연구과제로 Muse 2를 받게 되었다.그런데 기존에 다른 분이 조사하신건 Python으로 Muse LSL를 설치해서 하는 방법인데, 이 방법은 Python으로만 할 수 있는걸로 알고 있는데 프로젝트

cheongpark.tistory.com

 

Muse 2 연결 방식에 대한 것

위에 C++ 설치 방법에도 올렸었던 내용이다.

Muse 2의 경우 이전 글에서 말했듯이 일반 블루투스가 아닌 저전력 블루투스인 BLE를 사용하고 있다.

하지만 이 BLE의 경우 Muse 2를 처음 지원할 때 Brainflow에서는 추가적인 BLE 동글이 필요했었다.

https://brainflow.org/2021-06-22-muse-bled/

 

BrainFlow 4.3.0

Muse devices added to BrainFlow

brainflow.org

저 글을 살펴보면 BLED112라는 동글이 추가적으로 필요하다고 나와있다.. 이 동글을 컴퓨터에 꽃아서 Muse 2랑 Brainflow랑 연결할 수 있었는데.. 처음에 Muse 2를 검색하면서 저 글을 보았을 때.. 진짜 동글이 필요한가라고 생각했었다.. 그런데 2차 회의가 며칠 안 남아서 동글을 살 시간도 없고.. 구매하기도 어려웠어서.. 조금만 더 찾아봤었다.. 당시에는 C++를 먼저 사용해서 연결해 보려고 했기 때문에 BLE 옵션이 있다는 것도 이후에 알았었다..

그러다가 https://brainflow.org/2021-11-01-new-release/

 

BrainFlow 4.7.0

Built-in BLE library, Muse devices wo dongles

brainflow.org

여기에 적힌 대로 이후에는 BLED112라는 동글 없이.. Simple BLE 라이브러리를 사용하여 컴퓨터에서 일반 블루투스 기기도 연결할 수 있다면 연결이 가능하도록 지원이 추가된 걸 발견했다!!

그래서 이후에는 이거를 어떻게든 설치를 시도했던 게.. C++ 설치 방법에 나온 글이다.. 뭐 그걸 쓴 후에는 툴을 만들러 갔었지만.. 

암튼 당시에는 C++로 처음 해보려다 보니.. 설치가 어렵고 BLE도 제대로 지원하는지도 몰랐었다.. 

Brainflow Python 설치

C++과 다르게 Brainflow Python 버전은 설치가 매우 매우 쉽다.

이미 C++로 빌드한 것을 Python에 이식하는 것뿐이니 당연한 거긴 하겠지만;;

 

Python 이외의 언어들을 설치하는 방법에 대해 보려면 아래 사이트에 가서 선택해 보면 된다..!

https://brainflow.org/get_started/

 

Get Started

Supported Boards Indicate your device. Then, check supported OSes and Platforms for your board. Manufacturer BrainFlow BrainFlow AntNeuro BrainAlive EmotiBit Enophone G.Tec Mentalab Muse NeuroMD Neurosity OpenBCI OYMotion PiEEG Board Playback Board Streami

brainflow.org

 

참고로 Python의 경우 이 옵션을 사용하면 된다.

https://brainflow.org/get_started/?manufactorer=Muse&platform=windows&language=python&environ=pip&board=muse2&

 

Get Started

Supported Boards Indicate your device. Then, check supported OSes and Platforms for your board. Manufacturer BrainFlow BrainFlow AntNeuro BrainAlive EmotiBit Enophone G.Tec Mentalab Muse NeuroMD Neurosity OpenBCI OYMotion PiEEG Board Playback Board Streami

brainflow.org

 

아무튼 저거를 봐도 되고.. 이걸 봐도 된다..!

 

일단 기본적으로 Brainflow는 Python 3.5.6 이상 버전부터 지원하고 있다.

굉장히 범위가 높아서 여러 프로젝트에서 이용하기가 쉽다..!

 

암튼 설치 방법!

일단 나는 관리가 편하도록 Anaconda로 가상환경을 만들 것이다..! 참고로 윈도우에서 진행한다. 다른 운영체제도 비슷하긴 하다.

conda create -n blogEEG python=3.10 -y
conda activate blogEEG

뭐 가상환경 만드는 건 쉬우니 넘어가지만.. 일단 내가 Python 3.10을 선택한 건 내가 프로젝트를 만들 때 3.10을 가장 많이 써서이다..

그리고 blogEEG라는 이름은 그냥 블로그 용으로 쓰는 거라서 그렇게 이름을 지었다.

참고로 conda activate는 사용하는 커맨드 창에서 가상환경 활성화 시키는 거다..

 

암튼 Brainflow를 설치를 한다!

pip install brainflow

 

이게 끝이다..

 

C++ 보다 훨씬 간단하다..

 

사용방법

이후 글부터는 연결 방법이나 테스트 코드들을 작성할 건데

뭐.. 공식 문서는 아래 링크를 보면 된다.

 

https://brainflow.readthedocs.io/en/stable/UserAPI.html#python-api-reference

https://brainflow.readthedocs.io/en/stable/Examples.html#python

 

참고로 가끔 구글 검색하다 보면..

https://brainflow.readthedocs.io/en/master/Examples.html

이런 링크가 하나씩 보일 텐데.. master 저 문서는 오래된 문서이다..

처음에는 구글에 검색하는 거 아무거나 따라 했다가.. 왜 안 되지?라고 계속 생각했었는데..

 

결국 링크가 다른 거였던 것..

만약 구글에 검색해서 볼 때는 링크가 stable인지 master인지 살펴보는 게 좋을 것 같다..

가끔 테스트 코드들이 작동하지 않거나 이상하게 작동할 때가 많다..

반응형
반응형

대학교에 입학하고 2주 째에 어느 교수님께서.. 나에게 학생 연구원에 들어올 생각이 없는지 물었었다.

그러고 5월 부터 학생 연구원을 시작했는데, 당시에 EEG를 사용한 연구 때문에 EEG 기기를 찾아봐야 했었다.

 

그때 나는 Muse 2도 찾아보기는 했었지만.. 개발자 용으로 사용되기 보다는 일반적인 용도로 사용되는 경우가 많고 약간의 문제점이 검색됬었어서.. Muse 2 기기는 선정을 안했었다.. 그런데 나중에 회의에서 찾아본 EEG 기기에 대해서 말했는데.. Muse 2 기기는 자세하게는 말하지 않았지만.. 이미 교수님은 회의 전에 Muse 2 기기를 배송하셨던 것 같다.. 그래도 EEG 기기에 대해서 알게되었으니 다행이었지만..

솔직히 가격도 싸고 배송도 쉽긴 하다..

 

암튼 연구원에서 Muse 2를 사용해보는 것에 대해 해봤던 대부분을 써볼려고 한다..!

(그리고 지금 이 글을 쓰는 시점이 2025년 2월 16일인데.. 다시 교수님께 이 기기를 돌려줘야 해서.. 2025년 2월 28일이 되기 전에는 빨리 작성해야한다.. 뭐.. 못 쓰면.. 그냥 내가 따로 저장해둔 정보로 쓰거나.. 그만쓰게 될지도 모른다..)

(대부분의 글은 전부 나 혼자서.. 찾은 정보나 방법들이다.. EEG 쪽은 거의 내가 담당했기 때문에..)

 

EEG

EEG(Electroencephalography, 뇌전도)는 전극을 통해 뇌의 전기적 활동을 기록하는 전기생리학적 측정 방법이라고 위키백과에 나와있는데..

그냥 내가 이해한걸로는 머리에 전극을 부착시켜 신호를 측정하는 것으로 알고 있다. 그냥 머리에 장치를 부착시켜서 뇌의 전기적 신호를 읽는다 생각하면 될 것 같다..

 

Muse 2

그 중 Muse 2는 EEG 방식 기기중 하나인데..

Muse 라는 회사에서 제작한 Muse 2라는 EEG 기기이다.

정보

  • 가격 : 249.99$
  • EEG 채널 수 : 4개
  • 전극 위치 : TP9(귀쪽), AF7(이마쪽), AF8(이마쪽) , TP10(귀쪽)
  • 샘플링 속도 : 256hz
  • 연결 방식 : BLE(Bluetooth Low Energy) 4.2
  • 배터리 : 리튬 이온 배터리 (충전시간 : 3시간, 사용시간 : 5시간)
  • 무게 : 38.5g
  • 추가 센서 : 3축 가속도 센서, PPG(맥박 측정 센서)

쓸만할 것 같은 정보가 이정도다..

더 많긴 하지만.. 추가적인 정보는 https://intl.choosemuse.com/products/muse-2 여기 사이트 맨 아래를 보면 나올것이다.

 

추가적인 정보

추가적으로 내가 사용하면서 신기하던 것이나 말해야할 것 같은 정보는..

일단 이 기기는 블루투스 방식이다. 하지만 일반적인 블루투스는 아니고 저전력 블루투스인 BLE이다.

그렇기에 연결하는데 일반적인 방법으로는 연결하기 어렵고 특정 방법으로 연결해서 데이터를 가져와야 한다.

잘은 모르겠지만.. 내가 사용했을 때는 연결이 가끔 끊기기도 하고 연결 거리가 일반적인 BLE 4.2와 달리 좀 짧은 것 같다..

(주변에 다른 기기가 많아서 일지도..)

 

그리고 조금 특이한건 전극인데..

Muse 2를 보면 바로 보이는 전극은 밴드 앞쪽에 금박으로 되어있다. 

방금 찍은거

이런식으로 금박으로 되어있는데 오른쪽에 이상하게 빨간색 2개 led 나올 것 같은 센서는 추가 센서이다. (참고로 약간 2겹으로 이상하게 튀어나온건 그냥 이마에 잘 붙게 할려는 용도 같다.. 불량 아님..)

여기에서 특이한건 5개 금박인데.. 나는 처음에는 4개 전극이라면서 왜 5개가 있지? 라는 생각을 했다..

그래서 찾아보니 특이한 것이 있는데..

중간 3개 금박은 그냥 보정용? 같은 느낌으로 사용되고 나머지 2개가 이마쪽 센서이다.

그럼 나머지 2개는 어디있냐..! 하면 귀쪽에 있다.

방금 찍은거

고무같은 이것이.. 나머지 2개 전극이다.. 왼쪽 오른쪽에 각각으로.. 전도성 실리콘이다..

이것이 귀 뒤쪽에 붙어서 신호를 측정한다. Muse 2를 사용하면서 제일 신기했던 것이 이것인 것 같다..

 

그리고 참고로 실리콘은 문제가 안되는데.. 금박으로 된 전극은 문제가 있다.. 저것을 손으로 만지거나 해서 땀이나 이런게 묻어버리면 전극에 이상한 녹 슨 것 같은게 보인다.. 그리고 이 전극을 닦아보면 초록색 같은게 묻어버린다..

아마 녹 같은게 쓸은 느낌인데.. 좀.. 이건 문제가 있어보이긴 한다..

참고로 제일 중요할 수도 있는데.. 이 전극이 이마에 닿았을 때.. 오래 끼거나 민감하면 전기가 통하여 찌릿찌릿한 느낌이 들 수도 있다.. 나도 오래 끼거나 하면 느끼고 다른 친구들에게도 착용시켜보았는데 대부분 느끼긴 한다.. 조금 불편할 수도 있을 것 같긴 하다..

 

그리고 나중에 말할지는 모르겠지만.. 샘플링 속도가 256hz 라고 적혀있다만 항상 256hz로 데이터가 들어오진 않는 것 같다.. BLE 통신 때문인건지는 모르겠지만 몇몇 데이터가 손실날 때가 있는 것 같다.

참고로 내가 쓸지는 모르겠지만.. 추가 센서는 사용해본적이 없다.. (솔직히 지금 찾아보면서 알아버린..) 그래서 블로그에는 아마 전극 측정한 것만 쓸 것 같긴 한데.. 시간 되면 써볼 것 같다.

 

참고로 머리 크기에 맞춰서 길이를 늘릴 수도 있다. 내 머리가 꽤 많이 큰 편인데.. 내 머리에도 딱 맞다.

 

Python으로 연결할 수  있는 라이브러리

Muse 2를 연결할 수 있는건 일반 사용자들은 그냥 Muse 앱 설치해서 핸드폰하고 연결해서 측정하면 된다.

하지만 나는 연구용으로 뇌의 신호를 알아야 하기 때문에 데이터를 직접 가져와야 한다..

그러기 위해선 이것을 컴퓨터와 연결해서 데이터를 얻어야 하는데 라이브러리가 별로 없다..

 

나는 EEG 기기 구매할 것 정보 검색할 때 Brainflow에 대해서 찾았었는데.. 다른 같이 하는 연구원 분이 했었던건 Muse LSL에 대해서도 말했었다.

 

Brainflow : https://github.com/brainflow-dev/brainflow

 

GitHub - brainflow-dev/brainflow: BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of

BrainFlow is a library intended to obtain, parse and analyze EEG, EMG, ECG and other kinds of data from biosensors - brainflow-dev/brainflow

github.com

 

Muse LSL : https://github.com/alexandrebarachant/muse-lsl

 

GitHub - alexandrebarachant/muse-lsl: Python script to stream EEG data from the muse 2016 headset

Python script to stream EEG data from the muse 2016 headset - alexandrebarachant/muse-lsl

github.com

 

아무튼 나는 Muse LSL도 시도해보긴 했지만.. Muse LSL의 경우 추가적은 프로그램을 설치해야하고 연결이 어렵기도 해서 딱히 추천하지는 않는다.. 추가적으로 처리할 수 있는 기능들이 별로 없기도 하다..

 

그래서 내가 추천하는건 Brainflow이다..!

Brainflow는 여러 EEG 기기들을 대부분 연결할 수 있도록 지원하고 있고 처리할 수 있는 추가적인 기능들이 매우 많다..

예를 들면 단순한 인공지능으로 집중도를 분석하거나 편안함을 분석할 수도 있고.. 원하는 데이터를 추출하거나 하는 기능들이 매우 잘 되어있다.

그래서 나는 Brainflow로 설명할 것이다..!

 

그리고 자랑이긴 하지만.. Brainflow에 Muse 기기들의 전극 위치가 잘못 표시 되어있고 C++로 빌드할 때 UTF-8로 빌드가 안되어서 문제가 발생하는 것들이 있어 이 문제를 해결하기 위해 Github로 기여해서 수정한적도 있다..! ㅎㅎ

 

참고로 나는 Python을 사용해서 글을 쓸 것인데.. C++으로도 사용할 수 있다.

다만 C++의 경우 설치가 조금 빡셀 수도 있다. 설치 방법은 내가 전에 올려둔 블로그를 확인하면 된다..!

https://cheongpark.tistory.com/66

 

BrainFlow C++ 설치방법 (Muse 2, Muse S 지원 설치 | CMake 사용)

학생 연구원에서 연구과제로 Muse 2를 받게 되었다.그런데 기존에 다른 분이 조사하신건 Python으로 Muse LSL를 설치해서 하는 방법인데, 이 방법은 Python으로만 할 수 있는걸로 알고 있는데 프로젝트

cheongpark.tistory.com

 

그리고 이걸 하면서 일어났던 재밌었던 일들도 있는데.. 나중에 적어보겠다..!

 

암튼 이번껀 끝!

반응형
반응형

이번엔 22권이다!

 

내가 이전에 업로드한 21권은 9월 4일에 올렸는데.. 그동안 업로드 못한 이유는.. 학생 연구원에서 맡은 일을 하거나 과제들을 하면서 다른 일들을 다 미루다보니.. 적지 못했다.. 현재까지는 로봇을 50권 정도까지 조립하고.. 작성하고 있다..

 

아무튼 이번 책 내용은!

첫번째 이미지는 축구 로봇에 대한 이야기이다..!

개인적으로 축구는 않 좋아하지만.. 로봇 축구는 좋아한다..!

저것을 보고 생각난 영상이 있다.. https://youtu.be/gFjAltC2pj0?si=CRwMzbqRtKad_LUg 이 영상.. 2050년까지.. 인간과 로봇이 대결할 수 있도록 로봇을 만드는 것.. 에 대한 내용이다.

 

아무튼 저기에는 Nao Robot이 나오고 있는데.. Nao Robot을 내가 처음으로 알게된건.. 2012년 여수 엑스포였다. 그 때 당시에는 시간 때문에 로봇관이 종료되던 것을.. 다른 사람들이 따지거나 우리 아빠도 따져서 겨우 들어갔던건데.. 그러다 보니.. 그 때는 Nao Robot을 보여주지는 않았다. 가만히 앉아있는건 보였던 것 같은데.. 뭐 그래도 이후에 다시 여수 엑스포를 찾아보면서 알게 되긴 했는데.. 개인적으로 내가 제일 좋아하는 로봇 중에 하나이다. 프랑스의 알데바란에서 제작하고 현재는 일본 소프트 뱅크로 인수 되면서.. 아직까지도 판매하고 있는 Nao Robot.. 2000만원이지만.. 언젠가는 사보고 싶다.

 

그런데 한가지 별로였던 건.. 도데체 왜 저 책에는 Darwin OP가 안나왔던 것인가이다.. 현재 내가 제일 좋아하는 로봇인데..!!

뭐 다른 나라 로봇이라서 안나온 것도 있겠지만.. Darwin OP도 우승을 많이 한걸로 알고 있는데.. 그런 귀여운 로봇이 안나왔다는게.. 조금 아쉽긴 하다.. 내가 Darwin OP를 처음에 본 것도 여수 엑스포인데.. 거기에서 축구를 하던 것이.. 아직도 기억에 남는다.. 내가 분명 7살일텐데.. 어떻게 그걸 기억하고 있는거지..

그리고 내가 어릴 때 로봇을 판매하던 사이트에.. Darwin OP 2가 2000만원에 판매하던걸 본적이 있다.

그 때는 돈에 감각이 없어서 어른 되면 사야지 했는데.. 지금은 단종되기도 했고.. 그 돈이 얼마나 큰 돈인지도 알게 되었다..

아무튼 Darwin OP 로봇을 계기로 데니스 홍에 대해서도 알게 되고.. 얼마 전에는 안될과학 영상에서 Darwin OP가 오픈소스라는 것도 알게 되었는데.. 이것 때문에 무조건 군대를 가기전이든 가고난 후든 만들고 싶다고 생각했다.

 

그래서 예전에 구상한게.. Darwin OP 2에 들어가는 모터는.. MX 28T로 개당 26만원 하던데.. 이거를 우리 집에 있는 AX 12A 모터로 바꿔서 제작하면 되지 않을까 라는 생각을 했었다. 다만 문제가 몇가지 있어서.. 현재는 미루고 있는데..

첫번째는 내가 Fusion 360 같은 CAD를 못 만진다는 것과.. 두번째는 Darwin OP 2의 메인보드나 관련 부품들은 더 이상 판매하지 않는 다는 점이다.. 물론 대체 부품은 있지만.. 이런 점 때문에.. 제작은 더욱 어려워질 것이라고 생각했다.

그래서 데니스 홍 페이스북을 살펴보다가 Robex에 데니스 홍의 RoMeLa 연구소 로봇들이 온다고 해서.. 경기도 일산에서 바로 부모님과 2024년 10월 26일에 운전해서 간적이 있었는데.. 시간이 거의 끝나기 전이라서 로봇은 많이 없었지만.. 내가 그 토록 보고 싶던 Darwin OP 2가 전시되어 있었다..!! (그 전에 실제로 봤던 로봇은 Darwin OP.. 밖에.. 없던..)

아무튼 사실 데니스 홍과도 실제로 얘기하면서 메인보드는 어떻게 하면 구할 수 있을까요? 라는 것과 AX 12A로 만들어보고 싶은데 몇가지 문제가 있을까요? 라는 질문 등을 하고 싶었는데.. 내가 사람들과 얘기를 잘 못한다는 것을 잊고 있었다.. 끝날 때 되더니 사람들은 다 가고 정리하고 있었는데.. 그 때 솔직히 가서.. 물어보고 싶었는데.. 결국 못 물어보고 나왔다.. 언제 다시 볼 수 있을지도 모르는데.. 지금 생각하면.. 왜 그냥 나왔을까.. 라는 생각만 든다.. 

내가 어릴 때 부터 사람들을 도와줄 수 있는 로봇을 제작하자 라는 마음을 먹고 지금까지 유지하고 있는데 그런 로봇을 만들기 위해선 RoMeLa 연구소를 가는 것을 1순위 목표로 하고 싶은데.. 결국 내 성적이나.. 관련된 문제로.. 포기를 해야하나..라는 생각 밖에 들지 않기는 하다.. 

아무튼.. 지금 생각하면 왜 말을 걸지 않고.. 그냥 나왔는지.. 후회만 된다..

뭐.. 그래도 언젠가는.. Darwin OP 2를 만들고 싶기에 어떤 구조인지 파악하기 위해서 사진도 찍긴 했었다..!

MX 28T와 AX 12A의 모터 사이즈가 달르고 힘이나 성능이 달라서 솔직히 만들 수 있을지는 모르겠지만.. 최대한 빠른 시일 내로 CAD 쓰는 방법을 독학해서 라도 AX 12A를 MX 28T 크기와 동일하기 쓸 수 있는 케이스를 만들어서 3D Printer으로 저렴하게 제작해보고 싶다..!!

 

아무튼 첫번째 사진에 대해서 너무.. 많이 쓴 것 같지만.. 암튼..

두번째 사진은 로봇 디자인에 관련된 얘기인데.. 로봇을 만들기 위해서는 어떤 디자인이 필요한지에 대한 인터뷰이다.

로봇을 디자인할 때는 단순히 멋진 디자인이 아닌 기능성과 균형이 중요하다는 얘기와 애니메이션 로봇과 현실 로봇의 차이에 대해서 말하고 있다. 또한 메카닉 디자인에선 로봇의 관절과 동작이 자연스럽게 보이도록 제작해야한다고 하는데.. 지금 봐서 알았지만.. 내가 좋아하는 로봇의 디자인 이름이 메카닉 디자인인걸.. 처음 알았다..

Sony의 Aibo는 뭔가 진짜 같은 느낌이 들도록 디자인 한 것 같은데.. 내가 좋아하는 로봇을 생각해보면.. 다윈 OP 로봇, 나오 로봇, 메로 로봇, 제니보 로봇 등 전부 메카닉 디자인인 것 같다.. 

아무튼 난 단순하게 생기거나 제니보나 Darwin OP 같은 로봇들이 귀엽고 신기해서 역시 좋은 것 같다..

아무튼 이번 책 내용도 이전에 올린 14권과 방향만 다를 뿐 거의 같은 내용이다. 그렇기 때문에.. 제작하는 것에 대한건 패스!

 

이렇게 조립하여.. 왼쪽 팔과 오른쪽 팔 모두 완성했다!!

그리고 임시로 겹쳐보았는데..!

 

팔만 봐도 신기하고 뭔가 귀엽다!!

 

암튼 22권 조립은 끝!!

 

참고로.. 지금까지 쌓은 박스가..

이정도다..

 

암튼 로봇 만들고 싶고.. RoMeLa 연구소..에 가서 데니스 홍과 로봇에 대해서 얘기를 해보고 싶다..

반응형
반응형

NodeJS를 쓰면서 코드를 다 만들고 실행을 해보았다.

그런데 실행이 전혀 안되는.. 그래서 간단하게 console.log로 출력되게 해서 간단한 출력도 시도 했는데.. 안됬다..

 

그러다가 설마 경로에 한국어가 들어가서 인가 하고 한국어가 없는 경로에 넣었더니.. 정상적으로 실행된다..

검색해도 안나오던데..

 

아무튼 실행해도 오류나 어떠한 것도 안뜨면 경로에 영어 외의 문자가 있는지 확인하고 변경하기

반응형

+ Recent posts