se3labs.interface.eval.policyRobot Policies
Lifecycle
se3labs.interface.eval.policy.Policy defines the station-driven episode lifecycle:
| Method | Called when | Return value |
|---|---|---|
initialize(station, task) | Before each episode | Station_JointPose: the pose the arms move to before rollout |
infer(observation) | On each observation tick | Station_ActionChunk, or None to continue the current chunk |
resume() | After a station operator took the rollout over and handed it back, before the next observation | Nothing; the chunk in progress and an EndEpisode raised before are dropped: answer with a new chunk from where the operator left the arms. Optional: only a policy that overrides it is handed back; an operator who takes over any other policy ends the rollout |
refused(reason) | Before the next observation, after the station refused the chunk infer() returned | Nothing; that chunk did not run and the arms hold the last accepted target. Optional |
ended(end_condition, detail) | After an episode, before reset() | Nothing; why the station ended it, such as the safety check behind a safety_stop. Optional |
reset() | After an episode | Nothing; clear per-episode state |
Model initialization and access to task.instruction belong in initialize().
Inference frequency, preprocessing, and chunk length are controlled by the policy.
Observations and actions
station.arms and station.cameras describe the available hardware. Observation
and action dictionaries use those same names. A bimanual station uses left
and right for its arms; a single-arm station uses arm.
Joint targets are absolute angles in radians. Gripper positions are open
fractions: 0 is closed, 1 is open. Each arm action contains arrays shaped
(horizon, n_arm_dof) and (horizon, n_gripper_dof).
An action chunk's row i targets station time timestamp + i / rate. Use the
observation's station-clock timestamps, not your computer's wall clock.
All arms in a chunk share its horizon and rate. A new chunk replaces the
remaining targets from the previous chunk; returning None keeps that chunk.
Task specification
A Task_Spec contains an operator-facing description and reset_instruction,
a model-facing instruction, a max_rollout_time in seconds, and a
Task_ScoreSheet. Its success question must be binary: yes means success.
Additional questions can be binary, multiple
choice, or exact unscored counts. The station operator supplies the answers.
Sampled tasks also carry their initial layout, seed, and semantic parameters.
The policy's initialize() returns the starting arm pose for each episode.
Episode completion and errors
Raising se3labs.interface.eval.policy.EndEpisode from infer() requests
normal scoring, reset, and homing. Other inference exceptions are added to
SessionReport.errors; that tick sends no new chunk. An initialization
exception ends the session and propagates to the caller.
For a complete implementation, follow the Python API tutorial. Data types are listed in the Python Interface reference, and session reports in the Python SDK reference.
EndEpisode
class EndEpisode(Exception)
Raise from infer to finish normally: scoring, reset, then station homing.
Policy
class Policy(ABC)
Methods
initialize(station: Station_Spec, task: Task_Spec) -> Station_JointPose#-
Prepare for one episode of
taskonstationand return the pose the arms start from. infer(observation: Station_Observation) -> Station_ActionChunk | None#-
A new chunk, None to continue, or raise EndEpisode to score and home.
resume() -> None#-
A station operator took the rollout over and handed it back; called before the next observation. Override it to be handed back at all: without it the operator who takes over ends the rollout, so no policy runs on from state that predates the takeover.
refused(reason: str) -> None#-
The station refused the chunk the previous
inferreturned, forreason(a safety check's words, such as"unsafe: left: row 3 joint 5 at +2.301 rad, 0.057 rad outside the envelope's [-2.242, +2.244]"): it does not run, and the arms hold the last target the station accepted. Called before the nextinfer. No-op by default. ended(end_condition: str, detail: str) -> None#-
How the episode ended:
end_conditionas inEpisodeResult(timeout,safety_stop, ...) and the station'sdetail, such as the check that stopped the arms ("right: joint 4 reads +3.4 N*m"; empty when there is none). Called once, beforereset. No-op by default. reset() -> None#-
Clear per-episode state. Called once after the episode ends. No-op by default.
implements_resume
policy overrides Policy.resume, so a station operator who takes its rollout over may hand it back.