반응형

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

 

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

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

 

시작

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

 

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

 

암튼 이번껀 끝!

반응형
반응형

내가 대학교에 입학한 후 1학년인데 학생 연구원을 해볼 생각이 없는지 물어보셨었다.

나는 좋다고 한 후 들어갔는데

교수님이 연구를 위해 Tobii Eye Tracker를 빌려주셔서 그 기회에 이 프로젝트를 해보았다.


눈 제어해보기 전에 이것을 만들기 이전에 어떤 과정이 있었는지 말하면..

 

일단 내가 받은 아이트래커는 

이거인데 Tobii Eye Tracker 5이다. 받고난 후 어떻게 할지 검색해보다가 알게 되었다..

내가 받은건 게임용이고 연구용이 아니기에 연구용 SDK가 없다는 것을..

 

아무리 검색해도 이 기기에서는 아이트래킹 정보를 얻을 수가 없었다.

https://developer.tobii.com/product-integration/stream-engine/

 

Stream Engine - Tobii Developer Zone

The Stream Engine SDK is a low-level SDK intended for advanced users wanting to have tight control over the system resources used by Stream Engine and least amount of signal latency. To get started with the StreamEngine SDK, head over to the getting starte

developer.tobii.com

그러다가 이 것이 나와서 찾아봤는데.. 이건 일단 되는 것이다!

게임용으로 지원하는 SDK이고 이 SDK를 사용해서 아이트래킹이 지원되는 게임을 만드는 것 같다.

 

그래서 이걸 쓰기위해 보니 메일을 보내서 요청하라고 적혀있다.. 그래서 보냈더니..

한국어로 답변이 왔는데.. 아이트래커에 해킹을 시도하거나 다르게 이용을 하는 경우가 있어서 SDK를 못 준다는 말만 왔었다.. 개인적으로 사용할 것이라고 얘기해도 불가능이라고 했다.

 

그래서 그냥 포기할까 하고 한가지 생각이 들었다. 이 회사는 유명하니깐 누군가가 Github에 올려놓지 않았을까 하고..

그래서 찾아보니 진짜로 있었다. 32비트, 64비트 모두 Github에 아이트래커 프로젝트를 올리면서 같이 올려진 것들이 있다.

그래서 나는 이걸 다운받아서 시도했더니 아이트래커에 접근이 됬었던 것!

 

(근데 한가지 슬픈건.. 이 SDK가 C++과 C#만 가능한데.. 연구원에서.. 교수님과 나만 C++이 가능했던 것.. 그래서 이 연구와 관련된 프로그램을 제작하는건 모두 내가 맡아서 했었다.. 파이썬으로 실행할 수 있는 것이 github에 있던데.. 더 별로여 보여서 그냥 C++로 하기로 했다..)

 

https://developer.tobii.com/product-integration/stream-engine/tutorial_cplusplus/

 

Stream Engine Tutorial - C++ - Tobii Developer Zone

This policy applies to solutions that store or transfer Tobii eye tracking data, presence or position data, regardless of the purpose for the storing or transferring. End-users care about their data integrity and privacy. Therefore, as a vendor you must ga

developer.tobii.com

아무튼 예제 코드는 여기에 있다. C++ 프로젝트로 사용하는 방법도 나와있고..

뭐 일단 이제 VRChat과 관련된 얘기를 해보겠다!

일단 VRChat에는 OSC 라는 기능이 있다.

https://docs.vrchat.com/docs/osc-overview

 

OSC Overview

Intro to OSC OSC is a way to get different devices and applications to talk to each other. It's a favorite method of creative coders and people making weird interactive things because it's fast, networked, and very open-ended. What does this have to do wit

docs.vrchat.com

여기에 자세히 나와있는데 OSC를 이용하면 VRChat에서 아바타나 VRChat 기능과 소통하면서 여러가지 기능들을 제어할 수가 있는 것 같다.

예전에는 이걸로 번역기도 만들어보긴 했는데.. 굉장히 간단하게 할수가 있었다.

 

아무튼 저 글에서 보면 

https://docs.vrchat.com/docs/osc-eye-tracking

 

OSC Eye Tracking

VRChat now offers support for receiving eye tracking data (eyelook and eyelid) via OSC. Please note: This is an advanced feature! It is NOT plug-and-play. You must create your own program to transmit this data to VRChat using OSC. Hardware manufacturers ma

docs.vrchat.com

 

 

아이트래킹을 할 수 있는 링크가 설명이 되어있는데 대충 이거다.

/tracking/eye/EyesClosedAmount

/tracking/eye/CenterPitchYaw
/tracking/eye/CenterPitchYawDist
/tracking/eye/CenterVec
/tracking/eye/CenterVecFull
/tracking/eye/LeftRightPitchYaw
/tracking/eye/LeftRightVec

(참고로 여기로 9000 포트와 함께 데이터를 보내면 OSC가 작동한다.)

 

일단 Stream Engine을 살펴보니 잘은 모르겠지만 내가 보고 있는 시선의 위치만 가져올 수 있는 것 같다. 뭐.. 게임 용이라 그런 것 같은데.. 아니면 내가 제대로 안 찾아본 것일 수도 있고..

 

아무튼 내가 보고 있는 위치만 가져올 수 있으니깐.. 사용할 수 있는 OSC는 "/tracking/eye/CenterPitchYaw" 이거다. 이거는 문서에 잘 설명 되어있다.

그리고 눈을 감았는지에 대한건 "/tracking/eye/EyesClosedAmount" 이걸 사용하면 될 것 같다.

 

아무튼 이걸 알았으니 이제 예제 코드를 실행해볼껀데, 그냥 실행하면 이런 오류가 난다. 아마도 내꺼가 Github에서 가져와서 버전이 낮은 것 때문에 그런 것 같은데..

// Connect to the first tracker found
tobii_device_t* device = NULL;
result = tobii_device_create(api, url, TOBII_FIELD_OF_USE_STORE_OR_TRANSFER_FALSE, &device);
assert(result == TOBII_ERROR_NO_ERROR);

여기 코드에서 TOBII_FIELD_OF_USE_STORE_OR_TRANSFER_FALSE 이게 오류가 난다.

 

그냥 해결 방법은 그냥 저거 빼고 

사진에 나온거 2개 중에 아무거나 넣으면 된다.

그리고

이런 오류 나면.. 뭐 다르게 해결하는 방법이 있지만 빠르게 테스트 하기 위해 맨 상단에 이걸 넣는다.

#define _CRT_SECURE_NO_WARNINGS

아무튼 이러면 오류가 사라진다.

 

이제 실행해보면 Tobii Eye Tracker이 정상적으로 연결 되어있는 경우 작동하는데 해보면 내가 보고 있는 시선이 x, y로 나올꺼다. 그런데 내가 눈을 감거나 감지할 수 없는 영역을 쳐다보면 아무것도 출력이 안되는데.. 이걸 알 수 있다는 것은 눈을 깜박이는 것을 알 수 있다는 것이다! 확실히 알기 위해 코드를 수정했다!

void gaze_point_callback(tobii_gaze_point_t const* gaze_point, void* /* user_data */)
{
    // Check that the data is valid before using it
    if (gaze_point->validity == TOBII_VALIDITY_VALID)
        printf("Gaze point: %f, %f\n",
            gaze_point->position_xy[0],
            gaze_point->position_xy[1]);
    else // 추가
        printf("Gaze Error\n"); // 추가
}

// 추가 라고 되어있는 것이 내가 추가한건데 그냥 인식 안되면 출력하라는거다..

이렇게 하면 정상적으로 눈을 깜으면 Gaze Error이 표시되고 감지 영역에서 눈을 움직이면 쳐다보는 곳이 잘 출력된다..!

 

그리고 한가지 알아둘 것이 있는데 출력되고 있는 것을 보면

Gaze point: 0.695525, 0.411705
Gaze point: 0.634280, 0.405759
Gaze point: 0.541244, 0.393794
Gaze point: 0.465725, 0.383487
Gaze point: 0.423483, 0.384978
Gaze point: 0.402698, 0.381798
Gaze point: 0.368750, 0.379669
Gaze point: 1.126799, 0.609565
Gaze point: 1.110942, 0.619803
Gaze point: 1.121417, 0.617664
Gaze point: 1.146164, 0.605248
Gaze point: 1.153657, 0.606785
Gaze point: 1.154399, 0.599591
Gaze point: 1.155324, 0.597877

 

 

이런게 있는데.. 이 숫자에 대한 것이다.

이 숫자는 그냥 x와 y의 시선 값인데 0부터 1 사이 값들은 모니터 안에 있는 것이고

1 이상들은 모니터 밖을 쳐다보고 있다는 것이다.

또한 모니터가 1920 * 1080 이면 각각에 x와 y 값을 곱해보면 내가 보고 있던 픽셀을 알수도 있다는 것이다.

 

이번에는 VRChat OSC에 정보를 보내는 방법에 대한 얘기인데

Python이라면 그냥 python-osc 이거 사용해서 로컬 9000 포트로 보내면 끝난다.

근데 C++이기에 OSC 라이브러리를 사용해야한다.

 

일단 난 그냥 간단하게 이걸 사용했다.

https://github.com/CINPLA/oscpack

 

GitHub - CINPLA/oscpack: Automatically exported from code.google.com/p/oscpack

Automatically exported from code.google.com/p/oscpack - CINPLA/oscpack

github.com

 

아무튼 바로 코드!

#include "tobii/tobii.h"
#include "tobii/tobii_streams.h"
#include <iostream>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <conio.h>
#include <Windows.h>
#include <typeinfo>
#include "osc/OscOutboundPacketStream.h"
#include "ip/UdpSocket.h"
#include <opencv2/opencv.hpp>

#pragma comment(lib, "Ws2_32.lib")

#define CanvasW 1920 / 4
#define CanvasH 1080 / 4

const char* ip = "127.0.0.1";
const int port = 9000;

float last_sent_value = -1.0f; // 이전에 보낸 값을 저장할 변수
bool test_mode = true; // 테스트 모드 설정

cv::Point2f current_point(0.0f, 0.0f); // 현재 점의 위치
cv::Point2f target_point(0.0f, 0.0f); // 목표 점의 위치

const float gaze_x_min = -25.0f;
const float gaze_x_max = 25.0f;
const float gaze_y_min = -15.0f;
const float gaze_y_max = 20.0f;

cv::Mat canvas(CanvasH, CanvasW, CV_8UC3, cv::Scalar(255, 255, 255)); // canvas 선언

void send_message(UdpTransmitSocket& transmitSocket, const char* address, float value) {
    if (value == last_sent_value) return; // 이전에 보낸 값과 같다면 보내지 않음

    char buffer[1024];
    osc::OutboundPacketStream p(buffer, 1024);

    p << osc::BeginMessage(address) << value << osc::EndMessage;
    transmitSocket.Send(p.Data(), p.Size());

    last_sent_value = value; // 보낸 값을 저장
}

void send_gaze_message(UdpTransmitSocket& transmitSocket, const char* address, float x, float y) {
    char buffer[1024];
    osc::OutboundPacketStream p(buffer, 1024);

    p << osc::BeginMessage(address) << x << y << osc::EndMessage;
    transmitSocket.Send(p.Data(), p.Size());
}

void gaze_point_callback(tobii_gaze_point_t const* gaze_point, void* user_data)
{
    UdpTransmitSocket* transmitSocket = static_cast<UdpTransmitSocket*>(user_data);
    if (gaze_point->validity == TOBII_VALIDITY_VALID) {
        printf("Gaze point: %f, %f\n",
            gaze_point->position_xy[0],
            gaze_point->position_xy[1]);

        float x = gaze_point->position_xy[0] * (gaze_x_max - gaze_x_min) + gaze_x_min;
        float y = gaze_point->position_xy[1] * (gaze_y_max - gaze_y_min) + gaze_y_min;

        send_gaze_message(*transmitSocket, "/tracking/eye/CenterPitchYaw", y, x);

        send_message(*transmitSocket, "/tracking/eye/EyesClosedAmount", 0.0f);

        if (test_mode) {
            target_point = cv::Point2f(gaze_point->position_xy[0] * CanvasW, gaze_point->position_xy[1] * CanvasH);
            current_point = target_point;
            cv::circle(canvas, current_point, 3, cv::Scalar(255, 0, 0), -1); // 파란색으로 변경
        }
    }
    else {
        std::cout << "Invalid" << std::endl;
        send_message(*transmitSocket, "/tracking/eye/EyesClosedAmount", 1.0f);

        if (test_mode) {
            current_point = target_point;
            cv::circle(canvas, current_point, 3, cv::Scalar(0, 0, 255), -1); // 빨간색으로 변경
        }
    }
}

void url_receiver(char const* url, void* user_data)
{
    char* buffer = (char*)user_data;
    if (*buffer != '\0') return; // only keep first value

    if (strlen(url) < 256)
        strcpy_s(buffer, 256, url); // strcpy_s로 변경
}

int main()
{
    // Create API
    tobii_api_t* api = NULL;
    tobii_error_t result = tobii_api_create(&api, NULL, NULL);
    assert(result == TOBII_ERROR_NO_ERROR);

    // Enumerate devices to find connected eye trackers, keep the first
    char url[256] = { 0 };
    result = tobii_enumerate_local_device_urls(api, url_receiver, url);
    assert(result == TOBII_ERROR_NO_ERROR);
    if (*url == '\0')
    {
        printf("Error: No device found\n");
        //return 1;
    }

    // Connect to the first tracker found
    tobii_device_t* device = NULL;
    result = tobii_device_create(api, url, TOBII_FIELD_OF_USE_ANALYTICAL, &device);
    std::cout << typeid(result).name() << std::endl;
    assert(result == TOBII_ERROR_NO_ERROR);

    // Subscribe to gaze data
    UdpTransmitSocket transmitSocket(IpEndpointName(ip, port));
    result = tobii_gaze_point_subscribe(device, gaze_point_callback, &transmitSocket);
    assert(result == TOBII_ERROR_NO_ERROR);

    // Initialize socket and server address

    while (true) {
        // Optionally block this thread until data is available. Especially useful if running in a separate thread.
        result = tobii_wait_for_callbacks(1, &device);
        assert(result == TOBII_ERROR_NO_ERROR || result == TOBII_ERROR_TIMED_OUT);

        // Process callbacks on this thread if data is available
        result = tobii_device_process_callbacks(device);
        assert(result == TOBII_ERROR_NO_ERROR);

        if (test_mode) {
            cv::imshow("Gaze Point", canvas);
            cv::waitKey(1);
            canvas.setTo(cv::Scalar(255, 255, 255));
        }

        if (_kbhit()) {
            int ch = _getch();
            if (ch == 27) // ESC Key
                break;
        }
    }

    // Cleanup
    result = tobii_gaze_point_unsubscribe(device);
    assert(result == TOBII_ERROR_NO_ERROR);
    result = tobii_device_destroy(device);
    assert(result == TOBII_ERROR_NO_ERROR);
    result = tobii_api_destroy(api);
    assert(result == TOBII_ERROR_NO_ERROR);
    return 0;
}

 

일단 이건 내가 그냥 간단하게 테스트만 해볼려고 만든 코드이다.

그래서 코드가 더러운 것도 있고 굳이 필요 없는 기능이 있기도 하다.

뭐 그래도 대충 이 코드를 실행하면 정상적으로 눈 제어가 가능한 아바타에서는 작동한다!!

 

그리고 코드에 보면 캔버스 설정한게 있는데 그냥 그건 OpenCV로 어디를 보고 있는지 표시하기 위해 놔둔 것이다. test_mode 끄면 나오지 않는다.

 

const float gaze_x_min = -25.0f;
const float gaze_x_max = 25.0f;
const float gaze_y_min = -15.0f;
const float gaze_y_max = 20.0f;

그리고 이런 코드가 있는데.. 이 코드는 VRChat OSC로 데이터를 보낼 때 데이터 범위이다.

각 아바타 마다 데이터의 범위가 정해져있는 것 같다. 일단 내가 사용하는 아바타는 일일이 값을 보내서 테스트 해본 결과 저정도 인 것 같다.

 

아무튼 이렇게 하면 눈 제어와 눈 깜박이는 것을 모두 아이트래커로 가능하다! 심지어 놀라운건 나는 아이트래킹은 VR 유저만 가능한 줄 알았는데 데스크탑에서도 가능하다는 것..

이게 테스트 영상!

 

아무튼 끝!

반응형
반응형

학생 연구원에서 연구과제로 Muse 2를 받게 되었다.

그런데 기존에 다른 분이 조사하신건 Python으로 Muse LSL를 설치해서 하는 방법인데, 이 방법은 Python으로만 할 수 있는걸로 알고 있는데 프로젝트가 C++로 해야하는데 이 언어도 지원 안하고 심지어 추가적인 BlueMuse인가 하는 것도 설치해야한다.

 

그렇기 때문에 나는 BrainFlow를 알게되서 이 방법을 사용하기로 했는데 굉장히 어려웠다.. 4시간을 이러고 있었으니.. 내일 수업인데도 새벽 3시까지..

 

(혹시나 몰라서 말하는거지만 아래 글에 있는 경로 그대로 붙여넣으면 안된다. pkdpm은 그냥 내 컴퓨터 이름이기 때문에 자신의 컴퓨터 이름으로 바꿔야한다.)

 

Muse 2, Muse S 블루투스에 대해서

일단 Muse의 경우 블루투스로 연결하게 된다. 여기에서 문제는 아마 이게 BLE 같은데.. 저전력 블루투스.. 그렇다 보니 연결도 BlueMuse 같은 방법으로 연결하는 듯 하다.. 

그런데 다행이도 BrainFlow에선 무언가 설치 안하고 바로 동작할 수 있게 되어있는데 기존에는 BLED112라는 제품을 사용해야했다. https://brainflow.org/2021-06-22-muse-bled/ 이게 처음 Muse 지원했을 때 글 같은데 BLED112라는게 필수적으로 필요했다. 

그런데 https://brainflow.org/2021-11-01-new-release/ 지원 4개월 후 쯤에 BLED112를 사용하지 않고도 가능하게 바뀌었다라는 소식이 있었다.

이거를 할려면 글에 나온 것 처럼 빌드 할 때 --ble를 추가하던지, CMake에서 DBUILD_BLE를 ON으로 해야하는 것 같다.

(참고로 일반적으로 블루투스 연결할려고 하면 전혀 목록에 뜨지 않는게 정상이다. 모바일은 뜨긴 하지만..)

 

아무튼 일단 Python으로 설치해보기! - (전 안되네요..)

일단 공식적으로 파이썬으로 빠르게 설치하는 방법이 있는데.. 이상하게도 기본 방법은 설치가 되는데 --ble 라는 파라미터를 추가하면 빌드가 이상하게 오류가 뜬다.. 왜인지는 잘 모르겠지만..

일단 관련 글은 여기에 있다.

https://brainflow.readthedocs.io/en/stable/BuildBrainFlow.html#compilation-of-core-module-and-c-binding

 

Installation Instructions — BrainFlow documentation

R R binding is based on reticulate package and calls Python , so you need to install Python binding first, make sure that reticulate uses correct virtual environment, after that you will be able to build R package from command line or using R Studio, insta

brainflow.readthedocs.io

git clone https://github.com/brainflow-dev/brainflow.git
python -m pip install cmake
cd brainflow/tools
python build.py --help

일단 위에 명령어 처럼 친다. 명령어는 원하는 폴더에서 치면 되는데 나는 그냥 Downloads 폴더에서 했다.

이렇게 하면 brainflow 깃허브 파일이 모두 다운로드 되고 cmake 라이브러리를 설치하고 brainflow의 tools 폴더로 이동해서 build.py의 도움 내용을 출력하는 것이다.

 

usage: build.py [-h] [--oymotion] [--no-oymotion] [--msvc-runtime {static,dynamic}] [--generator GENERATOR]
                [--arch {x64,Win32,ARM,ARM64}] [--cmake-system-version CMAKE_SYSTEM_VERSION] [--build-dir BUILD_DIR]
                [--brainflow-version BRAINFLOW_VERSION] [--cmake-install-prefix CMAKE_INSTALL_PREFIX] [--use-openmp]
                [--onnx] [--warnings-as-errors] [--debug] [--clear-build-dir] [--num-jobs NUM_JOBS] [--bluetooth]
                [--no-bluetooth] [--ble] [--no-ble] [--tests] [--no-tests]

options:
  -h, --help            show this help message and exit
  --oymotion
  --no-oymotion
  --msvc-runtime {static,dynamic}
                        how to link MSVC runtime
  --generator GENERATOR
                        generator for CMake
  --arch {x64,Win32,ARM,ARM64}
                        arch for CMake
  --cmake-system-version CMAKE_SYSTEM_VERSION
                        system version for win
  --build-dir BUILD_DIR
                        build folder
  --brainflow-version BRAINFLOW_VERSION
                        BrainFlow Version
  --cmake-install-prefix CMAKE_INSTALL_PREFIX
                        installation folder, full path
  --use-openmp
  --onnx
  --warnings-as-errors
  --debug
  --clear-build-dir
  --num-jobs NUM_JOBS   num jobs to run in parallel
  --bluetooth
  --no-bluetooth
  --ble
  --no-ble
  --tests
  --no-tests

 

일단 이게 내용인데 일단 내가 아는 것 중에 먼저 말하자면 나중에 C++에서 사용할 때 Release로 빌드 해야하는 것 같다.. 만약에 Debug로 빌드를 하고 싶으면 --debug를 추가해야하는 것 같다. 근데 또 저거 추가하면 Release가 안먹힌다...

그리고 기본적으로 --bluetooth는 켜져있다. (bluetooth는 일반 블루투스 장치를 지원하는 것 ble와는 다른 것)

그리고 --ble가 필요한건데 저전력 블루투스를 지원하게 하는 것이다. 그렇기 때문에 명령어는 아래 처럼 치면 된다.

python build.py --ble

그런데 위에 소제목에서 말한 것 처럼 나는 이게 이상하게도 안된다.. (더 신기한건 --ble를 제거하면 되긴 한다)

실행하면 아래처럼 오류가 뜬다. (글 쓰면서 실행하는데.. 역시 C++ 빌드는 너무 오래걸리는..)

  Brainflow.vcxproj -> C:\Users\pkdpm\Downloads\brainflow\build\Release\Brainflow.lib
Traceback (most recent call last):
  File "C:\Users\pkdpm\Downloads\brainflow\tools\build.py", line 306, in <module>
    main()
  File "C:\Users\pkdpm\Downloads\brainflow\tools\build.py", line 302, in main
    build(args)
  File "C:\Users\pkdpm\Downloads\brainflow\tools\build.py", line 287, in build
    run_command(cmd_build, cwd=args.build_dir)
  File "C:\Users\pkdpm\Downloads\brainflow\tools\build.py", line 22, in run_command
    raise ValueError('Process finished with error code %d' % p.returncode)
ValueError: Process finished with error code 1

 

이런 오류가 뜨게 된다.. 아무리 해도 지우고 다시 해보고 다른 것도 켜보고 하는데도 안되었다.. (이것 때메 1시간을 날린..)

그래서 결국 안되서 그냥 수동으로 빌드하기로 했다.. 

 

2024-09-15 변경 - 해결 방법

지금 이건 9월 15일인 오늘 추가로 작성하는 것이다.

나는 이 문제가 다시 무엇인지 볼려고 확인을 해보니.. 문제를 알아냈다. 

간단하게 말한다면 컴파일을 하는데 UTF-8로 컴파일이 되지 않고 다른 인코더로 컴파일이 되면서 오류가 발생한 것 같다.

이 문제를 해결하는 제일 간단한 방법은 윈도우 검색 창에 "국가 또는 지역"을 검색하고 창을 열어서 관리자 옵션에 들어가면

이런식으로 "시스템 로캘 변경"이라고 뜬다. 저걸 눌른 후 

아래 보이는 "Beta: 세계 언어 지원을 위해 Unicode UTF-8 사용"를 활성화 한다.

이렇게 한 후에 다시시작하고 컴파일을 하면 정상적으로 된다.

아마도 한국어 윈도우나 일본어 윈도우 이런건 아마도 EUC-KR EUC-JP 같은 인코더로 되어있어서 컴파일할 때 저걸로 컴파일 해서 오류 나는 것 같은데 이건 윈도우에서 기본으로 쓰는 인코더를 UTF-8로 바꿔서 해결할 수 있는 것 같다.

 

일단 나는 이 문제를 발견해서 빌드할 때 처음부터 UTF-8로 빌드되도록 소스코드를 수정해서 풀 리퀘스트를 했다.

그래서 지금은 수정이 된 상태이다.

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

그래서 파일을 다운받고 그냥

python build.py --ble

이렇게 빌드해도 오류 없이 정상적으로 컴파일이 된다..!

 

그리고 컴파일 되면 "installed"라는 폴더가 생기는데 여기에 inc 폴더와 lib 폴더가 생긴다. 이거를 가져다가 이 글 아래에 설명되어있는 대로 하면 정상적으로 작동한다! (도데체 왜 저 문제를 이제야 찾은걸까..)

 

추가적으로 Muse에 대한 내용이 잘 못 기재되어있다.

구글에 검색해보면 Muse 기기의 EEG 이름이 TP9, AF7, AF8, TP10 로 되어있는데 여기에서는 TP9, Fp1, Fp2, TP10 이걸로 설정 되어있다. 그래서 결국에는 이 정보를 어디에서 가져오는지 찾아내고 정확한 정보를 기재해서 이것도 풀 리퀘스트를 했다. (심지어 Muse 관련 코드를 열어봐도 Fp1, Fp2는 없고 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

 

아무튼 오늘 날짜 기준으로 최신버전이 5.13.3인데

pip install brainflow

를 해도 최신버전으로 수정이 안되어있다.

이유는 간단하다. 그냥 새로운 버전을 개발자가 안올렸기 때문에..

 

뭐.. 5.13.4 쯤에는 올라와 있을테니 그 때 까지 기달려야 할 것 같다..

 

아무튼 이 아래 부터는 전에 쓴 내용이 어이진다.

 

CMake로 BrainFlow 수동 빌드하기

일단 아쉽게도 나는 CMake를 예전에 OpenCV 빌드하다가 빡쳐서 포기했던 녀석이라.. 왠만하면 쓰기 싫었다.. (지금은 VCPKG 쓰는..)

그렇지만 일단 이건 연구 과제이기 때문에 해야했다.

마찬가지로 아까 위에 올린 링크에 같이 설치 방법이 있다. (저거 링크 들어가보면 In VS installer make sure you selected “Visual C++ ATL support” 저거를 VS 인스톨러에서 설치하라는건데 2022 버전에선 사라진 것 같다. 없기도 하고 설치 안해도 문제가 없는 것 같다.)

https://brainflow.readthedocs.io/en/stable/BuildBrainFlow.html#compilation-of-core-module-and-c-binding

 

Installation Instructions — BrainFlow documentation

R R binding is based on reticulate package and calls Python , so you need to install Python binding first, make sure that reticulate uses correct virtual environment, after that you will be able to build R package from command line or using R Studio, insta

brainflow.readthedocs.io

 

만약에 전에 위 글 처럼 파이썬으로 빌드 해보셨다면 지울게 있습니다!

혹시라도 위에 글 처럼 빌드를 한번 해보았다면 지울게 있다.

compiled, build 폴더이다. 이건 brainflow 폴더에서 모두 지우고 실행해야한다. 아니면 그냥 brainflow 폴더를 지우고 다시 git clone 하는 것도 방법..

 

CMake 설치 & 설정!

https://cmake.org/download/ 여기에서 나는 Windows 이기 때문에 Windows x64 Installer 이걸로 선택해서 설치했다.

설치하는건 별로 어려운게 아니여서 패스..

 

이제 CMake로 구성 설정하고 프로젝트를 만들어야 한다. 

CMake를 설치하면 실행이 가능한데 실행을 한다!

이렇게 뜨는데 여기에서 Where is the source code에는 Browse Source... 버튼을 눌러 brainflow 위치로 잡는다.

나 같은 경우는 다운로드 폴더에 brainflow를 넣었기 때문에 이걸로 지정했다.

C:\Users/pkdpm/Downloads/brainflow

그러면 그 후에는 Where to build the binaries 폴더를 선택하는건데 이건 빌드 폴더를 만드는거다.

Browse Build...을 눌러서 폴더를 지정한다. 하지만 이건 일단 위에 있는 경로로 설정을 하고 뒤에 build만 붙이면 된다.

이렇게

C:/Users/pkdpm/Downloads/brainflow/build

이렇게 하면 아래처럼 된다.

이제 여기에서 프로젝트를 만들어야 하는데

아래에 있는 Configure 버튼을 눌른다.

혹시 만약에 아래 사진 처럼 경고 같은거 뜨면 Yes 눌르면 된다. build 폴더 지정했으면서 build 폴더를 안만들었으니 만들어주겠다는 표시이다.

 

암튼 Configure 눌르게 되면 이렇게 뜨는데

여기에서 맨 위에 있는 Visual Studio 17 2022는 그냥 자신이 설치한 Visual Studio 버전이다. BrainFlow에선 2019를 권장하고 2017만 말하고 있는데 테스트 해보니 2022도 상관없는 것 같다. 그냥 바로 Finish 눌른다.

 

그런 후에는 아무것도 없던 곳에 이런 화면처럼 바뀐다.

이제 여기에서 어떤걸 빌드할지 선택할 수 있는데 여기에서 Muse 2나 Muse S가 지원될 수 있는 BUILD_BLE를 선택한다.

그리고 여기에서 좀 고생한건데.. 나중에 컴파일 하다 보면 이상하게 INSTALL 프로젝트를 빌드하면 setlocal 이러면서 오류가 뜬다.. 대충 나한테 권한 없는 곳에 폴더 두라 했다면서 화내는 오류인 것 같다.

그렇기 때문에 미리 방지하기 위해 CMAKE_INSTALL_PREFIX 부분을 수정한다. 저게 이제 INSTALL 프로젝트를 빌드하면 빌드된걸 모으는건데 어디로 모을껀지인거다.

폴더 위치는 아무데나 잡아도 되는데 관리자 권한 필요없는 폴더로 지정한다. 나는 그냥 brainflow 안에 install 폴더 안에 넣기로 했다.

내가 말한거 키면 이렇게 아래처럼 된다.

이제 아래쪽에 Generate 버튼 눌르면 바로 프로젝트가 만들어지게 된다.

다 만들어지고 나면 아래 사진처럼 원래는 Open Project 버튼이 비활성화였는데 활성화로 바뀌게 된다.

그러면 빌드 하기위해 바로 Open Project를 눌른다! 

 

빌드하기

Open Project를 눌르면 바로 Visual Studio가 켜지는데 만약에 안켜지만 직접 build 폴더 가서 키는걸로..

암튼 킨 후에 위에 보면

이렇게 생긴게 있다. 어떤걸로 빌드할지인데 아까전에 CMake 설정할 때 Debug는 따로 설정안했기 때문에 Release로 바꾼다. 그냥 눌러서 Release로 바꾸면 된다.

이렇게

그 후에는 이제 빌드를 해야하는데 ALL_BUILD를 눌러서 전부 빌드하면 좋겠지만 이상하게도 아까 추가했던 BLE에 대한건 ALL_BUILD에 추가가 안되어있는 것 같다. (BLE 프로젝트 빌드 안하고 하면 뭐 없다면서 오류남)

그래서 먼저 BLE 프로젝트 부터 빌드 해야하는데 그냥 simpleble 프로젝트를 오른쪽 클릭해서 빌드 버튼 눌르면 된다.

조금 시간이 걸리긴 하는데.. 하다보면 뜰 수도 있고 안뜰 수도 있는데 나 처럼 오류가 생길 수 있다.

오류	C2220	다음 경고는 오류로 처리됩니다.	simpleble	chrono.h

사진 아래 경고는 딱히 상관없다. 그냥 인코더 오류인 것 같은데.. (아까는 안뜨던데..)

어쨋든 chrono.h가 뭔 문제인지 경고가 뜨는거다. simpleble 프로젝트를 보면 chrono.hfmt/chrono.h에 있다고 분명 정의가 되어있고 아무 문제가 없는 것 같은데도 저런 오류가 뜬다. 근데 저건 경고인데 오류로 처리한거라 그냥 무시하도록 바꾸면 된다.

simpleble 프로젝트를 오른쪽 클릭하고 속성에 들어간다.

그럼 대충 이런식으로 뜰텐데

여기에서 왼쪽 리스트에서 C/C++ 안에 일반에 보면 사진처럼 경고를 오류로 처리는 항목이 있다.

이거 떄문에 아까 오류가 뜬건데 그냥 아니요(/WX-)로 바꾸면 된다.

그 후에 확인을 눌러서 저장하고 다시 simpleble 프로젝트 오른쪽 클릭해서 빌드 눌러서 빌드한다.

그러면 정상적으로 빌드가 되게 된다.

이런식으로 아래에 출력창에 뜬다.

이제 필요한건 다 빌드 했으니 ALL_BUILD를 빌드하면 된다.

똑같이 그냥 ALL_BUILD 프로젝트를 오른쪽 클릭하고 빌드 버튼 눌러서 빌드 하면 된다.

이건 좀더 많이 오래걸린다.. 전부 빌드해야해서..

다 하면 이렇게 3분 동안 빌드한게 뜬다..

 

이제 마지막으로 이걸 한 폴더안에 전부 모을 수 있게 프로젝트를 빌드해야하는데 바로 INSTALL 프로젝트이다.

이것도 그냥 똑같이 INSTALL 프로젝트를 오른쪽 클릭해서 빌드 버튼 눌르고 빌드하면 된다.

(만약에 혹시 setlocal인가 하는 오류 뜨면 위에 못보고 지나친 글 보면 된다.)

이렇게 하면 위에서 말한 대로 

C:/Users/pkdpm/Downloads/brainflow/install

사전에 CMake에서 설정한 곳에 빌드된게 생긴다. inclib 폴더이다. 어떤 역할인지는 대충 폴더 보면 알 수 있고.. 암튼 이거를 사용할 수 있게 어디 C 안에 두면 된다.

나 같은 경우는 전에 VCPKG 설치하면서 만들어 두었던 dev 폴더에 넣기로 했다.

C:/dev/brainflow

 

나는 이 폴더에 inclib 폴더를 넣었다.

 

예제 프로젝트 만들어서 실행해보기

이제 잘 되는지 테스트 해보기 위해 프로젝트를 만들어서 실행해봐야 한다.

일단 Visual Studio를 실행한다.

새 프로젝트 만들기 해서 빈 프로젝트를 하나 만든다.

소스 파일 오른쪽 클릭해서 새 파일로 main.cpp 파일 만들고 그 안에 아래 링크에 있는걸 복사해서 붙인다.

https://github.com/brainflow-dev/brainflow/blob/master/cpp_package/examples/get_data/src/get_data_muse.cpp

 

brainflow/cpp_package/examples/get_data/src/get_data_muse.cpp 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

그냥 어쩌다가 예제 찾다보니 찾은건데 저거 하면 CSV로 레코딩된거 10초간 기록되는 것 같다.

일단 코드에서 수정할 것이 있는데 만약에 자신꺼가 Muse S라면 상관없는데 Muse 2면 

int board_id = (int)BoardIds::MUSE_S_BOARD;

23번째 줄에 있는 이 코드를 아래처럼 바꾼다

int board_id = (int)BoardIds::MUSE_2_BOARD;

그런 후에 실행하면 당연하게도 안된다. 

일단 Debug 모드에서 실행한 것도 있고 아까 빌드한걸 연결 안했기 때문이다.

일단 이걸 Release로 우선 바꾸고

이제 빌드한걸 연결해야하는데

프로젝트를 오른쪽 클릭해서 속성에 들어간다.

아까 본 것 처럼 이런 화면 뜨는데 

먼저 C/C++일반에 들어간다.

그러면

추가 포함 디렉토리가 뜨는데 여기에 아까전에 빌드한거 뒀던 곳의 inc 경로를 붙여넣으면 된다. 나 같은 경우엔 devbrainflow에 넣었기 때문에 여기 안에 있는 inc 폴더의 경로를 넣으면 된다.

이런식으로 그 후에는 lib 퐇더를 연결시켜야 하는데

링커일반추가 라이브러리 디렉터리 부분에 lib 폴더를 넣으면 된다.

이런식으로

 

이제 어떤 lib 파일을 쓰는건지 넣는 것 같은데 암튼 이것도 해야한다.

위 사진 처럼 링커입력추가 종속성 부분에 또 뭔가를 넣어야 한다.

이건 아래꺼를 그냥 넣으면 된다. 복사해서 넣어도 되고 파일 이름 찾아서 넣어도 되고

Brainflow.lib
BoardController.lib
DataHandler.lib
MLModule.lib

넣을 때는 저거 오른쪽에 화살표 같은거 눌러서 편집 버튼 눌러서 맨 위에 빈칸에 넣으면 된다.

그 후에 아래에 확인 버튼을 눌르면 설정이 완료된다.

 

이제 모두 파일을 연결했다!

오류	LNK2038	'RuntimeLibrary'에 대해 불일치가 검색되었습니다. 'MT_StaticRelease' 값이 'MD_DynamicRelease'(main.obj에 위치) 값과 일치하지 않습니다.

그런데 또 실행해보면 이런 문제가 발생한다.

 

대충 그냥 런타임 라이브러리가 이상하게 설정되어있다는 건데 뭐.. 설정하면 된다..

다시 프로젝트 속성에 가서

C/C++코드 생성에서 런타임 라이브러리를 수정하면 된다. 보통은 다중 스레드 DLL(/MD)로 되어있을 텐데 이걸 다중 스레드(/MT)로 바꿔주면 된다. 아래처럼

(뭐.. Debug 모드에선 MTd로 설정해야 하긴 하는데 어차피 Debug 껀 빌드 안했으니 뭐..)

 

(참고로 이후에 프로젝트 진행하면서 MD로 빌드 해야해서 해보니 CMake로 Open Project 했을 때 열리는 것에서 오른쪽 ALL_BUILD 등등의 프로젝트 속성에서 C/C++의 코드 생성에서 런타임 라이브러리를 모두 MD로 바꿔주고 빌드하면 MD로 빌드된다.)

 

그 후에 확인 버튼을 눌르고 다시 실행한다! 실행하는건 Ctrl + F5로 

 

그러면 드디어 빌드가 되고 실행이 된다!

근데 이런식으로 오류가 뜨게 된다.

컴파일 한걸 실행할려는데 DLL 파일이 없어서 안되는거다. 그냥 옮기면 된다.

아까전에 빌드 했던거 옮겨놓은 폴더에 가서 lib 폴더로 들어간다.

그러면 .dll로 되어있는 파일들이 여러개 보이는데 뭐인지 확인하는건 귀찮으니깐 .dll로 되어있는 파일은 모두 복사한다.

(Ctrl 키 눌르고 여러개 선택해서 복사하면 된다.)

 

붙여 넣는 곳은 프로젝트를 오른쪽 클릭해서 파일 탐색기에서 폴더 열기(X) 버튼을 눌른 후 뜨는 폴더에 다 붙여넣으면 된다.

그렇게 되면 이렇게 붙여넣어졌을 것이다.

이러면 끝이다!

이제 Muse 제품의 전원을 키고 실행해보면? 정상적으로 연결이 된다!

실행하면 아래처럼 뜨는데 

Found Muse device, Connected to Muse Device 이거 뜨면 정상적으로 연결된거다!

이렇게 된 후에는 자동으로 10초간 정보가 기록되고

이렇게 csv 파일로 기록이 된다!

만약에 연결이 안되거나 하면 전원을 켜보거나 자신의 PC에 블루투스 동글이나 블루트스 연결할 수 있는 장치가 있는지 확인해보는걸 추천한다..

 

마무리

 

그리고 csv에 저장된건 사실 봐도 지금은 뭔지는 모르겠다.. 일단 연구 과제가 이걸로 뭐 UI 만들어서 기록하도록 만드는건데 암튼 이걸 할려고 좀 이상한 짓을 한 것 같다.. 그냥 파이썬을 커맨드 라인으로 실행해서 하는 것도 빨랐을지도.. (뭐 시간이 중요해서 이렇게 하는거지만..) 

 

아무튼 어쩌다 보니 10분안에 쓸려고 했었는데 1시간 30분 동안 쓰고 있다.. 

이 외의 오류는 저도 해결은 잘 못하겠지만.. 암튼 잘해보시길요..

 

오늘 수업 있는데.. 왜 시계에 오전 4:38 이라고 떠있는거지..

 

그리고 위에 말한 것 처럼 이 이외에 오류나는건 나도 모른다.

반응형

+ Recent posts