Files
VLA/wall_x/serving/README.md
T

264 lines
7.7 KiB
Markdown
Raw Normal View History

# Wall-X Model Serving
This directory contains scripts for serving Wall-X models via a websocket server, allowing remote clients to connect and get action predictions from observations.
## Overview
The serving infrastructure consists of three main components:
1. **WebsocketPolicyServer** (`wall_x/serving/websocket_policy_server.py`): Generic websocket server that can serve any policy implementing the `BasePolicy` interface
2. **WallXPolicy** (`wall_x/serving/policy/wall_x_policy.py`): Policy wrapper that adapts the Wall-X model to the `BasePolicy` interface
3. **launch_serving.py**: Main script for starting the server with various configurations
## Quick Start
### Basic Usage
Serve a model with default LIBERO configuration:
```bash
cd /x2robot_v2/vincent/workspace/opensource
python -m wall_x.serving.launch_serving \
--env libero \
--model-config.model-path /path/to/libero_model_stuff \
--model-config.action-tokenizer-path /path/to/fast/ \
--model-config.train-config-path /path/to/config.yml
```
### Specify Environment
Serve with a specific environment preset:
```bash
# LIBERO (single arm, 7 DOF)
python -m wall_x.serving.launch_serving --env libero
# ALOHA (dual arm, 14 DOF)
python -m wall_x.serving.launch_serving --env aloha
```
### Custom Configuration
Serve with custom model paths and settings:
```bash
python -m wall_x.serving.launch_serving \
--model-config.model-path /path/to/model \
--model-config.action-tokenizer-path /path/to/tokenizer \
--model-config.train-config-path /path/to/train_config.yml \
--model-config.action-dim 7 \
--model-config.state-dim 8 \
--model-config.pred-horizon 32 \
--model-config.camera-key front_view left_wrist_view \
--port 8000
```
## Command Line Arguments
### Basic Arguments
- `--env {libero,aloha}`: Environment mode (default: libero)
- `--port PORT`: Port to serve on (default: 8000)
- `--host HOST`: Host to bind to (default: 0.0.0.0)
- `--default-prompt TEXT`: Default text prompt if not provided in observation
- `--debug`: Enable debug logging
### Model Configuration
All model configuration arguments use the `--model-config.` prefix:
- `--model-config.model-path PATH`: Path to pretrained model checkpoint (required)
- `--model-config.action-tokenizer-path PATH`: Path to action tokenizer (required)
- `--model-config.train-config-path PATH`: Path to train config YAML file (required)
- `--model-config.action-dim INT`: Action space dimension (default: 7)
- `--model-config.state-dim INT`: Robot state dimension (default: 8)
- `--model-config.pred-horizon INT`: Prediction horizon (default: 32)
- `--model-config.device {cuda,cpu}`: Device to run on (default: cuda)
- `--model-config.dtype {bfloat16,float16,float32}`: Model dtype (default: bfloat16)
- `--model-config.predict-mode {fast,diffusion}`: Prediction mode (default: fast)
- `--model-config.camera-key KEY1 KEY2 ...`: Camera keys for observation images
### Camera Keys
The `camera-key` parameter specifies which camera views are expected in the observation dictionary. This is **critical** for proper operation:
- Keys must match between server configuration and client observations
- Order matters: keys are processed in the order specified
- Common keys: `front_view`, `left_wrist_view`, `right_wrist_view`, `face_view`
Example:
```bash
--model-config.camera-key front_view left_wrist_view
```
Client must send observations with matching keys:
```python
obs = {
"front_view": image1, # Must match camera-key[0]
"left_wrist_view": image2, # Must match camera-key[1]
"prompt": "task description",
"state": robot_state,
}
```
## Default Configurations
### LIBERO (Single Arm)
```python
ModelConfig(
model_path="/path/to/model",
action_tokenizer_path="/path/to/action_tokenizer",
train_config_path="/path/to/train_config",
state_dim=8,
action_dim=7,
pred_horizon=32,
device="cuda",
dtype="bfloat16",
predict_mode="fast",
camera_key=["front_view", "left_wrist_view"],
)
```
### ALOHA (Dual Arm)
```python
ModelConfig(
model_path="/path/to/model",
action_tokenizer_path="/path/to/action_tokenizer",
train_config_path="/path/to/train_config",
state_dim=14,
action_dim=14,
pred_horizon=32,
device="cuda",
dtype="bfloat16",
predict_mode="fast",
camera_key=["face_view", "left_wrist_view", "right_wrist_view"],
)
```
## Server Protocol
### Connection Flow
1. Client connects to `ws://host:port`
2. Server sends metadata JSON with policy information
3. Client sends observation (msgpack-encoded)
4. Server responds with action prediction (msgpack-encoded)
5. Repeat steps 3-4 for each inference
### Observation Format
Observations must be a dictionary with camera keys matching server configuration:
```python
obs = {
# Image observations - keys must match server's camera_key configuration
"front_view": np.ndarray, # (H, W, 3) uint8 or float
"left_wrist_view": np.ndarray, # (H, W, 3) uint8 or float
# Required fields
"prompt": str, # Task description
"dataset_names": List[str], # Dataset/robot name, e.g., ["physical-intelligence/libero"]
"state": np.ndarray, # Robot proprioception state (state_dim,)
}
```
**Important**: The image keys (`front_view`, `left_wrist_view`, etc.) must exactly match the `camera_key` parameter configured on the server.
### Action Response Format
Actions are returned as a dictionary:
```python
{
"action": np.ndarray, # Predicted action [pred_horizon, action_dim]
"server_timing": {
"infer_ms": float, # Inference time in milliseconds
"prev_total_ms": float, # Total time for previous request
}
}
```
### Server Metadata
When connecting, the server sends metadata:
```python
{
"action_dim": int, # Action space dimension
"pred_horizon": int, # Number of future actions predicted
"device": str, # Device model runs on
"predict_mode": str, # Prediction mode (fast/diffusion)
"env": str, # Environment name
}
```
### Health Check
HTTP health check endpoint available at:
```
http://host:port/healthz
```
Returns `200 OK` if the server is running.
## Client Example
### Synchronous Python Client
For synchronous usage, see `wall_x/serving/client.py`:
```python
from wall_x.serving.client import WallXClient
# Create and connect
client = WallXClient(uri="ws://localhost:8000")
client.connect_sync()
# Prepare observation
obs = {
"front_view": image1,
"left_wrist_view": image2,
"prompt": "task description",
"state": robot_state,
"dataset_names": ["physical-intelligence/libero"],
}
# Get prediction
response = client.predict_sync(obs)
action = response["action"]
# Close connection
client.close_sync()
```
## Architecture
### WebsocketPolicyServer
Generic websocket server that:
- Handles websocket connections with msgpack serialization
- Tracks inference timing and performance metrics
- Provides health check endpoint
- Handles errors gracefully with proper logging
- Supports concurrent client connections
### WallXPolicy
Policy wrapper that:
- Loads and manages the Wall-X model from pretrained checkpoint
- Processes multi-camera observations
- Handles image preprocessing (smart resize, normalization)
- Manages device placement and dtype conversion
- Provides policy metadata to clients
- Supports both fast tokenizer and diffusion prediction modes
### Image Processing Pipeline
1. **Camera Key Matching**: Extracts images from observation dict using configured camera keys
2. **Format Conversion**: Converts numpy arrays to PIL Images
3. **Smart Resize**: Applies Qwen's smart resize algorithm based on min/max pixels
4. **Vision Token Formatting**: Inserts vision tokens in text prompt
5. **Batch Preparation**: Creates model-ready BatchFeature input