A simple STRIPS robot using WARPLAN/Prolog

 It’s about time I got another simple Warplan example down on "paper".

Here’s a setup to perform simple robot-related tasks, like switching on lights and moving blocks between rooms. This is only the "bare bones" of a simple system — there is no natural language front end (unless you count Prolog as a natural language :) , and there is no robot executing the low-level commands.

But in honour of my Robo-11 being back on the air — and having now ordered a slew more sensors for it, including sonars ("I don’t know what that is, but it’s that far away from me"), compassers  ("I don’t know where I am, but at least I know that’s north, unless someone around here has a magnet or something") and bluetooth links ("I told you never to SMS me here") – I think it’s about time a mini real-world robot of some kind gets up.

First, let’s start with a map of the world represented by the database.

The next Warplan  part of the exercise defines what facts are added/deleted from the Warplan runtime database for each kind of operator — things like gothrough(WhichDoor,FromRoom,ToRoom) and climbon(WhichBlock), etc.

:- style_check(-singleton).

add(at(robot,P), goto1(P,R)).
add(nextto(robot,X), goto2(X,R)).
add(nextto(X,Y), pushto(X,Y,R)).
add(nextto(Y,X), pushto(X,Y,R)).
add(status(S,on), turnon(S)).
add(on(robot,B), climbon(B)).
add(onfloor, climboff(B)).
add(inroom(robot,R2), gothru(D,R1,R2)).

del(at(X,Z),U) :- moved(X,U).
del(nextto(Z,robot),U) :- !,del(nextto(robot,Z),U).
del(nextto(robot,X),pushto(X,Y,R)) :- !,fail.
del(nextto(robot,B),climbon(B)) :- !,fail.
del(nextto(robot,B),climboff(B)) :- !,fail.
del(nextto(X,Z),U) :- moved(X,U).
del(nextto(Z,X),U) :- moved(X,U).
del(on(X,Z),U) :- moved(X,U).
del(onfloor,climbon(B)).
del(inroom(robot,Z),gothru(D,R1,R2)).
del(status(S,Z),turnon(S)).

moved(robot,goto1(P,R)).
moved(robot,goto2(X,R)).
moved(robot,pushto(X,Y,R)).
moved(X,pushto(X,Y,R)).
moved(robot,climbon(B)).
moved(robot,climboff(B)).
moved(robot,gothru(D,R1,R2)).

can(goto1(P,R), locinroom(P,R) & inroom(robot,R) & onfloor).
can(goto2(X,R), inroom(X,R) & inroom(robot,R) & onfloor).
can(pushto(X,Y,R), pushable(X) & inroom(Y,R) & inroom(X,R) & nextto(robot,X) & onfloor).
can(turnon(lightswitch(S)), on(robot,box(1)) & nextto(box(1),lightswitch(S))).
can(climbon(box(B)), nextto(robot,box(B)) & onfloor).
can(climboff(box(B)), on(robot,box(B))).
can(gothru(D,R1,R2), connects(D,R1,R2) & inroom(robot,R1) & nextto(robot,D) & onfloor).

always(connects(D,R1,R2)) :- connects1(D,R1,R2).
always(connects(D,R2,R1)) :- connects1(D,R1,R2).
always(inroom(D,R1)) :- always(connects(D,R0,R1)).
always(pushable(box(N))).
always(locinroom(point(6),room(4))).
always(inroom(lightswitch(1),room(1))).
always(at(lightswitch(1),point(4))).

connects1(door(N),room(N),room(5)) :- range(N,1,4).

range(M,M,N). range(M,L,N) :- not_equal(L,N), L1 is L+1, range(M,L1,N).

given(start,at(box(N),point(N))) :- range(N,1,3).
given(start,at(robot,point(5))).
given(start,inroom(box(N),room(1))) :- range(N,1,3).
given(start,inroom(robot,room(1))).
given(start,onfloor).
given(start,status(lightswitch(1),off)).

Now let’s feed in some goals. 

This first one, translated to English (we’ll make a simple translator later to do the opposite automajically), says "starting from the start state make it true that the lightswitch is in the on position".

Warplan spits out:


start; goto2(box(1),room(1)); pushto(box(1),lightswitch(1),room(1));climbon(box(1));turnon(lightswitch(1)).


Which, roughly, translates back to: "Go to the box I know is located in room1 (where the robot starts out); push the box up against the lightswitch position (because the robot knows it can’t reach that high); get on the box and turn on the switch".

Here’s another little example:


:- plans(nextto(box(1),box(2)) & nextto(box(2),box(3)),start).


"Put boxes 1, 2 and 3 next-to each other".

The Warplan rules (at the start, above) describe where the boxes and robot are located at the start state. So the planner can immediately spit out:


start; goto2(box(2),room(1));pushto(box(2),box(3),room(1));goto2(box(1),room(1));pushto(box(1),box(2),room(1)).


Finally, a longer problem. Make the above both true as well as have the robot end up in room2.


:- plans(nextto(box(2),box(3)) &nextto(box(3),door(1)) &status(lightswitch(1),on) &nextto(box(1),box(2)) &inroom(robot,room(2)),start).


The planner spits out the shortest plan (since it does a depth-first search) to get the final state:


start; goto2(box(3),room(1)); pushto(box(3),door(1),room(1)); goto2(box(2),room(1)); pushto(box(2),box(3),room(1)); goto2(box(1),room(1));pushto(box(1),lightswitch(1),room(1)); climbon(box(1)); turnon(lightswitch(1)); climboff(box(1)); goto2(box(1),room(1)); pushto(box(1),box(2),room(1)); goto2(door(1),room(1)); gothru(door(1),room(1),room(5)); goto2(door(2),room(5)); gothru(door(2),room(5),room(2)).


As I say, I’ll hook up a simple English language front end, and a simple controller for my Robo-11 at the back end, and maybe hook it to the Internet just for fun.

Happy hacking!

2 Comments

  • On 09.04.07 ash said:

    I haven’t done any Prolog programming since I last touched it at uni but it seems pretty logical to me (pun not originally intended until I realised what I had written :) .

    I might have to check out your planner as well once I get a platform to test it on. I finally shelled out and bought a good bluetooth dongle today so I should be able to connect to my Mindstorms kits and send and receive data while the little guy is driving around the room :)

  • On 09.04.07 kymhorsell said:

    Maybe I can add some "get acquainted" things on my blog.

    I’ve been playing with Prolog for quite a while. I’m not a big expert, but I learned the most important thing is — abandon everything you learned about programming in FORTRAN, C or Pascal.

    And a funny thing — Prolog is exactly the same as simple planners. You write operators (you may call them "procedures") and the Prolog system searches depth-first to find a sequence of them that will reach the goal of completing the top-level goal (you may call that a "command").

    On the "millions of options" front, does the Richtext have a CODE option or something. No matter what I seem to do, the example programs and even output data from programs, seems to get royally screwed up when I insert it. You seem to  get double spaced lines that look like normal text, or you get itty bitty printing and everything run into 1 line.

    The example Prolog code is still messed up somewhere between these 2 extremes and is essentially unreadable by man or machine.