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
사용 방법
- 파이썬
- C++
키를 누르고 있으면 계속 이동합니다. 키에서 손을 떼면 멈춥니다.
| 키 | 동작 |
|---|---|
→ / ← | +X / −X |
↑ / ↓ | +Y / −Y |
Page Up / Page Down | +Z / −Z |
= / - | 탐색 속도 증가 / 감소 |
0 | 작업 공간을 초기 상태로 재설정 |
H | 도움말 표시 |
Esc | 종료 (Ctrl+C(동작도) |
줄 단위로 입력하는 방식입니다. 명령을 입력한 후 ENTER 키를 누르십시오.
| 명령 | 동작 |
|---|---|
x+[N] / x-[N] | X 속도를 ±N mm/프레임으로 설정 (x+(입력만 하면 현재 기본 속도가 적용됩니다) |
y+[N] / y-[N] | Y 속도를 ±N mm/프레임으로 설정 |
z+[N] / z-[N] | Z 속도를 ±N mm/프레임으로 설정 |
s+[N] / s-[N] | 기본 속도를 ±N mm/프레임만큼 조정 |
stop | 모든 속도를 0으로 설정 |
reset | 속도를 0으로 설정하고 원점으로 복귀 |
h | 도움말 표시 |
종료하려면 Ctrl+C(또는 Ctrl+D / EOF)를 누르십시오.
작동 원리
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
};
소스 코드
튜토리얼 09는 SDK와 함께 로컬에 설치됩니다. 서비스 설치 디렉터리 아래의 tutorials/09-haply-inverse-ws-remote-control/에서 확인해 주십시오.
관련 문서: set_transform 명령 · 세션 — 첫 메시지 핸드셰이크 · 마운트 및 작업 공간 · 선택기 · 튜토리얼 08 — HTTP 원격 설정 · 튜토리얼 07 — Basis 및 마운트