「BF」カテゴリーアーカイブ

BFプログラムの解説

はじめに
 卒論実験ⅠのBF装置の開発にソフトウェアの解説も取り入れるため、ChatGPTを用いてプログラムを解読中。といっても長野(2022)の測定器開発で半分書いてあるから、「FB有無の切り替え, 液晶ディスプレイ, 安静管理の自動化」を組み込めば終わり。Processingのプログラム解説もつけるべきか?

プログラム

#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行目に温度変化量を表示
  }
}

【液晶ディスプレイ】

#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行目に温度変化量を表示
}

【安静期間の自動管理化】

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(" ");             //何も表示しない
    }

①測定している値を60回分覚えるプログラム

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


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

②180個覚えた履歴の中から最大値と最小値を求めて, その差を算出するプログラム

const int HISTORY_SIZE = 180;   // 履歴のサイズ
double history[HISTORY_SIZE];   // 測定値の履歴
int historyIndex = 0;           // 履歴のインデックス
double minTemp = 0.0;  // 最小温度
double maxTemp = 0.0;  // 最大温温度

void loop() {
    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];   // 最大温度を更新
        }
    }
}

③最大値と最小値の差が0.4℃以内であった場合は, 液晶ディスプレイにアスタリスクを表示するプログラム

const int HISTORY_SIZE = 180;   // 履歴のサイズ
double history[HISTORY_SIZE];   // 測定値の履歴
int historyIndex = 0;           // 履歴のインデックス
double minTemp = 0.0;           // 最小温度
double maxTemp = 0.0;           // 最大温度

void loop() {
    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];   // 最大温度を更新
        }
    }

    // 最大・最小温度の差が0.4℃以内の場合、アスタリスク表示
    if (maxTemp - minTemp <= 0.4) {
        lcd.setCursor(7, 0);  // 液晶ディスプレイの右上に
        lcd.print("*");       // 「*」表示
    } else {
        lcd.setCursor(7, 0);  // 液晶ディスプレイの右上に
        lcd.print(" ");       // 何も表示しない
    }
}

【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);         //ブザー音を消す
  }

230714 進捗報告

進捗

1. 装置10台作成

・10台分作成完了
・毎週10人計測中→7~8月まで
・ストラップを100均で追加購入
・新しい3Dプリンタでケース作成

2. イメージ方略の選択

選択
訓練2週間後の参加者が「イメージなし方略」を選択するようになった。

理由
・温冷感, 情動イメージなどイメージするネタが尽きた
・イメージ無しだとイメージする手間が省けて皮膚温制御に没頭できる

しかし、イメージ方略を使用して慣れてきたため、訓練1週目からイメージ無しは難しいかもとのこと。そのため、訓練1週目にイメージ方略(情動, 温冷感)で感覚を掴み、以降イメージ無しで訓練を行う方法は有効?実験Ⅲや分析

3. Processing3.5.4

安静終了の合図を液晶ディスプレイ, シリアルモニタに表示するようにした。さっそく実験に導入し、動作上の問題は今のところ見られない。

4. 3週間の訓練を終えた感想

①自宅などでの訓練
・訓練を負荷と感じず、むしろリラックスできる時間となった。楽しい
・毎日寝る前に訓練を行う習慣をつけたら、眠りやすくなった。
・当初より上達した、コツを掴めた
・学内で装置を身に着けている人と訓練した
・自分の体の変化に気を向ける機会がついた

②改善点
・エンターテインメント性は欲しい(訓練が上手くいくとモニタに犬の画像などランダム表示。日によって変われば、訓練のモチベアップ?)
・自宅での訓練で、安静2分が面倒(いきなり訓練を始めたい)
・モニタの数値だとわかりづらいため、折れ線グラフとかにしてほしい

5. 生理指標

大学での各訓練期間別の皮膚温制御成績を図に示した。訓練3週間後以外、訓練間であまり差はみられない。

・訓練初日12名
・1週間後8名
・2週間後5名
・3週間後5名

6. 心理指標(RSES-J)

心理指標RSES-Jの平均得点を図に示した。「訓練初日~3週間後」までの実験参加者5名を対象に、1要因4水準参加者内の分散分析を行った結果、有意差は認められなかった。

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

質問内容

・プロセッシングのエラー

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

やる事

・参加者16~18名を今月中に取る
・装置の修繕
・研究計画書の作成

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

ご指摘

・Processingの問題に関しては、配列の問題。要はプログラム側で指定したデータ容量の値が不足しているため、500から900や1200に変更すれば解決される。
・生理指標の分析に関しては、長野・廣田先生の論文のように皮膚温変化量を算出する。基礎実験の皮膚温計測のように、「安静・課題期間の最後1分間」を基準に変化量を算出する。下記動画が変化量の算出例。

「皮膚温変化量=課題期間-安静期間」

統計処理
・各期間:1要因4水準の分散分析/2要因4水準の分散分析(訓練による)
・訓練別:上昇・下降訓練で対応ありのt検定を行う

本実験 事前準備

実験環境

1. メモ
・室温24~26℃
・パーテーション2枚
・モニタ
・マスキングテープ
・計測機器(BF装置, PC, USB)

(BF装置配布時に、単4電池3本・教示文スライドを忘れずに)

2. 注意点
・窓は閉める
・室温は適切か
・センサに風は当たってないか

データ関連

1. Forms(編集者用)
計測初日:MAIA, 事前質問
計測4回分:Rosenberg, 独自項目→完成!!
自宅訓練:訓練時の温度など→インストラクション挿入のみ
実験参加のお願い:第1~3希望

2. Forms(参加者用)
・計測初日:リンク
・計測4回分:リンク
・自宅訓練:BF装置裏面に添付
・実験参加のお願い:リンク

3. 教示文

自宅での訓練・実験室での効果測定について

・教示文
1. 実験手順
2. 実験概要
3. 制御方略

*制御方略の「その他」は,1~3に該当しないもの,そして温冷感・情動イメージの併用などが該当します。制御方略には個人があるため,ご自身に合ったものを選択して訓練を行ってください。

4. 実験スケジュール
パターンA
パターンB
5. 自宅での訓練

・大学での計測:4回
・自宅などでの訓練:3週間

訓練毎にForms回答!!

訂正:目を開けて訓練, 安静中FBなし
6. BF装置の使用方法

連絡先:Teamsチャットにて

【電池交換】

(+基盤の薄さがわかる画像を添付

電池ボックスがかなりキツく簡単に取り外せません。電池交換の際は、千枚通し, ボールペンなど細く尖ったものをお使いください。

*基盤が非常に薄いため乱雑にとると簡単に壊れます。そのため、右画像のようにしっかりと両手で抑えて外すようにしてください。

【装着例】

ストラップは自由に付け替えてok

【説明に用いた資料】

内受容感覚_先行研究

MAIA
・構成:8因子32項目
・測定:身体感覚, 内的受容感覚, 気づきや注意制御, 信頼など。
→内受容感覚への気づきの程度, 自身の内受容感覚を信頼している程度, 注意を保つことができる程度。FFMQはマインドフルネス尺度のこと。

MAIA引用

Mehling,Price,Daubenmier, Acree,Bartmess,& Stewart(2012) のMultidimensional Assessment of Interoceptive AwarenessMAIA) 尺度がある。

これを庄子雅保・大野誠士・BeateM.HerbertWolfE.Mehlingが日本語化した「内受容感覚への気づきの多次元的アセスメント」では,「緊張しているとき,身体のどの部分が緊張しているか気づいている」などの質問に回答を求め,内受容感覚の程度を評価している。

寺澤・梅田,2014
・内受容感覚とバイオフィードバックについても少し述べられている。

内受容感覚とBF

心身相関,脳の機能

前島(島皮質_Takahiro Hayashi・Naoki Ikegaya,2022)

内受容感覚の3つの測定方法

  • Interoceptive Accuracy (IA): 心拍検出課題などで内受容感覚の正確さを測定。
  • Interoceptive Sensibility (IS): 質問紙を用いて身体内部に意識を向けた際の傾向を測定。
  • Interoceptive Awareness (IAc): 内受容感覚の正確さに関するメタ認知を測定。

栗野(2022),ciniiのサイト

寺澤,2017

Freedman,2012リンク

Christine(2014):内受容感覚の文化差について詳しくレビューされている
Chentsova&Dzokoto., 2014
・Ma-kellam,2012→庄司,2017に引用元がある

金井・湯川,2017

福島(2014)
・内受容感覚の性差(心拍知覚,呼吸不可,血糖値,血圧):男性>女性
・内受容感覚の文化差(心拍知覚):西洋人>アジア人

櫻井・清水
・BFによって,内受容感覚が鋭敏になるか(心拍カウント,心拍弁別)
→BF前後で変化はみられなかった。内受容感覚は,短期的に個人の中で安定

基板カット時の注意点

今日は、基盤カット・下板の張替えをしてきた。

3Dプリンタ同じく、またやらかしたので順を追って紹介。

【下板】

下板が長期間使用しているとガタガタになって、基盤の完成度が落ちるため、新しい下板と張り替えた。普段、切断している基板より少し分厚いらしい。

【今日のやらかし】

画像の通り、針の長さを出しすぎた状態で基盤カットをしてしまったため、せっかく貼り換えた下板に傷をつけたうえに4,000円の針を壊すとこだった。

同じミスをしないよう詳細を下画像に載せた。

先生に伺ったところ高さ調節は指一本らしい。これを超える・足りないと切れ味が悪くなるうえに、今回のようにミスを起こしてしまう。

念のためマジックペンで印をつけたが、憶えておくと間違えはおこらなくて済む。また、切断時に「ガガガガー」と音が聞こえたらUSB CNCから即座に中止で。

*針付近の黒いのは「プレッシャーフット」と言い、機械が高さを自動調節してくれるらしいから針の高さ調節のみ念頭に置く。

【反応無し】

切断マシーンのUSBをPCに差し込み動かしてみようと思ったものの…動かない。何故??おそらく、あれよね。

Reset (F2)を押していない」「USB接続前にソフトを立ち上げた??」が挙げられる(2つ目は微妙)。

とりあえずReset選択しろみたいなダイアログ表示されるから、その通りに進めれば問題なし。USB CNSの使用方法は以前投稿したブログを参考にしてください。

【グリス】

3Dプリンタと同様に、定期的にグリスを塗って滑りを良くしないと脱調や精度が悪くなるらしい。そのため、定期的にゴミ掃除とグリス塗りを欠かさずに。

塗る位置は、画像で見えている部分と裏の基盤台が動くところ。

【余談】
近々、基盤カット手順の動画をアップする予定。これから始める方・復習したい方はそちらも参考にしていただけると幸いです。何かご要望があれば本ブログのコメントに書き込みor直で気軽にお申し付けください。