Files
RealSense-D435i-G1/Dockerfile
T
henrytherobot fcbb087438 [port] adapt RealSense container to G1 hardware (D435i FW 05.15.x)
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
2026-05-18 15:13:10 +02:00

109 lines
4.5 KiB
Docker

# Multi-stage build for the RealSense D4xx API server on aarch64 (Jetson)
# and x86_64. The Intel apt repo ships librealsense2 runtime for arm64
# but no pyrealsense2 wheel, so the Python bindings are built from source
# in a dedicated builder stage and copied into the runtime image.
# ---------------------------------------------------------------------------
# Stage 1: build pyrealsense2 (Python bindings) from source
# ---------------------------------------------------------------------------
FROM ubuntu:22.04 AS librealsense-builder
ENV DEBIAN_FRONTEND=noninteractive
# 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 \
libssl-dev libusb-1.0-0-dev libudev-dev \
python3 python3-dev python3-pip \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /src
RUN git clone --depth 1 --branch ${RS_VERSION} https://github.com/IntelRealSense/librealsense.git
WORKDIR /src/librealsense/build
RUN cmake .. \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_EXAMPLES=OFF \
-DBUILD_GRAPHICAL_EXAMPLES=OFF \
-DBUILD_TOOLS=OFF \
-DBUILD_UNIT_TESTS=OFF \
-DBUILD_NETWORK_DEVICE=OFF \
-DBUILD_PYTHON_BINDINGS=ON \
-DPYTHON_EXECUTABLE=/usr/bin/python3 \
-DCMAKE_INSTALL_PREFIX=/opt/install \
&& make -j$(nproc) \
&& make install
# 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: 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 \
python3 python3-pip python3-venv \
libusb-1.0-0 libudev1 \
libgl1 libglib2.0-0 libsm6 libxext6 libxrender1 \
usbutils \
&& 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.
COPY --from=librealsense-builder /opt/pyrealsense2 /usr/lib/python3/dist-packages/pyrealsense2
WORKDIR /app
# Python dependencies — keep this layer cacheable: install before copying source.
RUN pip3 install --no-cache-dir \
"fastapi>=0.115" \
"uvicorn[standard]>=0.34" \
"pydantic>=2.0" \
"pydantic-settings>=2.0" \
"python-dotenv>=1.0" \
"nats-py>=2.9" \
"numpy>=1.24,<2" \
"opencv-python-headless>=4.8"
# Application source
COPY src/backend/ src/backend/
COPY src/frontend/react/dist/ src/frontend/react/dist/
# Entrypoint
COPY scripts/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# Optional .env (compose can also inject via env_file)
COPY .env* ./
EXPOSE 8878
ENTRYPOINT ["/app/entrypoint.sh"]