🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

3回答

6014閲覧

シリアル通信で受信した情報(ポテンショメータなどのByte値)をUnityの操作に使いたい。うt

HiroPokeHero

総合スコア45

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/09/22 08:00

編集2019/09/22 08:19

実現したいこと

シリアル通信で受信した情報(ポテンショメータなどのByte値)をUnityの操作に使いたい。
UnityのStandardAssetsのVehicleをつかったシミュレーションをしたいと考えており、
その入力を自作のArduinoコントローラからシリアルで入力し、
ハンドル操作、アクセル、ブレーキ操作ができないかと考えています。

しかし、現時点では再生モードにて車両は全く動かず、
ハンドル(実際にはタイヤ?)も全く動きません。
そして、Unityそのものが非常に重い状態になります。

シリアル通信を入力とすること自体がUnityの処理によくないのでしょうか?
もしくはシリアルの通信速度の問題(今は9600bps)でしょうか?

前提

入力装置として、複数のポテンショメータと接続したArduinoを制作し、
USBポートを使ってシリアル通信をしています。

また、課題をステップバイステップで解決するために、
事前にVehicleではなく、簡易的なブロックを回転させたり、移動させたりする
トライアルをしましたが、そのときは入力装置の入力がUnity上の3Dのモデルに
動きとして反映されていました。

発生している問題・エラーメッセージ

エラーはありません。

該当のソースコード

こちらはUnityのStandardAssetsのCarUserControlというスクリプトを編集した時のものです。

using System; using UnityEngine; using UnityStandardAssets.CrossPlatformInput; using System.Collections; using System.Collections.Generic; using System.IO.Ports; using System.Threading; namespace UnityStandardAssets.Vehicles.Car { [RequireComponent(typeof (CarController))] public class CarUserControl : MonoBehaviour { private CarController m_Car; // the car controller we want to use SerialPort SerPort = new SerialPort("COM3", 9600); string data; int ang1, ang2, ang3; float ang1f, ang2f, ang3f; private void Awake() { // get the car controller m_Car = GetComponent<CarController>(); if (SerPort.IsOpen) { SerPort.Close(); } else { SerPort.Open(); SerPort.ReadTimeout = 1000; } } private void FixedUpdate() { // pass the input to the car! float h = CrossPlatformInputManager.GetAxis("Horizontal"); float v = CrossPlatformInputManager.GetAxis("Vertical"); data = SerPort.ReadLine(); var datas = data.Split(','); var rb = GetComponent<Rigidbody>(); ang1 = Convert.ToInt32(datas[0], 16); ang2 = Convert.ToInt32(datas[1], 16); ang3 = Convert.ToInt32(datas[2], 16); ang1f = (float)(512 - ang1) / 512; // 1/300[deg] ang2f = (float)(512 - ang2) / 512; // [%] ang3f = (float)(512 - ang3) / 512; // [%] h = (float)(512 - ang1) / 512; v = (float)(512 - ang3) / 512; //Debug.LogFormat("%2.2f [deg], %3.2f [%], %3.2f [%]", ang1f, ang2f, ang3f); Debug.Log(h); Debug.Log(v); #if !MOBILE_INPUT float handbrake = CrossPlatformInputManager.GetAxis("Jump"); m_Car.Move(h, v, v, handbrake); #else m_Car.Move(h, v, v, 0f); #endif } } }

次にArudino側のスクリプトも添付します。

#define HDL A3 #define X A6 #define Y A7 char msgString[128]; void setup() { // put your setup code here, to run once: Serial.begin(9600); Serial.println("Start Box"); } void loop() { // put your main code here, to run repeatedly: int Ang1Raw, Ang2Raw, Ang3Raw; float Ang1, Ang2, Ang3; Ang1Raw = analogRead(HDL); Ang2Raw = analogRead(X); Ang3Raw = analogRead(Y); Ang1 = Ang1Raw * 5.0 / 1024; Ang2 = Ang2Raw * 5.0 / 1024; Ang3 = Ang3Raw * 5.0 / 1024; //Serial.print(Ang1); //Serial.print("[V]\t"); //Serial.print(Ang2); //Serial.print("[V]\t"); //Serial.print(Ang3); //Serial.println("[V]"); sprintf(msgString, "%x,%x,%x",Ang1Raw,Ang2Raw,Ang3Raw); Serial.println(msgString); delay(1000); }

試したこと

関連するByte値をConsole画面に表示しようとしましたが、
処理が重くなってLogが見れませんでした。
(正確に言うと見れてはいるが、Unityの画面の動きが遅く、リアルタイムに見ることができませんでした。)

補足情報(FW/ツールのバージョンなど)

Windows10
Unity5.5.4p4
visualStudio2015Community

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2019/09/22 08:54

fixedupdate()ではなく update()だとどうなります?
HiroPokeHero

2019/09/22 09:10

かなり良くなりました。 ありがとうございます。 ただ、まだタイムラグがあるようです。 以前と比べるとタイムラグのレベルが雲泥の差なのでまずは非常に助かりました。 そこで、もう少し進みたいのですが、 やはりUpDateにして ・「受信があれば次に」とうまくい ・「受信ごとに入力を反映させる」だと追いつかない くということはやはり通信速度を上げるべきでしょうか?
guest

回答3

0

Unityの構造をよく知らないのですが、そのFixedUpdate()あるいはUpdate()というのは関数中に長時間滞在しても全体の動作に影響はないのですか?

SerialPort.ReadLine()は、改行コードを受信するまであるいはタイムアウトまで戻って来ません。データの送信間隔が1秒、タイムアウトも1秒に設定されていますから、基本的にその関数は入ってから出るまで1秒かかることが予想されます。

C#

1 int ang1 = 512, ang2, ang3; //初期値を与えておかないと多分怒られる 2 <> 3 private void FixedUpdate() 4 { 5 <> 6 // data = SerPort.ReadLine(); //コメントアウト 7 // var datas = data.Split(','); 8 var rb = GetComponent<Rigidbody>(); 9 // ang1 = Convert.ToInt32(datas[0], 16); 10 // ang2 = Convert.ToInt32(datas[1], 16); 11 // ang3 = Convert.ToInt32(datas[2], 16); 12 ang1 = (ang1+1)%1024; //0~1023で順次変化 13 ang2 = ang1; 14 ang3 = ang1; 15

などとしたとき(つまり待ち時間をほぼ無くしたとき)、動きはどうなるでしょう。

これでスムーズに動くようなら、データ受信でブロックしないように、SerialPort.BytesToReadなどで受信データの有無を確認して自分でバッファを管理して行データを切り出すとか、受信はシリアルポートの受信イベントに任せる、などとすることが対処法となるかと思いますが。

投稿2019/09/22 09:55

thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

HiroPokeHero

2019/09/23 04:19

まずは、シリアル通信のタイミングだけではなく、 Arduino側の処理時間も考える必要がありそうですね。 また、受信側も受信データの取り扱いやタイムアウトの条件を見直す必要がありますね。 シリアル通信の速度においてどれくらいが最適かわかっていませんが、 Arduino側のloopを5ms間隔にしてもシリアル通信速度は9600でも問題ないでしょうか? もしくはシリアル通信ももう少し早くするべきでしょうか? ちなみに、USBマウスやUSBコントローラはどれくらいの速度なんでしょうか?
thkana

2019/09/23 09:24 編集

シリアル通信は、ありがちなデータ8bitパリティ無しスタート/ストップ1bit(いわゆる8N1)とすると、1バイトのデータ送信に10bitが必要です。 なので、簡易的には、9600bps(ビット/秒)であるなら、1バイトの送信にざっくり1msかかると考えられます。 送信のデータ形式が"a,b,c"(a,b,cは1~4桁)ですから、最小5バイト+改行コード1バイト(だったっけ?Arduinoでは。もしかしたら2バイトかも)、少なくとも6ないし7バイト多ければ15ないし16バイト。これを送るのに少なくとも6ms多くて16msかかるわけで、5ms毎に発生するデータを9600bpsでは送りきれない、ということになります。5msに収めるためには少なくとも38400bps以上、というところでしょうか。 動作上は、データを送りきるまで送信の関数から戻らなくなるので、loopの周期が長くなる、ということになります。 USBマウスとかは仮想COM(CDC)とは違うプロトコル(HID)で、通信自体はもっと早かったと思いますが扱ったことがないので... ざっとぐぐって http://userweb.alles.or.jp/chunichidenko/picusb30.html をみると64Kバイト/秒、512Kbpsということになります。
guest

0

ベストアンサー

readline で読もうとしてますが、
これだと改行を読むまで処理が止まってしまいます

今受信しているものを読み込んでバッファにためて
改行があったら処理をする、なかったらなにもしないで次のupdate()に。というようにしましょう

投稿2019/09/22 09:37

編集2019/09/22 09:37
ozwk

総合スコア13551

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

まず、シリアル通信はバイナリ(バイト値)で送るんではなくて、テキストに変換して1行ごとの通信としましょう。
テキストにすれば、ArduinoIDEやターミナルソフト(Teratermなど)で通信のモニタができます。

Arduino側で計測値をテキスト+改行コードをつけて送信するのをきちんと確認しましょう。

シリアル通信を入力とすること自体がUnityの処理によくないのでしょうか?

もしくはシリアルの通信速度の問題(今は9600bps)でしょうか?

ちがいます
シリアル通信周りのコードの組み方がまずいせいです

投稿2019/09/22 08:11

y_waiwai

総合スコア88040

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

HiroPokeHero

2019/09/22 08:17

詳しく教えていただけないでしょうか? シリアル通信周りのコードの組み方がまずい場合、 Unity側でデータが読めなかったり、受信できないような気がします。 また、Arduino側はテキストで送っていると思います。 テキストでByte値を送信しているつもりです。 ※Arudino側のスクリプトも本文に乗せます。
y_waiwai

2019/09/22 08:21

実際にPC側で、Teratermなどのターミナルソフトでシリアル通信を受信できるのを確認しましょう。 思います、つもりです、ではなにもできませんぜ。
HiroPokeHero

2019/09/22 08:42

通信はできています。 確認済みです。 「前提」として本文に書いておりますが、 同じ入力装置を使い、Unity側をVehicleではなく、 もっと簡易なオブジェクトにして、入力装置の値をUnity側反映するトライアルを実施したところ、 狙いの動きをしました。 具体的にはポテンショメータの回転角度を物体の角度にするのですが、 ポテンショメータを90deg回転させると物体も90deg回転しました。 また、このやり取りの間に気づきましたが、 Unity側に入力装置の操作が渡され、Vehicleの動作に反映されているようです。 ただ、問題は解決しておらず、ポテンショメータを回転させた直後にハンドルは回転せず、 かなり(数十秒とか)時間がたってからハンドルが動きます。 なので、Unity上で車が動いてもどの時の操作が効いているのか時間差がありすぎてわかりませんが。。。
y_waiwai

2019/09/22 08:45

通信できている、とは書かれてませんぜ。 できているのなら、シリアル受信でどういう文字列が得られて、どういうふうに分解されて、それぞれどういう数値が得られてるのかを追いかけていきましょう。 それがデバッグというものです。
HiroPokeHero

2019/09/22 08:52

本文を書きなおします。 説明不足でした。 物体の動きに反映できているので、通信は当然できていることを想像できると思いましたが、 こちらの都合でしたね。 申し訳ありません。
y_waiwai

2019/09/22 09:19

いきなりオブジェクトを動かそうとするんじゃなく、それぞれ取得した数値をコンソールなりどこかに表示するようにして、意図した通りの数値を意図したタイミングで実際に受信されてるのかを見るようにしましょう
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問