3D visionHow-to

Connect a depth camera to Jetson Orin Nano: build from source

Connect a depth camera to Jetson Orin Nano when lsusb finds it but the SDK does not: purge binaries, build librealsense from source, verify with v4l2-ctl.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01Why lsusb lists the camera and the SDK does not
  2. 02What you need
  3. 03Worked example: reading the USB speed with lsusb -t
  4. 04Build librealsense from source with the RSUSB backend
  5. 05Verify frames with v4l2-ctl and realsense-viewer
  6. 06Common mistakes that look like a broken camera
  7. 07If the sensor choice is still open
  8. 08Decision paths
  9. 09Questions

The fix for a depth camera that shows in lsusb on a Jetson Orin Nano while the SDK reports "no device connected": purge the x86 binary packages with sudo apt purge 'librealsense2*', then build librealsense from source with -DFORCE_RSUSB_BACKEND=TRUE and verify frames with v4l2-ctl --stream-mmap. No fresh JetPack flash fixes it, because the Debian and DKMS packages the librealsense SDK publishes target x86 Ubuntu kernels and the ARM kernel JetPack ships is not one of them. The order that works on JetPack 6.x: read the negotiated USB speed, purge the stale binaries, build librealsense from source with the RSUSB (libuvc) backend, bring the camera firmware current, then prove frames with v4l2-ctl and realsense-viewer. This guide gives the commands, the bandwidth arithmetic, and the decision at each branch.

Why lsusb lists the camera and the SDK does not

Three layers sit between the camera and your application, and each fails differently.

The kernel layer is the patched UVC driver that librealsense2-dkms installs. That package builds against a published list of stock Ubuntu LTS kernels, 4.4 through 6.5; JetPack's ARM kernel is not on it, so DKMS finds no matching source and the module never builds. The library layer is the backend librealsense uses to open streams. The V4L2 backend depends on those patched kernel nodes, which is why a stock install sees nothing. The RSUSB backend, built on libuvc, talks to the camera over USB from userspace and needs no kernel module at all — that is the one to build on a Jetson. The device layer is state: old camera firmware and leftover dpkg packages cause the permission and open errors that look like hardware faults.

Models with a built-in IMU benefit most, because the extra motion interfaces give the plain V4L2 route one more thing to strand. If your camera does not even enumerate in lsusb, that is a different failure; the rebuild-the-SDK-cleanly guide for a depth camera not detected on Jetson starts one layer earlier.

Layer What it expects Symptom on a Jetson The fix
Kernel module librealsense2-dkms built into a listed Ubuntu kernel module never builds skip it; the ARM kernel is not on the list
SDK backend V4L2 nodes from the patched module SDK reports no device build with -DFORCE_RSUSB_BACKEND=TRUE
Camera state current firmware, no stale packages permission and open errors purge binaries, update firmware

What you need

  • A Jetson Orin Nano on JetPack 6.x with internet access during the build.
  • The depth camera on a native USB 3 port, with a USB 3-rated cable and no hub.
  • sudo rights on the Jetson.
  • The apt build dependencies from step 2 and the SDK source tree.
  • Current camera firmware; step 7 covers the update, and a PC running the viewer can do it while the Jetson compiles.

Worked example: reading the USB speed with lsusb -t

This check costs one command and tells you whether you also have a physical-link problem. Run lsusb -t: the tree prints the negotiated speed of every bus, 480M for USB 2 high speed and 5000M for USB 3 SuperSpeed.

Take a typical depth-plus-color session as the demand side: depth at 848 × 480 px, 16 bits per pixel, 30 fps, and color at 1280 × 720 px, 16 bits per pixel, 30 fps. Those are illustrative profile sizes; the bus rates below are standards, not estimates.

The rule: stream load in MB/s is width × height × bytes per pixel × fps ÷ 1,000,000, and a bus hands only part of its raw rate to bulk data.

load (MB/s) = width × height × bytes per pixel × fps ÷ 1,000,000

The arithmetic: depth 848 × 480 × 2 B × 30 fps = 24.4 MB/s. Color 1280 × 720 × 2 B × 30 fps = 55.3 MB/s. Together 79.7 MB/s. On the supply side, USB 2 raw is 480 Mbit/s ÷ 8 = 60 MB/s, and protocol overhead leaves roughly 40 MB/s for bulk streams. USB 3 raw is 5 Gbit/s ÷ 8 = 625 MB/s; 8b/10b coding keeps four fifths, 500 MB/s, and real hosts deliver about 400 MB/s.

Result: 79.7 MB/s is roughly twice what a 480M link carries and about a fifth of a 5000M link's practical rate.

Decision: if lsusb -t shows 480M, fix the link before touching software — native USB 3 port, USB 3 cable, no hub — and expect 5000M on the re-test. A camera left at USB 2 still enumerates and may stream reduced profiles, which reads as flaky rather than dead; the numbers above say why. If the tree already shows 5000M and the SDK still finds nothing, the backend is the cause and the build below is the fix. For the port-side and controller-side causes of a silent drop to USB 2, the Jetson USB3 camera fallback troubleshooting guide walks them case by case.

Build librealsense from source with the RSUSB backend

The sequence matters: the old packages conflict with a source build, so they go first.

  1. Purge the binary packages. Run sudo apt purge 'librealsense2*', then sudo apt update. The leftovers are what produce the permission errors reported after source builds.

  2. Install the build dependencies. Run sudo apt install -y build-essential cmake git libusb-1.0-0-dev pkg-config libglfw3-dev libgl1-mesa-dev libpython3-dev python3-pip — the same set the validated Jetson recipes use.

  3. Fetch the source at a current tag. Clone the librealsense repository and check out the newest release tag; the v2.50.0 tarball older recipes pin predates later camera support. From the source root, install the device rules: ./scripts/setup_udev_rules.sh.

  4. Configure with the USB backend. From a new build folder:

    CUDACXX=/usr/local/cuda-11.4/bin/nvcc cmake ../ -DFORCE_RSUSB_BACKEND=TRUE \
      -DBUILD_PYTHON_BINDINGS:bool=true \
      -DBUILD_WITH_CUDA=true
    

    Point CUDACXX at the nvcc your JetPack image actually installed; list /usr/local to see which cuda directory you have. Omit the CUDACXX prefix and -DBUILD_WITH_CUDA=true if you do not want GPU work. Recipes that set -DFORCE_RSUSB_BACKEND=false assume the patched kernel module exists, and on a Jetson it does not — TRUE is the supported choice here.

  5. Build and install. Run make -j6 — the Orin Nano has six cores — then sudo make install and sudo ldconfig.

  6. Connect and open the viewer. Plug the camera into the USB 3 port and start realsense-viewer; the device should appear with its serial number.

  7. Bring the firmware current. If the viewer's device panel offers a firmware update, take it, then unplug and replug the camera. Outdated firmware is a documented connect failure on this path; the camera firmware version mismatch guide covers the version checks.

Verify frames with v4l2-ctl and realsense-viewer

Two independent checks prove the stack, weakest evidence first.

The kernel view: v4l2-ctl --list-devices should print the camera's video nodes. No nodes at all is its own failure class; the USB class check for a camera with no video device explains what the kernel bound and what it skipped.

The stream view: capture a few frames from the depth node with v4l2-ctl -d /dev/video0 --stream-mmap --stream-count=3; the v4l2-ctl raw capture walkthrough shows the full flag set in context.

The SDK view: enable the depth stream in realsense-viewer and expect a steady preview at the profile the bandwidth math allows — the 30 fps pair from the worked example at USB 3.

Decision: the viewer streams at full rate, bring-up is done. Nodes exist but the SDK cannot open the device: stale binaries or a skipped udev step, redo steps 1 and 3. No nodes and no SDK device: re-check the negotiated speed and power before rebuilding anything. If the viewer streams but long runs stall or drop frames, that is the bandwidth-and-driver class, and the ARM depth stream guide picks it up; brown-outs that kill an otherwise working link fall to the USB power budget check.

Common mistakes that look like a broken camera

  • Building with -DFORCE_RSUSB_BACKEND=false because an x86 recipe said so. That flag assumes the patched kernel module no JetPack kernel has.
  • Keeping the dpkg packages next to the source build. The symptom class is permission and open errors, which is why step 1 purges first.
  • Camera firmware left old from the factory or a previous host. Update through the viewer, on the Jetson or a PC.
  • A hub or a charge-only cable. Both leave 480M in lsusb -t, and the worked example's 40 MB/s budget explains the flaky streams that follow.
  • A virtual machine. The SDK's own documentation declines to support installs under VM USB translation; run natively on the Jetson.
  • Reflashing JetPack. A fresh flash reinstalls the same kernel and adds no backend, so the camera stays silent. It feels productive and changes nothing relevant.
  • Hand-patching kernel modules later. Toolchain mismatches surface at link time, and the Orin kernel build link-failure guide covers that stage.

If the sensor choice is still open

Everything above applies when the camera is fixed. If you are choosing hardware now, two paths skip the kernel-patch problem entirely. A depth camera that connects over GigE leaves USB and its ~400 MB/s ceiling behind; the first-connection guide for IP depth cameras covers that bring-up. Integrated 3D sensors are the other path: they ship as a supported stack, so no build-from-source detour exists. Inside the 3D sensors range, a ToF sensing camera such as the MV-DT01WMU returns a depth map directly, and speckle structured-light models target volume measurement in logistics cells.

3D sensorsHikrobot MV-DT01WMU ToF sensing cameraMV-DT01WMU
Type
Depth / ToF camera
Price on requestView and quote

To match a sensor to the host, resolution and optics you already run, start at the camera finder or the configurator:

Build the whole station

The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.

Start with the camera

Decision paths

  • lsusb -t shows 480M: fix the physical link first — native USB 3 port, USB 3 cable, no hub. Budget roughly 40 MB/s on USB 2 against about 400 MB/s on USB 3, and expect 5000M after the swap.
  • 5000M and the SDK still reports no device: purge the binaries, build with -DFORCE_RSUSB_BACKEND=TRUE, run setup_udev_rules.sh, update the firmware. Steps 1-7 carry this case end to end.
  • The viewer streams but runs stall or drop: bandwidth or power, not bring-up; use the ARM stream and USB power-budget guides from the verify section.
  • The camera itself is an open choice: the 3D sensors range above, or contact us and we will match a sensor to your host and quote it.

More bring-up and integration fixes for depth cameras sit on the 3D cameras topic page.

Questions

Why does librealsense2-dkms fail on a Jetson?

The DKMS package patches stock Ubuntu LTS kernels — the vendor list runs 4.4 through 6.5 — and the ARM kernel JetPack installs is not on it. With no matching kernel source, the module never builds, so the V4L2 backend finds no patched nodes. Skip the kernel path entirely: build the SDK from source with the RSUSB (libuvc) backend, which opens the camera from userspace and needs no kernel module.

How do I see which USB speed the camera negotiated?

Run lsusb -t and read the speed on the bus where the camera sits: 480M is USB 2 high speed, 5000M is USB 3 SuperSpeed. Plain lsusb lists devices but not link speed, which is why the tree view matters. A camera on 480M shares roughly 40 MB/s with everything else on the bus, so a full-rate depth-plus-color pair will not fit.

Do I still need the udev rules with the RSUSB backend?

Yes. The RSUSB backend still opens the camera's interfaces from userspace, and without the rules the nodes carry root-only permissions. Run scripts/setup_udev_rules.sh after cloning and before building, then unplug and replug the camera so the new permissions apply. Models with a built-in IMU expose extra interfaces, which makes this step even less optional.

Can I update the camera firmware before the Jetson build finishes?

Yes, and it is often the faster order. The viewer's firmware update tool runs wherever realsense-viewer is installed, so a PC or laptop can bring the camera current while the Jetson is still compiling. Firmware stays with the camera across hosts, so the Jetson sees the updated device as soon as the build finishes.

Which depth cameras avoid this driver work entirely?

Cameras that reach the host over GigE skip USB and its bandwidth ceiling, and integrated 3D sensors ship as a supported stack, so nothing depends on patching the host kernel. The 3D sensors range covers laser profilers, structured-light and ToF cameras, and each product page lists its interface so you can match the host you already run. Expert support comes with every order to wire the chosen sensor in.