Skip to main content

Admiral on Unitree Robots

This guide images the developer computer (PC2) of a Unitree G1 EDU with Admiral Base OS. PC2 is an NVIDIA Jetson Orin NX 16 GB on a Unitree carrier board with an NVMe SSD, mounted in the robot's chest. Everything is done in place over a USB-C cable. You don't need to remove the module or the SSD.

The process has two stages:

  1. Firmware. Update PC2's on-module boot firmware (QSPI: MB1/MB2, BPMP, UEFI) to JetPack 7.2 / L4T R39.2 with the G1 carrier fixes. This is done once per robot.
  2. Admiral. Write the Admiral raw disk image byte for byte to the NVMe, then boot, connect to the network and pair.
This erases PC2's NVMe

The Unitree factory OS on PC2 is replaced. Take a backup first (see Optional: back up the factory image) if you might want to return to it. The robot's locomotion computer (PC1) is not touched.


What you need​

ItemNotes
x86_64 Linux host, bare metalUbuntu 22.04 or 24.04 recommended. Flashing through a VM or hypervisor USB passthrough is unreliable: the board re-enumerates several times mid-flash.
USB-A → USB-C data cableFrom the host to PC2's flashing port. A USB 2.0 port or hub is the most reliable (see Troubleshooting).
Ethernet cableFrom the G1's Ethernet port to a network with DHCP and internet access, so Admiral can get an address and phone home.
Admiral raw image (.img)Jetson Orin NX image generated from a provisioning profile in Fleets → Provisioning in the Dashboard. See step 7 for why a pre-enrolled image is needed.
~25 GB free disk on the hostNVIDIA BSP, sample rootfs and build artefacts.

Install the host tools. On Debian-based hosts without /etc/lsb-release, NVIDIA's l4t_flash_prerequisites.sh exits early, so install the packages directly:

sudo apt-get update
sudo apt-get install -y git wget rsync cpp binutils cpio device-tree-compiler dosfstools \
e2fsprogs file gdisk iproute2 iputils-ping lbzip2 libxml2-utils netcat-openbsd \
nfs-kernel-server openssl parted python3-yaml python3-usb sshpass udev usbutils \
uuid-runtime whois xmlstarlet xxd zstd lz4 binfmt-support qemu-user-static

1. Locate the PC2 hardware​

Remove the G1's chest cover. PC2's carrier board has:

ComponentLocation
PWR / REC buttonsUpper right of the carrier, next to the power indicator LEDs
USB-C flashing portDirectly below the PWR/REC buttons

2. Put PC2 into recovery mode​

Connect the USB-C cable from the host to the flashing port first, then use any one of these methods.

A. Software (if PC2 is booted into Linux and you can get a shell):

sudo reboot --force forced-recovery

B. Buttons (robot powered on):

  1. Wait until all three power LEDs are steadily lit.
  2. Press and hold PWR + REC together for about 2 s. The LEDs drop from three to two, or go off.
  3. Release PWR, wait about 2 s, then release REC.

C. Cold start (most reliable, and required after a failed flash attempt):

  1. Power the robot off completely.
  2. Hold REC, power on, and release REC after about 2 s.
Re-enter recovery before every attempt

If a flash fails partway, the bootROM won't accept a new session. Power-cycle into recovery with method C before retrying.


3. How PC2 presents on USB​

PC2 shows up as a different USB device at each stage. Check with lsusb:

lsusb IDNameMeaning
0955:7323NVIDIA Corp. APX / T234 [Orin NX 16GB] recovery modeBootROM recovery. Ready to flash. (0955:7423 = Orin NX 8 GB.)
0955:7035Jetson device in initrd flashing modeNVIDIA's flashing initrd is running. USB network at fc00:1:1::2 (IPv6). This is a temporary, mid-flash stage.
0955:7020L4T (Linux for Tegra) running on TegraA JetPack/Ubuntu OS has booted. Exposes USB networking (board at 192.168.55.1) and a serial console (/dev/ttyACM0).
(nothing)Admiral Base OS is running (it doesn't expose a USB gadget), the board is powered off, or it's on the cable/port you're not watching.

Confirm the link speed. 480M (USB 2.0) is the most reliable for flashing:

lsusb -t # find the 0955 device: 480M = USB2, 5000M/10000M = USB3

4. Update the firmware to JetPack 7.2​

This uses the open-source unitree-jetpack tooling (branch g1). It wraps NVIDIA's L4T R39.2 BSP and adds the G1 carrier patches:

  • MB2 carrier-EEPROM fix: the G1 carrier has no EEPROM, and boot stalls in MB2 without this fix.
  • Carrier device tree: fixes the USB3 lane wiring so recovery USB and the host ports work.
git clone -b g1 https://github.com/legion1581/unitree-jetpack.git
cd unitree-jetpack
./g1_custom_jetpack.sh status # expect: APX — bootROM recovery (ready)
./g1_custom_jetpack.sh -j 7.2 flash all # type 'yes' to confirm

The first run downloads about 3.3 GB from NVIDIA and builds the BSP (20–40 min). The flash itself takes about 5–10 min. A successful run ends with:

Flash is successful
Reboot device
[+] flash 'all' complete

flash all writes the R39.2 firmware to QSPI and a stock JetPack 7.2 Ubuntu image to the NVMe. The Ubuntu image is temporary: it's used to confirm the firmware in the next step, then replaced by Admiral.

Host OpenSSH 10+ (Debian 13 or later)

NVIDIA's recovery-ramdisk builder generates a DSA host key, which OpenSSH 10 no longer supports. The flash then fails right after _BASE_KERNEL_VERSION=… with command is failed. After the BSP has been built, make that step non-fatal and re-run the flash:

sed -i 's|\(ssh-keygen -t dsa .*>/dev/null 2>&1\);check_error|\1 \|\| true|' \
bsp/7.2/Linux_for_Tegra/tools/ota_tools/version_upgrade/ota_make_recovery_img_dtb.sh

5. Validate the firmware​

After the flash, PC2 reboots into the temporary Ubuntu image (about 1–2 min) and enumerates as 0955:7020. Log in over the USB serial console:

sudo screen /dev/ttyACM0 115200 # login: unitree / 123

On PC2:

sudo nvbootctrl dump-slots-info # expect: Current version: 39.2.0, both slots "normal"
head -1 /etc/nv_tegra_release # expect: # R39 (release), REVISION: 2.0

If Current version reports 39.2.0, the firmware is ready for Admiral. While you're logged in, put PC2 back into recovery for the next step:

sudo reboot --force forced-recovery # host should now show 0955:7323 again

6. Flash Admiral to the NVMe​

The Admiral image is a complete GPT disk image (ESP, A/B boot slots, data partitions). It's written byte for byte to /dev/nvme0n1 using NVIDIA's raw-image restore. The tool boots the flashing initrd over USB, streams the image over NFS, writes it with dd, and then reads it back and checks the SHA-256.

Place the image inside the unitree-jetpack directory. The tool hard-links it into the BSP, which only works on the same filesystem.

cp ~/Downloads/admiral-orin-nx-g1.img ./ # your downloaded Admiral image
cd bsp/7.2/Linux_for_Tegra
sudo ./tools/backup_restore/l4t_backup_restore.sh -r -e nvme0n1 \
--raw-image "$(realpath ../../../admiral-orin-nx-g1.img)" \
--network usb0 jetson-orin-nano-devkit

(jetson-orin-nano-devkit is the correct NVIDIA board config for the Orin NX on this carrier.) Use the image downloaded from your G1 fleet's provisioning profile (see step 7).

A successful write ends with:

nvrestore_partitions.sh Restoring /dev/nvme0n1 with image /mnt/admiral-orin-nx-g1.img...
Done
nvrestore_partitions.sh: Successful to restore with raw disk image /mnt/admiral-orin-nx-g1.img
Operation finishes. You can manually reset the device

Power-cycle the robot to boot Admiral. PC2 drops off USB entirely. That's expected, because Admiral doesn't expose a USB device.

Secondary header doesn't reside at the end of the disk

The image is smaller than the NVMe, so its backup GPT sits at the end of the image, not the end of the disk. Tools like sgdisk report this. It's expected and harmless; the firmware boots from the primary GPT.


7. Connect to the network and come online​

Use a pre-enrolled image​

The G1 has no usable display or serial console for reading Admiral's on-screen pairing code. The USB-C flashing port doesn't help either: the /dev/ttyACM0 console in step 5 is a USB gadget set up by the temporary JetPack Ubuntu image, and Admiral doesn't provide one.

So generate the image in step 6 from a provisioning profile: Fleets → your G1 fleet → Provisioning → New Profile. The image is pre-enrolled, so the robot claims itself into that fleet on first boot without a pairing code. See Auto-Provisioning for the profile options, including pre-loaded Wi-Fi networks.

Wired first​

Before powering on, connect the G1's Ethernet port to a network with DHCP and outbound HTTPS (443) access to api.admrl.co. On first boot Admiral brings up the wired interface, takes a DHCP address and phones home. No local setup is needed.

Keep 192.168.123.0/24 free

Unitree's internal robot network uses static addresses in 192.168.123.0/24 on the same switch (for example, the locomotion computer at 192.168.123.161). Don't use that subnet for your LAN's DHCP pool, or addresses may clash.

Within a few minutes the robot appears under Devices in the Dashboard, in the profile's fleet. Rename it there (e.g. G1-Lab-01).

Then Wi-Fi​

Once the robot is online over Ethernet, configure Wi-Fi from the Dashboard. Fleet-level Wi-Fi networks are pushed to the device automatically (see Networking & Failover), as are any networks pre-loaded in the provisioning profile. You can then unplug the Ethernet cable if you want.


8. Deploy a workload​

Start from the Unitree G1 clean-slate workload:

github.com/AdmrlOS/workload-unitree-g1

Use it as the source for your workload deployments: fork it, add your application, build a linux/arm64 image, push it to a registry the robot can reach, and deploy it as a Configuration to your G1 fleet.

Recommended configuration settings for the G1:

SettingValueWhy
isolateDevicesfalsePass-through of /dev: GPU (CUDA), cameras, serial, CAN, USB
interactivefalseHeadless robot services (true only for an on-robot display UI)
Host NetworkonUnitree SDK2 / DDS traffic to the robot on 192.168.123.0/24
Volumese.g. /var/lib/<app>Durable data. The rootfs is replaced on each image update.

For GPU workloads, see Running GPU applications on Jetson Orin. Admiral supplies the NVIDIA driver; your image brings a Jetson/L4T-compatible CUDA runtime.


Optional: back up the factory image​

Before step 4, with PC2 in recovery (0955:7323):

./g1_custom_jetpack.sh backup # -> backups/<timestamp>_jp<ver>_l4t<ver>/

To restore it later, put PC2 into recovery and run ./g1_custom_jetpack.sh restore.


Troubleshooting​

SymptomCause / fix
ERROR: might be timeout in USB write right after Sending bct_brThe bootROM is stuck from an earlier attempt: cold-start into recovery (method C) and retry. If it still fails, use a USB 2.0 port or hub (check lsusb -t for 480M) and a bare-metal host, not a VM.
FileNotFoundError: … 'cpp'Host tools are missing. Install the package list in What you need.
command is failed right after _BASE_KERNEL_VERSION=…Host has OpenSSH 10+. Apply the DSA fix in step 4.
-c and --external-device must be specified togetherUse flash all as shown. The repo's flash qspi mode doesn't work with R39's tooling.
Flash stalls at "Waiting for target to boot-up" or the network/SSH stepUSB re-enumeration is being lost (VM passthrough or hub), or a host firewall is blocking NFS/SSH on the new USB interface. Make sure nfs-server is running.
ln: failed to create hard link during the Admiral writeThe image is on a different filesystem from the BSP. Copy it into unitree-jetpack/.
Nothing on USB after the Admiral flashExpected. Admiral doesn't expose a USB device; check the Dashboard instead.
Device never appears in the DashboardCheck that the image came from a provisioning profile (a generic image waits for a pairing code you can't read on the G1), then check the Ethernet link, DHCP and outbound 443.
Need to re-imageRepeat step 2 and step 6. The firmware (step 4) only needs doing once.