Home  >  Examples  >  Rat components

Home
Screenshots
Examples
Background
Changes
Contact
User guide
Simple navigation
Rat components
Programattic navigation

Dissecting the rat model

Figure 1 shows the diagram view of the example rat model. At the top are the components that can be used in constructing the rat. In the middle is the structure of this particular model. The scale on the diagram is in millimeters and the layout of components defines the physical layout of the rat (its a small rat).

Figure 1. Diagram view of the rat model

As with most models, there is a collection of logical components that define the functionality, and some basic shapes (lines, circles, rectangles etc) that define the structure. In this case, the body is just an oval and the tail a rectangle, but actual shape and relative positions of components can be important in some cases. There are three sensor/effector components attached: whiskers on each side and a paw for pressing levers.

Figure 2 shows the definition of the whiskers component.

Figure 2. Component design view of the whiskers component
Note that this is now with the workbench in component viewing/editing mode. Unlike models, all component definitions have exactly the same set of possible components in the top row. In diagram order, these are: (there is a cable in the picture that shouldn't be there); plug; socket; various types of field for adding parameters to the specification; and the usual drawing elements. The diagram has a dotted white square and three black lines. The dotted line is just there for guidance: anything drawn in this space gets used for the icon for the component. The tree lines are meant to represent whiskers in an easily recognizable form. Before explaining the rest of this diagram, it is necessary to introduce Catacomb's inter-component communication model.

About plugs and sockets

The communication model is based on "plugs" that can be thought of as a cable with a plug on the end, and "sockets" that plugs go into, collectively called ports. Either one can be used for input, output or both. Plugs and sockets come in a wide variety of types and you can only connect matching types together when making a model. Components are connected either by connecting a plug from one to a matching socket on the other, or by connecting a cable between output and input sockets. There is no functional difference between a component with a plug on and one with a socket and a cable. As a design rule, if a port always needs to be connected to something, then its simplest to use a plug. If the connection is optional then a socket is best (less cluttered when not in use), and if it could go to multiple places then you have to use a socket.

When building a component definition, you first add a generic plug or socket, and then set its type. When building models, there is only one cable type, but as soon as one end is connected to a socket, then the other end can only be connected to a compatible socket.

For each port you can set the flow direction (in, out or both) and the type of thing that goes through it. These types are also under user control (you can add new ones with the "new connection type" option if you right-click on the components tree). Each type of communication specifies how the ports look and what actually flows in the connection. The latter must be one of twenty or so predefined types that are distinguished according the the nature (event driven, continuous, or physical) and content (true/false values, numbers, vectors, object etc) of the connection.

Ports on the whiskers

Returning to figure 2, there are four ports on the diagram. In the middle is a socket of type "Contact". The contact communication type transmits physical contact (actually overlap) between components. This port operates in conjunction with the one labelled Plug_0 at the bottom which is of type "Position". When you use the whiskers in a model, the position plug (its the only type of plug that doesn't connect to a specific socket) can be moved around to indicate exactly where the component should be located. Then, when the line between this position (relative to the rat's body) and the rat itself crosses some solid object, the "Contact" port is activated. When it goes out of contact, it is deactivated (we'll see what this means in concrete terms shortly).

This particular component has two other sockets. One at the top is if type "BooleanState" which means it holds a single true-false value that can be read at any time by connected components. The one on the right, labelled "spike" is an event output socket that sends events when instructed by the component.

Figure 3. Details of the spike port
In figure 3, the right panel shows the parameters of this socket. As well as the type and flow direction, there are three textual fields. These are for textual information to help he user of the component understand what it does. The "id" field sets the name of the port; the tag field is a single line description that is used to make the popup tool-tip when you hover over it; and the longer "info" description goes in the help system.

Finally, there is one other component to the whiskers in the diagram, a field called "spikeFrequency". This is shown in edit mode in figure 4.

Figure 4. Spike frequency field
Adding this field to the component has the effect that when someone adds this type of whiskers to a rat they are prompted to enter a value for a quantity called "frequency" that is measured in Hertz and lies between 1 and 100. Note that the name the user sees is called the "label" here. This is not necessarily the same as the id. The id should be a locally unique string without any spaces with a name that is meaningful to the designer of the component; but the label is what you see when you are assembling models, can have spaces and should make sense in the context of the model.

It is worth stressing at this stage, that although the intended functions have been defined and there is a textual indication of the function in the names and info text, there is nothing "behind" the specification so far. The next step is to implement the logic of the whiskers. This is shown in figure 5, which presents the same component definition but in text mode.

Figure 5. Text view of the whiskers showing the script
Scrolling to the bottom shows the script that actually defines the behavior of the whiskers. The full script is included below.

Whiskers script

This is the full script that controls the whiskers, as shown in figure 5. The outline of the script is generated by Catacomb: the bits supplied by the component builder are the bodies of the methods ("method" is the Java term for what other languages call functions or procedures).


double period;

boolean inContact = false;
double timer;

public void init() {
     if (p.getSpikeFrequency() > 0) {
        period = 1000. / p.getSpikeFrequency();
    } else {
        period = 1.e9;
    } 	
}


public void contacted_contact(Object v) {
    inContact = true;
    show_touching(inContact);
    timer = 0.;
    send_spike(); 
}


public void decontacted_contact() {
  inContact = false;
  show_touching(inContact);
}


public void advanceTo(TimePoint tp) {
  if (inContact) {
      timer += tp.getDt();
      if (timer > period) {
          timer -= period;
          send_spike();
      }
  }
}


The outline methods are generated by the system when you press "refresh" for the first time. It knows what methods are needed because of the ports that are present on the diagram. For example, the contact port, gives rise to the two methods "contacted_contact(Object v)" and "decontacted_contact()". Contact ports always give methods called "contacted_" and "decontacted_" followed by the port name (which is a reason to consider a better port name than "contact"). Besides these two methods there are two standard methods called "init" and "advanceTo" that are present for all components. But what about the other ports? They do not define things that are done to the component, but rather, things that the component can, optionally, do to the components it is connected to. As such, they do not give methods that must be filled in, but provide functions that can be called. You can see what these are by clicking "info" with the result shown in figure 6.

Figure 6. Auxiliary information for the whiskers script
Figure 6 shows that when writing the script, there are a number of other methods available. In particular, you can access whatever value the user entered for the spike frequency as a member of the parameters object via the function "p.getSpikeFrequency()" [in generating the method names for accessing parameters it uses the standard Java "camelCase" convention of joining the words together and starting each one with a capital letter - it is conventional to give variables names starting with lower case letters (as in "spikeFrequency") that turn into upper case for the "get" method (as in "getSpikeFrequency")]. And the two ports each provides one method. To set the value shown by the port on the top there is "show_touching(boolean b)" and to send a spike through the port labelled "spike" there is "send_spike()". With this information it should be fairly easy to work out what the script does. When the whisker comes into contact with something it does two things: sets the contact output to true, and starts a counter for the time. This is updated each time "advanceTo" is called and periodically emits a spike. The roll-over on the counter is calculated at startup in the "init" method from the calculation timestep and the user's desired spike frequency.

This completes the definition of this sort of whiskers, but it should be stressed that this by no means exhausts the possibilities for making whiskers. It is just one simple case. By adding different ports and different fragments of Java you can make whiskers with functions tailored to particular applications.

Behavior of the rat model

Figure 7. Buzzer component definition
Returning to the rat model in figure 1, you can see it has a whisker on each side. The physical positions are set by the position of the plug coming out of the whisker component. It also has a lever pressing component which we'll skip for now, and two buzzers. The spike port from the whisker goes to the buzzer which has the component definition shown in figure 7 and uses script shown alongside. The definition specifies just one input port and three floating point numeric fields for the frequency, volume and duration of the sound.



public void init() {
}


public void receive_buzz() { 
     SoundSystem.beep(p.getFrequency(), 
                      p.getVolume(), 
                      p.getDuration());      
}

The methods in this script are automatically generated, so defining the buzzer function actually involves only one line of code. It calls a system method called "beep" in the system class "SoundSystem". The parameters of the sound are set by the model builder when they fill in the buzzer fields frequency, volume and duration. These are fetched from the parameter object and sent to the "beep" method. This is done inside the "receive_buzz" method so the final effect is that computer beeps each time a whisker sends a spike. The two buzzers are set to different frequencies so the left and right whiskers sound different.

So far, the rat has a physical presence, a couple of sensors and some auditory feedback to say what the sensors are doing. The next step is to define how the rat should respond to these sensors. There are two main ways of doing this: you can either specify a wiring diagram showing how spikes and other signals propagate and feed back to the rat; or you can define a model level script to implement the rat behavior. Here we take the second approach.

Model level scripting

Figure 8. Control script for the rat model

The text view of the rat model is shown in figure 8. One of the fields is called the "control script". This is where script based logic can go to control one single model. Such a script is rather different from whiskers script above. That defined the behavior of the whiskers component in general and gets used wherever that component is used. The control script here is specific for a particular rat model. [Footnote - there is plenty of scope for confusion here because there is also the possibility of attaching a script to the rat component definition. At present, that definition is extremely simple (it hasn't been covered here, but it just says what components are allowed to be included in a rat) but it should probably take on some of the logic that is accessed directly from the model script in this example - there are likely to be some changes to the methods available in model scripts in future].

When you click "refresh" for a control script the system examines all the components you have put in the model and creates empty methods for all the outputs that are possible from the model. In addition to these outputs there are a number of methods available from the script to control the rat as shown in figure 9. Of these, the current model uses only two: "setBearing(bearing)" to set the direction of the rat, and "step(distance)" to tell it to (try to) take a step.

Figure 9. Auxiliary info for the rat model script

double bearing = 0.; // angle clockwize from north in degrees
 
public void init() {
}

public void advance(TimePoint tp) {
    bearing += 8 * (Math.random() - 0.5);
    setBearing(bearing);  
    step(rp.stepLength);	      
}

public void receive_spike_leftWhiskers() {
    //    report("left whiskers");
    // turn a little right
     bearing += 20;
    setBearing(bearing);
}


public void receive_spike_rightWhiskers() {
    // report("right whisker"); 
    // turn a little left;
     bearing -= 20;
     setBearing(bearing);
}


public void receive_canPress_paw() {
}


The full rat control script is shown alongside. Again, the script is fairly short. Each timestep of the simulation, the bearing of the rat is randomly moved a little left or right, and a step is taken using the step length specified in the corresponding parameter in the model. In addition, whenever one of the whiskers sends a spike this causes the rat to turn away from the direction of contact.

Maze model

Figure 10. Maze model

The maze model is shown in figure 10. It has a number of components that are not used in this example. Indeed, the only bits that matter are the wall and the position component labelled "start". The wall is crated by dragging a closed shape (the little blue triangle) onto the diagram and adding points by first selecting the shape and then dragging the pink points. If you need to delete a point, click the wastebin, then select points to delete. To stop deleting, click the background.

The "start" component defines a position in the maze and attaches a label to it. This makes it easier to write the final script that governs the whole "rat in maze" experiment because you can include statements like "rat.moveTo('maze.start');" rather than specifying the coordinates of the position the rat should move to. In fact, this is the only executable line in the top-level control script for the model called MazeExperimnent_0. In general, however, you might want a longer script here if, for example, you want to repeatedly return the rat to the start position.

Just like the rat model, the maze logic can be implemented either by a circuit diagram or by a control script (combinations of the two are fine as well). The control script has access to any of the components you place in the maze, and receives the signals they generate.

Maze experiment model

Figure 11. Maze experiment
component definition

The last part of the model combines a rat with a maze. The component definition is shown in figure 11. In this case, the specification says the model should link to one thing of type "Rat" and one thing of type "Maze". The fields in the diagram are reference fields that pick one element from among the set of all elements of a given type (or types) in the workspace. The properties of the rat field are shown in figure 12.

Figure 12. Properties of the rat reference field

The key part of this specification is the target type field which says what types of model are acceptable values for the reference. Clicking edit brings up a dialog (shown in figure 13) to select model definitions from the workspace. In this case only "Rat" is selected, though if there were several different types of rat, then you might want multiple selections here. The same chooser is used for set fields, which specify a set of objects (e.g. the rat definition has a set field called components that specifies which types of thing are legitimate within a rat model).

Figure 13. Choosing values for the target type
in the rat and maze references
Figure 14. Text view of the overall rat experiment model

The model itself is shown in figure 14 in text mode. Each of the reference fields is presented as a drop-down menu containing all the models of the right type in the workspace. Building this model simply involves selecting which rat to use, which maze to put it in, and the control instructions in the script ("rat.moveTo('maze.start');").

Running the model

Figure 15. Running the model and viewing the results

After selecting the model "MazeExperiment_0", the "run" button is activated and brings up a separate window for running models as shown in in figure 15. At the top is a list of the calculations defined for the model (in this case only one, using the embedded scripts) and below that the results. What is available here depends on what is recorded during the calculation which, in this case is only the position and direction of the rat. The total running time and timestep can be changed here: they are included here, rather than as parameters of the model, so provide the option of running the same model in different ways. The display shows a movie of the rat that can be replayed using the controls at the bottom.

Performance of the model: physical interactions

The instructions that are available to control the rat work at a relatively high level compared to the actual motion. In particular, the system ensures that no part of the rat can cross the boundary. A consequence of this is that, depending on the shape of the rat, it can easily get into a position where it cannot actually turn about its midpoint. The current solution is twofold. If it tries to turn but can't, then it first backs up a little bit and then tries to turn with different points on its perimeter as the center of rotation until one is successful. This explains why it has a tendency to repeatedly hit its nose on the wall. Ideally, these hard-coded behaviors should only come into play as a "last resort" for the model to maintain consistency. A more realistic solution in this case would be to add whiskers in front of the rat so it senses when it is about to hit a wall and stops while it still has room to turn. It is instructive to move the whiskers around a little to see how their position affects the rats ability to run around. Of course, this effect is mainly due to the rather simplistic response of the model to whisker activation, but it does indicate how a richer environmental model can motivate more subtle motion control.

This example only uses the proximity sensing capabilities built into the whiskers, but a number of other options are available in the example components including sounds, lights, levers and food/water dispensers. Furthermore, these communication styles are themselves defined within the component building environment (eg see the Auditory connection type under the "communication" folder). They are built on the three fundamental (hard-coded) physical interactions defined by the underlying engine labelled "physical:contact", "physical:action", and "physical:sense" in the content type menu. The contact interaction registers when a sensor crosses or uncrosses the boundary of another component. The sense interaction is combines a range with a state change for the thing being sensed and registers on and off events when the target changes or the rat enters or leaves the range of the target. It can be used to define lights, sounds or odors. Finally the action interaction combines proximity sensing with the capacity for the rat to act on object (or another rat, for that matter). It leads to "can_do" and "do_action" methods in the component definition script. The first alerts the script that the action is possible and the second can be used by the script to actually do the action. On the receiving side, the component being acted upon has a "action_done" method.

This overview should give a flavor of what is possible within a Catacomb model using relatively short fragments of java within system generated scripts. For more details on writing scripts and other aspects of modelling with Catacomb 3 see the[user guide that is still in preparation].