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:
commit
c167c836e6
15 files changed
+3168
No files matched your search
@@ -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
@@ -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
|
||||||
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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
@@ -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 "=============================================================="
|
||||||
Executable
+82
@@ -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"
|
||||||
Executable
+29
@@ -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"
|
||||||
Executable
+57
@@ -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}"
|
||||||
Executable
+32
@@ -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"
|
||||||
@@ -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
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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"
|
||||||
Reference in new issue
Block a user