一般的にバイノーラルビートと呼ばれる左右から異なる周波数の音をヘッドホンやイヤホンで聴くことで、左右の周波数差分だけ脳波が誘導されると言われています。
バイノーラルビートによる脳波誘導の検証はこちらの記事も参照してみてください。
今回はこのバイノーラルビートを使って睡眠もコントロール可能なのかどうかを検証してみました。
検証に使用したセンサーとアプリケーション
バイノーラルビートの効果を検証するために脳波計(EEG)とジャイロセンサーを使います。
睡眠時は長時間バッテリーが動作する必要があり、寝返り等で電極がずれないようMuseS(Gen1)を使用しました。
計測データは正規化された脳波の値を使用しています。
サンプルデータの読み込みと表示
import pandas as pd
header = ["ts", "rel_delta", "rel_theta", "rel_alpha", "rel_beta", "rel_gamma"]
df = pd.read_csv("/logs/sample/waves.csv", names=header)
df["ts"] = df["ts"] - df.iloc[0]["ts"]
df = df.set_index("ts")
print(df)
以下は実行結果です
rel_delta rel_theta rel_alpha rel_beta rel_gamma
ts
0.000000 0.226307 0.14742 0.212869 0.289432 0.097327
0.004579 0.226307 0.14742 0.212869 0.289432 0.097327
0.002861 0.226307 0.14742 0.212869 0.289432 0.097327
0.008472 0.226307 0.14742 0.212869 0.289432 0.097327
0.009727 0.226307 0.14742 0.212869 0.289432 0.097327
... ... ... ... ... ...
2383.505793 0.299243 0.15809 0.245185 0.210305 0.075791
2383.502824 0.299243 0.15809 0.245185 0.210305 0.075791
2383.511717 0.299243 0.15809 0.245185 0.210305 0.075791
2383.508861 0.299243 0.15809 0.245185 0.210305 0.075791
2383.513762 0.299243 0.15809 0.245185 0.210305 0.075791
[73931 rows x 5 columns]
入眠の判定
一定時間の頭の動きから、入眠や寝返りを判定するためにジャイロセンサーの値も計測します。
以下はサンプルデータの読み込みと表示を行うコードです。
header = ["ts", "X", "Y", "Z"]
df_xyz = pd.read_csv("/logs/sample/gyro.csv", names=header)
df_xyz["ts"] = df_xyz["ts"] - df_xyz.iloc[0]["ts"]
df_xyz = df_xyz.set_index("ts")
print(df_xyz)
実行結果
X Y Z
ts
0.000000 6.198273 1.039276 2.803802
0.053275 7.312317 1.248627 4.680481
0.022578 6.766510 1.099091 4.082336
0.070760 8.217010 1.076660 4.717865
0.207850 8.770294 0.762634 3.671112
... ... ... ...
15482.648101 3.005676 0.381317 3.454285
15482.649199 3.760834 0.807495 4.067383
15482.732099 4.538422 1.136475 4.508514
15482.735680 4.426270 1.278534 5.031891
15482.738389 4.987030 1.749573 5.188904
[282821 rows x 3 columns]
睡眠ステージのプロット方法
脳波データとジャイロセンサーのデータを使って以下の手順で睡眠ステージを検出してプロットを行ってみます。
- アルファー波とデルタ波の差分の10分移動平均(基本指標)を閾値として睡眠ステージを分類してみる
- ノンレム睡眠からレム睡眠に切り替わるタイミングに寝返りを打つことが多いことから、ジャイロセンサーの閾値と基本指標からステージ変更を検知する
- 入眠のタイミングはジャイロセンサーの10分移動平均を閾値と基本指標から判定する
- 覚醒時も同様に基本指標とジャイロセンサーの閾値から判定する
figsize = (20, 3)
rate = 20 # 秒間に受け取るデータ量
# 睡眠状態をプロット
def plot_sleep_stage(waves_file_name, gyro_file_name):
pd.options.plotting.backend = "matplotlib"
stage = "wakeup"
fillcolor_map = {"wakeup": "yellow", "nonrem": "blue", "rem": "green"}
x0 = 0
x1 = 0
header = ["ts", "abs_delta", "abs_theta", "abs_alpha", "abs_beta", "abs_gamma", "rel_delta", "rel_theta", "rel_alpha", "rel_beta", "rel_gamma", "hsi"]
df = pd.read_csv(waves_file_name, names=header)
df["ts"] = df["ts"] - df.iloc[0]["ts"]
df = df.set_index("ts")
# 正規化された脳波のみを扱います
df = df.loc[:,["rel_delta", "rel_theta", "rel_alpha", "rel_beta", "rel_gamma"]]
# 正規化されたデルタ波とアルファ波の差分をとります
df["offset"] = df["rel_delta"] - df["rel_alpha"]
# 10分間の移動平均値をとります
df["offset"] = df["offset"].rolling(600*rate).mean()
# nullデータは除外します
df = df.dropna()
df_offset = df.loc[:,["offset"]]
# グラフの作成
fig = df_offset.plot(title='sleep stage', figsize=figsize)
fig.axhline(y=0.25, linestyle=':', color='black')
fig.axhline(y=0,linestyle=':', color='black')
fig.axhline(y=-0.2,linestyle=':', color='black')
# ジャイロセンサーデータの読み込み
header = ["ts", "X", "Y", "Z"]
df_xyz = pd.read_csv(gyro_file_name, names=header)
df_xyz["ts"] = df_xyz["ts"] - df_xyz.iloc[0]["ts"]
df_xyz = df_xyz.set_index("ts")
# 10分間移動平均の最大値を求めます
df_xyz = df_xyz.rolling(600*rate).max()
# X Y Zの平均値を求める
df_xyz["avg"] = df_xyz.mean(axis=1)
df_xyz = df_xyz.dropna()
df_xyz = df_xyz.loc[:,["avg"]]
for index,item in df_xyz.iterrows():
# 10分間体の動きが無ければ入眠とする
if item["avg"] < 20:
if x0 != index:
x1 = index
fillcolor = fillcolor_map[stage]
fig.axvspan(x0, x1, alpha=0.2, color=fillcolor)
x0 = index
stage = "rem"
break
for index,item in df_offset.iterrows():
# ノンレム睡眠
if item["offset"] > 0.23 and stage != "nonrem":
if x0 != index:
x1 = index
fillcolor = fillcolor_map[stage]
fig.axvspan(x0, x1, alpha=0.2, color=fillcolor)
x0 = index
stage = "nonrem"
# レム睡眠
if item["offset"] < 0 and stage == "nonrem":
if x0 != index:
x1 = index
fillcolor = fillcolor_map[stage]
fig.axvspan(x0, x1, alpha=0.2, color=fillcolor)
x0 = index
stage = "rem"
stage = "rem"
fillcolor = fillcolor_map[stage]
fig.axvspan(x0, index, alpha=0.2, color=fillcolor)
睡眠データのプロット
試しに8時間睡眠を行った場合のデータをプロットしてみます。
csvdir = "/logs/sleep-2022-05-19"
plot_sleep_stage(f"{csvdir}/waves.csv", f"{csvdir}/gyro.csv")
青で表示されている部分が最も深い睡眠状態の時間です。
入眠後90分(54000秒)あたりで最も深い状態となっているので一般的な睡眠サイクルがしっかり描画されているように見えます。
バイノーラルビートを使った睡眠誘導
ここからが本題です。
まずはじめに、深い睡眠状態であるデルタ波に導くために1Hz〜2Hz程度の脳波へ誘導するためのバイノーラルビートを作成します。
バイノーラルビートの音源を作成するためにAccelBrainBeat
をインストールします。
$ pip install AccelBrainBeat
インストールが完了したら、以下のリポジトリから音響ファイル生成用のスクリプトダウンロードしてビルドします
ビルドが面倒な方はこちらにビルド済みの音響ファイルをflac形式に変換して置いてあります。
$ git clone https://github.com/tatsu-i/metaWaves
$ python delta_binaural.py
デルタ波に導く部分は以下の通りで、50Hzおよび100Hz帯をキャリア周波数としています。
brain_beat = BinauralBeat()
brain_beat.save_beat(
output_file_name="/tmp/1.wav",
frequencys=(99, 101),
play_time=total_time,
volume=0.3
)
brain_beat.save_beat(
output_file_name="/tmp/2.wav",
frequencys=(49.5, 50.5),
play_time=total_time,
volume=0.3
)
音源の作成が完了したので、いよいよ睡眠導入の実験です。
音響ファイルをウォークマンにコピーしてループ再生できるように設定しておきます。
イヤホンから聞こえる音量が調節できたら、Muse Sを装着しMindMonitorで脳波の計測を開始して寝ます。
翌日の朝
朝起きたらMindMonitorを停止し、データを読み込ませてプロットすると以下のようになりました。
以下はバイノーラルビート無しの状態の脳波です。
どうでしょうか?
入眠から深い睡眠へと入る時間が短くなり、深い睡眠の持続時間が伸びているように見えます。
おまけ
わたしは朝起きるのに時間がかかるタイプだったのですが、ベータ波に導くバイノーラルビートを作成してみたところかなり効果がありました。
※長時間聞いていると頭痛やめまいがするので5分程度にしています。
入眠用のデルタ波誘導音と一緒に覚醒用のベータ波誘導音をこちらに置いておきますので、なかなか寝られない・起きれないという方は是非試してみてください。