diff --git a/.circleci/config.yml b/.circleci/config.yml index b4c20110e27cdcab5d031f3dd7e1c843f02219e8..47ab27cfd1ed404c146e3cfdfe8120e5d6e06349 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -186,6 +186,7 @@ jobs: - ~/miniconda - run: name: Run api tests + no_output_timeout: 20m command: | export PATH=$HOME/miniconda/bin:/usr/local/cuda/bin:$PATH . activate habitat; cd habitat-api diff --git a/habitat/core/simulator.py b/habitat/core/simulator.py index bee5c21164305aba2ed5850b0c57f5693d2b3765..e074c295a8efebb5df51561478e6eb754188f6a1 100644 --- a/habitat/core/simulator.py +++ b/habitat/core/simulator.py @@ -6,7 +6,7 @@ from collections import OrderedDict from enum import Enum -from typing import Any, Dict, Iterable, List, Optional +from typing import Any, Dict, Iterable, List, Optional, Union import attr from gym import Space @@ -265,12 +265,15 @@ class Simulator: raise NotImplementedError def geodesic_distance( - self, position_a: List[float], position_b: List[float] + self, + position_a: List[float], + position_b: Union[List[float], List[List[float]]], ) -> float: r"""Calculates geodesic distance between two points. :param position_a: coordinates of first point. - :param position_b: coordinates of second point. + :param position_b: coordinates of second point or list of goal points + coordinates. :return: the geodesic distance in the cartesian space between points :p:`position_a` and :p:`position_b`, if no path is found between diff --git a/habitat/datasets/pointnav/pointnav_generator.py b/habitat/datasets/pointnav/pointnav_generator.py index 1b276104b6bce96b0d12d08ef329c341652759e1..1ad36698c08a45a9904581fc8398450b5522ef22 100644 --- a/habitat/datasets/pointnav/pointnav_generator.py +++ b/habitat/datasets/pointnav/pointnav_generator.py @@ -40,7 +40,7 @@ def is_compatible_episode( if np.abs(s[1] - t[1]) > 0.5: # check height difference to assure s and # t are from same floor return False, 0 - d_separation = sim.geodesic_distance(s, t) + d_separation = sim.geodesic_distance(s, [t]) if d_separation == np.inf: return False, 0 if not near_dist <= d_separation <= far_dist: diff --git a/habitat/sims/habitat_simulator/habitat_simulator.py b/habitat/sims/habitat_simulator/habitat_simulator.py index 3dff4ffdc8c34b963572f7e2ca52a816f5f54ae5..fa3edb6dba57a23b50d23e9f18cc5f3982897e34 100644 --- a/habitat/sims/habitat_simulator/habitat_simulator.py +++ b/habitat/sims/habitat_simulator/habitat_simulator.py @@ -4,7 +4,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -from typing import Any, List, Optional +from typing import Any, List, Optional, Union import numpy as np from gym import spaces @@ -281,10 +281,27 @@ class HabitatSim(Simulator): self._update_agents_state() def geodesic_distance(self, position_a, position_b): - path = habitat_sim.ShortestPath() + path = habitat_sim.MultiGoalShortestPath() path.requested_start = np.array(position_a, dtype=np.float32) - path.requested_end = np.array(position_b, dtype=np.float32) + if isinstance(position_b[0], List) or isinstance( + position_b[0], np.ndarray + ): + path.requested_ends = np.array(position_b, dtype=np.float32) + # TODO(erikwijmans) Remove next line as soon as multi goal shortest + # path passes the tests. + return np.min( + [ + self.geodesic_distance(position_a, position) + for position in position_b + ] + ) + else: + path.requested_ends = np.array( + [np.array(position_b, dtype=np.float32)] + ) + self._sim.pathfinder.find_path(path) + return path.geodesic_distance def action_space_shortest_path( diff --git a/habitat/tasks/nav/shortest_path_follower.py b/habitat/tasks/nav/shortest_path_follower.py index c678a54211ffd90152fef43618a5b3b21db933be..ada66edc3f1e7e8190a23ab01a7f945d69ee3054 100644 --- a/habitat/tasks/nav/shortest_path_follower.py +++ b/habitat/tasks/nav/shortest_path_follower.py @@ -72,7 +72,7 @@ class ShortestPathFollower: """ if ( self._sim.geodesic_distance( - self._sim.get_agent_state().position, goal_pos + self._sim.get_agent_state().position, [goal_pos] ) <= self._goal_radius ): @@ -113,7 +113,7 @@ class ShortestPathFollower: def _geo_dist(self, goal_pos: np.array) -> float: return self._sim.geodesic_distance( - self._sim.get_agent_state().position, goal_pos + self._sim.get_agent_state().position, [goal_pos] ) def _est_max_grad_dir(self, goal_pos: np.array) -> np.array: diff --git a/habitat_baselines/common/environments.py b/habitat_baselines/common/environments.py index b7ffd48daeb983a1297ef38719366bff51e264c0..b7d43dd888a8f60fe0084b40058460f3d199dd66 100644 --- a/habitat_baselines/common/environments.py +++ b/habitat_baselines/common/environments.py @@ -75,9 +75,9 @@ class NavRLEnv(habitat.RLEnv): def _distance_target(self): current_position = self._env.sim.get_agent_state().position.tolist() - target_position = self._env.current_episode.goals[0].position distance = self._env.sim.geodesic_distance( - current_position, target_position + current_position, + [goal.position for goal in self._env.current_episode.goals], ) return distance diff --git a/test/test_habitat_env.py b/test/test_habitat_env.py index 698734679ff137a2f15de54e9a9a4ca1ad4622b8..9890b9c91ce8972bc45627e3b8f289042be61cc1 100644 --- a/test/test_habitat_env.py +++ b/test/test_habitat_env.py @@ -390,7 +390,7 @@ def test_action_space_shortest_path(): angles = [x for x in range(-180, 180, config.SIMULATOR.TURN_ANGLE)] angle = np.radians(np.random.choice(angles)) rotation = [0, np.sin(angle / 2), 0, np.cos(angle / 2)] - if env.sim.geodesic_distance(source_position, position) != np.inf: + if env.sim.geodesic_distance(source_position, [position]) != np.inf: reachable_targets.append(AgentState(position, rotation)) while len(unreachable_targets) < 3: @@ -400,7 +400,7 @@ def test_action_space_shortest_path(): angles = [x for x in range(-180, 180, config.SIMULATOR.TURN_ANGLE)] angle = np.radians(np.random.choice(angles)) rotation = [0, np.sin(angle / 2), 0, np.cos(angle / 2)] - if env.sim.geodesic_distance(source_position, position) == np.inf: + if env.sim.geodesic_distance(source_position, [position]) == np.inf: unreachable_targets.append(AgentState(position, rotation)) targets = reachable_targets diff --git a/test/test_habitat_sim.py b/test/test_habitat_sim.py index 5fea24ca9168cdc692943d85a7cbd7b97dccfc8a..3875503a69efa962c8be0aa132f3d18c4a7f7769 100644 --- a/test/test_habitat_sim.py +++ b/test/test_habitat_sim.py @@ -82,3 +82,27 @@ def test_sim_no_sensors(): sim = make_sim(config.SIMULATOR.TYPE, config=config.SIMULATOR) sim.reset() sim.close() + + +def test_sim_geodesic_distance(): + config = get_config() + if not os.path.exists(config.SIMULATOR.SCENE): + pytest.skip("Please download Habitat test data to data folder.") + sim = make_sim(config.SIMULATOR.TYPE, config=config.SIMULATOR) + sim.seed(0) + sim.reset() + start_point = sim.sample_navigable_point() + navigable_points = [sim.sample_navigable_point() for _ in range(10)] + assert np.isclose( + sim.geodesic_distance(start_point, navigable_points[0]), 1.3849650 + ), "Geodesic distance or sample navigable points mechanism has been changed." + assert np.isclose( + sim.geodesic_distance(start_point, navigable_points), 0.6194838 + ), "Geodesic distance or sample navigable points mechanism has been changed." + assert sim.geodesic_distance(start_point, navigable_points) == np.min( + [ + sim.geodesic_distance(start_point, position) + for position in navigable_points + ] + ), "Geodesic distance for multi target setup isn't equal to separate single target calls." + sim.close() diff --git a/test/test_mp3d_eqa.py b/test/test_mp3d_eqa.py index 41569dada2c951041834236b9922587c358bcaa9..59d2cffbbe883b17a18ba13857657d8bdf2a1c30 100644 --- a/test/test_mp3d_eqa.py +++ b/test/test_mp3d_eqa.py @@ -16,11 +16,12 @@ from habitat.core.embodied_task import Episode from habitat.core.logging import logger from habitat.datasets import make_dataset from habitat.tasks.eqa.eqa import AnswerAction -from habitat.tasks.nav.nav import MoveForwardAction +from habitat.tasks.nav.nav import MoveForwardAction, StopAction from habitat.utils.test_utils import sample_non_stop_action CFG_TEST = "configs/test/habitat_mp3d_eqa_test.yaml" CLOSE_STEP_THRESHOLD = 0.028 +OLD_STOP_ACTION_ID = 3 # List of episodes each from unique house @@ -207,7 +208,8 @@ def test_mp3d_eqa_sim_correspondence(): atol=CLOSE_STEP_THRESHOLD * (step_id + 1), ), "Agent's path diverges from the shortest path." - obs = env.step(action=point.action) + if point.action != OLD_STOP_ACTION_ID: + obs = env.step(action=point.action) if not env.episode_over: rgb_mean += obs["rgb"][:, :, :3].mean()