ラズパイでディープラーニングと筋電位センサを用いた筋電義手(プロトタイプ)作りました

最近、以前から興味のあった筋電位センサを触っています。
また、ディープラーニングの勉強も始めたのですが、
ディープラーニングの仕組みを理解する中で「ディープラーニングって筋電位センサに応用できるんじゃ?」と閃きました。
そこで色々試してみたところ、簡単な動作推定を行うことに成功したので、まとめてみます。
タイトルにある通り、ラズパイを使ってます。

実際の動作

実際に動いているときの動画です。
手を握った時、反った時、屈曲した時の3パターン(厳密には無動作時を併せて4パターン)における筋電位パターンをディープラーニングを用いて分類し、分類結果に応じてサーボモータを動かしています。

筋電位センサ(自作)

※この項で紹介するセンサは最終的には使いませんでした。
筋電位センサは全くのド素人なので、まずはコチラのサイトで紹介している筋電位センサを自作してみました。
www.f.waseda.jp


実際にPCのマイクアンプに入力すると、力を入れるごとに確かに筋電位波形が確認できました。
そこで、今度はAD変換ICを介してラズパイでセンサ値を読み取ろうとしたのですが、
力を入れた時と入れないときの違いが全く出ませんでした。
この理由として、前述の自作筋電位センサは神経が発する表面電位を計装アンプ(LT1167)により増幅していますが、
これを更にPCのアンプ・フィルタが増幅・フィルタリングしてくれているため、うまく波形を確認できたと考えられます。
つまり、前述の自作筋電位センサ単体では筋電位をセンシングすることは難しく、PCありきのセンサになっている、という事です。
実際に、ステレオプラグ→USB変換アダプタを用い、マイク端子を介さずにUSBを介することで自作筋電位センサをPCに接続したときの挙動を実験したところ、力を入れても波形を確認することができませんでした。
よって、自作筋電位センサの使用は今回は見送ることにします。

筋電位センサ(市販:MyoWare)

自作の筋電位センサは諦めて、市販の筋電位センサMyoWareを使用することにしました。
www.switch-science.com


結果論になりますが、センサ値が上手く取れなければディープラーニングも意味をなさないため、この選択は正解だったと思います。
併せてMyoWare専用のパッドも買ったのですが、こちらは自分的には買う必要はないかなという印象です。

www.switch-science.com

理由として、専用パッドの粘着性があまりに強いのか傷つきやすかったり、パッドを貼る箇所の自由度が低いことが挙げられます。
実際、MyoWareの写真を見てもらえればわかるかと思いますが、デフォルトのパッドは基板直下の2つとケーブルでつながった1つの計3つです。実際に腕に着けてみると、基板自体にパッドがついているせいなのか何か違和感があり、パッドが腕からとれやすかったりします。


実はMyoWareにはもう一つオプションがあり、デフォルトのパッド端子の他に、基板右下に設けられたR,E,M端子を用いることもできます。私は写真のように、R,E,M端子にピンソケットをはんだ付けして、オスピン端子を片側に圧着したワニ口クリップを差し込みました。
f:id:mou-tsukareta:20190717024635j:plain
f:id:mou-tsukareta:20190717024612j:plain


この方法ならば、自分の好きな位置にパッドを貼れますし、パッドも自分が好きなものを選ぶことができます。
今回のパッドは、前述の自作筋電位センサを作った時に使っていたオムロンのパッドが良い感じなのでこれを使うことにします。

akizukidenshi.com
akizukidenshi.com
akizukidenshi.com
akizukidenshi.com

回路

以下の回路図の写真の通り、AD変換モジュールにはMCP3208を用いました。
f:id:mou-tsukareta:20190717024723j:plain

ラズパイからMCP3208およびMyoWareに5Vを供給しています。
疑似差動入力は用いず、MyoWareの信号端子をMCP3208のCH0に接続しています。

ディープラーニング入門

私はディープラーニングについてもド素人です。
なのでどう勉強するかから考えたのですが、書店で色々探して吟味したところ、「はじめてのディープラーニング」が最も分かりやすく応用も効きやすそうな内容だったため、これで勉強することにしました。1週間もかからずに理解できる内容と思います。

ツール等に頼らずディープラーニングの仕組みを理解しながら学べる内容になっており、またソースコードも比較的短いものばかりのため、解説文が理解できなくともコードを読めば簡単に原理を理解できます。
私もこれを読む前は「ディープラーニング」と聞くと難しく抽象的なことをやってるのかなと思っていたのですが、これを読んだ後はあくまで「多層ニューラルネットの出力誤差を最急降下法を用いて重みとバイアスにフィードバックする」アルゴリズムでしかないことがわかりました(間違ってたらごめんなさい)。

ディープラーニング(データ解析)

筋電位センサ特有の前処理で、ある意味一番重要な処理だと思います。
実際の筋電位センサの出力波形は下図に示すような筋電位の強さに応じた電圧です(横軸[s],縦軸[V])。
ちなみに、サンプリング周期は0.005[s]で2[s]の区間を切り取っています。
f:id:mou-tsukareta:20190717024806j:plain

同じ動作でも力の入れ方によって、筋電圧の強度は変わるため、これをそのままディープラーニングに学習させてもうまくいかないことは明白です。
そこで、上図の筋電位波形を見てみると、動作によって波形の"感じ"が異なることがわかります。掲載は割愛しますが、同じ動作であれば毎回おなじ"感じ"になります。
この"感じ"を数値として表現するために、FFT高速フーリエ変換)を行います。
フーリエ級数展開ググると色々出てきますが、この世の様々な波形は色々な周波数(早い・遅い振動の周期)の波が合成されたものになっています。そこで、周波数解析を行うことで、対象となる波形にどれくらいの周波数の波がそれぞれどれくらい含まれているのかを知ることができます。この周波数解析をコンピュータ上で行うために離散化させたものがFFTです。
実際に前述の屈曲動作時における波形にFFTをかけると、下図のようになります(横軸[Hz]、縦軸[スペクトル強度])。
f:id:mou-tsukareta:20190717024823j:plain

このとき、FFTを施す前の波形は2[s]の区間をサンプリング周期0.005[s]で記録したものでした。これはデータ数400となります。
この時系列データに対してFFTを施すと、200[Hz](=1/サンプリング周期=1/0.005)の区間を周波数間隔0.5[Hz](区間/データ数=200/400)で離散化した周波数データとなります。これもデータ数400となります。
この事実はFFTをかけたときの図を見てもらえるとわかると思います。

ディープラーニング(データ取得)

今回は、10秒間隔でディスプレイに「力をいれてください」という文字列を表示させ、文字が表示されたら力を5秒入れるということを繰り返すことで、各動作における複数のデータを取得しました。
この方法であれば、出力結果を10秒間隔で切り出せばそれぞれの動作を切り出せるのでお勧めです。
実際は、10秒間隔の先頭2秒を切り出し、それぞれにFFTを施しました。
f:id:mou-tsukareta:20190717024902j:plain

もう一つのミソとして、AD変換の周期があります。
サンプリング周期は極めて正確にしたいので、今回はsignal.setitimerを使いました(ググればいろいろ出てきます)。
これを用いることで、signal.signalで設定した関数を定周期で起動することができます。
今回はサンプリング周期0.005[s]で処理時間計測してみました(結果を残し忘れました、、、、)が、ほとんど誤差なく起動できていましたので、問題ないと思います。

ディープラーニング(学習)

前述したとおり、2[s]の区間をサンプリング周期0.005[s]で記録した時系列データにFFTをかけると、200[Hz]の区間を0.5[Hz]間隔で記録したデータ数400の周波数データとなります。これを1つの学習用データとして、更にそれぞれのデータの末尾に実際の動作(握り、反り、屈曲、動作無し)に応じた分類用の数値(0,1,2,3)を付与します。
この手順で各動作につき5つのデータを用意し、計20個のデータを学習用データとして用意しました。
今回実装したディープラーニングの構成は以下になります。
・エポック数:1000
・学習係数:0.01
・入力層のニューロン数:400(=データ数)
・中間層のニューロン数:400×3=1200
・中間層数:2
・出力層のニューロン数:4(=分類数)
・中間層の活性化関数:ReLu
・出力層の活性化関数:ソフトマックス関数
・損失関数:交差エントロピー誤差
・最適化アルゴリズム:AdaGrad
・バッチサイズ:8
・中間層のドロップアウト率:50%

この学習によって得られた重みとバイアスはCSVファイルに記録しておきます。

ディープラーニング(推定)

CSVデータに記録した学習済みの重み・バイアスをロードし、リアルタイムに推定を行います。
やり方としては、リアルタイムに最新の筋電位データ(今回はサンプリング周期0.005[s]で2[s]の区間の400個のデータ)を常にバッファしておき、任意の推定周期でバッファリングしている筋電位データをFFT→フォーワードプロパゲーションを行うことで、リアルタイムに動作を推定することができました。この推定結果に応じてサーボモータへのパルス幅を変更してあげれば、簡単な筋電義手のできあがりです。

システムまとめ

以上を簡単に図としてまとめました。
f:id:mou-tsukareta:20190717095017j:plain
f:id:mou-tsukareta:20190717095031j:plain

感想

今回強く感じたのは、ディープラーニングが非常に容易に実装できる技術であるにもかかわらず、想像以上の威力を発揮してくれたことです。
今後はこれを様々なロボットや装置の操作用インタフェースとして応用していきたいと思います。