본문으로 건너뛰기

07. Basis와 Mount의 플레이그라운드

장치의 마운트 변환 를 대화형으로 조작하여,configure.basis、configure.preset、configure.mount 와이어상에서 어떻게 연동되는지 보여줍니다. 대화형 조작의 초점을 configure 명령어에 맞추기 위해, 수평 방향의 바닥은 고정되어 있습니다.

이 튜토리얼에서 배울 수 있는 내용:

  • 기저의 정렬설정("XZY" → (Y축이 위쪽에 있는 애플리케이션 프레임)
  • 사전 설정(arm_front)의 선택과, 그것이 무엇을 설정하는지 이해하기
  • 회전 쿼터니언을 이용한 런타임 마운트 덮어쓰기 (키보드 조작)
  • 배타 규칙: mount 그리고 preset 는, 같은 장치 구성 블록 내에서 공존할 수 없습니다
  • 원샷의 configure 동작: 키를 한 번 누를 때마다 configure 메시지가 하나만 전송됩니다.
  • (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) · 형(변환) · 튜토리얼 04 (Hello Floor)