Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PyTorch on JetPack 7.2 for Jetson Orin NX (the undocumented path)

NVIDIA just release Jet Pack 7.2 support for Orin! This is a great step to making the Jetson stack install much smoother and cleaner than current status. Right now, JetPack 6 only works with Ubuntu 22, which then forces you to use a ROS version that is a few years old, Python 3.10, and some contradicting PyTorch wheels that can be a bit difficult to nail correctly.

With JetPack 7.2, we now have Ubuntu 24! This means a domino effect of upgrading all other dependencies. However, although the 7.2 support is out, there are still quite some steps that haven't been documented as of right now.

These instructions should help setup faster and avoid some hours of debugging frustration.


Read this first (the four things that actually matter)

1. CUDA has to be on your PATH, not just installed. A fresh flash ships the CUDA 13.2 runtime, but if CUDA_HOME / PATH / LD_LIBRARY_PATH don't point at it, torch won't find it at runtime. "Installed" is not the same as "visible." This bit me and it's the first thing to check if imports or nvcc behave oddly.

2. The wheels are prerelease and undocumented. You need --pre or pip won't even see them. They are not in NVIDIA's compatibility matrix, not in the Jetson install docs, and not what the forum threads point you to. Without the --pre and you get nothing back and wrongly conclude no wheel exists. It exists. You just have to ask for prereleases.

3. Stay on Python 3.12. Do not jump to 3.13. JetPack 7.2 ships Python 3.12 and the cu132 aarch64 wheels are built for cp312. There is no matching 3.13 wheel for this stack right now, so moving to 3.13 puts you straight back into building from source. 3.12 is the supported island. Stay on it. (Similarily, don't bother with 3.10 wheels you might find - none of them I've tested have worked so far)

4. This is specific to my hardware: Jetson Orin NX 16GB (sm_87) on JetPack 7.2 / CUDA 13.2. The CUDA suffix (cu132) has to match your driver, and the wheel architecture has to match your GPU. Other Orin modules (Orin Nano, AGX Orin) are also sm_87 and will very likely work, but a different JetPack/CUDA version or a non-Orin board (Thor is sm_110, for example) needs a different wheel. Check yours before copying this blindly. See For other hardware.


Tested-on

Board Seeed reComputer Super, Jetson Orin NX 16GB (J401 carrier)
JetPack 7.2 (L4T r39.2)
OS Ubuntu 24.04
CUDA 13.2
Python 3.12
GPU arch sm_87
PyTorch torch 2.12.1+cu132, torchvision 0.27.1+cu132 (prerelease, from download.pytorch.org/whl/cu132)
numpy 2.2.6
ROS ROS 2 Jazzy (system install, separate from the venv)
LeRobot 0.6.0 (installed --no-deps, see below)

Step 0: get JetPack 7.2 onto the board

Before any of the PyTorch work, you need the JP7.2 BSP flashed. There are three ways depending on your hardware, and this is the part that mostly does work. Full flashing steps live in Seeed's wiki: https://wiki.seeedstudio.com/recomputer_jetson_super_getting_started/#flash-jetpack-os

  • Standard NVIDIA devkit (host flashing): if you have a plain NVIDIA Jetson Orin devkit, grab the image the usual way from the NVIDIA Jetson download center and flash from an Ubuntu host over USB-C.

  • reComputer, ISO route (what I did): download the JP7.2 image for your exact module from the Seeed wiki link above and flash it directly. Match the module and version carefully. For the Orin NX 16GB the correct row is the JP7.2 / Orin NX 16GB image, and its SHA256 should match before you flash. This is the reliable path today.

  • Seeed developer tool: install/upgrade it and follow the same wiki page.

    pip install --upgrade seeed-jetson-developer==0.2.0

    Heads up: at time of writing the tool's UI did not yet list the reComputer Super + JP7.2 BSP for me, and auto-detection tried to hand me an AGX Orin image (wrong module), which failed to flash. Check that it actually offers your board and the 7.2 BSP before relying on it. If it doesn't, use the ISO route above.

Once the board boots into Ubuntu 24.04 and cat /etc/nv_tegra_release shows R39 (JP7.2), continue below.


Why the obvious paths fail

Skip this if you just want the steps. Here so you don't repeat the experiment.

  • Plain pip install torch: gives a wheel that reports CUDA available but is built for datacenter GPUs (sm_110 / sm_121), not Orin's sm_87. It fails with no kernel image is available for execution on the device on the first real operation. torch.cuda.is_available() returning True is not proof of anything here.
  • Community SBSA cu130 wheels: install, but they're also sm_110 / sm_121, and they need system libraries a fresh flash lacks (NVPL, cuDSS). Even after adding those, they still won't run a kernel on sm_87.
  • Jetson PyTorch container: there is no r39 image yet, so the tooling (autotag) falls back to a JetPack 6 (r36.4) image, which fails on the JP7.2 driver with CUDA error 801 (operation not supported).
  • The one that works: --pre torch --extra-index-url https://download.pytorch.org/whl/cu132. cu132 matches CUDA 13.2, the aarch64 build includes sm_87, and --extra-index-url (not --index-url) lets numpy and the other deps still resolve from PyPI instead of failing on the CUDA index.

Prerequisites (fresh flash)

Depending on how you flashed, torch may fail to import with a missing lib*.so before it even gets to CUDA. Those are system libraries the wheel links against that a fresh JP7.2 flash does not include (NVPL for CPU BLAS/LAPACK, cuDSS for the sparse solver). Add NVIDIA's CUDA repo, then the libs:

# NVIDIA CUDA repo (provides NVPL, cuDSS, etc.)
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/sbsa/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update

# NVPL: the CPU BLAS/LAPACK backend the wheel links against
sudo apt install -y nvpl

# cuDSS runtime, then put it on the linker path (it installs to a versioned subdir)
sudo apt install -y libcudss0-cuda-13
echo "/usr/lib/aarch64-linux-gnu/libcudss/13" | sudo tee /etc/ld.so.conf.d/cudss.conf
sudo ldconfig

Two gotchas:

  • If apt fails with a lock held by another process, that's first-boot auto-updates running. Let them finish or reboot. Do not delete the lock file.
  • If the import later complains about a different .so, find it with find / -name "libNAME.so*" 2>/dev/null, add its directory to a file under /etc/ld.so.conf.d/, and re-run sudo ldconfig. Same pattern every time.

Install

The full runnable version is in install.sh. The steps, annotated:

# 1. Put CUDA 13.2 on PATH (caveat #1). Verify the dir name first:
#    ls /usr/local/ | grep cuda   -> point CUDA_HOME at the 13.2 dir.
export CUDA_HOME=/usr/local/cuda-13.2
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:${LD_LIBRARY_PATH}
which nvcc && nvcc --version    # sanity check CUDA is actually visible

# 2. Build basics + a clean Python 3.12 venv (caveat #3).
#    --system-site-packages lets the venv see JetPack's TensorRT bindings.
sudo apt install -y python3.12 python3.12-venv python3.12-dev python3-pip \
  build-essential cmake git pkg-config libopenblas-dev libjpeg-dev zlib1g-dev
python3.12 -m venv ~/.venvs/jetson-jp72 --system-site-packages
source ~/.venvs/jetson-jp72/bin/activate
python -m pip install --upgrade pip setuptools wheel packaging

# 3. Clear any wrong torch that snuck in, and purge the cache so pip
#    doesn't reuse a bad wheel.
python -m pip uninstall -y torch torchvision torchaudio torchdata torchtext torch-tensorrt
python -m pip cache purge

# 4. THE ONE THAT WORKS: prerelease cu132 wheels (caveats #2 and #4).
#    --pre is required. --extra-index-url (not --index-url) so numpy etc.
#    still come from PyPI.
python -m pip install --pre torch --extra-index-url https://download.pytorch.org/whl/cu132

# 5. Prove it (see Verify). Only add torchvision AFTER torch passes.
python -m pip install --pre torchvision --extra-index-url https://download.pytorch.org/whl/cu132

Persist the CUDA exports in ~/.bashrc so new terminals aren't broken (the script does this for you).


Verify

torch.cuda.is_available() returning True is not proof. Every failing wheel above returned True. The only test that counts is a real kernel launch.

python verify_stack.py

A clean pass prints the device as your Orin, the capability as (8, 7), and a completed matmul, with no sm_87 is not compatible warning. If you see that warning or a no kernel image error, you're on a wrong-arch wheel and this particular build isn't the right one for your board.


Don't install torchaudio (yet)

It's the usual source of version conflicts and you almost certainly don't need it for a computer-vision or robotics stack. If you do need it later, pin it to your exact installed torch version rather than letting pip pick.


ROS 2 Jazzy (system, not the venv)

ROS goes in the system environment, not the torch venv. ROS 2 Jazzy is built against the system Python 3.12 and its system packages (including a specific numpy), so sourcing it inside the cu132 venv causes two Python paths and two numpys to collide. Keep them as separate worlds that talk over ROS topics, which is the intended architecture anyway. If a single process genuinely needs both torch and ROS, make a --system-site-packages venv rather than pip-installing ROS into this one.

Install (in a plain terminal, venv deactivated):

sudo apt update && sudo apt install -y locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

sudo apt install -y software-properties-common curl
sudo add-apt-repository universe -y

export ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}')
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $VERSION_CODENAME)_all.deb"
sudo apt install -y /tmp/ros2-apt-source.deb

sudo apt update && sudo apt upgrade -y
sudo apt install -y ros-jazzy-desktop   # or: ros-jazzy-ros-base ros-jazzy-turtlesim  (lighter)
echo "source /opt/ros/jazzy/setup.bash" >> ~/.bashrc
source /opt/ros/jazzy/setup.bash

Sanity check with turtlesim, in two plain terminals (source ROS in each):

ros2 run turtlesim turtlesim_node        # terminal 1
ros2 run turtlesim turtle_teleop_key     # terminal 2, arrow keys to drive

LeRobot (in the venv, but do NOT let it touch torch)

LeRobot belongs in the same venv as torch because it imports torch directly. The trap: LeRobot 0.6.0 pins torch<2.12.0, but the only working Orin wheel is the 2.12.1 prerelease. A plain pip install lerobot will "resolve" that by ripping out your cu132 build and installing a generic torch 2.11.0 that fails on the Orin with no kernel image available. The version number even matches, so it looks fine until the first kernel launch.

Install it without deps, then add its extras manually, skipping anything torch-related:

source ~/.venvs/jetson-jp72/bin/activate

pip install lerobot --no-deps

# add LeRobot's runtime deps, pinned to what it wants, with NO torch/torchvision:
pip install \
  "cmake>=3.29,<4.2" "draccus==0.10.0" "opencv-python-headless>=4.9,<4.14" \
  einops gymnasium huggingface-hub safetensors termcolor tqdm \
  "numpy>=2.0,<2.3" "packaging<26.0,>=24.2" "requests<3.0,>=2.32.0"

After this, pip check will still report torch, torchvision, and setuptools as "incompatible" with LeRobot's pins. That is expected and intended: the torch pins are the soft ceiling you are deliberately overriding (LeRobot 0.6.0 runs fine on 2.12.1 in practice), and setuptools is a cosmetic build-tool nag. Everything else should be clean.

Never use pip install 'lerobot[extra]'. Any bracketed extras form (lerobot[feetech], lerobot[deepdiff-dep], etc.) re-resolves the whole dependency tree and pulls the generic torch 2.11.0 wheel, which silently replaces your cu132 build with one that fails on the Orin. Install each extra's underlying package by its bare name instead. The ones the SO101 path needs:

pip install feetech-servo-sdk deepdiff

numpy ABI note (from --system-site-packages). Because the venv can see system packages, an old system pandas (built against numpy 1.26) can get imported and crash with numpy.dtype size changed ... binary incompatibility against your venv's numpy 2.x. Fix by installing a matching pandas into the venv so it shadows the system one, kept inside LeRobot's numpy ceiling:

pip install --ignore-installed "pandas>=2.2,<2.3" "numpy>=2.0,<2.3"

Verify the whole stack coexists (real kernel, not just is_available):

python -c "import torch, torchvision, lerobot, numpy; print('numpy', numpy.__version__); x=torch.randn(1024,1024,device='cuda'); torch.cuda.synchronize(); print('STACK OK', float((x@x).sum()), '| torch', torch.__version__, '| tv', torchvision.__version__, '| lerobot', lerobot.__version__)"

STACK OK with torch still on +cu132 means torch, torchvision, and LeRobot all share the same GPU build. The sm_87 warning is cosmetic (NVIDIA confirmed it is being removed); the printed number is the proof the kernel ran.

SO101 teleop

LeRobot 0.6.0 ships hyphenated console scripts (not python -m lerobot.*).

lerobot-find-port                                 # identify the arm ports

Serial port permission. The quick fix that works immediately in the current shell (NVIDIA's SO101 docs list this) is chmod, but it resets on replug:

sudo chmod 666 /dev/ttyACM0 /dev/ttyACM1

The permanent fix is the dialout group, but it only takes effect after a full logout/login (not just a new terminal):

sudo usermod -aG dialout $USER

First-time arms need their motor IDs set, then calibration, before teleop:

lerobot-setup-motors --teleop.type=so101_leader  --teleop.port=/dev/ttyACM0
lerobot-setup-motors --robot.type=so101_follower --robot.port=/dev/ttyACM1
lerobot-calibrate    --teleop.type=so101_leader  --teleop.port=/dev/ttyACM0 --teleop.id=leader_arm
lerobot-calibrate    --robot.type=so101_follower --robot.port=/dev/ttyACM1 --robot.id=follower_arm

Then teleop:

lerobot-teleoperate \
  --robot.type=so101_follower --robot.port=/dev/ttyACM1 --robot.id=follower_arm \
  --teleop.type=so101_leader  --teleop.port=/dev/ttyACM0 --teleop.id=leader_arm

Failure decoder: Permission denied on the port means the chmod/dialout step above. Missing motor IDs ... found: {} on an open port means the arm is unpowered, mis-wired, or its motors were never ID'd (lerobot-setup-motors). Arm IDs (leader_arm, follower_arm) are labels calibration files key to, so keep them consistent between calibrate and teleoperate. Other useful scripts: lerobot-info, lerobot-find-cameras, lerobot-record, lerobot-train.


JetPack 6.2 vs JetPack 7.2 (why this matters)

The whole point of moving to JP7.2 is that it fixes the top domino, and every layer below it falls into place. JP6.2 was a working stack, but only one exact combination worked, and reaching it took source builds and manual library installs. Side by side:

Layer JetPack 6.2 (the old way) JetPack 7.2 (this repo)
Ubuntu 22.04 24.04
Kernel 5.15 6.8
Flashing Ubuntu host required ISO / direct image, no host
Python 3.10 only (3.13 silently pulls CPU torch) 3.12
CUDA 12.6 (not on PATH by default) 13.2
ROS Humble (Ubuntu 22 tier 1) Jazzy (Ubuntu 24 tier 1)
PyTorch torch 2.5.0, NVIDIA redist wheel only torch 2.12.1+cu132 (prerelease, --pre)
torchvision 0.20.0, built from source (MAX_JOBS=2, arch 8.7) 0.27.1+cu132 (prerelease wheel)
numpy pinned 1.26.0 2.2.6
cuSPARSELt installed by hand in the CUDA 13 toolkit
libcudss missing on JP6.2, blocked torch 2.8+ present / installable
LeRobot reinstalls CPU torch unless installed last --no-deps + bare extras (see below)

The honest nuance: JP7.2 is not zero-friction. The working torch is a prerelease cu132 wheel that is undocumented and invisible to normal pip listing, and LeRobot still tries to pull a wrong-arch torch. But the base OS, CUDA, ROS, and Python all line up cleanly now, where on JP6.2 every one of those was a fight. The remaining friction is at the framework edge, not the platform.


For other hardware

This was verified only on Jetson Orin NX 16GB, JetPack 7.2, CUDA 13.2, Python 3.12. If any of those differ:

  • Different Orin module (Orin Nano, AGX Orin): same sm_87 arch, so the cu132 wheel should work. Verify with verify_stack.py before trusting it.
  • Different CUDA version: the wheel suffix must match. On CUDA 13.0 you'd need cu130, on 13.3 a cu133 build, and so on. Check with nvcc --version and swap the --extra-index-url path accordingly.
  • Thor / non-Orin (sm_110+): different architecture entirely. The datacenter wheels that fail on Orin may actually be correct for you. Don't copy the workarounds here without checking your sm_ target.
  • Python 3.13: no matching wheel for this stack at time of writing. Stay on 3.12 or expect to build from source.

If pip can't find a wheel, diagnose what tags it actually sees:

python -m pip index versions torch --pre --extra-index-url https://download.pytorch.org/whl/cu132
python -m pip debug --verbose | grep -A 30 "Compatible tags"

That tells you whether pip sees a compatible cp312 + aarch64 wheel at all.


Status

As of writing, this path is undocumented and relies on prerelease wheels, so treat it as "works today, may change." If NVIDIA or Seeed publish official JP7.2 Orin wheels or an r39 container, prefer those. Reported upstream to both.

Maintained as field notes, not official guidance. Corrections welcome.

About

The correct configuration for setting up your Jetson board with the latest support for 7.2 - including PyTorch, tensorRT, CUDA, ROS, and LeRobot

Resources

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages