# CloudXR VR Teleoperation Setup Guide

将 Isaac Lab 仿真画面通过 NVIDIA CloudXR 流式传输到 PICO 4 Ultra，实现沉浸式 VR 遥操作。

## 架构

```
Isaac Sim (本地) ──OpenXR──► CloudXR Runtime (Docker)
                                    │
                               WebSocket (port 49100)
                                    │
                           HAProxy WSS Proxy (port 48322)
                                    │
                              PICO 4 Ultra 浏览器
                           https://<IP>:8080 (Web App)
```

## 系统要求

- Ubuntu 22.04 / 24.04
- NVIDIA GPU (RTX 系列)
- NVIDIA Driver 最新版
- Docker + NVIDIA Container Toolkit
- Isaac Lab 本地安装（`~/IsaacLab`）
- PICO 4 Ultra (OS 15.4.4U 或更高)
- CloudXR Early Access 资格（[申请地址](https://developer.nvidia.com/cloudxr-sdk)）
- Node.js 20 LTS

---

## 一、系统环境准备

### 1.1 安装 NVIDIA Container Toolkit

```bash
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
```

### 1.2 配置 Docker（国内镜像 + NVIDIA runtime）

```bash
sudo tee /etc/docker/daemon.json << 'EOF'
{
    "registry-mirrors": [
        "https://docker.1ms.run",
        "https://docker.1panel.live",
        "https://hub.rat.dev"
    ],
    "runtimes": {
        "nvidia": {
            "path": "nvidia-container-runtime",
            "runtimeArgs": []
        }
    }
}
EOF
sudo systemctl restart docker
```

### 1.3 开放防火墙端口

```bash
sudo ufw allow 47998:48000/udp
sudo ufw allow 48005/udp
sudo ufw allow 48008/udp
sudo ufw allow 48012/udp
sudo ufw allow 48010/tcp
sudo ufw allow 49100/tcp
sudo ufw allow 48322/tcp
sudo ufw allow 8080/tcp
```

### 1.4 安装 Node.js 20 LTS（通过 nvm）

```bash
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 20
nvm use 20
```

---

## 二、获取 CloudXR Early Access SDK

CloudXR.js SDK 已包含在本仓库 `deps/cloudxr/` 目录中，无需单独下载。

只需登录 NGC Docker registry 以拉取 CloudXR Runtime 镜像：

```bash
sudo docker login nvcr.io
# Username: $oauthtoken
# Password: <你的 NGC API Key>
```

> NGC API Key 获取：[ngc.nvidia.com](https://ngc.nvidia.com) → 右上角头像 → Setup → Generate API Key
>
> Early Access 申请：[申请地址](https://developer.nvidia.com/cloudxr-sdk) — 申请通过后才能拉取 `cloudxr-runtime-early-access` 镜像。

---

## 三、配置 Isaac Lab Docker

修改 `~/IsaacLab/docker/.env.cloudxr-runtime`：

```ini
CLOUDXR_RUNTIME_BASE_IMAGE_ARG=nvcr.io/nvidia/cloudxr-runtime-early-access
CLOUDXR_RUNTIME_VERSION_ARG=6.0.1-webrtc
ACCEPT_EULA=yes
```

修改 `~/IsaacLab/docker/docker-compose.cloudxr-runtime.patch.yaml`，将 cloudxr-runtime service 的 `ports` 替换为 `network_mode: host`：

```yaml
services:
  cloudxr-runtime:
    image: ${CLOUDXR_RUNTIME_BASE_IMAGE_ARG}:${CLOUDXR_RUNTIME_VERSION_ARG}
    network_mode: host
    # ... 其余保持不变
```

---

## 四、构建 WSS 代理（HAProxy）

```bash
cd deps/cloudxr

# 构建镜像
sudo docker build -t websocket-ssl-proxy -f Dockerfile.wss.proxy .

# 启动代理（监听 48322，转发到 CloudXR Runtime 的 49100）
sudo docker run -d --name wss-proxy \
    --network host \
    -e BACKEND_HOST=localhost \
    -e BACKEND_PORT=49100 \
    -e PROXY_PORT=48322 \
    websocket-ssl-proxy

# 验证
sudo docker logs wss-proxy
```

---

## 五、构建 Web 客户端

```bash
cd deps/cloudxr/isaac

# 安装依赖（需要先安装本地 CloudXR SDK）
npm install ../nvidia-cloudxr-6.0.0-beta.tgz
npm install

# 启动 HTTPS 开发服务器（端口 8080）
sudo sysctl fs.inotify.max_user_watches=524288
npm run dev-server:https
```

> **注意**：首次运行会下载 WebXR controller profile 资源，需要网络连接。

---

## 六、启动流程

每次使用前按以下顺序启动：

### 6.1 启动 CloudXR Runtime + Isaac Lab

```bash
cd ~/IsaacLab
sudo ./docker/container.py start \
    --files docker-compose.cloudxr-runtime.patch.yaml \
            /home/maic/xh/mindbot/deps/cloudxr/docker-compose.mindbot.patch.yaml \
    --env-files .env.cloudxr-runtime

# 进入 Isaac Lab 容器
./docker/container.py enter base
```

容器内运行 MindBot 遥操作：

```bash
/workspace/isaaclab/isaaclab.sh -p scripts/environments/teleoperation/teleop_se3_agent.py \
    --task Isaac-PickPlace-GR1T2-Abs-v0 \
    --teleop_device handtracking \
    --enable_pinocchio
```

Isaac Sim 启动后：**AR 面板 → OpenXR → System OpenXR Runtime → 点击 Start AR**

### 6.2 启动 WSS 代理

```bash
sudo docker start wss-proxy
```

### 6.3 启动 Web 服务

```bash
cd deps/cloudxr/isaac
npm run dev-server:https
```

---

## 七、PICO 4 Ultra 配置

### 7.1 启用手部追踪

设置 → Interaction → 选择 **Auto Switch between Hands & Controllers**

### 7.2 接受证书

1. 打开 PICO 浏览器，访问 `https://<工作站IP>:48322/`
   - 点击 **Advanced** → **Proceed (unsafe)**
   - 看到 "501 Not Implemented" 是正常的
2. 访问 `https://<工作站IP>:8080/`
   - 点击 **Advanced** → **Proceed (unsafe)**
   - Web App 加载成功

### 7.3 连接

1. 在 Web App 中输入工作站 IP
2. 点击 **CONNECT**
3. 点击 **Enter XR** 进入沉浸式模式

---

## 八、验证端口状态

```bash
sudo ss -tlnp | grep -E "49100|48322|8080"
```

预期输出：
```
LISTEN  0.0.0.0:49100   cloudxr-service   # CloudXR Runtime
LISTEN  0.0.0.0:48322   haproxy           # WSS Proxy
LISTEN  0.0.0.0:8080    node              # Web App
```

---

## 常见问题

| 症状 | 原因 | 解决 |
|---|---|---|
| 503 Service Unavailable (48322) | CloudXR Runtime 未运行 | 重启 docker-cloudxr-runtime-1 容器 |
| 501 Not Implemented (48322) | 正常 — HAProxy 只处理 WebSocket | 直接接受证书即可 |
| Stream start failed 0xC0F22202 | HTTPS + ws:// 混合内容 | 必须先接受 48322 代理证书 |
| EMFILE: too many open files | webpack 文件监听超限 | 已通过 `watchOptions.ignored` 修复 |
| Isaac Sim AR 面板看不到 | 未传 --cloudxr 或 --xr flag | 使用 `--cloudxr` 启动脚本 |
