Source code for agents.tools.config_creation

   1"""
   2This module creates type-hint schemas for the Hydra and OmegaConf backend.
   3
   4Note:
   5    The `conf/agent/default_settings.yaml` file is automatically created
   6    when this file runs.
   7"""
   8
   9# pyright: reportAttributeAccessIssue=warning
  10# pyright: reportCallIssue=warning
  11# pyright: reportInvalidStringEscapeSequence=false
  12# pyright: reportSelfClsParameterName=false
  13# pyright: reportAbstractUsage=false
  14from __future__ import annotations as _
  15
  16import inspect
  17import logging
  18import os
  19import sys
  20from copy import deepcopy
  21from dataclasses import asdict, dataclass, field, is_dataclass
  22from functools import partial
  23from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Tuple, Type, Union, cast, get_type_hints
  24
  25import carla
  26from hydra.conf import HydraConf
  27
  28# ---- Typing ----
  29# SI and II are for type-hinting interpolation strings
  30# II : Equivalent to ${interpolation}
  31# SI [StringInterpolation] : Use this for String interpolation, for example "http://${host}:${port}"
  32from omegaconf import II, SI, DictConfig, ListConfig, OmegaConf
  33from omegaconf.errors import InterpolationKeyError
  34from typing_extensions import Annotated, Literal, Never, Self, TypeAlias, overload
  35
  36# Type Annotations and Helpers
  37from ._config_tools import (  # Type Alias & special objects
  38    MT,
  39    _NOTSET,
  40    _T,
  41    AsDictConfig,
  42    ConfigType,
  43    DictConfigAlias,
  44    DictConfigLike,
  45    NestedConfigDict,
  46    OverwriteDictTypes,
  47    # OmegaConf tools
  48    config_path,
  49    config_store,
  50    # Export tools
  51    export_options,
  52    set_readonly_interpolations,
  53    set_readonly_keys,
  54    to_yaml,
  55)
  56from agents.tools._config_tools import (
  57    MISSING as _MISSING,
  58)
  59from agents.tools.hints import CameraBlueprint
  60from agents.tools.logs import logger
  61from classes.constants import (
  62    AD_RSS_AVAILABLE,
  63    READTHEDOCS,
  64    Phase,
  65    RoadOption,
  66    # Correct runtime versions, Stub or carla.RssLogLevel
  67    RssLogLevel,
  68    # Union Types for both cases
  69    RssLogLevelAlias,
  70    RssRoadBoundariesMode,
  71    RssRoadBoundariesModeAlias,
  72    RulePriority,
  73)
  74from classes.sensors.rss_visualization import RssDebugVisualizationMode
  75from launch_tools import class_or_instance_method
  76
  77if TYPE_CHECKING:
  78    from typing_extensions import TypeGuard
  79    from agents.leaderboard_agent import LunaticChallenger
  80    from classes.rule import Rule
  81    from classes.worldmodel import GameFramework
  82    from ._config_tools import ConfigWithOverwrites
  83
  84__all__ = [
  85    "AgentConfig",
  86    "AsDictConfig",
  87    "AutopilotBehavior",
  88    "BasicAgentSettings",
  89    "BehaviorAgentSettings",
  90    "CallFunctionFromConfig",
  91    "CameraConfig",
  92    "ContextSettings",
  93    "CreateRuleFromConfig",
  94    "LaunchConfig",
  95    "LunaticAgentSettings",
  96    "RssLogLevel",
  97    "RssRoadBoundariesMode",
  98    "RuleConfig",
  99    "config_path",
 100    "config_store",
 101]
 102
 103if sys.version_info < (3, 9):
 104    # fix for typing.get_type_hints when OmegaConf.structured is used with nested dataclasses
 105    __original_config_path = config_path
 106
 107    def config_path(path: Optional[str] = None):
 108        decorator = __original_config_path(path)
 109
 110        def wrapper(cls: type):
 111            globals()[cls.__name__] = cls  # add to globals
 112            return decorator(cls)
 113
 114        return wrapper
 115
 116
 117# ---------- Globals --------------
 118
 119_WARN_LIVE_INFO = True
 120"""Warn about a possibly malformed live_info in the config."""
 121
 122_file_path = __file__
 123"""
 124__file__ alias of this module. used for YAML comments.
 125Modify this variable if a config from a different file should be parsed.
 126
 127.. deprecated::
 128    Will take the file of the object automatically
 129"""
 130
 131if READTHEDOCS and not TYPE_CHECKING:
 132    # Import and usage with sphinx imported-members does not work
 133    from typing_extensions import TypeAliasType
 134
 135    # annotate MISSING instead of ???
 136    MISSING: Any = _MISSING  # type: ignore
 137    """
 138    Alias for :py:obj:`omegaconf.MISSING`, is literally :python:`"???"` but has type :python:`Any`.
 139
 140    If an attribute with this value is accessed from a :py:class:`DictConfig`,
 141    it will raise a :py:exc:`MissingMandatoryValue` error.
 142
 143    :meta hide-value:
 144    """
 145
 146    # prevent unpack of nested types
 147    NestedConfigDict = TypeAliasType(
 148        "NestedConfigDict", dict[str, "AgentConfig | DictConfig | Any |  NestedConfigDict"]
 149    )
 150    """
 151    Alias for nested configurations: :python:`Dict[str, NestedConfigDict | AgentConfig | DictConfig | Any]`
 152
 153    :meta hide-value:
 154    """
 155    __all__.insert(0, "MISSING")  # type: ignore
 156    __all__.insert(1, "NestedConfigDict")  # type: ignore
 157else:
 158    from omegaconf import MISSING  # type: ignore
 159
 160
 161# ---------------------
 162# Base Classes
 163# ---------------------
 164
 165
[docs] 166class AgentConfig(DictConfigLike if TYPE_CHECKING else object): 167 """ 168 Base interface and utility class for the agent settings. 169 170 Handling the initialization from a nested dataclass and merges in the changes 171 from the overwrites options. 172 """ 173 174 _config_path: ClassVar[Optional[str]] = "NOT_GIVEN" 175 """ 176 The key, relative to the LaunchConfig, where the config is stored to use Hydra's :py:obj:`ConfigStore<hydra>`. 177 178 Create subclasses in the following way: 179 180 .. code-block:: python 181 182 @config_path("agent/speed") 183 @dataclass 184 class AgentSpeedSettings(AgentConfig): 185 186 Attention: 187 - Use "/" as separator and not dots. 188 - This is used for the Hydra schema registration and repeated paths will overwrite each other. 189 - This value is inherited (if != :code:`NOT_GIVEN`), and the value of the parent is taken 190 as default. Do not type-hint this value it must be a ClassVar to not conflict with dataclasses. 191 192 See Also: 193 :py:func:`.config_path` 194 """ 195 196 overwrites: "Optional[NestedConfigDict]" = None 197 """Overwrites of nested dictionaries for used for the initialization of the config.""" 198
[docs] 199 @classmethod 200 def get_defaults(cls) -> "Self": 201 """Returns an instance with the default options.""" 202 return cls() # type: ignore[call-arg]
203
[docs] 204 @class_or_instance_method 205 def export_options( 206 cls_or_self: Union[Type[Self], Self], 207 path: Union[str, "os.PathLike[str]"], 208 *, 209 resolve: bool = False, 210 with_comments: bool = False, 211 detailed_rules: bool = False, 212 include_private: bool = False, 213 ) -> None: 214 """ 215 Exports the options to a YAML file. With the :py:meth:`to_yaml` method. 216 217 Args: 218 path : The path for the exported YAML file. 219 resolve : Whether to resolve the options before exporting. Defaults to False. 220 with_comments : Whether to include comments in the exported YAML file. Defaults to False. 221 detailed_rules : Whether to include detailed rules in the exported YAML file. Defaults to False. 222 include_private : Whether to include private fields in the exported YAML file. Defaults to False. 223 """ 224 export_options( 225 cls_or_self, 226 path, 227 resolve=resolve, 228 with_comments=with_comments, 229 detailed_rules=detailed_rules, 230 include_private=include_private, 231 )
232
[docs] 233 @class_or_instance_method 234 def to_yaml( 235 cls_or_self: Union[Type[Self], Self], 236 resolve: bool = False, 237 yaml_commented: bool = True, 238 detailed_rules: bool = False, 239 *, 240 include_private: bool = False, 241 ) -> str: 242 """ 243 Convert the options to a YAML string representation. 244 245 Args: 246 resolve : Whether to resolve interpolations. Defaults to False. 247 yaml_commented : Whether to include comments in the YAML output. Defaults to True. 248 detailed_rules : Whether to include detailed rules in the YAML output. Defaults to False. 249 include_private : Whether to include fields that are marked as private. Defaults to False. 250 251 Returns: 252 str: The YAML string representation of the options. 253 """ 254 return to_yaml( 255 cls_or_self, 256 resolve=resolve, 257 yaml_commented=yaml_commented, 258 detailed_rules=detailed_rules, 259 include_private=include_private, 260 )
261
[docs] 262 @classmethod 263 def from_yaml(cls, path: str, *, merge: bool = True) -> Self: 264 """ 265 Loads the options from a yaml file. 266 267 Args: 268 path: The path to the yaml file. 269 merge: Merges the loaded yaml into the base class settings. Otherwise returns 270 the settings as they are in the file. 271 Defaults to True. 272 273 Returns: 274 AgentConfig: An instance of this class 275 """ 276 loaded = OmegaConf.load(path) 277 if not merge: 278 return cast("Self", loaded) 279 options = OmegaConf.merge(OmegaConf.structured(cls, flags={"allow_objects": True}), loaded) 280 return cast("Self", options)
281
[docs] 282 @classmethod 283 def load_schema(cls, path: Optional[str] = None) -> AsDictConfig[Self]: 284 """ 285 Classes decorated with :py:func:`@config_path <.config_path>` can be loaded with this method. 286 287 This is equivalent to: 288 create(as_dictconfig=True, dict_config_no_parent=False) 289 290 See Also: 291 Hydra_ ConfigStore 292 """ 293 path = path or cls._config_path 294 if path is None: 295 raise ValueError("No path to the schema provided and the class has not been created with `@config_path`.") 296 return cast("Self", config_store.load(path).node)
297
[docs] 298 @classmethod 299 def create( 300 cls, 301 settings: "Union[os.PathLike[str], str, DictConfig, NestedConfigDict | OverwriteDictTypes | AgentConfig, None]" = None, 302 overwrites: "Optional[NestedConfigDict]" = None, 303 *, 304 assure_copy: bool = True, 305 as_dictconfig: Optional[bool] = True, 306 dict_config_no_parent: bool = True, 307 ) -> "Self | AsDictConfig[Self]": 308 r""" 309 Creates the agent settings based on the provided arguments. 310 311 Note: 312 By default this returns a DictConfig version of this class. 313 314 Parameters: 315 settings : The argument specifying the agent settings. It can be a path to a YAML file, a dictionary, a 316 :external-icon-parse:`:py:mod:\`@dataclass <dataclasses>\`` decorated class or a :external-icon-parse:`:py:class:\`omegaconf.DictConfig\``. 317 overwrites : Optional mapping containing additional settings to overwrite the default agent settings. 318 as_dictconfig : 319 320 - If True, the agent settings are returned as a :py:class:`DictConfig`. 321 - If False, the agent settings are returned as an instance of this class. 322 - If None, the return type is determined by the type of the args and other arguments 323 i.e. if args is a :py:class:`DictConfig` and assure_copy is False, the 324 original input is checked and returned. 325 326 Returns: 327 :py:class:`AgentConfig` (duck-typed); actually :external-icon-parse:`:py:class:\`omegaconf.DictConfig\``): The created agent settings. 328 329 Raises: 330 Exception: If the overwrites cannot be merged into the agent settings. 331 """ 332 # assert TYPE_CHECKING and (issubclass(cls, ConfigWithoutOverwrites) or issubclass(cls, ConfigWithOverwrites)) 333 behavior: Self 334 if settings is None: 335 if not as_dictconfig: 336 behavior = cls() 337 else: 338 behavior = cls.load_schema() 339 elif isinstance(settings, (str, os.PathLike)): 340 # load yaml file 341 logger.info("Using agent settings from file `%s`", settings) 342 behavior = cls.from_yaml(settings) # DictConfig # type: ignore[attr-type] 343 elif isinstance(settings, dict): 344 logger.debug( 345 "Using agent settings from dict with LunaticAgentSettings. Note settings are NOT a DictConfig. Interpolations not available." 346 ) 347 if cls.uses_overwrite_interface(cls): 348 behavior = cls(overwrites=settings) 349 else: 350 behavior = cls(**settings) # pyright: ignore[reportArgumentType] # because DictConfig parent 351 elif is_dataclass(settings) or isinstance(settings, DictConfig): 352 logger.debug("Using agent settings as is, as it is a dataclass or DictConfig.") 353 # clean_settings = {k : v for k, v in settings.items() if not OmegaConf.is_missing(v)} 354 if not cls.uses_overwrite_interface() and is_dataclass(settings): 355 from dataclasses import asdict # noqa: PLC0415 356 357 settings = cast("OverwriteDictTypes", asdict(settings)) 358 if assure_copy: 359 behavior = cls(overwrites=settings) if cls.uses_overwrite_interface() else cls(**settings) # pyright: ignore[reportArgumentType,reportCallIssue] 360 elif inspect.isclass(settings): 361 behavior = settings() # pyright: ignore[reportCallIssue] 362 else: 363 # instantiate class to check keys but use original type 364 cls(overwrites=settings) if cls.uses_overwrite_interface() else cls(**settings) # pyright: ignore[reportCallIssue] 365 behavior = settings # stays duck-typed DictConfig # type: ignore 366 else: 367 # Handle unknown cases 368 if as_dictconfig is None: 369 logger.warning( 370 "Type `%s` of launch argument type `agent_settings` not supported, trying to use it anyway. Expected are (str, dataclass, DictConfig)", 371 type(settings), 372 ) 373 if inspect.isclass(settings): 374 behavior = settings() # be sure to have an instance 375 if assure_copy: 376 behavior = cls(overwrites=settings) if cls.uses_overwrite_interface() else cls(**settings) 377 else: 378 # instantiate to class to check keys but use original type 379 cls(overwrites=settings) if cls.uses_overwrite_interface(cls) else cls(**settings) 380 behavior = settings # has Unknown type but keys are present # type: ignore 381 382 # Handle overwrites 383 if overwrites: 384 if isinstance(behavior, DictConfig): # pyright: ignore[reportUnnecessaryIsInstance] 385 behavior = cls.cast( 386 OmegaConf.merge(behavior, OmegaConf.structured(overwrites, flags={"allow_objects": True})) 387 ) 388 else: 389 try: 390 behavior.merge_with(overwrites) 391 except Exception: 392 logger.error( 393 "Overwrites could not be merged into the agent settings with `base_config.update(overwrites)`. Passing config_mode=True might help." 394 ) 395 raise 396 # Turn to DictConfig 397 if as_dictconfig and not isinstance(behavior, DictConfig): # pyright: ignore[reportUnnecessaryIsInstance] 398 try: 399 behavior = OmegaConf.structured(behavior, flags={"allow_objects": True}) 400 except Exception as e: 401 print(e) 402 # breakpoint() 403 raise 404 elif as_dictconfig is False and isinstance(behavior, DictConfig): # pyright: ignore[reportUnnecessaryIsInstance] 405 return cls(overwrites=behavior) if cls.uses_overwrite_interface() else cls(**behavior) # pyright: ignore[reportCallIssue] 406 elif as_dictconfig is None: 407 logger.debug( 408 "A clear return type of %s.create_from_args has not set as config_mode is None. Returning as %s", 409 cls.__name__, 410 type(behavior), 411 ) 412 413 if isinstance(behavior, DictConfig): # pyright: ignore[reportUnnecessaryIsInstance] 414 behavior._set_flag("allow_objects", True) # pyright: ignore[reportPrivateUsage] 415 if dict_config_no_parent: 416 # Dict config interpolations always use the full path, interpolations might go from the root of the config. 417 # If there is launch_config.agent, with launch config as root, the interpolations will not work. 418 behavior.__dict__["_parent"] = None # Remove parent from the config, i.e. make it a top-level config. 419 420 return cast("Self", behavior)
421 422 @overload 423 @classmethod 424 def check_config( 425 cls, 426 config: MT, 427 strictness: "Literal[0, False]", 428 as_dict_config: "Literal[False]", 429 ) -> MT: ... 430 431 @overload 432 @classmethod 433 def check_config( 434 cls, 435 config: ConfigType, 436 strictness: "Literal[0, False]", 437 as_dict_config: "Literal[True]", 438 ) -> ConfigType: ... 439 440 @overload 441 @classmethod 442 def check_config( 443 cls, config: ConfigType, strictness: int, as_dict_config: "Literal[True]" 444 ) -> Self: ... 445
[docs] 446 @classmethod 447 def check_config( 448 cls, config: "ConfigType | MT | NestedConfigDict", strictness: int = 1, as_dict_config: bool = True 449 ) -> "Self | ConfigType | MT": 450 """ 451 - :python:`strictness == 1` type-cast the config to this class, assuring all keys are present. 452 However the type and correctness of the field-contents are not checked. 453 - :python:`strictness > 1` the config will be a :external_py_class:`DictConfig` object. 454 **as_dict_config** is ignored. 455 - :python:`strictness == 2`: Will assure that the *initial* types are correct. 456 - :python:`strictness >= 2` will return the config as a structured config, forcing the 457 defined types during runtime as well. 458 459 Attention: 460 Type-forcing for more complex types does not work, e.g. types from the :py:mod:`carla` module, 461 this especially includes carla's enum objects. 462 For what is supported see https://omegaconf.readthedocs.io/en/2.3_branch/structured_config.html. 463 464 Parameters: 465 config: The configuration to check against this class 466 467 Args: 468 strictness: See above. Defaults to 1. 469 as_dict_config: Whether to return a duck-typed :external_py_class:`DictConfig` 470 instead of an instance of this class. 471 Defaults to :python:`True`. 472 473 Returns: 474 A version of this class or a duck-typed :external_py_class:`DictConfig`. 475 """ 476 if "experiments" in config and not hasattr(cls, "experiments"): # For LaunchConfig 477 print( 478 "\nWARNING: There is key 'experiments' in the config. Did you forget a '# @package _global_' in the first line? Keys in experiments: %s", 479 config["experiments"].keys(), 480 ) 481 if strictness <= 0: 482 if as_dict_config and not isinstance(config, DictConfig): 483 new_config0: MT = OmegaConf.structured(config, flags={"allow_objects": True}) 484 return new_config0 485 return cast("MT", config) 486 if strictness == 1: 487 if is_dataclass(config): 488 config = asdict(config) 489 new_config = cls(overwrites=config) if cls.uses_overwrite_interface() else cls(**config) # pyright: ignore[reportCallIssue] 490 if as_dict_config and not isinstance(config, DictConfig): 491 return cls.cast(OmegaConf.structured(new_config, flags={"allow_objects": True})) 492 return new_config 493 # TODO: # NOTE: This does not assure missing keys: 494 new_config = cls.create(config, as_dictconfig=True, assure_copy=False) 495 if strictness == 2: 496 if as_dict_config and not isinstance(new_config, DictConfig): 497 return cast("Self", OmegaConf.structured(new_config, flags={"allow_objects": True})) 498 return new_config 499 new_config: Self = OmegaConf.structured(new_config, flags={"allow_objects": True}) # include flag, yes no? 500 return new_config
501 502 if READTHEDOCS and not TYPE_CHECKING: 503 # simplify signature for online 504 check_config.__wrapped__.__annotations__ = check_config.__annotations__ = { 505 "config": "NestedConfigDict", 506 "strictness": "int", 507 "as_dict_config": "bool", 508 "return": "Self", 509 } # pylint: disable=protected-access, line-too-long 510
[docs] 511 @class_or_instance_method 512 def to_dict_config( 513 cls_or_self: Union[Type[Self], Self], 514 *, 515 lock_interpolations: bool = True, 516 lock_fields: Optional[List[str]] = None, 517 ) -> DictConfig: 518 r""" 519 Returns a :external-icon-parse:`:py:class:\`omegaconf.DictConfig\`` from the current options. 520 521 Interpolations can be locked to prevent them from being overwritten. 522 E.g. :code:`speed.current_speed` cannot diverge from :code:`live_info.current_speed`. 523 524 Parameters: 525 lock_interpolations: Whether to set interpolations to readonly. Defaults to :python:`True`. 526 lock_fields: A list of fields to set to readonly. Defaults to :python:`None`. 527 528 Returns: 529 :py:class:`AgentConfig` (duck-typed); actually :external-icon-parse:`:py:class:\`omegaconf.DictConfig\``) : The options as a :py:class:`DictConfig`. 530 """ 531 options = cls_or_self 532 conf = OmegaConf.structured(options, flags={"allow_objects": True}) 533 # This pre 534 if lock_interpolations: 535 set_readonly_interpolations(conf) 536 if lock_fields: 537 set_readonly_keys(conf, lock_fields) 538 return conf
539
[docs] 540 def copy(self) -> "Self": 541 """Returns a deep copy of this object.""" 542 return deepcopy(self)
543
[docs] 544 @class_or_instance_method 545 def get(cls_or_self: Union[Type[Self], Self], key: str, default: _T = _NOTSET) -> "Any | _T": 546 """ 547 Analog of :py:func:`.getattr`. 548 549 Raises: 550 AttributeError: If the **key** is not found and no **default** is provided. 551 """ 552 if default is _NOTSET: 553 return getattr(cls_or_self, key) 554 return getattr(cls_or_self, key, default)
555
[docs] 556 def update(self, options: "Union[NestedConfigDict, DictConfig, AgentConfig]", clean: bool = True) -> None: 557 """ 558 Updates the options with a new dictionary. Will call :py:meth:`update` recursively 559 for nested :py:class:`AgentConfig` objects. 560 561 Parameters: 562 options: The new options to update with. 563 clean: Whether to call :py:meth:`_clean_options` after updating. Defaults to True. 564 """ 565 try: 566 if is_dataclass(options): # instance of AgentConfig 567 key_values = options.__dataclass_fields__.items() 568 elif isinstance(options, DictConfig): 569 key_values = options.items_ex(resolve=False) # Calling this with missing keys will raise an error 570 else: 571 key_values = options.items() # type: ignore 572 573 for k, v in key_values: 574 if isinstance(getattr(self, k), AgentConfig): # pyright: ignore[reportArgumentType], k is str 575 getattr(self, k).update(v) # pyright: ignore[reportArgumentType] 576 else: 577 setattr(self, k, v) # pyright: ignore[reportArgumentType] 578 if clean: 579 self._clean_options() 580 except Exception as e: 581 print("\n ERROR updating", self.__class__.__name__, "with >", options, "< Error:", e, "\n") 582 raise
583 584 @overload 585 @classmethod 586 def uses_overwrite_interface(cls) -> bool: ... 587 588 @overload 589 @classmethod 590 def uses_overwrite_interface( 591 cls, cls_: type[Self] 592 ) -> "TypeGuard[type[ConfigWithOverwrites[Self]]]": ... 593
[docs] 594 @classmethod 595 def uses_overwrite_interface( 596 cls, cls_: type[Self] | None = None 597 ) -> "TypeGuard[type[ConfigWithOverwrites[Self]]] | bool": 598 """ 599 Whether or not the class is created by a single parameter "overwrites" 600 or via keyword arguments for each field. 601 602 .. the TypeGuard variant is not reliable in every case but rather depends on the __init__ 603 """ 604 return "overwrites" in inspect.signature((cls_ or cls).__init__).parameters
605
[docs] 606 @classmethod 607 def cast(cls, value: Any): 608 """Type-hinting method to cast a value to this class.""" 609 return cast("Self", value)
610
[docs] 611 def _clean_options(self): 612 """ 613 Postprocessing of possibly wrong values 614 615 :meta public: 616 """ 617 return
618 619 def __post_init__(self): 620 """ 621 Assures that if a dict is passed the values overwrite the defaults. 622 623 # NOTE: Will be used for dataclass derived children 624 """ 625 self._clean_options() 626 if self.overwrites is None: 627 return 628 assert is_dataclass(self) 629 # if isinstance(self.overwrites, DictConfig): 630 # self.overwrites = OmegaConf.to_container(self.overwrites, throw_on_missing=False) # convert to dict 631 # Merge the overwrite dict into the correct ones. 632 633 # Handle the overwrites 634 try: 635 value: Union[ 636 NestedConfigDict, 637 AgentConfig, 638 DictConfig, 639 str, 640 bool, 641 float, 642 int, 643 list[Any], 644 ListConfig, 645 Literal["None"], 646 None, 647 ] # noqa: PYI051 # literal and str 648 annotations = get_type_hints(self.__class__) 649 for key in self.overwrites.keys(): 650 if key in annotations: # This is "overwrites" -> to "self" 651 # retrieve safe value 652 if isinstance(self.overwrites, DictConfig): 653 if OmegaConf.is_missing(self.overwrites, key): 654 logging.debug("%s is MISSING, keeping as it is", key) 655 setattr(self, key, MISSING) 656 continue 657 if OmegaConf.is_interpolation(self.overwrites, key): 658 # Take interpolation as is 659 logging.debug("%s is an interpolation, keeping as it is", key) 660 value = self.overwrites._get_node(key)._value() # pyright: ignore[reportOptionalMemberAccess] 661 setattr(self, key, value) 662 continue 663 value = self.overwrites[key] 664 665 # Special cases 666 if key == "current_rule": 667 if value != ContextSettings.current_rule: 668 logging.warning( 669 "Overwriting `current_rule` with %s. Expecting interpolation '%s'", 670 value, 671 ContextSettings.current_rule, 672 ) 673 elif key == "overwrites": 674 if value: 675 logging.error( 676 "A non-empty overwrites key should not be set in the overwrites dict. Ignoring it" 677 ) 678 elif key == "rules": 679 setattr(self, key, value) # value is a list 680 elif key == "live_info": 681 if TYPE_CHECKING: 682 assert isinstance(value, LiveInfo) 683 live_info_dict: LiveInfo = self.live_info # type: ignore[attr-defined] 684 for live_info_key in value.keys(): 685 if _WARN_LIVE_INFO and not OmegaConf.is_missing(value, live_info_key): 686 logging.warning( 687 "WARNING: live_info should only consist of missing values. Setting %s to %s", 688 live_info_key, 689 value[live_info_key], 690 ) 691 setattr(live_info_dict, live_info_key, value[live_info_key]) # type: ignore[attr] 692 # Delegate False to a subfield 693 elif self.__dataclass_fields__[key].metadata.get("can_be_false", False) and value in ( 694 False, 695 None, 696 "None", 697 True, 698 ): 699 # redirect to .enabled subkey. 700 if value in (False, None, "None"): 701 # Rss or Datamatrix settings 702 getattr(self, key).update({"enabled": False}) 703 elif value is True: 704 getattr(self, key).update({"enabled": True}) 705 # NOTE: Do not use Union keys with AgentConfig, else this will throw an error 706 elif issubclass(annotations[key], AgentConfig): 707 getattr(self, key).update(value) # AgentConfig.update 708 else: 709 logging.debug( 710 "%s : %s is not a Config or annotated as AgentConfig", key, value 711 ) # this does normally not happen 712 setattr(self, key, value) 713 else: 714 logging.error( 715 "ERROR: Key '%s' not found in %s default options. Consider updating or creating a new class to avoid this message.", 716 key, 717 self.__class__.__name__, 718 ) 719 except InterpolationKeyError: 720 logging.exception("Interpolation error") 721 print("Interpolation error in", self.__class__.__name__) 722 # breakpoint() 723 raise 724 except Exception as e: 725 logging.exception("Error") 726 print("\n\nError updating", self.__class__.__name__, "key:", key, "value:", value, "Error:", e) # type: ignore 727 # breakpoint() 728 raise
729 730 731# --------------------- 732 733# --------------------- 734# Live Info 735# --------------------- 736 737 738@config_path("agent/live_info") 739@dataclass 740class LiveInfo(AgentConfig): 741 """ 742 .. @package agent.live_info 743 744 Keeps track of information that changes during the simulation. 745 """ 746 747 velocity_vector: carla.Vector3D = MISSING 748 """ 749 3D Vector of the current velocity of the vehicle. 750 """ 751 752 current_speed: float = MISSING 753 """ 754 Velocity of the vehicle in km/h. 755 756 Note: 757 The `z` component is ignored. 758 """ 759 760 current_transform: carla.Transform = MISSING 761 current_location: carla.Location = MISSING 762 763 current_speed_limit: float = MISSING 764 765 executed_direction: RoadOption = MISSING 766 """ 767 Direction that was executed in the last step by the local planner 768 769 planner.target_road_option is the option last executed by the planner (constant) 770 incoming direction is the next *planned* direction subject to change (variable) 771 """ 772 773 incoming_direction: RoadOption = MISSING 774 """ 775 RoadOption that will used for the current step 776 """ 777 778 incoming_waypoint: Optional[carla.Waypoint] = MISSING 779 """ 780 Waypoint that is planned to be targeted in this step. 781 """ 782 783 is_taking_turn: bool = MISSING 784 """ 785 incoming_direction in (RoadOption.LEFT, RoadOption.RIGHT) 786 """ 787 788 is_changing_lane: bool = MISSING 789 """ 790 incoming_direction in (RoadOption.CHANGELANELEFT, RoadOption.CHANGELANERIGHT) 791 """ 792 793 next_traffic_light: Union[carla.TrafficLight, None] = MISSING 794 """ 795 Traffic light that is closest to the next intersection. 796 797 Is `None` if the agent is at an intersection. 798 799 + NOTE: This might not be in the path or infront of the vehicle. 800 """ 801 802 next_traffic_light_distance: Union[float, None] = MISSING 803 """ 804 Distance to the assumed next traffic light. 805 """ 806 807 last_applied_controls: carla.VehicleControl = MISSING 808 """ 809 :py:class:`carla.VehicleControl` that was applied in the last step. 810 """ 811 812 # NOTE: Not ported to OmegaConf 813 @property 814 def speed(self): 815 return self.current_speed 816 817 @property 818 def speed_limit(self): 819 return self.current_speed_limit 820 821 # NOTE: not wr 822 # current_speed : float = II(".speed") # alias for convenience 823 # current_speed_limit : float = II(".speed_limit") 824 825 826# --------------------- 827# Speed 828# --------------------- 829 830 831@dataclass 832class BasicAgentSpeedSettings(AgentConfig): 833 current_speed: float = II("live_info.current_speed") 834 """This is a reference to live_info.current_speed, which is updated by the agent""" 835 836 current_speed_limit: float = II("live_info.current_speed_limit") 837 """This is a reference to live_info.current_speed_limit, which is updated by the agent""" 838 839 target_speed: float = 20 840 """desired cruise speed in Km/h; overwritten by SpeedLimit if follow_speed_limit is True""" 841 842 follow_speed_limits: bool = False 843 """If the agent should follow the speed limit. *NOTE:* SpeedLimit overwrites target_speed if True (local_planner.py)""" 844 845 846@dataclass 847class BehaviorAgentSpeedSettings(BasicAgentSpeedSettings): 848 """ 849 The three situations they adjust their speed; # SEE: `behavior_agent.car_following_manager` 850 851 Case A car in front and getting closer : slow down; slower than car in front 852 Take minium from, speed decrease, speed limit adjustment and target_speed 853 `target_speed` = min( other_vehicle_speed - self._behavior.speed_decrease, # <-- slow down BELOW the other car 854 self._behavior.max_speed # use target_speed instead 855 self._speed_limit - self._behavior.speed_lim_dist]) 856 Case B car in front but safe distance : match speed 857 `target_speed` = min([ 858 max(self._min_speed, other_vehicle_speed), # <- match speed 859 self._behavior.max_speed, 860 self._speed_limit - self._behavior.speed_lim_dist]) 861 Case C front is clear 862 `target_speed` = min([ 863 self._behavior.max_speed, 864 self._speed_limit - self._behavior.speed_lim_dist]) 865 """ 866 867 # DEPRECATED: deprecated max_speed use target_speed instead # NOTE: Behavior agents are more flexible in their speed. 868 max_speed: float = 50 869 """The maximum speed in km/h your vehicle will be able to reach. 870 From normal behavior. This supersedes the target_speed when following the BehaviorAgent logic.""" 871 872 # CASE A 873 speed_decrease: float = 10 874 """other_vehicle_speed""" 875 876 safety_time: float = 3 877 """Time in s before a collision at the same speed -> apply speed_decrease""" 878 879 # CASE B 880 min_speed: float = 5 881 """Implement als variable, currently hard_coded""" 882 883 # All Cases 884 speed_lim_dist: float = 3 885 """ 886 Difference to speed limit. 887 NOTE: For negative values the car drives above speed limit 888 """ 889 890 intersection_speed_decrease: float = 5.0 891 """Reduction of the targeted_speed when approaching an intersection""" 892 893 894@dataclass 895class AutopilotSpeedSettings(AgentConfig): 896 vehicle_percentage_speed_difference: float = 30 # in percent 897 """ 898 Sets the difference the vehicle's intended speed and its current speed limit. 899 Speed limits can be exceeded by setting the percentage to a negative value. 900 Default is 30. 901 902 Exceeding a speed limit can be done using negative percentages. 903 """ 904 905 906@config_path("agent/speed") 907@dataclass 908class LunaticAgentSpeedSettings(AutopilotSpeedSettings, BehaviorAgentSpeedSettings): 909 vehicle_percentage_speed_difference: float = MISSING # 30 910 """ 911 TODO: Port from traffic manager. 912 913 Sets the difference the vehicle's intended speed and its current speed limit. 914 Speed limits can be exceeded by setting the perc to a negative value. 915 Default is 30. 916 Exceeding a speed limit can be done using negative percentages. 917 """ 918 919 intersection_target_speed: float = SI( 920 "${min:${.max_speed}, ${sub:${.current_speed_limit}, ${.intersection_speed_decrease}}}" 921 ) 922 """Formula or value to calculate the target speed when approaching an intersection""" 923 924 925# --------------------- 926# Distance 927# --------------------- 928 929 930@dataclass 931class BasicAgentDistanceSettings(AgentConfig): 932 pass 933 934 935@dataclass 936class BehaviorAgentDistanceSettings(BasicAgentDistanceSettings): 937 """ 938 Collision Avoidance 939 ------------------- 940 941 Distance in which for vehicles are checked. 942 943 Usage: max_distance = max(min_proximity_threshold, self._speed_limit / (2 if <LANE CHANGE> else 3 ) ) 944 """ 945 946 emergency_braking_distance: float = 5 947 """Emergency Stop Distance Trigger""" 948 949 950@dataclass 951class AutopilotDistanceSettings(AgentConfig): 952 distance_to_leading_vehicle: float = 5.0 953 """ 954 Sets the minimum distance in meters that a vehicle has to keep with the others. 955 The distance is in meters and will affect the minimum moving distance. It is computed from front to back of the vehicle objects. 956 """ 957 958 959@config_path("agent/distance") 960@dataclass 961class LunaticAgentDistanceSettings(AutopilotDistanceSettings, BehaviorAgentDistanceSettings): 962 distance_to_leading_vehicle: float = MISSING # 5.0 963 """ 964 PORT from TrafficManager # TODO: 965 966 Sets the minimum distance in meters that a vehicle has to keep with the others. 967 The distance is in meters and will affect the minimum moving distance. It is computed from front to back of the vehicle objects. 968 """ 969 970 971# --------------------- 972# Lane Change 973# --------------------- 974 975 976@dataclass 977class BasicAgentLaneChangeSettings(AgentConfig): 978 """ 979 Timings in seconds to finetune the lane change behavior. 980 981 NOTE: see: `BasicAgent.lane_change` and `BasicAgent._generate_lane_change_path` 982 """ 983 984 same_lane_time: float = 0.0 985 other_lane_time: float = 0.0 986 lane_change_time: float = 2.0 987 988 989@dataclass 990class BehaviorAgentLaneChangeSettings(BasicAgentLaneChangeSettings): 991 pass 992 993 994@dataclass 995class AutopilotLaneChangeSettings(AgentConfig): 996 auto_lane_change: bool = True 997 """Turns on or off lane changing behavior for a vehicle.""" 998 999 random_left_lanechange_percentage: float = 0.1 1000 """ 1001 Adjust probability that in each timestep the actor will perform a left/right lane change, 1002 dependent on lane change availability. 1003 """ 1004 1005 random_right_lanechange_percentage: float = 0.1 1006 """ 1007 Adjust probability that in each timestep the actor will perform a left/right lane change, 1008 dependent on lane change availability. 1009 """ 1010 1011 keep_right_rule_percentage: float = 0.7 1012 """ 1013 During the localization stage, this method sets a percent chance that vehicle will follow the keep right rule, 1014 and stay in the right lane. 1015 """ 1016 1017 1018@config_path("agent/lane_change") 1019@dataclass 1020class LunaticAgentLaneChangeSettings(BehaviorAgentLaneChangeSettings): 1021 """ 1022 Lane Change 1023 1024 Adjust probability that in each timestep the actor will perform a left/right lane change, 1025 dependent on lane change availability. 1026 """ 1027 1028 # Moved to -> RandomLaneChangeRule 1029 # random_lane_change_interval : int = 200 1030 # """Cooldown value for a lane change in the 'lane_change' group.""" 1031 1032 1033# --------------------- 1034# Obstacles 1035# --------------------- 1036 1037 1038@dataclass 1039class BasicAgentObstacleDetectionAngles(AgentConfig): 1040 """ 1041 Detection Angles for the BasicAgent used in the `BasicAgent._vehicle_obstacle_detected` method. 1042 1043 The angle between the location and reference object. 1044 Being 0 a location in front and 180, one behind, i.e, the vector between has to satisfy: 1045 low_angle_th < angle < up_angle_th. 1046 """ 1047 1048 walkers_lane_change: Tuple[float, float] = (0.0, 90.0) 1049 """Detection angle of walkers when staying in the same lane""" 1050 1051 walkers_same_lane: Tuple[float, float] = (0.0, 60.0) 1052 """Detection angle of walkers when changing lanes""" 1053 1054 cars_lane_change: Tuple[float, float] = (0.0, 180.0) 1055 """Detection angle of cars when staying in the same lane""" 1056 1057 cars_same_lane: Tuple[float, float] = (0.0, 30.0) 1058 """Detection angle of cars when changing lanes""" 1059 1060 1061@dataclass 1062class BasicAgentObstacleSettings(AgentConfig): 1063 """ 1064 -------------------------- 1065 Agent Level 1066 see :py:meth:`_affected_by_traffic_light` and :py:meth:`_affected_by_vehicle` 1067 in :py:mod:`basic_agent.py <agents.navigation.basic_agent>` 1068 -------------------------- 1069 Agents is aware of the vehicles and traffic lights within its distance parameters 1070 optionally can always ignore them. 1071 """ 1072 1073 ignore_vehicles: bool = False 1074 """Whether the agent should ignore vehicles""" 1075 1076 ignore_traffic_lights: bool = False 1077 """Whether the agent should ignore traffic lights""" 1078 1079 ignore_stop_signs: bool = MISSING 1080 """ 1081 Whether the agent should ignore stop signs 1082 1083 Attention: 1084 No usage implemented yet. 1085 1086 Idea: 1087 Nearby landmarks from waypoints need to be retrieved 1088 and checked for stop signs. 1089 """ 1090 1091 use_bbs_detection: bool = True 1092 """ 1093 True: Whether to use a general approach to detect vehicles invading other lanes due to the offset. 1094 1095 False: Simplified approach, using only the plan waypoints (similar to TM) 1096 1097 See `BasicAgent._vehicle_obstacle_detected` 1098 """ 1099 1100 detect_yellow_tlights: bool = True 1101 """ 1102 If the the agent will treat a yellow light like a red light. If False will not detect them. 1103 1104 Rules must decide how to handle yellow lights. 1105 """ 1106 1107 base_tlight_threshold: float = 2.0 1108 """ 1109 Base distance to traffic lights to check if they affect the vehicle 1110 1111 Usage: max_vehicle_distance = base_vehicle_threshold + detection_speed_ratio * vehicle_speed 1112 Usage: max_tlight_distance = base_tlight_threshold + detection_speed_ratio * vehicle_speed 1113 """ 1114 1115 base_vehicle_threshold: float = 4.0 1116 """ 1117 Base distance to vehicles to check if they affect the vehicle 1118 1119 Usage: 1120 Only vehicles with distance < `nearby_vehicles_max_distance` are checked for 1121 ```python 1122 max_vehicle_distance = base_vehicle_threshold 1123 if dynamic_threshold: 1124 max_vehicle_distance += detection_speed_ratio * vehicle_speed 1125 ``` 1126 1127 A vehicle is considered if distance < max_vehicle_distance < nearby_vehicles_max_distance 1128 """ 1129 1130 detection_speed_ratio: float = 0.1 1131 """ 1132 Increases detection range based on speed 1133 1134 Usage: max_vehicle_distance = base_vehicle_threshold + detection_speed_ratio * vehicle_speed 1135 Usage: max_tlight_distance = base_tlight_threshold + detection_speed_ratio * vehicle_speed 1136 """ 1137 1138 dynamic_threshold: bool = True 1139 """ 1140 Whether to add a dynamic threshold based on the vehicle speed to the base threshold. 1141 1142 Usage: base_threshold + detection_speed_ratio * vehicle_speed 1143 1144 + NOTE: Currently only applied to traffic lights 1145 1146 + NOTE: Part of the agent overhaul 1147 """ 1148 1149 detection_angles: BasicAgentObstacleDetectionAngles = field(default_factory=BasicAgentObstacleDetectionAngles) 1150 """Defines detection angles used when checking for obstacles.""" 1151 1152 1153@dataclass 1154class BehaviorAgentObstacleSettings(BasicAgentObstacleSettings): 1155 nearby_vehicles_max_distance: float = 45 1156 """ 1157 For performance filters out vehicles that are further away than this distance in meters 1158 1159 Info: 1160 These vehicles are stored in `vehicles_nearby`. 1161 """ 1162 1163 nearby_walkers_max_distance: float = 10 1164 """ 1165 For performance filters out pedestrians that are further away than this distance in meters 1166 1167 Info: 1168 These pedestrians are stored in `walkers_nearby`. 1169 """ 1170 1171 min_proximity_threshold: float = 10 1172 """ 1173 When making lane changes determines the minimum distance to check for vehicles. 1174 1175 max_distance_check = max(obstacles.min_proximity_threshold, 1176 live_info.current_speed_limit / speed_detection_downscale) 1177 1178 Hint: 1179 Lower values mean that further away vehicles are maybe not considered, 1180 an agent might ignore fast vehicles coming from behind in the other lane, 1181 or ignores slower vehicles in front of it in the other lane. 1182 """ 1183 1184 # Python 3.7 compatibility allows no nesting here 1185 @config_path("agent/obstacles/speed_detection_downscale") 1186 @dataclass 1187 class SpeedLimitDetectionDownscale(AgentConfig): 1188 """see `speed_detection_downscale`""" 1189 1190 same_lane: float = 3.0 1191 other_lane: float = 2.0 1192 overtaking: float = 2.5 1193 """ 1194 Used by SimpleOvertakeRule, look further ahead for overtaking 1195 """ 1196 1197 tailgating: float = 2 1198 """ 1199 Used by AvoidTailgatorRule, look further behind for tailgators 1200 """ 1201 1202 speed_detection_downscale: SpeedLimitDetectionDownscale = field(default_factory=SpeedLimitDetectionDownscale) 1203 """ 1204 When making lane changes determines the maximum distance to check for vehicles. 1205 1206 max_distance_check = max(obstacles.min_proximity_threshold, 1207 live_info.current_speed_limit / speed_detection_downscale.[same|other]_lane) 1208 1209 Hint: 1210 Higher values mean that further away vehicles are not considered, 1211 an agent might ignore fast vehicles coming from behind in the other lane, 1212 or ignores slower vehicles in front of it in the other lane. 1213 """ 1214 1215 1216@dataclass 1217class AutopilotObstacleSettings(AgentConfig): 1218 ignore_lights_percentage: float = 0.0 1219 """ 1220 Percentage of time to ignore traffic lights 1221 """ 1222 1223 ignore_signs_percentage: float = 0.0 1224 """ 1225 Percentage of time to ignore stop signs 1226 """ 1227 1228 ignore_walkers_percentage: float = 0.0 1229 """ 1230 Percentage of time to ignore pedestrians 1231 """ 1232 1233 1234@config_path("agent/obstacles/detection_angles") 1235@dataclass 1236class LunaticAgentObstacleDetectionAngles(BasicAgentObstacleDetectionAngles): 1237 """ 1238 Detection Angles for the BasicAgent used in the `BasicAgent._vehicle_obstacle_detected` method. 1239 1240 The angle between the location and reference object. 1241 Being 0 a location in front and 180, one behind, i.e, the vector between has to satisfy: 1242 low_angle_th < angle < up_angle_th. 1243 1244 Note: 1245 These settings are NotImplemented 1246 """ 1247 1248 # -------------------------- 1249 # Note: Unused and deprecated 1250 # -------------------------- 1251 1252 when_turning: Tuple[float, float] = MISSING 1253 """Idea: When the agent is turning it might needs a wider angle to detect vehicles""" 1254 1255 1256@config_path("agent/obstacles") 1257@dataclass 1258class LunaticAgentObstacleSettings(AutopilotObstacleSettings, BehaviorAgentObstacleSettings): 1259 dynamic_threshold: bool = True 1260 """ 1261 Whether to add a dynamic threshold based on the vehicle speed to the base threshold. 1262 1263 Usage: base_threshold + detection_speed_ratio * vehicle_speed 1264 1265 #NOTE: Currently only applied to traffic lights 1266 """ 1267 1268 detection_angles: LunaticAgentObstacleDetectionAngles = field(default_factory=LunaticAgentObstacleDetectionAngles) # pyright: ignore[ reportIncompatibleVariableOverride] 1269 1270 nearby_statics_max_distance: float = 150 1271 """For performance filters out statics that are further away than this distance in meters""" 1272 1273 base_static_threshold: float = 2.0 1274 """ 1275 Base distance to vehicles to check if they affect the vehicle 1276 1277 Usage: 1278 static_detection_speed_ratio = base_static_threshold + static_detection_speed_ratio * vehicle_speed 1279 """ 1280 1281 static_detection_speed_ratio: float = 0.5 1282 """ 1283 Usage: 1284 static_detection_speed_ratio = base_static_threshold + static_detection_speed_ratio * vehicle_speed 1285 """ 1286 1287 nearby_tlights_max_distance: float = II("look_ahead_time:${live_info.current_speed_limit}, 5.0, 10.0") 1288 """ 1289 For performance filters out traffic lights that are further away than this distance in meters. 1290 1291 By default checks converts the current speed to a distance of 5 seconds and adds 10 meters. 1292 """ 1293 1294 1295# --------------------- 1296# ControllerSettings 1297# --------------------- 1298 1299 1300@dataclass 1301class BasicAgentControllerSettings(AgentConfig): 1302 """Limitations of the controls used one the PIDController Level""" 1303 1304 max_brake: float = 0.5 1305 """ 1306 Vehicle control how strong the brake is used, 1307 1308 NOTE: Also used in emergency stop 1309 """ 1310 max_throttle: float = 0.75 1311 """maximum throttle applied to the vehicle""" 1312 max_steering: float = 0.8 1313 """maximum steering applied to the vehicle""" 1314 1315 # Aliases used: 1316 @property 1317 def max_throt(self): 1318 return self.max_throttle 1319 1320 @property 1321 def max_steer(self): 1322 return self.max_steering 1323 1324 1325@dataclass 1326class BehaviorAgentControllerSettings(BasicAgentControllerSettings): 1327 pass 1328 1329 1330@dataclass 1331class AutopilotControllerSettings(AgentConfig): 1332 vehicle_lane_offset: float = 0 1333 """ 1334 Sets a lane offset displacement from the center line. Positive values imply a right offset while negative ones mean a left one. 1335 Default is 0. Numbers high enough to cause the vehicle to drive through other lanes might break the controller. 1336 """ 1337 1338 1339@config_path("agent/controls") 1340@dataclass 1341class LunaticAgentControllerSettings(AutopilotControllerSettings, BehaviorAgentControllerSettings): 1342 pass 1343 1344 1345# --------------------- 1346# PlannerSettings 1347# --------------------- 1348 1349 1350@dataclass 1351class PIDControllerDict(AgentConfig): 1352 """ 1353 PID controller using the following semantics: 1354 K_P -- Proportional term 1355 K_D -- Differential term 1356 K_I -- Integral term 1357 dt -- time differential in seconds 1358 """ 1359 1360 K_P: float = MISSING 1361 K_D: float = MISSING 1362 K_I: float = 0.05 1363 dt: float = 1.0 / 20.0 1364 """time differential in seconds""" 1365 1366 1367@dataclass 1368class BasicAgentPlannerSettings(AgentConfig): 1369 """ 1370 PID controller using the following semantics: 1371 K_P -- Proportional term 1372 K_D -- Differential term 1373 K_I -- Integral term 1374 dt -- time differential in seconds 1375 offset: If different than zero, the vehicle will drive displaced from the center line. 1376 Positive values imply a right offset while negative ones mean a left one. 1377 Numbers high enough to cause the vehicle to drive through other lanes might break the controller. 1378 1379 Notes: 1380 `sampling_resolution` is used by the global planner to build a graph of road segments, also to get a path of waypoints from A to B 1381 1382 `sampling_radius` is similar but only used only by the local_planner to compute the next waypoints forward. The distance of those is the sampling_radius. 1383 1384 """ 1385 1386 dt: float = 1.0 / 20.0 1387 """time differential in seconds""" 1388 1389 # NOTE: two variables because originally used with two different names in different places 1390 # lateral_control_dict : PIDControllerDict = field(default_factory=partial(PIDControllerDict, **{'K_P': 1.95, 'K_I': 0.05, 'K_D': 0.2})) 1391 lateral_control_dict: PIDControllerDict = field( 1392 default_factory=lambda: PIDControllerDict(**{"K_P": 1.95, "K_I": 0.05, "K_D": 0.2}) 1393 ) 1394 """values of the lateral PID controller""" 1395 1396 # NOTE: two variables because originally used with two different names in different places 1397 longitudinal_control_dict: PIDControllerDict = field( 1398 default_factory=lambda: PIDControllerDict(**{"K_P": 1.0, "K_I": 0.05, "K_D": 0}) 1399 ) 1400 """values of the longitudinal PID controller""" 1401 1402 offset: float = 0.0 1403 """ 1404 If different than zero, the vehicle will drive displaced from the center line. 1405 1406 Positive values imply a right offset while negative ones mean a left one. Numbers high enough 1407 to cause the vehicle to drive through other lanes might break the controller. 1408 """ 1409 1410 sampling_radius: float = 2.0 1411 """ 1412 Distance between waypoints when planning a path in `local_planner._compute_next_waypoints` 1413 1414 Used with Waypoint.next(sampling_radius) 1415 """ 1416 1417 sampling_resolution: float = 2.0 1418 """ 1419 Distance between waypoints in `BasicAgent._generate_lane_change_path` 1420 Furthermore in the GlobalRoutePlanner to build the topology and for path planning. 1421 1422 Used with the Waypoint.next(sampling_radius) and distance between waypoints. 1423 """ 1424 1425 min_distance_next_waypoint: float = 3.0 1426 """ 1427 Removes waypoints from the queue that are too close to the vehicle. 1428 1429 Usage: min_distance = min_distance_next_waypoint + next_waypoint_distance_ratio * vehicle_speed 1430 """ 1431 1432 next_waypoint_distance_ratio: float = 0.5 1433 """Increases the minimum distance to the next waypoint based on the vehicles speed.""" 1434 1435 # Alias 1436 @property 1437 def step_distance(self): 1438 return self.sampling_resolution 1439 1440 1441@dataclass 1442class BehaviorAgentPlannerSettings(BasicAgentPlannerSettings): 1443 sampling_resolution: float = 4.5 1444 """ 1445 Distance between waypoints in `BasicAgent._generate_lane_change_path` 1446 and GlobalRoutePlanner to build the topology and path planning. 1447 1448 Used with the Waypoint.next(sampling_radius) 1449 """ 1450 1451 1452@config_path("agent/planner") 1453@dataclass 1454class LunaticAgentPlannerSettings(BehaviorAgentPlannerSettings): 1455 dt: float = MISSING # 1.0 / 20.0 # Note: Set this from main script and do not assume it. 1456 """ 1457 time differential in seconds 1458 1459 NOTE: Should set from main script. 1460 """ 1461 1462 # NOTE: two variables because originally used with two different names in different places 1463 lateral_control_dict: PIDControllerDict = field( 1464 default_factory=partial(PIDControllerDict, **{"K_P": 1.95, "K_I": 0.05, "K_D": 0.2, "dt": II("${..dt}")}) 1465 ) 1466 """values of the lateral PID controller""" 1467 1468 # NOTE: two variables because originally used with two different names in different places 1469 longitudinal_control_dict: PIDControllerDict = field( 1470 default_factory=partial(PIDControllerDict, **{"K_P": 1.0, "K_I": 0.05, "K_D": 0, "dt": II("${..dt}")}) 1471 ) 1472 """values of the longitudinal PID controller""" 1473 1474 offset: float = II("controls.vehicle_lane_offset") 1475 """ 1476 If different than zero, the vehicle will drive displaced from the center line. 1477 1478 Positive values imply a right offset while negative ones mean a left one. Numbers high enough 1479 to cause the vehicle to drive through other lanes might break the controller. 1480 """ 1481 1482 1483# --------------------- 1484# Emergency 1485# --------------------- 1486 1487 1488@dataclass 1489class BasicAgentEmergencySettings(AgentConfig): 1490 throttle: float = 0.0 1491 max_emergency_brake: float = II("controls.max_brake") 1492 hand_brake: bool = False 1493 1494 1495@dataclass 1496class BehaviorAgentEmergencySettings(BasicAgentEmergencySettings): 1497 pass 1498 1499 1500@config_path("agent/emergency") 1501@dataclass 1502class LunaticAgentEmergencySettings(BehaviorAgentEmergencySettings): 1503 ignore_percentage: float = 0.0 1504 """Percentage of time to ignore an emergency situation and proceed as normal""" 1505 1506 hand_brake_modify_chance: float = 0.0 1507 """Chance to choose the opposite of hand_break""" 1508 1509 do_random_steering: bool = False # TODO: Should be evasive steering 1510 """Whether to do random steering""" 1511 1512 random_steering_range: Tuple[float, float] = (-0.25, 0.25) 1513 """Range of random steering that is applied""" 1514 1515 1516# --------------------- 1517# RSS 1518# --------------------- 1519 1520 1521@config_path("agent/rss") 1522@dataclass 1523class RssSettings(AgentConfig): 1524 enabled: bool = True 1525 """ 1526 Use the RSS sensor. 1527 1528 NOTE: Initializing with False and changing it to True is not supported. 1529 If RSS is not available (no ad-rss library) this will be set to False. 1530 """ 1531 1532 if AD_RSS_AVAILABLE: 1533 use_stay_on_road_feature: carla.RssRoadBoundariesMode = carla.RssRoadBoundariesMode.On # pyright: ignore[reportRedeclaration] 1534 """Use the RssRoadBoundariesMode. NOTE: A call to :py:meth:`.rss_set_road_boundaries_mode` is necessary""" 1535 1536 log_level: carla.RssLogLevel = carla.RssLogLevel.err # pyright: ignore[reportRedeclaration] 1537 """Set the initial log level of the RSSSensor""" 1538 else: 1539 enabled = False 1540 1541 use_stay_on_road_feature: RssRoadBoundariesModeAlias = RssRoadBoundariesMode.On # pyright: ignore[reportRedeclaration] 1542 """Use the RssRoadBoundariesMode. NOTE: A call to :py:meth:`.rss_set_road_boundaries_mode` is necessary""" 1543 1544 log_level: RssLogLevelAlias = RssLogLevel.err # pyright: ignore[reportRedeclaration] 1545 """Set the initial log level of the RSSSensor""" 1546 1547 debug_visualization_mode: RssDebugVisualizationMode = RssDebugVisualizationMode.RouteOnly 1548 """Sets the visualization mode that should be rendered on the screen.""" 1549 1550 always_accept_update: bool = False 1551 """Setting for the default rule to always accept RSS updates if they are valid""" 1552 1553 rss_max_speed: float = MISSING # NotImplemented 1554 """For fast vehicles RSS currently is unreliable, disables rss updates when the vehicle is faster than this.""" 1555 1556 # ------ 1557 1558 def _clean_options(self) -> None: 1559 if AD_RSS_AVAILABLE: 1560 if not isinstance(self.log_level, RssLogLevelAlias): # pyright: ignore[reportUnnecessaryIsInstance] 1561 if isinstance(self.log_level, str): 1562 self.log_level = getattr(carla.RssLogLevel, self.log_level) 1563 else: 1564 self.log_level = carla.RssLogLevel.values[self.log_level] 1565 if not isinstance(self.use_stay_on_road_feature, RssRoadBoundariesModeAlias): # pyright: ignore[reportUnnecessaryIsInstance] 1566 if isinstance(self.use_stay_on_road_feature, str): 1567 self.use_stay_on_road_feature = getattr(carla.RssRoadBoundariesMode, self.use_stay_on_road_feature) 1568 else: 1569 self.use_stay_on_road_feature = carla.RssRoadBoundariesMode.values[ 1570 bool(self.use_stay_on_road_feature) 1571 ] 1572 else: 1573 if not isinstance(self.use_stay_on_road_feature, RssRoadBoundariesModeAlias): # pyright: ignore[reportUnnecessaryIsInstance] 1574 if isinstance(self.use_stay_on_road_feature, str): 1575 self.use_stay_on_road_feature = RssRoadBoundariesMode[self.use_stay_on_road_feature] 1576 else: 1577 self.use_stay_on_road_feature = RssRoadBoundariesMode(bool(self.use_stay_on_road_feature)) 1578 # other not accessed 1579 if not isinstance(self.log_level, RssLogLevelAlias): # pyright: ignore[reportUnnecessaryIsInstance] 1580 if isinstance(self.log_level, str): 1581 self.log_level = RssLogLevel[self.log_level] 1582 else: 1583 self.log_level = RssLogLevel(self.log_level) 1584 1585 1586@config_path("agent/detection_matrix") 1587@dataclass 1588class DetectionMatrixSettings(AgentConfig): 1589 enabled: bool = True 1590 """Activate or deactivate the detection matrix""" 1591 1592 sync: bool = True 1593 """ 1594 When the world uses synchronous mode and sync is true, the detection matrix will be updated every sync_interval ticks. 1595 A low value will have a negative impact on the fps. 1596 If the world uses asynchronous mode or sync is False the detection matrix will be updated by a different thread. 1597 This increases the fps but updates will be less frequent. 1598 """ 1599 1600 sync_interval: int = 5 1601 """ 1602 The interval in frames after which the detection matrix should be updated. Sync must be true. 1603 """ 1604 1605 hud: Never = MISSING 1606 """ 1607 TODO: Do not have this in Agent config; instead use an interpolation 1608 hud : ${camera.hud.detection_matrix} 1609 However, non-trivial cannot interpolate to LaunchConfig. 1610 #drawing_options -> see camera.yaml; need a singleton LaunchConfig and resolve to it. 1611 1612 --- 1613 1614 Keyword arguments for `DetectionMatrix.render`. 1615 1616 Warning: 1617 Not implemented yet. Use `camera.hud.detection_matrix` instead. 1618 1619 :meta exclude: 1620 """ 1621 1622 1623# --------------------- 1624# Rules 1625# --------------------- 1626 1627
[docs] 1628@dataclass 1629class RuleConfig(DictConfigLike if TYPE_CHECKING else object): 1630 """Subconfig for rules; can have arbitrary keys""" 1631 1632 instance: object = MISSING # pyright: ignore[reportRedeclaration, reportAssignmentType] 1633 """The instance of the rule, can be accessed by :python:`ctx.current_rule.instance`""" 1634 1635 if TYPE_CHECKING: # Cannot import Rule but need a type for OmegaConf 1636 instance: Rule = MISSING 1637 """The instance of the rule, can be accessed by :python:`ctx.current_rule.instance`"""
1638 1639
[docs] 1640@dataclass 1641class CallFunctionFromConfig(DictConfigLike if TYPE_CHECKING else object): 1642 _target_: str 1643 """ 1644 The name of the function to call for generating one or more rules 1645 hydra.utils.instantiate function. 1646 1647 Note: 1648 The function must be imported in agents.rules.__init__ or 1649 `_target_` must be a dotted path to the function (see hydra docs). 1650 1651 See Also: 1652 https://hydra.cc/docs/advanced/instantiate_objects/overview/ 1653 1654 """ 1655 1656 _args_: List[Any] = field(default_factory=list) 1657 """Positional arguments to pass to the Rule or Function""" 1658 1659 random_lane_change: bool = False 1660 """For :py:func:`.create_default_rules`; Should the :py:class:`.RandomLaneChangeRule` be added"""
1661 1662
[docs] 1663@dataclass 1664class CreateRuleFromConfig(DictConfigLike if TYPE_CHECKING else object): 1665 r""" 1666 Keywords to instantiate Rule classes 1667 1668 :external-icon-parse:`:py:attr:\`omegaconf.MISSING\`` (alias for :python:`'???'`) attributes will not be passed to a :py:class:`.Rule`'s :py:meth:`~.classes.rule.Rule.__init__` method. 1669 """ 1670 1671 _target_: str 1672 """ 1673 The name of the rule class to instantiate to be used with the 1674 hydra.utils.instantiate function. 1675 1676 - overwrite_settings: These will be used to overwrite the settings of the agent. 1677 - self_config: This is a private storage for this rule instance to be used with 1678 its own condition and action functions. 1679 1680 Note: 1681 The class must be imported in agents.rules.__init__ or 1682 `_target_` must be a dotted path to the class (see hydra docs). 1683 1684 See Also: 1685 https://hydra.cc/docs/advanced/instantiate_objects/overview/ 1686 :py:class:`.Rule` for more information on the parameters. 1687 1688 """ 1689 1690 _args_: Optional[List[Any]] = MISSING 1691 """Positional arguments to pass to the Rule or Function""" 1692 1693 phases: Optional[Union[str, Phase]] = MISSING 1694 # /, # phases must be positional; python3.8+ only 1695 condition: Optional[str] = MISSING 1696 action: Optional[str] = MISSING 1697 false_action: Optional[str] = MISSING 1698 actions: Optional[Dict[Any, str]] = MISSING 1699 description: str = MISSING 1700 overwrite_settings: Optional[Dict[str, Any]] = MISSING 1701 """These will be used to overwrite the settings of the agent.""" 1702 1703 self_config: DictConfigAlias = MISSING 1704 """ 1705 This is a private storage for this rule instance to be used with 1706 its own condition and action functions. 1707 1708 Interpolation to agent, or rather, context keys is possible. 1709 1710 Note: 1711 - Also has an **instance** key which is the instance of the rule. 1712 - You can access config rule also with :python:`ctx.current_rule` 1713 """ 1714 if READTHEDOCS and not TYPE_CHECKING: 1715 self_config: NestedConfigDict = MISSING # lets not introduce a new variable 1716 1717 priority: RulePriority = MISSING 1718 cooldown_reset_value: Optional[int] = MISSING 1719 group: Optional[str] = MISSING 1720 enabled: bool = MISSING 1721 1722 if READTHEDOCS or TYPE_CHECKING: 1723 rules: "List[CreateRuleFromConfig]" = MISSING 1724 else: 1725 rules: list = MISSING # List[CreateRuleFromConfig] # Cannot use this forward ref with omegaconf 1726 1727 execute_all_rules: bool = MISSING 1728 1729 weights: Optional[Dict[str, float]] = MISSING 1730 repeat_if_not_applicable: bool = MISSING 1731 ignore_phase: bool = MISSING 1732 1733 MAX_TICKS: Optional[int] = MISSING 1734 max_tick_callback: Optional[str] = MISSING 1735 1736 gameframework: None = MISSING # pyright: ignore[reportRedeclaration] 1737 """Needed explicitly for :py:class:`BlockingRules` once. Depending on setup can be omitted""" 1738 1739 if READTHEDOCS or TYPE_CHECKING: 1740 gameframework: Optional[GameFramework] = MISSING 1741 1742 def __post_init__(self): 1743 if isinstance(self.phases, str): 1744 if self.phases == MISSING: 1745 return 1746 phase_strings = self.phases.strip().split(",") 1747 assert len(phase_strings) == 1, "Only one phase is allowed currently" 1748 phases = [Phase.from_string(phase_string) for phase_string in phase_strings] 1749 self.phases = phases[0] 1750 for key in self.__class__.__annotations__: 1751 if getattr(self, key) == MISSING: 1752 delattr(self, key)
1753 1754 1755RuleCreatingParameters: TypeAlias = Union[CreateRuleFromConfig, CallFunctionFromConfig, DictConfig] 1756""" 1757Alias of types that are valid for :py:func:`hydra.instantiate` 1758to create :py:class:`Rule | list[Rule] <Rule>`. 1759""" 1760 1761 1762def _from_config_default_rules(): 1763 """ 1764 Factory function to create a list of rule creating parameters. 1765 1766 Rules can be added by using 1767 - CreateRuleFromConfig(_target_="RuleName", **kwargs) 1768 - CallFunctionFromConfig(_target_="function_name", _args_=[*positional_arguments]) 1769 1770 Note: 1771 FunctionArguments does not support keyword arguments 1772 1773 See Also: 1774 - https://hydra.cc/docs/advanced/instantiate_objects/overview/ 1775 """ 1776 rules = [ 1777 # Rules cann be added from 1778 CallFunctionFromConfig("create_default_rules", random_lane_change=False), 1779 CreateRuleFromConfig( 1780 "DriveSlowTowardsTrafficLight", 1781 gameframework=None, 1782 # NOTE: Dot notation is NOT SUPPORTED you need to nest dictionaries 1783 overwrite_settings={"speed": {"follow_speed_limits": True}}, 1784 description="Drive slow towards while trying not to cross the line (experimental).", 1785 ), 1786 CreateRuleFromConfig( 1787 "PassYellowTrafficLightRule", 1788 self_config={ 1789 "try_to_pass": True, 1790 "passing_speed": II("max:${mul:${live_info.current_speed_limit},1.33},${speed.target_speed}"), 1791 }, 1792 description="Speed up to pass a yellow traffic light.", 1793 ), 1794 # CreateRuleFromConfig("RandomLaneChangeRule", 1795 # # NOTE: Dot notation is NOT SUPPORTED you need to nest dictionaries 1796 # overwrite_settings={"lane_change" : {"same_lane_time" : 0}}, 1797 # ) 1798 ] 1799 return rules 1800 1801 1802# --------------------- 1803# Final Settings 1804# --------------------- 1805 1806 1807@dataclass 1808class AutopilotBehavior(AgentConfig): 1809 """ 1810 These are settings from the autopilot :py:class:`carla.TrafficManager` 1811 which are not exposed or not used by the original carla agents. 1812 1813 In this class they are collected in a flat structure. 1814 1815 Note: 1816 That default values do not exist for most settings; 1817 and should be to something reasonable. 1818 1819 :meta private: 1820 """ 1821 1822 auto_lane_change: bool = True 1823 """Turns on or off lane changing behavior for a vehicle.""" 1824 1825 vehicle_lane_offset: float = 0 1826 """ 1827 Sets a lane offset displacement from the center line. 1828 1829 Positive values imply a right offset while negative ones mean a left one. 1830 Default is 0. 1831 1832 NOTE: Numbers high enough to cause the vehicle to drive through other lanes might break the controller. 1833 """ 1834 1835 random_left_lanechange_percentage: float = 0.1 1836 """ 1837 Adjust probability that in each timestep the actor will perform a left/right lane change, 1838 dependent on lane change availability. 1839 """ 1840 random_right_lanechange_percentage: float = 0.1 1841 """ 1842 Adjust probability that in each timestep the actor will perform a left/right lane change, 1843 dependent on lane change availability. 1844 """ 1845 1846 keep_right_rule_percentage: float = 0.7 1847 """ 1848 During the localization stage, this method sets a percent chance that vehicle will follow the keep right rule, 1849 and stay in the right lane. 1850 """ 1851 1852 distance_to_leading_vehicle: float = 5.0 1853 """ 1854 Sets the minimum distance in meters that a vehicle has to keep with the others. 1855 The distance is in meters and will affect the minimum moving distance. It is computed from front to back of the vehicle objects. 1856 """ 1857 1858 vehicle_percentage_speed_difference: float = 30 # in percent 1859 """ 1860 Sets the difference the vehicle's intended speed and its current speed limit. 1861 Speed limits can be exceeded by setting the percentage to a negative value. 1862 Exceeding a speed limit can be done using negative percentages. 1863 1864 Default is 30. 1865 1866 Note: 1867 Unit is in percent. 1868 """ 1869 1870 ignore_lights_percentage: float = 0.0 1871 ignore_signs_percentage: float = 0.0 1872 ignore_walkers_percentage: float = 0.0 1873 1874 update_vehicle_lights: bool = False 1875 """Sets if the Traffic Manager is responsible of updating the vehicle lights, or not.""" 1876 1877
[docs] 1878@dataclass 1879class BasicAgentSettings(AgentConfig): 1880 """ 1881 Settings used by the :py:class:`BasicAgent` provided with CARLA. 1882 """ 1883 1884 overwrites: Optional[OverwriteDictTypes] = field(default_factory=dict, repr=False) 1885 live_info: LiveInfo = field(default_factory=LiveInfo, init=False) 1886 speed: BasicAgentSpeedSettings = field(default_factory=BasicAgentSpeedSettings, init=False) 1887 distance: BasicAgentDistanceSettings = field(default_factory=BasicAgentDistanceSettings, init=False) 1888 lane_change: BasicAgentLaneChangeSettings = field(default_factory=BasicAgentLaneChangeSettings, init=False) 1889 obstacles: BasicAgentObstacleSettings = field(default_factory=BasicAgentObstacleSettings, init=False) 1890 controls: BasicAgentControllerSettings = field(default_factory=BasicAgentControllerSettings, init=False) 1891 planner: BasicAgentPlannerSettings = field(default_factory=BasicAgentPlannerSettings, init=False) 1892 emergency: BasicAgentEmergencySettings = field(default_factory=BasicAgentEmergencySettings, init=False)
1893 1894
[docs] 1895@dataclass 1896class BehaviorAgentSettings(AgentConfig): 1897 """ 1898 Settings used by the :py:class:`BehaviorAgent` provided with CARLA. 1899 """ 1900 1901 overwrites: Optional[OverwriteDictTypes] = field(default_factory=dict, repr=False) 1902 live_info: LiveInfo = field(default_factory=LiveInfo, init=False) 1903 speed: BehaviorAgentSpeedSettings = field(default_factory=BehaviorAgentSpeedSettings, init=False) 1904 distance: BehaviorAgentDistanceSettings = field(default_factory=BehaviorAgentDistanceSettings, init=False) 1905 lane_change: BehaviorAgentLaneChangeSettings = field(default_factory=BehaviorAgentLaneChangeSettings, init=False) 1906 obstacles: BehaviorAgentObstacleSettings = field(default_factory=BehaviorAgentObstacleSettings, init=False) 1907 controls: BehaviorAgentControllerSettings = field(default_factory=BehaviorAgentControllerSettings, init=False) 1908 planner: BehaviorAgentPlannerSettings = field(default_factory=BehaviorAgentPlannerSettings, init=False) 1909 emergency: BehaviorAgentEmergencySettings = field(default_factory=BehaviorAgentEmergencySettings, init=False) 1910 avoid_tailgators: bool = True
1911 1912
[docs] 1913@config_path("agent") 1914@dataclass 1915class LunaticAgentSettings(AgentConfig): 1916 """ 1917 .. @package agent 1918 1919 Config schema definition for the :py:class:`.LunaticAgent` class 1920 """ 1921 1922 overwrites: Optional[OverwriteDictTypes] = field(default_factory=dict, repr=False) 1923 """Nested dictionaries used for the manual initialization of the config.""" 1924 1925 live_info: LiveInfo = field(default_factory=LiveInfo, init=False) 1926 """.. <take doc|LiveInfo>""" 1927 speed: LunaticAgentSpeedSettings = field(default_factory=LunaticAgentSpeedSettings, init=False) 1928 """.. <take doc|LunaticAgentSpeedSettings>""" 1929 distance: LunaticAgentDistanceSettings = field(default_factory=LunaticAgentDistanceSettings, init=False) 1930 """.. <take doc|LunaticAgentDistanceSettings>""" 1931 lane_change: LunaticAgentLaneChangeSettings = field(default_factory=LunaticAgentLaneChangeSettings, init=False) 1932 """.. <take doc|LunaticAgentLaneChangeSettings>""" 1933 obstacles: LunaticAgentObstacleSettings = field(default_factory=LunaticAgentObstacleSettings, init=False) 1934 """.. <take doc|LunaticAgentObstacleSettings>""" 1935 controls: LunaticAgentControllerSettings = field(default_factory=LunaticAgentControllerSettings, init=False) 1936 """.. <take doc|LunaticAgentControllerSettings>""" 1937 planner: LunaticAgentPlannerSettings = field(default_factory=LunaticAgentPlannerSettings, init=False) 1938 """.. <take doc|LunaticAgentPlannerSettings>""" 1939 emergency: LunaticAgentEmergencySettings = field(default_factory=LunaticAgentEmergencySettings, init=False) 1940 """.. <take doc|LunaticAgentEmergencySettings>""" 1941 1942 # Can be set to False/None to disable 1943 rss: RssSettings = field(default_factory=RssSettings, init=False, metadata={"can_be_false": True}) 1944 """.. <take doc|RssSettings>""" 1945 detection_matrix: DetectionMatrixSettings = field( 1946 default_factory=DetectionMatrixSettings, init=False, metadata={"can_be_false": True} 1947 ) 1948 """.. <take doc|DetectionMatrixSettings>""" 1949 1950 # TODO: This attribute can be removed after the rules have been initialized for performance 1951 rules: list = field(default_factory=_from_config_default_rules) # pyright: ignore[reportRedeclaration,reportMissingTypeArgument,reportArgumentType] 1952 """ 1953 A list of Rule parameters that allow the instantiation of Rules, 1954 with the Hydra instantiate feature. 1955 1956 See Also: 1957 - :py:class:`CreateRuleFromConfig` 1958 - :py:class:`CallFunctionFromConfig` 1959 - :py:func:`_from_config_default_rules` : Creates the default rules in the YAML file 1960 """ 1961 1962 if READTHEDOCS or TYPE_CHECKING: 1963 # variant needs to be first. 1964 rules: "list[RuleCreatingParameters]" = field(default_factory=_from_config_default_rules) # pyright: ignore[reportArgumentType] 1965 1966 # ---- Special Attributes for Context and Rule overwrites ---- 1967 # These attributes are not usable by the agent 1968 1969 current_rule: Never = field(default=II("self"), init=False) 1970 """ 1971 Special settings of the current rule. Only available from Context within rules ctx.config.current_rule 1972 1973 :meta private: 1974 """ 1975 1976 self: Never = field(default=MISSING, init=False) 1977 """ 1978 Special settings of the current rule. Only available from Context within rules ctx.config.current_rule 1979 1980 :meta private: 1981 """
1982 1983
[docs] 1984@dataclass 1985class ContextSettings(LunaticAgentSettings): 1986 """ 1987 Config class for the :py:class:`.Context` object. 1988 1989 Extends the :py:class:`LunaticAgentSettings` by the **current_rule** attribute 1990 to accesses the :py:attr:`.Rule.self_config` attribute from the context. 1991 """ 1992 1993 current_rule: RuleConfig = field(default=II("self"), init=False) # pyright: ignore[reportIncompatibleVariableOverride] 1994 """ 1995 Special settings of the current rule. Only available from :py:class:`.Context` within rules :code:`ctx.config.current_rule` 1996 1997 Note: 1998 Internally :py:attr:`.Context.config.current_rule` and :py:attr:`.Rule.self_config` 1999 are the same object. 2000 2001 See Also: 2002 - :py:attr:`.RuleConfig.self_config` 2003 """ 2004 2005 self: RuleConfig = field(default=MISSING, init=False) # pyright: ignore[reportIncompatibleVariableOverride] 2006 """ 2007 Describes the :py:attr:`.Rule.self_config` attribute of the current rule, 2008 which is the same as the :py:attr:`.Context.config.current_rule` attribute. 2009 Rules should use the **self** key to access these settings. 2010 2011 :meta private: 2012 """
2013 2014 2015# --------------------- 2016# Launch Settings 2017# --------------------- 2018 2019
[docs] 2020@config_path("camera") 2021@dataclass 2022class CameraConfig(AgentConfig): 2023 """ 2024 .. @package camera 2025 """ 2026 2027 # Use from launch_config 2028 width: int = II("width") 2029 """With pygame window. Takes the value from the :py:class:`LaunchConfig`.""" 2030 height: int = II("height") 2031 """Height of pygame window. Takes the value from the :py:class:`LaunchConfig`.""" 2032 gamma: float = II("gamma") 2033 """Gamma correction of the camera. Takes the value from the :py:class:`LaunchConfig`.""" 2034 2035 spectator: bool = True 2036 """If True will update the Unreal Engine's spectator camera""" 2037 2038 # NotImplemented 2039 2040 # In structured mode named tuples and carla Types are problematic 2041 camera_blueprints: list = field( 2042 default_factory=lambda: [ # pyright: ignore[reportRedeclaration] 2043 CameraBlueprint("sensor.camera.rgb", carla.ColorConverter.Raw, "RGB camera") 2044 ] 2045 ) 2046 """ 2047 Cameras and sensors attached to the ego vehicle 2048 that can be viewed by the user in the pygame window. 2049 2050 Used with the :py:attr:`.CameraManager.sensors`. 2051 2052 Attention: 2053 Usage not yet implemented. 2054 """ 2055 2056 if TYPE_CHECKING: 2057 camera_blueprints: List["CameraBlueprint"] = field( 2058 default_factory=lambda: [ 2059 CameraBlueprint("sensor.camera.rgb", carla.ColorConverter.Raw, "RGB camera"), 2060 ] 2061 ) 2062
[docs] 2063 @config_path("camera/recorder") 2064 @dataclass 2065 class RecorderSettings(AgentConfig): 2066 """ 2067 Recorder settings for the camera. 2068 """ 2069 2070 enabled: bool = MISSING 2071 """ 2072 Whether the recorder is enabled 2073 2074 Set at WorldModel level 2075 2076 :meta private: 2077 """ 2078 2079 output_path: str = "${hydra:runtime.output_dir}/recorder/session%03d/%08d.bmp" 2080 """ 2081 Folder to record the camera 2082 2083 Needs two numeric conversion placeholders. 2084 2085 Note: 2086 When using the ${hydra:runtime.output_dir} resolver 2087 @hydra.main needs to be used or hydra must be initialized. 2088 """ 2089 2090 frame_interval: int = 4 2091 """Interval to record the camera"""
2092 2093 recorder: RecorderSettings = field(default_factory=RecorderSettings) 2094 """.. <take doc|RecorderSettings>""" 2095
[docs] 2096 @config_path("camera/hud") 2097 @dataclass 2098 class HUDConfig(AgentConfig): 2099 """ 2100 HUD settings for the pygame window. 2101 """ 2102 2103 # Block not implemented 2104 enabled: bool = True 2105 """Whether the HUD is enabled. Not Implemented""" 2106 2107 font_size: int = 20 2108 """Font size of the HUD. Not Implemented""" 2109 2110 font_color: Tuple[int, int, int] = (255, 255, 255) 2111 """Font color of the HUD. Not Implemented""" 2112 2113 font: str = "arial" 2114 """Font of the HUD. Not Implemented""" 2115 # ---------------------------- 2116
[docs] 2117 @config_path("camera/hud/detection_matrix") 2118 @dataclass 2119 class DetectionMatrixHUDConfig(AgentConfig): 2120 """ 2121 DetectionMatrix settings for the HUD 2122 2123 Attention: 2124 Keys must match keywords of :py:meth:`.DetectionMatrix.render` 2125 """ 2126 2127 draw: bool = True 2128 """Whether to draw the detection matrix""" 2129 2130 draw_values: bool = True 2131 """Whether to draw the numerical values as text""" 2132 2133 vertical: bool = True 2134 """Orient vertical (lanes are left to right) instead of horizontal.""" 2135 2136 imshow_settings: Dict[str, Any] = field(default_factory=lambda: {"cmap": "jet"}) 2137 """Settings for the pyplot.imshow function""" 2138 2139 text_settings: Dict[str, Any] = field(default_factory=lambda: {"color": "orange"}) 2140 """Settings for the text of pyplot.text when drawing the numerical values"""
2141 2142 detection_matrix: DetectionMatrixHUDConfig = field(default_factory=DetectionMatrixHUDConfig) 2143 """.. <take doc|DetectionMatrixHUDConfig>"""
2144 2145 hud: HUDConfig = field(default_factory=HUDConfig) 2146 """.. <take doc|HUDConfig>"""
2147 2148
[docs] 2149@config_path("launch_config_default.yaml") # NOTE: this may not be named launch_config 2150@dataclass 2151class LaunchConfig(AgentConfig): 2152 strict_config: Union[bool, int] = 3 2153 """ 2154 If enabled will assert that the loaded config is a subset of the `LaunchConfig` class. 2155 2156 If set to >= 2, will assert that during runtime the types are correct. 2157 """ 2158 2159 verbose: bool = True 2160 """unused kept for compatibility with carla examples.""" 2161 2162 debug: bool = True 2163 """If true will print out some more information and draws waypoints""" 2164 2165 interactive: bool = False 2166 """ 2167 If True will create an interactive session with command line input 2168 - NOTE: Needs custom code in the main file (Not implemented) 2169 """ 2170 2171 seed: Optional[int] = 1 2172 2173 # carla_service: 2174 map: str = "Town04_Opt" 2175 host: str = "127.0.0.1" 2176 port: int = 2000 2177 2178 timeout: float = 10.0 2179 """ 2180 Timeout for the :py:class:`carla.Client` connection. 2181 """ 2182 2183 fps: int = 20 2184 """ 2185 Used to fix :py:attr:`carla.WorldSettings.fixed_delta_seconds` 2186 2187 Experimental also used to cap fps in the simulation. 2188 """ 2189 2190 sync: Union[bool, None] = True 2191 """ 2192 If True, the simulation will be set to run in synchronous mode. 2193 For False, the simulation will be set to run in asynchronous mode. 2194 If None the world settings for synchronous mode will not be adjusted, 2195 assuming this is handled by the user / external system. 2196 """ 2197 2198 handle_ticks: bool = True 2199 """ 2200 Decide if the GameFramework & WoldModel are allowed to call carla.World.tick() 2201 or if `False` the ticks should be handled by an outside system. 2202 """ 2203 2204 loop: bool = True 2205 """ 2206 If True the agent will look for a new waypoint after the initial route is done. 2207 2208 Note: 2209 Needs custom implementation in the main file by the user. 2210 """ 2211 2212 # camera: 2213 width: int = 1280 2214 """width of pygame window""" 2215 height: int = 720 2216 """height of pygame window""" 2217 gamma: float = 2.2 2218 """ 2219 Gamma correction of the camera. 2220 Depending on the weather and map this might need to be adjusted. 2221 """ 2222 2223 # Actor 2224 externalActor: bool = True 2225 """ 2226 If False will spawn a vehicle for the agent to control, using the `filter` and `generation` settings. 2227 Otherwise will not spawn a vehicle but will wait until an actor with the name defined in `rolename` (default: "hero") is found. 2228 2229 This vehicle needs to be spawned by another process, e.g. through the scenario runner. 2230 """ 2231 2232 rolename: str = "hero" 2233 """Actor name to wait for if `externalActor` is True.""" 2234 2235 filter: str = "vehicle.*" 2236 """Filter for the ego blueprint. Kept for compatibility with carla examples.""" 2237 2238 generation: Union[int, str] = 2 # pyright: ignore[reportRedeclaration] 2239 """Generation for the ego blueprint. Kept for compatibility with carla examples.""" 2240 2241 if READTHEDOCS or TYPE_CHECKING: 2242 generation: Literal[1, 2, "all"] 2243 2244 autopilot: bool = False 2245 r""" 2246 Whether or not to use the CARLA's :external-icon-parse:`:py:class:\`carla.TrafficManager\`` to autopilot the agent 2247 2248 Note: 2249 This disables the usage of the LunaticAgent, however needs to be 2250 enabled in the main script by the user to work. 2251 """ 2252 2253 restart_clean_sensors: Optional[bool] = None 2254 """ 2255 If None will remove all sensors from an externalActor if :py:meth:`.WorldModel.restart` is 2256 called outside from the initialization, i.e. :py:meth:`~.WorldModel.restart` it is called a second time. 2257 Else will always/never remove the sensors when using :py:meth:`.WorldModel.restart`. 2258 """ 2259 2260 agent: LunaticAgentSettings = MISSING 2261 """The settings of the agent""" 2262 2263 camera: CameraConfig = field(default_factory=CameraConfig) 2264 """.. <take doc|CameraConfig>""" 2265 2266 pygame: bool = True 2267 """ 2268 Deactivates the pygame window and interface. 2269 2270 Attention: 2271 Setting this to False is experimental. 2272 """ 2273 2274 # --- 2275 2276 hydra: Annotated[HydraConf, "Key not guaranteed to be present or complete."] = field( 2277 default=MISSING, compare=False, hash=False 2278 ) 2279 """ 2280 Hydra_ config dict. 2281 2282 Attention: 2283 This field is not guaranteed to be present or the complete :py:class:`HydraConf` schema. 2284 2285 :meta exclude: # not included in yaml.comments 2286 """ 2287 2288 if not TYPE_CHECKING: 2289 # should not be HydraConf at runtime, as it is not complete 2290 hydra: HydraConf = field(default=MISSING, compare=False, hash=False) 2291 2292 if READTHEDOCS or TYPE_CHECKING: 2293 leaderboard: Annotated[DictConfig, "Only present for the", LunaticChallenger] = field(init=False, kw_only=True)
2294 2295 2296# --------------------- 2297 2298 2299def export_schemas(detailed_rules: bool = False): 2300 """ 2301 Exports the schemas as YAML files to the `conf/` folder with comment annotations. 2302 2303 Parameters: 2304 detailed_rules: If True, the :py:attr:`LunaticAgentSettings.rules` entry will also include comments and 2305 further information. Default is :code:`False`. 2306 2307 Note: 2308 This function is executed automatically when the module is imported.<br> 2309 **detailed_rules** may only be :python:`True` *after* the rules submodule has been initialized, 2310 i.e. cannot be called in this file. 2311 """ 2312 2313 # Using OmegaConf.set_struct, it is possible to prevent the creation of fields that do not exist: 2314 LunaticAgentSettings.export_options( 2315 "conf/agent/default_settings.yaml", with_comments=True, detailed_rules=detailed_rules 2316 ) 2317 2318 # Export the launch config 2319 lc = LaunchConfig() 2320 lc.camera.camera_blueprints = ["NotImplemented"] # TODO: Currently not converted to yaml layout 2321 lc.export_options("conf/launch_config_default.yaml", with_comments=True, detailed_rules=detailed_rules) 2322 lc.camera.export_options( 2323 "conf/config_extensions/camera_default.yaml", with_comments=True, detailed_rules=detailed_rules 2324 ) 2325 # LiveInfo 2326 LiveInfo.export_options("conf/agent/live_info.yaml", with_comments=True, detailed_rules=detailed_rules) 2327 2328 2329# Always want the schemas to be up to date 2330# Cannot extract rules because of circular imports; a second call is done in rules/__init__.py 2331if not READTHEDOCS: 2332 try: 2333 export_schemas(detailed_rules=False) 2334 except Exception: 2335 logging.exception("Error exporting schemas")