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 all 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
10 changes: 10 additions & 0 deletions omnigibson/configs/fetch_behavior.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
env:
action_frequency: 60 # (int): environment executes action at the action_frequency rate
physics_frequency: 60 # (int): physics frequency (1 / physics_timestep for physx)
rendering_frequency: 60 # (int): rendering frequency
device: null # (None or str): specifies the device to be used if running on the gpu with torch backend
automatic_reset: false # (bool): whether to automatic reset after an episode finishes
flatten_action_space: false # (bool): whether to flatten the action space as a sinle 1D-array
Expand Down Expand Up @@ -86,3 +87,12 @@ task:
max_steps: 500
reward_config:
r_potential: 1.0
metric_config:
step: 0.05
task_success: 0.6
wall_time: 0.05
work: 0.2
energy: 0.2
rotation: 1
translation: 1

14 changes: 10 additions & 4 deletions omnigibson/envs/env_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ def __init__(self, configs, in_vec_env=False):
self.render_mode = "rgb_array"
self.metadata = {"render.modes": ["rgb_array"]}

# Store if we are part of a vec env
self.in_vec_env = in_vec_env

# Convert config file(s) into a single parsed dict
configs = configs if isinstance(configs, list) or isinstance(configs, tuple) else [configs]

Expand Down Expand Up @@ -124,7 +121,7 @@ def __init__(self, configs, in_vec_env=False):
self.load()

# If we are not in a vec env, we can play ourselves. Otherwise we wait for the vec env to play.
if not self.in_vec_env:
if not in_vec_env:
og.sim.play()
self.post_play_load()

Expand Down Expand Up @@ -541,6 +538,10 @@ def _populate_info(self, info):

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

# perform necessary pre-step actions for the task
self.task.pre_step()

# 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 Down Expand Up @@ -589,6 +590,11 @@ def _post_step(self, action):

# Increment step
self._current_step += 1

# record end time
# perform necessary post-step actions for the task
self.task.post_step()

return obs, reward, terminated, truncated, info

def step(self, action, n_render_iterations=1):
Expand Down
5 changes: 5 additions & 0 deletions omnigibson/metrics/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from omnigibson.metrics.metrics_base import BaseMetric
from omnigibson.metrics.step_metric import StepMetric
from omnigibson.metrics.task_success_metric import TaskSuccessMetric
from omnigibson.metrics.wall_time_metric import WallTimeMetric
from omnigibson.metrics.work_energy_metric import WorkEnergyMetric
61 changes: 61 additions & 0 deletions omnigibson/metrics/metrics_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from abc import ABCMeta, abstractmethod
from copy import deepcopy


# from omnigibson.utils.python_utils import Registerable, classproperty
class BaseMetric:
"""
Base Metric class
Metric-specific reset and step methods are implemented in subclasses
"""

def __init__(self):

# Store internal vars that will be filled in at runtime. The is a metric dictionary computed metric subclasses
self.metric = None

@abstractmethod
def _step(self, task, env, action):
"""
Step the metric function and compute the metric at the current timestep. Overwritten by subclasses.

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

Returns:
2-tuple:
- bool: computed metric
- dict: any metric-related information for this specific metric
"""
raise NotImplementedError()

def step(self, task, env, action):
"""
Step the metricfunction and compute the metric at the current timestep. Overwritten by subclasses

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

Returns:
2-tuple:
- bool: computed metric
- dict: any metric-related information for this specific metric
"""

# Step internally and store output
self.metric = self._step(task=task, env=env, action=action)

# Return metric
return self.metric

def reset(self, task, env):
"""
General metrics reset
"""

# Reset internal vars
self.metric = None
19 changes: 19 additions & 0 deletions omnigibson/metrics/step_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from omnigibson.metrics.metrics_base import BaseMetric


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

def __init__(self):
# initialize step
self._metric = 0

def _step(self, task, env, action):
self._metric += 1
return {"step": self._metric}

def reset(self, task, env):
self._metric = 0
39 changes: 39 additions & 0 deletions omnigibson/metrics/task_success_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from omnigibson.metrics.metrics_base import BaseMetric


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

def __init__(self):
# initialize task success metric
self._metric = 0

def _step(self, task, env, action):
successes = []
partial_success = 0

# Evaluate termination conditions
for termination_condition in task._termination_conditions.values():

# Check if partial success is supported, and if so, store the score (e.g. Behavior Task)
if termination_condition.partial_success:
partial_success = task.success_score

done, success = termination_condition.step(task, env, action)
successes.append(success)

# Calculate metric
if any(successes):
self._metric = 1.0
elif partial_success > 0:
self._metric = partial_success
else:
self._metric = 0.0

return {"task_success": self._metric}

def reset(self, task, env):
self._metric = 0
19 changes: 19 additions & 0 deletions omnigibson/metrics/wall_time_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from omnigibson.metrics.metrics_base import BaseMetric


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

def __init__(self):
# initialize wall time metric
self._metric = 0

def _step(self, task, env, action):
self._metric += task.last_step_wall_time
return {"wall_time": self._metric}

def reset(self, task, env):
self._metric = 0
112 changes: 112 additions & 0 deletions omnigibson/metrics/work_energy_metric.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import torch as th

import omnigibson.utils.transform_utils as T
from omnigibson.metrics.metrics_base import BaseMetric


class WorkEnergyMetric(BaseMetric):
"""
Work and Energy Metric
Measures displacement * mass for every link
"""

def __init__(self, metric_config=None):

# Run work and energy metric initialization
self._work_metric = 0
self._energy_metric = 0
self.state_cache = None
self.prev_state_cache = None

# stores object mass and rotational inertia
self.link_info = {}
self.metric_config = metric_config

def _step(self, task, env, action):

# calculate the current pose of all object links
new_state_cache = {}
self.link_info = {}
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)

# compute this every step to account for object addition and removal
# store link mass and inertia for energy calculation
mass = link.mass
inertia = link.inertia
self.link_info[link_name] = (mass, inertia)

# if the state cache is empty, set it to the current state and return 0
if not self.state_cache:
self.state_cache = new_state_cache
self.prev_state_cache = new_state_cache
return {"work": 0, "energy": 0}

# calculate the energy spent from the previous state to the current state
work_metric = 0.0
energy_metric = 0.0

for linkname, new_posrot in new_state_cache.items():

# TODO: this computation is very slow, consider using a more efficient method

# check if the link is originally in the state cache, if not, skip it to account for object addition and removal
if linkname not in self.state_cache:
continue

init_posrot = self.state_cache[linkname]
mass, inertia = self.link_info[linkname]
work_metric = self.calculate_work_energy(init_posrot, new_posrot, mass, inertia)

# check if the link is in the prev_state_cache, if not, skip it to account for object addition and removal
if linkname not in self.prev_state_cache:
continue

if self.prev_state_cache is not None:
prev_posrot = self.prev_state_cache[linkname]
energy_metric = self.calculate_work_energy(prev_posrot, new_posrot, mass, inertia)

# update the prev_state cache for energy measurement
self.prev_state_cache = new_state_cache

# update the metric accordingly, set the work done (measuring work) and add energy spent this step to previous energy spent (measuring energy)
self._work_metric = work_metric
self._energy_metric += energy_metric
return {"work": self._work_metric, "energy": self._energy_metric}

def calculate_work_energy(self, new_posrot, curr_posrot, mass, inertia):
"""
Calculate the work done and energy spent in a single step

Args:
new_posrot (tuple): new position and orientation of the link
curr_posrot (tuple): current position and orientation of the link.
use initial posrot for work metric, and previous posrot for energy metric
mass (float): mass of the link
inertia (th.Tensor): rotational inertia of the link

Returns:
float: work/energy spent in a single step between two states
"""

position, orientation = T.relative_pose_transform(new_posrot[0], new_posrot[1], curr_posrot[0], curr_posrot[1])
orientation = T.quat2axisangle(orientation)

# formula for translational work metric: displacement * mass * translation weight coefficient
work_metric = th.norm(position) * mass * self.metric_config["translation"]

# formula for rotational work metric: rotation * moment of inertia * rotation weight coefficient
work_metric += sum(th.matmul(orientation, inertia)) * self.metric_config["rotation"]

return work_metric

def reset(self, task, env):

# reset both _work_metric and _energy_metric, and the state cache
self._work_metric = 0
self._energy_metric = 0
self.state_cache = None
self.prev_state_cache = None
self.link_info = {}
6 changes: 5 additions & 1 deletion omnigibson/objects/controllable_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
from omnigibson.utils.numpy_utils import NumpyTypes
from omnigibson.utils.python_utils import CachedFunctions, assert_valid_key, merge_nested_dicts
from omnigibson.utils.ui_utils import create_module_logger
from omnigibson.utils.usd_utils import ControllableObjectViewAPI
from omnigibson.utils.usd_utils import (
ControllableObjectViewAPI,
absolute_prim_path_to_scene_relative,
add_asset_to_stage,
)

# Create module logger
log = create_module_logger(module_name=__name__)
Expand Down
15 changes: 15 additions & 0 deletions omnigibson/prims/rigid_prim.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,21 @@ def mass(self):

return mass

@property
def inertia(self):
"""
Returns:
np.ndarray: inertia tensor of the rigid body in kg m^2.
"""

inertia = self._rigid_prim_view.get_inertias()

# if no inertia is set, return a zero array
if inertia is None:
return th.zeros((3, 3))

return inertia.reshape(3, 3)

@mass.setter
def mass(self, mass):
"""
Expand Down
Loading
Loading