【研究室セミナー】超スマート社会をつくるVRxIoTセミナ「実世界を仮想化するフィジカル・コンピューティング入門」【2018・2019】
※本ページはこのページをScrapboxに移行した内容です。 1. はじめに
このページは,コミュニケーションロボティクス研究室および神奈川工科大学ホームエレクトロニクス開発学科での研究室チュートリアル用のページです。
「現実世界のヴァーチャル空間への取り込み方」と銘打ってありますが,内容の実質はUnityとマイコンの連携方法です。ロボット,IoT,フィジカルコンピューティング,VR,筋トレ,関節技など,名前は違えど機器がリンクする場面では大方のことはシリアル通信で解決できます。
このページ内容をクリアした暁には
すべての局面をシリアル通信で解決する
くらいの気持ちで開発に取り組んでください(できるとは言ってない)。
2. 講義概要
マイコンに接続されたセンサ情報をシリアル通信でUnityに取り込めるようになることを目指します。かんたんな例として,Arduinoに接続されたスイッチを押して弾丸を打つシューティングをつくります。
手順としては次の2ステップです。
①UniRxを使ってArduino-Unity(PC)間で非同期シリアル通信する
②Unityの既存のプロジェクトにシリアル通信を組み込む
これらができるようになれば,自分が製作したUnityプロジェクトとマイコンを連携させ,外部のセンサ情報を処理できるようになります。
3. シューティングゲームのイメージ動画
ボタンスイッチでの入力
https://scrapbox.io/files/600fb4148aa3a200364a82f2.mp4
フォトリフレクタでの入力
https://scrapbox.io/files/600fb41d01a2d80022ab6f16.mp4
4. 参考資料
エラーがありC#のファイル名と中のクラス名を一致させる
使用サンプル: SampleShooting_Step2.zip (上のページ内からダウンロードできます)
5. 講義内容
5.1. UniRxを使ったArduino-Unity(PC)間での非同期シリアル通信
今回はUniRxを使ってシリアル通信を実装します(色々ためした結果,この方法が事故が少なかったです)。UniRxはUnity向け非同期処理向けライブラリです。
※最新(2019.5月現在)のUnityでは下記を注意してください。
https://gyazo.com/c9b2be3efd9e3da5418b599db5a8f9ff
5.2. Unityプロジェクトへのシリアル通信の組み込み
既存のUnityプロジェクトに先ほどのシリアル通信を組み込んでみましょう。
【Step 1】 サンプルをダウンロードし,プロジェクトを開く
キーボードの←,→キーとSpaceキーで動くシューティングゲームが動かせるはずです。
【Step 2】 設定準備:UniRXのインポート,Api Compatibility Levelを .NET 2.0 Subset -> .NET 4.x へ変更
「4.1. UniRxを使ってArduino-Unity(PC)間での非同期シリアル通信」を参照してください。
【Step 3】 下記のシリアル通信スクリプト:SerialController.cs,Judge.csをダウンロードしてAssetsに追加
code:SerialController.cs
// シリアル通信+読み込んだString値をflag(Bool値)に変換
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System;
using System.IO.Ports;
using UnityEngine;
using UniRx;
public class SerialController : MonoBehaviour
{
public bool Action; // Arduinoからの送信値引き渡し用変数
// シリアル通信用変数
public string portName;
public int baurate;
SerialPort serial;
bool isLoop = true;
void Start()
{
this.serial = new SerialPort(portName, baurate, Parity.None, 8, StopBits.One);
try
{
this.serial.Open();
Scheduler.ThreadPool.Schedule(() => ReadData()).AddTo(this);
}
catch (Exception e)
{
Debug.Log("can not open serial port");
}
}
public void ReadData()
{
while (this.isLoop)
{
//シリアル通信読み込み
string message = this.serial.ReadLine();
// String値をブール値に変換
// ※ Convert.ToBooleanは0,1の読み込み不可…
if (message == "1") Action = true;
else Action = false;
// デバッグ用
//Debug.Log(message);
}
}
void OnDestroy()
{
this.isLoop = false;
this.serial.Close();
}
}
code:Judge.cs
// flagに応じて弾丸を発射
using UnityEngine;
using System.Collections;
public class Judge : MonoBehaviour
{
public SerialController SerialController; //シリアル通信用
public bool Action; //センサ入力用
//弾丸発射用Prefab
public GameObject bulletPrefab;
//受信した値(SerialController.Action)に対する処理
void Update()
{
// デバッグ用
// Debug.Log(SerialController.Action);
// 以下にSerialController.Actionの値に応じた処理を書く
if (SerialController.Action == true)
{
//ここではシューティングゲームで弾を発射
Instantiate(bulletPrefab, transform.position, Quaternion.identity);
}
}
}
機器間でシリアル通信をする場合は変数の形に注意する必要があります。(以下はあまり読む必要はありません。いつかキレイなコードにします…。)
Arduinoでシリアル通信をする際の注意点ですが,Serial.print()やSerial.println()などでシリアル通信をすると数値(int型,float型,bool型など)ではなく,文字列(String型)として送信されてしまうためセンサ情報を数値として処理するさいに不都合が生じます。Serial.write()を使えば1バイトデータとし送信できるので,そちらを用いるべきです。
しかしながら,今回はArduino側のサンプルスケッチでSerial.println()を用いて文字列として送信し,SerialController.cs内でString値を読み取って判断しflag(bool値)に変換しています(型変換しているとは言っていない)。このようにして一見不要に見える手間をかけることでソースに深みが増し,同じ出力結果でもコクが生まれ……ウソです。マネしないでください(なんでこんなク◯みたいなコード書いたんだろう……。忘れてしまった……。write()じゃダメだったんかな……)。なお,Convert.ToBooleanは0,1の読み込み不可のためこんなことになってますが,floatに変換したいときはConvert.ToSingle()は普通に使えます。必要な際はご質問ください。
【Step 4】 SerialController.csをGameObjectにアタッチしてシリアルポートを設定
https://gyazo.com/7fee6044a1e99615819a013e3dbfa6bb
【Step 5】 Judge.cs rocketにアタッチしてSerial Controller,GameObjectを設定
https://gyazo.com/9330b28e4727910e7aad9976d7e2ae7d
【Step 6】Arduinoと接続し実行
Arduinoのスケッチは下記のものを使用してください。PIN 2をデジタル入力に設定し,High(5V)の時に1,Low(0V)の時に0を出力します。スイッチを押したときに1を出力するにはプルダウン回路をつくる必要があります。 Arduino側スケッチ: スイッチ判定用 シリアル通信
code: Arduino_serial_shooting_test01.ino
bool flag = 0;
void setup() {
pinMode(2,INPUT); //スイッチに接続ピンをデジタル入力に設定
pinMode(13,OUTPUT); //LEDに接続ピンをデジタル出力に設定
Serial.begin(9600); //シリアル通信設定
}
void loop() {
if (digitalRead(2) == HIGH) { //① スイッチのONのとき
digitalWrite(13,HIGH) ; //LEDを点灯
delay(100); // 100msec 待つ
flag = 1; // flagを1に設定
Serial.println(flag); // シリアル通信: 1 を送信
flag = 0; // flagを0に戻す
} else { //② スイッチのOFFのとき
digitalWrite(13,LOW) ; //LEDを消灯
delay(100); // 100msec 待つ
Serial.println(flag); //シリアル通信: 0 を送信
}
}
以上で終了です。
6. おわりに
いかがだったでしょうか?
今回の内容を応用しスイッチをセンサに変えればセンサ情報をUnityに取り込むことができます。HTC VIVEなどの専用デバイスがなくても同等,もしくはそれ以上の開発ができるようになっているはずです。今回は弾丸を発射するだけでしたが,色を変える,景色を変える,なんでも好きな使い方ができます。
また仮想COMポートとして扱われるBluetooth機器も同様に扱えます(OS側のドライバが処理してくれているので,プログラミングの際は機器のCOMポート番号を指定すればOKです)。
本内容を活用して,是非すべての局面をシリアル通信で解決してください。
山崎研究室セミナ Tips