Skip to main content
One-time setup for the Almond Axol CAN buses. Installs the 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. 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
The Axol Hub (dual-channel USB CAN adapter) is told apart from the single-channel adapters by its channel count. The wheel bus and the chest bus each use a physically identical single-channel CANable, so can.setup identifies them by probing: the bus whose Damiao wheel motors (CAN IDs 0x010x04) answer a register read is the wheel bus, and the bus whose jelly_legs lift controller answers a status request is the chest bus. For the probe to work the wheel motors / chest board must be powered; an adapter where nothing answers is offered to the operator to assign (or skip) instead of being guessed at. Each identified adapter is pinned by serial in the same udev rules file, and the @reboot startup script brings every configured interface up, skipping any that isn’t attached — so an unplugged cart or chest never blocks the arms, and vice versa. (A Jetson host’s built-in system CAN controller is ignored entirely.)
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.