筋肉をArduinoから操作する方法

「筋肉」をマイコンはじめ、コンピュータから操作する方法について解説します。

作成したデモ

コントローラから表情筋を動かしてみました。

まずは筋肉が動く仕組みについて調べる

感電すると筋肉が収縮するというのは比較的知っている人が多いと思います。

電気風呂や腹筋を鍛えるパッドでそのような体験をしたことのある人も多いはずです。

人間をはじめとした脊椎動物は、脳から延びる脊髄を中心とした中枢神経系が発する指令を筋肉へ伝えることで思い通りに手足を動かします。

ニューロンは神経細胞樹状突起部と呼ばれる部位で神経伝達物質を受け取ると、活動電位を発生して軸索の端まで伝わり神経伝達物質を放出。これによって次のニューロンや筋肉へ情報が伝達されていく仕組みだそうです。。

このときの活動電位と呼ばれる電気エネルギの発生が、人間の神経が電気信号とよく言われている理由なんですね。

活動電位は興奮性膜という細胞膜によって生み出されているらしいのですが、どうやらイオン濃度の偏りによって電位を生じさせているみたいです。

細胞の中から外の向きに電流を流すと、細胞内の電位が変化する脱分極と呼ばれる現象が起こり、電位を平衡まで戻そうとしてカリウムイオン+が細胞外にゆっくり流出していく。ただし、同時にナトリウムイオンのチャネルが開き、ナトリウムイオン+が細胞内に流入してくるために逆の反応も生じる。このとき、ナトリウムチャネルは脱分極の大きさによって開く数が増えるので、ある一定以上脱分極が大きくなると、核反応のように臨界に達して急激に平衡状態が移動するらしい。面白い!

閾値を超えないときは緩やかにカリウムイオンによって元の平衡状態へと戻ります。

最終的にナトリウムチャネルが閉じていくことで元のカリウムイオンの平衡状態に戻るのですが、このときの一連の電位の変化が活動電位の正体ですね。

神経はこの活動電位のスパイクのような信号を、隣接した細胞へリレーしていくことで命令を伝えているんですね。

活動電位の臨界の部分(電位が上がる部分)は時間にして2msくらいです。電圧は-80~+60mVくらいの正負電圧になります。

一度スパイクを発してからもう一度発することができる状態に戻るまで、大体20msくらいかかるようです。

これは重要です。

そして、信号を伝達するニューロンだけでなく、筋肉自体もこの活動電位の変化をトリガーに収縮を行っているそうです!

人間が感電したときに筋肉を収縮させてしまう理由はここにあるわけですね。

腕にパッドを貼り付けて、動かそうと思った意思を読み取って動く義手やアシスト装置を見たことがあるかもしれません。

あれは、筋肉やそれまでの神経が発する活動電位を読み取ることで動いているんですね。

実際にはたくさんの筋肉の繊維がそれぞれ20msくらいごとに各々のタイミングで収縮しているため、波形は複合されます。

これもまた重要で、筋肉に力を込めれば込めるほど収縮する筋細胞が増えるということなので、このスパイクの割合が増えていくということです。なんだかPWMみたいですね。

筋肉を動かすデバイスを作る

ここまで、筋肉の動く仕組みを軽くまとめると、筋肉を動かすためには2msくらいのP-to-Pで150mV程度の電気信号を、最大20ms周期で与えればよいことになります。

動きものの電子工作をしたことのある人なんかは気づいたかもしれませんが、これはかなりホビー用のサーボモータの信号に似ています。

サーボ信号はだいたいどれもON時間1,2msで周期20msですから、ベストマッチです。(もしかしてサーボモータのプロトコルは筋肉由来なんでしょうか!?)

となればもう決まりで、サーボ用のドライバを作って筋肉に接続します。

電圧はサーボだと5Vですが、筋肉ドライバではある程度可変できる必要がありそうなので、ゆとりを持たせた設計にします。

回路を作りました。

(図中のダイオードは不要です。他にも雑なところがたくさんあります。)

クリックで拡大

母体のマイコンはArduinoUnoです。

シールドにしてサクッとエッチングで作りました。

とりあえず10ch分用意しました。

肝心の筋肉との接合ですが、電極を身体に突き刺すのは嫌なので、そのまま電極を張ります。

ガーゼに生理食塩水をしみこませて、ほんとにそのまま電極を皮膚にあてがうだけです。

電極に関しては、↓のような格子っぽい基板を作ってみたり、市販の電気腹筋パッドのゲルシートを使ってみたりしましたが、直接電極を接触させるのが最もうまくいきました。

導電性を高めるために塩水を使いますが、濃度が高すぎると肌が荒れるので生理食塩水を使います。

電圧は8~15V程度がちょうどいいと思います。

電気腹筋パッドのゲルシートは、それ自体に導電性はなく、コンデンサとして交流成分を通しているものなので、波形や周波数をいじればうまくいく可能性は大いにありそうです。

プログラムを書く

プログラムは驚くほどシンプルです。

arduinoの場合、servoライブラリという便利なライブラリがあります。

これをそのまま使用します。

on時間を2msに設定して電極の駆動ピンに出力すればおしまいです。

servoライブラリはソフトウェアタイマーを使うので、GPIOピンなら大体どこでも使えます。そのため、より複雑な制御のためにch数を増やすのは簡単です。

私が今回作成したデモでは、ROSSerialというマイコンでROSに対応した通信ができるフレームワークを利用しましたが、普通に動かすならノーマルのスケッチでOKです。

以下はrosserialのサンプルコードです。

ROSパッケージも含めてgithubに公開しています。(スターめっちゃ来てヤバい……)

#include <ros.h>
#include <ros_face_msgs/FaceCmd.h>
#include <ros_face_msgs/Ch.h>


#include <Arduino.h>
#include <Servo.h>


#define pulse_width 2400

#define pin1 2
#define pin2 3
#define pin3 4
#define pin4 5
#define pin5 6
#define pin6 7
#define pin7 8
#define pin8 9
#define pin9 10
#define pin10 11
ros::NodeHandle  nh;
Servo channel_1;
Servo channel_2;
Servo channel_3;
Servo channel_4;
Servo channel_5;
Servo channel_6;
Servo channel_7;
Servo channel_8;
Servo channel_9;
Servo channel_10;


void Cb( const ros_face_msgs::FaceCmd& data){
    if(data.ch1.state){
        channel_1.writeMicroseconds(pulse_width);
    }else{
        channel_1.writeMicroseconds(0);
    }

    if(data.ch2.state){
        channel_2.writeMicroseconds(pulse_width);
    }else{
        channel_2.writeMicroseconds(0);
    }

    if(data.ch3.state){
        channel_3.writeMicroseconds(pulse_width);
    }else{
        channel_3.writeMicroseconds(0);
    }

    if(data.ch4.state){
        channel_4.writeMicroseconds(pulse_width);
    }else{
        channel_4.writeMicroseconds(0);
    }

    if(data.ch5.state){
        channel_5.writeMicroseconds(pulse_width);
    }else{
        channel_5.writeMicroseconds(0);
    }

    if(data.ch6.state){
        channel_6.writeMicroseconds(pulse_width);
    }else{
        channel_6.writeMicroseconds(0);
    }

    if(data.ch7.state){
        channel_7.writeMicroseconds(pulse_width);
    }else{
        channel_7.writeMicroseconds(0);
    }

    if(data.ch8.state){
        channel_8.writeMicroseconds(pulse_width);
    }else{
        channel_8.writeMicroseconds(0);
    }

    if(data.ch9.state){
        channel_9.writeMicroseconds(pulse_width);
    }else{
        channel_9.writeMicroseconds(0);
    }

    if(data.ch10.state){
        channel_10.writeMicroseconds(pulse_width);
    }else{
        channel_10.writeMicroseconds(0);
    }

    delay(20);
}

ros::Subscriber<ros_face_msgs::FaceCmd> sub("face_cmd", Cb);


void setup()
{
    pinMode(13, OUTPUT);
    nh.getHardware()->setBaud(115200);
    nh.initNode();
    nh.subscribe(sub);
    channel_1.attach(pin1,40,2400);
    channel_2.attach(pin2,40,2400);
    channel_3.attach(pin3,40,2400);
    channel_4.attach(pin4,40,2400);
    channel_5.attach(pin5,40,2400);
    channel_6.attach(pin6,40,2400);
    channel_7.attach(pin7,40,2400);
    channel_8.attach(pin8,40,2400);
    channel_9.attach(pin9,40,2400);
    channel_10.attach(pin10,40,2400);
}

void loop()
{
    nh.spinOnce();
    delayMicroseconds(5);
}

電極を任意の筋肉の近くに貼り、サーボ信号を出力すると感電するはずです。

たいてい、思った通りには動かないので人体の筋肉モデルを見たり、力を入れてみたりしながら最適な場所を探す筋肉マイニングを行ってください。

以上です。

handaru

handaru.net

handaru(はんだる)です。 工作が趣味です。

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です