uv tool install can’t manage on its own:
-
adb + the Quest udev rule — for the Quest-over-USB controller transport (the
adb reversetunnel that streams poses over the cable). Self-gates onapt. -
tracker.install— a pinned libsurvive build and Vive USB permissions for Mantis Lighthouse tracking. -
zed.driver— replaces a ZED Box’s outdated factory GMSL camera driver (stereolabs-zedbox-duoon the Duo,stereolabs-zedbox-minion the Mini,stereolabs-zedlink-duofor a ZED Link Duo card on a Jetson AGX Orin) with the release pinned for the ZED SDK. Takes effect at boot, so provisioning ends in a reboot (see below). A no-op on a host with no Stereolabs driver package installed; warns about a Stereolabs driver package it has no pin for. -
board IMU (
gyro.install) — grants theimugroup access to the carrier board’s BMI088 sampling timer, Jelly’s heading-hold yaw reference, so teleop can start it without root. Self-gates on the board driver’s presence. -
CAN e-stop purge grant — writes
/etc/sudoers.d/50-axol-can-purgeso a manualaxol serve/axol teleopcan flap a CAN interface without a password when motor power dies. The realtime core purges the interface’s queued position commands the moment it declares the bus stalled; without this grant that escalation fails on a non-root session, the frames survive, and the arm jerks to its pre-e-stop command on the next bring-up (seecan.setup). The rule covers only the root-owned reset and bring-up scripts (the arm hub is USB-reset, since its firmware keeps frames a flap cannot reach) and the named CAN interfaces, and is checked withvisudobefore it is installed. Takes effect immediately. -
real-time scheduling grant — writes
/etc/security/limits.d/50-axol-rtprio.confso your login can run the control loop and the camera relay’s capture chainSCHED_FIFOfrom a manualaxol serve/axol teleop(the systemd unit already hasLimitRTPRIO). Login shells otherwise start with anrtpriolimit of zero, and then:- the control loop refuses to start —
axol teleop,collect-data,collect-daggerandrun-policyraiserefusing to run without real-time schedulingrather than run a loop that lands roughly one tick in fifty 15–65 ms late (felt as the arms hitching and lunging, and masked by turning the cameras off). SetAXOL_ALLOW_CFS_CONTROL=1to run degraded anyway. - the relay silently falls back to CFS —
camera capture threads stay SCHED_OTHERat startup, and recordings under load discard episodes on skipped exposures.
ulimit -r(should print20). - the control loop refuses to start —
-
zed.install— the pyzed bindings (not on PyPI; needs the ZED SDK). -
gst.install— the GStreamer + PyGObjectappsinkstack (PyGObject builds against the system gobject-introspection and is dropped whenever the tool environment is rebuilt, e.g. by a release upgrade). -
gst.build-zed— the patchedzedxonesrc/zedsrcplugins (sensor-accurate PTS so collected images line up with joint samples). -
rt.install— the requiredaxol-rtrealtime core hardware-control binary (Rust toolchain via rustup if needed; sources fetched at the installed package’s exact ref on tool installs). Self-gates on network/toolchain availability.
apt / NVENC — so plain axol provision is safe to run on any host and
re-run anytime.
Use the strict form when the required control core must be present:
--require-rt leaves the optional steps best-effort but makes the command exit
non-zero if rt.install fails. The hosted installer and the
axol serve self-updater use this form, so an RT build or
capability-setup failure aborts installation or blocks the restart onto updated
code. Both still share this single provisioning path, keeping their steps in
lockstep.
When only optional steps fail (Lighthouse tracking, the patched zed-gstreamer
plugins, adb, …), the last line names them (Optional provisioning steps failed: ...) and the command exits 3. The installer then finishes with a
warning instead of aborting. A provision run by axol serve exits 0 in that
case, so an update still restarts onto the new release, and the control panel
shows the missing steps as a warning. A failed rt.install exits 1.
Every apt-get / dpkg run is protected from interruption: it never times
out, and it runs in its own session and systemd scope, so a stopped or
restarted axol.service or a timed-out update leaves it to finish. A package
manager killed partway leaves dpkg half-configured, and every later install on
the host then fails. When apt fails, the log names the package dpkg could not
process. Repair it with sudo dpkg --configure -a, then re-run axol provision.
Provisioning holds the host-wide update lock, which is root-only. Run from a terminal as a regular user, the steps still execute as that user (their uv, download caches, and Python environment) and only a small lock-holder process escalates via sudo (one password prompt, shared with the steps that need root). Without a usable sudo the command exits with a message instead of a traceback.
- host tuning — last, the runtime tuning for the host it runs on, detected from what the host exposes. On a Jetson (Orin NX 16GB, AGX Orin, Thor T5000): the max power mode, the NVENC/VIC/GPU and CPU clock pins, the CAN adapters’ interrupt steered onto a CAN core, and the Argus camera daemon run
SCHED_FIFOon the camera cores — seejetson.setupfor why each matters. On a Raspberry Pi 5 or a workstation there is nothing to tune. Either way it logs the board and the CPU core layout the control, IK, CAN, camera and recorder work will pin to, e.g.core layout: 14 cores online: CAN 12-13, control 2, IK 3, relay 4-5, recorder 10-11, camera 0-1,5-9.
This tuning resets at every reboot, so
axol provision --boot runs just this step — no installs, no update lock — and the installer’s axol.service runs it as its ExecStartPre at each boot. You never need a second command: after pulling a fix, axol provision (and logging in again for the scheduling grant) is enough. axol jetson.setup still works as an alias of --boot, for units written by older installers. The axol serve self-updater’s provisioning pass skips this step: it can overlap a robot session, and the service restart the update ends with re-tunes the host before serve starts.Reboots
Some steps only take effect at boot: a new ZED Box camera driver, or a Jetson power mode thatnvpmodel will only switch across a reboot. When one of them runs, a clean axol provision reboots the host at the end of the run, which ends an SSH session. A run with failed steps never reboots. The pending reboot is remembered until the host restarts, so the successful retry still reboots, even though the step that asked is now a no-op.
The hosted installer provisions with
--no-reboot and finishes with --apply-reboot, after writing the service. The axol serve self-updater provisions with --no-reboot, then reboots the host in place of its usual service restart, and only while no operation is running. A provision started by axol serve never reboots on its own, whatever its flags. If a reboot didn’t clear the step that asked for it, provision won’t reboot again for the same reason. It reports the problem instead of looping the robot through reboots.
When there’s no ZED SDK at /usr/local/zed, only the pyzed and zed-gstreamer build steps are skipped. The GStreamer/NVENC steps also skip on non-Jetson hosts, where that pipeline cannot run. adb, board access, the scheduling grant, Lighthouse tracking, and axol-rt are still provisioned on normal Linux workstations.
See Development install → Headset-video stack for when you’d run this by hand.