Source code for grutopia.core.robot.controller
from abc import ABC, abstractmethod
from functools import wraps
from typing import Any, Dict, List, Union
import numpy as np
from omni.isaac.core.articulations import ArticulationSubset
from omni.isaac.core.controllers import BaseController as Base
from omni.isaac.core.scenes import Scene
from omni.isaac.core.utils.types import ArticulationAction
from grutopia.core.config.robot import ControllerCfg, RobotCfg
from grutopia.core.robot.robot import BaseRobot
from grutopia.core.util import log
[docs]class BaseController(Base, ABC):
"""Base class of controller."""
controllers = {}
def __init__(self, config: ControllerCfg, robot: BaseRobot, scene: Scene):
"""Initialize the controller.
Args:
config (ControllerCfg): controller configuration.
robot (BaseRobot): robot owning the controller.
scene (Scene): scene from isaac sim.
"""
self.sub_controllers = None
self.scene = scene
if config.name is None:
raise ValueError('must specify controller name.')
super().__init__(config.name)
self._obs = {}
self._robot = robot
self.config = config
self.sub_controllers: List[BaseController]
[docs] @abstractmethod
def action_to_control(self, action: Union[np.ndarray, List]) -> ArticulationAction:
"""Convert input action (in 1d array format) to joint signals to apply.
Args:
action (Union[np.ndarray, List]): input control action.
Returns:
ArticulationAction: joint signals to apply
"""
raise NotImplementedError()
[docs] def get_obs(self) -> Dict[str, Any]:
"""Get observation of controller.
Returns:
Dict[str, Any]: observation key and value.
"""
return {}
[docs] @classmethod
def register(cls, name: str):
"""Register a controller with its name(decorator).
Args:
name (str): name of the controller
"""
def decorator(controller_class):
cls.controllers[name] = controller_class
@wraps(controller_class)
def wrapped_function(*args, **kwargs):
return controller_class(*args, **kwargs)
return wrapped_function
return decorator
@property
def robot(self):
return self._robot
@robot.setter
def robot(self, value):
self._robot = value
[docs] def cleanup(self):
"""
Operations that need to be cleaned up before switching scenes (or resetting)
"""
pass
[docs] def get_joint_subset(self) -> ArticulationSubset | None:
"""Get the joint subset controlled by the controller.
Returns:
ArticulationSubset: joint subset.
"""
if hasattr(self, 'joint_subset'):
return self.joint_subset
if not hasattr(self, 'sub_controllers'):
return None
if self.sub_controllers is None or len(self.sub_controllers) == 0:
return None
return self.sub_controllers[0].get_joint_subset()
[docs]def create_controllers(robot_cfg: RobotCfg, robot: BaseRobot, scene: Scene) -> Dict[str, BaseController]:
"""Create all controllers of one robot.
Args:
robot_cfg (RobotCfg): config of the robot.
robot (BaseRobot): robot instance.
scene (Scene): scene from isaac sim.
Returns:
Dict[str, BaseController]: dict of controllers with controller name as key.
"""
controller_map = {}
if robot_cfg.controllers is None:
return controller_map
for controller_cfg in robot_cfg.controllers:
controller_name = controller_cfg.name
controller_cls = BaseController.controllers[controller_cfg.type]
controller_ins: BaseController = controller_cls(config=controller_cfg, robot=robot, scene=scene)
if controller_cfg.sub_controllers is not None:
inject_sub_controllers(
parent=controller_ins,
configs=controller_cfg.sub_controllers,
robot=robot,
scene=scene,
)
controller_map[controller_name] = controller_ins
log.debug(f'==================== {controller_name} loaded==========================')
return controller_map
[docs]def inject_sub_controllers(
parent: BaseController,
configs: List[ControllerCfg],
robot: BaseRobot,
scene: Scene,
):
"""Recursively create and inject sub-controllers into parent controller.
Args:
parent (BaseController): parent controller instance.
configs (List[ControllerParams]): user configs of sub-controllers.
robot (BaseRobot): robot instance.
scene (Scene): scene from isaac sim.
"""
if len(configs) == 0:
return
sub_controllers: List[BaseController] = []
for config in configs:
controller_cls = BaseController.controllers[config.type]
controller_ins = controller_cls(config=config, robot=robot, scene=scene)
if config.sub_controllers is not None:
inject_sub_controllers(controller_ins, configs=config.sub_controllers, robot=robot, scene=scene)
sub_controllers.append(controller_ins)
parent.sub_controllers = sub_controllers