CUDA, Python and JupyterLab
Install the exact GPU package set that matches your board (JetPack 6.2.1, CUDA 12.6.68, Python 3.10.12), lock it so nothing can silently replace it, and open JupyterLab from your laptop.
Where Where to implement
Jetson: jetson
- venv:
~/sutraai/.venv(Python 3.10.12) - Version lock:
~/sutraai/constraints-jp621.txt - Notebooks:
~/sutraai/notebooks/
Laptop
- SSH tunnel to port 8888
- Browser at
http://localhost:8888, or VS Code Remote-SSH
Versions Your package set
These wheels come from the Jetson AI Lab index for JetPack 6 / CUDA 12.6 and are built for Python 3.10 (cp310), which is exactly your board. torch 2.8.0 is the version NVIDIA forum users confirmed on JetPack 6.2. torchvision and torchaudio must match it.
| Package | Version | Comes from | Why this one |
|---|---|---|---|
| torch | 2.8.0 | Jetson AI Lab jp6/cu126 | Built for CUDA 12.6 + cuDNN 9.3 on Orin (arch 8.7); classic ONNX exporter by default |
| torchvision | 0.23.0 | Jetson AI Lab jp6/cu126 | The release paired with torch 2.8 |
| torchaudio | 2.8.0 | Jetson AI Lab jp6/cu126 | Paired with torch 2.8 (the steps use librosa for audio, so this is a backup) |
| onnxruntime-gpu | 1.23.0 | Jetson AI Lab jp6/cu126 | CUDA and TensorRT 10.3 execution providers on your GPU |
| numpy | 1.26.4 | PyPI | The last 1.x release; safe with every package in these steps |
| matplotlib · pillow · scipy | 3.10.9 · 11.3.0 · 1.15.3 | PyPI | Pinned so plots work with JupyterLab's inline backend |
| TensorRT (Python) | 10.3.0.30 | JetPack (system), linked into the venv | The only system Python package the venv needs (substep 2) |
| cuDSS (system library) | 0.7.1.4 · CUDA 12 | NVIDIA download, substep 3 | torch 2.8.0 needs libcudss.so.0; JetPack doesn't ship it |
| cuSPARSELt (system library) | 0.6.3.2 | NVIDIA download, substep 3 | torch 2.8.0 needs libcusparseLt.so.0; same version PyTorch uses with CUDA 12.6 |
The index also has newer sets (torch 2.9.1, 2.10.0, 2.11.0). Stay on 2.8.0 for the 3-week notebook phase. Change versions only between phases, and then change all three torch packages together.
Do Do it
1Put CUDA 12.6 on your PATH
echo 'export PATH=/usr/local/cuda/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
nvcc --version | tail -n 2 # expect: Cuda compilation tools, release 12.6, V12.6.68
ls -l /usr/local/cuda # points to /usr/local/cuda-12.6| Library on your board | What it does for you |
|---|---|
| CUDA 12.6.68 | Runs code on the GPU. Everything else sits on top of it. |
| cuDNN 9.3.0 / cuBLAS | Fast neural-network layers and matrix maths used inside PyTorch |
| TensorRT 10.3.0.30 | Turns a trained model into a fast engine for this exact GPU, or for the Orin NX's DLA accelerators (Step 05) |
| VPI 3.2.4, DeepStream | Camera and video pipelines. Not needed until Month 3. |
| OpenCV 4.8.0 (no CUDA) | Not used in these steps: images go through PIL and torchvision. |
2Create the project, the venv and the version lock
cd ~/sutraai
mkdir -p notebooks sutra_core configs data/raw data/processed models exports/golden reports
touch sutra_core/__init__.py
python3 --version # expect: Python 3.10.12
python3 -m venv .venv # isolated: Ubuntu's old Python packages stay out
cat > constraints-jp621.txt <<'EOF'
# SutraAI tested set: Jetson Orin NX 16GB · JetPack 6.2.1 · CUDA 12.6.68 · cuDNN 9.3.0 · TensorRT 10.3.0.30 · Python 3.10.12
torch==2.8.0
torchvision==0.23.0
torchaudio==2.8.0
onnxruntime-gpu==1.23.0
numpy==1.26.4
matplotlib==3.10.9
pillow==11.3.0
scipy==1.15.3
EOF
echo 'export PIP_CONSTRAINT=$HOME/sutraai/constraints-jp621.txt' >> .venv/bin/activate # every pip install in this venv obeys the lock
source .venv/bin/activate
pip install --upgrade pip
pip install numpy==1.26.4
# Link only JetPack's TensorRT 10.3 Python bindings into the venv (nothing else from Ubuntu)
TRT_DIR=$(/usr/bin/python3 -c "import tensorrt, os; print(os.path.dirname(os.path.dirname(tensorrt.__file__)))")
echo "$TRT_DIR" # expect: /usr/lib/python3.10/dist-packages
SP="$VIRTUAL_ENV/lib/python3.10/site-packages"
for p in "$TRT_DIR"/tensorrt*; do ln -sfn "$p" "$SP/$(basename "$p")"; done
python -c "import tensorrt; print(tensorrt.__version__)" # expect: 10.3.0.30
python -c "import sys; print([p for p in sys.path if 'dist-packages' in p])" # expect: []Why an isolated venv: Ubuntu keeps its own old Python packages in /usr/lib/python3/dist-packages (matplotlib 3.5, numpy 1.21, scipy, Pillow…). If the venv can see them, pip treats them as "already installed", and you end up with half-new, half-old libraries: errors like 'RcParams' object has no attribute '_get' or Unable to import Axes3D. An isolated venv sees none of them. The one system package you do need, TensorRT, is linked in by name.
Why the lock matters: packages like timm or sentence-transformers depend on torch. Without the lock, pip can replace your GPU torch with a CPU-only one from normal PyPI. With PIP_CONSTRAINT set, pip refuses any other version instead.
Already made your venv with --system-site-packages (the earlier version of this page)? Switch it to isolated instead of starting over. Your torch install is kept.
cd ~/sutraai && source .venv/bin/activate
SP="$VIRTUAL_ENV/lib/python3.10/site-packages"
# 1) stop seeing Ubuntu's Python packages
sed -i 's/^include-system-site-packages = .*/include-system-site-packages = false/' .venv/pyvenv.cfg
grep include-system .venv/pyvenv.cfg # expect: include-system-site-packages = false
# 2) remove the two mpl_toolkits workaround files (no longer needed)
rm -f "$SP/00-sutraai-mpl-toolkits.pth" "$SP/mpl_toolkits/__init__.py"
# 3) link only TensorRT
TRT_DIR=$(/usr/bin/python3 -c "import tensorrt, os; print(os.path.dirname(os.path.dirname(tensorrt.__file__)))")
for p in "$TRT_DIR"/tensorrt*; do ln -sfn "$p" "$SP/$(basename "$p")"; done
# 4) install what pip skipped earlier because Ubuntu "already had it" (installed packages are kept)
pip install torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 onnxruntime-gpu==1.23.0 --index-url https://pypi.jetson-ai-lab.io/jp6/cu126/+simple/
pip install jupyterlab ipykernel ipywidgets matplotlib pandas scikit-learn tqdm pyyaml \
timm onnx librosa soundfile openai sentence-transformers thop nbstripout ai-edge-litert
pip check # expect: No broken requirements found.
# 5) verify
python -c "import sys; print([p for p in sys.path if 'dist-packages' in p])" # expect: []
python -c "import torch, tensorrt, matplotlib; print(torch.__version__, torch.cuda.is_available(), tensorrt.__version__, matplotlib.__version__)"
# expect: 2.8.0 True 10.3.0.30 3.10.9Then restart JupyterLab (in its tmux window: Ctrl+C, then jupyter lab --no-browser --port 8888 again), restart each notebook kernel, and run the NB00 test cell (substep 7). It should end with ✅.
3Add the NVIDIA libraries the torch 2.8 wheel needs
The Jetson torch 2.8.0 wheel is linked against libraries that JetPack 6.2.1 doesn't install: cuDSS (sparse solver), cuSPARSELt (sparse matmul) and OpenBLAS. Without them, the install finishes fine but import torch fails with ImportError: libcudss.so.0: cannot open shared object file (or the same for libcusparseLt.so.0). This is the fix NVIDIA staff give on the forum. Run it once; it survives reboots and works in notebooks too.
# a) OpenBLAS + OpenMP from Ubuntu
sudo apt-get update
sudo apt-get install -y libopenblas-dev libomp-dev
# b) cuDSS 0.7.1 for Jetson (CUDA 12) → provides libcudss.so.0
mkdir -p ~/downloads && cd ~/downloads
wget https://developer.download.nvidia.com/compute/cudss/redist/libcudss/linux-aarch64/libcudss-linux-aarch64-0.7.1.4_cuda12-archive.tar.xz
echo "f283c31b6badf4a5277014295705eac8e6e28f27de30d746719ea1cef7a750b8 libcudss-linux-aarch64-0.7.1.4_cuda12-archive.tar.xz" | sha256sum -c # expect: OK
sudo mkdir -p /opt/nvidia
sudo tar xf libcudss-linux-aarch64-0.7.1.4_cuda12-archive.tar.xz -C /opt/nvidia
dirname "$(find /opt/nvidia/libcudss-linux-aarch64-0.7.1.4_cuda12-archive -name 'libcudss.so.0' | head -n1)" | sudo tee /etc/ld.so.conf.d/cudss.conf
# c) cuSPARSELt 0.6.3 for Jetson (the version PyTorch uses with CUDA 12.6) → provides libcusparseLt.so.0
wget https://developer.download.nvidia.com/compute/cusparselt/redist/libcusparse_lt/linux-aarch64/libcusparse_lt-linux-aarch64-0.6.3.2-archive.tar.xz
echo "91501d0c05d1ff0dd67399ecd7c1bf76a620e842dce54ae4c8a1f07cec0673e5 libcusparse_lt-linux-aarch64-0.6.3.2-archive.tar.xz" | sha256sum -c # expect: OK
sudo tar xf libcusparse_lt-linux-aarch64-0.6.3.2-archive.tar.xz -C /opt/nvidia
dirname "$(find /opt/nvidia/libcusparse_lt-linux-aarch64-0.6.3.2-archive -name 'libcusparseLt.so.0' | head -n1)" | sudo tee /etc/ld.so.conf.d/cusparselt.conf
# d) register both with the system loader
sudo ldconfig
ldconfig -p | grep -E "libcudss.so.0|libcusparseLt.so.0|libopenblas.so.0" # expect 3+ lines
cd ~/sutraaiWhy /opt/nvidia + /etc/ld.so.conf.d and not LD_LIBRARY_PATH: the loader finds them everywhere (SSH shell, tmux, Jupyter kernels, systemd services) without editing any environment. To undo: delete the two .conf files and the two folders, then sudo ldconfig.
4Install the GPU wheels for JetPack 6 / CUDA 12.6
cd ~/sutraai && source .venv/bin/activate
pip install --no-cache-dir torch==2.8.0 torchvision==0.23.0 torchaudio==2.8.0 --index-url https://pypi.jetson-ai-lab.io/jp6/cu126/+simple/
pip install --no-cache-dir onnxruntime-gpu==1.23.0 --index-url https://pypi.jetson-ai-lab.io/jp6/cu126/+simple/
# check that every library torch links against is found (runs without importing torch)
TORCH_LIB=$(python -c "import importlib.util, pathlib; print(pathlib.Path(importlib.util.find_spec('torch').origin).parent / 'lib')")
for f in "$TORCH_LIB"/*.so*; do ldd "$f"; done | grep "not found" | sort -u # expect: no output
python -c "import torch, torchvision, numpy; print(torch.__version__, torchvision.__version__, numpy.__version__, torch.version.cuda, torch.cuda.is_available())"
# expect: 2.8.0 0.23.0 1.26.4 12.6 TrueIf the ldd line prints anything, the library it names is still missing: libcudss.so.0 or libcusparseLt.so.0 → redo substep 3 and check that sha256sum said OK; libopenblas.so.0 → sudo apt-get install -y libopenblas-dev; libnccl.so.2 → see the "If it breaks" table below.
The last value must be True. If it's False, a CPU wheel slipped in. Run pip uninstall -y torch torchvision torchaudio and repeat this substep.
If pip hangs or the index doesn't respond, retry in a few minutes, or try another network (some home routers block the port). Use the .io address, not the old .dev one. You can browse the available wheels at pypi.jetson-ai-lab.io/jp6/cu126.
5Install the rest of the toolkit (the lock stays in force)
pip install jupyterlab ipykernel ipywidgets matplotlib pandas scikit-learn tqdm pyyaml \
timm onnx librosa soundfile openai sentence-transformers thop nbstripout ai-edge-litert
python -m ipykernel install --user --name sutraai --display-name "SutraAI (venv · torch 2.8 · CUDA 12.6)"
python -c "import torch; print(torch.__version__, torch.cuda.is_available())" # still 2.8.0 True
pip check # expect: No broken requirements found.
# isolation check: every package the notebooks import must come from the venv
python - <<'EOF'
import importlib
for name in ["numpy", "matplotlib", "mpl_toolkits.mplot3d", "PIL", "scipy", "sklearn", "pandas", "torch", "torchvision", "onnxruntime", "tensorrt"]:
try:
m = importlib.import_module(name)
except Exception as e:
print(f"{name:22} FAILED: {type(e).__name__}: {e}"); continue
where = "venv" if "/.venv/" in (m.__file__ or "") else "OUTSIDE THE VENV <- check .venv/pyvenv.cfg (substep 2)"
print(f"{name:22} {str(getattr(m, '__version__', '')):12} {where}")
EOF
# expect: every line ends with "venv" (tensorrt too: it's linked in)If pip stops with a conflict that mentions numpy or torch, the lock is doing its job: a package wanted a different version. Copy the error into BUILDLOG and install that package with an older release (for example pip install "somepkg<X") rather than removing the lock.
Recommended: a free Hugging Face token. Pretrained weights (Step 04's MobileNet via timm, Step 11's Nemotron and embedding models) download from the Hugging Face Hub. Without a token, downloads still work but print "You are sending unauthenticated requests to the HF Hub" and get lower rate limits. Create a Read token at huggingface.co/settings/tokens, then:
source ~/sutraai/.venv/bin/activate
hf auth login # paste the token when asked; if asked "Add token as git credential?", answer n
hf auth whoami # expect: your Hugging Face usernameThe token is saved in ~/.cache/huggingface/token, outside the project, so it never lands in git. Never paste it into a notebook. Restart the notebook kernel afterwards.
6Start JupyterLab inside tmux
tmux new -s lab # detach with Ctrl+B then D; come back with: tmux attach -t lab
cd ~/sutraai && source .venv/bin/activate
jupyter lab --no-browser --port 8888ssh -N -L 8888:localhost:8888 <your-user>@<jetson-ip> # or your Tailscale address when away from home
# then open http://localhost:8888 and paste the token printed in the Jetson terminalIn JupyterLab, create notebooks in notebooks/ and choose the SutraAI (venv · torch 2.8 · CUDA 12.6) kernel.
7NB00: environment check with your exact versions
import platform, numpy, torch, torchvision, onnxruntime as ort, matplotlib
print("Python ", platform.python_version()) # 3.10.12
print("numpy ", numpy.__version__) # 1.26.4
print("matplotlib ", matplotlib.__version__, matplotlib.__file__) # 3.10.9 …/.venv/lib/python3.10/site-packages/…
print("torch ", torch.__version__, "| CUDA available:", torch.cuda.is_available()) # 2.8.0 True
print("torchvision", torchvision.__version__) # 0.23.0
print("CUDA (torch build):", torch.version.cuda) # 12.6
print("cuDNN ", torch.backends.cudnn.version()) # 9.3.x reported as 903xx
print("GPU ", torch.cuda.get_device_name(0)) # Orin
free, total = torch.cuda.mem_get_info()
print(f"GPU-visible memory: {free/1e9:.1f} GB free of {total/1e9:.1f} GB") # about 15 GB total on Orin NX 16GB
try:
import tensorrt
print("TensorRT ", tensorrt.__version__) # 10.3.0.30
except ImportError:
print("TensorRT not visible: run the TensorRT link lines in substep 2")
print("ONNX Runtime", ort.__version__, ort.get_available_providers()) # 1.23.0, includes CUDAExecutionProvider8NB01: feel the GPU
import torch, time
def bench(device, n=2048, reps=10, dtype=torch.float32):
a = torch.randn(n, n, device=device, dtype=dtype)
b = torch.randn(n, n, device=device, dtype=dtype)
for _ in range(2): a @ b # warm-up
if device == "cuda": torch.cuda.synchronize()
t = time.perf_counter()
for _ in range(reps): a @ b
if device == "cuda": torch.cuda.synchronize()
return (time.perf_counter() - t) / reps * 1000 # ms per multiply
cpu, gpu, gpu16 = bench("cpu"), bench("cuda"), bench("cuda", dtype=torch.float16)
print(f"CPU (8 cores) {cpu:.1f} ms | GPU fp32 {gpu:.1f} ms | GPU fp16 {gpu16:.1f} ms")A tensor is an array that can live on the GPU. .to("cuda") moves it there. fp16 uses half the memory and runs on the Tensor Cores. Run it once at 15 W and once at MAXN SUPER to see what the power mode does.
9Version control from day one
cd ~/sutraai && git init
nbstripout --install # strips notebook outputs on commit
printf ".venv/\n.venv-convert/\ndata/\nmodels/\nexports/\n*.engine\n" > .gitignore
pip freeze > requirements-lock.txt # full snapshot of what actually got installed
git add . && git commit -m "Step 02: environment ready (JetPack 6.2.1, CUDA 12.6.68, torch 2.8.0)"Test Test it
Add this as the last cell of NB00, then run Kernel → Restart Kernel and Run All Cells:
assert platform.python_version().startswith("3.10"), "use the venv made with Python 3.10"
assert torch.__version__.startswith("2.8.0"), f"expected torch 2.8.0, got {torch.__version__}"
assert torchvision.__version__.startswith("0.23"), f"expected torchvision 0.23.x, got {torchvision.__version__}"
assert torch.version.cuda.startswith("12.6"), f"torch built for CUDA {torch.version.cuda}, board has 12.6"
assert torch.cuda.is_available(), "PyTorch can't see the GPU"
assert numpy.__version__ == "1.26.4", numpy.__version__
assert "/.venv/" in matplotlib.__file__, "matplotlib comes from outside the venv: switch the venv to isolated (substep 2), then restart the kernel"
assert tensorrt.__version__.startswith("10.3"), tensorrt.__version__
assert "CUDAExecutionProvider" in ort.get_available_providers(), "ONNX Runtime has no CUDA provider"
print("✅ environment matches JetPack 6.2.1 / CUDA 12.6.68")Done Done when
nvcc --versionreports release 12.6, V12.6.68- constraints-jp621.txt created and PIP_CONSTRAINT set in the venv
- cuDSS and cuSPARSELt installed;
ldconfig -plistslibcudss.so.0andlibcusparseLt.so.0 - torch 2.8.0 / torchvision 0.23.0 / onnxruntime-gpu 1.23.0 installed from the jp6/cu126 index; the
ldd … | grep "not found"check prints nothing torch.cuda.is_available()is True inside the notebook kernel;pip checkis clean- JupyterLab opens through the SSH tunnel and survives an SSH disconnect (tmux)
- NB00 and NB01 pass Restart and Run All
- requirements-lock.txt saved and first git commit done
If it breaks
| Symptom | Fix |
|---|---|
ImportError: libcudss.so.0: cannot open shared object file | cuDSS isn't installed (JetPack doesn't include it). Do substep 3b, then sudo ldconfig. |
ImportError: libcusparseLt.so.0: cannot open shared object file | cuSPARSELt isn't installed. Do substep 3c, then sudo ldconfig. |
ImportError: libopenblas.so.0 | sudo apt-get install -y libopenblas-dev |
ImportError: libnccl.so.2 or undefined symbol: ncclCommWindowDeregister | You got an early copy of the torch 2.8.0 wheel; NVIDIA has since replaced it on the index. Reinstall: pip install --force-reinstall --no-cache-dir --no-deps torch==2.8.0 --index-url https://pypi.jetson-ai-lab.io/jp6/cu126/+simple/ |
Any error whose traceback shows /usr/lib/python3/dist-packages: 'RcParams' object has no attribute '_get', Unable to import Axes3D, cannot import name 'docstring' | The venv can still see Ubuntu's old Python packages. Run the one-time switch in substep 2 (sets include-system-site-packages = false), restart JupyterLab and the kernel. |
| The isolation check prints OUTSIDE THE VENV | Same cause: check that .venv/pyvenv.cfg says include-system-site-packages = false. |
sha256sum says FAILED | Download was cut off. Delete the .tar.xz and run the wget line again. |
| pip hangs with no output | The network is blocking the index (seen on some home routers). Try another network or a phone hotspot. |
cuda.is_available() is False | CPU wheel installed. Uninstall torch, torchvision and torchaudio; reinstall the pinned versions from the jp6/cu126 index with --no-cache-dir. |
operator torchvision::nms does not exist | torch and torchvision don't match. Install torch 2.8.0 and torchvision 0.23.0 together in one command from the same index. |
| pip says a package needs numpy 2 | Install an older release of that package that supports numpy 1.26, or keep it in the separate converter venv (Step 05). |
| TensorRT import fails in the venv | The link is missing. Run the TRT_DIR=… / for p in …tensorrt* lines from substep 2. |
| Index returns 404 or times out | Retry later; use pypi.jetson-ai-lab.io (not .dev). |
| Browser can't reach 8888 | Tunnel not running, or JupyterLab is on a different port; check the Jetson terminal output. |
Learn more
- Jetson AI Lab package index for JetPack 6 / CUDA 12.6 (browse available wheels)
- NVIDIA forum: PyTorch 2.8 wheel for JetPack 6.2
- NVIDIA forum: PyTorch 2.8.0 "libcudss.so.0 not found" (NVIDIA's answer: install cuDSS)
- cuDSS downloads for Jetson · cuSPARSELt downloads for Jetson
- pip: constraints files
- Free, 1 h: NVIDIA DLI "An Even Easier Introduction to CUDA" (listed on NVIDIA AI Learning Essentials)