Oculus Linqでモニター1だけ映らない問題(メモ)

概要

Windows10のゲーミングノートパソコン上において、Oculus Quest 2でOculus Linkをした際にモニター1(内蔵モニター)だけ真っ黒で表示されなかった。 その際に、解決できた方法をメモ。

解決法

UMotion Proで荒ぶるモーキャプデータを修正!編集!

こちらはUnity アセット真夏のアドベントカレンダー 2019 Summer!の9日目の記事になります。

内容

Unityエディタ上でアニメーション編集できるソフトであるUMotion Proを使ってモーションキャプチャーデータを修正していきたいと思います。 UMotion ProはFKとIKの相互変換での編集を行いやすくなっており、 噛めば噛むほどハマって行きます。 皆さんも是非試してみてください。 使用したデータはこちらからダウンロードできます。

背景:荒ぶるモーキャプデータを修正したい

データを見てみるとわかると思いますが、元のモーキャプデータは結構乱れていて、人間では動かせないような角度に骨格が荒ぶっています。 今回はこれを直していきたいと思います。

f:id:kyusque:20190808130204p:plain

手段:UMotion

UMotion ProはUnity Asset Storeに公開(有償)されているアニメーション編集ライブラリで、他のソフトウェアを使うことなくUnityエディタ上で簡単にアニメーションを編集することができるそうです。 特にモーションキャプチャのデータなどの既存のデータの手直しの際は群を抜いて利用しやすいそうです。 また、Uマニュアルや動画、フォーラムなども揃っており、困った時はこれをみると良さそうです(ダウンロードするとhtmlファイルが中に入っている)。

今回は既存のアニメーションデータ(fbx)の修正になりますので動画としてはこちらになります。

(2) Editing Existing Animations - UMotion In Practice - YouTube

日本語サイトとしては汗人柱さんのサイトが参考になりますので動画をみるのが面倒臭い方は参考にしてみてください。

サンプル:グランフェッテ

モーキャプを触らしてもらう機会があった時に、そこにいた友人に何かやってと頼んだらやってくれた大技。何回も回ります。

ただ大技すぎて回るごとにモーキャプ機材がずれていき、中のデータはなかなかひどいことになりました。 ただせっかくなので綺麗にして使いたい! そういうわけでUMotion Proです。

Unityちゃんモデルデータを入れる

とりあえず、元のデータを入れるまでの流れを説明したいと思います。 特に必要ない方は# UMotion Proをインポートしたらまずすること まで飛ばしていただいて結構です。

こちらからUnityちゃんのデータをダウンロード後、インポートしてください。 筆者の環境ではコンパイルエラーになりましたので、とりあえずautoblink.cs のusing System.Security.Policy;を消せば大丈夫なので気にせず進んでください。 インポート後、[UnityChan][Prefabs][unitychan_dynamic]を新しいシーンに入れてください。その後、Idle ChangerとFace Updateのチェックを外してください。

fbxをインポートする

先ほどのモーキャプデータのfbxファイルをAssetフォルダに入れてください。 この後、fbxデータを選択してInspecter上で[Rig]タブをクリックして、Animation TypeをGeneralからHumanoidにしてください。

この後、[Create]>[Animation Controller]を作成してAnimatorウィンドウにfbxファイルをドラッグアンドドロップした後、UnityちゃんのAnimatiorに作成したAnimation Controllerをアタッチすると元のデータがどのようなものか分かります。

UMotion Proをインポートしたらまずすること

ではお待ちかねUMotion Proを始めたいと思います。 筆者はこの時点で躓きました!

まずはじめに [Window]>[UMotion Editor]>[Clip Editor] [Window]>[UMotion Editor]>[Pose Editor] を両方とも開きます。 前者は下の画面に、後者は右の画面に配置すると良いそうです。 筆者はここで片方だけ開いて試そうとして何も動かず途方に暮れ、マニュアルを参照しました。

  • Clip Editorウィンドウから[File]>[New Poject]>[Humanoid]からプロジェクト作成
  • Pose Editorウィンドウの[Select a GameObject to animate]のフィールドにUnityちゃんをドラッグアンドドロップ
  • Clip Editorウィンドウの[File]>[Import Clips]でfbxファイルをインポート
  • Clip Editorの右下の方にレイヤーっぽいボタンがあるので、適宜レイヤーを追加して編集

これでとりあえずは編集できると思います。

他に重要な点としては

  • 自動でモーション変更のためのkeyを作成したい場合はPose EditorウィンドウのAutoKeyを使う
  • IK を使う場合はPoseModeからConfigModeにしてIK Setup Wizardを押す。
  • IKを動かすためのオブジェクトは通常のモデル部位(FK)とまた別でIKとして用意されていて、そこでIKの重み付けなどを行う。
  • FKは基本的にRotationで行う。IKの重みが1の場合は白色の骨格が動く。
  • IKとFKはSet IK to FKとSet FK to IKで相互変換できる。前者はIKの骨格をそのままFKへ、後者はIKに関与するアンカーの位置を変える。
  • 回転をカーブで変更させる場合はAnimated PropertiesのquatanionからEularにする必要が有る。
  • FKモードとIKモードがあるという訳ではなく、FKのオブジェクトとIKのオブジェクトを選択して編集していく。ただし、FK骨格とIK骨格の画面表示を切り替えることで編集や選択がしやすくなる。

編集途中の画像

今回のデータを編集した時の画像です。 まだまだ改良の余地が残されているのですが、今回はここでおしまいにしました。 編集前のデータと編集後のデータはこちらに有りますので、皆さんもお試しください。何に使っていただいても結構です。

手があらぬ方向を向いているUnityちゃんさん f:id:kyusque:20190809062525p:plain

FKで修正 f:id:kyusque:20190809062556p:plain

回っているうちに移動していくのを、骨盤の位置を原点付近にして修正 FKでは唯一のpositon f:id:kyusque:20190809062636p:plain

首を傾けながら回っていたので修正 f:id:kyusque:20190809062709p:plain

画面上で編集してどの値を直すべきかを確認 f:id:kyusque:20190809062801p:plain x, y, zのうち一つだけ消して初期値にするということはできないので、 幅を狭めて擬似的に定数にする f:id:kyusque:20190809062816p:plain

腕はIKで編集 f:id:kyusque:20190809062851p:plain

Editから元データをIKデータに編集することができる f:id:kyusque:20190809062924p:plain

最後の部分だけ修正しするため、汚い部分を消す f:id:kyusque:20190809062954p:plain

FKで先に編集してからSet IK to FKしている図 f:id:kyusque:20190809063009p:plain

とりあえず決めポーズだけ作成 f:id:kyusque:20190809070256p:plain

黒い画面(コンソール)はじめの一歩

阪医python会の投稿用に書いています。

対象は計算科学始めたてでlsコマンドやcdコマンドはわかる人向けです。

黒い画面のススメ

黒い画面=コンソールで作業ができるようになると、キーボードからマウスへ手を動かす回数が減り仕事が捗ります。

ただ、初心者には難しいかもしれないので、最初のとっかかりの部分だけ説明したいと思います。

コンソールはOSX(mac)の場合はターミナル、Windowsの場合はGit bash(gitをインストールした時についているbash)やbash on windows(WindowsLinuxサブシステム)を想定しています(前者がオススメ)。

はじめの一歩

Tabボタンで入力補完!

コンソール画面で、コマンドを途中まで打って「tab」ボタンを押すとそれ以降の文字が補完されます。

cleまで打ってtabを押すと f:id:kyusque:20190127183954p:plain

最後まで補完されます f:id:kyusque:20190127184011p:plain

補完候補が複数あると画面上に表示されます(clまで打ってtabを押した場合) f:id:kyusque:20190127184025p:plain

open(explorer)コマンドでファイル、ディレクトリ(フォルダ)を開く!

今回は「コンソール画面だけ」で作業するのではなく「コンソール画面でより多く」作業をしてマウスでの操作を減らすことを目的としています。 黒い画面のライトユーザーです。

初めはコンソールでファイルやフォルダを開けるようになるだけで、大幅に時間が短縮できるのでまずはこれを覚えましょう。

OSX(Mac)の場合

ファイルやフォルダを開くコマンドはopenになります。

open .

と入力するとファインダーが開きます。

open hoge.docx

と入力すると、hoge.docxがワード(拡張子に対してデフォルトのアプリ)で開きます。

Windows(Git bash)の場合

ファイルやフォルダを開くコマンドはexplorerになります。

explorer .

と入力するとエクスプローラーが開きます。

explorer hoge.docx

と入力すると、hoge.docxがワード(拡張子に対してデフォルトのアプリ)で開きます。

もう一歩

aliasでオレオレコマンド

alias 別名='コマンド'と打ち込むと特定のコマンドを別名のコマンドで実行できるようになります。

経験的にtabボタンに左手の小指が乗ってる状態で補完をかけられるまで打ち込めるような名前にしておくと良いです。

例えば、windows の場合、先のexplorerのexが打ちにくいので、openにエイリアスしておくと使いやすくなります。

alias open='explorer'

ただ、このままだと再起動すると設定がリセットされます。 .bash_profileという名前のファイルをホームフォルダに作って(隠しファイルと拡張子が見えるように設定しておく必要)、置いておくと毎回打ち込む必要が無くなります。

.bash_profileファイル

alias open='explorer'

この他にも、OSX(mac)だと alias アプリ名='open -a アプリ名'にしておくとアプリを直接起動できるので便利だったりします。

alias rstudio='open -a RStudio'

色々やりやすいように改造してみてください

ユニティちゃんトリプルアクセル(6回転してしまっている)

こちらはUnity #2 Advent Calender 2018 24日目の記事です。

筆者は2018全日本フィギュアスケート最終日に観戦に行きます。

最終日は男子のフリーですが、現役復帰した高橋大輔選手が演技するのが楽しみです。

最近は女子でもトリプルアクセルを余裕で降りる選手が増え、年々レベルが上がってるのを実感します。

というわけで、前回は非実装だったトリプルアクセルを今回なんとか実装してみようかと思います。

概要

  • Unityちゃんにトリプルアクセルをやってもらう。
  • アニメーションを作り込んで行くのはしんどいので、Final IKと組み合わせて少ない変数で制御する
  • モーキャプという手もあるが、陸地で足元をやるのは限界があるし、そもそも筆者は氷上でもトリプルアクセルを跳べない
  • 最終的にはプロシージャルアニメーションみたいな感じでやりたい(願望)
  • 上半身の動きは今回考えない(モーキャプでブレンドすることを想定)

Final IKで足の曲がりをいい感じにしてもらう

膝の曲げなどの設定をしていくのを出来るだけ減らしたいのでFinal IKのLeg IKを設定します。

左右の足にIKをきかせたいので、UnityちゃんのモデルにふたつLeg IKをアタッチします。

f:id:kyusque:20181224132541p:plain

で、Targetにつま先、Bend Goalに膝のIKのターゲットをとりたいのでUnityちゃんと同じ階層に左右の足のターゲットと、そのターゲットの子オブジェクト(膝用)をつま先の前方におきます

f:id:kyusque:20181224132246p:plain

トリプルアクセルの途中動作を書いていく

で、下のようなスクリプトを書いてUnityちゃんとIKターゲットの親オブジェクトにアタッチします。

基本的に3つのオブジェクト(Unityちゃん、左右のIKターゲット)の向きと高さで制御できるようになっています。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class FootManager : MonoBehaviour
{
    [SerializeField] private Transform character;
    [SerializeField] private Transform rightTarget;
    [SerializeField] private Transform leftTarget;

    private void OnGUI()
    {
        if (GUILayout.Button("Preparation"))
        {
            character.localEulerAngles = new Vector3(0f, 90f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, 180f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, 90f, 0f);

            character.localPosition = new Vector3(0f, -0.01f, 0f);
            rightTarget.localPosition = new Vector3(0.1f, 0f, -0.05f);
            leftTarget.localPosition = new Vector3(0.1f, 0.1f, 0.1f);
        }

        if (GUILayout.Button("Steping"))
        {
            character.localEulerAngles = new Vector3(0f, 45f, 0f);
            rightTarget.localEulerAngles = new Vector3(45f, 90f, 90f);
            leftTarget.localEulerAngles = new Vector3(0f, 0f, 0f);

            character.localPosition = new Vector3(0f, -0.09f, 0f);
            rightTarget.localPosition = new Vector3(0.5f, 0.5f, -0.5f);
            leftTarget.localPosition = new Vector3(0.1f, 0f, 0.1f);
        }

        if (GUILayout.Button("TakingOff1"))
        {
            character.localEulerAngles = new Vector3(0f, 0f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, 0f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, 0f, 0f);

            character.localPosition = new Vector3(0f, -0.09f, 0f);
            rightTarget.localPosition = new Vector3(0.3f, 0.1f, 0.2f);
            leftTarget.localPosition = new Vector3(0.1f, 0f, 0.1f);
        }

        if (GUILayout.Button("TakingOff2"))
        {
            character.localEulerAngles = new Vector3(0f, -45f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, -45f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, -45f, 0f);

            character.localPosition = new Vector3(0f, -0.02f, 0f);
            rightTarget.localPosition = new Vector3(0.0f, 0.3f, 0.4f);
            leftTarget.localPosition = new Vector3(0.1f, 0.0f, 0.0f);
        }

        if (GUILayout.Button("TakingOff3"))
        {
            character.localEulerAngles = new Vector3(0f, -90f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, -90f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, -90f, 0f);

            character.localPosition = new Vector3(0f, 0.1f, 0f);
            rightTarget.localPosition = new Vector3(0.0f, 0.3f, 0.4f);
            leftTarget.localPosition = new Vector3(0.1f, 0.12f, 0.0f);
        }

        if (GUILayout.Button("TakingOff4"))
        {
            character.localEulerAngles = new Vector3(0f, -180f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, -190f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, -190f, 0f);

            character.localPosition = new Vector3(0f, 0.27f, 0f);
            rightTarget.localPosition = new Vector3(0.05f, 0.3f, 0.1f);
            leftTarget.localPosition = new Vector3(0.0f, 0.33f, 0.0f);
        }


        if (GUILayout.Button("AirPosition1"))
        {
            character.localEulerAngles = new Vector3(0, 0, 0);
            rightTarget.localEulerAngles = new Vector3(0, -20, 0);
            leftTarget.localEulerAngles = new Vector3(0, -20, 0);

            character.localPosition = new Vector3(0, 0.28f, 0);
            rightTarget.localPosition = new Vector3(-0.05f, 0.3f, 0.0f);
            leftTarget.localPosition = new Vector3(0.05f, 0.33f, 0.1f);
        }

        if (GUILayout.Button("AirPosition2"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -200, 0);
            leftTarget.localEulerAngles = new Vector3(0, -200, 0);

            character.localPosition = new Vector3(0, 0.3f, 0);
            rightTarget.localPosition = new Vector3(0.05f, 0.32f, 0.1f);
            leftTarget.localPosition = new Vector3(-0.05f, 0.35f, 0.0f);
        }

        if (GUILayout.Button("AirPosition3"))
        {
            character.localEulerAngles = new Vector3(0, 0, 0);
            rightTarget.localEulerAngles = new Vector3(0, -20, 0);
            leftTarget.localEulerAngles = new Vector3(0, -20, 0);

            character.localPosition = new Vector3(0, 0.25f, 0);
            rightTarget.localPosition = new Vector3(-0.05f, 0.3f, 0.0f);
            leftTarget.localPosition = new Vector3(0.05f, 0.33f, 0.1f);
        }

        if (GUILayout.Button("AirPosition4"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -200, 0);
            leftTarget.localEulerAngles = new Vector3(0, -200, 0);

            character.localPosition = new Vector3(0, 0.2f, 0);
            rightTarget.localPosition = new Vector3(0.05f, 0.25f, 0.1f);
            leftTarget.localPosition = new Vector3(-0.05f, 0.28f, 0.0f);
        }

        if (GUILayout.Button("AirPosition5"))
        {
            character.localEulerAngles = new Vector3(0, 0, 0);
            rightTarget.localEulerAngles = new Vector3(0, -20, 0);
            leftTarget.localEulerAngles = new Vector3(0, -20, 0);

            character.localPosition = new Vector3(0, 0.15f, 0);
            rightTarget.localPosition = new Vector3(-0.05f, 0.2f, 0.0f);
            leftTarget.localPosition = new Vector3(0.05f, 0.23f, 0.3f);
        }

        if (GUILayout.Button("Landing"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -200, 0);
            leftTarget.localEulerAngles = new Vector3(0, -200, 0);

            character.localPosition = new Vector3(0, -0.07f, 0);
            rightTarget.localPosition = new Vector3(0.05f, 0.0f, 0.1f);
            leftTarget.localPosition = new Vector3(0.45f, 0.03f, -0.2f);
        }

        if (GUILayout.Button("Check"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -180, 0);
            leftTarget.localEulerAngles = new Vector3(0, -240, -90);

            character.localPosition = new Vector3(0, -0.17f, 0);
            rightTarget.localPosition = new Vector3(0.1f, 0.0f, 0.2f);
            leftTarget.localPosition = new Vector3(0.46f, 0.4f, 0.7f);
        }
    }
}

f:id:kyusque:20181224133536p:plainf:id:kyusque:20181224133541p:plainf:id:kyusque:20181224133548p:plainf:id:kyusque:20181224133553p:plainf:id:kyusque:20181224133559p:plainf:id:kyusque:20181224133605p:plainf:id:kyusque:20181224133609p:plainf:id:kyusque:20181224133615p:plainf:id:kyusque:20181224133621p:plainf:id:kyusque:20181224133626p:plainf:id:kyusque:20181224133631p:plainf:id:kyusque:20181224133638p:plainf:id:kyusque:20181224133644p:plain

コルーチンでLerpを使いながら実行

では上の変数を参考に補完も加えながら、コルーチン化してみたいと思います。

以下がコードです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FootManager2 : MonoBehaviour {

    [SerializeField] private Transform character;
    [SerializeField] private Transform rightTarget;
    [SerializeField] private Transform leftTarget;
    
    
    // Use this for initialization
    void Start () {
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Triple Axel"))
        {
            StartCoroutine(TripleAxel());
        }       
    }

    IEnumerator TripleAxel()
    {
        Vector3[] char_tmp;
        Vector3[] right_tmp;
        Vector3[] left_tmp;
        int span;
        
        //Preparation
        character.localEulerAngles = new Vector3(0, 90, 0);
        rightTarget.localEulerAngles = new Vector3(0, 180, 0);
        leftTarget.localEulerAngles = new Vector3(0, 90, 0);

        character.localPosition = new Vector3(0, -0.01f, 0);
        rightTarget.localPosition = new Vector3(0.1f, 0, -0.05f);
        leftTarget.localPosition = new Vector3(0.1f, 0.1f, 0.1f);
        
        for (int i = 0; i <= 10; i++)
        {
            yield return 0;
        }
                
        //Steping

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 45, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(45, 90, 90), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, 0, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.09f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.5f, 0.5f, -0.5f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0, 0.1f), (float)i / span);
            yield return 0;
        }

        
        //TakingOff1

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, 0, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.09f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.3f, 0.1f, 0.2f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0, 0.1f), (float)i / span);
            yield return 0;
        }

        //TakingOff2

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0f, -45f, 0f), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0f, -45f, 0f), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0f, -45, 0f), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0f, -0.02f, 0f), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.0f, 0.3f, 0.4f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0.0f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //TakingOff3

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0f, -90f, 0f), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0f, -90f, 0f), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0f, -90, 0f), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0f, 0.1f, 0f), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.0f, 0.3f, 0.4f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0.12f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //TakingOff4

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0f, -180f, 0f), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0f, -190f, 0f), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0f, -190f, 0f), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0f, 0.27f, 0f), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.3f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.0f, 0.33f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition1

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -20, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -20, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.28f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(-0.05f, 0.3f, 0.0f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.05f, 0.33f, 0.1f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition2

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -200, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -200, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.3f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.32f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(-0.05f, 0.35f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition3

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -20, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -20, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.25f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(-0.05f, 0.3f, 0.0f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.05f, 0.33f, 0.1f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition4

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i < span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -200, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -200, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.2f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.25f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(-0.05f, 0.28f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition5

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -20, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -20, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.15f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(-0.05f, 0.2f, 0.0f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.05f, 0.23f, 0.3f), (float)i / span);
            yield return 0;
        }
        
        //Landing

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -200, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -200, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.07f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.0f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.45f, 0.03f, -0.2f), (float)i / span);
            yield return 0;
        }
        
        //Check

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -240, -90), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.17f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.1f, 0.0f, 0.2f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.46f, 0.4f, 0.7f), (float)i / span);
            yield return 0;
        }
        
    }
    
}

動画

あれ!!!6回転以上してる!!!???

多分、Lerpで回りすぎているみたいです。この辺りを調査しないと…誰か……

プロシージャルアニメーションにしたいんだったらここを詰めないといけないかもです。

ただ、もうそろそろ男子フリーが始まるので今回はここまで!!

シングルセル解析ソフトScanpyを試してみる

この記事は創薬 Advent Calendar 2018 17日目の記事です。

シングルセル解析ソフトScanpyを試してみる

PythonのシングルセルRNA-seq解析ツールであるところのScanpy阪大医学部Python会@yyoshiakiさんに教えてもらったので、試してみました。

RだとSeuratというパッケージがいいらしいですが、Pythonの方をプッシュされたのでそちらを行いました。

筆者の知識レベル

  • in vitroの実験はやったことない。
  • FACSは同期がやってるので概要はなんとなく知ってる
  • RNA-seqはほぼ知識ゼロ

チュートリアル

最初のチュートリアルZheng 2017をやりました。

論文の概要

ドロップ型のscRNA-seqシステムの開発

サンプル中の細胞をひとつひとつ高い確率でキャプチャでき、高速にmRNAを数えることができる。

  • droplet内に一つずつ細胞を入れ、その細胞のmRNAをエマルジョン(a Gel bead in EMulsion, GEM)内でまるっと数えることができる(一細胞RNA-seq)
  • cDNAへの逆転写はGEM内でバルクで進行する
  • GEMごとのプライマーにバーコード(14bp)がついてる
    • どのRNAがどの細胞由来かわかる
  • GEMごとのバーコードに加え、さらにプライマーごとにランダムなUMI(unique molecular identifier, 10 bp)がついてる
    • UMIの数を数えることで、その遺伝子の数がわかる。
    • UMIにはサンプルに対応するIDも入ってるので、複数サンプルを平行に処理することが可能
  • タグ(バーコード、UMI)がついてるので、PCR増幅はGEMを壊してまとめて行える

元文献では色々なサンプルを使って測定したデータを使って解析してましたが、今回はperipheral blood mononuclear cells (PBMCs) のクラスタリングチュートリアルで行っていました。

チュートリアルのコード(改変あり)

元データ

1細胞データ

遺伝子発現ラベル(バルク)

全ファイル同じフォルダに入れてください

本家(scanpy == 1.0.0)とscanpyのバージョンが違い(scanpy == 1.3.6)、できないものがあったので、できたやつだけ書きました。

各種パッケージのインポート

import numpy as np
import pandas as pd
import matplotlib.pyplot as pl
import scanpy.api as sc

sc.settings.verbosity = 1  # verbosity: errors (0), warnings (1), info (2), hints (3)
sc.settings.set_figure_params(dpi=80)  # low dpi (dots per inch) yields small inline figures
sc.logging.print_versions()
scanpy==1.3.6 anndata==0.6.16 numpy==1.14.2 scipy==1.2.0 pandas==0.23.4 scikit-learn==0.20.2 statsmodels==0.9.0 

データの読み込み

%%time
adata = sc.read('matrix.mtx', cache=True).T  # transpose the data
adata.var_names = pd.read_csv('genes.tsv', header=None, sep='\t')[1]
adata.obs_names = pd.read_csv('barcodes.tsv', header=None)[0]
Variable names are not unique. To make them unique, call `.var_names_make_unique`.


CPU times: user 1.16 s, sys: 386 ms, total: 1.54 s
Wall time: 1.65 s
adata
AnnData object with n_obs × n_vars = 68579 × 32738 

重複した名前をユニークにする

adata.var_names_make_unique() #最初からユニークでは?
adata
AnnData object with n_obs × n_vars = 68579 × 32738 

バルク(1細胞じゃない)での遺伝子発現ラベルの読み込み

adata.obs['bulk_labels'] = pd.read_csv('./zheng17_bulk_lables.txt', header=None)[0].values

変数を減らして、正規化後、Cell Rangerでフィルタリング

記事筆者はCell Rangerについて何も知らない(力尽きた)

%%time
sc.pp.filter_genes(adata, min_counts=1)  # only consider genes with more than 1 count
sc.pp.normalize_per_cell(adata)          # normalize with total UMI count per cell
filter_result = sc.pp.filter_genes_dispersion(
    adata.X, flavor='cell_ranger', n_top_genes=1000, log=False)
CPU times: user 2.07 s, sys: 979 ms, total: 3.04 s
Wall time: 3.08 s

プロット

対数プロットになるらしいが……

sc.pl.filter_genes_dispersion(filter_result, log=True)

f:id:kyusque:20181220163745p:plain

PCAによる次元圧縮

%%time
adata = adata[:, filter_result.gene_subset]  # filter genes
sc.pp.normalize_per_cell(adata)  # need to redo normalization after filtering
sc.pp.log1p(adata)  # log transform: X = log(X + 1)
sc.pp.scale(adata)
# the PCA is *not* contained in the recipe sc.pp.recipe_zheng17(adata)
sc.tl.pca(adata, n_comps=50)
CPU times: user 5.54 s, sys: 668 ms, total: 6.21 s
Wall time: 3.18 s

PCAのloadingのプロット

便利。

記事筆者が知らない遺伝子ばっかりだ…

sc.pl.pca_loadings(adata)

f:id:kyusque:20181220163837p:plain

K meansによるクラスタリング

1細胞のmRNAからの情報のみによる分類でも、バルクのグループに分かれてますね。

%%time
sc.pp.neighbors(adata)
CPU times: user 20.5 s, sys: 4.07 s, total: 24.6 s
Wall time: 21.7 s
%%time
sc.tl.umap(adata)
CPU times: user 56 s, sys: 615 ms, total: 56.6 s
Wall time: 49.4 s
sc.pl.umap(adata, color='bulk_labels')
... storing 'bulk_labels' as categorical

f:id:kyusque:20181220163901p:plain

メモ

  • 他にもマーカー遺伝子を取り出す手法などありましたが、現バージョンでそのままできなかったので今回は諦めました。
  • 移植後の患者からの細胞群の解析(こういう実験はin vivoに入るのでしょうか?in vitro?)など元データの文献自体も面白そうだったので、輪読に使えるのかなーと思います。
  • 元文献を読んでると知らない知識が多く、質問事項がえらいことになったので、おいおい勉強していこうと思います。

各種手袋によるホロレンズのジェスチャー認識の検証

この記事はHoloLens Advent Calendar 2018 5日目の記事になります。

 

先日Hololensの開発をしてて思ったのですが、手袋着けててもジェスチャー操作できるのが結構便利なことに気づきました。

ただ、モノによってできたりできなかったりがあるので、今回、どんな手袋のジェスチャーがHololensで認識できるのか否かを検討してみることにしました。

HoloLensのジェスチャー認識機能

基本のジェスチャーはタップとブルームになります。

これらを各種手袋で認識するか試します。

普通の手袋

よく見る普通の手袋

f:id:kyusque:20181209165810j:plain

f:id:kyusque:20181209164005g:plain

 

タップ、ブルームともに問題なくできます。

 

グローブ

バイクとか乗るときに使うあったかいグローブ

 

f:id:kyusque:20181209170204j:plain

 

f:id:kyusque:20181209164232g:plain

 

タップは問題なくできますが、ブルームが認識しづらいです。

 

ゴム手袋

掃除用の洗剤を使うにつけるゴム手袋

f:id:kyusque:20181209170214j:plain

 

f:id:kyusque:20181209164127g:plain

 

タップは問題なくできますが、ブルームが少し認識しづらいです。

 

ビニール手袋

毛染めとかによくついてる安っぽい手袋

f:id:kyusque:20181209170137j:plain

 

f:id:kyusque:20181209164413g:plain

 

タップ、ブルームともに問題なくできます。

 

実験用手袋

実験用の手袋

f:id:kyusque:20181209170149j:plain

 

f:id:kyusque:20181209164758g:plain

 

タップ、ブルームともに問題なくできます。

 

汚い軍手

汚い軍手

 

f:id:kyusque:20181209170224j:plain

 

f:id:kyusque:20181209164926g:plain

 

タップ、ブルームともに問題なくできます。

 

野球のグローブ

内野用グローブ。ワンチャンでブルームはいけるのでは?

f:id:kyusque:20181209170234j:plain

f:id:kyusque:20181209171128g:plain

無理でした!!

薬学関連で計算科学をやっていると良く聞かれる質問集

  • 2018/12/06 宇宙海賊コブラ さん @TGrlmP3I4TRy023 からいただいた質問を追加しました

こちらは創薬 Advent Calendar 2018 3日目の記事です。

一応、薬学系の研究室で計算化学を使って研究している者です。

応用的な理論研究をやっていると良く実験系の人とコラボすることが多いですが、分野の違う人の前で発表するとプリミティプな質問や答えに窮する質問が飛んできます。

対応できるものやそもそも現状無理なものまでたくさんあります。

筆者が一番心に残っているものは、学部時代に実験の先生から言われた「絵に描いた餅は食えない(理論)」という言葉です。 すぐに「昔はそうやったが、最近は食べられるようになってきたのかなと思う」というフォローが入りましたが、含蓄のある言葉です。 実際、計算しただけでは意味が無く、計算した結果を実験や実世界にどう生かすのかも考えるのが重要に思います。 そのためには、自分の分野以外の人の研究も把握して、自分の研究がどういう立ち位置にあるかを理解しておくのが重要です。 餅の絵が欲しいから計算をやっているわけではなく、餅が食べたいから計算をやってるという意識で研究していきたいですね。 実験系にフィードバックするまでが計算科学です。

今回は、自分の分野や他の分野の人から受けた質問や聞いたことのある質問を思い出せる限り書いていきます。 対策とかはないです。

もし、コメントで他の質問をいただけたら、名前も含めて追記していきます。

薬学関連の計算科学の研究をしていると良く聞かれる質問集

普通の質問

この研究は意味ある?

良くある質問ですがこの質問をされる場合、発表の対象設定が間違えてるかもしれません。

この計算意味ある?

「無」という結果を発表した場合、このような質問が飛び出るかもしれません。

この計算研究として成立してる?

一つだけ計算やって、コントロール的な対象がなければこうなるかもしれません。

どういう風に論文にするんですか

多分本当に分野違いの人の純粋な疑問か、あるいは、目的が不明瞭な場合

新規性は何?

前のやつでもできるのでは?違いを教えて

研究をやって行く中で一番難しかった所はどこですか?

優しい質問。筆者も学部生に対して良くやる。

前任者はこう言ってましたが〜?

卒業した先輩のテーマでやってるとこんな質問が飛んできます。 はやく先輩を超えましょう。

その結果からはそういう結論にはならないと思いますが。

その先生の専門分野で地雷を踏んだかもしれません。 ただ詳しく聞けば、いい感じに直してくれるかもしれません。

オーバートークでは?

検討できていない条件があるっぽいです。

倫理上問題ないの?

臨床データの扱いには注意しましょう

ビジネスにはしづらいな。

お金にならない

これはアカデミアでやらなくてもいいのでは?

もっと基礎研究に寄せよう。

発表についての質問コメント

ストーリーがわかりづらい

事実の羅列だけで科学になるから、ストーリーなどいらない。そう考えていた時期もありました。

スライドが重い。

字が多すぎる

字が小さい。

字が小さい

私は分かったけど大多数はわからなかったと思う。

チェアマンの背景知識に合わせた発表にするのがいいとは言いますよね。

実験系の質問

実験的な裏付けはないんですか?

あるといいですね。

実験における先行研究の結果との整合性は?

(from 宇宙海賊コブラ さん @TGrlmP3I4TRy023)

整合性が取れてないと実験系の前で発表するのは厳しいかもです。 実験系で信じられていることを計算のみで覆して説得するのはとても難しい。

なぜ実験ではなく計算なのか?

(from 宇宙海賊コブラ さん @TGrlmP3I4TRy023)

根源的な質問で、状況によって答えが様々に変わる質問。

その人によってどこまで言えば納得してくれるかも分からないことが多く、ヒアリングが必要になってくるなため、『あとで話しましょう』になりがちな質問。 あとで話しましょう。

ただ、発表側からすると質問してくれるだけありがたいので、本当に気になった場合は積極的に使っていましょう。

返答例いろいろ

  • 実際の現象をシミュレーションで直接みることができる。
    • 生物系の人に対しては良く通じる言い方
    • 「何をみてるか」が明らか
    • その現象をみてると証明するためにポジコンとったりネガコンとったり苦労しているところは価値を分かってくれる
  • 測定に伴う撹乱要因を排除できる。
    • 測定機器や試薬による変化を取り除くことができる。
    • 実験では(科学的議論においては)それらをコントロール実験で排除するが、繊細すぎて取り除けないものが存在。
    • シミュレーションではその現象のみに絞って結果をだせる。
    • 物性系の人は機器分析でその現象そのものが見れてると思いがち(ToDo:適切な文章に)
  • 意思決定に役出つ
    • 科学的な議論はできなくとも、結果から今後の実験の計画を立てることができる。

他の例はないですか?

無かったら無いっていうしかない。 あるのであれば、最初からいうべきであった。

私はそうは思いません。

バトルの始まりです。 ただ、大抵の場合、計算条件の説明が足りなかったり、結論がデカすぎるだけなので、うまく条件を絞っていきましょう。

実験でやってみないん?

時間があれば。卒業する人にこれを言ってもという感じではある。

うちにはお金も人でもないからそれはできない

なんか言い方が悪かった部分あった可能性があります。 実験系は大変です。手伝ってもらってることに感謝しながら発表しましょう。

後付けの理論のように思いますが?

実験で部分的しにか検証できないことを、計算で補完または示唆できる例をあげましょう。

実験で分かっている(とされている)ことの紹介の仕方が悪かったのかもしれません。

何作ればいい?

何か提案しましょう。

これはすぐには作れない。

合成可能性も考えましょう。

ドクターには進むの?

「ドクター発表で最終結果を聞くから首を洗って待ってろ」の言い換え

計算科学系でも微妙に背景の違う人からの質問

これが分かって何かいいことはあるの?

目的と実験設定が乖離している?

計算科学で同じ分野の人

ターゲット選定間違えてない?

なぜこのターゲットを選んだか説明しましょう。 チャレンジングだから、でも良かったりします。

このパラメータおかしくない?

説明しましょう

なぜこの基底関数選んだの?

(from 宇宙海賊コブラ さん @TGrlmP3I4TRy023)

量子化学計算をやっていると聞かれる質問。 ガウス基底や平面波基底などがあって、系によって最適なものが異なる。

そもそもこの計算手法ではその現象は説明できないのでは?

やばい

別の手法でも試した?

できればやる。できなければ、できない理由を説明する。

ここを説明できないと、メジャーリビジョン以上ですよ。

リジェクト

追加募集中!

他に色々ある人はコメントしていただければ、名前も含めて追加します。