본문으로 바로 가기

06. 통합 예제 (Inverse3 + 무선 VerseGrip)

이 튜토리얼은 두 대의 기기를 사용하는 방법을 다룹니다. 그립을 원하는 방향으로 향하게 한 상태에서 버튼을 누르고 있으면 해당 방향으로 Inverse3 커서가 이동합니다. 커서는 구형 작업 공간 내부로만 이동할 수 있습니다.

학습 내용:

  • 동일한 상태 프레임에서 두 가지 기기 유형 읽기 (inverse3 및 wireless_verse_grip)
  • 쿼터니언그립이 가리키는 방향 추출하기 (로컬 +Y 축하)
  • set_cursor_position을 사용하여 계산된 목표 지점으로 커서를 이동시키기
  • 목표 지점을 안전한 작업 공간 내부로 제한하기 — Minverse는 Inverse3보다 작은 반경을 사용합니다
  • 원점이 도달 범위의 중앙에 오도록 작업 공간 사전 설정 (arm_front_centered) 설정하기

작업 흐름​

  1. 두 기기 검색:
    • C++ 버전은 시작 시 HTTP를 통해 GET /devices를 조회한 후 보정 안내 메시지를 표시하고 ENTER 키 입력을 기다립니다.
    • Python은첫 번째 WebSocket 상태 프레임에서 두 기기 ID를 모두 읽습니다.
  2. 첫 번째 메시지에서 세션 프로필을 등록하고 configure.preset: arm_front_centered를 설정합니다 (일회성 핸드셰이크).
  3. 틱마다 그립의 orientation 및 buttons.{a, b} 상태를 읽습니다.
  4. 이동 버튼이 눌려 있는 동안 그립의 월드 공간 방향(R(q) · ĵ — 회전된 단위(+Y축)을 계산하고, 이를 SPEED로 스케일링하여 목표 위치에 누적합니다.
  5. 목표 지점을 작업 공간 영역 내부로 제한한 후 set_cursor_position을 통해 전송합니다.
  6. (Python에만 해당) 기기의 config.type에 따라 원의 반지름을 조정합니다 — minverse는 0.04 m이고, 그 외에는 0.10 m입니다.

매개변수​

이름기본값용도
SPEED0.01 m/틱버튼을 누르고 있는 동안의 이동 간격
RADIUS_INVERSE30.10 mInverse3 / Inverse3x의 작업 공간 제한 반경
RADIUS_MINVERSE0.04 mMinverse의 작업 공간 제한 반경 (Python에만 해당 — C++은 0.10(로 고정)
PRINT_EVERY_MS200텔레메트리 출력 주기 제한
세션 프로필 이름co.haply.inverse.tutorials:combinedHaply Hub에서 이 시뮬레이션을 식별하는 값
실행 전에 보정하기
  • Inverse3가 자동으로 보정을 수행하도록 두거나 (또는 그립을 잉크웰 위에 올려놓고 LED가 켜진 상태로 바뀌기를 기다리십시오).
  • 그립을 잉크통에서 분리하십시오.
  • A 또는 B 버튼을 누른 상태에서 그립을 회전하십시오. — 커서는 그립이 가리키는 방향으로 이동합니다.

읽기 상태 필드​

틱마다 전달되는 상태 프레임에서 다음 값을 읽습니다:

  • data.inverse3[0].state.cursor_position — vec3
  • data.wireless_verse_grip[0].state.orientation — quaternion
  • data.wireless_verse_grip[0].state.buttons.{a, b, c} — 부울 값
  • (파이썬, 첫 번째 프레임에서만) data.inverse3[0].config.type — Inverse3과 Minverse 반경 중 하나를 선택합니다
  • (파이썬, 첫 번째 프레임에서만) data.inverse3[0].status.calibrated — 값이 false인 경우 사용자에게 알립니다

송신 / 수신​

쿼터니언에서 방향을 구하는 계산(R(q)로 +Y(회전 연산)과 구 내부 제한 처리는 전형적인 선형 대수 연산입니다. 자세한 내용은 소스 파일을 참조하십시오. Inverse API 측에서는 핸드셰이크 및 틱마다 호출되는 set_cursor_position로 구성됩니다.

단일 비동기 루프로 구성됩니다. 파이썬은 첫 번째 상태 프레임에서 두 기기 ID를 읽습니다. 핸드셰이크에서는 첫 번째 set_cursor_position 호출에 프로필과 configure.preset: arm_front_centered를 함께 첨부합니다.

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

if first_message:
first_message = False
inverse3_id = data["inverse3"][0]["device_id"]
grip_id = data["wireless_verse_grip"][0]["device_id"]
radius = get_workspace_radius(data["inverse3"][0].get("config", {}))

# Handshake: profile + preset + first position command
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": inverse3_id,
"configure": {"preset": {"preset": "arm_front_centered"}},
"commands": {"set_cursor_position": {"position": position}},
}],
}
else:
# Per tick: update position from grip pointing direction (classic math, not shown), send
request_msg = {
"inverse3": [{
"device_id": inverse3_id,
"commands": {"set_cursor_position": {"position": position}},
}],
}

await websocket.send(json.dumps(request_msg))
SDK 설치 프로그램에 포함

튜토리얼 06은 SDK와 함께 로컬에도 설치됩니다 — 서비스 설치 디렉터리 아래의 tutorials/06-haply-inverse-combined/ 폴더에서 확인하실 수 있습니다.

출처: Python · C++ · C++ Glaze

관련 항목: 제어 명령 (set_cursor_position) · 타입 (쿼터니언, vec3) · 마운트 및 작업 공간 (사전 설정) · 튜토리얼 03 (무선 VG) · 튜토리얼 05 (위치 제어)