nd Edition
Jennifer Owen edition of the manual uses Stage version 3.2.X as there are significantdifferences with the previous versions of Stage and the previous edition of thismanual is now out of date.
1Contents
1 Introduction 4 1.1 A Note on Installing Player/Stage . . . . . . . . . . . . . . . . . 4
2 The Basics 6 2.1 Important File Types . . . . . . . . . . . . . . . . . . . . . . . . 6 2.2 Interfaces, Drivers and Devices . . . . . . . . . . . . . . . . . . . 7
3 Building a World 9 3.1 Building an Empty World . . . . . . . . . . . . . . . . . . . . . . 9 3.1.1 Models . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.1.2 Describing the Player/Stage Window . . . . . . . . . . . . 14 3.1.3 Making a Basic Worldfile . . . . . . . . . . . . . . . . . . 15 3.2 Building a Robot . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.2.1 Sensors and Devices . . . . . . . . . . . . . . . . . . . . . 16 3.2.2 An Example Robot . . . . . . . . . . . . . . . . . . . . . . 20 3.3 Building Other Stuff . . . . . . . . . . . . . . . . . . . . . . . . . 30
2 5.3.5 Move To Item . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.3.6 Collect Item . . . . . . . . . . . . . . . . . . . . . . . . . . 60 5.4 Simulating Multiple Robots . . . . . . . . . . . . . . . . . . . . . 62
6 Useful Links 65
7 Appendices 67 7.1 A. General Stage Model of Bigbob . . . . . . . . . . . . . . . . . 67 7.2 B. Worldfile, Containing Robot and Items in World . . . . . . . . 69 7.3 C. Configuration file for Bigbob World . . . . . . . . . . . . . . . 71 7.4 D. Controlling Code for Bigbob Robot Simulation . . . . . . . . 71
3Chapter 1
Introduction
4For MAC users you might find the following install instructions useful: installing-playerstage-on-os-x-with.htmlAlternatively, you could try the suggestions on the Player “getting help” page:
5Chapter 2
The Basics
The .world file tells Player/Stage what things are available to put in the world.In this file you describe your robot, any items which populate the world andthe layout of the world. The .inc file follows the same syntax and format of a.world file but it can be included. So if there is an object in your world thatyou might want to use in other worlds, such as a model of a robot, putting therobot description in a .inc file just makes it easier to copy over, it also meansthat if you ever want to change your robot description then you only need todo it in one place and your multiple simulations are changed too. The .cfg file is what Player reads to get all the information about the robotthat you are going to use.This file tells Player which drivers it needs to use inorder to interact with the robot, if you’re using a real robot these drivers arebuilt in to Player1 , alternatively, if you want to make a simulation, the driveris always Stage (this is how Player uses Stage in the same way it uses a robot:it thinks that it is a hardware driver and communicates with it as such). The.cfg file tells Player how to talk to the driver, and how to interpret any datafrom the driver so that it can be presented to your code. Items described in the.world file should be described in the .cfg file if you want your code to be able tointeract with that item (such as a robot), if you don’t need your code to interactwith the item then this isn’t necessary. The .cfg file does all this specificationusing interfaces and drivers, which will be discussed in the following section. 1 Or you can download or write your own drivers, but I’m not going to talk about how to
do this here.
62.2 Interfaces, Drivers and Devices • Drivers are pieces of code that talk directly to hardware. These are built in to Player so it is not important to know how to write these as you begin to learn Player/Stage. The drivers are specific to a piece of hardware so, say, a laser driver will be different to a camera driver, and also different to a driver for a different brand of laser. This is the same as the way that drivers for graphics cards differ for each make and model of card. Drivers produce and read information which conforms to an “interface”. • Interfaces are a set way for a driver to send and receive information from Player. Like drivers, interfaces are also built in to Player and there is a big list of them in the Player manual2 . They specify the syntax and semantics of how drivers and Player interact. • A device is a driver that is bound to an interface so that Player can talk to it directly. This means that if you are working on a real robot that you can interact with a real device (laser, gripper, camera etc) on the real robot, in a simulated robot you can interact with their simulations.
The official documentation actually describes these 3 things quite well withan example.
The last paragraph there gets a bit technical, but don’t worry. Player talks toparts of the robot using ports (the default port is 6665), if you’re using Stagethen Player and Stage communicate through these ports (even if they’re running 2 interfaces.html
7on the same computer). All this line does is tell Player which port to listen toand what kind of data to expect. In the example it’s laser data which is beingtransmitted on port 6665 of the computer that Player is running on (localhost).You could just as easily connect to another computer by using its IP addressinstead of “localhost”. The specifics of writing a device address in this way willbe described in section 4.
8Chapter 3
Building a World
First we will run a world and configuration file that comes bundled with Stage.In your bash shell navigate to the Stage/worlds folder, by default (in Linux atleast) this is /usr/local/share/stage/worlds. Once in the correct folder type thefollowing command to run the “simple world” that comes with Player/Stage:
player simple.cfgAssuming Player/Stage is installed properly you should now have a windowopen which looks figure 3.1. Congratulations, you can now build Player/Stage simulations! You may notethat the robot in the simple.cfg simulation will immediately start moving, don’tworry about this for now, we will discuss how to achieve this in section 3.2.
driver( name "stage" plugin "stageplugin"
provides ["simulation:0" ]
9Figure 3.1: The simple.cfg world after being run
10which conforms to the simulation interface. To build the simulation Playerneeds to look in the worldfile called empty.world which is stored in the samefolder as this .cfg. If it was stored elsewhere you would have to include a filepath,for example ./worlds/empty.world. Lines that begin with the hash symbol(#) are comments. When you build a simulation, any simulation, in Stage theabove chunk of code should always be the first thing the configuration file says.Obviously the name of the worldfile should be changed depending on what youcalled it though. Now a basic configuration file has been written, it is time to tell Player/Stagewhat to put into this simulation. This is done in the .world file.
3.1.1 ModelsA worldfile is basically just a list of models that describes all the stuff in thesimulation. This includes the basic environment, robots and other objects. Thebasic type of model is called “model”, and you define a model using the followingsyntax:define model_name model( # parameters)This tells Player/Stage that you are defining a model which you have calledmodel_name, and all the stuff in the round brackets are parameters of themodel. To begin to understand Player/Stage model parameters, let’s look atthe map.inc file that comes with Stage, this contains the floorplan model,which is used to describe the basic environment of the simulation (i.e. walls therobots can bump into):define floorplan model( # sombre, sensible, artistic color "gray30"
gui_nose 0 gui_grid 0 gui_move 0 gui_outline 0 gripper_return 0 fiducial_return 0 laser_return 1)We can see from the first line that they are defining a model called floorplan. • color: Tells Player/Stage what colour to render this model, in this case it is going to be a shade of grey.
11 • boundary: Whether or not there is a bounding box around the model. This is an example of a binary parameter, which means the if the number next to it is 0 then it is false, if it is 1 or over then it’s true. So here we DO have a bounding box around our “map” model so the robot can’t wander out of our map. • gui_nose: this tells Player/Stage that it should indicate which way the model is facing. Figure 3.2 shows the difference between a map with a nose and one without. • gui_grid: this will superimpose a grid over the model. Figure 3.3 shows a map with a grid. • gui_move: this indicates whether it should be possible to drag and drop the model. Here it is 0, so you cannot move the map model once Player/Stage has been run. In section 3 when the Player/Stage example simple.cfg was run it was possible to drag and drop the robot because its gui_move variable was set to 1. • gui_outline: indicates whether or not the model should be outlined. This makes no difference to a map, but it can be useful when making models of items within the world. • fiducial_return: any parameter of the form some sensor return de- scribes how that kind of sensor should react to the model. “Fiducial” is a kind of robot sensor which will be described later in section 3.2.1. Setting fiducial_return to 0 means that the map cannot be detected by a fiducial sensor. • gripper_return: Like fiducial_return, gripper_return tells Player/Stage that your model can be detected by the relevant sensor, i.e. it can be gripped by a gripper. Here gripper_return is set to 0 so the map cannot be gripped by a gripper. To make use of the map.inc file we put the following code into our worldfile:include "map.inc"This inserts the map.inc file into our world file where the include line is. Thisassumes that your worldfile and map.inc file are in the same folder, if they arenot then you’ll need to include the filepath in the quotes. Once this is done wecan modify our definition of the map model to be used in the simulation. Forexample:floorplan( bitmap "bitmaps/helloworld.png" size [12 5 1])What this means is that we are using the model “floorplan”, and making someextra definitions; both “bitmap” and “size” are parameters of a Player/Stagemodel. Here we are telling Player/Stage that we defined a bunch of parameters
12Figure 3.2: The left picture shows an empty map without a nose. The rightpicture shows the same map with a nose to indicate orientation, this is thehorizontal line from the centre of the map to the right, it shows that the mapis actually facing to the right.
Figure 3.3: An empty map with gui grid enabled. With gui grid disabled thiswould just be an empty white square.
13Figure 3.4: The left image is our ”helloworld.png” bitmap, the right image iswhat Player/Stage interprets that bitmap as. The coloured areas are walls, therobot can move everywhere else.
for a type of model called “floorplan” (contained in map.inc) and now we’reusing this “floorplan” model definition and adding a few extra parameters. • bitmap: this is the filepath to a bitmap, which can be type bmp, jpeg, gif or png. Black areas in the bitmap tell the model what shape to be, non- black areas are not rendered, this is illustrated in figure 3.4. In the map.inc file we told the map that its “color” would be grey. This parameter does not affect how the bitmaps are read, Player/Stage will always look for black in the bitmap, the color parameter just alters what colour the map is rendered in the simulation. • size: This is the size in metres of the simulation. All sizes you give in the world file are in metres, and they represent the actual size of things. If you have 3m x 4m robot testing arena that is 2m high and you want to simulate it then the size is [3 4 2]. The first number is the size in the x dimension, the second is the y dimension and the third is the z dimension. A full list of model parameters and their descriptions can be found in theofficial Stage manual1 . Most of the useful parameters have already been de-scribed here, however there are a few other types of model which are relevantto building simulations of robots, these will be described later in section 3.2.
14window( # parameters...) The two most important parameters for the window are size and scale. • size: This is the size the simulation window will be in pixels. You need to define both the width and height of the window using the following syntax: size [width height]. • scale: This is how many metres of the simulated environment each pixel shows. The bigger this number is, the smaller the simulation becomes. The optimum value for the scale is fwindow size loorplan size and it should be rounded downwards so the simulation is a little smaller than the window it’s in, some degree of trial and error is needed to get this right.
A full list of window parameters can be found in the Stage manual under“WorldGUI”2 .
15 Figure 3.5: Our Empty World.
floorplan( bitmap "bitmaps/cave.png" size [15 15 1.5])If we save the above code as empty.world (correcting any filepaths if necessary)we can run its corresponding empty.cfg file (see section 3.1) to get the simulationshown in figure 3.5.
16but the stuff already included in Player/Stage should be sufficient for mostpeople’s needs. A full list of interfaces that Player supports can be found inthe Player manual4 although only the following are supported by the currentdistribution of Stage (version 3.2.X). Unless otherwise stated, these models usethe Player interface that shares its name:
camera5 The camera model adds a camera to the robot model and allows your code tointeract with the simulated camera. The camera parameters are as follows:
• pantilt [pan tilt]: the horizontal angle the camera can move through (pan) and the vertical angle (tilt). So for instance pantilt [90 20] al- lows the camera to move 45◦ left and 45◦ right and 10◦ up and 10◦ down.
blobfinder6 This simulates colour detection software that can be run on the image fromthe robot’s camera. It is not necessary to include a model of the camera in yourdescription of the robot if you want to use a blobfinder, the blobfinder will workon its own. The blobfinder can only find a model if its blob_return parameteris true. The parameters for the blobfinder are described in the Stage manual,but the most useful ones are here: • colors_count <int>: the number of different colours the blobfinder can detect • colors [ ]: the names of the colours it can detect. This is given to the blobfinder definition in the form ["black" "blue" "cyan"]. These colour names are from the built in X11 colour database rgb.txt. This is built in to Linux.7
• image [x y]: the size of the image from the camera, in pixels. • range <float>: The maximum range that the camera can detect, in metres. • fov <float>: field of view of the blobfinder in RADIANS. 4 interfaces.html 5 model camera.html 6 model blobfinder.html 7 rgb.txt can normally be found at /usr/share/X11/rgb.txt assuming it’s properly installed,
alternatively a Google search for “rgb.txt” will give you the document.
17fiducial8 A fiducial is a fixed point in an image, so the fiducial finder simulates imageprocessing software that locates fixed points in an image. The fiducialfinder isable to locate objects in the simulation whose fiducial_return parameter isset to true. Stage also allows you to specify different types of fiducial using thefiducial_key parameter of a model. This means that you can make the robotsable to tell the difference between different fiducials by what key they transmit.The fiducial finder and the concept of fiducial_keys is properly explained inthe Stage manual. The fiducial sensors parameters are: • range_min: The minimum range at which a fiducial can be detected, in metres. • range_max: The maximum range at which a fiducial can be detected, in metres. • range_max_id: The maximum range at which a fiducial’s key can be accurately identified. If a fiducial is closer that range_max but further away than range_max_id then it detects that there is a fiducial but can’t identify it. • fov: The field of view of the fiducial finder in RADIANS.
ranger9 This simulates any kind of obstacle detection device (e.g. sonars or infra-red sensors). These can locate models whose ranger_return is true. Using aranger model you can define any number of ranger devices and apply them allto a single robot. Unlike the other types of model this doesn’t use the interfacewith its name but instead the sonar interface, there is more about this in section4.2. The parameters for the ranger model and their inputs are described in theStage manual, but basically: • scount <int>: The number of ranger sensors in this ranger model • spose[ranger_number] [x y yaw]: Tells the simulator where the rangers are placed around the robot. How to write the [x y yaw] data is explained in section 3.2.2. • ssize [x y]: how big the sensors are. • sview [min max fov]: defines the maximum and minimum distances that can be sensed and also the field of view in DEGREES.
laser10 A laser is a special case of ranger sensor which only allows one ranger (sothere’s none of the scount, spose stuff), but it has a very large field of view.If a model has its laser_return parameter enabled then a laser can detect it.Details about laser parameters can be found in the Stage manual, however themost useful parameters are: 8 model fiducial.html 9 model ranger.html 10 model laser.html
18 • samples: The number of ranger readings the laser takes. The laser model behaves like a large number of rangers sensors all with the same x and y coordinates relative to the robot’s centre, each of these rangers has a slightly different yaw. The rangers are spaced so that there are samples number of rangers distributed evenly to give the laser’s field of view. So if the field of view is 180◦ and there are 180 samples the rangers are 1◦ apart. • range_max: the maximum range of the laser.
gripper11 The gripper model is a simulation of the gripper you get on a Pioneer robot.12If you put a gripper on your robot model it means that your robot is able topick up objects and move them around within the simulation. The online Stagemanual says that grippers are deprecated in Stage 3.X.X, however this is notactually the case and grippers are very useful if you want your robot to be ableto manipulate and move items. The parameters you can use to customise thegripper model are: • size [x y z]: The x and y dimensions of the gripper.
position13 The position model simulates the robot’s odometry, this is when the robotkeeps track of where it is by recording how many times its wheels spin and theangle it turns. This robot model is the most important of all because it allows therobot model to be embodied in the world, meaning it can collide with anythingwhich has its obstacle_return parameter set to true. The position model usesthe position2d interface, which is essential for Player because it tells Playerwhere the robot actually is in the world. The most useful parameters of theposition model are:
• drive: Tells the odometry how the robot is driven. This is usually “diff” which means the robot is controlled by changing the speeds of the left and right wheels independently. Other possible values are “car” which means the robot uses a velocity and a steering angle, or “omni” which means it can control how it moves along the x and y axes of the simulation.
• localization: tells the model how it should record the odometry “odom” if the robot calculates it as it moves along or “gps” for the robot to have perfect knowledge about where it is in the simulation. 11 model gripper.html 12 The Pioneer grippers looks like a big block on the front of the robot with two big slidersthat close around an object. 13 model position.html
19Figure 3.6: The basic shape we want to make Bigbob, the units on the axes arein metres.
• odom_error [x y angle]: The amount of error that the robot will make in the odometry recordings.
block ( points 6 14 In this example we’re using blocks with the position model type but we could equally use
20 Figure 3.7: The new shape of Bigbob.
point[0] [0.75 0] point[1] [1 0.25] point[2] [1 0.75] point[3] [0.75 1] point[4] [0 1] point[5] [0 0] z [0 1] ))
In the first line of this code we state that we are defining a position modelcalled bigbob. Next block declares that this position model contains a block.The following lines go on to describe the shape of the block; points 6 says thatthe block has 6 corners and point[number] [x y] gives the coordinates of eachcorner of the polygon in turn. It is important to go around the robot doing eachcorner in turn going anti-clockwise around the robot, otherwise Player/Stagewon’t properly render the block. Finally, the z [height_from height_to]states how tall the robot should be, the first parameter being a lower coordinatein the z plane, and the second parameter being the upper coordinate in the zplane. In this example we are saying that the block describing Bigbob’s bodyis on the ground (i.e. its lower z coordinate is at 0) and it is 1 metre tall. If Iwanted it to be from 50cm off the ground to 1m then I could use z [0.5 1].Now in the same way as we built the body we can add on some teeth for Bigbobto collect rubbish between. Figure 3.7 shows Bigbob with teeth plotted onto acartesian grid:
21 # the shape of Bigb] ))
To declare the size of the robot you use the size [x y z] parameter, thiswill cause the polygon described to be scaled to fit into a box which is x by yin size and z metres tall. The default size is 1 x 1 x 1 metres, so because theaddition of rubbish-collecting teeth made Bigbob longer, the size parameter wasneeded to stop Player/Stage from making the robot smaller than it should be.In this way we could have specified the polygon coordinates to be 4 times thedistance apart and then declared its size to be 1.25 x 1 x 1 metres, and wewould have got a robot the size we wanted. For a robot as large as Bigbob this isnot really important, but it could be useful when building models of very smallrobots. It should be noted that it doesn’t actually matter where in the cartesiancoordinate system you place the polygon, instead of starting at (0, 0) it couldjust as easily have started at (-1000, 12345). With the block parameter wejust describe the shape of the robot, not its size or location in the map.You may have noticed that in figures 3.6 and 3.7 Bigbob is facing to the rightof the grid. When you place any item in a Player/Stage simulation they are, by
22 Figure 3.8: A cartesian grid showing how angles are described.
default, facing to the right hand side of the simulation. Figure 3.3 shows thatthe grids use a typical Cartesian coordinate system, and so if you want to alterthe direction an object in the simulation is pointing (its “yaw”) any angles yougive use the x-axis as a reference, just like vectors in a Cartesian coordinatesystem (see figure 3.8) and so the default yaw is 0◦ . This is also why in section3.1 the gui_nose shows the map is facing to the right. Figure 3.9 shows a fewexamples of robots with different yaws. By default, Player/Stage assumes the robot’s centre of rotation is at itsgeometric centre based on what values are given to the robot’s size para-meter. Bigbob’s size is 1.25 x 1 x 1 so Player/Stage will place its centreat (0.625, 0.5, 0.5), which means that Bigbob’s wheels would be closer toits teeth. Instead let’s say that Bigbob’s centre of rotation is in the middleof its main body (shown in figure 3.6) which puts the centre of rotation at(0.5, 0.5, 0.5). To change this in robot model you use the origin [x-offset y-offset z-offset]command:define bigbob position( # actual size size [1.25 1 1] # centre of rotation offset origin [0.125 0 0]
23Figure 3.9: Starting from the top right robot and working anti-clockwise, theyaws of these robots are 0, 90, -45 and 200.
( # actual size size [1.25 1 1] # centre of rotation offset origin [0.125 0 0]
# positonal things drive "diff")
24Figure 3.10: The position of Bigbob’s sonars (in red) relative to its origin. Theorigin is marked with a cross, some of the distances from the origin to the sensorshave been marked. The remaining distances can be done by inspection.
25 scount 4
) The process of working out where the sensors go relative to the origin of therobot is the most complicated part of describing the sensor, the rest is easy. Todefine the size, range and field of view of the sonars we just consult the sonardevice’s datasheet.
26 # which colours to look for colors ["orange" "DarkBlue"])Then we define the properties of the camera, again these come from a datasheet:define bigbobs_eyes blobfinder( # number of colours to look for colors_count 2
# camera parameters image [160 120] #resolution range 5.00 fov 1.047196667 # 60 degrees = pi/3 radians) The last sensor that needs adding to Bigbob is the laser, which will be usedto detect whenever a piece of rubbish has been collected, the laser’s locationon the robot is shown in figure 3.11. Following the same principles as for ourprevious sensor models we can create a description of this laser:define bigbobs_laser laser( # distance between teeth in metres range_max 0.25
27Figure 3.11: The position of Bigbob’s laser (in red) and its distance, in metres,relative to its origin (marked with a cross).
# positonal things drive "diff"
28to add the robot to our worldfile empty.world:include "map.inc"include "bigbob.inc"
bigbob( name "bob1" pose [-5 -6 0 45] color "green")Here we’ve put all the stuff that describes Bigbob into a .inc file bigbob.inc,and when we include this, all the code from the .inc file is inserted into the.world file. The section here is where we put a version of the bigbob model intoour world:bigbob( name "bob1" pose [-5 -6 0 45] color "green")Bigbob is a model description, by not including any define stuff in the topline there it means that we are making an instantiation of that model, withthe name bob1. Using an object-oriented programming analogy, bigbob is ourclass, and bob1 is our object of class bigbob. The pose [x y yaw] parameterworks in the same was as spose [x y yaw] does. The only differences are thatthe coordinates use the centre of the simulation as a reference point and poselets us specify the initial position and heading of the entire bob1 model, notjust one sensor within that model. Finally we specify what colour bob1 should
29 Figure 3.12: Our bob1 robot placed in the empty world.
be, by default this is red. The pose and color parameters could have beenspecified in our bigbob model but by leaving them out it allows us to vary thecolour and position of the robots for each different robot of type bigbob, so wecould declare multiple robots which are the same size, shape and have the samesensors, but are rendered by Player/Stage in different colours and are initialisedat different points in the map.When we run the new empty.world with Player/Stage we see our Bigbob robotis occupying the world, as shown in figure 3.12.
The first thing to define is the shape of the orange. The block parameter isone way of doing this, which we can use to build a blocky approximation of acircle. An alternative to this is to use bitmap which we previously saw beingused to create a map. What the bitmap command actually does is take in a
30Figure 3.13: The left image is the original picture, the right image is itsPlayer/Stage interpretation.
picture, and turn it into a series of blocks which are connected together to makea model the same shape as the picture. This is illustrated in figure 3.13.
In this bit of code we describe a model called orange which uses a bitmap todefine its shape and represents an object which is 15cm x 15cm x 15cm and iscoloured orange. Figure 3.15 shows our orange model next to Bigbob.
Figure 3.14: ./bit- maps/circle.png
31 Building a juice carton model is similarly quite easy:define carton model( # a carton is retangular # so make a square shape and use size[] block ( points 4 point[0] [1 0] point[1] [1 1] point[2] [0 1] point[3] [0 0] z [0 1] )
color "DarkBlue") We can use the block command since juice cartons are boxy, with boxythings it’s slightly easier to describe the shape with block than drawing a bitmapand using that. In the above code I used block to describe a metre cube (sincethat’s something that can be done pretty easily without needing to draw acarton on a grid) and then resized it to the size I wanted using size. Now that we have described basic orange and carton models it’s time toput some oranges and cartons into the simulation. This is done in the same wayas our example robot was put into the world:orange( name "orange1" pose [-2 -5 0 0])
carton( name "carton1" pose [-3 -5 0 0])We created models of oranges and cartons, and now we are declaring that therewill be an instance of these models (called orange1 and carton1 respectively) atthe given positions. Unlike with the robot, we declared the color of the modelsin the description so we don’t need to do that here. If we did have differentcolours for each orange or carton then it would mess up the blobfinding onBigbob because the robot is only searching for orange and dark blue. At thispoint it would be useful if we could have more than just one orange or cartonin the world (Bigbob would not be very busy if there wasn’t much to pick up),it turns out that this is also pretty easy:
32orange(name "orange1" pose [-1 -5 0 0])orange(name "orange2" pose [-2 -5 0 0])orange(name "orange3" pose [-3 -5 0 0])orange(name "orange4" pose [-4 -5 0 0])
33Figure 3.16: The Bigbob robot placed in the simulation along with junk for itto pick up.
34Chapter 4
Writing a Configuration(.cfg) File
35that a driver provides can be used by your code. For a Stage simulated robotthe "stage" driver can provide the interfaces to the sensors discussed in section3.2.1. Each interface shares the same name as the sensor model, so for examplea ranger model would use the ranger interface to interact with Player andso on (the only exception to this being the position model which uses theposition2d interface). The Player manual contains a list of all the differentinterfaces that can be used3 , the most useful ones have already been mentionedin section 3.2.1, although there are others too numerable to list here. The input to the provides parameter is a “device address”, which specifieswhich TCP port an interface to a robot device can be found, section 4.1 has moreinformation about device addresses. This uses the key:host:robot:interface:indexform separated by white space.
provides ["key:host:robot:interface:index" "key:host:robot:interface:index" "key:host:robot:interface:index" ...]
After the two mandatory parameters, the next most useful driver parameteris model. This is only used if "stage" is the driver, it tells Player which partic-ular model in the worldfile is providing the interfaces for this particular driver.A different driver is needed for each model that you want to use. Models thataren’t required to do anything (such as a map, or in the example of section 3.3oranges and boxes) don’t need to have a driver written for them.The remaining driver parameters are requires and plugin. The requires isused for drivers that need input information such as "vfh", it tells this driverwhere to find this information and which interface it uses. The requires para-meter uses the same key:host:robot:interface:index syntax as the provides para-meter. Finally the plugin parameter is used to tell Player where to find all theinformation about the driver being used. Earlier we made a .cfg file in order tocreate a simulation of an empty (or at least unmoving) world, the .cfg file readas follows:
driver( name "stage" plugin "stageplugin"
provides ["simulation:0" ]
This has to be done at the beginning of the configuration file because it tellsPlayer that there is a driver called "stage" that we’re going to use and the codefor dealing with this driver can be found in the stageplugin plugin. This needsto be specified for Stage because Stage is an add-on for Player, for drivers thatare built into Player by default the plugin doesn’t need to be specified. 3 interfaces.html
364.1 Device Addresses - key:host:robot:interface:indexA device address is used to tell Player where the driver you are making willpresent (or receive) information and which interface to use in order to read thisinformation. This is a string in the form key:host:robot:interface:indexwhere each field is separated by a colon. • key: The Player manual states that: “The purpose of the key field is to allow a driver that supports multiple interfaces of the same type to map those interfaces onto different devices”[1]. This is a driver level thing and has a lot to do with the name of the driver that you are using, generally for "stage" the key doesn’t need to be used. If you’re using Player without Stage then there is a useful section about device address keys in the Player manual4 . • host: This is the address of the host computer where the device is located. With a robot it could be the IP address of the robot. The default host is “localhost” which means the computer on which Player is running. • robot: this is the TCP port through which Player should expect to receive data from the interface usually a single robot and all its necessary inter- faces are assigned to one port. The default port used is 6665, if there were two robots in the simulation the ports could be 6665 and 6666 although there’s no rule saying which number ports you can or can’t use. • interface: The interface to use in order to interact with the data. There is no default value for this option because it is a mandatory field. • index: If a robot has multiple devices of the same type, for instance it has 2 cameras to give the robot depth perception, each device uses the same interface but gives slightly different information. The index field allows you to give a slightly different address to each device. So two cameras could be camera:0 and camera:1. This is very different from the key field because having a “driver that supports multiple interfaces of the same type” is NOT the same as having multiple devices that use the same interface. Again there is no default index, as this is a mandatory field in the device address, but you should use 0 as the index if there is only one of that kind of device.If you want to use any of the default values it can just be left out of the device ad-dress. So we could use the default host and robot port and specify (for example)a laser interface just by doing "laser:0". However, if you want to specify fieldsat the beginning of the device address but not in the middle then the separatingcolons should remain. For example if we had a host at "127.0.0.1" with alaser interface then we would specify the address as "127.0.0.1::laser:0",the robot field is empty but the colons around it are still there. You may noticethat the key field here was left off as before. 4 tutorial config.html#device key
374.2 Putting the Configuration File TogetherWe have examined the commands necessary to build a driver for a model in theworldfile, now it is just a case of putting them all together. To demonstrate thisprocess we will build a configuration file for the worldfile developed in section3. In this world we want our code to be able to interact with the robot, so inour configuration file we need to specify a driver for this robot.driver( # parameters...) The inbuilt driver that Player/Stage uses for simulations is called "stage"so the driver name is "stage".driver( name "stage")The Bigbob robot uses position, laser, blobfinder and ranger sensors.These correspond to the position2d, laser, blobfinder and interfaces re-spectively. The ranger sensor is a special case because the ranger interface hasnot currently been implemented (for at least version Stage 3.2.2 or earlier). Towork around this you’ll need to use the sonar interfaces instead (there is ap-parently an IR interface which could be used instead of ranger, but it doesn’tseem to work on either Stage 2 or Stage 3). We want our code to be able to read from these sensors, so we need todeclare interfaces for them and tell Player where to find each device’s data, forthis we use the configuration file’s provides parameter. This requires that weconstruct device addresses for each sensor; to remind ourselves, this is in thekey:host:robot:interface:index format. We aren’t using any fancy drivers, so wedon’t need to specify a key. We are running our robot in a simulation on thesame computer as our Player sever, so the host name is localhost which isthe default, so we also don’t need to specify a host. The robot is a TCP portto receive robot information over, picking which port to use is pretty arbitrarybut what usually happens is that the first robot uses the default port 6665 andsubsequent robots use 6666, 6667, 6668 etc. There is only one robot in oursimulation so we will use port 6665 for all our sensor information from thisrobot. We only have one sensor of each type, so our devices don’t need separateindices. What would happen if we did have several sensors of the same type(like say two cameras) is that we put the first device at index 0 and subsequentdevices using the same interface have index 1, then 2, then 3 and so on.5 Finallywe use interfaces appropriate to the sensors the robot has, so in our examplethese are the position2d, laser, blobfinder interfaces and for our rangerdevices we will use sonar. Putting all this together under the provides parameter gives us: 5 There are lots of ranger sensors in our model but when we created the robot’s sensors in
section 3.2.2 we put them all into the same ranger model. So as far as the configuration fileis concerned there is only one raging device using either the sonar or IR interface, becauseall the separate ranger devices are lumped together into this one model. We don’t need todeclare each ranger on an index of its own.
38driver( name "stage" provides ["6665:position2d:0" "6665:sonar:0" "6665:blobfinder:0" "6665:laser:0" ])
The device addresses can be on the same line as each other or separate lines,just so long as they’re separated by some form of white space. The last thing to do on our driver is the model "model_name" parameterwhich needs to be specified because we are using Player/Stage. This tells thesimulation software that anything you do with this driver will affect the model"model_name" in the simulation. In the simulation we built we named our robotmodel “bob1”, so our final driver for the robot will be:driver( name "stage" provides ["6665:position2d:0" "6665:sonar:0" "6665:blobfinder:0" "6665:laser:0"] model "bob1")
If our simulation had multiple Bigbob robots in, the configuration file driverswould be very similar to one another. If we created a second robot in ourworldfile and called it “bob2” then the driver would be:driver( name "stage" provides ["6666:position2d:0" "6666:sonar:0" "6666:blobfinder:0" "6666:laser:0"] model "bob2")Notice that the port number and model name are the only differences becausethe robots have all the same sensors. A driver of this kind can be built for any model that is in the worldfile,not just the robots. For instance a map driver can be made which uses themap interface and will allow you to get size, origin and occupancy data aboutthe map. The only requirement is that if you want to do something to themodel with your code then you need to build a driver for it in the configurationfile. Finally when we put the bit which declares the stage driver (this part iscompulsory for any simulation configuration file) together with our drivers forthe robot we end up with our final configuration file:
39driver( name "stage" plugin "stageplugin"
provides ["simulation:0" ]
driver( name "stage" provides ["6665:position2d:0" "6665:sonar:0" "6665:blobfinder:0" "6665:laser:0"] model "bob1")
40Chapter 5
To learn how to write code for Player or Player/Stage it helps to understand thebasic structure of how Player works. Player uses a Server/Client structure inorder to pass data and instructions between your code and the robot’s hardware.Player is a server, and a hardware device1 on the robot is subscribed as a clientto the server via a thing called a proxy. The .cfg file associated with your robot(or your simulation) takes care of telling the Player server which devices areattached to it, so when we run the command player some_cfg.cfg this startsup the Player server and connects all the necessary hardware devices to theserver. Figure 5.1 shows a basic block diagram of the structure of Player whenimplemented on a robot. In Player/Stage the same command will start thePlayer server and load up the worldfile in a simulation window, this runs onyour computer and allows your code to interact with the simulation rather thanhardware. Figure 5.2 shows a basic block diagram of the Player/Stage structure.Your code must also subscribe to the Player server so that it can access theseproxies and hence control the robot. Player has functions and classes which willdo all this for you, but you still need to actually call these functions with yourcode and know how to use them. Player is compatable with C, C++ or Python code, however in this manualwe will only really be using C++ because it is the simplest to understand. Theprocess of writing Player code is mostly the same for each different languagethough. The Player and Player proxy functions have different names for eachlanguage, but work in more or less the same way, so even if you don’t plan onusing C++ or Stage this section will still contain helpful information. Before beginning a project it is highly recommended that for any programsother than basic examples you should always wrap your Player commandsaround your own functions and classes so that all your code’s interactions withPlayer are kept together the same file. This isn’t a requirement of Player, it’sjust good practice. For example, if you upgrade Player or if for some reason yourrobot breaks and a certain function no longer works you only have to changepart of a file instead of searching through all your code for places where Player 1 remember, a device is a piece of hardware that uses a driver which conforms to an interface.
41Figure 5.1: The server/client control structure of Player when used on a robot.There may be several proxies connected to the server at any time.
42functions have been used. Finally, in order to compile your program you use the following commands(in Linux):g++ -o example0 `pkg-config --cflags playerc++` example0.cc `pkg-config--libs playerc++` That will compile a program to a file called example0 from the C++ codefile example0.cc. If you are coding in C instead then use the following com-mand:gcc -o example0 `pkg-config --cflags playerc` example0.c `pkg-config--libs playerc`
What this line does is declare a new object which is a PlayerClient calledclient_name which connects to the Player server at the given address. Thehostname and port is like that discussed in section 4.1. If your code is run-ning on the same computer (or robot) as the Player server you wish to connectto then the hostname is “localhost” otherwise it will be the IP address of thecomputer or robot. The port is an optional parameter usually only needed forsimulations, it will be the same as the port you gave in the .cfg file. This isonly useful if your simulation has more than one robot in and you need yourcode to connect to both robots. So if you gave your first robot port 6665 andthe second one 6666 (like in the example of section 4.2) then you would needtwo PlayerClients, one connected to each robot, and you would do this with thefollowing code:PlayerClient robot1("localhost", 6665);PlayerClient robot2("localhost", 6666);If you are only using one robot and in your .cfg file you said that it wouldoperate on port 6665 then the port parameter to the PlayerClient class is notneeded.Once we have established a PlayerClient we should connect our code to thedevice proxies so that we can exchange information with them. Which proxiesyou can connect your code to is dependent on what you have put in your config-uration file. For instance if your configuration file says your robot is connectedto a laser but not a camera you can connect to the laser device but not thecamera, even if the robot (or robot simulation) has a camera on it. Proxies take the name of the interface which the drivers use to talk to Player.Let’s take part of the Bigbob example configuration file from section 4.2:
43driver( name "stage" provides ["6665:position2d:0" "6665:sonar:0" "6665:blobfinder:0" "6665:laser:0" ])
Here we’ve told the Player server that our “robot” has devices which use theposition2d, sonar, blobfinder and laser interfaces. In our code then, we shouldconnect to the position2d, sonar, blobfinder and laser proxies like so:Position2dProxy positionProxy_name(&client_name,index);SonarProxy sonarProxy_name(&client_name,index);BlobfinderProxy blobProxy_name(&client_name,index);LaserProxy laserProxy_name(&client_name,index);A full list of which proxies Player supports can be found in the Player manual2 ,they all follow the convention of being named after the interface they use. Inthe above case xProxy_name is the name you want to give to the proxy object,client_name is the name you gave the PlayerClient object earlier and index isthe index that the device was given in your configuration file (probably 0).
driver( name "stage" provides ["6665:position2d:0" "6665:sonar:0" "6665:blobfinder:0" "6665:laser:0"] model "bob1") 2 1 1ClientProxy.html
44To set up a PlayerClient and then connect to proxies on that server we can useprinciples discussed in this section to develop the following code:
#include <stdio.h>#include <libplayerc++/playerc++.h>
PlayerClient robot("localhost");
Position2dProxy p2dProxy(&robot,0); SonarProxy sonarProxy(&robot,0); BlobfinderProxy blobProxy(&robot,0); LaserProxy laserProxy(&robot,0);
5.2.1 Position2dProxyThe Position2dProxy is the number one most useful proxy there is. It controlsthe robot’s motors and keeps track of the robot’s odometry (where the robotthinks it is based on how far its wheels have moved).
Get/SetSpeedThe SetSpeed command is used to tell the robot’s motors how fast to turn.There are two different SetSpeed commands that can be called, one is for robotsthat can move in any direction and the other is for robots with differential orcar-like drives. • SetSpeed(double XSpeed, double YSpeed, double YawSpeed) 3 1 1ClientProxy.html
45Figure 5.3: A robot on a cartesian grid. This shows what directions the X andY speeds will cause the robot to move in. A positive yaw speed will turn therobot in the direction of the + arrow, a negative yaw speed is the direction ofthe - arrow.
Get PosThis function interacts with the robot’s odometry. It allows you to monitorwhere the robot thinks it is. Coordinate values are given relative to its startingpoint, and yaws are relative to its starting yaw.
46 • GetXPos(): gives current x coordinate relative to its x starting position. • GetYPos(): gives current y coordinate relative to its y starting position.
SetMotorEnable()This function takes a boolean input, telling Player whether to enable the motorsor not. If the motors are disabled then the robot will not move no matter whatcommands are given to it, if the motors are enabled then the motors will alwayswork, this is not so desirable if the robot is on a desk or something and is likelyto get damaged. Hence the motors being enabled is optional. If you are usingPlayer/Stage, then the motors will always be enabled and this command doesn’tneed to be run. However, if your code is ever likely to be moved onto a realrobot and the motors are not explicitly enabled in your code, then you may endup spending a long time trying to work out why your robot is not working.
5.2.2 SonarProxyThe sonar proxy can be used to receive the distance from the sonar to an obstaclein metres. To do this you use the command: • sonarProxy_name[sonar_number]Where sonarProxy_name is the SonarProxy object and sonar_number is thenumber of the ranger. In Player/Stage the sonar numbers come from the orderin which you described the ranger devices in the worldfile. In section 3.2.2 wedescribed the ranger sensors for the Bigbob robot like so:define bigbobs_sonars ranger( # number of sonars scount 4
) 4 See section 3.2.1 for how to give the robot perfect odometry.
47Figure 5.4: How laser angles are referenced. In this diagram the laser is pointingto the right along the dotted line, the angle θ is the angle of a laser scan point,in this example θ is negative.
In this example sonarProxy_name[0] gives us the distance from the left toothranger to an obstacle, sonarProxy_name[1] gives the distance from the frontright tooth to an obstacle, and so on. If no obstacle is detected then the functionwill return whatever the ranger’s maximum range is.
5.2.3 LaserProxyA laser is a special case of ranger device, it makes regularly spaced range meas-urements turning from a minimum angle to a maximum angle. Each measure-ment, or scan point, is treated as being done with a separate ranger. Whereangles are given they are given with reference to the laser’s centre front (seefigure 5.4).
• GetCount: The number of laser scan points that the laser measures. • laserProxy_name[laser_number] The range returned by the laser_numberth scan point. Scan points are numbered from the minimum angle at index 0, to the maximum angle at index GetCount(). • GetBearing[laser_number]: This gets the angle of the laser scan point. • GetRange[laser_number]: returns the range measured by the scan point laser_number. This is the same as doing laserProxy_name[laser_number]. • MinLeft: Gives the minimum range returned by a scan point on the left hand side of the laser. • MinRight: Gives the minimum range returned by a scan point on the right hand side of the laser.
48Figure 5.5: A laser scanner. The minimum angle is the angle of the rightmostlaser scan, the maximum angle is the leftmost laser scan. θ is the scan resolutionof the laser, it is the angle between each laser scan, given in 0.01 degrees.
5.2.4 RangerProxyThe RangerProxy is a proxy for more general ranging, it supports the sonar,laser and IR proxies. It has the same function as the SonarProxy in that you canuse the code rangerProxy_name[ranger_number] to return the distance fromranger ranger_number to an obstacle. It also has many of the same functionsas the LaserProxy, such as a minimum angle and a maximum angle and ascanning resolution, mostly these are for retrieving data about how the rangersare arranged or what size the ranging devices are.
5.2.5 BlobfinderProxyThe blobfinder module analyses a camera image for areas of a desired colourand returns an array of the structure playerc_blobfinder_blob_t, this is thestructure used to store blob data. First we will cover how to get this data fromthe blobfinder proxy, then we will discuss the data stored in the structure.
Once we receive the blob structure from the proxy we can extract data weneed. The playerc_blobfinder_blob_t structure contains the following fields: • color: The colour of the blob it detected. This is given as a hexadecimal value.
49Figure 5.6: What the fields in playerc blobfinder blob t mean. The blob onthe left has a geometric centre at (x, y), the blob on the right has a boundingbox with the top left corner at (lef t, top) pixels, and a lower right coordinateat (right, bottom) pixels. Coordinates are given with reference to the top leftcorner of the image.
• right: The horizontal coordinate of the right hand side of the blob’s bounding box (see figure 5.6). • top: The vertical coordinate of the top side of the blob’s bounding box (see figure 5.6). • bottom: The vertical coordinate of the bottom side of the blob’s bounding box (see figure 5.6).
5.2.6 GripperProxyThe GripperProxy allows you to control the gripper, once the gripper is holdingan item, the simulated robot will carry it around wherever it goes. Without agripper you can only jostle an item in the simulation and you would have tomanually tell the simulation what to do with an item. The GripperProxy canalso tell you if an item is between the gripper teeth because the gripper modelhas inbuilt beams which can detect if they are broken.
• GetBeams: This command will tell you if there is an item inside the grip- per. If it is a value above 0 then there is an item to grab.
50 • GetState: This will tell you whether the gripper is opened or closed. If the command returns a 1 then the gripper is open, if it returns 2 then the gripper is closed. • Open: Tells the gripper to open. This will cause any items that were being carried to be dropped. • Close: Tells the gripper to close. This will cause it to pick up anything between its teeth.
5.2.7 SimulationProxyThe simulation proxy allows your code to interact with and change aspects ofthe simulation, such as an item’s pose or its colour.
Get/Set PoseThe item’s pose is a special case of the Get/SetProperty function, because it islikely that someone would want to move an item in the world they created aspecial function to do it.SetPose2d(char *item_name, double x, double y, double yaw)In this case item_name is as with Get/SetProperty, but we can directly specifyits new coordinates and yaw (coordinates and yaws are given with reference tothe map’s origin).GetPose2d(char *item_name, double &x, double &y, double &yaw)This is like SetPose2d only this time it writes the coordinates and yaw to thegiven addresses in memory.
Get/Set PropertyIn version 3.2.2 of Stage the Get/SetProperty simulation proxy functions areunimplemented and will not work. Hopefully this will be rectified in futurereleases, but if you desperately need this functionality you can use an earlierrelease of Stage, and the previous edition of this manual describes how to getand set a model’s property in those distributions. In this edition of the manualI will try and describe how I think these functions will work in future versionsof Stage based on the Stage 3.2.2 source code and how the previous version ofthese functions worked. To change a property of an item in the simulation we use the following func-tion:SetProperty(char *item_name, char *property, void *value, size_t value_len) • item_name: this is the name that you gave to the object in the worldfile, it could be any model that you have described in the worldfile. For example, in section 3.2.2 in the worldfile we declared a Bigbob type robot which we called “bob1” so the item_name for that object is “bob1”. Similarly in section 3.3 we built some models of oranges and called the “orange1” to “orange4” so the item name for one of these would be “orange1”. Anything that is a model in your worldfile can be altered by this function, you just need to have named it, no drivers need to be declared in the configuration file for this to work either. We didn’t write drivers for the oranges but we could still alter their properties this way.
51 • property: There are only certain properties about a model that you can change. You specify which one you want with a string. Since the properties you can change are fixed then these strings are predefined (see stage.hh5 ):
• value_len: is the size of the value you gave in bytes. This can easily be found with the C or C++ sizeof() operator.The value parameter is dependant on which property you want to set. • "_mp_color": This requires an array of four float values, scaled between 0 and 1. The first index of the array is the red component of the colour, the second is the green, third is blue and fourth is alpha (how light or dark the colour is, usually 1). For example if we want a nice shade of green, which has RGB components 171/224/110 we scale these between 0 and 1 by dividing by 255 to get 0.67/0.88/0.43 we can now put this into a float array with the line float green[]={0.67, 0.88, 0.43, 1};. This array can then be passed into our SetProperty function like so: SetProperty("model_name", "_mp_color", (void*)green, sizeof(float)*4 ); • "_mp_watts" and "_mp_mass": These both require a double as their value.
525.2.8 General Useful CommandsRead()To make the proxies update with new sensor data we need to tell the playerserver to update, we can do this using the PlayerClient object which we used toconnect to the server. All we have to do is run the command playerClient_name.Read()every time the data needs updating (where playerClient name is the name yougave the PlayerClient object). Until this command is run, the proxies and anysensor information from them will be empty. The devices on a typical robot areasynchronous and the devices in a Player/Stage simulation are also asynchron-ous, so running the Read() command won’t always update everything at thesame time, so it may take several calls before some large data structures (suchas a camera image) gets updated.
GetGeom()Most of the proxies have a function called GetGeom or GetGeometry or RequestGeometry,or words to that effect. What these functions do is tell the proxy retrieve in-formation about the device, usually its size and pose (relative to the robot).The proxies don’t know this by default since this information is specific to therobot or the Player/Stage robot model. If your code needs to know this kind ofinformation about a device then the proxy must run this command first.
• Moving towards item: for when an item is spotted and the robot wants to collect it • Collecting item: for dealing with collecting items.The robot will also avoid obstacles but once this is done it will switch back toits previous behaviour. The control will follow the state transitions shown infigure 5.7.
53Figure 5.7: The state transitions that the Bigbob rubbish collecting robot willfollow.
PlayerClient robot("localhost");
Position2dProxy p2dProxy(&robot,0); SonarProxy sonarProxy(&robot,0); BlobfinderProxy blobProxy(&robot,0); LaserProxy laserProxy(&robot,0);
//request geometries
54p2dProxy.RequestGeom();sonarProxy.RequestGeom();laserProxy.RequestGeom();//blobfinder doesn’t have geometryOnce things are initialised we can enter the main control loop. At this point weshould tell the robot to read in data from its devices to the proxies.while(true){ robot.Read();
//control code}
5.3.3 Wanderfirst we will initialise a couple of variables which will be the forward speed andthe turning speed of the robot, we’ll put this with the proxy initialisations.Position2dProxy p2dProxy(&robot,0);SonarProxy sonarProxy(&robot,0);BlobfinderProxy blobProxy(&robot,0);LaserProxy laserProxy(&robot,0);
Let’s say that Bigbob’s maximum speed is 1 metre/second and it can turn90◦ a second. We will write a small subfunction to randomly assign forward andturning speeds between 0 and the maximum speeds.void Wander(double *forwardSpeed, double *turnSpeed){ int maxSpeed = 1; int maxTurn = 90; double fspeed, tspeed;
tspeed = rand()%(2*maxTurn); tspeed = tspeed-maxTurn; //tspeed is between -maxTurn and +maxTurn
*forwardSpeed = fspeed; *turnSpeed = tspeed;}In the control loop we include a call to this function and then set the resultingspeeds to the motors.
55while(true){ // read from the proxies robot.Read();
//wander Wander(&forwardSpeed, &turnSpeed);
//set motors p2dProxy.SetSpeed(forwardSpeed, dtor(turnSpeed));}The dtor() function is a Player function that turns a number in degrees into anumber in radians. Our calculations have been done in degrees but SetSpeedrequires radians, so this function is used to convert between the two. At presentthe motors are being updated every time this control loop executes, and thisleads to some erratic behaviour from the robot. Using the sleep()6 commandwe will tell the control loop to wait one second between each execution. At thispoint we should also seed the random number generator with the current timeso that the wander behaviour isn’t exactly the same each time. For the sleepcommand we will need to include unistd.h and to seed the random numbergenerator with the current system time we will need to include time.h.#include <stdio.h>#include <unistd.h>#include <time.h>#include <libplayerc++/playerc++.h>
//connect to proxies double forwardSpeed, turnSpeed;
srand(time(NULL));
//enable motors //request geometries
while(true) { // read from the proxies 6 sleep() is a standard C function and is included in the unistd.h header.
56 robot.Read();
//wander Wander(&forwardSpeed, &turnSpeed);
//set motors p2dProxy.SetSpeed(forwardSpeed, dtor(turnSpeed)); sleep(1); }}
57}This is a very basic obstacle avoidance subfunction will update the motor speedsonly if there is an obstacle to avoid. If we call this function just before sendingdata to the motors then it will overwrite any other behaviours so that theobstacle will be avoided. Once the obstacle is no longer in the way then therobot will continue as it was, this will allow us to transition from any behaviourinto obstacle avoidance and then back again, as per the requirement of ourcontrol structure. All we need to do now is call this function in our controlloop:while(true){ // read from the proxies robot.Read();
//wander Wander(&forwardSpeed, &turnSpeed);
//avoid obstacles AvoidObstacles(&forwardSpeed, &turnSpeed, sonarProxy);
//set motors p2dProxy.SetSpeed(forwardSpeed, dtor(turnSpeed)); sleep(1);}
58 int biggestBlob = 0;
return;} We want the robot to transition to this state whenever an item is seen, sowe put a conditional statement in our control loop like so:if(blobProxy.GetCount() == 0){
59 //wander Wander(&forwardSpeed, &turnSpeed);}else{ //move towards the item MoveToItem(&forwardSpeed, &turnSpeed, blobProxy);}
item_t itemList[8];
60 itemList[i].x, itemList[i].y, dummy); }
return;}Here we are making a string of the item names, for example orange1 and storingthat in the item’s name. We then use this string as an input into the GetPose2dfunction so that we can also get the item’s location in the simulation. Next we can begin the “Collect Item” behaviour, which will be triggered bysomething breaking the laser beam. When this happens we will check the areaaround Bigbob’s teeth, as indicated by figure 5.8. We know the distance fromthe centre of this search circle to Bigbob’s origin (0.625m) and the radius ofthe search circle (0.375m), we can get the robot’s exact pose with the followingcode.
double x, y, yaw;simProxy.GetPose2d("bob1", x, y, yaw);Cross referencing the robot’s position with the item positions is a matter oftrigonometry, so isn’t particularly relevant to a manual on Player/Stage. Wewon’t reproduce the code here, but the full and final code developed for theBigbob rubbish collecting robot is included in appendix D. The method weused is to find the Euclidian distance of the items to the circle centre, and thesmallest distance is the item we want to destroy. We made a subfunction calledFindItem that returns the index of the item to be destroyed. Now that we can find the item to destroy it’s fairly simple to trigger oursubfunction when the laser is broken so we can find and destroy an item.
61Figure 5.8: Where to look for items which may have passed through Bigbob’slaser.
RefreshItemList(itemList, simProxy);}
The laser has 180 samples, so sample number 90 is the one which is perpendicularto Bigbob’s teeth. This point returns a maximum of 0.25, so if its range wasto fall below this then something has passed through the laser beam. We thenfind the item closest to the robot’s teeth and move that item to coordinate(−10, −10) so it is no longer visible or accessible.Finally we have a working simulation of a rubbish collecting robot! The fullcode listing is included in appendix D, the simulation world and configurationfiles are in appendices B and C respectively.
bigbob
62(name "bob2"pose [5 6 225]color "yellow")If there are multiple robots in the simulation, the standard practice is to put eachrobot on its own port (see section 4.1). To implement this in the configurationfile we need to tell Player which port to find our second robot on:
//second robotPlayerClient robot2("localhost", 6666);Position2dProxy p2dprox2(&robot2,0);SonarProxy sprox2(&robot2,0);Each Player Client represents a robot, this is why when you connect to a proxythe PlayerClient is a constructor parameter. Each robot has a proxy for each ofits devices, no robots share a proxy, so it is important that your code connectsto every proxy of every robot in order to read the sensor information. Howyou handle the extra PlayerClients and proxies is dependent on the scale of thesimulation and your own personal coding preferences. It’s a good idea, if there’smore than maybe 2 robots in the simulation, to make a robot class which dealswith connecting to proxies and the server, and processes all the informationinternally to control the robot. Then you can create an instance of this classfor each simulated robot7 and all the simulated robots will run the same code. An alternative to using a port for each robot is to use the same port but adifferent index. This will only work if the robots are all the same (or at leastuse the same interfaces, although different robots could be run on a different 7 obviously the robot’s port number would need to be a parameter otherwise they’ll all
63ports) and the robots only use one index for each of its devices. For example,the Bigbob robot uses interfaces and indexes: position2d:0, sonar:0, blobfinder:0and laser:0 so it never uses more than one index. If we configured two Bigbobrobots to use the same port but a different index our configuration file wouldbe like this:driver( name "stage" provides ["6665:position2d:0" "6665:sonar:0" "6665:blobfinder:0" "6665:laser:0"] model "bob1" )
In our code we could then establish the proxies using only one PlayerClient:PlayerClient robot("localhost", 6665);
//first robotPosition2dProxy p2dprox1(&robot,0);SonarProxy sprox1(&robot,0);
//second robotPosition2dProxy p2dprox2(&robot,1);SonarProxy sprox2(&robot,1);
64Chapter 6
Useful Links
65Bibliography
66Chapter 7
Appendices
#Bigbob’s sonarsdefine bigbobs_sonars ranger( # number of sonars scount 4
# bigbob’s blobfinderdefine bigbobs_eyes blobfinder(# number of colours to look forcolors_count 2
67# which colours to look forcolors ["orange" "DarkBlue"]
# camera parametersimage [160 120] #resolution range 5.00 fov 1.047196667 # 60 degrees = pi/3 radians)
# bigbob’s laserdefine bigbobs_laser laser( # distance between teeth in metres range_max 0.25
# bigbob’s bodydefine bigbob position( # actual size size [1.25 1 1]
block(points 6point[5] [0 0]point[4] [0 1]point[3] [0.75 1]point[2] [1 0.75]point[1] [1 0.25]point[0] [0.75 0]z [0 1])
block
68(points 4point[3] [1 0.75]point[2] [1.25 0.75]point[1] [1.25 0.625]point[0] [1 0.625]z [0 0.5])
block(points 4point[3] [1 0.375]point[2] [1.25 0.375]point[1] [1.25 0.25]point[0] [1 0.25]z [0 0.5])
include "map.inc"include "bigbob.inc"
69# configure the GUI windowwindow(size [700.000 700.000]scale 35)
bigbob(name "bob1"pose [-5 -6 0 45]color "green")
70color "DarkBlue")
driver(name "stage"plugin "stageplugin"
provides ["simulation:0"]
71// Date: 16/04/2010
#include <stdio.h>#include <unistd.h>#include <time.h>#include <libplayerc++/playerc++.h>
struct Item{ char name[16]; double x; double y;}typedef item_t;
/**Randomly assigns new speeds into the given addresses.This function will always write to the given addresses.@param *forwardSpeed the address of the forward speedvariable you want this function to change.@param *turnSpeed the address of the turn speed variableyou want this function to change.*/void Wander(double *forwardSpeed, double *turnSpeed){ int maxSpeed = 1; int maxTurn = 90; double fspeed, tspeed;
tspeed = rand()%(2*maxTurn); tspeed = tspeed-maxTurn;
*forwardSpeed = fspeed; *turnSpeed = tspeed;}
/**Checks sonars for obstacles and updates the given addresseswith wheel speeds. This function will write to the addressesonly if there is an obstacle present. Very basic obstacle avoidance only.@param *forwardSpeed the address of the forward speed variableyou want this function to change.
72@param *turnSpeed the address of the turn speed variable youwant this function to change.@param &sp The sonar proxy that you want this function to monitor.*/void AvoidObstacles(double *forwardSpeed, double *turnSpeed, \ SonarProxy &sp){ //will avoid obstacles closer than 40cm double avoidDistance = 0.4; //will turn away at 60 degrees/sec int avoidTurnSpeed = 60;
/**If blobs have been detected this function will turn the robottowards the largest blob. This will be the closest blob (hopefully!).If called this function will always overwrite information in thegiven addresses.@param *forwardSpeed the address of the forward speed variable
73you want this function to change.@param *turnSpeed the address of the turn speed variable youwant this function to change.@param &bfp The blobfinder proxy that you want this functionto monitor.*/void MoveToItem(double *forwardSpeed, double *turnSpeed, \ BlobfinderProxy &bfp){ int i, centre; int noBlobs = bfp.GetCount(); playerc_blobfinder_blob_t blob; int turningSpeed = 10;
int biggestBlobArea = 0; int biggestBlob = 0;
74 else if(blob.x > centre+margin) { *forwardSpeed = 0; //turn right *turnSpeed = -turningSpeed; } //otherwise go straight ahead else { *forwardSpeed = 0.5; *turnSpeed = 0; }
return;}
/**Fills the item list array with the names and positions of itemsin the simulation@param itemList this is the item list which contains the namesand positions of all the items in the simulation.@param simProxy the simulation proxy for the Player/Stage simulation.*/void RefreshItemList(item_t *itemList, SimulationProxy &simProxy){ int i;
return;
75}
/**Finds an item in the simulation which is near the robot’s teeth.@param itemList this is the item list which contains the names andpositions of all the items in the simulation.@param listLength The number of items in the simulation@param sim the simulation proxy for the Player/Stage simulation.@return returns the index of the item in the array which is withinthe robot’s teeth. If no item is found then this will return -1.*/int FindItem(item_t *itemList, int listLength, SimulationProxy &sim){/*This function works by creating a search area justin front of the robot’s teeth. The search circle is afixed distance in front of the robot, and has afixed radius.This function finds objects within this search circleand then deletes the closest one.*/
76 circleX = robotX + circleX; circleY = robotY + circleY;
return closestItem;}
Position2dProxy p2dProxy(&robot,0);
77SonarProxy sonarProxy(&robot,0);BlobfinderProxy blobProxy(&robot,0);LaserProxy laserProxy(&robot,0);SimulationProxy simProxy(&robot,0);
RefreshItemList(itemList, simProxy);
srand(time(NULL));
//enable motorsp2dProxy.SetMotorEnable(1);
//request geometriesp2dProxy.RequestGeom();sonarProxy.RequestGeom();laserProxy.RequestGeom();//blobfinder doesn’t have geometry
while(true){ // read from the proxies robot.Read();
if(blobProxy.GetCount() == 0) { //wander printf("wandering\n"); Wander(&forwardSpeed, &turnSpeed); } else { //move towards the item printf("moving to item\n"); MoveToItem(&forwardSpeed, &turnSpeed, blobProxy); }
if(destroyThis != -1)
78 { //move it out of the simulation printf("collecting item\n"); simProxy.SetPose2d(itemList[destroyThis].name, -10, -10, 0); RefreshItemList(itemList, simProxy); } } //avoid obstacles AvoidObstacles(&forwardSpeed, &turnSpeed, sonarProxy);
//set motors p2dProxy.SetSpeed(forwardSpeed, dtor(turnSpeed)); sleep(1); }}.