From ec6c1df14ea8d83f0e81eeb52823bfc7b33c4b6e Mon Sep 17 00:00:00 2001 From: Oleksandr Maksymets <maksymets@gmail.com> Date: Tue, 28 Jan 2020 16:50:19 -0800 Subject: [PATCH] Added geodesic distance for multiple goals --- .circleci/config.yml | 1 + habitat/core/simulator.py | 9 ++++--- .../datasets/pointnav/pointnav_generator.py | 2 +- .../habitat_simulator/habitat_simulator.py | 23 +++++++++++++++--- habitat/tasks/nav/shortest_path_follower.py | 4 ++-- habitat_baselines/common/environments.py | 4 ++-- test/test_habitat_env.py | 4 ++-- test/test_habitat_sim.py | 24 +++++++++++++++++++ test/test_mp3d_eqa.py | 6 +++-- 9 files changed, 62 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b4c20110e..47ab27cfd 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 bee5c2116..e074c295a 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 1b276104b..1ad36698c 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 3dff4ffdc..fa3edb6db 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 c678a5421..ada66edc3 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 b7ffd48da..b7d43dd88 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 698734679..9890b9c91 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 5fea24ca9..3875503a6 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 41569dada..59d2cffbb 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() -- GitLab