Policies¶
A policy extension packages a frozen policy so Eden can run it: a local actor (a checkpoint or an ONNX graph with its
observation and action contract) or a client of a policy server (a VLA or world action model). Every policy is a
PolicyBase subclass registered in POLICY_REGISTRY, run with eden inference --policy <name>. The
policy zoo guide lists what each one needs, the
remote policies guide explains server-backed clients, and
extending policies shows how to add one.
from eden.extensions.policies.base import POLICY_REGISTRY, PolicyBase
from eden.options.extensions.policies import PolicyOptions, RemotePolicyOptions
Base class and registry¶
Base classes for policy extensions.
ClientMixin (server-backed policies) lives in :mod:eden.extensions.policies.client.
Attributes¶
Classes¶
PolicyBase
¶
PolicyBase(env: EnvBase, options: PolicyOptions)
Bases: ConfigurableMixin[PolicyOptions]
Base class for policies.
Any object that takes observations and returns actions are defined as a policy.
The *_options classmethods below describe the world the policy was trained in, and a
task-less run (eden inference|deploy --policy) calls them for you. In
:meth:action_options, :meth:observation_options and :meth:scene_entity_options, a
parameter named after one of the policy's configurable attributes is filled from it
(hand_name gets self.hand_name), so a name is declared once; entity_name is
the caller's (--entity_name wins) and anything else keeps its own default. Parameters
there therefore share a namespace with the class attributes — a knob that must keep its
literal default needs a name no attribute uses. :meth:command_options is filled by the
CLI instead (entity_name, motion_file, --clip_name), and
:meth:robot_options / :meth:env_options take no arguments at all.
:attr:reads_env_observations says which side of the env boundary the policy's
step() sits on, and a deploy is arranged around the answer.
Where a policy's files come from (registry, dataset, local_dir, assets_subdir) is fixed by
the class, not configured per instance: a lookup that runs before any instance exists (a clip catalogue in
:meth:command_options, a file an observation term loads) must read the same place __init__ does. To
fetch from a mirror (an offline cluster, a shared read-only cache), set EDEN_ASSETS_DIR, or set
local_dir on the class before anything reads it: MyPolicy.local_dir = "/data/mirror", as
:meth:use_local_checkpoint redirects a checkpoint.
| PARAMETER | DESCRIPTION |
|---|---|
checkpoint_path
|
The path to the checkpoint file.
|
Source code in eden/extensions/policies/base.py
Attributes¶
Methods:¶
resolve_local_dir
classmethod
¶
resolve_local_dir() -> str
Where this policy's files are cached: local_dir, else assets_subdir under the asset root.
Resolved on each call, so EDEN_ASSETS_DIR moves it. To fetch a file, call :meth:fetch_asset.
Source code in eden/extensions/policies/base.py
fetch_asset
classmethod
¶
The local path of file from this policy's Hub dataset, downloaded if missing.
Fetches with the class's registry, dataset and :meth:resolve_local_dir, so it gives the same
path on the class (a lookup before any env exists) as on an instance. Call this rather than passing
local_dir (None by default) to get_asset_path yourself. Unlike EntityOptions.fetch_asset,
file is required: a policy has no single file of its own.
Source code in eden/extensions/policies/base.py
use_local_checkpoint
classmethod
¶
Point a checkpoint at a local file and drop the hash pinned to the released one.
The two go together: md5_hash pins the released weights, so setting the path
without clearing it makes _verify_md5 reject the very file the caller asked
for. Use this for every --ckpt-path-style flag rather than assigning the path
by hand.
| PARAMETER | DESCRIPTION |
|---|---|
path
|
Local checkpoint to load instead of the released one. Not hashed: an unreleased file has no pinned digest to check it against.
TYPE:
|
checkpoint_attr
|
Which checkpoint to redirect, for policies that load more than one (sonic's
TYPE:
|
Source code in eden/extensions/policies/base.py
require_entity
¶
Resolve a scene entity the policy reads, failing loud when the scene has no such entity.
Call this in __init__ for every entity step() touches, so a scene missing one
raises at construction rather than deep in the first step. In step() read
self.env.entities[name] instead of caching the handle: deployment hot-swaps it.
Resolves against the mapping, like a term's resolve_entity.
Source code in eden/extensions/policies/base.py
robot_options
classmethod
¶
robot_options() -> RobotOptions | None
Return a robot configured for this policy, or None for the generic robot.
Frozen policies are usually trained against a specific default joint
pose and PD-gain set (e.g. OpenHomie's). Task-less rollout/deploy builds
the scene from this hook so the robot spawns in the pose and gains the
policy expects; returning None falls back to the caller-chosen
eden.robots class with its stock defaults.
Source code in eden/extensions/policies/base.py
scene_entity_options
classmethod
¶
Return the non-robot scene entities the policy reads, keyed by scene entity name (optional).
A policy that only reads its own robot needs none — the default. One whose observation
is defined against something else (SimToolReal's tool and table, HUSKY's skateboard)
cannot run on a robot-only scene, so a task-less run builds these alongside
:meth:robot_options; with --task the task's scene must declare them, which
:meth:require_entity checks at construction. A plane entity replaces the flat
ground (e.g. with terrain) rather than adding one.
Source code in eden/extensions/policies/base.py
env_options
classmethod
¶
action_options
abstractmethod
classmethod
¶
action_options() -> dict[str, ActionTermOptions]
Set up required action group options for the policy (required).
command_options
classmethod
¶
command_options() -> dict[str, CommandTermOptions]
observation_options
abstractmethod
classmethod
¶
observation_options() -> dict[str, ObservationGroupOptions]
Set up required observation group options for the policy (required).
step
abstractmethod
¶
reset
¶
reset(envs_idx: slice | Tensor | None = None) -> None
Reset per-env policy state at episode boundaries.
Override this in subclasses that hold per-env state across steps — e.g. heading offsets cached on the first step, last-action histories fed back as observations, attention KV caches, action-chunk queues. The default is a no-op, which is right for stateless policies.
When used as a teacher / motion generator in long-horizon rollouts the
owner is expected to call policy.reset(envs_idx) whenever the
underlying env resets the corresponding envs.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Indices of environments to reset.
TYPE:
|
Source code in eden/extensions/policies/base.py
Functions:¶
Options¶
Configuration options for policy extensions (local and remote clients).
Attributes¶
Classes¶
PolicyOptions
¶
Bases: DynamicConfigurableOptions
Options for policy extensions.
| PARAMETER | DESCRIPTION |
|---|---|
checkpoint_path
|
The path to the checkpoint file.
|
Source code in eden/options/options.py
ClientOptions
¶
Bases: DynamicConfigurableOptions
Options for policy client extensions (ClientMixin).
| PARAMETER | DESCRIPTION |
|---|---|
host
|
Policy server hostname, or a full URL (
|
port
|
Policy server port.
|
request_timeout
|
Request timeout in seconds for HTTP transports.
|
set_task_endpoint
|
The server's set-task API (HTTP route);
|
reset_endpoint
|
The server's reset API: an HTTP route, a ZMQ endpoint name or a GalaxeaVLA reset
message key;
|
act_endpoint
|
HTTP endpoint for requesting actions.
|
task_description
|
Language instruction sent on set_task / reset. Optional: policies without a
language input leave it
|
set_task_payload_key
|
JSON key carrying the task description on set_task.
|
reset_payload_key
|
JSON key carrying the task description on reset.
|
transport
|
|
serializer
|
|
chunk_mode
|
|
pred_action_horizon
|
Expected action-chunk length;
|
exec_horizon
|
Rows executed open-loop before re-querying;
|
short_chunk
|
|
chunk_temporal_ensemble_steps
|
Temporal ensembling of the previous chunk's unused tail into the new head; zero keeps plain open-loop execution.
|
chunk_temporal_ensemble_dims
|
Restrict ensembling to the first N action dims (e.g. to spare a binary gripper).
|
response_action_key
|
Key holding the action chunk in a dict reply (
|
Source code in eden/options/options.py
Attributes¶
set_task_payload_key
class-attribute
instance-attribute
¶
set_task_payload_key: str = 'task_description'
chunk_temporal_ensemble_steps
class-attribute
instance-attribute
¶
chunk_temporal_ensemble_steps: int = 0
RemotePolicyOptions
¶
Bases: PolicyOptions, ClientOptions
Merged options for policies that combine ClientMixin and PolicyBase.
Use as the type argument when subclassing both: class FooPolicy(
ClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]):. Without
this third base, __init_subclass__ would resolve _options_class_ to
whichever parent appears first in MRO, hiding the other parent's fields.
Source code in eden/options/options.py
ONNX runtime helpers¶
Shared ONNX-runtime infrastructure for policy extensions.
Provides the session layer (provider selection, thread policy, CPU fallback) and a generic
torch.nn.Module wrapper so policies don't each re-implement session
creation + inference. Single-input / single-output graphs use :class:OnnxModule
directly (see the sonic / holosoma ports); multi-input or IO-binding graphs
subclass :class:_OnnxWrapperBase (see omnixtreme).
Attributes¶
Classes¶
OnnxModule
¶
OnnxModule(onnx_path: str, input_name: str, output_name: str, *, device: str | None = None, label: str = 'ONNX', expected_input_dim: int | None = None, expected_output_dim: int | None = None, enable_iobinding: bool = False, dynamic_batch: bool = False)
Bases: _OnnxWrapperBase
Generic single-input / single-output ONNX graph as a torch Module.
Covers the common policy shape: one named float32 input (e.g. actor_obs
or obs_dict) and one named float32 output (e.g. action). forward
accepts a tensor or array, runs the graph with automatic CPU fallback, and
returns a float32 tensor on self.device.
| PARAMETER | DESCRIPTION |
|---|---|
onnx_path
|
Path to the ONNX file.
TYPE:
|
input_name
|
Names of the graph's single input / output tensor (validated against the loaded graph).
TYPE:
|
output_name
|
Names of the graph's single input / output tensor (validated against the loaded graph).
TYPE:
|
device
|
Device the returned tensor is placed on.
TYPE:
|
label
|
Human-readable tag used in log / fallback messages.
TYPE:
|
expected_input_dim
|
When set and the corresponding tensor has a static shape, the flattened
dim is validated and a
TYPE:
|
expected_output_dim
|
When set and the corresponding tensor has a static shape, the flattened
dim is validated and a
TYPE:
|
enable_iobinding
|
Use the GPU IO-binding fast path when the device is CUDA, a GPU provider is active and the output is float32 with static non-batch dims; otherwise the numpy path runs.
TYPE:
|
dynamic_batch
|
Patch a static-batch-1 graph to a dynamic leading axis first
(:func:
TYPE:
|
Source code in eden/extensions/policies/onnx.py
Attributes¶
Methods:¶
forward
¶
Source code in eden/extensions/policies/onnx.py
Functions:¶
dynamic_batch_onnx
¶
Patch a static-batch-1 ONNX graph to accept an arbitrary leading batch.
Rewrites every Reshape shape target whose leading dim is a constant 1
to -1 and marks each graph input/output's dim 0 as the symbolic
"batch". The patched graph is cached next to src as
*.dynbatch.onnx (falling back to a temp dir if src's directory is
read-only) and the cached path is returned; an existing cache is reused.
Needed because some released graphs (e.g. SONIC) are exported with a static
batch of 1, which rejects num_envs > 1 feeds outright. The patch is a
heuristic, so the freshly built graph is checked by :func:_verify_dynamic_batch
(accepts a batched feed + permutation-equivariant rows) before it is used — a
corrupt patch fails loudly here instead of silently returning wrong rows.
Source code in eden/extensions/policies/onnx.py
Server-backed clients¶
A server-backed policy mixes in ClientMixin: it builds a request from the env's observations, sends it through a
transport and serializer, and schedules the returned action chunk. The adapters turn a server's action format into
the env's action terms.
Server-backed policies: ClientMixin and the per-embodiment setup machinery.
The counterpart of :mod:eden.extensions.policies.onnx (local actors): a client policy is
class FooPolicy(ClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]) whose
inference happens on a server. The layer is policy-family neutral (VLA, world action model,
remote RL controller, ...) and owns three things:
- a transport from :mod:
eden.utils.policy_client, selected by key (transport="http_json","openpi_websocket","zmq_msgpack", ...), - an :class:
ActionChunkSchedulerthat turns server action chunks into one(num_envs, D)row per step, - a uniform lifecycle: :meth:
ClientMixin.set_task, :meth:ClientMixin.reset_server, :meth:ClientMixin.reset.
Every step runs the same pipeline::
obs -> observe(obs) per-step hooks (frame history, measured pose)
-> [if scheduler.needs_inference]
payload = pack_request(obs) default: pack_observations(obs)
reply = transport.infer(payload, request_options())
chunk = decode_response(reply) -> (num_envs, T, D)
scheduler.accept(chunk)
-> row = scheduler.pop() -> (num_envs, D)
-> unpack_actions(row) -> {action_term: tensor}
step is also exposed as its stages observe (every step: frame histories, measured
poses) / pack_request / infer_payload / accept_chunk / pop_action (plus
:attr:ClientMixin.needs_inference). An evaluator can then run the blocking round-trip on a
worker thread while it keeps stepping the simulation, so the robot moves on during the
wait as much as the inference latency would let it. The evaluator must still call
observe on every step. The contract is one request in flight: accept or discard its
chunk before the next pack_request. Do not call reset / reset_server /
set_task while a request is in flight, because nothing here locks or fences the
transport.
Servers that serve several embodiments use :class:SetupClientMixin with a
SETUP_REGISTRY of :class:PolicySetupBase strategies selected by policy_setup.
Attributes¶
Classes¶
ActionChunkScheduler
¶
ActionChunkScheduler(*, num_envs: int, mode: ChunkMode = 'client', pred_action_horizon: int | None = None, exec_horizon: int | None = None, short_chunk: ShortChunk = 'error', ensemble_steps: int = 0, ensemble_dims: int | None = None)
Queue server action chunks and hand them out one row per env per step.
| PARAMETER | DESCRIPTION |
|---|---|
num_envs
|
Number of envs; chunks are
TYPE:
|
mode
|
TYPE:
|
pred_action_horizon
|
Expected chunk length
TYPE:
|
exec_horizon
|
Rows executed before re-querying.
TYPE:
|
short_chunk
|
What to do when the server returns fewer than
TYPE:
|
ensemble_steps
|
Temporal ensembling of the previous chunk's unused tail into the new chunk's
head: for
TYPE:
|
ensemble_dims
|
Only the first
TYPE:
|
Source code in eden/extensions/policies/client.py
Attributes¶
queues
instance-attribute
¶
queues: list[deque[ndarray]] = [collections.deque() for _ in range(self.num_envs)]
raw_queues
instance-attribute
¶
raw_queues: list[deque[ndarray]] = [collections.deque() for _ in range(self.num_envs)]
needs_inference
property
¶
needs_inference: bool
True when the next :meth:pop needs a fresh server chunk first.
action_metadata
property
¶
Where the last popped row sat in its chunk (for recorders / evaluators).
Methods:¶
clear
¶
Drop buffered actions and ensembling state (task change / episode boundary).
Source code in eden/extensions/policies/client.py
accept
¶
Queue the first exec_horizon rows of a (num_envs, T, D) chunk.
raw_chunk (same shape) is an optional untouched copy queued alongside for
diagnostics, e.g. before a setup latched the gripper column.
Source code in eden/extensions/policies/client.py
pop
¶
Return the next (num_envs, D) row; raises if nothing is queued.
Source code in eden/extensions/policies/client.py
ModalitySchema
dataclass
¶
ModalitySchema(video_keys: tuple[str, ...], state_keys: tuple[str, ...], action_keys: tuple[str, ...], language_key: str, action_horizon: int | None = None, video_horizon: int = 1, video_stride: int = 1)
What a setup expects the server's get_modality_config to say.
Isaac-GR00T and its derivatives select the checkpoint's embodiment server-side
(--embodiment-tag) and advertise the resulting per-modality modality_keys and
delta_indices. A setup declares the schema it packs and decodes; the two are
compared once at connect time so a mismatch fails there, with both sides named,
instead of as a KeyError on the server or a silently wrong video window.
FrameHistory
¶
Per-step frame buffer that samples a strided temporal window the way delta_indices do.
Servers trained with a video window (GR00T delta_indices=[-15, 0], RLDX-1
[-6, -4, -2, 0]) expect horizon frames spaced stride control steps apart,
ending at the current one. :meth:append records one frame per control step and
:meth:window returns (horizon, ...) in chronological order; missing history is
padded with the oldest available frame, as the reference MultiStepWrapper does by
seeding its deque with copies of the first observation.
Built on :class:~eden.utils.buffers.circular_buffer.CircularBuffer (batch size 1,
backfilled), so no per-key list bookkeeping is needed.
Source code in eden/extensions/policies/client.py
Attributes¶
Methods:¶
reset
¶
append
¶
Source code in eden/extensions/policies/client.py
window
¶
(horizon, ...) frames at stride spacing, newest last.
Source code in eden/extensions/policies/client.py
ClientMixin
¶
ClientMixin(env: EnvBase, options: PolicyOptions)
Bases: ConfigurableMixin[ClientOptions]
Mixin for policy extensions that delegate inference to a remote policy server.
Concrete subclasses implement :meth:pack_observations and :meth:unpack_actions
(the serialization contract between Eden's observation/action format and the server's
API) and pick a transport. Everything else — connection handling, chunk queuing,
horizon rules, task/reset lifecycle — is shared. Override :meth:decode_response when
the reply does not carry the chunk under a single key, :meth:observe for per-step
state the policy needs even on steps that do not query the server, and
:meth:request_options for per-request server options (sessions, memory flags).
Two requirements must both be met when combining with PolicyBase:
ClientMixinmust appear beforePolicyBaseso that its concrete :meth:steptakes precedence over the abstract one.- The subclass must re-parameterize
ConfigurableMixinwith the merged options typeRemotePolicyOptions; otherwise__init_subclass__inherits_options_class_from whichever parent is first in MRO and :meth:configurerejects the other parent's fields.
| PARAMETER | DESCRIPTION |
|---|---|
host
|
Policy server address.
|
port
|
Policy server address.
|
request_timeout
|
Request timeout in seconds (HTTP transports).
|
set_task_endpoint
|
The server's set-task / reset API: an HTTP route, a ZMQ endpoint name or a
GalaxeaVLA reset message key.
|
reset_endpoint
|
The server's set-task / reset API: an HTTP route, a ZMQ endpoint name or a
GalaxeaVLA reset message key.
|
act_endpoint
|
HTTP route for inference.
|
task_description
|
Language instruction sent on
|
set_task_payload_key
|
JSON keys carrying the task description.
|
reset_payload_key
|
JSON keys carrying the task description.
|
transport
|
|
serializer
|
|
chunk_mode
|
Who owns the action chunk: this client (buffer and re-query when drained), the server (it caches the chunk and serves one row per call), or nobody (one action per call).
|
pred_action_horizon
|
Expected chunk length from the server;
|
exec_horizon
|
Rows executed open-loop before re-querying;
|
short_chunk
|
Whether a chunk shorter than
|
chunk_temporal_ensemble_steps
|
Temporal ensembling of overlapping chunks; zero disables.
|
chunk_temporal_ensemble_dims
|
Temporal ensembling of overlapping chunks; zero disables.
|
response_action_key
|
Key holding the action chunk in a dict reply (
|
Source code in eden/extensions/policies/client.py
Attributes¶
set_task_payload_key
class-attribute
instance-attribute
¶
set_task_payload_key: str = 'task_description'
chunk_temporal_ensemble_steps
class-attribute
instance-attribute
¶
chunk_temporal_ensemble_steps: int = 0
chunk_temporal_ensemble_dims
class-attribute
instance-attribute
¶
chunk_temporal_ensemble_dims: int | None = None
last_prediction_chunk_length
property
¶
last_prediction_chunk_length: int
Length T of the most recent server chunk.
last_temporal_ensemble_steps
property
¶
last_temporal_ensemble_steps: int
How many rows of the most recent chunk were temporally ensembled.
needs_inference
property
¶
needs_inference: bool
True when the next :meth:step will query the server.
action_metadata
property
¶
Chunk position of the last executed action.
Methods:¶
transport_config
¶
transport_config() -> TransportConfig
Collect the connection settings and the selected transport's knobs from this policy.
Source code in eden/extensions/policies/client.py
make_transport
¶
make_transport() -> TransportBase
Build the transport named by self.transport; override to inject a custom one.
set_task
¶
set_task(task_description: str) -> None
Set the task description locally and on the server, clearing pending actions.
Should be called after construction and before the first :meth:step.
Source code in eden/extensions/policies/client.py
reset_server
¶
Reset per-episode state locally, and on the server when it has a reset API (reset_endpoint).
Source code in eden/extensions/policies/client.py
close
¶
reset
¶
reset(envs_idx: slice | Tensor | None = None) -> None
PolicyBase hook: reset the server once every env this client serves is being reset.
The server's episode state (history, chunk cache, recurrent memory) belongs to this
client's connection / session as a whole, so a reset that covers every env maps to
:meth:reset_server; with num_envs == 1 that is every non-empty reset. A partial
reset of a batched client would wipe the other envs' state, so it raises until the
client implements a per-env scheme (e.g. one server session per env).
Source code in eden/extensions/policies/client.py
observe
¶
Per-step hook called before scheduling, on every step (default: no-op).
Use it for state that must track every control step regardless of when the server is queried: rolling frame histories, the measured pose a delta is applied to, ...
Source code in eden/extensions/policies/client.py
pack_request
¶
Build the server payload from an observation (main thread; reads sim state).
infer_payload
¶
infer_payload(payload: Any) -> ndarray
Blocking server round-trip -> (num_envs, T, D) chunk.
Safe on a worker thread while the main thread keeps stepping the simulation, as long
as it is the only call using the transport, i.e. no reset / reset_server /
set_task until it returns.
Source code in eden/extensions/policies/client.py
accept_chunk
¶
Validate a predicted chunk and queue its executable prefix.
Source code in eden/extensions/policies/client.py
pop_action
¶
Unpack and return the next queued (num_envs, D) row (main thread).
step
¶
Query the policy server when needed and return the next action in Eden format.
Source code in eden/extensions/policies/client.py
pack_observations
abstractmethod
¶
Convert Eden observations to the server payload.
When supports_batch=True, observation fields in the returned payload must
carry a leading batch axis of size env.num_envs.
Source code in eden/extensions/policies/client.py
decode_response
¶
decode_response(response: Any) -> ndarray
Extract the action chunk from a server reply as (num_envs, T, D).
Default: response[response_action_key] normalized by
:meth:_normalize_action_shape. Override for replies that spread the action over
several keys.
Source code in eden/extensions/policies/client.py
unpack_actions
abstractmethod
¶
Convert a (num_envs, D) action row from the server to an Eden action dict.
For non-batched subclasses this is always (1, D).
Source code in eden/extensions/policies/client.py
PolicySetupBase
¶
PolicySetupBase(policy: SetupClientMixin)
Bases: ABC
Strategy object for one server embodiment / schema.
Class-level defaults (pred_action_horizon, exec_horizon, default_image_size)
fill the policy's pred_action_horizon / exec_horizon / image_size when the
policy leaves them None, so each setup can carry the horizon and resolution its
checkpoint was trained with.
Concrete setups own observation_options / action_options (classmethod
factories so the env can be built without a live policy), pack_observations, and
unpack_actions; they optionally override decode_response (reply -> (T, D)
chunk), observe (per-step state) and reset.
Source code in eden/extensions/policies/client.py
Attributes¶
last_raw_action_chunk
class-attribute
instance-attribute
¶
Methods:¶
reset
¶
Clear per-episode state (adapter latches, frame history, cached indices).
observation_options
abstractmethod
classmethod
¶
observation_options(**kwargs) -> dict[str, ObservationGroupOptions]
action_options
abstractmethod
classmethod
¶
action_options(**kwargs) -> dict[str, ActionTermOptions]
validate_server_config
¶
validate_server_config(config: dict) -> None
Check the server's advertised configuration against this setup's schema.
config is transport.server_config() — the handshake metadata (WebSocket) or
the GR00T-family get_modality_config reply (ZMQ); {} when the server
advertises nothing, in which case there is nothing to check. Raise ValueError
naming the expected and served values on a mismatch.
Source code in eden/extensions/policies/client.py
pack_observations
abstractmethod
¶
set_raw_server_action
¶
Receive the untouched server row matching the row about to be unpacked (default: ignored).
unpack_actions
¶
One (D,) action row -> {action_term: tensor}; delegates to adapter by default.
Source code in eden/extensions/policies/client.py
SetupClientMixin
¶
SetupClientMixin(env: EnvBase, options: PolicyOptions)
Bases: ClientMixin
ClientMixin whose serialization contract is delegated to a policy_setup.
Subclasses set SETUP_REGISTRY (name -> :class:PolicySetupBase subclass) and the
default policy_setup. observation_options / action_options take
policy_setup= and forward the remaining kwargs to the setup.
| PARAMETER | DESCRIPTION |
|---|---|
policy_setup
|
Key in
|
image_size
|
Image size sent to the server;
|
Source code in eden/extensions/policies/client.py
Attributes¶
Methods:¶
observation_options
classmethod
¶
observation_options(policy_setup: str | None = None, **kwargs) -> dict[str, ObservationGroupOptions]
Observation group options for policy_setup (default: the class default).
Source code in eden/extensions/policies/client.py
action_options
classmethod
¶
action_options(policy_setup: str | None = None, **kwargs) -> dict[str, ActionTermOptions]
Action term options for policy_setup (default: the class default).
Source code in eden/extensions/policies/client.py
observe
¶
pack_observations
¶
decode_response
¶
decode_response(response: Any) -> ndarray
Source code in eden/extensions/policies/client.py
accept_chunk
¶
Source code in eden/extensions/policies/client.py
pop_action
¶
unpack_actions
¶
Source code in eden/extensions/policies/client.py
Functions:¶
concat_action_streams
¶
Concatenate per-key (1, T, d_k) action streams in keys order into one (T, D) chunk.
Servers of the Isaac-GR00T family answer with one array per modality key. A (T, d)
array (batch already squeezed) is accepted; a batch larger than one is rejected because
the client serves a single env.
Source code in eden/extensions/policies/client.py
validate_modality_config
¶
validate_modality_config(config: dict, schema: ModalitySchema, *, setup_name: str) -> None
Raise ValueError when config (a get_modality_config reply) contradicts schema.
An empty config means the server advertises nothing and passes. Keys are compared
as sets for video / state (payloads are keyed dicts) and as an ordered list for action
(the client concatenates streams in that order); horizons come from delta_indices.
Source code in eden/extensions/policies/client.py
Action adapters¶
Manipulation action adapters for remote policy clients.
An adapter converts a raw action array returned by a policy server into the
{controller_name: torch.Tensor} dict that Eden's action manager expects.
It is the mirror image of the server-specific obs packer: the packer serializes
Eden's world state for the server; the adapter deserializes the server's action
chunk back into Eden's controller contract.
The adapters here are transport-agnostic on purpose — they are composed into
per-setup strategies (OpenPI pi/, GR00T, RLDX-1, Cosmos3, GalaxeaVLA) but nothing
about them is wire-specific, so any client policy that receives a numpy action vector
can reuse them. They are the manipulation family's decoders; other families add
their own module beside this one. The module also carries the manipulation-side
composition helpers shared by the setups (image observation groups, the WidowX EE-delta
action pair, robot DOF layouts read from the robot option classes) and the construction
checks every client runs against the env it is given (check_*: the control rate, the
gripper decision term, the plain proprio group, joint terms that pass commands through,
the DROID arm term and joint state, LIBERO's camera resolution).
Four adapter shapes cover the setups that use them:
- :class:
EEDeltaAdapter— 7-D[ee_delta(6), gripper(1)]for the WidowX/Bridge and Google-Robot setups (OpenPI, GR00T, Octo). Euler or axis-angle rotation deltas; both reach the IK controller as a rotation vector. - :class:
JointVelocityAdapter—(n_arm + 1)-D joint velocity + gripper for OpenPI DROID. Emits true velocity commands by default, with an optional position-integration fallback for arms without a usable velocity controller. - :class:
JointPositionAdapter—(n_arm + 1)-D absolute joint position targets + gripper for DROID joint-position checkpoints (OpenPI, Cosmos3, GalaxeaVLA). - :class:
BimanualJointPositionAdapter— 14-D joint positions split into two arm controllers for Aloha.
None of them decides the gripper: each forwards the server's gripper scalar to the
env's GripperDecisionController, which applies the setup's rule (threshold,
hysteresis, sticky latch). See :func:check_env_gripper_term.
Attributes¶
DROID_ARM_DOFS
module-attribute
¶
DROID_ARM_DOFS: tuple[str, ...] = tuple(FrankaResearch3.arm_dofs_name)
DROID_CONTROL_TIMING_NOTE
module-attribute
¶
DROID_CONTROL_TIMING_NOTE: str = " as PolaRiS does, with sim_substeps=9 to keep the Robotiq 2F85's substep under 1 ms (Eden's PolaRiS tasks step every 20 ms)"
ROBOTIQ_CLOSE_POS
module-attribute
¶
ROBOTIQ_CLOSE_POS: float = Robotiq2f85.close_dofs_pos['left_driver_joint']
DROID_FLANGE_FROM_FR3_ATTACHMENT_EULER
module-attribute
¶
BRIDGE_DEFAULT_ROT
module-attribute
¶
BRIDGE_DEFAULT_ROT = np.array([[0.0, 0.0, 1.0], [0.0, 1.0, 0.0], [-1.0, 0.0, 0.0]], dtype=np.float64)
GRIPPER_DECISION_FIELDS
module-attribute
¶
GRIPPER_DECISION_FIELDS = ('input', 'scale', 'threshold', 'open_threshold', 'close_threshold', 'hold_steps', 'sticky_repeats', 'zero_holds', 'continuous', 'snap_threshold')
The :class:GripperDecisionController fields that decide what a gripper command does.
Classes¶
ActionAdapter
¶
Bases: ABC
A server action row -> {action_term: tensor} decoder.
bind(env) runs once, when the setup that owns the adapter is built: it resolves and
validates everything the adapter reads from the env (entity, DOF names), so a bad handle
raises at construction instead of on the first control step. reset() clears
per-episode state. Both default to no-ops.
EEDeltaAdapter
¶
EEDeltaAdapter(*, rotation: Literal['euler', 'axis_angle'] = 'euler')
Bases: ActionAdapter
Adapter for 7-D end-effector delta actions.
Input shape: (7,) — [dx, dy, dz, drx, dry, drz, gripper].
Output dict:
* ee_controller — (num_envs, 6) [dxyz, rotation vector]: the task-space delta
:class:DifferentialIKController solves J @ dq = delta for, whose angular rows are an
angular displacement (axis * angle), not Euler angles.
* gripper_controller — (num_envs, 1) the server's gripper scalar, unchanged; the env's
GripperDecisionController interprets it (openness / sign / sticky latch, per setup).
| PARAMETER | DESCRIPTION |
|---|---|
rotation
|
Rotation convention of the input delta slice
TYPE:
|
Source code in eden/extensions/policies/adapters.py
JointVelocityAdapter
¶
JointVelocityAdapter(*, n_arm: int = 7, entity_name: str = 'robot', arm_dofs_name: list[str] | None = None, dt: float | None = None, mode: Literal['velocity', 'velocity_integrate'] = 'velocity', action_scale: float | list[float] = 1.0, clip: float = 1.0, velocity_lag: dict[str, 'Sequence[float]'] | None = None, velocity_compensation: dict[str, 'Sequence[float]'] | None = None, velocity_lag_dt: float | None = None, gripper_threshold: float = 0.5, debug_gripper: bool = False, arm_key: str = 'ee_controller', gripper_key: str = 'gripper_controller')
Bases: _JointAdapterBase
Adapter for joint-velocity + gripper actions (DROID).
Pi0 DROID returns (T, 8) chunks where the first 7 dims are joint velocities in
DROID's unit action space, not rad/s, and the last dim is a gripper command
binarized at 0.5 on the robot side (here by the env's GripperDecisionController).
openpi's examples/droid/main.py clips every dim element-wise to [-1, 1] before
RobotEnv.step (which leaves DROID's own v / max|v| rescale nothing to do), and
DROID's RobotIKSolver.joint_velocity_to_delta turns a unit velocity into
max_joint_delta = 0.2 rad per 15 Hz control step, i.e. 3 rad/s per unit. That
conversion is the caller's action_scale (DroidSetup passes 3.0 with clip=1.0
and dt=1/15 s); the default action_scale=1.0 is a pass-through and is not DROID's.
In mode="velocity" (default), the arm output is action_scale * clip(v), the rad/s
velocity command consumed by :class:ImplicitVelocityController.
In mode="velocity_integrate", this
adapter integrates velocities to joint-position targets on the client side::
target_pos = current_pos + dt * action_scale * clip(v, -1, 1)
and the integrated target is consumed by a standard
:class:ImplicitPDController.
Limitation: client-side integration assumes the downstream position
controller tracks the reference cleanly. Stiff Franka-like gains usually
do, but over long horizons small integration errors accumulate; prefer
mode="velocity" wherever the arm has an ImplicitVelocityController.
| PARAMETER | DESCRIPTION |
|---|---|
n_arm
|
Number of arm joints (length of the velocity slice in
TYPE:
|
entity_name
|
Eden entity name used to read current joint positions for integration.
TYPE:
|
arm_dofs_name
|
Ordered DOF names under |
dt
|
Integration timestep (seconds). If
TYPE:
|
mode
|
TYPE:
|
action_scale
|
Rad/s per unit of the clipped arm output, applied before it is sent to the
downstream controller (scalar, or one value per arm joint). DROID's is 3.0:
0.2 rad per 1/15 s control step ( |
clip
|
Each arm dim is clipped element-wise to
TYPE:
|
velocity_lag
|
Optional
TYPE:
|
velocity_compensation
|
Optional
TYPE:
|
velocity_lag_dt
|
The period between two adapter calls in seconds, required when
TYPE:
|
gripper_threshold
|
Closedness above which the diagnostics label the gripper intent "close". The scalar
itself is forwarded unchanged; the env's
TYPE:
|
debug_gripper
|
Log each gripper scalar and its intent.
TYPE:
|
arm_key
|
Keys in the returned dict — must match the controller names registered in the env's action manager.
TYPE:
|
Source code in eden/extensions/policies/adapters.py
Attributes¶
arm_dofs_name
instance-attribute
¶
arm_dofs_name = None if arm_dofs_name is None else list(arm_dofs_name)
velocity_lag_dt
instance-attribute
¶
velocity_lag_dt = None if velocity_lag_dt is None else float(velocity_lag_dt)
Methods:¶
bind
¶
Resolve the arm joints the integration modes read, once, against env.
Only the derived indices are cached; the entity is looked up on every call, since
deployment can swap env.entities[entity_name].
Source code in eden/extensions/policies/adapters.py
reset
¶
JointPositionAdapter
¶
JointPositionAdapter(*, n_arm: int = 7, gripper_threshold: float = 0.5, debug_gripper: bool = False, arm_key: str = 'ee_controller', gripper_key: str = 'gripper_controller')
Bases: _JointAdapterBase
Adapter for absolute joint-position + gripper actions.
Pi0.5 DROID joint-position checkpoints are trained in delta joint-position
space, but OpenPI's output transform reconstructs absolute joint targets by
adding the current state back before the client receives the chunk. This
adapter therefore passes the first n_arm dimensions directly to an
ImplicitPDController configured with reference_source=ZERO, and the
gripper scalar to the env's GripperDecisionController. Shared by OpenPI's
droid_jointpos, Cosmos3 and GalaxeaVLA's DROID-Franka setup.
Source code in eden/extensions/policies/adapters.py
BimanualJointPositionAdapter
¶
BimanualJointPositionAdapter(*, left_n: int = 7, right_n: int = 7, left_key: str = 'left_arm_controller', right_key: str = 'right_arm_controller')
Bases: ActionAdapter
Adapter for 14-D bimanual joint-position actions (Aloha).
Splits a (left_n + right_n)-D action vector into its two arms' slices and emits
them under separate controller keys.
Source code in eden/extensions/policies/adapters.py
Functions:¶
ee_delta_to_world
¶
A robot-base-frame (num_envs, 6) [dxyz, rotation vector] delta in the world frame.
EE-delta servers (Bridge, Fractal, RobotArena) compose their deltas in the robot base frame, while
:class:DifferentialIKController solves against the world-frame Jacobian: the two agree only for a
base with identity rotation. Both parts are displacements, so the base's world orientation
base_quat_w ((num_envs, 4) wxyz, the controlled entity's get_quat()) alone maps them.
Source code in eden/extensions/policies/adapters.py
bridge_rpy
¶
Bridge / SimplerEnv WidowX rpy: the EE rotation relative to :data:BRIDGE_DEFAULT_ROT, xyz Euler.
R @ BRIDGE_DEFAULT_ROT.T as quaternions, decomposed into extrinsic xyz Euler angles.
Source code in eden/extensions/policies/adapters.py
robosuite_axis_angle
¶
Robosuite's quat2axisangle for a wxyz quaternion, as LIBERO's official clients compute EE state.
Both OpenPI (examples/libero/main.py) and Isaac-GR00T (gr00t/eval/sim/LIBERO/libero_env.py)
copy it. Kept robosuite-exact -- no sign canonicalisation (the angle may exceed pi when w < 0), hard
zero at the singularity -- rather than routed through :func:eden.utils.geom.axis_angle_from_quat or
Rotation.as_rotvec, because the servers' normalisation statistics were fit on robosuite's output.
Source code in eden/extensions/policies/adapters.py
libero_task_action_options
¶
The stock libero task's action terms, from the task's own builder (no restated constants).
osc_pose (robosuite OSC_POSE on the grip site) and gripper (the stateful
LiberoPandaGripperController, +1 closes / -1 opens): the controllers LIBERO's
demonstrations, and so every LIBERO checkpoint (OpenPI, GR00T), were recorded with.
Source code in eden/extensions/policies/adapters.py
check_libero_camera_resolution
¶
Refuse an env whose image / wrist_image groups render at another size than LIBERO's 256x256.
The camera is the one each group's term names in params["camera_name"] (:func:make_image_groups);
a camera term without one is judged by the image its group publishes. subject names the client and
setup, trained_on the upstream render the checkpoints saw, and wrong_fix is the closing "Do not
..." sentence about the client's own image-size knob. Envs without an observation manager skip.
Source code in eden/extensions/policies/adapters.py
libero_training_view
¶
An upright Eden render as LIBERO's training images show it: mirrored left-right, (H, W, 3) uint8.
LIBERO's official clients send obs["agentview_image"][::-1, ::-1] and the same for
robot0_eye_in_hand_image (OpenPI examples/libero/main.py lines 115-116, Isaac-GR00T
gr00t/eval/sim/LIBERO/libero_env.py lines 148-149), as the LIBERO datasets were recorded. robosuite
1.4's raw frame is OpenGL bottom-up (the upright view flipped vertically), so that pair of flips leaves the
upright view mirrored left-right. Eden renders upright, so only the left-right mirror reproduces it;
porting [::-1, ::-1] literally would turn the image upside down.
Source code in eden/extensions/policies/adapters.py
dofs_pos_joint_names
¶
The joints a built DofsPos term reads, in its column order.
Source code in eden/extensions/policies/adapters.py
gripper_dofs_from_name
¶
Expand a single Robotiq 2F-85 driver name to both actuated drivers; other names pass through.
Source code in eden/extensions/policies/adapters.py
make_image_groups
¶
make_image_groups(camera_name: str, use_wrist_image: bool, wrist_camera_name: str) -> dict[str, ObservationGroupOptions]
Build image (and optionally wrist_image) observation groups, one rgb term each.
Source code in eden/extensions/policies/adapters.py
make_ee_pose_group
¶
make_ee_pose_group(entity_name: str, ee_link_name: str, *, gripper_entity_name: str | None, gripper_dof_name: str, open_gripper_pos: float = open_finger_pos, gripper_close_pos: float = ROBOTIQ_CLOSE_POS, offset_pos: tuple[float, float, float] = (0.0, 0.0, 0.0)) -> ObservationGroupOptions
[ee_pos(3), ee_quat_wxyz(4), gripper_openness(1)] proprio group (Bridge / Google-robot / RobotArena).
With the gripper on the arm entity, openness is pos / open_gripper_pos (WidowX fingers:
fully open at open_gripper_pos, closed at 0). With a separately mounted gripper it is
1 - pos / gripper_close_pos (Robotiq driver: open at 0, closed at gripper_close_pos).
Source code in eden/extensions/policies/adapters.py
make_droid_joint_state_group
¶
make_droid_joint_state_group(arm_entity_name: str, arm_dofs_name: list[str] | tuple[str, ...], *, gripper_entity_name: str | None = None, gripper_dof_name: str, gripper_close_pos: float = ROBOTIQ_CLOSE_POS) -> ObservationGroupOptions
[joint_pos(N), gripper_closedness(1)] proprio group for DROID-style joint-state servers.
The last value is DROID's robot_state["gripper_position"]: closedness pos / gripper_close_pos
clipped to [0, 1] (a Robotiq driver can travel past its close target), read from
gripper_dof_name on gripper_entity_name -- the arm entity when that is None.
Source code in eden/extensions/policies/adapters.py
make_widowx_ee_delta_actions
¶
make_widowx_ee_delta_actions(entity_name: str, ee_link_name: str, dofs_name: list[str], scale: float, *, gripper_input: str = 'openness', gripper_threshold: float = 0.5, gripper_sticky_repeats: int = 0, gripper_zero_holds: bool = False) -> dict[str, ActionTermOptions]
ee_controller (differential IK) + gripper_controller for WidowX EE-delta policies.
The gripper term is a :class:~eden.managers.terms.actions.binary_actions.GripperDecisionController
that receives the server's raw gripper scalar (Bridge servers emit openness, > 0.5 =
open) and decides open/close itself; gripper_sticky_repeats enables the fractal latch
the Google-robot checkpoints were trained with.
Source code in eden/extensions/policies/adapters.py
make_droid_joint_position_actions
¶
make_droid_joint_position_actions(arm_entity_name: str = 'robot', arm_dofs_name: list[str] | None = None, *, gripper_entity_name: str | None = None, gripper_dof_name: str = 'left_driver_joint', arm_key: str = 'ee_controller', **gripper_options) -> dict[str, ActionTermOptions]
Absolute DROID arm joint targets + the Robotiq gripper term, as joint-position DROID servers need.
The servers return absolute joint targets, so the arm is an ImplicitPDController with a zero
reference. The gripper is :func:make_droid_gripper_action on gripper_entity_name (the arm
entity when the Robotiq is part of it), configured by gripper_options.
Source code in eden/extensions/policies/adapters.py
make_droid_gripper_action
¶
make_droid_gripper_action(gripper_entity_name: str, gripper_dof_name: str, *, gripper_close_pos: float = ROBOTIQ_CLOSE_POS, input: str = 'closedness', threshold: float = 0.5, open_threshold: float | None = None, close_threshold: float | None = None, hold_steps: int = 0, continuous: bool = False, snap_threshold: float | None = None, on_missing: str = 'hold') -> ActionTermOptions
gripper_controller for DROID-style servers: closedness in, [0, gripper_close_pos] targets out.
The open/close decision (threshold, hysteresis, hold, continuous) is made by the term, not the client. The single Robotiq driver name expands to both actuated drivers.
Source code in eden/extensions/policies/adapters.py
check_control_rate
¶
check_control_rate(env: 'EnvBase', *, control_hz: float, subject: str, source: str, timing: tuple[str, int] | None = None, timing_note: str = '', wrong_fix: str | None = None) -> None
Refuse an env whose control period env.dt is not the upstream client's 1 / control_hz s.
A remote checkpoint's rows are one upstream control step each (an EE or joint delta, the next waypoint
of a trajectory), and exec_horizon / a video window's stride count env steps, so at another rate
every chunk executes at the wrong speed, silently. subject names the client and setup
("PiPolicy.policy_setup='droid'") and source where upstream sets the rate. timing is the
(sim_dt as typed, decimation) the fix suggests, with timing_note appended (e.g. the
sim_substeps a Robotiq 2F85 needs); None derives one from the env's sim_dt. wrong_fix replaces
the closing "do not rescale or resample the actions" sentence for a client with a more plausible wrong fix.
Source code in eden/extensions/policies/adapters.py
check_env_gripper_term
¶
check_env_gripper_term(env, term_name: str, **expected) -> None
Fail fast when the env's built gripper term disagrees with settings a policy also carries.
Some client policies expose gripper knobs (thresholds, hold, decode mode) that the env's
:class:GripperDecisionController also carries. When the env is built (it always is before a
policy is constructed in the drivers), the two are compared so a mismatch is reported at
setup time rather than silently ignored. Envs without an action manager (unit tests) skip.
Every field of :data:GRIPPER_DECISION_FIELDS is compared: one the caller does not pass in
expected must be at the controller's default, because hysteresis, a hold, a sticky latch or
continuous mode each replace the plain threshold rule the caller relies on.
Clients forward the server's raw gripper scalar, so any other gripper term (e.g. a task
config's own BinaryJointController) would silently misread it: a missing term, a term of
another type, or an expected field the term does not have all raise.
Source code in eden/extensions/policies/adapters.py
check_plain_observation_group
¶
check_plain_observation_group(env, group: str, *, width: int | None = None, scales: dict[str, Sequence[float]] | None = None, fix: str) -> None
Fail fast when an observation group a client slices by position is not the plain current reading.
Clients pack a group by column (proprio[:7] is the arm), so the group must be one flat
(num_envs, width) tensor of this step's values: no history window (it puts older frames
first), no scale/clip/modifiers (a delay or a rescale reaches the server unannounced),
and no noise when the group corrupts. scales names the terms whose post-compute scale is
part of the wire contract (term -> the exact per-column scale the factory sets, e.g. robosuite's
second-finger sign flip): such a term must exist and carry exactly that scale, every other term
a scale of 1. fix is appended to the message and should name the factory that builds the
group. Envs without an observation manager (unit tests) skip.
Source code in eden/extensions/policies/adapters.py
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 | |
check_gripper_state_matches_action
¶
Fail fast when a gripper state term and the gripper action term describe different grippers.
A client sends the state normalised over [open_pos, close_pos] and the env maps the server's
command onto [open_action, close_action]; the two come from separate observation_options
and action_options arguments, so a stroke set in one call only would skew every request. The
state must read a DOF of the action term's entity, and its open / close positions must equal the
action term's (per-DOF dict targets are compared for that DOF). Envs without managers skip.
Source code in eden/extensions/policies/adapters.py
check_joint_pass_through
¶
check_joint_pass_through(term, *, term_name: str, subject: str, fix: str, commands: str = "the server's absolute joint targets", wrong_fix: str = _PASS_THROUGH_WRONG_FIX) -> None
Raise unless joint action term executes the client's commands untouched (see :func:_pass_through_problems).
subject names the client and setup ("PiPolicy.policy_setup='aloha'"); commands what it sends
(absolute targets by default); fix the action_options(...) call that builds the right term; and
wrong_fix the client-side workaround to rule out. The caller has already checked the term's type. Every
problem is listed at once.
Source code in eden/extensions/policies/adapters.py
check_droid_arm_term
¶
check_droid_arm_term(arm, *, term_name: str, subject: str, fix: str, commands: str = "the server's absolute joint targets", wrong_fix: str = _DROID_ARM_WRONG_FIX) -> None
:func:check_joint_pass_through for a DROID arm term, which must also take DROID's joints in DROID's order.
That is one column per DROID arm joint, and DROID's own joint names in DROID's order (a rig with other names is
checked by width only): the server reads row i as DROID's joint i.
Source code in eden/extensions/policies/adapters.py
check_droid_joint_state
¶
Raise unless group is [DofsPos of arm's joints in its order, GripperState closedness].
A DROID client sends group[:7] as joint_position and group[7] as gripper_position, and the
server replies commands for those joints in that order, which the adapter hands column by column to arm's
dofs_name. observation_options() and action_options() take the arm entity and DOF names separately,
so the two can differ without either failing to build. The joint angles must be absolute (no
offset_from_default); fix names the factory calls that build matching terms.
Source code in eden/extensions/policies/adapters.py
Transports, serializers and errors¶
Request/reply transports to an inference (or any request/reply) server.
A transport moves a payload to a server and returns the decoded reply. It is built from
an explicit :class:TransportConfig (address, timeout, endpoints, serializer key and
transport-specific knobs) and selected by TRANSPORT_REGISTRY key, so nothing here
depends on who the caller is. Optional dependencies are imported lazily.
Every transport maps failures onto :mod:eden.utils.policy_client.errors: unreachable server or
timeout -> :class:TransportError (a ConnectionError), server-side failure ->
:class:ServerError (a RuntimeError).
==================== ============================================= ==================
Key Wire Default serializer
==================== ============================================= ==================
http_json requests POST, JSON body / JSON reply (strict JSON)
http_json_numpy POST with a json_numpy text body json_numpy
http_multipart POST multipart form; payload is (data, files) none
openpi_websocket WebSocket, msgpack + OpenPI numpy hook msgpack_openpi
galaxea_websocket as above, str-keyed records, __reset__ msg msgpack_galaxea
zmq_msgpack ZMQ REQ/REP {"endpoint", "data"} envelope msgpack_numpy
==================== ============================================= ==================
Attributes¶
Classes¶
TransportConfig
dataclass
¶
TransportConfig(host: str = 'localhost', port: int | None = None, request_timeout: float = 30.0, act_endpoint: str = '/act', set_task_endpoint: str | None = None, reset_endpoint: str | None = None, set_task_payload_key: str = 'task_description', reset_payload_key: str = 'task_description', task_description: str | None = None, serializer: str | None = None, knobs: dict[str, Any] = dict())
Everything a transport needs to know, independent of the caller.
| PARAMETER | DESCRIPTION |
|---|---|
host
|
Server address, joined by :func:
TYPE:
|
port
|
Server address, joined by :func:
TYPE:
|
request_timeout
|
Request timeout in seconds (HTTP transports).
TYPE:
|
act_endpoint
|
HTTP route for inference, starting with
TYPE:
|
set_task_endpoint
|
The server's set-task / reset API: an HTTP route, a ZMQ endpoint name or a GalaxeaVLA
reset message key.
TYPE:
|
reset_endpoint
|
The server's set-task / reset API: an HTTP route, a ZMQ endpoint name or a GalaxeaVLA
reset message key.
TYPE:
|
set_task_payload_key
|
JSON keys carrying the task description (HTTP transports).
TYPE:
|
reset_payload_key
|
JSON keys carrying the task description (HTTP transports).
TYPE:
|
task_description
|
Initial task text; :meth:
TYPE:
|
serializer
|
TYPE:
|
knobs
|
Transport-specific settings; each transport declares the ones it reads (with
defaults) in its |
transport
|
|
ignoring
|
|
TransportBase
¶
TransportBase(config: TransportConfig)
Bases: ABC
Request/reply channel to a server.
Subclasses declare in knobs the optional settings they read from
config.knobs (with defaults), so a transport's configuration surface is visible
in one place.
Source code in eden/utils/policy_client/transport.py
Attributes¶
serializer
instance-attribute
¶
serializer: SerializerBase | None = make_serializer(key) if key is not None else None
Methods:¶
knob
¶
Read a declared knob, falling back to its default.
Source code in eden/utils/policy_client/transport.py
infer
abstractmethod
¶
set_task
¶
set_task(task_description: str) -> None
Record a new task text; servers with a set_task route also get told.
HttpTransportBase
¶
HttpTransportBase(config: TransportConfig)
Bases: TransportBase
Shared requests.Session plumbing and error mapping for the HTTP transports.
knobs: set_task_as_query (send the task as a URL query parameter instead of a
JSON body; Octo) and reset_with_payload (send {reset_payload_key:
task_description} on reset, else a bare POST).
Source code in eden/utils/policy_client/transport.py
Attributes¶
knobs
class-attribute
instance-attribute
¶
base_url
instance-attribute
¶
base_url = server_url(config.host, config.port, schemes=('http', 'https')).rstrip('/')
Methods:¶
post
¶
post(endpoint: str, **kwargs)
POST to endpoint and return the raw response, mapping request failures.
Source code in eden/utils/policy_client/transport.py
post_json
¶
POST a JSON body (none when payload is None) and return the parsed JSON reply.
Source code in eden/utils/policy_client/transport.py
set_task
¶
set_task(task_description: str) -> None
Source code in eden/utils/policy_client/transport.py
HttpJsonTransport
¶
HttpJsonTransport(config: TransportConfig)
Bases: HttpTransportBase
JSON body in, JSON reply out (RobotArena-style servers: open-pi-zero, Octo).
Source code in eden/utils/policy_client/transport.py
HttpJsonNumpyTransport
¶
HttpJsonNumpyTransport(config: TransportConfig)
Bases: HttpTransportBase
json_numpy-encoded text body and reply (MolmoAct2's host_server_droid.py).
Source code in eden/utils/policy_client/transport.py
Attributes¶
Methods:¶
infer
¶
Source code in eden/utils/policy_client/transport.py
HttpMultipartTransport
¶
HttpMultipartTransport(config: TransportConfig)
Bases: HttpTransportBase
Multipart form upload; the payload is a (data, files) pair as requests expects.
Source code in eden/utils/policy_client/transport.py
OpenPiWebsocketTransport
¶
OpenPiWebsocketTransport(config: TransportConfig)
Bases: TransportBase
Port of openpi_client.websocket_client_policy.WebsocketClientPolicy (no openpi_client needed).
Connects to ws://host:port (host may be a full ws:///wss:// URL, see
:func:server_url), reads the server's metadata handshake, then exchanges one msgpack
message per inference. A str reply is the server reporting an error.
knobs:
api_key: sent as anAuthorization: Api-Key …header.ws_ping_timeout: keepalive pings every 20 s, each answered within this many seconds, as the OpenPI client does by default; a dead server then closes the connection instead of blocking :meth:infer(which has no timeout of its own) forever.Nonedisables them: a server whose inference blocks its event loop (slow JIT compilation, diffusion sampling) cannot answer pings in time, and the client would drop a live connection.wait_for_server_s: a refused connection is retried every 5 s, as the OpenPI client waits for its server to come up, for at most this many seconds (None: forever).
Source code in eden/utils/policy_client/transport.py
Attributes¶
knobs
class-attribute
instance-attribute
¶
Methods:¶
connect
¶
Source code in eden/utils/policy_client/transport.py
infer
¶
GalaxeaWebsocketTransport
¶
GalaxeaWebsocketTransport(config: TransportConfig)
Bases: OpenPiWebsocketTransport
GalaxeaVLA scripts/serve_policy.py: OpenPI-style socket with str-keyed records and a reset message.
Always sends the Authorization header (empty key allowed), does not retry on
refused connections, and clears the server's chunk cache with {reset_endpoint: True}
(serve_policy.py's "__reset__") on both reset and set_task; with
reset_endpoint=None neither sends anything. A dict reply carrying error is a
server failure.
knobs: api_key, ws_ping_timeout, ws_timeout_s (connection open timeout).
Source code in eden/utils/policy_client/transport.py
Attributes¶
knobs
class-attribute
instance-attribute
¶
Methods:¶
reset
¶
reset(options: dict | None = None) -> None
Source code in eden/utils/policy_client/transport.py
ZmqMsgpackTransport
¶
ZmqMsgpackTransport(config: TransportConfig)
Bases: TransportBase
ZMQ REQ/REP client for Isaac-GR00T's PolicyServer and its derivatives (RLDX-1).
Requests are {"endpoint": name, "data": {...}, "api_token"?: str}; get_action
returns [action_dict, info] and reset a dict. A dict reply with error is a
server failure. After a timeout the REQ socket is wedged, so it is recreated before
the timeout is re-raised. Select the serializer per server: msgpack_numpy (GR00T,
default) or msgpack_npy_blob (RLDX-1).
knobs: timeout_ms (send/receive timeout) and api_token. config.reset_endpoint
names the server's reset endpoint ("reset" for GR00T / RLDX-1); None sends nothing.
Source code in eden/utils/policy_client/transport.py
Attributes¶
Methods:¶
call_endpoint
¶
Round-trip one {"endpoint": …} request.
Source code in eden/utils/policy_client/transport.py
infer
¶
Source code in eden/utils/policy_client/transport.py
Functions:¶
server_url
¶
Join host and port into scheme://host[:port][/path].
host is either a bare hostname (localhost, 10.0.0.1, node:8000), which gets
schemes[0], or a full URL whose scheme must be one of schemes. Its path is kept,
so host="wss://example.com/ws", port=None addresses the server as given. A port in
host and port must agree. Pass port=None to use the URL's port, or the
scheme's default port when the URL has none. tcp (ZMQ) needs a port and no path.
Source code in eden/utils/policy_client/transport.py
make_transport
¶
make_transport(key: str, config: TransportConfig) -> TransportBase
Instantiate the transport registered under key; unknown keys list the available ones.
Source code in eden/utils/policy_client/transport.py
Wire serialisers for request/reply clients.
A serialiser turns a payload dict into the bytes (or text) a server expects and decodes
the reply. They are selected by SERIALIZER_REGISTRY key (TransportConfig.serializer)
so the choice is a plain string that survives config serialisation. Each one imports its
optional dependency lazily and reports the extra to install when it is missing.
===================== ==========================================================
Key Wire format
===================== ==========================================================
json json.dumps / json.loads
json_numpy json_numpy (numpy arrays as base64 inside JSON text)
msgpack_openpi msgpack with OpenPI's numpy hook (b"__ndarray__" records)
msgpack_galaxea same record layout with str keys, as the GalaxeaVLA server speaks
msgpack_numpy msgpack_numpy (Isaac-GR00T PolicyServer)
msgpack_npy_blob msgpack with arrays tunnelled as np.save blobs (RLDX-1)
===================== ==========================================================
Attributes¶
Classes¶
SerializerBase
¶
JsonSerializer
¶
Bases: SerializerBase
Plain JSON text; numpy arrays must already be converted to lists by the policy.
JsonNumpySerializer
¶
Bases: SerializerBase
json_numpy text: numpy arrays are embedded as base64 records.
Source code in eden/utils/policy_client/serializer.py
MsgpackOpenPiSerializer
¶
Bases: SerializerBase
msgpack with OpenPI's numpy hook, byte-for-byte what openpi_client.msgpack_numpy produces.
Array records use bytes keys (b"__ndarray__") because that is what the OpenPI
WebsocketPolicyServer looks for when unpacking. Bytes keys survive msgpack's
raw=False decoding unchanged (they are packed as bin), so the same hook decodes
the server's replies.
Source code in eden/utils/policy_client/serializer.py
MsgpackGalaxeaSerializer
¶
Bases: MsgpackOpenPiSerializer
OpenPI record layout with str keys, matching the GalaxeaVLA server's codec.
Replies are normalised recursively so nested dicts come back with str keys.
Source code in eden/utils/policy_client/serializer.py
MsgpackNumpySerializer
¶
Bases: SerializerBase
msgpack_numpy as used by Isaac-GR00T's ZMQ PolicyServer.
GR00T's MsgSerializer additionally tunnels ModalityConfig objects as
{"__ModalityConfig__": True, "as_json": ...} records (the get_modality_config
reply), with as_json a dict or its JSON text. They are decoded to the plain dict so
clients can read modality_keys / delta_indices without importing gr00t.
Source code in eden/utils/policy_client/serializer.py
MsgpackNpyBlobSerializer
¶
Bases: SerializerBase
Client-side port of RLDX-1's MsgSerializer: arrays travel as np.save byte blobs.
Torch tensors are moved to CPU (half precision widened to float32) and encoded like
arrays. RLDX tunnels ModalityConfig objects (the get_modality_config reply) as
{"__ModalityConfig_class__": True, "as_json": ...} records; they are decoded to the
plain dict (from a dict or its JSON text), as :class:MsgpackNumpySerializer does for
GR00T's tag.
Source code in eden/utils/policy_client/serializer.py
Functions:¶
require_module
¶
Import an optional client dependency, or raise naming the extra that provides it.
Source code in eden/utils/policy_client/serializer.py
make_serializer
¶
make_serializer(key: str) -> SerializerBase
Instantiate the registered serialiser key; unknown keys list the available ones.
Source code in eden/utils/policy_client/serializer.py
Error hierarchy for request/reply clients (eden.utils.policy_client).
TransportError also derives from :class:ConnectionError and every error derives from
:class:RuntimeError, so callers that already distinguish "server unreachable" from
"protocol failure" with those builtins keep working.
::
ClientError (RuntimeError)
├── TransportError (…, ConnectionError) server unreachable / connection dropped
│ └── TransportTimeoutError request timed out
├── ServerError server answered with an error (HTTP 4xx/5xx, {"error": …})
└── SerializationError payload or reply could not be (de)serialised
Classes¶
ClientError
¶
Bases: RuntimeError
Base class for every remote-policy client failure.
TransportError
¶
Bases: ClientError, ConnectionError
The policy server could not be reached or the connection was lost.
TransportTimeoutError
¶
Bases: TransportError
A request to the policy server timed out.
ServerError
¶
Bases: ClientError
The policy server answered, but with an error.
SerializationError
¶
Bases: ClientError
A payload or reply could not be encoded or decoded.
Local policies¶
Locomotion¶
HolosomaLocomotionPolicy
¶
HolosomaLocomotionPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
Holosoma velocity-command locomotion controller for G1 29-DOF (ONNX backend).
Reads proprioception directly from the env each step and drives the frozen
ONNX policy with the current 3-D velocity command (see :meth:set_command).
Only the Unitree G1 29-DOF robot is supported; G1Dofs29.dofs_name order
matches Holosoma's joint order, so no DOF remapping is needed.
Load the ONNX graph, cache DOF indices, and allocate command/phase buffers.
Source code in eden/extensions/policies/holosoma/policy.py
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str | None = 'holosoma/ppo_g1_29dof.onnx'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = 'cfe906f8c5a87b5c4b367b1241e8b217'
Methods:¶
set_command
¶
Set the 3-D velocity command for all (or a slice of) environments.
| PARAMETER | DESCRIPTION |
|---|---|
command
|
Either
TYPE:
|
Source code in eden/extensions/policies/holosoma/policy.py
robot_options
classmethod
¶
robot_options() -> RobotOptions
env_options
classmethod
¶
Return env options matching Holosoma's training sim: 200 Hz physics,
decimation 4 → 50 Hz control (== rl_rate). Holosoma trains all
backends at fps=200, control_decimation=4, substeps=1
(holosoma.config_values.simulator), so reproduce that exact cadence
rather than another (sim_dt, decimation) pair with the same 50 Hz product.
Source code in eden/extensions/policies/holosoma/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Configure a 29-DOF implicit PD controller consuming absolute targets.
:meth:step already produces absolute joint-position targets
(default + scale * action), so the controller uses
reference_source=ZERO and scale=1.0 (no extra offset / scaling).
Source code in eden/extensions/policies/holosoma/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
No env-computed observations.
The policy builds its own 100-dim actor_obs from raw simulator state
in :meth:step (the obs argument is ignored), so the env needs no
observation terms — an empty group set builds and steps fine.
Source code in eden/extensions/policies/holosoma/policy.py
reset
¶
reset(envs_idx: slice | Tensor | None = None) -> None
Reset prev-action and gait-phase state for the given environments.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Environments to reset;
TYPE:
|
Source code in eden/extensions/policies/holosoma/policy.py
step
¶
Advance the gait clock, run the policy, and return 29-DOF targets.
| PARAMETER | DESCRIPTION |
|---|---|
obs
|
Ignored — raw simulator state is read directly from the env.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Tensor]
|
|
Source code in eden/extensions/policies/holosoma/policy.py
HomiePolicy
¶
HomiePolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
OpenHomie frozen walk/stand tracker for G1 lower-body locomotion.
Reads proprioception directly from the env each step and drives the frozen
tracker with the current 4-D command (see :meth:set_command). Only the
Unitree G1 29-DOF robot is supported; G1Dofs29.dofs_name order matches
HOMIE's joint order, so no DOF remapping is needed.
Build the tracker, cache DOF indices, and allocate command buffers.
Source code in eden/extensions/policies/homie/policy.py
Attributes¶
stand_model_path
class-attribute
instance-attribute
¶
stand_model_path: str = 'homie/model_stand.pt'
walk_md5_hash
class-attribute
instance-attribute
¶
walk_md5_hash: str | None = '556dda04493332fdaf83385c93218169'
stand_md5_hash
class-attribute
instance-attribute
¶
stand_md5_hash: str | None = '31edd14f666173d583218b5fac24e671'
Methods:¶
set_command
¶
Set the 4-D locomotion command for all (or a slice of) environments.
| PARAMETER | DESCRIPTION |
|---|---|
command
|
Either
TYPE:
|
Source code in eden/extensions/policies/homie/policy.py
robot_options
classmethod
¶
robot_options() -> RobotOptions
env_options
classmethod
¶
Return env options for 50 Hz control (sim_dt=0.002, decimation=10).
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Configure a 29-DOF implicit PD controller consuming absolute targets.
:meth:step already produces absolute joint-position targets
(default + scale * action for the lower body, default for the arms), so
the controller uses reference_source=ZERO and scale=1.0.
Source code in eden/extensions/policies/homie/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Return a minimal placeholder observation group.
HOMIE reads raw simulator state directly in :meth:step; Eden still
requires at least one observation term to build the env.
Source code in eden/extensions/policies/homie/policy.py
reset
¶
reset(envs_idx: slice | Tensor | None = None) -> None
Reset the tracker history/prev-actions for the given environments.
| PARAMETER | DESCRIPTION |
|---|---|
envs_idx
|
Environments to reset;
TYPE:
|
Source code in eden/extensions/policies/homie/policy.py
step
¶
Read proprioception, run the tracker, and return 29-DOF targets.
| PARAMETER | DESCRIPTION |
|---|---|
obs
|
Ignored — raw simulator state is read directly from the env.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Tensor]
|
|
Source code in eden/extensions/policies/homie/policy.py
OliVelocityLocoPolicy
¶
OliVelocityLocoPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
Frozen velocity-tracking loco/stand controller for LimX Oli (rsl_rl backend).
Source code in eden/extensions/policies/limx_oli_loco/policy.py
Attributes¶
Methods:¶
set_command
¶
Set the 3-D (vx, vy, wz) velocity command. All zeros (the default) is a stand.
Source code in eden/extensions/policies/limx_oli_loco/policy.py
reset
¶
Clear the action feedback at a handover so the policy re-enters from rest.
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
An absolute-target controller: :meth:step has already applied OLI_ACTION_SCALE.
The deploy slot contract requires reference_source=ZERO and scale=1.0 so the
returned targets are written to the robot unchanged; the training scale lives inside
:meth:step, not here.
Source code in eden/extensions/policies/limx_oli_loco/policy.py
command_options
classmethod
¶
command_options(entity_name: str = 'robot') -> dict[str, CommandTermOptions]
The velocity_command term the observation group below reads, held at zero (a stand).
Declared so the env this policy owns (--policy limx_oli_loco alone, or named first)
can build: generated_commands resolves its term on the env's command manager at
construction, and a task-less env has no command manager unless a policy asks for one.
:meth:step reads its own :meth:set_command buffer, not this term, so the operator's
--velocity_command / --base_command reach the policy through the deploy loop as
they do for Holosoma; the term here only has to exist and be quiet.
Source code in eden/extensions/policies/limx_oli_loco/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
The training group, in declaration order — the layout :meth:step reproduces.
Source code in eden/extensions/policies/limx_oli_loco/policy.py
step
¶
Run the frozen actor and return absolute 31-DOF joint targets.
obs is ignored: riding along on another task's env, this policy's observation group
is not the one that env computes, so it reads robot state directly (the Holosoma pattern).
Source code in eden/extensions/policies/limx_oli_loco/policy.py
HuskyPolicy
¶
HuskyPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
HUSKY skateboarding controller (G1 23-DOF + skateboard, ONNX backend).
Source code in eden/extensions/policies/husky/policy.py
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str = 'husky/skater_flat_g1.onnx'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = '69549f8d4a4fcc2629e4ba988666f841'
Methods:¶
robot_options
classmethod
¶
skateboard_options
classmethod
¶
Skateboard placed under the robot's left foot (training placement).
scene_entity_options
classmethod
¶
The board the policy rides — without it the robot has nothing to balance on.
env_options
classmethod
¶
Source code in eden/extensions/policies/husky/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
The training task's action term: step returns the actor's raw (normalized) action.
Source code in eden/extensions/policies/husky/policy.py
command_options
classmethod
¶
command_options() -> dict[str, CommandTermOptions]
The held [lin_vel_x, relative_heading] command and the phase clock the actor observes.
Source code in eden/extensions/policies/husky/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
The training task's actor group, with its 5-step history and without its training noise.
Source code in eden/extensions/policies/husky/policy.py
set_command
¶
Set the skate command [lin_vel_x, relative_heading] (shared or per-env).
lin_vel_x is the target board speed in m/s (training range 0–1.5);
below 0.1 the phase clock pins at 0 and the policy balances in place.
relative_heading (rad, trained on ±pi/4) steers the board relative
to its initial heading. Takes effect from the next observation the env computes.
Only an env built from :meth:command_options holds a command. On --task husky the
training task's :class:~eden.tasks.husky.custom_terms.commands.SkateCommand samples its
own, which is what the policy then observes.
Source code in eden/extensions/policies/husky/policy.py
Whole-body control and motion tracking¶
AMOPolicy
¶
AMOPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
AMO: Adaptive Motion Optimization for Hyper-Dexterous Humanoid Whole-Body Control.
Source code in eden/extensions/policies/amo/policy.py
Attributes¶
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = '3efbda329f247f41a7459f50f88f4bc5'
Methods:¶
robot_options
classmethod
¶
robot_options() -> G1Dofs29NoWristDof
Return a G1Dofs29NoWristDof with AMO's training default pose and PD gains.
env_options
classmethod
¶
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/amo/policy.py
command_options
classmethod
¶
command_options(**kwargs) -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/amo/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/amo/policy.py
step
¶
Source code in eden/extensions/policies/amo/policy.py
BeyondMimicMovesPolicy
¶
BeyondMimicMovesPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
BeyondMimic G1-Moves: 60 motion-tracking policies for the 29-DOF Unitree G1.
Source dataset: https://huggingface.co/datasets/exptech/g1-moves
The clip is selected at configure time via the clip_name option (e.g. "B_DadDance").
The policy resolves the per-clip checkpoint and motion file from the dataset's
manifest.json and downloads them on first use via huggingface_hub.
Notes
The expected env / observation / action setup mirrors examples/projects/mimic/beyondmimic (and the
BeyondMimicConfig task), since the upstream g1-moves env.yaml is functionally identical:
decimation=4,sim_dt=0.005, 29-DOF G1- Observation (160 dims):
[motion(58), motion_anchor_pos_b(3), motion_anchor_ori_b(6), base_lin_vel(3), base_ang_vel(3), dofs_pos_rel(29), dofs_vel(29), last_action(29)] - Joint order is the MuJoCo XML declaration order (no permutation needed).
The robot entity default_dofs_pos MUST equal :data:G1_MOVES_DEFAULT_DOFS_POS,
otherwise both dofs_pos_rel (obs) and the action target end up biased relative
to the policy's training frame and the robot won't track. eden.robots.G1Dofs29's
out-of-the-box defaults differ (e.g. hip_pitch=-0.1 vs. mjlab's -0.312,
elbow=1.5 vs. 0.6), so build the robot with :func:g1_moves_robot (what
:meth:robot_options returns).
Source code in eden/extensions/policies/beyondmimic_moves/policy.py
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str | None = self.fetch_asset(f'{clip_dir}/policy/{self.clip_name}_policy.pt')
Methods:¶
prepare_motion_file
classmethod
¶
Resolve a clip's motion file, downloading and remapping body axes as needed.
Returns the path to a Genesis-BFS-ordered NPZ next to the original mjlab
download. Safe to call before constructing the env (so the resolved path
can be passed to :meth:command_options); subsequent calls hit the cached
<clip>_eden.npz without re-reading the source.
Source code in eden/extensions/policies/beyondmimic_moves/policy.py
env_options
classmethod
¶
Source code in eden/extensions/policies/beyondmimic_moves/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/beyondmimic_moves/policy.py
command_options
classmethod
¶
command_options(clip_name: str | None = None, debug_vis: bool = False, entity_name: str = 'robot', ghost_entity_name: str = '', sampling_mode: str = 'start') -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/beyondmimic_moves/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/beyondmimic_moves/policy.py
step
¶
GMTPolicy
¶
GMTPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
GMT: General Motion Tracking for Humanoid Whole-Body Control.
NOTE
This policy only supports 23 dofs G1 robot.
Source code in eden/extensions/policies/gmt/policy.py
Attributes¶
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = 'eb4cfef3de37d27c3b7eae0ac7baa8bf'
Methods:¶
robot_options
classmethod
¶
robot_options() -> G1Dofs29NoWristDof
Return a G1Dofs29NoWristDof matching GMT's training pose / gains / armature / damping.
env_options
classmethod
¶
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/gmt/policy.py
command_options
classmethod
¶
command_options(motion_file: str, debug_vis: bool = False) -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/gmt/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/gmt/policy.py
step
¶
Get action from policy.
Source code in eden/extensions/policies/gmt/policy.py
HumanoidGptPolicy
¶
HumanoidGptPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
Humanoid-GPT whole-body motion-tracking policy for the Unitree G1 (29 DOF).
Runs the released Transformer (default) or MLP (policy_type="mlp")
ONNX checkpoint; both share the 136-dim observation / action contract, the
transformer additionally consuming a K-frame obs history.
policy_type, transformer_history_len, ref_ema_alpha and ref_cvel_at_com shape the
observation as well as the network, so pass the same values to :meth:observation_options and
:meth:configure; an instance configured differently from the env it is handed is refused.
Source code in eden/extensions/policies/humanoid_gpt/policy.py
Attributes¶
mlp_md5_hash
class-attribute
instance-attribute
¶
mlp_md5_hash: str | None = '7147c555a68432c76723c52468bbdd26'
transformer_checkpoint_path
class-attribute
instance-attribute
¶
transformer_checkpoint_path: str = _TRANSFORMER_CHECKPOINT
transformer_md5_hash
class-attribute
instance-attribute
¶
transformer_md5_hash: str | None = 'c2a884643074cee94ab57722bea116b4'
Methods:¶
env_options
classmethod
¶
Source code in eden/extensions/policies/humanoid_gpt/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
The network's raw action, scaled per joint about Humanoid-GPT's nominal stance.
Source code in eden/extensions/policies/humanoid_gpt/policy.py
command_options
classmethod
¶
command_options(motion_file: str | list[str] = 'motion.npz', debug_vis: bool = False, entity_name: str = 'robot', ghost_entity_name: str = '', sampling_mode: str = 'start') -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/humanoid_gpt/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot', policy_type: str = _CLASS_SETTING, transformer_history_len: int = _CLASS_SETTING, ref_ema_alpha: float | None = _CLASS_SETTING, ref_cvel_at_com: bool = _CLASS_SETTING) -> dict[str, ObservationGroupOptions]
The 136-dim frame; for the Transformer, stacked into a (K, 136) history backfilled at reset.
Each knob not passed is the class's own setting, which is what a task-less
eden inference|deploy --policy humanoid_gpt builds with. Pass the values the policy is
configured with otherwise, e.g. observation_options(policy_type="mlp") alongside
configure(policy_type="mlp").
Source code in eden/extensions/policies/humanoid_gpt/policy.py
step
¶
Source code in eden/extensions/policies/humanoid_gpt/policy.py
MetaMotivoPolicy
¶
MetaMotivoPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
MetaMotivo (FBcpr) policy for SMPL humanoid control on Eden.
The task-context vector z determines the behavior. It can be set
directly via :meth:set_context, inferred from a goal via :meth:compute_goal_context,
resampled from the prior via :meth:sample_random_context, or left as the random
sample drawn on the first :meth:step.
| PARAMETER | DESCRIPTION |
|---|---|
model_name
|
Hugging Face model id, loaded directly with
TYPE:
|
Source code in eden/extensions/policies/metamotivo/policy.py
Attributes¶
Methods:¶
robot_options
classmethod
¶
robot_options() -> SmplHumanoid
env_options
classmethod
¶
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/metamotivo/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/metamotivo/policy.py
step
¶
Compute action from observation using the MetaMotivo model.
| PARAMETER | DESCRIPTION |
|---|---|
obs
|
Observation dict from Eden's observation manager.
Expected key:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, Tensor]
|
Action dict with key |
Source code in eden/extensions/policies/metamotivo/policy.py
set_context
¶
Set the task-context vector directly.
| PARAMETER | DESCRIPTION |
|---|---|
z
|
Shape
TYPE:
|
Source code in eden/extensions/policies/metamotivo/policy.py
sample_random_context
¶
sample_random_context(batch_size: int | None = None) -> Tensor
Sample a random context from the model's z prior.
| PARAMETER | DESCRIPTION |
|---|---|
batch_size
|
If None, uses
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Shape |
Source code in eden/extensions/policies/metamotivo/policy.py
compute_goal_context
¶
Compute context from a goal observation via the backward map, and make it current.
| PARAMETER | DESCRIPTION |
|---|---|
goal_obs
|
Target proprioceptive observation, shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Context vector, shape |
Source code in eden/extensions/policies/metamotivo/policy.py
compute_tracking_context
¶
Compute tracking context from a sequence of target observations.
| PARAMETER | DESCRIPTION |
|---|---|
target_obs
|
Target observations, shape
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Tensor
|
Context vectors, shape |
Source code in eden/extensions/policies/metamotivo/policy.py
OmniXtremePolicy
¶
OmniXtremePolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
OmniXtreme: Dual-policy humanoid motion tracking (flow-matching base + residual correction).
Source code in eden/extensions/policies/omnixtreme/policy.py
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str = 'omnixtreme/base_policy.onnx'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = '67b75756640bbb52720ee5920bbf0910'
residual_checkpoint_path
class-attribute
instance-attribute
¶
residual_checkpoint_path: str = 'omnixtreme/residual_policy.onnx'
residual_md5_hash
class-attribute
instance-attribute
¶
residual_md5_hash: str | None = '7ad3515080d32e51241f959984c0b568'
fk_checkpoint_path
class-attribute
instance-attribute
¶
fk_checkpoint_path: str = 'omnixtreme/fk.onnx'
fk_md5_hash
class-attribute
instance-attribute
¶
fk_md5_hash: str | None = '94c9896adda6939b0ddfa508ddc0d0be'
Methods:¶
robot_options
classmethod
¶
Return the OmniXtreme G1 (7520-22.5 hip-pitch motors, training default pose).
env_options
classmethod
¶
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/omnixtreme/policy.py
command_options
classmethod
¶
command_options(motion_file: str, debug_vis: bool = False) -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/omnixtreme/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/omnixtreme/policy.py
step
¶
Source code in eden/extensions/policies/omnixtreme/policy.py
ProtoMotionV3Policy
¶
ProtoMotionV3Policy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
ProtoMotions v3 (g1-bones-deploy) motion-tracking policy on Unitree G1 (29 DOF).
Loads the AMP actor directly from last.ckpt and reproduces the obs +
action transform pipeline in PyTorch, so checkpoints can be tested without
re-exporting an ONNX bundle.
Source code in eden/extensions/policies/protomotionv3/policy.py
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str = 'protomotion_v3/g1_bones_deploy.ckpt'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = '205a22b9becc4c8b589da7509d940b5c'
Methods:¶
robot_options
classmethod
¶
robot_options() -> G1Dofs29
Return the stock G1Dofs29, whose gains match the training values.
Its default pose does not: the training pose is the action term's offset.
Source code in eden/extensions/policies/protomotionv3/policy.py
env_options
classmethod
¶
Source code in eden/extensions/policies/protomotionv3/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
bm_pd_action: the actor's mean action, scaled per joint about the training default pose.
Source code in eden/extensions/policies/protomotionv3/policy.py
command_options
classmethod
¶
command_options(motion_file: str = 'motion.npz', debug_vis: bool = False, entity_name: str = 'robot', ghost_entity_name: str = '', sampling_mode: str = 'start') -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/protomotionv3/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
The 349-dim input: reduced coords (64), future target poses (256), previous joint targets (29).
Source code in eden/extensions/policies/protomotionv3/policy.py
step
¶
SonicPolicy
¶
SonicPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
GEAR-SONIC universal-token whole-body controller (g1 mode, ONNX backend).
The SONIC release is split into two ONNX graphs (encoder + decoder); both
are downloaded from Kashu7100/eden_policies (mirror of NVIDIA's HF repo)
on first use. To use the official NVIDIA bundle directly, set the
checkpoint_path / decoder_checkpoint_path class attrs to a local
path before instantiating, or pass them through configure(...).
Source code in eden/extensions/policies/sonic/policy.py
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | |
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str = 'sonic/model_encoder.onnx'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = 'a21244b1cdceac9dc8625442a55d42e0'
decoder_checkpoint_path
class-attribute
instance-attribute
¶
decoder_checkpoint_path: str = 'sonic/model_decoder.onnx'
decoder_md5_hash
class-attribute
instance-attribute
¶
decoder_md5_hash: str | None = '1d4391ad404d0ff36281abec349b23fd'
Methods:¶
robot_options
classmethod
¶
robot_options() -> G1Dofs29
Return a G1Dofs29 carrying the standing pose and PD gains the checkpoint expects.
This is the robot a task-less eden deploy/inference --policy sonic builds its
scene from, so it decides both where the robot stands before the policy takes over
and — via :class:~eden.extensions.deployment.base.DeploymentBase, which resolves
default_dofs_kp / default_dofs_kd once into the Kp/Kd sent on the wire —
what PD loop tracks its targets.
Pose is :data:~eden.extensions.policies.sonic.joint_maps.SONIC_DOFS_POS, the
default_angles[] the action transform is centred on. The stock G1's shallower
stance would leave the robot 0.37 rad off at the knees and 0.9 rad at the elbows
when SONIC's first target lands.
Gains differ from the stock G1 in the hip-pitch row alone: SONIC derives them from the same motor table by the same Raibert heuristic Eden does, and hip pitch is the one row where the tables disagree — SONIC's is a 7520-22, Eden's stock spec a 7520-14. The stock gains would run the hips at 0.41x the released checkpoint's stiffness (40.18 vs 99.10 N·m/rad) under the 7520-22 action scale, i.e. 2.4x less torque per unit action than trained.
Armature and effort limits stay on the stock spec: those describe the physical actuator, not the controller, and which motor a given G1 revision carries is a hardware question this port cannot settle. Note the consequence — Genesis builds hip pitch with the 7520-14 armature (0.0102 vs SONIC's 0.0251 kg·m²) and clamps its torque at 88 rather than 139 N·m, so a Genesis rollout is still not SONIC's training dynamics even with the gains fixed.
Source code in eden/extensions/policies/sonic/policy.py
env_options
classmethod
¶
Source code in eden/extensions/policies/sonic/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/sonic/policy.py
command_options
classmethod
¶
command_options(motion_file: str | list[str] = 'motion.npz', debug_vis: bool = False, entity_name: str = 'robot', ghost_entity_name: str = '', sampling_mode: str = 'start') -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/sonic/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/sonic/policy.py
reset
¶
reset(envs_idx: slice | Tensor | None = None) -> None
Clear per-episode state at episode boundaries.
Resets:
* History ring buffers (q, qdot, action, base_ang,
gravity). Without clearing, the first decoder call after a reset
sees the previous episode's last 10 frames and produces a
discontinuous initial action.
* The heading offset — re-captured per env on the next step()
against whatever pose the motion was just resampled to. Critical for
sampling_mode != "start" (where the motion frame index changes
across episodes) and on motion wrap-around. Only the affected rows
are marked for re-capture, so partial resets in batched rollouts
leave the other envs' offsets intact.
Source code in eden/extensions/policies/sonic/policy.py
step
¶
Source code in eden/extensions/policies/sonic/policy.py
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 | |
Twist2Policy
¶
Twist2Policy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
TWIST2 whole-body motion-tracking controller (G1 29-DOF, ONNX backend).
Source code in eden/extensions/policies/twist2/policy.py
Attributes¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str = 'twist2/pretrained.onnx'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = 'a045b72bd49ce0993aa145f8958a5564'
Methods:¶
robot_options
classmethod
¶
robot_options() -> G1Dofs29
G1Dofs29 with the knees-bent default pose TWIST2 trains against.
The stock gains / action scale already match mjlab; only the default joint pose differs (Eden ships a home-ish pose), so we override it.
Source code in eden/extensions/policies/twist2/policy.py
env_options
classmethod
¶
Source code in eden/extensions/policies/twist2/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
The actor's raw (normalized) action, scaled per joint onto the knees-bent default pose.
Source code in eden/extensions/policies/twist2/policy.py
command_options
classmethod
¶
command_options(motion_file: str = 'motion.npz', debug_vis: bool = False, entity_name: str = 'robot', ghost_entity_name: str = '', sampling_mode: str = 'start') -> dict[str, CommandTermOptions]
Source code in eden/extensions/policies/twist2/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
One 127-dim frame, mimic then proprio, stacked into an 11-frame history (backfilled at reset).
Source code in eden/extensions/policies/twist2/policy.py
step
¶
Source code in eden/extensions/policies/twist2/policy.py
Manipulation¶
OmniResetPolicy
¶
OmniResetPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
UWLab's state-based OmniReset expert for one object pair (pair), acting at 10 Hz.
Source code in eden/extensions/policies/omnireset/policy.py
Attributes¶
Methods:¶
robot_options
classmethod
¶
robot_options() -> RobotOptions
scene_entity_options
classmethod
¶
The workspace and pair's objects (pair is filled from the policy's own).
Source code in eden/extensions/policies/omnireset/policy.py
env_options
classmethod
¶
action_options
classmethod
¶
action_options() -> dict[str, ActionTermOptions]
observation_options
classmethod
¶
observation_options() -> dict[str, ObservationGroupOptions]
Return the actor's 43-D group, term for term in POLICY_OBSERVATION_LAYOUT order.
See :data:~eden.extensions.policies.omnireset.constants.POLICY_OBSERVATION_LAYOUT.
Source code in eden/extensions/policies/omnireset/policy.py
step
¶
SimToolRealPolicy
¶
SimToolRealPolicy(env: EnvBase, options: PolicyOptions)
Bases: PolicyBase
Object-centric zero-shot tool-manipulation controller (29-DOF, SAPG-LSTM).
Source code in eden/extensions/policies/simtoolreal/policy.py
Attributes¶
object_scales
class-attribute
instance-attribute
¶
checkpoint_path
class-attribute
instance-attribute
¶
checkpoint_path: str | None = 'simtoolreal/model.pth'
md5_hash
class-attribute
instance-attribute
¶
md5_hash: str | None = 'c9f9207125f56b4d4ebaea1e816ab7a8'
Methods:¶
set_goal
¶
Set the per-env object goal pose (world frame, wxyz).
Source code in eden/extensions/policies/simtoolreal/policy.py
set_object_scales
¶
Set the per-env object grasp-bbox scale (the object_scales obs).
robot_options
classmethod
¶
robot_options() -> KukaSharpa
KukaSharpa at SimToolReal's base placement, fingertips grippier than the palm.
The composite already defaults to this port's home pose, gains, armature and gravity
compensation. The fingertip friction reproduces the original's
modify_asset_frictions (robot 0.5, fingertips 1.5); the whole hand keeps colliding,
since the fingers must contact an object all over to wrap it.
Source code in eden/extensions/policies/simtoolreal/policy.py
scene_entity_options
classmethod
¶
scene_entity_options(*, object_asset: str | None = None, vis_mode: str = 'visual') -> dict[str, EntityOptionsLike]
The workspace the policy's observations are defined against: table, tool, goal ghost.
The observation is object-centric (tool keypoints relative to the palm and the goal),
so a robot-only scene cannot host this policy. Frictions and placements are
SimToolReal's; see constants.
| PARAMETER | DESCRIPTION |
|---|---|
object_asset
|
:data:
TYPE:
|
vis_mode
|
TYPE:
|
Source code in eden/extensions/policies/simtoolreal/policy.py
env_options
classmethod
¶
action_options
classmethod
¶
action_options(entity_name: str = 'robot', hand_name: str = 'hand') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/simtoolreal/policy.py
observation_options
classmethod
¶
observation_options(entity_name: str = 'robot') -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/simtoolreal/policy.py
reset
¶
reset(envs_idx: slice | Tensor | None = None) -> None
Source code in eden/extensions/policies/simtoolreal/policy.py
step
¶
Source code in eden/extensions/policies/simtoolreal/policy.py
Remote policy clients¶
PiPolicy
¶
PiPolicy(env: EnvBase, options: PolicyOptions)
Bases: SetupClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]
OpenPI (pi0 / pi0-FAST) remote inference client.
All class attributes are configurable via PiPolicy.configure(...).
Source code in eden/extensions/policies/client.py
Attributes¶
chunk_temporal_ensemble_steps
class-attribute
instance-attribute
¶
chunk_temporal_ensemble_steps: int = 0
chunk_temporal_ensemble_dims
class-attribute
instance-attribute
¶
chunk_temporal_ensemble_dims: int | None = None
aloha_left_camera_name
class-attribute
instance-attribute
¶
aloha_left_camera_name: str = 'cam_left_wrist'
droid_arm_controller_kind
class-attribute
instance-attribute
¶
droid_arm_controller_kind: str = 'velocity'
Gr00tPolicy
¶
Gr00tPolicy(env: EnvBase, options: PolicyOptions)
Bases: SetupClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]
NVIDIA Isaac-GR00T remote-inference client.
All class attributes are configurable via :meth:Gr00tPolicy.configure.
Source code in eden/extensions/policies/client.py
Attributes¶
use_sim_policy_wrapper
class-attribute
instance-attribute
¶
use_sim_policy_wrapper: bool | None = None
Methods:¶
observe
¶
observe(obs: dict) -> None
Refuse an observation dict without a group the setup reads, then run the setup's per-step hook.
Construction checks the env publishes every group in observation_groups; this catches a dict the caller
built by hand (a dropped wrist_image, a renamed group) on the step it arrives, naming the fix.
Source code in eden/extensions/policies/gr00t/policy.py
pack_observations
¶
infer_payload
¶
infer_payload(payload: Any) -> ndarray
Round-trip as the base does; a server rejecting the request's format gets the flag to flip named.
Source code in eden/extensions/policies/gr00t/policy.py
Cosmos3Policy
¶
Cosmos3Policy(env: EnvBase, options: PolicyOptions)
Bases: ClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]
Cosmos3-Nano-Policy-DROID remote inference client (WebSocket).
All class attributes are configurable via Cosmos3Policy.configure(...).
Source code in eden/extensions/policies/client.py
Attributes¶
Methods:¶
observation_options
classmethod
¶
observation_options(camera_name: str = 'camera', wrist_camera_name: str = 'wrist_camera', secondary_camera_name: str | None = None, arm_entity_name: str = 'robot', arm_dofs_name: list[str] | None = None, gripper_entity_name: str | None = None, gripper_dof_name: str = 'left_driver_joint', gripper_close_pos: float = 0.8) -> dict[str, ObservationGroupOptions]
Wire the DROID joint-state proprio + exterior/wrist camera groups.
Mirrors OpenPI's droid_jointpos observation contract: an 8-D
proprio vector [joint_pos(7), gripper(1)] plus an image
(exterior) and wrist_image group. When secondary_camera_name
is given, an image_2 group carries the second exterior view; pass
the same value to Cosmos3Policy.configure() (the policy checks the
two agree when it is constructed).
Source code in eden/extensions/policies/cosmos3/policy.py
action_options
classmethod
¶
action_options(arm_entity_name: str = 'robot', arm_dofs_name: list[str] | None = None, gripper_entity_name: str | None = None, gripper_dof_name: str = 'left_driver_joint', gripper_close_pos: float = 0.8, gripper_threshold: float = 0.5) -> dict[str, ActionTermOptions]
Absolute joint-position arm controller + GripperDecisionController.
The server returns absolute arm joint targets, so the arm controller is
an ImplicitPDController with a zero reference (targets are applied
directly). The gripper term receives the server's closedness and binarizes
it at gripper_threshold (> threshold closes).
Source code in eden/extensions/policies/cosmos3/policy.py
pack_observations
¶
Convert Eden obs tensors to the Cosmos3 RoboLab server payload dict.
Source code in eden/extensions/policies/cosmos3/policy.py
unpack_actions
¶
Decode one (1, 8) action row → Eden controller dict.
Layout: [joint_pos(7), gripper(1)]. Gripper convention: the server
already re-applies its 1 - x transform, so the scalar is closedness
(> 0.5 ⇒ CLOSE). Delegated to JointPositionAdapter.
Source code in eden/extensions/policies/cosmos3/policy.py
GalaxeaVlaPolicy
¶
GalaxeaVlaPolicy(env: EnvBase, options: PolicyOptions)
Bases: SetupClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]
GalaxeaVLA (G0.5) remote WebSocket inference client.
All class attributes are configurable via GalaxeaVlaPolicy.configure(...).
Source code in eden/extensions/policies/client.py
Attributes¶
gripper_close_threshold
class-attribute
instance-attribute
¶
gripper_close_threshold: float = DROID_GRIPPER_THRESHOLD_DEFAULTS['gripper_close_threshold']
gripper_decode_mode
class-attribute
instance-attribute
¶
gripper_decode_mode: str = 'continuous-snap'
MolmoAct2Policy
¶
MolmoAct2Policy(env: EnvBase, options: PolicyOptions)
Bases: ClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]
Client for the MolmoAct2 DROID inference server (absolute joint control).
Source code in eden/extensions/policies/client.py
Attributes¶
Methods:¶
pack_observations
¶
Source code in eden/extensions/policies/molmoact2/policy.py
accept_chunk
¶
Refuse a chunk with any non-finite arm target before its first row executes, then queue it.
Every row of the reply is scanned, including any past the 15 that execute: a non-finite value anywhere means the server's output is broken, so the rows before it are not trusted either.
Source code in eden/extensions/policies/molmoact2/policy.py
unpack_actions
¶
Decode an (num_envs, 8) MolmoAct2-DROID action row.
raw_action[:, :7] are absolute Franka joint targets in rad and are
passed straight through to the arm ImplicitPDController (scale=1).
raw_action[:, 7] is gripper closedness in [0, 1] (0=open,
1=closed), forwarded raw to the env's GripperDecisionController, which
binarizes it at gripper_threshold (or follows it with binarize_gripper=False).
Stores self.last_diagnostics with the raw gripper scalar, threshold, and the intent
the env's gripper term derives from it, so callers can record/plot gripper traces.
Source code in eden/extensions/policies/molmoact2/policy.py
action_options
classmethod
¶
action_options(arm_entity_name: str = 'robot', arm_dofs_name: list[str] | None = None, gripper_entity_name: str | None = None, gripper_dof_name: str = 'left_driver_joint', gripper_close_pos: float = 0.8, gripper_threshold: float = 0.5, binarize_gripper: bool = True, flip_gripper: bool = False) -> dict[str, ActionTermOptions]
Absolute joint arm controller + GripperDecisionController fed the server's closedness.
flip_gripper treats the scalar as openness instead; binarize_gripper=False
drives the fingers continuously by the (clipped) closedness. Keep these equal to the
policy's attributes of the same name (checked at policy construction).
Source code in eden/extensions/policies/molmoact2/policy.py
observation_options
classmethod
¶
observation_options(external_camera: str = 'camera', wrist_camera: str = 'wrist_camera', arm_entity_name: str = 'robot', arm_dofs_name: list[str] | None = None, gripper_entity_name: str | None = None, gripper_dof_name: str = 'left_driver_joint', gripper_close_pos: float = 0.8) -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/molmoact2/policy.py
OctoPolicy
¶
OctoPolicy(env: EnvBase, options: PolicyOptions)
Bases: ClientMixin, PolicyBase, ConfigurableMixin[RemotePolicyOptions]
Octo: A transformer-based robot manipulation policy.
Communicates with a remote Octo policy server (RobotArena/src/server/server_octo.py):
POST /set_task?task_description=
Action layout sent to Eden: [world_vector (3), rotation_delta (3), gripper (1)] - [:6] -> ee_controller (DifferentialIKController; the Euler delta becomes a rotation vector) - [6] -> gripper_controller (from action["gripper"], server-binarized +1=open/-1=close, forwarded raw)
Gripper convention: server +1=open / -1=close, forwarded raw; the env's GripperDecisionController (input="open_sign") turns the sign into open/close.
Source code in eden/extensions/policies/client.py
Attributes¶
Methods:¶
decode_response
¶
decode_response(response: dict) -> ndarray
Flatten Octo's two-dict reply into one (1, 1, 7) action row.
Uses raw_action["world_vector"] + raw_action["rotation_delta"] as the 6-D EE delta. RobotArena's Genesis default_test.py also uses the raw Euler rotation_delta for its target-pose transform, while action["gripper"] carries the processed WidowX open/close convention. The processed action["world_vector"] / action["rot_axangle"] are not a substitute: the server scales both by its action_scale, and rot_axangle is axis-angle.
Source code in eden/extensions/policies/octo/policy.py
action_options
classmethod
¶
action_options(entity_name: str = 'robot') -> dict[str, ActionTermOptions]
Source code in eden/extensions/policies/octo/policy.py
observation_options
classmethod
¶
observation_options(camera_name: str = 'camera', **kwargs) -> dict[str, ObservationGroupOptions]
Source code in eden/extensions/policies/octo/policy.py
pack_observations
¶
Convert Eden observations to the Octo server payload.
Source code in eden/extensions/policies/octo/policy.py
unpack_actions
¶
Convert a (1, 7) (or (7,)) action [world_vector (3), rotation_delta (3), gripper (1)].
Returns tensors with a leading batch axis of 1. OctoPolicy keeps
ClientMixin.supports_batch at the default False because the
upstream Octo HTTP server accepts a single image per /act request.
The Euler rotation_delta reaches the IK controller as the rotation vector of the
same rotation (:class:EEDeltaAdapter). The gripper sign (+1 open / -1 close) is
forwarded raw; the env's GripperDecisionController(input="open_sign") interprets it.