gs_usb kernel driver if the kernel lacks it (see can.driver — needed on NVIDIA Jetson / ZED Box), writes persistent udev rules, assigns fixed interface names, registers a startup script in the root crontab, installs a hotplug bring-up service, and brings up the interfaces immediately.
Driver selection is host-safe: NVIDIA L4T/Jetson kernels that omit gs_usb build the vendored backport; an older or unverifiable native Linux driver is conservatively replaced; a Linux 6.13+ in-tree driver (including a resolved weak-updates symlink) is kept when it advertises every ID needed by the adapters currently attached. Maintained 6.2+ distro kernels with a backport are also kept when kmod metadata proves endpoint discovery, including signed Secure Boot builds. On a Secure Boot/signature-enforcing host that genuinely needs an unsigned backport, setup stops before changing the active module and tells you to sign/enroll it or use a newer native kernel. A trusted, signed legacy Almond backport is recognized only when its selected/loaded source identity, canonical path, and USB-ID fingerprint match. Installation verifies the selected and loaded module identities and restores the previous module/config on failure.
Up to four buses, each optional and independent:
- Left arm →
can_alm_axol_l(Axol Hub channel 0) - Right arm →
can_alm_axol_r(Axol Hub channel 1) - Powered cart’s wheel bus (if present) →
can_alm_axol_b - Chest bus / lift controller (if present) →
can_alm_axol_c
can.setup identifies all of them by probing the devices on their CAN lines: an Axol arm has a shoulder motor at ID 0x001, a Mantis publishes trigger frames on ID 0x009 (both the 6-byte and current 7-byte firmware frame are accepted), the wheel bus has Damiao motors at IDs 0x01–0x04, and the jelly_legs lift controller answers a status request on the chest bus. The attached devices must be powered for automatic identification. Already configured adapters are probed again when attached, so a live response corrects stale Axol/Mantis or wheel/chest assignments from an earlier setup. If an apparently-up gs_usb adapter is not receiving, setup resets the relevant bus and retries before deciding it is silent. A silent or unplugged configured single-channel adapter keeps its previous assignment as an explicitly unverified fallback; a new silent adapter is offered to the operator and can replace that fallback. An unresolved adapter pinned to two roles is rejected before rules are written. For a silent dual-channel hub, the normal axol can.setup prompt lets the operator keep the previous role or choose Axol/Mantis; no separate reassignment command is needed. Each identified adapter is pinned by USB serial, and the startup scripts bring every configured interface up while skipping anything unplugged. A Jetson host’s built-in system CAN controller is ignored entirely.
Running axol can.setup is deliberately a disruptive link reset for every attached adapter in its supported scope: the Axol arm pair, powered-cart wheel bus, chest/lift bus, and Mantis pair are taken down, restored to 1 Mbit/s with the expected queue length, and brought back up before they are trusted. Both channels of a dual hub go down before either comes back up; wheel and lift adapters reset independently. This does not reboot/zero/calibrate an actuator, clear a healthy saved assignment, reload a healthy USB driver, or touch the Jetson’s built-in mttcan interfaces. As a one-time migration on older Jetson installs, setup automatically rebuilds an Almond-managed gs_usb override that predates reliable build identity; overrides outside Almond’s managed path and signature-enforcing hosts still fail closed. Do not run setup during an active robot operation.
The Mantis handheld data-collection rig uses the same dual-channel CAN board as the arm hub, with one gripper bus per channel — each bus carries that side’s Damiao gripper motor (ID 0x008) and trigger node (ID 0x009):
- Channel 0 →
can_mantis_l(left gripper) - Channel 1 →
can_mantis_r(right gripper)
axol can.setup run. Its managed names are isolated from the Axol profile, so configuring or correcting one profile does not evict the other. A replug of the handheld brings its interfaces back up without operator action. If a trigger and arm motor are both silent, setup asks whether that adapter is Axol, Mantis, or should be skipped; no hardware-profile flag is needed.
sudo will be invoked automatically where required.After setup, the interfaces come up automatically on every reboot (via a
@reboot cron hook) and whenever the adapter re-enumerates mid-session: a udev rule pulls in the axol-can-up.service bring-up whenever the hub (re-)appears, so a replug — or a USB drop caused by motor EMI — heals itself within a second or two without operator action. You don’t need can.setup just to use the cart — axol teleop brings the cart’s own interface up on its own — but pinning it gives the adapter a stable name across reboots.The same bring-up script also recovers the bus after an emergency stop. Cutting motor power mid-motion leaves the in-flight CAN commands unacknowledged, so without intervention they would all replay the instant the arm is powered back on — snapping it to its pre-e-stop position. The CAN layer detects the stalled bus, flaps the interfaces to drop the stale frames, and holds every send until a probe frame is acknowledged on the wire again, so the arm stays put until the bus is genuinely back online.A momentary TX-queue overflow is handled more gently. Both arm channels share one dual-channel USB adapter, so a brief stall of its USB pipe — e.g. camera traffic during data collection — can back the queue up while the bus itself is healthy and draining. Told apart from a dead bus by how long it persists, a brief overflow just drops the frame (a rate-limited warning) and the next command proceeds, so heavy USB traffic no longer crashes teleop or data collection mid-session; only an overflow that keeps persisting (nothing draining) escalates to the interface flap and probe-gated hold above.
On Raspberry Pi 5 hosts,
can.setup additionally applies an RP1 USB quirk (axol-rp1-usb-quirk.service): the Pi 5’s USB controllers ship with a hair-trigger babble/loss-of-activity detector that EMI from the arms can trip, kicking the hub off the bus (disabled by hub (EMI?) in dmesg). The quirk enables the controller’s noise filter — the workaround recommended by Raspberry Pi engineers. If disconnects persist, add a ferrite choke to (or shield) the hub’s USB cable, or put a small powered USB hub between the Pi and the Axol Hub.