diff --git a/Dockerfile.g1-sim b/Dockerfile.g1-sim new file mode 100644 index 0000000000..287a2ca937 --- /dev/null +++ b/Dockerfile.g1-sim @@ -0,0 +1,39 @@ +# Headless DimOS Unitree G1 simulation smoke image. +FROM python:3.12-slim + +ENV UV_SYSTEM_PYTHON=1 \ + PYTHONUNBUFFERED=1 \ + DIMOS_INSTALL=0 \ + DIMOS_USE_SYSTEM=1 \ + UV_VERSION=0.8.15 \ + RUN_SECONDS=20 \ + BLUEPRINT=unitree-g1-sim + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + git \ + libegl1 \ + libgl1 \ + libglib2.0-0 \ + libgomp1 \ + libsm6 \ + libx11-6 \ + libxext6 \ + libxrender1 \ + pkg-config \ + portaudio19-dev \ + && rm -rf /var/lib/apt/lists/* \ + && curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh +ENV PATH="/root/.local/bin:${PATH}" + +WORKDIR /app +COPY pyproject.toml setup.py README.md LICENSE MANIFEST.in ./ +COPY dimos ./dimos +COPY scripts/g1_sim_smoke.sh ./scripts/g1_sim_smoke.sh + +RUN uv pip install '.[unitree,sim]' + +CMD ["bash", "scripts/g1_sim_smoke.sh"] diff --git a/cloudbuild.g1-sim.yaml b/cloudbuild.g1-sim.yaml new file mode 100644 index 0000000000..4220c16085 --- /dev/null +++ b/cloudbuild.g1-sim.yaml @@ -0,0 +1,26 @@ +substitutions: + _IMAGE: dimos-g1-sim + _TAG: smoke + +steps: + - name: gcr.io/cloud-builders/docker + args: + - build + - -f + - Dockerfile.g1-sim + - -t + - ${_IMAGE}:${_TAG} + - . + - name: gcr.io/cloud-builders/docker + args: + - run + - --rm + - -e + - RUN_SECONDS=10 + - -e + - BLUEPRINT=unitree-g1-sim + - ${_IMAGE}:${_TAG} + +timeout: 1800s +options: + logging: CLOUD_LOGGING_ONLY diff --git a/docs/platforms/humanoid/g1/sprint-plan.md b/docs/platforms/humanoid/g1/sprint-plan.md new file mode 100644 index 0000000000..f507dccc10 --- /dev/null +++ b/docs/platforms/humanoid/g1/sprint-plan.md @@ -0,0 +1,63 @@ +# Unitree G1 simulation sprint plan + +This plan is for running DimOS/DimensionalOS G1 without physical hardware, then making it easy to move the same blueprint shape onto a real G1 later. + +## Sprint 0 — discovery / runnable baseline + +Goal: prove the repo, CLI, and G1 simulation entrypoints are discoverable without hardware. + +- Use Python 3.12 and install `dimos[unitree,sim]` for simulation. +- Confirm the CLI exposes `unitree-g1-sim`, `unitree-g1-basic-sim`, `unitree-g1-agentic-sim`, and `unitree-g1-nav-sim`. +- Prefer `--viewer none` for headless CI/smoke checks; use Rerun/native viewer for local interactive runs. +- Default smoke command: + +```bash +uv venv --python 3.12 +source .venv/bin/activate +uv pip install 'dimos[unitree,sim]' +dimos --simulation --viewer none run unitree-g1-sim +``` + +## Sprint 1 — local sim loop + +Goal: make the basic humanoid simulation usable for local development. + +- Run `unitree-g1-sim` first; it is the smallest perceptive G1 simulation blueprint. +- If visualizing, use `dimos --simulation --viewer rerun run unitree-g1-sim`. +- Keep `dimos status`, `dimos log`, and `dimos stop` as the standard lifecycle commands for daemon runs. +- If Rerun or the viewer crashes, reduce visual load before changing robot logic. + +## Sprint 2 — navigation / command-center loop + +Goal: validate the navigation stack against the simulated G1. + +- Run the native navigation sim blueprint: + +```bash +dimos --simulation --viewer rerun run unitree-g1-nav-sim +``` + +- `unitree-g1-nav-sim` connects `UnityBridgeModule`, native nav stack, `MovementManager`, and visualization. +- The blueprint rate-limits heavy visualization (`vis_throttle=0.1`) because G1 visualization can overwhelm Rerun. +- Use this sprint for path-planning, waypoints, local planner tuning, and command-center/Rerun checks. + +## Sprint 3 — agentic / cloud-ready harness + +Goal: package a repeatable G1 sim run for agent/MCP work and optional GCP execution. + +- Agentic local command: + +```bash +dimos --simulation --viewer rerun run unitree-g1-agentic-sim --daemon +dimos status +dimos mcp list-tools +dimos log -n 100 +``` + +- For GCP, use the requested project alias/ID explicitly on every command, e.g. `gcloud ... --project YOUR_PROJECT_ID`. If a short name is only a local alias, resolve it before submitting builds. +- A practical cloud target is a Docker image that runs the same smoke command headlessly (`--viewer none`) for CI; interactive graphics/GPU should stay local or on a GPU VM with display forwarding. +- Do not require real robot networking (`ROBOT_IP`, DDS/WebRTC, sport mode) for simulation sprints. + +## Real-G1 boundary + +Simulation sprints do **not** perform real G1 actions. Real hardware requires the G1 doc flow: SSH/network setup, sport mode safety positioning, robot-side DimOS install, and explicit human supervision. diff --git a/scripts/g1_sim_smoke.sh b/scripts/g1_sim_smoke.sh new file mode 100755 index 0000000000..5bbb9231c0 --- /dev/null +++ b/scripts/g1_sim_smoke.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Headless smoke for DimOS Unitree G1 simulation entrypoints. +# Use RUN_SECONDS to limit runtime; set DIMOS_INSTALL=1 to install deps into .venv. +# Set DIMOS_USE_SYSTEM=1 inside containers where dependencies are already installed +# into the system Python. + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +: "${PYTHON_VERSION:=3.12}" +: "${RUN_SECONDS:=20}" +: "${BLUEPRINT:=unitree-g1-sim}" +: "${DIMOS_INSTALL:=0}" +: "${DIMOS_USE_SYSTEM:=0}" + +if [[ "$DIMOS_USE_SYSTEM" != "1" ]]; then + if [[ ! -d .venv ]]; then + uv venv --python "$PYTHON_VERSION" + fi + # shellcheck disable=SC1091 + source .venv/bin/activate + + if [[ "$DIMOS_INSTALL" == "1" ]]; then + uv pip install '.[unitree,sim]' + fi +elif [[ "$DIMOS_INSTALL" == "1" ]]; then + uv pip install --system '.[unitree,sim]' +fi + +python - <<'PY' +from dimos.robot.all_blueprints import all_blueprints +required = { + "unitree-g1-sim", + "unitree-g1-basic-sim", + "unitree-g1-agentic-sim", + "unitree-g1-nav-sim", +} +missing = sorted(required - set(all_blueprints)) +if missing: + raise SystemExit(f"Missing G1 simulation blueprints: {missing}") +print("G1 simulation blueprints OK:", ", ".join(sorted(required))) +PY + +run_limited() { + if command -v timeout >/dev/null 2>&1; then + timeout "$RUN_SECONDS" "$@" + return $? + fi + if command -v gtimeout >/dev/null 2>&1; then + gtimeout "$RUN_SECONDS" "$@" + return $? + fi + python - "$RUN_SECONDS" "$@" <<'PY' +import subprocess +import sys + +seconds = float(sys.argv[1]) +cmd = sys.argv[2:] +try: + raise SystemExit(subprocess.run(cmd, timeout=seconds).returncode) +except subprocess.TimeoutExpired: + raise SystemExit(124) +PY +} + +set +e +run_limited dimos --simulation --viewer none run "$BLUEPRINT" +status=$? +set -e + +if [[ "$status" == "124" ]]; then + echo "G1 simulation smoke reached RUN_SECONDS=${RUN_SECONDS}; treating bounded run as pass." + exit 0 +fi +exit "$status"