Leader is the base class for the machine that drives. Subclass it to read
your own controller, and the SDK handles the session, the lease, video sync and
recording.
Constructor
str | None
The room the follower is in. Both ends must join the same room.
str
default:"VIDEOSDK_TOKEN"
VideoSDK token.
int
default:"50"
The rate
run() paces at. Heartbeats flow at this rate whether or not the
deadman is held.int
default:"10"
Heartbeat rate. Must be comfortably faster than the follower’s
watchdog_timeout_s. Heartbeats flow whether or not the deadman is held, so
the follower can tell “operator let go” from “link died”.float
default:"120.0"
How long each video frame is held so state samples can arrive either side of
it.
0 means align on arrival, never “skip alignment”. See
Sync.bool
default:"True"
Receive the follower’s video.
False sends and receives joints only, for a
controller that never shows a frame, and saves the cost of decoding video.
observation() still pairs joints with frames whenever there
are frames, and returns images == {} when there are none.float | None
default:"None"
Refuse to arm while any joint differs from the follower by more than this, in
descriptor units. Set it on real hardware. See
arm_when_aligned().str
default:"operator"
Display name in the meeting.
Running it
Call one of these from your own program. Both pace the loop for you.leader.run(hz=30) is the same three steps in one call, for when there is
nothing to read each period. On the leader there usually is, so ticks() is
the one you want.
Already have a scheduler, such as a ROS 2 timer? Call
leader.tick() from it
instead and skip both.Methods
You implement
returns RobotDescriptor
The joint schema, matching the follower’s. Called once, at construction.
See
RobotDescriptor.returns Mapping[str, float]
Where the operator’s controller is right now. Called every tick.
read_joints() is whatever the controller is: a backdrivable arm, a VR headset
pose, a simulator, or a policy’s output.
You call
Provided by the base class.Taking control
returns bool
Ask the follower for control and wait for its answer.
True means you have
it, False means you were refused or nobody answered.returns None
Give control back. The follower stops applying your commands.
returns None
True starts sending commands, False stops. Releasing is never refused.returns None
Stop the follower now, on a channel of its own so it cannot queue behind
video.
Starting to drive
returns bool
Wait until the two arms match, hold still for
hold_s, then start sending.
Holding the pose is the trigger, since a leader arm falls the moment the
operator lets go to press a key.on_status runs each poll if you want to show progress, and timeout gives
up instead of waiting forever.returns dict[str, float] | None
How far each joint is from the follower’s, leader minus follower, in
descriptor units.
None until the follower’s pose is known.These compare positions, so they fit an arm driven by another arm, a VR
hand or a wearable controller. For a base or a drone, which report speed
rather than position, leave
max_misalignment unset and call
hold_deadman(True) directly.Observing
returns SyncedObservation
The latest frame from the follower with the joint positions from the instant
that frame was taken. Until video is up you get joints and
images == {}, which is how you know it is not ready yet. See
Observation.Recording
returns EpisodeRecorder
Attach a recorder. Does not open an episode.
returns str | None
Begin one recorded demonstration, ending any open episode first.
None if
there is no recorder.returns None
Close the episode.
success is True, False, or None for “not judged”.returns None
Close the recorder and detach it.
Episodes do not open on their own here, so call
start_episode() and
end_episode() yourself. The follower does that from the deadman.Properties
Events
once, on the first descriptor
The follower announced itself, so its joints are known.
on grant
You have control.
on refusal
Your claim was refused, or the joints did not match. It does not fire when
claim_control() simply times out, so a False return covers both refused
and never answered.per new observation
A newer observation arrived from the follower.
Keep callbacks short. Anything slow in one holds up the session, so hand the
work off rather than doing it here.
Example
A controller on a generic device, driving one follower.- Open your device before
super().__init__(). The leader has noconnect()hook, so this is the only place to do it. max_misalignmentstops the follower moving suddenly to meet a controller that is somewhere else.- Check what
claim_control()returns.Falsemeans you do not have control, and commands go nowhere. arm_when_aligned()is what starts the commands. Nothing moves before it.
Related
Follower
The other half of a session.
Observation
What observation() hands back, field by field.
Adapters
The LeRobot and ROS 2 subclasses that ship with the SDK.
Sync
How joints and frames are matched to the same instant.