File size: 9,752 Bytes
c26f879
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#!/usr/bin/env bash
# install_pico.sh
# Sets up the gear_sonic_teleop venv for PICO VR teleop on any x86_64 or arm64
# machine (desktop, laptop, or G1 onboard).
#
# Usage:
#   bash install_scripts/install_pico.sh             # full install
#   SKIP_SIM_AND_UNITREE=1 bash install_scripts/install_pico.sh
#                                                    # publisher-only profile
#                                                    # (Thor/Orin used as a
#                                                    # headless Isaac Teleop /
#                                                    # CloudXR ROS publisher
#                                                    # β€” neither sim nor the
#                                                    # unitree DDS bindings
#                                                    # are on that path)
#
# Optional env vars:
#   SKIP_SIM_AND_UNITREE=1   Skip the mujoco sim extra and unitree_sdk2_python.
#                            On aarch64 also skips the CycloneDDS C-lib build.
#   CYCLONEDDS_HOME=<path>   Override the CycloneDDS install prefix on aarch64
#                            (default: ~/cyclonedds/install). Not used on
#                            x86_64 because prebuilt cyclonedds wheels exist.
#
# Run from the repo root.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

# ── 0. Print detected architecture ───────────────────────────────────────────
ARCH="$(uname -m)"
echo "[OK] Architecture: $ARCH"

# ── 1. Ensure uv is installed and available ──────────────────────────────────
if ! command -v uv &>/dev/null; then
    echo "[INFO] uv not found – installing via official installer …"
    curl -LsSf https://astral.sh/uv/install.sh | sh

    # Source the uv env so it's available in this session
    if [ -f "$HOME/.local/bin/env" ]; then
        # shellcheck disable=SC1091
        source "$HOME/.local/bin/env"
    elif [ -f "$HOME/.cargo/env" ]; then
        # shellcheck disable=SC1091
        source "$HOME/.cargo/env"
    else
        export PATH="$HOME/.local/bin:$PATH"
    fi

    # Verify uv is now reachable
    if ! command -v uv &>/dev/null; then
        echo "[ERROR] uv installation succeeded but binary not found on PATH."
        echo "        Please add ~/.local/bin (or ~/.cargo/bin) to your PATH and re-run."
        exit 1
    fi
fi
echo "[OK] uv $(uv --version)"

# ── 2. Install a uv-managed Python 3.10 (includes dev headers / Python.h) ────
echo "[INFO] Installing uv-managed Python 3.10 (includes development headers) …"
uv python install 3.10
MANAGED_PY="$(uv python find --no-project 3.10)"
echo "[OK] Using Python: $MANAGED_PY"

# ── 3. Clean previous venv (if any) ──────────────────────────────────────────
cd "$REPO_ROOT"
echo "[INFO] Removing old .venv_teleop (if present) …"
rm -rf .venv_teleop

# ── 4. Create venv & install teleop extra ─────────────────────────────────────
echo "[INFO] Creating .venv_teleop with uv-managed Python 3.10 …"
uv venv .venv_teleop --python "$MANAGED_PY" --prompt gear_sonic_teleop
# shellcheck disable=SC1091
source .venv_teleop/bin/activate
echo "[INFO] Installing gear_sonic[teleop] …"
uv pip install -e "gear_sonic[teleop]"

# ── 5. Install xrobotoolkit_sdk (CMake-based, not a pip package) ──────────────
echo "[INFO] Installing XRoboToolkit SDK …"
# Install cmake + pybind11 into the venv so the CMake-based build can find them.
# Build with --no-build-isolation so CMake inherits the venv's pybind11.
uv pip install cmake pybind11 setuptools
echo "[OK] cmake $(cmake --version | head -1)"
# Point CMake at pybind11's cmake config so find_package(pybind11) succeeds
export CMAKE_PREFIX_PATH="$(python -m pybind11 --cmakedir)"
echo "[OK] pybind11 cmake dir: $CMAKE_PREFIX_PATH"

# On aarch64 (Jetson Orin), build the PXREARobotSDK native lib from source
# because pre-built aarch64 binaries are not shipped in the repo.
XRT_DIR="$REPO_ROOT/external_dependencies/XRoboToolkit-PC-Service-Pybind_X86_and_ARM64"
if [ "$ARCH" = "aarch64" ] && [ ! -f "$XRT_DIR/lib/aarch64/libPXREARobotSDK.so" ]; then
    echo "[INFO] Building PXREARobotSDK for aarch64 (Jetson Orin) …"
    XRT_TMP="$XRT_DIR/tmp"
    mkdir -p "$XRT_TMP"
    if [ ! -d "$XRT_TMP/XRoboToolkit-PC-Service" ]; then
        git clone -b orin https://github.com/XR-Robotics/XRoboToolkit-PC-Service.git "$XRT_TMP/XRoboToolkit-PC-Service"
    fi
    pushd "$XRT_TMP/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK" > /dev/null
    bash build.sh
    popd > /dev/null
    mkdir -p "$XRT_DIR/lib/aarch64" "$XRT_DIR/include/aarch64"
    cp "$XRT_TMP/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/PXREARobotSDK.h" \
       "$XRT_DIR/include/aarch64/"
    cp -r "$XRT_TMP/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/nlohmann" \
       "$XRT_DIR/include/aarch64/nlohmann/"
    cp "$XRT_TMP/XRoboToolkit-PC-Service/RoboticsService/PXREARobotSDK/build/libPXREARobotSDK.so" \
       "$XRT_DIR/lib/aarch64/"
    rm -rf "$XRT_TMP"
    echo "[OK] PXREARobotSDK aarch64 native library built and installed"
fi

uv pip install --no-build-isolation -e external_dependencies/XRoboToolkit-PC-Service-Pybind_X86_and_ARM64/

# ── 5c. Install isaacteleop[cloudxr] for the in-process CloudXR / DeviceIO path
#       (--input-source isaac-teleop in pico_manager_thread_server.py).
# Hosted on pypi.nvidia.com (public index, no auth). Replaces the legacy
# multi-container path (./scripts/run_cloudxr_via_docker.sh + teleop_ros2_ref).
echo "[INFO] Installing isaacteleop[cloudxr]~=1.3.0 from pypi.nvidia.com …"
uv pip install 'isaacteleop[cloudxr]~=1.3.0' --prerelease=allow \
    --extra-index-url https://pypi.nvidia.com

# Seed ~/cloudxr.env with the device profile CloudXRLauncher negotiates against.
# Skip if the file already exists.
if [ ! -f "$HOME/cloudxr.env" ]; then
    echo "NV_DEVICE_PROFILE=Quest3" > "$HOME/cloudxr.env"
    echo "[OK] Seeded $HOME/cloudxr.env with NV_DEVICE_PROFILE=Quest3"
else
    echo "[OK] $HOME/cloudxr.env already exists (leaving as-is)"
fi

# ── 5b, 6, 7: CycloneDDS C lib (aarch64) + sim extra + unitree_sdk2_python ────
# Skip when:
#   β€’ onboard unitree-provisioned image (aarch64 + user==unitree): the image
#     already ships CycloneDDS, sim has no display, and the on-robot deploy
#     uses the C++ stack directly. Applies to both Orin and Thor onboards.
#   β€’ SKIP_SIM_AND_UNITREE=1 is set explicitly: e.g. a Thor or Orin used as a
#     headless Isaac Teleop / CloudXR streamer β€” neither mujoco nor the
#     unitree DDS bindings are on that path.
if { [ "$ARCH" = "aarch64" ] && [ "$(whoami)" = "unitree" ]; } \
   || [ "${SKIP_SIM_AND_UNITREE:-0}" = "1" ]; then
    echo "[SKIP] Skipping CycloneDDS build, sim extra & unitree_sdk2_python"
else
    # ── 5b. Build CycloneDDS C library on aarch64 (needed by the cyclonedds
    #       Python binding which unitree_sdk2_python depends on).
    # x86_64 hosts get prebuilt cyclonedds wheels and skip this entirely.
    # Pattern follows Unitree's own README for this exact error
    # (https://github.com/unitreerobotics/unitree_sdk2_python#faq):
    # per-user source checkout in $HOME, sibling install/ dir, no sudo.
    if [ "$ARCH" = "aarch64" ]; then
        CDDS_DIR="$HOME/cyclonedds"
        CDDS_PREFIX="${CYCLONEDDS_HOME:-$CDDS_DIR/install}"
        if [ ! -f "$CDDS_PREFIX/lib/libddsc.so" ]; then
            echo "[INFO] Building CycloneDDS releases/0.10.x β†’ $CDDS_PREFIX …"
            # Track the releases/0.10.x maintenance branch (per Unitree's FAQ).
            # The 0.10.2 tag is unpatched 2022 code and trips glibc FORTIFY_SOURCE
            # in dds_create_domain on modern Ubuntu / glibc; the branch has fixes.
            if [ ! -d "$CDDS_DIR/.git" ]; then
                git clone -b releases/0.10.x --depth 1 \
                    https://github.com/eclipse-cyclonedds/cyclonedds.git "$CDDS_DIR"
            fi
            cmake -S "$CDDS_DIR" -B "$CDDS_DIR/build" \
                -DCMAKE_INSTALL_PREFIX="$CDDS_PREFIX" \
                -DBUILD_EXAMPLES=OFF \
                -DBUILD_TESTING=OFF
            cmake --build "$CDDS_DIR/build" -j"$(nproc)"
            cmake --install "$CDDS_DIR/build"
            echo "[OK] CycloneDDS installed at $CDDS_PREFIX"
        else
            echo "[OK] CycloneDDS already present at $CDDS_PREFIX (libddsc.so found)"
        fi
        export CYCLONEDDS_HOME="$CDDS_PREFIX"
    fi

    # ── 6. Install sim extra (for run_sim_loop.py / sim2sim testing)
    echo "[INFO] Installing sim extra …"
    uv pip install -e "gear_sonic[sim]"

    # ── 7. Install unitree_sdk2_python (needed by the sim2sim bridge)
    echo "[INFO] Installing unitree_sdk2_python …"
    uv pip install -e external_dependencies/unitree_sdk2_python
fi

echo ""
echo "══════════════════════════════════════════════════════════════"
echo "  Setup complete!  Activate the venv with:"
echo ""
echo "    source .venv_teleop/bin/activate"
echo ""
echo "  You should see (gear_sonic_teleop) in your prompt."
echo "══════════════════════════════════════════════════════════════"