From fcbb087438802daa52a01e819ea37eb9396c385e Mon Sep 17 00:00:00 2001 From: henrytherobot Date: Mon, 18 May 2026 15:13:10 +0200 Subject: [PATCH] [port] adapt RealSense container to G1 hardware (D435i FW 05.15.x) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The G1's head camera is a D435i (USB 3.2, FW 05.15.01.55) — same family as the H1's D435 but on a different bus and with older firmware. The existing image opened the device but wait_for_frames timed out indefinitely; this turned out to be a librealsense v2.55.x regression against the D4xx firmware on this robot. Pinning the SDK back to v2.50.0 (the version known to work on the host install at /home/unitree/librealsense) restores frame delivery. While in there, refactor the runtime stage to copy librealsense2.so.* straight from the builder instead of apt-installing it, so the binding ABI always matches the .so version. Also drop the now-unused Intel apt repo + GPG key dance. Frontend stage removed: npm-in-docker can't resolve registry.npmjs.org on this Jetson (docker's default DNS 127.0.0.53 doesn't work from inside containers). Build the React app on the host one-shot via: docker run --rm --dns 8.8.8.8 -v $(pwd)/src/frontend/react:/app \\ -w /app node:22-alpine sh -c 'npm install && npm run build' then `docker compose build` just copies the prebuilt dist/. Tested on the G1: all REST endpoints (rgb, depth, depth/color, capture, intrinsics, pointcloud, measure, snapshot) return 200 with valid payloads at 640x480@30 via USB 3.2. Note: requires videohub_pc4 stopped (it holds /dev/video4): /unitree/sbin/mscli stopservice video_hub_pc4 --- .dockerignore | 3 +++ Dockerfile | 69 ++++++++++++++++++++++----------------------------- 2 files changed, 33 insertions(+), 39 deletions(-) diff --git a/.dockerignore b/.dockerignore index b862d3a..8c124d6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,3 +8,6 @@ tests/ docs/ *.md src/libascam_bridge.so +# Frontend dev artifacts (we copy only dist/ which is built on host) +src/frontend/react/node_modules/ +src/frontend/react/.vite/ diff --git a/Dockerfile b/Dockerfile index 7b1c5bb..06fc323 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,11 @@ # --------------------------------------------------------------------------- FROM ubuntu:22.04 AS librealsense-builder ENV DEBIAN_FRONTEND=noninteractive -ARG RS_VERSION=v2.55.1 +# Pinned to v2.50.0: this is the version proven to work with the D435i +# firmware 05.15.x shipped on the G1 robot. v2.55+ opens the device but +# wait_for_frames times out (likely an interaction between the newer USB +# backend and the older D4xx firmware). +ARG RS_VERSION=v2.50.0 RUN apt-get update && apt-get install -y --no-install-recommends \ git cmake build-essential pkg-config ca-certificates \ @@ -33,56 +37,43 @@ RUN cmake .. \ && make -j$(nproc) \ && make install -# librealsense@v2.55.1 has a buggy install rule that doesn't install the -# main pyrealsense2.so — only pyrsutils.so. We pick the main .so straight -# from the build dir to work around it. -RUN mkdir -p /opt/pyrealsense2 \ - && cp -r /usr/lib/python3/dist-packages/pyrealsense2/. /opt/pyrealsense2/ \ - && cp /src/librealsense/build/Release/pyrealsense2.cpython-310-aarch64-linux-gnu.so /opt/pyrealsense2/ \ - && ls /opt/pyrealsense2/pyrealsense2.cpython-310-aarch64-linux-gnu.so > /dev/null \ - && printf 'from .pyrealsense2 import *\n' > /opt/pyrealsense2/__init__.py \ - && echo "=== /opt/pyrealsense2 contents ===" \ - && ls -la /opt/pyrealsense2 +# Collect both the python bindings and the native runtime .so. We copy them +# from the builder stage rather than apt-installing librealsense2 in the +# runtime stage, so the .so version always matches the bindings. +RUN mkdir -p /opt/pyrealsense2 /opt/librealsense-runtime \ + && cp -r /usr/lib/python3/dist-packages/pyrealsense2/. /opt/pyrealsense2/ 2>/dev/null || true \ + && find /src/librealsense/build -name 'pyrealsense2*.so' -exec cp {} /opt/pyrealsense2/ \; \ + && find /src/librealsense/build -name 'librealsense2.so*' -exec cp -P {} /opt/librealsense-runtime/ \; \ + && ( [ -f /opt/pyrealsense2/__init__.py ] || printf 'from .pyrealsense2 import *\n' > /opt/pyrealsense2/__init__.py ) \ + && echo "=== /opt/pyrealsense2 ===" && ls -la /opt/pyrealsense2 \ + && echo "=== /opt/librealsense-runtime ===" && ls -la /opt/librealsense-runtime # --------------------------------------------------------------------------- -# Stage 2: build React frontend -# --------------------------------------------------------------------------- -FROM node:20-alpine AS frontend-build -WORKDIR /app -COPY src/frontend/react/package*.json ./ -RUN npm ci -COPY src/frontend/react/ . -RUN npm run build - -# --------------------------------------------------------------------------- -# Stage 3: runtime image +# Stage 2: runtime image +# +# Note: the React frontend is built on the host (npm in the Jetson's Docker +# can't reach registry.npmjs.org — systemd-resolved stub at 127.0.0.53 +# doesn't work inside containers). Build with: +# docker run --rm --dns 8.8.8.8 -v $(pwd)/src/frontend/react:/app \ +# -w /app node:22-alpine sh -c 'npm install && npm run build' +# Then `docker compose build` just copies the prebuilt dist/. # --------------------------------------------------------------------------- FROM ubuntu:22.04 AS runtime ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl gnupg \ + ca-certificates \ python3 python3-pip python3-venv \ libusb-1.0-0 libudev1 \ libgl1 libglib2.0-0 libsm6 libxext6 libxrender1 \ usbutils \ && rm -rf /var/lib/apt/lists/* -# Intel RealSense apt repo for the librealsense2 native runtime (pinned to v2.55.1). -# Intel rotated the signing key in 2025: the legacy librealsense.pgp file on -# their server is the old 2018 key (C8B3A55A...), but the current `jammy` -# Packages index is signed by the new key FB0B24895113F120. We fetch the new -# key from Ubuntu's keyserver and dearmor it into the trusted keyring. -RUN mkdir -p /etc/apt/keyrings \ - && curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xFB0B24895113F120" \ - | gpg --dearmor -o /etc/apt/keyrings/librealsense.gpg \ - && echo "deb [signed-by=/etc/apt/keyrings/librealsense.gpg] https://librealsense.intel.com/Debian/apt-repo jammy main" \ - > /etc/apt/sources.list.d/librealsense.list \ - && apt-get update \ - && apt-get install -y --no-install-recommends \ - librealsense2=2.55.1-0~realsense.3337 \ - librealsense2-udev-rules=2.55.1-0~realsense.3337 \ - && rm -rf /var/lib/apt/lists/* +# librealsense native runtime — copied straight from the builder stage so +# the .so version matches the python bindings exactly (see RS_VERSION above +# for why we don't apt-install). +COPY --from=librealsense-builder /opt/librealsense-runtime/ /usr/local/lib/ +RUN ldconfig # Python bindings (built in stage 1). System Python 3.10 looks in # /usr/lib/python3/dist-packages, so install there. @@ -103,7 +94,7 @@ RUN pip3 install --no-cache-dir \ # Application source COPY src/backend/ src/backend/ -COPY --from=frontend-build /app/dist src/frontend/react/dist/ +COPY src/frontend/react/dist/ src/frontend/react/dist/ # Entrypoint COPY scripts/entrypoint.sh /app/entrypoint.sh