# Camera System for MuJoCo G1 Simulator ## Overview The simulator has two cameras defined: ### 1. **`head_camera`** - Robot Ego-View - **Location**: Attached to `torso_link` body - **Position**: `[0.06, 0.0, 0.45]` relative to torso (6cm forward, 45cm up) - **Orientation**: `euler="0 -0.8 -1.57"` (facing forward, slightly tilted down) - **FOV**: 90 degrees - **Purpose**: First-person view from the robot's perspective (like a head-mounted camera) ### 2. **`global_view`** - Third-Person View - **Location**: Fixed in world coordinates - **Position**: `[2.910, -5.040, 3.860]` (behind and above the robot) - **Purpose**: External observer view for visualization ## How Camera Publishing Works The camera system uses a **zero-copy architecture** with three components: ``` ┌─────────────────────┐ │ MuJoCo Simulator │ │ (Main Process) │ │ │ │ 1. Render cameras │ │ 2. Copy to shmem │──┐ └─────────────────────┘ │ │ Shared Memory │ (fast IPC) ┌────▼────────────────┐ │ Image Publisher │ │ (Subprocess) │ │ │ │ 3. Encode images │ │ 4. ZMQ publish │ └─────────┬───────────┘ │ │ TCP (ZMQ) │ port 5555 ▼ ┌─────────────────────┐ │ Your Policy/Client │ │ (Subscribe) │ └─────────────────────┘ ``` ### Key Technologies: - **MuJoCo Renderer**: Captures RGB images from virtual cameras - **Shared Memory (`multiprocessing.shared_memory`)**: Zero-copy transfer between processes - **ZMQ (ZeroMQ)**: Network socket for publishing images (TCP) - **No ROS2 required!** Pure Python multiprocessing ## Usage ### Basic Simulation (No Camera Publishing) ```bash python run_sim.py ``` ### With Camera Publishing ```bash # Publish head camera on default port 5555 python run_sim.py --publish-images # Publish multiple cameras python run_sim.py --publish-images --cameras head_camera global_view # Custom port python run_sim.py --publish-images --camera-port 6000 ``` ### Viewing Camera Streams In a **separate terminal**, run the camera viewer: ```bash # Basic usage (default: localhost:5555) python view_cameras.py # Custom host/port python view_cameras.py --host 192.168.1.100 --port 6000 # Save images to directory python view_cameras.py --save ./camera_recordings # Adjust display rate python view_cameras.py --fps 60 ``` **Keyboard Controls:** - `q`: Quit viewer - `s`: Save snapshot of current frame **Example Workflow:** ```bash # Terminal 1: Start simulator with camera publishing python run_sim.py --publish-images # Terminal 2: View the camera feed python view_cameras.py ``` ### Receiving Images in Your Code ```python import zmq import numpy as np from gr00t_wbc.control.sensor.sensor_server import ImageMessageSchema # Connect to camera publisher context = zmq.Context() socket = context.socket(zmq.SUB) socket.connect("tcp://localhost:5555") socket.setsockopt(zmq.SUBSCRIBE, b"") # Subscribe to all messages while True: # Receive serialized image data data = socket.recv_pyobj() # Decode images if "head_camera" in data: # Decode image (returns numpy array HxWx3 uint8) img = decode_image(data["head_camera"]) # Use image for your policy process_observation(img) ``` ## Camera Configuration Edit `config.yaml` to change camera settings: ```yaml IMAGE_DT: 0.033333 # Publishing rate (30 Hz) ENABLE_OFFSCREEN: false # Enable for camera rendering MP_START_METHOD: "spawn" # Multiprocessing method ``` Or programmatically in `run_sim.py`: ```python camera_configs = { "head_camera": { "height": 480, "width": 640 }, "custom_camera": { "height": 224, "width": 224 } } ``` ## Adding Custom Cameras Edit `assets/g1_29dof_with_hand.xml` or `assets/scene_43dof.xml`: ```xml