Shigeta のすべての投稿

修論進捗_241009

1. BF投稿論文の提出書類
・投稿申請書:記入事項多め(原稿の種類,印鑑等)
・著作権譲渡に関する同意書:申請日の記入のみ
・利益相反自己申告書:申請日の記入のみ

2. 修論:食事アドバイスを今度こそ…:
食事画像のアップロードリンク:ID入力欄,写真アップロード,VAS項目を追加
・3食分の食事画像を1枚にまとめるのをphpから行った
・UnityのAIアドバイザーと連携:出力結果は下記画像の通り

食事アドバイスの出力結果
 mes2:カレーライス、いい匂いしそうで美味そうだよな!ただ、栄養のバランスを考えるなら少し工夫も必要かも。たとえば、もう少し野菜の種類を増やして緑黄色野菜を一緒に摂るのが良いだろうよ。ブロッコリーやほうれん草、あるいはサイドにサラダを用意するとか。デザートには果物を選べば、ビタミンも補えるしな。あとは、カレーのルーの塩分も考えると、できれば手作りでコントロールするのがベスト。それでも十分楽しむことができるぜ。結局は、無理に改善しようとせず、少しずつ、より健康的な選択を取り入れていくと良いだろうよ。,7,6

====================================================

メモ
・BF然り,気づきが重要:Formsの主観報告に追加する

BF論文2_効果量一覧

 統計ソフトjamoviによる効果量の算出方法がわかったため,BF論文で記述する統計処理の効果量を片っ端からスクリーンショットを貼り付けていく。ここでの効果量は,偏η²とする。出力結果のデータファイルを下記に添付した。

1. 3週間の訓練効果_3要因参加者内(訓練回×訓練方向×期間)
・反復測定分散分析で,参加者内のところのみに投入したらできた。

2. 性別と皮膚温制御成績_3要因混合(性別×訓練回×訓練方向)

3. 内受容感覚とBF_3要因混合(群×訓練方向×期間)

①気づき因子
・群×訓練方向×期間の交互作用は,惜しくも単純・単純主効果の検定(0.9と0.8)で有意でなく断念。これ出てたら,勝ちだったな…

②注意制御因子
・群×訓練方向×期間の交互作用のp値が0.100であと少しで有意傾向だった
→グラフ見たら,低群の方がうまくいってた

③感情への気づき因子

第2因子

第3因子

第6因子

第7因子

第8因子

4. 自宅での訓練回数と制御成績_3要因混合(群×訓練回×訓練方向)

===============================================================*何故かわからないが,交互作用の多重比較だけjs-starと一致しない。一致しないだけならソフト使用上仕方がないが,一番の問題は「訓練方向×期間」の下位検定で有意差が出ないため,根本的にjamovi側に問題がある気がする(js-star, HAD, Rは出てた気がする)。設定ミスもあるかもしれないが,資料が不足しているためどうすることもできない現状にある…

修論進捗_240925

進捗メモ

1. 修士論文の研究計画書:10/10(木)の15時までに,教務グループに提出

2. BF学会に投稿する論文2の執筆(内受容感覚と皮膚温BF)
・序論:何を書くか決める,内受容感覚の部分を文献で補強
・目的~結果:記入終えました
・考察:BFの制御段階を入れれば完成,構成を整える

*論文2は,来週のゼミまでに完成させる予定です。

3. 食事のアドバイス:リンク
 参加者が食べたものをスマホで撮影しHTTPSサーバーにアップロードするphpスクリプトを作成。これから,UnityのFitbitアドバイザーが参照できるようにする予定です。

今後,追加する内容
・いつご飯を食べたか選択式にする
・Unityでも参照できるように,fitbitデータの各ユーザーディレクトリに保存する
・食事面での知識をAIアドバイザーに与えておく

============================================================

先生からのコメント
 論文2の方は,統計ソフトJamoviを用いて効果量を求める。修論の開発アプリに関しては,Fitbitアプリにない物と組み合わせて新しいものを作る。その日の気分や仕事の忙しさを5件法やVASなどで評価する。

修論進捗_240918

進捗内容

1. FitbitMaxでのAIアドバイザーのアプリ開発
・模式図を作成した
・安静時心拍数(RHR)のデータ取得も可能になり,グラフの追加を行った
・AgentRPの写真撮影機能も追加した
・Fitbitから取得したグラフを参照できるようになり,アドバイス可能になった

2. ランキング形式用のサイト開発:確認リンク
・同意を得ている重田・庵袋さん・長野先生のデータを基に開発してます。
・データは,「歩数と消費カロリー」の運動量に絞るか思案中です。
・機能としては、ドロップダウンから過去データの閲覧・指標の任意選択が可能。
・総合スコアの算出方法は,各指標で基準点を設ける予定です。

3. BF学会投稿予定の2本目の論文(内受容感覚がBF訓練に与える影響)を作成中
・導入と考察をまとめれば完成

=============================================================

今後の予定
・BF論文1(装置開発)の修正を行いたいので,お時間のある時にお願いします
・10/10(木)までの修論計画書を来週までに作成する

先生からのFBメモ
・Unityのアドバイス画面のサイズを整える
・グラフの修正:日付部分の表記(日付のみ)、フォントサイズの調整等
・食事面のアドバイスは、食事AIとか探してもカロリー計算をしているのが探した感じないから(アプリは除く)、GPT-4oにさせる。GPTで完結
・模式図の修正:グラフ送信と書かないとわからない
・来年に、生理心理学会・健康心理学会で発表する。内容は、主観的幸福感と運動・心臓血管反応についてで、今年の11月下旬に九州で学会発表があるようだ。
・物体認識等:OpenCV(約1万円)と、YOLOv10(オープンソース)がある。
・軍人のスキン:永久先生もおっしゃてたが、影があると暗い印象になるため、肌艶よく明るめのほうがいいようだ。そうか、軍人の人格設定も変えないとだ。

皮膚温バイオフィードバック装置_プログラム解説

導入
 バイオフィードバック(以下,BF)は,心身のリラクセーション法として有効な手法であり,脳波や心拍数,筋電図,血圧などの生理的指標を自己制御可能にする訓練方法です。特に,末梢皮膚温度(以下,皮膚温)は,測定が容易で反応が理解しやすく,リラクゼーション効果を得るために重要です。しかし,従来のBF装置は高価であり,専門施設でしか利用できないため,その普及が阻まれています。本研究は,オープンソースハードウェアのArduinoとデジタルファブリケーション技術を活用し,低コストで持ち運び可能な皮膚温BF装置を開発し,自宅でも手軽に訓練を行えるようにすることを目的としています。

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

使用した電子部品の入手先
 使用した部品の入手先については,下記の部品一覧の「こちら」を選択すると,販売先のサイトに移動できます。

・MakerNano:こちら
・LM35DZ:こちら
・スピーカー:こちら
・RGBLED:こちら
・液晶ディスプレイ:こちら
・DCDCコンバータ:こちら
・ピンヘッダー:こちら
・熱収縮チューブ:こちら
・細径3心並列線:こちら
・電池ボックス:こちら
・単4電池:こちら

測定方法
 温度センサは,指サックを用いて,非利き手の第2指腹測部に直接固定します。次に,右側面(下部)にある電源スイッチを入れて装置を起動します。安静時は,装置左下部のスイッチを右にスライドさせFBを無効にし,訓練時は左にスライドしてFBを有効にします。注意点として,温度センサに風が直接当たらない場所で訓練をしてください。詳しい訓練方法については,下記の2分程度の動画をご覧ください。

プログラムの解説
 ここでは,皮膚温BF装置の開発に用いたソフトウェアの説明を行います。使用したソフトウェアは,長野(2022)を参考にArduino開発環境1.8.19と,長野(2016)を参考にProcessing開発環境3.5.4のプログラムの作成を行いました。

1. Arduino開発環境

プログラム名説明
st7032液晶ディスプレイで,皮膚温を数値化
Adpatesion安静期間の自動管理化
switch光や音のFB有無の切り替え
BFst7032_230705プログラムの全体像

2. Processing開発環境

プログラム名説明
SBF230707皮膚温のグラフ表示とCSV形式の発行
Subroutine1現在時刻と経過時間

1. Arduino開発環境

プログラム:st7032
 液晶ディスプレイ部分に該当するプログラムです。GNDと5Vで電源供給を行い,アナログピンの4,5番からI2C通信(Inter-Integrated Circuit)を行い,マイコンと液晶ディスプレイの間でデータの送受信を行っています。1行目で,液晶ディスプレイの制御に必要な「ST7032_asukiaaa.h」ライブラリをインクルードし,バージョン1.0.5を使用しました。3行目で,液晶ディスプレイの制御を行うために「lcd」とクラスをインスタンス化しています。これにより,11行目で表示領域8列2行とし,12行目でコントラスト(文字の濃さ)0~63を指定するなど液晶ディスプレイを制御する機能を呼び出しています。6行目のsetup関数でこれらの初期化を行っています。5行目のtemp1,tempdは,現在温度,1秒間で生じた温度の変化量を格納しています。15~24行目は安静期間の自動管理化を行っています。安静期間終了の条件を満たした場合,液晶ディスプレイの7列0行(画面右上)に「*」を表示し,満たされない場合,何も表示しませんでした。26~28行目は,Stringオブジェクトのbuf1,buf2を用いることで,temp1,tempdのデータを文字列に変換し,表示するための準備を行っています。30~33行目では,液晶ディスプレイの画面1行目にbuf1から現在温度,2行目にbuf2から1秒間で生じた温度の変化量をStringオブジェクトから「.c_str()」メソッドを用いて表示しました。これらを15行目のloop関数で繰り返し実行しました。

#include <ST7032_asukiaaa.h>    //液晶ディスプレイのライブラリ

ST7032_asukiaaa lcd;  //液晶ディスプレイのライブラリを使用

double temp1, tempd;   //温度データを格納する変数

double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温度

void setup() {
  lcd.begin(8, 2);     //液晶ディスプレイで8列2行で指定表示
  lcd.setContrast(5);  //液晶ディスプレイのコントラスト
}

void loop() {
  //3分間の皮膚温の変動が0.4℃以内の時にアスタリスク表示
  if (maxTemp - minTemp <= 0.4) {   //最大・最小温度の差が0.4℃以内の場合
    lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
    lcd.print("*");             //「*」表示
    readyFlg=true;
  } else {                      //条件を満たさなかった場合
    lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
    lcd.print(" ");             //何も表示しない
  }

  String buf1, buf2;
  buf1 += temp1;      //buf1は現在温度
  buf2 += tempd;      //buf2は温度変化量

  lcd.setCursor(0, 0); //液晶ディスプレイの1行目にカーソルを移動
  lcd.print(buf1.c_str()); //1行目に現在温度を表示
  lcd.setCursor(0, 1);    //液晶ディスプレイの2行目にカーソルを移動
  lcd.print(buf2.c_str());  //2行目に温度変化量を表示
}

プログラム:Adpatesion 
 このプログラムは、安静期間の自動管理化を行うため、大河内(1990)の安静期間の処理を参考に行っています。具体的には、1分ごとの皮膚温の平均値が連続する3分間で増減せず、その範囲が0.4℃以内である場合に安静期間の終了を示す「*」を液晶ディスプレイに表示します。プログラム内では、現在の温度を格納する変数temp1、平均値計算用のsum、average、cntが定義され、連続する3分間(180サンプル)の温度データを保持するための配列historyと、そのデータを格納するインデックスhistory Indexが用意されています。history Indexは180を超えると自動的に最初の位置に戻り、配列の境界をループしながらデータを保存します。初期化として、最小・最大温度を0℃で設定し、取得した温度データをhistory配列に格納、次のインデックスを更新します。履歴内のデータと比較して、現在の最小・最大温度を更新し、連続する180サンプルが増減なく0.4℃以内であるかをチェックします。条件が満たされた場合はArduinoのシリアルモニタ内に「***」を表示し、満たされない場合は「—」を表示しました。温度データの取得と処理は10msごとに行われ、安静期間の判定と表示が繰り返されることで、安静状態をモニタリングします。この処理により、安静期間の終了を正確に管理し、リアルタイムで液晶ディスプレイにフィードバックを提供します。

double temp1;   //現在温度のデータを格納する変数
double sum, average;    //温度データの合計値と平均値を格納
long cnt;      //温度データのサンプリング数

const int HISTORY_SIZE = 180; // 履歴のサイズ
double history[HISTORY_SIZE]; // 測定値の履歴
int historyIndex = 0;         // 履歴のインデックス

double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温度

bool readyFlg=false;  //安静期間のフラグ


void loop() {

    if(readyFlg==true){
      Serial.print(",***"); //安静期間終了時に「***」表示
    }
    else{
      Serial.print(",---"); //安静期間が終了していない場合「---」表示
    }
    
    Serial.println();   //シリアルモニタに「***/---」を表示するための空行
    sum = 0;    //合計値の初期化
    cnt = 0;
    delay(10);  //10ms単おきに行う

    history[historyIndex] = temp1;  //温度データを記録するための配列
    historyIndex = (historyIndex + 1) % HISTORY_SIZE;  //新しい温度データを保存

    minTemp = history[0];   //最小温度をデータの最初の要素で初期化
    maxTemp = history[0];   //最大温度をデータの最初の要素で初期化

    for (int i = 1; i < HISTORY_SIZE; i++) {  //温度データから最小・最大温度を算出
      if (history[i] < minTemp) {   //データ内で最小温度より低い場合
        minTemp = history[i];       //最小温度を更新
      }
      if (history[i] > maxTemp) {    //データ内で最大温度より低い場合
        maxTemp = history[i];        //最大温度を更新
      }
    }

    //3分間の皮膚温の変動が0.4℃以内の時にアスタリスク表示
    if (maxTemp - minTemp <= 0.4) {   //最大・最小温度の差が0.4℃以内の場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print("*");             //「*」表示
      //Serial.println("***");
      readyFlg=true;
    } else {                      //条件を満たさなかった場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print(" ");             //何も表示しない
    }

プログラム:switch
 スライドスイッチを用いて,光や音のFB有無の切り替えを行うプログラムです。1行目では視覚FBに用いるLEDの制御にAdafruit_NeoPixelライブラリ(Ver1.10.7)をインクルードし,2行目では液晶ディスプレイの制御にST7032_asukiaaa.hライブラリ(Ver1.0.5)をインクルードしました。4,5行目では,LEDの制御ピン番号,使用するLEDの個数を指定し,7行目ではこれらをもとにAdafruit_NeoPixelクラスをインスタンス化しています。9,10行目で,FB有無の切り替えに用いるスイッチピン7,ブザーピン2など制御に用いるピン番号を指定しています。13行目で,スイッチの状態を入力モードにして,「INPUT_PULLUP」から内部プルアップ抵抗を有効にして,ピンの状態がHigh(オン)になっています。14行目で,ブザーのピンを出力モードに設定して,制御するための処理を行い,これらをsetup関数から初期化しています。17行目では,スイッチの状態(オン/オフ)を読み取ります。19行目で,スイッチの状態がオフの時に19~32行目で実行するFB有の状態にしました。温度が上昇した際(dir==1)に視覚FBのLEDライトが赤く点灯し,聴覚FBのブザー500Hzを10ms間出力しました(19~26行目)。温度が下降した際(dir==-1)に視覚FBのLEDライトが青く点灯し,聴覚FBのブザー1000Hzを10ms間出力しました(27~32行目)。33~35行目では,FB無しの状態を割り当て,スイッチの状態がオフの時にLEDライトが消灯し,ブザーが消音するようにFBの有無の切り替えを行いました。

#include <Adafruit_NeoPixel.h>  //LEDのライブラリ
#include <ST7032_asukiaaa.h>    //液晶ディスプレイのライブラリ
 
#define PIN        3   //LEDの制御ピン
#define NUMPIXELS 1    //LEDの数
 
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);   //LEDのライブラリを使用

const int switchPin = 7;    //FB有無の切り替えに用いるスイッチ
int Bpin = 2;  //ブザーPIN2

void setup() {
  pinMode(switchPin, INPUT_PULLUP);  // 内部プルアップ抵抗を有効にする
  pinMode(Bpin, OUTPUT);

void loop() {
  int switchState = digitalRead(switchPin);
 
  if (switchState == LOW) {   //FBスイッチがオフの時に
    pixels.clear();
    if (dir == 1) {
        pixels.setPixelColor(0, pixels.Color(mag, 0, 0)); //温度上昇を赤で表示
      if (mag > 0) {
        tone(Bpin, 500, 10);    //温度上昇時は500Hz
      }
    }
    if (dir == -1) {
      pixels.setPixelColor(0, pixels.Color(0, 0, mag)); //温度下降を青で表示
      if (mag > 0) {
        tone(Bpin, 1000, 10);   //温度下降時は1000Hz
      }
    }
  } else if (switchState == HIGH) {         //左下のスイッチを入れるとFBなし
    pixels.setPixelColor(0, pixels.Color(0, 0, 0));    //LEDを消す
    noTone(Bpin);         //ブザー音を消す
  }

プログラム:BFst7032_230705
 長野(2022)が作成した皮膚温BFプログラムと,上記で開発したプログラムを統合した全体像です。温度センサーから入力された値をもとに,「現在温度,温度変化量,変化の強さ,サンプリング数,安静期間終了の判定」が算出されます。温度が上昇したときに,LEDが赤く点灯し,ブザーから500HzでFBが行われます。温度が下降したときにLEDが青く点灯し,ブザーから1000HzでFBが行われました。温度変化が顕著に行われない場合LEDの色は変化せず,温度の変化量が大きい場合はLEDがより強く点灯しました。1秒毎に平均値の算出および各種FBなどが行われました。これらの機能を用いて,BF装置のプログラムを作成しました。

#include <Adafruit_NeoPixel.h>  //LEDのライブラリ
#include <ST7032_asukiaaa.h>    //液晶ディスプレイのライブラリ

#define PIN        3   //LEDの制御ピン
#define NUMPIXELS 1    //LEDの数

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);   //LEDのライブラリを使用
ST7032_asukiaaa lcd;        //液晶ディスプレイのライブラリを使用

const int switchPin = 7;    //FB有無の切り替えに用いるスイッチ

double mv, temp0, temp1, tempd;   //温度データを格納する変数
double sum, average;    //温度データの合計値と平均値を格納
long t, t0, cnt;      //現在の時刻・温度データをサンプリングした時の時刻・温度データのサンプリング数
int Bpin = 2;  //ブザーPIN2

const int HISTORY_SIZE = 180; // 履歴のサイズ
double history[HISTORY_SIZE]; // 測定値の履歴
int historyIndex = 0;         // 履歴のインデックス

double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温度

bool readyFlg=false;  //安静準備フラグ

void setup() {
  pinMode(switchPin, INPUT_PULLUP);  // 内部プルアップ抵抗を有効にする

  pinMode(Bpin, OUTPUT);
  pinMode(13, OUTPUT);
  Serial.begin(115200);  //シリアル通信115200
  analogReference(INTERNAL); //参照電圧を1.1Vに設定
  cnt = sum = 0;
  pixels.begin();      //LEDを初期化

  lcd.begin(8, 2);     //液晶ディスプレイで8列2行で指定表示
  lcd.setContrast(5);  //液晶ディスプレイのコントラスト
}

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;                    //温度上昇時
    } else if (tempd <= 0) {
      mag = abs(tempd * 100);
      dir = -1;                   //温度下降時
    }

    int switchState = digitalRead(switchPin);

    if (switchState == LOW) {   //FBスイッチがオフの時に
      pixels.clear();
      if (dir == 1) {
        pixels.setPixelColor(0, pixels.Color(mag, 0, 0)); //温度上昇を赤で表示
        if (mag > 0) {
          tone(Bpin, 500, 10);    //温度上昇時は500Hz
        }
      }
      if (dir == -1) {
        pixels.setPixelColor(0, pixels.Color(0, 0, mag)); //温度下降を青で表示
        if (mag > 0) {
          tone(Bpin, 1000, 10);   //温度下降時は1000Hz
        }
      }
    } else if (switchState == HIGH) {         //左下のスイッチを入れるとFBなし
      pixels.setPixelColor(0, pixels.Color(0, 0, 0));    //LEDを消す
      noTone(Bpin);         //ブザー音を消す
    }

    pixels.show();

    Serial.print(temp1); //現在温度
    Serial.print(",");   //カンマ区切り
    Serial.print(tempd); //温度変化量
    Serial.print(",");
    Serial.print(mag);   //変化の強さ
    Serial.print(",");
    Serial.print(cnt);   //サンプリング数

    if(readyFlg==true){
      Serial.print(",***"); //安静期間終了時に「***」表示
    }
    else{
      Serial.print(",---"); //安静期間が終了していない場合「---」表示
    }
    
    Serial.println();   //シリアルモニタに「***/---」を表示するための空行
    sum = 0;    //合計値の初期化
    cnt = 0;
    delay(10);  //10ms単おきに行う

    history[historyIndex] = temp1;  //温度データを記録するための配列
    historyIndex = (historyIndex + 1) % HISTORY_SIZE;  //新しい温度データを保存

    minTemp = history[0];   //最小温度をデータの最初の要素で初期化
    maxTemp = history[0];   //最大温度をデータの最初の要素で初期化

    for (int i = 1; i < HISTORY_SIZE; i++) {  //温度データから最小・最大温度を算出
      if (history[i] < minTemp) {   //データ内で最小温度より低い場合
        minTemp = history[i];       //最小温度を更新
      }
      if (history[i] > maxTemp) {    //データ内で最大温度より低い場合
        maxTemp = history[i];        //最大温度を更新
      }
    }

    //3分間の皮膚温の変動が0.4℃以内の時にアスタリスク表示
    if (maxTemp - minTemp <= 0.4) {   //最大・最小温度の差が0.4℃以内の場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print("*");             //「*」表示
      //Serial.println("***");
      readyFlg=true;
    } else {                      //条件を満たさなかった場合
      lcd.setCursor(7, 0);        //液晶ディスプレイの右上に
      lcd.print(" ");             //何も表示しない
    }

    String buf1, buf2;
    buf1 += temp1;      //buf1は現在温度
    buf2 += tempd;      //buf2は温度変化量

    lcd.setCursor(0, 0); //液晶ディスプレイの1行目にカーソルを移動
    lcd.print(buf1.c_str()); //1行目に現在温度を表示
    lcd.setCursor(0, 1);    //液晶ディスプレイの2行目にカーソルを移動
    lcd.print(buf2.c_str());  //2行目に温度変化量を表示
  }
}

2. Processing開発環境

プログラム:SBF230707
 1つ目は、シリアル通信を介して受信した皮膚温データを処理し、リアルタイムでグラフ表示しながら、そのデータをCSVファイルに保存するプログラムです。1行目では、Processing開発環境のシリアル通信ライブラリをインポートして、3行目ではシリアル通信を行うための準備として、Serialオブジェクトを宣言しています。4、5行目では、BF装置とシリアル通信を行うため、Arduino開発環境で表示されたシリアルポートの指定および通信速度115200ポートレートを使用しています。6、7行目では、ST波形などを表示するためのエリアとして、幅640、高さ200と指定しています。8~10行目では、受信したデータ「現在温度、温度変化量、変化の強さ、サンプリング数、安静期間終了の判定」の5つのデータについて、float型の配列の宣言からデータ格納を行い、String関数からデータ文字列を格納するための宣言を行っています。また、現在データと前のデータを線でつなぐため、prevDate配列を使用しています。11~15行目は、グラフを描画するために、赤・青・透明色の指定を行っています。17行目は、8~10行目のデータの初期値を0と指定し、18行目はCSVファイルの出力に使用するファイル名の格納を行っています。19行目は、一時的に取得したデータを格納するため、buf2という変数を指定。20行目では、CSVファイルにデータを書き込むためにオブジェクトの指定を行っています。21行目では、プログラムの開始時刻をミリ秒単位で格納するため、startMills変数を指定しています。27行目では、プログラムの実行開始時に表示されるウィンドウサイズを幅600ピクセル、高さ500ピクセルに設定しています。28行目では、myPortオブジェクトを作成し、4、5行目で指定したポート名とポートレートで初期化を行っています。30、31行目は、現在時刻および経過時間を描画するプログラム(図3-6)内で用いたgetTimestamp関数から取得した現在日時のタイムスタンプに基づいて、CSVファイル名を指定しています。32行目は、新しいデータが一定数蓄積するたびにファイル内に書き込むため初期化を行っています。

 36~92行目では、draw関数を用いて取得したデータをグラフに描画するための処理を行っています。39~43行目は、データの表示設定を行っており、変数datecountを用いてデータの受信回数が600の倍数の時に(39行目)次の条件を実行しました。background(255)からウィンドウ全体を白く塗りつぶし、fill(0, 0, 0)からテキスト描画を黒色で塗りつぶしました。さらにテキストサイズを12として、センサーから取得された温度データの単位を示すため、画面左上に摂氏40℃を表す40degを表示しました。46行目では、myPortオブジェクトから指定したシリアル通信が可能な場合、次の処理を行いました。48行目のmyPort.readStringUntiでデータを読み込み、取得したデータがnull表記ではない場合(50行目)、trim(inString)からデータ前後の空白を削除し、データ処理を行いました(52行目)。シリアルポートから読み込まれた1行のテキストデータを格納するためのInstring関数をCSV形式のデータ入力のため、カンマで分割して、得られた文字列をparts配列に格納しており(53行目)、取得中のデータに不足はないか確認しています(55、56行目)。57行目では、現在のSTをグラフに描画する際に、前回のデータが紛失しないようにデータの保存が行われています。60行目では、グラフの縦軸の範囲を可視化するため、元の範囲20~40の値を0~500に変換しています。これらデータをCSVファイルに記録するため、dataMV配列を使用しています(61行目)。64、65行目では、getTimestamp2から現在時刻、getElapsedTimeから経過時間を取得しています。67、68行目では、Processing開発環境のコンソール画面に表示するために、bufという変数を用いて「タイムスタンプ、経過時間」とdataMV配列に格納された温度データがカンマ区切りで表示されました。これにより、Arduino開発環境のシリアルモニタなどから温度データを確認する手間を省きました。69行目では、連続して受信したデータbufをbuf2に追加し、一度に大量のデータをファイルに書き込む回数を減らしました。70行目では、取得したデータ数が10の倍数ごとにCSVファイルに、writer.flushからリアルタイムで書き込む処理を行っています(71行目)。その後、ファイル内の残りのデータを書き込み(72行目)、73行目でbuf2を空にしました。これにより、ファイルのサイズを一定に保ちつつ、連続してデータを記録することができました。76~82行目では、グラフを描画するための処理を行っています。受信したデータ数を600で割った値からグラフのx座標を計算して(76行目)、描画する線の太さを1に設定(77行目)。78行目で実際に描画を行い、11~16行目で設定した色を用いて、描画する線の輪郭線の色を設定しています。81行目では、直前のデータから現在のデータまでを、グラフ上での位置を計算して線で結ぶ描画処理を行っています。「xp-1」から線の始点のx座標、「500-prevData[i]」から線の始点のy座標、「xp」から線の終点のx座標、「500 – data[i]」から線の終点のy座標をそれぞれ表しています。84、85行目では、現在時刻と経過時間を描画し、描画された文字が潰れないようにデータのカウントの更新を行っています(88行目)。94~98行目では、プログラムの終了時にファイルに書き込めてないデータをCSVファイルに強制的に書き込み(95行目)、ファイルを閉じるための処理を行っています(96、97行目)。

import processing.serial.*;

Serial myPort;
String portName = "COM6"; //BT serial port
int baudRate = 115200;
int dataPlotWidth = 640;
int dataPlotHeight = 200;
float[] data = new float[5];
String[] dataMV = new String[5];
float[] prevData = new float[5];
color[] colors = {
  color(255, 0, 0), 
  color(0, 0, 255), 
  color(0, 0, 0, 0), 
  color(0, 0, 0, 0)
};
int datacount=0;
String OutFileName;
String buf2;
PrintWriter writer;
int startMillis;


void setup() 
{
  //size(dataPlotWidth, dataPlotHeight);
  size(600,500);
  myPort = new Serial(this, portName, baudRate);
  
  OutFileName=getTimestamp()+".csv";
  writer = createWriter("./data/"+OutFileName); // Create a new file in the sketch directory
  buf2="";

}

void draw()
{
  
  if((datacount % 600) == 0) {
    background(255);  // clear screen    
    fill(0, 0, 0); // set the fill color to black
    textSize(12);
    text("40 deg", 10, 20); // 
  }
  
  while (myPort.available() > 0) 
  {
    String inString = myPort.readStringUntil('\n');
    
    if (inString != null) 
    {
      inString = trim(inString);
      String[] parts = split(inString, ',');
      
      if(parts.length >3) 
      {
        for (int i = 0; i < 5; i++) 
        {
          prevData[i] = data[i];
          data[i] = map(float(parts[i]), 20, 40, 0, 500);  // Assuming your data is in the range 0-200
          dataMV[i] = parts[i];
          
        }
        String stamp=getTimestamp2();
        String etime= getElapsedTime();
        String buf=stamp+","+etime+","+dataMV[0] + "," + dataMV[1] + "," + dataMV[2] + "," + dataMV[3] + "," + dataMV[4];
        println(buf);
        buf2=buf2+buf+"\n";
        
        if(datacount%10==0){
          writer.print(buf2);
          writer.flush();
          buf2="";
        }

        int xp = datacount%600;
        strokeWeight(1);
        for (int i = 0; i < 1; i++) 
        {
          stroke(colors[i]);
          line(xp-1, 500 - prevData[i], xp, 500 - data[i]);
        }
        
        drawCurrentTime();
        drawElapsedTime();

    
        datacount++;
      }
    }
  }
}

void stop() {
  writer.flush(); // Writes the remaining data to the file
  writer.close(); // Finishes the file
  super.stop();
}

プログラム:Subroutine1
 2つ目は、現在時刻および経過時間を描画するプログラムです(図3-6)。1行目は、現在の日時情報をフォーマットして文字列で返すために、getTimestampというString型の関数の宣言を行っています。2~7行目では、現在の日時を「年、月、日、時、分、秒」から取得して、それぞれ変数に代入しています。10行目では、2~7行目で取得した現在日時の情報を連結した文字列のタイムスタンプを作成しています。12行目では、作成した文字列のタイムスタンプは、getTimestamp関数内のtimestampに格納されているため、その関数内でしか使用できません。そのため、return timestampを使用し、timestampを関数の外に返すことでgetTimestampの呼び出し元に、作成した文字列のタイムスタンプが出力できます。15行目では、getTimestam2というString型の関数の宣言を行っています。16~18行目では、現在時刻「時、分、秒」を取得し、それぞれ変数に代入しています。21行目では、現在時刻の情報を連結した文字列のタイムスタンプを作成しています。23行目では、12行目と同様にreturn timestampを使用し、getTimestam2の呼び出し元に、作成した文字列のタイムスタンプを出力する処理を行っています。

26~42行目は、Processing開発環境の実行ボタンを選択した際、ウィンドウ内に表示する現在時刻および経過時間の描画設定を行っています。26行目では、現在時刻を描画するため、drawCurrentTimeという関数の宣言を行っています。28行目では、getTimestam2から現在時刻をstamp2に格納しています。29行目のfill(255)は描画される形状の塗りつぶしを白色に設定し、noStroke()は形状の輪郭線を非表示に設定しています。これにより、白色の背景が表示されます。30行目では、rect関数を用いて、位置(450, 5)に幅130、高さ50の現在時刻を表示するための長方形の背景を描画しています。31行目では、描画の属性を設定しており、fill(0)から黒色のテキストに設定し、Stroke(0)は形状の輪郭線を黒色に設定しています。また、textSize(24)からテキストサイズを24としました。32行目では、text関数を使用して、28行目のstamp2から画面右上(x:450, y:30)に現在時刻を表示しました。35~42行目も同様の処理を行い、経過時間を描画するための関数をdrawElapsedTimeと定義し(35行目)、経過時刻を取得するための変数をelapsedTimeに格納しました(37行目)。経過時刻を描画するための背景色やテキストサイズは同様です(38~40行目)。描画位置に関しては、画面右上の現在時刻の下の段(x:450, y:60)に表示しました(41行目)。

44~55行目は、経過時間の計算を行い、「時、分、秒」のフォーマットに整形するString型の関数getElapsedTime(44行目)を用いたプログラムです。45行目では、millis()関数を使用してプログラムの実行開始からの経過時間をミリ秒数単位で取得し、elapsedMillis変数に格納しています。47~49行目は、取得したミリ秒数を元に、経過時間を「時間、分、秒」に変換しています。経過ミリ秒数を1000で割った値を「秒」、秒を60で割った値を「分」、分を60で割った値を時間に変換しています。51、52行目では、秒数や分数で60を超えた値を再計算できるように、60未満の範囲に収めています。54行目では、nf()関数を使用して、表示される経過時間の間にコロンで区切り「時間:分:秒」の形式で表示する文字列を形成しています。以上から、プログラムの実行開始と同時に設定したウィンドウ内に現在時刻および経過時間を表示することが可能となりました。

String getTimestamp() {
  int y = year();
  int m = month();
  int d = day();
  int h = hour();
  int min = minute();
  int s = second();

  // Use nf() to add leading zeroes to each time component if needed
  String timestamp = nf(y, 4) + nf(m, 2) + nf(d, 2) + "_" + nf(h, 2) + nf(min, 2) + nf(s, 2);
  
  return timestamp;
}

String getTimestamp2() {
  int h = hour();
  int min = minute();
  int s = second();

  // Use nf() to add leading zeroes to each time component if needed
  String timestamp =  nf(h, 2) +":"+ nf(min, 2) +":"+ nf(s, 2);
  
  return timestamp;
}

void drawCurrentTime()
{
  String stamp2=getTimestamp2();
  fill(255); noStroke();
  rect(450, 5,130,50); 
  fill(0); stroke(0);textSize(24); 
  text(stamp2, 450, 30); //   
}

void drawElapsedTime()
{
  String elapsedTime = getElapsedTime();
  fill(255); noStroke();
  rect(450, 55,130,50); 
  fill(0); stroke(0);textSize(24); 
  text(elapsedTime, 450, 60);
}

String getElapsedTime() {
  int elapsedMillis = millis() - startMillis;

  int s = elapsedMillis / 1000;
  int m = s / 60;
  int h = m / 60;

  s = s % 60;
  m = m % 60;

  return nf(h, 2) + ":" + nf(m, 2) + ":" + nf(s, 2);
}

====================================================================

引用文献
長野祐一郎(2016). 自作測定装置で学ぶ皮膚温バイオフィードバック バイオフィードバック研究,43,49-51.
長野祐一郎(2022). 自作測定機器を用いたバイオフィードバック バイオフィードバック研究,49,77-81.
大河内浩人(1990). 皮膚温制御におよぼす訓練課題とフィードバックの効果 バイオフィードバック研究,17,8-14.

ゼミ進捗_240714

Fitbitプログラムの開発

開発内容
①HRV_Sleep_Stepsのデータ取得用プログラム3つを結合し、複数人のデータを自動取得し、説明文とグラフ作成を行う。
② ①の内容に加え、「現在の週と、1周間前の過去データ」を比較しアドバイス
③ ②の内容に加え、「現在から1週間後のデータ」を予測しアドバイスする

*音声合成AIの説明文読み上げ機能に関しては、音声サーバーのHTTPリクエストが成功せず作業が停滞している(visualizeHRV_Sleep_Steps_AIvoice.py)。

==========================================================================

複数人のHRV_Sleep_Stepsデータを取得し、Client ID別に一つのcsvデータに出力する。さらに、グラフ化を行い、説明文をつけるプログラム(RTget_visualizeHRV_Sleep_Steps.py)

(左画像:データを一つのcsvにまとめたもの 右画像:複数人の実行例)

複数人のHRV_Sleep_Stepsのデータを取得し、「現在の週、1週間前のデータ」を比較し、「現在の週と、1週間前のデータ」の比較を行いアドバイスをする(RTget_visualizeHRV_Sleep_Steps_FB.py)

複数人のHRV_Sleep_Stepsのデータを取得し、「現在の週、1週間前、1週間後のデータ」を比較し、アドバイスをする(RTget_visualizeHRV_Sleep_Steps_FB2.py)

=====================================================================

音声合成AIの説明文読み上げのプログラム開発が停滞しているため、お時間あるとき、もしくは来週の生理心理学特殊研究の授業で教えていただけませんか。

本郷OC資料_240816

紹介内容
1. 自作計測器の使い方を動画で紹介
2. 基板切削マシーンの使い方と,作成物の紹介
3. 3Dプリンタの過去の作成物を紹介
4. AI模擬人格との会話体験
5. その他:非接触AIによる表情計測

1. 自作計測器の使い方:ジェンガプレイ中の皮膚コンダクタンスを計測
 (パワーポイントのスライドで紹介しながら)従来の計測器は,「高価,サイズが大きい,操作が難しい」という問題があり,生体計測機の所有台数が少なかったり,そもそも大学に置かれていないケースがある。しかし,小型マイコンやデジタルファブリケーション技術を用いて,自作で計測器をすることによって,これらの物的・金銭的コスト問題が解消される。具体的には,1台あたり3,000円程度の手のひらサイズの計測器が開発でき,尚且つ50台と量産可能である。今回は,実際に開発した皮膚コンダクタンスの計測器と,バイオフィードバックというリラクゼーション法を用いて計測体験を行います。

さらに,生体計測のデモンストレーションを行い,4名の参加者によるジェンガ課題中の皮膚コンダクタンスを計測します。皮膚コンダクタンスは、ストレスや興奮時に変化し、その変化を測定することで、緊張状態や集中度を客観的に評価できます。この計測を通じて、どのような状況で緊張が高まるのか、またその影響がどのように現れるのかを実際に体験していただきます。生体計測は、健康管理だけでなく、パフォーマンス向上やリラクゼーションの研究にも応用できる重要な手法です。

2. 基板切削マシーンの紹介
 基板切削マシーンを使用する理由は,主に小型化と持ち運びの容易さ,そしてコストの削減にあります。(パワーポイントのスライドで紹介しながら)画像にあるように、装置自体がコンパクトであり,バックやズボンなどストラップとして身につけることが可能になります。さらに,基板切削マシーンは外部の製造業者に依頼することも可能なため,これらのサービスを利用して人的コストを削減することも可能です。

・切削マシーンの操作方法

3. 3Dプリンタの紹介
 3Dプリンタを使用する理由は,ケース作成の柔軟性とコスト削減,試作の反復性,そしてカスタマイズ性にあります。3Dプリンタは,デジタル設計を基に複雑な形状のケースを短時間で製作できるため,プロトタイプの迅速な作成が可能です。さらに、従来の製造方法に比べて材料費が安く,コスト効率が良い点も魅力です。また,3Dプリントは試作品の反復製作が容易で,デザインの微調整や改善を繰り返し行うことができ、市販のケースではサイズや強度が合わないオリジナルの計測器に対して,最適なケースを製作できます。

・過去の作成物

3. AIエージェント
 近年話題になっている大規模言語AIのChatGPTを活用したAIと会話できるアプリケーションです。今回紹介するAIエージェントは,「アイオワ州在住の陸軍兵士」「カリフォルニア在住のEV社長」「アラスカ在住の女子高校生」などと会話が楽しめます。

・AIエージェントの使用方法

4. その他:非接触AIによるオンライン対戦ゲーム中の表情を計測

・実験刺激

・ゲーム中の表情と皮膚コンダクタンスの変化

======================================================================

準備する物

1. デジファブ機器
①切削マシーン
・切削マシーン
・両面テープ
・生基板
・六角レンチ
・掃除機
・マジックペン
・スクレーパー
・予備のドリル×3本
・Dell PC,PC充電器
・科研費で作成したM5の完成物

②3Dプリンタ
・3Dプリンタ(Anker)
・TPUフィラメント:透明,オレンジ,緑色など
・工具:ニッパー,ペンチなど
・過去の作成物:BF装置,M5,フィギュアなど

2. ジェンガ中の計測体験
・ジェンガ×2
・計測器:SC・HRの自作計測器×8台(予備含む)
・電極:箱ごと
・計測器の充電器×2
・手袋×6(予備含む)

3. AIエージェント
・PC:MSI01, 02の2台
・PCの充電器×2
・Webカメラ×2:1台はタクトスイッチがついてるカメラ

(4. 表情の非接触計測→PCとカメラはAIエージェントと同じ)
・タブレット:実験室の水色のタブレット×2
・タブレット置き:木材で作成したタブレット置き

プレゼン資料

HR, HRV, SC_計測練習

1. PolarUnite:腕時計型心拍センサー

・インストラクション動画

・アカウント

=====================================================================

2. PolarH10:チェスト型心拍センサー(心拍・心拍変動を測定)

・インストラクション動画(インストーラー:リンク)

・マニュアル,装着位置

・HRVの論文

=====================================================================

3. 皮膚コンダクタンス(SC)の計測

・インストラクション動画

・皮膚コンダクタンス計測機の引用文献

・作成例

・図の作成方法

=====================================================================

4. Coospo

名刺の作り方

使用ソフトラベル屋さん

web版を使えば,インストール不要ですぐに使える。また,テンプレートが山ほどあるため,名前部分を変えればすぐに作れる。

名刺の紙Amazonリンク

・上記のが100枚×8で,名刺800枚作れる
・詳しくはわからないが,購入基準はA4サイズでたくさん刷れればいいのかも

完成物
 

 コピー機にそのまま紙を入れれば,プリントできるようだ。注意点としては,紙が破れやすいため,プリント中に引っかかる恐れがあること。紙を入れる向きは、左90°に回転させること(院生室の場合)。とりあえず,学会用に10枚くらい刷っておく。