WendyOS Docs
TutorialsPython

Deploy a G1 MuJoCo Simulation

Run the G1 Fruit Ninja simulation with a live browser view, inspect its telemetry, and deploy it to a Wendy device.

Deploy a Unitree G1 simulation and open its live rendered view in your browser. You will run it locally with Docker, check its health and telemetry, reset the scene, and optionally move it to a Wendy device.

The G1 Fruit Ninja example includes the robot model, Python application, and container configuration. MuJoCo simulates fruit trajectories, contacts, gravity, and split fruit. A scripted controller moves the upper body while the robot's base stays supported. This example is simulation only and sends no commands to a physical robot. It does not load a trained policy.

Prerequisites

  • Wendy CLI, Git, and curl on your development machine.
  • Docker running with Docker Compose available.
  • Internet access for the first image build.
  • A browser and an unused local port 8878.
  • Optional: a reachable Linux Wendy device for the remote deployment step. An NVIDIA GPU with working GPU access can use EGL rendering; software rendering is also available.

The local Compose configuration uses OSMesa for software rendering, so a physical robot or NVIDIA GPU is not required.

1. Get the example

Run these commands from the directory where you keep source checkouts:

git clone --depth 1 https://github.com/wendylabsinc/wendyos.git
cd wendyos/Examples/G1FruitNinjaMujoco

Keep this terminal in Examples/G1FruitNinjaMujoco. Check Docker before starting the build:

docker info
docker compose version

If either command fails, start Docker and confirm that Compose is installed before continuing.

The Dockerfile installs MuJoCo and its rendering libraries. compose.yml publishes port 8878 and selects OSMesa. wendy.json supplies GPU access, host networking, and a TCP readiness check for deployment to a Wendy device.

2. Run locally

From the example directory, run:

wendy run --device docker --build-type compose

Wendy builds the image and starts the Compose service. Keep the terminal running. During startup, the application checks whether its renderer can create a frame. Look for these log messages:

[bootstrap] selected backend=osmesa
[fruit-ninja] ready http://0.0.0.0:8878 backend=osmesa

Open the local simulation in your browser. You should see the G1 in a supported pose, fruit moving through the scene, and live hit and rendering statistics. The status badge changes to LIVE after the application reports readiness.

0.0.0.0 in the startup log is the server's listening address. Use localhost:8878 in the browser when running locally.

3. Check health and telemetry

Leave wendy run open. In a second terminal, request the health endpoint:

curl --fail --show-error http://localhost:8878/api/health

The endpoint returns JSON. Once a rendered frame is available, expect these fields among the result:

{
  "ready": true,
  "error": null,
  "simulation": "MuJoCo",
  "renderer": "osmesa",
  "controller": "scripted upper-body strike",
  "policyLoaded": false,
  "simulationOnly": true
}

Read the live state again:

curl --fail --show-error http://localhost:8878/api/status

The response also includes frameSeq, simTimeSeconds, launches, hits, and streamFps. Repeat the request while the simulation runs. The frame sequence and simulation time should advance. The reported frame rate depends on the host and renderer.

If the health endpoint returns HTTP 503, inspect its JSON without --fail:

curl --show-error http://localhost:8878/api/health

The ready and error fields help distinguish startup from a simulation or rendering error.

4. Reset the scene and use cinematic mode

Click RESET LOOP in the browser to restart the scene and its counters. The same operation is available over HTTP:

curl --fail --show-error --request POST http://localhost:8878/api/reset

The response includes "ok": true. The simulation immediately continues, so its counters start changing again after the reset.

Click CINEMATIC to hide the surrounding panels, or open cinematic mode. The live image comes from /stream.mjpg; telemetry comes from /api/status.

5. Deploy to a Wendy device

This step is optional. Return to the first terminal and press Ctrl+C to stop the local run. Stay in Examples/G1FruitNinjaMujoco and discover your target:

wendy discover

Replace gpu-device.local below with the hostname or address of your Linux Wendy device:

SIM_DEVICE=gpu-device.local
wendy device info --device "$SIM_DEVICE"
wendy run --device "$SIM_DEVICE" --build-type docker

The --build-type docker flag selects the Dockerfile for this deployment. The app's gpu entitlement exposes the target GPU, and host networking makes its HTTP server reachable on port 8878. Wendy waits for that port to open and prints a reachable URL. Open that URL in your browser; use the device's address with :8878 if opening it manually.

The renderer tries EGL first when NVIDIA devices are visible. If its EGL check fails, it tries OSMesa. Look for [bootstrap] selected backend=egl if you want to confirm GPU rendering. A running simulation with renderer: "osmesa" is using software rendering.

In a second terminal, set SIM_DEVICE to the same device and check the remote app:

SIM_DEVICE=gpu-device.local
curl --fail --show-error "http://$SIM_DEVICE:8878/api/health"
curl --fail --show-error "http://$SIM_DEVICE:8878/api/status"

Use the same checks from step 3. A listening TCP port proves the server is reachable; ready: true and advancing frames confirm that the simulation is producing output.

Troubleshooting

ResultWhat to check
Docker is unavailableStart Docker, then rerun docker info and docker compose version.
Port 8878 is already in useStop the process using that port. For local runs, you can instead change the published port in compose.yml from 8878:8878 to 8879:8878 and use localhost:8879.
The browser shows a blank imageCheck /api/health, the error field, and renderer output in the running terminal.
No renderer passes startupRead the renderer preflight errors. Use the included Dockerfile so the container has EGL and OSMesa libraries. For local Compose, keep MUJOCO_GL: osmesa.
A remote app uses OSMesaCheck the target's NVIDIA driver and Wendy GPU access if you need EGL. Software rendering is a supported fallback.
The remote page does not openCheck that the hostname resolves, the target is reachable, and TCP port 8878 is allowed on the device's network. Use the address printed by Wendy.
Rendering is slowCheck renderer and streamFps in /api/status. Software rendering speed depends on CPU resources; use a target with working EGL for GPU rendering.

Cleanup

For the local run, press Ctrl+C in the wendy run terminal. Wendy tears down the Compose deployment. You can also remove the local Compose containers and network explicitly from Examples/G1FruitNinjaMujoco:

docker compose -f compose.yml down

This retains the built image for the next run.

For a remote run, press Ctrl+C in its terminal, or stop the app explicitly with the device variable set:

wendy device apps stop g1-fruit-ninja-mujoco --device "$SIM_DEVICE"

Remove the remote app and its image when finished:

wendy device apps remove g1-fruit-ninja-mujoco --device "$SIM_DEVICE" --cleanup

Confirm the removal when prompted. See managing apps for restarting a deployed app and GPU access for the permissions used by this example.

On this page