Everything you need for a pair of SO-101 arms on ROS 2, from an empty machine to one arm following the other across the internet.

What you need

  • Two SO-101 arms, one you hold and one that moves
  • ROS 2 on each machine, and a working colcon workspace
  • A VideoSDK token, and one room id both machines join
  • LeRobot installed, for calibrating the arms

Install

Two pieces. The bridge node comes from pip, and the ROS packages come from git.
That one repo carries the driver and the bridge, so check both are on your path:
If either prints nothing, build once more. The first colcon build does not always write the environment hook that makes a package findable.

The driver

so101_driver talks to the servos, so you do not have to write that part. It publishes the topics the bridge reads.
Run teleop_node or the bridge, never both. Each commands the follower, and together they fight over the same arm.

Find the ports

The two arms use identical adapters with no serial number, so /dev/ttyACM0 and /dev/ttyACM1 swap between reboots. Use the by-path name, which follows the socket the cable is in.
Not sure which is which? The follower runs on 12 V and the leader on 5 V, so a multimeter on the two supplies tells you. Getting it backwards makes the arm you meant to hold lock up, and the one that should move go loose.

Calibrate

Calibrate both arms with LeRobot. Its SO-101 guide covers the procedure.
The driver reads those same files, so there is no second calibration to keep in step.

Start the drivers

One per arm, with the port and the calibration named.
The leader should go loose enough to move by hand, and the follower should hold its position under power. If it is the other way round, the two ports are swapped.
Without calibration_file the driver falls back to placeholder offsets and warns that joint angles are not physically meaningful. It still runs, but the numbers do not correspond to real positions.

Point the bridge at them

The driver publishes under node-relative names, so the bridge’s defaults will not match. Set these three:
params.yaml
calibration_profile fills joint_min and joint_max from the same LeRobot calibration, so there are no travel numbers to type. Leave joint_min and joint_max unset when you use it: the node refuses to start with both.
follower_node publishes no joint states, so leave follower_states_topic empty and max_misalignment at 0.0. The bridge then echoes what it commanded instead of what the arm reached, which also means tracking error and the arming check stop working.

Run it

Then take control from the operator’s machine and move the leader arm:

Quickstart

The same path for a robot that is not an SO-101.

Reference

Every parameter, topic and service the bridge takes.

SO-101 on LeRobot

The same arm with no ROS install at all.

Safety

What the bridge stops, and what your driver has to stop.