[ROS] Comunicación del tema

La comunicación por temas es el modo de comunicación más utilizado en ROS. La comunicación por temas se basa en el modelo de publicación-suscripción , es decir, un nodo publica un mensaje y otro nodo se suscribe al mensaje. Los escenarios de aplicación de la comunicación temática también son extremadamente amplios, como el siguiente escenario común:

El robot está realizando la función de navegación y el sensor utilizado es el lidar. El robot recopilará la información detectada por el lidar y la calculará, y luego generará información de control de movimiento para hacer que el chasis del robot se mueva.

En el escenario anterior, la comunicación temática se utiliza más de una vez.

  • Tomando como ejemplo la recopilación y el procesamiento de información LIDAR, hay un nodo en ROS que libera los datos recopilados por el radar actual de vez en cuando cuando es necesario, y también hay nodos en el módulo de navegación que se suscribirán y analizarán el datos de radar.

  • Tomando como ejemplo la publicación de información de movimiento, el módulo de navegación calculará la información de control de movimiento de vez en cuando en función de los datos recopilados por el sensor y la publicará en el chasis. El chasis también puede tener un nodo para suscribirse al movimiento. información y finalmente convertirla en una señal de pulso para controlar el motor.

Por analogía, la recopilación de datos de sensores como radar, cámara, GPS, etc., también utiliza comunicación tópica, es decir, la comunicación tópica es adecuada para escenarios de aplicación relacionados con la transmisión de datos constantemente actualizados.

concepto

El modo de comunicación de la interacción de datos entre diferentes nodos se realiza en forma de publicación y suscripción.

efecto

Se utiliza para escenarios de transmisión de datos que se actualizan constantemente y se procesan de forma menos lógica.

1 Modelo teórico

El modelo de implementación de la comunicación temática es más complicado. El modelo se muestra en la figura siguiente. Hay tres roles involucrados en el modelo:

  • ROS Master (Gerente)

  • Talker (editor)

  • Oyente (suscriptor)

ROS Master es responsable de mantener la información registrada por Talker y Listener, y hacer coincidir a Talker y Listener con el mismo tema, ayudando a Talker y Listener a establecer una conexión. Una vez establecida la conexión, Talker puede publicar mensajes, y los mensajes publicados serán suscrito por Listener.

Todo el proceso se realiza mediante los siguientes pasos:

(0). Registro de orador

Después de que se inicie Talker, registrará su propia información en ROS Master a través de RPC, que contiene el nombre del tema del mensaje publicado. ROS Master agregará la información de registro del nodo al registro.

(1) Registro de oyentes

Una vez que se inicia el Listener, también registrará su propia información en el ROS Master a través de RPC, incluido el nombre del tema al que debe suscribirse. ROS Master agregará la información de registro del nodo al registro.

(2). ROS Master se da cuenta de la coincidencia de información

ROS Master comparará Talker y Listener de acuerdo con la información en el registro y enviará la información de la dirección RPC de Talker al Listener a través de RPC.

(3) El oyente envía una solicitud a Talker

Según la dirección RPC recibida, el Oyente envía una solicitud de conexión al Talker a través de RPC y transmite el nombre del tema, el tipo de mensaje y el protocolo de comunicación (TCP / UDP) al que está suscrito.

(4) Solicitud de confirmación del hablante

Una vez que el Talker recibe la solicitud del Listener, también confirma la información de conexión al Listener a través de RPC y envía su propia información de dirección TCP.

(5). El oyente y el hablante están conectados

El oyente usa TCP para establecer una conexión de red con Talker de acuerdo con el mensaje devuelto en el paso 4.

(6) El hablante envía mensajes al oyente

Una vez establecida la conexión, Talker comienza a publicar mensajes en el oyente.

Nota 1: En el proceso de implementación anterior, el protocolo RPC utilizado en los primeros cinco pasos y el protocolo TCP utilizado en los dos últimos pasos

Nota 2: No hay ningún requisito de pedido para el inicio de Talker and Listener

Nota 3: puede haber varios hablantes y oyentes

Nota 4: Una vez establecida la conexión entre Talker y Listener, ROS Master ya no es necesario. Es decir, incluso si ROS Master está cerrado, Talker y Listern se comunican como de costumbre.

Puntos a los que prestar atención en la comunicación temática:

0: la mayor parte de la implementación se ha encapsulado;

1: Configuración del tema;

2: Siga la implementación del editor;

3: Siga la realización del suscriptor;

4: preste atención al portador de mensajes

2 Funcionamiento básico de la comunicación temática A (C ++)

demanda:

Escriba una implementación de publicación y suscripción, que requiera que el editor publique mensajes de texto con una frecuencia de 10 HZ (10 veces por segundo) y que el suscriptor se suscriba al mensaje e imprima el contenido del mensaje.

análisis:

En la implementación del modelo, no es necesario implementar el maestro ROS y se ha encapsulado el establecimiento de la conexión.Hay tres puntos clave a los que se debe prestar atención:

(1) Emisor

(2) Destinatario

(3) Datos (texto normal aquí)

Proceso:

(1) Escriba la fiesta de lanzamiento para lograr;

(2) Implementación de suscriptor de escritura;

(3) Edite el archivo de configuración;

(4) Compilar y ejecutar.

1. Emisor

Versión simple

#include "ros/ros.h"
#include  "std_msgs/String.h"

/**
 * 发布方实现:
 *          1 包含头文件;   ROS中的文本类型是-------》std_msgs/String.h
 *          2 初始化ros结点;
 *          3 创建结点句柄;
 *          4 创建发布者对象;
 *          5 编写发布逻辑并发布数据
*/

int main(int argc, char  *argv[])
{

    //设置编码
    setlocale(LC_ALL,"");

  	//2.初始化 ROS 节点:命名(唯一)
    // 参数1和参数2 后期为节点传值会使用
    // 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
    ros::init(argc,argv,"pub");

    // 3  创建结点句柄;
    ros::NodeHandle nh;    //该类封装了 ROS 中的一些常用功能

    // 4 创建发布者对象;
  	//泛型: 发布的消息类型
    //参数1: 要发布到的话题
    //参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
    ros::Publisher  pub = nh.advertise<std_msgs::String>("fang",10);

    // 5 编写发布逻辑并发布数据【循环发布】
    //先创建被发布的消息
    std_msgs::String msg;


    //编写循环,循环中发布数据
    while(ros::ok())          //如果结点还存在,则一直循环
    {
        msg.data = "hello";
        pub.publish(msg);               //发布数据
    }

    return 0;
}

Modifique el archivo de configuración nuevamente

ctrl+shift+b
roscore
source ./devel/setup.bash
rosrun plumbing_pub_sub demo01_pub_c

Prueba simple

 rostopic echo fang

 

Solicite publicar datos con una frecuencia de 10 Hz y agregue un número después del texto

#include "ros/ros.h"
#include  "std_msgs/String.h"
#include <sstream>

/**
 * 发布方实现:
 *          1 包含头文件;   ROS中的文本类型是-------》std_msgs/String.h
 *          2 初始化ros结点;
 *          3 创建结点句柄;
 *          4 创建发布者对象;
 *          5 编写发布逻辑并发布数据
*/

int main(int argc, char  *argv[])
{

    //设置编码
    setlocale(LC_ALL,"");


    //2 初始化ros结点
    ros::init(argc,argv,"pub");


    //3 创建结点句柄
    ros::NodeHandle nh;


    //4 创建发布者对象
    ros::Publisher publisher = nh.advertise<std_msgs::String>("fang",10);


    //5 编写发布逻辑并且发布数据
    //5.5要求以10hz的频率发布数据,并且在文本后添加编号
    //5.1先创建被发布的消息
    std_msgs::String msg;

    //5.6 设置发布频率
    ros::Rate rate(10);            //10hz的频率

    //5.2编写循环,在循环中发布数据

    int count = 0;

    while(ros::ok())
    {

        count++;
     //   msg.data = "hello";                  //5.3数据赋值

        //实现字符串拼接数据
        std::stringstream ss;
        ss<<"hello----->"<<count;
        msg.data = ss.str();         //将ss流的数据转换成字符串付给msg.data

        publisher.publish(msg);                  //5.4发布数据

        //添加日志
        ROS_INFO("发布的数据是:%s",ss.str().c_str());

        rate.sleep();
    }

    return 0;
}
编译
ctrl+shift+b
roscore
source ./devel/setup.bash
rosrun plumbing_pub_sub demo01_pub

查看
rostopic echo fang

2. Suscriptor

#include "ros/ros.h"
#include "std_msgs/String.h"



/**
 * 订阅方实现:
 *          1 包含头文件;   ROS中的文本类型是-------》std_msgs/String.h
 *          2 初始化ros结点;
 *          3 创建结点句柄;
 *          4 创建订阅者者对象;
 *          5 处理订阅到的数据
 *          6 声明spin函数
*/

  //  5 处理订阅到的数据
//编写回调函数:参数是订阅到的消息
void doMsg(const std_msgs::String::ConstPtr &msg)
{
    //通过msg参数获取并操作订阅的数据
    ROS_INFO("订阅到数据是:%s",msg->data.c_str());
}

int main(int argc, char  *argv[])
{
   
     // 2 初始化ros结点;
    ros::init(argc,argv,"sub");

    // 3 创建结点句柄;
    ros::NodeHandle nh;

    // 4 创建订阅者者对象;
    ros::Subscriber subscriber = nh.subscribe("fang",10,doMsg);

    ros::spin();     //循环读取接收的数据,并调用回调函数处理

    return 0;
}

Modificar el archivo de configuración

编译
ctrl+shift+b
roscore
source ./devel/setup.bash
rosrun plumbing_pub_sub demo02_sub_c

3 atención

Suplemento 0:

La función principal en vscode declara int main (int argc, char const * argv []) {}, el argv generado por defecto es modificado por const, este modificador debe ser eliminado

Suplemento 1:

ros / ros.h No existe tal archivo o directorio .....

Verifique CMakeList.txt y find_package está duplicado, simplemente elimine los que tienen menos contenido

Material de referencia: https://answers.ros.org/question/237494/fatal-error-rosrosh-no-such-file-or-directory/

Suplemento 2:

Find_package puede ejecutarse sin agregar algunos paquetes. La respuesta de ros.wiki es la siguiente

 You may notice that sometimes your project builds fine even if you did not call find_package with all dependencies. This is because catkin combines all your projects into one, so if an earlier project calls find_package, yours is configured with the same values. But forgetting the call means your project can easily break when built in isolation.

Suplemento 3:

Al suscribirse, se pierden los datos anteriores

Ejecute el suscriptor primero, luego el editor

Motivo: Al enviar los datos anteriores, el editor aún no se ha registrado en roscore, por lo que no se puede suscribir.

Solución: Después del registro, agregue sleep ros :: Duration (3.0) .sleep (); retrase el envío de los primeros datos

Puede utilizar rqt_graph para ver la relación de nodo.

Mensaje personalizado de comunicación de 3 temas

En el protocolo de comunicación ROS, el soporte de datos es una parte relativamente importante. En ROS, algunos tipos de datos nativos se encapsulan a través de std_msgs, como: String, Int32, Int64, Char, Bool, Empty ... Sin embargo, estos datos generalmente son solo Contiene un campo de datos. La estructura única significa limitaciones funcionales. Cuando se transmiten algunos datos complejos, como: información lidar ... std_msgs es débil debido a una descripción deficiente. Puede usar personalizado en este escenario Tipo de mensaje

msgs es solo un archivo de texto simple, cada línea tiene un tipo de campo y un nombre de campo, los tipos de campo que se pueden usar son:

  • int8, int16, int32, int64 (o tipo sin firmar: uint *)

  • float32, float64

  • cuerda

  • Duración de tiempo

  • otros archivos msg

  • matriz de longitud variable [] y matriz de longitud fija [C]

También hay un tipo especial en ROS: el Headerencabezado contiene la información de la marca de tiempo y el marco de coordenadas que se usa comúnmente en ROS. A menudo verá que la primera línea del archivo msg tiene Header标头.

Requisitos: Crear un mensaje personalizado, el mensaje contiene la información de la persona: nombre, altura, edad, etc.

Proceso:

  1. Crea archivos msg en un formato fijo

  2. Editar archivo de configuración

  3. Compile y genere archivos intermedios que puedan ser llamados por Python o C ++

1. Defina el archivo msg

Cree un nuevo directorio de mensajes en el paquete de funciones y agregue el archivo Person.msg

string name
uint16 age
float64 height

2. Edite el archivo de configuración

Agregue dependencia de compilación y dependencia de ejecución en package.xml

  <build_depend>message_generation</build_depend>
  <exec_depend>message_runtime</exec_depend>
  <!-- 
  exce_depend 以前对应的是 run_depend 现在非法
  -->
Copy

CMakeLists.txt editar la configuración relacionada con msg

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  message_generation
)
# 需要加入 message_generation,必须有 std_msgs
## 配置 msg 源文件
add_message_files(
  FILES
  Person.msg
)
# 生成消息时依赖于 std_msgs
generate_messages(
  DEPENDENCIES
  std_msgs
)
#执行时依赖
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES demo02_talker_listener
  CATKIN_DEPENDS roscpp rospy std_msgs message_runtime
#  DEPENDS system_lib
)

3. Compilar

Ver el archivo intermedio compilado:

Archivos intermedios que C ++ necesita llamar (... / workspace / devel / include / package name / xxx.h)

Archivos intermedios que Python necesita llamar (... / workspace / devel / lib / python2.7 / dist-packages / package name / msg)

Cuando se llama posteriormente al msg relacionado, se llama desde estos archivos intermedios

Comunicación de 4 temas llamada de mensaje personalizado A (C ++)

demanda:

Escriba una implementación de publicación y suscripción, que requiera que el editor publique un mensaje personalizado a una frecuencia de 10 HZ (10 veces por segundo) y que el suscriptor se suscriba al mensaje personalizado e imprima el contenido del mensaje.

análisis:

En la implementación del modelo, no es necesario implementar el maestro ROS y se ha encapsulado el establecimiento de la conexión.Hay tres puntos clave a los que se debe prestar atención:

  1. Editor

  2. receptor

  3. Datos (mensaje personalizado aquí)

Proceso:

  1. Escriba la implementación del editor;

  2. Implementación de suscriptor de escritura;

  3. Edite el archivo de configuración;

  4. Compila y ejecuta.

0.vscode configuración

Para facilitar las solicitudes de código y evitar lanzar excepciones por error, primero debe configurar vscode y configurar la ruta del archivo principal generado previamente en la propiedad includepath de c_cpp_properties.json:

{
    "configurations": [
        {
            "browse": {
                "databaseFilename": "",
                "limitSymbolsToIncludedHeaders": true
            },
            "includePath": [
                "/opt/ros/noetic/include/**",
                "/usr/include/**",
                "/xxx/yyy工作空间/devel/include/**" //配置 head 文件的路径 
            ],
            "name": "ROS",
            "intelliSenseMode": "gcc-x64",
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++17"
        }
    ],
    "version": 4
}

1. Emisor

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"

/*
发布方:发布人消息
            1 包含头文件
            2 初始化ros结点
            3 创建结点句柄
            4 创建发布者对象
            5 编写发布逻辑,发布数据


*/

int main(int argc, char  *argv[])
{
   setlocale(LC_ALL,"");

    //2 初始化ros结点
    ros::init(argc,argv,"banZhuRen");

    //3 创建结点句柄
    ros::NodeHandle nh;

    // 4 创建发布者对象
       ros::Publisher pubPerson = nh.advertise<plumbing_pub_sub::Person>("chat",100);

    //5 编写发布逻辑,发布数据
    //创建被发布的数据
    plumbing_pub_sub::Person person;
    person.name = "张三";
    person.age = 18;
    person.height = 1.73;

    //发布的频率
    ros::Rate  rate(1);
    
    //循环发布数据
    while(ros::ok())
    {
       //修改被发布的数据
       person.age++;


       //核心是发布数据
       pubPerson.publish(person);

       //休眠
       rate.sleep();

       //回头函数
       ros::spinOnce();
    }



    return 0;
}

Archivo de configuración

Para utilizar mensajes personalizados, también debe configurar

add_dependencies(demo03_sub_person ${PROJECT_NAME}_generate_messages_cpp)

Asegúrese de que, al compilar, compile primero el msg personalizado y compile el archivo que llama a msg

Compilar y probar

roscore
source ./devel/setup.bash
rosrun plumbing_pub_sub demo03_pub_person 
这里不能直接rostopic echo chat
需要先进入工作空间,再source ./devel/setup.bash
然后rostopic echo chat

2. Suscriptor

#include "ros/ros.h"
#include "plumbing_pub_sub/Person.h"

/**
 * *
  发布方:发布人消息
            1 包含头文件
            2 初始化ros结点
            3 创建结点句柄
            4 创建订阅者对象
            5 编写回调函数,处理数据
            6 spin()函数


*/

//回调函数
void doMsg(const plumbing_pub_sub::Person::ConstPtr& person)
{
    ROS_INFO("订阅的人的信息是:%s,%d,%.2f",person->name.c_str(),person->age,person->height);
}


int main(int argc, char  *argv[])
{
    
    setlocale(LC_ALL,"");
    ROS_INFO("这是订阅方:");

     //2 初始化ros结点
    ros::init(argc,argv,"student");

    // 3 创建结点句柄
    ros::NodeHandle nh;

    // 4 创建订阅者对象
    ros::Subscriber subPerson = nh.subscribe("chat",100,doMsg);

    // 5 编写回调函数,处理数据
   	// 6 spin()函数
    ros::spin();
  
    return 0;
}

Archivo de configuración

Compilar y probar

roscore
需要先进入工作空间,再source ./devel/setup.bash
然后rosrun plumbing_pub_sub demo03_pub_person 
rosrun plumbing_pub_sub demo04_sub_person 

Vista de gráfico de cálculo

Supongo que te gusta

Origin blog.csdn.net/Zhouzi_heng/article/details/114496792
Recomendado
Clasificación