Example #1
0
    def __init__(self, config: Config) -> None:
        self.config = config
        agent_config = self._get_agent_config()

        sim_sensors = []
        for sensor_name in agent_config.SENSORS:
            sensor_cfg = getattr(self.config, sensor_name)
            is_valid_sensor = hasattr(
                habitat.sims.habitat_simulator,
                sensor_cfg.TYPE  # type: ignore
            )
            assert is_valid_sensor, "invalid sensor type {}".format(
                sensor_cfg.TYPE)
            sim_sensors.append(
                getattr(
                    habitat.sims.habitat_simulator,  # type: ignore
                    sensor_cfg.TYPE,
                )(sensor_cfg))

        self._sensor_suite = SensorSuite(sim_sensors)
        self.sim_config = self.create_sim_config(self._sensor_suite)
        self._current_scene = self.sim_config.scene.id
        self._sim = habitat_sim.Simulator(self.sim_config)
        self._action_space = spaces.Discrete(
            len(self.sim_config.agents[0].action_space))

        self._is_episode_active = False
        self._controls = SIM_ACTION_TO_NAME
Example #2
0
class HabitatSim(habitat.Simulator):
    """Simulator wrapper over habitat-sim

    habitat-sim repo: https://github.com/facebookresearch/habitat-sim

    Args:
        config: configuration for initializing the simulator.
    """
    def __init__(self, config: Config) -> None:
        self.config = config
        agent_config = self._get_agent_config()

        sim_sensors = []
        for sensor_name in agent_config.SENSORS:
            sensor_cfg = getattr(self.config, sensor_name)
            is_valid_sensor = hasattr(
                habitat.sims.habitat_simulator,
                sensor_cfg.TYPE  # type: ignore
            )
            assert is_valid_sensor, "invalid sensor type {}".format(
                sensor_cfg.TYPE)
            sim_sensors.append(
                getattr(
                    habitat.sims.habitat_simulator,  # type: ignore
                    sensor_cfg.TYPE,
                )(sensor_cfg))

        self._sensor_suite = SensorSuite(sim_sensors)
        self.sim_config = self.create_sim_config(self._sensor_suite)
        self._current_scene = self.sim_config.scene.id
        self._sim = habitat_sim.Simulator(self.sim_config)
        self._action_space = spaces.Discrete(
            len(self.sim_config.agents[0].action_space))

        self._is_episode_active = False
        self._controls = SIM_ACTION_TO_NAME

    def create_sim_config(
            self,
            _sensor_suite: SensorSuite) -> habitat_sim.SimulatorConfiguration:
        sim_config = habitat_sim.SimulatorConfiguration()
        sim_config.scene.id = self.config.SCENE
        sim_config.gpu_device_id = self.config.HABITAT_SIM_V0.GPU_DEVICE_ID
        agent_config = habitat_sim.AgentConfiguration()
        overwrite_config(config_from=self._get_agent_config(),
                         config_to=agent_config)

        sensor_specifications = []
        for sensor in _sensor_suite.sensors.values():
            sim_sensor_cfg = habitat_sim.SensorSpec()
            sim_sensor_cfg.uuid = sensor.uuid
            sim_sensor_cfg.resolution = list(
                sensor.observation_space.shape[:2])
            sim_sensor_cfg.parameters["hfov"] = str(sensor.config.HFOV)
            sim_sensor_cfg.position = sensor.config.POSITION
            # TODO(maksymets): Add configure method to Sensor API to avoid
            # accessing child attributes through parent interface
            sim_sensor_cfg.sensor_type = sensor.sim_sensor_type  # type: ignore
            sensor_specifications.append(sim_sensor_cfg)

        agent_config.sensor_specifications = sensor_specifications
        agent_config.action_space = {
            SimulatorActions.LEFT.value:
            habitat_sim.ActionSpec("lookLeft",
                                   {"amount": self.config.TURN_ANGLE}),
            SimulatorActions.RIGHT.value:
            habitat_sim.ActionSpec("lookRight",
                                   {"amount": self.config.TURN_ANGLE}),
            SimulatorActions.FORWARD.value:
            habitat_sim.ActionSpec("moveForward",
                                   {"amount": self.config.FORWARD_STEP_SIZE}),
            SimulatorActions.STOP.value:
            habitat_sim.ActionSpec("stop", {}),
        }
        sim_config.agents = [agent_config]
        return sim_config

    @property
    def sensor_suite(self) -> SensorSuite:
        return self._sensor_suite

    @property
    def action_space(self) -> Space:
        return self._action_space

    @property
    def is_episode_active(self) -> bool:
        return self._is_episode_active

    def _update_agents_state(self) -> bool:
        is_updated = False
        for agent_id, _ in enumerate(self.config.AGENTS):
            agent_cfg = self._get_agent_config(agent_id)
            if agent_cfg.IS_SET_START_STATE:
                self.set_agent_state(
                    agent_cfg.START_POSITION,
                    agent_cfg.START_ROTATION,
                    agent_id,
                )
                is_updated = True
        return is_updated

    def reset(self):
        sim_obs = self._sim.reset()
        if self._update_agents_state():
            sim_obs = self._sim.get_sensor_observations()

        self._is_episode_active = True
        return self._sensor_suite.get_observations(sim_obs)

    def step(self, action):
        assert self._is_episode_active, (
            "episode is not active, environment not RESET or "
            "STOP action called previously")
        sim_action = self._controls[action]
        if sim_action == SimulatorActions.STOP.value:
            self._is_episode_active = False
            sim_obs = self._sim.get_sensor_observations()
        else:
            sim_obs = self._sim.step(sim_action)

        observations = self._sensor_suite.get_observations(sim_obs)
        return observations

    def render(self, mode: str = "rgb") -> Any:
        """
        Args:
            mode: sensor whose observation is used for returning the frame,
                eg: "rgb", "depth", "semantic"

        Returns:
            rendered frame according to the mode
        """
        sim_obs = self._sim.get_sensor_observations()
        observations = self._sensor_suite.get_observations(sim_obs)

        output = observations.get(mode)
        assert output is not None, "mode {} sensor is not active".format(mode)

        return output

    def seed(self, seed):
        self._sim.seed(seed)

    def reconfigure(self, config: Config) -> None:
        # TODO(maksymets): Switch to Habitat-Sim more efficient caching
        is_same_scene = config.SCENE == self._current_scene
        self.config = config
        self.sim_config = self.create_sim_config(self._sensor_suite)
        if not is_same_scene:
            self._current_scene = config.SCENE
            del self._sim
            self._sim = habitat_sim.Simulator(self.sim_config)

        self._update_agents_state()

    def geodesic_distance(self, position_a, position_b):
        path = habitat_sim.ShortestPath()
        path.requested_start = np.array(position_a, dtype=np.float32)
        path.requested_end = np.array(position_b, dtype=np.float32)
        self._sim.pathfinder.find_path(path)
        return path.geodesic_distance

    def action_space_shortest_path(
            self,
            source: AgentState,
            targets: List[AgentState],
            agent_id: int = 0) -> List[ShortestPathPoint]:
        """
        Returns:
            List of agent states and actions along the shortest path from
            source to the nearest target (both included). If one of the
            target(s) is identical to the source, a list containing only
            one node with the identical agent state is returned. Returns
            an empty list in case none of the targets are reachable from
            the source. For the last item in the returned list the action
            will be None.
        """
        assert agent_id == 0, "No support of multi agent in {} yet.".format(
            self.__class__.__name__)
        action_pathfinder = self._sim.make_action_pathfinder(agent_id=agent_id)
        action_shortest_path = habitat_sim.MultiGoalActionSpaceShortestPath()
        action_shortest_path.requested_start.position = source.position
        action_shortest_path.requested_start.rotation = source.rotation

        for target in targets:
            action_shortest_path.requested_ends.append(
                habitat_sim.ActionSpacePathLocation(target.position,
                                                    target.rotation))

        if not action_pathfinder.find_path(action_shortest_path):
            return []

        # add None action to last node in path
        actions: List[Optional[int]] = [
            SIM_NAME_TO_ACTION[action]
            for action in action_shortest_path.actions
        ]
        actions.append(None)

        shortest_path = [
            ShortestPathPoint(position, rotation, action)
            for position, rotation, action in zip(
                action_shortest_path.points,
                action_shortest_path.rotations,
                actions,
            )
        ]

        return shortest_path

    def sample_navigable_point(self):
        return self._sim.pathfinder.get_random_navigable_point().tolist()

    def semantic_annotations(self):
        """
        Returns:
            SemanticScene which is a three level hierarchy of semantic
            annotations for the current scene. Specifically this method
            returns a SemanticScene which contains a list of SemanticLevel's
            where each SemanticLevel contains a list of SemanticRegion's where
            each SemanticRegion contains a list of SemanticObject's.

            SemanticScene has attributes: aabb(axis-aligned bounding box) which
            has attributes aabb.center and aabb.sizes which are 3d vectors,
            categories, levels, objects, regions.

            SemanticLevel has attributes: id, aabb, objects and regions.

            SemanticRegion has attributes: id, level, aabb, category (to get
            name of category use category.name()) and objects.

            SemanticObject has attributes: id, region, aabb, obb (oriented
            bounding box) and category.

            SemanticScene contains List[SemanticLevels]
            SemanticLevel contains List[SemanticRegion]
            SemanticRegion contains List[SemanticObject]

            Example to loop through in a hierarchical fashion:
            for level in semantic_scene.levels:
                for region in level.regions:
                    for obj in region.objects:
        """
        return self._sim.semantic_scene

    def close(self):
        self._sim.close()

    @property
    def index_stop_action(self):
        return SIM_NAME_TO_ACTION[SimulatorActions.STOP.value]

    def _get_agent_config(self, agent_id: Optional[int] = None) -> Any:
        if agent_id is None:
            agent_id = self.config.DEFAULT_AGENT_ID
        agent_name = self.config.AGENTS[agent_id]
        agent_config = getattr(self.config, agent_name)
        return agent_config

    def get_agent_state(self, agent_id: int = 0):
        assert agent_id == 0, "No support of multi agent in {} yet.".format(
            self.__class__.__name__)
        state = habitat_sim.AgentState()
        self._sim.get_agent(agent_id).get_state(state)
        return state

    def set_agent_state(
        self,
        position: List[float] = None,
        rotation: List[float] = None,
        agent_id: int = 0,
    ) -> None:
        """Sets agent state similar to initialize_agent, but without agents
        creation.

        Args:
            position: numpy ndarray containing 3 entries for (x, y, z).
            rotation: numpy ndarray with 4 entries for (x, y, z, w) elements
            of unit quaternion (versor) representing agent 3D orientation,
            (https://en.wikipedia.org/wiki/Versor)
            agent_id: int identification of agent from multiagent setup.
        """
        agent = self._sim.get_agent(agent_id)
        state = self.get_agent_state(agent_id)
        state.position = position
        state.rotation = rotation
        agent.set_state(state)

        self._check_agent_position(position, agent_id)

    # TODO (maksymets): Remove check after simulator became stable
    def _check_agent_position(self, position, agent_id=0):
        if not np.allclose(position, self.get_agent_state(agent_id).position):
            logger.info("Agent state diverges from configured start position.")