• No results found

6.2 A Control Architecture for Acting under Uncertainty

6.2.3 Modeling the Low-Level Processes

While during real execution the actual sensor processes provide reply answers, for the task of projection we need a model specifying the possible ways the activation of a sensor process may result in a reply action. For example, we need a model of inspect specifying that if the widget is blemished then inspect will execute reply(inspect, OK) with probability 90%. As mentioned in Section 6.1, we model all low-level processes by probabilistic pGOLOG programs. Thereby, however, we restrict the primitive actions involved in the pGOLOG model of a low- level process: we do not allow the use of send actions; see Appendix A.3.1 for the details. Intuitively, this restriction reflects the fact that only the high-level controller may execute send actions.

The following pGOLOG procedure simulates the effects of inspect, taking into account that inspect is only perfect if the widget is not blemished, and that otherwise there is a 10% probability to erroneously report OK although the widget is blemished. We assume that inspect first operates a camera for 7 seconds to get an image of the widget, and thereafter makes use of image processing routines which need another 3 seconds to provide an estimate. The program makes use of the procedure waitTime discussed below to represent the temporal extent of the low-level process:

proc(inspectProc, [waitTime(7), if(PR,setER,

if(BL,[waitTime(3), prob(0.9,reply(inspect, OK), reply(inspect, OK))], [waitTime(3), reply(inspect, OK)]))]).

Intuitively waitTime(n) waits for n seconds, that is, it remains blocked until start has increased by n. The following is a simple realization of waitTime(n); at the end of this paragraph, we will see a more elegant definition of waitTime(n). We assume a continuous fluent clock, which intuitively represents the actual time. Besides clock, we assume a functional fluent timer, which is set to the actual time by the primitive action setTimer and remains unmodified else. Formally:

5We remark that here OK and OK are ordinary terms, not logical formulas. We could as well use the terms

6.2. A CONTROL ARCHITECTURE FOR ACTING UNDER UNCERTAINTY 119

proc(waitTime(n), [setTimer, waitFor(clock≥ timer + n)]);

Poss(a, s)⊃ timer(do(a, s)) = t ≡ a = setTimer ∧ start(s) = t∨

a6= setTimer ∧ timer(s) = t;

Poss(setTimer, s)≡ True;

clock(S0) = linear(0, 1, 0) and Poss(a, s)⊃ clock(do(a, s)) = clock(s).6

We remark that the conditional in inspectProc is evaluated 7 seconds after the activation of the process, reflecting the fact that the last 3 seconds are merely needed for the execution of image processing routines. Again, we stress that not all pGOLOG programs are meant to be actually executed. In fact, given the robot’s uncertainty about the value of a fluent like BL it is unclear how the high-level controller should execute a pGOLOG program like inspectProc (similarly, the pGOLOG program paintProc cannot be executed by the high-level controller because it cannot directly execute actions like setPA). pGOLOG programs like the above are only used to reason about the effects of low-level processes; during real execution, the actual low-level processes are activated.

Next, let us model the other low-level processes. The following model of the paint process, which replaces the simpler one from Section 6.1, distinguishes two distinct steps: first, the widget is undercoated (UC), after which it is painted (PA). We assume that a possible blemish is removed immediately after activation of paint, and that after 10 seconds the widget is undercoated. After 30 seconds, the widget becomes painted with probability 95%. Finally,

paint confirms completion by means of a reply(painted,>) action:7

proc(paintProc, [clipBL, waitTime(10), if(PR, setER, setUC),

waitTime(20), if(PR, setER, prob(0.95, setPA)), reply(painted,>)]).

Similarly, we model the remaining low-level processes ship and reject, accounting for their duration. The following two programs state that both ship and reject take 10 seconds to

complete execution, whereupon they confirm completion by means of a reply(processed,>)

action. Unlike paint, the low-level processes ship and reject have no uncertain outcomes, which means that we can specify their behavior without making use of prob instructions. However, ship and reject have conditional effects: if the widget is flawed, the execution of ship results in an error, and, vice versa, if it is not flawed, reject causes an error:

proc(shipProc, [waitTime(10), if(PR∨ FL, setER), setPR, reply(processed, >)]);

proc(rejectProc, [waitTime(10), if(PR∨ ¬FL, setER), setPR, reply(processed, >)]).

Aside - A More General Definition of waitTime As defined above, the procedure

waitTime suffers from the drawback that it depends on the fact that no concurrent program affects the value of timer. This assumption is problematic for example if many programs modeling low-level processes are running concurrently. The following is a more general def- inition of waitTime which does not rely on the use of a fluent to memorize the time where it was activated. In the remainder of this thesis, whenever we write waitTime(n) we refer to the following definition:

6

Note that this ensures val(clock(s), t) = t.

7Strictly speaking, paint is thus also a sensor process: the reply(painted,>) action informs the high-level

proc(waitTime(n), waitUntil(start(now ) + n));

proc(waitUntil(t), [True?, waitFor(clock≥ t)]).

The new procedure waitTime(n) is realized by a call to waitUntil with start(now ) + n as argument. waitUntil(t) simply waits until time t, using the continuous fluent clock introduced above. The reason why we need the dummy test True? in waitUntil is quite subtle; the test is needed to ensure that the term start(now ) in the procedure call waitUntil(start(now ) + n) is evaluated as soon as possible even if other programs are running concurrently. That is, the dummy test ensures that start(now) is evaluated as soon as the procedure waitTime(n) is called. To see why the dummy test True? is needed, let us consider the following program:

{proc(waitUntilnaive(t), waitFor(clock≥ t));

withPol(waitFor(clock≥ 5), waitUntilnaive(start(now ) + 10))}.

An execution of the above program in situation S0, assuming start(S0) = 0, would result in

do([waitFor(clock≥ 5), waitFor(clock ≥ 5 + 10)], S0), i.e. in a situation with starting time 15

instead of 10! The reason is that the term start(now ) in the low-priority branch is actually re-evaluated after execution of the first waitFor action. The dummy test True? in the body of waitUntil is used to avoid a similarly delayed evaluation of start(now ) if waitTime is called. This trick depends on the transition semantics of cc-Golog regarding procedure invocation (see Sections 4.2.1 and 3.2.3). The dummy test ensures that the execution of the procedure body of waitUntil starts as soon as possible, resulting in a transition to a new configuration which has the same situation component and a remaining program where start(now) has already been evaluated at transition time.

The Overall Low-Level Execution System Given a characterization of every low-level

process in terms of a pGOLOG program, we can characterize the behavior of the whole exe- cution level by a single pGOLOG program. This program models the runtime-system of the robot, that is of the robot’s operating system or “kernel process”, which ensures that a send action results in the activation of the corresponding low-level process:

proc(kernelProc, [reg(fork)6= nil?,

if(reg(fork) = inspect, [reply(fork, nil), withPol(inspectProc, kernelProc)], if(reg(fork) = paint, [reply(fork, nil), withPol(paintProc, kernelProc)],

if(reg(fork) = ship, [reply(fork, nil), withPol(shipProc, kernelProc)], if(reg(fork) = reject, [reply(fork, nil), withPol(rejectProc, kernelProc)],

[reply(fork, nil), kernelProc]))))]).

As long as reg(fork) is nil, nothing happens. If reg(fork) is assigned the name of a low- level process, then reg(fork) is reset to nil, and the low-level process is run concurrently to the operating system’s kernel process. In Section 7.2 (page 152), we will see that this activation scheme is also suited for the activation of low-level processes involving arguments (like the navigation process). We remark that similar to the procedure navProc used as a model of the low-level processes in the previous chapters (see page 72), kernelProc will run forever.

As an aside, we remark that we could as well stick to the activation scheme corresponding to the view where all processes are continuously active. In this case, we could use something like the following program as a model of the overall low-level execution system:

6.2. A CONTROL ARCHITECTURE FOR ACTING UNDER UNCERTAINTY 121 conc(forever([reg(paint) = nil?, reply(paint, nil), paintProc]),

forever([reg(inspect) = nil?, reply(inspect, nil), inspectProc]), forever([reg(ship) = nil?, reply(ship, nil), shipProc]),

forever([reg(reject) = nil?, reply(reject, nil), rejectProc])).

Note that the actual priority ordering of the different programs is arbitrary.