Yuichiron のすべての投稿

Fitbit+Python(3)

睡眠・歩数・RHR・HRVデータの処理
 1週間、1ヶ月といった単位で、行動・生理指標の変化を追う場合は、睡眠・歩数・RHR(安静時心拍)・HRV(心拍変動)データの日単位の取得が必要になります。

睡眠データの取得
 ClientIDとClientSecretを用いて,過去4日分の睡眠データを取得し,csv形式で保存する。40~46行目の関数は,それぞれ「総睡眠時間,目覚め後の時間,入眠時間,覚醒時間,寝返りの回数,寝返り時間,睡眠効率」の7つを取得している。

「合計睡眠時間=Awake(起きている時間)+MainLen(寝ている時間)」
"""
getSleepSmple.py
ClientIDとClientSecretで、Sleep関連データを取得するプログラム
"""
import sys
import fitbit
import pandas as pd
import gather_keys_oauth2 as Oauth2
from datetime import datetime, date, timedelta
     
print('Hello FitbitSleepAPP1') 
   
USER_ID     = "hoge"
CLIENT_SECRET = "hoge"
     
def requestFitbit(DATE):
    rval=""
    global auth2_client
    
    #fitbit_stats = auth2_client.intraday_time_series('activities/minutesSedentary', DATE, detail_level='1min')
    #HRstats = fitbit_stats['activities-minutesSedentary-intraday']['dataset']
       
    sleep_data = auth2_client.sleep(date=DATE)    
    #print(DATE+"睡眠データ取得実行しました") 
   
    OUTPUT_FILE = "SLEEP.csv"
    csv_file = open(OUTPUT_FILE, 'a')
    csv_file.write(DATE+",")
   
    sleepcnt=len(sleep_data["sleep"])
    #print(str(sleepcnt)+"回眠っています") 
   
    totallength=0;
    TAfter=0;
    TFall=0;
    TAwake=0;
    TLessCnt=0;
    TLessDur=0;
    TEffi=0;
    for var in range(0, sleepcnt):
        #print("睡眠:"+str(var)) 
        totallength+=sleep_data["sleep"][var]["minutesAsleep"] #総睡眠時間
        TAfter+=sleep_data["sleep"][var]["minutesAfterWakeup"]
        TFall+=sleep_data["sleep"][var]["minutesToFallAsleep"]
        TAwake+=sleep_data["sleep"][var]["minutesAwake"]
        TLessCnt+=sleep_data["sleep"][var]["restlessCount"]
        TLessDur+=sleep_data["sleep"][var]["restlessDuration"]
        TEffi+=sleep_data["sleep"][var]["efficiency"]
          
    AvgEffi=1
    if sleepcnt!=0:
        AvgEffi=TEffi/sleepcnt;    
      
    print(str(sleepcnt)+"回の睡眠で総睡眠時間は"+str(totallength)+"分です") 
    csv_file.write(str(totallength)+","+str(sleepcnt)+","+str(TAfter)+","+str(TFall)+","+str(TAwake)+","+str(TLessCnt)+","+str(TLessDur)+","+str(AvgEffi)+",")
   
    for var in range(0, sleepcnt):
        if sleep_data["sleep"][var]["isMainSleep"] == True:
            csv_file.write(sleep_data["sleep"][var]["startTime"]+","+str(sleep_data["sleep"][var]["minutesAsleep"])+","+str(sleep_data["sleep"][var]["efficiency"]))
   
    csv_file.write("\n")
    csv_file.close()
       
    return rval
##################################################
    
def writeindex():   
    OUTPUT_FILE = "SLEEP.csv"
    csv_file = open(OUTPUT_FILE, 'a')
                     
    csv_file.write("date,totallength,count,AfterWakeup,FallAsleep,Awake,restlessCount,restlessDur,AvgEffic,MainStart,MainLen,MainEffic\n")
    csv_file.close()
##################################################
    
"""Get tokens"""
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
     
print(ACCESS_TOKEN)  
print(REFRESH_TOKEN)
    
"""Authorization"""
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
     
"""request"""
#requestFitbit("2022-02-22")
writeindex();   #インデックス記入
today = datetime.today()
for var in range(0,4):
    #day=today + timedelta(days=1)
    #print(var+":" + datetime.strftime(yesterday, '%Y-%m-%d'))
    stamp=datetime.strftime(today - timedelta(days=var), '%Y-%m-%d')
    print("target: " + stamp, end=' ')
    requestFitbit(stamp)

歩数データの取得
1日あたりの歩数を取得する

"""
getStepSample.py
個人の1日の歩数データを取得するプログラム
"""
import sys
import fitbit
import gather_keys_oauth2 as Oauth2
from datetime import datetime, timedelta
import json
 
print('Hello FitbitDailyStepsAPP')
 
USER_ID = "hoge"
CLIENT_SECRET = "hoge"
 
def requestFitbit(DATE):
    global auth2_client
 
    # APIエンドポイントを使用して1日の歩数データを取得
    steps_data = auth2_client.make_request(f'https://api.fitbit.com/1/user/-/activities/steps/date/{DATE}/1d.json')
 
    OUTPUT_FILE = "Steps.csv"
    with open(OUTPUT_FILE, 'a') as csv_file:
        csv_file.write(f"{DATE},")
        if "activities-steps" in steps_data and len(steps_data["activities-steps"]) > 0:
            steps_info = steps_data["activities-steps"][0]  # リストの最初の要素を取得
            csv_file.write(f"{steps_info['value']}\n")
        else:
            csv_file.write("No steps data\n")
 
def writeindex():
    OUTPUT_FILE = "Steps.csv"
    with open(OUTPUT_FILE, 'w') as csv_file:
        csv_file.write("date,steps\n")
 
# トークンの取得
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
 
print("AT:"+ACCESS_TOKEN)
print("RT:"+REFRESH_TOKEN)
 
# 認証
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
 
# インデックスの書き込み
writeindex()
 
# 1日の歩数データのリクエスト
today = datetime.today()
for var in range(0, 7):
    stamp = datetime.strftime(today - timedelta(days=var), '%Y-%m-%d')
    print(f"target: {stamp}", end=' ')
    requestFitbit(stamp)

RHR(安静時心拍数)の取得
RHRはその日の安静時の心拍数の評価値

"""
getRHRsample.py
個人のHRVデータを取得するプログラム
"""import sys
import fitbit
import gather_keys_oauth2 as Oauth2
from datetime import datetime, timedelta
import json

print('Hello FitbitRHRAPP')

USER_ID = "hoge"
CLIENT_SECRET = "hoge"

def requestFitbitRHR(DATE, ID):
    global auth2_client

    # APIエンドポイントを使用して安静時心拍数データを取得
    heart_data = auth2_client.make_request(f'https://api.fitbit.com/1/user/-/activities/heart/date/{DATE}/1d.json')

    OUTPUT_FILE = ID + "_RHR.csv"

    with open(OUTPUT_FILE, 'a') as csv_file:
        csv_file.write(f"{DATE},")
        if "activities-heart" in heart_data and len(heart_data["activities-heart"]) > 0:
            heart_info = heart_data["activities-heart"][0]['value']
            if 'restingHeartRate' in heart_info:
                rhr = heart_info['restingHeartRate']
                csv_file.write(f"{rhr}\n")
            else:
                csv_file.write("No resting heart rate data\n")
        else:
            csv_file.write("No heart rate data\n")

def writeindex(ID):
    OUTPUT_FILE = ID + "_RHR.csv"
    with open(OUTPUT_FILE, 'w') as csv_file:
        csv_file.write("date,restingHeartRate\n")

# トークンの取得
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])

print(ACCESS_TOKEN)
print(REFRESH_TOKEN)

# 認証
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)

# インデックスの書き込み
writeindex(USER_ID)

# 安静時心拍数データのリクエスト
today = datetime.today()
for var in range(0, 7):
    stamp = datetime.strftime(today - timedelta(days=var), '%Y-%m-%d')
    print(f"target: {stamp}", end=' ')
    requestFitbitRHR(stamp, USER_ID)

HRV(心拍変動)データの取得
HRVは睡眠時のものとなるため、1日あたり一つ。評価値はrMSSDを示し単位はms。

"""
getHRVsample.py
個人のHRVデータを取得するプログラム
"""
import sys
import fitbit
import gather_keys_oauth2 as Oauth2
from datetime import datetime, timedelta
import json
 
print('Hello FitbitHRVAPP')
 
USER_ID = "hoge"
CLIENT_SECRET = "hoge"
 
def requestFitbit(DATE):
    global auth2_client
 
    # APIエンドポイントを使用してHRVデータを取得
    hrv_data = auth2_client.make_request(f'https://api.fitbit.com/1/user/-/hrv/date/{DATE}.json')
 
    OUTPUT_FILE = "HRV.csv"
    with open(OUTPUT_FILE, 'a') as csv_file:
        csv_file.write(f"{DATE},")
        if "hrv" in hrv_data and len(hrv_data["hrv"]) > 0:
            hrv_info = hrv_data["hrv"][0]  # リストの最初の要素を取得
            csv_file.write(f"{hrv_info['value']['dailyRmssd']},{hrv_info['value']['deepRmssd']}\n")
        else:
            csv_file.write("No HRV data\n")
 
def writeindex():
    OUTPUT_FILE = "HRV.csv"
    with open(OUTPUT_FILE, 'w') as csv_file:
        csv_file.write("date,dailyRmssd,deepRmssd\n")
 
# トークンの取得
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
 
print(ACCESS_TOKEN)
print(REFRESH_TOKEN)
 
# 認証
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
 
# インデックスの書き込み
writeindex()
 
# HRVデータのリクエスト
today = datetime.today()
for var in range(0, 4):
    stamp = datetime.strftime(today - timedelta(days=var), '%Y-%m-%d')
    print(f"target: {stamp}", end=' ')
    requestFitbit(stamp)

Fitbit+Python(2)

1週間ぶんのHRを取得する
計測したデータはすべてクラウド上に上がっているので、1週間ぶんのデータを取得することも可能です。おおまかな手順は下記の動画をご覧ください。

下記スクリプトは、指定した日付から遡って3日ぶんのHRを取得するスクリプトです。

#getWeekHR.py
import sys
import fitbit
import gather_keys_oauth2 as Oauth2
from datetime import datetime, date, timedelta
    
print('Hello FitbitAPP4') 
USER_ID     = ""; CLIENT_SECRET = ""
    
def requestFitbit(DATE):
    rval=""
    global auth2_client,USER_ID 
   
    fitbit_stats = auth2_client.intraday_time_series('activities/heart', DATE, detail_level='1min')
    HRstats = fitbit_stats['activities-heart-intraday']['dataset']
      
    OUTPUT_FILE = USER_ID+"_HR.csv"
    csv_file = open(OUTPUT_FILE, 'a')
     
    csv_file.write(DATE+",")
    for num1 in range(24):
        for num2 in range(60):
            key='{:02}'.format(num1)+':{:02}'.format(num2)+':00'
            hr=""
            for var in range(0, len(HRstats)):
                if str(HRstats[var]['time']) == key:
                    hr=str(HRstats[var]['value'])
                    break
            csv_file.write(hr)
            csv_file.write(",")              
               
    csv_file.write("\n")
    csv_file.close()
    return rval
##################################################
   
def writeindex():    
    global USER_ID 
    OUTPUT_FILE = USER_ID+"_HR.csv"
    csv_file = open(OUTPUT_FILE, 'a')
       
    csv_file.write(",")
    for num1 in range(24):
        for num2 in range(60):
            key='{:02}'.format(num1)+':{:02}'.format(num2)+':00'
            csv_file.write(key)
            csv_file.write(",")              
               
    csv_file.write("\n")
    csv_file.close()
##################################################
   
"""Get tokens"""
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
    
"""Authorization"""
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
    
"""request"""
   
writeindex()
today=date(2023, 6, 26)
for var in range(0, 3):
    stamp=datetime.strftime(today - timedelta(days=var), '%Y-%m-%d')
    print("target: " + stamp)
    requestFitbit(stamp)

無事に実行されると、「ID_HR.csv」のようなファイル名のデータが作成されます。エクセルで開くと、下記のような構造になっています。60(分)✕24(時間)=1440個のデータが3日ぶん格納されていることがわかります。

無事に実行されると、「ID_HR.csv」のようなファイル名のデータが作成されます。エクセルで開くと、下記のような構造になっています。60(分)✕24(時間)=1440個のデータが3日ぶん格納されていることがわかります。

無事に実行されると、「ID_HR.csv」のようなファイル名のデータが作成されます。エクセルで開くと、下記のような構造になっています。60(分)✕24(時間)=1440個のデータが3日ぶん格納されていることがわかります。

Fitbit+Python(1)

ここでは、Fitbitで計測されたデータをPythonで取得する方法を解説します。作業のおおまかな手順は下記の動画をご覧ください。

Anacondaのインストール
Python実行環境であるAnacondaのインストールを行う。とりあえずAnaconda3をダウンロードして道なりにインストール。Environmentから仮想環境「Fitbit」を作成し、「OpenTerminal」でターミナルを表示する。Pythonコマンドで動作を確認する。Ctrl+Zで対話モードを終了できる。インストールは、こちらのサイトが参考になる。

Fitbitの開発者向けサイトでアプリケーションを登録する
PythonからFitbitAPIを使うにはアプリの登録が必要となる。FitbitDevelopperサイトにログインして、Manage>RegisterAppの順で進み、アプリケーションを登録する。設定は以下の画像の通りにする。URL関係は全てダミーのURLで問題なく、RedirectURLは、「http://127.0.0.1:8080/」とする。ページ内で作成された「OAuth 2.0 Client ID」と「Client Secret」をPythonスクリプト中で使用することになる。
Devサイトでのアプリ作成方法

Fitbit API Python Clientのインストール
GitHub – orcasgit/python-fitbit: Fitbit API Python Client Implementationからcodeボタンを押し、Zipファイルをダウンロードし、中身をCドライブ直下に配置する(こちらのサイトが参考になる)。さらに、下記コマンドで追加パッケージをインストールする。

pip install -r requirements/base.txt
pip install cherrypy 

Pythonスクリプトでクラウド上のファイルを取得
c:\python-fitbitフォルダに下記スクリプトをfitbit01.pyという名前で保存し、実行する。Anaconda Promptから下記コマンドを打ち込む

python fitbit01.py

#fitbit01.py
import sys
import fitbit
import gather_keys_oauth2 as Oauth2
   
print('Hello FitbitAPP3') 
   
USER_ID     = "hoge"
CLIENT_SECRET = "hoge"
   
DATE = "2021-06-03" # 取得したい日付
   
   
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
   
#print(ACCESS_TOKEN) print('\n') 
#print(REFRESH_TOKEN)print('\n') 
   
"""Authorization"""
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
   
"""Getting data"""
#print('Getting data\n') 
#fitbit_stats = auth2_client.intraday_time_series('activities/heart', DATE, detail_level='1min')
    
##################################################
fitbit_stats = auth2_client.intraday_time_series('activities/heart', DATE, detail_level='1min')
stats = fitbit_stats['activities-heart-intraday']['dataset']
   
OUTPUT_FILE = "HR_%s.csv" % DATE
csv_file = open(OUTPUT_FILE, 'w')
for var in range(0, len(stats)):
    csv_file.write(stats[var]['time'])
    csv_file.write(",")
    csv_file.write(str(stats[var]['value']))
    csv_file.write("\n")
csv_file.close()
   
##################################################
fitbit_stats = auth2_client.intraday_time_series('activities/steps', DATE, detail_level='1min')
stats = fitbit_stats['activities-steps-intraday']['dataset']
   
OUTPUT_FILE = "STEP%s.csv" % DATE
csv_file = open(OUTPUT_FILE, 'w')
for var in range(0, len(stats)):
    csv_file.write(stats[var]['time'])
    csv_file.write(",")
    csv_file.write(str(stats[var]['value']))
    csv_file.write("\n")
csv_file.close()
   
##################################################
fitbit_stats = auth2_client.intraday_time_series('activities/calories', DATE, detail_level='1min')
stats = fitbit_stats['activities-calories-intraday']['dataset']
   
OUTPUT_FILE = "CALO%s.csv" % DATE
csv_file = open(OUTPUT_FILE, 'w')
for var in range(0, len(stats)):
    csv_file.write(stats[var]['time'])
    csv_file.write(",")
    csv_file.write(str(stats[var]['value']))
    csv_file.write("\n")
csv_file.close()

トラブルシューティング

・RedirectURLが、http://127.0.0.1:8080/で正しく動作しない場合
ポート8080をなにかのプログラムが使っている可能性がある。その場合は、RedirectURLを、「http://127.0.0.1:8088/」とし、python-fitbitフォルダ中のgather_keys_oauth2.pyの該当部分を「8088」に変更してスクリプトを実施する。

・Pythonのスクリプトは何で作ればいいの?
プログラミング用のテキストエディタ、Notepad++がおすすめです。

H10+EliteHRV+Kubios

PolarH10+EliteHRV+Kubiosで心拍変動解析
 PolarH10は、運動研究分野で広く使われるチェストバンド型心拍モニターであり、多くの研究に採用されています。EliteHRVは、PolarH10と併用することでHRV測定を行うことのできるスマートフォンソフトウェアであり、無料で利用できます。HF・LFパワーやSDNN、RMSSDなど、基本的なHRV指標をPC無しで確認できます。Kubiosは、HRV解析に広く用いられるソフトウェアであり、機能を限定したStandard版を無料で利用することができます(有料版は、エラー補正機能など、より先進的な追加機能を使用することができます)。Kubiosのマニュアルは、HRV解析を理解する上で非常に参考になります。
 これらのハードウェア・ソフトウェアの組み合わせは、心拍・心拍変動の計測と解析を行う上で、コストパフォマンスの高い優れた方法と言えます。下記動画に大まかな手順を示したので、研究や実験演習における、心拍・心拍変動解析に導入されてはいかがでしょうか。

Schaffarczyk, M., Rogers, B., Reer, R., & Gronwald, T. (2022). Validity of the polar H10 sensor for heart rate variability analysis during resting state and incremental exercise in recreational men and women. Sensors, 22(17), 6536. 

Perrotta, A. S., Jeklin, A. T., Hives, B. A., Meanwell, L. E., & Warburton, D. E. (2017). Validity of the elite HRV smartphone application for examining heart rate variability in a field-based setting. The Journal of Strength & Conditioning Research, 31(8), 2296-2302. 

Tarvainen, M. P., Niskanen, J. P., Lipponen, J. A., Ranta-Aho, P. O., & Karjalainen, P. A. (2014). Kubios HRV–heart rate variability analysis software. Computer methods and programs in biomedicine, 113(1), 210-220.

Arduinoで生理指標を測る

論文中で必要とする電子部品等は下記から入手可能です。
秋月電子
スイッチサイエンス
RS

Arduinoを用いた電子工作の参考サイト
ブレッドボードの使い方
Arduino 日本語リファレンス
アナログ回路の基礎

参考になる文献・書籍
・神崎 康宏(2012)Arduinoで計る,測る,量る : 計測したデータをLCDに表示,SDカードに記録,無線/インターネットに送る方法を解説 CQ出版 
https://ci.nii.ac.jp/ncid/BB08696362
・鈴木 哲哉(2014)ボクのArduino工作ノート 改訂版 ラトルズ 
https://ci.nii.ac.jp/ncid/BB1457169X
・増田正・大路駿介  (2020). PT・OT・アスリートのためのプログラミングとマイコン電子工作入門 (1) プログラミングとマイコンを用いた電子工作. バイオメカニズム学会誌, 44, 48-52.
https://www.jstage.jst.go.jp/article/sobim/44/1/44_48/_pdf/-char/ja
・増田正, & 大路駿介. (2020). PT・OT・アスリートのためのプログラミングとマイコン電子工作入門 (2) Arduino® マイコンと電子回路の活用. バイオメカニズム学会誌, 44(2), 119-123.
https://www.jstage.jst.go.jp/article/sobim/44/2/44_119/_pdf/-char/ja

論文中で紹介されたプログラムを掲載します。

ECG測定に用いたプログラム(Arduino)

#include <Wire.h> //I2C通信ライブラリ
#include <Adafruit_ADS1X15.h> //ADS1015ライブラリ
Adafruit_ADS1015 ads; //ADS1015クラスのインスタンス化

void setup(void)
{
  Serial.begin(115200);  //シリアル通信開始
  // ADS1015 gain 2/3  input range +/- 6.144V 
  ads.begin(); //ADS1015通信開始
}

void loop(void)
{
  int16_t results; //AD変換結果
  results = ads.readADC_Differential_0_1();  //差動入力
   
  float   multiplier = 3.0F;  // デフォルトゲイン(2/3倍)における係数
  Serial.print(results * multiplier); //得られたデジタル値をmvに換算しシリアルモニタに表示
  Serial.print(",4000,1000"); 
  Serial.println(); //シリアルモニタ改行
  delay(0);
}

EMG測定に用いたプログラム(Arduino)

#include <Wire.h>
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;

void setup(void)
{
  Serial.begin(115200);
  // ADS1015 gain 16x  input +/- 0.256V
  ads.setGain(GAIN_SIXTEEN); //ゲインを16倍に設定
  ads.begin();
}

void loop(void)
{
  int16_t results;
  results = ads.readADC_Differential_0_1();  //差動入力   

  float multiplier = 0.125F; //ゲイン16倍時の係数
  Serial.print(results * multiplier); 
  Serial.print(",100,-100"); 
  Serial.println();
  delay(0);
}

SCC測定に用いたプログラム(Arduino)

#include <Wire.h>
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;

void setup(void)
{
  Serial.begin(115200);
  // ADS1015 gain 8x  input +/- 0.512V
  ads.setGain(GAIN_EIGHT);  //ゲインを8倍に設定
  ads.begin();
}

void loop(void)
{
  int16_t results1,results2;
  results1 = ads.readADC_Differential_0_1();  //差動入力
    
  float   multiplier = 0.25F; //ゲイン8倍時の係数
  Serial.print(results1 * multiplier); 
  Serial.print(","); 
  Serial.print("194,"); //24k Ohm = 42uS
  Serial.print("-7,");  //1M Ohm = 1uS
  Serial.println();

  delay(200);
}

SCLおよびSCR測定に用いたプログラム(Arduino)

#include <Wire.h>
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;

void setup(void)
{
  Serial.begin(115200);
  // ADS1015 gain 2x  input +/- 2.048V
  ads.setGain(GAIN_TWO);        
  ads.begin();
}

void loop(void)
{
  int16_t results1,results2;
  results1 = ads.readADC_Differential_0_1();  //作動入力
  results2 = ads.readADC_Differential_2_3();
  
  float   multiplier = 1.0F;
  Serial.print(results1 * multiplier); 
  Serial.print(","); 
  Serial.print(results2 * multiplier); 
  Serial.print(",");   
  Serial.print("1111,"); //24k Ohm = 42uS
  Serial.print("-41,");  //1M Ohm = 1uS
  Serial.println();

  delay(200);
}

EMGの無線計測に用いたプログラム(Arduino)

#include "BluetoothSerial.h" //BT通信ライブラリ
#include <Wire.h>
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;
BluetoothSerial SerialBT; //BluetoothSerial クラスのインスタンス化
long t1,t0; //時間管理用変数
int cnt=0; //加算回数
int16_t results;
float multiplier = 0.125F;
float mv,average,sum; //平均値算出用変数

void setup(void)
{
  pinMode(10, OUTPUT); //内蔵赤色LEDを出力モードに
  digitalWrite(10, LOW); //内蔵赤色LEDを点灯

  Serial.begin(115200);
  SerialBT.begin("EMG32"); //Bluetooth device name
  Wire.begin(32, 33); //32端子をSDA,33端子をSCLとしてI2C通信開始
  
  // ADS1015 gain 16x  input +/- 0.256V
  ads.setGain(GAIN_SIXTEEN);
  ads.begin();
}

void loop(void)
{
  results = ads.readADC_Differential_0_1();  //作動入力   
  mv=abs(results * multiplier); //筋電位の絶対値を得る
  sum+=mv;  
  cnt++;
  
  t0=t1;
  t1=millis()/10;   //10ms間隔で平均mV数を表示
  if(t1!=t0){    
    average=sum/cnt;
    SerialBT.print(average);
    SerialBT.print(",100,0");
    SerialBT.println();
    sum=0;
    cnt=0;
  }  
}

SCCをWi-Fi経由でサーバーに送信するプログラム(Arduino)

#include <WiFi.h> //Wifiライブラリ
#include <WiFiMulti.h> //複数のアクセスポイントを管理するためのクラス
#include <HTTPClient.h>//HTTP通信ライブラリ
#include <Wire.h>
#include <Adafruit_ADS1X15.h>

WiFiMulti wifiMulti; //WiFiMultiクラスのインスタンス化
Adafruit_ADS1015 ads;

void setup(void)
{
  pinMode(10, OUTPUT);
  digitalWrite(10, LOW);

  Serial.begin(115200);
  Wire.begin(32, 33); //I2C connection
  
  ads.setGain(GAIN_EIGHT);  // ADS1015 gain 8x
  ads.begin();

  delay(4000);  //4秒待機した後に通信開始
  wifiMulti.addAP("MySSID", "MyPassword"); //WiFiアクセスポイントの登録
}

void loop(void)
{
  int16_t results;
  float multiplier = 0.25F;
  float SC;
  results = ads.readADC_Differential_0_1();  //作動入力   
  SC=results * multiplier;

  if((wifiMulti.run() == WL_CONNECTED)) { //登録したアクセスポイントに接続
    HTTPClient http; //HTTPClientをインスタンス化
    Serial.print("[HTTP] begin...\n");
    String URL="http://hogehoge.ne.jp/hogehoge/getsc.php";
    URL+="?SC="+String(SC);
    http.begin(URL); //HTTP通信開始

    Serial.print("[HTTP] GET...\n");
    Serial.println(URL);
    int httpCode = http.GET(); //URLをGET方式でリクエスト

    if(httpCode > 0) {
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);
      if(httpCode == HTTP_CODE_OK) { //通信に成功したら
          String payload = http.getString(); //通信結果をpayloadへ格納
          Serial.println(payload);
      }
    } else { //失敗したらエラーとコードを表示
      Serial.printf("[HTTP] GET... failed, error: %s\n", 
                    http.errorToString(httpCode).c_str());
    }
    http.end(); //HTTP通信終了
  }
  delay(1000); //1秒ごとに繰り返す
}

Wifi経由受信したSCCデータをサーバーに記録するプログラム(PHP)

<?
$SCval=$_GET['SC'];	//Arduinoから送られたデータを受け取る
$timestr=strftime("%Y%m%d_%H%M%S"); 	//タイムスタンプ作成

if($SCval!=""){
	$fp=fopen("SCdata.csv","a");	//追加形式でファイル開く
	fprintf($fp,"%s,%s\n",$timestr,$SCval);	//タイムスタンプとデータを保存
	fclose($fp);		//ファイル閉じる
	echo"saved SC value!";	//Arduino側へ文字列送信
}
?>


22BF学会講習会

導入
 近年、Arduino等の安価なマイクロコンピュータを用いた電子工作が広く普及しています。これらを利用し、バイオフィードバック装置を自作し、利用する方法を紹介します。
 マイクロコンピュータの多くはAD変換機能を持っているため、様々なセンサーを接続することで測定装置として利用可能です。センサーを読み取るだけでなく、LEDやスピーカー、液晶ディスプレイなどに測定結果を出力することも可能です。シングルタスクOSを搭載しているため、測定値のリアルタイム処理できる点や、比較的コンパクトかつ低価格で装置を構成可能な点も、バイオフィードバックへの応用に適しています。
 本講習会では、末梢皮膚温変化を題材に、ハードウェア・ソフトウェアの両面から、バイオフィードバック装置の動作を学びます。 

装置の構成
 マイクロコンピュータはArduinoUNOを、皮膚温センサーはLM35DZを用います。フィードバック部分は、視覚フィードバックにカラーLEDモジュールを、聴覚フィードバックにスピーカーを用います。

測定の方法
 センサーはメンディングテープを用い、非利き手人差し指の腹側に装着する。テープをきつく巻き過ぎて指が圧迫されないように注意する。計測中は手のひらを上に向ける。下側に向けると机の温度を測ることになるので注意。室内温度は23~25℃程度に調節し、エアコンの風が直接指先にあたらないよう、風量・風向を調節する。


学習の手順
 ここでは下記の順序でバイオフィードバック機器の動作を学んでいきます。

プログラム名:ADC・・・AD変換の基礎
プログラム名:temp01・・・基本的な皮膚温測定
プログラム名:temp02・・・より細かく皮膚温を測定する方法
プログラム名:Tone・・・聴覚フィードバックに用いるスピーカーの使い方
プログラム名:ColorLED・・・視覚フィードバックに用いるスピーカーの使い方
プログラム名:TempABF・・・聴覚を用いた皮膚温バイオフィードバックの方法
プログラム名:TempVBF・・・視覚を用いた皮膚温バイオフィードバックの方法
プログラム名:TempAVBF・・・視聴覚を用いた皮膚温バイオフィードバックの方法

 使用するプログラム一式は下記のアドレスからダウンロードできます。Zip形式で圧縮されているため、必ず展開してから使用してください(圧縮されたままだとプログラムが動作しません)。

 ハードウェアの組み立ては、各プログラムの動画と下の画像をご覧ください。

プログラム:ADC
 AD変換は生体計測をする上で欠かすことのできない方法です。ここでは、ArduinoUNOを用い、皮膚温センサーから得られる電圧をAD変換し、数値として表示する方法を学びます。

//A0に入力された値をAD変換し表示
void setup() {
  Serial.begin(115200);        // シリアル通信の初期化
}

void loop() {
  int val = analogRead(0);
  Serial.println(val);        //analog0の値を表示
  delay(1000);
}

プログラム:temp01
 AD変換された値には単位がありません。デジタル化された値をmVに換算することで、温度を表示する方法を学びます。今回用いる皮膚温センサーは、1℃あたり10mVの出力があるため、mV値を0.1倍することで温度を得ることができます。

//1秒間隔でデジタル値を取得して温度に変換し表示
double mv,temp;

void setup() {
  Serial.begin(115200);         // シリアル通信の初期化
  analogReference(INTERNAL);    //参照電圧を1.1Vに設定
}

void loop() {
  mv = (double)1100/(double)1024 * analogRead(0); //デジタル値を電圧に変換
  temp=mv*0.1;           //電圧を温度に変換
  Serial.print(mv);  
  Serial.print(",");
  Serial.print(temp);   //温度表示
  Serial.println();
  delay(1000);          //1000ms停止
}

プログラム:temp02
 皮膚温の精神的な変化は微弱であるため、自己制御を実現するためには、0.01℃程度の変化を測定する必要があります。ここでは、1秒間の平均値を算出することで、より細かく皮膚温を調べる方法を学びます。

//1秒間隔で可能な限りデジタル値を取得して平均を表示
double mv,temp;
double sum,average;
long t,t0,cnt;

void setup() {
  Serial.begin(115200);        // シリアル通信の初期化
  analogReference(INTERNAL);    //参照電圧を1.1vに設定
  cnt=sum=0;
}

void loop() {
  t0=t;
  t=millis();   //現在時刻をms単位で取得
  mv = (double)1100/(double)1024 * analogRead(0); //デジタル値を電圧に変換
  sum=sum+mv;   //mv値の足し込み
  cnt++;
  if(t0/1000 !=t/1000){       //1秒(1000ms)毎に平均算出
    average=sum/(double)cnt;  //平均を計算
    temp=average*0.1;         //電圧を温度に変換
    Serial.print(cnt);        //サンプル数表示
    Serial.print(",");
    Serial.print(average);    //平均電圧を表示
    Serial.print(",");
    Serial.print(temp);       //温度を表示
    Serial.println();
    sum=0;cnt=0;              //合計値とカウントを初期化
  }
}

プログラム:Tone
 聴覚フィードバックに用いるスピーカーの使用方法を学びます。tone命令を用いることで、出力する音の周波数と、長さを指定できます。プログラム中の12は、デジタルポートの12番から音を出す事を意味します。

//スピーカーから500Hz,1000Hzの音を表示
void setup() {
}

void loop() {
  tone(12, 500, 100);   //500Hz 100ms
  delay(1000);          //1000ms停止
  
  tone(12, 1000, 100);  //1000Hz 100ms
  delay(1000);          //1000ms停止
}

プログラム:ColorLED
 視覚フィードバックに用いるカラーLEDの使用方法を学びます。カラーLEDの制御には、Adafruit_NeoPixelライブラリを用います(ライブラリのインストール方法は動画をご覧ください)。プログラム冒頭で、デジタルポートの13番を制御用に使うこと、LEDの数が1個であることを定義しています。プログラムは、setup関数で初期化を行い、loop関数でLEDの点灯を処理しています。色のセットは、pixels.clear→pixels.setPixelColor→pixels.showの順で行います。pixels.Color(R, G, B)の形式で、色の強さは0~255で指定します。

//カラーLEDを用い、赤・緑・青色を点灯
#include <Adafruit_NeoPixel.h>
#define PIN 13        //制御用ピン
#define NUMPIXELS 1   //LEDの数
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();     //LEDを初期化
}

void loop() {
  pixels.clear();
  pixels.setPixelColor(0, pixels.Color(20, 0, 0));  //赤
  pixels.show();
  delay(1000);    //1000ms停止

  pixels.clear();
  pixels.setPixelColor(0, pixels.Color(0, 20, 0));  //緑
  pixels.show();
  delay(1000);    //1000ms停止

  pixels.clear();
  pixels.setPixelColor(0, pixels.Color(0, 0, 20));  //青
  pixels.show();
  delay(1000);    //1000ms停止
}

プログラム:TempABF
 皮膚温変化を聴覚刺激として2値(上昇もしくは下降)でフィードバックします。上昇・下降は23行において求められる皮膚温変化量によって判定します。25行から27行では、温度変化に応じて上昇下降の方向を求めています。29行から34行では、変化の方向に応じてフィードバックする音を、500Hzもしくは1000Hzに振り分けています。36行から41行は、現在温度や変化量をシリアルモニタに表示しています。

//皮膚温を聴覚でフィードバック
double mv,temp0,temp1,tempd;
double sum,average;
long t,t0,cnt;

void setup() {
  Serial.begin(115200);         // シリアル通信の初期化
  analogReference(INTERNAL);    //参照電圧を1.1vに設定
  cnt=sum=0;
}

void loop() {
  t0=t;
  t=millis(); //現在時刻をms単位で取得
  mv = (double)1100/(double)1024 * analogRead(0); //デジタル値を電圧に変換
  sum=sum+mv;
  cnt++;  
  if(t0/1000 !=t/1000){   //1秒(100ms)毎に平均算出
    average=sum/(double)cnt;  //平均を計算

    temp0=temp1;          //前回の温度を保存
    temp1=average*0.1;    //電圧を温度に変換
    tempd=temp1-temp0;    //温度変化量を算出

    int mag,dir;  //変化の程度(mag)と方向(dir)
    if(tempd>0){mag=abs(tempd*100); dir=1;}   //温度上昇時
    if(tempd<=0){mag=abs(tempd*100); dir=-1;} //温度下降時
       
    if(dir==1){
      if(mag>0){tone(12, 500, 10);}                     //温度上昇時は500Hz
    }
    if(dir==-1){
      if(mag>0){tone(12, 1000, 10);}                    //温度下降時は1000Hz
    }

    Serial.print(temp1);  Serial.print(",");  //現在温度
    Serial.print(tempd);  Serial.print(",");  //温度変化量 
    Serial.print(mag);    Serial.print(",");  //変化の強さ
    Serial.println();
    sum=0;cnt=0;
    delay(10);  
  }
}

プログラム:TempVBF
 皮膚温変化を視覚刺激として色と強さでフィードバックします。具体的には、上昇時は赤、下降時は青でLEDが点灯し、皮膚温の変化量が大きいほど明るく光ります。変化量の算出、上昇・下降の判定方法はTempABFと同様です。視覚フィードバックを処理する34行から41行では、上昇の場合に

1
pixels.Color(mag, 0, 0)
で赤色に、下降の場合には
1
pixels.Color( 0, 0,mag)
で青色に、LEDを点灯させています。magには、皮膚温変化量tempdを100倍した値が入っていますので、変化量が大きいほど強く光ります。それ以外の処理は、TempABFと同様です。

//皮膚温を視覚でフィードバック
#include <Adafruit_NeoPixel.h>
#define PIN        13 
#define NUMPIXELS 1
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
double mv,temp0,temp1,tempd;
double sum,average;
long t,t0,cnt;

void setup() {
  Serial.begin(115200);         // シリアル通信の初期化
  analogReference(INTERNAL);    //参照電圧を1.1vに設定
  cnt=sum=0;
  pixels.begin();               //LEDを初期化
}

void loop() {
  t0=t;
  t=millis(); //現在時刻をms単位で取得
  mv = (double)1100/(double)1024 * analogRead(0); //デジタル値を電圧に変換
  sum=sum+mv;
  cnt++;  
  if(t0/1000 !=t/1000){   //1秒(100ms)毎に平均算出
    average=sum/(double)cnt;  //平均を計算

    temp0=temp1;          //前回の温度を保存
    temp1=average*0.1;    //電圧を温度に変換
    tempd=temp1-temp0;    //温度変化量を算出

    int mag,dir;  //変化の程度(mag)と方向(dir)
    if(tempd>0){mag=abs(tempd*100); dir=1;}   //温度上昇時
    if(tempd<=0){mag=abs(tempd*100); dir=-1;} //温度下降時
       
    pixels.clear();
    if(dir==1){
      pixels.setPixelColor(0, pixels.Color(mag, 0, 0)); //温度上昇を赤で表示
    }
    if(dir==-1){
      pixels.setPixelColor(0, pixels.Color( 0, 0,mag)); //温度下降を青で表示
    }
    pixels.show();

    Serial.print(temp1);  Serial.print(",");  //現在温度
    Serial.print(tempd);  Serial.print(",");  //温度変化量 
    Serial.print(mag);    Serial.print(",");  //変化の強さ
    Serial.println();
    sum=0;cnt=0;
    delay(10);  
  }
}

プログラム:TempVABF
 TempVABFは、視覚・聴覚、双方のフィードバックを行うものです。内容は、TempVBFに聴覚フィードバック処理(37・41行部分)を追加しただけです。

//皮膚温を視聴覚でフィードバック
#include <Adafruit_NeoPixel.h>
#define PIN        13 
#define NUMPIXELS 1
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
double mv,temp0,temp1,tempd;
double sum,average;
long t,t0,cnt;

void setup() {
  Serial.begin(115200);         // シリアル通信の初期化
  analogReference(INTERNAL);    //参照電圧を1.1vに設定
  cnt=sum=0;
  pixels.begin();               //LEDを初期化
}

void loop() {
  t0=t;
  t=millis(); //現在時刻をms単位で取得
  mv = (double)1100/(double)1024 * analogRead(0); //デジタル値を電圧に変換
  sum=sum+mv;
  cnt++;  
  if(t0/1000 !=t/1000){   //1秒(100ms)毎に平均算出
    average=sum/(double)cnt;  //平均を計算

    temp0=temp1;          //前回の温度を保存
    temp1=average*0.1;    //電圧を温度に変換
    tempd=temp1-temp0;    //温度変化量を算出

    int mag,dir;  //変化の程度(mag)と方向(dir)
    if(tempd>0){mag=abs(tempd*100); dir=1;}   //温度上昇時
    if(tempd<=0){mag=abs(tempd*100); dir=-1;} //温度下降時
       
    pixels.clear();
    if(dir==1){
      pixels.setPixelColor(0, pixels.Color(mag, 0, 0)); //温度上昇を赤で表示
      if(mag>0){tone(12, 500, 10);}                     //温度上昇時は500Hz
    }
    if(dir==-1){
      pixels.setPixelColor(0, pixels.Color( 0, 0,mag)); //温度下降を青で表示
      if(mag>0){tone(12, 1000, 10);}                    //温度下降時は1000Hz
    }
    pixels.show();

    Serial.print(temp1);  Serial.print(",");  //現在温度
    Serial.print(tempd);  Serial.print(",");  //温度変化量 
    Serial.print(mag);    Serial.print(",");  //変化の強さ
    Serial.println();
    sum=0;cnt=0;
    delay(10);  
  }
}

補足

・温度センサーに関して
現在温度センサーLM35DZは入手が難しくなっており、TMP36GT9Zを代用することができます。その場合は、温度算出部分を下記のように変更してください。

temp1=average*0.1-50;    //電圧を温度に変換

・使用した電子部品の入手先
ArduinoUNO
TMP36GT9Z
カラーLEDモジュール
スピーカー
ピンヘッダー
収縮チューブ
細径3心並列線

2020環境心理学会WS

今回はUnityを使ってオンライン実験を行うことを想定したワークショップです。Unityのインストールから基本的な操作方法の学習、FPS(1人称視点)コントローラの導入、景観の作成、Web上での公開方法までを扱います。これらの動画では、以下のアセット(ソフトウェア資産)を使います。
Standard Assets (for Unity 2018.4)
AllSky Free – 10 Sky / Skybox Set
Rock package

1.Unityのインストール
開発元は、UnityHubを使ったインストールを推奨しているようなので、ここでもまずはUnityHubをインストールし、つぎにダウンロードアーカイブからUnityをインストールします。Unity にはたくさんのバージョンがありますが、ここでは2019.4.12をインストールしておくようにしてください。

2.Unityエディターを使う
視点の変更やオブジェクトの配置など、エディタ操作の基本を学びます。Unityエディターを使うにはUnityでアカウントを登録する必要があります。googleアカウントを持っている方は、それを使うこともできます。また、Visual Studio(プログラムエディタ)を使うために、Microsoftのアカウントが必要になりますので、必要に応じて用意してください。

「マウス中央ボタンドラッグ」で視点移動
「Alt+マウス左ボタンドラッグ」で視点回転
「F」キーで選択中のオブジェクトを視点の中心にする

3.FPSコントローラを設定する
FPSコントローラーを配置し、一人称視点で世界の中を動き回る方法を学びます。FPSコントローラで飛び石を渡る簡単なゲームを作成してみます。

「Ctrl+D 」でオブジェクトを複製する(Duplicate)
「Ctrl+P」でゲームモードに移行するorエディターに復帰する

4.Terrain(地形)を作成する
Unityを使った地形作成は、箱庭を作っているような楽しさがあります。動画では大急ぎで作っていますが、少し時間をかけてディテールにこだわって作っていただくと、楽しく学べると思います。青空、夕空、曇り空の3種類を作り、スクリプトで切り替えられるようにします。

5.WebGL形式でビルドして公開する
公開しないと閲覧してもらえないので、WebGL形式でのビルドは非常に重要なプロセスです。しかし、WebGL自体が比較的新しい技術ということもあり、難易度は高めです。パソコンに登録されたユーザー名や作業フォルダ名に日本語が含まれていると、ビルドに失敗する事があるようです。

自分のWebサイトをもっていない場合は、UnityRoomのようなWebGL形式のコンテンツを無料で公開させてくれるサイトを利用する事も可能です(ただし広告が出ます)。レンタルサーバーはさくらインターネットロリポップなどで年間5000円程度で借りる事ができるので、実験用にはこちらが良いと思います。

愛知淑徳大学WS

2017年1月21日 愛知淑徳大学にてWSを開催します。(工事中!)

パワーポイントファイル:PV170118
スライド1 スライド2 スライド3 スライド4 スライド5 スライド6 スライド7 スライド8 スライド9 スライド10 スライド11 スライド12 スライド13 スライド14 スライド15 スライド16 スライド17 スライド18

プログラム一式:ArduPV

持参品リスト

・工具
・Arduino
・USBメモリ
・工具箱
・センサー等部品一式
・資料
・サンプル計測機
・感度調整用ドライバーはどうする?
などなど・・・

2015日本心理学会TWS

日本心理学会第79会大会において、チュートリアルワークショップを開催しました。

SvHbRozEQCiiRaO2UtOWu6ngctu4gE58SmCMK1Qc3PNuIK855xqWq02NDZ_fVTssGRfexw=w1045-h728

当日は約30名の方々にご参加いただくことができました。前半は、皮膚温測定器を用いた実験を、後半は心電図測定器により心拍ゆらぎを測定する実験を行いました。後半はやや時間不足となりましたが、概ね予定通りの実験を時間内にこなすことができました。

当日用いた資料:TWS2015A_150924

後半の心拍ゆらぎに関しては、一部しか結果を報告できなかったため、下記に参加してくださった方の平均値を示します。

TWS2015

実験のスケジュールは、安静5分、パズル課題5分、再び安静5分というものでした。パズル課題中(赤線枠内)、IBIは減少し、RMSSDに関しても、やや低下がみとめられました。測定したデータに関しては、6名中5名が問題なく解析可能でしたが、1名の方に関しては、おそらくR波検出スレッショルドが適切でなかったため、ほとんどの期間が解析不能でした。したがって、上記グラフは5名分の結果で算出しました。

来年以降も、機会があれば再びWSを開催したいと思います。参加してくださった皆さま、ありがとうございました。