雑食エンジニアの気まぐれレシピ

日ごろ身に着けた技術や見知った知識などの備忘録的なまとめ.主にRaspberry Piやマイコンを使った電子工作について綴っていく予定.機械学習についても書けるといいな.

WiiBalanceBoard(WiiFit)で操作するロボット作った

前回の記事からだいぶ間が空いてしまいましたが,前回の最後に「操作してて面白い系のホビーロボットを作る」と言っていたものがコチラになります.

f:id:shikky_lab:20190813022041p:plain
帽子とお目々がチャームポイント.実は目玉はMFT会場で会った方からのプレゼント
下にWiiBalanceBoard(WiiFitの板)が置いてありますが,タイトルの通り操縦者がこの板の上で体を傾けて重心をずらすことにより操作することができるロボットです.動作の様子はコチラ.
youtu.be
またその折に機会があったので,こちらをMakerFaireTokyo2019に持っていきました.
Maker Faire Tokyo 2019 | Make: Japan
もし会場でご覧になられた方がいましたら,その節はお世話になりました.

さて,今回はこのマシンの中身について徒然と書いていきたいと思います.

名前とか

マシン名はWRIOS 2019といいます.Wii Remote Intuitive Operating System の略です.大学時代の学内ロボコンにてWiiリモコンで操作するロボットを初めて作った際の名前を引き継いでいます.
読みは「ライオス」なのですが,WRIでライって読むのは末尾がeのときだけだと英語強者のサークル仲間になじられた覚えがあります.ホントかどうかは未だにわかりませんが,響きが好きなのでライオス読みで通していきたいと思います.
[追記]調べたらマジでそうでした.そうなんか.でもこの響き好きなんよなぁ.
”I”を”アイ”と発音する時、”イ"と発音する時の違い | 英語の達人になりたい!ダラダラ学習帳feat.コーギー雀セキセイオカメ(旧:英語達人への道徒然練習日記) 様より

構成

f:id:shikky_lab:20190813023346p:plain
帽子を外して上からパシャリ

以上.
・・・・・・はい,そうなんです.フィードバック用のセンサー載ってないんです.
ホントはジャイロ積んで姿勢制御するつもりだったんですが間に合わず...近いうちにそれらを実装した版を公開します.たぶん.

ちなみに上の動画ではそこそこまともに動いてるっぽく見えてますが,実はコントローラで微調整入れてます.WiiFit[だけ]で操作するとは言ってないのでセーフ...

ソフト的な話

ラズパイを使用しているので言語は何でもよかったのですが,せっかくなので組み込みっぽくC++で書きました. 先述の通りフィードバックは入れてない故に制御に関しては特に語ることも無いので,WiiFit関連の話だけ触れておきます.

Wiiリモコンとのアクセスはlibcwiidというライブラリを使用しました.
GitHub - abstrakraft/cwiid: Linux Nintendo Wiimote interface
過去に使用したことがあるライブラリだったのであまり心配はしていなかったのですが.どうやら使い方やチュートリアルをまとめた公式ページがなくなってしまったらしく,過去の自分の実装をヒントに使い方を思い出すという面倒なことになってしまいした.
とはいえWiiリモコン自体とのつなぎ方などは先人がいろいろと公開しているので,WiiFitの扱いに特化して書いていきます.
※WiiBalanceBoardって書くの面倒なので,WiiFitって書いていきます.

WiiFitとの接続

libcwiidではリモコンに取り付ける拡張モジュール(ヌンチャクなど)の入力はcwiid_stateのcwiid_ext_typeを見て判断します.WiiFitにもこのext_typeが割り当てられているのですが,WiiFitはリモコンに接続できないので,Wiiリモコンとは別のリモコンとして接続させたうえで,ext_typeを見て判定します.

WiiFitのext_typeが抱えるバグ

WiiFitのext_typeは0x03なので,接続したデバイスのext_typeがこれならWiiFitと判断できるわけですが,なぜかモーションプラスと認識されてしまうことがあります.
これは古いlibcwiidが抱えているバグで,下記で解決しています.
Balance Board is identified as MotionPlus · Issue #2 · abstrakraft/cwiid · GitHub
というわけで最新版のlibcwiidでは起こらないのですが,apt installで取得できるlibcwiidはこのバグが発現するので要注意です.「なんだ,aptでゲットできんじゃん,やったー」とか思ってた自分は思いっきりハマりました.最初からソースからビルドしろやって話ですね.ズルは良くない.

体重・重心の取得方法

接続できた後も,WiiFitの扱いは地味に大変です.実はlibcwiidから重心などの実用的な値を直接取得することはできません.取得できるのは生の値となります.この生の値に対して,別途cwiid_get_balance_cal()で取得できる補正用の値を使用して計算する必要があります.その計算式は例の閉鎖した公式ページには記載されていた記憶があるのですが,今や見当たらないのでネットの海に散らばった断片を探します.
今回は wii-sensors/bal.c at master · derf/wii-sensors · GitHub 様のコードを丸パクしました(余談ですがWTFPLなんてライセンスは初めて聞きました).

float wiimote_c::calcWeightFromBalanceBoard(uint16_t reading, uint16_t cal[3]) {
    if (reading < cal[1])
        return ((float) reading - cal[0]) / (cal[1] - cal[0]) * 17.0;
    else
        return (((float) reading - cal[1]) / (cal[2] - cal[1]) * 17.0) + 17.0;
}

17.0とかどこから出てきた数字か知りませんが,この計算の結果,4隅のセンサそれぞれにかかる重さ(kg)が取得できます.
あとはそれぞれの検出する重さの偏りから重心を算出できます.

 \displaystyle
左右の重心 = \begin{cases}\frac{左側の重さ合計}{右側の重さ合計}  & (重心が右寄りの時) \\\\
\frac{右側の重さ合計}{左側の重さ合計}  & (重心が左寄りの時)
\end{cases}\\\\

上下の重心 = \begin{cases}\frac{上側の重さ合計}{下側の重さ合計}  & (重心が下寄りの時) \\\\
\frac{下側の重さ合計}{上側の重さ合計}  & (重心が上寄りの時)
\end{cases}

といった感じですね.この場合,中央が1で,端に近づくにつれ0になります.それを反転させて+1して使っています.グラフにすると下記ですね.

f:id:shikky_lab:20190813115101p:plain
x>0の時の数値変化グラフ(使用するのは0≦x≦1の範囲)
これはx>0の範囲ですが,実際には負の領域についてもこれをy軸で鏡写しにしたようなグラフになります.

誤動作対策

WiiFitで操作するとなると操縦者がバランスを崩したりすると暴走しかねないので,安全対策として下記の二つを組み込んでいます.

  • 台から下りたら止まる
  • ボタンを押している間だけ動作する

前者は動作する重さに閾値を設けて,それ以下の場合には無視するという実装となります.この閾値はテキトーに30kgで設定したのですが,MFTの会場で子供が乗った差異に動作しないことが多くて困りました.調べてみると30kgって10歳女子平均くらいらしいですね.そりゃダメだ.
ぶっちゃけ0じゃないことだけ検出できればいいだけなので,最終的には5kgに設定しました.
後者はWiiリモコンのBボタンを割り当てました.ただ最終的な理想としてはWiiFitだけで操作させたいので暫定対策ですね.

なお,コードは下記にあります.ライセンスは特に決めてませんが,そうですね,せっかくなので上に倣ってWTFPLとしましょうか(笑)
GitHub - shikky-lab/WRIOS2019

おわりに

こんなところでしょうか.記事ネタとしてはもう一つ,"wiiリモコンの接続が切れた際にプログラムがハングアップする"というものがあるのですが,こちらはまだ解決できてないので解決後に書きたいと思います.

実はWiiFitで操作するということ自体は一要素でしかなく,私のやりたいこととしてはまだまだ続きがあります.そのWiiFit部分についてすらもジャイロ制御が間に合っていないというひどい体たらくですね.
なのでさしあたりジャイロによる姿勢制御は搭載しますが,WRIOS Projectは更に続いていくので,来年のMFTでは完成版をお見せ出来たら良いなと思います.それでは.