Behaviors and Behavior Trees

The Behavior Tree is an artificial intelligence technology that allows the user to quickly create flexible agents with various tactical modules, called “behaviors” or “behavior nodes”. The nodes can be arranged together in diverse and interrelated ways with connector nodes to define behavior.

Quick Start

A behavior may be defined as follows:

behavior report
   precondition
      return true;
   end_precondition

   execute
      WsfPlatform plat = PLATFORM;
      writeln(plat.Name(), " Report: Status - OK");
   end_execute
end_behavior

The report behavior may be implemented in a task processor behavior tree as follows:

processor TASK_MANAGER WSF_TASK_PROCESSOR
   update_interval 10 sec
   behavior_tree
      selector
         behavior_node report
      end_selector
   end_behavior_tree
end_processor

Any platform or platform type that implements this processor will report the OK status message to the console and simulation log. The behavior will be selected only if the precondition returns true - a selector will check the preconditions of each behavior_node in order and execute only the first behavior with a precondition returning true. Alternative behavior control mechanisms include sequence, parallel, priority_selector, and weighted_random. These can be used in combination and in series to create arbitrarily complex behavior trees.

Closer Look

Note

The following steps assume that the “simple_flight” scenario is present as discussed in Processors.

Adding Behaviors

To add behaviors to the simple_flight scenario, perform the following steps:

  1. Create a new text file, named behaviors.txt in the simple_flight directory.

  2. Add the following code line to track_processor.txt:

    include_once behaviors.txt
    
  3. The behaviors implemented in the following steps make use of aux_data, an AFSIM command that may be used to define custom variables with a specific platform or platform type for use in any number of applications. For the behaviors to work correctly, the following aux_data command block must be added to the platform type definition of CARGO_AIRCRAFT:

    aux_data
       struct CARGO_AUX_DATA
          bool collisionExpected = false
          bool offRoute = false
          double lastKnownDistance = -1
       end_struct
    end_aux_data
    
  4. Additionally, a climb rate limit should be added to the mover definition on same platform type. This will ensure the aircraft change pitch within reason of what is possible. Update the CARGO_AIRCRAFT mover definition to reflect the following:

    mover WSF_AIR_MOVER
       update_interval 3 sec
       maximum_flight_path_angle     10 deg
    end_mover
    
  5. Open behaviors.txt.

  6. Add the following behavior blocks to behaviors.txt:

    • The behavior defined below will be the default behavior of the cargo aircraft - executed when no tracks are detected by the platform’s AIR_RADAR.

    behavior report_all_clear
       precondition
          WsfLocalTrackList trackList = PLATFORM.TrackList("track_proc");
          return trackList.Empty();
       end_precondition
    
       execute
          WsfPlatform plat = PLATFORM;
          writeln(plat.Name(), " Report: All Clear");
          if (plat.AuxDataBool("offRoute"))
          {
             plat.ReturnToRoute();
             writeln("\t - Returning to route\n");
             plat.SetAuxData("offRoute", false);
          }
       end_execute
    end_behavior
    
    • The next behavior will only execute when a track is detected by the AIR_RADAR on either platform. During execution, it checks if the track indicates the presence of a platform on a collision course. It determines whether or not a collision is likely to occur by comparing the tracks latitude and altitude with its own, then determines over time, using aux data, whether or not the track is getting closer. If they are at the same latitude, and altitude, and they are getting closer, the task processor generates a warning - a collision is expected, and provides that information to the platform aux data.

    behavior report_entity_detected
       precondition
          WsfLocalTrackList trackList = PLATFORM.TrackList("track_proc");
          return !trackList.Empty();
       end_precondition
    
       execute
          WsfPlatform plat = PLATFORM;
          writeln(plat.Name(), " Report: Entity Detected");
    
          WsfLocalTrackList trackList = plat.TrackList("track_proc");
          foreach (WsfTrack t in trackList)
          {
             if (plat.Altitude() == t.Altitude())
             {
                double lastDist = plat.AuxDataDouble("lastKnownDistance");
                double currDist = plat.GroundRangeTo(t.CurrentLocation());
                if (lastDist == -1 || lastDist > currDist)
                {
                   plat.SetAuxData("lastKnownDistance", currDist);
                   writeln("\t - WARNING: Collision Expected!\n");
                   plat.SetAuxData("collisionExpected", true);
                }
             }
          }
       end_execute
    end_behavior
    
    • The third and final behavior executes only after the platform is known to be on a collision course. When this behavior executes, the platform instructs itself to randomly alter its altitude within a limited range. After changing altitude, the system returns to normal and maintains this altitude until the track is no longer detected, at which point, it returns to the assigned route.

    behavior reroute
       precondition
          WsfPlatform plat = PLATFORM;
          bool collisionExpected = PLATFORM.AuxDataBool("collisionExpected");
          if (collisionExpected)
          {
             writeln(plat.Name(), " Report: Rerouting...");
          }
          return collisionExpected;
       end_precondition
    
       execute
          WsfPlatform plat = PLATFORM;
          double altDelta = 100 * Math.Ceil(RANDOM.Uniform(0, 30) - 20);
          plat.GoToAltitude(plat.Altitude() + altDelta);
          writeln("\t - New Target Altitude = ", plat.Altitude() + altDelta, "m\n");
          plat.SetAuxData("collisionExpected", false);
          plat.SetAuxData("offRoute", true);
      end_execute
    end_behavior
    

Adding a Behavior Tree

The order of these behaviors is critical to their correct execution.

  1. A behavior tree may be implemented in the CARGO_TASK_MANAGER processor definition block as follows:

    behavior_tree
       selector
          behavior_node report_all_clear
          behavior_node reroute
          behavior_node report_entity_detected
       end_selector
    end_behavior_tree
    
  2. With these additions, the cargo aircraft are now capable of autonomously routing around one another. Simulate simple_flight in Warlock.

  3. Advance to time 1000 sec (just before the aircraft are close enough to detect one another).

  4. Observe the messages reported to the console output as the platforms determine a course of action independently. Compare this to their altered flight paths.

The flight paths should reflect something similar to the following:

../../_images/simple_flight_with_behaviors.png

Review

With the increasing size of the scenario, it is no longer practical to provide the full version of every file that was changed in this module. The full versions will be available within demos/simple_flight if needed for reference.

In this module, a new file behaviors.txt was added to the simple_flight scenario, and additions were made to cargo_aircraft.txt and track_processor.txt.

../../_images/simple_flight_project_with_behaviors.png

track_processor.txt

# track_processor.txt
# A component of the simple_flight demo

include_once behaviors.txt

processor TRACK_MANAGER WSF_TRACK_PROCESSOR
   master_track_processor
   purge_interval 10 sec
   report_interval 3 sec
end_processor

processor CARGO_TASK_MANAGER WSF_TASK_PROCESSOR
   update_interval 10 sec
   behavior_tree
      selector
         behavior_node report_all_clear
         behavior_node reroute
         behavior_node report_entity_detected
      end_selector
   end_behavior_tree
end_processor

cargo_aircraft.txt (platform type)

# cargo_aircraft.txt
# A component of the simple_flight demo
include_once track_processor.txt

...

platform_type CARGO_AIRCRAFT WSF_PLATFORM
   icon c130
   spatial_domain air
   category cargo

   mover WSF_AIR_MOVER
      update_interval 3 sec
      maximum_flight_path_angle     10 deg
   end_mover

   radar_signature CARGO_AIRCRAFT_RADAR_SIG

   add sensor cargo_radar AIR_RADAR
      internal_link track_proc
   end_sensor

   add processor track_proc TRACK_MANAGER
      internal_link task_mgr
   end_processor

   add processor task_mgr CARGO_TASK_MANAGER
   end_processor

   aux_data
      struct CARGO_AUX_DATA
         bool collisionExpected = false
         bool offRoute = false
         double lastKnownDistance = -1
      end_struct
   end_aux_data
end_platform_type

Summary

Behaviors offer a small fractional view of the advanced capability offered through AFSIM. With these additions, any number of platforms may be automated to behave independently based on what they perceive directly. AFSIM’s capability is expanded further when behaviors and processors are connected with communications devices.