JavaScript
電子工作
obniz

池袋晶葉ちゃん誕生日に向けてobnizで動くウサちゃんロボを作った

追記:SSR実装しました。
池袋晶葉ちゃんSSRが可愛いので衣装の一部をobnizで再現した

ウサちゃんロボを作る

JavaScriptで電子工作ができる、obnizというものが最近発売されました。
詳細はこの辺が詳しい。
JavaScriptで制御できる電子工作ボード「obniz」が発売、実売4,980円

要はブラウザさえあればJavaScriptで簡単に電子工作ができるという超便利ツール。
こ、こりゃ面白そうじゃん!
電子工作やったことないけどJavaScriptならイける!
と食いついてみたものの、

  • 電子工作 ⇒ ロボ
  • ロボ ⇒ メイドインアビスのレグ or アイドルマスターシンデレラガールズの池袋晶葉

くらいしか思い浮かばないオタクだったので、6月10日が池袋晶葉ちゃんの誕生日ということもあり、彼女の代表作ウサちゃんロボを作ってみることにしました。
というかこのタイミングでobnizが出たのはもはやそういう啓示だったのでは?

ちなみにアイドルマスターシンデレラガールズに明るくない方々向けに説明すると、これが池袋晶葉ちゃんで、彼女が抱えているのがウサちゃんロボです。

Cu229.jpg

うむ。可愛い。
その可愛さについて語るのはQiita向けではないと思うので泣く泣く割愛します。

やっていきます

早速やっていきましょう。
今回使った素材は以下の通り

上記リンクを見てもらうとわかるように、ウサちゃんロボは本来二足歩行
しかし、いきなり二足歩行ロボを自作するのはハードルが高い。

そこで、俗にいうプロトタイプウサちゃんロボに挑むことにしました。
さっきの画像のような、キャタピラで動くタイプですね。
というわけでお手軽に手に入るタミヤの工作キットを使うことに。

上記以外に準備したものは、obnizを動かすためのmicroUSB接続の電源

ちょうど最近スマホがType-C接続になったことで持て余していたモバイルバッテリーがあったので、こいつを使うことにしました。
大きさも名刺サイズでちょうどいい感じ。
DSC_0089.JPG

あとは、自分は電子工作ど素人だったので初めて知ったのですが、諸々の機器をつなぐにはジャンパワイヤというものを使うんですね。

はんだを使わずに差し込んでつなげる、固めのピンつきの導線。
obnizへの接続もこれが前提みたいですね。
Amazonとかで適当に買えます。

では前振りも長くなってきたのでいよいよ製作に入っていきますよ。

1. obinizを組み込んでツインモーター式のラジコンを作る

今回ツインモーター式にこだわったのは、モーター1つだと単純な直進後退しかできないから。
やっぱり超信地旋回したいじゃないですか。オタクなので。
Wikipedia: 超信地旋回

さてさて、タミヤのラジコンプラモです。
これはもう言われるがままに組み立てるだけ。
ただし、モーターにつなぐのが付属のリモコンではなく、obnizになるというところだけ違います。

さあお待たせいたしました。
ようやくobnizのプログラム部分についての解説です。

obniz購入後にアカウントと機器のシリアルを登録すると、ブラウザ上で使える開発環境が提供されます。
この辺のチュートリアルだったりモーターとのつなぎ方の詳細は公式が詳しいので割愛しますね。
quickstart - obniz

JavaScriptからモーターを操作するにはSDKのDCMotorというパーツを使用します。

JavaScript
var obniz = new Obniz("XXXX-XXXX");
obniz.onconnect = async function () {

  var lmotor = obniz.wired("DCMotor", {forward:0, back:1});
  var rmotor = obniz.wired("DCMotor", {forward:2, back:3});

}

今回はツインモーター式なので割り当ても二つですね。

そしてこれらのモーターを動かすだけならとても簡単。
HTMLのボタンを置いて、クリックイベントでモーターを動かすメソッドを呼ぶだけです。

HTML
<input id="forward" type="button" value="前進" />
<input id="reverse" type="button" value="後退" />
<input id="stop" type="button" value="停止" />
JavaScript
var obniz = new Obniz("XXXX-XXXX");
obniz.onconnect = async function () {

  var lmotor = obniz.wired("DCMotor", {forward:0, back:1});
  var rmotor = obniz.wired("DCMotor", {forward:2, back:3});

  $("#forward").click(function(){
    lmotor.forward();
    rmotor.forward();
  });

  $("#reverse").click(function(){
    lmotor.reverse();
    rmotor.reverse();
  });

  $("#stop").click(function(){
    lmotor.stop();
    rmotor.stop();
  });

}

たったこれだけのコードで前進、後退、停止が実装できました。

あとはobnizさんの電源を入れて、プログラムを実行して表示されたボタンを押せば動きます。

スクリーンショット 2018-06-09 18.35.19.png

は、早すぎる・・・!

さらに、上記実装だと両方のモーターを同じ方向に動かしていますが、これを互い違いに動かすことで夢の超信地旋回になります。

ということでボタンを増やして動かしてみた結果がこちら。


これでobnizでラジコンを動かすという第一段階はクリア!

2. ラジコンをスマホで操作できるようにする

とりあえず動いたものの、もっと手軽に、いうなればラジコンみたいに動かしたいですよね。

手軽といえばスマホ。

スマホで画面を表示して、横持ちでレバーみたいに操作できたらいい感じになるのでは?

あとはレバーっぽくするなら倒し方に応じてモーターの動くスピードも変えたい。
さらに超信地旋回が捗りそう(どんだけ好きなんだ)
この辺は<input type="range" />使ったらいい感じにできるかも??

ということで、スマホ用の操作画面を作成してみましょう。

HTML
  <div id="controller">
    <input id="right" type="range" min="-50" max="50" value="0" step="1" />
    <input id="left" type="range" min="-50" max="50" value="0" step="1" />
  </div>
CSS
    html{
      overflow-y: hidden;
    }
    input[type=range] {
      -webkit-appearance:none;
      background:rgb(128,128,128);
      height:24px;
      width:200px;
      border-radius:8px;
      transform:rotate(-90deg);
    }
    input[type=range]::-webkit-slider-thumb{
      -webkit-appearance:none;
      background:#f00;
      height:40px;
      width:40px;
      border-radius:50%;
    }
    input[type=range]::-ms-tooltip{
      display:none;
    }
    input[type=range]::-moz-range-track{
      height:0;
    }
    input[type=range]::-moz-range-thumb{
      background:#f00;
      height:50px;
      width:50px;
      border:none;
      border-radius:50%;
    }
    #controller{
      position: relative;
      width: 100%;
    }
    #left{
      position: absolute;
      left: -60px;
      top: 100px;
    }
    #right{
      position: absolute;
      right: -60px;
      top: 100px;
    }
JavaScript
var obniz = new Obniz("XXXX-XXXX");
obniz.onconnect = async () => {

  //左右のモーター
  var lmotor = obniz.wired("DCMotor", {forward:0, back:1});
  var rmotor = obniz.wired("DCMotor", {forward:2, back:3});

  //モーターを動かす関数
  var move = (motor, val) => {

    //パワー(絶対値)
    var absval = Math.abs(val);

    //メモリが0なら停止
    if(val == 0){ motor.stop(); }

    //パワーをセットして正の値なら前進、負の値なら後退
    motor.power(absval);
    motor.move(0 < val);

  }

  /*
    rangeのイベントは以下を使い分け
    ・input  : メモリの値が変わる度に発火
    ・change : ツマミを動かし終わった=手を離したタイミングで発火
  */
  $("#controller").on("input", "#right", function(){

    //右のレバー倒してる間はメモリの位置に応じてモーターを動かす
    var val = $(this).val();
    move(rmotor, val);

  }).on("input", "#left", function(){

    //左のレバー倒してる間も同様に
    var val = $(this).val();
    move(lmotor, val);

  }).on("change", "#right", function(){

    //レバーから手を離したら0の位置に戻してモーターを止める
    $("#right").val(0);
    rmotor.stop();

  }).on("change", "#left", function(){

    //左も同様に
    $("#left").val(0);
    lmotor.stop();

  });

}

一気に全部のソースを載せましたが、ポイントをいくつか。

rangeコントロールを縦にして左右配置

やたらとstyle要素の内容が多いですが、これほとんどrangeコントロール用。
この辺を参考にスマホからでも操作しやすいようにツマミを大きくしました。
input type=range タグをカスタマイズするために

縦に配置するのはtransform: rotateで90度回してあげるだけ。
左右配置はposition: absoluteでチャチャっと。

rangeのイベントの使い分け

値をとるためのイベントはchangeinputの2つがあります。
コメント中にもある通り、それぞれ

イベント 発火タイミング
input メモリの値が変わる度
change ツマミを動かし終わったら

なので、これを使い分けてます。

実際にモーターを動かすのはinputイベントで。
つまみから手を離したら0の位置に戻す処理をchangeイベントで。

ちなみに各rangeに直接イベントを仕込むと、2つのツマミを同時に動かすのがうまくいかなかったので、親要素のdelegateの形で設定しています。

rangeの閾値設定

rangeの値をそのままDCMotorのパワーに設定することで、メモリの位置と走るスピードを連動させます。
負の値の場合は後退ですね。

HTML
<input id="right" type="range" min="-50" max="50" value="0" step="1" />
<input id="left" type="range" min="-50" max="50" value="0" step="1" />
JavaScript
//モーターを動かす関数
var move = (motor, val) => {

  //パワー(絶対値)
  var absval = Math.abs(val);

  //メモリが0なら停止
  if(val == 0){ motor.stop(); }

  //パワーをセットして正の値なら前進、負の値なら後退
  motor.power(absval);
  motor.move(0 < val);

}

左右それぞれのrangeでそれぞれで上記のmove関数を呼び出します。
呼び出すイベントについては前述の通り。

さて、obnizのDCMortorは最大パワーを100まで設定できます。
そりゃ爆走させたいから当たり前のように100から-100を閾値に設定して…

と、いきたいところだったのですが、全速力にしたところあっという間にバッテリーが限界を迎えて停止する羽目に。
確かにモーター2つ動かしてるし、モバイルバッテリーちゃんの電圧では厳しかったか。

そんなわけで半分の50から-50でやってみたところ安定したのでこの値に。
まあスピードにこだわっても仕方ないしね。

やっかいなPull-to-Refresh

自分はAndroidを使っているのでChromeさんでコントロール画面を表示することになるのですが、ツマミをぐりぐりしてるとPull-to-Refresh(引っ張って更新)が邪魔をします。

これはよろしくない。
ということで苦肉のスタイル設定。

CSS
html{
  overflow-y: hidden;
}

まあそもそもスクロールしちゃダメな画面だし問題なし。


ってわけでこんな感じの画面になりました。
横持ち親指スタイルですね。

3. ウサちゃんロボのプラモをこさえる

届いて、

完成。

特に説明はありません。
「わー、かわいいー、ちっちゃーい」って言いながら作るだけです。

あと勢いあまって足つけちゃってますね。
後で取り外します。

4. ワハハ、いよいよ完成だぞ!

余ったダンボールを使って箱を作成して、モバイルバッテリーとobniz本体と配線を収納。
そしてラジコンにマウント。

その上にさらに足を外したウサちゃんロボを乗っけて完成!

さあ、いよいよ動かします。

わーいわーい楽しい。

多少スピード感には欠けますが、超信地旋回もできるし、操作自体もほとんどラグがなくてストレスフリー。
obnizは同じWi-Fiスポットにつながった機器であればそのローカルエリア内で接続するため、ラグが少なくできるらしいです。

そしてモバイルバッテリーのLEDが光ってるのがロボ感あって個人的に好き。

ウサちゃんロボのプラモは顔のシールがいくつか用意してあって差し替えができるんですが、充電中はこんな風にもできます。

うーん超可愛い。

おしまいに

そんなわけでobnizの存在を知ってからわずか4日でここまで工作できました。

繰り返しになりますが自分は電子工作に関しては超ド素人。
それでもobnizなら、JavaScriptが使えればちょっとしたアイディアがすぐに実現できます。

しかも一番うれしいのが公式ドキュメントがめちゃくちゃ充実してるんですよね。
これが本当に助かる。

そもそものインターフェースもシンプルってのはあるんですけど、ドキュメントがちゃんとしてるってだけでかなり実装のハードルが下がりますね。

とりあえずはパッと思いついたものがとても簡単に作れて満足しましたが、またアイディアが浮かんだらなんか作ってみようと思います。

最後に今回参考にした記事を。