DPPO policies¶
Use diffusion-style server policies with the DPPO algorithm.
This page covers:
- Built-in
dppo-policy - Built-in
pi0-policy(OpenPI PI0) - Implementing a diffusion-style policy with
BasePolicyGradientDiffusionPolicy
Quickstart¶
DPPO policy.
OpenPI PI0 policy (requires a checkpoint directory). It is a flow policy: it
pairs with fpo or eval, never with dppo. These are the two invocations
E11 ran.
# evaluation - no learning
plugrl-run-server pi0-policy default eval default \
--policy.name pi05_libero \
--policy.checkpoint-path /path/to/pi0_checkpoint \
--policy.device cuda
# FPO fine-tuning
plugrl-run-server pi0-policy default fpo default \
--policy.name pi05_libero \
--policy.checkpoint-path /path/to/pi0_checkpoint \
--policy.device cuda \
--algo.learning-rate 1e-5 --algo.batch-size 8 \
--algo.n-samples-per-action 4 --algo.buffer-size 4096 \
--algo.global-steps 40960
Verify¶
List registered policies and variants.
Smoke-test a plug-in policy UID.
python -c "import my_pkg.plugrl_policies; from plugrl_server.cli import main; main()" \
my-dppo-policy default dummy default
Built-in: dppo-policy¶
- UID:
dppo-policy - Code:
plugrl-server/src/plugrl_server/policy/dppo/dppo_policy.py - Dependency:
dppomust be installed in the server environment.- uv: in
plugrl-server, runuv sync --extra dppo.
- uv: in
- Loads:
plugrl_server/meta/dppo/cfg/<env_type>/<env_name>.yamlplugrl_server/meta/dppo/asset/<env_type>/<env_name>/normalization.npz- Expects worker obs to contain
statesand concatenateslow_dim_keys.
Common flags.
--policy.env-type gym--policy.env-name hopper-medium-v2--policy.checkpoint-path /path/to/checkpoint.pt--policy.critic.*
Built-in: pi0-policy (OpenPI)¶
- UID:
pi0-policy - Code:
plugrl-server/src/plugrl_server/policy/openpi/openpi_policy.py --policy.checkpoint-pathis required and must point to a directory with:model.safetensorsassets/with normalization stats- Setup notes:
plugrl-server/src/plugrl_server/policy/openpi/README.md.
Common flags.
--policy.name pi05_libero--policy.denoising-steps 5--policy.train-expert-only true--policy.default-prompt "..."
Implement a diffusion-style policy¶
Subclass BasePolicyGradientDiffusionPolicy in
plugrl-server/src/plugrl_server/policy/base_policy_gradient_diffusion_policy.py.
The base class drives the denoising loop and fills a DiffusionRuntimeState:
- Per-step:
action,logprob,entropy, plusobs["x"]andobs["t"]. - Final: calls
_postprocess_actionand storesvalueintoruntime_state.value.
What you implement.
_get_timesteps,_initialize_x,_denoising_step,_iterative_process_action,_postprocess_actionfake_diffusion_condfor buffer preallocation_get_value. It is not abstract, but the base assigns its result intoruntime_state.value, so a subclass that leaves it out fails mid-rollout withTypeError: can't assign a NoneType to a torch.FloatTensor.- Optional
build_obs_cacheto cache expensive conditioning. The base calls it once per inference and passes the result to_denoising_stepas the keyword-onlycond_cache=and to_get_valueasobs_cache=.
Key shapes (from fake_runtime_state).
action/logprob/entropy/obs["x"]:(B, S, H, D)obs["t"]:(B, S)value:(B,)
Minimal template.
import dataclasses
from typing import Any
import numpy as np
import torch
from plugrl_server.policy.base_policy_gradient_diffusion_policy import (
BasePolicyGradientDiffusionPolicy,
BasePolicyGradientDiffusionPolicyConfig,
TorchTree,
)
from plugrl_server.policy.registration import register_policy, register_policy_config
UID = "my-dppo-policy"
@register_policy_config(UID)
@dataclasses.dataclass
class MyDPPOPolicyConfig(BasePolicyGradientDiffusionPolicyConfig):
checkpoint_path: str | None = None
@register_policy(UID)
class MyDPPOPolicy(BasePolicyGradientDiffusionPolicy):
def __init__(self, config: MyDPPOPolicyConfig):
super().__init__(config)
...
def prepare_observation(self, _obs: dict) -> dict[str, np.ndarray]:
...
def _get_timesteps(self) -> torch.Tensor:
...
def _initialize_x(self, batch_size: int) -> torch.Tensor:
...
def _denoising_step(
self,
x: torch.Tensor,
t: torch.Tensor,
cond: TorchTree,
x_next: torch.Tensor | None = None,
*,
cond_cache: Any = None,
sampling_noise_level: float | None = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
...
def _iterative_process_action(self, action: torch.Tensor) -> torch.Tensor:
return action
def _postprocess_action(self, action: torch.Tensor, obs: TorchTree) -> Any:
...
def _get_value(self, obs: TorchTree, obs_cache: Any = None) -> torch.Tensor:
...
def fake_diffusion_cond(self, batch_size: int) -> TorchTree:
...
Example: LeRobot diffusion adapter¶
See plugrl-server/examples/lerobot/lerobot_diffusion.py (UID = "lerobot-diffusion-policy").
Patterns to copy.
- Pack multi-step observations into one batched nested
dictof arrays - that is whatTorchTreeis; the base converts it to tensors for you. - Cache encoder outputs in
build_obs_cache. - Support expanded batch
B * num_denoising_stepswithrepeat_interleave. - Deterministic sampling can return zero
logproblikePi0Policy. - Stochastic sampling should compute
logprob/entropylikeDPPOPolicy.
Troubleshooting¶
dppo-policyimport fails: installdppoin the environment that runsplugrl-run-server.pi0-policyimport/setup fails: follow the OpenPI setup in the server repo.- Shape mismatch in training: keep action shapes stable and align the runtime state with your buffers.
- Policy UID not listed: your registration module was not imported.