2016年3月30日水曜日

UnityとAVRのUSART シリアル通信とReadLineで問題

概要

Unityからシリアルポートの入出力を行います。入出力先はAVRマイコンです。AVRマイコンはUSART機能を使って通信を行います。

これまで

UnityのC#ではなく、通常のWindows .NetFrameworkでシリアル通信を実装してきました。(最新バージョンの.Netでも.Net2.0でも正しく動作しました。)

Unityでシリアル通信

Unity(mono)と.Netの違いはあるとはいえ、同じ実装でシリアル通信できるはずです。
ただし、使用するフレームワークを.Net2.0に指定しなければいけません。
ツールバのEditor→Project Settings→PlayerからApi Compability Levelを.Net2.0にします。
.Net2.0を選択

ReadLineで通信が途絶える問題

基本的には通常のC#の実装と同じです。しかし、次の問題に遭遇し、苦労しました。(おそらくUnityに付属するライブラリに問題があると思われます。)
  • SerialPort#ReadLine() で入力できない。
ReadLine()を実行する場所で止まってしまいます。また、タイムアウトするように設定したら、毎回タイムアウトしてしまいます。

1バイトずつ読みこむように変更

SerialPort#ReadLine()は文字通り、改行コードまでの1行を取得するメソッドです。
AVRマイコン側も
    uint16_t hallCount = 0;
・・・・
    printf("%d\n", hallCount);
とprintf()で改行コードを書き出しているため問題ないはずです。ところが、通信が止まってしまいます。
そこで、諦めて1バイトずつ入出力するようにしました。

  • AVR側は、16bit int型をビットシフトして1バイトずつ2回に分けて送信レジスタに登録する。
  • C#側はSeralPort#ReadByte()メソッドを使って2回に分けて取得する。
C#のコードは前回実装したコードと同じですが、次の変更を行いました。
var high = serialPort.ReadByte();
var low = serialPort.ReadByte();
var val = (high << 8) + low;
また、AVRのコードもこのように1バイトずつ書き込むように変更しました。

これで通信ができるようになりました。

まとめ

Unityでシリアル通信をするとき、SerialPort#ReadLine()では通信が止まってしまうため、ReadByte()で1バイトずつ通信することで問題を回避しました。

2016年3月22日火曜日

基本: AVRのUARTとC#でシリアル通信 (受信割り込み)

概要

今回は、AVRでUSART受信割り込みを使ったプログラムを作成したいと思います。

要件

  • AVRはPCから送信された値を受信し、受信割り込みを発生する。
  • 受信割り込み時に"echo back: <val> \n"という文字列をPCに送信できる。
  • <val>はPCから送信された値で置換する。

準備

ハードウェア

もちろんこれは必須ではなく、AVR168Pもしくはこれと互換性のあるAVRマイコンとFT232RLが実装されており、USBでパソコンと接続できればOKです。
AVRとFT232RLの取り付けの解説は割愛しますが、次のように実装します。
  • AVRのTXDとFT232RLのRXDが接続されている。
  • AVRのRXDとFT232RLのTXDが接続されている。
AVRとFT232RLを使ったUSB-シリアル通信の実験に解説されているとおりです。

ライブラリ

UARTを使った処理をかんたんに実装するために、次のコードを実装しました。初期化と読み書き、割り込み処理といった基本的なレジスタ操作をまとめたものです。

開発

AVR

dev3/usartsample/main.cを作成しました。割り込みなしのコードとほぼ同じですが、割り込み設定をしている箇所が異なります。
/**
* 受信割り込み発生時に実行される関数
* @param data 受信データ
*/
void rxEventListener(uint8_t data) {    
    printf("echo back: %d \n", data);
}

int main(void) {
    // 受信割り込みありでUSARTを初期化(8MHzクロック時, 9600ボーレート)
    USART_init(RX_COMPLETION_INTERRUPT, 51);

    // 受信割り込み発生時に実行する関数を登録
    USART_setRxCompletionInterruptListener(*rxEventListener);

    // printfで使用する関数を登録
    fdevopen(*USART_sendData, NULL);

    // 割り込み全許可
    sei();

    printf("Initialized>>\n");
    while (1) {
    }
}
  • UCSRnBレジスタのRXCIEn(7bit目)を1(true)にして、受信割り込み許可を行っています。(該当コード
  • 割り込みベクタUSART_RX_vectで受信割り込みを駆動できます。(該当コード)
  • 初期化しても、sei()を唱えないと割り込みは発生しません。
これで、PC側からの値に応じて、処理を変えたり、レスポンスする内容を変更したりできますね。

C#

前回同様にSerialSample/Program.csを使用します。

基本: AVRのUARTとC#でシリアル通信

概要

AVRとPC間でシリアル通信する方法を確認しました。AVRでは、UART機能を使用し、C#では.NetFrameworkのSerialPortクラスを使用して実現します。
ここでは、通信の基本であるエコーバックを実装し、通信のやり方を確認します。

準備

ハードウェアの準備

もちろんこれは必須ではなく、AVR168Pもしくはこれと互換性のあるAVRマイコンとFT232RLが実装されており、USBでパソコンと接続できればOKです。

AVRとFT232RLの取り付けの解説は割愛しますが、次のように実装します。
  • AVRのTXDとFT232RLのRXDが接続されている。
  • AVRのRXDとFT232RLのTXDが接続されている。
AVRとFT232RLを使ったUSB-シリアル通信の実験に解説されているとおりです。

ライブラリ

UARTを使った処理をかんたんに実装するために、次のコードを実装しました。初期化と読み書き、割り込み処理といった基本的なレジスタ操作をまとめたものです。

サンプル

単純なエコーバック

  • パソコンから文字をAVRに送信する。
  • AVRは受信した文字のアスキコードをエコーバックする。

AVR

これを実現するために、AVRで実装したコードはdev1/usartsample/main.cです。

int main(void) {
    # USARTを初期化します。割り込みなし,ボーレート9600
    USART_init(NOMAL, 51);

    # printfをするときはUSART_sendData関数を利用するように登録します。
    fdevopen(*USART_sendData, NULL);

    # printfしてみます。
    printf("Enter>>\n");

    # 以下、データを受信して、受信したデータを送信するエコーバックを繰り返します。
    while (1) {
        uint8_t val = USART_recieveData();
        printf("%d\n", val);
    }
}


  • USART_initの2つ目の引数51はボーレート設定値を示します。
    • クロック8MHz動作時で9600のボーレートです。
    • データシートに値の設定方法が解説してありますので、参照してください。
  • fdevopen()でUSART_sendData関数を登録することでprintfを使えるようにしています。

C#

VisualStudioからC#プロジェクトを作成します。今回はコンソールアプリケーションとします。
コンソールアプリケーションの作成(C#)

SerialPortクラスを使用して、Read&Writeを行うプログラムを実装します。( SerialSample/Program.cs )

            if (!serialPort.IsOpen) {
                serialPort.Open();
            }

            while (true) {
                string val = System.Console.ReadLine();               
                serialPort.Write(val.Substring(0, 1));
                var readVal = serialPort.ReadLine();
                System.Console.WriteLine(readVal);
            }

  • AVR側は複数バイトの受信に対応していないため、Substringメソッドで先頭文字だけ抽出しています。
  • SerialPort.Write()で書き込み、SerialPort.ReadLine()で\nまで読み込みます。

実行結果

次のように、入力した文字に対するアスキーコードを返しています。アスキーコード表で確認すると正しくレスポンスされていることがわかります。
実行結果

補足

今回、C#でシリアル通信の実装を確認するためC#で通信を実装しましたが、TeraTermを用いても等価のことを実行できます。

2016年3月13日日曜日

Unity5 ThetaSの画像をSkyboxで動的にアニメーション

ThetaSの映像を動的アニメーションで表示したい


360度カメラThetaSで撮影した映像をUnityに取り込んで表示したいです。

要件

要件は次の通りです。
  1. 1人称視点でカメラの向きに応じた角度の映像を見れる。
  2. プログラムから映像の再生をコントロールできる(再生、停止、x倍速再生など)。
1.の要件だけであれば「360度カメラthetaで撮った写真をunityで使う」のやり方で実現できます(このブログエントリでは、Blenderで円球を作っていますが、通常のSphereでもこだわりを捨てれば問題ないと思います。)。
しかし、1.のやり方では、通常の再生のみで2.の再生をコントロールすることができません。現状、MovieTextureでは動画の再生コントロールには対応していないからです。

方針

  • 動画の各フレームを静止画に変換し、静止画のテクスチャを高速で切り替えることでアニメーションを実現する。
  • Sphereマテリアルではなく、Skyboxにアニメーションを投影する。
現状、MovieTextureによる動画の再生コントロールができないようです。そこで、ThetaSで撮影した360度動画の各フレームを静止画として出力して、高速で切り替えることによって上記の要件を実現します。静止画を割り当てたテスクチャを切り替えはプログラムからコントロールするため再生速度などの変更も可能と言えるでしょう。

また今回は、ThetaSで撮影した素材をSkyboxに割り当てます。他のオブジェクトを設置しやすくなるからです。(夜空のThetaS映像をSkyboxで投影し、UFOなどのオブジェクトが動いていたら素敵です。)

早速 作ってみましょう。

Theta Sの360度動画を静止画群に変換

Adobe PremiereとAdobe Media Encoderで動画の各フレームをJPEG画像に変換しました。
ThetaS動画を出力した静止画群

画像群をUnityに取り込み

作成した360度画像群をUnityに取り込みます。一つのディレクトリにまとめてます。
画像群を取り込み

取り込み後はテクスチャをCubeMapとして設定します。すべての画像を選んで、[Unity4,Unity5]thetaとかで撮った全球画像をSkyboxに指定する方法を適応します。すべての画像がCubeMap Typeとして設定されるまでしばらく時間がかかるかと思います。
取り込んだ全ての画像をCubemapテクスチャにする

Skyboxの作成とMain Cameraへアタッチ

  1. 新規マテリアルを作成し、Shaderを「Skybox/Cubemap」にします。
  2. HierarchyでMain Cameraを選択し、InspectorのAdd Componentボタンを押します。
  3. Rendering→Skyboxを選び、Skyboxコンポーネントを追加します。
  4. Custom Skyboxに1.で作成したSkyboxをドラッグし、設定します。
Skyboxを作成し、Main Cameraに登録

スクリプトの作成

Skyboxの設定はUnityEditor上で設定することが多いと思います。しかし、C#(もしくはJavaScript)のコード上からSkyboxのテクスチャを設定します。再生コントロールをするためです。
次の手続きの実装を行います。
  • 作成したコードはMain Cameraにアタッチする。
  • Startメソッドで、AssetDatabaseを使ってテクスチャを読み込み、リストに追加する。
  • UpdateメソッドでSkyboxのテクスチャを適切に切り替える。
まずは次のコードを書いてみました。Updateでは毎回フレームを切り替えています。テクスチャが繰り返し一定速度で再生されるはずです。(フレームレートは入力元の映像とおなじになるとは限らないです。)


// SkyboxRunner.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class SkyboxRunner : MonoBehaviour {

    Skybox skybox;
    List skyboxTextures = new List();
    int index = 0;

    void Start () {
        // アタッチしたスカイボクスを取得
        skybox = GetComponent();

        // AssetDatabaseからテクスチャをロード
        var guids = AssetDatabase.FindAssets("t:Texture", new string[] { "Assets/SkyboxTex/nightsky1" });
        foreach (string guid in guids) {
            Texture texture = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid));
            skyboxTextures.Add(texture);
        }
    }
 
    void Update () {        
        // スカイボックスのテクスチャを切り替える
        skybox.material.SetTexture("_Tex", skyboxTextures[index]);
        index++;
        if (index >= skyboxTextures.Count) {
            index = 0;
        }
    }

}

ここでは、一定速度での繰り返し再生であるため、通常の再生と変わりません。Updateメソッドの内容を変更すれば、フレーム更新を自在に操ることができます。


まとめ

Theta Sで撮影した映像をUnityの3D空間に投影し、再生コントロールもしたかったのですが、MovieTextureなどでは実現できなかったため、各フレームを静止画に書き出してアニメーションすることで実現しました。
(もっといいほうがあればいいのですが、、、)