Habitat-API uses Yacs configuration system
with the paradigm of your code + a YACS config for experiment E (+ external dependencies + hardware + other nuisance terms ...) = reproducible experiment E
. Yacs advantages:
- Checks for type consistency.
- All parameters and default values are searchable in the code.
- A parameter doesn't need to be set always as each parameter has a default value.
- Ability to freeze config to prevent unintended changes.
An example of how to merge default config with 2 others configs and overwrite one parameter that could come from the command line:
merged_config = get_config(
config_paths=["configs/tasks/pointnav.yaml",
"configs/dataset/val.yaml"],
opts=["ENVIRONMENT.MAX_EPISODE_STEPS", steps_limit]
)
Below is the structure of config used for Habitat:
- Environment
- Task
- Sensors
- Measurements
- Simulator
- Agent
- Sensors
- Agent
- Dataset
We use node names (e.g. SENSORS: ['RGB_SENSOR', 'DEPTH_SENSOR']
) instead of list
of config nodes (e.g. SENSORS: [{TYPE = "HabitatSimDepthSensor", MIN_DEPTH = 0}, ...]
) to declare the Sensors attached to an Agent or Measures
enabled for the Task . With this approach, it's still easy to overwrite a
particular sensor parameter in yaml file without redefining the whole sensor
config.
Example of how to extend a config outside of habtiat-api
repository.
First, we create a config extending the default config in the code and re-use
habitat.get_config()
:
import habitat
import argparse
from typing import List, Optional, Union
_C = habitat.get_config()
_C.defrost()
# Add new parameters to the config
_C.TASK.EPISODE_INFO = habitat.Config()
_C.TASK.EPISODE_INFO.TYPE = "EpisodeInfo"
_C.TASK.EPISODE_INFO.VALUE = 5
_C.TASK.MEASUREMENTS.append("EPISODE_INFO")
# New function returning extended Habitat config that should be used instead
# of habitat.get_config()
def my_get_config(
config_paths: Optional[Union[List[str], str]] = None,
opts: Optional[list] = None,
) -> habitat.Config:
CONFIG_FILE_SEPARATOR = ","
config = _C.clone()
if config_paths:
if isinstance(config_paths, str):
if CONFIG_FILE_SEPARATOR in config_paths:
config_paths = config_paths.split(CONFIG_FILE_SEPARATOR)
else:
config_paths = [config_paths]
for config_path in config_paths:
config.merge_from_file(config_path)
if opts:
config.merge_from_list(opts)
config.freeze()
return config
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--task-config",
type=str,
default="configs/tasks/pointnav.yaml,"
"configs/datasets/pointnav/habitat_test.yaml",
)
parser.add_argument(
"opts",
default=None,
nargs=argparse.REMAINDER,
help="Modify config options from command line",
)
args = parser.parse_args()
config = my_get_config(config_paths=args.task_config, opts=args.opts)
env = habitat.Env(config)