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")