Dentro de la carpeta src de nuestro paquete, crearemos un archivo llamado cliente.cpp en el que alojaremos el servidor de nuestra acción.
Recuerda que estamos haciendo uso de la librería actionlib y del mensaje creado por nosotros mismos para realizar la acción. Además del servidor definido en la entrada anterior
El código es el siguiente, recuerda que puedes acceder a él en nuestro GitHub
#include <ros/ros.h> #include <actionlib/client/simple_action_client.h> #include <actionlib/client/terminal_state.h> #include <entrada_acciones_mensajes/ContadorAction.h> #include <entrada_acciones_mensajes/ContadorGoal.h> #include <entrada_acciones_mensajes/ContadorResult.h> #include <entrada_acciones_mensajes/ContadorFeedback.h> class ContadorCliente{ protected: ros::NodeHandle _nh; actionlib::SimpleActionClient<entrada_acciones_mensajes::ContadorAction> _ac; public: ContadorCliente(): _ac("/contador", true) { ROS_INFO("Wait for the action server to start..."); _ac.waitForServer(); ROS_INFO("Server is now up."); } void sendGoal() { entrada_acciones_mensajes::ContadorGoal goal; goal.objetivo = 12; _ac.sendGoal(goal, boost::bind(&ContadorCliente::doneCb, this, _1, _2), boost::bind(&ContadorCliente::activeCb, this), boost::bind(&ContadorCliente::feedbackCb, this, _1)); ROS_INFO("Goal has been sent."); // Uncomment to cancel the goal after 2 seconds //ros::Duration(2.0).sleep(); //_ac.cancelGoal(); } void doneCb(const actionlib::SimpleClientGoalState& state, const entrada_acciones_mensajes::ContadorResultConstPtr &result) { ROS_INFO("Finished in state: %s", state.toString().c_str()); ROS_INFO("Count result: %d", (int)result->contador); } void activeCb() { ROS_INFO("Goal just went active"); } void feedbackCb(const entrada_acciones_mensajes::ContadorFeedbackConstPtr &feedback) { ROS_INFO("Feedback received. Percentage: %lf", feedback->proporcion); } }; int main (int argc, char **argv) { ros::init(argc, argv, "count_until_client"); ContadorCliente cliente; cliente.sendGoal(); ros::spin(); }
Con #include <ros/ros.h> cargamos la librería interna de ROS para C++.Luego cargamos la librería actionlib que nos ayudará a crear el cliente de nuestra acción.
Incluimos también el mensaje creado por nosotros
#include <ros/ros.h> #include <actionlib/client/simple_action_client.h> #include <actionlib/client/terminal_state.h> #include <entrada_acciones_mensajes/ContadorAction.h> #include <entrada_acciones_mensajes/ContadorGoal.h> #include <entrada_acciones_mensajes/ContadorResult.h> #include <entrada_acciones_mensajes/ContadorFeedback.h>
Al igual que para el servidor creamos un clase, en este caso llamada ContadorCliente
actionlib::SimpleActionClient<entrada_acciones_mensajes::ContadorAction> _ac;
Creamos un cliente para nuestra acción _ac y le decimos que el mensaje a usar es del tipo <entrada_acciones_mensajes::ContadorAction>
Ahora en el constructor de la clase se le pasa el nombre de la accion y se pone al cliente en estado de espera hasta que el servidor de la acción este activo.
_ac.waitForServer();
Creamos la función sendGoal(), función en la que definimos el goal que posteriormente se le pasará al servidor. además dentro de esta acción mandamos este valor de goal a través del cliente _ac
_ac.sendGoal(goal,
Y en función del estado de nuestra acción, es decir si está acabada ejecutaremos una callback determinada (doneCb), si está activa ejecutaremos activeCb, si está ejecutaándose mostraremos el feedback (feedbackCb) etc..
boost::bind(&ContadorCliente::doneCb, this, _1, _2), boost::bind(&ContadorCliente::activeCb, this), boost::bind(&ContadorCliente::feedbackCb, this, _1));
Ahora definimos cada función Cb por separado, aquí la función doneCb
void doneCb(const actionlib::SimpleClientGoalState& state, const entrada_acciones_mensajes::ContadorResultConstPtr &result) { ROS_INFO("Finished in state: %s", state.toString().c_str()); ROS_INFO("Count result: %d", (int)result->contador); }
Aquí la función activeCb
void activeCb() { ROS_INFO("Goal just went active"); }
Y por último la función feedbackCb encargada de obtener el valor del feedback que gestiona el servidor de la acción.
void activeCb() { ROS_INFO("Goal just went active"); }
Ahora y tal y como hemos hecho con el servidor en la entrada anterior, debemos modificar el archivo CmakeLists.txt de nuestro paquete, pero como ya lo hemos echo antes, sólo nos falta añadir el ejecutable y las librerías.
add_executable(contador_cliente src/cliente.cpp) target_link_libraries(contador_cliente ${catkin_LIBRARIES})
De acuerdo, ya tenemos nuestra acción montada, no te olvides de escribir catkin_make en una terminal para construir correctamente el paquete.
Si ahora escribiésemos primero
Y luego:
Y ahora en otra terminal distinta ejecutamos el cliente de la acción que le pasará el goal al servidor:
Resultando en la terminal:
Es decir, nuestra acción se ha ejecutado gracias a que el cliente de la acción le ha pasado un objetivo o goal al servidor de la acción. Este servidor al comprobar que la acción ni está acabada, ni se ha mandado cancelar, va a devolver el feedback al cliente. Y este cliente nos va a pasar el valor del feedback por pantalla.
En cuanto se llega al valor objetivo el servidor finaliza la acción.
Si ahora una vez que hemos ejecutado la acción y vemos los topics que hay por pantalla
Así vemos que en realidad hemos publicado 5 topics, pero la librería actionlib nos está ayudando y hace que nos olvidemos de ello facilitándonos mucho la vida.
Pues bien hasta aquí la entrada de acciones de ROS. Pronto empezaremos a aplicar estos conocimientos en un caso práctico.
Hasta la próxima!!!!!