MATLAB/SimulinkとUnityをつなげてベイブレードシミュレータをつくる

こんにちは。
本記事は[呉高専 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/kurekosen)の記事です。
友人に書けと言われたのでこの記事を書いていますが、残念ながら私はプログラミングド素人です。
こんな素人でも記事が書けるなんて、ネットの情報って本当に信用できませんね。
変なこといっぱい書きますが、修正とか指摘とかして頂けると本当にありがたいです。


#動機
私事ですが、最近ベイブレードを始めました。[ベイブレードバースト公式サイト](https://beyblade.takaratomy.co.jp/)
![bbburst_main.jpg](https://qiita-image-store.s3.amazonaws.com/0/326891/f6a10f2c-4c13-1bc9-a2cf-c1a7d28f2453.jpeg)
やっぱり何歳になってもおもちゃで遊ぶのは楽しい。
最近のベイブレードは「バースト」という、ベイがバラバラに分解されるギミックが搭載されており、大きな子供も大満足の迫力です。


しかし、ベイブレードには一つ大きな問題点がありまして･･･
![bey.png](https://qiita-image-store.s3.amazonaws.com/0/326891/1572029d-32ff-190a-355b-12b7f5cdca22.png)
***え、高。***

そう、ベイブレードはそのカスタマイズ性の高さ故にパーツを揃えようとすると結構お金がかかるんです。
このままではベイブレードがバーストする前にお財布がバーストしてしまう可能性があります。それはよくない。
![50cf6975fd0488852bdc9e2c2bcd9cb0_t.jpeg](https://qiita-image-store.s3.amazonaws.com/0/326891/7e612acc-5108-4aa9-99c8-f93509c7f937.jpeg)
というわけで、ベイブレードのシミュレータを作ろうと思いました。

#開発環境
MATLAB R2018b / Simulink
Unity 2018,3.0f2 Personal
blender

#機能分析
で、思い立ったのはいいのですが、何を隠そう私は物理が大の苦手なんです。
私の実力ではベイブレードに働く全ての物理現象をモデリングし尽くすのは無理ですので考慮する物理現象を絞る必要があります。
今回は「歳差運動の角周波数」に注目しようと思います。ざっくり言うと「ベイがどのくらいの速さで揺れるか調べる」ということです。
![p_6_1.gif](https://qiita-image-store.s3.amazonaws.com/0/326891/e3d79fa5-f261-d571-749e-1c5c94251a3b.gif)画像は[ここ](http://misc.kankyo-u.ac.jp/~kokamoto/tango/p_6.html)からお借りしました。
というわけで機能として、
・ベイの各パラメータを指定すると、歳差運動の角周波数を算出してくれる。（MATLAB側の処理）
・結果の可視化を行う。（Unity側の処理）
MATLABとUnityはTCP/IPで接続しようとしました。（この書き方で察して下さい）

#モデリング
こちらを参考にモデリングを行いました。感謝。
[円錐の慣性モーメントについて](https://mathwords.net/entyukansei)
[コマの歳差運動について](http://www.asem.kyushu-u.ac.jp/qq/qq02/kikanbuturi/chap15.pdf)

#MATLAB/Simulinkで数式モデルを図式化
とりあえずこんな感じでモデルを作ってみました。
![0.png](https://qiita-image-store.s3.amazonaws.com/0/326891/1cbfd3ea-cc68-2358-2431-bc608af77bbb.png)

各定数として、私の相棒のリヴァイブフェニックス.10.Frのパラメータを代入します。
スクリプトはこんな感じです。スクリプト内でパラメータの定義してからモデルを呼び出して実行します。

  ```
　%parameters(revivePhoenix.10.Fr)
  centroid = 0.0254; %[m]
　rotationSpeed = 3000; %[rpm]
　mass = 0.053425; %[kg] (layer(revivePhoenix):23.725[g]+disk(10):23.1[g]+driver(Fr):6.6[g])
　gravitationalAcceleration = 9.8; %[N/kg]
　radius = 0.02775; %[m]
　open_system('beyblade');
　sim('beyblade');
  ```



#とりあえず動かしてみる
実行してみるとこんな感じです。
組み合わせはレイヤ－、ディスク、ドライバーの順でリヴァイブフェニックス.10.Frです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/326891/5bcc6f0a-7bc6-1a54-572c-8efa4267b5ae.png" width=50%>
![1.png](https://qiita-image-store.s3.amazonaws.com/0/326891/a1cc504c-1361-6b65-dec6-b160243a556a.png)
この結果を基準にしていきます。

#レイヤーの変更
レイヤーというのはベイの一番上に乗っかっているド派手なパーツのことです。
これを、少し小さなものに取り替えてみました。ここではガイストファブニルというレイヤーを想定しています。
組み合わせはレイヤ－、ディスク、ドライバーの順でガイストファブニル.10.Frです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/326891/fd3fc428-cb60-1cb0-c20e-c03f7ff2c98b.png" width=50%>
![2.png](https://qiita-image-store.s3.amazonaws.com/0/326891/121737ac-1930-bdc6-f00b-09e17c01b4cf.png)
あれ、さっきより揺れが速い･･･？
ここで勘違いしてはいけないのは、これはあくまで「揺れるのが速い」ということがわかっただけということです。
決して揺れが遅い方が強いというわけではありませんのでそこはご了承下さい。
とはいえ、揺れが速いというのはあまり気持ちのいいものではありません。

#ドライバーの変更
ドライバーというのはコマの軸の役割を果たすためのパーツです。
これを、少し背の低いものに取り替えてみました。ここではヤードというドライバーを想定しています。
組み合わせはレイヤ－、ディスク、ドライバーの順でガイストファブニル.10.Yrです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/326891/2c4777c9-b78d-71d3-64a5-b3cd6836a666.png" width=50%>
![3.png](https://qiita-image-store.s3.amazonaws.com/0/326891/61f6fffe-827c-06c9-13ca-63581a9c05ec.png)
ちょっと落ち着きました。

#回転数の変更
ここでは公式から発売されているシューターという器具を用いて回すことを想定していますが、私の大好きなYoutuberさんでベイを電動で回している方がいらっしゃいまして。
これもちょっと試してみます。シミュレータ上ならベイ本体や付近に置いてるものが壊れる心配はありませんし、怪我の心配も勿論ありません。シミュレータ万歳。
では、いざ実行。
![4.png](https://qiita-image-store.s3.amazonaws.com/0/326891/af91169f-1c39-bb26-7eba-7496d8af6c0a.png)
ﾏｼﾞﾊﾟﾈｪ･･･

#Unityでコマを回す
ベイの3Dモデルを配布してくれている方がいらっしゃいましたので遠慮なく使わせて頂きます。感謝（本日2回目）。[リンク](https://sketchfab.com/models/b861cab5630f448282f37e10a9796e99#download)
とりあえず、Unity上にダウンロードしたblenderファイルを読み込んで回してみました。

コードはこんな感じです。といっても追加したのはUpdate()内に一行。

  ```
　transform.Rotate(new Vector3(0, 0, 5));
  ```
![by695-7a26v.gif](https://qiita-image-store.s3.amazonaws.com/0/326891/98d85f77-c042-517d-d16d-fccd7c397450.gif)
なんか、シュール･･･

#MATLABとUnityをつなげたい
とりあえず、数値のやりとりをしてみました。

Unity側のコードはこんな感じです。（ほぼコピペですが）

  ```
　using System.Collections;
　using System.Collections.Generic;
　using UnityEngine;
　using System.Net;
　using System.Net.Sockets;
　using System.Linq;
　using System;
　using System.IO;
　using System.Text;

　public class paramRead : MonoBehaviour
　{
    　TcpListener listener;
    　String msg;

    　void Start()
    　{
        　listener = new TcpListener(55001);
        　listener.Start();
        　print("is listening");
    　}

    　void Update()
     {
         if (!listener.Pending())
         {
         }
         else
         {
             TcpClient client = listener.AcceptTcpClient();
             NetworkStream ns = client.GetStream();
             StreamReader reader = new StreamReader(ns);
             msg = reader.ReadToEnd();
             print(msg);
         }
     }
 }
  ```

MATLAB側コードはこんな感じです。（我ながら1行目の処理が酷い。いやマジで。）

  ```
　result = num2str(angularFrequency(1:1));
  tcpipClient = tcpip('127.0.0.1',55001,'NetworkRole','Client');
  set(tcpipClient,'Timeout',30);
  fopen(tcpipClient);
  fwrite(tcpipClient,result);
  fclose(tcpipClient);
  ```

[こちら](https://jp.mathworks.com/matlabcentral/answers/196774-connection-between-matlab-and-unity3d)を参考にしました。感謝（本日n回目）。

実行結果はこんな感じです。さっきの結果を文字列に変換して送っています。To Workspaceブロックは配列として出力しています。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/326891/57945f92-5153-f81f-c7c8-8db3f59f84ab.png)
うん。良好良好。

#ここで試合終了。
と、ここまで来たところで残念ながら試合終了です。敗因は期日でした。無念。
公開日前日から作業を始めるからこうなるんですよね。反省します。

#まとめ
いかがだったでしょうか。
お金を使わずカスタマイズが考えられたり、危険な回転数を試すことができたり、その恩恵を少しは感じられたかなと個人的には思っています。
ただ、課題も山積みで
・そもそも完成してない。
・無視した物理現象が多すぎて当てにならない。
ただ、後者についてはMATLAB使ってるのでそこらへん拡張性は高いかなと思ってます。
シミュレーション環境をUnityで構築して、MATLAB側からはパラメータを渡すだけの方がいい気がしないでもないです。

ベイブレードを遊んでいる方は是非試してみて下さい。