Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add benchmark metrics to simulator #823

Open
wants to merge 38 commits into
base: og-develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
457a8be
add metrics to simulator
steven200796 Jul 5, 2023
1910c1b
build-push action for docker
steven200796 Jul 19, 2023
374f842
merged with og-develop
Jul 29, 2024
b536a4c
action primitives initial commit
Jul 30, 2024
067cded
Delete .github/workflows/ci.yml
cgokmen Jul 30, 2024
a14f99d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 30, 2024
b58f38c
Delete scripts/download_datasets.py
cgokmen Jul 30, 2024
30de844
metric refactor
Jul 30, 2024
e32ebb4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 30, 2024
a15102a
updated energy metric comment
Jul 30, 2024
fb3402f
updated energy metric comment
Jul 30, 2024
420edd2
setting metric to 0
Jul 30, 2024
ba7e992
metrics change
Jul 31, 2024
ae74fc5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 31, 2024
328e772
fixed base robot fix
Aug 2, 2024
aa2f4c1
Merge branch 'metrics' of https://github.com/StanfordVL/OmniGibson in…
Aug 2, 2024
0b3b1d1
object initialization bugfix
Aug 5, 2024
628490b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 5, 2024
dbec742
metric tests for work added
Aug 6, 2024
f2a9997
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 6, 2024
d267a51
pending for _dummy
Aug 12, 2024
75aa0ed
pending for _dummy
Aug 12, 2024
b6fc0ea
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 12, 2024
b20bc4a
metric refactoring
Aug 13, 2024
f018043
refactoring
Aug 13, 2024
a60f36c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 13, 2024
c7eed0e
refactoring
Aug 13, 2024
4773e07
work energy metric
Aug 13, 2024
88c2105
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 13, 2024
a9ef20e
corrected rotational inertia calculation
Aug 15, 2024
efd3353
rotational work fix
Aug 15, 2024
9f51314
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 15, 2024
85dabe9
metric work energy finished
Aug 16, 2024
5ecf8cf
work energy metric complete
Aug 16, 2024
0227d71
metric functions
Sep 10, 2024
539da44
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 10, 2024
1446b9b
styling fix
Sep 10, 2024
7806a71
Merge branch 'metrics' of https://github.com/StanfordVL/OmniGibson in…
Sep 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 46 additions & 5 deletions omnigibson/envs/env_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
merge_nested_dicts,
)
from omnigibson.utils.ui_utils import create_module_logger
from omnigibson.utils.python_utils import assert_valid_key, merge_nested_dicts, create_class_from_registry_and_config,\
Recreatable


import time


# Create module logger
log = create_module_logger(module_name=__name__)
Expand All @@ -50,6 +56,17 @@ def __init__(self, configs, in_vec_env=False):
self.render_mode = "rgb_array"
self.metadata = {"render.modes": ["rgb_array"]}

# Initialize other placeholders that will be filled in later
self._initial_pos_z_offset = None # how high to offset object placement to account for one action step of dropping
self._task = None
self._loaded = None
self._current_episode = 0

self._prev_sim_end_ts = 0
self._cur_sim_start_ts = 0

# Variables reset at the beginning of each episode
self._current_step = 0
# Store if we are part of a vec env
self.in_vec_env = in_vec_env

Expand Down Expand Up @@ -531,8 +548,13 @@ def _populate_info(self, info):
if self._scene_graph_builder is not None:
info["scene_graph"] = self.get_scene_graph()

def _pre_step(self, action):
def _pre_step(self, action, time_step=False):
"""Apply the pre-sim-step part of an environment step, i.e. apply the robot actions."""

# record the start time
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
if time_step:
self._cur_sim_start_ts = time.clock()

# If the action is not a dictionary, convert into a dictionary
if not isinstance(action, dict) and not isinstance(action, gym.spaces.Dict):
action_dict = dict()
Expand All @@ -549,7 +571,7 @@ def _pre_step(self, action):
for robot in self.robots:
robot.apply_action(action_dict[robot.name])

def _post_step(self, action):
def _post_step(self, action, time_step=False):
"""Apply the post-sim-step part of an environment step, i.e. grab observations and return the step results."""
# Grab observations
obs, obs_info = self.get_obs()
Expand All @@ -560,6 +582,7 @@ def _post_step(self, action):

# Grab reward, done, and info, and populate with internal info
reward, done, info = self.task.step(self, action)

self._populate_info(info)
info["obs_info"] = obs_info

Expand All @@ -581,9 +604,14 @@ def _post_step(self, action):

# Increment step
self._current_step += 1

# record end time
if time_step:
self._prev_sim_end_ts = time.clock()

yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
return obs, reward, terminated, truncated, info

def step(self, action):
def step(self, action, time_step=False):
"""
Apply robot's action and return the next state, reward, done and info,
following OpenAI Gym's convention
Expand All @@ -601,9 +629,9 @@ def step(self, action):
- bool: truncated, i.e. whether this episode ended due to a time limit etc.
- dict: info, i.e. dictionary with any useful information
"""
self._pre_step(action)
self._pre_step(action, time_step=time_step)
og.sim.step()
return self._post_step(action)
return self._post_step(action, time_step=time_step)
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved

def render(self):
"""Render the environment for debug viewing."""
Expand Down Expand Up @@ -633,6 +661,9 @@ def _reset_variables(self):
"""
self._current_episode += 1
self._current_step = 0
self._prev_sim_end_ts = 0
self._cur_sim_start_ts = 0
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved


def reset(self, get_obs=True, **kwargs):
"""
Expand Down Expand Up @@ -690,6 +721,16 @@ def reset(self, get_obs=True, **kwargs):

return obs, {}

@property
def last_step_wall_time(self):
"""
Returns:
int: return the amount of wall time the last simulation step took
"""
if self._prev_sim_end_ts == 0 or self._cur_sim_start_ts == 0:
return 0
return self._cur_sim_start_ts - self._prev_sim_end_ts

yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
@property
def episode_steps(self):
"""
Expand Down
4 changes: 4 additions & 0 deletions omnigibson/reward_functions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
from omnigibson.reward_functions.point_goal_reward import PointGoalReward
from omnigibson.reward_functions.potential_reward import PotentialReward
from omnigibson.reward_functions.reaching_goal_reward import ReachingGoalReward
from omnigibson.reward_functions.step_metric import StepMetric
from omnigibson.reward_functions.wall_time_metric import WallTimeMetric
from omnigibson.reward_functions.energy_metric import EnergyMetric
from omnigibson.reward_functions.task_success_metric import TaskSuccessMetric
from omnigibson.reward_functions.reward_function_base import REGISTERED_REWARD_FUNCTIONS, BaseRewardFunction
58 changes: 58 additions & 0 deletions omnigibson/reward_functions/energy_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from omnigibson.reward_functions.reward_function_base import BaseRewardFunction
import numpy as np


class EnergyMetric(BaseRewardFunction):
"""
Energy Metric

Measures displacement * mass for every link

Args:
measure_work: If true, measure beginning and end delta rather than step by step delta
"""

def __init__(self, measure_work=False):
# Run super
super().__init__()
self._reward = 0
self.initialized = False
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
self.state_cache = {}
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
self.link_masses = {}
self.measure_work = measure_work

def calculate_displacement(self, posrot, posrot2):
return np.linalg.norm(posrot[0] - posrot2[0])
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved

def _step(self, task, env, action):
new_state_cache = {}
for obj in env.scene.objects:
for link_name, link in obj._links.items():
pos, rot = link.get_position_orientation()
new_state_cache[link_name] = (pos, rot)

if not self.initialized:
self.initialized = True
self.state_cache = new_state_cache

for obj in env.scene.objects:
for link_name, link in obj._links.items():
self.link_masses[link_name] = link.mass
return 0.0, {}

work_metric = 0.0
for linkname, posrot in new_state_cache.items():
work_metric += self.calculate_displacement(posrot, self.state_cache[linkname]) * self.link_masses[linkname]
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved

if self.measure_work:
self._reward = 0
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
if not self.measure_work:
self.state_cache = new_state_cache
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved

self._reward += work_metric
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
return self._reward, {}

def reset(self, task, env):
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
super().reset(task, env)
self.state_cache = {}
self.initialized = False
17 changes: 17 additions & 0 deletions omnigibson/reward_functions/step_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from omnigibson.reward_functions.reward_function_base import BaseRewardFunction


class StepMetric(BaseRewardFunction):
"""
Step Metric
Metric for each simulator step
"""

def __init__(self):
# Run super
super().__init__()
self._reward = 0

def _step(self, task, env, action):
self._reward += 1
return self._reward, {}
30 changes: 30 additions & 0 deletions omnigibson/reward_functions/task_success_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from omnigibson.reward_functions.reward_function_base import BaseRewardFunction

class TaskSuccessMetric(BaseRewardFunction):
"""
TaskSuccessMetric
Metric for partial or full task success
"""

def __init__(self):
# Run super
super().__init__()
self._reward = 0

def _step(self, task, env, action):
successes = []
partial_successes = []
for termination_condition in task._termination_conditions.values():
if termination_condition.partial_success >= 0.0:
partial_successes.append(termination_condition.partial_success)
done, success = termination_condition.step(task, env, action)
# success <=> done and non failure
successes.append(success)
if sum(successes) > 0:
self._reward = 1.0
elif partial_successes:
self._reward = sum(partial_successes) / len(partial_successes)
else:
self._reward = 0.0
# Populate info
return self._reward, {}
17 changes: 17 additions & 0 deletions omnigibson/reward_functions/wall_time_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from omnigibson.reward_functions.reward_function_base import BaseRewardFunction


class WallTimeMetric(BaseRewardFunction):
"""
WallTimeMetric
Metric for wall time accumulated in policy steps
"""

def __init__(self):
# Run super
super().__init__()
self._reward = 0

def _step(self, task, env, action):
self._reward += env.last_step_wall_time
return self._reward, {}
50 changes: 49 additions & 1 deletion omnigibson/tasks/task_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from omnigibson.utils.gym_utils import GymObservable
from omnigibson.utils.python_utils import Registerable, classproperty

from omnigibson.reward_functions import StepMetric, TaskSuccessMetric, WallTimeMetric, EnergyMetric

REGISTERED_TASKS = dict()


Expand Down Expand Up @@ -48,6 +50,7 @@ def __init__(self, termination_config=None, reward_config=None):
# Generate reward and termination functions
self._termination_conditions = self._create_termination_conditions()
self._reward_functions = self._create_reward_functions()
self._metric_functions = self._create_metric_functions()

# Store other internal vars that will be populated at runtime
self._loaded = False
Expand Down Expand Up @@ -144,6 +147,23 @@ def _create_reward_functions(self):
"""
raise NotImplementedError()

def _create_metric_functions(self):
"""
Creates the metric functions in the environment

Returns:
dict of BaseRewardFunction: Metric functions created for this task
"""
metrics = dict()

metrics['steps'] = StepMetric()
metrics['task_success'] = TaskSuccessMetric()
metrics['wall_time'] = WallTimeMetric()
metrics['energy'] = EnergyMetric()
metrics['work'] = EnergyMetric(measure_work=True)

return metrics

def _reset_scene(self, env):
"""
Task-specific scene reset. Default is the normal scene reset
Expand Down Expand Up @@ -193,6 +213,10 @@ def reset(self, env):
termination_condition.reset(self, env)
for reward_function in self._reward_functions.values():
reward_function.reset(self, env)
for metric_function in self._metric_functions.values():
metric_function.reset(self, env)
metric_function._reward = 0.0
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved


def _step_termination(self, env, action, info=None):
"""
Expand Down Expand Up @@ -261,6 +285,28 @@ def _step_reward(self, env, action, info=None):

return total_reward, total_info

def _step_metrics(self, env, action):
"""
Step and aggregate metric functions

Args:
env (Environment): Environment instance
action (n-array): 1D flattened array of actions executed by all agents in the environment

Returns:
- the break down of the metric scores and the metric info
"""

# We'll also store individual reward split
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
breakdown_dict = dict()

for metric_name, metric_function in self._metric_functions.items():
metric, _ = metric_function.step(self, env, action)
breakdown_dict[metric_name] = metric

return breakdown_dict
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved


@abstractmethod
def _get_obs(self, env):
"""
Expand Down Expand Up @@ -320,16 +366,18 @@ def step(self, env, action):
# Make sure we're initialized
assert self._loaded, "Task must be loaded using load() before calling step()!"

# We calculate termination conditions first and then rewards
# (since some rewards can rely on termination conditions to update)
done, done_info = self._step_termination(env=env, action=action)
reward, reward_info = self._step_reward(env=env, action=action)
metric_score, metrics_info = self._step_metrics(env=env, action=action)

# Update the internal state of this task
self._reward = reward
self._metrics = metric_score
self._done = done
self._success = done_info["success"]
self._info = {
"metrics": metrics_info,
"reward": reward_info,
"done": done_info,
}
Expand Down
10 changes: 10 additions & 0 deletions omnigibson/termination_conditions/predicate_goal.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,13 @@ def goal_status(self):
of the predicates matching either of those conditions
"""
return self._goal_status


@property
def partial_success(self):
"""
Returns:
float: partial success if supported, -1.0 otherwise
"""
assert self._done is not None, "At least one step() must occur before partial_success can be calculated!"
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
return len(satisfied) / (len(satisfied) + len(unsatisfied))
yyf20001230 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ def success(self):
assert self._done is not None, "At least one step() must occur before success can be calculated!"
return self._done and self._terminate_is_success

@property
def partial_success(self):
"""
Returns:
float: partial success if supported, None otherwise
"""
assert self._done is not None, "At least one step() must occur before partial_success can be calculated!"
return None

@classproperty
def _terminate_is_success(cls):
"""
Expand Down
Loading
Loading