본문으로 바로 가기

09. WebSocket 워크스페이스 내비게이터

set_transform WebSocket 채널을 통해 명령을 스트리밍함으로써 장치의 워크스페이스 위치를 실시간으로 탐색합니다. 이 튜토리얼은 HTTP를 기반으로 세션과 장치를 탐색하며(튜토리얼 08(이와 정확히 동일한 방식), 이후 매 틱마다 위치 업데이트를 전송하기 위해 WebSocket 연결을 엽니다.

이 표는 두 가지 변환 연산의 차이점을 보여줍니다:

연산튜토리얼일반적인 용도
configure.mount — 영구 마운트 변환08장치의 물리적 장착 방법을 설명하기 위해 한 번(또는 드물게) 설정
set_transform — 영구 워크스페이스 변환09 (본 튜토리얼)역시 영구적이지만, 실시간 워크스페이스 탐색을 위해 매 틱마다 스트리밍할 수 있음

두 명령 모두 HTTP 및 WebSocket 환경에서 사용할 수 있으며, 둘 다 영구적인입니다. 서비스는 마지막으로 전송된 값을 기억합니다. 두 명령의 차이는 목적과 업데이트 빈도에 있습니다. configure.mount는 드문 업데이트(물리적 설정)에 최적화되어 있는 반면, set_transform은 고빈도 스트리밍(장면 탐색)에 최적화되어 있습니다.

런타임 중에 워크스페이스 원점이 변경되는 대상을 추적해야 할 때 set_transform을 사용하십시오. 예를 들어, 손의 움직임을 따라가거나, 절차적 오프셋을 애니메이션으로 구현하거나, 운영자가 가상 작업 공간을 실시간으로 미세 조정할 수 있도록 할 때 사용합니다.

마운트와 워크스페이스는 부모-자식 관계입니다

개별적으로 보면 configure.mount와 set_transform은 같은 효과를 내는 것처럼 보이지만, 두 값은 부모-자녀 관계입니다. 좌표 파이프라인(device → basis → mount → workspace → application)에서 워크스페이스 변환은 마운트 위에 적용됩니다. 전체 파이프라인은 마운트 및 작업 공간를 참조하십시오.

사용 사례​

  • 실시간 워크스페이스 탐색. 햅틱 장면이 중단되지 않은 상태에서 장치의 커서 워크스페이스(엔드 이펙터가 도달할 수 있는 경계 영역)를 이동합니다.
  • 사용자 주도 정렬. 메인 햅틱 앱이 실행되는 동안 두 번째 터미널에서 다른 사용자가 방향키를 눌러 작업 공간을 이동할 수 있도록 합니다. 라이브 세션 중 사용자별 작업대 보정에 유용합니다.
  • 절차적 워크스페이스 모션. 매 틱마다 위치 전용 변환 패치를 전송하여 스크립트(오디오 반응형, 물리 기반 등)를 통해 워크스페이스 위치를 제어합니다.

사전 요구 사항​

튜토리얼 09를 진행하려면 활성 WebSocket 세션이필요합니다. 즉, 이미 실행 중인 햅틱 루프에서 Inverse3을 사용하고 있는 클라이언트여야 합니다. 튜토리얼 09는 해당 세션에 연결한 후 워크스페이스를 실시간으로 이동합니다.

세션을 가장 빠르게 실행하는 방법

Haply Hub를열고 Orb 데모를 실행하십시오. 그러면 바로 대상으로 지정할 수 있는 영구 햅틱 세션이 열립니다:

./09-haply-inverse-ws-remote-control --session co.haply.hub::demo-orb
python 09-haply-inverse-ws-remote-control.py --session "co.haply.hub::demo-orb"

'Orb' 장면은 작업 공간의 원점에 구체를 렌더링합니다. 튜토리얼 09로 작업 공간을 이동하면 장면 전체가 실시간으로 함께 이동하여 즉각적인 시각적 피드백을 제공합니다.

햅틱 루프를 실행하는 다른 튜토리얼(01 – 07)도 모두 정상적으로 작동합니다. 한 터미널에서 해당 튜토리얼을 시작한 다음, 두 번째 터미널에서 튜토리얼 09를 실행하여 세션을 대화형으로 탐색해 보십시오.

사용법​

# Discover session interactively, use first detected device
./09-haply-inverse-ws-remote-control
python 09-haply-inverse-ws-remote-control.py

# Target the Haply Hub Orb demo directly
./09-haply-inverse-ws-remote-control --session co.haply.hub::demo-orb
python 09-haply-inverse-ws-remote-control.py --session "co.haply.hub::demo-orb"

# Target a session directly, specify device
./09-haply-inverse-ws-remote-control --session :my_profile:0 --device A14
python 09-haply-inverse-ws-remote-control.py --session "#42" --device A14

사용 방법​

키를 누르고 있으면 계속 이동합니다. 키에서 손을 떼면 멈춥니다.

키동작
→ / ←+X / −X
↑ / ↓+Y / −Y
Page Up / Page Down+Z / −Z
= / -탐색 속도 증가 / 감소
0작업 공간을 초기 상태로 재설정
H도움말 표시
Esc종료 (Ctrl+C(동작도)

작동 원리​

1단계 — HTTP 세션 및 기기 탐색​

이 튜토리얼은 튜토리얼 08과 동일한 discover_session() / discover_device() 헬퍼를 재사용합니다. GET /sessions(또는 --session~와 함께 사용하는 GET /sessions/<selector>)를 호출한 다음, GET /devices를 호출하거나 --device를 직접 사용합니다. 두 헬퍼는 동일한 SESSION_HELP 텍스트와 함께 동일한 CLI 플래그를 받으며, 튜토리얼 00 또는 08의 선택기는 여기에서도 수정 없이 작동합니다.

2단계 — WebSocket 세션, 첫 메시지 핸드셰이크​

이 튜토리얼은 ws://localhost:10001WebSocket 연결을 엽니다. 처음 수신한 메시지에 대해서만 세션 프로필 등록 정보를 전송합니다:

{
"session": {"configure": {"profile": {"name": "co.haply.inverse.tutorials:ws-remote-control"}}},
"inverse3": [{"device_id": "...", "commands": {"set_transform": {"transform": {"position": {"x": 0, "y": 0, "z": 0}}}}}]
}

이후 틱에서는 session을 생략하고, inverse3 명령어 배열만 전송됩니다.

3단계 — 틱마다 set_transform 실행​

set_transform은 부분 변환을 허용합니다. 즉, 변경하려는 필드만 지정하면 됩니다. 이 튜토리얼에서는 위치만 전송하며, 회전과 스케일은 세션 기본값(설정된 경우)을 따릅니다. configure.mount (명령에서 지정한 값)을 그대로 유지합니다.

{
"inverse3": [{"device_id": "...", "commands": {"set_transform": {"transform": {"position": {"x": 0.02, "y": 0.0, "z": 0.01}}}}}]
}

위치는 틱 단위로 누적됩니다. 약 1 kHz에서 서비스는 매 틱마다 set_transform을 다시 적용합니다(영차 홀드, zero-order hold). 따라서 워크스페이스는 마지막으로 전송된 위치에 그대로 머물러 있습니다.

set_transform과 configure.mount의 차이

두 명령어 모두 세션별로 영구 변환을 저장하지만, 파이프라인에서는 워크스페이스 변환이 마운트됩니다. 위에 놓입니다(마운트 및 작업 공간 (참조). 튜토리얼 09에서는 매 틱마다 set_transform스트리밍 중에는 새로운 WebSocket 프레임이 도착할 때마다 이전에 스트리밍된 워크스페이스 변환이 대체됩니다. 따라서 같은 장치에서 set_transform을 스트리밍하는 다른 클라이언트는 다음 틱에서 덮어쓰입니다. configure.mount는 독립적이며 자체 값을 유지합니다. 실무에서는 관심사별로 채널을 하나씩 선택하십시오. 물리적 설정에는 마운트를, 실시간 탐색에는 set_transform을 사용합니다.

스레딩 모델 (C++)​

이 C++ 튜토리얼에서는 위치 누적기를 공유하는 두 개의 스레드를 사용합니다:

스레드역할
메인 스레드입력 읽기(std::getline(std::cin, ...)), 토큰 파싱, nav_vel[] 및 default_step 갱신
WebSocket 콜백 스레드nav_vel[] * dt 값을 사용하여 nav_pos[]을 추진하며, set_transform을 직렬화하여 전송

std::mutex nav_mutex는 nav_pos[], nav_vel[], default_step을 보호합니다. last_tick 타임스탬프와 first_ws_msg 플래그는 WS 콜백 내에서만 사용되는 지역 변수이므로 뮤텍스가 필요하지 않습니다.

ws.onmessage = [&](const std::string &) {
float pos_snap[3];
{
std::lock_guard<std::mutex> lk(nav_mutex);
for (int i = 0; i < 3; ++i) nav_pos[i] += nav_vel[i] * 0.001f; // mm/frame → m
for (int i = 0; i < 3; ++i) pos_snap[i] = nav_pos[i];
}
// build and send set_transform with pos_snap
};

소스 코드​

SDK 설치 프로그램과 함께 제공됩니다

튜토리얼 09는 SDK와 함께 로컬에 설치됩니다. 서비스 설치 디렉터리 아래의 tutorials/09-haply-inverse-ws-remote-control/에서 확인해 주십시오.

관련 문서: set_transform 명령 · 세션 — 첫 메시지 핸드셰이크 · 마운트 및 작업 공간 · 선택기 · 튜토리얼 08 — HTTP 원격 설정 · 튜토리얼 07 — Basis 및 마운트