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