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