Fernando-TFM-ROS02
- Project Name: Programas de control para el robot Turtlebot sobre ROS
- Authors: Fernando Casado García
- Web of Authors: http://www.fernando.casadogarcia.es
- Academic Year: 2011-2012
- Degree: Master
- SVN Repositories: http://robotica.unileon.es/svn/Projects/PFC/FernandoTFMROS02/
- Tags: Turtlebot
- Technology: ROS, c++
- State: Finished
Contents
Instalando ROS
Lo primero que necesitamos es un computador con el sistema operativo Ubuntu, que es el único soportado por ROS, o instalarlo; otros sistemas operativos para los que está disponible solo son experimentales. Para instalar Ubuntu en un equipo con sistema operativo windows, podemos optar entre una instalación tradicional desde un CD-ROM o USB, en una partición diferente a la de windows, o la opción más rápida y sencilla , instalarlo desde windows como si se tratara de una aplicación o programa mas.
Provistos ya de un equipo con Ubuntu, para descargar e instalar el sistema ROS simplemente deberemos seguir las indicaciones de instalación. No esta de más el tomarse un tiempo realizando los tutoriales que proponen, son de gran ayuda para conocer la estructura de ficheros y el funcionamiento de Nodes y Topics.
Instalando Turtlebot-desktop
Con el sistema ROS instalado, necesitamos instalar las herramientas para desarrollo del robot Turtlebot en el equipo de trabajo. Ejecutando el siguiente comando en un terminal comenzará la descarga e instalación:
sudo apt-get install ros-electric-turtlebot-desktop
Es importante que el reloj del PC estación de trabajo se encuentre sincronizado con el reloj del PC robot, para prevenir las posibles perdidas de red inalámbrica. Para instalar el cliente NTP Chrony se ejecutará el siguiente comando en un terminal:
sudo apt-get install chrony
Instalando Turtlebot-robot
Puede que se utilice otro equipo diferente al que suministran con el Turtlebot o que se actualice Ubuntu a una nueva versión, por lo que será necesario instalar la versión para el robor de ROS. Ejecutando el siguiente comando en un terminal comenzará la descarga e instalación:
sudo apt-get install ros-electric-turtlebot-robot
Se necesita crear un archivo llamado “52-turtlebot.rules” en el directorio /etc/udev/rules.d/. Con el siguiente contenido:
ATTRS{idProduct}=="XXXX",ATTRS{idVendor}=="XXXX",MODE="666",GROUP="turtlebot"
Donde las “XXXX” corresponden a la identificación del producto y del fabricante, respectivamente, del adaptador USB de conexión con el iRobot Create. Esta información la obtiene ejecutando el siguiente comando en un terminal, con el adaptador conectado:
lsusb
Que mostrará una información similar a esta:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 002 Device 002: ID 0a5c:2101 Broadcom Corp. Bluetooth Controller Bus 003 Device 002: ID 04fc:0801 Sunplus Technology Co., Ltd Bus 004 Device 002: ID 08ff:2580 AuthenTec, Inc. AES2501 Fingerprint Sensor Bus 004 Device 003: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC
Donde “6001” corresponde a la identificación del producto y “0403” a la identificación del fabricante. También se debe añadir el usuario, con el que hemos instalado ROS, al grupo “dialout”, para que tenga permisos sobre el puerto que se emplea para la comunicación con el iRobot Create, ttyUSB0.
Conociendo el Turtlebot
Hardware
- iRobot Create
- Laptop ASUS 1215N
- Microsoft Kinect
- Placa de gestión de alimentación y giroscopio
- Cables USB de comunicaciones y alimentación
- Estructura de soporte de elementos
Software
Conexión estación de trabajo-robot
Configuración de la red
Lo habitual es conectar ambos equipos a una red Wi-Fi, pero puede darse que el PC estación de trabajo no disponga de un interfaz de red inalámbrica. En este caso es necesario poder conectarse a un puerto del router que provee la red Wi-Fi, para que exista una ruta de acceso entre el PC estación de trabajo y el PC robot.
En ambos equipos, PC estación de trabajo y PC robot, se añadirá el ROS_MASTER_URI en el archivo de configuración del terminal (~/.bashrc), que apunta a la dirección IP del PC robot, que es el que ejecuta el servicio maestro. Simplemente se debe escribir el siguiente comando en un terminal, sustituyendo "IP_OF_TURTLEBOT", por la dirección IP asignada en la red al PC robot:
echo export ROS_MASTER_URI=http://IP_OF_TURTLEBOT:11311 >> ~/.bashrc
También se debe asignar un nombre de equipo, tanto al PC estación de trabajo como al PC robot. Este nombre será la dirección IP de cada equipo, que se almacenará en el archivo de configuración del terminal (~/.bashrc). En un terminal del PC robot se ejecutará el siguiente comando, sustituyendo "IP_OF_TURTLEBOT", por la dirección IP asignada en la red al PC robot:
echo export ROS_HOSTNAME=IP_OF_TURTLEBOT >> ~/.bashrc
En un terminal del PC estación de trabajo se ejecutará el siguiente comando, sustituyendo "IP_OF_WORKSTATION", por la dirección IP asignada en la red al PC estación de trabajo:
echo export ROS_HOSTNAME=IP_OF_WORKSTATION >> ~/.bashrc
- Nota: Para conocer la dirección IP, asignada en la red a un equipo, simplemente se debe ejecutar en un terminal:
ifconfig
que mostrará una información similar a esta:
eth0 Link encap:Ethernet direcciónHW 00:16:d4:55:85:b4 ACTIVO DIFUSIÓN MULTICAST MTU:1500 Métrica:1 Paquetes RX:0 errores:0 perdidos:0 overruns:0 frame:0 Paquetes TX:0 errores:0 perdidos:0 overruns:0 carrier:0 colisiones:0 long.colaTX:1000 Bytes RX:0 (0.0 B) TX bytes:0 (0.0 B) Interrupción:44 lo Link encap:Bucle local Direc. inet:127.0.0.1 Másc:255.0.0.0 Dirección inet6: ::1/128 Alcance:Anfitrión ACTIVO BUCLE FUNCIONANDO MTU:16436 Métrica:1 Paquetes RX:64 errores:0 perdidos:0 overruns:0 frame:0 Paquetes TX:64 errores:0 perdidos:0 overruns:0 carrier:0 colisiones:0 long.colaTX:0 Bytes RX:5184 (5.1 KB) TX bytes:5184 (5.1 KB) wlan0 Link encap:Ethernet direcciónHW 00:18:de:21:4b:ad Direc. inet:192.168.1.10 Difus.:192.168.1.255 Másc:255.255.255.0 Dirección inet6: fe80::218:deff:fe21:4bad/64 Alcance:Enlace ACTIVO DIFUSIÓN FUNCIONANDO MULTICAST MTU:1500 Métrica:1 Paquetes RX:130972 errores:0 perdidos:0 overruns:0 frame:0 Paquetes TX:87840 errores:0 perdidos:0 overruns:0 carrier:0 colisiones:0 long.colaTX:1000 Bytes RX:169415208 (169.4 MB) TX bytes:9379691 (9.3 MB)
Puesta en marcha
Con la red configurada, ya se puede proceder a la puesta en marcha del Turtlebot. Se conectarán los cables USB de comunicación y alimentación del robot al PC robot, se encenderá el iRobot Create y se arrancará el PC robot.
Las operaciones sobre el PC robot se pueden hacer directamente sobre él, pero no es la opción recomendable por comodidad de trabajo. Así que se comenzará conectando por ssh, desde el PC estación de trabajo con el PC robot. Se ejecutará el siguiente comando en un terminal en la estación de trabajo, sustituyendo "IP_OF_TURTLEBOT" por la dirección IP asignada en la red al PC robot:
ssh turtlebot@IP_OF_TURTLEBOT
Pedirá la contraseña asignada al PC robot. Cuando se conecte el prompt del terminal cambiará por el del PC robot. Se puede comprobar el estado del servicio del robot, tecleando el siguiente comando en el terminal:
sudo service turtlebot status
Devolverá un mensaje diciendo que está iniciado. El servicio se inicia siempre que encendemos el PC robot, pero se puede detener y poner en marcha desde el terminal con los siguientes comandos:
sudo service turtlebot stop sudo service turtlebot start
En un terminal nuevo, de forma local sin conectar con el PC robot, se ejecutará la aplicación del tablero de instrumentos (dashboard) del robot. Simplemente se debe introducir este comando en el terminal:
rosrun turtlebot_dashboard turtlebot_dashboard&
Aparecerá una pequeña ventana similar a esta:
La ventana del tablero de instrumentos consta de tres zonas bien diferenciadas:
- Diagnóstico del sistema, mensajes del sistema y modo de operación.
- Interruptores de los circuitos de las tres salidas digitales del iRobot Create.
- Niveles de batería de el iRobot Create y el PC robot.
Si todo va bien, como en la imagen, estarán en verde los iconos de diagnostico del sistema (llave inglesa) y mensajes del sistema (nube de llamada). El modo de operación se debe poner poner en Full Mode, para poder operar el robot. En los otros modos no está permitida la operación del robot, son el modo de carga (Passive Mode) y de espera (Safety Mode).
También se debe activar al menos el interruptor "1", que es el que controla el circuito de alimentación de la cámara Kinect. Ahora ya esta todo listo para comenzar a operar con el robot. Para verlo en funcionamiento es recomendable probar alguna de las aplicaciones de demostración.
Primer programa (Vuelta al mundo)
Creando un espacio de trabajo
Lo más cómodo para trabajar es crear un directorio propio para stacks y packages en el PC estación de trabajo, ya que ROS tiene por defecto como directorio para stacks y packages /opt/ros/electric/stacks. Se pueden crear los nuevos stacks o packages en este directorio por defecto, pero queda más organizado y accesible de esta forma.
El directorio se puede situar donde y con el nombre que se quiera. Se comenzará creando un directorio, por ejemplo en la carpeta personal:
mkdir ~/ros_workspace
Para que el sistema también busque stacks y packages en nuestra carpeta, se debe crear en la carpeta personal un archivo llamado "setup.sh", con el siguiente contenido:
#!/bin/sh source /opt/ros/electric/setup.bash export ROS_ROOT=/opt/ros/electric/ros export PATH=$ROS_ROOT/bin:$PATH export PYTHONPATH=$ROS_ROOT/core/roslib/src:$PYTHONPATH export ROS_PACKAGE_PATH=~/ros_workspace:/opt/ros/electric/stacks:$ROS_PACKAGE_PATH
Se debe añadir este nuevo archivo creado al archivo de configuración del terminal (~/.bashrc), para hacer efectiva esta modificación. Para ello se debe ejecutar el siguiente comando en un terminal:
echo source ~/setup.sh >> ~/.bashrc
Creando un package
En el directorio de trabajo creado se creará un package, el cual va a depender de otros packages del sistema. Las dependencias van en función del tipo de mensajes que queremos enviar o recibir, y el lenguaje empleado en los programas. En este primer programa no se necesitan todas las dependencias, pero se usaran en los siguientes programas, por lo que las añadiremos al crear el package que va a contener los programas. Para esto se introducirá la siguiente secuencia de comandos en un terminal:
cd ~/ros_workspace roscreate-pkg turtlebot_fer std_msgs turtlebot_node geometry_msgs nav_msgs sensor_msgs image_transport roscpp
También se pueden añadir dependencias a un package ya creado, simplemente se debe editar el archivo "manifest.xml" que se encuentra dentro de la carpeta del package creado. El contenido del archivo "manifest.xml" es similar al siguiente, las dependencias se encuentran al final:
<package>
<description brief="turtlebot_fer">
turtlebot_fer
</description>
<author>FeR</author>
<license>BSD</license>
<review status="unreviewed" notes=""/>
<url>http://ros.org/wiki/turtlebot_fer</url>
<depend package="turtlebot_node"/>
<depend package="geometry_msgs"/>
<depend package="nav_msgs"/>
<depend package="std_msgs"/>
<depend package="sensor_msgs"/>
<depend package="image_transport"/>
<depend package="roscpp"/>
</package>
Una vez creado el package, o modificadas las dependencias de un package existente, se debe compilar para que se hagan efectivas las dependencias. Simplemente se debe ejecutar el siguiente comando en el terminal, indicando el nombre del package:
rosmake turtlebot_fer
Escribiendo y compilando el programa
Los programas se pueden escribir en c++ o python, el escogido ha sido c++. Para la redacción del programa se puede emplear algún editor de código, como Geany. Los archivos de los programas se guardarán en la carpeta "src" del package creado, turtlebot_fer. Este primer programa, simplemente crea el nodo "gira" y publica el topic "cmd_vel", que hace mover el robot con una velocidad de avance y de giro, describiendo un círculo, hasta que se reciba la señal de parada "Ctrl+C". El programa es el siguiente:
#include "ros/ros.h" //Librería de funciones del sistema ROS
#include "geometry_msgs/Twist.h" //Librería "Twist" del package "geometry_msgs"
int main(int argc, char **argv)
{
ros::init(argc, argv, "gira"); //Creación del nodo "gira"
ros::NodeHandle n;
ros::Publisher vel_pub_=n.advertise<geometry_msgs::Twist>("cmd_vel", 1); //Publicación del topic "cmd_vel"
geometry_msgs::Twist vel;
ros::Rate loop_rate(10); //Frecuencia de realización del bucle (10 Hz)
while (ros::ok()) //Bucle mientras no se reciba "Ctrl+C"
{
vel.linear.x = 0.2; //velocidad de avance
vel.angular.z = 0.4; //velocidad de giro
vel_pub_.publish(vel);
loop_rate.sleep();
}
return 0;
}
Se guardará con el nombre "gira.cpp". Para compilarlo y generar el ejecutable se debe añadir la siguiente línea de código al archivo "CMakeLists.txt" del package creado, donde indicamos el nombre para el ejecutable y la ruta y nombre del archivo a compilar:
rosbuild_add_executable(gira src/gira.cpp)
Para compilar el programa hay que situarse en el directorio del package. Simplemente con ejecutar la siguiente secuencia de comandos en un terminal se compilará y creará el ejecutable, siempre que no existan errores:
roscd turtlebot_fer make
Ejecutando el programa
Simulador
Lo primero que hay que hacer es descargar el simulador del Turtlebot, ejecutando el siguiente comando en unterminal:
sudo apt-get install ros-electric-turtlebot-simulator
Se puede ejecutar primero en el simulador para salvaguardar la integridad del robot. Lo primero que se debe hacer para poder arrancar el simulador es comentar las líneas que se han añadido para la comunicación con el PC robot en el archivo "~/.bashrc", del PC estación de trabajo, tal y como se muestra, añadiendo el símbolo "#" al principio de la línea:
# export ROS_MASTER_URI=http://IP_OF_TURTLEBOT:11311 # export ROS_HOSTNAME=IP_OF_WORKSTATION
Se debe reiniciar la sesión de usuario para que surtan efecto los cambios. Para ejecutar el simulador del Turtlebot simplemente se bebe ejecutar el siguiente comando en un terminal:
roslaunch turtlebot_gazebo turtlebot_empty_world.launch
Terminada la carga aparecerá una ventana gráfica del simulador Gazebo con un escenario vacío y el modelo 3D del robot, como se muestra en la imagen:
Se puede cambiar el punto de vista jugando con la rueda del ratón y el botón derecho. Además, se pueden añadir objetos 3D, como esferas, prismas y cilindros. Para ejecutar el programa creado ejecutamos el siguiente comando en un terminal nuevo, indicando el package donde se encuentra y el nombre del ejecutable:
rosrun turtlebot_fer gira
Si todo ha ido bien, el modelo 3D del robot comenzara a desplazarse formando un circulo, y no se detendrá hasta que reciba la señal de parada el terminal desde el que se ejecuta el programa, "Ctrl+C".
Robot
Si se han modificado las lineas para comunicación del archivo "~/.bashrc", para ejecutar el simulador, se deben restaurar y reiniciar la sesión de usuario para que surtan efecto los cambios. Ahora se procederá a la puesta en marcha del robot.
Conectado el PC estación de trabajo al PC robot, se debe crear un espacio de trabajo y crear un package, al igual que en el PC estación de trabajo. Creado el espacio de trabajo y el package, se copiará el programa situado en el PC estación de trabajo al PC robot, escribiendo el siguiente comando en un terminal del PC estación de trabajo:
roscd turtlebot_fer cd src scp gira.cpp turtlebot@IP_OF_TURTLEBOT:/home/turtlebot/ros_workspace/turtlebot_fer/src/gira.cpp
Una vez copiado el archivo del programa al PC robot, para poder ser ejecutado hay que compilarlo desde un terminal del PC robot. Para ejecutar el programa simplemente se debe introducir el siguiente comando en un terminal del PC robot, tomando la precaución que el robot se encuentre en un espacio lo bastante diáfano para evitar colisiones:
rosrun turtlebot_fer gira
En el siguiente vídeo puede verse el robot ejecutando el programa:
- Nota: si se encuentra conectada al robot alguna de las fuentes de alimentación, PC robot o iRobot Create, este no se moverá.
Segundo programa (Golpea y escapa)
Este segundo programa añade la suscripción a un topic, además de la publicación. El funcionamiento es bastante simple, comenzará a avanzar en línea recta hasta que se detecte el accionamiento del parachoques del iRobot Create, provocado por el choque con un objeto; en este momento realizará un pequeño retroceso y girará un ángulo, de sentido y magnitud aleatorios, para intentar evitar el obstáculo. No se detendrá hasta recibir la señal de parada "Ctrl+C". El código del programa, escrito en lenguaje c++, es el siguiente:
#include "ros/ros.h" //Librería de funciones del sistema ROS
#include "geometry_msgs/Twist.h" //Librería "Twist" del package "geometry_msgs"
#include "turtlebot_node/TurtlebotSensorState.h" //Librería "TurtlebotSensorState" del package "turtlebot_node"
geometry_msgs::Twist vel; //Declaración de la variable global vel
double alea; //Declaración de la variable global alea
void callback(const turtlebot_node::TurtlebotSensorState& sen) //Función de llamda con cada dato recibido
{
ros::NodeHandle n;
ros::Publisher vel_pub_=n.advertise<geometry_msgs::Twist>("cmd_vel", 1); //Publicación del topic "cmd_vel"
ros::Rate loop_rate(10); //Frecuencia de realización del bucle (10 Hz)
ros::Time ahora; //Declaración de variable tipo tiempo, variable ROS
if (sen.bumps_wheeldrops == 0) //Comprobación si ha habido señal del parachoques
{
::vel.linear.x = 0.2;
::vel.angular.z = 0.0;
vel_pub_.publish(::vel);
}
else
{
ahora = ros::Time::now(); //Asignación del tiempo actual a la variable ahora
while (ros::Time::now() < (ahora + ros::Duration(0.5))) //Bucle durante 0,5 segundos, retroceso después de choque
{
::vel.linear.x = -0.2;
::vel.angular.z = 0.0;
vel_pub_.publish(::vel);
loop_rate.sleep();
}
::alea = ((rand() %40) -20)/10; //Asignación de valor aleatorio
while (::alea == 0) //Bucle mientras la variable aleatoria sea cero
{
::alea = ((rand() %40) -20)/10;
}
ahora = ros::Time::now(); //Asignación del tiempo actual a la variable ahora
while (ros::Time::now() < (ahora + ros::Duration(1.5))) //Bucle durante 1,5 segundos, giro de ángulo aleatorio
{
::vel.linear.x = 0.0;
::vel.angular.z = ::alea;
vel_pub_.publish(::vel);
loop_rate.sleep();
}
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "juguete_golpe"); //Creación del nodo "juguete_golpe"
ros::NodeHandle n;
ros::Publisher vel_pub_=n.advertise<geometry_msgs::Twist>("cmd_vel", 1); //Publicación del topic "cmd_vel"
ros::Subscriber odom_sub_= n.subscribe("turtlebot_node/sensor_state", 1, callback); //Suscripción del topic "turtlebot_node/sensor_state"
ros::spin(); //Mantiene la suscripción al topic hasta que se reciba "Ctrl+C"
return 0;
}
Si se tiene ya creado el espacio de trabajo y el package, simplemente se debe guardar en un archivo llamado "juguete_golpe.cpp", compilar y ejecutar. En el siguiente vídeo puede verse el robot ejecutando el programa:
- Nota: El modelo 3D del Turtlebot empleado en el simulador no implementa este sensor, por lo que no será posible probar el programa en el simulador.
Tercer programa (Toro loco)
La peculiaridad de este programa es que el topic de suscripción es la imagen obtenida por la cámara Kinect. La imagen se recibe empaquetada en un array de "píxeles ancho x píxeles alto x 3 (componentes color rgb)" elementos, por defecto (640 x 480 x 3) = 921600 elementos. Los píxeles se encuentran representados en sus componentes rgb, con ese orden, comenzando en el píxel de la esquina superior izquierda, continuando por filas hasta llegar al último píxel, esquina inferior derecha de la imagen.
Píxel 1 | Píxel 2 | Píxel 3 | Píxel 307200 | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
100 | 65 | 50 | 110 | 63 | 49 | 109 | 65 | 48 | ... | 75 | 98 | 244 |
[0] | [1] | [2] | [3] | [4] | [5] | [6] | [7] | [8] | [921597] | [921598] | [921599] |
Este tercer programa realiza el seguimiento de objetos de color rojo. Para ello analiza cada imagen píxel a píxel, contando los píxeles del lado izquierdo y del lado derecho. Cuando el número total de píxeles de tonalidades rojas es superior al 0,1 %, de los píxeles totales de la imagen, e inferior al 5 %, comienza el seguimiento hasta hacer el numero de píxeles totales cercano al 2,5 % e igual a cada lado (estos valores dependerán del tamaño del objeto). Cuando el objeto se encuentre demasiado lejos o demasiado cerca se detendrá el seguimiento. Para facilitar la calibración cada vez que se quiera modificar alguno de los parámetros del programa se creará un archivo .h, en el que estarán definidas las constantes para cada parámetro, se guardará con el nombre "param_sigue_rojo.h" y con el siguiente contenido:
#ifndef __PARAM_SIGUE_ROJO_H
#define __PARAM_SIGUE_ROJO_H
#define li_red 100 //Límite inferior para canal rojo
#define ls_red 255 //Límite superior para canal rojo
#define li_green 0 //Límite inferior para canal verde
#define ls_green 30 //Límite superior para canal verde
#define li_blue 0 //Límite inferior para canal azul
#define ls_blue 50 //Límite superior para canal azul
#define tam_min 0.001 //Porcentaje de píxeles de tonalidades rojas mínimo
#define tam_max 0.05 //Porcentaje de píxeles de tonalidades rojas máximo
#define tam_med 0.025 //Porcentaje de píxeles de tonalidades rojas medio
#define Px 0.08 //Ganancia velocidad de avance-retroceso
#define Pz 1.2 //Ganancia velocidad de giro
#endif /*__PARAM_SIGUE_ROJO_H*/
El código del programa, escrito en lenguaje c++, es el siguiente:
#include "ros/ros.h" //Librería de funciones del sistema ROS
#include "sensor_msgs/Image.h" //Librería "Image" del package "sensor_msgs"
#include "geometry_msgs/Twist.h" //Librería "Twist" del package "geometry_msgs"
#include "param_sigue_rojo.h" //Parámetros de ajuste
geometry_msgs::Twist vel; //Declaración de la variable global vel
sensor_msgs::Image region; //Declaración de la variable global vel
void callback(const sensor_msgs::Image& ima)
{
ros::NodeHandle n;
ros::Publisher vel_pub_=n.advertise<geometry_msgs::Twist>("cmd_vel", 1); //Publicación del topic "cmd_vel"
uint pix_r; //Declaración de variables de almacenaje valores rgb de cada píxel
uint pix_g;
uint pix_b;
uint ancho = ima.step; //Número de elementos de cada fila de la imagen
uint tam = ima.data.size(); //Número de elementos del array
double cont_izq = 0; //Declaración e inicialización de contadores de píxeles
double cont_der = 0;
::region = ima;
uint up;
uint right;
uint down;
uint left;
for (uint i = 0; i < tam; i = i + ancho) //Filtro de regiones pequeñas o píxeles aislados
{
for (uint j = 0; j < ancho; j = j + 3)
{
pix_r = ima.data[i + j];
pix_g = ima.data[i + j + 1];
pix_b = ima.data[i + j + 2];
if (pix_r >= li_red && pix_r <= ls_red && pix_g >= li_green && pix_g <= ls_green && pix_b >= li_blue && pix_b <= ls_blue)
{
up = 12;
if ((i/ancho) < 12)
{
up = (i/ancho);
}
right = 12;
if (((ancho - j)/3) < 12)
{
right = ((ancho - j)/3);
}
down = 12;
if (((tam/ancho) - (i/ancho)) < 12)
{
down = ((tam/ancho) - (i/ancho));
}
left = 12;
if ((j/3) < 12)
{
left = (j/3);
}
uint contr = 0;
for (uint k = i + j - (ancho*up) - (3*left); k <= (i + j + (ancho*down) - (3*left)); k = k + ancho)
{
for(uint l = 0; l < ((3*left) + (3*right) +3); l = l + 3)
{
pix_r = ima.data[k + l];
pix_g = ima.data[k + l + 1];
pix_b = ima.data[k + l + 2];
if (pix_r >= li_red && pix_r <= ls_red && pix_g >= li_green && pix_g <= ls_green && pix_b >= li_blue && pix_b <= ls_blue)
{
contr++;
}
}
}
if (contr > (625*0.5))
{
::region.data[i + j] = 1;
::region.data[i + j + 1] = 1;
::region.data[i + j + 2] = 1;
}
}
}
}
for (uint i = 0; i < (tam - (ancho/2)); i = i + ancho) //Contar píxeles tonos rojos lado izquierdo imagen
{
for (uint j = 0; j < (ancho/2); j = j + 3)
{
pix_r = ::region.data[i + j];
pix_g = ::region.data[i + j + 1];
pix_b = ::region.data[i + j + 2];
if (pix_r == 1 && pix_g == 1 && pix_b == 1)
{
cont_izq++;
}
}
}
for (uint i = (ancho/2); i < tam; i = i + ancho) //Contar píxeles tonos rojos lado derecho imagen
{
for (uint j = 0; j < (ancho/2); j = j + 3)
{
pix_r = ::region.data[i + j];
pix_g = ::region.data[i + j + 1];
pix_b = ::region.data[i + j + 2];
if (pix_r == 1 && pix_g == 1 && pix_b == 1)
{
cont_der++;
}
}
}
double cont = cont_izq + cont_der;
if (cont > ((tam/3) * tam_min) && cont < ((tam/3) * tam_max)) //Comprobación si se está dentro del limite inferior y superior de píxeles
{
::vel.angular.z = (cont_izq - cont_der) / (::abs(cont_izq - cont_der) * Pz); //Velocidad de giro
::vel.linear.x = (((tam/3) * tam_med) - cont) / ((tam/3) * Px); //Velocidad de avance-retroceso
vel_pub_.publish(vel);
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "sigue_rojo"); //Creación del nodo "sigue_rojo"
ros::NodeHandle n;
ros::Publisher vel_pub_=n.advertise<geometry_msgs::Twist>("cmd_vel", 1); //Publicación del topic "cmd_vel"
ros::Subscriber image_sub_= n.subscribe("camera/rgb/image_color", 1, callback); //Suscripción del topic "camera/rgb/image_color"
ros::spin(); //Mantiene la suscripción al topic hasta que se reciba "Ctrl+C"
return 0;
}
- Nota: Ya que la tonalidad percibida por la cámara depende de la iluminación del recinto, es necesaria una calibración de los límites de los canales RGB, para un correcto funcionamiento.
Si se tiene ya creado el espacio de trabajo y el package, simplemente se debe guardar en un archivo llamado "sigue_rojo.cpp", compilar y ejecutar. Para poner en marcha la cámara Kinect se debe lanzar en el PC robot el "kinect.launch", ejecutando el siguiente comando en un terminal:
roslaunch turtlebot_bringup kinect.launch
En el siguiente vídeo puede verse el robot ejecutando el programa:
Además para la supervisión de las imagenes procesadas por el programa se ha realizado otro programa que publica el topic "/camara/rojo" con la imagen modificada, en la que los píxeles de tonalidad roja se colorean en rojo y el resto se muestra en escala de grises, con una linea divisoria de color negro en el centro. El código del programa, escrito en lenguaje c++, es el siguiente:
#include "ros/ros.h" //Librería de funciones del sistema ROS
#include "sensor_msgs/Image.h" //Librería "Image" del package "sensor_msgs"
#include "param_sigue_rojo.h" //Parámetros de ajuste
sensor_msgs::Image region; //Declaración de la variable global region
void callback(const sensor_msgs::Image& ima)
{
ros::NodeHandle n;
ros::Publisher rojo_pub_=n.advertise<sensor_msgs::Image>("camara/rojo", 1); //Publicación del topic "camara/rojo"
uint pix_r; //Declaración de variables de almacenaje valores rgb de cada píxel
uint pix_g;
uint pix_b;
uint ancho = ima.step; //Número de elementos de cada fila de la imagen
uint tam = ima.data.size(); //Número de elementos del array
::region = ima;
uint up;
uint right;
uint down;
uint left;
for (uint i = 0; i < tam; i = i + ancho) //Filtro de regiones pequeñas o píxeles aislados
{
for (uint j = 0; j < ancho; j = j + 3)
{
pix_r = ima.data[i + j];
pix_g = ima.data[i + j + 1];
pix_b = ima.data[i + j + 2];
if (pix_r >= li_red && pix_r <= ls_red && pix_g >= li_green && pix_g <= ls_green && pix_b >= li_blue && pix_b <= ls_blue)
{
up = 12;
if ((i/ancho) < 12)
{
up = (i/ancho);
}
right = 12;
if (((ancho - j)/3) < 12)
{
right = ((ancho - j)/3);
}
down = 12;
if (((tam/ancho) - (i/ancho)) < 12)
{
down = ((tam/ancho) - (i/ancho));
}
left = 12;
if ((j/3) < 12)
{
left = (j/3);
}
uint contr = 0;
for (uint k = i + j - (ancho*up) - (3*left); k <= (i + j + (ancho*down) - (3*left)); k = k + ancho)
{
for(uint l = 0; l < ((3*left) + (3*right) +3); l = l + 3)
{
pix_r = ima.data[k + l];
pix_g = ima.data[k + l + 1];
pix_b = ima.data[k + l + 2];
if (pix_r >= li_red && pix_r <= ls_red && pix_g >= li_green && pix_g <= ls_green && pix_b >= li_blue && pix_b <= ls_blue)
{
contr++;
}
}
}
if (contr > (625*0.5))
{
::region.data[i + j] = 255;
::region.data[i + j + 1] = 0;
::region.data[i + j + 2] = 0;
}
else //Conversión a escala de grises regiones pequeñas o píxeles aislados
{
if (pix_r > pix_g)
{
::region.data[i + j] = pix_r;
::region.data[i + j + 1] = pix_r;
::region.data[i + j + 2] = pix_r;
}
else if (pix_g > pix_b)
{
::region.data[i + j] = pix_g;
::region.data[i + j + 1] = pix_g;
::region.data[i + j + 2] = pix_g;
}
else
{
::region.data[i + j] = pix_b;
::region.data[i + j + 1] = pix_b;
::region.data[i + j + 2] = pix_b;
}
}
}
else //Conversión a escala de grises resto píxeles
{
if (pix_r > pix_g)
{
::region.data[i + j] = pix_r;
::region.data[i + j + 1] = pix_r;
::region.data[i + j + 2] = pix_r;
}
else if (pix_g > pix_b)
{
::region.data[i + j] = pix_g;
::region.data[i + j + 1] = pix_g;
::region.data[i + j + 2] = pix_g;
}
else
{
::region.data[i + j] = pix_b;
::region.data[i + j + 1] = pix_b;
::region.data[i + j + 2] = pix_b;
}
}
}
}
for (uint i = (ancho/2)-3; i < tam; i = i + ancho) //Línea negra divisoria central
{
for (uint j = 0; j < 6; j = j + 3)
{
::region.data[i + j] = 0;
::region.data[i + j + 1] = 0;
::region.data[i + j + 2] = 0;
}
}
rojo_pub_.publish(::region);
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "visualizacion"); //Creación del nodo "visualizacion"
ros::NodeHandle n;
ros::Publisher rojo_pub_=n.advertise<sensor_msgs::Image>("camara/rojo", 1); //Publicación del topic "camara/rojo"
ros::Subscriber image_sub_= n.subscribe("camera/rgb/image_color", 1, callback); //Suscripción del topic "camera/rgb/image_color"
ros::spin(); //Mantiene la suscripción al topic hasta que se reciba "Ctrl+C"
return 0;
}
Si se tiene ya creado el espacio de trabajo y el package, simplemente se debe guardar en un archivo llamado "visualizacion.cpp", compilar y ejecutar.
Para poder ejecutar "kinect.launch", "sigue_rojo" y "visualización"de forma simultanea se creará un launcher. Para ello crearemos dentro de la carpeta de nuestro package una carpeta llamada "launch", dentro de la cual crearemos un archivo llamado "rojo.launch" con el siguiente contenido:
<launch> <include file="$(find turtlebot_bringup)/kinect.launch" /> <node name="sigue_rojo" pkg="turtlebot_fer" type="sigue_rojo" /> <node name="visualizacion" pkg="turtlebot_fer" type="visualizacion" /> </launch>
Para ejecutar el launcher que lanza los dos nodos, "sigue_rojo" y "visualización", simplemente habrá que ejecutar el siguiente comando en un terminal del PC robot:
roslaunch turtlebot_fer rojo.launch
Para poder visualizar la imagen modificada de la cámara Kinect en el PC estación de trabajo, para la supervisión. Simplemente hay que ejecutar el siguiente comando en un terminal:
rosrun image_view image_view image:=/camara/rojo
Las imágenes tendrán una tasa de frames baja y un retardo, teniendo en cuenta que simplemente son para supervisión pueden valer. Las imágenes obtenidas tendrán un aspecto similar al siguiente:
Calibración de odómetro y giroscopio
Para un correcto seguimiento del movimiento del robot se debe hacer una calibración de su odómetro y su giroscopio, para corregir el error que puedan cometer. Para ello colocaremos el robot frente a una pared recta, con al menos 2 metros de superficie libre, separado unos 30 centímetros de ella, como se muestra en las imágenes.
Para ejecutar el programa de calibración simplemente se debe ejecutar el siguiente comando en un terminal del PC robot:
roslaunch turtlebot_calibration calibrate.launch
Finalizado el proceso de calibración aparecerán los valores de corrección de escala de los parámetros turtlebot_node/gyro_scale_correction y turtlebot_node/odom_angular_scale_correction.
[INFO] [WallTime: 1299286750.821002] Multiply the 'turtlebot_node/gyro_scale_correction' parameter with VALOR_CALIBRACION
[INFO] [WallTime: 1299286750.822427] Multiply the 'turtlebot_node/odom_angular_scale_correction' parameter with VALOR_CALIBRACION
El nuevo valor a aplicar a los parámetros será el resultado de multiplicar el valor antiguo, por defecto 1, por el valor obtenido en la calibración. Para aplicar este nuevo valor a los parámetros de forma permanente se debe editar el archivo "turtlebot.launch", situado en la ruta /etc/ros/electric, tal como se muestra a continuación:
<launch>
<!-- sample file, copy this to /etc/ros/electric/turtlebot.launch -->
<param name="turtlebot_node/gyro_scale_correction" value="VALOR_ANTIGUO_x_VALOR_CALIBRACION"/>
<param name="turtlebot_node/odom_angular_scale_correction" value="VALOR_ANTIGUO_x_VALOR_CALIBRACION"/>
<include file="$(find turtlebot_bringup)/minimal.launch">
<arg name="urdf_file" value="$(find xacro)/xacro.py '$(find turtlebot_description)/urdf/turtlebot.urdf.xacro'" />
</include>
</launch>
- Nota: El parámetro gyro_measurement_range tiene por defecto un valor de 150, el valor adecuado si el Turtlebot es de Willow Garage, pero debe ser 250 si es de Turtlebot de Clearpath y 300 si es de iHeartEngineering (en este caso el giroscopio tiene un rango de medida de 300). Para esto será necesario añadir otra linea al archivo "turtlebot.launch" para establecer el valor adecuado según el modelo.
<param name="turtlebot_node/gyro_measurement_range" value="VALOR_SEGÚN_MODELO"/>
Para que el cambio de valores de los parámetros sea efectivo deberemos detener, y poner en marcha de nuevo el servicio del Turtlebot, con los siguientes comandos:
sudo service turtlebot stop
sudo service turtlebot start
- Nota: También se puede hacer una modificación dinámica de estos parámetros usando un interfaz gráfico ejecutando el siguiente comando en un terminal:
rosrun dynamic_reconfigure reconfigure_gui
Creación del mapa
Para la creación del mapa se usará el stack proporcionado por ROS "slam_gmapping", que contiene el package "gmapping" que implementa un método para SLAM. El archivo "crear_mapa.launch", que se guardará en la carpeta "launch" de nuestro package, tendrá el siguiente contenido:
<launch>
<include file="$(find turtlebot_bringup)/kinect.launch" />
<arg name="scan_topic" default="scan" />
<node pkg="gmapping" type="slam_gmapping" name="slam_gmapping" output="screen">
<param name="odom_frame" value="odom"/>
<param name="map_update_interval" value="3.0"/>
<param name="maxUrange" value="16.0"/>
<param name="sigma" value="0.05"/>
<param name="kernelSize" value="1"/>
<param name="lstep" value="0.05"/>
<param name="astep" value="0.05"/>
<param name="iterations" value="5"/>
<param name="lsigma" value="0.075"/>
<param name="ogain" value="3.0"/>
<param name="lskip" value="0"/>
<param name="srr" value="0.01"/>
<param name="srt" value="0.02"/>
<param name="str" value="0.01"/>
<param name="stt" value="0.02"/>
<param name="linearUpdate" value="0.5"/>
<param name="angularUpdate" value="0.436"/>
<param name="temporalUpdate" value="-1.0"/>
<param name="resampleThreshold" value="0.5"/>
<param name="particles" value="80"/>
<param name="xmin" value="-1.0"/>
<param name="ymin" value="-1.0"/>
<param name="xmax" value="1.0"/>
<param name="ymax" value="1.0"/>
<param name="delta" value="0.05"/>
<param name="llsamplerange" value="0.01"/>
<param name="llsamplestep" value="0.01"/>
<param name="lasamplerange" value="0.005"/>
<param name="lasamplestep" value="0.005"/>
<remap from="scan" to="$(arg scan_topic)"/>
</node>
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find turtlebot_fer)/config/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find turtlebot_fer)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find turtlebot_fer)/config/local_costmap_params.yaml" command="load" />
<rosparam file="$(find turtlebot_fer)/config/global_costmap_params.yaml" command="load" />
<rosparam file="$(find turtlebot_fer)/config/base_local_planner_params.yaml" command="load" />
</node>
</launch>
- Nota: los archivos de configuración del package "move_base" se han obtenido del package "turtlebot_navigation", carpeta "config", y se han situado en nuestro package en una carpeta llamada, igualmente, "config".
Para ejecutar "crear_mapa.launch", que inicia la Kinect, "slam_gmaping" y "move_base", se debe introducir el siguiente comando en un terminal del PC robot:
roslaunch turtlebot_fer crear_mapa.launch
Para el desplazamiento del Turtlebot a través del espacio a mapear se empleará el teleoperador por teclado, ejecutando en un terminal del PC robot el siguiente comando:
roslaunch turtlebot_teleop keyboard_teleop.launch
- Nota: Para la correcta realización del mapa es necesario dar varias pasadas por el espacio a mapear, saliendo de un punto de partida y regresando a él para cerrar el bucle.
Para poder visualizar el proceso de creación del mapa, en un terminal del PC estación de trabajo se ejecutará el siguiente comando, que lanza el entorno de visualización "rviz" contenido en ROS:
rosrun rviz rviz -d `rospack find turtlebot_navigation`/nav_rviz.vcg
El resultado obtenido después de varias pasadas por el entorno a mapear es el siguiente (Laboratorio F6, robótica):
En el siguiente vídeo puede verse el el proceso de creación del mapa:
Se creará una carpeta llamada "mapas" dentro de nuestro package, donde se guardará el mapa con el nombre "labf6" introduciendo el siguiente comando en un terminal del PC robot:
rosrun map_server map_saver -f /home/turtlebot/ros_workspace/turtlebot_fer/mapas/labf6
Con el mapa ya creado, para que el robot se desplace por él se hará uso del stack proporcionado por ROS "navigation", que contiene los packages "map_server", "amcl" (adaptive Monte Carlo localization) y “move_base”. Cada uno de ellos, respectivamente, carga el mapa creado, ejecuta el método probabilístico de localización adaptativa Monte Carlo y planifica la ruta para ir del origen al destino dentro del mapa evitando los obstaculos. El package “move_base” se encarga de la planificación de rutas, navegación global, y evitación de obstáculos, navegación local. En la siguiente imagen se muestra el diagrama de bloques de la estructura de este package:
Cuando el robot se encuentra atascado el procedimiento, por defecto, que sigue “move_base” para intentar recuperarlo es el que se muestra en la siguiente imagen, pudiendo ser modificado este comportamiento en el parámetro “recovery_behaviors”, por otro de los disponibles:
Se creará, en la carpeta "launch" de nuestro package, el archivo "usar_mapa.launch" con el siguiente contenido:
<launch>
<include file="$(find turtlebot_bringup)/kinect.launch" />
<arg name="map_file" default="$(find turtlebot_fer)/mapas/labf6.yaml"/>
<node name="map_server" pkg="map_server" type="map_server" args="$(arg map_file)" />
<arg name="use_map_topic" default="false"/>
<arg name="scan_topic" default="scan" />
<node pkg="amcl" type="amcl" name="amcl">
<param name="use_map_topic" value="$(arg use_map_topic)"/>
<!-- Publish scans from best pose at a max of 10 Hz -->
<param name="odom_model_type" value="diff"/>
<param name="odom_alpha5" value="0.1"/>
<param name="gui_publish_rate" value="10.0"/>
<param name="laser_max_beams" value="60"/>
<param name="laser_max_range" value="12.0"/>
<param name="min_particles" value="500"/>
<param name="max_particles" value="2000"/>
<param name="kld_err" value="0.05"/>
<param name="kld_z" value="0.99"/>
<param name="odom_alpha1" value="0.2"/>
<param name="odom_alpha2" value="0.2"/>
<!-- translation std dev, m -->
<param name="odom_alpha3" value="0.2"/>
<param name="odom_alpha4" value="0.2"/>
<param name="laser_z_hit" value="0.5"/>
<param name="laser_z_short" value="0.05"/>
<param name="laser_z_max" value="0.05"/>
<param name="laser_z_rand" value="0.5"/>
<param name="laser_sigma_hit" value="0.2"/>
<param name="laser_lambda_short" value="0.1"/>
<param name="laser_model_type" value="likelihood_field"/>
<!-- <param name="laser_model_type" value="beam"/> -->
<param name="laser_likelihood_max_dist" value="2.0"/>
<param name="update_min_d" value="0.25"/>
<param name="update_min_a" value="0.2"/>
<param name="odom_frame_id" value="odom"/>
<param name="resample_interval" value="1"/>
<!-- Increase tolerance because the computer can get quite busy -->
<param name="transform_tolerance" value="1.0"/>
<param name="recovery_alpha_slow" value="0.0"/>
<param name="recovery_alpha_fast" value="0.0"/>
<remap from="scan" to="$(arg scan_topic)"/>
</node>
<node pkg="move_base" type="move_base" respawn="false" name="move_base" output="screen">
<rosparam file="$(find turtlebot_fer)/config/costmap_common_params.yaml" command="load" ns="global_costmap" />
<rosparam file="$(find turtlebot_fer)/config/costmap_common_params.yaml" command="load" ns="local_costmap" />
<rosparam file="$(find turtlebot_fer)/config/local_costmap_params.yaml" command="load" />
<rosparam file="$(find turtlebot_fer)/config/global_costmap_params.yaml" command="load" />
<rosparam file="$(find turtlebot_fer)/config/base_local_planner_params.yaml" command="load" />
</node>
</launch>
Para ejecutar "usar_mapa.launch", que inicia la Kinect, "map_server", "amcl" y "move_base", se debe introducir el siguiente comando en un terminal del PC robot:
roslaunch turtlebot_fer usar_mapa.launch map_file:=/home/turtlebot/ros_workspace/turtlebot_fer/mapas/labf6.yaml
Para interactuar con el robot se debe iniciar el entrono de visualización "rviz", ejecutando el siguiente comando en un terminal del PC estación de trabajo:
rosrun rviz rviz -d `rospack find turtlebot_navigation`/nav_rviz.vcg
Primero se deberá indicar la posición y orientación aproximada del robot dentro del mapa, pulsando en el botón "2D Pose Estimate" y posteriormente pulsando sobre el mapa en la posición estimada, sin soltar el botón del ratón indicar la orientación estimada y soltar. El modelo del robot se situará ahora en esta posición, rodeado por una nube de puntos en color rojo como se muestra en la imagen:
Para indicar el punto de destino dentro del mapa, pulsando "2D Nav Goal" y posteriormente pulsando sobre el mapa en la posición de destino, sin soltar el botón del ratón se indicara la orientación final. En este momento el robot comenzará a moverse siguiendo la trayectoria calculada, línea de color verde, hasta alcanzar el punto de destino, como se muestra en la imagen:
En el siguiente vídeo puede verse el el proceso de indicación de la situación estimada del robot y navegación al destino marcado:
En el siguiente vídeo se puede ver al Turtlebot usando la navegación autónoma, y como esquiva un objeto no incluido en el mapa, después de ejecutar el comportamiento de recuperación tras un atascamiento, recalculando la ruta:
- Nota: Para poder probar la navegación autónoma en el simulador es necesario añadir la siguiente línea de código al archivo "robot.launch", situado en la ruta opt/ros/electric/stacks/turtlebot_simulator/turtlebot_gazebo/launch:
<param name="output_frame" value="odom"/>
Esta línea deberá añadirse en la ejecución del node "robot_pose_ekf". Quedando el archivo "robot.launch" con el siguiente contenido:
<launch>
<param name="robot_description" command="$(find xacro)/xacro.py '$(find turtlebot_description)/urdf/turtlebot.urdf.xacro'" />
<node name="spawn_turtlebot_model" pkg="gazebo" type="spawn_model" args="$(optenv ROBOT_INITIAL_POSE) -unpause -urdf
-param robot_description -model turtlebot" respawn="false" output="screen"/>
<node pkg="diagnostic_aggregator" type="aggregator_node" name="diagnostic_aggregator" >
<rosparam command="load" file="$(find turtlebot_bringup)/config/diagnostics.yaml" />
</node>
<node pkg="robot_state_publisher" type="state_publisher" name="robot_state_publisher" output="screen">
<param name="publish_frequency" type="double" value="30.0" />
</node>
<!-- The odometry estimator -->
<node pkg="robot_pose_ekf" type="robot_pose_ekf" name="robot_pose_ekf">
<param name="freq" value="30.0"/>
<param name="sensor_timeout" value="1.0"/>
<param name="publish_tf" value="true"/>
<param name="odom_used" value="true"/>
<param name="imu_used" value="false"/>
<param name="vo_used" value="false"/>
<param name="output_frame" value="odom"/> <!-- NUEVA LINEA -->
</node>
<!-- throttling -->
<node pkg="nodelet" type="nodelet" name="pointcloud_throttle" args="load pointcloud_to_laserscan/CloudThrottle openni_manager" respawn="true">
<param name="max_rate" value="20.0"/>
<remap from="cloud_in" to="/camera/depth/points"/>
<remap from="cloud_out" to="cloud_throttled"/>
</node>
<!-- Fake Laser -->
<node pkg="nodelet" type="nodelet" name="kinect_laser" args="load pointcloud_to_laserscan/CloudToScan openni_manager" respawn="true">
<param name="output_frame_id" value="/camera_depth_frame"/>
<!-- heights are in the (optical?) frame of the kinect -->
<param name="min_height" value="-0.15"/>
<param name="max_height" value="0.15"/>
<remap from="cloud" to="/cloud_throttled"/>
</node>
<!-- Fake Laser (narrow one, for localization -->
<node pkg="nodelet" type="nodelet" name="kinect_laser_narrow" args="load pointcloud_to_laserscan/CloudToScan openni_manager" respawn="true">
<param name="output_frame_id" value="/camera_depth_frame"/>
<!-- heights are in the (optical?) frame of the kinect -->
<param name="min_height" value="-0.025"/>
<param name="max_height" value="0.025"/>
<remap from="cloud" to="/cloud_throttled"/>
<remap from="scan" to="/narrow_scan"/>
</node>
</launch>