Portable Ithaca capture node: one install for every Jetson

Extract the install that was applied by hand to the Jetson Nano into a single
checkout that builds itself for whatever Jetson it lands on.

The two nodes on the rig share no JetPack — tegra210 caps at 4, tegra234 needs
5+ — so nothing binary is portable between them. The source and the procedure
are: install.sh detects the platform (L4T / JetPack / SoC), installs deps,
builds the OrbbecSDK and the preview server from source locally, and generates
a per-user config and systemd unit. The tested bridge.py ships verbatim; all
machine-specific paths live in the generated config, so it stays unmodified —
its one hardcoded path is made home-relative.

The retired GStreamer/RTSP target is dropped: the direct route encodes no video.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
AngePierreandClaude Opus 5 committed 2026-09-10 15:57:15 +02:00
commit c167c836e6
15 files changed
+3168

No files matched your search

+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# Makes sure an OrbbecSDK v2 build exists for THIS platform, and prints where it
# is as OB_SDK_ROOT=... / OB_SDK_BUILD=... / OB_SDK_GENERATED=... for build_server.sh.
#
# Never ships a prebuilt binary between Jetsons: CUDA and glibc differ by
# generation, so the SDK is built from source on the machine that will run it.
# The heavy artifact stays out of git.
#
# Order of preference:
# 1. An existing build pointed to by $OB_SDK_ROOT — reused as-is. This is what
# keeps a working node (the Nano already has one) untouched.
# 2. A build at one of the known locations, likewise reused.
# 3. A fresh clone + build. On a new JetPack this is the step to watch; if it
# fails, build the SDK by hand once and re-run install.sh with OB_SDK_ROOT set.
set -euo pipefail
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
eval "$("$here/detect_platform.sh")"
# The official v2 source. Pinned loosely: a caller can override both to match a
# tag known to build on their JetPack.
SDK_REPO="${OB_SDK_REPO:-https://github.com/orbbec/OrbbecSDK_v2.git}"
SDK_REF="${OB_SDK_REF:-main}"
find_build() {
local root="$1"
[[ -f "$root/include/libobsensor/ObSensor.hpp" ]] || return 1
local b
b=$(ls -d "$root"/build/linux_* 2>/dev/null | head -1)
[[ -z "$b" ]] && b="$root/build"
[[ -f "$b/lib/libOrbbecSDK.so" ]] || return 1
echo "$b"
}
emit() {
local root="$1" build="$2"
echo "OB_SDK_ROOT=$root"
echo "OB_SDK_BUILD=$build"
echo "OB_SDK_GENERATED=$root/build/src/generated"
}
# 1 & 2: reuse whatever is already built.
for candidate in "${OB_SDK_ROOT:-}" \
"$HOME/OrbbecSDK_v2" \
"$HOME/OrbbecSDK_v2_jetson"; do
[[ -z "$candidate" ]] && continue
if b=$(find_build "$candidate" 2>/dev/null); then
echo ">> SDK deja bati, reutilise: $candidate" >&2
emit "$candidate" "$b"
exit 0
fi
done
# 3: clone + build from source.
root="${OB_SDK_ROOT:-$HOME/OrbbecSDK_v2}"
echo ">> Aucun SDK bati — clone et compilation (JetPack ${JETPACK}, ${SOC})" >&2
echo ">> ${SDK_REPO} @ ${SDK_REF} -> ${root}" >&2
if [[ ! -d "$root/.git" ]]; then
git clone --depth 1 --branch "$SDK_REF" "$SDK_REPO" "$root" >&2
fi
build="$root/build/linux_$(uname -m)"
mkdir -p "$build"
(
cd "$build"
# -DOB_BUILD_EXAMPLES=ON gives the k4a recorder sample used as recorder_bin.
cmake ../.. -DCMAKE_BUILD_TYPE=Release -DOB_BUILD_EXAMPLES=ON >&2
make -j"$(nproc)" >&2
)
if ! b=$(find_build "$root" 2>/dev/null); then
cat >&2 <<EOF
!! La compilation du SDK ne s'est pas terminee comme attendu.
!! Bâtis-le une fois a la main, puis relance install.sh en pointant dessus :
!! OB_SDK_ROOT=<chemin> ./install.sh
EOF
exit 1
fi
echo ">> SDK bati: $b" >&2
emit "$root" "$b"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Builds ithaca_rgbd_server against the SDK build_sdk.sh located. Prints nothing
# but the binary path on success.
set -euo pipefail
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
repo=$(cd "$here/.." && pwd)
# SDK locations come from build_sdk.sh (or the environment, when a caller already
# knows them).
if [[ -z "${OB_SDK_ROOT:-}" || -z "${OB_SDK_BUILD:-}" ]]; then
eval "$("$here/build_sdk.sh")"
fi
build="$repo/server/build"
mkdir -p "$build"
(
cd "$build"
cmake "$repo/server" \
-DCMAKE_BUILD_TYPE=Release \
-DOB_SDK_ROOT="$OB_SDK_ROOT" \
-DOB_SDK_BUILD="$OB_SDK_BUILD" \
-DOB_SDK_GENERATED="${OB_SDK_GENERATED:-$OB_SDK_ROOT/build/src/generated}" >&2
make -j"$(nproc)" >&2
)
bin="$build/ithaca_rgbd_server"
[[ -x "$bin" ]] || { echo "build failed: $bin absent" >&2; exit 1; }
echo "$bin"
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# Prints the facts every other script branches on, one KEY=value per line, so a
# caller can `eval "$(detect_platform.sh)"`.
#
# The point of the whole repo is that these differ between Jetsons and nothing is
# assumed: the original Nano is tegra210 on L4T R32 (JetPack 4, Ubuntu 18.04,
# CUDA 10.2), an Orin is tegra234 on R35/R36 (JetPack 5/6). One install procedure,
# values read here, binaries built locally to match.
set -euo pipefail
# L4T release, e.g. "32.7.1" or "36.4.3". Two sources; the file is the reliable
# one on every generation, the package is the cross-check.
l4t=""
if [[ -f /etc/nv_tegra_release ]]; then
# "# R32 (release), REVISION: 7.1, ..." -> 32.7.1
rel=$(sed -n 's/^# R\([0-9]*\).*REVISION: \([0-9.]*\).*/\1.\2/p' /etc/nv_tegra_release)
l4t="$rel"
fi
if [[ -z "$l4t" ]] && command -v dpkg-query >/dev/null 2>&1; then
l4t=$(dpkg-query --showformat='${Version}' -W nvidia-l4t-core 2>/dev/null \
| sed 's/-.*//') || true
fi
major="${l4t%%.*}"
# SoC family, from the device tree — the ground truth for what can run here.
soc="unknown"
if [[ -r /proc/device-tree/compatible ]]; then
comp=$(tr '\0' '\n' < /proc/device-tree/compatible)
case "$comp" in
*tegra210*) soc="tegra210" ;; # Nano, TX1
*tegra186*) soc="tegra186" ;; # TX2
*tegra194*) soc="tegra194" ;; # Xavier
*tegra234*) soc="tegra234" ;; # Orin
*tegra264*) soc="tegra264" ;; # Thor
esac
fi
model="unknown"
[[ -r /proc/device-tree/model ]] && model=$(tr -d '\0' < /proc/device-tree/model)
# JetPack generation follows from the L4T major, and it is what decides toolchain
# and dependency names.
case "$major" in
32) jetpack="4" ;;
35) jetpack="5" ;;
36) jetpack="6" ;;
38) jetpack="7" ;;
*) jetpack="unknown" ;;
esac
echo "L4T=${l4t:-unknown}"
echo "L4T_MAJOR=${major:-unknown}"
echo "JETPACK=${jetpack}"
echo "SOC=${soc}"
echo "ARCH=$(uname -m)"
echo "MODEL=${model}"
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# Installs the build- and run-time packages the node needs. Idempotent: apt is a
# no-op for what is already there.
#
# The set is the same across Jetson generations — only the distro release behind
# it changes (18.04 -> 22.04), and apt resolves the right versions itself. No
# GStreamer: the direct preview route encodes no video.
set -euo pipefail
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
eval "$("$here/detect_platform.sh")"
echo ">> Dependances pour JetPack ${JETPACK} (L4T ${L4T}, ${SOC})"
# python3 is present on every JetPack image; websockets is the only bridge dep and
# is pure Python, so it works identically everywhere. Prefer apt's package, fall
# back to pip when the distro is too old to carry it.
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
build-essential cmake pkg-config \
libusb-1.0-0-dev libudev-dev liblz4-dev libjpeg-turbo8-dev \
python3 python3-pip
if ! python3 -c 'import websockets' 2>/dev/null; then
echo ">> websockets absent — installation via pip"
# 8.1 is what the Nano runs and what bridge.py was written against; anything
# >=8 keeps the same asyncio API surface the bridge uses.
python3 -m pip install --user "websockets>=8,<13" || \
sudo apt-get install -y python3-websockets
fi
echo ">> Dependances OK"