Part I: The ROS 2 stack · §10 of 43

10.1 Hardware you need

  • Two CAN-FD capable USB-CAN adapters supported by SocketCAN (OpenArm uses gs_usb-class adapters such as CANable 2.0 / candleLight-FD firmware), one per arm.
  • Power supply for the arms (per Enactic docs), with an emergency stop / power cut you can reach instantly.
  • Bus termination per the OpenArm hardware docs.

Default mapping used by the launch files:

ArmInterfaceMotor IDs (send → receive)
Rightcan0J1–J7: 0x01…0x07 → 0x11…0x17; gripper 0x08 → 0x18
Leftcan1same IDs on its own bus

Both arms use the same IDs, which is why each arm must be on its own bus.

zeus

One PEAK PCAN-USB Pro FD (lsusb: 0c72:0011, driver pcan_usb_pro_fd) with two channels, not two gs_usb adapters: can0 = channel 1 = left arm, can1 = channel 2 = right arm, the reverse of the launch defaults (§22). Both appear on the same USB device, so the names never swap. The adapter is plugged into a USB-C docking station, which adds USB latency (§12.5).

10.2 Install the CAN tools on the host

Interface setup has to happen on the host, or in a container with NET_ADMIN. The easiest way to get the tools on the host is Enactic’s PPA:

sudo add-apt-repository -y ppa:openarm/main
sudo apt update
sudo apt install -y libopenarm-can-dev openarm-can-utils can-utils

(can-utils gives candump/cansend for low-level debugging.) Alternatively, run the container’s copy: /root/ros2_ws/install/openarm_can/bin/openarm-can-cli. It works from the container because of --network host, but can_configure needs --cap-add NET_ADMIN.

10.3 Identify and name the adapters

USB-CAN adapters enumerate in plug-in order, so can0 and can1 can swap between boots. Swapping them sends left-arm commands to the right arm. Plug in one adapter at a time, or check serials:

ip -details link show type can
udevadm info -a /sys/class/net/can0 | grep -m1 'ATTRS{serial}'

Pin the names with a udev rule (on the host), e.g. /etc/udev/rules.d/80-openarm-can.rules:

SUBSYSTEM=="net", ACTION=="add", ATTRS{serial}=="<RIGHT_ADAPTER_SERIAL>", NAME="can0"
SUBSYSTEM=="net", ACTION=="add", ATTRS{serial}=="<LEFT_ADAPTER_SERIAL>",  NAME="can1"

Then run sudo udevadm control --reload && sudo udevadm trigger and replug. Label the cables too.

On zeus this isn’t needed: the single two-channel PCAN adapter always gives can0 = channel 1 and can1 = channel 2.

10.4 Bring the interfaces up (CAN-FD, 1 Mbit/s / 5 Mbit/s)

openarm-can-cli -i can0 can_configure          # defaults: 1 Mbit/s nominal, 5 Mbit/s data, FD on
openarm-can-cli -i can1 can_configure

Equivalent raw command:

sudo ip link set can0 down
sudo ip link set can0 type can bitrate 1000000 dbitrate 5000000 fd on
sudo ip link set can0 up

For classic CAN 2.0 (only if your motors are configured that way):

openarm-can-cli -i can0 can_configure -d 1000000 --no-fd
# and launch with can_fd:=false

Notes:

  • By default the interface stays down after a bus-off (--rm 0) so you notice faults. --rm 100 auto-restarts.
  • Interface config is lost on unplug or reboot. Rerun it, or add a systemd/networkd unit.
  • Check it: ip -details -statistics link show can0 should show state ERROR-ACTIVE, fd on, and no growing error counters.

On zeus (host terminal, after every reboot or adapter re-plug; the interfaces come back down):

sudo ip link set can0 type can bitrate 1000000 dbitrate 5000000 fd on    # interface must be down to change it
sudo ip link set can0 up                                                    # same two lines for can1
ip -details link show can0 | grep -E "state|bitrate"                       # state UP, ERROR-ACTIVE, bitrate 1000000, dbitrate 5000000
timeout 3 candump can0                                                      # motors off: prints nothing

mtu 72 in ip link = CAN-FD frames (mtu 16 = classic CAN, i.e. not configured yet). The sample points the kernel picks (0.75 / 0.75) are the same as openarm-can-cli can_configure’s defaults; the LeRobot setup script uses the same 1M/5M FD settings.

Why the bit rates must be set by hand

CAN has no clock wire and no auto-negotiation: every device on a bus must already agree on how long a bit lasts, or frames become errors and the bus shuts down (bus-off). The motors’ rates are stored in their firmware (1 Mbit/s for the start of each frame, where devices compete for the bus; 5 Mbit/s for the data part, the “FD” in CAN-FD). Linux can’t ask them, so the adapter is told; the setting lives in the driver and is lost on reboot or unplug.

10.5 Check the motors before ROS

openarm-can-cli -i can0 discover               # should list IDs 1–8 (7 joints + gripper)
openarm-can-cli -i can0 monitor                # live pos/vel/torque/temp for IDs 1–8 (6 s by default)
openarm-can-cli -i can0 monitor -d 60000       # watch for 60 s; move the arm by hand
openarm-can-cli -i can0 diagnose               # status/error codes and temp/torque ranges
openarm-can-cli -i can0 clear_error            # clear latched motor errors
candump can0                                    # raw traffic

Repeat for can1. With monitor running, move each joint by hand and check that:

  • each joint changes the motor you expect (ID n = joint n, ID 8 = gripper);
  • the sign makes sense against RViz;
  • at the physical zero pose the positions read ≈ 0. If not, see §11.

What the CLI commands really do (source: openarm_can f340d4b, setup/cli/commands/)

CommandWhat it doesTorque?
discoverReconfigures the interface itself (sudo ip link set …) at 1M, 5M, 8M and 10M data rates in turn and asks each ID for a parameter (MST_ID); at the end it restores the can_configure defaults (1M/5M FD). May ask for your password.no
monitorenable_all(), then only state requests (no position command), then disable_all() on exit (Ctrl+C included).enabled, nothing commanded
diagnose, motor_statusalso call enable_all()enabled
set_zeroDisable → set zero (0xFE) → disable. Without --id it applies to all 8 motors.no

Earlier versions of this guide said the motors are disabled during monitor. They’re enabled; with no position command sent they stay limp in practice (verified on zeus with freshly powered motors), but hold the arm the first time. monitor also decodes every motor as a DM4310: the position column is right, the velocity and torque columns are wrong for joints 1–4 (DM8009/DM4340). Never run monitor while ROS (or LeRobot) is using the bus.

Other CLI subcommands (use carefully, they change motor firmware settings):

CommandPurpose
enable / disable [--id …]Torque on/off. disable makes the arm fall.
set_zero [--id …]Store the current position as the zero for those motors.
show_param, write_param -c ID -r RID -v VAL [--save]Read/write internal motor registers.
change_id -c CUR -s NEW_SLAVE -m NEW_MASTER --saveChange CAN IDs (e.g. a replaced motor).
change_baud -c ID -b BAUD --saveChange motor bus speed (e.g. to 5 Mbit/s for CAN-FD).