Skip to main content
Version: 10

Configure Qualcomm Dragonwing

Required Version
This feature will not be released until MoveIt Pro version 10.2.

Qualcomm Dragonwing boards run the MoveIt Pro Runtime on their arm64 CPU cores and can run ONNX models on the Hexagon NPU through the -qnn image. This guide takes a board from a fresh Ubuntu image to a working Runtime, with a command after every step that shows whether the step worked. Run every command on the board unless the step says otherwise.

Tested configurations

MoveIt Pro has been run on the Arduino VENTUNO Q (Dragonwing IQ-8275, chipset qualcomm-qcs8275) and the Dragonwing IQ-9075 EVK (chipset qualcomm-qcs9075), both on Qualcomm's Ubuntu 24.04 image. Neither is a qualified MoveIt Pro platform. The output blocks in this guide come from the IQ-9075 EVK; other boards print different device numbers and firmware names.

What the NPU does and does not run today

The model bundles shipped with MoveIt Pro run on the CPU on this platform: a SAM3 segmentation Objective takes about two minutes per call on the IQ-9075 EVK. A SAM3 model exported for the NPU with Qualcomm AI Hub takes about three seconds per call on the same board. Run a Model on the NPU covers the export and which Behaviors load it.

Before You Start​

You need:

  • A Dragonwing board with its power supply, a network connection (Ethernet or Wi-Fi), and either a monitor and keyboard or a serial console for the first boot.
  • A MoveIt Pro license key.
  • At least 64 GB of storage for Docker. The Runtime image is about 10 GB, the example workspace build adds several more, and NPU model files can add gigabytes each. MicroSD card storage is slow, so keep Docker's images on an M.2 NVMe drive if the board has a slot for one; Step 5 moves them.

The MoveIt Pro Desktop App can run on the board itself or on another computer on the same network.

Step 1: Install Ubuntu​

Flash Qualcomm's Ubuntu 24.04 image for your board by following Qualcomm's own guide. For the IQ-9075 EVK that is Install or update Ubuntu, which covers both the graphical Qualcomm Launcher and the command-line Qualcomm Device Loader. Flashing erases the board. Afterwards, follow Qualcomm's Set up the device page to create the user account and connect to the network.

Check that the board identifies itself as a Snapdragon SoC. The MoveIt Pro CLI reads the same family file to decide whether to use the -qnn image; machine names the chipset.

cat /sys/devices/soc0/family /sys/devices/soc0/machine
Snapdragon
QCS9075

Step 2: Set the Clock and Enable SSH​

Boards that have been in a box for a while boot with a clock months behind. Package downloads then fail with certificate errors, because the certificates are "not yet valid" from the board's point of view. Turn on time synchronization and confirm it took.

sudo timedatectl set-ntp true
timedatectl show -p NTP -p NTPSynchronized
NTP=yes
NTPSynchronized=yes

If NTPSynchronized=no persists for more than a minute, set the date by hand once with sudo date -u -s "YYYY-MM-DD HH:MM:SS" and check again.

Enable SSH so you can leave the monitor behind, then note the board's address.

sudo systemctl enable --now ssh
hostname -I

Step 3: Add Swap​

Qualcomm's image ships without swap. When a process on the board runs out of memory, the board stops responding instead of killing the process. An 8 GB swap file turns that into a slowdown you can recover from.

sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
swapon --show
NAME TYPE SIZE USED PRIO
/swapfile file 8G 0B -2

Step 4: Install MoveIt Pro​

Install the MoveIt Pro CLI on the board by following MoveIt Pro Runtime Installation. Skip the NVIDIA and AMD steps. Install the MoveIt Pro Desktop App on the board or on the computer you will use it from.

moveit_pro --version
MoveIt Pro version: 10.2.0

Step 5: Move Docker Storage to an NVMe Drive​

Skip this step if the board has no M.2 NVMe slot or its onboard storage already meets Before You Start. Install the drive by following the board's hardware guide, then format it, mount it, and move Docker's storage onto it.

Format and Mount the Drive​

  1. Power on the board.
  2. Log in using your username and password.
  3. Open a terminal window (keyboard shortcut: CTRL+ALT+T).
  4. In the terminal window type lsblk and hit enter. If your new drive was detected correctly, there should be an entry at the bottom of the output similar to nvme0n1 259:0 0 931.5G 0 disk. If your device is named differently, write down the name and use it in the following steps.
  5. Type sudo fdisk /dev/nvme0n1, hit enter, enter your password, then hit enter again.
    1. Type g and hit enter to create a new GPT partition table.
    2. Type n and hit enter to create a new partition.
    3. Hit enter to accept the default partition number.
    4. Hit enter to accept the default first sector.
    5. Hit enter to accept the default last sector and use the remaining drive capacity.
    6. Type w and hit enter to write the changes and exit fdisk.
  6. Type lsblk and hit enter. Confirm that the drive nvme0n1 now has a new partition below it named similarly to nvme0n1p1.
  7. Type sudo mkfs.ext4 /dev/nvme0n1p1 which will format the new partition as ext4 (common Linux extended filesystem format).
  8. Type sudo mkdir /mnt/nvme which will create a new folder that we will use as the mount point for the new partition.
  9. Type lsblk -f which will print the partition information but also include the UUID field which we will use in the following steps. Highlight the UUID of the new partition with your mouse and copy it to your clipboard (right-click -> Copy).
  10. Edit the /etc/fstab file with a terminal editor like vim or nano. Add a new line to the end of this file with the format UUID=<partition_uuid> /mnt/nvme ext4 defaults 0 2, replacing <partition_uuid> with the UUID that you copied in step 9 (right-click -> Paste).
  11. Save and exit the editor.
  12. Type sudo reboot and hit enter to reboot the board.
  13. Once the board has rebooted and you have logged in again, you can verify that the new partition is mounted using the command ls /mnt/nvme in a terminal to see the contents of the new partition.

Move Docker's Storage​

note

The following instructions assume that you already have Docker installed and that you carefully followed the instructions above to install and configure an NVMe drive.

  1. Confirm that /mnt/nvme is the intended mounted filesystem and not a symlink:

    test -d /mnt/nvme && test ! -L /mnt/nvme &&
    mountpoint --quiet /mnt/nvme ||
    { echo "NVMe target validation failed." >&2; exit 1; }
  2. Detect whether this Docker installation uses the containerd image store, then back up the current service configuration. Do not infer the backend from the Docker version: upgraded Docker 29 installations can still use the legacy image store.

    set -euo pipefail
    DRIVER_STATUS="$(docker info --format '{{json .DriverStatus}}')"
    STORAGE_DRIVER="$(docker info --format '{{.Driver}}')"
    case "$DRIVER_STATUS" in
    *io.containerd.snapshotter.v1*) MIGRATE_CONTAINERD=true ;;
    *)
    test -n "$STORAGE_DRIVER" ||
    { echo "Could not determine Docker's active image store." >&2; exit 1; }
    MIGRATE_CONTAINERD=false
    ;;
    esac

    sudo install -d -m 0755 /var/backups
    BACKUP_DIR="$(sudo mktemp -d -p /var/backups moveit-pro-docker-storage.XXXXXXXX)"
    sudo chmod 0700 "$BACKUP_DIR"

    if sudo test -e /etc/docker/daemon.json; then
    sudo cp -a /etc/docker/daemon.json "$BACKUP_DIR/daemon.json"
    else
    sudo touch "$BACKUP_DIR/daemon.json.absent"
    fi

    if sudo test -e /etc/containerd/config.toml; then
    sudo cp -a /etc/containerd/config.toml "$BACKUP_DIR/containerd-config.toml"
    else
    sudo touch "$BACKUP_DIR/containerd-config.toml.absent"
    fi

    if $MIGRATE_CONTAINERD; then
    sudo touch "$BACKUP_DIR/containerd-migration.enabled"
    fi
    printf 'Configuration backup: %s\n' "$BACKUP_DIR"

    Record the printed backup path; the rollback commands require it. The marker records the backend reported by docker info; do not create or remove it based only on the Docker version.

  3. Record the current images and named volumes, then stop Docker and containerd:

    set -euo pipefail
    docker image ls
    docker volume ls
    sudo systemctl stop docker.socket docker.service containerd
    for unit in docker.socket docker.service containerd; do
    if sudo systemctl is-active --quiet "$unit"; then
    echo "$unit is still active; refusing to touch live data." >&2
    exit 1
    fi
    done
  4. Copy the active data without deleting its source. Copy containerd only when step 2 created the migration marker:

    set -euo pipefail
    sudo test ! -e /mnt/nvme/docker ||
    { echo "/mnt/nvme/docker already exists; refusing to merge data." >&2; exit 1; }
    sudo install -d -m 0711 /mnt/nvme/docker
    sudo rsync -aHAXx --numeric-ids /var/lib/docker/ /mnt/nvme/docker/ ||
    { echo "Docker data copy failed." >&2; exit 1; }

    if sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; then
    sudo test ! -e /mnt/nvme/containerd ||
    { echo "/mnt/nvme/containerd already exists; refusing to merge data." >&2; exit 1; }
    sudo install -d -m 0711 /mnt/nvme/containerd
    if sudo test -d /var/lib/containerd; then
    sudo rsync -aHAXx --numeric-ids /var/lib/containerd/ /mnt/nvme/containerd/ ||
    { echo "containerd data copy failed." >&2; exit 1; }
    sudo touch "$BACKUP_DIR/containerd-data-source.present"
    fi
    fi
  5. Set "data-root": "/mnt/nvme/docker" in /etc/docker/daemon.json, preserving any existing settings. If $BACKUP_DIR/containerd-migration.enabled exists, ensure a version-appropriate containerd configuration exists, then set root = "/mnt/nvme/containerd" in it. Do not change the containerd root otherwise.

    set -euo pipefail
    if sudo test -e "$BACKUP_DIR/containerd-migration.enabled" &&
    ! sudo test -e /etc/containerd/config.toml; then
    sudo install -d -m 0755 /etc/containerd
    containerd config default | sudo tee /etc/containerd/config.toml >/dev/null
    fi
  6. Start the services and verify the migration before changing the old directories:

    set -euo pipefail
    sudo systemctl start containerd docker
    test "$(docker info --format '{{ .DockerRootDir }}')" = /mnt/nvme/docker ||
    { echo "Docker is not using the NVMe data root." >&2; exit 1; }
    if sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; then
    sudo containerd config dump |
    grep -Eq '^[[:space:]]*root[[:space:]]*=[[:space:]]*"/mnt/nvme/containerd"' ||
    { echo "containerd is not using the NVMe root." >&2; exit 1; }
    fi
    docker image ls
    docker volume ls
    sudo systemctl --no-pager --full status containerd docker
    mountpoint --quiet /mnt/nvme
    test "$(findmnt -n -o TARGET -T /mnt/nvme/docker)" = /mnt/nvme
    if sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; then
    test "$(findmnt -n -o TARGET -T /mnt/nvme/containerd)" = /mnt/nvme
    fi

    Confirm that the expected images, named volumes, and volume contents are present. If validation fails, the original data still exists under /var/lib; restore the exact pre-migration configuration before restarting the services:

    set -euo pipefail
    # If this is a new shell, set BACKUP_DIR to the path printed in step 2.
    sudo test -d "$BACKUP_DIR" || { echo "Backup directory not found." >&2; exit 1; }
    sudo systemctl stop docker.socket docker.service containerd
    for unit in docker.socket docker.service containerd; do
    ! sudo systemctl is-active --quiet "$unit" ||
    { echo "$unit is still active; aborting rollback." >&2; exit 1; }
    done
    if sudo test -e "$BACKUP_DIR/daemon.json.absent"; then
    sudo rm -f /etc/docker/daemon.json
    else
    sudo cp -a "$BACKUP_DIR/daemon.json" /etc/docker/daemon.json
    fi
    if sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; then
    sudo rm -f /etc/containerd/config.toml
    else
    sudo cp -a "$BACKUP_DIR/containerd-config.toml" /etc/containerd/config.toml
    fi
    sudo systemctl start containerd docker
  7. After the migrated system has been validated, stop the services and quarantine only the data roots that were migrated. Refuse to overwrite an earlier quarantine:

    set -euo pipefail
    sudo test -d "$BACKUP_DIR" || { echo "Backup directory not found." >&2; exit 1; }
    mountpoint --quiet /mnt/nvme ||
    { echo "NVMe target is not mounted; refusing to quarantine data." >&2; exit 1; }
    test "$(findmnt -n -o TARGET -T /mnt/nvme/docker)" = /mnt/nvme ||
    { echo "Docker destination is not on the NVMe mount." >&2; exit 1; }
    if sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; then
    test "$(findmnt -n -o TARGET -T /mnt/nvme/containerd)" = /mnt/nvme ||
    { echo "containerd destination is not on the NVMe mount." >&2; exit 1; }
    fi
    if sudo test -e "$BACKUP_DIR/daemon.json.absent"; then
    :
    else
    sudo test -f "$BACKUP_DIR/daemon.json" ||
    { echo "Docker configuration backup not found." >&2; exit 1; }
    fi
    if sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; then
    :
    else
    sudo test -f "$BACKUP_DIR/containerd-config.toml" ||
    { echo "containerd configuration backup not found." >&2; exit 1; }
    fi
    sudo test -d /var/lib/docker || { echo "Docker data root not found." >&2; exit 1; }
    if sudo test -e /var/lib/docker.pre-nvme-migration; then
    echo "Docker quarantine already exists." >&2
    exit 1
    fi
    if sudo test -e "$BACKUP_DIR/containerd-data-source.present"; then
    sudo test -d /var/lib/containerd ||
    { echo "containerd data root not found." >&2; exit 1; }
    if sudo test -e /var/lib/containerd.pre-nvme-migration; then
    echo "containerd quarantine already exists." >&2
    exit 1
    fi
    fi

    sudo systemctl stop docker.socket docker.service containerd
    for unit in docker.socket docker.service containerd; do
    ! sudo systemctl is-active --quiet "$unit" ||
    { echo "$unit is still active; refusing to move live data." >&2; exit 1; }
    done
    sudo mv /var/lib/docker /var/lib/docker.pre-nvme-migration
    if sudo test -e "$BACKUP_DIR/containerd-data-source.present"; then
    sudo mv /var/lib/containerd /var/lib/containerd.pre-nvme-migration
    fi
    sudo systemctl start containerd docker

    Verify the Docker root, images, volumes, and application data again. Keep the quarantined directories until the deployment has operated successfully and you have a separate backup.

  8. To roll back after quarantine, preflight every backup and destination before stopping services. Then preserve any directories recreated by the failed configuration, restore the old data to its exact path, restore or remove each configuration file according to its marker, and restart the services:

    set -euo pipefail
    # Set BACKUP_DIR to the path printed in step 2.
    sudo test -d "$BACKUP_DIR" || { echo "Backup directory not found." >&2; exit 1; }
    if sudo test -e "$BACKUP_DIR/daemon.json.absent"; then
    :
    else
    sudo test -f "$BACKUP_DIR/daemon.json" ||
    { echo "Docker configuration backup not found." >&2; exit 1; }
    fi
    if sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; then
    :
    else
    sudo test -f "$BACKUP_DIR/containerd-config.toml" ||
    { echo "containerd configuration backup not found." >&2; exit 1; }
    fi

    ROLLBACK_SUFFIX="failed-nvme-$(date +%Y%m%d-%H%M%S)"
    sudo test -d /var/lib/docker.pre-nvme-migration ||
    { echo "Docker quarantine not found." >&2; exit 1; }
    sudo test ! -e "/var/lib/docker.$ROLLBACK_SUFFIX" ||
    { echo "Docker failure quarantine already exists." >&2; exit 1; }
    if sudo test -e "$BACKUP_DIR/containerd-data-source.present"; then
    sudo test -d /var/lib/containerd.pre-nvme-migration ||
    { echo "containerd quarantine not found." >&2; exit 1; }
    sudo test ! -e "/var/lib/containerd.$ROLLBACK_SUFFIX" ||
    { echo "containerd failure quarantine already exists." >&2; exit 1; }
    fi

    sudo systemctl stop docker.socket docker.service containerd
    for unit in docker.socket docker.service containerd; do
    ! sudo systemctl is-active --quiet "$unit" ||
    { echo "$unit is still active; aborting rollback." >&2; exit 1; }
    done
    if sudo test -e /var/lib/docker; then
    sudo mv /var/lib/docker "/var/lib/docker.$ROLLBACK_SUFFIX"
    fi
    sudo mv /var/lib/docker.pre-nvme-migration /var/lib/docker

    if sudo test -e "$BACKUP_DIR/containerd-data-source.present"; then
    if sudo test -e /var/lib/containerd; then
    sudo mv /var/lib/containerd "/var/lib/containerd.$ROLLBACK_SUFFIX"
    fi
    sudo mv /var/lib/containerd.pre-nvme-migration /var/lib/containerd
    fi

    if sudo test -e "$BACKUP_DIR/daemon.json.absent"; then
    sudo rm -f /etc/docker/daemon.json
    else
    sudo cp -a "$BACKUP_DIR/daemon.json" /etc/docker/daemon.json
    fi
    if sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; then
    sudo rm -f /etc/containerd/config.toml
    else
    sudo cp -a "$BACKUP_DIR/containerd-config.toml" /etc/containerd/config.toml
    fi
    sudo systemctl start containerd docker

Step 6: Start the Runtime​

Start the example workspace in simulation. The first launch builds the workspace on the board, which takes about ten minutes on the IQ-9075 EVK; later launches start in about a minute.

moveit_pro run -c lab_sim

On a Snapdragon board the CLI selects the -qnn image automatically. If Qualcomm's NPU packages, qcom-fastrpc1 and qairt-dsp-binaries, are missing, it offers to install them with apt, which asks for your password. With -y, or without a terminal, it stops instead and prints the command to run:

sudo apt-get update && sudo apt-get install -y qcom-fastrpc1 qairt-dsp-binaries

Confirm the Runtime is serving once the launch output settles.

docker ps --format '{{.Names}} {{.Status}}'
moveit_pro-runtime-1 Up About a minute (healthy)
moveit_pro-drivers-1 Up About a minute (healthy)

Step 7: Connect the Desktop App​

Open the Desktop App. It discovers a Runtime on the same computer or the local network; if the board is not listed, choose Add Connection and enter the board's address and a name. The app asks for a frontend key the first time, which the board prints with:

moveit_pro frontend-key

See Connect the Desktop App to a Runtime for the connection flow. Run the Look at Table Objective from the Objectives list; the arm moving in the 3D view confirms the whole path from the app to the board.

Step 8: Check the NPU From the Runtime​

Open Debugging → GPU Status in the Desktop App. The Simulation Rendering row reads Accelerated, because MuJoCo cameras render on the Adreno GPU. The ML Inference row reads CPU only until a model compiled for the NPU runs; with the shipped model bundles that is the expected state, not an error.

You can also confirm from the board that the Runtime's user can reach the NPU devices:

docker exec moveit_pro-runtime-1 bash -c 'id -nG 1000; ls -l /dev/fastrpc-cdsp /dev/dma_heap/system'
ubuntu dialout video nopasswdsudo messagebus realtime render tss fastrpc dmaheap
crw-rw-r-- 1 fastrpc fastrpc 10, 269 Oct 6 12:53 /dev/fastrpc-cdsp
crw-rw---- 1 root dmaheap 249, 0 Oct 6 12:53 /dev/dma_heap/system

The fastrpc and dmaheap entries in the first line are what let the Runtime open the NPU without running as root.

Run a Model on the NPU​

The NPU runs a model only when Qualcomm AI Hub compiled it for the board's chipset; Configure GPU Acceleration has the rules.

Export​

Run the export on any computer with Python 3.10, 3.11, or 3.12, the board included. qai-hub-models install sam3 installs the recipe's dependencies. You'll need your AI Hub API token which is under Settings after you sign in to Qualcomm AI Hub with a Qualcomm ID.

python3 -m venv ~/qai-hub-venv && source ~/qai-hub-venv/bin/activate
pip install qai-hub qai-hub-models
read -rsp 'AI Hub API token: ' QAIHUB_TOKEN && echo
qai-hub configure --api_token "$QAIHUB_TOKEN" && unset QAIHUB_TOKEN
qai-hub-models install sam3
qai-hub-models export sam3 --chipset qualcomm-qcs9075 --target-runtime precompiled_qnn_onnx --skip-profiling --skip-inferencing --output-dir ~/aihub/sam3

For SAM2, install and export sam2 the same way and add --model-type large, the size the shipped SAM2 bundles use. Unlike SAM3, the SAM2 export traces the model locally and uploads it before AI Hub compiles it, so on the board stop the Runtime first. Its weights download from Meta's public URL, so it needs no Hugging Face access.

Replace the chipset with your board's name from qai-hub list-devices. --skip-profiling and --skip-inferencing skip AI Hub's timing and accuracy runs on a hosted device, which the export does not need. The output directory holds a small .onnx file and a context binary per graph, named <graph>.bin or <graph>_qairt_context.bin depending on the AI Hub release; keep each pair together.

Load It​

Write a bundle manifest beside the exported files and pass its path to the Behavior's model_bundle_manifest port. AI Hub splits each model into different graphs from the shipped bundles, which limits the prompts each Behavior takes on the NPU.

GetMasks2DFromExemplar runs the SAM3 export for text prompts. Its manifest sits beside the two .onnx files and their context binaries:

schema_version: 1
model_id: sam3
model_version: ai-hub-qcs9075
graph_abi: moveit_pro.sam3_text.v1
runtimes:
onnxruntime:
vision_backbone: {path: vision_backbone.onnx, sha256: <sha256 of vision_backbone.onnx>}
head: {path: head.onnx, sha256: <sha256 of head.onnx>}

sha256sum vision_backbone.onnx head.onnx prints the two digests. The digest covers the .onnx file only, not the context binary it loads. AI Hub's SAM3 head has no geometry encoder, so a request with target or exemplar bounding boxes fails and names the moveit_pro.sam3.v1 bundle it needs; the shipped SAM3 bundle serves those prompts on the CPU. An exemplar_image without bounding boxes is not a prompt for this bundle.

GetMasks2DFromPointQuery runs the SAM2 export:

schema_version: 1
model_id: sam2_segment
model_version: ai-hub-qcs9075-large
graph_abi: moveit_pro.sam2_segment_fused.v1
runtimes:
onnxruntime:
image_encoder: {path: encoder.onnx, sha256: <sha256 of encoder.onnx>}
decoder: {path: decoder.onnx, sha256: <sha256 of decoder.onnx>}

sha256sum encoder.onnx decoder.onnx prints the two digests, which cover the .onnx files only. AI Hub's SAM2 image encoder also embeds the point prompt, so this bundle runs the encoder on every request and takes at most two foreground points and no mask prompt.

GetMasks2DAutomask stays on the CPU: automasking reuses one image encoding across its grid of points, which needs the separate prompt encoder of the shipped SAM2 bundle.

When a model compiled for the NPU is running, GPU Status shows the ML Inference row as Accelerated, and its Provider entry names QNNExecutionProvider.

Known Limitations​

  • Models shipped in the moveit_pro_example_ws run on the CPU. See the warning at the top of this page.
  • Objectives that capture a point cloud, run a long inference, and then transform the cloud can fail because the cloud's timestamp has left the transform buffer by the time it is used; re-capture the cloud after the inference step in such Objectives.
  • One Runtime per board. The IQ-9075 EVK has 34 GB of memory. Running the Runtime beside a SAM2 export, a workspace build, or another memory-heavy job on the same board is what the swap file in Step 3 protects against.

Troubleshooting​

What you seeCauseFix
apt fails with Certificate verification failed: The certificate is NOT trusted. The certificate chain uses not yet valid certificate.The board's clock is behind.Step 2.
moveit_pro run stops with Qualcomm NPU access is incomplete: ...A Qualcomm host package or device node is missing or not accessible. The message lists each one and the fix for it.Run the command the message prints, or run moveit_pro run without -y and accept the install offer. If the message names add-apt-repository, Qualcomm's package archive is not configured; add it first. If it says a node is reachable only by root or through an ACL, installing does not help; give the node a dedicated group with a udev rule, as Qualcomm FastRPC Access describes.
A simulation configuration stops at startup, and the drivers log shows Trying EGL candidate platform device ... just before the process running MuJoCo diesThe GPU driver in the image failed while creating the camera rendering context. That line names the device it was trying.Check that MOVEIT_TARGET is unset or -qnn. An empty MOVEIT_TARGET selects the CPU-only image, which cannot render on the Adreno; see Qualcomm NPUs. If the -qnn image fails the same way, report the line and the board's chipset to PickNik support.
/usr/lib/dsp/cdsp is empty after installing the packagescdsprpcd did not recognize your SoC and linked no DSP files.cat /sys/devices/soc0/soc_id and compare with the IDs in /usr/lib/systemd/system/cdsprpcd.service. If yours is absent, the NPU is not reachable from a container on this image. For a hardware configuration, run on CPU with an empty MOVEIT_TARGET, as Qualcomm FastRPC Access describes.
GPU Status shows ML Inference as CPU only after an ML ObjectiveThe model is not compiled for the NPU. This is the expected state for the shipped model bundles.Export SAM3 or SAM2 with AI Hub and point the Behavior's model_bundle_manifest at its bundle, as Load It describes.
The board stops answering SSH during a SAM2 export or a buildMemory exhausted with no swap.Power-cycle the board, then do Step 3 and stop the Runtime before exporting.
The Desktop App shows the board as not running after the board rebootedThe app is still attached to the previous Runtime instance.Disconnect and reconnect in the app's connection list.