Un artículo para entender spin_some, spin y spinOnce de ROS2

Tabla de contenido

escrito en frente

1. girar y girar una vez en ROS

1.1 Análisis del mecanismo de devolución de llamada

1.2 ¿Por qué especificar queue_size al suscribirse a un tema?

1.3 Consejos para configurar queue_size

1.4 Resumen del uso de spin y spinOnce:

2. spin_some y girar en ROS2

2.1 Piensa en las notas oficiales de spin y spin_some

2.2 Una pequeña diferencia en spin_some

ultimas palabras


escrito en frente

ROS2 tiene spin_some, spin y ROS tiene spinOnce, spin, ¿cuál es la diferencia y la conexión entre ellos?

Si ha aprendido ROS, solo lea la primera parte.

Si aprende ROS2 directamente, también se recomienda leerlo para profundizar su comprensión.

1. girar y girar una vez en ROS

Si es nuevo en ROS, probablemente haya leído este tutorial oficial de ROS muy detallado, que menciona el uso básico de spin y spinOnce. Sin embargo, estimo que es muy probable que después de leerlo, todavía no entiendas la diferencia entre los dos y cómo usarlos.

ROS/Tutorials/WritingPublisherSubscriber(c++) - ROS Wiki http://wiki.ros.org/ROS/Tutorials/WritingPublisherSubscriber%28c++%29 No se preocupe, extraeremos el contenido relevante y probaremos.

ros::girarOnce()

Llamar a ros::spinOnce() aquí no es necesario para este programa simple, porque no estamos recibiendo ninguna devolución de llamada. Sin embargo, si agregara una suscripción a esta aplicación y no tuviera ros::spinOnce() aquí, sus devoluciones de llamada nunca serían llamadas. Entonces, agréguelo por si acaso.

El párrafo anterior se extrajo de los comentarios del código de nodo del editor.

La idea general es que, como un programa de publicación puro y simple, no hay necesidad de usar spinOnce(), porque no ejecuta ninguna devolución de llamada. Sin embargo, si desea agregar la funcionalidad de suscripción a este programa sin usar spinOnce(), no se generará la devolución de llamada.

ros::girar() 

ros::spin() entra en un bucle, llamando a las devoluciones de llamada de mensajes lo más rápido posible. Sin embargo, no se preocupe, si no hay nada que hacer, no usará mucha CPU. ros::spin() se cerrará una vez que ros::ok() devuelva falso, lo que significa que se ha llamado a ros::shutdown(), ya sea por el controlador predeterminado Ctrl-C, el maestro nos dice que apaguemos, o se llama a mano.

Este pasaje es un extracto de los comentarios del código de nodo del suscriptor.

La idea general es que spin() entra en un bucle infinito y ejecuta la devolución de llamada del mensaje lo más rápido posible. Sin embargo, no necesita preocuparse por el uso de la CPU, ya que no consume muchos recursos de la CPU cuando no tiene nada que hacer. Hay varias formas de activar la salida de spin(), como que ros:ok() devuelva falso (normalmente a partir del resultado de la llamada a ros::shutdown(), desde el identificador ctrl-c o manualmente).

1.1 Análisis del mecanismo de devolución de llamada

Echemos un vistazo a la lógica detrás de esto.

Primero, solo los nodos que usan funciones de devolución de llamada necesitan usar spin o spinOnce. Por lo general, es el nodo el que necesita suscribirse al tema.

Sin embargo, el suscriptor del mensaje Subscriber solo especifica la función de devolución de llamada para el tema. Cuando el programa recibe el tema, no ejecuta la función de devolución de llamada inmediatamente, sino que coloca la función de devolución de llamada en una cola de funciones de devolución de llamada. Podemos pensar que cada vez que se recibe un tema, la función de devolución de llamada correspondiente se pondrá en la cola. Sus nombres de función son los mismos, pero los parámetros reales son diferentes.

Entonces, ¿cuándo se ejecutará la función de devolución de llamada en la cola de la función de devolución de llamada?

Esto requiere la ayuda de ros::spin() y ros::spinOnce().

Cuando se llama a la función spinOnce, el sistema procesará la función de devolución de llamada al principio de la cola de la función de devolución de llamada y saldrá después de la ejecución. Entonces, hay un problema con eso. Dado que la longitud de cualquier cola de función de devolución de llamada es limitada, si el editor envía datos demasiado rápido, se sobrescribirán los datos antiguos en la cola. Cuando la frecuencia de la llamada a la función spinOnce es demasiado baja, se producirá una pérdida de datos.

La función de giro también puede llamar a la función de devolución de llamada en la cola de la función de devolución de llamada. A diferencia de spinOnce, incluso si la cola de la función de devolución de llamada está vacía, no saldrá, sino que esperará nuevas tareas en la cola de la función de devolución de llamada cíclicamente. Una vez que la cola tenga una función de devolución de llamada, se ejecutará inmediatamente. Si no, seguirá bloqueándose.

Sí, spin() hará que el nodo entre en un estado de bloqueo, pero no se preocupe, no consume muchos recursos de la CPU.

1.2 ¿Por qué especificar queue_size al suscribirse a un tema?

Al suscribirse a un tema, se especifica el parámetro queue_size. Es la longitud de la lista de funciones de devolución de llamada. Cuando se suscribe a un tema, hay un búfer de suscripción; cuando se publica un tema, hay un búfer de publicación, que limita la longitud de la cola del búfer.

Entonces, ¿por qué existe este parámetro?

Para un tema suscrito, si la frecuencia de publicación es muy rápida, pero el tiempo de procesamiento en la función de devolución de llamada es muy largo, o porque la frecuencia de ejecución de spinOnce es demasiado baja, entonces, durante esta ventana de tiempo de procesamiento, se recibirán algunos temas suscritos Contenido, el contenido de este tema activará las tareas de devolución de llamada correspondientes, estas tareas de devolución de llamada son demasiado tarde para ser procesadas y solo pueden ingresar a la cola, es decir, al búfer de suscripción.

Cuando se ejecuta la función de giro o se vuelve a ejecutar spinOnce, el sistema volverá a procesar las tareas de devolución de llamada en la cola.

Si el área del búfer es lo suficientemente grande, ocasionalmente se agota el tiempo de ejecución de la función de devolución de llamada y la información histórica del tema también se puede guardar en el búfer para garantizar que la información no se pierda. Sin embargo, si el área del búfer no es lo suficientemente grande, o si el tiempo de procesamiento de la función de devolución de llamada se agota, inevitablemente se producirá un desbordamiento del búfer. En este punto, se perderá la tarea de devolución de llamada más antigua en el caché y se agregarán nuevas tareas de devolución de llamada.

En otras palabras, el búfer es una cola FIFO de primero en entrar, primero en salir.

1.3 Consejos para configurar queue_size

Por lo general, si tiene requisitos elevados en tiempo real y desea procesar la información de publicación más reciente cada vez, entonces queue_size se puede establecer en 1, de modo que cada devolución de llamada procese el tema más reciente.

Si queue_size es 0, indica que la cola de la función de devolución de llamada es infinita. Si no quiere perderse todos los temas publicados, puede configurar queue_size un poco más grande, lo que consumirá más recursos en consecuencia.

La frecuencia de ejecución de ros::spinOnce() es de 5 Hz y la frecuencia del tema suscrito es de 10 Hz. En términos sencillos, el tema se publica dos veces durante el intervalo entre dos ejecuciones de spinOnce, luego, obviamente, la longitud de la cola del búfer debe be Si es mayor que 2, es posible asegurarse de que los datos no se perderán.

Por supuesto, esto todavía no puede garantizar que la información no se perderá, solo se puede decir que "es posible garantizar que los datos no se perderán". Porque la función de devolución de llamada puede tardar mucho tiempo en ejecutarse, etc. Esta es otra pregunta, más allá del alcance de este artículo.

1.4 Resumen del uso de spin y spinOnce:

1) No piense que siempre que se especifique una función de devolución de llamada, el sistema la activará automáticamente. Solo cuando el programa se ejecuta ros::spin()o ros::spinOnce(),la función de devolución de llamada puede realmente tener efecto.

2) Después de que el programa se ejecuta en ros::spin(), siempre irá al búfer de suscripción de temas para verificar si hay una función de devolución de llamada. Si es así, procese la función de devolución de llamada; de lo contrario, continúe comprobando y esperando. Por lo tanto, cuando el programa ejecuta ros::spin(), continuará esperando la función de devolución de llamada sin procesar otras tareas. En otras palabras, el código detrás de ros::spin() no tiene posibilidad de ejecutarse.

3) Cuando el programa llegue a ros::spinonce(), irá al búfer de suscripción de temas para verificar si hay una función de devolución de llamada. Si la hay, procese inmediatamente una función de devolución de llamada y salga; de lo contrario, salga de esta instrucción y ejecute los códigos subsiguientes.

4) ros::spinOnce es más flexible que ros::spin(). A menudo se usa para escribir bucles manualmente y se puede colocar en cualquier parte del programa. Sin embargo, la relación entre la frecuencia de ejecución de spinOnce y la frecuencia de publicación del tema suscrito debe ser considerado. Y spin () es más grosero. De todos modos, ha estado esperando a que se genere el tema. Solo preste atención a que la función de devolución de llamada no siga agotando el tiempo de espera, o asegúrese de que el búfer de suscripción sea lo suficientemente grande.

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

2. spin_some y girar en ROS2

No hay spinOnce en ros2, y se reemplaza por spin_some.

Simplemente podemos entender que la función spin() de ROS1 y ROS2 se usa de la misma manera, y spin_some() se usa de la misma manera que ROS1 ros::spinOnce().

2.1 Piensa en las notas oficiales de spin y spin_some

girar()

Cree un ejecutor de subproceso único predeterminado y gire el nodo especificado.
Trabaje periódicamente a medida que esté disponible para nosotros. Llamada de bloqueo, puede bloquear indefinidamente.

spin() crea un ejecutor de subproceso único predeterminado que sirve al nodo especificado. Después de que surta efecto, el ciclo se ejecuta. Estado de bloqueo infinito.

girar_algo()

Cree un ejecutor de subproceso único predeterminado y ejecute cualquier trabajo disponible de inmediato.
Complete todo el trabajo en cola disponible sin bloquear.

Esta función se puede anular. La implementación predeterminada es adecuada para un modelo de ejecución de un solo subproceso. Agregar suscripciones, temporizadores, servicios, etc. con devoluciones de llamadas de bloqueo hará que esta función se bloquee (lo que puede tener consecuencias no deseadas).
 

spin() crea un ejecutor de subproceso único predeterminado que procesa inmediatamente las tareas de devolución de llamada válidas y procesa todas las tareas en la cola sin bloquear. A juzgar por la descripción hasta aquí, es similar a la función spinOnce de ROS1.

Entonces, el segundo párrafo dice, ¡esta función puede anularse! Su implementación predeterminada es adecuada para un solo hilo de ejecución. Al agregar temas de suscripción, temporizadores, servicios y bloquear funciones de devolución de llamada, puede causar que esta función "originalmente sin bloqueo" también se bloquee y tenga consecuencias impredecibles.

Está bien, eso lo sabemos.

Según tengo entendido, en términos simples, la mayor diferencia entre spin_some y spinOnce es que una vez que el primero comienza a funcionar, debe hacer todo lo posible para procesar las devoluciones de llamada válidas en el búfer de suscripción; mientras que el segundo, después de que spinOnce comienza a funcionar , solo procesa el frente de la cola.

2.2 Una pequeña diferencia en spin_some

Hay algunos pequeños detalles a los que prestar atención cuando se usa spin_some, tomando C++ como ejemplo.

Después de spin_some, hay un nodo de parámetro, que es un puntero inteligente de tipo rclcpp::Node. Al usar spinOnce, este puntero apunta a una clase que personalizamos, es decir, una subclase de rclcpp::Node. Por lo tanto, traiga directamente en el puntero this, y rclcpp::spin_some(this) informará una discrepancia de tipos.

El uso correcto es crear un puntero inteligente de rclcpp::Node en el contexto y apuntar a su objeto de subclase this, a saber

rclcpp::Node::SharedPtr node_(this); // 创建基类指针,指向子类对象this
rclcpp::spin_some(node_); // 运行正常

ultimas palabras

El llamado bloqueo significa que una vez que la función entra en el rol, quedará atrapada en su propio mundo y no podrá salir. Como girar.

El llamado no bloqueo es trabajar cuando es hora de trabajar y descansar cuando es hora de descansar. Solo haz un trabajo a la vez, eso es spinOnce; una vez que tengas la oportunidad de hacerlo, puedes hacer más, eso es spin_some.

Deténgase aquí.

Espero que te ayude.

Supongo que te gusta

Origin blog.csdn.net/slampai/article/details/127992755
Recomendado
Clasificación