qulog/examples/bottle_collector/bottleCollector.qlg
contains a slight extension of the example program above. In this case, the agent repeatedly finds and goes to a bottle, picks it up and delivers it to a drop. This example is supported by a Python simulation of the environment that you can interact with: creating and removing bottles and dragging them around. Instructions on running the program are in the comments near the beginning of the file.
We now update the type declarations to add dead_centre
as a direction and a type declaration for things the agent is interested in.
The percepts and the robotic actions are extended.
We have added a speed for moving and turning that we vary as we get close to the object of interest.
The top-level TeleoR procedure is more complex as it needs to collect a bottle and deliver it.
The guards are now typically relation calls that are defined in terms of the facts in the BS. For example
You will notice that the action of two rules are both calls on the TeleoR procedure get_next_to
but with different arguments. This procedure is defined as follows.
Again notice that the TeleoR procedure approach takes different arguments (speeds) depending on the situation.
The new construct in this procedure is the action of the last rule: a timed sequence. This is a list of action, time pairs and the semantics for this case is that as long as this rule continues then the turn action will be active for 7 seconds and then the move action will be active for 2 seconds and this will be repeated indefinitely (for as long as the rule is active). This gives the robot a simple wandering (searching) behaviour. If the last entry has no time component then the semantics is that the last action will be active indefinitely.
This example shows where we really need the call stack as it keeps track of any timers (delays) and the evaluator needs to be able to access all the current delays recorded in the call stack.
The objective of the procedure approach is to get close to a given thing.
The second rule uses another new construct: commit_while
. In the example the semantics is, when this rule fires, the rule will stay active within this procedure while dir_or_centre(Th, Dir)
is true for the fired instantiation of Dir even if see(Th,centre) becomes true. In other words when this rule fires it “takes over” the procedure. The purpose of this construct is to “over achieve”.
Superficially, it seems that the commit_while
is not needed - if the second rule is chosen it will turn in the appropriate direction until the thing is seen at centre. The problem is that we will flip rules as soon as the boundary between left, say, and centre. After moving a small distance we will flip back to the second rule. This causes the robot to “flutter” along the boundry between left and centre. If you remove the commit_while
and run the program and simulation you will see this happening. By commiting until dead_centre
is reached it will be a while before it moves off track.
Because the commit_while
takes over you need to be very careful getting the test right. If it's not quite right it might not give up control when it should. The definition of dir_or_centre
covers the cases in which we want the action to continue:
dir_or_centre(Th, Dir)
will fail and the second rule would refire and the action would now be to turn right. On the other hand if the thing stays where it is then dir_or_centre(Th, Dir)
will remain true until the direction becomes dead_centre
.