Skip to content

docs: Bus/ServoDefinition as the higher-level API for LeRobot - #146

Closed
pierre-rouanet wants to merge 6 commits into
feat/motor-setupfrom
docs/high-level-api
Closed

pierre-rouanet wants to merge 6 commits into
feat/motor-setupfrom
docs/high-level-api

Conversation

@pierre-rouanet

Copy link
Copy Markdown
Member

Closes #145.

Says explicitly that Bus / ServoDefinition are a higher-level API, designed mostly for LeRobot:

  • README, "APIs": three layers instead of two. DynamixelProtocolHandler (protocol), the controllers and typed module functions (general purpose, one servo family), and ServoDefinition / Bus (works across families, registers by name, motor setup, built mostly for LeRobot and carrying some of its conventions).
  • README, "Mixed buses": a short pointer back to that section.
  • src/bus.rs: the module doc and the Python Bus docstring say the same, with change_id / change_baudrate leaving the torque off and the lock open as the example of a convention.
  • src/servo/definition.rs: the module doc points to the bus doc.

This is based on #142 (feat/motor-setup) and should merge after it. Once #143 has landed, the convention list can also mention the lock following the torque in set_torque, and find_model and the servo facts.

Docs only: cargo doc shows no new warnings, and the doc tests pass.

🤖 Generated with Claude Code

nicolas-rabault and others added 5 commits October 1, 2026 23:17
Finding the motors on a port meant reading the model number of every id
in turn, under a short timeout so that 254 absent ids do not take minutes.
That timeout has to be short (5 ms) for the sweep to stay usable, and a USB
adapter with a 16 ms latency timer, such as the FTDI chip of a U2D2, can
hold a healthy motor's answer longer than that.

A broadcast ping reaches every motor with one instruction; each one that
hears it answers in turn. The handler listens for one window, sized like
the vendor SDKs size it: the status packets of every possible id, 3 ms of
turn per id and 16 ms of adapter latency, about 0.8 s at 1 Mbps. A status
packet that does not parse is skipped; the port's timeout is put back.

Checked on two SO-101 arms (six STS3215 each): the six ids at 1 Mbps,
none at 500 kbps or 115.2 kbps, in 790 ms.
ServoDefinition::scan read the model number of every id asked, each under
a 5 ms timeout. Behind a USB adapter that holds short answers for its
16 ms latency timer, a motor that is there reads as absent, and a full
sweep at one baud rate costs 254 timeouts.

A definition now states whether its firmware answers a broadcast ping:
the protocol v2 Dynamixel (XL320, XL330, XL430) and the Feetech STS3215
do, the protocol v1 Dynamixel and the Feetech SCS do not. For those that
do, scan pings once and reads the model number of the motors that
answered only, under the port's own timeout, so the adapter's latency is
covered once for the whole bus. The others keep the sweep.

On two SO-101 arms, scan_all through the STS3215 definition finds the six
motors (777) in 0.8 s, where the sweep takes 1.3 s.
Setting up a new motor means giving it its id and the bus's baud rate.
Both live in EEPROM: a Dynamixel only writes it with the torque off, a
Feetech also needs its lock register open, and the baud rate register
takes a code, not a rate. Every caller has to know all three, and LeRobot
does it by hand for each family.

change_id and change_baudrate on ServoDefinition and Bus do it: torque
off, lock open where the servo has one (writing 0 is harmless on the
AX and MX, whose lock only clears at power-up), then the write; the baud
rate is looked up in the definition's baudrates, and a rate it does not
list fails as a RegisterError before anything is sent. Like scan, they
take the definition and reach motors the bus does not have, since a new
motor answers at its factory id.

Checked on an SO-101 leader: its gripper motor taken from id 6 to 7 and
back, then to 500 kbps and back to 1 Mbps, each step confirmed by a scan.
Both static methods built their dict from a HashMap, so its order
changed from one process to the next. A caller trying each baud rate in
turn to find a motor (LeRobot's setup does) tried them in a different
order on every run, and took a different time to find the same motor.

They now come from a BTreeMap: baud rates slowest first, models by name.
The return type is still a dict.
The README listed two APIs, the protocol handler and the controllers.
Bus and ServoDefinition have become a third one: they work across servo
families, reach registers by name and cover motor setup, and some of
their choices are LeRobot conventions (change_id leaves the torque off
and the Feetech lock open). Say so in the README, the bus and definition
module docs and the Python Bus docstring, so other users pick the
controllers when they want typed access to one servo family.

Refs #145

Assisted-by: Claude:claude-opus-5-5
Refs #145

Assisted-by: Claude:claude-opus-5-5
@pierre-rouanet
pierre-rouanet marked this pull request as ready for review October 5, 2026 13:14
@nicolas-rabault
nicolas-rabault force-pushed the feat/motor-setup branch 2 times, most recently from dbfeffa to cf861c5 Compare October 5, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants