この記事はShanonAdventCalendar2018 ・2日目の記事として投稿している。
今日は身体を鍛え健康的にクリスマスを過ごしたいと考えるエンジニアに広く読まれるべき内容として、
マッスルセンサーを紹介をしたい。
マッスルセンサーを紹介をしたい。
そもそも私がこのマッスルセンサーというものを手に入れようと考えたのは、趣味でやっているタブラというインドの打楽器を叩くときに速度をあげて叩こうとすると手の筋肉がつったような感じになるため困っていたからだ。筋肉がどういう状態なのか切実に知りたかったが、筋肉に話しかけたところで答えが返ってくるはずもない。
しかし会話ができないからといってコミュニケーションできないと考えるのは早計である。筋肉には筋肉の言葉がある。それは電気である。
筋繊維が興奮するときには、電位の変化が起こっている。つまり筋肉は絶えず声なき声を上げ続けているのだが、それが電気で発せられているので、いまいち私たちに聞こえていなかったのだ。
彼らの声に耳を傾けるために、筋電計というものがあって、それを使うと筋繊維の状態を測定することができる。
とはいえ医療用に販売されている筋電計はそれなりに値が張るので簡単には入手できない。頭を抱えていたところ出会ったのが「MyoWare Muscle Sensor」だった。
これであれば数千円で入手できるので、お手軽である。
諸々準備して、最終的には以下の材料を使った。
- MyoWare Muscle Sensor
- 生物医学センサパッド
- Arduino Uno
- 長めの電線(マッスルセンサーは筋肉に貼り付けて使うので、よく使うジャンパワイヤーより長いものをおすすめする)

ちなみにこれらの材料はデータシートに記載がある一番簡単な構成で作成する場合のものになる。
あまりおすすめしない構成とのことだったが、自分の筋肉で試す分には問題なかった。
これらをつないで手に貼り付けた。
そもそもの目的がインドの打楽器の技術向上なので、もちろんタブラも用意している。
これにより得られた筋電図がこれになる。
なおインドの楽器に詳しくないとあまりピンとこないと思うが、連続して「ta ta tirkit」というパターンを繰り返し叩いた測定結果になっている。
(手の動きとしては、「右人差し指、右人差し指、右中指、右人差し指、左手、右中指」という順番)
マッスルセンサーは筋肉に力が入っている時により大きい値を出力しており、最初の右人差し指を打つときに割と指を振りかぶっているような動きをしているので、それが定期的にとがっている山の部分になっている。
あとうまく叩き続けられているときは、打っていないときは波が下がっていて筋肉がリラックスした状態になっていることがわかる。
うまく叩けていない状態のグラフはこちらになる。叩いているパターンは同じなのだが、叩くペースを上げて叩き続けた時のグラフだ。
うまく叩けている時と比較すると、筋肉がリラックスしているタイミングが少なくなってしまっていて、筋肉の緊張が高まった状態になっている。
センサー使って観察することで、指が動かなくなっているときには筋肉が緊張した状態が続いてしまっていることがわかった。こうして計測することで、筋肉をリラックスさせながら同時に速く演奏するような練習をしていくことができそうだ。
あと、タブラの練習を日常的に行なっている方は少数派だと思うから、もう少し一般的なユースケースも想定して、大腿直筋にマッスルセンサーを貼り付けてスクワットを行ったグラフも載せておく。
こちらは見ればわかると思うが、台地みたいな波形になっている最初のところがしゃがみこんだところで、台地が終わるところが立ち上がり切ったところだ。どのタイミングで負荷がかかっているか、負荷がどのくらいの時間継続しているかということが容易にわかる。
こうしてマッスルセンサーを使ってみると、頭で筋肉への負荷を勝手に推量することがいかに自己中心的な態度であったかと感じる。筋肉トレーニングは自分が一方的に筋肉を酷使するような利己的なものであってはいけない。筋肉と対話をして彼らの思い、考えを理解しながらともに深い会話の世界にはいりこんでいくべきである。
なお、今回のコードは下記する。ぜひ各自で筋肉との出会いを楽しんでもらいたい。
それでは次回のアドベンドカレンダーもお楽しみに。
Arduino
int onboardLED = 13;
int voltageThreshold = 400; // any reading higher will trigger an action
void setup() {
Serial.begin(9600);
pinMode(onboardLED, OUTPUT);
}
void loop() {
int currentVoltage = analogRead(A0);
Serial.print(currentVoltage); // processingに渡すためにセンサーの結果をシリアル出力する
//入力値が高すぎるときにLEDをつける
if(currentVoltage > voltageThreshold){
Serial.println("CONTRACTION!");
digitalWrite(onboardLED, HIGH);
} else {
Serial.println("");
digitalWrite(onboardLED, LOW);
}
delay(200);
}
Processing
import processing.serial.*;
Serial port;
int[] values = new int[100];
int screen_size_x = 800;
int screen_size_y = 500;
int base_height = screen_size_y - 50;
void settings() {
size(screen_size_x, screen_size_y);
}
void setup() {
frameRate(100);
smooth();
String[] ports = Serial.list();
for (int i = 0; i < ports.length; i++) {
println(i + ": " + ports[i]);
}
// portはそれぞれの環境で適切なindexを入れる
port = new Serial(this, ports[3], 9600);
}
void draw() {
background(color(255));
int x_stroke_length = screen_size_x / 100;
// gridline
fill(#95a5a6);
textAlign(RIGHT);
stroke(color(#ecf0f1));
for (int i = 0; i < 10; i++) {
int h = i*50;
line(0, base_height - h,
screen_size_x, base_height - h);
text(h, 25, base_height - h);
}
// baseline
stroke(color(#34495e));
line(0, base_height,
screen_size_x, base_height);
// maxline
stroke(color(#e74c3c));
int max_value = base_height - max(values);
line(0, max_value,
screen_size_x, max_value);
// value text
fill(#2c3e50);
textAlign(LEFT);
text("MAX=" + max(values), screen_size_x - 80, 25);
text("MIN=" + min(values), screen_size_x - 80, 40);
// graph
stroke(color(#e67e22));
for (int j = 0; j < 99; j++) {
line(x_stroke_length * j, base_height - values[j],
x_stroke_length * (j + 1), base_height - values[j + 1]);
}
}
void serialEvent(Serial p) {
if (p.available() > 0) {
try {
String input = p.readStringUntil('\n');
if (input != null) {
input = trim(input);
println(input);
values = append(subset(values, 1), int(input));
}
} catch (RuntimeException e) {
}
}
}