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

+12
View File
@@ -0,0 +1,12 @@
# This tree runs on Linux (Jetson). Everything checks out with LF, whatever the
# committer's OS — a CRLF shebang (`#!/usr/bin/env bash\r`) is a broken script.
* text=auto eol=lf
*.py text eol=lf
*.sh text eol=lf
*.cpp text eol=lf
*.txt text eol=lf
*.json text eol=lf
*.md text eol=lf
*.in text eol=lf
*.rules text eol=lf
*.service text eol=lf
+16
View File
@@ -0,0 +1,16 @@
# Generated for the local user by install.sh — never committed, it holds this
# machine's paths.
bridge/config.json
# Build outputs.
server/build/
# The runtime state the bridge writes beside itself.
bridge/cam_settings.json
bridge/cam_index.json
bridge/stream_map.txt
bridge/*.log
# Python.
__pycache__/
*.pyc
+104
View File
@@ -0,0 +1,104 @@
# Ithaca capture node
The software a Jetson runs to join an Ithaca RGBD capture rig: a Python bridge that
puts the node on the WebSocket mesh and answers Preview / Record, and a C++ preview
server that streams each camera's colour (the sensor's own JPEG) and depth (its own
16-bit millimetres, LZ4-compressed) straight to the Unity viewer.
One checkout, one command, on any Jetson:
```sh
git clone <this repo> ~/ithaca-node
cd ~/ithaca-node
./install.sh
```
## Why an install script and not a disk image
The rig currently spans two Jetsons with **no JetPack in common**:
| Node | SoC | JetPack | Ubuntu / CUDA |
|------|-----|---------|---------------|
| Jetson Nano (2019) | tegra210 | **4.x only** (EOL) | 18.04 / 10.2 |
| Jetson Orin Nano | tegra234 | **5 or 6** | 22.04 / 12.x |
tegra210 never got a newer L4T and the Orin refuses the old one, so no single OS
image covers both, and no compiled binary is portable between them — CUDA, glibc
and the C++ ABI all differ by generation.
What *is* portable is the **source and the procedure**. `install.sh` detects the
platform and builds the C++ locally against a locally-built OrbbecSDK, so the same
command produces a correct binary on each machine. Adding a future Orin NX or a Thor
needs nothing new here — the script compiles against whatever toolchain it finds.
The bridge is pure Python and already runs identically everywhere; keep it that way
by never giving it a dependency that has to be compiled.
## Layout
```
install.sh one entry point, idempotent
bridge/
bridge.py the node client — shipped verbatim, tested in place
config.template.json @PLACEHOLDERS@ filled per user by install.sh
server/
ithaca_rgbd_server.cpp the preview server
CMakeLists.txt links a prebuilt libOrbbecSDK, no GStreamer
systemd/
ithaca-bridge.service.in templated with the user and repo path
usbfs-memory.service raises usbfs_memory_mb for the camera bandwidth
udev/
99-obsensor-libusb.rules reference copy (the SDK's own installer wins if present)
scripts/
detect_platform.sh L4T / JetPack / SoC, as KEY=value
install_deps.sh apt + websockets
build_sdk.sh reuse or build OrbbecSDK v2 for this platform
build_server.sh cmake + make the preview server
```
## What install.sh does
1. Detects L4T / JetPack / SoC (`scripts/detect_platform.sh`).
2. Installs build and run dependencies (no GStreamer — the direct route encodes no
video).
3. Locates a working OrbbecSDK build, or clones and builds one. A node that already
has a good build (the Nano) is left untouched.
4. Builds `ithaca_rgbd_server` against it.
5. Generates `bridge/config.json` for the current user — this is where all the
machine-specific paths live, so `bridge.py` itself stays unmodified.
6. Installs the udev rules and the `usbfs-memory` service, and adds the user to the
`video` group.
7. Generates and starts the `ithaca-bridge` systemd service.
Re-run it after a `git pull`: it rebuilds, regenerates the unit and config, and
restarts the service. It keeps an existing `config.json` (delete it to regenerate)
and an existing SDK build.
## The SDK is the one thing to watch on a new JetPack
`build_sdk.sh` reuses an existing build when it finds one. On a machine with none it
clones the official OrbbecSDK v2 and builds it — this is the step most likely to
need attention on a JetPack it has not been tried on. If it fails, build the SDK by
hand once and re-run with its location:
```sh
OB_SDK_ROOT=/path/to/OrbbecSDK_v2 ./install.sh
```
Override the source with `OB_SDK_REPO` / `OB_SDK_REF` to pin a tag known to build on
your JetPack.
## Operating the node
```sh
systemctl status ithaca-bridge # is it up
journalctl -u ithaca-bridge -f # live log
```
Runtime state the bridge writes beside itself (git-ignored):
- `config.json` — this machine's config (generated).
- `cam_settings.json` — per-camera settings, kept across restarts.
- `stream_map.txt` — the live composition: line 1 the cameras to stream, line 2 the
alignment (`raw` or `compare`). The preview server re-reads it once a second, so a
camera can be turned on or off, and Compare switched, without restarting.
+1431
View File
File diff suppressed because it is too large. Load diff
+9
View File
@@ -0,0 +1,9 @@
{
"server_ip": null,
"working_directory": "@HOME@/IthacaRecordings",
"recorder_bin": "@SDK_BUILD@/bin/ob_stream_depth_color_k4arec_pipeline",
"stream_server": "@REPO@/server/build/ithaca_rgbd_server",
"sync_mode": "standalone",
"camera_serial": null,
"log_level": "INFO"
}
Executable
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# One command, the same on every Jetson: installs the Ithaca capture node from this
# checkout. Detects the platform, installs dependencies, builds the C++ against a
# locally-built OrbbecSDK, generates a config and a service for the CURRENT user,
# and starts the bridge.
#
# Idempotent: safe to re-run after a git pull. It rebuilds the server, rewrites the
# config and unit, and restarts the service; it does not touch a working SDK build.
#
# Why a script and not a prebuilt image: the two Jetsons here have no common
# JetPack — tegra210 caps at 4, tegra234 needs 5+ — so nothing binary is portable
# between them. The source and this procedure are; the binaries are made locally.
set -euo pipefail
REPO=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
USER_NAME=$(id -un)
HOME_DIR="$HOME"
echo "=============================================================="
echo " Ithaca node install"
eval "$("$REPO/scripts/detect_platform.sh")"
echo " modele : $MODEL"
echo " L4T : $L4T (JetPack $JETPACK, $SOC, $ARCH)"
echo " user : $USER_NAME home: $HOME_DIR"
echo " repo : $REPO"
echo "=============================================================="
if [[ "$ARCH" != "aarch64" ]]; then
echo "!! Cette install vise les Jetson (aarch64). Arch detectee: $ARCH." >&2
echo "!! Continue quand meme dans 3 s (Ctrl-C pour arreter)…" >&2
sleep 3
fi
# ---- 1. dependencies ------------------------------------------------------
"$REPO/scripts/install_deps.sh"
# ---- 2. the SDK, then the server ------------------------------------------
# build_sdk.sh reuses an existing build (leaving a working node untouched) or
# builds one; build_server.sh compiles ithaca_rgbd_server against it.
echo ">> Localisation / compilation de l'OrbbecSDK…"
eval "$("$REPO/scripts/build_sdk.sh")"
echo " OB_SDK_ROOT = $OB_SDK_ROOT"
echo " OB_SDK_BUILD = $OB_SDK_BUILD"
echo ">> Compilation du serveur de preview…"
SERVER_BIN=$("$REPO/scripts/build_server.sh")
echo " $SERVER_BIN"
# ---- 3. config, generated for this user -----------------------------------
# The tested bridge.py ships unchanged; portability lives here, in the config it
# reads. @PLACEHOLDERS@ become real paths for the current user.
CONFIG="$REPO/bridge/config.json"
if [[ -f "$CONFIG" ]]; then
echo ">> config.json existe deja — conserve (supprime-le pour regenerer)."
else
sed -e "s#@HOME@#$HOME_DIR#g" \
-e "s#@REPO@#$REPO#g" \
-e "s#@SDK_BUILD@#$OB_SDK_BUILD#g" \
"$REPO/bridge/config.template.json" > "$CONFIG"
echo ">> config.json genere: $CONFIG"
fi
mkdir -p "$HOME_DIR/IthacaRecordings"
# ---- 4. udev + usbfs (need root, affect the whole machine) ----------------
echo ">> Regles udev (acces camera sans root) + usbfs_memory…"
# Prefer the SDK's own installer when it is there — it is the maintained source of
# the rule set; fall back to the copy shipped here.
if [[ -x "$OB_SDK_ROOT/scripts/env_setup/install_udev_rules.sh" ]]; then
sudo bash "$OB_SDK_ROOT/scripts/env_setup/install_udev_rules.sh" || true
else
sudo install -m 0644 "$REPO/udev/99-obsensor-libusb.rules" \
/etc/udev/rules.d/99-obsensor-libusb.rules
fi
sudo udevadm control --reload-rules && sudo udevadm trigger || true
sudo install -m 0644 "$REPO/systemd/usbfs-memory.service" \
/etc/systemd/system/usbfs-memory.service
# So the camera is reachable without being root right now, not only after a
# reboot: the video group is what the udev rules grant to.
sudo usermod -aG video "$USER_NAME" || true
# ---- 5. the bridge service, generated for this user -----------------------
UNIT=/etc/systemd/system/ithaca-bridge.service
sudo sed -e "s#@REPO@#$REPO#g" -e "s#@USER@#$USER_NAME#g" \
"$REPO/systemd/ithaca-bridge.service.in" | sudo tee "$UNIT" >/dev/null
sudo systemctl daemon-reload
sudo systemctl enable --now usbfs-memory.service
sudo systemctl enable ithaca-bridge.service
sudo systemctl restart ithaca-bridge.service
echo "=============================================================="
echo " Installe. Etat du bridge :"
systemctl is-active ithaca-bridge.service || true
echo
echo " Journal en direct : journalctl -u ithaca-bridge -f"
echo " Si la camera n'est vue qu'apres reconnexion USB, c'est le"
echo " groupe 'video' qui vient d'etre ajoute — deconnecte/reconnecte"
echo " la session, ou rebranche la camera."
echo "=============================================================="
+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"
+93
View File
@@ -0,0 +1,93 @@
# Copyright (c) French Touch Factory. All Rights Reserved.
#
# Builds the one preview server, ithaca_rgbd_server, against an already-built
# OrbbecSDK v2. Deliberately outside the SDK tree: reconfiguring the SDK on an
# older Jetson can break the recorder build that already works, so we link the
# library it produced instead of building inside it.
#
# Portable across Jetson generations by construction: every SDK path comes from a
# cache variable that build_server.sh fills from OB_SDK_ROOT, and nothing here is
# tied to one L4T, one home directory, or one arm64 build folder name.
#
# The retired GStreamer/RTSP target is gone — the direct route encodes no video,
# so GStreamer is not a dependency any more.
cmake_minimum_required(VERSION 3.10)
project(ithaca_node_server CXX)
# Where the SDK source (headers) and its build output (libs, generated headers,
# extensions) live. build_server.sh passes these; the defaults match the layout
# build_sdk.sh produces.
set(OB_SDK_ROOT "$ENV{HOME}/OrbbecSDK_v2" CACHE PATH
"OrbbecSDK v2 source root (headers)")
set(OB_SDK_BUILD "" CACHE PATH
"OrbbecSDK v2 build output (lib/, lib/extensions/)")
set(OB_SDK_GENERATED "" CACHE PATH
"OrbbecSDK generated headers, where Export.h lands")
# The build folder is named per platform (linux_arm64, linux_aarch64, ...), so it
# is discovered rather than assumed when the caller did not pass it.
if(NOT OB_SDK_BUILD)
file(GLOB _ob_build_candidates "${OB_SDK_ROOT}/build/linux_*")
if(_ob_build_candidates)
list(GET _ob_build_candidates 0 OB_SDK_BUILD)
else()
set(OB_SDK_BUILD "${OB_SDK_ROOT}/build")
endif()
endif()
if(NOT OB_SDK_GENERATED)
set(OB_SDK_GENERATED "${OB_SDK_ROOT}/build/src/generated")
endif()
if(NOT EXISTS "${OB_SDK_ROOT}/include/libobsensor/ObSensor.hpp")
message(FATAL_ERROR "OrbbecSDK headers not found under ${OB_SDK_ROOT}/include "
"— set OB_SDK_ROOT or run scripts/build_sdk.sh first.")
endif()
if(NOT EXISTS "${OB_SDK_BUILD}/lib/libOrbbecSDK.so")
message(FATAL_ERROR "libOrbbecSDK.so not found in ${OB_SDK_BUILD}/lib "
"— build the SDK first (scripts/build_sdk.sh).")
endif()
if(NOT EXISTS "${OB_SDK_GENERATED}/Export.h")
message(FATAL_ERROR "Export.h not found in ${OB_SDK_GENERATED} "
"— the SDK is not fully built yet.")
endif()
# Directory-scoped rather than target_link_directories(): that one needs CMake
# >= 3.13 and the oldest supported Jetson (Nano, JetPack 4) ships 3.10.2. Must
# precede add_executable().
link_directories("${OB_SDK_BUILD}/lib")
add_executable(ithaca_rgbd_server ithaca_rgbd_server.cpp)
set_property(TARGET ithaca_rgbd_server PROPERTY CXX_STANDARD 14)
target_compile_options(ithaca_rgbd_server PRIVATE -Wall -Wextra -O2)
# Where the depth engine and filter plugins are loaded from at runtime.
target_compile_definitions(ithaca_rgbd_server PRIVATE
OB_EXTENSIONS_DIR="${OB_SDK_BUILD}/lib/extensions")
# SYSTEM: the SDK's public headers are not warning-clean and this builds -Wall
# -Wextra. ObTypes.h includes Export.h, which the SDK generates into its build
# tree rather than shipping in include/, hence both directories.
target_include_directories(ithaca_rgbd_server SYSTEM PRIVATE
"${OB_SDK_ROOT}/include"
"${OB_SDK_GENERATED}")
# libjpeg by absolute path, not -ljpeg: the SDK ships its own libjpeg.a in the
# directory link_directories() adds above, built without its SIMD objects — the
# linker would pick it first and fail on jsimd_* symbols. Only C2D alignment
# needs an encoder at all.
find_library(SYSTEM_JPEG NAMES jpeg PATHS
/usr/lib/aarch64-linux-gnu /usr/lib/arm-linux-gnueabihf NO_DEFAULT_PATH)
if(NOT SYSTEM_JPEG)
message(FATAL_ERROR "system libjpeg not found — install libjpeg-turbo8-dev.")
endif()
target_link_libraries(ithaca_rgbd_server PRIVATE OrbbecSDK lz4 ${SYSTEM_JPEG} pthread)
# The SDK lives outside any standard search path; without an rpath the binary
# only runs from a shell that already exports LD_LIBRARY_PATH.
set_target_properties(ithaca_rgbd_server PROPERTIES
BUILD_RPATH "${OB_SDK_BUILD}/lib"
INSTALL_RPATH "${OB_SDK_BUILD}/lib")
install(TARGETS ithaca_rgbd_server RUNTIME DESTINATION bin)
File diff suppressed because it is too large. Load diff
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Ithaca WebSocket bridge (Jetson node)
Documentation=file:@REPO@/bridge/bridge.py
After=network.target usbfs-memory.service
Wants=network.target
[Service]
Type=simple
User=@USER@
WorkingDirectory=@REPO@/bridge
ExecStart=/usr/bin/python3 -u @REPO@/bridge/bridge.py @REPO@/bridge/config.json
Restart=always
RestartSec=5
KillSignal=SIGINT
TimeoutStopSec=20
StandardOutput=journal
StandardError=journal
SyslogIdentifier=ithaca-bridge
[Install]
WantedBy=multi-user.target
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Raise usbfs_memory_mb for Orbbec depth cameras
After=sysinit.target
Before=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'echo 1000 > /sys/module/usbcore/parameters/usbfs_memory_mb'
[Install]
WantedBy=multi-user.target
+111
View File
@@ -0,0 +1,111 @@
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0501", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Bootloader Device"
# UVC Modules
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0635", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Femto"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0638", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Femto-w"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0668", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Femto-live"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0636", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Astra+"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0637", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Astra+s"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0536", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Astra+_rgb"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0537", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Astra+s_rgb"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0669", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Femto-mega"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="066b", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Femto Bolt"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0660", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Astra 2"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0670", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 2"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0671", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 2 XL"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0673", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 2 L"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0675", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 2 VL"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0800", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 335"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0801", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 330"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0802", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini dm330"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0803", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 336"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0804", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 335L"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0805", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 330L"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0806", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini dm330L"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0807", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 336L"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="080b", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 335Lg"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="080d", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 336Lg"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="080e", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 335Le"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0810", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 336Le"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0674", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Orbbec Gemini 2 I"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="0701", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Dabai DCL"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2bc5", ATTRS{idProduct}=="069d", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="Astra Pro2"
# OpenNI Modules
SUBSYSTEM=="usb", ATTR{idProduct}=="0401", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra"
SUBSYSTEM=="usb", ATTR{idProduct}=="0402", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_s"
SUBSYSTEM=="usb", ATTR{idProduct}=="0403", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_pro"
SUBSYSTEM=="usb", ATTR{idProduct}=="0404", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_mini"
SUBSYSTEM=="usb", ATTR{idProduct}=="0407", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_mini_s"
SUBSYSTEM=="usb", ATTR{idProduct}=="0601", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_NH_GLST"
SUBSYSTEM=="usb", ATTR{idProduct}=="060b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="deeyea"
SUBSYSTEM=="usb", ATTR{idProduct}=="050b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="deeyea_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="060e", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="petrel"
SUBSYSTEM=="usb", ATTR{idProduct}=="050e", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="petrel_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="060f", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astro_pro_plus"
SUBSYSTEM=="usb", ATTR{idProduct}=="050f", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astro_pro_plus_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0610", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="bus_cl"
SUBSYSTEM=="usb", ATTR{idProduct}=="0603", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="atlas"
SUBSYSTEM=="usb", ATTR{idProduct}=="0510", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="atlas_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0614", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini"
SUBSYSTEM=="usb", ATTR{idProduct}=="0511", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0616", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="u1s"
SUBSYSTEM=="usb", ATTR{idProduct}=="0516", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="u1s_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0617", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="projector"
SUBSYSTEM=="usb", ATTR{idProduct}=="0517", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="projector_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0618", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="butterfly"
SUBSYSTEM=="usb", ATTR{idProduct}=="0518", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="butterfly_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="061b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="pavo"
SUBSYSTEM=="usb", ATTR{idProduct}=="051b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="pavo_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="062b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="petrel_pro"
SUBSYSTEM=="usb", ATTR{idProduct}=="052b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="petrel_pro_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="062c", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="petrel_plus"
SUBSYSTEM=="usb", ATTR{idProduct}=="052c", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="petrel_plus_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="062d", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="pictor"
SUBSYSTEM=="usb", ATTR{idProduct}=="0632", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra+"
SUBSYSTEM=="usb", ATTR{idProduct}=="0532", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra+rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0633", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra+s"
SUBSYSTEM=="usb", ATTR{idProduct}=="0533", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra+s_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0634", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_petal_b"
SUBSYSTEM=="usb", ATTR{idProduct}=="0534", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_petal_b_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0635", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="femto"
SUBSYSTEM=="usb", ATTR{idProduct}=="0636", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="jarvis"
SUBSYSTEM=="usb", ATTR{idProduct}=="0536", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="jarvis_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0637", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="jarvis+s"
SUBSYSTEM=="usb", ATTR{idProduct}=="0537", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="jarvis+s_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0638", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="femto_w"
SUBSYSTEM=="usb", ATTR{idProduct}=="0639", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="argus"
SUBSYSTEM=="usb", ATTR{idProduct}=="0539", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="argus_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="063a", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="u2"
SUBSYSTEM=="usb", ATTR{idProduct}=="0650", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astradepth"
SUBSYSTEM=="usb", ATTR{idProduct}=="0651", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astradepth"
SUBSYSTEM=="usb", ATTR{idProduct}=="0654", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="qi_long_zhu"
SUBSYSTEM=="usb", ATTR{idProduct}=="0554", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="qi_long_zhu_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0655", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_plus"
SUBSYSTEM=="usb", ATTR{idProduct}=="0656", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_mini"
SUBSYSTEM=="usb", ATTR{idProduct}=="0657", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dc1"
SUBSYSTEM=="usb", ATTR{idProduct}=="0557", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dc1_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0658", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_d1"
SUBSYSTEM=="usb", ATTR{idProduct}=="0657", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dc1"
SUBSYSTEM=="usb", ATTR{idProduct}=="0557", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dc1_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="0659", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dcw"
SUBSYSTEM=="usb", ATTR{idProduct}=="0559", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dcw_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="065a", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dw"
SUBSYSTEM=="usb", ATTR{idProduct}=="065b", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_mini_pro"
SUBSYSTEM=="usb", ATTR{idProduct}=="065c", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_e"
SUBSYSTEM=="usb", ATTR{idProduct}=="055c", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_e_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="065d", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_e_lite"
SUBSYSTEM=="usb", ATTR{idProduct}=="065e", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="astra_mini_s_pro"
SUBSYSTEM=="usb", ATTR{idProduct}=="0698", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="j1"
SUBSYSTEM=="usb", ATTR{idProduct}=="069c", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="TB2201"
SUBSYSTEM=="usb", ATTR{idProduct}=="06a0", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dcw2"
SUBSYSTEM=="usb", ATTR{idProduct}=="0561", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dcw2_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="069f", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_dw2"
SUBSYSTEM=="usb", ATTR{idProduct}=="069a", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_max"
SUBSYSTEM=="usb", ATTR{idProduct}=="069e", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_max_pro"
SUBSYSTEM=="usb", ATTR{idProduct}=="0560", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_max_pro_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="06aa", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_gemini_uw"
SUBSYSTEM=="usb", ATTR{idProduct}=="05aa", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="dabai_gemini_uw_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="06a6", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_ew"
SUBSYSTEM=="usb", ATTR{idProduct}=="05a6", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_ew_rgb"
SUBSYSTEM=="usb", ATTR{idProduct}=="06a7", ATTR{idVendor}=="2bc5", MODE:="0666", OWNER:="root", GROUP:="video", SYMLINK+="gemini_ew_lite"