VIVE Ultimate Tracker はお手軽な自己位置推定モジュールとして期待されていますが、2025/12/07現在、あまり情報はまとまっていません。そこで Windows 11で動作させる手順をまとめてみました。Pythonのopenvrライブラリを利用しています。なお Unity には依存していません。
動作の様子 https://x.com/ksasao/status/1997664729884496049
ポイント
- SteamVR は ヘッドマウントディスプレイ(HMD)が存在することを前提としているため、HMDなしでは起動しません。そこで、設定を修正してHMDが無くてもSteamVRを起動するようにします
各種セットアップ
- https://store.steampowered.com/ より Steam をインストールする。
- https://store.steampowered.com/app/250820/SteamVR/ より SteamVR をインストールする。
- https://www.vive.com/jp/vive-hub/download/ より VIVE Hub をインストールする。インストールに失敗する場合には、テザリング回線を使うとなぜかインストールできる。
- ドングルとトラッカーをPCに接続する。これらはUSB-Cのコネクタだが、裏表を区別するようなので、うまくデバイスが認識されない場合には裏表を逆向きに刺す。
- VIVE Hub を起動し Settings > VIVE Ultimate Tracker を選択、 Pair tracker, Tracker setup を行う。これで絶対位置の原点を決めることができる。
- Ultimate Tracker のファームウェアアップデートが走るのでアップデートする(何回か実行した)。
Nullドライバ有効化
VIVE Hub, SteamVR, Steam を閉じて、下記のファイルを編集する
C:\Program Files (x86)\Steam\steamapps\common\SteamVR\drivers\null\resources\settings\default.vrsettings
をエディタで開き、それぞれの項目を以下のように修正
"driver_null": {
"enable": true,
"loadPriority": -999,
"serialNumber": "Null Serial Number",
"modelNumber": "Null Model Number",
SteamVR の設定で HMD を必須にしない
C:\Program Files (x86)\Steam\steamapps\common\SteamVR\resources\settings\default.vrsettings
をエディタで開き、それぞれの項目を以下のように修正
"steamvr" : {
"requireHmd": false,
"forcedDriver": "null",
"activateMultipleDrivers": true,
動作確認
- Steam, SteamVR, VIVE Hub を起動する。HMDのセットアップを実行しようとするが無視でよい
- 適当なフォルダを作成し、下記のコマンドで Python の仮想環境を作り、必要なライブラリをインストールする
python -m venv vive_env
vive_env\Scripts\activate
pip install openvr flask
下記のコードを作成し、実行
import time
import math
import openvr
def pose_to_pos_quat(m):
# m は 3x4 の行列 (OpenVR HmdMatrix34_t)
# 行列:
# [ m[0][0] m[0][1] m[0][2] m[0][3] ]
# [ m[1][0] m[1][1] m[1][2] m[1][3] ]
# [ m[2][0] m[2][1] m[2][2] m[2][3] ]
x = m[0][3]
y = m[1][3]
z = m[2][3]
# 回転成分をクォータニオンに変換
r00, r01, r02 = m[0][0], m[0][1], m[0][2]
r10, r11, r12 = m[1][0], m[1][1], m[1][2]
r20, r21, r22 = m[2][0], m[2][1], m[2][2]
trace = r00 + r11 + r22
if trace > 0:
s = 0.5 / math.sqrt(trace + 1.0)
w = 0.25 / s
qx = (r21 - r12) * s
qy = (r02 - r20) * s
qz = (r10 - r01) * s
elif r00 > r11 and r00 > r22:
s = 2.0 * math.sqrt(1.0 + r00 - r11 - r22)
w = (r21 - r12) / s
qx = 0.25 * s
qy = (r01 + r10) / s
qz = (r02 + r20) / s
elif r11 > r22:
s = 2.0 * math.sqrt(1.0 + r11 - r00 - r22)
w = (r02 - r20) / s
qx = (r01 + r10) / s
qy = 0.25 * s
qz = (r12 + r21) / s
else:
s = 2.0 * math.sqrt(1.0 + r22 - r00 - r11)
w = (r10 - r01) / s
qx = (r02 + r20) / s
qy = (r12 + r21) / s
qz = 0.25 * s
return (x, y, z), (w, qx, qy, qz)
def main():
openvr.init(openvr.VRApplication_Other)
try:
system = openvr.VRSystem()
# 最大デバイス数
max_devices = openvr.k_unMaxTrackedDeviceCount
while True:
# すべてのデバイスのPoseを取得
poses = system.getDeviceToAbsoluteTrackingPose(
openvr.TrackingUniverseStanding,
0, # 予測時間 (0: 現在)
max_devices
)
for device_index in range(max_devices):
pose = poses[device_index]
if not pose.bDeviceIsConnected or not pose.bPoseIsValid:
continue
device_class = system.getTrackedDeviceClass(device_index)
# GenericTracker クラスのみを見る(Vive系トラッカー)
if device_class == openvr.TrackedDeviceClass_GenericTracker:
m = pose.mDeviceToAbsoluteTracking
pos, quat = pose_to_pos_quat(m)
print(
f"idx={device_index} "
f"pos=({pos[0]:.2f}, {pos[1]:.2f}, {pos[2]:.2f}) "
f"quat=({quat[0]:.2f}, {quat[1]:.2f}, {quat[2]:.2f}, {quat[3]:.2f})"
)
time.sleep(0.01)
finally:
openvr.shutdown()
if __name__ == "__main__":
main()
注意点
- SteamVRのバージョンアップなどがあると上記で編集した設定ファイルが上書きされてしまうことがあるので適宜修正する