Behaviors

Table of content:

Built-in Behaviors
AccelerationBehavior
AlignmentBehavior
BoundaryMirrorBehavior
BoundaryWrapBehavior
BoundaryRepulsionBehavior
CohesionBehavior
CopyBehavior
DampingBehavior
EvasionBehavior
GridAvgBehavior
RandomizeBehavior
ResetBehavior
SpiralBehavior
SplineFollowBehavior
DistanceFieldFollowBehavior
EulerIntegration
Circular Behavior
ConeVisionBehavior
NeighborStoreBehavior
NeighborIndexStoreBehavior
NeighborDistanceStoreBehavior
NeighborDirectionStoreBehavior
ParameterCombineBehavior
ParameterMapBehavior
ParameterPrintBehavior
ParameterScaleBehavior
How to create your own behavior

Built-in Behaviors

This page is a reference for all built-in behaviors as well as a small tutorial on how to create your own behaviors. Each behavior section will list all input and output parameters along with the number of dimensions. In all cases "n-dim" could be replaced with "3-dim", because that's the standard number of dimensions for realistic flocking behavior. However we would like to keep this open, so that even a string theorist can get his fix and create 11-dimensional swarms ;-). Some of the listed parameters have the remark "(set parameter)" next to them, this means that this parameter is in most cases just set once at initiation and is left more or less the same throughout the simulation run. This parameters are also characterized by the behavior name prepended to the parameter name (e.g. acceleration_maxAngularAcceleration for AccelerationBehavior).

At the bottom of this page, you will find a instruction for creating custom hehaviors.

AccelerationBehavior

calculates the agent's acceleration

Syntax

swarm.addBehavior( "acceleration", AccelerationBehavior( "mass velocity force", "acceleration") );
swarm.set("acceleration_maxAngularAcceleration", 0.1);
swarm.set("acceleration_maxAngularAcceleration", 1.0);

Input parameters

mass 1-dim
velocity n-dim
force

n-dim

acceleration_maxAngularAcceleration (set parameter) 1-dim
acceleration_maxAngularAcceleration (set parameter) 1-dim

Output parameters

acceleration n-dim

The AccelerationBehavior has been explained as part of the Hello World 1: Moving Agents code example tutorial.

AlignmentBehavior

Boids behavior component. See Hello World 3 code example.

Syntax

swarm.addBehavior( "alignment", AlignmentBehavior("position:agent_position velocity", "force") );
swarm.set("alignment_minDist", 0.0);
swarm.set("alignment_maxDist", 1.7);
swarm.set("alignment_amount", 0.5);

Input parameters

position:position_space * n-dim
velocity n-dim
alignment_minDist (set parameter) 1-dim
alignment_maxDist (set parameter) 1-dim
alignment_amount (set parameter) 1-dim

* supplied by the neighbor parameters (parameter space)

Output parameters

force n-dim

BoundaryMirrorBehavior

reflects agents at a specified boundary (pool table effect)

Syntax

swarm.addBehavior( "mirror", BoundaryMirrorBehavior("position velocity force", "velocity force") );
swarm.set("mirror_upperBoundary", 2.0);
swarm.set("mirror_lowerBoundary", -2.0);

Input parameters

position

n-dim

velocity n-dim
force n-dim
mirror_upperBoundary n-dim
mirror_lowerBoundary n-dim

Notice how position is only an input parameter. This is due to the fact, that when an agent is reflected at a boundary, at the moment of the actual reflection only the motion direction (= 3-dimensional velocity parameter) changes as well as the force acting on the agent. The position itself however stays the same.

The two internal parameters upperBoundary and lowerBoundary mark the edge points of a rectangular space. This space can be cubic, but doesn't have to be.

Boundaries

Output parameters

velocity n-dim
force n-dim

BoundaryWrapBehavior

Agents exiting the boundary on one side enter it from the opposite side. This behavior is only concerned with an agent's position inside a boundary. If that position would be outside of this boundary, the agent is moved to the opposite side of the boundary space.

Syntax

swarm.addBehavior( "wrap", BoundaryWrapBehavior("position", "position") );
swarm.set("wrap_upperBoundary", 2.0);
swarm.set("wrap_lowerBoundary", -2.0);

Input parameters

position n-dim
wrap_upperBoundary n-dim
wrap_lowerBoundary n-dim

Output parameters

position n-dim

BoundaryRepulsionBehavior

Similar to Boundary Mirror Behavior, but the repulsion starts at a certain distance from the boundary and the repulsion force rises steadily from there up until the boundary itself. This behavior has been distribed in detail in the previous "Hello World 2: Motion Boundaries" section.

Syntax

swarm.addBehavior( "repulsion", BoundaryRepulsionBehavior( "position", "force" ) );
swarm.set("repulsion_lowerBoundary", math::Vector3<float>(-5.0, -5.0, -5.0));
swarm.set("repulsion_upperBoundary", math::Vector3<float>(5.0, 5.0, 5.0));
swarm.set("repulsion_maxDist", 2.0);
swarm.set("repulsion_amount", 0.03);

Input parameters

position

n-dim

repulsion_lowerBoundary n-dim
repulsion_upperBoundary n-dim
repulsion_maxDist 1-dim
repulsion_amount 1-dim

Output parameters

force n-dim

CohesionBehavior

Boids behavior component. See Hello World 3 code example.

Syntax

swarm.addBehavior( "cohesion", CohesionBehavior( "position:agent_position", "force") );
swarm.set("cohesion_minDist", 0.0);
swarm.set("cohesion_maxDist", 1.7);
swarm.set("cohesion_amount", 0.1);

Input parameters

position:position_space * n-dim
cohesion_minDist 1-dim
cohesion_maxDist 1-dim
cohesion_amount 1-dim

* supplied by the neighbor parameters (parameter space)

Output parameters

force n-dim

CopyBehavior

Simple template behavior which copies an agents parameter without modifying it. As with most behavior parameters it is not actually defined what this parameter is (position, velocity, ...) but rather chosen by the user of this behavior class, when he initializes the behavior. In the example syntax bellow we will assume you would like to copy

Use this code when you attempt to create your own behavior class.

Syntax

swarm.addBehavior( "copy", CohesionBehavior( "position", "position") );

Input parameter

position (e.g.) n-dim

Output parameter

(same as input parameter) n-dim

DampingBehavior

This behavior acts like an attractor that pulls an agent's speed towards a preferred value. We are using the term speed here on purpose for the following reason: Our usage of the term velocity defines it as a three dimensional parameter, which means it includes not only the "speed" at which it moves, but also the direction. However the motion direction is unaffected by this behavior. It simply makes sure that any acceleration forces that speed up the agent will be gradually countered by damping it back down to the norm speed.

It's important to understand that not velocity itself is changed by this behavior but an additional force parameter is created and cumulated with the force parameter outputs of previous behaviors. This process has been explained in the figure in the "Hello world 1: Moving Agents" section.

Syntax

swarm.addBehavior( "damping", DampingBehavior( "velocity", "force") );
swarm.set("damping_prefVelocity", 0.30);
swarm.set("damping_amount", 0.5);

Input parameters

velocity n-dim
damping_prefVelocity 1-dim (!)
damping_amount 1-dim

damping_prefVelocity: prefered speed

damping_amount: aggressiveness of the behavior. Smaller values will attract the speed more gently, meaning it will take more time until this preferred velocity has been reached.

Output parameters

force n-dim

EvasionBehavior

Boids behavior component. See Hello World 3 code example.

Syntax

swarm.addBehavior( "evasion", EvasionBehavior( "position:agent_position", "force") );
swarm.set("evasion_maxDist", 1.0);
swarm.set("evasion_amount", 0.5);

Input parameters

position:position_space * n-dim
evasion_maxDist 1-dim
evasion_amount 1-dim

* supplied by the neighbor parameters (parameter space)

Output parameters

force n-dim

GridAvgBehavior

Syntax

swarm->addBehavior( "trackMotion", GridAvgBehavior( "position:trackMotion", "force" ) );
swarm->set("trackMotion_amount", 0.01);

Input parameters

position:grid_space * n-dim
trackMotion_amount 1-dim

trackMotion_amount: scale down factor on force. 1.0 = maximal force

Output parameters

force  

RandomizeBehavior

The RandomizeBehavior will generate individual random forces for each agent. Again, keep in mind, that not actually position or velocity is randomized, but an additional force parameter that will be cumulated with all other forces generated by pre-acceleration-step behaviors. If you have difficulties grasping the concept, please read the "Hello world 1: Moving Agents" section, that explains the cumulative function of force.

Syntax

swarm.addBehavior( "randomize", RandomizeBehavior( "", "force" ) );
swarm.set("randomize_range", 0.001);

Input parameters

randomize_range 1-dim

randomize_range: output force will in a range from 0 to this value.

Output parameters

force n-dim

ResetBehavior

This behavior sets the force to 0, preparing it for the cumulation of all forces generated by subsequent behaviors.

Syntax

swarm.addBehavior( "resetForce", ResetBehavior( "", "force" ) );

Output parameters

force n-dim

Again, this output force value is not cumulated, but actually set to zero.

SpiralBehavior

...todo...

Syntax

Input parameters

   

Output parameters

   

SplineFollowBehavior

... todo...

Syntax

swarm->addBehavior( "splineFollow", SplineFollowBehavior( "position:trackContour", "force" ) );
swarm->set("splineFollow_minDist", 0.0);
swarm->set("splineFollow_maxDist", 1.0);
swarm->set("splineFollow_amount", 0.03);

Input parameters

position:shape_space * n-dim
splineFollow_minDist 1-dim
splineFollow_maxDist 1-dim
splineFollow_amount 1-dim

* shape_space: space that contains the SplineShape(s) (see Shapes chapter).

Output parameters

force n-dim

DistanceFieldFollowBehavior

...todo...

Syntax

swarm->addBehavior( "forcegrid", DistanceFieldFollowBehavior( "position:forcegrid velocity", "force" ) );
swarm->set("forcegrid_amount", 0.02);

Input parameters

position:grid_space * n-dim
forcegrid_amount 1-dim

* grid_space: space that contains the GridShape (see Shapes chapter)

Output parameters

force n-dim

EulerIntegration

Necessary step in calculating the final position and velocity. Like AccelerationBehavior, EulerIntergration is not a behavior in the usual sense of flocking behaviors, but rather a calculation process that integrates the position and velocity of the previous time step into the position and velocity of the next time step. There are other and more precise ways to accomplish this, but Euler integration is the most computationally inexpensive one.

This "behavior" comes immediately after the AccelerationBehavior without which you wouldn't have an acceleration parameter to work with!

Syntax

swarm->addBehavior( "integration", EulerIntegration("position velocity acceleration", "position velocity") );
swarm->set("integration_timestep", 0.01);

Input parameters

position n-dim
velocity n-dim
acceleration n-dim
integration_timestep 1-dim

integration_timestep: time granulation, ... todo

Output parameters

position n-dim
velocity n-dim

 

CircularBehavior

Allows the agents to move in circles.

Syntax

swarm->addBehavior( "circular", CircularBehavior("position", "force") );
swarm->set("circular_innerRadius", 2.0);
swarm->set("circular_outerRadius", 3.0);
swarm->set("circular_radialAmount", 0.1);
swarm->set("circular_tangentialAmount", 0.1);

innerRadius/outer Radius = radius of the inner and outer ring, agents are forced back into the defined zone if the are propelled outside of it. Of course outerRadius is expected to be greater than innerRadius.
radialAmount/tangentialAmount: ...TODO...

Input parameters

position n-dim

Output parameters

force n-dim

 

ConeVisionBehavior

...TODO

Syntax

swarm->addBehavior( "conevision", ConeVisionBehavior("???", "???") );
swarm->set("conevision_???", 0.01);

Input parameters

??? n-dim
??? n-dim
??? n-dim
convevision_??? 1-dim

... todo

Output parameters

??? n-dim
??? n-dim

 

NeighborStoreBehavior

Saves all neighborhood information in a parameter. This parameter can be sent to an external instance (like any other parameter), for example if neighborhood information is needed in a particular sound synthesis environment. The number of dimensions has to be specified on creation. If there are less neighbors availabe than the parameter dimension allows, the remaining parameter values (positions) will be overwritte with "-1.0".
This neighbor store parameter has the following format:

agent_idx, neighbor_1_idx, neighbor_1_distance, neighbor_2_idx, neighbor_2_distance, ...

Syntax

swarm->addBehavior( "nbStore", NeighborStoreBehavior("???", "???") );
swarm->set("nbStore_???", 0.01);

Input parameters

??? n-dim

... todo

Output parameters

??? n-dim
??? n-dim

 

NeighborIndexStoreBehavior

This one's similar to NeighborStoreBehavior, however only the indices of the neighbors are stored (not the actual distance)

Format of the neighbor parameter:

agent_index, neighbor_1_index, neighbor_2_index, ...

Syntax

swarm->addBehavior( "nbIndexStore", NeighborIndexStoreBehavior("???", "???") );
swarm->set("nbIndexStore_???", 0.01);

Input parameters

??? n-dim
??? n-dim

... todo

Output parameters

??? n-dim
??? n-dim

 

NeighborDistanceStoreBehavior

Another variation of the NeighborStoreBehavior, but this one stores only the distances (and not the indices)

The neighbor parameter has the following format:

0.0 (distance to the agent itself), neighbor_1_distance, neighbor_2_distance, ...

Syntax

swarm->addBehavior( "nbDistStore", NeighborDistanceStoreBehavior("???", "???") );
swarm->set("nbDistStore_???", 0.01);

Input parameters

??? n-dim
??? n-dim

... todo

Output parameters

??? n-dim
??? n-dim

 

NeighborDirectionStoreBehavior

Like NeighborStoreBehavior this one stores the neighbor indices plus neighbor information. However unlike NeighborStoreBehavior that addresses the distance to each neighbor, the NeighbroDirectionStoreBehavior is stores the directions in (TODO what format, rad?, degree?, ...)

Syntax

swarm->addBehavior( "nbDirStore", NeighborDirectionStoreBehavior("???", "???") );
swarm->set("nbDirStore_???", 0.01);

Input parameters

??? n-dim
??? n-dim

... todo

Output parameters

??? n-dim
??? n-dim

 

ParameterCombineBehavior

Allows the combination (addition?? TODO) of multiple input parameters into one output parameter

Syntax

swarm->addBehavior( "combine", ParameterCombineBehavior("x1 x2 x3", "y1") );
swarm->set("combine_???", 0.01);

Input parameters

Input parameters are arbitrarely chosen by the instantiator of this behavior

Output parameters

Output parameters are also chosen on creation.

 

ParameterMapBehavior

This behavior is a more flexible version of ParameterCombineBehavior. It allows it's parameters to be scaled or even map specific input value ranges to a specific output value.

Syntax

swarm->addBehavior( "map", ParameterMapBehavior("x1 x2 x3", "y1") );
swarm->set("integration_timestep", 0.01);

Input parameters

Chosen on creation, TODO

Output parameters

Chosen on creation, TODO

 

ParameterPrintBehavior

This behavior is intended mainly for debugging. It prints out its parameter names and values to the system console.

Syntax

swarm->addBehavior( "print", ParameterPrintBehavior("a b c", "") );
swarm->set("print_???", 0.01);

Input parameters

Input parameters are chosen on creation

 

ParameterScaleBehavior

Similar to ParameterMapBehavior, but only scales (multiplies) the received parameter values

Syntax

swarm->addBehavior( "scale", ParameterScaleBehavior("a", "a") );
swarm->set("scale_factor???", 0.01);

Input parameters

Input parameters are chosen at creation and must comply with output parameters. TODO: > 1 parameter possible, is the above assertion correct? interparam = factor?

 

How To Create Your Own Behaviors

As a final section in this behavior-related page, we will show you how to implement your own behavior class. This includes as well, creating and using your own parameters (if your behavior needs additional ones).

Please remind yourself of the relation between agents, parameters and behaviors:

  1. A swarm consists of agents (or can be treated as an agent itself)
  2. Agents have a set of parameters
  3. Agents have a set of behaviors
  4. Parameters can have n dimensions (in most cases one or three)
  5. Parameters serve as input to and output from a behavior
  6. Agents can share parameters with other agents by placing their parameters in a neighborhood space in order to find e.g. the three closest parameters to itself.

First of all, make sure you have the ISOFlock SVN code downloaded (details in "Installation"). Open your iso_flock project. You will notice, that all built-in behaviors in this page, can be found in a folder called behavior. However, we recommend you create your own folder (something like "my behaviors"), otherwise you will likely forget code you've written yourself and even erase it accidentally.

As you can observe in any of these behavior source code files, they all inherit from the Behavior base class:

class MyNewBehavior : public Behavior

which gives them a set of methods to use as is and another set of methods to re-implement yourself (aka programming interface):

First and foremost, of course the constructors:

/**
\brief create behavior
\param pInputParameterString input parameter string (parameters are space separated)
\param pOutputParameterString output paramaters are space separated)
*/
MyNewBehavior(const base::String& pInputParameterString, const base::String& pOutputParameterString);

/**
\brief create behavior
\param pAgent agent this behavior belongs to
\param pBehaviorName name of behavior
\param pInputParameterString input parameter string (parameters are space separated)
\param pOutputParameterString output paramaters are space separated)
\exception FlockException wrong number of type of parameters
*/
MyNewBehavior(Agent* pAgent, const base::String& pBehaviorName, const base::String& pInputParameterString, const base::String& pOutputParameterString) throw (FlockException);

These are of course the means by which you will later instantiate a behavior as part of your flock.

The first constructor is actually only a blueprint from which you can copy behaviors to different agents using the create() method:

virtual Behavior* create(const base::String& pBehaviorName, Agent* pAgent) 
		const  throw (FlockException);

This will be used if you create swarms of agents, so that the swarm itself can copy the behavior for all its agents.

However you can of course use the second constructor and pass a target agent to the behavior at the point of instantiation.

By far the most important method of your behavior class is the act() method:

/**
\brief perform behavior action
*/
virtual void act(); 

This method will be called every simulation step. It calculates the output parameters of your behavior using the input parameters as source. Since this is the one method you will spend of your time programming, we will first explain the rest of the methods and types you need to be concerned with.

In the protected body, you will keep the pointers to the parameters you need in your behavior. Let's look for example at the parameter pointers of AccelerationBehavior, one of the built-in behaviors:

 Parameter* mMassPar; /// \brief mass parameter (input)
 Parameter* mVelocityPar; /// \brief velocity parameter (input)
 Parameter* mForcePar; /// \brief force parameter (input)
 Parameter* mAcceleration; /// \brief acceleration parameter (output)
 Parameter* mMaxLinearAccelerationPar; /// \brief maximum linear acceleration parameter (internal)
 Parameter* mMaxAngularAccelerationPar; /// \brief maximum angular acceleration parameter (internal)

The way these parameter pointers are being set correctly goes like this: In your constructor you provided the class with a set of input parameters in the form of a string holding the parameter names (e.g. "mass velocity force"). The behavior base class will parse these strings and create a vector of mInputParameters and mOutputParameters. You then read the content of these vectors to your parameter pointers:

mMyFirstInputParam* = mInputParameters[0];  
mMySecondInputParam* = mInputParameters[1];
...

If you need additional internal parameters, you have to create them yourself in your constructor:

 mMyInternalParameter = createInternalParameter("PARAMETER_NAME", math::Vector<real>(NUM_OF_DIMENSIONS, DEFAULT_VALUE)); 

Internal Parameters are parameters created and used only by your behavior. However, since they are parameters as well, nothing will actually keep you from using them as input for other behaviors.

Okay, time to do some acting!

For your act() method to run as fast as possible, it's a good idea, to first of all create local references of your parameters, so that you have to access the parameter object only once every act() method call:

real& param1 = mMyFirstInputParam->value(); //1-dimensional 
math::Vector<real>& param2 = mMySecondInputParam->values(); //n-dimensional 

The above example depicts this process with a one-dimensional parameter (e.g. "mass") and a three-dimensional parameter (e.g. "position" in a 3D space). Do the same thing for all output and internal parameters.

In some cases for output parameters, you need the output of your previous act() call while at the same time already filling that specific output parameter with new values. This is why we do not only supply a values() method for parameters, but also a backupValues() method, which will not be overwritten immediately.

math::Vector<real>& param3 = mMyOutputParam->backupValues();

Now, all that's left to do is you personal monkey voodoo magic using the local references we just created. Write your output directly to these references as well.

Pitfalls in writing a act algorithm

1) Keep it fast! There is a Pitfalls section at the bottom of "Create your own units", that will give you some hints, to keep your code fast. In short: no casts, no unnecessary computation, avoid division, minimize conditional structures.

2) Test it for boundary conditions.