導入
近年、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) |
//皮膚温を視覚でフィードバック
#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心並列線