Configure Qualcomm Dragonwing
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.
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.
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
- Power on the computer and open a terminal on it, locally or over SSH.
- Run
lsblk -o NAME,SIZE,MODEL,MOUNTPOINTS. The new drive is a disk with the size of the drive you installed and no mount points, for examplenvme0n1 931.5G Samsung SSD 980 1TB. The commands below call itnvme0n1; if your drive has a different name, use that name in every command. Confirm the name before you continue: steps 3 and 5 erase the drive. - Run
sudo fdisk /dev/nvme0n1.- Type
gand press Enter to create a new GPT partition table. - Type
nand press Enter to create a new partition. - Press Enter to accept the default partition number.
- Press Enter to accept the default first sector.
- Press Enter to accept the default last sector and use the remaining drive capacity.
- Type
wand press Enter to write the changes and exitfdisk.
- Type
- Run
lsblk /dev/nvme0n1and confirm that the drive now has one partition below it, named similarly tonvme0n1p1. If the partition has a different name, use that name in the next step. - Run
sudo mkfs.ext4 /dev/nvme0n1p1to format the new partition asext4. - Run
sudo mkdir /mnt/nvmeto create the mount point for the new partition. - Run
lsblk -f /dev/nvme0n1and copy the UUID of the new partition. - Edit
/etc/fstabwith a terminal editor likevimornano, and add a line in the formatUUID=<partition_uuid> /mnt/nvme ext4 defaults 0 2, replacing<partition_uuid>with the UUID from step 7. - Run
sudo reboot. - Once the computer has rebooted, run
mountpoint /mnt/nvmeand confirm that it prints/mnt/nvme is a mountpoint.
Move Docker's Storage
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. Each script block runs in a subshell, so a failed check stops that block without closing your terminal or SSH session.
-
Confirm that
/mnt/nvmeis the intended mounted filesystem and not a symlink:(set -euo pipefailtest -d /mnt/nvme && test ! -L /mnt/nvme &&mountpoint --quiet /mnt/nvme ||{ echo "NVMe target validation failed." >&2; exit 1; }) -
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 pipefailDRIVER_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;;esacsudo install -d -m 0755 /var/backupsBACKUP_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; thensudo cp -a /etc/docker/daemon.json "$BACKUP_DIR/daemon.json"elsesudo touch "$BACKUP_DIR/daemon.json.absent"fiif sudo test -e /etc/containerd/config.toml; thensudo cp -a /etc/containerd/config.toml "$BACKUP_DIR/containerd-config.toml"elsesudo touch "$BACKUP_DIR/containerd-config.toml.absent"fiif $MIGRATE_CONTAINERD; thensudo touch "$BACKUP_DIR/containerd-migration.enabled"fiprintf 'Configuration backup: %s\n' "$BACKUP_DIR")Set
BACKUP_DIRto the printed path in the shell you run the remaining steps from, and again if you open a new one. Each later block checks it before doing anything:BACKUP_DIR=/var/backups/moveit-pro-docker-storage.XXXXXXXXThe marker records the backend reported by
docker info; do not create or remove it based only on the Docker version. -
Record the current images and named volumes, then stop Docker and containerd:
(set -euo pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR not found." >&2; exit 1; }docker image lsdocker volume lssudo systemctl stop docker.socket docker.service containerdfor unit in docker.socket docker.service containerd; doif sudo systemctl is-active --quiet "$unit"; thenecho "$unit is still active; refusing to touch live data." >&2exit 1fidone) -
Copy the active data without deleting its source. Copy containerd only when step 2 created the migration marker. If either copy fails, the block removes everything it copied to
/mnt/nvmeand restarts the services, so you can rerun this step:(set -euo pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR not found." >&2; exit 1; }sudo test ! -e /mnt/nvme/docker ||{ echo "/mnt/nvme/docker already exists; refusing to merge data." >&2; exit 1; }if sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; thensudo test ! -e /mnt/nvme/containerd ||{ echo "/mnt/nvme/containerd already exists; refusing to merge data." >&2; exit 1; }fisudo install -d -m 0711 /mnt/nvme/dockersudo rsync -aHAXx --numeric-ids /var/lib/docker/ /mnt/nvme/docker/ ||{ echo "Docker data copy failed; removing the partial copy and restarting the services." >&2; sudo rm -rf /mnt/nvme/docker; sudo systemctl start containerd docker; exit 1; }if sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; thensudo install -d -m 0711 /mnt/nvme/containerdif sudo test -d /var/lib/containerd; thensudo rsync -aHAXx --numeric-ids /var/lib/containerd/ /mnt/nvme/containerd/ ||{ echo "containerd data copy failed; removing the partial copies and restarting the services." >&2; sudo rm -rf /mnt/nvme/containerd /mnt/nvme/docker; sudo systemctl start containerd docker; exit 1; }sudo touch "$BACKUP_DIR/containerd-data-source.present"fifi) -
Point Docker at
/mnt/nvme/dockerin/etc/docker/daemon.json, keeping its other settings. If step 2 created the containerd migration marker, also point containerd at/mnt/nvme/containerdin/etc/containerd/config.toml, writing containerd's default configuration first if that file does not exist. Otherwise the block leaves the containerd root unchanged:(set -euo pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR not found." >&2; exit 1; }sudo install -d -m 0755 /etc/dockersudo python3 - <<'EOF'import json, pathlibpath = pathlib.Path("/etc/docker/daemon.json")text = path.read_text() if path.exists() else ""try:config = json.loads(text) if text.strip() else {}except ValueError as error:raise SystemExit(f"{path} is not valid JSON ({error}); fix it, then rerun this step.")if not isinstance(config, dict):raise SystemExit(f"{path} must contain a JSON object; fix it, then rerun this step.")config["data-root"] = "/mnt/nvme/docker"path.write_text(json.dumps(config, indent=2) + "\n")EOFif sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; thenif ! sudo test -e /etc/containerd/config.toml; thensudo install -d -m 0755 /etc/containerdcontainerd config default | sudo tee /etc/containerd/config.toml >/dev/nullfisudo python3 - <<'EOF'import pathlib, repath = pathlib.Path("/etc/containerd/config.toml")lines = path.read_text().splitlines(keepends=True)# Top-level keys end at the first table header; replace any top-level root there.first_table = next((i for i, line in enumerate(lines) if re.match(r"\s*\[", line)), len(lines))kept = [line for i, line in enumerate(lines) if i >= first_table or not re.match(r"\s*root\s*=", line)]path.write_text('root = "/mnt/nvme/containerd"\n' + "".join(kept))EOFfi) -
Start the services and verify the migration before changing the old directories:
(set -euo pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR not found." >&2; exit 1; }sudo systemctl start containerd dockertest "$(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"; thensudo containerd config dump |grep -Eq "^[[:space:]]*root[[:space:]]*=[[:space:]]*[\"']/mnt/nvme/containerd[\"']" ||{ echo "containerd is not using the NVMe root." >&2; exit 1; }fidocker image lsdocker volume lssudo systemctl --no-pager --full status containerd dockermountpoint --quiet /mnt/nvmetest "$(findmnt -n -o TARGET -T /mnt/nvme/docker)" = /mnt/nvmeif sudo test -e "$BACKUP_DIR/containerd-migration.enabled"; thentest "$(findmnt -n -o TARGET -T /mnt/nvme/containerd)" = /mnt/nvmefi)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 pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR not found." >&2; exit 1; }sudo systemctl stop docker.socket docker.service containerdfor unit in docker.socket docker.service containerd; do! sudo systemctl is-active --quiet "$unit" ||{ echo "$unit is still active; aborting rollback." >&2; exit 1; }doneif sudo test -e "$BACKUP_DIR/daemon.json.absent"; thensudo rm -f /etc/docker/daemon.jsonelsesudo cp -a "$BACKUP_DIR/daemon.json" /etc/docker/daemon.jsonfiif sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; thensudo rm -f /etc/containerd/config.tomlelsesudo cp -a "$BACKUP_DIR/containerd-config.toml" /etc/containerd/config.tomlfisudo systemctl start containerd docker) -
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 pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR 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"; thentest "$(findmnt -n -o TARGET -T /mnt/nvme/containerd)" = /mnt/nvme ||{ echo "containerd destination is not on the NVMe mount." >&2; exit 1; }fiif sudo test -e "$BACKUP_DIR/daemon.json.absent"; then:elsesudo test -f "$BACKUP_DIR/daemon.json" ||{ echo "Docker configuration backup not found." >&2; exit 1; }fiif sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; then:elsesudo test -f "$BACKUP_DIR/containerd-config.toml" ||{ echo "containerd configuration backup not found." >&2; exit 1; }fisudo test -d /var/lib/docker || { echo "Docker data root not found." >&2; exit 1; }if sudo test -e /var/lib/docker.pre-nvme-migration; thenecho "Docker quarantine already exists." >&2exit 1fiif sudo test -e "$BACKUP_DIR/containerd-data-source.present"; thensudo test -d /var/lib/containerd ||{ echo "containerd data root not found." >&2; exit 1; }if sudo test -e /var/lib/containerd.pre-nvme-migration; thenecho "containerd quarantine already exists." >&2exit 1fifisudo systemctl stop docker.socket docker.service containerdfor 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; }donesudo mv /var/lib/docker /var/lib/docker.pre-nvme-migrationif sudo test -e "$BACKUP_DIR/containerd-data-source.present"; thensudo mv /var/lib/containerd /var/lib/containerd.pre-nvme-migrationfisudo 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.
-
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 pipefailsudo test -d "${BACKUP_DIR:?Set BACKUP_DIR to the path that step 2 printed.}" ||{ echo "Backup directory $BACKUP_DIR not found." >&2; exit 1; }if sudo test -e "$BACKUP_DIR/daemon.json.absent"; then:elsesudo test -f "$BACKUP_DIR/daemon.json" ||{ echo "Docker configuration backup not found." >&2; exit 1; }fiif sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; then:elsesudo test -f "$BACKUP_DIR/containerd-config.toml" ||{ echo "containerd configuration backup not found." >&2; exit 1; }fiROLLBACK_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"; thensudo 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; }fisudo systemctl stop docker.socket docker.service containerdfor unit in docker.socket docker.service containerd; do! sudo systemctl is-active --quiet "$unit" ||{ echo "$unit is still active; aborting rollback." >&2; exit 1; }doneif sudo test -e /var/lib/docker; thensudo mv /var/lib/docker "/var/lib/docker.$ROLLBACK_SUFFIX"fisudo mv /var/lib/docker.pre-nvme-migration /var/lib/dockerif sudo test -e "$BACKUP_DIR/containerd-data-source.present"; thenif sudo test -e /var/lib/containerd; thensudo mv /var/lib/containerd "/var/lib/containerd.$ROLLBACK_SUFFIX"fisudo mv /var/lib/containerd.pre-nvme-migration /var/lib/containerdfiif sudo test -e "$BACKUP_DIR/daemon.json.absent"; thensudo rm -f /etc/docker/daemon.jsonelsesudo cp -a "$BACKUP_DIR/daemon.json" /etc/docker/daemon.jsonfiif sudo test -e "$BACKUP_DIR/containerd-config.toml.absent"; thensudo rm -f /etc/containerd/config.tomlelsesudo cp -a "$BACKUP_DIR/containerd-config.toml" /etc/containerd/config.tomlfisudo 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
Copy each export directory into your user workspace, write a bundle manifest beside the exported files, and list the manifest under objectives.model_bundle_overrides in your robot configuration's config.yaml. The shipped Objectives then load the export with no edits. 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 Behavior does not fall back to the shipped SAM3 bundle; remove the override to run 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 and ignores the overrides: automasking reuses one image encoding across its grid of points, which needs the separate prompt encoder of the shipped SAM2 bundle.
Map each shipped bundle to its export in config.yaml. The Runtime mounts your user workspace at /home/<user>/user_ws/, and the replacement is the path inside the Runtime:
objectives:
model_bundle_overrides:
- bundle: "package://moveit_pro_sam3/models/model.yaml"
replacement: "/home/<user>/user_ws/aihub/sam3/model.yaml"
- bundle: "package://moveit_pro_sam2/models/model.yaml"
replacement: "/home/<user>/user_ws/aihub/sam2/model.yaml"
Restart the Runtime to apply the change. The first time an Objective loads a replaced bundle, the Behavior reports the export it loaded and the bundle it replaced. objectives.model_bundle_overrides lists the accepted forms.
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_wsrun 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 see | Cause | Fix |
|---|---|---|
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 dies | The 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 packages | cdsprpcd 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 Objective | The 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 list its bundle under objectives.model_bundle_overrides, as Load It describes. |
| The board stops answering SSH during a SAM2 export or a build | Memory 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 rebooted | The app is still attached to the previous Runtime instance. | Disconnect and reconnect in the app's connection list. |