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:
| Arm | Interface | Motor IDs (send → receive) |
|---|---|---|
| Right | can0 | J1–J7: 0x01…0x07 → 0x11…0x17; gripper 0x08 → 0x18 |
| Left | can1 | same 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, driverpcan_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_configureEquivalent 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 upFor 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:=falseNotes:
- By default the interface stays down after a bus-off (
--rm 0) so you notice faults.--rm 100auto-restarts. - Interface config is lost on unplug or reboot. Rerun it, or add a systemd/networkd unit.
- Check it:
ip -details -statistics link show can0should showstate 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 nothingmtu 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 trafficRepeat 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_canf340d4b,setup/cli/commands/)
Command What it does Torque? 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 thecan_configuredefaults (1M/5M FD). May ask for your password.no monitorenable_all(), then only state requests (no position command), thendisable_all()on exit (Ctrl+C included).enabled, nothing commanded diagnose,motor_statusalso call enable_all()enabled set_zeroDisable → set zero ( 0xFE) → disable. Without--idit 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.monitoralso 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 runmonitorwhile ROS (or LeRobot) is using the bus.
Other CLI subcommands (use carefully, they change motor firmware settings):
| Command | Purpose |
|---|---|
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 --save | Change CAN IDs (e.g. a replaced motor). |
change_baud -c ID -b BAUD --save | Change motor bus speed (e.g. to 5 Mbit/s for CAN-FD). |