mbed日記 (3)

長嶋 洋一


→ mbed日記(1)

→ mbed日記(2)

2014年11月17日(月)

9月26日からの mbed日記(1) 、10月27日からの mbed日記(2) に続いて、11月17日からpart3というのは、なかなかに高密度でmbedと遊んでいる、と実感できる(^_^)。 CQ出版「インターフェース」のmbed生体信号処理特集の原稿執筆はまだこれからだが、 「スケッチング」ワークショップ の1日目の夕方に、RAKASU PROJECT.さんとライヴパフォーマンスを行う予定になっていて、まだ何をするか白紙という状況から、今週はCQを棚上げしてこちらに没頭する事になる。

   

研究室を見回してみると、購入したのにまだ手が付いていないブツがいくつも溜ってきている。 上にあるのは、 ここ に載っているので、11月16日には届いていた2種類の生体センサボードだが、まだ手付かずである。 それらが乗っているWiiバランスボードを、ようやく昨日、 このように 改造したところなのだ。

 

そして上のように、 ここ に載っているので、10月4日には届いていた、LittleBitsの拡張用キットも手付かずになっていた。 今回は、ここからのスタートである。 イメージとしては以下の、NIMEコミュニティでお友達の、MITメディアラボの研究者、Joe Paradiso氏が2004年のアルスエレクトロニカでデモンストレーション・ライヴをしていた、アナログシンセのライブ・パッチングのパフォーマンスである。

YouTube

まずはLittleBitsのあれこれをテーブルに並べて、そして今回の目玉としてはNucleoF401REをユニバーサル基板に載せてみて、設計することなくハンダ付けを開始した。 システム設計、回路設計そのものも、作りながら即興的に進めていくのが、醍醐味なのである。

 

そして9時から19時まで、 このように 奮闘した結果、初めての「mbedでMIDI」は、上のようにサクサクとMax6と双方向にやりとりできた(^_^)。 ハードウェアの情報(MIDIコネクタのピン番号、HSCMOSロジックの端子、等々)は覚えようともしないのでデータシートを広げて参照するが、さすがに長年やっているMIDIフォーマットについては、何も見ずに全て淡々と記述できてしまう。

・・・そして、6ポートのアナログ電圧出力回路まで製作がほぼ出来たところで時間切れとなった。 実際には、まだMIDI入力で電圧がちゃんと出ていなくて、若干のハードのバグがありそうだが、朝型人間の僕としては、遅くまで粘ったりしないで潔く帰宅して、明日に備えて21時就寝するのである。 まだバグがあるかもしれないが、今日のところのNucleoF401REプログラム「MIDI_test_01」は以下である。

main.cpp

#include "mbed.h"
#include "MIDI_sub.h"

int main(){
    int i, j, k;
    i = j = k = 0;
    rx_top = rx_end = tx_top = tx_end = 0; 
    MIDI.baud(31250);
    MIDI.attach(&rx_fifoset, MIDI.RxIrq);
    LatchPulse.write(0x01F8);
    for(i=0; i<6; i++) DAC_out(i,0);
    while(1){
        tx_fifo_check();
        if(rx_fifo_check() == 1){
            switch(midi_message>>20){
                case 0x08:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    tx_fifoset(midi_message & 0x7F);
                    break;
                case 0x09:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    tx_fifoset(midi_message & 0x7F);
                    break;
                case 0x0A:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    tx_fifoset(midi_message & 0x7F);
                    break;
                case 0x0B:
                    if(((midi_message>>16) & 0x0F) == 0){
                        k = (midi_message>>8) & 0x7F;
                        if(k < 6){
                            DAC_out(k, (midi_message & 0x7F)<<1);
                        }
                    }
                    break;
                case 0x0C:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    break;
                case 0x0D:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    break;
                case 0x0E:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    tx_fifoset(midi_message & 0x7F);
                    break;
            }         
        }
        if(++j > 1000000){
            j = 0;
            myled = !myled;
        }
    }
}

MIDI_sub.cpp

unsigned char rxFIFO[256], txFIFO[256];
unsigned char rx_top, rx_end, tx_top, tx_end, status, keyno, dcb;
int midi_message;

RawSerial MIDI(PA_2, PA_3);
DigitalOut myled(LED1);
PortOut DataBus(PortA, 0x1FE0); // PA_12 --- PA_5
PortOut LatchPulse(PortB, 0x01F8); // PB_8 --- PB_3

void DAC_out(int address, int data){
    DataBus.write(data<<5);
    switch(address){
        case(0):
            LatchPulse.write(0x01F0);
            break;
        case(1):
            LatchPulse.write(0x01E8);
            break;
        case(2):
            LatchPulse.write(0x01D8);
            break;
        case(3):
            LatchPulse.write(0x01B8);
            break;
        case(4):
            LatchPulse.write(0x0178);
            break;
        case(5):
            LatchPulse.write(0x00F8);
            break;
        default:
            break;
    }
    LatchPulse.write(0x01F8);
    return;
}

int rx_fifo_check(void){
    unsigned char data;
    if(rx_top != rx_end){
        data = rxFIFO[rx_end];
        ++rx_end &= 255;
        if (data > 127){
            status = data;
            dcb = 0;
            return(0);
        }
        else if(status > 0xEF){
            return(0);
        }
        else if( (status > 0xBF) && (status < 0xE0) ){
            midi_message = (status<<16) + (data<<8);
            dcb = 0;
            return(1);
        }
        else if(dcb==0){
            keyno = data;
            dcb++;
            return(0);
        }
        else{
            midi_message = (status<<16) + (keyno<<8) + data;
            dcb = 0;
            return(1);
        }
    }
    return(0);
}

void rx_fifoset(void){
    rxFIFO[rx_top] = MIDI.getc();
    ++rx_top &= 255;
    return;
}

void tx_fifo_check(void){
    if(MIDI.writeable() == 1){
        if(tx_top != tx_end){
            MIDI.putc(txFIFO[tx_end]);
            ++tx_end &= 255;
        }
    }
    return;
}

void tx_fifoset(unsigned char data){
    txFIFO[tx_top] = data;
    ++tx_top &= 255;
    return;
}

2014年11月18日(火)

「スケッチング」ワークショップ での新作公演(のための新システム製作)、という目標が出来て(切羽詰まって)、テンションが上がってきている。 昨夜はいつもの就寝21時、そして5時に目覚めて研究室には06:30前に到着した。 NucleoF401REに初めてのMIDIでは、昨日、既にちゃんと動作していたものの、フォトカプラTLP552からの+5V出力(1kΩでpull-up)をそのままNucleoF401REのUART_RX端子に接続していた事に気付いて、2.2kΩのプルダウン抵抗を付けるところからスタートした。

昨日の最後のところでは、6個のラッチ74HC574にそれぞれR-2Rラダー抵抗を付けたD/Aコンバータを並べて、それぞれの74HC574のラッチパルスとして、デコードせずに多数あるNucleoF401REのポートを割り当てていたが、何故か出力電圧が期待したようになっていなかった。 そこで試行錯誤で調べているうちに、NucleoF401REのラッチ用データパスのビットが逆順になっている事に気付いて、繋ぎ換えをして、電圧は正常となった。 ところが何故か、メインルーチン中でソフトタイマーで書き込むと正しくD/A出力するのに、Max6からMIDI経由で送るとその情報に対応してD/A出力されない・・・という、あり得ない感じのバグに悩まされた。 Max6からのMIDI受信→エコーバックMIDI送信、が正しく動いているので、これはまったく謎である。 よくあるデバックのハマりパターンということで、8時半にいったんテーブルを離れた。 こういうのは、気分転換も大事なのだ。

・・・そして1限には、リュ君の修了制作作品(新楽器)のためのプログラミングの一部として。さっそく実験したばかりのmbedルーチンを仕込んで、XBeeでのMaxへの通信を開通させた。 ここからの作業は院生室で続ける、という事になったが、リュ君はそのまま研究室に残って2限となった。 そこに、Wiiバランスボードの荷重センサを使用した卒業制作作品を目指す生産造形学科の菅内さんと、準ゼミの藤石さんがやってきて、 このように なんとか無事に、2個の「調子いいセンサ」を使った実験が完了した。 これで、続きは 「スケッチング」ワークショップ を経て、着実に進んでいけそうである。

 

・・・そして午後に入り、昼休みから苦闘すること3時間、ようやくトラブルの原因が判明して、問題は解決した。 これは、ここまで「かなりイケてる!」と思い込んでいたNucleoF401REの、意外な弱点でもあったのだ。 要点をまとめると以下である。

 

YouTube

そして、 このように 1日が終ったところで、ようやく上のように「6ポートのD/A(0〜5V)電圧出力」が完成した。 もちろん、出力コネクタとしてLittleBitsに取り付けた。 本当はこんなトラブルに遭遇することなく、今日のうちにA/D拡張まで行くつもりだったが、リュ君のシステムが進展し、菅内さんの作品に道筋が見えたこともあり、今日もまずまず、進んだということにしよう。(^_^;)

2014年11月19日(水)

今日は4-5限に「サウンドデザイン演習」、放課後にアカペラと埋まっているので、それまでが勝負である。 そして、朝8時から14時までの6時間1本勝負で、 このように 10チャンネルのA/D入力を得てMIDIでホストに送る、という機能も加わって、とりあえず一応の完成となった(^_^)。 NucleoF401REプログラム「MIDI_test_02」は以下である。 これを活用して、いよいよ明日から(といっても実質2日間で(^_^;))、作曲である。

main.cpp

#include "mbed.h"
#include "MIDI_sub2.h"

int main(){
    int i, j, k, p;
    unsigned char adc_new;
    i = j = k = p = 0;
    rx_top = rx_end = tx_top = tx_end = adc_no = 0; 
    MIDI.baud(31250);
    MIDI.attach(&rx_fifoset, MIDI.RxIrq);
    LatchPulse.write(0x01F8);
    for(i=0; i<6; i++) DAC_out(i,0);
    for(i=0; i<10; i++) adc_old[i] = 0;
    while(1){
        if(++p > 10000){
            p = 0;
            if(++adc_no > 9) adc_no = 0;
            adc_new = ADC_get(adc_no);
            if(adc_old[adc_no] != adc_new){
                adc_old[adc_no] = adc_new;
                tx_fifoset(0xBF);
                tx_fifoset(adc_no);
                tx_fifoset(adc_new);
            }
        }
        tx_fifo_check();
        if(rx_fifo_check() == 1){
            switch(midi_message>>20){
                case 0x0B:
                    if(((midi_message>>16) & 0x0F) == 0){
                        k = (midi_message>>8) & 0x7F;
                        if(k < 6){
                            DAC_out(k, (midi_message & 0x7F)<<1);
                        }
                    }
                    break;
                case 0x0C:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    break;
            }         
        }
        if(++j > 1000000){
            j = 0;
            myled = !myled;
        }
    }
}

MIDI_sub2.cpp

unsigned char rxFIFO[256], txFIFO[256], adc_old[10];
unsigned char rx_top, rx_end, tx_top, tx_end, status, keyno, dcb;
int midi_message, adc_no;

RawSerial MIDI(PA_2, PA_3);
DigitalOut myled(LED1);
PortOut DataBus(PortA, 0x1FE0); // PA_12 --- PA_5
PortOut LatchPulse(PortB, 0x01F8); // PB_8 --- PB_3
AnalogIn analog_value1(PA_0);
AnalogIn analog_value2(PA_1);
AnalogIn analog_value3(PA_4);
AnalogIn analog_value4(PB_0);
AnalogIn analog_value5(PC_1);
AnalogIn analog_value6(PC_0);
AnalogIn analog_value7(PC_2);
AnalogIn analog_value8(PC_3);
AnalogIn analog_value9(PC_4);
AnalogIn analog_value10(PB_1);

unsigned char ADC_get(int num){
    unsigned short data = 0;
    switch(num){
        case(0):
            data = analog_value1.read_u16();
            break;
        case(1):
            data = analog_value2.read_u16();
            break;
        case(2):
            data = analog_value3.read_u16();
            break;
        case(3):
            data = analog_value4.read_u16();
            break;
        case(4):
            data = analog_value5.read_u16();
            break;
        case(5):
            data = analog_value6.read_u16();
            break;
        case(6):
            data = analog_value7.read_u16();
            break;
        case(7):
            data = analog_value8.read_u16();
            break;
        case(8):
            data = analog_value9.read_u16();
            break;
        case(9):
            data = analog_value10.read_u16();
            break;
    }
    return(data>>9);
}
    
void DAC_out(int address, int data){
    DataBus.write(data<<5);
    switch(address){
        case(0):
            LatchPulse.write(0x01F0);
            break;
        case(1):
            LatchPulse.write(0x01E8);
            break;
        case(2):
            LatchPulse.write(0x01D8);
            break;
        case(3):
            LatchPulse.write(0x01B8);
            break;
        case(4):
            LatchPulse.write(0x0178);
            break;
        case(5):
            LatchPulse.write(0x00F8);
            break;
        default:
            break;
    }
    LatchPulse.write(0x01F8);
    return;
}

int rx_fifo_check(void){
    unsigned char data;
    if(rx_top != rx_end){
        data = rxFIFO[rx_end];
        ++rx_end &= 255;
        if (data > 127){
            status = data;
            dcb = 0;
            return(0);
        }
        else if(status > 0xEF){
            return(0);
        }
        else if( (status > 0xBF) && (status < 0xE0) ){
            midi_message = (status<<16) + (data<<8);
            dcb = 0;
            return(1);
        }
        else if(dcb==0){
            keyno = data;
            dcb++;
            return(0);
        }
        else{
            midi_message = (status<<16) + (keyno<<8) + data;
            dcb = 0;
            return(1);
        }
    }
    return(0);
}

void rx_fifoset(void){
    rxFIFO[rx_top] = MIDI.getc();
    ++rx_top &= 255;
    return;
}

void tx_fifo_check(void){
    if(MIDI.writeable() == 1){
        if(tx_top != tx_end){
            MIDI.putc(txFIFO[tx_end]);
            ++tx_end &= 255;
        }
    }
    return;
}

void tx_fifoset(unsigned char data){
    txFIFO[tx_top] = data;
    ++tx_top &= 255;
    return;
}

 

YouTube

そういえば、アドリブでここまで製作してきて、まだ回路図を描いていなかった(^_^;)。 放置すると忘れてしまって、後で改造とか出来ないので、回路図と仕様をまとめておかないといけない。 ここにそう記すことで、明日にでもやっつけてみよう。

2014年11月20日(木)

4限に学生委員会があるだけの、お仕事dayである。 朝、研究室に出てみると、(メディア造形学科の前身の)技術造形学科4期生の伴野さんからメイルがあり、3年ぶりに帰国しているという。 4期生といえば、「中学星」でデビューした清水誠一郎クンが後輩の憧れであるが、同じグループで元気だった伴野さんも、いまやNYで活躍するデザイナーである。 メイルを何往復かさせて、来週水曜日に研究室に来てくれることになり、「サウンドデザイン演習」は4限だけで(5限は各グループでの演習)抜けて来ることにした。

 

そして昨日からの懸案を、午前中に片付けることにした。 ハンダ付けしながら書き込んでいた、上のビン配置マップを参照して、ここだけは手描きで、以下の回路図を完成させた。 ついでに、この新インターフェースを「NucleoMIDI_1」と命名した(^_^)。

 

そして、「完成した」と言いながら、ちょっと謎な現象がある事も、ここでメモしておこう。 Max6から100msec間隔でプログラムチェンジをインクリメントして送信し続け、NucleoF401REはこれをMIDI受信して再び送信バッファに積んで刻々とMax6にMIDI送信する、という動作をしていると、Max6からのD/A出力にクイックレスポンスする。 このダミーのMIDI送受信(Max6からの一定送信)を止めると、何故かMax6からのD/A出力に対する反応が異常に遅くなり、データが間引きされたような印象となる。 原因は不明なのだが、とりあえず「おまじない」としてMax6から100msec間隔で送信してエコーバック受信する、という対症療法なのである。 NucleoF401REの場合には、C言語で記述した割り込みハンドラに対して、実際にどう動作しているかはブラックボックスの中なので、全てアセンブラで記述したAKI-H8やPropellerのようにスッキリしない所があるが、まぁ仕方ない。 そして、「NucleoMIDI_1」の仕様は以下である。

さらにこちらも懸案だった、3回生の根木クンから依頼されていた、彼の映像作品のための音楽のアレンジ作業を行い、彼が作ったメロディーにピアノ伴奏のパートを加えてみた。 基本的に自分の音楽としては打ち込みをしない僕としては、講義で触れる以外には無縁のGarageBandである。 小学生にメロディー(エレピの音色)を歌ってもらう予定らしいので、何のヒネりもなく(^_^;)、音楽の教科書のようなアレンジであるが、まぁ最初のバージョンとしては こんなもの  だろう。

・・・そして11時あたりから、いよいよ「NucleoMIDI_1」とLittleBitsSynthとClydeとを繋いでみたが、スグに新しい課題に気付いた。 LittleBitsSynthのエンベロープモジュールをトリガするのに、キーボードmoduleとシーケンサmoduleから出ている「Trigger」という第二の出力が、どうしても欲しくなってきたのである。 「NucleoMIDI_1」のA/D入力でモニタしてみると、トリガは「通常OFF(0V)」で、ONでは「+5V」を続ける、つまりvelocity=127固定のノート情報のようなものである、と判明した。 手元にあるLittleBitsSynthのエンベロープジェネレータは4個なので、とりあえず4ポートのディジタル出力があると嬉しい。 そこでNucleoF401REの空きポートを調べてみると、ちょうど都合良く、PB15-PB12の4ビットが余っていた(^_^)。

こうなると、作曲の筈だった日ではあるものの、やはり「NucleoMIDI_1」を1ランク機能向上させたくなる。 幸いに、まだ基板にはコネクタとか部品を詰め込むスペースもわずかに残っている。 そしてそこから、午後の会議で90分抜けただけで、なんと20時過ぎまで このように 頑張ってしまった(^_^;)。 途中、トランジスタバッファではスレショルドを越えられず、遂に基板の半田面に74HC04を直付けする(^_^;)など、いろいろとトラブルもあったものの、無事に解決して、希望した機能をなんとか実現できた。
以下が、4チャンネルのトリガ出力機能を追加した、最終的な「NucleoMIDI_1」の仕様である。

 

 

上は、最終的なNucleoF401REのピンマップと最終的な「NucleoMIDI_1」の回路図であり、最終的なNucleoF401REプログラム「MIDI_test_03」は以下である。

main.cpp

#include "mbed.h"
#include "MIDI_sub3.h"

int main(){
    int i, j, k, p;
    unsigned char adc_new;
    i = j = k = p = 0;
    rx_top = rx_end = tx_top = tx_end = adc_no = 0; 
    MIDI.baud(31250);
    MIDI.attach(&rx_fifoset, MIDI.RxIrq);
    LatchPulse.write(0x01F8);
    TriggerOut.write(0xF000);
    trigger_status = 0;
    for(i=0; i<6; i++) DAC_out(i,0);
    for(i=0; i<10; i++) adc_old[i] = 0;
    while(1){
        if(++p > 10000){
            p = 0;
            if(++adc_no > 9) adc_no = 0;
            adc_new = ADC_get(adc_no);
            if(adc_old[adc_no] != adc_new){
                adc_old[adc_no] = adc_new;
                tx_fifoset(0xBF);
                tx_fifoset(adc_no);
                tx_fifoset(adc_new);
            }
        }
        tx_fifo_check();
        if(rx_fifo_check() == 1){
            switch(midi_message>>20){
                case 0x0B:
                    if(((midi_message>>16) & 0x0F) == 0){
                        k = (midi_message>>8) & 0x7F;
                        if(k < 6){
                            DAC_out(k, (midi_message & 0x7F)<<1);
                        }
                    }
                    else if(((midi_message>>16) & 0x0F) == 1){
                        k = (midi_message>>8) & 0x7F;
                        if(k < 4){
                            Trigger_send(k, (midi_message & 0x7F));
                        }
                    }
                    break;
                case 0x0C:
                    tx_fifoset(midi_message>>16);
                    tx_fifoset((midi_message>>8) & 0x7F);
                    break;
            }         
        }
        if(++j > 1000000){
            j = 0;
            myled = !myled;
        }
    }
}

MIDI_sub3.cpp

unsigned char rxFIFO[256], txFIFO[256], adc_old[10], trigger_status;
unsigned char rx_top, rx_end, tx_top, tx_end, status, keyno, dcb;
int midi_message, adc_no;

RawSerial MIDI(PA_2, PA_3);
DigitalOut myled(LED1);
PortOut DataBus(PortA, 0x1FE0); // PA_12 --- PA_5
PortOut LatchPulse(PortB, 0x01F8); // PB_8 --- PB_3
PortOut TriggerOut(PortB, 0xF000); // PB_15 --- PB_12
AnalogIn analog_value1(PA_0);
AnalogIn analog_value2(PA_1);
AnalogIn analog_value3(PA_4);
AnalogIn analog_value4(PB_0);
AnalogIn analog_value5(PC_1);
AnalogIn analog_value6(PC_0);
AnalogIn analog_value7(PC_2);
AnalogIn analog_value8(PC_3);
AnalogIn analog_value9(PC_4);
AnalogIn analog_value10(PB_1);

void Trigger_send(int address, int data){
    unsigned char mask;
    mask = 0x01 << address;
    trigger_status &= (~mask);
    if(data == 0){
        trigger_status += mask;        
    }
    TriggerOut.write((trigger_status & 0x0F) << 12);
    return;
}

unsigned char ADC_get(int num){
    unsigned short data = 0;
    switch(num){
        case(0):
            data = analog_value1.read_u16();
            break;
        case(1):
            data = analog_value2.read_u16();
            break;
        case(2):
            data = analog_value3.read_u16();
            break;
        case(3):
            data = analog_value4.read_u16();
            break;
        case(4):
            data = analog_value5.read_u16();
            break;
        case(5):
            data = analog_value6.read_u16();
            break;
        case(6):
            data = analog_value7.read_u16();
            break;
        case(7):
            data = analog_value8.read_u16();
            break;
        case(8):
            data = analog_value9.read_u16();
            break;
        case(9):
            data = analog_value10.read_u16();
            break;
    }
    return(data>>9);
}
    
void DAC_out(int address, int data){
    DataBus.write(data<<5);
    switch(address){
        case(0):
            LatchPulse.write(0x01F0);
            break;
        case(1):
            LatchPulse.write(0x01E8);
            break;
        case(2):
            LatchPulse.write(0x01D8);
            break;
        case(3):
            LatchPulse.write(0x01B8);
            break;
        case(4):
            LatchPulse.write(0x0178);
            break;
        case(5):
            LatchPulse.write(0x00F8);
            break;
        default:
            break;
    }
    LatchPulse.write(0x01F8);
    return;
}

int rx_fifo_check(void){
    unsigned char data;
    if(rx_top != rx_end){
        data = rxFIFO[rx_end];
        ++rx_end &= 255;
        if (data > 127){
            status = data;
            dcb = 0;
            return(0);
        }
        else if(status > 0xEF){
            return(0);
        }
        else if( (status > 0xBF) && (status < 0xE0) ){
            midi_message = (status<<16) + (data<<8);
            dcb = 0;
            return(1);
        }
        else if(dcb==0){
            keyno = data;
            dcb++;
            return(0);
        }
        else{
            midi_message = (status<<16) + (keyno<<8) + data;
            dcb = 0;
            return(1);
        }
    }
    return(0);
}

void rx_fifoset(void){
    rxFIFO[rx_top] = MIDI.getc();
    ++rx_top &= 255;
    return;
}

void tx_fifo_check(void){
    if(MIDI.writeable() == 1){
        if(tx_top != tx_end){
            MIDI.putc(txFIFO[tx_end]);
            ++tx_end &= 255;
        }
    }
    return;
}

void tx_fifoset(unsigned char data){
    txFIFO[tx_top] = data;
    ++tx_top &= 255;
    return;
}

 

2014年11月21日(金)

今週は「濃い」週となったが、もう金曜日である。 明日の推薦入試では、新生デザイン学部デザイン学科(3学科が合体して1学科化)の倍率が4.2倍であり、午後には全学入構禁止となる、入試直前のピリピリした空気が漂っている。 2限のゼミが終れば、学生たちも帰宅しなければならない。

昨日の日記となっているものの、最終的な回路図とかソースリストを上げたのはこの朝であり、昨夜から考えていたことだが、 「スケッチング」ワークショップ の初日晩のライヴの場では、当初「(新作)」と予定していたパフォーマンスを止めて、LIttleBitsSynth+NucleoF401REのシステムを使った「デモ」を行う、と決心して大学に出て来た。 もちろん、今日の午後と明日の入試業務の合間に何か作品をやっつける事は可能ではあるのだが、肝心のワークショップでのプレゼンの中身を充実させたい、というもう一つの重要なモチベーションが勝ったのである。 今回は、ライヴは落先生に任せて、その前座のデモで盛り上げる役に徹したいと思う。

そしてなんと、朝9時過ぎになって、さらに 「スケッチング」ワークショップ への参加申し込みメイルが舞い込んできた。 社会人3人目、地元の某楽器メーカのエンジニアからである。 締め切りをさりげに延ばしておいて良かった(^_^)。

そして2限のゼミが終わり、午後じゅうかかって、「スケッチング」ワークショップの プレゼン ページを作りかけたが、終らなかった(^_^;)。 時間に限りがあり、現物を持参してのデモをしていると、あっという間に時間が尽きてしまうので、これまでにYouTubeに上げた動画があれば、それをサクッとクリックして提示する・・・という作戦はたぶん効果的なのだが、Webサイトのリンク切れの修復なども行いながらの作業は、予想以上に時間がかかると気付いた。 まぁ、このページを当日までに完成させて、1106の公式ページにリンクを置けば、後日も資料として使えるので、明日の推薦入試業務の合間も含めて、もう少し、頑張っていこう。

2014年11月25日(火)

もう火曜日である。 「スケッチング」ワークショップ・フォトレポート と、その最後のあたりのYouTube動画までアップして、これで無事に今回の「スケッチング」ワークショップも、おしまいである。 いろいろに収穫のある、「濃い」ものとなって、企画・主催した者としての醍醐味をまた味わえた(^_^)。 いろいろに溜っている事務仕事とか、懸案となっているWebサイトの整理(リンク切れの補修と相対リンク化の作業)とかで、今日は一日が終る予想である。 いよいよ次には、CQの原稿が直面してくるので、mbedには明日以降、新たに取り組んでいこう。

2014年11月26日(水)

昨日の作業(Webサイトの整理)は、一日がかりで1000件以上の修正をしたのだが、まだ終っていない(^_^;)。 あと1000件程度、残っているが、これはまたいずれ、頑張ってみよう。 そして今日の午前は、1年半後にSUACの情報機器が全て更新されるので、新たに導入する機器とソフトウェアの希望を2部屋分(マルチメディア室と電制御機器制作室)、とりまとめて書類を書いていたら、終ってしまった。 今日は4限だけ「サウンドデザイン演習」、そして5限は学生の演習に任せて研究室に返ってきて、3年ぶりに帰国した卒業生(4期生)の伴野さんがやってくるのを待つ予定である。

4期生といえば、「中学星」で活躍する清水クンをはじめとして、かなりの個性派が揃った学年であるが、伴野さんもニューヨークで活躍するデザイナ(の卵)なのだ。 アメリカで就労するための特別なビザの申請のために、出身大学の推薦状という前例の無い書類作成のために学科教員が応援した甲斐がある、というものである。 活躍の様子を聞かせてもらって、来週以降に学生(後輩)に紹介すれば、またいい刺激になる、という積み重ねは素晴らしい。

   

ネットでは、上のような「絵」が話題になっていた。 合板の上に描かれた左の絵は、中央の部分だけ現物らしい。 右の絵はどうなっているかと思ったら、 こうなっている  のだった。 凄い人もいるもんである。 ただ、こういう3Dアートは、探すとたくさんあるもので、 こんなもの  も出て来た。 いずれにしても、これは簡単には真似できないなぁ(^_^;)。 3限の時間は、 この資料ページ を書き進めていて、終った。

5限には4人の卒業生(うち1人は子供を抱えて)が研究室に来てくれたが、 伴野舞さん は、NYを拠点にデザインの仕事をしていて、ただしどうも英語バリバリではなくて、現地の日本人向けのいろいろな印刷物のデザインをしているとのことだった。 明日は月末の木曜日、午後に学科会議と学部教授会と大学院教授会があり、さらに5限に会議があって、午後が丸潰れ(;_;)である。 金曜日も卒制中間報告会があって2-3限が潰れるし、日曜日はCGクリエイター検定試験でまた潰れる。 なかなかまとまった時間が取れない、ちょっと苦しい期間となりそうだ。

2014年12月1日(月)

上に書いたように予定に追われ、さらに土曜日はマルマル、事務仕事の「個人調書」書きで潰れた。 大学教員であれば学長を含めて年に一度のお仕事で、文科省に出す書類なのか、大学教員としての色々な活動を「調書」として(1年分、加筆して)提出する義務があるらしい。 既に僕の調書は110ページぐらいになっているが、去年の10月1日から今年の9月30日までの活動を調べて加筆するのも大変だが、過去の記述にバグを発見して毎年それも修正しているので、何も生み出さないこのお仕事は集中するならマルマル1日仕事、片手間にやっていると10日ほどかかってしまうのである。 これを一気に書いたらドッと疲れて土曜日が終わり(^_^;)、日曜はCGクリエイター検定試験の監督でまたまたドッと疲れて終った。

・・・ただし先週の後半には、CQ出版のN氏と何度もメイルを往復させて、今回の僕の担当する「mbedで生体信号の情報処理」という特集記事のおよそのプランが確定していたのである。 事務仕事を一気に片付けたのも、あとはこの楽しい執筆に没頭するためなのだ。 僕は原稿執筆依頼が来ると、ページ数と各ページの文字数をまず、問い合わせる。 とりあえず全部全角だとすると、2倍すれば、plain text(ワープロでなくテキストエディタで執筆)で何バイトか、というおよその規模が体感で判る。 たとえば 「図解 新しいノイズ対策」  の執筆の時には、ページ数と文字数から「220KB」という分量を確認したので、およそ執筆は2週間、と回答した。 執筆内容に新たな実験/調査等が無い場合(書かれる内容を全てモノにしている場合)、専従だと1日に20KB、つまり約1万字を毎日連続して書けるので、220KBは11日+α、と見込んだわけである。 以下はその、今回の企画案メモ(→のちの改訂も反映)である。

1ページ2200文字 (24文字×49行×2列)
全て文字の場合 = 約4KB/ページ

目次

■第4部 マイコンでトライ! 生体情報の信号処理の基礎■

第1章 生体情報のディジタル信号処理

1-1 生体情報の信号処理のために
	生体情報の種類
	生体情報の計測・監視 - 生体信号の電気的特性
	生体信号処理に特有の注意点

1-2 筋電信号の特徴と筋電情報処理
	筋電信号の種類
	筋電信号の特徴
	筋電情報処理の基本
	「筋電情報」のメリット

1-3 生体信号処理のアルゴリズム
	デジタル信号処理の概要
	移動平均(積分)
	差分(微分)
	共振器(ピーク強調、レゾナンスフィルタ)
	ノッチフィルタ
	デジタルフィルタ(FIR・IIR)
	パワー検出(検波+平滑)
	FFTとスペクトル分析
	ニューラルネットワーク、遺伝的アルゴリズム、独立成分分析
	ノイズ除去(アーティファクト)

第2章 mbedによる筋電情報システムの例

2-1 mbedとNucleoF401RE
	各種マイコンとIDE
		AKI-H8
		Arduino
		Propeller
		RaspberryPi
		mbed
	mbedの色々
		FRDM-K22F
		トラ技ボード
		FRDM-K64F
		NucleoF401RE
	NucleoF401REの特長
		ファームウェア書き込み専用の別CPU
		ArduinoヘッダとMorphoヘッダ

2-2 Arduinoシールド上に筋電センサ回路を作る
	筋電センサ回路の製作と実装
		筋電センサ電極の製作
		「USBオーディオ治具」の製作
			Appendix1 筆者オリジナル筋電センサ回路の変遷
			Appendix2 筋電センサ電極
			Appendix3 USBオーディオ治具
	XBeeによるWiFi伝送
		XBeeの設定
		NucleoF401REのジャンパ設定
		システムの電源回り
		XBeeシリアル通信のプロトコル設計
			Appendix4 信号線ノイズ、フォトカプラ分離、光ファイバ、無線伝送

2-3 NucleoF401REの基本プログラミング
	オンラインIDEとバイナリ書き込み
	メインルーチン速度の性能評価
	シリアル通信とFIFO処理 - 割り込み受信、ポーリング送信
	タイマ割り込み
	A/D変換入力 - ポーリングと高速変換

2-4 パワー検出と移動平均
	シミュレーションによる検波/平滑の検討
		低い搬送周波数の場合の検波/平滑
		高い搬送周波数の場合の検波/平滑
		オフセットが乗った場合の検波/平滑
		「二乗平均平方根」との違い
	NucleoF401REシステムでの実現
	サイン信号を使った検証実験
	筋電信号を使った検証実験

2-5 ノイズ除去フィルタ
	高域ノイズ抑止と積分(平滑) - LPF
	直流成分カット - HPF(5Hz)
	ハム抑止 - ノッチフィルタ
	アーティファクトのフィルタリング
		変動成分カット - HPF(50Hz)
		レンジ監視

2-6 FFTと筋電情報パターン認識
	リサジュー図形によるパターン認識
	FFTによるスペクトル抽出
	ニューラルネットワークによるパラメータ補間
		Appendix5 筋電スペクトルからのパターン認識

第3章 生体情報の計測/信号処理のためのアナログ回路技術

3-1 生体信号処理に関するアナログ回路
	OPアンプ増幅回路
	フィルタ回路
	全波整流回路

3-2 デジタル〜アナログ境界に関する周辺回路
	アナログスイッチ
	S/H回路とA/D変換・D/A変換
	多重化回路 - アナログマルチプレクサ
	MIDIアナログ信号収集分配システムの製作例

3-3 生体信号センシング製品の実例
	「BITalino」と「e-Health」
	BITalino
		Arduino/Bluetoothスタンドアロン
		筋電センサ
		心電センサ
		皮膚電気抵抗センサ
		光センサ(血流/脈拍)
		3次元加速度センサ
	e-Health
		Arduino/RaspberryPi用シールド
		血流/脈拍
		心電図
		呼吸
		体温
		血圧
		姿勢
		皮膚電気抵抗
		血糖値
		筋電
出版前にこんなのをWebに書く、というのは普通はあり得ないが、誰が事前にこれを読んだとしても絶対に記事は書けない、という確信があるので、自分へのプレッシャーとしてここに記しておこう。 ただし、実際の執筆で、この目次や、原稿の内容と分量が変わって行くのは、N氏も了承済みである。

しかしまだ、事務仕事ともこの原稿執筆とも異なる、もう1件のプロジェクトを抱えているので、この原稿の執筆開始はあと1日ほど、お預けである。 昨夜は20時に寝て、今朝は5時半起床、朝6時半から研究室に来て全開モードであるが、この別件について目処を立てる、というのが今日の目標なのだ。 上記の原稿執筆企画案を整理する中で、ほぼこちらは「見えて」きているので、今日のもう一つの企画検討で、この年末年始を全力で疾走する基盤が確立する、そのための「満タン睡眠」だったのである。

・・・そしてここからマル1日、途中で3回生の根木クンの映像作品のための音楽の一部修正はあったものの、ずっと研究室でこのプロジェクトの企画案を練って、そこそこまとまってしまった(^_^)。 そこで、さらに4時間ほどかけて、1000件程度、残っていたWebサイトの整理を最後まで、片付けてしまった。 これは数年ぶりに気持ちいい、「後顧の憂い」の解消なのである。 僕は自分で「nagasm.org」と「suac.net」のドメインを持っているが、過去には自分のページとして以下のようなURL構成としていた。

そして、これらのURL(***の部分が何であっても)を指定すると、 このページ に飛ぶようになっている。 ドメインが存在しないのであれば、例えば このように 表示され、ドメインが生きていて該当ファイルが存在しないのであれば、例えば このように 表示される筈なので、つまり、 このページ は、意図的に設置されている有効ページなのである。 そのために、現在では使わない「suac.net」というドメインも、このリダイレクトだけのためにお金を払って保持しているのだ。 この新しいURL体系になってもう7-8年になるが、インターネットの世界に勝手に置かれている過去のURLは このページ に飛んできて、それが読める人であればURLを手打ちで修正すると新しいところに到達できる。 しかし、自分のサイトの中の膨大なHTMLないしtxtファイル中に、この旧URLが残っていて このページ に飛ぶ、というのは、甚だ格好わるいので、ずっと気になっていたのだ。

一昨日の「個人調書」のお仕事で調べてみると、僕のドメインにあるファイルは、「2014年11月29日現在、個人ページ(nagasm.org/ASL)および研究室ページ(nagasm.org/1106)に置いたコンテンツの総数は241,886ファイル(31,616,689,373bytes)ほどである」という事だった。 その中に残っていた、約2000件の絶対URL指定を全て相対パス指定に変更して、ようやく課題を解決したのである。 そして、その作業の中で、 懐かしい教材 とか、 「風見屏風」のメイキング とか、さらにはmbedの記事で書く予定の 筋肉マウス など、埋もれていた情報を発掘することも出来た(^_^)。 早起きして12時間も頑張ってしまったが、まずまず、手応えのある1週間のスタートとなった。 明日は準ゼミの藤石さんと、手付かずのMOSSで遊ぶ予定もあり、楽しみである。

2014年12月2日(火)

昨日は数年来の「Web内の整理」が出来て、昨夜はあまり覚えていないが「HTMLを修正している」夢までみたのは、余程、引っかかっていたのだろう(^_^;)。 要するに、インターネットのどこかから僕のサイトに飛んできてコンテンツ内部にあるリンクをクリックする場合に、お客さんにとっては何の違いもなくクリック先に飛んで行くだけであるが、実はそのHTMLソースには「http://」が全く書かれていないのである。 アクセスするクライアントのブラウザのURL欄にはもちろん「http://」が表示されるので、外部から見ている人にとって違いは無いのだが、今回、ようやく整理できたのは、一部に抜けバグはあるにしても、僕のコンテンツ相互のリンクは全て内部的に「相対リンク」(../../など)で記述されている、という事なのだ。

この意味するところは大きくて、現在、トータルで32GBほど(動画をYouTube限定公開に移動したので数GB減った)の全コンテンツを、例えば64GBのUSBメモリで全て持ち歩いて、ネットと途絶したオフライン環境でも全てアクセス出来る。 あるいは、自分の全ての知的生産物をWebに上げている僕としては、遺言としてこのUSBメモリを何個もあちこちにプレゼントする、あるいは公開クラウドに置くことで、長嶋洋一の死後も簡単に全コンテンツを「遺産」としてWeb公開し続けられる事を意味する。 ドメインが変わっても、そのトップ(public_html)の直下にこれをコピーすれば、そこから以下の全コンテンツは互いに相対リンクにより呼び出せるので、僕個人の「nagasm.org」というドメインに縛られないのである。

 

いつものように夜中3時に ハムスター に起こされて睡眠が中断したものの、また8時間睡眠で朝7時には研究室に着いた。 夜中に思い付いて記していたメモに従って最初にやったのは、午後に備えて「MOSS」の バッテリモジュールの充電 であり、これはUSB電源アダプタに繋ぐと1時間ほどで完了した。 そしてもう一つのメモは大仕事で、なんとMacBookAirのうち1台を、遂に「10.6.8」から一つ上げて 「10.7」にする という作業である。 研究室のMacはたいていが10.6.8で止まっていたが、3台のMac miniだけは、Xcodeに対応するために10.7に上げてあり、過去に「10.5から10.6に上げるDVDROM}と「10.6.8から10.7に上げるUSB-ROM」は購入してあったのだ。 自分のポリシーとしてこれまで、「購入したMacのOSはそのdefaultのマイナーアップデートに留めて、メジャーアップデートはしない」を徹底してきたので、初めての挑戦である。 これは、もうじき新しいMacBookAir(Mac OSX10.10 !!)が来ること、Mac7がOSX10.7以降に対応していること、等々の理由による。 そしてインストールは簡単に終ったが、そこから10.7の最新までのアップデートは延々と何時間もかかりそうだ、と判明した。

ここからいよいよCQの原稿執筆作業であるが、まず午前には、既にこれまでに59本も行き来していた、CQのN氏とのメイルをざっと読み返して、執筆に関する条件などを整理した。 最近、問い合わせて「1ページの文字数は2200」と回答されていたが、いちばん最初のメイルに「1ページ1100文字」と書かれていた事も判明した(^_^;)。 そして昼休みをまたいでサクサクと執筆し、3限からは藤石さんがやってきて このように MOSSをBluetoothでコントロールするまで到達した。 ・・・そして、なんとか夕方までに、第1章の「1-1 生体情報の信号処理のために」と「1-2 筋電信号の特徴と筋電情報処理」の原稿をほぼ完成させてしまった。 まずまず快調である。

   

この合間には、無事にMacBookAirがOSX 10.7.5になって問題なかったので、もう1台もOSX 10.7.5に上げてしまった。 これにはもう一つ、理由があり、 欧州ツアー2014 のように、長期出張中にMacBookAirが立ち上がらなくなっても、USB-ROMだけ持っていけば、もう世界中どこでもOSの再インストールが出来る、という体制にしたかったのである。 ・・・今日もいろいろ充実したなぁ。(^_^)

2014年12月6日(土)

日付けとしては上の日記の4日後であるが、この間、ただひたすら黙々と、以下のように頑張った。

12月3日(水)
朝から3限までひたすら原稿執筆で「2-1 mbedとNucleoF401RE」が一応、完了。4-5限は「サウンドデザイン演習」でいよいよ各グループの企画がスタート。放課後のアカペラは久しぶりに全員集合。(^_^)

12月4日(木)
4限に短時間の推薦入試の判定教授会があるだけの日ということで、終日ひたすらひたすら原稿執筆。しかし前日までに書いた部分をあちこち改良したり、「過去の筋電センサの紹介Appendix」をここに移動させたこともあり、おそらく最大ボリュームの「2-2 Arduinoシールド上に筋電センサ回路を作る」は最後の「WiFi」のところを残して、終らず。(^_^;)

12月5日(金)
2-3限の総合演習中間報告会、4限のゼミ、以外の時間を全て注ぎ込んで、「2-2 Arduinoシールド上に筋電センサ回路を作る」を終えて「2-3 NucleoF401REの基本プログラミング」の最初の部分に突入した。

・・・そして今日、12月6日(土)である。明日12/7(日)は、MDWワークショップに参加してくれた4回生2人と院生1人とともに約束していた、白昼堂々9時間マラソンカラオケがあるので大学には出ない予定であり、そのためにも今日は研究室で完全没頭執筆モードである。 まずは、昨日、情報処理学会から届いていた3月の研究会の発表参加募集(音楽情報科学研究会とエンタテインメントコンピューティング研究会の合同。僕は両方の研究会の正規登録会員)を前にして、しばし悩んだ。 普通であれば迷わず「参加」なのだが、なんせ研究費の旅費枠を、ほぼ使い尽くしているので、参加となれば「自費」なのである(^_^;)。

しかし、今回のCQの原稿執筆で原稿料が入るので、そのネタで学会発表するための出張(甲府の「富士やホテル」で合宿)を個から出す、というのは整合がとれている。ちょっとだけ自腹で立て替える、ということなので、エンタテインメントコンピューティング研究会と音楽情報科学研究会の両方に1件ずつ、発表申し込みを出してしまった。後のことはこれから考えるちとして、まずは発表申し込みを出す、というのが自分にとってのトリガなのである。 正式にはそれぞれの研究会のプログラム担当(かつては僕も担当した)から受領の連絡が届くが、以下は学会から自動返信されてきたその記録である。

情報処理学会 第106回音楽情報科学・第35回エンタテインメントコンピューティング合同研究発表会の発表申込を受領いたしました。
【和文タイトル】
  生体信号の情報処理のためのプラットフォームについて
【英文タイトル】
  A study of platforms for biologic information processing
【和文アブストラクト】
  筋電情報など生体信号の情報処理に関して、マイクロエレクトロニクスとオープンソースの進展により登場した新たな
プラットフォームについて検討するとともに、新・筋電センサ回路を実装した生体情報処理システムについて紹介する。
検討の対象とした生体情報は、血流/脈拍・脳波・心電・呼吸・体温・血圧・姿勢・皮膚電気抵抗・血糖値・筋電など
約10種類、マイコン・プラットフォームはAKI-H8・Arduino・Propeller・Raspberry Pi・mbedの5種類である。実装の
詳細や追試可能なサンプルコードも全て雑誌記事とWebにて公開した。
【英文アブストラクト】
  This is a report about new platforms for biological signal processing like electromyography information. 
Recently we can get good systems that appeared from the development of micro-electronics and open-
source culture. I introduce the biological information processing system that implements the new EMG 
sensor and the new mbed-platform. The target examples of biological information are blood flow / pulse,
 EEG, ECG, respiration, body temperature, blood pressure, attitude, galvanic skin resistance, blood sugar 
levels, and EMG. The target examples of microcomputer platform are AKI-H8, Arduino, Propeller, 
Raspberry Pi and mbed.
申込内容の詳細は以下でご確認ください。
https://ipsj1.i-product.biz/ipsjsig/EC/ 

情報処理学会 第106回音楽情報科学・第35回エンタテインメントコンピューティング合同研究発表会の発表申込を受領いたしました。
【和文タイトル】
  音楽エンタテインメントを「作る」 〜SUACスタジオレポート2014〜
【英文タイトル】
  Making Musical Entertainment - Studio Report of SUAC 2014
【和文アブストラクト】
  静岡文化芸術大学(SUAC)のデザイン活動報告として、「音楽エンタテインメントを作る」活動にfocusして紹介する。
(1)学生作品として、アニメのアフレコを自分で簡単に録音したり他人のアフレコとコラボするシステム「あふれこっつ」と、
暗闇空間で鋏を操作すると切り裂き音が直進飛来する「Cut Sound Room」の2作品、(2)院生の修了作品として3D
プリンタによる造形と多種センサを盛り込んで「新楽器」を実現したプロジェクト、(3)自在にアナログシンセサイザーの
要素を取付け/切離しできる電子プロック「LittleBitsSynth」をfirmataとmaxuinoとmbedを活用してカスタマイズし
可能性を拡大させたプロジェクト、の3件の事例を紹介する。
【英文アブストラクト】
  This is a design activity and studio report 2014 of Shizuoka University of Art and Culture (SUAC),  
in focus to "making music entertainment". I will introduce 3 topics. (1) student's 2 works - "Af-Reco-
Ttsu" [dubbing / collaboration system for after-recording of animation], and "Cut Sound Room" 
[tearing sound is flying straight by manipulating the scissors in the dark space]. (2) graduate student's
 "new instrument" project [with 3D printing and many sensors]. (3) arranging/remodeling/expanding 
the "LittleBitsSynth" system - with firmata, maxuino and mbed.
申込内容の詳細は以下でご確認ください。
https://ipsj1.i-product.biz/ipsjsig/MUS/ 
発表前にこんなのをWebに書く、というのは普通はあり得ないが、誰が事前にこれを読んだとしても絶対に中身は書けない、という確信があるので、自分へのプレッシャーとしてここに記しておこう。 そして。浜松から甲府ということは、新幹線で浜松→東京、そして東京→甲府の特急かなぁ・・・と検索してみると、なんと以下のように、富士から甲府まで「身延線」というのがあり(これは未体験)、新幹線を使うと2時間半ほど、浜松→静岡を鈍行でも3時間ほどで行ける、と判明した。新幹線にしても、新横浜から横浜線で八王子にショートカットすると(これは知ってた)、やはり3時間ほどである。 申し込みに「遠隔地なので」と書いたが、意外や意外、浜松から甲府は近かったのだぁ。(^_^;)

 

 

そしてここからマル1日、清水エスパルスが情けない引き分けでJ1残留を決めるまでの間かかって、なんとか「2-3 NucleoF401REの基本プログラミング」までやっつけた。「2-2 Arduinoシールド上に筋電センサ回路を作る」にはAppendixを入れて図を29枚も用意したが、こちら「2-3 NucleoF401REの基本プログラミング」でも、ソースリストが4本、図も13枚となった。 リストはmbedのサイトに公式に公開して、CQの原稿にそのURLを入れた。以下である。

考えてみると、自分のサイトではさんざん全てのソースコードを公開しているものの、英語圏の「http://github.com/」のようなオープンソースのサイトに自分のソースコードを公開したのは、たぶんこれが初めて、つまり「デビュー(*^o^*)」である。 あまりいないと思うが、今後は(ずっと)、NucleoF401REを初めて使う人がここにやって来る可能性もあるのだ。

2014年12月8日(月)

新しい週のスタートである。 昨日のマラソン前にも大学に寄って1時間ほど作業するなどCQの執筆もいよいよ佳境、この週末には京都での日本音楽即興学会大会に行くが旅程(ホテル予約だけ)も発表プレゼン準備もまったく白紙(^_^;)、という、密度の高い「師走」である。 CQの執筆は、以下がとりあえず完了、というあたりまで進んできた。 ここまでは、材料は全てこの「mbed日記」に置いてきたものを整理しているので、文章だけ補足してサクサクと進めてきたが、昨日の朝から着手した「2-4 パワー検出と移動平均」を書き進めていて、まだ「抜け」があった事が判明した、 新たに実験しつつの執筆となるので、ここからは大幅にスピードダウンしそうである。 まずは今週末の出張申請も出していなかったと判明して、朝はその書類書きから始まったが、大会プログラムを確認すると僕の発表は2日目だったので、プレゼン準備は1日目の合間とかに内職することにした。

そして、米国ルイジアナで開催される、 NIME2015 の案内(Call for Works)は先月から届いていたものの、deadlineが原稿執筆に没頭し始めた12月1月だったので断念バーグしていたのだが、今日になって「12/12までextendする」という、お約束の案内が届いた。 こうなれば、とりあえず駄目モトで応募することにした。 ネタはとりあえず今年のパフォーマンスとしては、OMMF2014でやった「GHI2014+LittleBItsSynth+Maxuino+Clyde」しかないので、これをエイヤでやっつけたPDFを作り、YouTubeにも上げている、RAKASU PROJECT.さんがステージ上で撮ってくれたカメラをプロジェクションしたパフォーマンスの動画を添える、という作戦で、まぁ、駄目モトなので、こんな程度である。 本命のpaperについては、1月下旬がdeadlineなので、CQの原稿を片付けてから、もう少しちゃんと取り組むことにしよう。

・・・そして午後2時過ぎまで頑張って、 こんなもの  を作って、無事にコンサートセッションへのsubmissionが完了した。 さすがに英語での文書作りは大変であるが、これを乗り越えないと土俵に乗れないので仕方ない、 この最終確認(自分宛に「OK」というメイルを送らせるとは、なかなか憎い)をやっているところに、業者に注文していたUSB-LANアダプタが届いた。 なんと2台のMaxBookAirを10.7に上げたのはいいが、それまで(アップデートの途中まで)使えていたUSB-LANアダプタが、どうやら10.7では使えないと判明していたのである。 付録CDROMからドライバをインストールして、難なく稼働するようになったが、これで過去に3個、仕入れていたUSB-LANアダプタはもう引退・廃棄である。 勿体ないが、まぁ仕方ないのかな。

 

 

そして、Max6で新たに上のようなシミュレーションを行って、「二乗平均平方根」(RMS)はとりたてて有効性が無い、と確認したところまでで今日の執筆はおしまいとなった。「2-4 パワー検出と移動平均」のほぼ半分までである。 明日はいよいよ、この「検波/平滑」のアルゴリズムをNucleoF401REで実装した部分について加筆することになるが、それが終ると、手持ちの材料が尽きて、そこからは実験と並行しての執筆となるのだ。

2014年12月9日(火)

朝6時半に研究室に出て来ると、多数のスパム以外に2本のメイルが届いていた。 卒業生の(大学院でも教えていた)見崎さんからで、なんと遂に焼津を飛び出し、 表参道のカフェ  で作品を展示するという(^_^)。 後輩に紹介するために、さっそく研究室Webにもこのスクリーンショットを上げた。 ちょっと東京に行く予定がないのが残念である。

そしてもう1本は、昨日のNIME2015に続いて、 ICMC2015 のCFPである。 今年はNIMEもICMCも米国で、University of North Texasで開催とあるが、なんと時期が「September 25th to October 1st 2015」である。 後期スタートにかぶる、というのはちょっと悩ましいところなので、しばし様子見することにした。 これで2015年の国際会議などのCFPも、以下のようにだいぶ溜まってきたが、6月〜7月にはなかなか行けないのが残念だ。

日々のメイル/Webチェックを終えてもまだ8時半で。ここから昨日の届きである。 昨日に続いて補足が必要、と思い付いたので、 Maxパッチ  を追加して作って、「二乗平均平方根」はオフセットの乗った信号に対して解決策にはならない、という確認を追加した。 考えてみれば、統計学の標準偏差、そして物理学では「光の強度は電磁場の二乗としてしばしば定義されるため、その平均強度は二乗平均平方根の形を取る」というように、いたるところで出現するこの計算が、単純にダイオードで全波整流するだけのゲルマラジオと代わり映えしない、というのは面白いことだ。 そして、昨日の画像をお蔵入りとして、原稿の「図」を、以下の画像に差し換えた。

 

 

ここから午前中かかって、「2-4 パワー検出と移動平均」を書き終えた。 紙面に入るかどうか、リストが1本で、図が25枚もある(^_^;)。 mbedのサイトに CQ_nagasm_05_detection も公開した。 ここまで、第1章で「1-3 生体信号処理のアルゴリズム」を飛ばしていて、第2章ではこの後の「2-5 ノイズ除去フィルタ」と「2-6 FFTと筋電情報パターン認識」が残っていて、さらに第3章が残っている。 CQのN氏の要請として、第2章を分厚く書いて欲しい・・・というのがあるのだが、残った「2-5」と「2-6」については、NucleoF401REに実装する「とっかかり」から実験することが必要な「最後の難所」である。

ここで昼休みをまたいで、完全に棚上げしていた週末の京都出張について旅程を調べたり、現地でプレゼンを準備するためのデータのバックアップ等の事務作業に追われた。 そして、内部リンクを改訂した「nagasm.org」ドメインの全データ(31.62GB)と、それ以外のお仕事領域のデータ(34.88GB)をそれぞれUSBメモリにコピーするのに4時間かかる、と言われたので、ここで第1章と第3章のための作業として、文献と拙著の調査とスキャン作業をまず進めた。 その後、一気に「1-3 生体信号処理のアルゴリズム」を書き進めて、12枚の図とともに、最後の1項目だけ残して、1日(12時間)の作業が終った。 もう帰宅したらバタンキューである。(^_^;)

2014年12月10日(水)

昨日の勢いを受け継いで、朝から引き続き頑張って、原稿の手直しとともに「1-3 生体信号処理のアルゴリズム」まで完了させた。第3章は手持ちの材料で今週末の出張中でも進められるので、残る課題は「2-5」と「2-6」、実際にmbedにこれから新たに信号処理アルゴリズムを実装していく、という山場である。 ただし、mbedという新しいプラットフォームの出現を受けて、本年度に新基板を作るのをパスしたために余った研究費でLittleBitsのoptionモジュール類をごっそり発注した後は、午前にはゼミ土佐谷さんの復活アポ、昼〜午後には卒業生の見崎さんアポ(教職員食堂で一緒にランチ)、そして4・5限に「サウンドデザイン演習」の各グループのプロジェクト支援、放課後にお楽しみのアカペラ、と充実満載の日となり、例によって牛歩の進展となった。

そして5限が終って研究室に戻ってみると、なんと照岡さんから素晴らしい情報が届いていた。 「BITalino」と「e-Health」について、既に入手して、この日記でも個々の要素を簡単に検討していたが、e-Healthは全ての回路図などの情報をWebで公開しているのに対して、BITalinoは公開していなくて謎だった。 その詳細な資料をWebで発見した、というのである(^o^)。 ここにある これ である。 どうやら、開発した大学院生の修士論文らしく、ポルトガル語で読めないところもあるが、全部で85ページもある。 さっそくプリントして、持ち帰って読むことにした。

2014年12月11日(木)

「BITalino」の論文「A Biosignal Embedded System for Physiological Computing」はJosé Guerreiro氏が2013年10月に出したもので、印刷した85ページをザッと眺めたところ、途中に白紙ページが3ページ、ポルトガル語で読めない(^_^;)ページが4ページ、目次2ページ、図の目次が4ページ、表の目次が1ページ、とここまで14ページは今後不要となった。 そして表紙1ページ、概要2ページ、イントロダクション4ページに続いて。基礎知識として生体計測の全般を解説した「2 Concepts of Biosignals Measurement」が19ページあった。 ただし、「心電」「筋電」「皮膚電気抵抗」「3次元加速度」までは教科書の内容を整理したもので目新しい事項は無かった。 後半の以下の項目が、生体センシングシステム「BITalino」論文の一つのポイントだろう。 ただし、この内容も、読んでみるとJosé Guerreiro氏がここで明らかにした事は一つもなくて、初めて生体信号センシングに取り組む人にとってはコンパクトに整理された参考情報であるものの、個々の内容は全て、これまでの生体計測のノウハウとか最新計測回路技術のサーベイである。 そして、続く23ページにわたる「3 BITalino - Architecture Description」こそ、この論文の中核である(後述)。 その後、完成したシステムの性能計測評価「4 Evaluation Tests」が8ページ、BITalinoの応用例「5 Applications」として、以下の4つ(後述)を紹介するのにたった6ページ、あとは「まとめ/将来の構想」が2ページ、「参考文献」が5ページとあり、典型的な「理工系」の論文だった。 コアとなる著者オリジナルの部分は、性能評価8ページを除くと、全85ページのうち23+6=29ページとなったが、それでもちゃんとKickStarterでクラウドファンディングを集めつつ、実際にこのシステムを実現してしまった、という部分は賞賛すべき仕事である。 リスボンの技術研究機関 ISEL (Instituto Superior de Engenharia de Lisboa) に出された修士論文ということだが、幻のSTAP細胞などコピペと偽実験で量産された博士論文を認めている早稲田大学とは雲泥の違いである。(^_^;)

 

朝6時半から上の部分を書いて、明日に出発する出張などあれこれ準備していると、なんと「エアバス:超大型機A380、生産打ち切りの可能性示唆」というニュースを発見した。 たしか2010年に、学生たちとシドニーでの NIME2010 に行った時に、行きにシンガポールからシドニーまでのフライトが、この全2階建てのA380だったような記憶があるが、乗り継ぎが夜の出発だったので、上のような機体の写真を撮った記憶は無い。 ジャンボが引退し、小型のB777が国際線でも国内線でも主力の時代なのだろうか。

 

そしてここから、 BITalinoの論文 に載っている、センサ回路図などを確認しておくことにした。 これはCQの原稿の「第3章」でe-Healthとともに紹介することになるが、ここにまとめておけば、「mbed日記(1)」に書いたe-Healthの資料とともに参照して、週末の音楽即興学会の合間の内職でも、原稿を書き進められるのでは・・・という作戦である(^_^;)。 上のように、論文にはきちんと、BITalinoのスペックも整理されていた。 これに倣って、今回のNucleoF401REの筋電センサシステムのスペックを書いてみると、以下のような感じである。

 

上の回路図の左側は「ACC」、つまり3次元の加速度センサであり、ホストマイコンがArduinoである事からシリアルタイプでなく、「Bandwidth: 0-50Hz」、「Range: +/- 3g 」というスペックは秋月電子のセンサと同じで、これをArduinoのA/Dに入れているだけである。 まぁ身体に3次元加速度センサを取り付ければ、じっとしていても重力加速度の成分によって身体の「姿勢」が検出できるし、何か動けばもちろん「身振り」をセンシング出来るが、このセンサの値がBluetoothで飛んでくるだけでは、あまり何も出来ないだろう。

上の回路図の中央は「LUX」、つまりフォトトランジスタによる「明るさ」センサであり(Range: 360-970nm)、環境光の計測とか、光源を用いての血流計測(→脈拍)が出来る、という建て前のものだ。 そして回路図の右側では、CPUと同じ+3.3Vの単電源OPアンプ回路の基準電圧として、+3.3V電源を100kΩで1/2に分圧した1.65Vを、OPA364のボルテージフォワを介して全体に供給している。 誰がやっても、ここはこうなるのだ(^_^)。

 

そして上の回路図が、開発者本人による、BITalinoの筋電センサ回路である(μFを「nF」と書くのがちょっと気持ち悪いのは欧米に「μ」という全角文字が無いのでご愛嬌)。 「mbed日記(1)」の最後のあたりで、なんと照岡さんは自力でこれを解析していたが、チップ部品であるLPF回りのコンデンサの定数を実測した値が、ちょっと違っていたようで、「回路図の値では、LPFのf0が400Hzとなり、特に問題はないですね」とのコメントも届いた。

初段のINA333は、僕のシステムのAD627の上位互換で、より高性能なものであり、同相信号除去比(CMRR)が120dbあたりと抜群である。 そして、入力のDCオフセットを除去するために、初段INA333の出力を積分してINA333のリファレンスに戻すことで、等価的に f0≒10HzのHPFを形成していて、どうやらこのアイデアが、開発者のJosé Guerreiro氏の真骨頂のようだ。

 

上の回路図は、心電センサ回路であるが、対象となる生体情報の電圧と周波数帯域によって定数を変えただけで、まったく筋電センサと同じである。 ある意味で意地になっているようで、つまりは「初段INA333の出力を積分してINA333のリファレンスに戻すことで、等価的に HPFを形成して入力のDCオフセットを除去する」というアイデアを強調している。 もしかして、この方式で特許を出願しているかもしれないが、アイデア自体は計測アンプの世界ではまずまず知られているので、登録されるかどうかは微妙なところだ。

 

上の回路図は、皮膚電気抵抗のセンサ回路であり、普通に増幅回路+LPFである。 ところで、抵抗値の「102.31kΩ」とか「204.62kΩ」というのは、たまたま実測した値なのだろうか。 大事なのはその「抵抗比」の精度であり、チップ部品として特性を揃えたものを1個と2個直列で実現しているとすれば、高精度のアナログフィルタ特性を得られるのかもしれない。 ディスクリートの5%精度(金の帯)では、ちょっとこれは無理である(^_^;)。

 

そして上のフローは、BITalinoの内蔵Arduinoのファームウェアの解説の部分にあった、データを伝送するまでのフローである。 定番の手法として、タイマで定期的にセンサA/D入力して、これをいったんFIFOバッファに積んで、別のタイマからUSART出力して、これはBluetoothモジュール内でもさらにStackされ送信される、というものである。

ここで改めて気付いたが、つまりBITalino上のArduinoのファームウェアというのは、開発者のJosé Guerreiro氏によって完成されているのであり、これを我々ユーザがオリジナルに改造する事は出来ないのである。 つまりArduinoと言ってはダメで、これは単に「ATMEGA328P - AVR 8-bit RISC, clock 8MHz」なのだ。 José Guerreiro氏はおそらくArduinoをUSBで繋いで開発したのだが、ユーザはこのBITalino上のマイコンのソフト開発は出来ず、さらにBluetoothで送られたセンサ情報で何かする、というツールも「お仕着せ」だった。 ・・・これは、やはり僕としては「使えない」モノ、と言わざるをえないなぁ。(^_^;)

 

さて、気を取り直して、4種類の「応用例」をチェックしていこう。 上は「Beat-by-BIT」というもので、なんと「心拍に対応してLEDが点滅する」(^_^;)という、凄いシステムである。 心拍センサの出力は、肺に行く肺動脈と、全身に行く大動脈の両方に血液を送るパルスの筋電信号なので、心拍の1回に2つのピークがある。 これを、心電ECG信号にまずLPFをかけて、次に「slope sum function」で変換するSSFアルゴリズムによって最適化して、血圧のピーク検出を行う、という、実に「まっとうな」信号処理を行っているのである。

 

次の応用例は「LockBIT and LightBIT」であり、なんと、「生体認証ドアロック」(^_^;)という、凄いシステムである。 写真左のように、首からかけたBITalinoから筋電センサを腕に取り付けておいて、一方で写真右のように、ドアには別のBITalinoがあって、その出力ポートから電子錠/ライトに接続している。 つまり、149ユーロ(10/22に書いた時点では20267円だったものの、最近の円高で今日は21904円)のこのシステムを2個使って、電子錠を入れて計5万円ほどのシステムである。 そして、筋電センサ電極を貼った腕に力を入れるか、身体を揺すって首からぶら下げているBITalinoの加速度センサ出力を出すと、設定されたスレショルドレベルを超えた、という情報により、電子錠のライトが点灯したり、電子錠が開閉する。 これはつまり、人間でなく、猫に取り付けても反応する「生体認証ドアロック」なのだ。

 

次の応用例は「Mentir de Verdade」いうもので、Google自動翻訳によればポルトガル語で「横たわる真実」とあり、YouTubeではこのタイトルのお芝居の動画がたくさん並んでいた。 要するにこれは「嘘発見器」ということらしい。 「嘘発見器」であれば、メディア造形の学生が僕の講義で作る電子工作キットの中に うそ発見器「まことくん」  (1944円)というのがあるが、これは、嘘をつくと無意識に「掌に汗をかく」のを、その抵抗値の変化として検出するものである。 BITalinoの応用例「Mentir de Verdade」の場合には、皮膚電気抵抗と血流のセンサを用いて、Heart Rate(HR)の時間的変化を検出して、作られているソフトを使って2人で体験できるという。 まぁ、嘘を言っていればドキドキする、というアレである(^_^;)。

 

最後の応用例「FlowerBIT」とは、検索すると今でもAmazonで新品(8640円)が買える、「プラントーン PLANTONE 植物感情センサー 植物の囁き」をBITalinoで実現する、というものである。 計測するのは、「土壌水分」・「気温」・「環境光(照度)」・「周囲湿度」であり、これにより植物のHealthをセンシングしてBluetoothでお知らせする、というものである。 こちらの監視ソフトはPythonで記述された「SignalBIT frameworkというもので、HTML5やJavaScriptで自由にカスタマイズできる。 一例として、植物が苦しい環境になった時に、ツイッターで「水をちょうだい」とツイートするような自動ソフトを走らせることも可能なのだという。 アナログながら、下の「プラントーン」(ヤフオクで2個ゲットしてから絶賛放置中)の方が可愛い、と思うのは気のせいかなぁ。(^_^;)

   

2014年12月13日(土)

昨日の午後に浜松から京都に向かう新幹線の中では、「3-1 生体信号処理に関するアナログ回路」の前半を書き進めて、晩には照岡さんと四条烏丸で楽しく飲んだ(^_^)。 そして12/13-14の2日間は、京都精華大学で日本音楽即興学会の大会に参加していて、学会総会の合間に簡単なプレゼン(JPEGを1枚だけ)を このように  作ってしまった。 今回は敢えて現物を持ってきてのデモをせずに、ムービーをたくさん見せる作戦なのだ。

・・・その後、ここは学会大会の会場(ホール)で書いている。 面白い発表も散見するもののかなり退屈な講演・公演もあってバリバリ内職執筆が進み、既に「mbed日記」にあった「BITalino」と「e-Health」の材料を整理して、「3-3 生体信号センシング製品の実例」を、さらにの「3-1 生体信号処理に関するアナログ回路」の後半まで、原稿執筆を完了してしまった。「3-1」の図は5枚なのに対して、「3-3」では図は35枚にもなったが、果たしてこれは全部、載るのかなぁ。

2014年12月15日(月)

新しい週となった。 CQの原稿締め切りまであと10日、ただし12/24から3日間は研究室の空調を新しくする工事があり、その前に片付ける予定なので、もう実質1週間ほどである。 昨日は音楽即興学会での発表もあり、この日記を書かなかったが、会場での内職でこれまで書いた原稿をザッとチェックして修正した。 音楽即興学会フォトレポートはかなり手抜きなものの、 こんな感じ  である。 昨日は、金曜日の晩に飲んで語った照岡さんから、新たな情報などズッシリとしたメイルが届いていたが、今日はこれを読むところからスタートしつつ、いよいよNucleoF401REでの生体信号の情報処理の重点部分に挑戦の週となる。

 

・・・そして気付いたら20時まで頑張って、上のように進んだ。 一見するとほとんど前と変わっていないようで、実はNucleoF401REによるディジタルフィルタとして、FIRによるLPFが走っているのである。 mbedのサイトに CQ_nagasm_06_LPF も公開したが、以下のように、CQの三上氏がmbedサイトで公開しているソースをそのまま組み込ませていただいた。 カットオフ周波数が高いのでほとんど現状は無意味のLPFだが、これが後でON/OFFによってFFTで効果を検証する時に効いてくる筈で、要はNucleoF401REはこんなのも楽勝ですよ、という確認になった。

●メイン main.cpp

#include "mbed.h"
#include "sub.hpp"
#include "FIR_LPF.hpp"

float mean_sum, ad_data[101];
int average_mode, max_count, ad_pointer[5];

void sum_clear(){
    int i;
    for (i=0; i<5; i++) ad_pointer[i] = 0;
    for (i=0; i<101; i++) ad_data[i] = 0;
    mean_sum = 0;
}

float move_mean_calc(float data){
    mean_sum = mean_sum - ad_data[ad_pointer[0]] + data;
    ad_data[ad_pointer[0]] = data;
    ad_pointer[0]++;
    if(ad_pointer[0] == max_count) ad_pointer[0] = 0;
    return(mean_sum / (float)max_count);
}

int main(){
    int i, detection, sum, gain;
    i = 0;
    detection = 0;
    gain = 1;
    for (i=0; i<5; i++) timer_value[i] = 0;
    for (i=0; i<=order; i++) xn[i] = 0.0;
    sum_clear();
    xbee.baud(38400);
    xbee.attach(&rx_fifoset, xbee.RxIrq);
    timer_setup.attach_us(&timer_interrupt, 5); // 割り込みを5usec
    while(1){
        if(timer_value[1] > 19){ 		// 0.1msec サンプリング
            timer_value[1] = 0;
            float data = (float)gain * (analog_value3.read() - 0.5f);
            if(detection == 1){
                if (data < 0) data = -data;
            }
            data = FIR_calc(data);      // ここで FIR LPFを呼ぶ
            if(timer_value[2] > 1999){  // 10msec ごとにホストPCにXBeeで送信
                timer_value[2] = 0;
                if(average_mode != 0) data = move_mean_calc(data);
                tx_message((uint16_t)((data + 1.0f) * 2047)<<4);
            }
        }
        if(timer_value[0] > 99999){ // 500msec
            timer_value[0] = 0;
            myled = !myled;
        }
        tx_fifo_check();
        if(rx_fifo_check() == 1){
            sum = 0;
            for (i=0; i<6; i++) sum += conv_hex(raw_data[i])<<(4*(5-i));
            tx_message(sum); // Echo Back 
            if(sum>>16 == 0x80){
                switch((sum & 0xff00)>>8){
                    case 0x00:
                        detection = sum & 0x01;
                        break;
                    case 0x01:
                        gain = sum & 0x0f;
                        break;
                    case 0x02:
                        average_mode = sum & 0x07;
                        max_count = 5;
                        switch(average_mode){
                            case(2):
                                max_count = 10;
                                break;
                            case(3):
                                max_count = 20;
                                break;
                            case(4):
                                max_count = 50;
                                break;
                            case(5):
                                max_count = 100;
                                break;
                            }
                        sum_clear();
                        break;
                }
            }
        }        
    }
}

●サブ sub.hpp

unsigned char rxFIFO[256], txFIFO[256], raw_data[6];
unsigned char rx_top, rx_end, tx_top, tx_end, phase;
int timer_value[6];

RawSerial xbee(PA_2, PA_3);
Ticker timer_setup;
AnalogIn analog_value0(A0);
AnalogIn analog_value1(A1);
AnalogIn analog_value2(A2);
AnalogIn analog_value3(A3);
DigitalOut myled(LED1);

void common_setup(){
    rx_top = rx_end = tx_top = tx_end = phase = 0; 
}

void timer_interrupt(){
    int i;
    for (i=0; i<6; i++) ++timer_value[i] &= 65535;
}

void tx_fifo_check(){
    if(xbee.writeable() == 1){
        if(tx_top != tx_end){
            xbee.putc(txFIFO[tx_end]);
            ++tx_end &= 255;
        }
    }
}

int rx_fifo_check(){
    unsigned char data;
    if(rx_top != rx_end){
        data = rxFIFO[rx_end];
        ++rx_end &= 255;
        if (data < 33){
            phase = 0;
            return(1);
        }
        raw_data[phase] = data;
        if(++phase > 5) phase = 0;
        return(0);
    }
    return(0);
}

void rx_fifoset(void){
    rxFIFO[rx_top] = xbee.getc();
    ++rx_top &= 255;
}

void tx_fifoset(unsigned char data){
    txFIFO[tx_top] = data;
    ++tx_top &= 255;
}

unsigned char hex_conv(unsigned char data){
    data &= 15;
    if(data < 10) return(data+48);
    else return(data+55);
}

unsigned char conv_hex(unsigned char data){
    if((data > 47) && (data < 58)) return(data-48);
    else if((data > 64) && (data < 71)) return(data-55);
    return(0);
}

void tx_message(int data){
    int i;
    for (i=0; i<6; i++) tx_fifoset(hex_conv((data>>(4*(5-i))) & 15));
    tx_fifoset(13);
}

●サブ FIR_LPF.hpp

const int order = 200;
float xn[order+1];

// from CQpub0 Mikami / FIR_LPF_Direct
//  http://developer.mbed.org/users/CQpub0Mikami/code/FIR_LPF_Direct
//
//                                   Band1       Band2
// Lower band edge frequency (kHz) 0.000000    0.600000
// Upper band edge frequency (kHz) 0.500000    5.000000
// Gain                            1.000000    0.000000
// Weight                          1.000000    1.000000
// Deviation                       0.009734    0.009734
// Deviation [dB]                  0.084139  -40.234188

const float fc[order+1] = {   
    -3.566292E-03f,  2.335185E-03f,  1.917338E-03f,  1.681921E-03f,  1.522689E-03f,
     1.364066E-03f,  1.157961E-03f,  8.803014E-04f,  5.296940E-04f,  1.236180E-04f,
    -3.047488E-04f, -7.123405E-04f, -1.051992E-03f, -1.279654E-03f, -1.360180E-03f,
    -1.273725E-03f, -1.018960E-03f, -6.155554E-04f, -1.029986E-04f,  4.621914E-04f,
     1.013316E-03f,  1.480161E-03f,  1.798055E-03f,  1.916527E-03f,  1.805744E-03f,
     1.463100E-03f,  9.143371E-04f,  2.130115E-04f, -5.640853E-04f, -1.326518E-03f,
    -1.978121E-03f, -2.430574E-03f, -2.613837E-03f, -2.486610E-03f, -2.043447E-03f,
    -1.317959E-03f, -3.802110E-04f,  6.680631E-04f,  1.704594E-03f,  2.600376E-03f,
     3.235972E-03f,  3.515095E-03f,  3.379365E-03f,  2.817894E-03f,  1.872141E-03f,
     6.322899E-04f, -7.684087E-04f, -2.167773E-03f, -3.393601E-03f, -4.284042E-03f,
    -4.705797E-03f, -4.577073E-03f, -3.875274E-03f, -2.648668E-03f, -1.012065E-03f,
     8.616354E-04f,  2.758435E-03f,  4.447251E-03f,  5.706155E-03f,  6.350451E-03f,
     6.257734E-03f,  5.386359E-03f,  3.786968E-03f,  1.602121E-03f, -9.435526E-04f,
    -3.565645E-03f, -5.948320E-03f, -7.781761E-03f, -8.797770E-03f, -8.807067E-03f,
    -7.725336E-03f, -5.594939E-03f, -2.585050E-03f,  1.010693E-03f,  4.809328E-03f,
     8.361672E-03f,  1.121444E-02f,  1.295107E-02f,  1.324389E-02f,  1.191081E-02f,
     8.934171E-03f,  4.488148E-03f, -1.062401E-03f, -7.182842E-03f, -1.321242E-02f,
    -1.841506E-02f, -2.204769E-02f, -2.343209E-02f, -2.202840E-02f, -1.749386E-02f,
    -9.730157E-03f,  1.092382E-03f,  1.453105E-02f,  2.989524E-02f,  4.629194E-02f,
     6.269474E-02f,  7.802411E-02f,  9.123583E-02f,  1.014078E-01f,  1.078185E-01f,
     1.100078E-01f,  1.078185E-01f,  1.014078E-01f,  9.123583E-02f,  7.802411E-02f,
     6.269474E-02f,  4.629194E-02f,  2.989524E-02f,  1.453105E-02f,  1.092382E-03f,
    -9.730157E-03f, -1.749386E-02f, -2.202840E-02f, -2.343209E-02f, -2.204769E-02f,
    -1.841506E-02f, -1.321242E-02f, -7.182842E-03f, -1.062401E-03f,  4.488148E-03f,
     8.934171E-03f,  1.191081E-02f,  1.324389E-02f,  1.295107E-02f,  1.121444E-02f,
     8.361672E-03f,  4.809328E-03f,  1.010693E-03f, -2.585050E-03f, -5.594939E-03f,
    -7.725336E-03f, -8.807067E-03f, -8.797770E-03f, -7.781761E-03f, -5.948320E-03f,
    -3.565645E-03f, -9.435526E-04f,  1.602121E-03f,  3.786968E-03f,  5.386359E-03f,
     6.257734E-03f,  6.350451E-03f,  5.706155E-03f,  4.447251E-03f,  2.758435E-03f,
     8.616354E-04f, -1.012065E-03f, -2.648668E-03f, -3.875274E-03f, -4.577073E-03f,
    -4.705797E-03f, -4.284042E-03f, -3.393601E-03f, -2.167773E-03f, -7.684087E-04f,
     6.322899E-04f,  1.872141E-03f,  2.817894E-03f,  3.379365E-03f,  3.515095E-03f,
     3.235972E-03f,  2.600376E-03f,  1.704594E-03f,  6.680631E-04f, -3.802110E-04f,
    -1.317959E-03f, -2.043447E-03f, -2.486610E-03f, -2.613837E-03f, -2.430574E-03f,
    -1.978121E-03f, -1.326518E-03f, -5.640853E-04f,  2.130115E-04f,  9.143371E-04f,
     1.463100E-03f,  1.805744E-03f,  1.916527E-03f,  1.798055E-03f,  1.480161E-03f,
     1.013316E-03f,  4.621914E-04f, -1.029986E-04f, -6.155554E-04f, -1.018960E-03f,
    -1.273725E-03f, -1.360180E-03f, -1.279654E-03f, -1.051992E-03f, -7.123405E-04f,
    -3.047488E-04f,  1.236180E-04f,  5.296940E-04f,  8.803014E-04f,  1.157961E-03f,
     1.364066E-03f,  1.522689E-03f,  1.681921E-03f,  1.917338E-03f,  2.335185E-03f,
    -3.566292E-03f
};

float FIR_calc(float data){		// FIRフィルタの演算部分
    int i;
    float acc = 0;
    xn[0] = data;
    for (i=0; i<=order; i++) acc = acc + fc[i]*xn[i];
    for (i=order; i>0; i--) xn[i] = xn[i-1];
    return(acc);
}

2014年12月16日(火)

朝から雨のこの日は、今週唯一、何も予定が無い、執筆巣篭もりの一日である。 まずは朝7時から、昨日の日記の後半を追記。 続いて第2章「2-5 ノイズ除去フィルタ」の原稿の「高域ノイズ抑止と積分(平滑) - LPF」に加筆。 次に「直流成分カット - HPF(5Hz)」の項、これは既にACカットしている本システムでは不要なのであっさりと終了(^_^;)。 そしていよいよ朝9時から、「ハム抑止 - ノッチフィルタ」である。

 

ノッチフィルタには苦い思い出があって、かつて 第3世代 の時に、上のようなアルゴリズムで「4-2. ソフトウェアDSPによるノッチフィルタ」というのに挑戦した。 しかし、当時のAKI-H8では、「AKI-H8のアナログ入力A/Dは8ビットの精度しかない」・「AKI-H8の内蔵RAMの容量からくる制限により、ディジタルフィルタに16ビット精度の確保が困難で8ビット幅にした」という情けない制限により、ほとんど効かないものだった。 ここで参考にしていた文献「中村尚五、ビギナーズディジタルフィルタ、東京電機大出版局、1989年、pp.154-173」をSUAC図書館で検索したところ、無かったので、さっそくamazonに行って、中古を注文した。今週の後半に届くので、こちらも余裕があればリベンジしてみたい。

 

amazonからこの本が届くのを待っていられないので、CQの原稿の方は、上の、またまた三上氏の文献からノッチフィルタのアルゴリズムと係数の情報をいただく事にした。 ただし数式しかないので、こちらはちゃんと自分で係数を計算しなければならない。 ノッチフィルタを実装するとなれば、もちろん浜松の60Hzだけでなく関東の50Hzとも切り替え可能である必要がある。

 

しかしここで、作業をしていてずっと気掛かりだった方をまず片付けた。 システムの「2チャンネル化」である。 ほぼ午前中かかったものの、無事にNucleoF401REのプログラムも CQ_nagasm_07_2ch のように改訂できて、上のようにMaxのホスト画面は2チャンネル化できた。 これで今後、パターン認識のための環境が整ったことになる。(^_^)

 

ここで、ノッチフィルタの係数を計算するパッチをMax6で作り始めたところ、なんとも凄いバグ(??)に遭遇した。Maxの数値計算では「指数関数」というのはなくて、「pow」という累乗計算オブジェクトで代行しているのだが、「pow 2.718281828459045」というようにオブジェクトを用意して入力にデータを入れると、なんと入力が負の時には「nan」という表示が出て、計算されないのである(^_^;)。 まぁ、累乗には負は無いとしても、指数関数を代行させていてこれは無い。 仕方なく、上のようにターミナルを開いて、C言語でプログラムを書いてgccでコンパイルして、係数の値をゲットした。

 

そしてなんとか、ノッチフィルタまでを CQ_nagasm_08_notch_test のように実装して、上のように、同一の筋電信号に周波数の異なるサイン波を重ねて、ノッチフィルタによって影響が異なるところを(やや微妙ながら)確認するまで、到達した。 ・・・長い12時間だった。(^_^;)

2014年12月17日(水)

昨日のノッチフィルタの「効き」に若干のバグの不安がありつつ、まだ続きがあるために、なんと朝6時に研究室に出て来た。 そして改めて冷静に状況を整理して、Max側のパッチと CQ_nagasm_08_notch_test の両方を改訂して、ちゃんとノッチフィルタが効いていることを確認して、昨日の図を捨てて、原稿をかなり改訂した。

 

上のように、60Hzのノイズを同じように乗せた同一の筋電信号に対して、ノッチフィルタをONにした上チャンネルは、素通しの下チャンネルに対して、あきらかにハム成分を抑止してくれた。 これで安心して、次に進めることになり、午前中かかって、アーティファクトのフィルタまで入れた、ほぼ完成版として、以下を CQ_nagasm_09_emergency に上げた。 これで山場の「2-5」と「2-6」のうち、「2-5」が完了である(^_^)。

●メイン main.cpp

#include "mbed.h"
#include "sub.hpp"
#include "FIR_LPF.hpp"
#include "notch.hpp"

int main(){
    common_setup();
    xbee.baud(38400);
    xbee.attach(&rx_fifoset, xbee.RxIrq);
    timer_setup.attach_us(&timer_interrupt, 5); // 5usec
    while(1){
        tx_fifo_check();
        if(timer_value[1] > 19){ // 0.1msec sampling
            timer_value[1] = 0;
            float data1 = (float)gain * (analog_value2.read() - 0.5f);  // A/D in (3)
            if(emergence!=0 && data1<0.05 && data1>0.95){
                data1 = old_1;
                if(++em_count1 > 10){
                    em_count1 = 0;
                    tx_message(0xff0000);   // Emergency 1
                }
            }
            if(data1 < 0) data1 = -data1; // always detection ON
            if(fir_lpf != 0) data1 = FIR_calc1(data1);      // FIR calc (1) call
            if(notch_1 != 0) data1 = notch_filter1(data1);
            old_1 = data1;
            if(timer_value[2] > 2999){  // 15msec
                timer_value[2] = 0;
                if(max_count != 0) data1 = move_mean_calc1(data1);
                tx_message((uint16_t)((data1 + 1.0f) * 2047)<<4);
            }
        }
        if(timer_value[3] > 19){ // 0.1msec sampling
            timer_value[3] = 0;
            float data2 = (float)gain * (analog_value3.read() - 0.5f);  // A/D in (4)
            if(emergence!=0 && data2<0.05 && data2>0.95){
                data2 = old_2;
                if(++em_count2 > 10){
                    em_count2 = 0;
                    tx_message(0xff0001);   // Emergency 2
                }
            }
            if(data2 < 0) data2 = -data2; // always detection ON
            if(fir_lpf != 0) data2 = FIR_calc2(data2);      // FIR calc (1) call
            if(notch_2 != 0) data2 = notch_filter2(data2);
            old_2 = data2;
            if(timer_value[4] > 2999){  // 15msec
                timer_value[4] = 0;
                if(max_count != 0) data2 = move_mean_calc2(data2);
                tx_message(0x400000 + ((uint16_t)((data2 + 1.0f) * 2047)<<4));
            }
        }
        if(timer_value[0] > 199999){ // 1000msec
            timer_value[0] = 0;
            myled = !myled;
        }
        if(rx_fifo_check() == 1){
            int sum = 0;
            for (int i=0; i<6; i++) sum += conv_hex(raw_data[i])<<(4*(5-i));
            tx_message(sum); // Echo Back 
            if(sum>>16 == 0x80){
                switch((sum & 0xff00)>>8){
                    case 0x00:
                        fir_lpf = sum & 0x01;
                        break;
                    case 0x01:
                        gain = sum & 0x0f;
                        break;
                    case 0x02:
                        max_count = sum & 0x7f;
                        if (max_count>100) max_count = 100;
                        sum_clear();
                        break;
                    case 0x03:
                        notch_1 = sum & 0x01;
                        break;
                    case 0x04:
                        notch_2 = sum & 0x01;
                        break;
                    case 0x05:
                        coef_set(sum & 0x3f);
                        break;
                    case 0x06:
                        emergence = sum & 0x01;
                        break;
                }
            }
        }        
    }
}

●サブ notch.hpp

/*
#include 
#include 
#include 
int main(){
    int fs = 10000;
    double  B0, a1, a2, b1, c0, F0, T;
    F0 = 60;    // or 50
    B0 = 100.;
    T  = 1./fs;
    a1 = 2. * exp(-M_PI * B0 * T) * cos(2. * M_PI * F0 * T);
    a2 = -exp(-2. * M_PI * B0 * T);
    b1 = -2. * cos(2. * M_PI * F0 * T);
    c0 = (1-a1-a2)/(2+b1);
    printf("a1 = %f\n", a1);
    printf("a2 = %f\n", a2);
    printf("b1 = %f\n", b1);
    printf("c0 = %f\n", c0);
    return 0;
}
60Hz
    a1 = 1.936768
    a2 = -0.939101
    b1 = -1.998579
    c0 = 1.642174
50Hz
    a1 = 1.937188
    a2 = -0.939101
    b1 = -1.999013
    c0 = 1.938304
*/

float notch_filter1(float data){
    y1[0] = data + a1*y1[1] + a2*y1[2];
    float reault = y1[0] + b1*y1[1] + y1[2];
    y1[2] = y1[1];
    y1[1] = y1[0];
    return(reault);
}

float notch_filter2(float data){
    y2[0] = data + a1*y2[1] + a2*y2[2];
    float reault = y2[0] + b1*y2[1] + y2[2];
    y2[2] = y2[1];
    y2[1] = y2[0];
    return(reault);
}

●サブ sub.hpp

unsigned char rxFIFO[256], txFIFO[256], raw_data[6];
unsigned char rx_top, rx_end, tx_top, tx_end, phase;
float mean_sum1, mean_sum2, ad_data1[101], ad_data2[101];
int timer_value[6], max_count, fir_lpf, gain, ad_pointer1, ad_pointer2;
const int order = 200;
float xn1[order+1], xn2[order+1];
float a1, a2, b1, c0, y1[3], y2[3], old_1, old_2;
int notch_1, notch_2, emergence, em_count1, em_count2;
const float _60Hz_a1 = 1.936768, _60Hz_a2 = -0.939101, _60Hz_b1 = -1.998579, _60Hz_c0 = 1.642174;
const float _50Hz_a1 = 1.937188, _50Hz_a2 = -0.939101, _50Hz_b1 = -1.999013, _50Hz_c0 = 1.938304;

RawSerial xbee(PA_2, PA_3);
Ticker timer_setup;
AnalogIn analog_value0(A0);
AnalogIn analog_value1(A1);
AnalogIn analog_value2(A2);
AnalogIn analog_value3(A3);
DigitalOut myled(LED1);

void sum_clear(){
    int i;
    for (i=0; i<101; i++) ad_data1[i] = ad_data2[i] = 0;
    ad_pointer1 = ad_pointer2 = mean_sum1 = mean_sum2 = 0;
}

void coef_set(int herz){
    if(herz < 55){
        a1 = _50Hz_a1;
        a2 = _50Hz_a2;
        b1 = _50Hz_b1;
        c0 = _50Hz_c0;
    }
    else{
        a1 = _60Hz_a1;
        a2 = _60Hz_a2;
        b1 = _60Hz_b1;
        c0 = _60Hz_c0;
    }
    for (int i=0; i<3; i++) y1[i] = y2[i] = 0;
}
void common_setup(){
    int i;
    rx_top = rx_end = tx_top = tx_end = phase = 0;
    em_count1 = em_count2 = 0;
    max_count = 100;
    fir_lpf = emergence = old_1 = old_2 = 0;
    gain = 5;
    for (i=0; i<3; i++) timer_value[i] = 0;
    timer_value[3] = 10;
    timer_value[4] = 1500;
    for (i=0; i<=order; i++) xn1[i] = xn2[i] = 0.0;
    sum_clear();
    notch_1 = notch_2 = 60;
    coef_set(60);
}

void timer_interrupt(){
    int i;
    for (i=0; i<6; i++) timer_value[i]++;
}

void tx_fifo_check(){
    if(xbee.writeable() == 1){
        if(tx_top != tx_end){
            xbee.putc(txFIFO[tx_end]);
            ++tx_end &= 255;
        }
    }
}

int rx_fifo_check(){
    unsigned char data;
    if(rx_top != rx_end){
        data = rxFIFO[rx_end];
        ++rx_end &= 255;
        if (data < 33){
            phase = 0;
            return(1);
        }
        raw_data[phase] = data;
        if(++phase > 5) phase = 0;
        return(0);
    }
    return(0);
}

void rx_fifoset(void){
    rxFIFO[rx_top] = xbee.getc();
    ++rx_top &= 255;
}

void tx_fifoset(unsigned char data){
    txFIFO[tx_top] = data;
    ++tx_top &= 255;
}

unsigned char hex_conv(unsigned char data){
    data &= 15;
    if(data < 10) return(data+48);
    else return(data+55);
}

unsigned char conv_hex(unsigned char data){
    if((data > 47) && (data < 58)) return(data-48);
    else if((data > 64) && (data < 71)) return(data-55);
    return(0);
}

void tx_message(int data){
    int i;
    for (i=0; i<6; i++) tx_fifoset(hex_conv((data>>(4*(5-i))) & 15));
    tx_fifoset(13);
}

float move_mean_calc1(float data){
    mean_sum1 = mean_sum1 - ad_data1[ad_pointer1] + data;
    ad_data1[ad_pointer1] = data;
    ad_pointer1++;
    if(ad_pointer1 == max_count) ad_pointer1 = 0;
    return(mean_sum1 / (float)max_count);
}

float move_mean_calc2(float data){
    mean_sum2 = mean_sum2 - ad_data2[ad_pointer2] + data;
    ad_data2[ad_pointer2] = data;
    ad_pointer2++;
    if(ad_pointer2 == max_count) ad_pointer2 = 0;
    return(mean_sum2 / (float)max_count);
}

●サブ FIR_LPF.hpp

(ここは上の例と変更ナシ)

 

そして「2-6」に取りかかって、昼過ぎまであれこれ検討して、上のようにリサジュー図形化して何か出来そうだ・・・というところまでで時間切れとなった。ちょっと実装のアイデアも出たのだが、残念ながら細切れの時間では実験できないので、続きは明日に頑張ろう。

2014年12月18日(木)

午後に学科会議と学部教授会と大学院教授会と入試関係後援会の予定があって丸潰れ(;_;)するこの日、09:30から予定していた「予約シリーズ」があれこれあって、フライト4便の予約、ホテル3件の予約、レンタカーの予約などしていたら、お昼になってしまった(^_^;)。 もっともその間に米国Cycling'74のテクニカルスタッフと英語のメイルを2往復させたり、事務局と何本か電話したり、さらにはSketchingコミュニティのMLから、下のような「NYの地下鉄でもLittleBitsの広告を見たぜ!」という写真が届いたりした。 LittleBitsは、我々Sketchingコミュニティの仲間が、KickStarterでファンドを集めて実現したプロジェクトなのだ。(^_^)

 

このメイルに触発されて、LittleBitsなら僕も活用してまっせ、と以下のメイルを久しぶりにSkwtchingコミュニティのMLにポストした。 なんせ、今年のSketching in Hardwareはベルリンで開催されたものの、参加できなかったので、たまに「生存証明」するのも大事なのだ。

Hello all,

I'm enjoying LittleBits every day.

http://nagasm.org/1106/news4/20140514/
http://nagasm.org/1106/news4/20140515-2/
http://nagasm.org/1106/news4/20140516/
http://nagasm.org/1106/news4/20141004/
http://nagasm.org/1106/news4/20141117/
http://nagasm.org/1106/news4/20141118-2/
http://nagasm.org/1106/news4/20141119/
http://nagasm.org/1106/news4/20141120/

http://www.youtube.com/watch?v=sRhxdGaSQfE
http://www.youtube.com/watch?v=c1e1U4tTkcU
http://www.youtube.com/watch?v=wh_iztSy-B8

Thanks !

Yoichi Nagashima
・・すると、「Oh my god, these are amazing!! We should have your modules on bitlab [www.littlebits.cc/bitlab] and also should write about your work on our blog!」 との書き込みがあった。 直訳すると、「オーマイガッ、何てことやってんだ! お前の作ったモジュールをLittleBitsの公式サイトで扱いたいぜ。お前の仕事をブログに書き込んでくれ」というような感じである(^_^)。 光栄なことに、LittleBitsの開発者じきじきのおでましである。 このサイトを眺めてみると、

  1. SUBMIT YOUR MODULE
    Upload your brilliant ideas. Include a working prototype, schematics, videos and pictures.

  2. GATHER VOTES
    You’ll need 1,000 votes to move to the next stage. Share your module with the world!

  3. LITTLEBITS MANUFACTURES
    An elite littleBits squad will review your submission. If approved it will go into production.

  4. WE SELL, YOU COLLECT
    We'll manufacture your shiny, new module while you gain credit, fame and cash!
となっていて、つまり、「LittleBits関連のモジュールを提案する」→「実現賛成の1000票以上を獲得」→「実際に製造する」→「販売して実績と名声とキャッシュをゲットできる(^_^)」という流れのようである。 このページの「MODULES」というタブのページには、色々なモジュール案と、採用されてもうすぐ出て来るものが並んでいる。 そしてなんと、
筋電センサ・モジュール まであった(^_^;)。 ワニグチクリップを使っているところだけはいただけないが(^_^;)、 説明動画 は素晴らしい。 筋電のナマの音とパワー情報しか使っていないが、僕のやってきたのも第3世代までは同じだった。 しかし、ちょっと余裕がないので、ここは当面シカトするしかないなぁ。

2014年12月19日(金)

原稿執筆期間もカウントダウンとなり、なんと朝5時起きで研究室には朝6時ジャストに出て来た。 照岡さんからの この動画 についてのメイルとか、Max7に関してCycling'74のテクニカルスタッフからの英語のメイルとか、をとりあえず受け流して(^_^;)、さっそく、昨日の夕方にやりかけていた実験と、今朝目覚めた瞬間に思い付いたアイデアを実験である。

 

まずは上のように、昨日の夕方にやりかけていた実験の続きをやって、その部分まで原稿を書き進めた。 2チャンネルの筋電信号をxとyに入れたリサジュー図形を描いてみて、そこから何か出来ないか・・・というシミュレーションであり、この図面ではNucleoF401REでなく、Max6上でまずは検討、という事である。

   

そして、2限のゼミまで、上のようなシミュレーションをしながら、あれこれと考えているうちに午前が終ってしまった。 6段並んでいるグラフの上の2段は筋電信号(x)と(y)の2チャンネル分、3段目がベクトル(x,y)の大きさ、4段目がベクトル(x,y)の傾き×大きさ、5段目がベクトル(x,y)から直線「y=x」までの距離、最下段がベクトル(x,y)の傾きだけ、である。

 

 

 

・・・ここから午後、ゼミの土佐谷さんのサポートを交えつつ頑張って、遂に「リサジュー解析」による筋電パターン認識のアルゴリズムを完成させ、プログラムを CQ_nagasm_10_Lissajous として上げた。 上のように、3つの筋電記録データに対して、2チャンネルの筋電信号の片方が強いと上方向/下方向に変動し、その振幅は筋電情報の大きさ(パワー)に対応するとともに、さらに両チャンネルの筋電パワーを乗算して低レベルでのノイズを抑止できている。 これで、最後の砦の「2-6」の前半の山場まで攻略できたことになる。(^o^)

そしてさらに2時間ほど、最後の壁である「FFT」について調べてみた。 PropellerでMIDIを実装した時に、それまでUARTを使ってザクッとシリアルを扱っていたのを、1ビットずつアセンブラで上げ下げ/モニタしてシリアルのMIDIを送受信したのが初めてだったように、これまでMaxの便利なオブジェクトとして使ってきたFFTを、NucleoF401REのC言語として実際に初めて実装する、という、あと2日しかない状況では無謀な挑戦である(^_^;)。

頼りのCQ・三上氏のFFTサンプルは これ なのだが、どうにも読みにくい(^_^;)ので、解読を諦めた。 そして、さすがオープンソースのmbed、「FFT」というキーワードで検索すると、以下のように11本ほど出て来た。 一部に同じ作者の改良版も重複していそうだが、ホストのマイコンがNucleoF401REと違っていても、ここまで進めてきたお陰で、読み替えて取り込む事も出来そうな気がする。

 

そこで、とりあえずこの11本のメイン「main.cpp」をザッと印刷して、持ち帰って当たりをつける事にした。 なんせ上のmbed開発ツールは、文字が小さくて薄くて、なんとも読みにくいのである(^_^;)。 いやまてよ、カスタマイズしてコントラストを上げる方法もあるのかもしれない・・・と思って、ブラウザのPreferenceの「ページ固有のフォントを優先/許可する」という2ヶ所をいずれもOFFってみると、下のようにくっきり、見えた。 ところが、これだと、テキストをハイライトしてコピー&ペーストするという作業がまったく出来ず、つまり「見やすいが使えない」と判明した。

 

仕方なく、やはり 多量のプリント  (7ポイントで打ち出して28ページほど)を持ち帰り、今日はこれでおしまいである。 なんせ、一杯やりながら、「一夜限りの復活・巨泉のクイズダービー」というのがあるのだ。 一杯やりながら11種類のFFTのCプログラムを眺めつつ、あとはもう、明日の勝負である。

2014年12月20日(土)

いよいよ逃げられず、FFTと正面から取り組む日である。 対象となったmbedサイトで公開されているサンプル資料は以下であった。
Craig Evans / AudioIn
	http://developer.mbed.org/users/eencae/code/AudioIn/

Frank Vannieuwkerke / KL25Z_FFT_Demo
	http://developer.mbed.org/users/frankvnk/code/KL25Z_FFT_Demo/

不韋 呂 / UIT_FFT_Real
	http://developer.mbed.org/users/MikamiUitOpen/code/UIT_FFT_Real/

Tony Abbey / KL25Z_FFT_Demo_tony
	http://developer.mbed.org/users/tony1tf/code/KL25Z_FFT_Demo_tony/

Igor Skochinsky / FFT
	http://developer.mbed.org/users/igorsk/code/FFT/

Daniel Dobano Fernandez / pruebas
	http://developer.mbed.org/users/ddobano/code/pruebas/

Eli Hughes / CMSIS_DSP_401
	http://developer.mbed.org/users/emh203/code/CMSIS_DSP_401/

Ale C.- / FFT_Example
	http://developer.mbed.org/users/Suky/code/FFT_Example/

James Cobb / audio_FFT
	http://developer.mbed.org/users/jcobb/code/audio_FFT/

Yue Kong / WoYaoChengGOng
	http://developer.mbed.org/users/KongXiangyue/code/WoYaoChengGOng/

Nathan Lasseter / GraphicEqFFT
	http://developer.mbed.org/users/User_4574/code/GraphicEqFFT/

Marcelo Rebonatto / PMED_Tempo
	http://developer.mbed.org/users/rebonatto/code/PMED_Tempo/

Elliot Hernandez / SpectrumAnalyzer
	http://developer.mbed.org/users/gth646f/code/SpectrumAnalyzer/

CQpub0 Mikami / FFT_Sampling
	http://developer.mbed.org/users/CQpub0Mikami/code/FFT_Sampling/
ここから選んだのは、「Nathan Lasseter / GraphicEqFFT」(http://developer.mbed.org/users/User_4574/code/GraphicEqFFT/)である。 そして、じっくり構えて数時間、ようやく、なんとかやっつけて、プログラムを CQ_nagasm_11_FFT1 として上げた。 筋電記録信号もあるので大きいが、ここまでのMax6パッチは これ  である。

 

上は、記録した筋電センサ信号出力を表示したもので、画面内には上下2段、2つの筋電チャンネルに対応したそれぞれ7つのウインドウが、左端は周波数バンド(1)とバンド(2)の平均レベル、2番目が周波数バンド(2)とバンド(3)の平均レベル・・・となっている。 かなり波形が激しく振動しているように見えるが、このフルスケールで30秒間の記録筋電センサ信号よりも長い時間を描画しているので、3種類の記録筋電情報ごとに異なったスペクトル情報をリアルタイムに得られていることが判る。

FFTさえ出来ればあとは「お話」ということで、22年前に作った「ニューラルネットによるパラメータ補間を使ったグラニュラシンセサイザ制御」のお話として こんなリスト  を発掘して紹介した。 今は亡きPC-9801ノートで作ったCプログラムで、入力層2ノード、中間層16ノード、出力層2ノード構成の倍精度変数の巨大な行列を「学習」するのに2週間ほどかかったものである(^_^;)。 そして「Appendix」として、 これ  とか これ  とかで発表している内容を簡単に紹介した。 ここは、そうは気付かれないかもしれないが、これから照岡さんとも研究していきたい「ネタ」が潜んでいるのだ。 これで1日が終って、明日は心おきなく、某オフを堪能できそうだ。 執筆はあと月曜・火曜の2日勝負。月曜に「3-2」をクリアして、火曜に全体を再読校正、という作戦でいこう。

2014年12月23日(祝)

予定通りに昨日は「3-2」を完了して、あとは全ての図にキャプションを入れれば・・・と甘く考えて出て来たが、原稿の全てを読みつつチェクッしてみると、なんと夕方までかかってしまった。 全原稿は これ  である(ただしインターフェース誌が発行されるまではサーバに上げないので「Not Found」となる(^_^;))。 以下のように、分量を考えずにがんがん書いたところ、文字数で98,269文字。これは単行本マルマル1冊分のほぼ半分だ。

 

これが全部、特集記事として載ってくれるかどうかはまったく不明だが、目がショボショボになりつつ、とにもかくにも脱稿である(^_^)。 明日のアカペラのクリスマスバーティも、明後日の工事休み(病院2件ハシゴ)も、心おきなく、というところだ。 次には1月上旬までに1万字程度の論文、そして3月の学会発表予稿を2本、と目標はどんどん推移していく。 原稿の校正とかあるものの、ここが区切りなので、これにて「mbed日記」は、おしまいである。(^o^)

→ 話はここに続く