メインコンテンツへスキップ

07. Basis と Mount のプレイグラウンド

デバイスの マウント変換 をインタラクティブに操作し、configure.basis、configure.preset、configure.mount がワイヤー上でどのように連携するかを示します。インタラクティブな操作の焦点を configure コマンドに保つため、水平方向の床を固定しています。

このチュートリアルで学べる内容:

  • 基底の並べ替えの設定("XZY" → Y軸が上にあるアプリケーションフレーム)
  • プリセット(arm_front)の選択と、それが何を設定するのかの理解
  • 回転クォータニオンを用いた、実行時のマウントの上書き(キーボード操作)
  • 排他ルール: mount と preset は、同じ デバイスの設定 ブロック内で共存できません
  • ワンショットの configure 動作:キーを 1 回押すごとに、configure メッセージが 1 つだけ送信されます
  • (C++ Glaze)std::optional を用いた、排他的なフィールドのモデル化

ワークフロー​

  1. 最初のメッセージで、セッションプロファイル、configure.basis: "XZY"、configure.preset: arm_front を送信します。床が固定されているため set_cursor_force の送信を開始します。
  2. 各ティックごとに、カーソルの Y 座標を読み取り、force_y = max(0, (floor_pos - y) * stiffness) を計算して送信します。
  3. ユーザーがマウント回転キーを押すと、pending_configure = true のフラグを立てます。
  4. 次のティックで、configure.mount ブロックを構築します。そのトランスフォームの rotation は単位クォータニオンです(現在のピッチとヨーから、Z、続いて X の順に合成されたもの)。preset は省略します。両者はワイヤ上で排他的だからです。
  5. リセットキー(R)で上書きを解除します。次の configure では、再び preset が使用されます。

パラメータ​

名前既定値用途
BASIS"XZY"軸の並べ替え— Y軸が上にあるアプリケーションフレーム
DEVICE_PRESET / DEVICE_CONFIG_PRESET"arm_front"名前付きプリセット — 原点はデバイスのベース
FLOOR_POS_Y0.0 m固定された床の平面(アプリケーションの Y 軸)
STIFFNESS1000 単位なし床のばね定数
MOUNT_STEP_DEG10°キーを押すたびに回転する量
PRINT_EVERY_MS200テレメトリのサンプリング間隔

操作​

キー動作
W / Sマウントを、デバイスの +X 軸を中心に ±10° 回転(ピッチ)
A / Dマウントを、デバイスの +Z 軸を中心に ±10° 回転(ヨー)
Rマウントをリセット — プリセットに戻します
H操作方法を表示する
Q終了
mount と preset は同時に指定できません

このサービスには、その両方が含まれます デバイスの設定 ブロックを拒否します。ユーザーがマウントを上書きした場合、このチュートリアルでは、それ以降のすべての configure から preset を省略します。R を押すと、次の configure で preset が再び有効になり、mount は削除されます。

入力モデルの違い

C++ のバリアントは、バックグラウンドの stdin スレッドで行単位で入力を読み取ります(文字を入力するたびに ENTER キーを押します)。Python は、メインの非同期ループでリアルタイムにキー入力を取得するため、keyboard パッケージを使用します。ENTER キーを押す必要はありません。キーもコマンドも同じです。

読み取り状態フィールド​

data.inverse3[i].state から読み取ります:

  • cursor_position.y — vec3。床への貫入量の計算に使用します
  • current_cursor_force — テレメトリとして報告されます

送信/受信​

ペイロードの形式は、すべてのバリアントで同じです。注目すべき違いは、各実装が排他的な mount / preset その分岐をどのように構築するか、そして入力スレッドが WebSocket スレッドにどのように通知するかということです。

keyboard パッケージ内でリアルタイムのキー入力をポーリングする、単一の非同期ループです。pending_configure は、キーハンドラによって設定されるグローバルフラグであり、configure ブロックが送信されるたびにクリアされます。

async with websockets.connect(URI) as websocket:
while True:
msg = await websocket.recv()
data = json.loads(msg)

if first_message:
first_message = False
device_id = data["inverse3"][0]["device_id"]
# Handshake: profile + basis + preset
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": device_id,
"configure": build_configure_block(first_handshake=True),
# -> {"basis": {"permutation": "XZY"},
# "preset": {"preset": "arm_front"}}
}],
}
else:
handle_key_inputs() # may set pending_configure = True (classic, not shown)

y = data["inverse3"][0]["state"]["cursor_position"]["y"]
force_y = 0.0 if y > FLOOR_POS_Y else (FLOOR_POS_Y - y) * STIFFNESS

entry = {
"device_id": device_id,
"commands": {"set_cursor_force":
{"vector": {"x": 0.0, "y": force_y, "z": 0.0}}},
}
if pending_configure:
entry["configure"] = build_configure_block(first_handshake=False)
# -> {"mount": {...}} OR {"preset": {...}} (never both)
pending_configure = False

request_msg = {"inverse3": [entry]}

await websocket.send(json.dumps(request_msg))
SDKインストーラーに同梱

チュートリアル 07 は、SDK とともにローカルにもインストールされています。サービスのインストールディレクトリ配下の tutorials/07-haply-inverse-basis-and-mount/ ご確認ください。

出典: Python·C++·C++ Glaze

関連項目: 基底の並べ替え ・ マウントとワークスペース ・ デバイスの設定 ・ 制御コマンド(set_cursor_force) ・ 型(transform) ・ チュートリアル 04(Hello Floor)