ふと思いいたってVIVE Base Stationを作ることになった。
本当は2.0が作りたいけどとりあえず1.0のほうが資料が多かったので、調査と試作にあたっての実物の信号を計測した。
本当はモックを試作したけど動かなかった。
VIVE Base Station 1.0の仕様
この分野には若干の先人がいる
note.com
www.youtube.com
github.com
github.com
この辺から情報を調べた。また、公式の情報は参照していない。
位置特定の仕組み
Base Station 1.0の仕組みは以下の動画がわかりやすい。
あるBase StationからみたX方向の角度を把握する流れは次のようになっている。
- 部屋全体を照らす高輝度LEDを一瞬光らせる(走査開始の信号)
- X or Y方向に等速回転する線レーザー(Line Lensで直線に引き伸ばされたレーザー)で部屋全体を走査する
これをX, Yの順で交互に実施する。
この結果、走査開始の信号を受信してから、レーザー光を受信するまでの時間を計測すれば、X方向/Y方向のBase Stationからの角度がわかる。
そして、2つのBase Stationは対角に設置されているため、2つのBase Stationからの角度が分かれば空間内の位置が特定できるという算段らしい。
X方向だけで見るとこんな感じ。
あるトラッカーの位置を特定するプロセスを整理すると
- Base Station 1からのX方向の角度を走査
- Base Station 1からのY方向の角度を走査
- Base Station 2からのX方向の角度を走査
- Base Station 2からのY方向の角度を走査
という流れになる。
このレーザーはそれぞれ60Hzで回転しており、またXとYは180度ズレて回転している。なので走査周期は120Hzとなる。そのため、物体の位置は30Hzで更新されていることがわかる。
またこの挙動の把握には以下の資料も非常に参考になった。
走査開始信号の仕様
この詳細はこのページに情報がある。この辺になると読めばいいじゃんと思わなくもないが、日本語資料が無く、また分かりにくい点もあったので解説する。
走査開始信号は、立ち上がりが走査レーザーが0度であることを通知する信号である。他方で、光っている長さに3bitの情報が含まれている。
Name | skip | data | axis | length (ticks) | length (µs) |
---|---|---|---|---|---|
j0 | 0 | 0 | 0 | 3000 | 62.5 |
k0 | 0 | 0 | 1 | 3500 | 72.9 |
j1 | 0 | 1 | 0 | 4000 | 83.3 |
k1 | 0 | 1 | 1 | 4500 | 93.8 |
j2 | 1 | 0 | 0 | 5000 | 104 |
k2 | 1 | 0 | 1 | 5500 | 115 |
j3 | 1 | 1 | 0 | 6000 | 125 |
k3 | 1 | 1 | 1 | 6500 | 135 |
このbitについて説明すると
- skip: レーザーが出ているか。0でON、1でOFF
- data: データ転送に使う。0/1の1bit情報
- axis: レーザーの方向。0でX、1でY
skipについて補足すると、一般にBase Station 1.0では2つのBase Stationを利用する。そのため、片方のBase Stationがレーザーを飛ばしているときは、もう片方のBase Stationはレーザーを止めないとどちらのレーザーかわからなくなるためその際に利用されている。
そのためあるBase Stationの出力信号は送信データの内容に依存して
- j0 or j1
- k0 or j1
- j2 or j3
- k2 or k3
の順番で送られることとなる。
データ仕様
ザーッと書いていく。
正直文字だけだとわかりにくいので最後に実際に手元のBase Station 1.0から受信した実データを置いておくので安心してほしい。
図をもっと使ったほうがわかりやすいけど気が向いたら....
OOTX(Omnidirectional Optical Transmitter) Frameの仕様
前章と同じく以下を参照する。
OOTXとは、前章の走査開始信号におけるdata bitで送信される内容である。
構造は以下の通り。
Preamble | Payload Length | S | Payload | CRC32 |
- Preamble(18bit)
- 18bitで0x001
- Preamble以外では、16bitに1bitの間隔でSync Bit(1の値)が挟まる。そのためPreamble以外で17bitの0が続くことがないため、これで先頭を判断する
- Payload Lenght(16bit)
- そのまま、Payload Length
- Base Station 1.0ではBase Station Info Block以外に送信するデータがないため、0x21で固定
- このPayload LengthにはPadding byteを含まない
- S(1bit)
- Sync Bit
- Payload(34bytes+17bits)
- CRC32(2bytes + 2bits)
- PayloadのCRC32
- Padding byteもSync BitもPayload Lengthも含まない
Base Station Info Block
ここを参照した。
0x00 | uint16 | fw_version | Firmware version (bit 15..6), protocol version (bit 5..0) |
0x02 | uint32 | ID | Unique identifier of the base station |
0x06 | float16 | fcal.0.phase | "phase" for rotor 0 |
0x08 | float16 | fcal.1.phase | "phase" for rotor 1 |
0x0A | float16 | fcal.0.tilt | "tilt" for rotor 0 |
0x0C | float16 | fcal.1.tilt | "tilt" for rotor 1 |
0x0E | uint8 | sys.unlock_count | Lowest 8 bits of the rotor desynchronization counter |
0x0F | uint8 | hw_version | Hardware version |
0x10 | float16 | fcal.0.curve | "curve" for rotor 0 |
0x12 | float16 | fcal.1.curve | "curve" for rotor 1 |
0x14 | int8 | accel.dir_x | "orientation vector" |
0x15 | int8 | accel.dir_y | "orientation vector" |
0x16 | int8 | accel.dir_z | "orientation vector" |
0x17 | float16 | fcal.0.gibphase | "gibbous phase" for rotor 0 (normalized angle) |
0x19 | float16 | fcal.1.gibphase | "gibbous phase" for rotor 1 (normalized angle) |
0x1B | float16 | fcal.0.gibmag | "gibbous magnitude" for rotor 0 |
0x1D | float16 | fcal.1.gibmag | "gibbous magnitude" for rotor 1 |
0x1F | uint8 | mode.current | Currently selected mode (default: 0=A, 1=B, 2=C) |
0x20 | uint8 | sys.faults | "fault detect flags" (should be 0) |
- Protocol Version
- 6固定
- ID
- Base Stationの裏に書いてあるシリアルID
- phase/tilt/curve/gibbous phase/gibbous magnitude
- レーザー補正用のパラメータだと思われる
- accel
- 加速度センサー。Base Stationの向きを特定するのに使っていると思われる
- mode
- Base Stationのモード
- fault detect flags
- 何か壊れてたらbitを立てる
実測
今回はArduinoを使って実測した。
実測条件
回路図
コード
Read signals of base station 1.0 · GitHub
BaseStation
- Base Station 1.0
- 机の上に静置した状態で実施
- モードはbで起動
- b/cの2台で使っている環境からbだけ引っぺがしてきたものを使った
セットアップ
データ概要
実データはこのような感じとなった。
多いところで合わせて前後1点ずつの3点、その次は2点、3点と交互に線で区切ったところで分けて処理している。これはArduinoの時間解像度が4usで、このグラフが1usでプロットしているため離散的に飛んでしまうためである。それぞれのデータは10usごとに存在しているため、きれいに集まっている3点の枠と半々くらいに散っている2点の枠がある。
値としてはそれぞれ上からk3, j3...と順に並ぶ。
データの中身を見るために、0-1のみを識別してセルを色塗りした。(緑: 0, 白: 1)
このようにPreambleを見つければあとはデータ復元ができる。
見ていくと、データは上位bitから送信されていることがわかる。(はじめは下位bitから送信していると思っていた。)複数bytesのデータはリトルエンディアンとなっている。
データの妥当性は、CRC32を計算すると確認できる。
実際にダンプしたデータを16進数で書き起こすとこのようになっている。
実測の所感
正直Arduinoの時間解像度が4usだったので、10usを区別はできたが結構ギリギリだった。また、センサーの都合か全体的に信号が遅延して見えた。今回は信号間隔が10usであることを前提に分離している。
結果的にCRC含めてデータの整合性が確認できたので、データ収集できたが、おすすめはできないと思う。
次
次はオシロスコープを買って実際に生成している波形を見て、直したい。