axol tracker.* commands are the CLI side of Mantis Tracking: the control panel’s Mantis settings → Tracking flow runs the same commands on the host. This page is the flag and file-format reference; the setup walkthrough for each source lives on the tracking page.
Direct
axol collect-data --mantis true runs inherit the source, channel map, bindings, and (for Quest) calibration key saved by the panel, own their bridge, and enforce the same readiness gates. Do not launch a second tracker.bridge beside a managed run — it stays view-only and cannot win pose control. (axol teleop --mantis never tracks: it drives the grippers from the rig triggers over CAN and starts no bridge.)
axol tracker.bridge
Streams a hardware tracker backend’s poses (and each side’s trigger value) to the VR server in the standard VRFrame format.
A side whose tracker loses tracking (occlusion, SLAM relocalising) holds its last good pose and logs a warning rather than feeding IK a glitch. A stale trigger node (no frames for ~250 ms) likewise holds the last grip command and logs the dropout. A managed bridge freezes motion, waits for both trackers to become fresh, then requires the two-trigger start gesture before it re-anchors; the standalone bridge keeps its manual controls: Enter toggles engage, r returns to rest, q quits. A standalone bridge warns and continues fully open when a configured trigger is absent; a managed production run fails readiness instead.
Hardware-free protocol demo
To exercise the absolute-mapping protocol without a Mantis source, run a generic sim server and a standalone static bridge:static over synthetic when isolating engage/reset plumbing. This proves only the JSON/IK/input path — it selects no Mantis source, resolves no tracker→TCP transform, and is not acceptance. Both hardware-free backends bind their own devices and need no tracker.identify.
axol tracker.identify
Discovers the powered-on trackers, then asks you to move each rig in turn; the device with the most motion is bound to that side. For Lighthouse it runs the same channel check as tracker.lighthouse.check after discovery and refuses to bind while a clash is present. Bindings are kept separately per family so the panel can switch between Lighthouse and Ultimate.
Saves
~/.almond/tracker/config.json:
axol tracker.pair
Pairs one VIVE Tracker 3.0 with its Watchman dongle through survive-cli --pair-device 1 (which needs the libusb backend tracker.install builds). The tracker must be unplugged from USB and blinking blue. Run it once per tracker/dongle pair.
axol tracker.install
Installs the tested libsurvive commit f1e6eddb669320f2a30760f4b42936bdb4306da0: build dependencies, survive-cli built with Eigen and the native Linux libusb backend (not OpenBLAS or HIDAPI), and the Vive udev rule. axol provision runs it automatically; rerun it directly to repair. When the machine-wide install proof under /var/lib/almond/ is missing or in an older format but the local build cache already holds the pinned build with the same options (for example after updating Axol), the command only refreshes the udev rule and re-attests the installed files instead of rebuilding. The proof is machine-wide, so root provisioning/serve and an operator CLI agree even though their writable build caches differ.
On ARM, avoid linking libsurvive against OpenBLAS (known instability/CPU blowups);
-DUSE_EIGEN=ON sidesteps it. Close SteamVR if one is somehow present — it competes for the devices.axol tracker.lighthouse.check
~/.almond/tracker/lighthouse_survey.json and gates the Trackers step of the panel flow.
axol tracker.ultimate.install
fcfcd33f4c1f16b0d84f5f741dc1319abdc7942a, the hid>=1.0.5 Python binding (not the unrelated hidapi package), system HID libraries, and a persistent udev rule for dongle 0bb4:0350. Safe to rerun; do not replace it with an unpinned uv pip install or a one-off chmod /dev/hidraw*. The Windows pairing and mapping steps that must precede it are on the tracking page.
Ultimate Wi-Fi file
ultimate_wifi.json is the private shared-map AP configuration the panel’s Wi-Fi step writes. For a CLI/manual setup, create ~/.almond/tracker/ultimate_wifi.json (or $ALMOND_HOME/tracker/ultimate_wifi.json) with this shape and chmod 600 it:
country is consumed directly, so the SSID/password/frequency are fallback configuration, not a promise of the active broadcast network. Choose a country and frequency valid for the workspace. The readiness check reports only validity, never credential values.
axol tracker.ultimate.check
tracker.identify again when you need to prove live poses.
Transform file
~/.almond/mantis/tcp_transform.json holds measured tracker→TCP transforms, keyed per side by the identified device. The built-in factory values for the standard Tracker 3.0 and Ultimate flat-back mounts apply automatically when no conflicting entry exists; a saved entry is a higher-priority override. Quest has no factory value and needs an entry for both sides.
~/.almond/tracker/config.json (or the Quest key the panel reports) and the measured values; the identity values above are schema placeholders, not a usable calibration. pos is the gripper TCP origin in the tracker’s local frame in metres; quat is the TCP orientation in that frame as [qx, qy, qz, qw], normalized on load. The file can also be written through PUT /api/tracker/calibration/<source>; the control panel does not edit it.
Rules the loader enforces:
- Quest production collection accepts only a
quest:<reported-profile>:gripentry on both sides. A barequestkey, a different Touch profile, missing profile metadata, or atarget-rayentry fails closed. - Ultimate entries must carry
ultimate_pose_conventionmatching both the convention used during the measurement and the activeultimate_quat_order/ultimate_up_axis. A convention-less or mismatched entry is not authoritative and suppresses the factory fallback for that side until fixed or removed, because it may represent a non-standard mount. - An old unkeyed entry, a bare
survive/ultimatekey, or a same-family entry left behind after rebinding also suppresses the factory value for that side until it is re-keyed or deleted. - A standalone Advanced/CLI transform can support a bring-up capture, but production
collect-datarequires an active device-keyed override or an approved factory value on both sides;--mantis_allow_uncalibrated trueis only for a calibration capture that will not be used for training.
Ultimate factory value
The Ultimate constant derives from the Tracker 3.0 mount transform. In the shared gripper/CAD frame the V3 and Ultimate reference origins are[47, 0, 35] mm and [47, 0, 46] mm, so the tracker-origin change is ΔO = [0, 0, +11] mm. For the stored tracker→TCP transform, Δp = −R·ΔO; the inherited quaternion [0.7071068, 0, 0, 0.7071068] is R = Rx(+90°) and maps CAD +z to tracker −y, so Δp = [0, +11, 0] mm: the 35.5 mm tracker-y component becomes 46.5 mm while the −92 mm forward component is unchanged. The approved value is [0, 0.0465, -0.092, 0.7071068, 0, 0, 0.7071068] for both sides; the tracker-local axes and flat-back orientation match V3, so there is no additional rotation. This mount-frame calculation is distinct from the bridge’s world-frame z-up→y-up relabel.
Deriving a missing constant
If a source transform is missing — Quest, or an Ultimate run under a non-factory parser convention — first pass the acceptance checks for side, scale, and recovery, then run a short capture with--mantis_allow_uncalibrated true, inspect it to derive the constant, install it under the exact source/device key, and repeat the loss/recovery check. Normal collect-data --mantis stays blocked until both transforms resolve; never mix an uncalibrated capture into training data.