MindWaveからPythonを使って脳波の値を取得し、好きなプログラムで利用する方法
どうもどうも、Mikenerianです。
先日ハッカソンというイベントに初めて参加してみましたが、とっても良かったです!
ものづくりにすっかり目覚めました。
内容を詳しく教えることはできませんが、
クリスマスが近いですね!メリークリスマス!!
思いの外苦労したので、
MindWaveから脳波の値を出力させ、それを他の入力系(ラズパイとか)に送信する方法をまとめたいと思います。
ちなみにこの記事のコードなどは、自分一人では到底書けないものでした。
チームメンバーの方々には大変感謝しています。
この記事では、
MindWaveとは何ぞや?
ってところから解説するので、既知の方は「MindWaveの値を別プログラムに転送する」から見てください。
MindWaveとは
これはNeuroSky社が開発した、簡易脳波計です!
かなり前から発売していたようで、なんと某大手通販サイトでも販売しています。
従来の脳波計は頭を完全に覆ってジェルなんかをつける必要があったので、ヘッドギアをつけるだけの本脳波計はかなりシンプルといえます。
ただ、研究に使う脳波計ほどちゃんとした値を計測できてるわけではないので、あくまでもお遊び程度の商品です。
脳波データに色々フィルターかけたものを送っているようですが、delta, theta, lowalpha, highalpha, lowbeta, highbeta, lowgamma, midgammaの8種類を計測しているみたいです。
さらに、ATTENTION(集中度)とMEDITATION(リラックス度)という独自の値を算出しており、0~100の値で出力してくれるスグレモノです。(アルゴリズムは完全なブラックボックスですが…)
MindWaveから値を取得する
まず、私が使った環境をのせておきます。
PC: MacBook Pro
MindWave: よく分からないが2011年版と2017年版
基本的に、付属の説明書の手順に沿えばMacでもWindowsでもiOSでもAndroidでも接続することはできます。
PCの場合、ThinkGearConnectorというソフトがプラットフォームになっています。
脳波の値はまずここで取得され、他のアプリへと送信されます。
ThinkGear Serial Stream Guide
http://developer.neurosky.com/docs/doku.php?id=thinkgear_communications_protocol
後はMindWave Mobile Tutorialというアプリを立ち上げ、ちゃんと接続されていれば問題はありません!
ただ不良品が多いらしく、この段階でつまずくようでしたらその可能性が考えられます。
ちなみにアプリを立ち上げていない間は、MindWaveをPCに接続中でも「未接続」となることが多々あります。
またMindWaveのライト点滅について、古いやつと新しいやつでは意味が違っています。
旧バージョンの場合は以下のような意味があるらしいです。
ライト | MindWaveの状態 | 意味 |
---|---|---|
オフ | 電源オフ | MindWaveは電源オフか、バッテリーが無い |
2回青に点滅 | ペアリングモード | MindWaveはデバイスに接続する準備ができている |
赤に点灯 | ペアリングしていない | MindWaveはデバイスとペアリングする必要がある |
青に点滅 | 準備完了 | MindWaveはペアリングしてくれるデバイスを待っている |
青に点灯 | 接続中 | MindWaveはペアリングされたデバイスと通信中 |
赤で2回点滅 | バッテリー低 | MindWaveのバッテリー交換の必要性(電池の交換) |
青に点滅でなかなか点灯に変わってくれない…そんなあなた、安心してください!
アプリを立ち上げたら点いてくれることがよくあります。
MindWaveの値を別プログラムに転送する
アプリでMindWaveの値を見ることはできた。
だけど、こんなオシャレなビジュアルデータじゃなくて、値が欲しい!
ここからが本当の戦いでした。
色々見ましたが、僕が一番参考になったのは、この記事でした。
necomimiを改造して脳波データを取り出した
http://developer.neurosky.com/docs/doku.php?id=thinkgear_communications_protocol
このやり方を応用して、MindWaveからATTENTION、MEDITATIONの値をテキスト出力してみます!
①下準備
ターミナルで
$pip install thinkgear
と入力してthinkgearライブラリをMacにインストール($は実際には入力しません)
次に、
$ ls /dev/tty.*
と入力して、Bluetooth接続されている機器一覧を取得。
その中から、tty.MindWave~となっている値をコピペします。
②動作確認
以下のプログラムを適当な名前で保存し、実行
test1.py
import thinkgear PORT = '/dev/tty.MindWaveMobile-SerialPo' /*ここに/dev/tty.~を記述 for packets in thinkgear.ThinkGearProtocol(PORT).get_packets(): for p in packets: if isinstance(p, thinkgear.ThinkGearRawWaveData): continue print p
これでちゃんと出力されていれば、成功です
③パッケージの内容変更
インストールしたパッケージを変更
おそらく以下のパスになりますが、インストールしたパッケージファイルを開きます
/Library/Frameworks/Python.framework/Versions/2.7/bin/lib/thinkgear/thinkgear.py
そしてこのコードを見ていくと、どうやら_strfmtというコマンドで値を送っているようです。(どうやっているのかはわからない)
なので、ATTENTIONとMEDITATIONの_strfmtを変更します。
また、それ以外の_strfmtを何も送らないようにします。
私はATTENTIONとMEDITATIONの出力を、冒頭の数値が1か2かによって分類するプログラムを作成したので、それぞれ以下のようにしました。
ATTENTION: _strfmt = '1%(value)s'
MEDITATION: _strfmt = '2%(value)s'
その他: _strfmt = ''
④実行!
これで、ATTENTIONだけを出力するコードは以下のようになります
tkg.py
import thinkgear PORT = '/dev/tty.MindWaveMobile-SerialPo' /*ここに/dev/tty.~を記述 for packets in thinkgear.ThinkGearProtocol(PORT).get_packets(): for pkt in packets: if isinstance(pkt, thinkgear.ThinkGearRawWaveData): continue t = str(pkt) if t != '': differencer = t[0:1] /*最初の文字を取り出す if int(differencer) == 1: attention = t[1:] /*最初の文字以外をattentionとして代入 print attention
出力値はstr値のようです。
ATTENTIONとMEDITATION以外も出力するので、それ以外の_strfmtはすっ飛ばすようにしてます。
t[0:1]は、tの1文字目だけを抜き出すコマンドです。
⑤応用編
最終的に使ったコードはラズパイに送ったので、以下のようになります。
tkg_01.py
import thinkgear import commands from time import sleep import paho.mqtt.client as mqtt import sys '''以下はラズパイの設定''' HOST = PORT = KEEP_ALIVE = TOPIC = MESSAGE = args = sys.argv PUBLISH_NUMBER = 1 SLEEP_TIME = 1 def publish_many_times(client, topic='topic/default', message='default', number=1, time=1, print_flag=False): for i in range(number): client.publish(topic, message) if print_flag == True: print (topic + ' ' + message) sleep(time) # client.disconnect() client = mqtt.Client(protocol=mqtt.MQTTv311) client.connect(HOST, port=PORT, keepalive=KEEP_ALIVE) PORT = '/dev/tty.MindWaveMobile-DevA' /*ここに/dev/tty.~を記述 count = 1 for packets in thinkgear.ThinkGearProtocol(PORT).get_packets(): for pkt in packets: if isinstance(pkt, thinkgear.ThinkGearRawWaveData): continue sleep(0.7) /*ラズパイが処理しきれなくなるので一定時間処理をストップ t = str(pkt) '''setting identifier in the beginning of signal attention = 1, meditation = 2''' if t != '': differencer = t[0:1] send = '1' meditation = '0' if int(differencer) == 1: attention = t[1:] elif int(differencer) == 2: meditation = t[1:] if int(meditation) > 50: send = '0' else: send = '1' MESSAGE = attention + ',' + send print MESSAGE + ' ' + meditation publish_many_times(client,TOPIC, MESSAGE, PUBLISH_NUMBER, SLEEP_TIME)
このプログラムではattention値とsend値をカンマ区切りで送っています。
send値はmeditationが50以上なら0、それ以下なら1と定義しています。