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
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.")