「開発」カテゴリーアーカイブ

VRコンテンツ作成入門

Quest2でのVRコンテンツ作成に関する概略
この記事は、Unityで非VRのコンテンツをある程度作成できる程度のスキルを前提にしています。

Quest2を使ってVRコンテンツを動作させる方法は2つあります。1)PCとUSBケーブルで接続して、PCのGPUを使って映像を描画する方式と、2)Quest2本体でアンドロイドアプリとして実行する方法です。それぞれ長所と短所がありますが、まずは下の動画を視聴し、1)の方法から始めるのが一般的です。

PCを使ったVRコンテンツ作成
 OculusQuest2をPCと接続して、Unityを使った簡単なVRアプリケーションを作ってみましょう。下記の動画コンテンツは、Unity2021.1.15OculusIntegrationを使いますので、事前にインストールを済ませておくとよいでしょう。また、Quest2とPCを接続する(Oculus Link)ためにUSB3ケーブルが必要です。PCは、OculusLinkに対応したGPUを搭載している必要があります。

Quest2本体を使ったVRコンテンツ作成
 OculusQuest2本体でVRコンテンツを作成するためには、いろいろ難しい側面もありますが、PCレス、ケーブルレスでVRコンテンツを実行できるアドバンテージがあります。下記動画を見ながらコンテンツを作成する際、Android build supportの使用方法や、Quest2を開発者モードにする方法などを参照すると良いでしょう。Androidアプリの動作画面を他の人に見せるためには、OculusDevelopperHUBもインストールしておくとよいでしょう。

Quest2でVRホラーを作る
 下記動画では、簡単なホラーを作成してみます。ホラーにつきもののゾンビや、怖いBGMも追加してみます。背景は、Hospital Horror Packを使います。ゾンビとアニメーションはMixamoから入手します。背景音はフリー音声素材サイトなどから、好きなものをチョイスして追加してみてください。

ハンドルコントローラを使ったコンテンツを作成する
 Oculusuには大変良くできたハンドコントローラーであるOculusTouchがついています。つかむ/はなす、指差すなどの「手を用いた動作」が、他のゲーム機などと大きく異る部分です。下記動画では、Auto Hand – VR Physics InteractionJapanese Apartmentというアセットを用い、ハンドルコントローラを用いたコンテンツの作成方法を解説します。
 AutoHandアセット内の、OculusVRというパッケージをインポートしないと、Oculus用のデモシーンがインストールされないのでご注意ください。また、TPM Importerというダイアログが出たら、importボタンを押すようにしてください。これをインポートしないと、動作が不安定になるようです。

Unityと扇風機の連動

前からアイデアとしては存在していた、Unityと扇風機の連動をとうとう実現。自分の操作に対するフィードバックがマルチモーダルに返ってきたとき、われわれはそれを現実であると認識しやすい、という知見に基づき、触覚フィードバック(つまり風)の存在意義を再確認したわけである。

風量の信号は、
Unity→(http)→Webサーバー→(http)→M5StickC→(PWM)→扇風機
の順で送られる。なぜhttpなのか。俊敏な信号を送っても、どのみちプロペラが回転し始めるのには時間がかかるし、なんといっても一番簡単だからである。

using UnityEngine;
using System.Collections;
 using UnityEngine.Networking;

public class HTTP_GET : MonoBehaviour {

    int power = 20000;

	// Use this for initialization
	void Start () {
        //StartCoroutine(GetText());
    }
	
	// Update is called once per frame
	void Update () {
        if (Input.GetKeyDown(KeyCode.Alpha0))
        {
            power = 0; StartCoroutine(GetText());
        }
        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            power = 10000; StartCoroutine(GetText());
        }
        if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            power = 15000; StartCoroutine(GetText());
        }
        if (Input.GetKeyDown(KeyCode.Alpha3))
        {
            power = 20000; StartCoroutine(GetText());
        }
        if (Input.GetKeyDown(KeyCode.Alpha4))
        {
            power = 25000; StartCoroutine(GetText());
        }
    }

    public void HTTPGET() {
        //GetText();
        
    }


    IEnumerator GetText()
    {
        string url = "";
        url = "http://kodamalab.sakura.ne.jp/FAN/control.php" + "?power="+power.ToString() ;
        Debug.Log("request:"+url);

        UnityWebRequest request = UnityWebRequest.Get(url);
        // 下記でも可
        // UnityWebRequest request = new UnityWebRequest("http://example.com");
        // methodプロパティにメソッドを渡すことで任意のメソッドを利用できるようになった
        // request.method = UnityWebRequest.kHttpVerbGET;

        // リクエスト送信
        yield return request.Send();

        // 通信エラーチェック
        if (request.isNetworkError)
        {
            Debug.Log(request.error);
        }
        else
        {
            if (request.responseCode == 200)
            {
                // UTF8文字列として取得する
                string text = request.downloadHandler.text;
                Debug.Log("payload:"+text);

                // バイナリデータとして取得する
                byte[] results = request.downloadHandler.data;
            }
        }
    }
}

#include <M5StickC.h>  // ----A
#define SERIAL_BAUDRATE     115200

#define LEDC_CHANNEL_0 0 //channel max 15
#define LEDC_TIMER_BIT 16 //max 16bit
//16bitの場合、最大周波数1220Hz
#define LEDC_BASE_FREQ 1220.0 //double 1220.0Hz以下
#define GPIO_PIN 26 //GPIO #36~#39 は設定不可

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>

#define USE_SERIAL Serial

WiFiMulti wifiMulti;
int cnt=0;
String payload;

void setup() {

    USE_SERIAL.begin(115200);

    USE_SERIAL.println();
    USE_SERIAL.println();
    USE_SERIAL.println();

    for(uint8_t t = 4; t > 0; t--) {
        USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
        USE_SERIAL.flush();
        delay(1000);
    }

    wifiMulti.addAP("HELL", "kodamamasahisa");

  //////////////
  ledcSetup(LEDC_CHANNEL_0, LEDC_BASE_FREQ, LEDC_TIMER_BIT);
  ledcAttachPin(GPIO_PIN, LEDC_CHANNEL_0);
  //16bit の場合 duty 0x0000~0xFFFF (max:65535)
  //duty 50%: 0x8000 (=32768)
  ledcWrite(LEDC_CHANNEL_0, 0x8000);

}

void loop() {
    // wait for WiFi connection
    if((wifiMulti.run() == WL_CONNECTED)) {

        HTTPClient http;

        USE_SERIAL.print("[HTTP] begin...\n");
        // configure traged server and url
        //http.begin("https://www.howsmyssl.com/a/check", ca); //HTTPS
        http.begin("http://kodamalab.sakura.ne.jp/FAN/control.php"); //HTTP

        USE_SERIAL.print("[HTTP] GET...\n");
        // start connection and send HTTP header
        int httpCode = http.GET();

        // httpCode will be negative on error
        if(httpCode > 0) {
            // HTTP header has been send and Server response header has been handled
            USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode);

            // file found at server
            if(httpCode == HTTP_CODE_OK) {
                payload = http.getString();
                USE_SERIAL.println(payload);
            }
        } else {
            USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
        }

        http.end();
    }

    //delay(5000);

    int power;
    power=payload.toInt();
    ledcWrite(LEDC_CHANNEL_0, power);
    Serial.printf("PWM:%d \n", power);
    delay(2000);      

    cnt++;
}

液晶画面に風速を表示

FitBit/Python210622

今日は、自分の心拍数25日分(5/28~6/21)で分析をしてみる。まずは、授業日と非授業日の比較から。

就寝中は案外差がないように見受けられる。活動時間帯は、やはり全般的に授業日が高いように見える。特に、9時付近、14~16時、19~21時あたりで授業日vs非授業日の差がついているように見える。14~16時は授業だろう。月曜日は専門演習、金曜日は生理心理学実験がある。9時付近は授業準備だろうか?間に合わなくて朝やることもある。19~21時はタイ料理のせいだろうか。とりあえず授業中頑張っている事がわかったので良かった。次は、土曜日,日曜日,全体平均の比較だ。

土曜日の就寝中心拍数が低い。一方で日曜日の深夜は全体平均より高い。日曜日に大学でまとめて仕事をするせいだろうか・・・。日曜部の8~10時が低いのは、自動車に乗っていないからだと思う(残りの日は全部車を運転している・・・)。土曜日は全体的に平均より低いように見えるが、なんとも言えない。

思えば自分は「土日しっかり休む」タイプの生活ではなく、休みが広く浅く分散しているような生活スタイルだ。平日と土日のコントラストがハッキリしているような生活の人だと、より明確に差が現れるのかもしれない。

import sys
import fitbit
import gather_keys_oauth2 as Oauth2
from datetime import datetime, date, timedelta
 
print('Hello FitbitAPP4') 
 
USER_ID     = ""
CLIENT_SECRET = ""
 
def requestFitbit(DATE):
    rval=""
    global auth2_client

    fitbit_stats = auth2_client.intraday_time_series('activities/heart', DATE, detail_level='1min')
    HRstats = fitbit_stats['activities-heart-intraday']['dataset']
   
    OUTPUT_FILE = "ALL.csv"
    csv_file = open(OUTPUT_FILE, 'a')
    csv_file.write(DATE+",")
    for var in range(0, len(HRstats)):
        csv_file.write(str(HRstats[var]['value']))
        csv_file.write(",")

    csv_file.write("\n")
    csv_file.close()

    return rval
##################################################

"""Get tokens"""
server = Oauth2.OAuth2Server(USER_ID, CLIENT_SECRET)
server.browser_authorize()
ACCESS_TOKEN = str(server.fitbit.client.session.token['access_token'])
REFRESH_TOKEN = str(server.fitbit.client.session.token['refresh_token'])
 
#print(ACCESS_TOKEN)  
#print(REFRESH_TOKEN)

"""Authorization"""
auth2_client = fitbit.Fitbit(USER_ID, CLIENT_SECRET, oauth2=True, access_token=ACCESS_TOKEN, refresh_token=REFRESH_TOKEN)
 
"""request"""
#DATE = "2021-06-19"
#today = datetime.today()
#requestFitbit(datetime.strftime(today, '%Y-%m-%d'))
#requestFitbit("2021-06-19")

today = datetime.today()
for var in range(0, 26):
    #day=today + timedelta(days=1)
    #print(var+":" + datetime.strftime(yesterday, '%Y-%m-%d'))
    stamp=datetime.strftime(today - timedelta(days=var), '%Y-%m-%d')
    print("target: " + stamp)
    requestFitbit(stamp)

24*60=1440サンプルあるはずが、日によって少し足りなかったりする。サンプル出来ていない時間帯がツメられてしまっているものと思う。正しく計算するには、タイムスタンプも反映させながらワークシートを作成する必要がありそうだ・・・。
 自宅でFitbitAPIにアクセスしようとしてハマった。途中で心が折れそうになるが、どうにか原因を特定、プログラムの改造にこぎつけた。注意点は下記の3つ。

1.自宅および主要ノートPCはポート8080を何かで使っているらしい。Application登録時のRedirect URLを8088に変更したら動作するようになった。これどういう意味があるんだろう?

2.Tokenは発行されるのにHRなどの計測値が落ちてこない・・・これは、ApplicationのOAuth 2.0 Application Type をPersonalにし忘れていたことが原因。

3.途中で「fitbit.exceptions.HTTPTooManyRequests: Too Many Requests」なるエラーが出て、一切リクエストができなくなる。リクエスト回数に制限があるらしい。これはアプリケーションを新たに登録してIDとSECRETを取得することで回避できた。

美術館実験210620

栗山さんと大野田くんのために、美術館スタイルの実験刺激作成がスタートする。まずは栗山さんバージョンを意識し、作り込んで見る。

・ライトマップをベイクするのに手こずる。
・画像はすべてPrefabをUnwrapする。でないと一部だけグレーになる。
・壁のマテリアルの明るさを変える。
・Lightning設定からAmbientLightの設定を暗めにする。
・PointLight/SpotLightの明るさを変える。

などを繰り返し調整し、ベイクしまくった結果が以下のものである。今日はこのあたりで許してもらおう。どの刺激の側にいて、どこを見ていたのか、が記録できるようにしないといけないんだけど。。。

美術館形式実験210620

自律神経系指標の多人数測定環境

ウェアラブルでWifi接続式の計測器(下記は皮膚コンダクタンスと心拍数が同時に測定できるタイプ)を最大50個ほど生産し、ゲームや映画、お笑いなどのコンテンツを、多人数同時測定する仕組みを構築しています。

下図は、お笑い動画(ミルクボーイの湿布クールポコ)を視聴した際の心拍数、皮膚コンダクタンス、主観感情(PA:ポジティブ感情、NA:ネガティブ感情、CA:リラックス感情)を19名同時測定したものです。動画視聴中は心拍数の低下、皮膚コンダクタンスの上昇が認められます。主観感情変化が大きいほど、生体情報の変化も大きい事がうかがえます。


下記は、ミッションインポッシブルゴースト・プロトコル(主人公が高層ビルを登り、降りてくる最中転落死しそうになるシーン)を視聴中の皮膚温を20名同時測定したものです。下グラフ中の青色部分が動画視聴部分で、交感神経活動亢進による急激な皮膚温低下が見られます。皮膚温変化のトレンドは、動画内容に対応しているように見受けられます。

M5StickでSC その1

M5StickCで教師の皮膚コンダクタンスを測るべく、準備をすすめている。計測回路はブレッドボード型基板に載せたいとのことで、まずは計測回路をブレッドボードに構築した。ウェアラブルにするとの事で、頑張って左側に寄せてみる。LT1167から出た出力は、LMC6482は用いず、直接ADS1015に入力し、差動入力かつx16モードでAD変換してみることにする。回路図中の720 Ω は間違いであり、実際は750 Ω を、同じく500 Ω は510Ωを用いている。

急いでウェアラブル化して、Bluetooth経由で計測してみると下記のようになった。 ブレッドボードとM5Stickは、付属のケースにちょうど入るサイズ。 計測結果は、ややノイズが目立つが許容範囲と思われる。 ワイヤーをまとめて強引に蓋を閉じると、計測波形にノイズが出た。線をきれいまとめると、計測波形上も都合が良いかもしれない。

実際はここに皮膚温センサーとバッテリーを追加することになる。1400mAhのバッテリーを追加すると125gだが、ブレッドボードがブレッドボード型基板に置き換わることで-30gなので、およそ90g程度と予想され、十分ウェアラブルな予感である。

あとやらねばならないことは・・・

・使用部品リストを作成する。
・ブレッドボード型基板に実際に載せて動作をみる。
・電源関係をどのようにするか考える。
・装着時に便利な補助パーツを3Dプリンタで作成する。

などであろうか。ああそうだ、ブレッドボード用の単芯ワイヤー線が必要であったので調達するよう伝えなければ。。。


smartlock200106

スマートロックをM5StickCベースに改めた。充電回路が搭載されているので,5Vさえ給電してやれば意外にやることは少ない・・・。ただし,本体の電池は容量が少ないうえに,電力消費は大きめなので(クロックを下げれば良い??),ドア解放後の気絶までの時間は短い(20分くらい?) 。本当はブザーがほしいところだけど今回は諦め,LCDの色でバッテリー残量を訴えることにしてみた。まぁ基本開放しないでねということで・・・。 壁側に充電ケーブルを保持するガイドが欲しく,新たに3Dパーツを作成した。

151のスマートロックを回復した。マイクロコンピュータはBanggoodで購入したTTGO-Tだ。ディスプレイの使い方は,ここに解説されている。電源電圧のとり方は,調べたがよくわからなかったので,開放されっぱなしのときどうなるのか・・・。

M2@SC171106

11/5 森田先生、長濱先生、宮西さん、加藤くん、新井くん、永田くん、吉田くん、長野の8人で計測機の作製をおこなった。永田・新井の両氏は3Dプリンティングと切削、残りのメンバーは回路の組み立てを主に行った。
img_2598.jpg img_2594.jpgimg_2595-1.jpg
img_2596.jpg img_2599.jpg img_2597.jpg

最終的に計5個の計測機が完成し、当方に存在した計測機を加え、6台で同時に皮膚コンダクタンスを測定した。課題はサイコロを転がして、出た目の内容に応じて話をするというもの。皆自分の担当時に明確な発汗反応が観測された。
wscpic結果エクセル:wscdata

計測機に関する情報:

img_2567.jpg img_2566.jpg img_2565.jpg

Arduinoプログラム一式:M2

表示用PHPプログラム:showchannel

 

The Progress of Quadruped Robot Development #04

プログラム:Base Program of Quadruped Robot
・各サーボモーターの定義付け、Arduinoからの出力チャンネルの設定が完了。
・定義”if”で、足の動かす領域を設定できる。(パーツの故障防止用プログラム)
//以下は右前脚のプログラム//
if(RF_AngLim<“X”){RF_AngLim=”X”;}・・・”X”以下の数字にはならない
if(RF_AngLim>”Y”){RF_AngLim=”Y”;}・・・”Y”以上の数字にはならない
*RF_AngLim(変数)はコントローラーなどの外部のコマンド入力により数値が変わる予定。
・定義”write”で各関節の動きが設定可能。(計算式が必要)
//以下は右の第一関節の例//
RF.write(“C”-(RF_AngLim-“D”)・・・”D”からRF_AngLimを引いてから”C”を引いた角度になる
*各関節の変数は一つに纏めてあるのでそれぞれにあった公式が必要。
・後に関数”delay”が必要になるかもしれない。
・定義”loop”は未完成。

EyeTribeソフトウェア群がだいたい完成する

少し前から開発してきたEyeTribe用のソフトウェアだが、6/4の基礎実験での実施を控え、ようやくだいたい完成した。
g
ソフトウェアは、注視データ計測用のRec、再生用のPlay、集計用のAnalyzeの三種に分かれる。実行環境はProcessing3で、EyeTribeによる記録を行う際は、EyeTribe用のライブラリをインストールする必要がある。

計測ソフトウェアETRec
刺激画像フォルダinpicに、インターネット検索等で取得した刺激画像を入れる。画像はできるだけ解像度が高いほうが良いが、最大1920×1080くらいまでにおさめたほうが良い。画像は、実行マシンのディスプレイにあわせ適宜拡大縮小されて提示される。画像は001.pngなどのように通し番号をつける必要がある。また、フォーマットはPNGでなければいけない。画像提示は、フィクセーション(十字マーク)、画像、ブランクの順で進むが、それぞれの提示時間は、ソースコード冒頭の、fixt、tgtt、blanktで指定する(単位はmsで5000とすれば5秒提示となる)。用意した画像を全て表示し終わると、resultフォルダに各画像の注視データがCSV形式で保存される。また、outpicフォルダに、実際に提示されたサイズ(マシンにあわせて拡大縮小されたサイズ)の画像が保存される。resultフォルダ内のデータは、次回実行時に上書きされるので、計測後に参加者名をつけて移動しておく。
ファイル:ETREC_160523

再生ソフトウェアETPlay
ソースコードがあるフォルダ内に、ETRecで作成されたresultフォルダ(改名しておく)、outpicフォルダをおいておく。ソースコードの冒頭で、folder=”./d2/”;の部分があるので、注視データが格納されているフォルダ名を指定し実行する。フォルダ内の注視データを読み込み、outpic内の画像に実時間で表示する。表示の際は、tgttをETRecと同一にしておく必要がある。正しく動作すると、上記のように注視点が白丸で、軌跡が赤で表示される。
ファイル:ETPlay160523

集計ソフトウェアETAn
ETRecでoutpicに出力された画像を、ソースコードがあるフォルダ内のpicフォルダとareapicフォルダにコピーする。注視データは、gazedataフォルダにいれる。実行すると、提示された画像が表示されるので、注視回数をチェックしたい領域を塗りつぶす。色は数字キーの1~7で、white,red,green,blue,yellow,skyblue,purpleが切り替えられる。左ドラッグで塗りつぶし、右ドラッグで修正できる。画像は、カーソルキーの左右で切り替える。全ての画像を塗り終わったら、スペースキーを押す。すると、フォルダ内のresult.csvに集計結果が保存される。
ファイル:ETAn160524

EyeTribe注視座標データを記録/再生

EyeTribe。前回は、眼球注視位置を画像に書き込みながら刺激提示したが、普通は注視点は表示しないで実験するものだろう。今回は注視点の座標をディスクにCSV形式で保存するプログラムを作成した。気がついたことは、、、

1.実はEyeTribeは注視(Fixation)を検出し、isFixedのようなフラグを立ててくれる。
2.ライブラリで得られるGazedataにはタイムスタンプが入っており、キッチリ30Hzでサンプルしてくれている。
3.GazePointだけでなく、右目左目の瞳孔径も取得できる(できそう)。
4.瞬目情報が含まれていない(瞳孔径などの情報から自分で判断しろと?)
旬目中はGaze座標が(0,0)になるという情報あり。

で、CSVになってデータが落ちたはいいが、正しく記録できているか検証するために、CSVをビジュアライズするプログラムが必要ですよね。今回とったデータは、画像提示が3000msなので、30Hzつまり1サンプルあたり33.33msなので、3000/33.33=90個のデータがあるはずだが。。。。あれ、81~82個しかないね?注視点の影響が出ないように、画像提示直後記録しないようにしているせいかな。。。とりあえずこれを読み込み、提示画像に描画するプログラムを書いてみた。

ふむ、、、プログラムの動作は悪くないようだね。記憶にあるとおりだ。興味のない画像などは、背景に目がいっていまう。「これなんだろう・・・ああ、洗面台か」などのように。それにしても、16ポイントキャリブレーションをしたせいか、かなり正確に測れている。この価格でこの性能はやはり驚きだ。あとはエリア指定して、そのエリアに入っている時間を算出してくれるインターフェイスを作れば最低限の使用に耐えそうだ。10個買って、基礎実験(等)に投入予定なのだ。注視情報や瞳孔径をどのように使うかは、今後の課題としましょう。(撮影はBandicamでやりました。これまた素晴らしく便利。)

記録プログラム:ET2
再生プログラム:ETP1
—-

Processing、EyeTribe用ライブラリのリファレンスを発見
http://jorgecardoso.eu/processing/eyetribeprocessing/reference/index.html
EyeTribe本家のサイトに、FrameObjectの解説がある
http://dev.theeyetribe.com/api/

 

日本心理学会TWSの準備

今回の日本心理学会では、「自作計測器で学ぶ精神生理学」というチュートリアルワークショップを開催します。前半は、ポータブル皮膚温計で、自己紹介時の皮膚温変化を視覚化します。後半は、ポータブル心拍計を使って、タングラム課題中の心拍ゆらぎ(RMSSD)を観測します。で、ポータブル心拍計をチェックしていたのですが、液晶が半分映らないものや、電源のハンダがとれて接触不良になってしまってるものなど、いろいろな故障が見受けられました。

20150909-184459-67499794.jpg 20150909-184500-67500590.jpg

ケーブルも、かなり接触が不安定なものがあり、中身を確認すると、どこかで見たようなヒドイ状態のものがあったので、これも治しました。でも、全部で10台あるはずが、6台しかないんですよね。あとは、一台はBF機能を追加して福島が専有してて、もう一台は平良さんの手元にあるはず・・・、もう2台は??心当たりがあるかたは教えて下さい。まぁ作ればいいんですが、作るのが面倒くさくて。