Mikel-pfc-itis

From jderobot
Jump to: navigation, search
  • Project Name:
  • Authors: Maikel González Baile (m [dot] gonzalezbai [at] alumnos [dot] urjc [dot] es)
  • Academic Year: 2011-2012
  • Degree: Undergree
  • Jde Version: jde-5.0
  • Tags: C++, JdeRobot, ICE, GTK+, Autotools, CMake, Gazebo 1.0.
  • Technology:
  • State: Developing
  • Document License:




Contents

THE INIT

Bump & Go

The following video shows my first application with JDErobot. The application is very simple, the robot should move forward until finding a wall, when it has been found, the robot should move back and turn to the left or to the right. Then, the robot moves forward again.



GUI (Graphic User Interface)

My second application is based on the previous component, but in this case we can choose some filters on the images captured by the cameras. For example, we can choose the sobel filter. This filter is a good way for testing our applications. In addition, the introrob interface has been divided in three windows, the first one to manipulate the controll, the second one to view the two cameras and choose the filter, and the last one to show "the world".

The target of this application is to start handled GUIs. I've had to add the "Sobel_button" widget in the glade file, to do this I've used the program "Diseñador de interfaces Glade" because with it I can modify the GUI in a more simple and intuitive. Then, with the "libglademm" library I can get the widget and using GTK+ I can program the events what I want to execute when the button is clicked, for example.

(This is a beta version with a poor interface).


The next video shows the introrob GUI with another filter. The filter transforms a RGB image to gray scale using the cameras data. Although this filter seems easier,to do it I've needed to use the OpenCV's libraries to process the image (in the previous video I take the sobel filter implemented in the "opencv" component).


ICE (The Internet Communications Engine)

After several months learning about all concepts around ICE, I have implemented my first application using this middleware. This application has been implemented on C++, its function is to communicate a client (the introrob component) with a server (the gazeboserver component). For this, I have used one thread to manipulate the robot controll and other thread to view the GUI, both in an iterative way.

With this app, I have learned several concepts like middleware, proxys, applications interfaces... and I've reminded others like POO or concurrent programming.

Below I will show and explain how to make ​​an application with ICE and comment the lines of code to see how to establish a connection client-server with ICE. The example is the tipical HelloWorld.

Hello World (Server & Client C++)

The Interface

First of all, we need to define the ICE interface. In this case the interface is very simple because the application displays a string only.

In the interface we declare the remote functions that will be called by the client and the server. In this way, multiple clients can send multiple requests to be served by a single server or one data can be received by multiple clients.


The code would be:

    module Demo {
       interface Printer {
            void printString(string s);
       };
    };

The Client

In this case, the client's target is to call the remote function that will write the string that is transferred as input called printString in the interface.

The first step is to declare the variable to communicate:

   Ice::CommunicatorPtr ic;

And we must initialize it:

   ic = Ice::initialize(argc,argv);

When we have the variable declared and initialized, the next step is to contact with the interfaces; in this example with Printer interface.

We then need to stablish the proxy settings. We can do this in two ways, on the one hand we can use the function "propertyToProxy" to stablish the settings in other file with extension .cfg. Using this way we don't need to change the code if we want to change any property.

On the other hand, we can use the function "stringToProxy" to configurate the proxy with an input string like this example:

   Ice::ObjectPrx base = ic->stringToProxy("SimplePrinter:default -p 10000"); // -p xxx -> where xxx is the port to send requests
   PrinterPrx printer = PrinterPrx::checkedCast(base); // With the variable casted "printer" we can acces to remote functions.

Finally, we close the connection and show the possibles errors.

   catch (const Ice::Exception& ex) {
     std::cerr << ex << std::endl;
     status = 1;
     } catch (const char* msg) {
     std::cerr << msg << std::endl;
     status = 1;
     }
   
   
     if (ic)
        ic->destroy();
   

The Server

The server is more complex than the client, although we can stablish a standard way to implement it. Remember that the server's target is to listen on a port the client requests and, that these requests come through the functions defined in the interface. In this example, the server shows the text received from the remote function "printString" as seen below:

   class PrinterI : public Printer {
      public:
          virtual void printString(const string& s, const Ice::Current&);
   };

Here we declare the class PrinterI which inherits from Printer class generated from printer.ice by the compiler (this is explained later).

   void 
   PrinterI::printString(const string& s, const Ice::Current&){
       cout << s << endl;
   }

In this code is implemented the printString method, we can see that it only shows the string "s" received from client. And the main function:

   int
   main(int argc, char* argv[])
   {
       int status = 0;
       Ice::CommunicatorPtr ic;

As in the client, the variable "ic" is the communicator which must be initialized:

   try {
       ic = Ice::initialize(argc, argv);

The next steps are used to specify the server behaviour:

The first step is to create an adapter with a port where is listening the server and a name to identify it:

   Ice::ObjectAdapterPtr adapter = ic->createObjectAdapterWithEndpoints("SimplePrinterAdapter", "default -p 10000");

Now, we instantiate our Printer interface by a PrinterI object:

   Ice::ObjectPtr object = new PrinterI;

With the "add" function we inform to the adapter of the existence of the new object and then we activate it (The server starts to process incoming requests from clients as soon as the adapter is activated):

   adapter->add(object, ic->stringToIdentity("SimplePrinter"));
   adapter->activate();

And finally:

   ic->waitForShutdown();
   } //try

Then, we close the connection and show the possibles errors like in the client:

   } catch (const Ice::Exception& e) {
       cerr << e << endl;
       status = 1;
   } catch (const char* msg) {
       cerr << msg << endl;
       status = 1;
   }
   if (ic) {
       try {
           ic->destroy();
       } catch (const Ice::Exception& e) {
           cerr << e << endl;
           status = 1;
       }
   }

Compiling all

THE INTERFACE

The first step is to generate the skeletons and proxies. In order to do this we must compile our Slice definition "Printer.ice" and two files will be generated: the header "Printer.h" (which must be added on the server and the client) and the source file "Printer.cpp". The command line is:

   >> slice2cpp Printer.ice

THE SERVER

When the source file is generated, we can compile the server (assuming that the name server is "Server.cpp") as follows:

   >>  c++ -I. -I$ICE_HOME/include -c Printer.cpp Server.cpp

and we need to generate the executable linking it:

   >> c++ -o server Printer.o Server.o -L$ICE_HOME/lib -lIce -lIceUtil

THE CLIENT

The steps are similars to the server. First compiling and then linking:

   >> c++ -I. -I$ICE_HOME/include -c Printer.cpp Client.cpp
   >> c++ -o client Printer.o Client.o -L$ICE_HOME/lib -lIce -lIceUtil

Finally to run both (client and server) we must start the server in a terminal:

   >> ./server

and the client in another terminal:

   >>./client

DEVELOPING COMPONENTS

After learning about software needed to develop JDEROBOT, I begin this section where I will explain and show each of the new components modified or created by me.

The first target is to modify the component INTROROB. As is explained in the manual, this component is used by some students to develop and debug navigation algorithms on a world 3D and with a robot simulated. In the previous sections can be saw the GUI used by the first INTROROB.

Although modifying introrob is one of the objectives, another one is to create a simple component which uses less resources and will be lighter to run, this component will be called "BASIC COMPONENT" and it won't have a 3D world to reduce computation. This component will have a section to try navigation algorithms created by students and a GUI where we can teleoperate and see images which are get by the robot cameras. However, my INTROROB will contain the same than the BASIC COMPONENT, and will add a world 3D where we can interact with the mouse to select positions and we can see the geolocation.

The code structure is simple in both components. There are two parts, the interface and the control, each in a different thread. The control stablishes the communication with gazeboserver through ICE, also updates and sets the values to motors, ptencoders, laser, etc. The interface displays a GUI to teleoperate the robot and shows the cameras.

THE BASIC COMPONENT

The next image shows and explains how is the code structure:

As can be see, there is two source codes (gui.cpp and control_class.cpp). The control_class.cpp is divided in two parts:

- Class: Here is implemented the control class with its methods. The methods are:

 + IterationControl: This method runs the student algorithm, here we can implement and test differents behaviors.
 + HandleCameras: In this method are processed the cameras images. Then, we can manipulate the images with OpenCV.
 + ResetControl: This method resets all values on motors, encoders, etc.

- Main: The main function, which is executed when run the component. In the main fuction are created two threads:

 - The GUI thread: This thread displays the GUI with a determinate frecuency.
 - The control thread: This trhead run the student algorithm with an independent frecuency.

- GUI: Contains the widgets and two canvas to teleoperate (robot and cameras). The gui contains a "play" button to run the method iterationControl.

This is a video to show the first version:



The first version modifies the code and the gui from previous version. With the new GUI, we can set different values on the v (velocity) and w (angular velocity) to teleoperate the robot. The code has been modified as is explained in the previous section.

The next video shows the second version:


The GUI in the second version is more complex because has been added a canvas to teleoperate the robot, on the canvas we can move an object ("X") to stablish the v and w. The control code is the same.

The next video shows the final version:

This is the final version. In the video can be see how is teleoperated the robot, the cameras and is running a determinate algorithm (bump and go).


THE NEW INTROROB

Introrob is a component to visualize robot sensor information and to control its actuators. It's implemented with a GUI that facilitates the usage of these capabilities. The initial version has been developed for working with gazeboserver component though it's possible to adapt its configuration file to refer to different data signals. As it was supported the previous version of JDERobot 4.3, to communicate with gazebo using JDErobot platform. It's possible to retrieve information from several sensor devices, such us: laser, encoders, camera, ptencoders and sonar. It's also possible to control robot's behavior by sending control signals to motors and ptmotors.

Configuring introrob

To use introrob we just have to edit the component's configuration to set the sensor and actuator location.

A configuration file example may be like this:

introrob.Motors.Proxy=motors1:tcp -h localhost -p 9999
introrob.Camera1.Proxy=cameraA:tcp -h localhost -p 9999
introrob.Camera2.Proxy=cameraB:tcp -h localhost -p 9999
introrob.Encoders.Proxy=encoders1:tcp -h localhost -p 9999
introrob.Laser.Proxy=laser1:tcp -h localhost -p 9999
introrob.PTEncoders1.Proxy=ptencoders1:tcp -h localhost -p 9999
introrob.PTEncoders2.Proxy=ptencoders2:tcp -h localhost -p 9999
introrob.PTMotors1.Proxy=ptmotors1:tcp -h localhost -p 9999
introrob.PTMotors2.Proxy=ptmotors2:tcp -h localhost -p 9999
introrob.Pose3Dencoders2.Proxy=pose3dencoders2:tcp -h localhost -p 9999
introrob.Pose3Dencoders1.Proxy=pose3dencoders1:tcp -h localhost -p 9999
introrob.Pose3Dmotors2.Proxy=pose3dmotors2:tcp -h localhost -p 9999
introrob.Pose3Dmotors1.Proxy=pose3dmotors1:tcp -h localhost -p 9999

With the format "Introrob.X.Proxy=Y:Z", it is possible to specify the individual services to be used. X corresponds to sensor or actuator name: Motors, PTMotors, Encoders, PTEncoders, Sonars, Camera, Laser. Y refers to service name (how it has been denominated at ICE interface). The example shows the default name given by gazeboserver. Finally, Z means the network connection path (protocol, IP address and TCP port).

Structure

Introrob has the following classes distribution:

The structure, as can be seen in the image, is divided in:

- INTROROB: This is a source file that contains the main function. In the main function are stablished the ICE interfaces and are created two threads (GUI and CONTROL). The iteration frequency is controlled here.

- API: This is a class that contains the shared memory, this memory is used by: GUI, MyAlgorithms and CONTROL.Also in this class are the functions used by students to implement their navigation algorithms in the file "MyAlgorithms.cpp".

- CONTROL: is responsible for updating sensory information and send the data to the actuators. When the info is updated, the "control" allocates the info in the "shared memory" (API).

- GUI: is responsible for displaying the graphical user interface.

- MyAlgorithms: students can add code to implement different behaviors for robots in this file.


How to modify MyAlgorithms.cpp

MyAlgorithms embeds your own navigation logic. Here we can see some methods contained in this file:

---------------------------------------------------------
Métodos para obtener los datos de los sensores:

this->getMotorV: Este método devuelve la velocidad lineal (mm./s.) del robot. 
EJEMPLO: float v = this->getMotorV();

this->getMotorW: Este método devuelve la velocidad rotacional (deg./s.) del robot. 
EJEMPLO: float w = this->getMotorW();

this->getLaserData: Este método devuelve una estructura con dos campos, por un lado el número de lasers del robot (180) y por otro un vector de 180 posiciones en cada una de las cuales se almacena la distancia obtenida por el láser, estando relacionada cada posición del robot con el ángulo del láser. Ejemplo: jderobot::LaserDataPtr laser = this->getLaserData() || 
USO: printf("laser[45]: %d\n", laser->distanceData[45]); || 
USO: printf("numLasers: %d\n", laser->numLaser);

this->getDistancesLaser: Este método devolvería únicamente el vector mencionado anteriormente. 
EJEMPLO: jderobot::IntSeq VectorDistances this->getDistancesLaser();

this->getNumLasers: Este método devuelve el otro campo mencionado, el número de grados del láser. 
EJEMPLO: int numLasers = this->getNumLasers();

this->getEncodersData: Este método devuelve una estructura con tres campos: robotx, roboty y robottheta, siendo la posición "x", "y" y su orientación "theta" respectivamente. 
EJEMPLO: jderobot::EncodersDataPtr myPosition getEncodersData(); || 
USO: printf("myPosition = [%f, %f]\n", myPosition->robotx, myPosition->roboty);

this->getImageCamera1: Este método devuelve la imagen obtenida por la cámara izquierda del Pioneer. La imagen ya se encuentra en formato "colorspace" con lo que es posible de manipular por medio de OpenCV.
EJEMPLO: colorspaces::Image* imageIzq this->getImageCamera1();

NOTA: Antes de usar getImageCameraX hay que llamar al método imageCameras2openCV, la cual obtiene las imágentes en formato "colorspace".

this->getImageCamera2: Idéntica al método anterior pero con la imagen derecha.

---------------------------------------------------------

Métodos para manipular los actuadores:

this->setMotorV(float V): Con este método definimos la velocidad lineal (mm./s.) del robot.
EJEMPLO: this->setMotorV(40.)

this->setMotorW(float W): Con este método definimos la velocidad rotacional (deg./s.) del robot.
EJEMPLO: this->setMotorW(10.)



---------------------------------------------------------

Métodos gráficos (mundo 3D)

pintaSegmento: Traza una línea entre dos puntos dados a partir de un color dado.
USO:
      CvPoint3D32f aa,bb;
      CvPoint3D32f color;

      bb.x=this->destino.x;
      bb.y=this->destino.y;
      bb.z=0.;
      
      aa.x=encodersData->robotx;
      aa.y=encodersData->roboty;
      aa.z=0;
      
      color.x = 1.; // Red
      color.y = 0.; // Green
      color.z = 0.; // Blue
      this->pintaSegmento (aa, bb, color); 

drawProjectionLines: Traza líneas desde el origen de coordenadas a un punto seleccionado (click izquierdo) en una de las cámaras del robot.
USO: this->drawProjectionLines();

---------------------------------------------------------

Otros:

destino: Variable que almacena las coordenadas de la pocición seleccionada en el mundo 3D con el botón central del ratón.
EJEMPLO: printf ("destPoint = [%f, %f]\n", this->destino.x, this->destino.y);

absolutas2relativas: Método que calcula la posicion relativa respecto del robot de un punto absoluto. El robot se encuentra en robotx, roboty con orientacion robotheta respecto al sistema de referencia absoluto.

relativas2absolutas: Método que calcula la posicion absoluta de un punto expresado en el sistema de coordenadas solidario al robot. El robot se encuentra en robotx, roboty con orientacion robotheta respecto al sistema de referencia absoluto.
USO:
        CvPoint3D32f aa,a,b;

	aa.x=0.; aa.y=0.;
	this->relativas2absolutas(aa,&a);
	aa.x = 1000.; aa.y = -2000.;  // en mm.	     		
	this->relativas2absolutas(aa,&b);

	**** PARA MAS INFO ACCEDER AL FICHERO API.CPP y API.H ****

*/

Using introrob

Introrob provides the following interface by default:

Introrob can be used easily, with its user interface we can manipulate the robot and cameras position (while the position is changing we can see the odometry with the red cross).

We can show three windows:

  • Laser: is a simple interface where is showed the laser data.
  • Camera: in this window, we can see the images taken by the cameras.
  • 3D World: a 3D visualizer is shown for representing robot's world: robot pioneer, map and laser/sonar measurements.

CMAKE

Personal tools