Empezando con Linux / R.O.S. / OO
Los robots modernos, como el Youbot de nuestro laboratorio, son refinados productos de ingeniería, tanto a nivel de diseño mecánico (p.e. el sistema omnidireccional de la plataforma), a nivel de sus sistemas de control (p.e. el brazo tiene una precisión de rango industrial), y también a nivel de ingeniería de sistemas ya que soporta herramientas avanzadas, como ROS, para integrarlos en un entorno de robotización (p.e. en una línea de producción, con otras máquinas, entre personas, etc).
ROS es un framework para la programación de robots y sensores de muchos tipos. Su popularidad ha crecido exponencialmente al ser la herramienta escogida para dar soporte al último DARPA Robotics Challenge. ROS es una herramienta libre y gratuita, desarrollada y soportada por una amplia comunidad de roboticistas de todo el mundo. Aprender a manejar esta herramienta no es algo que se consiga en unos pocos días, pero el esfuerzo merece la pena. El proceso requiere:
- Manejarse con familiaridad con el S.O. (Linux-Ubuntu): instalar el S.O. Linux (Ubuntu) y conocer las características del UNIX, y su uso en línea de comandos.
- Saber desarrollar software: familiarizarse con un IDE para programar (p.e. Eclipse), aprender sobre diseño orientado a objetos, aprender lo fundamental para programar en C++ o Python.
- Comprender y saber utilizar las funcionalidades que proporciona el framework ROS.
En lo que sigue se pueden encontrar algunos enlaces a información seleccionada para ayudarte a adquirir estas habilidades, incluyendo algunos tutoriales "locales" aportados por estudiantes de cursos anteriores:
Instalar Ubuntu
A. Ubuntu nativo
Si ya tienes Ubuntu instalado en tu ordenador (probablemente siguiendo un procedimiento como el descrito aquí), felicidades, puedes saltarte este paso y pasar a instalar ROS.
Si no, la versión de Ubuntu que te recomendamos es la 12.04 14.04 que es la última con soporte de largo alcance, y recomendada para la versión del ROS que utilizaremos para trabajar con nuestro robot youbot. Para que un robot/sensor funcione en ROS necesitaremos disponer de drivers para el mismo, normalmente proporcionados por el fabricante del mismo.
B. Máquina Virtual
Otra opción es recurrir a la preinstalación disponible en la youbot-store (archivo youBot_0.4.0.iso youBot_1.0.1.iso), que te permitirá poner en marcha tu equipo con Ubuntu 12.04 y ROS Fuerte ROS Hydro, e incluye la API del youbot. Una vez la hayas descargado se te plantean dos posibilidades, siempre después de haberte asegurado que tienes una copia de seguridad de tu sistema, pues cualquier error puede ser fatal:
- Crear un Live CD/DVD (recomendado por Ubuntu) o una Live USB (por ejemplo mediante unebootin o siguiendo el siguiente proceso si usas mac) con la imagen descargada y realizar la instalación del SO desde el soporte elegido. Posiblemente durante el proceso tendrás que arremangarte y particionar tu disco duro. Aquí puedes encontrar la ayuda para todo este proceso.
- Utilizar una máquina virtual, como MV VirtualBox. La combinación Virtualbox + ISO_de_youbot parece ahora mismo el camino más sencillo para tener una instalación operativa que incluye el ROS y las APIs del Youbot listas para trabajar, aunque por supuesto el hecho de trabajar con un máquina virtual puede suponer un disminución del rendimiento de tu equipo.
La linea de comandos en UNIX
Para trabajar con fluidez con el S.O. UNIX es útil saber manejarse en la terminal para operaciones cotidianas como manejar archivos, instalar aplicaciones, gestionar el sistemas, etc. Internet está lleno de tutoriales para esto, pero, ¿por dónde empezar?. Por ejemplo, sigamos el consejo del libro "ROS by example" de R.P. Goebel:
" If you are already a veteran Linux user, you are well ahead of the game. If not, you might want to run through a tutorial or two before proceeding. Since web tutorials come and go every day, you can simply Google something like "Ubuntu tutorial" to find something you like. However, bear in mind that most of your work with ROS will take place at the command line or in some form of text editor. A good place to start on command line basics is Using the Ubuntu Terminal. "
Se recomienda pasarse una tranquila sesión de experimentación con ese u otro tutorial similar, por ejemplo:
- Secciones 1 a 7 de este tutorial, producido en el Dpto. de ingeniería de la U. de Cambridge
- Secciones 1 a 5 de este otro, de la Universidad de Surrey en el Reino Unido
- Este tutorial local, del profesor Antonio López
El objetivo es ser capaz de hacer con soltura operaciones sencilla como:
- manejarte por el sistema de archivos: navegar por directorios, visualizar su contenido, copiar, mover, renombrar y eliminar ficheros y directorios.
- editar y visualizar ficheros de texto (por ejemplo con gedit).
- ejecutar programas (te sugiero este ejercicio tan simple para crear y ejecutar un programa).
- gestionar procesos (visualizar, finalizar procesos activos).
Un IDE para programar
Para programar necesitamos, cuando menos, un editor de texto y un compilador o, mejor aún, un entorno de desarrollo integrado (IDE-Integrated Development Environment). Aquí tenemos muchas opciones según los gustos de cada uno. Atendiendo de nuevo a los consejos de "ROS by example" de R.P. Goebel:
- "The text editor you choose for programming is up to you. Choices include gedit, nano, pico, emacs, vim, Eclipse and many others. (See for example https://help.ubuntu.com/community/Programming.) Programs like Eclipse are actually full featured IDEs and can be used to organize projects, test code, manage SVN repositories and so on. For more information on using different IDEs with ROS, see http://www.ros.org/wiki/IDEs. "
En lo que sigue os proponemos el uso de Eclipse IDE for C/C++ Developers.
Instalar Eclipse (2018: ya no recomendado)
La instalación en Ubuntu se puede hacer desde el Software Center de Ubuntu: busca "Eclipse" e instálalo y después busca "eclipse-cdt" e instálalo también. Con esto deberías tener instalado el Eclipse y el plugin C/C++ Development Tools for Eclipse.
Otra alternativa es instalar un paquete del Eclipse con la opción de C++ ya incluída:
- Go to www.eclipse.org
- Click on "download now" from the top-right corner
- Download eclipse for C/C++ developers
- Extract eclipse into a folder of your choice
Tutoriales propuestos:
- En este tutorial aportado por Fernando Soto, te dejamos una breve guía de otras alternativas para instalarlo y crear un programa sencillo. Te recomendamos que te familiarices con el entorno en cuanto a la creación, compilación y depuración de programas.
- Este tutorial para instalar Eclipse en windows, del profesor Ignacio Alvarez, incluye una sección de introducción sobre cómo editar, compilar y depurar un programa en C y empezar familiarizarte con el entorno.
GIT: control de versiones
A lo largo del ciclo de vida de un producto software se producen diferentes revisiones que suponen modificaciones del código fuente, a veces de gran calado y en muchas ocasiones no exentas de riesgo (de estropear el producto). Estas modificaciones son consecuencia de la necesidad de ampliar la funcionalidad del producto, corregir errores, particularizar el producto a las necesidades específicas de un cliente o contexto de aplicación, etc.
Por ello, durante el desarrollo del proyecto, se precisa disponer de una metodología que permita gestionar todas las versiones del producto. En principio, la idea es que, al realizar una modificación, partamos de una versión estable, cambiemos/ampliemos lo necesario y, una vez generada una nueva versión estable, la almacenemos correctamente etiquetada para poder recuperarla en un futuro.
Si bien todo esto puede realizarse manualmente, la industria del software ha generado a lo largo de su historia herramientas para la ayuda en el proceso. Una de ellas es el Git, al cual nos referimos en este tutorial.
Si estás interesado en su uso, re recomiendo que veas la documentación del producto. Para una iniciación rápida los dos primeros capítulos pueden ser suficiente. Y si te gusta y decides utilizarlo, adelante!!. Instálatelo y, como dice el poema, "Caminante, no hay camino, se hace camino, se hace camino al andar".
Por nuestra parte, te dejamos una guía rápida de consulta con los comandos más habituales:
Makefiles
Utilizar entornos de desarrollo integrados (p.e. Eclipse) es, por supuesto, una alternativa muy adecuada para el desarrollo de software. Sin embargo, a veces necesitamos realizar una compilación a bajo nivel de los archivos de nuestro proyecto software para generar el ejecutable. Cuando el número de archivos de nuestro proyectos crece, la compilación puede hacerse tediosa o requerir demasiado tiempo si decidimos recompilar todos los archivos ante cualquier cambio menor. Para ayudarnos con la tarea podemos recurrir a crear makefiles y la utilidad make disponible en todos los sistemas basados en Unix. Existen infinidad de tutoriales en la red que muestran su uso y utilidad. Como ejemplo, os dejo este Utilización de Makefiles para programar que recoge la esencia del uso de makefiles.
Sugerencias para iniciarse en C++
La mayoría de vosotros ya habéis programado en ANSI C y quizás algún otro lenguaje. Para empezar a conocer C++ de manera rápida y eficaz (en 4-8 horas) hay multitud de recursos en internet, de los que os destacamos:
- Algún curso de los que se ofrecen en la EPIG durante el año, oficiales (cursos de extensión, de libre configuración, etc) o por la Rama de Estudiantes del IEEE de la EPIG. Aprender con otros estudiantes en una ambiente distendido siempre es interesante.
- El módulo 1 del curso online C++ For C Programmers de Ira Pohl en Coursera (entre 5-8 horas) tiene videos y ejercicios interesantes. Además tienes otros 7 módulos para aprender cosas más avanzadas si te va picando la curiosidad.
- Si no has programado antes ni siquiera en C, puedes salvar los muebles con este Tutorial Guide to C++ Programming del Profesor Cipolla, del Dpto. de Ingeniería de la Universidad de Cambridge, puede servirte de arranque (unas 8 horas).
- La guía Aprenda C++ como si estuviera en primero es ya muy extensa, pero puedes utilizarla para empezar a pensar en objetos, clases, herencia y polimorfismo.
En internet podrás encontrar más consejos sobre cómo introducirse en C++ sabiendo C, o cuáles son buenos libros para el novato en C++. Sea cual sea tu elección prepárate para las dificultades y ten paciencia, que la de programar no es una habilidad que se adquiera sin mucha práctica.
El programa "hola mundo" desde la terminal
En este sencillo ejercicio vamos a aprender a crear un simple programa que escriba el texto "hola Mundo!" en la consola. Lo haremos en C y C++. Las diferencias son el lenguaje de programación y el compilador que utilizamos, pero el proceso es básicamente el mismo.
Lenguaje C
Sitúate en una carpeta, la que tú desees (puedes incluso crear una nueva), y créate un fichero llamado holamundo.c" (por ejemplo usando gedit) con el siguiente contenido:
#include <stdio.h>
int main(int argc, char * argv[])
{
printf("Hola Mundo! (en C)\n");
return 0;
}
Ahora "compila" el programa (gcc es el compilador de c; te recomiendo que veas la ayuda, mediante el comando man gcc, para tener más información acerca del mismo):
gcc holamundo.c -o hola_mundo_en_c
Verás (comando ls -l) que se ha creado un archivo ejecutable llamado hola_mundo_en_c. Ejecutarlo es muy simple:
./hola_mundo
Lenguaje C++
Sitúate en una carpeta, la que tú desees (puedes incluso crear una nueva), y créate un fichero llamado holamundo.cpp" (por ejemplo usando gedit) con el siguiente contenido:
#include <iostream>
using namespace std;
int main(int argc, char * argv[])
{
cout << "Hola Mundo! (en C++)" << endl;
return 0;
}
Ahora "compila" el programa (g++ es el compilador de c++; te recomiendo que veas la ayuda, mediante el comando man g++, para tener más información acerca del mismo):
g++ holamundo.cpp -o hola_mundo_en_c++
Verás (comando ls -l) que se ha creado un archivo ejecutable llamado hola_mundo_en_c++. Ejecutarlo es muy simple:
./hola_mundo
Mini-tutorial de OO
Lenguajes como UML son OO y sirven para describir sistemas complejos. Para entender mejor la documentación del robot youbot y la del sistema ROS es conveniente manejar esta terminología. Para ello podemos recorrer desde introducciones muy rápidas, hasta libros enteros como "Object-Oriented Analysis and Design with Applications".
Según [3] un sistema se califica como Orientado a Objetos cuando reúne las características de: abstracción, encapsulación, herencia y polimorfismo.
- Abstracción consiste en la generalización conceptual de un determinado conjunto de objetos y de sus atributos y propiedades.
- Encapsulación se refiere a la capacidad de agrupar en un entorno distintos elementos.
- Herencia está fuertemente ligada a la reutilización de código. En C++ se implementa mediante un mecanismo denominado derivación de clases, de modo que se posibilita el uso del código creado para la clase base por parte de sus clases derivadas.
- Polimorfismo se refiere a la posibilidad de acceder a un variado rango de funciones distintas a través de un mismo interfaz.
Clases y Objetos
La abstracción y encapsulación en C++ están representada por el concepto de clase. Abstracción en el sentido de que una clase define una serie de atributos genéricos de determinados objetos con características comunes. Y encapsulación en cuanto a que la clase comprende tanto los datos de que constan los objetos como los procedimientos que permiten manipularlos.
Una clase en C++ puede verse como un struct en C que, además de variables, admite funciones y permite diferentes niveles de acceso en función de unas etiquetas.
La finalidad de las clases en C++ es proporcionar una herramienta para crear nuevos tipos. Una clase es un tipo definido por el programador. La idea fundamental en definir un nuevo tipo consiste en mantener separados los detalles de implementación de las propiedades para su uso.
Un objeto es una instancia de una clase. Se crea mediante una función denominada constructor. Los constructores tienen el mismo nombre que la clase y su objetivo es realizar las funciones necesarias de inicialización. Una clase puede tener varios constructores, cada uno con diferentes argumentos.
Si un objeto hace uso de recursos (ficheros, memoria, mecanismos de bloqueo, etc.) es conveniente incluir el código necesario para su liberación en un destructor, que será llamado de manera automática cuando el objeto salga fuera de ámbito. El nombre del destructor consiste en el nombre de la clase precedido del símbolo '~'
Programa Ejemplo:Mónica y Alberto son objetos de clase Persona. La clase Persona tiene dos atributos o miembros (Nombre y Edad), un método o función miembro (Habla), un constructor (Persona) y un destructor (~Persona).
#include <iostream>
#include <cstring>
using namespace std;
class Persona {
public: char* Nombre;
int Edad;
Persona(const char* inNombre, int inEdad);
~Persona();
void Habla(); };
Persona::Persona(const char* inNombre, int inEdad)
{ Nombre = new char[strlen(inNombre)+1];
strcpy(Nombre,inNombre); Edad = inEdad; }
Persona::~Persona() { cout << Nombre << " ha muerto a los " << Edad << " años." << endl; } void Persona::Habla() { cout << "hola! mi nombre es " << Nombre << endl; }
int main(int argc, char **argv)
{
Persona alberto("Alberto",25);
Persona monica("Mónica",28);
alberto.Habla();
monica.Habla();
if (alberto.Edad > monica.Edad) {
cout << alberto.Nombre << " es mayor que " << monica.Nombre << endl; } else { cout << alberto.Nombre << " NO es mayor que " << monica.Nombre << endl;
}
return 0;
}
Ejecución del programa:
hola! mi nombre es Alberto hola! mi nombre es Mónica Alberto NO es mayor que Mónica Mónica ha muerto a los 28 años. Alberto ha muerto a los 25 años.
El puntero implícito this
Para referirse a miembros de un objeto sin ambigüedades a veces es útil utilizar el puntero implícito this. Por ejemplo, la función Habla() de la clase Persona podría haberse escrito como
void Persona::Habla() { cout << "hola! mi nombre es " << this->Nombre << endl; }
donde this se entiende implícitamente declarado como
Persona const* this;
Herencia
Julián es un objeto de clase Asesino, que hereda de Persona los métodos y atributos y añade el método público Mata y el atributo privado NumCrimenes. La clase Asesino se denomina clase derivada de Persona. En C++ es posible derivar de más de una clase.
... class Asesino : public Persona { private: int NumCrimenes; public: Asesino(const char* inNombre, int inEdad); ~Asesino(); void Mata(Persona* inVictima); }; Asesino::Asesino(const char* inNombre, int inEdad) : Persona(inNombre,inEdad) { NumCrimenes = 0; } Asesino::~Asesino() { cout << "Tras dejar " << NumCrimenes << " crímenes a sus espaldas "; } void Asesino::Mata(Persona* inVictima) { NumCrimenes++; delete inVictima; } int main(int argc, char **argv) { Persona alberto("Alberto",25); Persona monica("Mónica",28); Persona* victima1 = new Persona("Juan",22); Persona* victima2 = new Persona("María",19); Asesino julian("Julián",35); alberto.Habla(); monica.Habla(); julian.Habla(); julian.Mata(victima1); julian.Mata(victima2); if (alberto.Edad > monica.Edad) { cout << alberto.Nombre << " es mayor que " << monica.Nombre << endl; } else { cout << alberto.Nombre << " NO es mayor que " << monica.Nombre << endl; } return 0; }
Ejecución del programa modificado:
hola! mi nombre es Alberto hola! mi nombre es Mónica hola! mi nombre es Julián Juan ha muerto a los 22 años. María ha muerto a los 19 años. Alberto NO es mayor que Mónica Tras dejar 2 crímenes a sus espaldas Julián ha muerto a los 35 años. Mónica ha muerto a los 28 años. Alberto ha muerto a los 25 años.
Control de Acceso
Mediante las etiquetas public, private, protected y friend en la definición de una clase, se puede especificar la accesibilidad de sus miembros.
- Los miembros public no tienen restricciones de acceso. Forman parte de la interfaz de la clase con el exterior
- Los miembros private solamente son accesibles por otros miembros de la misma clase.
- Los miembros protected son accesibles por los miembros de la misma clase y de sus clases derivadas.
Los miembros private y protected se usan para la funcionalidad interna de la clase, que no interesa conocer desde el punto de vista del uso de los objetos de la clase. El método Mata de la clase Asesino puede invocarse desde la función main porque es público, sin embargo si tratásemos de acceder al atributo privado NumCrimenes obtendríamos un error de compilación. Al ser privado solamente puede ser accedido por los métodos de la propia clase (como por ejemplo su destructor). Si fuera protegido (protected) sería también accesible por los métodos de clases derivadas.
- Una función declarada como friend en una clase tiene acceso a los miembros privados de la clase sin ser estrictamente miembro de la misma. Esto es de utilidad por ejemplo si se necesita acceder a miembros privados de mas de una clase. Si tenemos las clases Vector y Matriz, podemos definir el operador producto de Matriz por Vector como función amiga de ambas (ejemplo extraído de [1]).
Polimorfismo
En C++ se establece mediante la sobrecarga de identificadores y operadores y mediante las funciones virtuales.
El término sobrecarga se refiere al uso del mismo identificador u operador en distintos contextos con distintos significados. Por ejemplo la suma de números reales y la suma de números imaginarios mediante el operador "+" da lugar a operaciones diferentes.
Mediante la sobrecarga de funciones un mismo nombre puede representar distintas funciones con distinto tipo y número de argumentos, lo que favorece la legibilidad del código.
Las funciones virtuales son funciones miembro de una clase destinadas a ser definidas o redefinidas en clases derivadas. Se denominan virtuales puras cuando solamente están definidas en clases derivadas (es decir, en la clase base no se implementan). Si una clase contiene métodos virtuales puros se denomina clase abstracta y no admite ser instanciada.
Ejemplo:
#include
#include
using namespace std;
class Abstracta {
public: // con el modificador 'virtual' en la declaración indicamos que es una función virtual // Al igualar a 0 en la declaración indicamos que la función es además virtual pura virtual void funcionVirtualPura() = 0; };
class HijaDeAbstracta : public Abstracta { public: HijaDeAbstracta(){}; virtual ~HijaDeAbstracta(){}; void funcionVirtualPura(); }; void HijaDeAbstracta::funcionVirtualPura(){ cout << "Soy un objeto de clase HijaDeAbstracta y esta es mi versión de funcionVirtualPura()" << endl; } class NoAbstracta { public: NoAbstracta(){}; virtual ~NoAbstracta(){}; virtual void funcionVirtual(); }; void NoAbstracta::funcionVirtual(){ cout << "Soy un objeto de clase NoAbstracta y esta es mi versión de funcionVirtual()" << endl; } class HijaDeNoAbstracta : public NoAbstracta { public: HijaDeNoAbstracta(){}; virtual ~HijaDeNoAbstracta(){}; virtual void funcionVirtual(); };
void HijaDeNoAbstracta::funcionVirtual(){
cout << "Soy un objeto de clase HijaDeNoAbstracta y esta es mi versión de funcionVirtual()" << endl;
}
int main(int argc, char **argv) {
NoAbstracta NoA; HijaDeNoAbstracta HijaDeNoA; HijaDeAbstracta HijaDeA; NoA.funcionVirtual(); HijaDeNoA.funcionVirtual(); HijaDeA.funcionVirtualPura(); // Abstracta A; Esto generaría un error de compilación! return 0;
}
Ejecución:
Soy un objeto de clase NoAbstracta y esta es mi versión de funcionVirtual() Soy un objeto de clase HijaDeNoAbstracta y esta es mi versión de funcionVirtual() Soy un objeto de clase HijaDeAbstracta y esta es mi versión de funcionVirtualPura()
Composición y Agregación
La composición en C++ es el uso de clases como miembros de otras clases en la que existe una relación de pertenencia. Por ejemplo la clase persona podría estar compuesta por dos miembros de clase Brazo y dos de clase Pierna. La creación/destrucción de un objeto Persona implica la creación/destrucción de sus extremidades.
La agregación en C++ el el uso de clases como miembros de otras clases sin relación de pertenencia. La clase Duo está compuesta por dos miembros de clase Persona. Estas dos personas existen antes de la creación y permanecen después de la destrucción del dúo.
La diferencia entre composición y agregación en C++ es principalmente semántica, pudiendo ser muy similar su implementación.
Bibliografía
[1] - The C++ Programming Language. Bjarne Stroustrup. 3rd Ed. Addison Wesley
[2] - Aprenda C++ como si estuviera en primero. Javier García de Jalón, José Ignacio Rodríguez, José María Sarriegui, Alfonso Brazález
[3] - Programación Orientada a Objetos en C++. Ricardo Devís Botella. Paraninfo
Empezando con ROS
If you already have Ubuntu installed, follow the instructions at Ubuntu install of Indigo (http://wiki.ros.org/indigo/Installation/Ubuntu).
If succeed, you can jump to the first ROS tutorial: Installing and Configuring Your ROS Environment.
- Una primera lectura rápida del ROS Start Guide, para empezar a familiarizarnos con conceptos de ROS (nodos, publishing/suscription, services, stocks, packages...)
- Hacer los 20 tutoriales de ROS de nivel principiante (mirror). Se recomienda hacer el test del final para verificar el aprendizaje. Este trabajo nos debe llevar algunos días.
- También hay libros disponibles en línea como "ROS by example" de R.P. Goebel.
A partir de aquí podremos intentar: