| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| """Synchronous inference engine: inline policy call per control tick.""" |
|
|
| from __future__ import annotations |
|
|
| import logging |
| from contextlib import nullcontext |
| from copy import copy |
|
|
| import torch |
|
|
| from lerobot.policies.pretrained import PreTrainedPolicy |
| from lerobot.policies.utils import make_robot_action, prepare_observation_for_inference |
| from lerobot.processor import PolicyProcessorPipeline |
|
|
| from .base import InferenceEngine |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| class SyncInferenceEngine(InferenceEngine): |
| """Inline synchronous inference: compute one action per call. |
| |
| ``get_action`` runs the full policy pipeline (pre/post-processor + |
| ``select_action``) on the given observation frame and returns a |
| CPU action tensor reordered to match the dataset action keys. |
| """ |
|
|
| def __init__( |
| self, |
| policy: PreTrainedPolicy, |
| preprocessor: PolicyProcessorPipeline, |
| postprocessor: PolicyProcessorPipeline, |
| dataset_features: dict, |
| ordered_action_keys: list[str], |
| task: str, |
| device: str | None, |
| robot_type: str, |
| ) -> None: |
| self._policy = policy |
| self._preprocessor = preprocessor |
| self._postprocessor = postprocessor |
| self._dataset_features = dataset_features |
| self._ordered_action_keys = ordered_action_keys |
| self._task = task |
| self._device = torch.device(device or "cpu") |
| self._robot_type = robot_type |
| logger.info( |
| "SyncInferenceEngine initialized (device=%s, action_keys=%d)", |
| self._device, |
| len(ordered_action_keys), |
| ) |
|
|
| def start(self) -> None: |
| """No background resources to start.""" |
| logger.info("SyncInferenceEngine started (inline mode — no background thread)") |
|
|
| def stop(self) -> None: |
| """No background resources to stop.""" |
| logger.info("SyncInferenceEngine stopped") |
|
|
| def reset(self) -> None: |
| """Reset the policy and pre/post-processors.""" |
| logger.info("Resetting sync inference state (policy + processors)") |
| self._policy.reset() |
| self._preprocessor.reset() |
| self._postprocessor.reset() |
|
|
| def get_action(self, obs_frame: dict | None) -> torch.Tensor | None: |
| """Run the full inference pipeline on ``obs_frame`` and return an action tensor.""" |
| if obs_frame is None: |
| return None |
| |
| |
| |
| observation = copy(obs_frame) |
| autocast_ctx = ( |
| torch.autocast(device_type=self._device.type) |
| if self._device.type == "cuda" and self._policy.config.use_amp |
| else nullcontext() |
| ) |
| with torch.inference_mode(), autocast_ctx: |
| observation = prepare_observation_for_inference( |
| observation, self._device, self._task, self._robot_type |
| ) |
| observation = self._preprocessor(observation) |
| action = self._policy.select_action(observation) |
| action = self._postprocessor(action) |
| action_tensor = action.squeeze(0).cpu() |
|
|
| |
| |
| action_dict = make_robot_action(action_tensor, self._dataset_features) |
| return torch.tensor([action_dict[k] for k in self._ordered_action_keys]) |
|
|