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

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

学習の手順
ここでは下記の順序でバイオフィードバック機器の動作を学んでいきます。
プログラム名:ADC・・・AD変換の基礎
プログラム名:temp01・・・基本的な皮膚温測定
プログラム名:temp02・・・より細かく皮膚温を測定する方法
プログラム名:Tone・・・聴覚フィードバックに用いるスピーカーの使い方
プログラム名:ColorLED・・・視覚フィードバックに用いるスピーカーの使い方
プログラム名:TempABF・・・聴覚を用いた皮膚温バイオフィードバックの方法
プログラム名:TempVBF・・・視覚を用いた皮膚温バイオフィードバックの方法
プログラム名:TempAVBF・・・視聴覚を用いた皮膚温バイオフィードバックの方法
使用するプログラム一式は下記のアドレスからダウンロードできます。Zip形式で圧縮されているため、必ず展開してから使用してください(圧縮されたままだとプログラムが動作しません)。
ハードウェアの組み立ては、各プログラムの動画と下の画像をご覧ください。



プログラム:ADC
AD変換は生体計測をする上で欠かすことのできない方法です。ここでは、ArduinoUNOを用い、皮膚温センサーから得られる電圧をAD変換し、数値として表示する方法を学びます。
1 2 3 4 5 6 7 8 9 10 | //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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //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番から音を出す事を意味します。
1 2 3 4 5 6 7 8 9 10 11 | //スピーカーから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で指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | //カラー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行は、現在温度や変化量をシリアルモニタに表示しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | //皮膚温を聴覚でフィードバック 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) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | //皮膚温を視覚でフィードバック #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行部分)を追加しただけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | //皮膚温を視聴覚でフィードバック #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心並列線