본문으로 건너뛰기

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

WebSocket 채널을 통해 set_transform 명령을 스트리밍함으로써, 장치의작업 공간 위치를 실시간으로 탐색가능합니다. 이 튜토리얼에서는 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 (직접 사용)을 호출합니다. 두 헬퍼 모두 동일한 CLI 플래그와 동일한 SESSION_HELP 텍스트를 입력받기 위해, 튜토리얼 00 또는 08의 선택기를 여기에서도 변경하지 않고 그대로 사용할 수 있습니다.

2단계 — WebSocket 세션 및 첫 번째 메시지의 핸드셰이크​

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

{
"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 을 다시 적용합니다(제로차 홀드). 따라서 작업 공간은 마지막으로 전송된 위치에 그대로 유지됩니다.

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 — 베이스와 마운트