Using the ZED SDK with a Virtual Display on NVIDIA® Jetson™

Open in ClaudeOpen in ChatGPT

When working with a ZED camera and a ZED SDK-based application running on a remote NVIDIA® Jetson™ device, you may need to access the display output without physically connecting a monitor (headless mode). While a dummy HDMI dongle is a common solution, you can also configure a virtual display entirely through software.

This guide shows how to set up a virtual display on your NVIDIA® Jetson™ device, allowing you to use the ZED camera with remote desktop applications like VNC, NoMachine, or X11 forwarding without requiring a physical dongle.

Prerequisites

Before starting, ensure you have:

  • Set up your remote NVIDIA® Jetson™ device to run ZED SDK applications.
  • Access to the device physically or remotely via SSH.
  • [Optional] Installed and configured a remote desktop application if you plan to use one.

Virtual Display Setup

Create the Setup Script

First, create a script file to configure the virtual display.

You can download a pre-made script from this link or manually create a file named setup-virtual-display.sh with the following content:

Expand to view the content of setup-virtual-display.sh
#!/bin/bash
# =============================================================================
# Jetson Headless Virtual Display Setup (GPU-Accelerated)
# =============================================================================
#
# Creates a virtual 1920x1080@60Hz display on Jetson AGX Orin without a
# physical monitor. EGL, CUDA, and NVIDIA Argus all work. A real monitor
# can still be hot-plugged at any time. Works with NoMachine/VNC for remote
# desktop access.
#
# Platform: Jetson AGX Orin
# - JetPack 5.x (L4T R35.x, Ubuntu 20.04) — Xorg "nvidia" DDX driver
# - JetPack 6.x (L4T R36.x, Ubuntu 22.04) — Xorg "nvidia" DDX driver
#
# HOW IT WORKS:
# -------------
# Uses the nvidia DDX Xorg driver with ConnectedMonitor + CustomEDID options
# to fake a connected 1920x1080 display on the DP port. This provides:
# - Full NVIDIA EGL support (nvbufsurface, Argus camera can create EGLImages)
# - CUDA-EGL interop (cuGraphicsEGLRegisterImage works)
# - Hardware-accelerated X11/GLX rendering via NVIDIA GPU
# - Proper DRI2 for remote desktop protocols (NoMachine, VNC, etc.)
#
# JP6-SPECIFIC NOTES:
# -------------------
# nvidia-drm.modeset=1 is NOT compatible with nvidia DDX driver — it causes
# "Failed to acquire modesetting permission". We remove it from boot params.
# The nvidia DDX handles modesetting directly without nvidia-drm KMS.
#
# EGL/CUDA/Argus work fine WITHOUT nvidia-drm.modeset=1 when using nvidia DDX,
# because applications access NVIDIA EGL/CUDA directly via the ICD, not through
# the kernel DRM modesetting path.
#
# REAL MONITOR:
# -------------
# If you plug in a real DP monitor, it appears automatically in xrandr
# and is usable alongside (or instead of) the virtual framebuffer.
#
# USAGE:
# chmod +x setup-virtual-display-fixed.sh
# sudo ./setup-virtual-display-fixed.sh
# sudo reboot
#
# VERIFY:
# DISPLAY=:0 xrandr # 1920x1080 screen
# DISPLAY=:0 eglinfo 2>&1 | head -15 # EGL vendor: NVIDIA
# python3 -c "import ctypes; c=ctypes.CDLL('libcuda.so.1'); print(c.cuInit(0))" # 0
# # Connect via NoMachine — should show full GNOME desktop
#
# TO REVERT:
# sudo cp /etc/X11/xorg.conf.bak.original /etc/X11/xorg.conf # or rm it
# sudo rm -f /etc/X11/edid-1080p.bin
# sudo rm -f /etc/udev/rules.d/99-jetson-drm-seat.rules
# sudo systemctl enable seatd 2>/dev/null # if you need seatd back
# # For JP6: add back nvidia-drm.modeset=1 to /boot/extlinux/extlinux.conf if needed
# sudo reboot
#
# =============================================================================
set -euo pipefail
if [[ $EUID -ne 0 ]]; then
echo "ERROR: Must run as root (sudo)."
exit 1
fi
# --- Detect JetPack / L4T version ---
detect_jetpack_version() {
local l4t_major=""
if [[ -f /etc/nv_tegra_release ]]; then
l4t_major=$(sed -n 's/^# R\([0-9]*\).*/\1/p' /etc/nv_tegra_release)
fi
if [[ -z "$l4t_major" ]] && command -v dpkg-query &>/dev/null; then
l4t_major=$(dpkg-query -W -f='${Version}' nvidia-l4t-core 2>/dev/null \
| sed -n 's/^\([0-9]*\)\..*/\1/p') || true
fi
if [[ -n "$l4t_major" && "$l4t_major" -ge 36 ]]; then
echo 6
elif [[ -n "$l4t_major" && "$l4t_major" -ge 35 ]]; then
echo 5
else
echo 0
fi
}
# --- Detect first available DP connector ---
detect_dp_connector_drm() {
# DRM naming (1-indexed): DP-1, DP-2, etc.
for card_dp in /sys/class/drm/card*-DP-*; do
if [[ -d "$card_dp" ]]; then
basename "$card_dp" | sed 's/card[0-9]*-//'
return
fi
done
echo "DP-1"
}
detect_dp_connector_xorg() {
# nvidia DDX uses 0-indexed: DP-0, DP-1, etc.
local drm_idx
drm_idx=$(detect_dp_connector_drm | sed 's/.*DP-//')
echo "DP-$((drm_idx - 1))"
}
JP_VERSION=$(detect_jetpack_version)
DP_DRM=$(detect_dp_connector_drm)
DP_XORG=$(detect_dp_connector_xorg)
echo "=== Jetson Virtual Display Setup ==="
if [[ "$JP_VERSION" -eq 6 ]]; then
echo "Detected: JetPack 6 (L4T R36.x)"
elif [[ "$JP_VERSION" -eq 5 ]]; then
echo "Detected: JetPack 5 (L4T R35.x)"
else
echo "WARNING: Could not detect JetPack version, defaulting to JP5 path"
JP_VERSION=5
fi
# =========================================================================
# COMMON PATH (JP5 + JP6): nvidia DDX driver with ConnectedMonitor + CustomEDID
# =========================================================================
echo "Using nvidia DDX driver with connector ${DP_XORG}"
STEP=0
TOTAL_JP5=3
TOTAL_JP6=5
# ------------------------------------------------------------------
# Step 1: Backup xorg.conf
# ------------------------------------------------------------------
STEP=$((STEP+1))
if [[ -f /etc/X11/xorg.conf && ! -f /etc/X11/xorg.conf.bak.original ]]; then
cp /etc/X11/xorg.conf /etc/X11/xorg.conf.bak.original
echo "[${STEP}] Backed up xorg.conf"
else
echo "[${STEP}] Backup: skipped (already exists or no file)"
fi
# ------------------------------------------------------------------
# Step 2: EDID binary
# ------------------------------------------------------------------
STEP=$((STEP+1))
base64 -d > /etc/X11/edid-1080p.bin << 'EDID_EOF'
AP///////wBZ5QEAAQAAAAEiAQOAAAB4Cu6Ro1RMmSYPUFQhCAABAQEBAQEBAQEBAQEBAQEBAjqA
GHE4LUBYLEUADyghAAAeAAAA/ABWaXJ0dWFsIDEwODBwAAAA/QAySx5REQAKICAgICAgAAAA/wAw
MDAwMDAwMDAwMDAxAJc=
EDID_EOF
chmod 644 /etc/X11/edid-1080p.bin
echo "[${STEP}] Installed EDID binary"
# ------------------------------------------------------------------
# Step 3: xorg.conf (nvidia DDX)
# ------------------------------------------------------------------
STEP=$((STEP+1))
cat > /etc/X11/xorg.conf << XORG_EOF
# Jetson JP${JP_VERSION} - nvidia DDX driver, virtual display
Section "DRI"
Mode 0666
EndSection
Section "Module"
Disable "dri"
SubSection "extmod"
Option "omit xfree86-dga"
EndSubSection
EndSection
Section "Device"
Identifier "Tegra0"
Driver "nvidia"
Option "AllowEmptyInitialConfiguration" "true"
Option "ConnectedMonitor" "${DP_XORG}"
Option "CustomEDID" "${DP_XORG}:/etc/X11/edid-1080p.bin"
Option "HardDPMS" "false"
EndSection
Section "Monitor"
Identifier "Virtual-1080p"
HorizSync 30-81
VertRefresh 50-75
ModeLine "1920x1080_60" 148.50 1920 2008 2052 2200 1080 1084 1089 1125 +HSync +VSync
Option "PreferredMode" "1920x1080_60"
EndSection
Section "Screen"
Identifier "Default Screen"
Device "Tegra0"
Monitor "Virtual-1080p"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1920x1080_60"
EndSubSection
EndSection
XORG_EOF
echo "[${STEP}] Wrote xorg.conf (nvidia DDX)"
# ------------------------------------------------------------------
# JP6-SPECIFIC: Additional steps
# ------------------------------------------------------------------
if [[ "$JP_VERSION" -eq 6 ]]; then
# ------------------------------------------------------------------
# Step 4: Remove nvidia-drm.modeset=1 from boot params
# ------------------------------------------------------------------
STEP=$((STEP+1))
EXTLINUX_CONF="/boot/extlinux/extlinux.conf"
if [[ -f "$EXTLINUX_CONF" ]]; then
if grep -q "nvidia-drm.modeset=1" "$EXTLINUX_CONF"; then
cp "$EXTLINUX_CONF" "${EXTLINUX_CONF}.bak.$(date +%Y%m%d%H%M%S)"
sed -i 's| nvidia-drm\.modeset=1||g' "$EXTLINUX_CONF"
echo "[${STEP}] Removed nvidia-drm.modeset=1 from extlinux.conf"
else
echo "[${STEP}] nvidia-drm.modeset=1 not present (OK)"
fi
else
echo "[${STEP}] WARNING: ${EXTLINUX_CONF} not found"
fi
# ------------------------------------------------------------------
# Step 5: Disable seatd if present (optional, helps avoid conflicts)
# ------------------------------------------------------------------
STEP=$((STEP+1))
if systemctl is-enabled seatd &>/dev/null 2>&1; then
systemctl stop seatd 2>/dev/null || true
systemctl disable seatd 2>/dev/null || true
echo "[${STEP}] Disabled seatd (avoids DRM master conflicts)"
else
echo "[${STEP}] seatd not present or already disabled"
fi
fi
echo ""
echo "=== Done! ==="
echo ""
echo "A reboot is REQUIRED:"
echo " sudo reboot"
echo ""
echo "After reboot, verify:"
echo " DISPLAY=:0 xrandr # 1920x1080 screen"
echo " DISPLAY=:0 eglinfo 2>&1 | head -15 # EGL vendor: NVIDIA"
echo " python3 -c 'import ctypes; c=ctypes.CDLL(\"libcuda.so.1\"); print(c.cuInit(0))' # 0"
echo ""
if [[ "$JP_VERSION" -eq 6 ]]; then
echo "JP6 NOTES:"
echo " - nvidia DDX driver active (NOT modesetting)"
echo " - nvidia-drm.modeset=1 removed from boot (incompatible with nvidia DDX)"
echo " - EGL/CUDA/Argus work via nvidia DDX's own rendering path"
echo " - NoMachine/VNC will show full hardware-accelerated desktop"
echo ""
fi
echo "Connect via NoMachine or VNC to access the desktop remotely."

Make the script executable:

chmod +x setup-virtual-display.sh

Apply the Virtual Display Configuration

Run the setup script and restart the display manager to apply the changes:

sudo ./setup-virtual-display.sh && sudo systemctl restart gdm

This script configures your system’s X server to create a virtual display. After the display manager restarts, you can connect via remote desktop applications and use the ZED camera as if a physical monitor were connected.

This configuration will disable physical display output on HDMI and DisplayPort connectors. If you need to use a physical monitor directly, revert this configuration first (see below).

Reverting to Physical Display

To restore physical display functionality (for HDMI or DisplayPort monitors), run:

sudo cp /etc/X11/xorg.conf.bak.original /etc/X11/xorg.conf && sudo systemctl restart gdm

This restores the original X server configuration and restarts the display manager.

How It Works

The virtual display setup configures the X server to use a virtual framebuffer instead of requiring a physical display connection. This allows the system to:

  • Create a display session without a physical monitor
  • Enable GPU acceleration for graphics applications
  • Allow remote desktop connections to access the display
  • Run GUI applications that require a display environment

This is useful for headless robotic systems where you need to run ZED SDK applications with GUI components or perform development and debugging remotely.

Troubleshooting

If you encounter issues after setting up the virtual display:

  • Remote desktop not connecting: Ensure your remote desktop server (VNC, NoMachine, etc.) is properly configured and running.
  • Need to switch back to physical display: Use the revert command shown above.
  • Display resolution issues: You may need to modify the script to adjust the virtual display resolution. The current script sets it to 1920x1080@60Hz, which is common for ZED SDK applications.