Erstellen von benutzerdefinierten Szenarien für CARLA mit Scenic

Car driving on street, another car in front of the first car, measuring the distance between the two cars, categorising level red dangerous, CARLA program

What is Scenic?

In der offiziellen Dokumentationheißt es: "Scenic ist eine domänenspezifische probabilistische Programmiersprache zur Modellierung der Umgebung von cyber-physischen Systemen wie Robotern und autonomen Fahrzeugen. Ein Scenic-Programm definiert eine Verteilung über Szenen, Konfigurationen von physischen Objekten und Agenten; Stichproben aus dieser Verteilung ergeben konkrete Szenen, die simuliert werden können, um Trainings- oder Testdaten zu erzeugen. Scenic kann auch (probabilistische) Richtlinien für dynamische Agenten definieren, was die Modellierung von Szenarien ermöglicht, in denen Agenten im Laufe der Zeit als Reaktion auf den Zustand der Welt Aktionen ausführen."

Unser Hauptziel bei der Verwendung von Scenic ist der Entwurf von Szenarien - nicht die Datengenerierung. Dieser Blog-Beitrag befasst sich mit Scenic - einem praktischen und nützlichen Tool, mit dem Sie festlegen können, wie Ihre virtuelle Umgebung aussehen soll. Genauer gesagt nutzen wir flexible Möglichkeiten, um Objekte in der Szene zu platzieren und benutzerdefinierte Eigenschaften in Scenic zu implementieren.

Um Ihre eigene Verkehrssimulation mit Scenic zu erstellen, sind 5 Schritte zu beachten. Alle Schritte folgen einem Beispiel und setzen die Verwendung von CARLA voraus, einer Open-Source-Simulationsplattform, die die Entwicklung, das Training und die Validierung des autonomen Fahrens unterstützt. Weitere Details finden Sie in dem Beitrag Was ist CARLA und wie nutzt man es um autonomes Fahren zu simulieren?

Schauen wir uns ein Beispiel eines Überholvorgangs an, um die Schritte in einer Verkehrssimulation mit Scenic zu sehen. Das autonome Fahrzeug, das wir in diesem Beitrag ego vehicle nennen, überholt ein anderes Fahrzeug und wechselt zurück auf seine ursprüngliche Fahrspur.

Schritt 1: Platzieren von Autos in der Szene

Scenic bietet eine sehr flexible Möglichkeit, Objekte in der Welt zu platzieren. Der einfachste Weg dies zu tun ist die Erstellung von exakten Punkten, an denen das autonome Auto, oder wie wir es nennen, das“ego vehicle”, platziert werden kann. Zum Beispiel: Car at -50 @ 1.75. Kurz gesagt, es werden Punkte definiert und Objekte auf diesen Punkten platziert. Die genauen Koordinaten für einen Punkt können direkt im UE4-Editor abgerufen werden oder indem man ein Scenic Skript ohne das Flag --simulate startet.

scenic my_traffic_sim.scenicCode-Sprache: CSS (css)
Landschaftliche Ansicht nach dem Start des Skripts mit dem obigen Befehl. Das Ego-Fahrzeug ist durch das Auto mit dem Kegel gekennzeichnet. Sie können die Koordinaten von Ihrem Cursor in der unteren rechten Ecke sehen.

Sie können sehen, dass die Koordinaten in der unteren rechten Ecke der Ansicht angezeigt werden. Drücken Sie CTL+C, um die Ansicht zu schließen. Platzieren wir das ego vehicle an einem Punkt, der durch seine x- und y-Koordinaten definiert ist.

# my_traffic_sim.scenic

EGO_MODEL = "vehicle.audi.tt"

ego = Car at -50 @ 1.75,
	with blueprint EGO_MODEL,
	with behavior EgoBehavior()Code-Sprache: PHP (php)

Nachdem das Ego Vehicle definiert ist, können andere Fahrzeuge einfacher platziert werden. Sie können relativ zum Ego Vehicle definiert werden, z. B. mit "ahead of

CAR_1_MODEL = "vehicle.ford.mustang"

car_1 = Car ahead of ego by (0, 10),
    with blueprint CAR_1_MODEL,
    with behavior Car1Behavior()Code-Sprache: JavaScript (javascript)

Scenic ist sehr anschaulich und die natürliche Sprache macht es einfach zu verstehen, wie die Autos platziert sind.

Der Zugriff auf eine bestimmte Straße/Spur in Scenic ist möglich, da das hinterliegende Netzwerk eine Dictionary-Struktur mit Schlüsseln und Werten hat. Wir verwenden einen online OpenDRIVE viewer , um die Straßennamen schnell abzurufen und sie Variablen zuzuordnen.

# the network variable can be directly used without special initialization

road4 = network.elements['road4']
road7 = network.elements['road7']
road2 = network.elements['road2']Code-Sprache: PHP (php)

Schritt 2: Benutzerdefinierte Verhaltensweisen erstellen - Beispiel: Überholen

Sie können zwei weitere Zeilen in dem Codeblock sehen, in denen wir das Auto an einem bestimmten Punkt platziert haben.

car_1 = Car ahead of ego by (0, 10),
    with blueprint CAR_1_MODEL,
    with behavior Car1Behavior()Code-Sprache: JavaScript (javascript)

In Zeile 2 wird das 3D-Modell des Fahrzeugs zugewiesen, ein sogenannter Blueprint (spezifisch für Unreal Engine 4). In Zeile 3 wird dem Auto ein Verhalten zugewiesen. Die so genannten Behaviors sind für die Aktionen verantwortlich, die ein Auto ausführt. Sie können grundlegende Verhaltensweisen wieFollowLaneBehavior und FollowTrajectoryBehaviordirekt verwenden. Für komplexere Verhaltensweisen müssen Sie jedoch Ihre eigenen erstellen. Dies ist höchstwahrscheinlich der Fall, wenn Sie eine Verkehrssimulation erstellen möchten. Verhaltensweisen können mit dem Schlüsselwort do.

Hier ein Beispiel dafür, wie ein Überholverhalten implementiert werden kann.

behavior Overtake(leadCar, target_speed, change_back_distance=10, is_oppositeTraffic=False):
    """
    leadCar: Car object of the leading car
    target_speed: Integer of the target speed to overtake
    change_back_distance: Integer of the distance after overtaking to change back to initial lane
    is_oppositeTraffic: Boolean if there is opposite traffic
    """
    # get the left lane of the current lane section
    leftLane = self.laneSection.laneToLeft
    
    # blink before overtaking
    take SetVehicleLightStateAction(VehicleLightState.LeftBlinker)
    
    # follow lane for 2 seconds
    do FollowLaneAndDoNotCollideWith(leadCar, target_speed=target_speed, is_oppositeTraffic=is_oppositeTraffic) for 2 seconds
    
    # change to left lane for overtaking and turn of the blinker
    do LaneChangeBehavior(laneSectionToSwitch=leftLane, target_speed=target_speed, is_oppositeTraffic=is_oppositeTraffic)
    take SetVehicleLightStateAction(VehicleLightState.NONE)

    # follow the overtaking lane until there is enough distance to the leading car, the car you want to overtake
    do FollowLaneBehavior(target_speed=target_speed, laneToFollow=leftLane.lane, is_oppositeTraffic=is_oppositeTraffic) \
        until (distance from ego to leadCar) > change_back_distance
    
    # change back to the initial lane and set the right blinker
    take SetVehicleLightStateAction(VehicleLightState.RightBlinker)
    do FollowLaneBehavior(target_speed=target_speed, laneToFollow=leftLane.lane, is_oppositeTraffic=is_oppositeTraffic) for 1 seconds
    if is_oppositeTraffic:
        rightLane = self.laneSection.laneToLeft
    else:
        rightLane = self.laneSection.laneToRight
    do LaneChangeBehavior(laneSectionToSwitch=rightLane, target_speed=target_speed)
    
    # turn of blinker
    take SetVehicleLightStateAction(VehicleLightState.NONE)Code-Sprache: PHP (php)

Schritt 3: Checkpoints setzen

Alle Fahrzeuge sind in der Szene platziert und die Verhaltensweisen sind definiert. Nun wollen wir die Verhaltensweisen kontrolliert auslösen. Dies gewährleistet eine konsistentere Art der Simulationen. Der Trigger, an dem ein Verhalten ausgelöst wird, ist genau definiert und reduziert das Risiko von unerwünschten und zufälligen Verhaltensmustern während der Simulation. Beachten Sie, dass Scenic Simulationen zulässt, die mehr oder weniger zufällig sind. Der Auslöser kann mit einem Punkt implementiert werden. Wir nennen ihn einen Checkpoint. Für das vorgestellte Beispiel definieren wir die Checkpoints.

# checkpoint when to start overtaking
CHECKPOINT_1 = Point at -320 @ 5.25

# this is needed to terminate the simulation
EGO_GOAL = Point at -890 @ 5.25Code-Sprache: PHP (php)

Checkpoints können zum Beispiel ausgelöst werden, wenn das Auto nahe genug ist. Auch hier macht die natürliche Sprache von Scenic dies deutlich:

# example of how to use checkpoint to interrupt behaviors
behavior ExampleBehavior():
  try
    # default behavior
    do ...
  interrupt when (distance from self to CHECKPOINT_1) <= 2:
    # default behavior is interrupted
    # self refers to the object this behavior is assigned to
    take ...
    do ...

# this terminates the simulation and is absolutetly required in the scenic script
terminate when (distance from ego to EGO_GOAL) <= 2Code-Sprache: PHP (php)

Fassen wir die bisher gecodeten Dinge zusammen. Verhaltensweisen, die nicht im Detail erwähnt werden, sind höchstwahrscheinlich integrierte Verhaltensweisen, die Sie direkt verwenden können.

### my_traffic_sim.scenic

## CAR MODELS AND MORE
EGO_MODEL = "vehicle.audi.tt"
# it is not clear which unit the speed is given in but we think it is m/s
EGO_SPEED = 10

CAR_1_MODEL = "vehicle.ford.mustang"
CAR_1_SPEED = 10

# checkpoints
CHECKPOINT_1 = Point at -320 @ 5.25
EGO_GOAL = Point at -890 @ 5.25
CAR_1_GOAL = Point at -360 @ 5.25 


# define the ego trajectory that is followed
road4 = network.elements['road4']
road7 = network.elements['road7']
road2 = network.elements['road2']
ego_trajectory = [road4.lanes[1], road7.lanes[0], road2.lanes[0]]

## BEHAVIORS
behavior FollowLaneAndDoNotCollideWith(leadCar, target_speed=10, speedier_threshold=15, speed_threshold=12, brake_threshold = 7, is_oppositeTraffic=False):
  """
  leadCar: Car object of the leading car
  target_speed: Integer of the target speed to overtake
  speedier_treshold: Integer of distance to lead car (far away)
  speed_threshold: Integer of distance to lead car (closer)
  brake_treshold: Integer of distance to lead car (too close)
  is_oppositeTraffic: Boolean if there is opposite traffic
  """
  try:
      print("The fast xor furious!")
      do FollowLaneBehavior(target_speed=target_speed, is_oppositeTraffic=is_oppositeTraffic)
  interrupt when (distance from self to leadCar) <= speedier_threshold:
      try:
          print("What a sloth in front of me!")
          do FollowLaneBehavior(target_speed=leadCar.speed + 1, is_oppositeTraffic=is_oppositeTraffic) until (distance from self to leadCar) > speedier_threshold
      interrupt when (distance from self to leadCar) <= speed_threshold:
          try:
              print("I'll better go as fast as this snail!")
              do FollowLaneBehavior(target_speed=leadCar.speed, is_oppositeTraffic=is_oppositeTraffic) until (distance from self to leadCar) > speed_threshold + 2
          interrupt when (distance from self to leadCar) <= brake_threshold:
              print("Ooof, I must brake! Out of the way!")
              brake_intensity = min(max(math.exp(-0.1535 * (distance to leadCar)), 0), 1)
              take SetBrakeAction(brake_intensity)
              take SetThrottleAction(0.0)

behavior Overtake(leadCar, target_speed, change_back_distance=10, is_oppositeTraffic=False):
  """
  leadCar: Car object of the leading car
  target_speed: Integer of the target speed to overtake
  change_back_distance: Integer of the distance after overtaking to change back to initial lane
  is_oppositeTraffic: Boolean if there is opposite traffic
  """
  # get the left lane of the current lane section
  leftLane = self.laneSection.laneToLeft
  
  # blink before overtaking
  take SetVehicleLightStateAction(VehicleLightState.LeftBlinker)
  
  # follow lane for 2 seconds
  do FollowLaneAndDoNotCollideWith(leadCar, target_speed=target_speed, is_oppositeTraffic=is_oppositeTraffic) for 2 seconds
  
  # change to left lane for overtaking and turn of the blinker
  do LaneChangeBehavior(laneSectionToSwitch=leftLane, target_speed=target_speed, is_oppositeTraffic=is_oppositeTraffic)
  take SetVehicleLightStateAction(VehicleLightState.NONE)

  # follow the overtaking lane until there is enough distance to the leading car, the car you want to overtake
  do FollowLaneBehavior(target_speed=target_speed, laneToFollow=leftLane.lane, is_oppositeTraffic=is_oppositeTraffic) \
      until (distance from ego to leadCar) > change_back_distance
  
  # change back to the initial lane and set the right blinker
  take SetVehicleLightStateAction(VehicleLightState.RightBlinker)
  do FollowLaneBehavior(target_speed=target_speed, laneToFollow=leftLane.lane, is_oppositeTraffic=is_oppositeTraffic) for 1 seconds
  if is_oppositeTraffic:
      rightLane = self.laneSection.laneToLeft
  else:
      rightLane = self.laneSection.laneToRight
  do LaneChangeBehavior(laneSectionToSwitch=rightLane, target_speed=target_speed)
  
  # turn of blinker
  take SetVehicleLightStateAction(VehicleLightState.NONE)

behavior Car1Behavior():
  """
  car1 follows the same trajectory as the ego vehicle and slows down after the goal is reached
  """
  take SetSpeedAction(10)
  do FollowTrajectoryBehavior(trajectory= ego_trajectory, target_speed=CAR_1_SPEED) until (distance from self to CAR_1_GOAL) <= 2
  do FollowLaneBehavior(target_speed=5)
    
behavior EgoBehavior():
  """
  the ego vehicle follows its trajectory and overtakes after checkpoint 1 is reached
  """
  take SetSpeedAction(10)
  try:
      do FollowTrajectoryBehavior(trajectory=ego_trajectory, target_speed=EGO_SPEED, turn_speed=EGO_SPEED)
  interrupt when (distance from self to CHECKPOINT_1) <= 2:
      take SetVehicleLightStateAction(VehicleLightState.NONE)
      do Overtake(car_1, target_speed=EGO_SPEED*1.2, is_oppositeTraffic=False, change_back_distance=10)
      FollowLaneBehavior(target_speed=EGO_SPEED)

## DEFINE CARS
ego = Car at -50 @ 1.75,
  with blueprint EGO_MODEL,
  with behavior EgoBehavior()
	
car_1 = Car ahead of ego by (0, 10),
  with blueprint CAR_1_MODEL,
  with behavior Car1Behavior()
  
# terminate condition
terminate when (distance from ego to EGO_GOAL) <= 2Code-Sprache: PHP (php)

Schritt 4: Starten der Scenic Simulation

Bei der Ausführung einer Scenic Simulation sucht Scenic lokal nach dem CARLA-Server. Weitere Informationen zur Einrichtung eines CARLA-Servers für den Fernzugriff finden Sie in unserem Blog-Beitrag “Einrichten von CARLA auf einem Server für den Fernzugriff”.

Um die Simulation auszuführen, müssen Sie Parameter entweder an die CLI oder direkt im Skript angeben. Fügen Sie oben in Ihrem Scenic-Skript Folgendes hinzu:

## SET MAP AND MODEL
# required
param map = localPath('../to/opendrive_map.xodr')
param carla_map = 'name of your carla map'
model scenic.simulators.carla.model

# optional
param timestep = 0.02
param timeout = 20Code-Sprache: PHP (php)

Für Scenic-spezifische Parameter führen Sie einfach scenic --help aus oder sehen Sie hierFür CARLA-spezifische Parameter sehen Sie sich die Tabelle hieran. Nun können Sie Ihre Scenic-Simulation mit dem Flag --simulate starten. Es sollte ein Fenster mit der gerenderten Simulation erscheinen.

scenic path/to/my_simulation.scenic --simulate

Schritt 5: Aufnahme speichern (optional)

Wir haben Scenic zusätzliche Parameter hinzugefügt, um eine Aufnahme direkt zu speichern. Die Höhe und Breite des Fensters sind wichtige Parameter. Auch der Parameter timestep hatte einen erheblichen Einfluss auf die Simulation. Für weitere Details empfehlen wir Ihnen unseren Blog-Beitrag “Videoaufzeichnung von benutzerdefinierten Szenarien mit CARLA“ zu lesen.

Teile diesen Artikel mit Deinem Netzwerk

Facebook
Reddit
Twitter
WhatsApp
LinkedIn
Email
Telegram

Haben Sie fragen zu unserem Artikel?
Kontaktieren Sie uns!

Danke!

Wir haben Deine Nachricht erhalten und werden uns schnellstmöglich bei Dir melden!

Beginnen wir noch heute,
Ihre Erfolgsstory zu schreiben!

Sind Sie bereit, mit der Entwicklung Ihres Produkts zu starten? Warten Sie nicht länger! Geben Sie hier Ihre E-Mail-Adresse ein und wir setzen uns umgehend mit Ihnen in Verbindung!

This Website Uses Cookies

We use cookies to provide social media features and to analyze our traffic. You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice, or by continuing to browse otherwise. You can read more about our cookie consent policy hier.