Distributed Graphic Planner (DGP)
El Distributed Graphic Planner (DGP) es un entorno ejecutivo independiente del middleware (middleware-agnostic) diseñado para especificar, programar, analizar y supervisar aplicaciones distribuidas complejas utilizando Redes de Petri Interpretadas por Mensajes (MIPN). En lugar de depender de pasarelas o puentes inter-middleware especializados y de alta latencia (como los acopladores ROS-a-MQTT), DGP implementa una Interfaz de Middleware Abstracta que se conecta de forma nativa y concurrente a ROS 1, ROS 2, MQTT y JIPC bajo una única lógica de tareas. El entorno incluye un entorno de desarrollo integrado (IDE) para la especificación visual de tareas, la verificación formal de modelos y el seguimiento gráfico de la ejecución, así como un despachador (dispatcher) de alto rendimiento guiado por eventos que ejecuta los modelos de tareas de manera eficiente y con un consumo de recursos despreciable.
GDGP. General Distributed Graphical Planner.
Esta versión de DGP es capaz de trabajar con varios middlewares al mismo tiempo: MQTT, ROS1, ROS2 y JIPC. Ha sido probada con los siguientes middlewares:
- ROS1. ROS Noetic.
- ROS2. ROS Jazzy.
- MQTT. Utilizando el bróker Mosquitto.
- JIPC. Versión 4.0.
Todos los comandos en este documento y los scripts fueron desarrollados para linux. Sin embargo, dado que todo se ha programado en Java, se puede encontrar fácilmente un equivalente en windows.
Requisitos
- Java 17+
Descarga
- Descargue la última versión here.
Instalación
Para instalar GDGP, siga estos sencillos pasos:
- Descomprima el archivo tar en el directorio donde desee instalarlo. De aquí en adelante se asumirá que se realiza en su carpeta personal (
~):
tar -xzvf GDGP.tar.gz
Ejecución
Tras instalar el sistema como se describe en la sección anterior, si tiene instalado Java (versión 17 o superior) debería poder iniciar los módulos de GDGP:
- Inicie el despachador (dispatch) de RoboGraph para ejecutar tareas (redes de Petri):
~/DGP/bin$ ./GDGPDispatch - Inicie la interfaz gráfica (GUI) de RoboGraph para editar tareas y monitorizar su ejecución:
~/DGP/bin$ ./GDGPGui
Debería ver la siguiente ventana:
Por defecto, solo está activado el middleware ROS1. Para cambiar esto, consulte la sección "Activación de middlewares". En este punto ya tiene el sistema básico funcionando. Utilizando la GUI puede crear su primer proyecto DGP con paquetes y algunas tareas básicas (redes de Petri) con temporizadores, por ejemplo. Sin embargo, una aplicación real incluirá varios módulos (modulo1, modulo2, ...) ejecutándose sobre uno o varios middlewares (hasta el momento se han implementado JIPC, ROS1, ROS2 y MQTT) junto con mensajes (msg1, msg2, ...) para compartir información entre ellos.
Activación de middlewares
La lista de middlewares activados en DGP se define en el archivo de texto DGP/res/middleware_list.
Al iniciarse, DGPDispatch intentará conectarse a todos los middlewares enumerados allí. Tenga en cuenta que si activa middlewares como JIPC, ROS1 o MQTT, deberá iniciar JCentral, roscore o el bróker de MQTT antes de arrancar cualquier otra cosa:
- JIPC. Para iniciar jcentral proporcionamos el script
~/DGP/bin/jcentral. También puede iniciar un jcentral externo. - ROS1. Para iniciar roscore proporcionamos el script
~/DGP/bin/startROSCore. También puede iniciar un roscore externo. - ROS2. ROS2 no tiene un roscore centralizado. No es necesario iniciar nada de forma previa.
- MQTT. Recomendamos iniciar el bróker mosquitto (
mosquitto -v).
Pruebas del sistema
Se proporcionan algunos ejemplos para probar si los diferentes middlewares funcionan correctamente. En primer lugar, es recomendable probar cada middleware de forma individual. Las MIPNs utilizadas en esta sección se encuentran en el proyecto DGP/examples/paper_RAS. Primero debe abrir el proyecto y compilarlo para generar los archivos de clase (.class).
Pruebas de DGP con ROS1.
- Inicie roscore:
~/DGP/bin$ ./startROSCore - Inicie DGP dispatch:
~/DGP/bin$ ./GDGPDispatch - Inicie DGP GUI:
~/DGP/bin$ ./GDGPGui
A continuación, en la GUI mostrada en la imagen anterior, debe seguir estos pasos:
- Cargue el proyecto
~/DGP/examples/paper_RAS/paperROS1- Archivo -> Abrir proyecto y seleccione el proyecto (si desea ver la MIPN, abra la red "latency" dentro del paquete
ROS1Tests).
- Archivo -> Abrir proyecto y seleccione el proyecto (si desea ver la MIPN, abra la red "latency" dentro del paquete
- Compile el proyecto
- Haga clic en el menú "Compile" y compile el proyecto.
- Inicie la tarea. Para ello, debe enviar un mensaje a DGP dispatch solicitando el inicio de la tarea. Esto se puede hacer desde la interfaz gráfica de DGP:
- Haga clic en el botón de la izquierda ("switch running mode") para cambiar al modo de depuración (debug).
- Haga clic en el cuarto botón desde la izquierda (flecha azul: "start monitoring running task") para monitorizar las tareas en ejecución.
- Haga clic en el segundo botón desde la izquierda (coche de carreras: "execute a task") para enviar un mensaje al despachador solicitando la ejecución de una tarea:
- En el cuadro del nombre de la red de Petri, escriba "ROS1Tests.latency" como se muestra en la imagen anterior.
- En el campo "Owner" puede escribir unos pocos caracteres (por ahora no importa), por ejemplo, "XXX".
- Haga clic en Ok.
- Debería ver la red de Petri y una marca (token) en el lugar "start" que, en un par de segundos, se moverá a "wait". En ese estado, permanece a la espera del mensaje de ROS1 "std_msgs.String" con el tema (topic) "/disa/dgp/paper/". Cada vez que recibe este mensaje, dispara la transición MSG_arrive, lo registra y vuelve a wait. Esto se realiza tan rápido que es posible que solo vea parpadear la marca. Tras tres minutos, se dispara la transición T3 y la tarea finaliza.
4. Inicie otro módulo de ROS1 que publique el mensaje:
- Proporcionamos como ejemplo un módulo que publica el mensaje de ROS1 "std_msgs.String" con el tema "/disa/dgp/paper/". Para iniciar este programa:
~/DGP/bin$ ./ROS1PublisherPaper
Comenzará a publicar el mensaje e imprimirá en pantalla el número del mensaje y el tiempo. El lugar Report en la MIPN escribirá en el archivo ROS1.txt una línea por cada mensaje recibido con el número de mensaje, la marca de tiempo en que fue enviado y el tiempo en que el despachador reaccionó al mensaje.
Pruebas de DGP con ROS2.
- Comente (usando %) todos los middlewares en el archivo
DGP/res/middleware_listexcepto la línea conROS2_LP. - Inicie el despachador ejecutando el archivo:
~/DGP/bin$ ./GDGPDispatch - Ordene la ejecución de la red de Petri. Puede hacerlo desde la GUI iniciando
DGP/bin/GDGPGuicomo en el caso de ROS1, cambiando luego al modo de monitorización, haciendo clic en el botón de ejecutar red de Petri y escribiendoROS2Tests.latencycomo nombre de la red. - Por último, debe iniciar el módulo de ROS2 que publica mensajes con el tema
"/helloRos"de tipoid.jrosmessages.std_msgs.StringMessagecon una cadena que incluya"Hello ROS #ROS2%"+ index +" "+ time +" $", donde 'index' es el índice del mensaje y 'time' es el tiempo de publicación actual en milisegundos (System.currentTimeMillis()). También puede suscribirse al mensaje"ROS2/miTopic2"publicado por la red de Petri. Proporcionamos un ejemplo que se puede arrancar con:~/DGP/bin$ ./ROS2PublisherPaper
Pruebas de DGP con MQTT.
- Comente (usando %) todos los middlewares en el archivo
DGP/res/middleware_listexcepto la línea conMQTT_PAHO. - Inicie el bróker de MQTT (por ejemplo, el bróker mosquitto).
- Inicie el despachador ejecutando el archivo:
~/DGP/bin$ ./GDGPDispatch - Ordene la ejecución de la red de Petri. Puede hacerlo desde la GUI iniciando
DGP/bin/GDGPGui, cambiando luego al modo de monitorización, haciendo clic en el botón de ejecutar red de Petri y escribiendoMQTTTests.latencycomo nombre de la red. - Por último, debe iniciar el módulo de MQTT que publica mensajes con el tema
"miTopic"con una cadena que incluya"Hello ROS #MQTT%"+ index +" "+ time +" $", donde 'index' es el índice del mensaje y 'time' es el tiempo de publicación actual en milisegundos (System.currentTimeMillis()). También puede suscribirse al mensaje"MQTT/miTopic2"publicado por la red de Petri. Proporcionamos un ejemplo que se puede arrancar con:~/DGP/bin$ ./MQTTPublisherPaper
Pruebas de DGP con JIPC.
- Comente (usando %) todos los middlewares en el archivo
DGP/res/middleware_listexcepto la línea conJIPC. - Inicie jcentral. Puede hacerlo en su propio sistema o ejecutando el archivo:
~/DGP/bin$ ./jcentral - Inicie el despachador ejecutando el archivo:
~/DGP/bin$ ./GDGPDispatch - Ordene la ejecución de la red de Petri. Puede hacerlo desde la GUI iniciando
DGP/bin/GDGPGui, cambiando al modo de monitorización, haciendo clic en el botón de ejecutar red de Petri e ingresandoJIPCTests.latencycomo nombre de la red. - Finalmente, necesita iniciar el módulo de JIPC que publica mensajes con el tema
"/disa/dgp/paper/"de tipostd_msgs.Stringcon una cadena que incluya"Hello ROS #ROS1%"+ index +" "+ time +" $", donde 'index' es el índice del mensaje y 'time' es el tiempo de publicación actual en milisegundos (System.currentTimeMillis()). También puede suscribirse al mensaje"ROS1/miTopic2"publicado por la red de Petri. Proporcionamos un ejemplo que puede iniciarse con:~/DGP/bin$ ./ROS1PublisherPaper
NOTA: Rara vez verá el token en el lugar Report porque la transición T2 se dispara inmediatamente (no tiene condición asociada). Sin embargo, debería ver el archivo correspondiente con los tiempos de latencia (ROS1.txt, ROS2.txt, MQTT.txt o JIPC.txt).
La siguiente imagen muestra la red de Petri para la prueba de latencia:
Tras dos segundos, el temporizador startTime finalizará y el lugar wait recibirá un token. En este estado, si se recibe el mensaje correcto desde el módulo publicador, se disparará la transición Msg_arrive escribiendo en un archivo de texto (<MDLWR>.txt) el índice del mensaje, el tiempo en que llegó y el tiempo en que se disparó la transición. Se activa el lugar Report y se publica un mensaje "<MDLWR>/miTopic2".
Ejemplo de la Tortuga (Turtlesim)
Otro ejemplo proporcionado es una red de Petri que mueve la tortuga de ROS llamada turtle_square. La red de Petri se encuentra en el proyecto DGP/examples/Tasks y se muestra en la siguiente imagen:
Esta red consta de cinco ramas que se comportan de la misma manera. Cada rama de esta red de Petri tiene dos lugares. Las acciones asociadas al primer lugar (P1) son iniciar un temporizador (pub_rate) con 500 ms y enviar el mensaje para avanzar (geometry_msgs/Twist). La transición T9 está asociada con el fin del temporizador pub_rate, de modo que se dispara tras 500 ms y el primer lugar (P1) recupera el token, volviendo a ejecutar las acciones asociadas. El resultado es que cada 500 ms se publica el mensaje para avanzar hasta que se dispara la otra transición (T1). Esta transición también está asociada a otro temporizador (action_time), pero esta vez de 5 segundos. Transcurrido ese tiempo, el token se mueve al segundo lugar, donde se envía un mensaje que ordena a la tortuga girar cada 500 ms.
Para ejecutar esta tarea, debe realizar los siguientes pasos, que son muy similares para los ejemplos de ROS1 y ROS2:
- ROS1.
- Comente (usando %) todos los middlewares en el archivo
DGP/res/middleware_listexcepto la línea conROS1_SA. - Inicie roscore. Puede hacerlo en su propio sistema o ejecutando el archivo:
~/DGP/bin$ ./startROSCore - Inicie turtlesim en ROS1. Puede utilizar una versión de Docker (se probó con
osrf/ros:noetic-desktop-full). El simulador de la tortuga debe iniciarse de la siguiente manera:rosrun turtlesim turtlesim_node - Inicie el despachador (dispatch):
~/DGP/bin$ ./GDGPDispatch - Ordene la ejecución de la red de Petri. Puede hacerlo desde la GUI iniciando
DGP/bin/GDGPGui, cambiando luego al modo de monitorización, haciendo clic en el botón de ejecutar red de Petri y escribiendoros1.turtle_squarecomo nombre de la red.
- Comente (usando %) todos los middlewares en el archivo
Después de esto, debería ver a la tortuga moviéndose, ¡aunque no espere un cuadrado perfecto! :-)
Definición de mensajes personalizados
El tipo de mensajes que se pueden publicar o suscribir depende del middleware. Por ejemplo, en el caso de MQTT solo hay un tipo de mensaje (String), pero middlewares como ROS1, ROS2 y JIPC permiten definir mensajes que contienen diferentes tipos de datos. Para su proyecto personal, es posible que desee incluir los mensajes que los módulos intercambian con DGP. Estos mensajes deben definirse como clases Java que deben incluirse en paquetes Java. Dichos paquetes deben estar incluidos en el CLASSPATH al ejecutar el sistema. DGP lee la lista de estos paquetes desde los siguientes archivos de texto:
DGP/res/modulesROS1. Lista de los paquetes Java donde se definen los mensajes de ROS1.DGP/res/modulesROS2. Lista de los paquetes Java donde se definen los mensajes de ROS2.DGP/res/modulesJIPC. Lista de los paquetes Java donde se definen los mensajes de JIPC.
Puede utilizar los mensajes estándar incluidos en los entornos de desarrollo de ROS1 o ROS2 (consulte "Mensajes estándar" más abajo) o definir sus propios mensajes (consulte "Añadir mensajes personalizados" más abajo).
Mensajes estándar
Puede utilizar los mensajes estándar de ROS 1 (jros1messages) o ROS 2 (jros2messages) junto con los mensajes generales de ROS (jrosmessages), pero tenga en cuenta que DGP adopta un espacio de nombres de mensajes unificado para todos los middlewares soportados (ver más abajo). Para añadir estos paquetes de mensajes estándar, debe agregar el paquete al archivo de texto correspondiente:
DGP/res/modulesROS1. Para mensajes de ROS1.DGP/res/modulesROS2. Para mensajes de ROS2.DGP/res/modulesJIPC. Para mensajes de JIPC.
Evidentemente, los paquetes Java de la lista deben encontrarse en el CLASSPATH de Java.
Añadir mensajes personalizados
La adición de mensajes personalizados depende del middleware que se vaya a utilizar. De manera general, se proporciona un proyecto de IntelliJ IDEA para cada middleware:
- ROS1.
DGP/IdeaMsgProjects/ROS1CustomMsgs - ROS2.
DGP/IdeaMSgProjects/ROS2CustomMsgs
Debe seguir estos pasos para incluir sus mensajes personalizados:
- Añada sus mensajes personalizados al proyecto. Consulte el archivo
readme.mddentro de ese proyecto. - Se creará un archivo .jar en el directorio
build/libs. Copie dicho archivo jar a un directorio que esté en el CLASSPATH. Por ejemplo, en~/DGP/classsi descomprimió el archivoGDGP.tar.gzen su carpeta personal. - Añada el nombre del paquete de sus mensajes al archivo
~/DGP/res/modules.
En general, el formato de los mensajes depende del middleware:
- ROS1. Para mensajes personalizados, consulte el proyecto de IntelliJ
ROS1Messages. Ese proyecto genera los archivos Java a partir de los archivos .msg, pero también puede crear los archivos Java manualmente dentro de un paquete Java y añadir el paquete aDGP/res/modulesROS1. - ROS2. Para mensajes personalizados, consulte el proyecto de IntelliJ
ROS2CustomMsgs. Ese proyecto genera los archivos Java a partir de los archivos .msg, pero también puede crear los archivos Java manualmente dentro de un paquete Java y añadir el paquete aDGP/res/modulesROS2. - JIPC. Puede crear las clases de mensajes Java en un paquete Java y añadir dicho paquete a
DGP/res/modulesJIPC.
Manejo de los mismos mensajes en diferentes middlewares
DGP adopta un espacio de nombres de mensajes unificado para todos los middlewares soportados. Los mensajes que representan el mismo tipo de datos pueden definirse en múltiples middlewares (por ejemplo, std_msgs/String en ROS1 y ROS2). La publicación de un mensaje provoca su propagación a todos los middlewares en los que ese mensaje esté definido; el mismo comportamiento se aplica a las suscripciones. For ejemplo, el mensaje robograph/roboGraph_dispatch_run_PN_message debe estar definido en cada middleware destinado a ordenar la ejecución de tareas a DGP dispatch. Este diseño simplifica la comunicación entre middlewares y la gestión de espacios de nombres. El aislamiento de mensajes específicos de un middleware se puede lograr organizando los mensajes bajo un paquete raíz con el nombre del middleware correspondiente.
PROBLEMA CON ROS2: Hemos detectado varios inconvenientes con la API de ROS2. Uno de ellos está relacionado con la publicación de una gran cantidad de mensajes sin un suscriptor activo. Esto implica que se debe tener especial cuidado cuando se defina el mismo tipo de mensaje en ROS2 y en otros middlewares.
Mensajes de DGP
DGP dispatch define sus propios mensajes. Por un lado, se suscribe a mensajes para:
- Ejecutar una tarea (
roboGraph_dispatch_run_PN_message), - Detener una tarea (
roboGraph_dispatch_kill_PN_message), - Solicitar el estado de una tarea (
roboGraph_dispatch_query_stat_PN_message), - Solicitar el estado de todas las tareas (
roboGraph_dispatch_query_stat_message).
Por otro lado, publica mensajes para:
- Informar el estado de una tarea (
roboGraph_dispatch_stat_PN_message) - Informar el estado de todas las tareas (
roboGraph_dispatch_stat_message) - Informar la finalización de una tarea (
roboGraph_dispatch_end_PN_message)
La comunicación entre DGP dispatch y DGP GUI utiliza la mayoría de estos mensajes. Dado que no tiene sentido que se comuniquen a través de más de un middleware, cuando DGP GUI se inicia, si se definen varios middlewares, se seleccionará uno según el siguiente orden de preferencia: primero MQTT; si no está habilitado, se utilizará JIPC; si ninguno de estos está activo, se empleará ROS1; y finalmente, como último recurso, se usará ROS2.
PROBLEMA CON ROS2: Debido al problema de la API de ROS2 mencionado anteriormente, el módulo de mensajes ros2.robograph no debe incluirse en DGP/res/modulesROS2 a menos que este sea el único middleware activo.