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
사용 방법
- 파이썬
- 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 (직접 사용)을 호출합니다. 두 헬퍼 모두 동일한 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
};
출처
튜토리얼 09는 SDK와 함께 로컬에 설치되어 있습니다. 서비스의 설치 디렉터리 내의 tutorials/09-haply-inverse-ws-remote-control/ 확인해 주십시오.
관련: set_transform 명령어 · 세션 — 첫 번째 메시지를 통한 핸드셰이크 · 마운트와 작업 공간 · 셀렉터 · 튜토리얼 08 — HTTP 원격 설정 · 튜토리얼 07 — 베이스와 마운트