Files
Jetson/README.md
T
AngePierreandClaude Opus 5 23b30ac1e8 Add the MKV recorder so a node can Record, not only preview
The recorder was the gap: it is FTF's own customised SDK example (a 1139-line
K4A MKV writer), not a stock sample, and it lived only in one machine's build
tree. Bring its source into the repo and build it as part of install.

It writes K4A-format MKV — the format the rest of Ithaca reads — so it stays,
and gets its libk4a from the Orbbec K4A wrapper rather than Microsoft's Azure
Kinect SDK, which is set aside. build_k4a_wrapper.sh builds that wrapper;
build_sdk.sh injects the recorder into the SDK examples tree and builds it there
against the wrapper (it depends on the SDK's own cmake targets). Found by rpath
at runtime, so no LD_LIBRARY_PATH.

install.sh now builds wrapper -> SDK+recorder -> preview server, each reusing an
existing build so the Nano is untouched. If the wrapper is unavailable the
install still completes preview-only and says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:21:44 +02:00

5.9 KiB

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 prebuilt libOrbbecSDK.so. Simple.
  • Recorder (recorder/) — FTF's own source, a customised SDK example, so it is injected into the SDK's examples/ tree and built there (it uses the SDK's own cmake targets). It writes K4A-format MKV, and at runtime dlopens libk4a.so / libk4arecord.so — provided by the Orbbec K4A wrapper, not Microsoft's Azure Kinect SDK. Found by rpath, so no LD_LIBRARY_PATH.
  • Orbbec K4A wrapper — Orbbec's libk4a, backed by their own SDK, cloned and built by build_k4a_wrapper.sh. This is what makes Record work while setting the Microsoft SDK aside.

What install.sh does

  1. Detects L4T / JetPack / SoC (scripts/detect_platform.sh).
  2. Installs build and run dependencies (OpenCV for the recorder; no GStreamer — the direct route encodes no video).
  3. Builds (or reuses) the Orbbec K4A wrapper — libk4a for the recorder.
  4. 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.
  5. Builds ithaca_rgbd_server against the SDK.
  6. Generates bridge/config.json for the current user — this is where all the machine-specific paths live, so bridge.py itself stays unmodified.
  7. Installs the udev rules and the usbfs-memory service, and adds the user to the video group.
  8. Generates and starts the ithaca-bridge systemd 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_REF pin the source (default v1.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_REF pin the source (default main). 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 (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.