2026-08-04 16:09:17 +04:00

71 lines
3.9 KiB
Docker

# Inspire hand service + web dashboard, self-contained (aarch64 / Jetson).
# Build context is assembled by build_image.sh (pulls unitree_sdk2 + CycloneDDS
# from the robot). Run with: --network host --privileged -v /dev:/dev
FROM arm64v8/ubuntu:20.04
ENV DEBIAN_FRONTEND=noninteractive TZ=UTC
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake \
libboost-all-dev libspdlog-dev libeigen3-dev \
python3 python3-flask \
iproute2 procps ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# CycloneDDS (ddscxx + iceoryx) headers & runtime libs from the robot's /usr/local
COPY deps/usr_local_include/ /usr/local/include/
COPY deps/usr_local_lib/ /usr/local/lib/
RUN ldconfig
# unitree_sdk2 headers + prebuilt static lib (for build.sh linking)
COPY deps/unitree_sdk2/ /opt/unitree_sdk2/
# Record/replay panel: the recorder is a python child process that speaks DDS itself, so
# the image needs the bindings as well as the script. Skipped cleanly when build_image.sh
# could not find the sources (empty dirs) — the panel then reports "recorder not found".
# NOTE: this layer pip-installs, so the robot needs network access at BUILD time. Offline
# alternative: copy the robot's own site-packages in the same way the C libs are copied.
COPY deps/recorder/ /opt/recorder/
COPY deps/unitree_sdk2_python/ /opt/unitree_sdk2_python/
# cyclonedds is pinned to what unitree_sdk2py demands (0.10.2) — installing anything else
# just gets downgraded when the SDK lands. It is built from sdist here, so it compiles
# against the robot's own CycloneDDS headers (0.10.5) and links its libddsc.
ARG CYCLONEDDS_PY=0.10.2
ENV CYCLONEDDS_HOME=/usr/local
# The SDK is COPIED into dist-packages rather than pip-installed, and both alternatives
# were tried on the robot first:
# * a wheel install loses four subpackages — b2, comm, g1, h1 ship with no __init__.py,
# so find_packages() drops them and the package's own
# `from . import idl, utils, core, rpc, go2, b2` dies with "cannot import name 'b2'";
# * `pip install -e` reports success (runs setup.py develop) but never lands on sys.path
# in this image — the very next line still gets "No module named 'unitree_sdk2py'".
# A copy is deterministic and keeps every subdirectory, including the ones that resolve as
# implicit namespace packages. numpy comes from apt; opencv-python (its other declared
# dependency, ~100MB+) is skipped because nothing in the SDK imports cv2 — verified.
# The destination is derived from the freshly-installed cyclonedds rather than from
# sysconfig: on Debian/Ubuntu sysconfig's purelib is /usr/lib/python3.8/site-packages,
# which is not even on sys.path (it is dist-packages here), so the copy landed nowhere.
RUN if [ -d /opt/unitree_sdk2_python/unitree_sdk2py ]; then \
apt-get update && apt-get install -y --no-install-recommends \
python3-pip python3-dev python3-numpy && \
pip3 install --no-cache-dir "cyclonedds==${CYCLONEDDS_PY}" && \
cp -r /opt/unitree_sdk2_python/unitree_sdk2py \
"$(python3 -c 'import cyclonedds,os;print(os.path.dirname(os.path.dirname(cyclonedds.__file__)))')/" && \
python3 -c "import unitree_sdk2py, cyclonedds; print('python DDS ok')" && \
rm -rf /var/lib/apt/lists/* ; \
else echo "no unitree_sdk2_python in context — record/replay disabled"; fi
ENV RECORDER_DIR=/opt/recorder RECORDER_PY=python3
# the service sources (everything except web/), then build the binaries in-image
COPY app/ /opt/hand/
WORKDIR /opt/hand
RUN chmod +x build.sh docker/start.sh manage.sh 2>/dev/null; \
UNITREE_SDK2_DIR=/opt/unitree_sdk2 ./build.sh
# The dashboard goes in LAST, on purpose: it is the part that changes every other day, and
# from here a redeploy is a seconds-long layer copy instead of a full C++ recompile.
COPY app_web/ /opt/hand/web/
ENV HAND_REPO=/opt/hand
EXPOSE 8088
CMD ["/opt/hand/docker/start.sh"]