The Orin came with the SDK source at ~/Downloads/OrbbecSDK_v2; add it to the reuse candidates so a clean install finds the build there instead of cloning a second copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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, 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, and a C++ recorder that writes K4A-format MKV takes on Record — the same format the rest of Ithaca reads.
One checkout, one command, on any Jetson:
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
recorder/
depth_color_k4arec_pipeline.cpp the MKV recorder — FTF source, not a stock example
CMakeLists.txt built inside the SDK examples tree, links the K4A wrapper
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_k4a_wrapper.sh reuse or build the Orbbec K4A wrapper (libk4a)
build_sdk.sh reuse or build OrbbecSDK v2 + inject/build the recorder
build_server.sh cmake + make the preview server
The three C++ pieces, and how each is built
- Preview server (
server/) — our source, built standalone against the prebuiltlibOrbbecSDK.so. Simple. - Recorder (
recorder/) — FTF's own source, a customised SDK example, so it is injected into the SDK'sexamples/tree and built there (it uses the SDK's own cmake targets). It writes K4A-format MKV, and at runtime dlopenslibk4a.so/libk4arecord.so— provided by the Orbbec K4A wrapper, not Microsoft's Azure Kinect SDK. Found by rpath, so noLD_LIBRARY_PATH. - Orbbec K4A wrapper — Orbbec's
libk4a, backed by their own SDK, cloned and built bybuild_k4a_wrapper.sh. This is what makes Record work while setting the Microsoft SDK aside.
What install.sh does
- Detects L4T / JetPack / SoC (
scripts/detect_platform.sh). - Installs build and run dependencies (OpenCV for the recorder; no GStreamer — the direct route encodes no video).
- Builds (or reuses) the Orbbec K4A wrapper —
libk4afor the recorder. - Builds (or reuses) the OrbbecSDK, injecting and building the recorder example against the wrapper. A node that already has a good build (the Nano) is left untouched; the recorder is added to it only if missing.
- Builds
ithaca_rgbd_serveragainst the SDK. - Generates
bridge/config.jsonfor the current user — this is where all the machine-specific paths live, sobridge.pyitself stays unmodified. - Installs the udev rules and the
usbfs-memoryservice, and adds the user to thevideogroup. - Generates and starts the
ithaca-bridgesystemd service.
If the K4A wrapper is unavailable, install.sh still completes: the node then does preview only, and says so. Record needs the wrapper.
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 two source builds to watch on a new JetPack
Both scripts reuse an existing build when they find one. On a machine with none they clone and build from source — the steps most likely to need attention on a JetPack they have not been tried on:
- Orbbec K4A wrapper —
OB_K4A_REPO/OB_K4A_REFpin the source (defaultv1.10.4). If it fails, build it by hand once and re-run with its location:K4A_WRAPPER_DIR=/path/to/OrbbecSDK-K4A-Wrapper ./install.sh - OrbbecSDK v2 —
OB_SDK_REPO/OB_SDK_REFpin the source (defaultmain). Likewise:OB_SDK_ROOT=/path/to/OrbbecSDK_v2 ./install.sh
Both can be passed together. Neither is Microsoft's Azure Kinect SDK — the recorder
gets its libk4a from the Orbbec wrapper, which is buildable from source across
platforms.
Operating the node
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 (raworcompare). The preview server re-reads it once a second, so a camera can be turned on or off, and Compare switched, without restarting.