OSVR-HDK1をSDKとか使わず直接使ってみた

# OSVR-HDK1って何？

Razerとかが売ってるVR-HMD。性能的にはOculusのRift DK2程度と考えていい。UnityやUE4には対応プラグインがあるのでCV1とかViveが高いと思う人には丁度いい...のだが...罠だらけ(それになぜかVRなソフトがあまり対応してない)。

**だが$299だ**。

細かいことは[OSVRのページ](https://osvr.github.io/)とか[OSVRの開発ページ](https://github.com/OSVR)から辿れる資料を読むしかない。ググったところで**日本語情報はほとんどない**(たぶん今のところこれが日本語では一番詳しい。リクエストがあればコメントしておくれ)。

ちなみにもうすぐHDK2が出る。Rift CV1程度の性能で$399らしい。

# ここで注意

少なくとも2017年頭の現況ではグラボがAMDだと問題が多い。ダイレクトモードがどうたらみたいな自分ではなかなか直せそうにないところなのでグラボがAMDだったら避けるが吉。

# 日本から買うには？

~~残念ながら今の所Razerはこれを日本向けには出荷していない。~~[もうすぐ日本からも買えるようになるっぽい](http://www.razerzone.com/jp-jp/store/osvr-hdk)。でも転送屋使うほうがずっと安いよなぁ...。

# 届いたら

## とりあえず説明書通りに接続

とにかくケーブルが多い。腰に付けるようになってる箱(belt box module：以後「箱」)を中心に考えるとわかりやすいかも。上にはヘッドフォン端子とHMD本体への特殊なコネクタ。反対側には電源とカメラへのケーブル、USBやHDMIが繋がる。

カメラへは二股になった電源ケーブルと別にUSBの接続がある。このUSBはPC本体へ直接繋いでもいいし、(たぶん)箱の飽き端子に繋いでもいい。

それと、箱と本体にはUSBの空き端子が一つづつ、箱にはさらにヘッドホン端子が空くので端子が埋まっていないからと言って不安になる必要はない。

**本体から出てる太い方のケーブルはうしろのバックルっぽいのを貫通しているだけなので実はバックルに対して動く**。頭の上側のベルトの長さを調整するには太い方のケーブルをバックルから少し引き出す必要があるのでこれを知らずに「調整できねーだろ！」と怒らないように(細い方は元々長い)。

それにしてもケーブルの長さが座って使うにはいろいろ中途半端で困る。椅子に箱を固定できるようにするのがベストなのかもしれない。

## ソフトウェアを準備

HDKと唱っているように、今の所動かすのは結構面倒だ。

https://osvr.github.io/using/ から以下のものを落としてくる。全部要るんだから最初のに含めとけばいいのに...。

* OSVR Runtime for Windows
* OSVR HDK Windows Driver Pack
* OSVR Config
* Firmware update tool for HDK IR Camera
* Windows utility to upgrade HDK firmware, set 3D video mode

で、インストーラーになってるものはインストール、アーカイブ(たぶん下2つ)は展開しておく。

## ファームウェアアップデート

「Windows utility to upgrade HDK firmware, set 3D video mode」を展開した中にあるツールを動かす。出荷状態ではファームウェアが古すぎる事が多々ある(出てくるデータがアレとか電源切るまでUSBを再接続できないとか)。

Linuxからでもファームウェアの書き換えはできるのでWindows環境を持ってなくても大丈夫。まずは[ここ](https://github.com/OSVR/OSVR-Docs/blob/master/Utilities/HDKFirmwareVersions.md)からファームウェアを落としておいて、[この手引書](https://github.com/OSVR/OSVR-Docs/blob/master/Utilities/UpdateHDKLinux.md)の通りにすればいい。

# あと知らね

私ゃ自分でソフトを書く前提で買ったので一般的な消費者の体験はしていないし、自宅にはゲームエンジンを動かせる環境がないので普通の人向けの解説はこれまでだ。以下は自分でドライバも含めてソフトを書くという奇特な人向けになる。

## OSVR-HDKのインターフェイス仕様

USB-HIDが二つ(hidrawとhiddev)。USB-Serial(ttyACM)が一つ。それとUSB-Audio(箱に出てるヘッドフォン端子だろう)。私ゃLinuxで開発してるので窓でそれらがどうなっているのかは知らない。

udevのルールを設定してシンボリックファイルでも作らせておいてファイル名決め打ちで開くほうが簡単だが、自分で識別するにはIOCTLでIDをチェックする。RazerのベンダID(vid)は0x1532、OSVR-HDKのプロダクトID(pid)は0x0b00だ(オーディオとかは違ったような記憶があるので具体的にはdmesgなりlsusbなりで調べれ)。たとえばこんな感じ。

```:c++
struct hidraw_devinfo info;
if(ioctl(fd, HIDIOCGRAWINFO, &info) < 0){
	//ioctlできない
	close(fd);
	continue;
}
if(vid != info.vendor || pid != info.product){
	//探しているデバイスではない
	close(fd);
	continue;
}
```

### /dev/hidraw

これが本命。データフォーマットは[ここ](https://github.com/OSVR/OSVR-Docs/blob/master/Developing/OSVRhdk.md)にある。hexdumpで観察したところでは16bytesごとだった(hexdumpが16bytesごとに取得していてそれが原因になっている可能性もある)。だがとりあえず今は後半には用はない。

しかし困ったことに**ここからは向き情報しか出てこない。**方位付き絶対角の四元数が出てくるので何も考えずに向きを取れるものの加速度が出てこないとポジトラがカメラ頼みになって処理重くなって困るのだが。個人的にはMEMS加速度センサで十分だと思っているのでポジトラにカメラは使わない方針でもある。

で、OSVR-HDKから出てくる四元数のビットフォーマットは14bit目に小数点があるリトルエンディアンの16bits長の固定小数点なのでレコード取得とデコードはこんな感じ。データがビッグエンディアンのビットフィールドで、角速度を四元数に直して蓄積したり磁気情報から方位を推定したり加速度から垂直を推定したりする必要があるRiftに比べると非常に楽だ。

```:c++
struct{
	unsigned char version;
	unsigned char sequence;
	short i;
	short j;
	short k;
	short w;
	short rot[3];
}__attribute__((packed)) r;
const int rb(read(fd, &r, sizeof(r)));
//戻り値の判定とか
COMPLEX<4> q(
	(double)r.w / 16384,
	(double)r.i / 16384,
	(double)-r.j / 16384,
	(double)-r.k / 16384)
```

最初のフィールドのversionには今の所0x03か0x13か0x33が、sequenceにはシーケンス番号が入る。本当はversionのところは下位4bitsがバージョン、上位4bitsがver.3以降で有効なフラグになっていて、bit4が映像信号認識、bit5が1なら縦長画面、bit5が0なら横長画面になるので映像信号が認識されているなら0x33が縦長、0x13が横長画面ということになる。

ちなみにファームウェアが古いと**ランダムな向きからの絶対角**という非常に使いにくいデータが出たりする。

### /dev/usb/hiddev

よくわからない。資料も見当たらない。

### /dev/ttyACM

設定用のコマンドラインになってる。コマンドは一部を除いて非公開。窓環境があるなら設定ツールで何か設定すると表示される。方位補正するかとか、画面の点灯割合(10%-90%)、リフレッシュレート(60Hz、240Hz)などを設定できるようだ(60Hzの時の点灯割合が逆っぽいが)。

点灯割合はいわゆる「ハーフライト」とか「ゼロライト」とかいう奴で、網膜の、変化に対する感度が高いという性質を使って網膜の色素の消費を抑えることで残像を軽減する技術のようだ。

が、個人的には残像の抑制は動き認識を妨げると考えているので余計なお世話だと感じている。その上60Hzで点灯割合を設定してもチラつくだけでいいことなんてないし、240Hzでも点灯割合を小さくすると色が汚く(薄く？)なるように思える。

とりあえず今わかっているコマンドは以下の通り(#もコマンドの一部、点灯割合は端折った)。改行コードはCRのみのようだ。

|内容|コマンド|
|:--|:--|
|バージョン取得|#?v|
|普通の画面／side-by-side(トグル)|#f1s|
|60Hz,10%|#sp03C0A|
|60Hz,50%|#sp03C32|
|60Hz,90%|#sp03C5a|
|240Hz,18%|#sp0f012|
|240Hz,50%|#sp0f032|
|Rotation vector(方位補正あり)|#sg0|
|Game rotation vector(方位補正なし)|#sg1|

リフレッシュレートと点灯割合は見るからに「#sp0XXYY」でXXがリフレッシュレート、YYが点灯割合で、それもXXには60と240が、YYには百分率がそのまま入っている。OSVRは他のハードでもこのコマンド体系を使うつもりなのか...。

# 画面モード

### 縦画面モード／横画面モード

OSVR-HDKの縦画面は1080x1920、横画面は1920x1080だ。画面の縦横は自動で選択されるらしい。ちなみに横長モードのときには表示が1フレーム遅れるそうだ。

### 普通の画面モード／side-by-sideモード

「普通の画面」モードでは入力画像が横方向で半分に縮小されて左右にほぼ同じものが表示される。目の前に画面があるような感じ。「side-by-side」ではFHDの画面パネルに入力された画像がそのまま表示される。

# それから...

### ちょっといいところ

- 色消しレンズっぽい。後述するように相当屈折させているはずだが色収差がほとんどない。欲を言えばOSVR-HDKに限らずグラストロンのように反射光学系だったら良かったのに。あの光学系は真っ平らかつ(反射光学系なので当然だけど)色収差もないという優秀なものだったので。

### ちょっとダメなところ

- 普通は画面から画面モードのリストを取得して選択するわけだが、縦モードのときには横モードの、横モードのときには縦モードのリストが取得できないのでなんか違うことをしていそうだ。
- 画面モードがいろいろ選べるようだけど、縦も横も普通の画面もside-by-sideも全部ひっくるめてVGA〜XGA横(かつ普通の画面モード)とFHD縦(side-by-sideモード)だけあれば十分なのに...。
- 状態を取得できないのにトグルで設定される項目がある。それに設定はttyがあってもいいけどhidrawのioctlで設定できるようにすべき。複数のデバイスファイルを開くのは面倒だ。
- 焦点が近め。光学系が倒立像になっているっぽいが、光学的に遠くに焦点があるのと同じにすればいいだけなのでそんなに屈折させる必要はない(倒立像だと判断したのは視野の外側に反対側の像が見えているのと左右逆に描画する必要があることが理由)。正立像ならもっとコンパクトにするか焦点を遠くにできたはず。近視の人には朗報だろうが(RiftやViveだとメガネを併用しなければならない人が多いようだ)。
- 歪みが陣笠型。中央と周辺で特性が逆になってて補正パラメタを決定しにくい。
- DPMIで画面の電源を切ってても結構熱を持つ。ポジトラ用LEDを消灯する設定が欲しいところ。個人的には外部機器を使うポジトラにはあまり興味がないし。

### バグっぽい挙動

DPMIで画面の電源を切ったとしても電源入れっぱなしはやめたほうが良さそうだ。うちでは暴走した...暴走した結果記憶してくれるはずの画面モードを記憶してくれなくなった(画面のON/OFFのたびにメッセージが出力されるがこれを溜め込んでいる関係でttyのバッファオーバーフローが疑わしい。なので開きっぱなしにして掃除してやればいいのかも知れない)。

### ちなみに

OSVR-HDK1はリフレッシュレートが60Hzなので、60Hzより速いレートを出力させると色々問題のあるAtom系SoC(IntelHD)でも問題なく動く。「色々」ってのは動作が異様に重くなったりなぜか赤い点が大量に混ざったり。