| name | nvidia-jetson-deployment |
| description | Déploiement IA sur NVIDIA Jetson (Nano/Orin/AGX) — JetPack SDK, TensorRT optimisation, DeepStream pipelines vidéo, DLA accélération, multi-stream, profiling Nsight, déploiement CUDA natif, optimisation énergétique. |
| version | 1.0.0 |
| author | EVA |
| license | Privée EVA St-Étienne |
| platforms | ["linux","macos","windows"] |
| metadata | {"EVA":{"tags":["nvidia-jetson","tensorrt","deepstream","cuda","dla","jetpack","nsight","edge-ai","computer-vision","multi-stream"],"related_skills":["onnx-edge-deployment","tensorflow-lite-deep-dive","model-optimization-edge","google-coral-edge-tpu"]}} |
NVIDIA Jetson — Déploiement Edge IA
Vue d'ensemble
La famille NVIDIA Jetson est la plateforme Edge AI la plus puissante du marché, intégrant GPU NVIDIA avec Tensor Cores, DLA (Deep Learning Accelerator), et un pipeline vidéo matériel. Elle couvre du Nano (0.5 TOPS FP16) à l'AGX Orin (248 TOPS INT8).
Comparatif des modules Jetson
| Module | GPU | Tensor Cores | DLA | TOPS (INT8) | RAM | Consommation |
|---|
| Nano | 128-core Maxwell | 0 | 0 | 0.5 | 4 GB | 5-10 W |
| TX2 | 256-core Pascal | 0 | 1 | 1.3 | 8 GB | 7-15 W |
| Xavier NX | 384-core Volta | 48 | 2 | 21 | 8 GB | 10-20 W |
| Orin NX | 1024-core Ampere | 32 | 2 | 100 | 16 GB | 10-25 W |
| Orin Nano | 512-core Ampere | 16 | 0 | 40 | 8 GB | 7-15 W |
| AGX Orin | 2048-core Ampere | 64 | 2 | 248 | 64 GB | 15-60 W |
Architecture logicielle Jetson
┌─────────────────────────────────────────────────────┐
│ Application (ROS / Python / C++) │
├─────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│ │ TensorRT │ │ DeepStream│ │ CUDA/CuDNN/Tensor │ │
│ │ (moteur │ │ (pipeline │ │ Core (NVIDIA SDK) │ │
│ │ inf.) │ │ vidéo) │ │ │ │
│ └────┬─────┘ └────┬─────┘ └────────┬─────────┘ │
│ │ │ │ │
├───────┼──────────────┼─────────────────┼─────────────┤
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────┐ │
│ │ JetPack SDK (L4T / BSP) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ GPU Ampere│ │ DLA │ │ Video Engine │ │ │
│ │ │ (CUDA) │ │(Accel DL)│ │ (H.264/H.265)│ │ │
│ │ └──────────┘ └──────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
1. TensorRT — Optimisation et Inférence
1.1 Conversion ONNX → TensorRT Engine
import tensorrt as trt
import numpy as np
import os
TRT_LOGGER = trt.Logger(trt.Logger.INFO)
class BuildTensorRT:
"""Build et inférence TensorRT sur Jetson."""
def __init__(self, onnx_path: str, engine_path: str = None,
precision: str = "fp16", max_batch: int = 8):
self.onnx_path = onnx_path
self.engine_path = engine_path or onnx_path.replace(".onnx", ".trt")
self.precision = precision
self.max_batch = max_batch
self.engine = None
self.context = None
self.builder = None
self.network = None
self.parser = None
def build(self, calibration_data=None) -> bool:
"""Build un engine TensorRT depuis ONNX."""
self.builder = trt.Builder(TRT_LOGGER)
network_flags = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
self.network = .builder.create_network(network_flags)
.parser = trt.OnnxParser(.network, TRT_LOGGER)
(.onnx_path, ) f:
.parser.parse(f.read()):
i (.parser.num_errors):
()
config = .builder.create_builder_config()
config.set_memory_pool_limit(
trt.MemoryPoolType.WORKSPACE,
* * *
)
.precision == :
.builder.platform_has_fast_fp16:
config.set_flag(trt.BuilderFlag.FP16)
()
:
()
.precision == :
.builder.platform_has_fast_int8 calibration_data:
config.set_flag(trt.BuilderFlag.INT8)
config.int8_calibrator = Int8Calibrator(calibration_data)
()
:
()
config.set_flag(trt.BuilderFlag.FP16)
profile = .builder.create_optimization_profile()
input_name = .network.get_input().name
input_shape = .network.get_input().shape
profile.set_shape(input_name,
=(, *input_shape[:]),
opt=(.max_batch // , *input_shape[:]),
=(.max_batch, *input_shape[:]),
)
config.add_optimization_profile(profile)
serialized = .builder.build_serialized_network(.network, config)
serialized :
()
(.engine_path, ) f:
f.write(serialized)
()
() -> :
os.path.exists(.engine_path):
(.engine_path, ) f:
runtime = trt.Runtime(TRT_LOGGER)
.engine = runtime.deserialize_cuda_engine(f.read())
.context = .engine.create_execution_context()
()
:
.build():
.load()
() -> np.ndarray:
pycuda.driver cuda
d_input = cuda.mem_alloc(input_data.nbytes)
d_output = cuda.mem_alloc(
.engine.get_binding_shape().volume() * np.dtype(np.float32).itemsize
)
cuda.memcpy_htod(d_input, input_data)
.context.execute_v2(
bindings=[(d_input), (d_output)]
)
output_shape = (.engine.get_binding_shape())
output_data = np.empty(output_shape, dtype=np.float32)
cuda.memcpy_dtoh(output_data, d_output)
output_data
(trt.IInt8MinMaxCalibrator):
():
().__init__()
.data = calibration_data
.index =
.buffer =
():
.data.shape[]
():
.index >= (.data):
batch = .data[.index: .index + .get_batch_size()]
.index += .get_batch_size()
[batch.astype(np.float32).ravel()]
():
():
1.2 trtexec — Outil CLI de build et benchmark
trtexec --onnx=model.onnx \
--saveEngine=model_fp16.trt \
--fp16 \
--workspace=1024 \
--verbose
trtexec --onnx=model.onnx \
--saveEngine=model_int8.trt \
--int8 \
--calib=calibration_data \
--workspace=1024
trtexec --loadEngine=model_fp16.trt \
--warmUp=100 \
--iterations=500 \
--duration=10 \
--useSpinWait \
--separateProfileRun
trtexec --loadEngine=model_fp16.trt \
--shapes=input:4x3x224x224 \
--batch=4 \
--best
trtexec --loadEngine=model_fp16.trt --dumpLayerInfo --profilingVerbosity=detailed
trtexec --loadEngine=model_fp16.trt --exportProfile=profile.json
1.3 Optimization des couches TensorRT
def inspecter_engine(engine_path: str):
"""Inspecte la structure d'un engine TensorRT."""
import tensorrt as trt
runtime = trt.Runtime(trt.Logger(trt.Logger.WARNING))
with open(engine_path, "rb") as f:
engine = runtime.deserialize_cuda_engine(f.read())
n_layers = engine.num_layers
n_bindings = engine.num_bindings
infos = []
for i in range(n_layers):
layer = engine.get_layer_info(i)
infos.append({
"name": layer.name,
"type": str(layer.type),
"input_dims": [str(layer.input_format)],
"output_dim": str(layer.output_format),
"precision": str(layer.precision),
})
tensor_core_layers = sum(
1 for l in infos if "CBR" in l["name"] or "FC" in l["name"]
)
return {
"n_layers": n_layers,
"n_bindings": n_bindings,
"layers": infos,
"tensor_core_fusions": tensor_core_layers,
}
2. DLA — Deep Learning Accelerator
2.1 Activation et utilisation du DLA
config.set_flag(trt.BuilderFlag.GPU_FALLBACK)
config.set_default_device_type(trt.DeviceType.DLA)
config.DLA_core = 0
for i in range(self.network.num_layers):
layer = self.network.get_layer(i)
if i < 20:
layer.precision = trt.float32
layer.set_device_type(trt.DeviceType.DLA)
else:
layer.set_device_type(trt.DeviceType.GPU)
2.2 Benchmark DLA vs GPU
trtexec --onnx=model.onnx \
--saveEngine=model_dla0.trt \
--fp16 \
--useDLACore=0 \
--allowGPUFallback
trtexec --onnx=model.onnx \
--saveEngine=model_gpu.trt \
--fp16
3. DeepStream — Pipeline Vidéo
3.1 Pipeline type
import sys
def creer_pipeline_deepstream(config_path: str, n_sources: int = 4):
"""Crée un pipeline DeepStream multi-caméras."""
pipeline = (
f"nvarguscamerasrc sensor-id=0 ! "
f"nvvidconv ! "
f"video/x-raw,width=640,height=480,framerate=30/1 ! "
f"nvstreammux name=mux batch-size={n_sources} ! "
f"nvinfer config-file-path={config_path} ! "
f"nvtracker tracker-width=640 tracker-height=384 ! "
f"nvdsosd ! "
f"nvegltransform ! nveglglessink"
)
return pipeline
3.2 Configuration infer (config_infer_primary.txt)
[property]
gpu-id=0
net-scale-factor=0.0039215697906911373
model-file=model_fp16.trt
proto-file=labels.txt
model-engine-file=model_fp16.trt
labelfile-path=labels.txt
net-input-dims=3
net-input-order=0
input-blob-name=input
output-blob-name=output
network-mode=1
batch-size=4
workspace-size=1024
parse-function=2
num-detected-classes=80
interval=0
gie-unique-id=1
process-mode=1
3.3 Multi-stream avec DeepStream
"""
[application]
enable-perf-measurement=1
perf-measurement-interval=1
[source0]
enable=1
type=3 # 3=USB camera
uri=/dev/video0
[source1]
enable=1
type=3
uri=/dev/video1
[source2]
enable=1
type=3
uri=/dev/video2
[source3]
enable=1
type=3
uri=/dev/video3
[sink0]
enable=1
type=2 # 2=display
sync=0
[primary-gie]
enable=1
config-file-path=config_infer_primary.txt
batch-size=4
"""
3.4 Métriques DeepStream
def metriques_deepstream() -> dict:
"""Métriques en temps réel du pipeline DeepStream."""
return {
"fps_moyen": 120.0,
"fps_par_source": [30.0, 30.0, 30.0, 30.0],
"latence_inference_ms": 8.2,
"utilisation_gpu": 65.0,
"utilisation_dla": 0.0,
"n_frames_perdues": 0,
"memoire_gpu_mb": 450,
}
4. Mode Énergétique et Performance
4.1 Modes NV Power
sudo nvpmodel -q
sudo nvpmodel -m 0
sudo nvpmodel -m 2
cat /proc/nvpmodel
cat /sys/devices/gpu.0/devfreq/17000000.gv11b/available_frequencies
sudo sh -c "echo 612000000 > /sys/devices/gpu.0/devfreq/17000000.gv11b/userspace/set_freq"
sudo sh -c "echo performance > /sys/devices/gpu.0/devfreq/17000000.gv11b/governor"
4.2 Jetson Stats
sudo apt install python3-pip
pip3 install jetson-stats
sudo jtop
sudo jetson_release
cat /sys/devices/virtual/thermal/thermal_zone*/temp
4.3 Optimisation perf/watt
def trouver_mode_optimal(fps_cible: float = 30.0):
"""Trouve le mode de puissance optimal pour un FPS cible."""
modes = {
0: {"nom": "15W", "fps_max": 60},
1: {"nom": "25W", "fps_max": 120},
2: {"nom": "MAXN", "fps_max": 200},
}
for mode_id, info in sorted(modes.items()):
if info["fps_max"] >= fps_cible * 1.2:
return mode_id
return max(modes.keys())
5. Profilage avec Nsight Systems
5.1 Profilage CUDA
nsys profile \
-o ./profils/inference_report \
--stats=true \
--trace cuda,nvtx,opengl \
python3 infer.py
5.2 Analyse des goulots
import pycuda.driver as cuda
def profiler_inference(model, input_data: np.ndarray, n_iter: int = 100):
"""Profiling fin avec CUDA Events."""
start = cuda.Event()
end = cuda.Event()
for _ in range(10):
model.inferer(input_data)
start.record()
for _ in range(n_iter):
model.inferer(input_data)
end.record()
end.synchronize()
elapsed_ms = start.time_since(end) / n_iter
print(f"Temps moyen par inference (CUDA Events) : {elapsed_ms:.2f} ms")
print(f"Throughput : {1000 / elapsed_ms:.1f} FPS")
return elapsed_ms
6. Déploiement Python vs C++
6.1 API Python (rapide prototypage)
import torch
import tensorrt as trt
model = torch.jit.load("model.ts").eval()
6.2 API C++ (production)
#include <NvInfer.h>
#include <NvOnnxParser.h>
class TRTInference {
private:
nvinfer1::IRuntime* runtime;
nvinfer1::ICudaEngine* engine;
nvinfer1::IExecutionContext* context;
void* device_buffers[2];
float* host_output;
public:
TRTInference(const std::string& engine_path) {
runtime = nvinfer1::createInferRuntime(gLogger);
std::ifstream file(engine_path, std::ios::binary);
file.seekg(0, std::ios::end);
size_t size = file.tellg();
std::vector<char> buffer(size);
file.seekg(0, std::ios::beg);
file.read(buffer.data(), size);
engine = runtime->deserializeCudaEngine(buffer.data(), size);
context = engine->createExecutionContext();
cudaMalloc(&device_buffers[0], engine->getBindingBytes(0));
cudaMalloc(&device_buffers[1], engine->getBindingBytes(1));
host_output = [engine->() / ()];
}
{
(device_buffers[], input,
engine->(), cudaMemcpyHostToDevice);
context->(device_buffers);
(output, device_buffers[],
engine->(), cudaMemcpyDeviceToHost);
;
}
~() {
(device_buffers[]);
(device_buffers[]);
[] host_output;
context;
engine;
runtime;
}
};
7. Installation JetPack
sdkmanager --cli install \
--target_os Linux \
--product Jetson \
--version 6.0 \
--target_jetson_orin_nx \
--logintype remote \
--host <jetson-ip>
jetson_release
sudo apt update
sudo apt install python3-pip libopenblas-dev libjpeg-dev
pip3 install numpy pandas matplotlib pycuda
Pièges Courants
-
JetPack version incompatible : utiliser exactement la version de JetPack supportée par votre module. Orin = JetPack 6+, Xavier = JetPack 5.x, Nano = JetPack 4.x.
-
TensorRT build trop long : le build d'engine TensorRT peut prendre 10-30 minutes. Toujours sauvegarder l'engine (--saveEngine) et le charger en production.
-
DLA non activé : le DLA n'est activé que pour certains opérateurs (Conv, Pooling). Vérifier avec --useDLACore=0.
-
DeepStream buffer management : le pipeline DeepStream gère ses buffers internes. Modifier la résolution après l'initialisation peut causer des plantages.
-
nvpmodel après boot : le mode de puissance n'est pas persistant. L'ajouter dans /etc/rc.local ou systemd.
-
Mémoire GPU insuffisante : les modèles lourds (YOLOv8-L, ViT) peuvent nécessiter > 8 GB. Réduire batch size, utiliser INT8, ou activer le swap GPU (zram).
-
Température GPU > 80 °C : le throttling GPU commence vers 85 °C. Ajouter un ventilateur (PWM) ou réduire nvpmodel.
-
CUDA out of memory avec PyTorch : PyTorch ne libère pas la mémoire GPU immédiatement. Utiliser torch.cuda.empty_cache() après chaque inférence.
Références