3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

自然非言語処理(一人)Advent Calendar 2017

Day 5

自然非言語処理第5日目:体性感覚による非言語処理(応用編)

Last updated at Posted at 2017-12-05

telmin.jpg

体性感覚によるコミュニケーション

みなさん、体性感覚でコミュニケーションしてますかぁ?
もちろんしてますよね?
してなかったやばいですよ?

え?
ほんと?

はい

そもそも体性感覚なんて言葉、知るわけないですよね。(知っている人もいるかもしれませんが

体性感覚とは、筋肉や皮膚、骨格で感じ取れる感覚のことです。体性感覚の中には、触覚も含まれます。

体性感覚でコミュニケーションを取ろうとすると
必然的に運動についても説明しないといけなくなります。
ここでは、体性感覚っていうものがなんなのかを理解してもらって
、のちに体性感覚と運動を合わせて、運動によるコミュニケーションを理解してもらいたいなと思っております。

コミュニケーション例が紹介できないということで、今回、実装に悩みました。頭をひねった結果、もうなにもかもがどうでもよくなったため、体性感覚と聴覚を利用した機械とのコミュニケーションツール、その名も電子謎楽器テフミンを作りました。

では、テフミンを紹介する前に体性感覚ってなにさという話に入って行きましょう。

体性感覚のメカニズム

体性感覚とは先程言った通り、筋骨格系で感じ取れる感覚です。さまざまな感覚に細かく分類できるため、説明するのが若干めんどくさい感覚です。体性感覚は大きく皮膚感覚と深部感覚に分かれます。皮膚感覚には、触覚、温度覚、痛覚、痒みがあります。また深部感覚には筋紡錘による感覚、ゴルジ腱器官による感覚、関節受容器による感覚があります。前回は皮膚感覚について主に説明したので、今回はテフミンと関係がある、深部感覚の方を説明しましょう。

深部感覚のメカニズム

IMG_0115.jpg
図1 深部感覚を司る各受容器の位置(左腕の一部を抜粋、[1]を参考に作成)

深部感覚を説明するためのイメージ図を図1に示します。深部感覚のうちの一つ、筋紡錘による感覚と筋紡錘そのものについて説明します。筋紡錘とは筋肉の収縮具合を伝える受容器、つまり感覚を伝える細胞です。筋紡錘による感覚のおかけで筋肉の張り具合が分かり、脳からの指令だけでなく反射の時でも筋肉の入れ具合を調節できるわけです。次にゴルジ腱器官による感覚について説明します。ゴルジ腱器官は筋肉と腱の繋がっているところにあります。筋肉が引っ張られるとこのゴルジ腱器官の中にある神経細胞が刺激され脊髄に筋肉が引っ張られたことが伝えられます。最後に関節受容器による感覚とは関節の中にあるルフィ二終末やパチニ小体、自由神経終末[2]により生み出される複雑な感覚のことです。実は皮膚にある受容器が関節にも入っているわけですね。以上の3つの感覚から人間の体の運動具合や体の位置を理解できるわけです。

#実装:深部感覚を用いた謎楽器テフミン
IMG_0116.jpg

さて、深部感覚がわかったところで、今回は深部感覚を用いた楽器、テフミンを作って行きましょう。今回作るテフミンの特徴は、

  • 左手の位置で音の高さがドレミと上がって行く
  • 右手の振動センサが震えると音が1オクターブ上がる
    というとても変な楽器です。
    早速作って行きましょう。

材料

開発用パソコンMac mini 1
ディスプレイ 1
スピーカー 1
センサ用マイコンArduino uno 1
ToF式測距センサ VL53L0X 1
振動センサ SW-420 1
ブレッドボード 2
ジャンパワイヤ 多数

回路図

回路図は以下のとおりです。
IMG_0117.jpg

ハードウェアの写真はこのとおりとなりました。
IMG_5893.JPG

ソースコード

パソコン側のソースコードは以下の通りです。
今回はメディアアート用のライブラリとしてopenFrameworksを使っており、
そのサンプルの1つ、communication -> serialをベースに改造しています。
ここではその主な修正点を抜き出して表示しています。

ofApp.cpp
//Author: alfredplpl
//Licence: MIT

//--------------------------------------------------------------
void ofApp::setup(){
	ofSetVerticalSync(true);
	
	ofBackground(255);	
	ofSetLogLevel(OF_LOG_VERBOSE);
	
	font.load("DIN.otf", 64);
	
    //serial: arduino -> of
	int baud = 19200;
	serial.setup("/dev/cu.usbmodem14631", baud);
    
    memset(buffer,0,baud);
    values.push_back(0);
    shakes.push_back(false);
    
    
    //sound
    int bufferSize        = 512;
    sampleRate             = 44100;
    phase                 = 0;
    baseFreq=440.0f;
    
    
    lAudio.assign(bufferSize, 0.0);
    rAudio.assign(bufferSize, 0.0);
    
    soundStream.printDeviceList();
    soundStream.setup(this, 2, 0, sampleRate, bufferSize, 4);
}

vector<string> ofApp::split(const string &s, char delim) {
    vector<string> elems;
    stringstream ss(s);
    string item;
    while (getline(ss, item, delim)) {
        if (!item.empty()) {
            elems.push_back(item);
        }
    }
    return elems;
}

//--------------------------------------------------------------
void ofApp::update(){
    //中途半端に受信してしまったときのバッファ
    static string head="";
    
    if ( serial.available() > 0 )
    {
        // 受信する
        int result = serial.readBytes( buffer,19200 );
        
        // check for error code
        if ( result == OF_SERIAL_ERROR )
        {
            // something bad happened
            ofLog( OF_LOG_ERROR, "unrecoverable error reading from serial" );
            return;
        }
        else if ( result == OF_SERIAL_NO_DATA )
        {
            return;
        }

        buffer[result]='\0';
        string data((char*)buffer);
        auto valStrings=split(head+data,'\n');

        //中途半端に受信してしまったかどうか判定
        if(buffer[result-1]!='\n'){
            head=valStrings[valStrings.size()-1];
            valStrings.pop_back();
        }else{
            head="";
        }
        
        //測距センサと振動センサの出力を接頭語で判断して各自記憶
        for(auto str : valStrings){
            const char* chars=str.c_str();
            if(chars[0]=='t'){
                int tmp=std::atoi(&chars[1]);
                if(tmp>12*90)tmp=12*90;
                values.push_back(tmp);
            }else if(chars[0]=='s'){
                int tmp=std::atoi(&chars[1]);
                bool isShaked=tmp>0?true:false;
                shakes.push_back(isShaked);
            }
        }
    }
    
}

//--------------------------------------------------------------
void ofApp::draw(){
    static unsigned int i=0;
    static unsigned int j=0;
    
    //再生する周波数は、測距センサの距離をキーとした平均律を使用。
    //振動すると1オクターブ上がる
    baseFreq=(440.0f*std::pow(2.0,(values[i]-25.0)/12.0/25.0))*(shakes[j]?2.0:1.0);
    
    ofSetColor(220);
    ofDrawCircle(1024/2, 768/2,768/2*baseFreq/2000.0);

    ofSetColor(0);
    //なお、めんどくさいので排他処理はしないし、メモリリークも考慮しない
    font.drawString("|", 1000-values[i], 300);
    font.drawString("shake!", 256, shakes[i]?100:200);
    
    if(i+1<values.size())i++;
    if(j+1<shakes.size())j++;
    
}

//--------------------------------------------------------------
//正弦波を再生する関数
void ofApp::audioOut(float * output, int bufferSize, int nChannels){
    
    // sin (n) seems to have trouble when n is very large, so we
    // keep phase in the range of 0-TWO_PI like this:
    while (phase > TWO_PI){
        phase -= TWO_PI;
    }
    
    float phaseAdder = (baseFreq / (float) sampleRate) * TWO_PI;
    for (int i = 0; i < bufferSize; i++){
        phase += phaseAdder;
        float sample = sin(phase);
        lAudio[i] = output[i*nChannels    ] = sample;
        rAudio[i] = output[i*nChannels + 1] = sample;
    }
    
}

マイコンの方のソースコードは以下の通りです。

Tefmin.ino
//Author: alfredplpl
//Licence: MIT

#include <Wire.h>
#include <VL53L0X.h>

VL53L0X sensor;
int shakePin=2;

void setup()
{
  Serial.begin(19200);
  Wire.begin();

  pinMode(shakePin, INPUT); 
  
  sensor.init();
  sensor.setTimeout(500);

  sensor.startContinuous();
}

void loop()
{
  Serial.print('t');
  Serial.print(sensor.readRangeContinuousMillimeters());
  //if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }
  Serial.println();

  int val = digitalRead(shakePin);
  Serial.print('s');
  Serial.print(val);
  Serial.println();
  
}

実験結果

実際にテフミンで演奏した結果がこちらになります。

感想

やっぱね、ハイになるよ。
キマってくる、どんどんふればふるほど。
まじやばい。
一回やったら飽きるけど。

まとめ

今回までで、視覚、触覚、体性感覚について説明しました。
次回は毛色を変えて、そもそもコミュニケーションってなんだったっけ?というところに
戻り、感覚による知覚とは何なのか考えてみましょう。

参考文献

[1] カンデル神経科学
[2] 奈良 勲、内山 靖、“理学療法辞典、” 医学書院、2006
[3] 橋本 照男、入來 篤史、"体性感覚、" 脳科学辞典、2012 http://bsd.neuroinf.jp/wiki/%E4%BD%93%E6%80%A7%E6%84%9F%E8%A6%9A

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?